andrewyng--aisuite
e0f8bac9f1
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
325 行
19 KiB
Markdown
325 行
19 KiB
Markdown
# 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 frontmatter** — `persona ⊇
|
||
skill`, so authoring is familiar. The markdown body *is* the system prompt; the frontmatter
|
||
declares the capability surface.
|
||
|
||
```yaml
|
||
---
|
||
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. **Timer** — `sleep_for(duration)` / `sleep_until(time)`.
|
||
2. **On-completion** — `wake_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-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 are `code`-family.
|
||
- **2026-06-27** — **Code 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-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 `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.
|