- Node.js 24+ and npm
- macOS 12 (Monterey)+ or Windows 10/11
sqlite3CLI (required for eval corpus/replay scripts)- macOS: Xcode Command Line Tools (needed for
better-sqlite3):xcode-select --install - Windows: Visual Studio Build Tools 2022 (C++) and Python 3 (needed for native module builds)
- macOS location helper: Swift toolchain (included with Xcode CLI tools) for compiling the Core Location helper binary
- Linux location helper:
gdbus(part ofglib2/libglib2.0-bin) and a running GeoClue2 service for desktop location support - LLM provider credentials are optional for development, but AI task execution still needs a working route: ChatGPT sign-in, local Ollama, or provider credentials.
# Clone the repository
git clone https://github.com/CoWork-OS/CoWork-OS.git
cd CoWork-OS
# Install dependencies
npm install
# Set up native modules for Electron (includes macOS retry and Windows ARM64 fallback handling)
npm run setup
# Build and package the app
npm run build # compile TypeScript and bundle the UI
npm run build:cli # compile the standalone cowork CLI
npm run package # package desktop installers (.dmg on macOS, .exe on Windows)Once complete, the packaged app will be in the release/ folder:
*.dmg— macOS installer image*.exe— Windows NSIS installermac-*/CoWork OS.app— unpacked macOS app bundlewin-*/— unpacked Windows app directory
The Linux server artifact is separate from desktop packaging. It is a Linux x64 tarball for VPS/systemd deployments:
npm run package:linux:server
npm run package:linux:server:smokeThis must run on Linux x64 so native runtime modules match the target. The package script builds the daemon and connectors, stages runtime dependencies, installs the Electron binary compatibility dependency, copies the full resources/ tree, derives the connector list from build:connectors, writes release/cowork-os-server-linux-x64-v<version>.tar.gz, and writes a matching .sha256 file.
The smoke test extracts the tarball, verifies required files/resources/dependencies, checks better-sqlite3, confirms the Electron binary exists, starts coworkd-node on a temporary Control Plane port, and checks /health.
Managed deployment hardening is part of this release path: the daemon reports deployment posture through config.get, blocks unsafe public Control Plane binds in headless/managed mode, and the Docker/systemd templates default to loopback/private exposure with hardened process settings.
Run the app with hot reload:
npm run devOn macOS, development startup enables Node's system certificate store by default with NODE_OPTIONS=--use-system-ca. This helps provider calls work on corporate networks where tools such as Zscaler install a company root certificate into the macOS trust store. To opt out for comparison, run:
COWORK_DEV_USE_SYSTEM_CA=0 npm run devIf a corporate root certificate still is not trusted by Node, export it as PEM and use:
NODE_EXTRA_CA_CERTS=/path/to/company-root.pem npm run devnpm run dev checks Settings → Appearance → Developer logging (default: off).
When enabled, each captured run writes a readable text log and a structured JSONL log:
logs/dev-YYYYMMDD-HHMMSS.log— human-readable output with an ISO date/time prefix.logs/dev-YYYYMMDD-HHMMSS.jsonl— machine-readable events for diagnostics and self-improvement ingestion.logs/dev-latest.logandlogs/dev-latest.jsonl— overwritten mirrors for the most recent captured run.logs/dev-runs.json— retained-run manifest with start/end time, exit status, file paths, byte size, line count, and warning/error counts.
The terminal output is unchanged. Files written under logs/ are redacted before they are stored:
common bearer/basic auth headers, API keys, tokens, secrets, passwords, and URL credentials are replaced with [REDACTED].
JSONL events include timestamp, runId, process, stream, level, component, message,
rawLine, and optional taskId, workspaceId, error, and metadata fields.
When Developer logging is enabled, renderer performance telemetry is sent through the
renderer:perfLog IPC channel and appears in the same dev logs. Startup marks include
app_shell_ready, sidebar_ready, main_view_ready, composer_ready, and
first_task_rows_ready; task-surface logs also summarize render counts, projection timings,
event append latency, scroll-follow writes, and long-task/frame-gap samples.
Captured dev logs are local-only and cleaned up automatically. Defaults keep logs from the last 14 days,
always retain the newest 20 runs, and cap retained dev-*.log/dev-*.jsonl files at 100 MB by deleting
oldest run pairs first. Local overrides are available:
COWORK_DEV_LOG_RETENTION_DAYS=7 COWORK_DEV_LOG_MIN_RUNS=10 COWORK_DEV_LOG_MAX_MB=50 npm run dev:logForce log capture regardless of Settings:
npm run dev:log| Command | Description |
|---|---|
npm run dev |
Start development mode; log capture follows Settings toggle |
npm run dev:log |
Start development mode and force redacted text + JSONL logs to logs/ |
npm run dev:start |
Internal raw dev start command (used by wrappers) |
npm run build |
Production build |
npm run build:cli |
Compile the cowork CLI TypeScript output |
npm run package |
Package desktop installers (.dmg on macOS, .exe on Windows) |
npm run package:linux:server |
Build the Linux x64 server tarball and checksum on Linux |
npm run package:linux:server:smoke |
Extract and boot-smoke the Linux server tarball on Linux |
npm run setup |
Set up native modules for Electron |
npm run fmt |
Format code with Oxfmt |
npm run fmt:check |
Check formatting without writing |
npm run lint |
Run Oxlint (fast, Rust-based linter) |
npm run type-check |
TypeScript validation |
npm run qa:eval:build |
Build regression eval corpus from failed/partial tasks |
npm run qa:eval:run |
Replay eval suite (deterministic or hooks mode) |
npm run qa:eval:enforce-regressions |
Enforce production-fix -> eval-case policy |
npm run qa:renderer-perf |
Replay renderer task-surface performance fixtures, including noisy failure storms |
npm run qa:timeline:backfill |
Recompute timeline completion telemetry for task_completed timeline events |
npm run qa:timeline:enforce |
Enforce timeline reliability thresholds from completion telemetry |
npm run qa:reliability |
Reliability loop (qa:eval:run + battery script) |
npm run skills:validate-routing |
Validate skill routing metadata |
npm run skills:validate-content |
Validate skill prompt content, placeholders, and references |
npm run skills:audit |
Generate skill audit scorecards in tmp/qa/ |
npm run skills:check |
Run full skill quality gate (routing + content + audit + eval) |
On macOS, npm run dev brands the local node_modules/electron/dist/Electron.app display name and icon as CoWork OS by default. The branding script preserves CFBundleName=Electron and CFBundleIdentifier=com.github.Electron so development safeStorage continues to use the Electron identity.
Use these overrides only when you explicitly need them:
COWORK_DEV_BRAND_APP=0 npm run dev
COWORK_CODESIGN_ENABLE=1 node scripts/codesign_electron_dev.mjs
COWORK_CODESIGN_IDENTITY="Apple Development: Name (TEAMID)" node scripts/codesign_electron_dev.mjsDevelopment codesigning remains opt-in for a valid app bundle. If local branding or another file mutation leaves Electron.app with an invalid signature, scripts/codesign_electron_dev.mjs repairs it with an ad-hoc development signature without app entitlements, so macOS can register the app while Electron's run-as-node native-module checks keep working.
The standalone terminal entrypoint is cowork. It is separate from in-app terminal tabs: terminal tabs give users a real PTY inside the desktop workspace, while cowork starts CoWork tasks from an external terminal.
Implementation landmarks:
bin/cowork-cli.js: npm binary launcher and source-checkout artifact bootstrapsrc/cli/main.ts: argument parsing, interactive terminal UI, slash commands, diagnostics, and remote dispatchsrc/cli/direct-run.ts: local one-shot task executionsrc/electron/main.ts: hidden--cowork-cli-direct-runapp-entry mode for local CLI taskstsconfig.cli.json: CLI TypeScript build target
Common local commands:
npm run build:cli
cowork --help
cowork
cowork run "who are you?"
cowork run "who are you?" --json
COWORK_CLI_DEBUG=1 cowork run "diagnose local provider setup"Local cowork run should not require COWORK_CONTROL_PLANE_TOKEN. It uses the local profile and normally prefers the hidden Electron app-entry runner so desktop-encrypted settings can be read with the same app identity. cowork run ... --remote is the explicit Control Plane client path and is the mode that requires Control Plane URL/token configuration.
Focused validation:
npm run build:cli
npm run build:electron
npx vitest run src/cli/__tests__/main.test.ts src/cli/__tests__/terminal-ui.test.ts src/cli/__tests__/local-control-plane-discovery.test.tsFor packaged or npm-release checks, verify the package includes dist/cli, bin/cowork-cli.js, and tsconfig.cli.json, then test both cowork --help and one local cowork run command from a clean install.
The renderer startup bundle is intentionally kept separate from secondary product surfaces and heavyweight renderers.
Current bundle-splitting rules:
- Keep the app shell and
Sidebaravailable on initial load. The selected task surface rendersTaskViewSkeletonimmediately while the lazyMainContentchunk hydrates;RightPanelis also lazy-loaded behind its own lightweight fallback. - Lazy-load secondary views from
App.tsx: Settings, Browser, Home, Devices, Health, Ideas, Inbox Agent, Agents Hub, and Mission Control. - Keep the in-app browser workbench renderer-owned.
BrowserWorkbenchViewregisters its Electron webviewwebContentsIdwith the main process, and browser tools route visible actions throughBrowserWorkbenchServiceintoBrowserSessionManager. Browser V2 automation should be CDP-backed through ElectronwebContents.debuggerfor snapshots, ref-aware actions, dialogs, diagnostics, downloads/uploads, emulation, traces, and screenshots; renderer DOM scripts are compatibility fallback, not the primary control plane. Browser Workbench IPC also carries open requests, status updates, screenshot capture, annotation handoff, diagnostics state, cursor events for visible agent movement, and viewport events sobrowser_emulatecan resize the shared webview for responsive QA. Do not replace generated web artifact iframes with this live browser path; artifact previews and live website testing are separate surfaces. See Browser Workbench and Browser V2 Architecture. - Keep Browser Use Cloud as an explicit backend, not a default fallback. The Browser Use client lives in
src/electron/agent/browser/browser-use-cloud-client.ts, usesBROWSER_USE_API_KEYor encryptedbrowser-usesettings, and connects through the existing Playwright CDP path. Preserve the private/local target block, API error redaction, stale-session retry, and pending-stop behavior when changing browser routing. Focused checks:npx vitest run src/electron/agent/browser/__tests__/browser-use-cloud-client.test.ts src/electron/agent/tools/__tests__/browser-tools.test.ts src/electron/security/__tests__/network-policy.test.ts tests/tools/shell-tools.test.ts. - Keep CSS split by surface.
src/renderer/styles/index.cssis for app-wide tokens/layout plus the small critical composer startup block needed before lazy chunks hydrate.src/renderer/components/main-content.cssowns the heavier task surface, welcome view, remote file picker, workspace/permission dropdowns, and skills menu. Do not move task composer rules intoright-panel.css; otherwise the center view can restart with unstyled native controls before the right panel chunk loads. - Do not import heavyweight optional renderers at module top level when they are needed only for
specific content. Markdown rendering, GFM plugins, syntax highlighting, and Mermaid rendering are
loaded behind visible message/code surfaces instead of being eager
MainContentdependencies. - Do not import the
highlight.jspackage root in renderer code. Usehighlight.js/lib/coreinside the lazy highlighting component and register only the language grammars the UI should support. - Prefer feature-level dynamic imports before adding Rollup
manualChunks; manual chunks improve cache boundaries, but they do not remove code from startup if imports remain static. - Keep terminal tabs lazy-loaded.
TerminalTabsDockimports xterm.js and its CSS, so it should stay behind the terminal-open path rather than becoming part of the startup bundle.
The initial optimization reduced the renderer entry chunk from about 4,842 kB minified (1,267 kB gzip) to about 1,259 kB minified (364 kB gzip) in npm run build:react. Large feature code now appears as separate chunks such as Settings, mermaid.core, PDF, KaTeX, and chart/diagram chunks.
When changing renderer imports, validate with:
npm run build:react
npm run qa:renderer-perf
npm run type-checkUse npm run build:react output as a budget check: the main renderer entry should remain below
900 kB minified, the Settings initial chunk below 700 kB minified, and built global CSS below
450 kB. If the entry chunk grows unexpectedly, rebuild with sourcemaps and inspect the generated
dist/renderer/assets/index-*.js.map to identify newly eager modules.
Task view supports ... > Add automation..., a renderer-side shortcut that creates a task-sourced routine from the current task. Schedule, API, and event triggers then compile to the existing cron, webhook, and event-trigger engines.
Implementation contract:
- The task title/three-dot menu lives in
src/renderer/components/MainContent.tsx. - The modal is
TaskAutomationModalin the same file so task-derived defaults stay local to task view. - Saving must call
window.electronAPI.createRoutinewithbuildTaskRoutineCreate; do not create a parallel automation store for this flow. - Default target mode is
Continue thread, which stores the selected task astargetTaskIdand compiles schedule triggers to cron jobs withrunMode: "thread_follow_up". New taskcompiles schedule triggers to the normalrunMode: "new_task"cron path.- API-triggered routines compile same-thread targets to webhook mappings with
action: "task_message"and an explicittargetTaskId. - Event-triggered routines compile same-thread targets to event triggers with
runMode: "thread_follow_up"; invalid thread targets must fail instead of silently creating a new task. - Default run mode is
Chat, withshellAccess: false,allowUserInput: false, and no clarifying check-ins. Execute-mode tasks use hard-blocker-only human input by default; Plan/Debug can opt into structuredrequest_user_input. LocalsetsshellAccess: true.Worktreemust not be combined withContinue thread; the UI disables that path and lower-level worktree payloads forceNew task.- Saved prompts should include a source task title, task ID, and
cowork://tasks/<taskId>deeplink so future runs remain traceable. - Template selection should fill name, prompt, and schedule only; templates are not managed agents.
- Routine observability belongs in the routines surface. Compiled scheduled-task observability belongs in
src/renderer/components/ScheduledTasksSettings.tsx: uselistCronJobs,getCronRunHistory, andclearCronRunHistoryfor run health, latest result, delivery status, target-thread/new-task labels, and per-run task links. Do not duplicate scheduler history in the task automation modal. - Automation-specific agent config must be passed as transient run override for same-thread follow-ups; it must not overwrite the persisted task agent config.
Focused helpers exported from MainContent.tsx for tests:
TASK_AUTOMATION_TEMPLATESbuildTaskAutomationSchedulebuildTaskAutomationPromptbuildTaskRoutineCreatebuildTaskAutomationCronJobCreate
Validate changes with:
npx vitest run src/renderer/components/__tests__/main-content-working-state.test.ts
npx vitest run src/electron/agent/__tests__/executor-schedule-slash.test.ts src/electron/cron/__tests__/service.test.ts src/electron/routines/__tests__/service.test.ts src/electron/hooks/__tests__/server.test.ts src/electron/triggers/__tests__/EventTriggerService.test.ts
npm run build:reactSee Task Automations for the product concept and user-facing behavior.
Terminal tabs are real PTY-backed work surfaces, not a custom terminal emulator.
Implementation landmarks:
src/renderer/components/TerminalTabsDock.tsx: xterm.js instances, tab controls, fit/resize, keyboard input forwarding, and output renderingsrc/renderer/components/terminal-tabs-dock.css: terminal dock layout and xterm style isolationsrc/electron/terminal/TerminalPtyManager.ts:node-ptylifecycle, replay buffers, cwd/status tracking, platform shell environment, resize, stop, and closesrc/electron/ipc/handlers.ts: terminal tab IPC handlerssrc/electron/preload.ts: renderer-safe terminal tab APIsrc/shared/types.ts: terminal tab channel and event contracts
Design rules:
- Do not reimplement shell editing, Tab completion, Ctrl+C, cursor movement, prompt rendering, or command history in React. Those belong to the shell inside the PTY.
- Renderer input should be forwarded as raw xterm data to the PTY.
- Output replay should happen only on first attach for a renderer listener; replaying on every input duplicates prompts and output.
- Keep app typography out of xterm: reset letter spacing, word spacing, and transforms inside the terminal surface.
- Keep
node-ptynative files unpacked from Electron ASAR. - On macOS, keep zsh prompt/cwd setup in generated startup files rather than writing setup commands into the visible terminal.
- On Windows, validate through a real Windows build because modern systems use ConPTY and older systems can fall back to winpty.
Focused validation:
npx oxlint src/electron/terminal/TerminalPtyManager.ts src/renderer/components/TerminalTabsDock.tsx
npm run build:electron
npm run build:react
npm run type-checkProduct behavior, platform details, and release QA are documented in Terminal Tabs.
# Build/refresh local regression corpus
npm run qa:eval:build -- --window-days 30 --limit 300 --suite reliability-regressions
# Deterministic suite replay
npm run qa:eval:run -- --suite reliability-regressions --mode deterministic
# Optional: run against a custom DB path
COWORK_DB_PATH=/tmp/cowork-eval.db npm run qa:eval:run -- --suite reliability-regressions --mode deterministic
# Validate production-fix regression policy (mainly used by PR CI)
npm run qa:eval:enforce-regressions
# Recompute timeline completion telemetry for an existing DB
npm run qa:timeline:backfill -- --db /absolute/path/to.db
# Enforce timeline reliability thresholds on completion telemetry
npm run qa:timeline:enforce -- --db /absolute/path/to.dbSee also:
Run focused memory-observation checks when touching structured memory metadata, Memory Hub Inspector actions, prompt recall privacy, or memory backfill:
npx vitest run src/electron/memory/__tests__/MemoryObservationService.mock.test.ts src/electron/memory/__tests__/MemoryObservationService.test.ts
npm run type-checkThe native SQLite test file can skip locally when better-sqlite3 is unavailable. Keep the mock-level suite passing because it covers startup backfill behavior, failed metadata-write accounting, workspace-scoped soft-delete, and prompt-recall suppression without native SQLite.
Run focused durable-context checks when touching active-task recall, compaction-summary persistence, Memory Hub durable-context settings, context_grep, context_describe, or memory clearing:
npx vitest run src/electron/agent/tools/__tests__/system-tools-new.test.ts src/electron/settings/__tests__/memory-features-manager.test.ts src/electron/agent/__tests__/executor-chat-mode.test.ts
npx vitest run src/electron/memory/__tests__/DurableContextService.test.ts
npm run type-checkThe durable-service suite uses native SQLite and may skip locally when better-sqlite3 is unavailable. The tool-level suite should still run because it covers disabled behavior, tool exposure, active-task scope enforcement, and the explicit-user-request override without needing native SQLite.
See Durable Runtime Context for test prompts, expected behavior, and implementation landmarks.
Run these checks when editing bundled skills:
npm run skills:validate-routing
npm run skills:validate-content
npm run skills:audit
npm run skills:checkNotes:
skills:checkis phase-driven (SKILLS_CHECK_PHASE=1|2|3).- Phase 2+ enables path enforcement for
{baseDir}references. - Phase 3 enables strict warning enforcement.
The bundled manim-video skill has non-Node runtime dependencies, so when editing it you should validate both the content contract and the local helper scripts:
python3 -m py_compile resources/skills/manim-video/scripts/bootstrap_project.py
bash resources/skills/manim-video/scripts/setup.sh
npm run skills:checksetup.sh verifies the local Manim toolchain (python3, Manim CE, ffmpeg, and LaTeX). If Manim is missing, the skill can still scaffold projects, but render execution should be considered unavailable until the dependency is installed.
The bundled kami skill also has non-Node runtime dependencies, so validate both the content contract and the local helper scripts:
python3 -m py_compile \
resources/skills/kami/scripts/bootstrap_project.py \
resources/skills/kami/scripts/render_html.py
bash resources/skills/kami/scripts/setup.sh
node resources/skills/kami/scripts/render_slides.mjs --check
npm run skills:checksetup.sh reports the local Kami render toolchain (python3, node, weasyprint, pypdf, pptxgenjs, playwright, pdffonts, and local Chromium-family browser availability). If some render dependencies are missing, the skill can still scaffold and edit source projects, but PDF/PPTX export should be treated as conditional.
PPTX generation and previews use the bundled @oai/artifact-tool runtime first. Generation falls back to pptxgenjs if that runtime is missing. Preview loading is two-phase: fast mode extracts slide text/notes and cached images immediately, while full mode renders missing slide images through artifact-tool, then local soffice (LibreOffice) plus pdftoppm, then text-only preview if no renderer succeeds. See Presentation Artifacts and PPTX Preview.
The bundled react-best-practices skill has no helper scripts or native dependencies. When editing the skill, validate the content contract and routing coverage:
npm run skills:check:core
npm run skills:eval-routing
npm run skills:checkThe routing eval includes a React workspace feature prompt so the skill remains discoverable for React/Next.js implementation work without colliding with React Native guidance.
The bundled unbroker skill has Python helper scripts and a local PII ledger. When editing it, keep test data in a temporary PDD_DATA_DIR and validate both the skill contract and the deterministic engine:
python3 -m py_compile resources/skills/unbroker/scripts/*.py
PDD_DATA_DIR=/tmp/cowork-unbroker-qa python3 resources/skills/unbroker/scripts/pdd.py doctor
PDD_DATA_DIR=/tmp/cowork-unbroker-qa python3 resources/skills/unbroker/scripts/pdd.py setup --auto
PDD_DATA_DIR=/tmp/cowork-unbroker-qa-smoke python3 resources/skills/unbroker/scripts/pdd.py intake \
--full-name "Test Person" \
--email test@example.com \
--city Oakland \
--state CA \
--residency US-CA \
--consent \
--consent-method self
PDD_DATA_DIR=/tmp/cowork-unbroker-qa-smoke python3 resources/skills/unbroker/scripts/pdd.py next <subject_id>
npm run skills:checkThe smoke subject should be fake test data only. The skill stores sensitive PII under PDD_DATA_DIR, COWORK_HOME/unbroker, COWORK_USER_DATA_DIR/unbroker, or the legacy upstream HERMES_HOME/unbroker fallback, so never point validation at a real operator ledger unless the test is explicitly about migration or recovery.
The shared positioning and UX contract is documented in Everything Workbench. Treat generated documents, spreadsheets, presentations, web pages, PDFs, and previews as one artifact workbench family:
- task outputs should render as compact artifact cards when a dedicated surface exists
- default Open should prefer the in-app sidebar for previewable/editable artifacts
- fullscreen mode should keep the functional follow-up composer and latest-turn/working context
- active follow-up work should keep the current preview visible and defer refresh until the relevant output is updated or the task completes
- external app and folder actions should remain available for advanced native workflows
Spreadsheet artifact behavior is documented in Spreadsheet Artifacts.
Implementation notes:
readFileForViewerbuilds structured workbook and CSV/TSV preview data throughsrc/electron/utils/spreadsheet-preview.ts.FileViewerResult.data.spreadsheetPreviewis optional; keep the tab-separatedcontentfallback intact for older callers.SpreadsheetArtifactCardowns the task-feed card and open dropdown. DefaultOpenshould route to the in-app sidebar viewer; dropdown options can open external apps or the folder.SpreadsheetArtifactViewerowns grid selection, range/row/column copy, inline editing, add row/column, zoom, save, sidebar/fullscreen rendering, and the fullscreen follow-up composer.App.tsxowns sidebar/fullscreen state, persisted sidebar width, and the follow-up turn filter used by the fullscreen task context frame.- Fullscreen follow-up context should filter to events emitted after the fullscreen prompt is sent. Do not clear that filter timestamp when the follow-up completes; only clear the optimistic working state.
Focused checks:
npx vitest run \
src/electron/utils/__tests__/spreadsheet-preview.test.ts \
src/renderer/components/__tests__/spreadsheet-artifact-card.test.ts \
src/renderer/components/__tests__/spreadsheet-artifact-viewer.test.ts
npm run build:react
npm run type-checkDocument artifact behavior is documented in Document Artifacts.
Implementation notes:
readFileForViewerbuilds structured document preview data throughsrc/electron/utils/document-preview.ts.FileViewerResult.data.documentPreviewis optional; keep existingcontentandhtmlContentfallbacks intact for older callers.DocumentArtifactCardowns the task-feed card and open dropdown. DefaultOpenshould route previewable local documents to the in-app sidebar viewer; dropdown options can open external apps or the folder.DocumentArtifactViewerowns sidebar/fullscreen rendering, DOCX direct editing, toolbar commands, copy, save, external actions, and the fullscreen follow-up composer.- DOCX save writes editable block data back through
src/electron/utils/document-writer.tsandFILE_UPDATE_DOCUMENT. - Non-DOCX Word-style formats are recognized as document artifacts, but v1 should keep them preview/external-open only unless a reliable local editor is added.
App.tsxowns shared artifact sidebar/fullscreen state, persisted sidebar width, preview refresh keys, and the follow-up turn filter used by the fullscreen task context frame.- Fullscreen follow-up context should filter to events emitted after the fullscreen prompt is sent. Do not clear that filter timestamp when the follow-up completes; only clear the optimistic working state and refresh the active document preview from disk.
Focused checks:
npx vitest run \
src/electron/utils/__tests__/document-preview.test.ts \
src/electron/utils/__tests__/document-writer.test.ts \
src/renderer/components/__tests__/document-artifact-card.test.ts \
src/renderer/components/__tests__/document-artifact-viewer.test.ts
npm run build:react
npm run build:electron
npm run type-checkPresentation artifact behavior is documented in Presentation Artifacts and PPTX Preview.
Implementation notes:
src/shared/presentation-formats.tsowns PowerPoint-style artifact detection and labels.readFileForVieweracceptspresentationRenderMode: "fast" | "full"for.pptxfiles.- Fast mode extracts slide text and speaker notes and reuses cached slide images without running expensive renderers.
- Full mode uses the shared singleton
PptxPreviewService, renders missing slide images in the background, dedupes in-flight renders, and returnsimageUrlmedia links for cached PNGs. PresentationArtifactCardowns the compact task-feed card and open dropdown. DefaultOpenroutes previewable.pptxfiles to the in-app sidebar viewer; legacy PowerPoint formats use external-app/folder actions.PresentationArtifactViewerowns sidebar/fullscreen rendering, copy/external/folder actions, the fullscreen follow-up composer, and the cached viewer data shared between sidebar and fullscreen.PresentationViewerowns thumbnails, slide navigation, zoom, the white top-aligned slide canvas, text fallback, and speaker notes.App.tsxowns shared artifact sidebar/fullscreen state, persisted sidebar width, refresh keys, and the follow-up turn filter. During an active follow-up, artifact previews should keep the current deck visible and defer reloads until the follow-up completes.
Focused checks:
npx vitest run \
src/electron/utils/__tests__/PptxPreviewService.test.ts \
src/renderer/components/__tests__/presentation-artifact-card.test.ts \
src/renderer/components/__tests__/presentation-artifact-viewer.test.ts
npm run build:react
npm run build:electron
npm run type-checkWeb page artifact behavior is documented in Web Page Artifacts.
Implementation notes:
src/shared/web-page-formats.tsowns HTML/HTM artifact detection and labels.readFileForViewerreturnsFileViewerResult.data.webPreviewfor generated HTML, built React output, and React-style project paths.src/electron/utils/web-preview.tsresolves HTML files directly, detects React/Vite/Next project roots frompackage.json, and looks for builtdist/index.html,build/index.html, orout/index.html.src/electron/utils/html-preview-assets.tsinlines local assets for sandboxed iframe preview where possible.WebArtifactCardowns the compact task-feed card and open dropdown. DefaultOpenroutes previewable web pages to the in-app sidebar viewer.WebArtifactViewerowns sidebar/fullscreen rendering, the sandboxed iframe, browser/folder/copy actions, unavailable-state rendering, and the fullscreen follow-up composer.App.tsxowns shared artifact sidebar/fullscreen state, persisted sidebar width, refresh keys, and the follow-up turn filter. During an active follow-up, web previews should keep the current page visible and defer reloads until the follow-up completes or a matching file output is emitted.- V1 must not auto-start React, Vite, or Next dev servers from the artifact viewer. Missing build output should remain a structured preview-unavailable state.
Focused checks:
npx vitest run \
src/electron/utils/__tests__/web-preview.test.ts \
src/renderer/components/__tests__/web-artifact-card.test.ts \
src/renderer/components/__tests__/web-artifact-viewer.test.ts
npm run build:react
npm run build:electron
npm run type-checkThe native compile_latex tool is separate from generate_document. It compiles an existing workspace .tex file into a PDF by discovering a system engine in this order: tectonic, latexmk, xelatex, lualatex, pdflatex.
Implementation and QA notes:
- Do not shell-interpolate compiler commands; use bounded
execFilecalls. - Keep all source/output paths inside the active workspace.
- Preserve the
.texsource even when no compiler is installed or compilation fails. - Register successful PDFs as artifacts with
mimeType: "application/pdf"andsourcePathmetadata pointing back to the.texfile. - Renderer pairing is driven by
artifact_created.sourcePathfirst, with same-folder/same-basename fallback for older events.
For completion/output UX changes, run the focused suites:
npx vitest run \
src/electron/utils/__tests__/latex-compiler.test.ts \
src/electron/agent/tools/__tests__/document-tools.test.ts \
src/renderer/utils/__tests__/latex-artifacts.test.ts \
src/renderer/utils/__tests__/task-outputs.test.ts \
src/renderer/utils/__tests__/task-completion-ux.test.ts \
src/renderer/utils/__tests__/task-event-visibility.test.ts \
src/electron/agent/__tests__/daemon-complete-task.test.ts \
src/electron/control-plane/__tests__/task-event-bridge-contract.test.ts \
src/renderer/__tests__/task-event-status-map.test.tsWhen unit-testing TaskExecutor completion paths, mock daemon.getTaskEvents() in harnesses.
finalizeTask() always reads task events to build output summaries.
For structured input, executor recovery, and timeline-lane changes, run:
npx vitest run \
src/daemon/__tests__/control-plane-methods.test.ts \
src/electron/agent/__tests__/daemon-input-request.test.ts \
src/electron/agent/tools/__tests__/request-user-input.test.ts \
src/electron/agent/__tests__/path-alias.test.ts \
src/electron/agent/__tests__/executor-context-overflow-recovery.test.ts \
src/electron/agent/__tests__/executor-parallel-batch.test.ts \
src/electron/agent/__tests__/executor-workspace-preflight-ack.test.ts \
src/renderer/components/timeline/__tests__/parallel-group-projection.test.ts \
src/renderer/components/timeline/__tests__/parallel-group-feed.test.ts \
src/renderer/utils/__tests__/task-event-compat.test.tsFor sidebar virtualization and @chenglou/pretext measurement work in the CoWork-OS/CoWork-OS repo, run:
npx vitest run \
src/renderer/__tests__/sidebar-helpers.test.ts \
src/renderer/hooks/__tests__/useVirtualList.test.ts \
src/renderer/utils/__tests__/pretext-adapter.test.ts \
src/renderer/components/timeline/__tests__/semantic-timeline-projection.test.ts| Directory | Description |
|---|---|
src/electron/ |
Main process (Node.js/Electron) |
src/renderer/ |
React UI components |
src/shared/ |
Shared types between main and renderer |
resources/skills/ |
Built-in skill definitions |
connectors/ |
Enterprise MCP connector implementations |
The main composer supports a grouped @ autocomplete for Agents, Integrations, and Files. The user-facing behavior is documented in Composer Mentions; this section captures the developer contract.
Implementation boundaries:
src/shared/types.tsownsIntegrationMentionOptionandIntegrationMentionSelection.src/electron/integrations/integration-mention-options.tsbuilds integration mention options from local configured state only. It must stay fast and must not run network checks while the user types.src/electron/ipc/handlers.tsandsrc/electron/preload.tsexposelistIntegrationMentionOptions().src/renderer/components/PromptComposerInput.tsxowns rich inline mention editing. It keeps clean text serialization such as@Gmail, renders icon+label chips, and treats each chip as one removable unit for Backspace/Delete.src/renderer/components/MainContent.tsxowns grouped menu filtering, section order, task/follow-up submission metadata, user message rendering, and@Inboxmain-composer routing.src/renderer/components/IntegrationMentionText.tsxrenders integration chips in sent user messages and restored session history.src/electron/agent/executor.tsturnsagentConfig.integrationMentionsinto a soft routing guidance block. Do not convert mentions intoallowedTools.
Focused checks:
npx vitest run \
src/electron/integrations/__tests__/integration-mention-options.test.ts \
src/renderer/components/__tests__/prompt-composer-input.test.ts \
src/renderer/components/__tests__/integration-mention-text.test.ts
npm run lint
npm run type-checkAsk Inbox is the mailbox-specific agentic question surface inside Inbox Agent. The product and architecture contract is documented in Ask Inbox Architecture; this section captures the developer boundaries.
Implementation boundaries:
src/renderer/components/InboxAgentPanel.tsxowns the right-sidebarAgent Rail/Ask Inboxtabs, the left ask launcher behavior, the Ask Inbox transcript, live step timeline, matched email rows, and pinned Ask composer.src/electron/mailbox/MailboxService.tsownsaskMailbox(), action-intent classification, progress emission, provider search adapters, answer generation, and safe draft creation paths.src/electron/mailbox/MailboxAgentSearchService.tsowns query planning, local FTS retrieval, semantic mailbox retrieval, provider-result normalization, attachment-aware search, shortlist/read/rerank behavior, and no-evidence answers.src/shared/mailbox.tsownsMailboxAskInput,MailboxAskResult, andMailboxAskRunEvent.src/shared/types.ts,src/electron/ipc/handlers.ts, andsrc/electron/preload.tsown the transientmailbox:askEventchannel andonMailboxAskEvent()subscription.
Rules:
- Do not stream Ask progress through persisted
MailboxEvent. Ask progress is transient UI telemetry and must not trigger mailbox automations, Heartbeat, Knowledge Graph, or playbooks. - Keep provider-native search additive. If Gmail or Outlook/Microsoft Graph search fails, Ask Inbox must fall back to local evidence.
- Keep destructive actions out of Ask Inbox. It may answer questions and create reviewable drafts, but it must not silently send, archive, trash, mark done, or bulk mutate mail.
- Preserve source metadata on results (
local_fts,local_vector,provider_search,attachment_text) so UI evidence labels remain truthful.
Focused checks:
npx vitest run src/electron/mailbox/__tests__/MailboxAgentSearchService.test.ts
npx tsc -p tsconfig.electron.json
npm run type-checkThe main composer supports a grouped / autocomplete for deterministic app commands and skill-backed workflow shortcuts. The user-facing behavior is documented in Message Box Shortcuts; this section captures the developer contract.
Implementation boundaries:
src/shared/message-shortcuts.tsowns the deterministic app command catalog and parser for/schedule,/clear,/plan,/cost,/compact,/doctor, and/undo.src/shared/multitask-command.tsowns/multitask [N] <task>parsing because it is a task-creation command that strips its prefix before persistence and adds collaborative multitask task metadata.src/renderer/utils/message-slash-options.tsowns picker option ordering, filtering, app-vs-skill display, optional/required parameter classification, invalid-token filtering, and keyboard selected-index clamping.src/renderer/components/MainContent.tsxowns composer detection, selection behavior, slash-token insertion, app command task creation, legal workflow intake cards, and/scheduletarget selection for standalone tasks versus same-thread follow-ups.src/renderer/utils/legal-demand-intake.tsowns Claude-for-Legal slash detection, demand-intake prefill/serialization, generic legal workflow context serialization, and the allow/deny filter for legal commands that should show matter-context UI.src/electron/agents/MultitaskLanePlanner.tsplans/multitasklanes from explicit list items, LLM JSON output, or deterministic fallback lanes.src/renderer/App.tsxowns the safe/cleartask-view reset./clearmust not delete task history or switch workspaces.src/electron/agent/skill-slash-aliases.tsresolves plugin-packslashCommandsaliases to target skill IDs. Backend precedence must match picker display; enabled plugin aliases win over direct skill IDs when tokens collide.src/electron/agent/executor.tsowns generic skill slash execution. The deterministic/schedulehandler must continue to run before generic skill slash routing.resources/plugin-packs/cowork-shortcuts/cowork.plugin.jsonseeds the bundled CoWork Shortcuts workflow pack as normal skills and aliases.
Focused checks:
npx vitest run \
src/shared/__tests__/message-shortcuts.test.ts \
src/shared/__tests__/skill-slash-commands.test.ts \
src/electron/agent/__tests__/skill-slash-aliases.test.ts \
src/electron/agent/__tests__/executor-schedule-slash.test.ts \
src/shared/__tests__/multitask-command.test.ts \
src/electron/agents/__tests__/MultitaskLanePlanner.test.ts \
src/electron/agents/__tests__/AgentTeamOrchestrator.test.ts \
src/renderer/utils/__tests__/legal-demand-intake.test.ts \
src/renderer/utils/__tests__/message-slash-options.test.ts \
src/renderer/components/__tests__/main-content-working-state.test.ts
npm run type-check
npm run build:reactUse the connector template:
cp -r connectors/templates/mcp-connector connectors/my-connector
cd connectors/my-connector
npm install
# Edit src/index.ts to implement your tools
npm run buildSee Enterprise Connectors for the full connector contract.
| Requirement | Minimum | Recommended |
|---|---|---|
| Desktop OS | macOS 12 / Windows 10 | macOS 13+ / Windows 11 |
| RAM | 4 GB | 8 GB+ |
| CPU | 2 cores | 4+ cores |
| Architecture | x64 or arm64 | Native architecture of your host |
- macOS 12 Monterey, 13 Ventura, 14 Sonoma, 15 Sequoia
- Windows 10 and Windows 11 (x64 and ARM64)
- Base memory: ~300-500 MB (Electron + React UI)
- Per bot integration: ~50-100 MB additional
- Playwright automation: ~200-500 MB when active
- CPU: Mostly idle; spikes during AI API calls
| Host Platform | VM Options |
|---|---|
| Apple Silicon Mac | UTM, Parallels Desktop, VMware Fusion |
| Intel Mac | Parallels Desktop, VMware Fusion, VirtualBox |
| Windows | Hyper-V, VMware Workstation, VirtualBox |
Recommended VM specs: 4+ GB RAM, 2+ CPU cores, 40+ GB disk space.
See Troubleshooting for common build and setup issues.
Hard executor budget contracts are now opt-in.
- Env var:
COWORK_AGENT_BUDGET_CONTRACTS - Default:
false - Effect when disabled: strict budget-contract caps (including tool-call caps) are not enforced by default.
- To restore legacy behavior: set
COWORK_AGENT_BUDGET_CONTRACTS=true
Validation after this change:
executor-step-failurestests pass.npm run type-checkpasses.npm run build:electronpasses.