The open-source alternative to Claude Cowork (powered by opencode)
| Files | Last commit | Last update |
|---|---|---|
feat(den): redesign AI Gateway before GA (#5210) * feat(den): present AI Gateway as one provider form Admins could not tell what Gateway was for. Collapse provider, key, audience, and models into the Paper add/edit journey without changing the API. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(den): drop the Gateway matrix screens and share the model-access policy The one-form editor made the detail page, access matrix, and /edit route dead. The BYOK page and the Gateway sheet now save the desktop policy through one module. Co-authored-by: Cursor <cursoragent@cursor.com> * test(evals): boot the AI Gateway proof world and record witnesses GATEWAY_ENABLED=true made den-api require proxy URLs, so the world never booted. Point them at a closed loopback port, declare the web/den resources, and record one evidence line per step including the teammate's 403. Co-authored-by: Cursor <cursoragent@cursor.com> * test(evals): let the AI Gateway world run on Daytona Grant the dashboard capability through the platform-admin API instead of MySQL so the world no longer needs local placement, and cap Next build workers in the sandbox: it spawned one per host CPU and was OOM killed. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(evals): run AI Gateway worlds on Daytona Den provisioning learns GATEWAY_ENABLED: it starts ee/apps/gateway beside Den in the sandbox and hands back its preview URL. hosts gains startScriptOnSandbox so a witness upstream can run on the sandbox loopback. acme-web boots Den + gateway + upstream on either placement; the OpenWork web runtime stays local. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(den-db): spell shared locks so MariaDB parses them Drizzle's .for("share") emits FOR SHARE, which MariaDB rejects with ER_PARSE_ERROR; every gateway usage read and invite accept then 500s on the Daytona server snapshot. Rewrite the trailing clause to LOCK IN SHARE MODE on the pool and pooled connections. Also let seedProfile demo-org allowlist the seeded owner on Daytona, and cap Next build workers at 4 since nproc reports host CPUs. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(den-db): match the share-lock suffix without backtracking Co-authored-by: Cursor <cursoragent@cursor.com> * feat(den): match the AI Gateway boards List: flat header with Add provider in the top bar, a policy row with its state chip, provider rows with models / audience / status columns, Usage and Spending limits as link rows, and the BYOK migration line. Catalog: taglines, OpenRouter first, Show more, Another provider. Form: Key / Who can use it / Models cards with the Everyone toggle, Add person and Add team, and two model radios. Sheet: nested admin checkbox, free starter toggle, effect note. Usage and limits move to gateway-providers/usage. acme-web on Daytona also starts the OpenWork web runtime on a private sandbox proxying this Den. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(den-web): mount the header action slot on every dashboard page AI Gateway portals its Add provider button into the top bar; the slot only existed on the Library route. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(den): put Add provider by the Providers heading and start model picks empty The top-bar action was hard to find; the button now sits beside the list it adds to. Choosing only some models starts from none, with Select all and Clear and a count. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(worlds): sign the Daytona acme-web runtime into Den so gateway models reach the picker The browser holds only the client token; den-session and provider sync are host-token routes. The launcher now pushes the seeded owner's Den session and runs one sync, matching the local world. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(worlds): carry the sync body as base64 through the sandbox exec Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> | 13 days ago | |
ci(warden): approve PRs with no high/medium security or confidentiality findings (#5662) | 15 hours ago | |
feat(worlds): preview-workbot, Workbot with Den sign-in and the headless runner (#5649) * feat(worlds): preview-workbot, Workbot with Den sign-in and the headless runner A new world that runs Workbot the way production does: the seeded Acme Den (where people sign in), the headless runner (where the conversation runs) and the Workbot app, with Workbot and headless Automations on for Acme. The model is the deterministic Acme upstream and the runner's computer is off, so no paid or Freestyle key enters the world. Before reporting ready, every placement signs in to Workbot through Den's OAuth server as alex@acme.test, sends a message and checks the runner's answer. - local: Den on the local MySQL/Redis, the runner and Workbot as processes. - daytona: Den in its own sandbox (as preview-den); the runner, Workbot and the fake model in one private sandbox at the same commit, with signed URLs minted before Den starts so Den knows where Workbot lives. - freestyle: a new `workbot` VM kind (MySQL/Redis only, Workbot prebuilt) booting the local world under template origins. Two gateway fixes make an OAuth round trip through the edge work: query strings are translated back to template origins like headers and bodies (Den compares and signs redirect_uri), and a launch link can chain to a sibling service (`then=workbot`) so one link authorizes both hosts the flow crosses. A loopback preload lets Workbot call Den's sign-in at its template origin. The CLI pins the Daytona source like acme-web; the catalog, README and the preview skill describe the world. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(worlds): Den's sign-in pages through the preview edge, and patient private sandbox checks - The preview gateway sent every /mcp path on Den's host to Den's API, so /mcp/select-organization and /mcp/consent (Den web pages an OAuth sign-in passes through) answered {"error":"not_found"}. They now reach Den web. - privateSandboxId retries `daytona info` for about half a minute: a sandbox from a large snapshot can briefly answer with an error right after create. Its final error now carries Daytona's message instead of hiding it. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(worlds): verify Workbot's private Daytona URLs with Workbot's own checks verifyPrivateWebPreview checks the web app's Vite client, its WebSocket token and /api/openwork; Workbot has none of them. preview-workbot now checks what matters for it: without the signed token Daytona refuses Workbot's page, API and health and the runner's health; with it Workbot serves its page and both answer. Scripted requests skip Daytona's browser warning page. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 15 hours ago | |
ci(warden): approve PRs with no high/medium security or confidentiality findings (#5662) | 15 hours ago | |
Licensing v0.5.1: Enterprise Features scoping, stewardship page, DCO, machine-readable licensing (#4076) * Licensing v0.5.1: Enterprise Features scoping, 30-day evaluation, 1-year MIT conversion, stewardship, DCO License (ee/LICENSE; applies to future releases only, never retroactive): - Clause (a): free <=5-user production now excludes the Enterprise Features (as defined in the Subscription Terms) - Clause (b): internal evaluation tuned to thirty (30) days from first deployment, any user count, extendable in writing - Grant of Future License: each version converts to MIT on the first anniversary of its public release (was second) - strictly more customer-favorable than any prior release promised - New clause: client-side-served assets (images, fonts, CSS, compiled client-side JavaScript) are MIT Subscription Terms: new Section 4 defines Enterprise Features (SSO/SAML + SCIM, usage and adoption analytics, desktop policies and version controls, white-labeling); included in Enterprise, evaluations, and pilots per order; list changes are prospective and never shrink a paid tier mid-term. Pricing surfaces (landing + docs): analytics moves from the Team card to Enterprise, matching the entitlement gating already in the platform; white-labeling and support lines added; footnotes updated for the new grant boundaries. New surfaces: docs Stewardship page (released versions keep their license, MIT core stays MIT, one-year MIT conversion, paid tiers never shrink mid-term, trademark / white-labeling policy); REUSE.toml + LICENSES/ for machine-readable license scanning; CONTRIBUTING.md requiring DCO sign-off and signed IP agreements for paid or trial engagements before substantive contributions. Proof: landing next build clean (/pricing and /terms/subscription prerender); docs.json parses; no stale 90-day or two-year references (grep-verified); remaining files are inert license/docs content. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Add contributor license agreements for ee/ contributions Mirror the GitLab contribution structure: DCO everywhere, plus an Individual or Corporate CLA for contributions under ee/. The CLA texts are adapted from the Google Open Source Programs Office agreements (CC BY 3.0, the same source GitLab uses), with one clarification added: the copyright grant expressly covers distribution under the licenses applied to the Work, including the EE License's scheduled MIT conversion. Contributors retain ownership; the sublicensable grant is what makes external ee/ contributions legally safe to accept rather than rejecting them outright. CONTRIBUTING.md updated to state the tri-split and that ee/ pull requests are not merged until the applicable CLA is in place. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Clarify enterprise feature and white-label terms Signed-off-by: Benjamin Shafii <ben@prologe.io> * Set EE conversion window to two years Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> | 1 month ago | |
fix(app): match the command palette's model rows to the design (#5672) * fix(app): space the command palette's model rows and show its keys per platform Palette rows get a 10px gap between the provider mark, the name, the source mark and the hint, so the Models list no longer runs icons into text. Hints keep normal letter spacing. Next pinned model and Cycle model source format their chords for the platform: ⌃⇧M and ⌃⌥M on macOS, as Settings shows them, and Ctrl+Shift+M and Ctrl+Alt+M elsewhere. * test(evals): prove the command palette's model actions end to end The spec types "model" in the palette and checks the Models row names the current model and both actions show their keys, opens Models and checks every row keeps its provider mark apart from its name with the current model marked, switches models from the palette, and runs Next pinned model from the palette and from Control+Shift+M. | 12 hours ago | |
docs(changelog): release notes for v0.18.56 (#5598) Co-authored-by: benjaminshafii <11430621+benjaminshafii@users.noreply.github.com> | 3 days ago | |
feat(auto): offer free Auto to every organization by default (#5670) * feat(auto): offer free Auto to every organization by default Remove the per-organization free Auto rollout. Den and the Gateway no longer check metadata.inferenceFree.rolloutEnabled or INFERENCE_FREE_ROLLOUT_ALL_ORGS for status, credential issuance or member requests, so every eligible organization gets free Auto whenever INFERENCE_FREE_ENABLED is on. Organization opt-outs, DPA, desktop policy and the weekly allowance still apply. Drop the /admin rollout switch, its PATCH route and audit action, the reserved-metadata guard, and enrollment from the free Auto usage report. Regenerate OpenAPI and the SDK. Replace the rollout spec with one that shows two new organizations get Auto with no admin action. * test(auto): cover the free Auto usage page without enrollment | 13 hours ago | |
feat(den): add member API key enrollment (#5645) * feat(mcp): support per-member API keys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): cover member key error paths Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * feat(den): add member API key enrollment Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): exercise member key scheme controls Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(den): clarify personal key dialog guidance Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): preserve live personal key status coverage Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): use retained DOM harness for member keys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): observe personal keys across Den boundaries Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(mcp): canonicalize non-secret identity transport Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): prove member key recovery through real web journeys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * fix(den): bound member key saves while preserving cancellation Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(den): keep personal keys a member still reaches after a team edit Losing one team association deleted every personal API key the member held in the organization. Delete only keys whose connection no longer has an org-wide, member or current-team grant for that member. Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * ci: retrigger checks after org membership update Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * ci: retrigger checks after org membership update Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * refactor(mcp): reuse one reach rule and route shape for personal keys Review changes on top of the per-member API key work: - One reach rule (memberApiKeyReachesMember: active member + direct grant) shared by enrollment, use and pruning instead of three copies. - Authorize once per operation; each request only re-reads the stored key (memberApiKeyStillCurrent), keeping the in-flight replace/reject guarantee without re-listing usable connections on every HTTP request. - One usability predicate (memberApiKeyUsable) and one member credential gate in external-capabilities instead of six inline copies. - Pruning lives in external-mcp-connections (pruneUnreachableMemberApiKeys). Team edits call it; access edits now remove only the keys of members who lost access instead of every member's key. - Route matches PUT /v1/llm-providers/:id/my-credential: PUT /v1/mcp-connections/:id/my-credential (still hidden from OpenAPI). - Desktop bridge types move to the desktop PR; evals declare zod instead of importing it from apps/app/node_modules. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore: merge feat/member-personal-keys; enroll keys through PUT my-credential Signed-off-by: Benjamin Shafii <ben@prologe.io> * refactor(den-web): save member keys with a plain useMutation Replace the bespoke boundary/generation save machinery with the same useMutation shape as neighbouring connection hooks. The dialog form is keyed by connection and closes on an organization switch, so a late result cannot land in another dialog. gcTime 0 plus reset() keep the key out of mutation state after the form closes; server text is never shown. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(den-web): align member key UI with DESIGN.md P1/P2: dialog, ready header and toast keep a title without explanatory copy. P3/C3: the Authorization scheme moves behind a collapsed Advanced row in setup and settings. C2/V2: a rejected key reads 'Key rejected' with warning tone, not the action label in a success badge. S2: a saved key row keeps one action (Replace key); Remove key lives in the row menu with the shared confirm. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> Co-authored-by: diff-warden[bot] <311721145+diff-warden[bot]@users.noreply.github.com> Co-authored-by: Benjamin Shafii <ben@prologe.io> | 12 hours ago | |
feat(den): add member API key enrollment (#5645) * feat(mcp): support per-member API keys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): cover member key error paths Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * feat(den): add member API key enrollment Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): exercise member key scheme controls Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(den): clarify personal key dialog guidance Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): preserve live personal key status coverage Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): use retained DOM harness for member keys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): observe personal keys across Den boundaries Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(mcp): canonicalize non-secret identity transport Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(den): prove member key recovery through real web journeys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * fix(den): bound member key saves while preserving cancellation Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(den): keep personal keys a member still reaches after a team edit Losing one team association deleted every personal API key the member held in the organization. Delete only keys whose connection no longer has an org-wide, member or current-team grant for that member. Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * ci: retrigger checks after org membership update Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * ci: retrigger checks after org membership update Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * refactor(mcp): reuse one reach rule and route shape for personal keys Review changes on top of the per-member API key work: - One reach rule (memberApiKeyReachesMember: active member + direct grant) shared by enrollment, use and pruning instead of three copies. - Authorize once per operation; each request only re-reads the stored key (memberApiKeyStillCurrent), keeping the in-flight replace/reject guarantee without re-listing usable connections on every HTTP request. - One usability predicate (memberApiKeyUsable) and one member credential gate in external-capabilities instead of six inline copies. - Pruning lives in external-mcp-connections (pruneUnreachableMemberApiKeys). Team edits call it; access edits now remove only the keys of members who lost access instead of every member's key. - Route matches PUT /v1/llm-providers/:id/my-credential: PUT /v1/mcp-connections/:id/my-credential (still hidden from OpenAPI). - Desktop bridge types move to the desktop PR; evals declare zod instead of importing it from apps/app/node_modules. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore: merge feat/member-personal-keys; enroll keys through PUT my-credential Signed-off-by: Benjamin Shafii <ben@prologe.io> * refactor(den-web): save member keys with a plain useMutation Replace the bespoke boundary/generation save machinery with the same useMutation shape as neighbouring connection hooks. The dialog form is keyed by connection and closes on an organization switch, so a late result cannot land in another dialog. gcTime 0 plus reset() keep the key out of mutation state after the form closes; server text is never shown. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(den-web): align member key UI with DESIGN.md P1/P2: dialog, ready header and toast keep a title without explanatory copy. P3/C3: the Authorization scheme moves behind a collapsed Advanced row in setup and settings. C2/V2: a rejected key reads 'Key rejected' with warning tone, not the action label in a success badge. S2: a saved key row keeps one action (Replace key); Remove key lives in the row menu with the shared confirm. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> Co-authored-by: diff-warden[bot] <311721145+diff-warden[bot]@users.noreply.github.com> Co-authored-by: Benjamin Shafii <ben@prologe.io> | 12 hours ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
chore(den): remove Polar billing, inert flags and unreachable code (#5648) * chore(den): remove Polar billing, inert flags and unreachable code Den API: - Remove Polar billing. The feature gate defaulted off, which always allowed cloud workers; worker creation now behaves that way unconditionally. Delete billing/polar.ts, POLAR_* env and helm values, the /v1/workers/billing* 410 stubs, requireCloudWorkerAccess, the 402 response on POST /v1/workers and the Polar-only Loops event. The org billing response keeps a fixed gate-off `polar` object because Den web's onboarding flow still reads it; Den web's Polar section, which could no longer render, is removed. - Delete the inert DEN_MCP_CONNECTIONS_GATING_ENABLED and DEN_INSTALL_LINKS_GATING_ENABLED flags and the ignored `gatingEnabled` option at every call site, in helm, terraform and docs. - Remove the deprecated render_dynamic_artifact tool alias. - Delete isolateExternalMcpOAuthCallback, which had no callers. - Regenerate the API contract (the 402 worker response is gone). Desktop app and browser (unreachable code only; the browser, artifacts and their agent actions are unchanged): - Unused attachBrowserView options, BACKGROUND_TAB_PRESENCE_BOUNDS, the never-produced plugin-loaded enablement and chrome-devtools detection. - The auto-open target picker that always returned null and its plumbing, syncArtifactTargets, the unused artifact list/download client calls, and the desktop billing client that called the deleted routes. - Two unused doc images and stale browser docs and comments. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> Co-authored-by: diff-warden[bot] <311721145+diff-warden[bot]@users.noreply.github.com> | 16 hours ago | |
feat(mcp): add list_skills and get_skill to the agent gateway (#5208) * feat(mcp): add list_skills and get_skill to the agent gateway Skills on /mcp/agent were only reachable through search_capabilities (type skills) followed by execute_capability on an opaque plugin:/skill: name, so agents guessed keywords or skipped org skills entirely. Register two read-only tools next to the routing pair: list_skills returns every built-in and marketplace skill the member may use (optional query and limit), and get_skill returns one skill's SKILL.md by name or exact capability with the same framing as the skill:// resource. The catalog is resolved once per request and only by the methods that need it. The server instructions and tool descriptions point at the direct path; search and execute keep working for skills. Desktop: the agent prompt, on-demand steering, remote skill catalog instruction, and the composer connect-skill prompt now name get_skill first with execute_capability as the fallback for gateways that do not expose it. The routing sentence is a shared constant so the v2 native-skill swap can no longer drift from it. Analytics and error attribution count the new tools as Cloud skill tools. The evals mock Cloud advertises and serves both tools; Connect docs describe them. Linear: ENG-244 * test(evals): prove list_skills and get_skill on the skill access journey Extend the grant-native skill boundary journey so the creator lists the new skill and reads it by capability and by name through the direct tools, the denied member neither lists it nor reads it (unknown_skill), and sharing the plugin makes both work. Same Den, same tokens, same framing as the skill:// resource. Linear: ENG-244 * test(evals): desktop agent reads a Cloud skill through list_skills and get_skill The preseeded Connect journey is where the desktop agent uses an assigned organization skill. Its scripted model now takes the path the desktop prompt recommends: list_skills narrows the catalog to the assigned skill, get_skill reads it by the returned capability, and the transcript shows the skill's unique phrase. The journey also proves the direct tools over the gateway next to the existing search/execute check, and captures the composer before the prompt and the answer after it. The mock model server learns a "skill-list" argument source that hands the single listed capability to the next tool, mirroring "capability-search". Linear: ENG-244 * fix(evals): route /api/den through the local fault proxy without a cross-origin redirect On a split local Den, den-web answers /api/den/* with a 307 to den-api on another origin, and fetch drops Authorization on cross-origin redirects, so every bearer call through the fault proxy came back 401 and proxy-shaped desktop journeys could not even sign the desktop in (local lane and the CI proof lane alike). The local proxy now sends /api/den/* straight to den-api with the prefix stripped, keeping the original path in its request log so token-path assertions are unchanged. A single-origin Den, as the Daytona remote and the existing unit test present, is forwarded as before. Linear: ENG-244 | 14 days ago | |
Licensing v0.5.1: Enterprise Features scoping, stewardship page, DCO, machine-readable licensing (#4076) * Licensing v0.5.1: Enterprise Features scoping, 30-day evaluation, 1-year MIT conversion, stewardship, DCO License (ee/LICENSE; applies to future releases only, never retroactive): - Clause (a): free <=5-user production now excludes the Enterprise Features (as defined in the Subscription Terms) - Clause (b): internal evaluation tuned to thirty (30) days from first deployment, any user count, extendable in writing - Grant of Future License: each version converts to MIT on the first anniversary of its public release (was second) - strictly more customer-favorable than any prior release promised - New clause: client-side-served assets (images, fonts, CSS, compiled client-side JavaScript) are MIT Subscription Terms: new Section 4 defines Enterprise Features (SSO/SAML + SCIM, usage and adoption analytics, desktop policies and version controls, white-labeling); included in Enterprise, evaluations, and pilots per order; list changes are prospective and never shrink a paid tier mid-term. Pricing surfaces (landing + docs): analytics moves from the Team card to Enterprise, matching the entitlement gating already in the platform; white-labeling and support lines added; footnotes updated for the new grant boundaries. New surfaces: docs Stewardship page (released versions keep their license, MIT core stays MIT, one-year MIT conversion, paid tiers never shrink mid-term, trademark / white-labeling policy); REUSE.toml + LICENSES/ for machine-readable license scanning; CONTRIBUTING.md requiring DCO sign-off and signed IP agreements for paid or trial engagements before substantive contributions. Proof: landing next build clean (/pricing and /terms/subscription prerender); docs.json parses; no stale 90-day or two-year references (grep-verified); remaining files are inert license/docs content. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Add contributor license agreements for ee/ contributions Mirror the GitLab contribution structure: DCO everywhere, plus an Individual or Corporate CLA for contributions under ee/. The CLA texts are adapted from the Google Open Source Programs Office agreements (CC BY 3.0, the same source GitLab uses), with one clarification added: the copyright grant expressly covers distribution under the licenses applied to the Work, including the EE License's scheduled MIT conversion. Contributors retain ownership; the sublicensable grant is what makes external ee/ contributions legally safe to accept rather than rejecting them outright. CONTRIBUTING.md updated to state the tri-split and that ee/ pull requests are not merged until the applicable CLA is in place. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Clarify enterprise feature and white-label terms Signed-off-by: Benjamin Shafii <ben@prologe.io> * Set EE conversion window to two years Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> | 1 month ago | |
feat(mcp): support per-member API keys (#5643) * feat(mcp): support per-member API keys Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): cover member key error paths Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * test(mcp): observe personal keys across Den boundaries Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * fix(mcp): canonicalize non-secret identity transport Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * chore: regenerate the Den API contract Regenerated with 'pnpm den:contract' by the den-contract check after merging dev. * fix(den): keep personal keys a member still reaches after a team edit Losing one team association deleted every personal API key the member held in the organization. Delete only keys whose connection no longer has an org-wide, member or current-team grant for that member. Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * ci: retrigger checks after org membership update Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> * refactor(mcp): reuse one reach rule and route shape for personal keys Review changes on top of the per-member API key work: - One reach rule (memberApiKeyReachesMember: active member + direct grant) shared by enrollment, use and pruning instead of three copies. - Authorize once per operation; each request only re-reads the stored key (memberApiKeyStillCurrent), keeping the in-flight replace/reject guarantee without re-listing usable connections on every HTTP request. - One usability predicate (memberApiKeyUsable) and one member credential gate in external-capabilities instead of six inline copies. - Pruning lives in external-mcp-connections (pruneUnreachableMemberApiKeys). Team edits call it; access edits now remove only the keys of members who lost access instead of every member's key. - Route matches PUT /v1/llm-providers/:id/my-credential: PUT /v1/mcp-connections/:id/my-credential (still hidden from OpenAPI). - Desktop bridge types move to the desktop PR; evals declare zod instead of importing it from apps/app/node_modules. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Guillaume Loret <guillaume@openworklabs.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> Co-authored-by: diff-warden[bot] <311721145+diff-warden[bot]@users.noreply.github.com> Co-authored-by: Benjamin Shafii <ben@prologe.io> | 13 hours ago | |
fix(docker): copy headless-protocol into the Den API image (#5666) den-api depends on @openwork-ee/headless-protocol since #5636, but Dockerfile.den never copied it, so the image build failed with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. | 13 hours ago | |
fix(den-api): accept CIMD clients that list unsupported grant types (#5467) Claude's Client ID Metadata Document lists urn:ietf:params:oauth:grant-type:jwt-bearer next to authorization_code and refresh_token. @better-auth/cimd rejected the whole document, so connecting Claude to the MCP server failed with invalid_client since #5293 started advertising CIMD support. Patch the plugin to keep only the supported grant types before validation. Signed-off-by: Benjamin Shafii <ben@prologe.io> | 6 days ago | |
Revert "fix(server): report headless prompt acceptance honestly (#4950)" (#5111) This reverts commit ae4b8d24718e46f68689daa38f045524a1c1d986. | 18 days ago | |
fix(deps): remediate all current Dependabot security alerts (#4675) * fix(deps): remediate September Dependabot security alerts * fix(diagnostics): keep Bun tests outside the Next app typecheck | 26 days ago | |
ci: remove the bot that pushes Den contract merges to PR branches (#5659) Den Contract Conflicts and Den Contract Regenerated (#5488) pushed diff-warden[bot] merge and regenerate commits onto open PR branches. Remove both workflows, their classifier script, and the drift-artifact upload in the den-contract check that only fed them. The den-contract check still fails on drift; authors merge dev and run pnpm den:contract themselves. | 15 hours ago | |
chore: remove the openwork-orchestrator (#3181) * chore: remove the openwork-orchestrator The orchestrator was the last thing standing between OpenWork and a single server binary. Cloud already ran without it; this removes it everywhere else and deletes the package. Render workers are now purely openwork-server: the build installs the published npm package instead of the orchestrator, the start command drops every orchestrator-only flag (--remote-access becomes --host 0.0.0.0, --opencode-source/--opencode-bin become OPENWORK_MANAGE_OPENCODE and OPENWORK_OPENCODE_BIN, --allow-external and --connect-host simply go), and it resolves the extension plugins from the installed package - failing loudly if they are absent rather than booting into the silent extensions_plugin_missing state Cloud just hit. install-opencode.mjs reads the pinned OpenCode version from the repo's own constants.json instead of from the globally installed orchestrator. The desktop stops shipping it. It was compiling, signing, notarizing and shipping eight orchestrator binaries per release that nothing ever ran: engineStart has always hardcoded the direct runtime, and both startOrchestratorRuntime and startDirectRuntime were defined and never called. The dead runtime paths, the detached-start IPC and its types, and the sidecar packaging entries are gone; boot now calls engineStart directly instead of a vestigial orchestratorWorkspaceActivate that only delegated to it. The single-container and microsandbox images run openwork-server directly, with the plugins shipped and OPENWORK_EXTENSIONS_PLUGIN_DIR set. Their data directory keeps its historical name on purpose - existing deployments have volumes mounted there and renaming it would silently orphan their state. That is a directory name only; the CLI is gone. The release pipeline loses the sidecar job and the npm publish, and verify-tag, review and bump-version no longer read a package.json that no longer exists - they would have thrown on every release. Legacy cleanup is deliberately preserved: users still have ~/.openwork/openwork-orchestrator on disk, so nuke keeps wiping it and the tests keep asserting that. Verified: apps/orchestrator deleted and the lockfile updated; app, server, desktop-electron, den-api and den-web typechecks clean; 53 desktop electron tests, 20 den-api cloud tests, 20 server db/env tests and the stats tests pass; i18n audit passes across all locales; release review and verify-tag run clean without the folder; every changed YAML parses. The server suite is 1083 pass / 7 fail, byte-identical to the pre-existing baseline on clean dev (reload watcher/event timing plus one suite-order plugin test). The landing prebuild parity check also fails identically on clean dev - unrelated. * test(evals): make the cloud e2e sign-in frame state-independent Frame 2 asserted the org-chooser screen, which only appears when a workspace or server is already configured in the browser profile. It passed on the dirty profiles I had been reusing and failed on a clean one - the app legitimately lands on the first-run welcome screen instead. It now proves what the frame actually claims: Den accepts the signed-in admin session (node-side /v1/me, asserting the email matches), the browser carries it, and the forced sign-in gate is no longer blocking. The wait is a positive one for any settled post-gate surface, because a negative wait passes on the empty DOM right after reload and the capture then lands on the "checking" flash where app-root re-renders the gate. Two runner behaviors worth recording: ctx.eval does not await promises, so an async IIFE serializes to {} - probes belong node-side; and the screenshot validator sees a later moment than the assertions, so transient states must be waited out, not asserted around. * fix(scripts): let the Daytona snapshot build run on arm64 The snapshot image is always linux/amd64 because that is what Daytona runs, but the Dockerfile's runtime asserts execute the installed binaries. On an arm64 machine those x86_64 binaries run under emulation and die with "Illegal instruction (core dumped)", so the script could never build a snapshot on Apple Silicon - it failed at the final assert stage every time. Default RUNTIME_ASSERTS to 0 when the host is not x86_64, which is exactly the cross-build path the Dockerfile already documents as "binaries must be verified on target hardware". x86_64 hosts and CI keep asserting as before, and the chosen value is echoed so a cross-build is obvious in the log. | 2 months ago | |
feat(worlds): preview-workbot, Workbot with Den sign-in and the headless runner (#5649) * feat(worlds): preview-workbot, Workbot with Den sign-in and the headless runner A new world that runs Workbot the way production does: the seeded Acme Den (where people sign in), the headless runner (where the conversation runs) and the Workbot app, with Workbot and headless Automations on for Acme. The model is the deterministic Acme upstream and the runner's computer is off, so no paid or Freestyle key enters the world. Before reporting ready, every placement signs in to Workbot through Den's OAuth server as alex@acme.test, sends a message and checks the runner's answer. - local: Den on the local MySQL/Redis, the runner and Workbot as processes. - daytona: Den in its own sandbox (as preview-den); the runner, Workbot and the fake model in one private sandbox at the same commit, with signed URLs minted before Den starts so Den knows where Workbot lives. - freestyle: a new `workbot` VM kind (MySQL/Redis only, Workbot prebuilt) booting the local world under template origins. Two gateway fixes make an OAuth round trip through the edge work: query strings are translated back to template origins like headers and bodies (Den compares and signs redirect_uri), and a launch link can chain to a sibling service (`then=workbot`) so one link authorizes both hosts the flow crosses. A loopback preload lets Workbot call Den's sign-in at its template origin. The CLI pins the Daytona source like acme-web; the catalog, README and the preview skill describe the world. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(worlds): Den's sign-in pages through the preview edge, and patient private sandbox checks - The preview gateway sent every /mcp path on Den's host to Den's API, so /mcp/select-organization and /mcp/consent (Den web pages an OAuth sign-in passes through) answered {"error":"not_found"}. They now reach Den web. - privateSandboxId retries `daytona info` for about half a minute: a sandbox from a large snapshot can briefly answer with an error right after create. Its final error now carries Daytona's message instead of hiding it. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(worlds): verify Workbot's private Daytona URLs with Workbot's own checks verifyPrivateWebPreview checks the web app's Vite client, its WebSocket token and /api/openwork; Workbot has none of them. preview-workbot now checks what matters for it: without the signed token Daytona refuses Workbot's page, API and health and the runner's health; with it Workbot serves its page and both answer. Scripted requests skip Daytona's browser warning page. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 15 hours ago | |
feat(den): add daytona-backed docker dev flow (#918) * feat(den): add daytona-backed docker dev flow * fix(den): allow multiple cloud workers in dev * fix(den): use Daytona snapshots for sandbox runtime Use a prebuilt Daytona snapshot for the dev worker runtime so sandboxes start with openwork and opencode already installed. Pass the snapshot through the local Docker flow and add a helper to build the snapshot image for repeatable setup. * chore(den): lower Daytona snapshot defaults Reduce the default snapshot footprint to 1 CPU and 2GB RAM so local Daytona worker testing fits smaller org limits more easily. * Omar is comfortable Make Daytona-backed cloud workers stable enough to reconnect through a dedicated proxy instead of persisting expiring signed preview URLs. Split the proxy into its own deployable service, share Den schema access through a common package, and fix the web badge so healthy workers show ready. * chore(den-db): add Drizzle package scripts Move the shared schema package toward owning its own migration workflow by adding generate and migrate commands plus a local Drizzle config. * chore: update lockfile Refresh the workspace lockfile so the new den-db Drizzle tooling is captured in pnpm-lock.yaml. * feat(den-worker-proxy): make Vercel deployment-ready Align the proxy service with Vercel's Hono runtime entry pattern and keep a separate Node server entry for Docker/local runs. Also scaffold the Vercel project/env setup and wire Render deploy sync to pass Daytona variables needed for daytona mode. * feat(den-db): add db mode switch for PlanetScale Support DB_MODE=planetscale with Drizzle's PlanetScale serverless driver while keeping mysql2 as the local default. This lets Vercel-hosted services use HTTP database access without changing local development workflows. * refactor(den-db): adopt shared TypeID ids Move the Den TypeID system into a shared utils package and use it across auth, org, worker, and sandbox records so fresh databases get one consistent internal ID format. Wire Better Auth into the same generator and update Den request boundaries to normalize typed ids cleanly. * fix(den): restore docker dev stack after refactor Include the shared utils package in the Den Docker images, expose MySQL to the host for local inspection, and fix the remaining Den build/runtime issues surfaced by the Docker path after the shared package and TypeID changes. * docs(den): document Daytona snapshot setup Add README guidance for building and publishing the prebuilt Daytona runtime snapshot, including the helper script, required env, and how to point Den at the snapshot for local Daytona mode. * refactor(den-db): reset migrations and load env files Replace the old Den SQL migration history with a fresh baseline for the current schema, and let Drizzle commands load database credentials from env files. Default to mysql when DATABASE_URL is present and otherwise use PlanetScale credentials so local Docker and hosted environments can share the same DB package cleanly. * fix(den): prepare manual PlanetScale deploys Update the Render workflow and Docker build path for the shared workspace packages, support PlanetScale credentials in the manual SQL migration runner, and stop auto-running DB migrations on Den startup so schema changes stay manual. * feat(den-v2): add Daytona-first control plane Create a new den-v2 service from the current Daytona-enabled control plane, default it to Daytona provisioning, and add a dedicated Render deployment workflow targeting the new v2 Render service. * feat(den-worker-proxy): redirect root to landing Send root proxy traffic to openworklabs.com so direct visits to the worker proxy domain do not hit worker-resolution errors. --------- Co-authored-by: OmarMcAdam <gh@mcadam.io> | 6 months ago | |
Add Den Redis session cache (#3679) * Add Den Redis session cache * Harden Redis cache invalidation * Redact Redis cache credentials in logs | 1 month ago | |
fix(den-db): make pending Gateway preflight compatible with Vitess (#4896) | 24 days ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
chore: add Infisical project config (#959) | 6 months ago | |
fix(ci): prepare workflows for Node 24 rollout (#1445) Co-authored-by: src-opn <src-opn@users.noreply.github.com> | 5 months ago | |
feat(den-api): cursor pagination for the three limit-only lists (additive) (#4707) * feat(den-api): cursor pagination for GET /v1/workflow-runs Additive: optional cursor query parameter and nextCursor response field. Keyset paging on (created_at desc, id desc) via a shared opaque base64url cursor helper (src/list-pagination.ts); no-cursor calls keep the same default limit and ordering. Invalid cursors are a 400 invalid_request. ow-list-pagination 26 -> 25. * feat(den-api): cursor pagination for GET /v1/workflows/{configObjectId}/snapshots Additive: optional cursor query parameter and nextCursor response field. Keyset paging on the existing (finished_at desc, id desc) order through workflowSnapshotPage; getWorkflowDetail keeps its uncursored 100-row read. Adds an operation description. ow-list-pagination 25 -> 24, operation-description 42 -> 41. * feat(den-api): cursor pagination for GET /v1/workers Additive: optional cursor query parameter and nextCursor response field. Moves the inline worker query into listWorkersPage (keyset on created_at desc, id desc); default limit 20 and ordering unchanged. Test also proves an undecodable cursor is a 400 invalid_request through the real queryValidator. ow-list-pagination 24 -> 23. * docs(api-style): record cursor pagination on the former limit-only lists * chore(api): regenerate snapshot, baseline and SDK after rebase onto dev | 25 days ago | |
feat(remote-sessions): choose computer, workspace and model (#5596) * feat(automations): run cloud Automations on the headless runner for Team plans Cloud agent Automations needed a $50 OpenWork Web computer per member: Den woke a Daytona VM, drove OpenCode on it, and shared four global slots with every other cloud run. Organizations with the new default-off headlessAutomations capability (Team or Enterprise when plan gating is on) now run them on the shared headless runner instead. - One Den client for the runner (src/headless-runner/client.ts); Slack uses it with unchanged wire behavior. - A run is one turn in its own runner session. The receipt is saved before the turn is sent, so a Den restart resumes the same turn and a runner restart resumes it by re-sending the same message id. - Every send mints a member-scoped MCP token that lives for the run, instead of relying on a client-minted 7-day token. - Headless runs get their own pool (DEN_HEADLESS_AUTOMATIONS_MAX_CONCURRENCY, default 16) and never take OpenWork Web slots. - In-Den Workflow runs in those organizations skip the Web seat too. Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): let headless cloud Automations use the cloud default model An agent creating an Automation (for example from a chat) had to pick a provider and model it could not know. AUTOMATION_CLOUD_DEFAULT_MODEL means "whatever the organization's cloud runs on": accepted only for cloud Automations on the headless runtime, run with the runner's configured model, and refused (needs attention) if the organization moves back to OpenWork Web. Signed-off-by: Benjamin Shafii <ben@prologe.io> * fix(automations): retry transient headless failures and keep only the answer as the result A run that failed because MCP or the model was briefly unavailable is retried; the retry resumes the same turn, and the runner never repeats a tool call it already made. The saved result is the final reply, without the narration between steps. Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): run a chosen model on the cloud computer when there is one With the headless runtime on, an organization that also has OpenWork Web computers now gets both: the cloud default model ("only connected accounts") runs headless, and a chosen model runs on the owner's cloud computer, where it can use that computer's files. Without a computer every run stays headless. Cloud claims started together could deadlock in MySQL and leave one run waiting a scheduler tick; a claim that is picked as the deadlock victim is now retried at once. Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): run on any of a member's desktops, or once in the cloud Runner rows were keyed by the desktop's own id, so one install signed in to a second organization hit an identity conflict, reset its id and left a stale runner behind; runner rows and wake-up notifications were never pruned; the first desktop to poll claimed every run and failed the ones pinned to another desktop's workspace; and placement was fixed forever. - Runner rows are keyed by organization, member and install (a derived id, no migration), so one install is a runner in every organization and a member can register any number of desktops. Registering replaces the desktop's pre-scoping row. The app keeps one install id; an older Den that still refuses it gets a per-organization id instead of a reset. - The scheduler prunes desktops unseen for 30 days and notifications older than 7 days in bounded batches. Capability presence sees every desktop instead of the latest 100 rows. - GET /v1/automation-runners lists the member's desktops and whether Cloud can run their Automations. It is not an MCP tool. - Work items name a pinned workspace (absent otherwise, so released runners see the same shape). A desktop skips runs pinned to a workspace it lacks, leaving them for the desktop that has it, and looks at up to 20 runs per poll. Unclaimed pinned runs are missed with that reason. - POST /v1/automations/:id/run takes an optional executionTarget to run once on the other target, and PATCH may move an Automation between desktop and cloud, with the access checks of cloud creation. Workflows never run on a desktop; a pin never follows a move. Agents keep runAutomationNow (its body is optional) but may only move work to the cloud. A failed one-off run never pauses the Automation's schedule. - The Automations page, in Desktop and Web, offers Runs on: My desktops or Cloud when both exist, and Run in cloud for Desktop Automations. Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): name the placement choice by what an Automation can use "Runs on: My desktops / Cloud" asked people to reason about infrastructure. The choice now says what the run can reach: - "Your connected accounts and files on this computer" (your computer in the Web app) runs on the person's desktops, as before. - "Only your connected accounts", with the logos of the accounts it would use, runs on the headless runtime. Organizations on OpenWork Web computers see "... and files on your cloud computer" instead, because those runs can reach that computer's files. One line under the choice says when it runs: "Runs in the cloud, even when your computer is off" or "Needs OpenWork open on one of your computers at the scheduled time." Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): show the computer and the connected accounts on each choice Both choices now show the logos of the person's connected accounts. The one that can also use files adds a computer before them (a cloud for an OpenWork Web computer), so each row says at a glance what the run can reach. Labels are shorter: "Connected accounts and files on this computer" and "Only connected accounts". Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(automations): offer all three things an Automation can use There are three, not two: files on this computer, files on your cloud computer, and only connected accounts. An organization on the headless runtime whose member also has an OpenWork Web computer now sees all three. - GET /v1/automation-runners reports cloudComputer next to the runtime. - "Only connected accounts" runs on the organization's one cloud model, so the editor hides the model picker and saves the cloud default; "files on your cloud computer" keeps a chosen model and runs on that computer. - Each choice shows the connected-account logos; the two with files show their computer first. Signed-off-by: Benjamin Shafii <ben@prologe.io> * feat(remote-sessions): choose the computer, workspace and model Desktops report their computer label, workspaces, engine, default model and models to PUT /v1/automation-runner/inventory when they connect and when it changes. remote-session:targets lists them for the caller, and remote-session:create accepts computerId and workspaceId (validated against the inventory, with model checks) and pins the command so only that runner can claim it. The desktop creates the session in the pinned workspace or fails with workspace_unavailable. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore: renumber the remote session targets migration to 0122 and allow the bounded runner list in the API lint Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 3 days ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
Simplify Warden to security reviews with timing reports (#5220) * ci: simplify Warden security review and record timing * test: cover mixed-case HTML tags in Warden reporting | 13 days ago | |
docs: add community health docs and triage rubric (#725) | 6 months ago | |
Licensing v0.5.1: Enterprise Features scoping, stewardship page, DCO, machine-readable licensing (#4076) * Licensing v0.5.1: Enterprise Features scoping, 30-day evaluation, 1-year MIT conversion, stewardship, DCO License (ee/LICENSE; applies to future releases only, never retroactive): - Clause (a): free <=5-user production now excludes the Enterprise Features (as defined in the Subscription Terms) - Clause (b): internal evaluation tuned to thirty (30) days from first deployment, any user count, extendable in writing - Grant of Future License: each version converts to MIT on the first anniversary of its public release (was second) - strictly more customer-favorable than any prior release promised - New clause: client-side-served assets (images, fonts, CSS, compiled client-side JavaScript) are MIT Subscription Terms: new Section 4 defines Enterprise Features (SSO/SAML + SCIM, usage and adoption analytics, desktop policies and version controls, white-labeling); included in Enterprise, evaluations, and pilots per order; list changes are prospective and never shrink a paid tier mid-term. Pricing surfaces (landing + docs): analytics moves from the Team card to Enterprise, matching the entitlement gating already in the platform; white-labeling and support lines added; footnotes updated for the new grant boundaries. New surfaces: docs Stewardship page (released versions keep their license, MIT core stays MIT, one-year MIT conversion, paid tiers never shrink mid-term, trademark / white-labeling policy); REUSE.toml + LICENSES/ for machine-readable license scanning; CONTRIBUTING.md requiring DCO sign-off and signed IP agreements for paid or trial engagements before substantive contributions. Proof: landing next build clean (/pricing and /terms/subscription prerender); docs.json parses; no stale 90-day or two-year references (grep-verified); remaining files are inert license/docs content. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Add contributor license agreements for ee/ contributions Mirror the GitLab contribution structure: DCO everywhere, plus an Individual or Corporate CLA for contributions under ee/. The CLA texts are adapted from the Google Open Source Programs Office agreements (CC BY 3.0, the same source GitLab uses), with one clarification added: the copyright grant expressly covers distribution under the licenses applied to the Work, including the EE License's scheduled MIT conversion. Contributors retain ownership; the sublicensable grant is what makes external ee/ contributions legally safe to accept rather than rejecting them outright. CONTRIBUTING.md updated to state the tri-split and that ee/ pull requests are not merged until the applicable CLA is in place. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Clarify enterprise feature and white-label terms Signed-off-by: Benjamin Shafii <ben@prologe.io> * Set EE conversion window to two years Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> | 1 month ago | |
fix(chat): clear model errors and calm, readable tool steps (#5547) * fix(chat): calm, readable Code Mode steps - Groups never open or close on their own; one call renders as one row. - Steps show Den's real script error instead of a generic placeholder. - Readable labels: "Script on OpenWork Cloud failed", "Searched Exa for ...", clean service names (no openwork-direct-*-<hash> ids), brand casing. - Direct org connections get their own logo and name. - Tool catalog lookups (search, search_capabilities) are hidden. - Technical details move behind a discreet per-step code icon. * fix(apps): one creation card per app, with real build failures - The v2 results plugin accepts dotted Code Mode tool names (openwork-cloud.create_app), so App results and launches are recorded again. - When nothing was recorded, the adapter rebuilds App steps from the script's call list: rejected builds end as failed with Den's reason, and the preparation id links retries into one card. - Rejected attempts fold into the same card ("Fixed 2 problems"). - Technical details use the shared code icon. * fix(chat): clear model errors, one error per turn, steadier rail - v2 transport failures read as "Couldn't reach the model provider", "Can't reach the model service", "Connection to the model dropped"; "Model unavailable: <ids>" and "HTTP 5xx/429/401/403" text are classified. - Retrying reads "Reconnecting to the model" with attempt progress. - A failed turn shows one error even when live and snapshot keys differ. - Recovery row: one line, visible Retry, raw error behind a code icon. - The step height hold only applies to the live group and releases on click. * fix(chat): connect card keeps its live connected state The card trusted the tool result from when the step ran, plus an in-memory record keyed by the session's UI scope. Reopening a session changed that key, so a connection signed in earlier showed "Connect" again. The card now also reads the member's live status (connectedForMe) from the org connection list. * fix(apps): app card advances and ends even when results were not recorded When an engine did not record App results, successful steps are marked unrecorded instead of guessed from the script's return value: the card moves through its steps, ends as "Created ... Open it from your Library", and an unmatched build joins the latest card only when that preparation's id is unknown. * fix(chat): name empty and failed scripts, classify decode errors - A Code Mode script with no tool calls is hidden once it succeeds, and reads "Running a script" or "Script failed" otherwise (was "Worked on this step" / "Couldn't finish this step"). - "Decode error (200 POST <url>)" reads as an interrupted model response; the URL stays in technical details. - Technical details drop the internal tool id line and empty inputs. * fix(chat): readable native capability steps, no invented durations - "native:<connection>:getCapabilitiesGoogleWorkspaceCalendarEvents" reads "Fetched calendar events · Google Workspace" (was "Ran a capability"). - Calls inside a Code Mode script show no duration: the engine records no per-call timing, so the shown value was made up. * fix(chat): quieter rows inside a Code Mode group Rows inside a group named for one service drop the repeated service name ("Searched for ..." under "Searched Slack"), and identical consecutive finished calls fold into one row ("2 times"). * fix(chat): keep the live step on its own line so the chat stops jumping The running command (or "Thinking...") was a separate line under the step group that disappeared when the step finished, moving everything below it up by about 22px each time. It now sits on the group's summary line. Measured on five sequential commands: upward jumps of the Working line went from 6 to 1. * docs(design): per-step technical details icon, no engine names in rows * test(chat): align tool-row tests with the new labels and details panel * fix(chat): one error per failed turn on every render path The live error event falls back to a session-keyed message when the turn has no assistant message yet, and some history merge paths keep both it and the snapshot's error. The message list now applies the one-error-per-turn rule itself, whatever path built the list. * fix(chat): one plain row per step, including app creation - App creation is a single rail row ("Writing app", "Checking app", "Created app ... Open", "Couldn't create app ... <reason>") instead of a bold four-step card; earlier rejected attempts and raw calls sit behind the details icon. - "Script failed" shows its reason on the line. - Steps that render nothing (catalog lookups, empty reasoning, step markers) no longer leave gaps in the rail. - Org MCP calls made through OpenWork Cloud (mcp:<id>:web_search_exa) read "Searched Exa for ..." with the connector's name. - "This model isn't available" offers Retry; it is usually transient. | 3 days ago | |
Licensing v0.5: OpenWork EE License for ee/ + public pricing (Free ≤5 / Team $20 / Enterprise $50) (#4043) * Relicense ee/ from FSL to OpenWork EE License; add root LICENSE pointer and README licensing section * Public pricing v2 (Free ≤5 / Team $20 / Enterprise $50) + subscription terms page + docs pricing-and-licensing page * Remove Memory Bank feature; extract telemetry into @openwork-ee/telemetry (fresh implementation) - Delete /v1/memory routes, memory + fulltext schema, scripts, and tests; add drizzle migration 0077 dropping memory tables; prune openapi paths - New ee/packages/telemetry package: dimension derivation, ingest/analytics contracts, and query builders written fresh; den-api telemetry routes are a thin glue layer over the package - Rewrite db-executor and password-policy response helpers - Proof: telemetry + den-api tsc clean; bun test den-api (17 pass); den-db node tests (14 pass, 3 skipped: DB-bound) * den-web: repackage analytics and org-selection as feature modules - New _features/analytics: screen composed from stat-card, trend-chart, model-usage-list, a fresh project-filter, and a fresh use-analytics data layer that validates responses with @openwork-ee/telemetry/contracts (zod) instead of hand-rolled readers - @openwork-ee/telemetry gains a browser-safe ./contracts subpath; the den-db-bound event-type allowlist moves to a server-only module and metadata sizing uses TextEncoder instead of Buffer - New _features/org-selection screen (fresh props contract, extracted OrgRow/actions) plus _lib/org-selection.ts owning both sides of the pending-picker sessionStorage handshake and the required/available mode rules; provider/shell/home-screen wiring updated, old den-org helpers removed - Proof: den-web tsc clean; next build clean; den-web bun tests 135 pass (incl. org-selection-background against the new path); den-api tsc clean and 17 bun tests pass; den-db suite 14 pass * ci: restore xlsx lockfile integrity; add @openwork-ee/telemetry to den images - pnpm-lock.yaml: local pnpm 11 install dropped the integrity hash on the xlsx CDN tarball entry, which breaks every --frozen-lockfile install (openwork-tests, Vercel deploys, gateway image); restored from dev - Dockerfile.den: copy + build ee/packages/telemetry (new den-api dep) - Dockerfile.den-web: copy telemetry manifest+source and the den-db manifest so the filtered install resolves the new workspace graph * Split client-safe @openwork-ee/telemetry-contracts out of the telemetry package The den-web image install failed because the telemetry manifest dragged den-db into the filtered closure, and den-db's drizzle instance is peer-parameterized with better-sqlite3 — a native build the slim image cannot compile. - New ee/packages/telemetry-contracts: zod-only, ships TypeScript source (types/default -> src, the @openwork/types pattern), no build step; den-web transpiles it via transpilePackages - @openwork-ee/telemetry keeps den-db as a real dependency for den-api runtime resolution and bundles the contracts source into dist (tsup noExternal) so node consumers never resolve the source package - den-web depends only on telemetry-contracts; imports + transpile list updated - Dockerfile.den-web: install closure is now types/ui/utils/contracts only; also build @openwork-ee/utils before next build — cold image builds have always been broken without it (masked by layer caching, exposed when this PR invalidated the cache) - Dockerfile.den: copy telemetry-contracts alongside telemetry - Proof: den-web closure verified via pnpm ls filter; cold simulation (no telemetry dist, utils built image-style) tsc + next build clean; den-web bun tests 135 pass; den-api tsc clean + 17 bun tests pass; telemetry dist verified bundled (no runtime contracts import) * docs: explain HIBP k-anonymity SHA-1 in auth-protection CodeQL flagged the rewritten breach-lookup helper as insufficient password hashing; the digest is the Pwned Passwords range-lookup protocol, never storage. Alert 117 dismissed as false positive with this rationale. --------- Co-authored-by: Benjamin Shafii <ben@openworklabs.com> | 1 month ago | |
refactor(worlds): name previews by what they boot, pass app settings (#5441) * refactor(worlds): name previews by what they boot and pass app settings `worlds/` held 21 recipes; about half served one test, doc or nothing. - preview-desktop is now the desktop app alone (no Den): source on local, Daytona or Freestyle, or exact published release bytes. One Daytona sandbox instead of two. - preview-full is the old Den plus desktop preview (fresh, team, restricted, workspace); preview-den is unchanged. - Renames: app-web -> preview-app-web, dev-headless -> dev-app-web, desktop-prod-live -> live-desktop, headless-prod-live -> live-app-web. Old names fail with the new one (RENAMED_WORLDS). - Single-use worlds move next to their user and run by path; unused worlds (solo, acme-demo, azure-byok, gateway-local, den-gateway-local) are removed. - `pnpm world up --env KEY` values now reach desktop apps on local and Daytona (e.g. OPENWORK_ENGINE_V2_PREVIEW=1). The selection marker now lists only keys the person selected, not keys the CLI adds itself. Freestyle refuses --env for desktops: its snapshot starts the app at build time. - evidence-web accepts its documented --source again. Signed-off-by: Benjamin Shafii <ben@prologe.io> * test(evals): remove the failing cross-workspace split view spec It fails on unchanged dev at the same step (the session context menu never renders), so it proves nothing. Its world had no other user. Signed-off-by: Benjamin Shafii <ben@prologe.io> * test(evals): remove the preview-world tooling spec It needs several Daytona sandboxes at once (over the 10 GiB org cap), and the proof lane runs it locally where every test skips, so it never proved anything. windows-published-preview still proves an app-only preview on Daytona; the preview rules and --env handling are unit-tested. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 7 days ago | |
Licensing v0.5.1: Enterprise Features scoping, stewardship page, DCO, machine-readable licensing (#4076) * Licensing v0.5.1: Enterprise Features scoping, 30-day evaluation, 1-year MIT conversion, stewardship, DCO License (ee/LICENSE; applies to future releases only, never retroactive): - Clause (a): free <=5-user production now excludes the Enterprise Features (as defined in the Subscription Terms) - Clause (b): internal evaluation tuned to thirty (30) days from first deployment, any user count, extendable in writing - Grant of Future License: each version converts to MIT on the first anniversary of its public release (was second) - strictly more customer-favorable than any prior release promised - New clause: client-side-served assets (images, fonts, CSS, compiled client-side JavaScript) are MIT Subscription Terms: new Section 4 defines Enterprise Features (SSO/SAML + SCIM, usage and adoption analytics, desktop policies and version controls, white-labeling); included in Enterprise, evaluations, and pilots per order; list changes are prospective and never shrink a paid tier mid-term. Pricing surfaces (landing + docs): analytics moves from the Team card to Enterprise, matching the entitlement gating already in the platform; white-labeling and support lines added; footnotes updated for the new grant boundaries. New surfaces: docs Stewardship page (released versions keep their license, MIT core stays MIT, one-year MIT conversion, paid tiers never shrink mid-term, trademark / white-labeling policy); REUSE.toml + LICENSES/ for machine-readable license scanning; CONTRIBUTING.md requiring DCO sign-off and signed IP agreements for paid or trial engagements before substantive contributions. Proof: landing next build clean (/pricing and /terms/subscription prerender); docs.json parses; no stale 90-day or two-year references (grep-verified); remaining files are inert license/docs content. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Add contributor license agreements for ee/ contributions Mirror the GitLab contribution structure: DCO everywhere, plus an Individual or Corporate CLA for contributions under ee/. The CLA texts are adapted from the Google Open Source Programs Office agreements (CC BY 3.0, the same source GitLab uses), with one clarification added: the copyright grant expressly covers distribution under the licenses applied to the Work, including the EE License's scheduled MIT conversion. Contributors retain ownership; the sublicensable grant is what makes external ee/ contributions legally safe to accept rather than rejecting them outright. CONTRIBUTING.md updated to state the tri-split and that ee/ pull requests are not merged until the applicable CLA is in place. Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> * Clarify enterprise feature and white-label terms Signed-off-by: Benjamin Shafii <ben@prologe.io> * Set EE conversion window to two years Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <benjamin.shafii@gmail.com> Signed-off-by: Benjamin Shafii <ben@prologe.io> | 1 month ago | |
Update SECURITY.md updated ben's email | 5 months ago | |
ignore: update download stats 2026-07-30 | 2 months ago | |
ignore: update download stats 2026-07-30 | 2 months ago | |
docs: update documentation based on recent changes (#1294) | 6 months ago | |
I18n: Add Russian support (based on DrMaks22 PR) (#1642) * Add Russian locale * Include Russian locale in i18n audit * Fix plural placeholders in i18n locales * Russian language support, based off of DrMaks22 https://github.com/different-ai/openwork/pull/1635 * Improve i18n-audit --prune script to include non-EN locale orphaned translations, and prune the Russian translations * I18n audit script: do not require {count} key for _zero and _one * Fix Russian plurals * Add missing Russian translations * Fix LANGUAGE_OPTIONS label vs. nativeName * Fix orphan check for plurals * Fix source scan --------- Co-authored-by: OpenAI Codex <codex@openai.com> | 4 months ago | |
fix: replace video tag with GIF for README demo display GitHub strips <video> tags with release asset URLs in rendered READMEs, so the demo was invisible. Converted the mp4 to an optimized GIF and embedded it as an <img> tag which renders everywhere. | 7 months ago | |
fix(models): preserve cloud Fast mode across desktop engines (#4888) * fix(models): preserve cloud Fast mode across desktop engines * fix(desktop): compile Fast helper for packaged runtime | 24 days ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
push | 6 months ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
fix(deps): remediate all open Dependabot security alerts (#5535) Patch the 93 open alerts (436-531) across both lockfiles: next 16.3.6, hono 4.13.9, undici 6.28.1/7.29.1, electron 43.7.5, nodemailer 10.0.11, dompurify 3.4.16, axios 1.20.0, @grpc/grpc-js 1.14.5, ip-address 10.7.2, engine.io 6.6.11, brace-expansion 1.1.21/2.1.7/5.0.12, fast-uri 3.1.8 (root and evals). All chosen versions are older than the 3-day minimumReleaseAge. Signed-off-by: OmarMcAdam <gh@mcadam.io> | 14 hours ago | |
fix(deps): remediate all open Dependabot security alerts (#5535) Patch the 93 open alerts (436-531) across both lockfiles: next 16.3.6, hono 4.13.9, undici 6.28.1/7.29.1, electron 43.7.5, nodemailer 10.0.11, dompurify 3.4.16, axios 1.20.0, @grpc/grpc-js 1.14.5, ip-address 10.7.2, engine.io 6.6.11, brace-expansion 1.1.21/2.1.7/5.0.12, fast-uri 3.1.8 (root and evals). All chosen versions are older than the 3-day minimumReleaseAge. Signed-off-by: OmarMcAdam <gh@mcadam.io> | 14 hours ago | |
chore: clean up repo configuration (#1817) * chore: clean up repo configuration * fix: restore pnpm patch file --------- Co-authored-by: src-opn <src-opn@users.noreply.github.com> | 4 months ago | |
chore: delete dead code, unused deps and orphaned assets (#5647) * chore: delete dead code, unused deps and orphaned assets Remove code that nothing reaches, verified against origin/dev with knip (new knip.jsonc) plus manual reference checks, including hidden dirs. Typecheck passes for app, server, desktop, den-api, den-web, landing, gateway and telemetry; the app builds; app boot and desktop tests pass. App (apps/app): - React-port leftovers and orphaned modules: actions store/provider, global sync and SDK providers, unused settings, connections and session modules, test-only helpers and their scripts. - Dead exports: unused utils, Advanced sections, migration deferral helpers, compactSession, the deprecated signUpEmail stub. - The unregistered OpenAI image settings state and the never-read microsandboxCreateSandbox flag. - The menu-overlay renderer (native context menus replaced it in #4650), unused i18n keys and 192 unused grain-art images. - Deps: @ai-sdk/react, @radix-ui/colors, @radix-ui/react-use-controllable-state, remark-breaks, remark-gfm. Desktop (apps/desktop) and types: - 16 renderer-unreachable IPC handlers and their types and re-exports (some wrote files), plus 7 unused browser and microphone channels. - menu-overlay preload, unused dev icons, electron-devtools-installer. Server (apps/server): - Unused exports, @types/minimatch, @openwork/headless-threads and the opencode-chrome-devtools devDep with its module declaration (the bundled openwork-chrome-devtools plugin replaced it in #4423). Den: - Orphaned landing components, the den-api plugin-system barrel and desktop-policy-role-assignments re-export, a test-only cache hook, test helpers and evidence scripts left without tests, and unused deps (grammy, drizzle-orm in gateway, zod in telemetry). Repo: - apps/ui-demo, Tauri migration scripts, the broken dev-web-local and dev-den-local scripts, the den-controller stub and DEN_CONTROLLER_PORT, one-off scripts, HANDOFF.md, committed tsbuildinfo files and tracked evals/results output, the Remote MCP Apps docs. - Add knip.jsonc and make scripts/find-unused.sh a thin knip wrapper that uses pnpm. Signed-off-by: Benjamin Shafii <ben@prologe.io> * chore(app): keep only the artifact helpers production uses lib/artifacts.ts keeps ArtifactType, getArtifactType and useOpenArtifactPath; the rest had no production caller and its tests were already removed. Signed-off-by: Benjamin Shafii <ben@prologe.io> --------- Signed-off-by: Benjamin Shafii <ben@prologe.io> | 16 hours ago | |
ci(warden): approve PRs with no high/medium security or confidentiality findings (#5662) | 15 hours ago |
OpenWork
OpenWork 是 Claude Cowork 和 Codex 的免费开源替代品:一款适用于 macOS、Windows 和 Linux 的桌面应用,让 AI 智能体在你自己的文件上完成实际工作。它基于 OpenCode 构建,支持任意模型——50+ 提供商、你自己的 API 密钥,或通过 Ollama 运行本地模型——并让团队共享技能和 MCP 服务器。
下载 OpenWork · GitHub 发布 · 文档
只需为 Codex、Claude Code、Cursor 或其他兼容智能体添加一个 OpenWork MCP,即可在你的工具、团队成员和设备间复用相同的技能、MCP 和已连接服务。创建一次,即可与同事或朋友共享,也可以仅自用。
当你希望拥有专属工作区时,桌面应用随时可用,但它并非必需。你也可以从已有的智能体中使用 OpenWork。对于规模较大的组织,管理界面允许你发布能力、管理访问权限,并配置共享连接或按用户连接。
为什么选择 OpenWork 而不是 Claude Cowork
- 免费开源。 桌面应用采用 MIT 许可证;本地使用无需 OpenWork 账号。
- 任意模型。 自带 API 密钥,使用 ChatGPT 登录,或通过 Ollama 或任意 OpenAI 兼容服务器运行本地模型。查看 添加自定义 LLM。
- 以桌面应用形式运行于 macOS、Windows 和 Linux——而非 CLI。
- 你的文件保留在本地。 云端为可选项。
- 与团队共享技能和 MCP 服务器。 查看 与团队共享技能 和 共享 MCP 连接。
- 将团队控制平面自托管到你自己的基础设施上。查看 自托管。
- 迁移你的 Cowork 配置。 技能、兼容 Claude 的插件以及 MCP 服务器均可沿用。查看 从 Claude Cowork 迁移。
通过你的 AI 智能体安装
你已经在用 AI 智能体了吗?复制这段提示词,并粘贴到 Claude Code、Cursor、Codex、ChatGPT,或任何能够在你电脑上执行命令的智能体中。
Install OpenWork on my computer, set up my first workspace, and open it ready to use. Follow the steps in https://openworklabs.com/start.md?v=hero
- 安装 OpenWork
- 创建你的工作区
- 打开后即可运行
在任意智能体中使用 OpenWork
OpenWork MCP 可将你已分配的技能、插件、MCP 连接,以及 Google Workspace 和 Microsoft 365 能力接入任何兼容的智能体。
它提供四个工具:search_capabilities 查找你可使用的能力,execute_capability 执行该能力,list_skills / get_skill 列出你的技能并直接读取某个 SKILL.md。添加 MCP 后,客户端会打开浏览器,让你登录并选择你的 OpenWork 组织。
Codex
codex mcp add openwork --url https://api.openworklabs.com/mcp/agent
Claude Code
claude mcp add --transport http openwork https://api.openworklabs.com/mcp/agent
OpenCode
将以下内容添加到 opencode.json:
{
"mcp": {
"openwork": {
"type": "remote",
"enabled": true,
"url": "https://api.openworklabs.com/mcp/agent",
"oauth": {}
}
}
}
任意 MCP 客户端
使用此远程 MCP 服务器地址:
https://api.openworklabs.com/mcp/agent
OpenWork Den
OpenWork Den 是用于在团队或组织范围内管理 OpenWork 的控制平面。
- 大规模开通推理服务,并控制各模型提供商可供哪些成员和团队使用。
- 邀请团队成员、创建团队,并集中管理访问权限。
- 设置桌面端策略,限制本地模型访问,并控制组织可使用的应用版本。
- 通过市场发布技能和插件,然后将其分配给组织、团队或特定人员。
- 导入 Agent Plugins 或兼容 Anthropic 的插件,并通过 OpenWork MCP 使其支持的技能和远程 MCP 可用。
许可
本仓库采用按目录拆分的许可证,类似 GitLab:
ee/目录之外的所有内容均为 MIT — 桌面应用和核心平台均为开源,可自由用于任何用途。ee/目录下的所有内容(OpenWork Den — 组织控制平面)均采用 OpenWork EE License,这是一种源码可用许可证。代码是公开的,你可以准确审查实际部署的内容。生产环境使用需要 OpenWork 订阅,但用户数不超过 5 的组织免费,任意规模均可免费评估 30 天,并且开发和测试始终免费。每个ee/版本在发布两年后还将转为 MIT。
在此许可证采用之前发布的版本仍沿用其原始许可证(FSL-1.1-MIT)。请参阅定价和订阅条款。
文档
快速开始(贡献者)
从全新克隆到可运行的开发构建的最快路径。
前置要求
- Node 24 — 锁定在
.nvmrc中(nvm use会自动切换到该版本)。 - pnpm 11 — 锁定在
package.json中(packageManager);运行corepack enable可自动使用锁定版本。切勿使用 npm 或 yarn。 - 带 DCO 签名的 Git — 每个提交都需要
Signed-off-bytrailer(git commit -s)。参见 CONTRIBUTING.md。
首次运行
git clone https://github.com/different-ai/openwork.git
cd openwork
corepack enable
pnpm install
pnpm dev # launches the Electron desktop app with hot reload
仓库结构
| 路径 | 存放内容 |
|---|---|
apps/ |
桌面应用:React 界面(apps/app)、Electron 外壳(apps/desktop)以及 openwork-server(apps/server)(MIT) |
packages/ |
共享核心包(MIT) |
ee/ |
OpenWork Den — 组织控制平面、MCP 网关和推理服务(EE License,参见 许可) |
evals/ |
基于 @openwork/testkit 构建的可执行测试规范 — 参见 evals/README.md |
worlds/ |
用于 pnpm world 的声明式开发/测试环境定义 |
docs/ |
运维、功能与发布文档 |
.opencode/skills/ |
仓库智能体技能(测试、发布、Daytona 等) |
测试
所有可执行测试覆盖均位于 evals/specs/**/*.test.ts;应用驱动旅程使用 .e2e.test.ts。
pnpm --dir evals install --frozen-lockfile # once
pnpm evals:pr specs/<name>.test.ts # app-less PR-lane spec
pnpm evals:e2e <name> --local # app-driving E2E journey, run locally
Runtime-observable changes need test evidence on the PR. AGENTS.md and evals/README.md describe the verification contract and vocabulary.
Sending a pull request
- Branch from
dev(the default branch) and open your PR againstdev. - Sign off every commit:
git commit -s. - Keep the diff as small as possible, and include or update test evidence for runtime-observable changes.
- Read CONTRIBUTING.md for the DCO and licensing rules — contributions under
ee/additionally require a CLA.
Local development
For one checkout, keep using pnpm dev; with no extra environment variables it reuses the existing shared dev profile.
To run multiple git worktrees at once, use:
pnpm dev:worktree
它会设置 OPENWORK_DEV_PROFILE=auto,从 worktree 路径派生一个稳定的 profile 名称,让 Electron 选择一个空闲的 CDP 端口,并让 Vite 选择一个空闲的 dev-server 端口。你也可以指定一个命名 profile,例如 OPENWORK_DEV_PROFILE=my-feature OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=0 PORT=0 pnpm dev。
dev:worktree 还会默认设置 OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=1。全新的 profile 没有已存储的凭据,因此在 macOS 上,一旦 Chromium 持久化一个已认证的 cookie,系统 keychain 就会弹出提示;该弹窗会阻塞 Electron 主循环,直到被关闭。如果你希望在隔离的 profile 中明确使用系统 keychain,可以设置 OPENWORK_ELECTRON_USE_MOCK_KEYCHAIN=0。
开发启动时会打印一条类似 [openwork] dev profile=... cdp=http://127.0.0.1:9823 的横幅;可据此找到 profile 目录,并将 CDP URL 传递给本地工具。
如果第二个实例无法获取 profile lock,现在会明确提示并退出,而不是停留在 CDP 端口已打开但没有窗口的状态。
Headless web(无 Electron)
要在浏览器中运行 OpenWork UI,并连接本地 openwork-server(无 desktop shell):
pnpm world up dev-app-web --detach
pnpm dev:headless-web 是同一脚本的兼容别名。该别名
默认保持前台运行,并接受 --detach;world up 保持前台
运行,除非显式指定 --detach。
这是一个独立的启动器:
- 写入
tmp/headless-server.json,且从不读取~/.config/openwork/server.json - 自动授权所选的工作区根目录,并在重新启动时合并(从不重写)该配置,因此通过 UI 添加的工作区在
--replace后仍会保留 - 启动 Vite +
openwork-server,并强制将稳定的所有者 Bearer 凭证注入 UI。崩溃重启时,已打开的标签页仍可继续使用。特权主机令牌保留在服务进程中,绝不会内联到 Vite 打包产物中。 - 以同源方式代理 Den Cloud 调用:Vite 提供
/api/den(转发到 Den 控制面),应用通过VITE_DEN_API_BASE_URL将 Den API 固定到该地址,因此 Cloud 调用不会被 CORS 阻止,过期的localStorage基础 URL 也会在加载时清除 - 在
tmp/dev-headless-web.json(0600)发布仅限所有者的运行时清单,并仅允许来自 Web 应用自身来源的浏览器调用本地服务器——而不是你访问的任意网站 - 默认使用稳定端口(Web
5178、服务器8778;端口被占用时回退到空闲端口,可用OPENWORK_WEB_PORT/OPENWORK_PORT覆盖) - 以
dev-app-web作为单实例运行;再次启动前请先使用pnpm world down dev-app-web停止它 - 将 Vite 和后端置于同一脚本生命周期中,因此任一兄弟进程退出都会停止另一个,而不会留下孤儿进程
- 在分离模式下,等待健康检查通过,打印非敏感输出以及回执/日志路径,然后退出
脚本专属选项必须位于 -- 之后:
pnpm world up dev-app-web --detach -- --replace
pnpm world up dev-app-web --detach -- --replace --keep-tokens
--replace 会以全新的令牌重新启动无头运行时;如需保留之前的令牌,请添加
--keep-tokens。该脚本也接受
--rotate-tokens。这些都不是通用的 world 选项。
打开输出的 Web 地址。无头 Web 中的云端登录采用复制/粘贴交接方式(托管 Den 无法将会话授权重定向回 http://127.0.0.1):
- 账户 → 登录(打开 Den;粘贴字段会在设置中打开)
- 在 Den 上登录
- 复制 Den 显示的 OpenWork 链接 / 一次性代码
- 将其粘贴到 粘贴登录代码 下方 → 完成登录
在运行 pnpm dev:web-local 时,使用 OPENWORK_DEV_DEN_PROXY_TARGET=http://127.0.0.1:3005 将 Den 指向本地技术栈。设置 OPENWORK_DEV_HEADLESS_WEB_DEN_PROXY=0 可禁用 Den 接入。
其他已签入的脚本包括 worlds/live-app-web.ts 和
worlds/live-desktop.ts。两者有意共享已安装的生产环境状态,并需要在 -- 后使用其各自脚本的专属显式启用项,例如
pnpm world up live-desktop -- --allow-shared-state。使用 pnpm world list 可列出脚本和运行回执。无头生产 world 严格限制在回环地址;由于其浏览器会话使用生产凭据,远程访问/公网主机设置会被拒绝。