fixing/merge-conflict - #1
Open
Pavitra-programmers wants to merge 4679 commits into
Open
Conversation
* fix(ci): restore npm trusted publishing * fix(ci): isolate npm publish credentials
* fix(den): bound GitHub token minting * test(den): default empty retry witness env * test(den): isolate GitHub retry classifier
* fix(desktop): recover rejected automation credentials * test(evals): publish automation auth evidence * fix(evals): use current assertion evidence API
* test(evals): prove OpenCode MCP OAuth flow * fix(ci): install OpenCode before Daytona auth * ci(evals): install opencode CLI without lifecycle scripts before Daytona credentials
* feat(app): replace Library Add menu with kind-picker radio rows The Add dropdown boxed six actions as menu items. Open a picker that matches the Paper add-flow: radio rows, fill-only selection, then Continue into the existing create dialog. Soften Just me / Everyone on that form the same way. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(app): drop stacked borders on Library create fields Name, description, and prompt inputs stacked a hairline with a drop shadow or inset ring, so they read as a thick frame in the app. Use a fill well only, matching the Paper form. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(app): keep Library picker selection stable --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): use supported runner for npm OIDC * fix(evals): use current evidence recorder API
Co-authored-by: Benjamin Shafii <benjamin.shafii@gmail.com>
* fix(mcp): close unused agent sse listener * fix(mcp): preserve agent get auth challenge * fix(mcp): close agent get probe compatibly
* fix(desktop): reduce automation runner polling * test(evals): update automation runner polling assertions
* feat(den): add skill-created MCP app * fix(den): package MCP app in Docker image
* fix(den): cache hot auth lookups * fix(den): invalidate deleted org auth caches * fix(den): clear session liveness on bearer revoke * fix(den): renew cached mcp session liveness * fix(den): clear admin deleted user session caches * fix(den): avoid db checks on cache hits * fix(den): tombstone revoked auth cache entries
* fix(mcp): tolerate newer protocol-version headers * fix(mcp): keep protocol-version witness free of workspace builds
…#3775) A desktop occurrence was claimable for 60 seconds and then permanently skipped, so a laptop that was asleep, restarting, or between networks at the scheduled time lost the run even when it returned moments later. Give scheduled desktop occurrences a bounded recovery window (default 15 minutes, clamped to the automation's own next occurrence, never below one minute) and keep manual runs on the deliberate one-minute deadline. A desktop that returns inside the window discovers the still-queued run through the existing work query — no new claim path, no second attempt. Name the cause when an occurrence is still missed: busy with another run (exact, from the run table), never connected, or present but silent. The receipt code stays runner_unavailable so existing clients read it unchanged; the app renders the recorded message and falls back to the previous wording for older receipts. Report presence to management clients as a read-only view of the liveness the work poll already records (GET /v1/automation-runners/presence). The desktop treats a Den without the route as presence unknown — one probe, no further polling, no warning — so a newer desktop against an older Den keeps its Automations page intact; only an authoritative disconnected answer renders the warning. Align wake handling with the polling runner lifecycle: powerMonitor resume and unlock-screen trigger an immediate deduplicated work poll (an active run keeps its lease; no overlapping claim loops), the renderer re-registers on the online event, and the idle work poll is bounded so a socket left half-open by a suspended machine retries instead of parking the loop.
* feat(den): scope MCP token liveness to OAuth grants * test(evals): build workspace deps for grant witnesses
…reads (#3942) * fix(automations): report Desktop execution outcomes accurately The runner interpreted only one narrow execution-result shape. A nested provider error or an assistant turn that reported an explicit error in the local thread snapshot was still reported to Den as a successful run, while a run whose assistant turn completed only tool calls — with no closing text — was treated as incomplete until it timed out. Terminal failure and cancellation paths also dropped the native session and workspace identifiers the desktop engine had already supplied, so the receipt lost the one handle a person needs to inspect what happened. Classify the outcome from what the engine actually reported: extract nested error messages, treat an assistant turn carrying an error as the run's failure, and accept an idle thread whose assistant turn completed a tool call as a successful run even without assistant text. Attach the created session and workspace to errors thrown after thread creation so failed and cancelled completions carry the same native identifiers a successful one does, without fabricating identifiers the engine never returned. Reconnect, backoff, wake, and credential-retirement behavior is unchanged and re-proven by the existing runner suite alongside the new regression coverage. * fix(automations): persist and open Desktop execution threads (#3945) The Desktop runner reports the native session and workspace of each execution, but Den only retained an engine receipt for cloud runs, so the identity of the local thread never survived into the run receipt. The Automations UI therefore could not open the thread a run actually executed in, and a receipt fetched for a previously selected run could be rendered — and navigated — against the currently selected one. Persist the runner-reported session and workspace as the desktop run's engine receipt on success, failure, and cancellation, and map it through the existing executionThread shape for every execution target. The receipt field is additive: older runs keep rendering exactly as before, and the run contract is unchanged for clients that ignore the thread. In the app, resolve a desktop thread to its local workspace session route, render an Open local thread action on the selected run, and accept a receipt only when it belongs to the currently selected run so a stale response cannot navigate to an unrelated thread. Cover the journey end to end with a Desktop lifecycle spec: UI-triggered and scheduled runs with durable receipts and correct thread opening, restart with queued work, concurrent manual requests, cancellation before claim, provider outage with linked failed threads and recovery, duplicate claim protection, expired-lease recovery, and stale-completion rejection.
…3948) * feat(mcp): first-party MCP App cards for connect steering flows Every "go do X in OpenWork Cloud" moment the gateway used to steer with plain text now renders a standard MCP App card, built from one shared React design system in @openwork/mcp-apps: - skill-created app restyled on the shared components, and a new update_skill tool publishes new immutable versions through the same card (updated mode) instead of raw postConfigObjectsVersions calls. - connection-action app: executing a connection_status capability now returns a successful live probe whose result mounts an actionable card (sign in, admin setup, provider fix, connected) via a same-server openwork/mcpApp launch, with the app-only connection_action tool backing refresh probes. - plugin-flow app: successful postMarketplacesPlugins, postPluginsAccess, and postMarketplacesAccess executions attach a confirmation card. Steering text (gateway instructions, search hints, builtin skills, and the desktop Connect instruction) now tells the model to execute connection_status matches once so members get the card, and the mcp-apps build stages per-app bundles atomically so parallel test runs never observe a half-built dist. Co-authored-by: Cursor <cursoragent@cursor.com> * test(evals): settle session sync before card screenshots Co-authored-by: Cursor <cursoragent@cursor.com> * test(evals): record card screenshots as ambient evidence Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp-apps): open links from cards and match the app's design language The desktop bridge never handled ui/open-link requests, so card buttons (Open in Library, Connect) silently did nothing; it now routes them through openDesktopUrl (electron shell / noopener fallback). The host also passed no style variables, so cards rendered on fallback styling: it now maps the live DLS tokens (surface, border, text, accent, radix status scales, radius, shadow) onto the standard MCP Apps style vocabulary, and the shared card UI is rebuilt to the app's native card language — sectioned rounded-2xl cards with hairline dividers, round soft-tone icon tiles, and the dls-accent primary button. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(app): apply trailing MCP App resize so tall cards are not clipped Co-authored-by: Cursor <cursoragent@cursor.com> * fix(lockfile): restore xlsx tarball integrity dropped by filtered install Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(app): dedupe session search results * test(evals): record session search identity evidence
…key provisioning (#4223)
) Dragging the left sidebar wider than 420px silently did nothing: the width clamp swallowed the drag, so long session and workspace titles stayed clipped even though the user was explicitly asking for more room. Raise the ceiling to 600px (the right sidebar already allows 960px) so expansion past the old cap actually grants titles the space. Covered by evals/specs/sidebar-left-width-expansion.test.ts: widths past the old 420px ceiling are stored, and the min/max guardrails still clamp.
* feat(automations): pin Automations to an explicit workspace Automations carried no workspace: both the desktop runner and the cloud executor resolved the runner's *currently active* workspace at run time (activeId ?? workspaces[0]), so activating a different workspace silently retargeted every automation. A revision now carries an optional workspaceId captured at creation: - packages/types: workspaceId on revision, legacy create, proposal, update, and desktop runner assignment schemas (all optional/nullable) - digest: includes workspaceId only when set, so existing revision digests stay byte-identical - den-db: nullable automation_revision.workspace_id + migration 0083 - den-api: create/update persist the pin; the desktop assignment and the cloud executor input carry it; cloud recovery still prefers the receipt's workspace, then the pin, then the worker's active workspace - desktop runner: a pinned workspace must exist locally or the run fails with execution_runtime_unavailable instead of silently retargeting; unpinned (legacy) assignments keep the active-workspace fallback - creation surfaces: the automation.propose affordance pins the proposing conversation's workspace; the proposal card falls back to the pane it renders in; the manual editor pins the selected workspace Validation: desktop runner suite 38/38 (4 new witnesses), evals automation-runner-reconnect-backoff green, den-api automation suites green (1 pre-existing readyWorker source-witness failure exists on clean dev and is not run in CI), den-db tests green, tsc clean for app/server/den-api. * refactor(server): make the Connect MCP desired config account-global (#4225) The openwork-cloud Connect MCP entry was persisted per workspace: N runtime rows carrying the same org-scoped token, reconciled per workspace, where a stale legacy row could shadow a fresh one and workspace switching could rewrite the engine-visible entry and bounce the connection. Desired config now lives in the single ENGINE_GLOBAL runtime row: - persistDesiredConfig writes global (host connect-state copy kept as cache) - idempotent migration promotes the newest valid legacy workspace entry to global before cleaning openwork-cloud out of every workspace row, runs at startup (cli/embedded), before persisted reconcile, and before the all-workspaces engine sync; read-only servers never mutate - effective-config merge: the global openwork-cloud entry wins over any leftover legacy workspace copy - dynamic engine MCP sync reads the effective config so every directory instance receives the global entry; delivery/health stay per directory - token/config changes fan out disconnect+re-register to other workspaces only when the persisted config actually changed - DELETE of openwork-cloud removes global desired state and disconnects every workspace directory; generic MCP CRUD stays workspace-scoped - Connect catalogs, passive inspection, and agent-context diagnostics read the global entry first (legacy fallback kept for the migration window) Validation: server tsc clean; health/state/inspect/skill-catalog 40 pass; cloud-mcp-reconcile e2e 26 pass; engine-sync e2e 22 pass; diagnostics + app-host 90 pass; full apps/server suite 787 pass / 8 skip with only the two known environment failures (DNS guard fixture, bun node:sqlite) that reproduce identically on clean origin/dev.
* fix(app): make composer draft hydration atomic * fix(app): stabilize draft store snapshots * fix(app): ignore stale draft persistence effects * test(evals): harden composer draft reload proof * fix(dev): build Electron workspace dependencies * fix(app): gate draft persistence on hydration
* fix(app): keep desktop restriction checker stable * test(evals): bound Library lifecycle requests
* fix(app): stop den-session settings echo loop * test(evals): wait for the skill card before opening it * test(evals): assert signed-in inventory stability
* fix(security): remediate CodeQL findings * docs(security): record CodeQL verification * fix(security): remove ambiguous password hash paths * fix(security): harden CodeQL remediation boundaries * fix(security): remove assessment reports and key OAuth bindings * fix(desktop): mirror server HTML parser dependency * fix(ci): make Daytona regression CLI install portable * fix(ci): locate global OpenCode package under pnpm 11 * fix(ci): select the installed OpenCode package path * fix(evals): audit the shared Daytona regression lane * fix(evals): clear eligible Daytona regression failures * fix(evals): target the visible composer model picker * fix(evals): scope the composer model trigger * fix(evals): select the current composer model button * fix(evals): enter the compact model submenu * fix(evals): wait for workspace session row
* fix(server): isolate provider sync contexts Signed-off-by: Jalil <reachjalil@gmail.com> * test(server): make concurrent sync join deterministic --------- Signed-off-by: Jalil <reachjalil@gmail.com>
…view (#4249) * fix(app): drain queued composer messages while the session is not in view Queued drafts and the drain machine are module-level, but the only driver feeding engine status and sending lived in SessionSurface effects, so a queued message never sent unless its session stayed on screen — and switching workspaces disposed the SSE stream so idle was never observed. Add a global queue drainer that keeps workspace session sync alive for sessions with queued drafts and runs the same admission protocol (claimQueuedSend before any await) when idle is observed, coexisting race-free with mounted surfaces. The mounted surface registers a per-session send context; draftToParts moves out of session-route so sends work outside React. Proven by evals/specs/composer-queued-drain-while-away.e2e.test.ts: a gated mock keeps session A busy, the user queues a follow-up and switches to workspace B, and after release the follow-up lands in A exactly once (engine API + provider request log) while B stays clean; negative control on unfixed code reproduces the stuck queue. * test(evals): count the away-drain spec in the Daytona regression audit composer-queued-drain-while-away.e2e.test.ts imports desktop from @openwork/hosts, so the audited inventory grows to 147 files and the raw-desktop set to 53; the eligible shared-topology count is unchanged.
Live "Working Ns" counters (main chat loading row and sub-agent task lines) kept counting raw seconds forever, reading "Working 403s" while the settled line already reads "Worked for 1m 55s". Format live elapsed time the same way once a run crosses a minute: "Working 6m 43s".
…#4255) The engine event fan-in had no deadline on establishing its per-connection streams: one pool sibling that accepted the socket without answering kept Promise.allSettled pending and the client's live feed never started, even with a healthy primary. Both SSE frame parsers (the merged client body and the drain activity watch) also grew an unbounded buffer on a frame that never terminates. - Give each event-stream fetch a 5s establishment deadline (matching proxyEngineAggregateRead) that is cleared once headers arrive, so the long-lived body itself is never put on a timer; lease aborts still tear bodies down through reader cancellation, and bodies the merged stream will not own are cancelled instead of leaking. - Share one bounded SSE frame splitter between the client fan-in and the drain watch; on overflow the malformed connection is dropped (the drain watch re-dials) instead of buffering the frame forever. - Release reader locks in the cancel/finally paths for consistency with readBoundedEngineMcpRegistrationResponse.
session.error lowered the run level in the activity store (sidebar) but never wrote the react-query status cache the chat surface derives its thread status from, and never gave status listeners an idle edge. A run that errors without a follow-up idle event left the transcript's "Working…" row counting forever beside the error card and wedged queued sends behind a run that would never finish. Mirror the session.idle handling: write idle to the status cache for tracked sessions and notify status listeners.
* fix(desktop): serialize and bound automation runner lifecycle requests * test(evals): track expanded automation-runner lifecycle suite
…ry (#4256) * feat(app): report web boot failures and Stripe webhook errors to Sentry Web deployments previously had no error reporting: a web instance that failed to load, or any uncaught runtime error, was invisible. Stripe webhook processing failures were also converted to HTTP responses by a local catch before the observability middleware could see them. - apps/app: zero-dependency, env-gated Sentry reporter (VITE_OPENWORK_SENTRY_DSN + web deployment only, never inside Electron), started before the bootstrap await, respecting the analyticsEnabled preference, with bounded, deduped, identity-only payloads. - apps/app/index.html: inline pre-boot beacon so an entry bundle that never loads is still reported; hands over to the in-app monitor. - den-api: capture Stripe webhook 500-path failures to observability with a stripe_webhook component tag; signature 400s stay excluded. Proof: evals/specs/web-error-monitoring.test.ts (pnpm evals:pr), apps/app bun tests, apps/app typecheck, web/desktop build gate checks. * fix(app): strip query and fragment from web error report URLs Sign-in and deep-link URLs can carry credentials (grant, openworkToken, accessToken) in the query string. Report only origin + path from both the pre-boot beacon and the runtime monitor, sanitize failed-resource URLs, and return empty for unparseable input instead of leaking it.
) * fix(app): archive sessions from non-selected workspaces correctly The sidebar archive button silently failed for sessions outside the selected workspace: session.update was sent with the selected workspace's directory, and even after resolving the owning workspace the sidebar never refetched a non-selected workspace's session list (planRouteWorkspaceLoads skips already-loaded workspaces), so the row never moved to Archived. - resolve the session's owning workspace in handleArchiveSession and pass its path to setSessionArchived - add reloadWorkspaceSessions(workspaceId) and call it after archiving so the owning workspace's list refetches - update sessionsByWorkspaceIdRef synchronously in the background loader so refreshRouteState cannot clobber a fresh list - canonicalize macOS /private/tmp to /tmp in normalizeDirectoryPath; the mismatch filtered refreshed sessions out of /tmp workspaces - add session-archive-button.e2e.test.ts covering archive/unarchive in the selected workspace and cross-workspace archive, with negative halves * test(evals): count session-archive-button spec in the Daytona regression audit
Give new contributors one fast path from clone to running dev build: prerequisites (Node 24, pnpm via corepack, DCO sign-off), first-run commands, a repository layout map, the testing entry points, and the PR checklist. Also correct the dev banner CDP port example to the actual default (9823). Signed-off-by: Jalil <reachjalil@gmail.com>
- release: add the standard YAML frontmatter (name + description) so the skill is discoverable like every other repo skill. - build-a-witness: point at the current witness sources in @openwork/labs (evals/packages/labs/src) instead of the removed labs/mock-mcp path. - browser-automation: replace a machine-specific temp log path with a portable /tmp path. Signed-off-by: Jalil <reachjalil@gmail.com>
* feat(evals): add script worlds — plain executable TS worlds alongside topology DSL
Worlds can now be ordinary executable TypeScript files instead of declarative
topology objects. A script world composes the same primitives specs already
use (server, createAdmin, createOrg, inviteMember, liteLlmPerMemberProvider,
app) with native `AsyncDisposableStack` for lifecycle, and calls `hold()` to
register a snapshot and block until torn down.
- packages/world: new `hold()` + script-world runtime; loader classifies
`worlds/*.ts` as `definition` or `script` without importing (discovery
stays import-free); CLI gains `world down <name>` and script-aware
`up [--detach] [--timeout]` / `list` / `forget`.
- evals/packages/env: extract `liteLlmPerMemberProvider` out of the topology
interpreter into a standalone exported primitive (interpreter now delegates
to it, unchanged behavior); export `createAdmin`/`createOrg` so scripts can
compose an org without the `server({ org })` bundle.
- worlds/litellm-per-member.ts: rewritten as an import-safe script
(`if (import.meta.main)`) demonstrating the new shape; the topology
definition is gone.
- evals/specs: litellm-per-member-world.e2e.test.ts updated to the new
builder; new script-world-lifecycle.test.ts (PR lane) covers detached
launch ownership, duplicate-launch/unknown-teardown isolation, and graceful
stack disposal on `world down`.
Verified: script-world-lifecycle + shared-world-engine specs pass; full evals
package suite (242 tests) passes; litellm-per-member-world.e2e.test.ts passes
cold against a fresh local Den; typecheck diagnostics unchanged from the
origin/dev baseline (571, none in touched files).
* refactor(evals): remove declarative world layer
* docs(world): document script-only lifecycle
* docs(world): remove declarative lifecycle guidance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
Issue
Scope
Out of scope
Testing
Ran
...Result
CI status
Manual verification
Evidence
N/A (docs-only)Risk
Rollback