| 1 | # RFC: CodeWhale Workrooms — Chat-native Threaded Agent Work |
| 2 | |
| 3 | **Issue:** #3209 |
| 4 | **Status:** Future RFC — Phase 1 shipped (protocol types + link parser in |
| 5 | `crates/protocol/src/workroom.rs`); Phase 2 (store, app-server endpoints, |
| 6 | `resolve_workroom_link` tool, TUI inbox) not started |
| 7 | **Date:** 2026-06-17 (status refreshed 2026-07-15) |
| 8 | **Target:** post-v0.9.0; the release label is a maintainer decision, not part |
| 9 | of this design |
| 10 | |
| 11 | This document is design scaffolding. As of v0.9.0 the tree carries the |
| 12 | shared protocol types and link parser only; runtime endpoints, mobile UI |
| 13 | integration, persistent state, and model-visible tools remain follow-up work. |
| 14 | |
| 15 | ## 1. Problem |
| 16 | |
| 17 | CodeWhale agent work currently lives in transient TUI sessions, local Runtime API |
| 18 | threads, Fleet runs, and chat-bridge message loops — each with its own lifecycle, |
| 19 | state representation, and context boundary. There is no first-class abstraction |
| 20 | that: |
| 21 | |
| 22 | - lets a user start work on one surface (TUI) and resume it on another (mobile) |
| 23 | - gives a stable, shareable link to a thread of agent work |
| 24 | - attaches GitHub issues/PRs/commits as context without copying transcripts |
| 25 | - records which agent/model produced each event for multi-agent workflows |
| 26 | - provides a unified inbox of mentions, approvals, failures, and completions |
| 27 | |
| 28 | ## 2. Proposed Abstraction: `Workroom` |
| 29 | |
| 30 | A `Workroom` is a durable, addressable container for a threaded conversation |
| 31 | involving one or more agents, models, and human participants. It maps onto |
| 32 | the existing Runtime API thread infrastructure and extends it with: |
| 33 | |
| 34 | ### 2.1 Core Types |
| 35 | |
| 36 | ```rust |
| 37 | /// Unique identifier for a workroom, stable across restarts. |
| 38 | pub struct WorkroomId(pub String); // e.g. "wr_abc123def456" |
| 39 | |
| 40 | /// A workroom aggregates threads, members, and metadata. |
| 41 | pub struct Workroom { |
| 42 | pub id: WorkroomId, |
| 43 | pub title: String, |
| 44 | pub workspace: Option<String>, // repo root or project path |
| 45 | pub repo_identity: Option<RepoRef>, // GitHub repo identity (owner/name) |
| 46 | pub owner: String, // local user or identity handle |
| 47 | pub created_at: DateTime<Utc>, |
| 48 | pub updated_at: DateTime<Utc>, |
| 49 | pub visibility: WorkroomVisibility, |
| 50 | } |
| 51 | |
| 52 | pub enum WorkroomVisibility { |
| 53 | Private, |
| 54 | Shared { allowed_tokens: Vec<String> }, |
| 55 | } |
| 56 | |
| 57 | /// A thread within a workroom — can be a channel, DM, or linked external ref. |
| 58 | pub struct WorkroomThread { |
| 59 | pub id: String, |
| 60 | pub workroom_id: WorkroomId, |
| 61 | pub title: String, |
| 62 | pub kind: WorkroomThreadKind, |
| 63 | pub external_ref: Option<ExternalThreadRef>, |
| 64 | pub created_at: DateTime<Utc>, |
| 65 | } |
| 66 | |
| 67 | pub enum WorkroomThreadKind { |
| 68 | Channel, |
| 69 | DirectMessage, |
| 70 | AgentTask, // spawned by an agent for sub-work |
| 71 | ApprovalQueue, // pending human approvals |
| 72 | ReceiptLog, // completed agent receipts |
| 73 | } |
| 74 | |
| 75 | /// An external reference that can be attached to a workroom thread. |
| 76 | pub enum ExternalThreadRef { |
| 77 | GitHubIssue { |
| 78 | owner: String, |
| 79 | repo: String, |
| 80 | number: u64, |
| 81 | }, |
| 82 | GitHubPullRequest { |
| 83 | owner: String, |
| 84 | repo: String, |
| 85 | number: u64, |
| 86 | }, |
| 87 | GitHubCommit { |
| 88 | owner: String, |
| 89 | repo: String, |
| 90 | sha: String, |
| 91 | }, |
| 92 | GitHubCheck { |
| 93 | owner: String, |
| 94 | repo: String, |
| 95 | check_run_id: u64, |
| 96 | }, |
| 97 | } |
| 98 | |
| 99 | /// An event within a workroom thread, attributed to an agent/model. |
| 100 | pub struct WorkroomEvent { |
| 101 | pub id: String, |
| 102 | pub thread_id: String, |
| 103 | pub workroom_id: WorkroomId, |
| 104 | pub timestamp: DateTime<Utc>, |
| 105 | pub kind: WorkroomEventKind, |
| 106 | pub agent: Option<AgentAttribution>, |
| 107 | } |
| 108 | |
| 109 | pub enum WorkroomEventKind { |
| 110 | Message { content: String }, |
| 111 | Mention { mentioned_user: String }, |
| 112 | ToolCall { tool_name: String, summary: String }, |
| 113 | ToolResult { tool_name: String, success: bool }, |
| 114 | ApprovalRequest { tool_name: String }, |
| 115 | ArtifactLinked { path: String, kind: String }, |
| 116 | Receipt { summary: String }, |
| 117 | Failure { error: String }, |
| 118 | NeedsHuman { reason: String }, |
| 119 | Resumed, |
| 120 | } |
| 121 | |
| 122 | pub struct AgentAttribution { |
| 123 | pub provider: String, // e.g. "deepseek" |
| 124 | pub model: String, // e.g. "deepseek-v4-pro" |
| 125 | pub agent_id: String, // sub-agent or fleet worker id |
| 126 | } |
| 127 | |
| 128 | /// A link that can be pasted into any surface and resolved back to a workroom. |
| 129 | pub struct WorkroomLink { |
| 130 | pub workroom_id: WorkroomId, |
| 131 | pub thread_id: Option<String>, |
| 132 | pub event_id: Option<String>, |
| 133 | } |
| 134 | ``` |
| 135 | |
| 136 | ### 2.2 Link Format |
| 137 | |
| 138 | ``` |
| 139 | codewhale://workroom/wr_abc123def456 |
| 140 | codewhale://workroom/wr_abc123def456/thread/thr_xyz |
| 141 | codewhale://workroom/wr_abc123def456/event/evt_789 |
| 142 | ``` |
| 143 | |
| 144 | ### 2.3 Mapping to Existing Infrastructure |
| 145 | |
| 146 | | Workroom concept | Existing mapping | |
| 147 | |---|---| |
| 148 | | `Workroom` | New abstraction; future persisted state alongside Runtime API threads | |
| 149 | | `WorkroomThread` | Maps to a `ThreadId` in the Runtime API | |
| 150 | | `WorkroomEvent` | Wraps existing thread/fleet events with agent attribution | |
| 151 | | `WorkroomLink` | New URL scheme resolvable by the Runtime API | |
| 152 | | `ExternalThreadRef` | New; metadata-only, no secret/token storage | |
| 153 | | `AgentAttribution` | Extracted from sub-agent metadata and fleet worker identity | |
| 154 | |
| 155 | ## 3. Planned Runtime API Endpoints |
| 156 | |
| 157 | ### 3.1 `GET /workrooms` |
| 158 | |
| 159 | List all workrooms visible to the authenticated caller. |
| 160 | |
| 161 | Response: |
| 162 | ```json |
| 163 | { |
| 164 | "workrooms": [ |
| 165 | { |
| 166 | "id": "wr_abc123", |
| 167 | "title": "PR #3231 — DeepInfra support", |
| 168 | "updated_at": "2026-06-15T12:00:00Z", |
| 169 | "active_threads": 3 |
| 170 | } |
| 171 | ] |
| 172 | } |
| 173 | ``` |
| 174 | |
| 175 | ### 3.2 `GET /workroom/:id/threads` |
| 176 | |
| 177 | List active threads within a workroom. |
| 178 | |
| 179 | ### 3.3 `GET /workroom/resolve?link=codewhale://workroom/wr_abc/thread/thr_x` |
| 180 | |
| 181 | Resolve a workroom link to scoped context (thread metadata, recent events) |
| 182 | without replaying the full transcript. |
| 183 | |
| 184 | ### 3.4 Planned tool: `resolve_workroom_link` |
| 185 | |
| 186 | A model-visible tool that takes a `codewhale://workroom/...` URL and returns |
| 187 | the scoped context (thread title, recent event summaries, external refs). This |
| 188 | should not be registered until the backing runtime resolution behavior exists. |
| 189 | |
| 190 | ## 4. Security Model |
| 191 | |
| 192 | - **Local-first by default.** Persisted workroom state should live under the |
| 193 | CodeWhale home directory alongside existing state. No cloud service is |
| 194 | assumed. |
| 195 | - **Runtime API auth required.** Planned workroom endpoints must use the same |
| 196 | `Authorization: Bearer <token>` protection as other runtime surfaces. |
| 197 | - **No secrets in links.** Workroom links contain only opaque IDs, never API |
| 198 | keys or tokens. Resolution requires local Runtime API access. |
| 199 | - **No secrets in events.** Event payloads must not contain API keys, auth |
| 200 | tokens, or plaintext credentials. The `ArtifactLinked` event kind references |
| 201 | paths, not contents. |
| 202 | - **Share semantics.** `WorkroomVisibility::Shared` lists allowed bearer tokens, |
| 203 | not usernames. The operator controls which tokens can access a workroom. |
| 204 | - **No public links.** There is no unauthenticated read path for workrooms. |
| 205 | |
| 206 | ## 5. Integration Points |
| 207 | |
| 208 | ### 5.1 Mobile Control Page |
| 209 | |
| 210 | The mobile page at `/mobile` already lists active threads. Replace its |
| 211 | ad-hoc thread listing with the `/workrooms` projection so it renders the |
| 212 | same inbox that the TUI and chat bridges see. |
| 213 | |
| 214 | ### 5.2 Chat Bridges (Telegram, Feishu) |
| 215 | |
| 216 | Chat bridges currently maintain their own message loops. Each bridge should |
| 217 | publish bridge-originated messages as `WorkroomEvent::Message` into a |
| 218 | designated workroom thread, and consume `WorkroomEvent::Mention` events |
| 219 | as bridge notifications. |
| 220 | |
| 221 | ### 5.3 TUI |
| 222 | |
| 223 | The TUI should surface workroom inbox events (mentions, approvals) in the |
| 224 | sidebar, and allow pasting `codewhale://` links into the composer for |
| 225 | context resolution. |
| 226 | |
| 227 | ## 6. Implementation Plan |
| 228 | |
| 229 | ### Phase 1: Foundation (this PR) |
| 230 | - [x] RFC design doc |
| 231 | - [x] `WorkroomId`, `Workroom`, `WorkroomThread`, `WorkroomEvent`, `WorkroomLink` types |
| 232 | - [x] `ExternalThreadRef` (GitHub refs as workroom context) |
| 233 | - [x] `AgentAttribution` (multi-agent/model event attribution) |
| 234 | - [x] Security model documentation |
| 235 | - [x] Architecture docs |
| 236 | |
| 237 | ### Phase 2: Integration (follow-up) |
| 238 | - [ ] Persistent workroom state store |
| 239 | - [ ] Runtime API endpoints: `GET /workrooms`, `GET /workroom/:id/threads` |
| 240 | - [ ] `resolve_workroom_link` tool for link resolution |
| 241 | - [ ] Mobile page consumes workroom projection |
| 242 | - [ ] Chat bridges publish/consume workroom events |
| 243 | - [ ] TUI inbox sidebar |
| 244 | - [ ] Workroom link paste resolution in composer |
| 245 | |
| 246 | ## 7. Non-goals for Phase 1 |
| 247 | |
| 248 | - No hosted public CodeWhale cloud service |
| 249 | - No default-on Slack/Discord/Feishu/Telegram/GitHub App integration |
| 250 | - No arbitrary public share links without explicit auth story |
| 251 | - No model-specific workroom format |
| 252 | - No migration of existing threads (new workrooms only) |
| 253 |