Short list of changes - - Automations quickstart - Slack mentions router - Live turns keep narration inside. Add commentary for tasks. - Approvals card UI enhancements - Newer models - Fable, Sol, Terra, etc added to list
19 KiB
Coworker Personas — design
STATUS: design (draft for review). Discussed with Rohit 2026-06-26. Generalizes the
fixed surfaces of SURFACES.md into declarative, distributable personas. Supersedes
the four-fixed-surfaces model: Code becomes a built-in persona, Cowork becomes the
default persona, and MyHelper goes away — replaced by pinnable long-running persona
sessions. Decisions log at the bottom.
Why
Today the product ships a fixed set of hardcoded agents (Chat / Code / Cowork / MyHelper). We want specialized coworkers — a SWE Coworker, an Ops Coworker, later Marketing and Sales — and we want third parties to distribute their own (internal or external) without forking the app.
The key realization: a "persona" needs no new primitive. It is a packaging and configuration layer over things we already have — a system prompt, a tool allowlist, skills, connectors/MCP, a default permission mode, a recommended model. So a persona is a declarative bundle, and our own built-ins are expressed in the same format third parties use. If SWE Coworker can't be written as a persona manifest, the format isn't good enough yet (the dogfooding test).
The model: three orthogonal concepts
Pulling these apart keeps the data model and UI clean — one mechanism covers all three.
| Concept | What it is | Notes |
|---|---|---|
| Persona | a definition / template (the manifest) | lifecycle: installed → enabled → surfaced |
| Session | a running instance, born from exactly one persona | pinnable + renameable; identity fixed at birth |
| View | how sessions are grouped in the sidebar | per-persona or flat under Coworker — pure presentation |
- A session is born from exactly one persona. It can pull in skills mid-flight, but its identity (prompt, tool surface, defaults) is fixed.
- Pinning is purely an accessibility affordance — a bookmark. It keeps a session one click away (in the sidebar's Pinned band) and has no behavioral meaning: it grants no powers, changes no permissions, and does not make a session "run." See No "always-on" mode below.
- View is just a grouping toggle; every session already knows its persona, so nothing in the data model changes.
Family — the workspace model (and the first-class "specialness")
Personas are not flat. Every persona belongs to a family, and the family is what carries a persona's structural specialness. Critically, family encodes the workspace model, not the job role — and it is binary:
| Family | Workspace model | Starts with | UI consequences |
|---|---|---|---|
code |
works inside a repo you pick (git-bound) | a chosen repo, writable | Projects grouping, git/diff tools, explorer subagents |
knowledge |
produces deliverables in a scratch folder | an auto-provisioned writable scratch (no repo needed) | no Projects; outcome-oriented; low barrier to begin |
Roles compose on top of a family — a role = family + tools + connectors + prompt:
- DevOps =
codefamily (lives in IaC/repos) + shell + cloud/k8s connectors + ops prompt. - SecOps =
codefamily (scans repos) + security tools + alerting connectors + a read-leaning prompt. - Ops / Marketing / Sales =
knowledgefamily + role tools/connectors/prompt.
So we do not add families per role (devops, secops, …). Family stays binary; everything
role-specific is composition. Code stays a persona — the canonical code-family persona —
which is exactly what lets a third party ship a DevOps/SecOps/Rust coworker that inherits all the
code-family behavior for free.
Folders make everything else possible (power users). Family sets the starting point and defaults, not a hard limit:
- A knowledge persona can add a repo/dir to read (default read-only) or a dir to write results into. It already has a writable scratch, so it rarely needs more to begin.
- A code persona can add read-only reference repos/dirs, or a second writable repo for cross-repo work.
- Per-folder writability is a user choice in both families; family only chooses the sensible
default/emphasis. (This is already the backend model: writable scratch +
extra_rootswith per-rootwritable+ therequest_directorygrant flow for knowledge; a single writable repo root for code.)
The frontend must be family-aware, not id-aware. "New Project"/grouping, the add-folder
defaults, git affordances — all key off family === "code", never a hardcoded "code" id.
(Today the sidebar hardcodes the id; that's the bug behind "why does only Code have Projects.")
What a persona is (the bundle)
A persona manifest is skill-shaped markdown with richer YAML frontmatter — persona ⊇ skill, so authoring is familiar. The markdown body is the system prompt; the frontmatter
declares the capability surface.
---
id: ops-coworker
name: Ops Coworker
icon: ops # from the built-in icon set
tagline: Watches your infra and runs the runbook
tools: [shell, files, web_search, http] # vetted-catalog IDs (allowlist)
connectors: [pagerduty, datadog] # expected integrations
mcp: [] # MCP servers it wants
recommended_models: [anthropic:claude-opus-4-8, openai:gpt-5.5]
skills: [incident-runbook, postmortem-writer] # referenced skill IDs
default_permission_mode: read-only # cannot self-grant higher
family: knowledge # code | knowledge — the workspace model (see Family)
workspace: deliverable # git | deliverable | none
automations: [] # optional starter schedules
---
You are the Ops Coworker. Your job is to ... (operating doctrine)
- Tools are referenced by ID from a vetted catalog (see Distribution). A persona never ships executable code.
recommended_modelsare recommendations, not requirements. If the user runs a different / unconfigured model, that's their choice — at the risk of worse results. We surface a gentle nudge (reusing the existingmodel_readymachinery), never a hard block.default_permission_modeis the starting posture. A persona cannot self-escalate; a higher posture requires explicit user opt-in at enable time.
Lifecycle states
- Installed — present on disk (built-in, local dir, or fetched from a git URL).
- Enabled — the user turned it on in the Personas settings panel.
- Surfaced — the user chose to show it as a selector when starting a new Coworker session. (Enabled-but-not-surfaced personas still run; they just don't clutter the new-session picker.)
Cowork is the default persona and is always present, but can be disabled for users who want only specific personas. If Cowork is disabled, the new-session button defaults to the user's chosen default persona (set in the same panel) — so there's never a dead end.
The collapse (replaces SURFACES.md)
- Code → a built-in
code-family persona (git-bound workspace, coding tools). - Cowork → the default
knowledge-family persona (deliverable workspace, general tools). - MyHelper → gone. Its "personal helper" role becomes a pinned, renamed persona session. The win: any persona session can be triggerable (self-wake / messages / events), and there's no special fourth subsystem to maintain. There is no "always-on" mode (see above).
Sidebar information architecture
Decided with Rohit (UX pass, 2026-06-27). Layout = "Pinned band on top":
✦ OpenCoworker ⚙
🔍 Search 🔌 Integrations ⏰ Automations ← shared top zone (global, all coworkers)
── PINNED ──────────────────────────
📌 Ops Watcher · (liveness dot)
📌 Daily Brief ② (attention count)
── SURFACES ─────────────────────────
▾ ⬥ OpenCoworker ★default
Q3 research memo ● (attention on this session)
+ New session
▸ ⬦ Ops Coworker ① (rolled-up attention; collapsed)
▸ ◧ Code › Projects
─────────────────────────────────────
📥 Inbox ② 🗒 Audit ⚙ Manage
Rules:
- Shared top zone above the persona accordions for things common to all coworkers: Search, Integrations (the global connect page), Automations. (Previously these lived inside the Cowork accordion only, so Code/Ops couldn't reach them — a real gap.) The footer keeps the meta items: Inbox, Audit, Manage.
- Default persona (OpenCoworker) leads the persona list; Chat is hidden by default (not-surfaced — recoverable from the Personas tab, not deleted; Cowork covers quick Q&A).
- A cross-persona "Pinned" band sits at the top. It contains only manual pins (pure bookmarks). Nothing auto-surfaces into it.
- Expand ≠ switch. Expanding a persona accordion browses its sessions; the chat area only changes when you actually pick a session or hit New session — so you can peek at another persona's sessions while keeping your current conversation loaded.
- Projects nest only under
code-family personas (family-aware, not id-aware).
Attention vs. liveness (sidebar indicators)
Two orthogonal per-session signals, neither tied to pinning (any session can show either):
- Attention = an Inbox item is pending for that session. Rendered as an amber count
that bubbles up: session row → persona accordion header (rolled-up count) → footer
Inbox total. All three are views of the one Inbox queue — answering anywhere resolves it
everywhere (the resolve-once, first-responder-wins state machine in
PERMISSIONS-AND-INBOX.md). There is no separate "Needs attention" tab — that would just rebuild the Inbox. - Liveness = "working now" or "sleeping with a pending wake." A quiet, count-less dot. Informational; it never bubbles into a count (so idle scheduled agents don't inflate the attention number).
Distribution
Hard rule: we never ship or run third-party executable code. The platform ships a broad catalog of vetted tools; personas compose them by ID. New capabilities arrive via MCP (which has its own boundary), not via persona code. This makes "install a persona" a light trust event — the user consents to a declared set of tools / connectors / permission mode, not to arbitrary code.
Two distribution modes, different weight:
- Persona packages (lightweight, build first). A folder + manifest, loaded from a
local dir (
~/.coworker/personas/) or a git URL. Runs on stock OpenCoworker. This is how "Acme Ops Coworker" reaches people who already run the app. At install we show an install-time capability consent: "This Coworker will use shell, files, PagerDuty, and defaults to read-only mode. Allow?" - White-label builds (heavy, later). A company configures/brands the app, bakes in its default personas, and ships it as their own coworker product.
The vetted-tool catalog (how tools are namespaced/grouped so frontmatter can reference
them cleanly) is designed in PERMISSIONS-AND-INBOX.md.
No "always-on" mode — being triggerable is a consequence, not a state
There is no "always-on" agent class. Every session is equally "on": it can resume whenever
something can trigger it — a self-wake (sleep_*), an inbound message, or (later) an event.
Being triggerable is simply a consequence of the tools/connectors that session holds, not a
mode you switch into. A plain Cowork session that called sleep_for is exactly as "always-on" as
a dedicated Ops watcher. This is why pinning is pure accessibility (above) and never a
behavioral toggle — there's no behavior to toggle.
Long-running agents — self-wake tools
A long-running agent is not a process that's always running (that was MyHelper's cost problem). Instead it is suspend/resume — event-driven: it sleeps at ~zero cost and the scheduler re-invokes the session on a trigger. "Many long-running agents" is cheap precisely because idle ones aren't running.
Triggers (a small set):
- Timer —
sleep_for(duration)/sleep_until(time). - On-completion —
wake_on(job_id)when a backgrounded command/job exits; the exit code and output are handed back on resume. - On-message (free) — a user steering message into the session wakes it (reuses the inbound queue/steering of the messaging connectors).
- (Later) On-event — a connector/webhook fires (an email arrives, a PR opens, an alert triggers). Same plumbing; this is what makes a pinned Ops Coworker genuinely useful.
Relationship to Automations (AUTOMATION-SCHEDULING.md): different surfaces over the
same scheduler / TaskStore. An Automation is user-authored — a cron that spawns a
session. Self-wake is agent-authored — a live session scheduling its own resumption.
Both reduce to "enqueue a future invocation of a session," so they share plumbing but stay
distinct in the UI.
The Inbox
Full design — risk classes, modes, the Unattended toggle, item state machine, resume reconciliation, and multi-inbox routing — lives in
PERMISSIONS-AND-INBOX.md. Summary below.
The Inbox is the canonical, cross-session human-attention queue. While you're working in one session, it tells you another agent needs you — an approval, a question, or a result. It decouples agent progress from your attention: agents keep working (or sleep) and route the human-needed items to one place.
Three item kinds, each deep-linking back to its originating session:
- Approval — actionable allow/deny with context ("Ops Coworker wants to restart the service").
- Question — a free-text answer the agent needs to continue.
- Notification — FYI / completion ("your report is ready" → links to the artifact).
The Inbox is the store of record; messaging connectors and a future mobile app are
transports of the same items, not separate notification systems. Slack / Telegram / OCW
Mobile render and deliver inbox items to wherever the user is, and replies flow back as
steering. One item model, many delivery channels — matches the existing messaging-connector
design (outbound send_message + inbound steering).
Why this comes before guardrails: if consequential actions must route to the Inbox rather than self-execute, the human becomes the rate-limiter. An unattended agent can't run away if every consequential step waits on a person. So the Inbox is also a lightweight substitute for the heavier safety machinery below.
Tabled for later — unattended guardrails
Wake budgets / rate caps and runaway-loop detection are noted but deliberately not designed yet. With the Inbox gating consequential actions, the immediate need is lower. Revisit once truly autonomous (non-gated) unattended work is on the table.
Phasing
Live progress + per-phase test gates:
IMPLEMENTATION-LEDGER.md.
- Phase 0 — foundation: the vetted tool catalog + risk-class refactor of
permissions.py(no behavior change). Both personas and permissions sit on it. SeePERMISSIONS-AND-INBOX.md. - Phase 1 — persona registry + manifest format; convert Code/Cowork into built-in personas; ship 2–3 defaults (SWE, Ops, generic); persona picker at new-session; Personas settings panel (enable / surface / set-default). Pinnable + renameable sessions.
- Phase 2 — load personas from local dir / git URL + install-time capability consent; self-wake tools (timer + on-completion); the Inbox (in-app).
- Phase 3 — connect the Inbox to Slack/Telegram/mobile; on-event wake; registry / marketplace; white-label build tooling.
- Phase 4 (UX/IA) — family-aware frontend (Projects/folders keyed off
family, not id); "Pinned band on top" sidebar; default-persona-first ordering; Chat hidden by default; expand≠switch; attention badges bubbling session→persona→Inbox + a count-less liveness dot.
Open questions
- Persona versioning / update — refresh semantics for git-URL personas; namespacing to avoid collisions between two "Ops Coworker"s from different authors.
- Unattended guardrails (tabled, above).
- (Tool catalog, risk classes, Inbox persistence — now in
PERMISSIONS-AND-INBOX.md.)
Decisions log
- 2026-06-26 — A persona = declarative skill-shaped bundle; no third-party executable code; tools referenced from a vetted catalog by ID; new capability via MCP.
- 2026-06-26 — Recommended (not required) models; user may override at their own risk.
- 2026-06-26 — A session is born from exactly one persona (identity fixed; may pull skills mid-flight).
- 2026-06-26 — Three orthogonal concepts: Persona (installed→enabled→surfaced) / Session (pinnable + renameable) / View (per-persona or flat).
- 2026-06-26 — Code → built-in persona; Cowork → default persona (disable-able; if off, new-session defaults to the user's chosen default persona); MyHelper removed, replaced by pinned long-running persona sessions.
- 2026-06-26 — Long-running = suspend/resume via self-wake (timer / on-completion / on-message; on-event later); shares the scheduler with Automations but is a distinct, agent-authored surface.
- 2026-06-26 — Inbox is the canonical human-attention queue (approval / question / notification, each deep-linking to its session); messaging connectors + mobile are transports of the same items; it also serves as the lightweight rate-limiter.
- 2026-06-26 — Wake budgets / runaway detection tabled (not yet designed).
- 2026-06-27 (UX pass) — Family is binary = the workspace model (
code= git-bound, Projects/diffs/explorer;knowledge= auto-scratch deliverables). Roles (DevOps/SecOps/Ops/…) compose on top (family + tools + connectors + prompt); we do not add families per role. DevOps/SecOps arecode-family. - 2026-06-27 — Code stays a persona (canonical
code-family persona);familyis the first-class "specialness". Frontend must be family-aware, never id-aware (the"code"id hardcode is the bug behind "only Code has Projects"). - 2026-06-27 — Per-folder writability is a user choice in both families; family only sets the default/emphasis. Knowledge can add read/write folders; code can add read-only refs or a 2nd writable repo. (Already the backend model.)
- 2026-06-27 — No "always-on" mode. Every session is equally triggerable as a consequence of its tools/connectors (self-wake / message / event). Pinning = pure accessibility (a bookmark), no behavioral meaning. Supersedes the "pinned = your always-on agent" framing above.
- 2026-06-27 — Sidebar IA = "Pinned band on top": cross-persona Pinned band (manual pins
only) → default-first persona accordions → footer nav. Expand≠switch; Chat hidden by
default; Projects only under
codefamily. - 2026-06-27 — Sidebar attention (Inbox-pending) = amber count bubbling session→persona-header→footer Inbox (views of one queue, not a new tab); liveness (working/sleeping) = a separate count-less dot. Orthogonal to pinning.