ben 9784c618bd feat(app): build React session composer parity on Lexical (#1367)
* feat(app): add Lexical React composer shell

Replace the React session textarea with a Lexical-based plain-text composer shell so subsequent parity work can build on a real editor instead of continuing to extend the temporary textarea path.

* feat(app): route React composer through shared session draft flow

Make the React Lexical composer use the existing ComposerDraft send/draft pipeline from the session page and expose transcript copy plus model picker access so parity work builds on the same session actions as the Solid path.

* feat(app): add React composer controls and attachments

Extend the Lexical React composer with model/variant/agent controls, file attachments, prompt-vs-shell draft mode, and slash command suggestions while continuing to use the shared ComposerDraft send path from the existing session actions.

* feat(app): add React mention suggestions

Add end-of-input @ mention suggestions for agents and files in the Lexical React composer and serialize selected mentions into the shared ComposerDraft parts so the React path can target agent/file inputs instead of plain text only.

* feat(app): render React mentions as Lexical chips

Upgrade accepted React composer mentions from plain text into Lexical token nodes so agent and file selections render as real inline chips instead of raw @text while still serializing back into ComposerDraft parts.

* feat(app): add React composer notices and file paste/drop

Bring the Lexical React composer closer to the Solid UX by adding inline composer notices plus file paste/drop intake and attachment feedback without leaving the shared ComposerDraft pipeline.

* fix(app): make Lexical mention node serializable

Implement importJSON/exportJSON and constructor defaults for the React composer mention node so Lexical can safely instantiate and rehydrate accepted mention chips without throwing at runtime.

* feat(app): remove React shell composer mode

Drop the temporary Prompt/Shell toggle from the React session composer so the React path stays focused on prompt-mode parity instead of exposing an incomplete shell-mode branch.

* feat(app): add keyboard navigation for React composer menus

Add ArrowUp/ArrowDown, Enter/Tab accept, Escape close, and highlighted active-row state for the React slash and mention suggestion menus so the Lexical composer behaves more like the Solid composer.

* feat(app): make React mention chips behave like tokens

Treat accepted Lexical mention chips as real inline tokens by improving their visuals and adding backspace/arrow behavior so chip editing feels much closer to the Solid composer path.

* feat(app): add richer React paste and remote composer warnings

Extend the Lexical React composer with collapsed long-paste handling, unsupported-file link insertion, and remote/sandbox-specific notices so paste and drop behavior moves closer to the Solid composer path.

* feat(app): add per-message copy actions to React transcript

Add individual copy actions on React transcript messages so the React session surface closes another gap with the Solid path beyond the existing whole-transcript copy action.

* feat(app): render React slash commands as chips

Upgrade accepted slash commands in the Lexical React composer into real inline token nodes so slash completion feels more intentional and benefits from the same token navigation behavior as mentions.

* feat(app): polish React attachment cards and validation

Upgrade the React composer attachment UI with richer cards, image previews, mime badges, size display, and oversized-file warnings so attachment handling feels closer to the Solid composer path.

* feat(app): polish React composer menu interactions

Close the React mention/slash menus cleanly after selection and keep active-row state in sync with mouse hover so the Lexical composer menus feel less sticky and more intentional.

* feat(app): add pasted text chip UX to React composer

Promote collapsed pasted text into a first-class React composer surface with preview, copy, and remove actions so long pasted content is no longer only a hidden placeholder token inside the draft.

* feat(app): add React code and tool copy actions

Add copy affordances for Markdown code blocks plus tool request/result/diff sections so the React transcript and tool-call surfaces close more of the remaining practical parity gap with the Solid session UI.

* feat(app): polish React menu scrolling and upload actions

Keep the active React mention/slash selection scrolled into view and make remote-path warnings offer a more useful shared-folder upload action when attachments are already present in the composer.

* feat(app): align React drop behavior with attachment support

Handle unsupported dropped files more deliberately in the React composer by matching the attachment support matrix and surfacing an explanatory notice instead of silently treating every dropped file the same.

* feat(app): add keyboard navigation to React agent picker

Extend the React composer agent picker with active-row highlighting plus ArrowUp/ArrowDown/Enter/Escape behavior so it matches the rest of the keyboard-driven Lexical composer interactions.

* feat(app): anchor React composer to session bottom

Keep the React composer pinned to the bottom of the session layout with a dedicated footer container so the transcript scrolls above it instead of letting the composer drift upward when the session is sparse.

* feat(app): delay React session loading and switching UI

Only show the React session loading/switching affordances after a short delay and stop treating normal streaming as a switching state so the session surface feels calmer during quick transitions and active chats.

* feat(app): add visible React composer dropzone

Turn the React composer drag/drop path into a real dropzone with isolated local drag-over state and visible drop affordances instead of relying only on hidden drag handlers.

* fix(app): remove React composer glow

Drop the extra card shadow from the React composer container so the input surface stops showing the grey halo and sits closer to the cleaner Solid styling.

* fix(app): simplify React composer footer chrome

Remove the extra sticky footer treatment from the React session surface so the composer sits more cleanly in the layout without the added gradient wrapper.

* fix(app): clean up React transcript rendering

Remove the useless Step started label from the React transcript, render markdown horizontal rules as subtle dividers instead of heavy black lines, and tone down heading sizes so the session surface looks cleaner.

* fix(app): use light gray for React markdown dividers

Switch the horizontal rule color to a softer gray so markdown dividers feel like subtle section breaks instead of standing out.

* fix(app): clean up React composer layout, copy buttons, and pasted text chips

Fix the session layout so the transcript scrolls and the composer stays at the bottom. Replace raw Copy text buttons with cleaner icon-based copy affordances with check feedback. Render pasted multi-line text as Lexical inline chips instead of raw text in the editor.

* fix(app): unify React composer Run/Stop into one button

Replace the separate Stop and Run task buttons with a single button that switches between Run task and Stop based on streaming state so the composer footer stays clean.

* fix(app): remove Copy transcript button and fix user bubble spacing

Drop the standalone Copy transcript button and move per-message copy affordances to absolute positioning so they don't inflate message bubble height when hidden.

* fix(app): style React model variant picker as a proper menu

Replace the unstyled native select dropdown for model variant/behavior with a styled button + dropdown menu that matches the rest of the Lexical composer controls.

* fix(app): make React session mount chain full height

Add explicit full-height sizing through the Solid session wrapper and React island container so the React session surface can reliably fill the available vertical space without manual inline devtools overrides.

* feat(app): add React session scroll memory and auto-follow

Remember scroll position per session so switching between sessions restores where you left off. Scroll to bottom on first load of a new session. Auto-follow streaming text unless the user scrolls up. Show a scroll-to-bottom pill when not at the bottom.

* fix(app): prevent horizontal scroll in React session transcript

Add overflow-x-hidden to the transcript scroll container so wide content like code blocks or tool output cannot cause unwanted horizontal scrolling.

* fix(app): hide base64 data URLs in React file cards

Stop leaking raw data: URLs and application/octet-stream labels in file attachment cards. Show a clean filename or Attached file label instead, with a human-readable file type badge derived from the extension or mime type.

* feat(app): add React file card preview and desktop actions

Upgrade the React transcript file card with a cleaner design, proper file icon, and a desktop-only actions dropdown for opening files with the OS default app, revealing in Finder, or copying the path. Actions are hidden on web where Tauri APIs are not available.

* fix(app): parenthesize React variant label fallback

Use explicit parentheses around the nullish-coalescing variant label fallback so esbuild accepts the expression during production builds.
2026-04-08 15:13:07 -07:00
2026-03-24 14:28:57 -07:00
2026-04-08 15:12:28 -07:00
2026-02-09 00:06:31 -08:00
2026-03-27 14:58:24 -07:00
2026-03-27 07:39:18 -07:00
2026-02-14 12:23:50 -08:00
2026-04-04 12:46:01 -06:00
2026-04-04 12:46:01 -06:00
2026-04-05 20:03:00 -06:00
2026-03-18 12:16:23 -07:00

OpenWork is the open source alternative to Claude Cowork/Codex (desktop app).

Core Philosophy

  • Local-first, cloud-ready: OpenWork runs on your machine in one click. Send a message instantly.
  • Composable: desktop app, WhatsApp/Slack/Telegram connector, or server. Use what fits, no lock-in.
  • Ejectable: OpenWork is powered by OpenCode, so everything OpenCode can do works in OpenWork, even without a UI yet.
  • Sharing is caring: start solo on localhost, then explicitly opt into remote sharing when you need it.

OpenWork demo

OpenWork is designed around the idea that you can easily ship your agentic workflows as a repeatable, productized process.

Alternate UIs

  • OpenWork Orchestrator (CLI host): run OpenCode + OpenWork server without the desktop UI.
    • install: npm install -g openwork-orchestrator
    • run: openwork start --workspace /path/to/workspace --approval auto
    • docs: apps/orchestrator/README.md

Quick start

Download the desktop app from openworklabs.com/download, grab the latest GitHub release, or install from source below.

  • macOS and Linux downloads are available directly.
  • Windows access is currently handled through the paid support plan on openworklabs.com/pricing#windows-support.
  • Hosted OpenWork Cloud workers are launched from the web app after checkout, then connected from the desktop app via Add a worker -> Connect remote.

Why

Current CLI and GUIs for opencode are anchored around developers. That means a focus on file diffs, tool names, and hard to extend capabilities without relying on exposing some form of cli.

OpenWork is designed to be:

  • Extensible: skill and opencode plugins are installable modules.
  • Auditable: show what happened, when, and why.
  • Permissioned: access to privileged flows.
  • Local/Remote: OpenWork works locally as well as can connect to remote servers.

Whats Included

  • Host mode: runs opencode locally on your computer
  • Client mode: connect to an existing OpenCode server by URL.
  • Sessions: create/select sessions and send prompts.
  • Live streaming: SSE /event subscription for realtime updates.
  • Execution plan: render OpenCode todos as a timeline.
  • Permissions: surface permission requests and reply (allow once / always / deny).
  • Templates: save and re-run common workflows (stored locally).
  • Debug exports: copy or export the runtime debug report and developer log stream from Settings -> Debug when you need to file a bug.
  • Skills manager:
    • list installed .opencode/skills folders
    • import a local skill folder into .opencode/skills/<skill-name>

Skill Manager

image

Works on local computer or servers

Screenshot 2026-01-13 at 7 05 16 PM

Quick Start

Requirements

  • Node.js + pnpm
  • Rust toolchain (for Tauri): install via curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Tauri CLI: cargo install tauri-cli
  • OpenCode CLI installed and available on PATH: opencode

Local Dev Prerequisites (Desktop)

Before running pnpm dev, ensure these are installed and active in your shell:

  • Node + pnpm (repo uses pnpm@10.27.0)
  • Bun 1.3.9+ (bun --version)
  • Rust toolchain (for Tauri), with Cargo from current rustup stable (supports Cargo.lock v4)
  • Xcode Command Line Tools (macOS)
  • On Linux, WebKitGTK 4.1 development packages so pkg-config can resolve webkit2gtk-4.1 and javascriptcoregtk-4.1

One-minute sanity check

Run from repo root:

git checkout dev
git pull --ff-only origin dev
pnpm install --frozen-lockfile

which bun
bun --version
pnpm --filter @openwork/desktop exec tauri --version

Install

pnpm install

OpenWork now lives in apps/app (UI) and apps/desktop (desktop shell).

Run (Desktop)

pnpm dev

pnpm dev now enables OPENWORK_DEV_MODE=1 automatically, so desktop dev uses an isolated OpenCode state instead of your personal global config/auth/data.

Run (Web UI only)

pnpm dev:ui

All repo dev entrypoints now opt into the same dev-mode isolation so local testing uses the OpenWork-managed OpenCode state consistently.

Arch Users:

sudo pacman -S --needed webkit2gtk-4.1
curl -fsSL https://opencode.ai/install | bash -s -- --version "$(node -e "const fs=require('fs'); const parsed=JSON.parse(fs.readFileSync('constants.json','utf8')); process.stdout.write(String(parsed.opencodeVersion||'').trim().replace(/^v/,''));")" --no-modify-path

Architecture (high-level)

  • In Host mode, OpenWork runs a local host stack and connects the UI to it.
    • Default runtime: openwork (installed from openwork-orchestrator), which orchestrates opencode, openwork-server, and optionally opencode-router.
    • Fallback runtime: direct, where the desktop app spawns opencode serve --hostname 127.0.0.1 --port <free-port> directly.

When you select a project folder, OpenWork runs the host stack locally using that folder and connects the desktop UI. This lets you run agentic workflows, send prompts, and see progress entirely on your machine without a remote server.

  • The UI uses @opencode-ai/sdk/v2/client to:
    • connect to the server
    • list/create sessions
    • send prompts
    • subscribe to SSE events(Server-Sent Events are used to stream real-time updates from the server to the UI.)
    • read todos and permission requests

Folder Picker

The folder picker uses the Tauri dialog plugin. Capability permissions are defined in:

  • apps/desktop/src-tauri/capabilities/default.json

OpenCode Plugins

Plugins are the native way to extend OpenCode. OpenWork now manages them from the Skills tab by reading and writing opencode.json.

  • Project scope: <workspace>/opencode.json
  • Global scope: ~/.config/opencode/opencode.json (or $XDG_CONFIG_HOME/opencode/opencode.json)

You can still edit opencode.json manually; OpenWork uses the same format as the OpenCode CLI:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-wakatime"]
}

