modelstudioai / modelstudioai/openwork
Migration tracker: re-found OpenWork on the Qwen Code Web Shell + Tauri architecture
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 27
- Forks
- 6
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Re-found OpenWork on the Qwen Code stack: fork QwenLM/qwen-code into a standalone OpenWork repository, adopt the Tauri 2 desktop shell (packages/desktop-shell) and Web Shell UI (packages/web-shell), and port OpenWork's existing product surface onto that architecture.
This issue is the master tracker. Workstreams below will be broken out into sub-issues.
1. Context: what OpenWork actually is today
Establishing this correctly matters, because it determines what "sync from upstream" can and cannot mean.
OpenWork is not a fork of Qwen Code. Its root commit 7b57e21 (2026-05-01) is a subtree extraction from eb45067f "Make backend Qwen-only", whose tree is the root-level Craft layout (apps/{cli,electron,viewer,webui}, Dockerfile.server). Qwen Code did not gain packages/desktop until 62e45c567 (#3778) on 2026-06-11 — six weeks later. eb45067f is not an ancestor of qwen-code@main.
The real topology is two siblings from a common Craft-derived ancestor:
craft-ai-agents/craft-agents-oss (craft-agent, Electron)
│
▼
eb45067f "Make backend Qwen-only" (2026-05-01)
│
┌───────┴────────┐
▼ ▼
OpenWork qwen-code/packages/desktop
228 commits 50 commits
v0.1.4 v0.0.5
Current coupling to Qwen Code is narrow: qwenCodeRuntime.version: 0.15.11 in package.json, vendored from the npm tarball by scripts/vendor-qwen-code.ts, spawned as a subprocess by packages/shared/src/agent/qwen-agent.ts with ['--acp', '--channel=desktop']. Upstream CLI is now at 0.21.6 — six minors ahead.
Consequence of this migration: tracking craft-agents-oss ends. Adopting Web Shell replaces the Craft-derived UI and server layers. That is an accepted, deliberate trade — noted here so it is never a surprise.
2. Target architecture
Mirrors upstream's packages/desktop-shell:
- Tauri 2 shell (~1,537 lines of Rust:
main.rs678,runtime.rs547,desktop_state.rs312) replaces Electron. - Shell starts the daemon on an ephemeral loopback port with a per-launch bearer token, polls
/health, then opens the daemon-served Web Shell in a native window. scripts/prepare-runtime.jsstagesruntime/qwen-code/with a platform Node.js, the built CLI, and Web Shell underlib/web-shell/.
All artifacts build from source. prepare-runtime.js resolves sourceRoot from QWEN_CODE_ROOT or the repo root and runs npm run build -- --cli-only, --workspace=packages/webui, --workspace=packages/web-shell. No @qwen-code/* package is fetched from npm. This is deliberate — @qwen-code/web-shell is unpublished, and @qwen-code/webui on npm is 0.1.0-beta.4 against a monorepo at 0.21.6. Only third-party deps come from the registry.
3. Open questions — resolve before Phase 2
These are genuine unknowns, not rhetorical. Each can change scope materially.
- Browser panes / CDP.
apps/electron/src/main/browser-cdp.tsandbrowser-pane-manager.tsdepend on ElectronBrowserView+ Chrome DevTools Protocol, backing the agent browser tools inpackages/shared/src/agent/browser-tools.ts. Tauri can create multiple and child webviews, but it has no portable equivalent for the ElectronBrowserView+ CDP contract; WebKitGTK exposes no CDP. The current browser-agent contract may therefore be architecturally blocked. Spike before committing. - Web Shell variant seam.
client/customization.tsxcovers only presentation (markdown components, code-block/chart renderers,WelcomeHeader, theme);client/extensions/contains one module. There is no seam for pages, navigation, or settings sections — which is most of what OpenWork adds. Decide whether to build that seam upstream first (strongly preferred) or patch Web Shell source directly. - Session/credential data migration. OpenWork stores product data under
~/.craft-agent/, includingconfig.json,preferences.json, workspace/session data, and an AES-256-GCM credential file at~/.craft-agent/credentials.enc. Its key is derived from the machine UUID; this is not a macOS Keychain re-binding problem. Session formats still differ from daemon transcripts. Decide whether to migrate each data class or explicitly document a one-way break. - Feature freeze policy — the parity target is currently moving. OpenWork is still shipping actively (12 commits in W30; 17 UI features in the last ~30 commits, driven by the autonomous feature loop bootstrapped in #38). If that continues through a multi-month migration, every new Electron feature adds to Phase 4's port backlog and parity recedes as it is approached. Decide one of: (a) freeze feature work on the Electron app at a named commit, (b) require new features to land on the new stack only once Phase 3 completes, or (c) accept dual-maintenance and staff for it explicitly. This must be answered before Phase 4 begins — it is the difference between a finite port and an open-ended one.
4. Phase plan
Phase 0 — De-risk (no migration work)
- Bump
qwenCodeRuntime.version0.15.11→0.21.6; re-vendor; validate ACP. Note: OpenWork is on@agentclientprotocol/sdk ^0.21.0while qwen-code0.21.6CLI is on^0.14.1— verifyPROTOCOL_VERSIONnegotiation rather than assuming. Confirm--acp(not deprecated--experimental-acp) and--channel. Valuable independently of migration. - Browser-pane / CDP spike (see §3).
- Run
bun run server:prodand exercise the app in a plain browser. Whatever breaks is the true Electron-coupling inventory — 95 of 509 renderer files importwindow.electron/electrondirectly. - Build
desktop-shellunmodified on macOS/Windows/Linux; measure real bundle size and memory before promising numbers (runtime/qwen-code/ships Node + CLI, so the Chromium saving is partly offset).
Phase 1 — Repository foundation
- Freeze current tree as
modelstudioai/openwork-legacy(reference during porting — do not disturb the shipping product). - Create new OpenWork repo from
QwenLM/qwen-code. - First commit: neutralize CI. 43 workflows, 13 release/publish.
release.ymlruns on daily cron;release-vscode-companion.ymlfires onrelease: published. Alsorelease-sdk*.yml,build-and-publish-image.yml,sync-release-to-oss.yml. Scheduled workflows are auto-disabled only in true GitHub forks, not plain repos. - Establish branch model:
vendor/upstream(pristine mirror, never edited) →main. -
.gitattributes:package-lock.json merge=ours, regenerate post-merge. - Keep all upstream packages (~18 + 10 channels). Do not prune — deleting files conflicts on every future merge.
- Preserve upstream
LICENSE/NOTICE; append attribution rather than replacing (Apache-2.0 requirement). Clean the stale Craft-era Anthropic Commercial Terms clause, which no longer applies post-eb45067f.
Phase 2 — Shell and branding
- Fork
desktop-shell→ OpenWork shell with own identifier, icons, signing identity, updater keypair, endpoint. - Overlay, do not edit
tauri.conf.json(currentlycom.alibaba.qwen-code, pubkey, and aQwenLM/qwen-codeupdater endpoint). Usetauri build --config tauri.openwork.conf.jsondeep-merge; verify merge semantics for the pinned Tauri version. - Release-time assertion that the shipped updater endpoint and identifier are OpenWork's. Shipping with Qwen's endpoint would push users into a different application — unrecoverable, so it must be enforced mechanically.
- Add
desktop-release.ymlas a new file, not an edit to upstream's.
Phase 3 — Variant seam
- Land page/route registry, settings-section registry, slot-based navigation in Web Shell (upstream where possible).
- Establish
packages/web-shell/client/openwork/andpackages/openwork-*for additive product code. - Start
PATCHES.mdledger recording every intentional divergence in shared files, with rationale.
Phase 4 — Feature port
Port against the parity matrix (§5), one sub-issue per cluster.
Phase 5 — Data migration
- Settings, workspace/session history, and
credentials.encmigration or in-place reuse, validated with upgrade fixtures. - Electron → Tauri update bridge. Upstream's
electron_bridgehandles update delivery only — data migration is separate and ours to build.
Phase 6 — Release
- Platform QA, signing/notarization, staged rollout, rollback plan.
5. Preservation inventory — what must not be lost
Web Shell already covers parts of Skills, MCP, Agents, Extensions/Plugins, Channels, Scheduled Tasks, themes, permissions, voice, raw-Markdown copy, prompt history, and jump-to-latest. The matrix must classify every item below as reuse / adapt / rebuild / consciously drop; do not assume either full absence or full parity.
OpenWork-owned overlay (separate release blocker):
- Branding/distribution:
openworkidentity,com.alibaba.openwork, OpenWork assets/copy/co-author text, themodelstudioai/openworkupdater and homepage, attribution, and an explicit decision on inheritedcraftagents://deep links and theagents.craft.doviewer URL. - Curated skill marketplace: Bailian CLI, Bailian Docs LLM Wiki, and Spark Video Episode, including icons, examples, install sources, and descriptions.
- Repository skills:
.agents/skills/{desktop-brand-builder,desktop-develop,desktop-pet}. Port the brand builder to Tauri; rewrite or retire Electron/CDP-specific instructions; retain the pet skill only if pets remain. - Agent/product behavior: preserve the OpenWork contracts currently assembled in
packages/shared/src/prompts/system.ts—source workflows/auth, skill discovery and precedence, product docs/preferences, permission and plan flow, MCP naming, OAuth/credential operations, rich content blocks,call_llm, browser tools, labels/status/session automation, document tools, and tool display metadata. - Custom agents: no repo-owned OpenWork custom-agent definitions were found. Preserve user-created Qwen agents and add a migration fixture, but do not invent a custom-agent porting scope unless one is identified.
- Skill storage: preserve
~/.qwen/skills,~/.agents/skills, project.qwen/skills/.agents/skills, and the OpenWork workspace-skill layer, with an explicit precedence and migration rule.
Settings surface (16 logical page IDs + searchable navigator): General, Appearance, App, AI, Input, Shortcuts, Memory, MCP Servers, Hooks, Extensions, Permissions, Labels, Messaging, Server, Workspace, and Preferences. QwenSettingsPage backs General/MCP/Hooks/Extensions; it is not an additional route. Hidden and feature-flagged pages still need explicit parity decisions.
Pages: ChatPage, DraftChatPage, PreferencesPage, ShortcutsPage, SkillInfoPage, SourceInfoPage.
Feature domains (per apps/electron/resources/docs/): automations, browser-tools, data-tables, html-preview, image-preview, labels, llm-tool, mermaid, pdf-preview, permissions, skills, sources, statuses, themes, tool-icons.
Doc tools (Python, stack-independent — needs new sidecar packaging): pdf_tool, xlsx_tool, docx_tool, pptx_tool, img_tool, ical_tool, doc_diff, markitdown_cli.
Packages: messaging-gateway + messaging-whatsapp-worker (upstream has 10 channels but no WhatsApp — consider rebuilding against packages/channels/base plugin API and upstreaming), session-tools-core, session-mcp-server, apps/viewer (transcript sharing).
Recent UI work — 17 features shipped in the last ~30 commits, highest regression risk because it is newest and least documented. Against current Web Shell, raw-Markdown copy, jump-to-latest, and prompt history are reusable; thinking selection and reduced motion are partial; the other 12 are absent: starter prompt suggestions (#73), interface zoom ⌘+/⌘-/⌘0 (#69), increase contrast (#67), chat text size (#65), conversation width (#63), live word/char count (#61), shortcuts search (#59), command palette recents (#57), thinking-menu shortcut (#55), prompt-history recall (#53), composer expand/collapse (#49), jump-to-latest (#47), copy-as-Markdown (#71), reduce motion (#51), thinking-level picker (#45), command palette ⌘K (#42), settings navigator search (#40).
Cross-cutting: i18n catalogs + lint:i18n:parity tooling (OpenWork ships 7 locales; Web Shell currently has English and zh-CN), Sentry wiring, voice, network-proxy.ts (Electron session proxy API — no Tauri equivalent), pets registry.
Retired by design: apps/electron main/preload/renderer (~119k LOC), packages/ui (30k), packages/server-core (35k), most of packages/shared's agent loop (95k).
6. Risks
| # | Risk | Severity | Mitigation |
|---|---|---|---|
| 1 | Product-shape divergence. Web Shell is coding-tool shaped (GitBranchIndicator, GitModePopover, BranchPickerPopover, artifacts/, agents/); OpenWork is knowledge-work shaped (Sources, Skills, Labels, Memory, Messaging). Pressure to add if (product === …) into shared code. |
High | Phase 3 seam before Phase 4 |
| 2 | Browser panes may be unportable (§3). | High | Spike in Phase 0 |
| 3 | Data/credential loss on upgrade. credentials.enc key derivation and incompatible session formats need fixture-based migration tests. |
High | Phase 5; treat as release blocker |
| 4 | Updater cross-contamination. | High (low likelihood, unrecoverable impact) | Generated config + release assertion |
| 4b | Inherited upstream defaults. The fork carries qwen-code's endpoint and telemetry plumbing: dashscope.aliyuncs.com / -intl / -us, coding.dashscope.aliyuncs.com/v1, gateway.alibaba-inc.com/dashscope/v1, a CLEARCUT path, and privacy.usageStatisticsEnabled handling. Some ModelStudio/DashScope routes may be intentional for OpenWork; the risk is shipping any endpoint, telemetry, auth, or privacy default without explicit ownership and disclosure. |
High | Full audit in Phase 2; release assertion against an approved endpoint/telemetry allowlist |
| 4c | Moving parity target (§3). Feature work continuing on the Electron app during migration grows the Phase 4 backlog continuously. | High | Freeze policy decided before Phase 4 |
| 5 | Toolchain collision. OpenWork is bun; qwen-code is npm, and prepare-runtime.js hard-requires it (npm_execpath is unavailable. Run through npm.). |
Medium | Decide convergence explicitly |
| 6 | Engine differences. WebView2 / WebKitGTK vs bundled Chromium. Web Shell leans on Shiki, KaTeX, mermaid, echarts, CodeMirror, and shadowDom.ts. |
Medium | Budget Linux QA or scope out of v1 |
| 7 | Release-train coupling. Web Shell is versioned in lockstep with the monorepo (0.21.6). | Medium | Document branching/cherry-pick policy |
| 8 | Merge debt accumulation. Upstream ships ~2 minors/month; conflict cost scales worse than linearly with drift. | Medium | Monthly cadence, never skip |
| 9 | Attribution. Craft-derived code moving between trees carries Apache-2.0 notice obligations; Craft ships TRADEMARK.md. |
Low | Legal pass in Phase 1 |
| 10 | Perf/size expectations set too high. | Low | Measure in Phase 0 before committing publicly |
7. Areas requiring special attention
- Never reformat or reorganize upstream files. A single prettier run across
packages/web-shellwould poison every future merge. - Additive files never conflict. Prefer new files over in-place edits; when editing upstream files, insert a call into our module rather than inlining logic.
- Upstream everything generic. Since the same team maintains both sides, a fix landed upstream permanently leaves our patch set — the single most effective conflict reducer available.
- The 95 renderer files touching
window.electronare the concrete Electron-coupling inventory;web-api.ts(277 lines) and the preload (421 lines) define the contract to re-implement. - Web Shell is 182,393 LOC — larger than OpenWork's current renderer (103,203). Porting to it is the bigger codebase absorbing the smaller.
- Audit every inherited default, not just visible branding. Model/API endpoints, auth flows, telemetry targets, crash reporting, update endpoints, and analytics all arrive preconfigured for Qwen Code. Branding is what you see; endpoints are what ships. Treat this as a checklist with an automated assertion, not a review pass.
- Security posture is inherited too. The daemon binds loopback with a per-launch bearer token, and Web Shell receives that token via URL fragment (
#token=, never sent to the server).tauri.conf.jsonships a restrictive CSP. Any OpenWork addition that widens the network surface — notably the messaging gateways and the WhatsApp worker — must be reviewed against that model rather than assuming it still holds. - Crash reporting needs rebuilding, not porting.
@sentry/electronhas no meaning post-migration; only@sentry/reactcarries over, and main-process crash capture needs a Tauri-native equivalent.
8. Upstream synchronization strategy
QwenLM/qwen-code main
│ fetch (never edit)
▼
vendor/upstream pristine mirror branch
│ merge at release tags only
▼
main OpenWork product branch
git remote add upstream https://github.com/QwenLM/qwen-code
git fetch upstream --tags
git checkout -b vendor/upstream upstream/main
# each sync:
git checkout vendor/upstream && git merge --ff-only v0.22.0
git checkout main && git merge vendor/upstream
npm install && git add package-lock.json
- The pristine branch provides a stable three-way merge base and separates "what changed upstream" from "what we changed" — the question actually asked during conflict resolution.
- Merge release tags, not
main. Bounded, tested diffs. - Monthly cadence. Three minors behind costs far more than three times one minor.
PATCHES.mdledger so that, months later, a resolver knows whether a divergent line was deliberate.
9. Validation & acceptance criteria
Feature parity (blocking):
- Parity matrix complete: every item in §5 marked ported / rebuilt / consciously dropped, with sign-off on each drop.
- All 16 logical settings pages and searchable navigation reachable with equivalent function.
- OpenWork-owned overlay in §5 verified: branding/distribution, all 3 curated marketplace entries, agent/product behavior, repository skills, and skill/agent storage.
- All 17 recent UI features (§5) verified by hand — newest code, weakest coverage.
- Doc tools pass existing smoke suites (
test:doc-tools). - i18n parity lint passes across merged catalogs; no untranslated regressions.
Data integrity (blocking):
- Upgrade from latest Electron build preserves settings, session history, and credentials with zero re-authentication.
- Rollback path validated.
Release safety (blocking):
- Automated assertion: shipped identifier + updater endpoint are OpenWork's, on every platform artifact.
- Automated assertion: only explicitly approved and disclosed OpenWork/ModelStudio endpoints are reachable by default; no unintended upstream telemetry, CLEARCUT, updater, or auth endpoint ships; telemetry and usage-statistics defaults match OpenWork's stated privacy policy.
- Crash reporting verified end-to-end from both the Rust shell and the web layer.
- Signing and notarization green on macOS and Windows.
Platform:
- macOS, Windows verified. Linux verified or explicitly descoped with a written decision.
Performance (measured, not assumed):
- Bundle size and cold-start vs current Electron build, recorded before/after.
Sync process:
- One full practice merge from an upstream release tag completed, conflicts resolved, time recorded as the cadence baseline.
10. Sub-issue breakdown
- Runtime bump
0.15.11→0.21.6+ ACP validation (Phase 0, independent value) - Browser-pane / CDP portability spike (Phase 0, potential blocker)
- Browser-context audit via
server:prod(Phase 0) - Baseline bundle/perf measurement (Phase 0)
- Repo foundation + CI neutralization (Phase 1)
- Shell fork, branding, updater safety (Phase 2)
- Web Shell variant seam (Phase 3 — gates Phase 4)
- Feature port: settings surface (Phase 4)
- Feature port: Sources / Skills / Labels / Memory (Phase 4)
- Feature port: doc tools + sidecar packaging (Phase 4)
- Feature port: messaging + WhatsApp as channel plugin (Phase 4)
- Data migration: settings, sessions, credentials (Phase 5)
- Inherited-defaults audit: endpoints, telemetry, auth, crash reporting (Phase 2, release blocker)
- Platform QA + release pipeline (Phase 6)
- OpenWork-owned overlay: curated marketplace, agent/product behavior, repository skills, and skill/agent storage (Phases 2–5, release blocker)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Phase 0 checklist and inspect apps/electron/src/main/browser-cdp.ts, browser-pane-manager.ts, packages/shared/src/agent/browser-tools.ts, and scripts/vendor-qwen-code.ts. Run bun run server:prod and build desktop-shell unmodified to establish the Electron coupling and platform baseline. Done requires resolving the listed architectural questions and splitting the migration into scoped sub-issues.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, github-actions, node.js, python, rust, typescript
- Domain
- ai, build-system, desktop, devtools, release
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 15/100