项目文件夹

文件
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

325 行
19 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 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.