Useful Commands

pnpm dev
pnpm dev:ui
pnpm typecheck
pnpm build
pnpm build:ui
pnpm test:e2e

Troubleshooting

If you need to report a desktop or session bug, open Settings -> Debug and export both the runtime debug report and developer logs before filing an issue.

Linux / Wayland (Hyprland)

If OpenWork crashes on launch with WebKitGTK errors like Failed to create GBM buffer, disable dmabuf or compositing before launch. Try one of the following environment flags.

WEBKIT_DISABLE_DMABUF_RENDERER=1 openwork
WEBKIT_DISABLE_COMPOSITING_MODE=1 openwork

Security Notes

  • OpenWork hides model reasoning and sensitive tool metadata by default.
  • Host mode binds to 127.0.0.1 by default.

Contributing

  • Review AGENTS.md plus VISION.md, PRINCIPLES.md, PRODUCT.md, and ARCHITECTURE.md to understand the product goals before making changes.
  • Ensure Node.js, pnpm, the Rust toolchain, and opencode are installed before working inside the repo.
  • Run pnpm install once per checkout, then verify your change with pnpm typecheck plus pnpm test:e2e (or the targeted subset of scripts) before opening a PR.
  • Use .github/pull_request_template.md when opening PRs and include exact commands, outcomes, manual verification steps, and evidence.
  • If CI fails, classify failures in the PR body as either code-related regressions or external/environment/auth blockers.
  • Add new PRDs to apps/app/pr/<name>.md following the .opencode/skills/prd-conventions/SKILL.md conventions described in AGENTS.md.

Community docs:

  • CODE_OF_CONDUCT.md
  • SECURITY.md
  • SUPPORT.md
  • TRIAGE.md

First contribution checklist:

  • Run pnpm install and baseline verification commands.
  • Confirm your change has a clear issue link and scope.
  • Add/update tests for behavioral changes.
  • Include commands run and outcomes in your PR.
  • Add screenshots/video for user-facing flow changes.

For Teams & Businesses

Interested in using OpenWork in your organization? We'd love to hear from you — reach out at ben@openworklabs.com to chat about your use case.

License

MIT — see LICENSE.

Description
Mirrored from GitHub
Readme MIT 1.1 GiB
Languages
TypeScript 83.8%
JavaScript 7.7%
Rust 4.3%
CSS 2.5%
Shell 1%
Other 0.6%