Architecture
CoWork OS Architecture
Synced from github.com/CoWork-OS/CoWork-OS/docs
CoWork OS is a free, open-source, GUI-first, CLI-capable local AI super app, everything app, and runtime for task execution, many-agent orchestration, generated knowledge-work artifacts, background operator loops, and multi-surface automation. Its open multi-provider harness keeps tools, skills, memory, agents, approvals, artifacts, and workflows above the model-access layer so supported routes can change without replacing the surrounding work environment.
Core Architecture
- Electron main process: task orchestration, agent runtime, heartbeat orchestration, IPC, and tool execution
- React renderer: desktop UI, the Bots page (with Workspace agents), Mission Control, task timeline, settings, task boards, approval dialogs, xterm.js terminal tabs, and monitoring surfaces for managing many agents visually while the external CLI covers terminal-native starts
- First-result setup: onboarding opens on an editable task request, reuses configured model access, and keeps the existing guided personalization optional. A renderer preflight holds new task and local follow-up submissions while users connect explicitly named apps through existing connector controls. Cancellation restores the request; admitted work uses the normal task runtime, workspace permissions, and approvals. Pending preflight requests stay in renderer memory. See First-result onboarding and contextual connections.
- Model-access layer: supported account connections, provider APIs, compatible gateways, cloud credentials, local inference, and explicitly delegated external agent runtimes resolve through shared provider interfaces. Route-specific capabilities, eligibility, limits, and billing remain visible rather than being flattened into a promise of universal model parity. See Model Providers.
- MLX-LM local inference lane: the
mlxcatalog entry is implemented as an OpenAI-compatible local provider. The renderer uses the shared custom-provider settings model; Electron validates native macOSarm64plusmlx_lm/mlx.coreimports, owns themlx_lm.serversubprocess and/v1/modelsreadiness probe, and the provider adapter sends/v1/chat/completionsrequests. The server shares CoWork's local-AI process slot and port withhf-agents; it is not an embedded Electron dependency or an externally exposed production gateway. See MLX-LM Local Inference. - Atomic Chat inference lane: the opt-in
atomic-chatcatalog entry connects to an already-running Atomic Chat/v1API, resolves exact model IDs from/v1/models, and exposes typed discovery/connection states without silently replacing a saved model on failure. It sends non-streaming chat/tool requests through the existing provider interface. CoWork does not start Atomic Chat, install models, or import Atomic Agent's loop; its task, permission, approval, scheduler, evidence, and cancellation boundaries remain authoritative. The opt-inlocal-balanced-v1profile adds bounded local budgets and admission control. See Atomic Chat inference and local-model execution. - Tool and connector layer: file, shell, browser, web, native integrations, document generation/compilation tools including source-first LaTeX PDF compilation, MCP connectors, remote execution, computer use (
screenshot,click,type_text, and related tools) as a governed desktop-GUI lane (platform helper, single-session lock, policy-gated routing), and on macOSnative_appbackground control of a specific app window. See Computer use and Local background app control. - Remote MCP OAuth layer: streamable-HTTP MCP servers can sign in with OAuth 2.1. Delegate identities use authorization code with PKCE and an http or https loopback redirect (https uses a per-sign-in self-signed certificate held only in memory); ambient identities use RFC 7523 JWT bearer assertions. Endpoints come from a preset (Workday), manual entry, or RFC 9728/8414 discovery. The transport renews tokens before expiry and once after a 401, records refresh proofs so approved calls keep their authority, backs off on 429, and never resends a tool call after a 503. Servers with more than 25 tools load them on demand through
tool_search. Workday preset servers also have in-app agent setup: an admin API client signs in (token kept in memory only) to register the external agent's skills and tools through the tenant's ASOR API. Renewal is plain Node, so it works in headless runs and the daemon. See Remote MCP servers with OAuth. - Secure MCP tunnel layer: outbound-only WebSocket clients can expose selected local/private MCP JSON-RPC endpoints through a CoWork-operated or self-hosted relay, with separate client/caller tokens, relay-side policy, local policy, request limits, and audit events. See Secure MCP Tunnels.
- MCP connection lifecycle and events: modern discovery uses the server's connection timeout (30 seconds by default), separate from tool request deadlines, and failed connections close their transports. Before webhook routing is configured, a receiver can select another loopback port when the default
8766is occupied. Explicit routing stays fixed, and Event Triggers shows the active forwarding address. See MCP Events. - Browser upload accounting: the shared host upload sink reserves durable byte/file budgets before staging and retains committed usage across restarts at workspace, profile, and host-data-root scope. A locked atomic ledger is outside browser upload authority; uncertain cleanup stays charged. Default limits are 512 MiB/2,048 files per workspace, 1 GiB/4,096 files per profile, and 4 GiB/8,192 files per host data root, with 60 admissions per profile per minute and a 1 GiB target free-space floor. See Browser preview for persistence and local recovery.
- Terminal tab layer: workspace terminal tabs use xterm.js in the renderer and
node-ptyin Electron so user-visible terminal work flows through native PTYs instead of custom text emulation. macOS launches the user's login shell with zsh prompt/cwd integration; Windows launchescmd.exethrough node-pty's ConPTY/winpty backend with a cwd-only prompt. See Terminal Tabs. - CLI runtime layer: the
coworkcommand provides an interactive terminal UI and local one-shot task runner on top of the same database, settings, providers, workspaces, skills, MCP servers, and agent runtime as the desktop app. Localcowork runprefers a hidden Electron app-entry mode (--cowork-cli-direct-run) so encrypted desktop settings keep the same app identity.--remoteis the explicit Control Plane client path. See CoWork OS CLI. - Composer mention layer: the renderer and Electron preload expose a grouped
@autocomplete for Agents, configured Integrations, and Files. Integration mentions are resolved locally, render as rich chips, persist in task/session metadata, and inject soft routing guidance into the executor without changing permissions orallowedTools. See Composer Mentions. - Composer prediction layer: enabled-by-default renderer predictions request a bounded recent conversation through main-window-only Electron IPC after completion. The selected provider supplies its Cheap model when profile routing is enabled, or its default model otherwise; generation consumes tokens even when suggestions are dismissed. One cancellable provider call, a bounded queue, revision checks, and a successful-result cache fence stale work. The first usable prediction enables a persistent sidebar notice with a one-time tooltip and a link to the Appearance on/off setting. See Composer Predictions.
- Message shortcut layer: the renderer exposes one
/picker for deterministic app commands and skill-backed workflow shortcuts. Shared app command parsing handles/schedule,/clear,/plan,/cost,/multitask,/compact,/doctor, and/undo; plugin-pack aliases resolve to target skill IDs before generic skill slash execution. Skill-backed picker selections insert editable slash tokens before launch, and Claude-for-Legal workflows can surface structured main-view matter intake cards. See Message Box Shortcuts and Claude-for-Legal Workflows. - Chronicle screen-context lane: desktop-only passive recent-screen capture, local ranking/OCR enrichment, source resolution, provenance-aware
screen_context_resolvetool exposure, and promotion of task-used observations into workspace-backedscreen_contextevidence plus optional linked background memory generation. See Chronicle. - Managed resource layer: first-class
ManagedAgent,ManagedEnvironment, andManagedSessionresources package reusable execution definitions and durable run identities on top of existingTask,AgentTeamRun, andSessionRuntimeprimitives. The renderer exposes them through Workspace agents on the Bots page, but all manual agent actions create runtime managed sessions and open their backing tasks in the main task view rather than running in a separate agent-detail chat surface. See Managed Agents. - Persistent bot layer: reusable
AgentRoleidentities power the sidebar Bots roster and durable bot conversations. Bot transcripts reuseTaskandtask_eventsstorage but are marked withagentConfig.botConversation, filtered out of Sessions, and queried by workspace plus role. Workspace-scoped persistent bot teams verify peer messages before waking teammate conversations. See Bots, conversations, and tasks. - Structured automation layer: main-sidebar Automation Studio authors a versioned
RoutineWorkflowDefinitiongraph with one starter, typed inputs, variables, branch ports, bounded loops, per-step risk/approval/retry policy, Google account bindings, and retention settings. The Electron runtime validates and activates immutable versions, normalizes schedule/Google/channel signals into a durable deduplicated inbox, runs version-pinned deterministic steps, persists approval/cancellation/recovery state, and applies connector, network, scope, secret, redaction, and retention policy. Prompt-based Routines and task automations continue to compile to Scheduled Tasks, Webhooks, and Event Triggers through compatibility paths. See Automation Studio. - Automation/event ingress layer: scheduled tasks, webhooks, channel events, Google pollers, and MCP connector/resource notifications feed trigger services; structured workflow signals are intercepted into
routine_event_inboxbefore execution so restart recovery and idempotency do not depend on renderer state - Gateway specialization layer: channel messages can resolve durable channel/chat/thread specialization records before task creation, applying workspace, agent role, prompt guidance, tool restrictions, gateway context, and shared-memory opt-in on top of the existing gateway session and context-policy model
- Integration auth notification layer: shared Electron-side auth detection classifies stale tokens, revoked OAuth grants, missing scopes, sign-in challenges, and connector auth failures; it creates de-duped warning notifications through the existing notification service so users can reconnect integrations in Settings instead of background workers retrying silently
- Turn and tool orchestration: a session-scoped
SessionRuntimeowns task-session state, session checklists, permission state, turn coordination, resume/snapshot persistence, and task projection, while a lower-levelTurnKernelhandles the active step, follow-up, or text turn; a metadata-drivenToolSchedulerbatches concurrency-safe reads, serializes conflicting writes, and keeps tool-result ordering stable - Prompt stack and tool guidance: execution prompts are assembled from named session- and turn-scoped sections with explicit budgets; stable session sections form a provider-cacheable prefix, volatile turn sections stay uncached, memory injection adds the memory folder's
MEMORY.mdand workspace file plusL0 IdentityandL1 Essential Storyby default while deep recall (memory_recall,context_recall) stays tool-driven, retry-aware recovery guidance can inject attempt/retry state plus recent session evidence, and visible tools receive prompt-aware descriptions rendered only after policy and mode filtering - Mixture of Agents LLM layer: the provider factory can expose MoA presets as virtual model routes. A preset resolves tool-free advisor slots first, appends bounded advisory context as a separate user message, and then runs the aggregator slot with the original tools and tool choice. Slot providers can use their own fallback chains, while whole-preset MoA fallback remains explicit. See Mixture of Agents.
- Additive skill runtime: canonical task text remains immutable for skill routing purposes, while
use_skillattaches structuredSkillApplicationcontext plus scoped runtime directives instead of rewriting the task prompt. Skillrequires.toolsvalues are eligibility prerequisites and recorded metadata; they do not become task-wide allowlists or hide capabilities needed by later steps. Caller and worker tool-policy boundaries remain authoritative. See Skill runtime model. - Delegation graph: delegated work now runs through a normalized orchestration graph engine so spawned agents,
/multitasklane runs, team work, workflow phases, and ACP tasks share one run/node/event model - Worker roles and verification: built-in worker roles (
researcher,implementer,verifier,synthesizer) carry hard tool scopes, delegated work receives a structured brief instead of raw prompt passthrough, and verification runs use both early nudges and a dedicated verdict/report contract. Researchers are restricted to local read-only inspection; saved-task starts, resumes and follow-up overrides reapply the boundary before native/external runtime selection. Web and external-service research require a separately authorized task. See Execution runtime model. - Jev structured-decision and harness layer: an optional typed TypeSafe/OpenRouter route makes bounded model-profile, task-strategy, team/lane, browser-action, loop-control, context-retention, eligible skill/tool, output-guardrail, and semantic tool-review decisions. Deterministic eligibility, access profiles, hard policy, Numbat security, approvals, execution, and verification remain authoritative; Jev calls and provider-reported usage are persisted separately in
jev_call_events. See Jev Decision Support and Harness. - Federated agent orchestration: ACP registry + remote invocation let orchestrators target local roles or remote A2A-compatible agents under shared approval and policy controls
- Local persistence: SQLite, local files,
memory_itemsfacts, archive memory rows and summaries, the conversation index, checkpoints with structured summaries + verbatim evidence packets, the memory folder (a separate git repo,~/CoWork Memoryby default) with its dream records, knowledge graph state including temporal edge validity, run records, structured workflow versions/runs/steps/event inbox/event samples/starter cursors, orchestration graph nodes/events, ACP agent registrations and ACP task state, Playbook outcome entries, evidence and reinforcement links (playbook_entries,playbook_success_evidence,playbook_success_links), proactive suggestions and their feedback (suggestions,suggestion_feedback), Pulse consent windows/outbox/sent-day receipts, usage telemetry, feedback events,session_runtime_v2task snapshots, managed-agent tables (managed_agents,managed_agent_versions,managed_environments,managed_sessions,managed_session_events),.cowork/memory/topics, and workspace-kit contracts in.cowork/ - Artifact preview layer: file preview IPC resolves workspace-contained outputs, extracts document content, and enriches artifacts with renderer-ready previews. Spreadsheet previews are extracted in Electron into shared sheet structures (
spreadsheetPreview) for sheet names, used bounds, display values, formulas, styles, and column widths; workbook formats useexceljs, while CSV/TSV use a delimited parser and save back with the original delimiter. Native/app-owned spreadsheet formats such as Numbers and Google Sheets shortcuts are recognized as artifacts but open externally. Word-style document previews are extracted intodocumentPreview; DOCX-like files use Mammoth plus editable block metadata, RTF and ODT/OTT use best-effort local text extraction, legacy DOC attempts local converter fallback, and Pages is recognized for external handling. Web page previews are extracted intowebPreview; HTML/HTM files and built React output entrypoints return sandbox-ready iframe HTML with local assets inlined where possible, while React-style projects without build output return a structured preview-unavailable state. ExistingcontentandhtmlContentfallbacks remain for compatibility. PPTX previews usepresentationPreviewwith fast text/notes extraction, cachedimageUrlslide PNGs, background full rendering through Codex@oai/artifact-tool, localsoffice+pdftoppmfallback, in-flight render dedupe, and text-only fallback when image rendering is unavailable. - Browser V2 workbench layer: interactive browser-use tools target a renderer-owned Electron webview by default, with main-process automation owned by
BrowserSessionManagerand routed through ElectronwebContents.debugger/ CDP. The main process maps{ taskId, sessionId }to the webview'swebContentsId; browser tools route navigation, accessibility snapshots, ref-aware click/fill/type/read/hover/drag/upload actions, dialogs, downloads, diagnostics, emulation, tracing, and screenshots to that visible session. The renderer opens the resizable right-sidebar/fullscreen Browser Workbench on demand and carries status, screenshot capture, annotation handoff, diagnostics UI, snapshot overlay state, cursor events, and viewport events so users can see agent movement and responsive breakpoint changes over the page. The embedded session uses a persistent per-workspace partition isolated from system Chrome; explicit forced-headless, profile, browser-channel, Chrome DevTools attach, and Browser Use Cloud provider options keep Playwright/local, external-CDP, and remote stealth-browser fallback paths available when explicitly needed. Browser preflight and pinned fetch honor explicit administrator internal-host exceptions. Browser Use Cloud external debugger attachment is unavailable and rejected before remote session allocation. Real-browser profile control requires explicit consent, and Browser Use Cloud is explicit opt-in for public HTTP(S) targets with private/local target blocking and remote-session stop handling. See Browser Workbench and Browser V2 Architecture. - Private browser credentials: the toolbar and
browser_vaultshare a main-process service for exact-origin login, 2FA, address, and card entry. Owner-only IPC collects private answers outside task messages; local secrets require OS encryption and manager unlock sessions remain in memory. Saved-secret release requires native approval; agent card fills also require a fresh non-recurring approval, separate from placing an order. Document/tab/task checks and privacy generations prevent stale approvals and overlapping browser/native captures. Prompt protection persists until the renderer acknowledges clearance. Headless and external browser routes fail closed. See private browser flows. - Permission engine: access profiles are resolved first and then layered tool approval decisions combine workspace capabilities, explicit rules, hard guardrails, session grants, workspace-local policy files, legacy modes, and mode defaults including
dangerous_only, with workspace rule browsing/removal in Settings. New tasks derive command-tool availability from the profile rather than a separate shell toggle. - Runtime visibility surfaces: the task runtime emits learning progression, unified recall, persistent shell, live terminal tabs, routing events, semantic tool-batch summaries, curated external progress relays for text-first channels, session-checklist events, and follow-up completion events into Mission Control and the renderer so operator state stays visible instead of hidden in services
- Everything Workbench artifact surfaces: completion cards, timeline details, and Files panels share output metadata so generated docs, sheets, decks, web pages, PDFs, previews, and live browser sessions stay attached to the task that produced or used them. Spreadsheet outputs render as compact cards; editable workbook/CSV/TSV files open into a sidebar/fullscreen artifact workbench with editable grid state, persisted sidebar width, and fullscreen follow-up context, while native app formats keep external-app/folder actions. Word-style document outputs render as compact cards; DOCX opens into a direct-edit sidebar/fullscreen document workbench with Google Docs-style controls, save/copy actions, persisted sidebar width, fullscreen follow-up context, and preview refresh after follow-up edits, while non-editable document formats keep best-effort preview and external-app/folder actions. Presentation outputs render as compact cards; PPTX opens into a sidebar/fullscreen presentation workbench with thumbnails, navigation, zoom, speaker notes, cached slide rendering, persisted sidebar width, and deferred refresh after follow-up completion, while legacy PowerPoint formats keep external actions. Web page outputs render as compact cards; generated HTML/HTM and built React output open into a sandboxed sidebar/fullscreen iframe workbench with browser/folder/copy actions, persisted sidebar width, and deferred refresh after follow-up completion, while React-style projects without build output show a build-output-needed state instead of starting a dev server. Sidebar widths are clamped by one shared constraint helper (
src/renderer/utils/resizable-sidebar-layout.ts, mirrored in CSS) so the main conversation column always keeps its minimum width and the sidebar minimum shrinks in narrow windows. Live website testing opens a browser workbench in the same right-sidebar/fullscreen model so the agent can interact with a visible page and validate responsive breakpoints without launching an external browser. LaTeX PDFs compiled throughcompile_latexcarrysourcePathmetadata so the renderer can pair the editable.texsource with the generated PDF in one artifact workbench. - Lifecycle reconciliation: completion persists terminal task state before emitting terminal events, and resume paths re-derive canonical persisted status before writing
executing, so late approval or follow-up resumes cannot reopen completed tasks - Completion hardening: verified-mode evidence bundles, step-intent alignment/decomposition heuristics, read-only entropy sweeps, and verifier verdict/report projection make completion checks more explicit without mutating the task's final result
- Outcome classification: ACP (acpx) prompt results pass through one classifier (
src/electron/agent/runtime/acp-prompt-outcome.ts) shared by initial prompts and follow-ups. A stop reason is not success:end_turncompletes only with a final response or a reported file that exists in the workspace, limits become budget-exhausted partial success, refusals and missing/unknown reasons fail, and externally reported cancellation is persisted through the daemon's shared cancellation cleanup. A local cancellation always wins, and ACP completion never records learning success - Scheduled-run accounting: each cron job keeps versioned per-category outcome counts (
src/shared/cron-outcomes.ts,src/electron/cron/outcome-counts.ts) recorded together with each run's history entry. Run success isok / classified attempts; partial, needs-attention, failed, cancelled, skipped, and unknown-outcome runs are reported separately, follow-ups are classified from the thread's durable result, and legacy aggregates keep their old semantics for existing callers - Learning evidence: Playbook promotion and success context read the
playbook_success_evidenceledger (src/electron/memory/PlaybookEvidenceStore.ts) instead of searching memory text. Outcomes areplaybook_entriesrows, not archive memories, written only when memory settings allow (privacy redaction applied); only terminal-ok executions with a durably recorded entry create success evidence; one row per task; the ledger holds no text, so readable fields come from the source entry and private entries are never served; reinforcement is a durable link that requires a deterministic relevance gate plus a matching approach key; corrections and deleted/edited source entries invalidate dependent evidence. Playbook outcomes never reach generic prompt recall or archive synthesis, and skill proposals carry evidence provenance (legacy auto-proposals areunverifiedand cannot be approved until revalidated)
Profiles and Isolation
CoWork Pulse analytics boundary
PulseService reads existing local task/event/LLM tables and reduces them to daily counters. It
stores consent windows, an identity-scoped outbox and acknowledged-day receipts
(pulse_sent_days) locally; the random profile ID/token, consent revision, identity start/endpoint and any pending deletion live
in encrypted settings. One lifecycle-owned instance serves the desktop timer and Settings IPC; the
daemon keeps its own and settles it before closing the database; CLI commands create a
short-lived one. Shared types, Electron IPC, onboarding, and the Pulse settings panel expose
consent, a tagged preview (queued / candidate / ineligible / already_sent), send
outcomes, and deletion-pending retry.
Every user decision runs in one short IMMEDIATE SQLite transaction over the encrypted settings record, consent windows and outbox, and increments the revision; no transaction is held across HTTP. A delivery re-checks revision, identity and endpoint before saving any result, so late responses cannot reverse a decision. A refused settings write (keychain key changed) fails the operation instead of reporting success. Remote deletion is reported as the collector's acknowledgement; it cannot retract a request the server already accepted.
HTTPS enrollment/daily requests reach the separate services/pulse-worker deployment at
pulse.coworkosapp.com. The Worker validates closed payload shapes and stores pseudonyms and
aggregates in D1. A cron prunes old rows; a bearer-protected API serves aggregate reports.
The desktop updater is separate from Pulse. For installed builds, manual checks (Settings) go
directly to GitHub with an 8-second total deadline; background checks (startup) may first ask the
identifier-free CoWork version endpoint for up to 2 seconds. Source checkouts fetch and compare the
exact origin/main commit within one 15-second budget that includes local Git probes and the remote
fetch. Their result is explicitly current, a new target, or unavailable; a failed fetch cannot
establish that the checkout is current. Every UpdateInfo says whether it is a live answer, no
published release, or an unavailable source check, with its check time. Release metadata is not
cached: a failed release check is an error, same-intent checks are coalesced, and only a manual
check sets the install target. Operator OTLP export and local Usage Insights remain distinct data
flows.
See CoWork Pulse for contracts and delivery limitations, and the collector runbook for deployment and recovery. Server deployment does not publish a Pulse-enabled client release.
Profile storage
CoWork supports multiple app profiles so one install can keep separate operating environments for different users, clients, or trust zones.
- each profile has its own user-data root, SQLite database, encrypted settings, channel configs, managed skills, and session history
- profile export/import moves a complete app profile bundle without merging it into another profile implicitly
- workspaces still live outside the app profile, but the profile controls the credentials, automations, channels, and runtime state that operate on those workspaces
- profile switching is an app-level concern, separate from personality export/import or workspace-kit files
PDF edit output
PDF region edits use packaged Noto Sans and Noto Sans CJK fonts with fontkit for Unicode text and style variants. Output creation reserves a unique file exclusively; version selection and asynchronous workspace resolution prevent concurrent edits from overwriting an existing result. Font licenses ship with the assets.
Access Profiles
Saved HTTP allows retain their effect class, method, body presence, custom-header presence, and credential identity across storage and reload. A rule for an ordinary read cannot authorize data export or protected credentials; legacy ambiguous allows fail closed for sensitive effects. Malformed methods are rejected at the HTTP sink.
Access profiles are task-level policy objects and are separate from app profiles. They combine the
logical sandbox (read-only, workspace-write, or danger-full-access), approval and reviewer
policy, network posture, and optional filesystem/domain scope. The selected profile is applied to
the task workspace before tool registry filtering and per-request permission evaluation.
The shared contract is implemented by src/shared/access-profiles.ts, resolved by
src/electron/security/access-profile-resolver.ts, and enforced for paths by
src/electron/security/access-profile-paths.ts. Profile settings are validated and persisted by
the encrypted permission-settings manager. Custom profiles can inherit only from a policy that is
at least as broad; child tasks, managed sessions, automations, remote dispatch, and worktrees keep
the same ceiling. Missing or invalid named profiles fail closed as unavailable read-only profiles.
QA server commands require explicit command approval and revalidate current task and administrator authority before launch. Their subprocess networking follows the shared administrator and domain policy; on-request networking requires a grant bound to the specific invocation and never overrides administrator denial. The grant is not inherited by another shell invocation or execute_code. Restricted execution requires an approved OS sandbox. Offline macOS servers may listen locally under the loopback listener guard, which terminates their process group if a non-loopback listener appears.
Direct one-shot shell commands on POSIX systems also lead their own process
group. Abort, timeout, and the final stop escalation signal that owned group in addition to the
existing parent-first descendant cleanup, so a failed process lookup cannot
leave ordinary child processes holding command output pipes open. Sandboxed
commands retain their sandbox lifecycle, and Windows retains tree termination
through taskkill.
See Access Profiles for the product, API, migration, and edge-case contract.
Gateway context admission
Gateway authorization grants bind the channel, sender, and DM or group context. Pairing codes are context-specific, revocation removes the corresponding context grant, and ambiguous legacy grants fail closed. Channel settings expose DM and group access independently; an owner or approval decision in one context does not authorize another.
Heartbeat V3
Heartbeat v3 is the default background automation architecture.
- Signal ledger: ambient changes, mentions, manual wakes, and awareness events emit normalized heartbeat signals instead of accumulating raw wake requests
- Pulse: cheap, deterministic, non-LLM state reduction that evaluates merged signals, due proactive work, checklist cadence, foreground contention, and dispatch guardrails
- Dispatch: escalation lane invoked only when Pulse decides the situation warrants user-visible or task-visible work
- Run records: every Pulse and Dispatch execution is tracked, and any heartbeat-created task is linked back to its originating heartbeat run
- Defer and compress: foreground manual work suppresses churn by compressing pending signals into resumable deferred state instead of growing a queue
- Dreaming handoff: idle pulses offer the memory folder its daily dream, which runs without consuming dispatch budget or creating tasks
See Heartbeat v3 for the detailed runtime contract.
Build checkouts
Build sessions can use the local workspace or an isolated Git worktree and select an existing starting branch. Checkout reuse validates ownership and preserves files. Missing saved checkouts fail explicitly instead of silently redirecting work to the workspace root. See Worktrees.
Dreaming
Dreaming is the background memory-curation phase inside Workflow Intelligence.
It works on the memory folder (MemoryRepoDreamer). About once a day (Heartbeat's idle pulses offer a run; it starts when the last dream is more than 20 hours old and something new happened) or on Dream now, one model pass reads the folder and recent task conversations, applies safe edits to agent-written notes as one undoable commit, and leaves anything that touches the user's notes, MEMORY.md or the inbox on a review branch shown in Settings > Memory > Review. It uses the configured provider within a daily token budget (50,000 by default). The earlier rule-based curator of memory_items is retired. See Dreaming.
Memory Engine
Facts about the user, workspaces and lessons live in the memory folder (~/CoWork Memory, a local git repo of markdown notes in the Agent Memory Repo format); commitments and notes about other people stay in memory_items. Both are written only through MemoryWriter (salience, redaction, policy, dedupe, supersession by subject, hot-memory invalidation). The memories archive keeps episodic outcomes. The legacy lanes (curated entries, user profile, relationship memory, awareness beliefs, adaptive style) were copied in once by a migration that runs at startup before anything reads memory; there are no dual writes, the old services are views of memory_items, a later migration copied global and workspace facts into the memory folder, and a one-time data retirement exports the old stores to an encrypted backup and drops them. Every producer (agent tools, Memory Hub, core memory candidates, imports, Chronicle) goes through the same hygiene. Recall (MemoryRecall) fuses facts, the archive, conversations and knowledge; four agent tools (memory_recall, memory_remember, memory_forget, context_recall) cover it. See Memory Engine.
Workspace Kit
The .cowork/ workspace kit holds durable human-edited operating context.
BOOTSTRAP.mdis a one-time onboarding checklistHEARTBEAT.mdis reserved for recurring heartbeat checklist workUSER.mdandMEMORY.mdhold human-authored content; the CoWork-generated blocks were removed once in 0.5.60 (with a.historysnapshot), and hand edits sync back into memory- project-scoped context lives under
.cowork/projects/<projectId>/ CROSS_SIGNALS.mdand the feedback files (plus theMISTAKES.mdpattern block while the memory folder is off) are written by the kit writers (CrossSignalService,FeedbackService). A desktop app and a node daemon on one profile would both rewrite them, so only the owner of the profile's kit-writer lease runs them (agents/kit-writer-ownership.ts, rowkit_writer_leaseinmaintenance_state): a 60 s lease renewed every 15 s, taken over by the next process once a crashed owner's lease expires. The desktop app wins: it asks a daemon holding the lease to hand it over, the daemon flushes and stops its writers on its next renewal and passes the lease on. When the desktop quits it releases the lease and the daemon takes it back. A new owner's writers rebuild from the shared database, so tasks the other process ran reach the files then.
Skills Runtime Model
The skill system now follows an additive contract:
- the canonical user request is resolved as
rawPrompt -> userPrompt -> prompt - task creation normalizes prompt fields centrally so new tasks always persist canonical prompt data
- skill routing works as shortlist-and-hint guidance, not prompt takeover
- slash commands can still invoke skills deterministically, including first-class bundled workflows such as
/simplify,/batch,/llm-wiki, direct skill IDs, and plugin-pack aliases from the message box shortcut picker, but the result is applied additively use_skillreturns structured context plus scoped directives, not a replacement task definition- the executor builds runtime context from canonical prompt + task notes + applied skill content
- the renderer always shows canonical task text and renders applied skills separately
This prevents skills from hijacking the task while preserving proactive skill selection.
See Skills Runtime Model for the detailed contract.
Gateway Message Lifecycle
Remote channel messages are routed through a shared lifecycle for command dispatch, task-session ownership, specialization lookup, follow-ups, cancellations, progress delivery, skill slash invocation, and scheduled-task output delivery. The gateway treats recognized slash commands as owned commands, not task text, resolves channel/chat/thread specialization before creating fresh tasks, and uses generation guards so stale task updates are not delivered after a chat starts fresh or cancels.
See Gateway Message Lifecycle for the user-facing command and delivery model.
Integration Auth Notifications
Integration auth failures are surfaced through src/electron/notifications/integration-auth.ts. The helper owns auth-error classification, reason redaction, and per-integration de-duping before it writes a standard warning notification through NotificationService.
Current producers are:
- Google Workspace API helpers for Gmail, Calendar, Drive, and shared native/MCP token refresh or scope failures
- MCP connector tool/status errors that look like auth failures
MCP transport disconnects that classify as auth failures stop at error rather than scheduling reconnect backoff. Non-auth transient disconnects still use the normal reconnect path.
Repo Landmarks
src/electron/: main-process runtime, services, database, scheduling, monitoringsrc/electron/agent/runtime/SessionRuntime.ts: canonical task-session owner for execution, recovery, snapshotting, and task projectionsrc/cli/:coworkCLI argument parsing, terminal UI, local one-shot runner, diagnostics, and remote Control Plane dispatchbin/cowork-cli.js: npm binary launcher for thecoworkcommandsrc/renderer/components/RightPanel.tsx: renderer-side read-only projection of the latest session checklist statesrc/electron/agent/runtime/PermissionEngine.ts: layered tool-approval evaluation, rule matching, and fallback escalationsrc/electron/security/access-profile-resolver.ts: named-profile resolution, legacy mapping, admin constraints, and workspace projectionsrc/electron/security/access-profile-paths.ts: canonical path resolution, profile filesystem rules, protected roots, and one-shot external-file approvalssrc/electron/telemetry/pulse-service.ts: Pulse consent lifecycle, fenced delivery, receipts, and deletionsrc/electron/updater/update-manager.ts: bounded update discovery, provenance, and install-target authoritysrc/electron/agent/runtime/acp-prompt-outcome.ts: ACP prompt stop-reason classificationsrc/electron/cron/outcome-counts.ts: scheduled-run recording, legacy migration, and versioned outcome countssrc/electron/memory/PlaybookEvidenceStore.ts: Playbook evidence ledger and reinforcement linkssrc/electron/applications/: application operation lifecycle, local Photoshop route, native desktop session probesrc/electron/agent/tools/native-app-tools.ts: backgroundnative_appcontrol with journaled, non-replayed mutationssrc/renderer/: React UI and settings surfacessrc/shared/: shared contracts and typesdocs/: product and architecture documentation.cowork/: local workspace operating context
Desktop Location
get_current_location exposes the user's desktop location for nearby-place, walking-distance, and local-errand queries. The implementation lives in src/electron/location/DesktopLocationService.ts and delegates to platform-specific helpers that share a common JSON envelope format.
- macOS: a compiled Swift helper (
native/location-helper-macos/) uses Core Location. The.appbundle is code-signed with thecom.apple.security.personal-information.locationentitlement and launched viaopen -W -n. Built byscripts/build_location_helper_macos.mjs. - Windows: a bundled PowerShell script (
native/location-helper-windows/Get-Location.ps1) uses theWindows.Devices.GeolocationWinRT API. Invoked viapowershell.exe -ExecutionPolicy Bypass. No compilation step. - Linux: a bundled Bash script (
native/location-helper-linux/get-location.sh) uses GeoClue2 over the system D-Bus viagdbus. Requiresgdbus(part of glib2 utilities). No compilation step.
Each helper accepts --accuracy coarse|precise, --timeout-ms N, and --response-file <path>, and outputs { ok, location } or { ok: false, error: { code, message } }. Error codes: LOCATION_DENIED, LOCATION_UNAVAILABLE, LOCATION_TIMEOUT, LOCATION_NOT_CONFIGURED, LOCATION_UNSUPPORTED_PLATFORM.
The permission engine treats get_current_location as a location_access approval — it requires explicit one-time user consent, cannot be auto-approved, and does not persist across tasks. Failed location requests are cached for 2 minutes to prevent retry storms.
The bundled Maps MCP connector (connectors/maps-mcp/) consumes location coordinates for nearby place search and walking route calculations.
Computer use
Native GUI control is implemented in the main process (src/electron/computer-use/, src/electron/agent/tools/computer-use-tools.ts) with a persistent platform helper runtime and a singleton session that coordinates single-task ownership plus Esc abort. macOS uses helper-targeted permission bootstrap; Windows uses a bundled Win32 helper for visible, non-minimized windows. Tool policy and the executor only expose the computer-use lane when native desktop GUI intent is detected so routine web and repo work stays on browser and shell paths. Product-level behavior, permissions, and troubleshooting are documented in Computer use.
Application execution and background app control
Application work runs locally; there is no SSH or remote worker route. Routes are an application's API (MCP or a connector, including remote OAuth MCP servers), its own scripting through an application operation, and its window through native_app. A route is chosen by what it can actually do for the target (identity, authority, fidelity, readiness and a verification path), not by a fixed ranking of protocols, and a denial is never routed around. Working with apps in the background is the user-facing overview.
- Application operations (
src/electron/applications/, toolapplication_operation): a durable operation lifecycle of prepare, optional feedback, dispatch, observation, cancellation and domain verification. Operations are stored through services-domain database units, use task-scoped consent and recovery boundaries, and never replay a dispatched effect. A lost reply is reconciled against durable state and the original route. - Android phone route (
phone.android.v1,src/electron/phone/): opt-in ADB device selection and a profile-local pinned phone-harness/Pillow runtime. Approved observations provide screenshots and accessibility data; with workspace write authority, captures are also delivered as task-owned PNG files with linkable paths and independently checked artifact hashes. Each input uses a fresh task-owned capture, explicit approval, the shared device resource lock, and a durable worker receipt. The trusted approval-onlyauthorizehook runs before the adapter effect deadline; cancellation there releases the lock without dispatch. Stop invalidates pending approvals/discovery and interrupts workers. Unknown input is reconciled through its original receipt without replay. Bounded volatile context carries phone operation IDs/status and the latest approved observation across steps; a planned operation is never described as executed work. Package/text readback is bounded verification; this initial adapter is unqualified. See Android phone control. - Local Photoshop route (
local_photoshop_read,inspect,create,edit): runs installed Photoshop through bounded background scripting. Edits bind to the source hash, write fresh PSD/PNG outputs and are verified by independent saved-file readback. See Local Photoshop editing. native_app(macOS,src/electron/agent/tools/native-app-tools.ts,resources/computer-use/bridge.swift): generic background control of one exact app window.- Actions: nonactivating launch, window discovery, bounded AX observation with a window screenshot, token-scoped semantic actions (press, increment, decrement, confirm, set_value) and window-scoped key/text/pointer input.
- It never activates, raises, moves the physical pointer or uses the clipboard.
- Mutations are journaled before dispatch and never replayed. Target self-activation, unexplained pointer movement or a lost reply yields
outcome_unknown, which blocks further mutations in the task. - Requires an unlocked Mac, enforced by a native session probe.
- Harness evidence: the latest native observation per window and the latest saved-file readback per path stay in later steps' context. Verification and summary steps also receive a native action ledger, built from the durable attempt journal, with per-action foreground/pointer/keyboard/clipboard evidence. Steps performed through native UI require
native_apprather than a file or scripting writer, and a dispatch acknowledgement is never treated as proof of the app outcome. - Task-stated restrictions: a task that forbids scripting turns the scripting-based mutation tools into task tool restrictions. They are hidden from the catalog, and the policy pipeline denies them at execution. Task tool restrictions in general are now enforced at execution, not only by hiding the tool.
- Default: native computer use is Background only. Legacy
background_firstsettings migrate to it, and foreground modes are explicit opt-ins.
Terminal Tabs
Terminal tabs are implemented with a renderer/main split:
src/renderer/components/TerminalTabsDock.tsxowns the dock UI, tab controls, xterm instances, fitting, focus, keyboard input forwarding, and output rendering.src/electron/terminal/TerminalPtyManager.tsownsnode-ptyprocesses, replay buffers, cwd/status metadata, PTY resize, and tab lifecycle.src/electron/ipc/handlers.ts,src/electron/preload.ts, andsrc/shared/types.tsexpose typed channels for create/list/write/resize/stop/close/output.
The design keeps profile-gated structured command tools available for agent-run commands while giving humans a real terminal work surface in the same task workspace. This is the terminal counterpart to the Everything Workbench and Browser Workbench: direct CLI work no longer has to leave CoWork OS. Product behavior and QA guidance are documented in Terminal Tabs and Access Profiles.
File edit integrity
edit_file holds the target descriptor, revalidates authority and rebases exact replacements onto
current content before writing. Within a user-data profile, device/inode keyed recovery records
coordinate cooperating processes, including hard-link aliases. A private journal retains before
and after bytes for failed or interrupted writes. Reconciliation never overwrites ambiguous current
content automatically. This preserves existing inode metadata but does not provide arbitrary-writer
compare-and-swap or power-loss atomicity. Limits and recovery behavior are documented in
File edit integrity and recovery.
CoWork CLI
The standalone cowork CLI is implemented separately from in-app terminal tabs:
bin/cowork-cli.jsresolves the installed package and launches built CLI output.src/cli/main.tsowns argument parsing, the interactive welcome screen, slash commands, local diagnostics, and remote-mode handling.src/cli/direct-run.tsowns one-shot local task execution once the app runtime has initialized.src/electron/main.tsrecognizes--cowork-cli-direct-runand starts a hidden Electron runner without opening the desktop window.
The local path is intentionally not a Control Plane dependency. It initializes the local database,
settings manager, provider routing, workspace state, skill registry, MCP manager, and AgentDaemon,
then resolves the selected access profile before creating and waiting on a task. The daemon is
started with startup recovery disabled in CLI direct-run mode so a short-lived terminal process
does not recover or rewrite GUI-owned tasks.
Use cowork run ... --remote for the token-gated Control Plane client path. Product behavior and first-run guidance are documented in CoWork OS CLI.
Chronicle
Chronicle is implemented as a dedicated desktop screen-context subsystem under src/electron/chronicle/.
ChronicleCaptureServicemaintains the local recent-screen ring buffer in app-local storageChronicleSelectorranks frames by recency, app/window metadata, and OCR-derived local textChronicleSourceResolverenriches Chronicle captures with frontmost URL/file/app references when availableChronicleProvenanceturns screen-derived text into untrusted, provenance-tagged contextChronicleObservationRepositorypromotes only task-used observations into.cowork/chronicle/ChronicleMemoryServicecan create linkedscreen_contextmemory rows through the normal memory pipelinescreen_context_resolveis registered from the agent tool registry, exposed through the dedicated built-inchronicletool category, and hidden when Chronicle is disabled, paused, or unavailable- Mission Control and runtime visibility consume promoted observations as
screen_context, not as a separate memory database - renderer surfaces for Chronicle live in Settings > Tools > Chronicle (the only place it is configured), the Chronicle observations list under Settings > Memory > Sources, task-creation toggles, and tray/menu-bar controls
Chronicle shares the Screen Recording prerequisite with computer use, but it is a different lane: local screen understanding rather than direct GUI control. Product-level behavior, testing, and privacy boundaries are documented in Chronicle.
Evaluation battery
scripts/qa/run_battery.cjs defaults to synthetic fixtures in a disposable profile and workspace.
Its authenticated local fixture service exercises task lifecycle, bounded cancellation, explicit
approval scopes and independent artifact parsing. These results are labeled as fixture evidence.
Explicit live mode starts an owned Node daemon in a fresh profile and uses its local Control Plane;
legacy hook/database configuration is rejected by CI rather than counted as live coverage.
Artifact graders run in bounded subprocesses, and unresolved cleanup retains the profile for
inspection. This is application-profile isolation, not an operating-system sandbox. The optional
scripted-provider smoke exercises native file tools without measuring model quality. Commands,
prerequisites and rendering limits are documented in Disposable evaluation battery.
Database Ownership
SQLite is owned by worker threads by default in the desktop app, the daemon and the CLI.
- Write worker:
- One per runtime (
src/electron/database/async/). It runs every mutation for the timeline, reports, settings, storage, services, memory, mailbox and control-plane domains. - A write is a catalogued unit: one IMMEDIATE transaction, so a read-then-write is never interleaved with another writer.
- At most 256 requests are in flight per client. A burst beyond that waits on the host in submission order (bounded at 4,096 requests and 32 MiB). It is delayed, never resent, so writes are neither duplicated nor reordered.
- A reply means the write committed. If the worker exits before replying, the outcome is reported as
unknown, and the caller reconciles against durable state instead of replaying the write.
- One per runtime (
- Read workers:
- The reporting reader runs heavy reports and the mailbox reads.
- The memory FTS worker runs lexical recall.
- Neither falls back to host SQL or returns an empty success.
- Host thread:
- The host bootstraps and migrates the schema, then starts the workers.
- It keeps only the reviewed exceptions listed in
scripts/qa/sqlite-audit-rules.json, each with an owner: the daemon hot path, synchronous settings saves and the vault commit, service schema DDL, and one-time startup migrations. - The daemon's milestone and timeline commits go through
TimelineWriter, which commits in the worker.
- Backend choice:
- The backend is chosen once per run (
DATABASE_WORKER_ROLLOUT,src/electron/database/async/runtime.ts). COWORK_DB_WORKER=0restarts on the host backend. Setting a domain flag (for exampleCOWORK_DB_WORKER_SERVICES=0) to0keeps only that domain on the host.- Nothing switches backend mid-run. After a worker failure, operations fail explicitly.
- The backend is chosen once per run (
- Rollback:
- Both backends use the same schema.
- To roll back: quit the app so the worker drains, then restart it with the kill switch.
- Never delete the profile database to recover.
- Enforcement:
npm run qa:db:ratchetandnpm run qa:db:auditfail on new host SQL outside unit stores, on files covered only by backstop rules, and on rules without an owner.
Design and phases: async SQLite migration plan.
Update Rule
If defaults, behavior, or architecture change, update this file in the same PR.