项目文件夹

文件
Rohit C Prasad e0f8bac9f1 Squash commit with multiple features and fixes.
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
2026-07-14 16:51:52 -07:00

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 = code family (lives in IaC/repos) + shell + cloud/k8s connectors + ops prompt.
  • SecOps = code family (scans repos) + security tools + alerting connectors + a read-leaning prompt.
  • Ops / Marketing / Sales = knowledge family + 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_roots with per-root writable + the request_directory grant 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 frontmatterpersona ⊇ 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_models are 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 existing model_ready machinery), never a hard block.
  • default_permission_mode is the starting posture. A persona cannot self-escalate; a higher posture requires explicit user opt-in at enable time.

Lifecycle states

  1. Installed — present on disk (built-in, local dir, or fetched from a git URL).
  2. Enabled — the user turned it on in the Personas settings panel.
  3. 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:

  1. 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?"
  2. 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):

  1. Timersleep_for(duration) / sleep_until(time).
  2. On-completionwake_on(job_id) when a backgrounded command/job exits; the exit code and output are handed back on resume.
  3. On-message (free) — a user steering message into the session wakes it (reuses the inbound queue/steering of the messaging connectors).
  4. (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. See PERMISSIONS-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-26Inbox 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 are code-family.
  • 2026-06-27Code stays a persona (canonical code-family persona); family is 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-27Per-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-27No "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 code family.
  • 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.