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 mlx catalog entry is implemented as an OpenAI-compatible local provider. The renderer uses the shared custom-provider settings model; Electron validates native macOS arm64 plus mlx_lm/mlx.core imports, owns the mlx_lm.server subprocess and /v1/models readiness probe, and the provider adapter sends /v1/chat/completions requests. The server shares CoWork's local-AI process slot and port with hf-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-chat catalog entry connects to an already-running Atomic Chat /v1 API, 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-in local-balanced-v1 profile 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 macOS native_app background 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 8766 is 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-pty in 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 launches cmd.exe through node-pty's ConPTY/winpty backend with a cwd-only prompt. See Terminal Tabs.
  • CLI runtime layer: the cowork command 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. Local cowork run prefers a hidden Electron app-entry mode (--cowork-cli-direct-run) so encrypted desktop settings keep the same app identity. --remote is 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 or allowedTools. 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_resolve tool exposure, and promotion of task-used observations into workspace-backed screen_context evidence plus optional linked background memory generation. See Chronicle.
  • Managed resource layer: first-class ManagedAgent, ManagedEnvironment, and ManagedSession resources package reusable execution definitions and durable run identities on top of existing Task, AgentTeamRun, and SessionRuntime primitives. 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 AgentRole identities power the sidebar Bots roster and durable bot conversations. Bot transcripts reuse Task and task_events storage but are marked with agentConfig.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 RoutineWorkflowDefinition graph 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_inbox before 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 SessionRuntime owns task-session state, session checklists, permission state, turn coordination, resume/snapshot persistence, and task projection, while a lower-level TurnKernel handles the active step, follow-up, or text turn; a metadata-driven ToolScheduler batches 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.md and workspace file plus L0 Identity and L1 Essential Story by 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_skill attaches structured SkillApplication context plus scoped runtime directives instead of rewriting the task prompt. Skill requires.tools values 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, /multitask lane 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_items facts, archive memory rows and summaries, the conversation index, checkpoints with structured summaries + verbatim evidence packets, the memory folder (a separate git repo, ~/CoWork Memory by 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_v2 task 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 use exceljs, 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 into documentPreview; 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 into webPreview; 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. Existing content and htmlContent fallbacks remain for compatibility. PPTX previews use presentationPreview with fast text/notes extraction, cached imageUrl slide PNGs, background full rendering through Codex @oai/artifact-tool, local soffice + pdftoppm fallback, 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 BrowserSessionManager and routed through Electron webContents.debugger / CDP. The main process maps { taskId, sessionId } to the webview's webContentsId; 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_vault share 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 through compile_latex carry sourcePath metadata so the renderer can pair the editable .tex source 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_turn completes 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 is ok / 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_evidence ledger (src/electron/memory/PlaybookEvidenceStore.ts) instead of searching memory text. Outcomes are playbook_entries rows, 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 are unverified and 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.md is a one-time onboarding checklist
  • HEARTBEAT.md is reserved for recurring heartbeat checklist work
  • USER.md and MEMORY.md hold human-authored content; the CoWork-generated blocks were removed once in 0.5.60 (with a .history snapshot), and hand edits sync back into memory
  • project-scoped context lives under .cowork/projects/<projectId>/
  • CROSS_SIGNALS.md and the feedback files (plus the MISTAKES.md pattern 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, row kit_writer_lease in maintenance_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_skill returns 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, monitoring
  • src/electron/agent/runtime/SessionRuntime.ts: canonical task-session owner for execution, recovery, snapshotting, and task projection
  • src/cli/: cowork CLI argument parsing, terminal UI, local one-shot runner, diagnostics, and remote Control Plane dispatch
  • bin/cowork-cli.js: npm binary launcher for the cowork command
  • src/renderer/components/RightPanel.tsx: renderer-side read-only projection of the latest session checklist state
  • src/electron/agent/runtime/PermissionEngine.ts: layered tool-approval evaluation, rule matching, and fallback escalation
  • src/electron/security/access-profile-resolver.ts: named-profile resolution, legacy mapping, admin constraints, and workspace projection
  • src/electron/security/access-profile-paths.ts: canonical path resolution, profile filesystem rules, protected roots, and one-shot external-file approvals
  • src/electron/telemetry/pulse-service.ts: Pulse consent lifecycle, fenced delivery, receipts, and deletion
  • src/electron/updater/update-manager.ts: bounded update discovery, provenance, and install-target authority
  • src/electron/agent/runtime/acp-prompt-outcome.ts: ACP prompt stop-reason classification
  • src/electron/cron/outcome-counts.ts: scheduled-run recording, legacy migration, and versioned outcome counts
  • src/electron/memory/PlaybookEvidenceStore.ts: Playbook evidence ledger and reinforcement links
  • src/electron/applications/: application operation lifecycle, local Photoshop route, native desktop session probe
  • src/electron/agent/tools/native-app-tools.ts: background native_app control with journaled, non-replayed mutations
  • src/renderer/: React UI and settings surfaces
  • src/shared/: shared contracts and types
  • docs/: 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 .app bundle is code-signed with the com.apple.security.personal-information.location entitlement and launched via open -W -n. Built by scripts/build_location_helper_macos.mjs.
  • Windows: a bundled PowerShell script (native/location-helper-windows/Get-Location.ps1) uses the Windows.Devices.Geolocation WinRT API. Invoked via powershell.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 via gdbus. Requires gdbus (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/, tool application_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-only authorize hook 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_app rather 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_first settings 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.tsx owns the dock UI, tab controls, xterm instances, fitting, focus, keyboard input forwarding, and output rendering.
  • src/electron/terminal/TerminalPtyManager.ts owns node-pty processes, replay buffers, cwd/status metadata, PTY resize, and tab lifecycle.
  • src/electron/ipc/handlers.ts, src/electron/preload.ts, and src/shared/types.ts expose 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.js resolves the installed package and launches built CLI output.
  • src/cli/main.ts owns argument parsing, the interactive welcome screen, slash commands, local diagnostics, and remote-mode handling.
  • src/cli/direct-run.ts owns one-shot local task execution once the app runtime has initialized.
  • src/electron/main.ts recognizes --cowork-cli-direct-run and 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/.

  • ChronicleCaptureService maintains the local recent-screen ring buffer in app-local storage
  • ChronicleSelector ranks frames by recency, app/window metadata, and OCR-derived local text
  • ChronicleSourceResolver enriches Chronicle captures with frontmost URL/file/app references when available
  • ChronicleProvenance turns screen-derived text into untrusted, provenance-tagged context
  • ChronicleObservationRepository promotes only task-used observations into .cowork/chronicle/
  • ChronicleMemoryService can create linked screen_context memory rows through the normal memory pipeline
  • screen_context_resolve is registered from the agent tool registry, exposed through the dedicated built-in chronicle tool 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.
  • 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=0 restarts on the host backend. Setting a domain flag (for example COWORK_DB_WORKER_SERVICES=0) to 0 keeps only that domain on the host.
    • Nothing switches backend mid-run. After a worker failure, operations fail explicitly.
  • 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:ratchet and npm run qa:db:audit fail 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.