Skip to content

feat(automations): execute Den-scheduled Automations on desktop runners - #3466

Merged
benjaminshafii merged 3 commits into
devfrom
agent/automations-den-hosted
Aug 4, 2026
Merged

feat(automations): execute Den-scheduled Automations on desktop runners#3466
benjaminshafii merged 3 commits into
devfrom
agent/automations-den-hosted

Conversation

@reachjalil

@reachjalil reachjalil commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Desktop execution for Den-scheduled Automations

This feature supersedes the Den-hosted OpenCode execution design previously
implemented on this PR.

Current candidate: 92041026a9b04b0c4dd7e564ea160cd26869c372

Signed history normalization on 92041026a

  • Replaced the mixed signed/unsigned feature history with one GPG-signed commit on different-ai/openwork:dev@6785129e7.
  • The feature source tree is byte-identical to the pre-normalization head 2699dcd8a; only newer dev changes were inherited.
  • git verify-commit 92041026a passed, and GitHub reports the PR's single commit as Verified.
  • The branch is zero commits behind dev and one commit ahead.
  • No product checks were rerun for this history-only rewrite; the force-push restarted GitHub CI and dismissed the prior approval.

Prior focused checks on e8f564615

  • That checkpoint was synchronized with different-ai/openwork:dev@6c5068f77 without conflicts.
  • Preserved the credential-catalog fix at b9fd17dea.
  • pnpm --filter @openwork/app typecheck — passed
  • pnpm --dir apps/server typecheck — passed
  • pnpm --filter @openwork-ee/den-api typecheck:automations — passed
  • Focused Automation UI tests — 9 passed
  • git diff --check upstream/agent/automations-den-hosted..HEAD — passed

Free-model cleanup on a90b54944

  • Centralized the Automation free-model identity in the shared contract.
  • Removed stale Den-side provider endpoint, environment, pricing, model config, and credential projection.
  • pnpm --filter @openwork-ee/den-api typecheck:automations — passed
  • pnpm --filter @openwork/app typecheck — passed
  • Automation model-authority tests — 4 passed
  • Automation model-option tests — 3 passed
  • git diff --check — passed

Review-fix verification on 46899f7be

  • pnpm --filter @openwork-ee/den-api typecheck:automations — passed
  • pnpm --filter @openwork/desktop typecheck:electron — passed
  • pnpm --dir apps/server typecheck — passed
  • pnpm --filter @openwork/app typecheck — passed
  • Den runner protocol/auth tests — 8 passed
  • Portable Automation core/engine tests — 10 passed
  • Focused Automation UI tests — 11 passed
  • Den DB migration generation/schema comparison — passed with no unrecorded schema changes
  • git diff --check — passed

Summary

Den remains the durable scheduler and source of truth, while the authenticated
OpenWork desktop app executes Automations through its existing local OpenCode
runtime.

The desktop maintains an authenticated Server-Sent Events connection to Den.
SSE is notification-only and carries exactly a wake-up type plus a resumable
cursor. On connect, reconnect, or notification, the desktop discovers work over
HTTP, atomically claims an eligible desktop occurrence, executes it as a
normal visible OpenWork thread, and reports attempt-bound heartbeats, ordered
events, usage, cancellation, and completion over HTTP.

The scheduled thread uses the selected model and the same workspace, tools, and
OpenWork Connect experience as a thread started manually in the desktop app.
Den never sends or launches an arbitrary command.

What changed

  • Added strict shared contracts for desktop runner registration, notification,
    discovery, claim assignment, attempt-bound heartbeat/events/completion,
    lease loss, and runner-unavailable outcomes.
  • Added durable desktop execution target fields on Automation revisions and
    runs, plus durable MySQL runner registration and notification cursors.
  • Added restart-safe scoped runner credentials and an Electron-main SSE client
    with Last-Event-ID, immediate discovery, and exponential backoff with
    jitter. Credentials never enter the URL, renderer storage, event data, or
    logs.
  • Added authenticated HTTP discovery, atomic/idempotent claim, heartbeat,
    ordered idempotent event append, and idempotent terminal completion APIs.
  • Added authoritative cancellation and lease state to every heartbeat; expired
    leases reject late events/results.
  • Runs claimed work through the desktop's existing local OpenWork session API,
    producing a normal sidebar thread and preserving the usual model/tool/
    integration experience.
  • Added explicit scheduled skipped / runner_unavailable / desktop persistence
    and the UI label Missed — desktop runner unavailable. Offline Run now
    returns No desktop runner is online without queueing work.
  • Removed Den-side OpenCode process launching, execution state directories,
    run-scoped MCP execution code, OpenCode dependency, and Docker state/binary
    changes used only by server execution.
  • Kept the execution-target seam extensible for a future sandbox runner without
    implementing sandbox execution.

Baseline checks on 18463bcc9

  • pnpm --filter @openwork-ee/den-api typecheck:automations — passed
  • pnpm --filter @openwork/desktop typecheck:electron — passed
  • pnpm --dir apps/server typecheck — passed
  • pnpm --filter @openwork/app typecheck — passed
  • Runner protocol/auth/model tests — 9 passed
  • Desktop normal-thread runner test — 1 passed
  • Portable Automation core/engine tests — 9 passed
  • Focused Automation UI tests — 6 passed
  • git diff --check — passed
  • Packaged macOS directory build on Electron 43 — passed

A broad app suite was also run during development: 610 tests passed and two
pre-existing SSR loading tests failed with target.addEventListener is not a function. Those unrelated failures are not reported as passing checks.

Packaged desktop proof

The packaged, ad-hoc-signed, LaunchServices-registered
OpenWork 3466 E2E.app is connected to the existing local Den profile.

Connected scheduled result:

  • Automation: Desktop visible thread E2E 08:45:31
  • Scheduled run: atr_01kz5zf9hcf6aaaykkqdv78myc
  • Result: DESKTOP_VISIBLE_THREAD_OK
  • Engine: openwork-desktop-runner-v1
  • Usage: 18,426 input / 10 output tokens
  • Events: contiguous user → assistant → usage → terminal
  • Visible local thread: Automation: Desktop visible thread E2E 08:45:31

Disconnected result:

  • Automation: Disconnected desktop final 08:46:51
  • Scheduled run: atr_01kz5z8kdhf6aaawf2v2ks906q
  • Durable status: skipped
  • Reason: runner_unavailable
  • Target: desktop
  • UI: Missed — desktop runner unavailable
  • Offline Run now: HTTP 409, No desktop runner is online

The exact packaged app was rebuilt after merging current dev, deep-link
scheme openwork remains registered to bundle com.differentai.openwork.dev,
and the desktop, Den API/Web, and MySQL stack are left running for manual review.

Remaining hardening

  • Cross-platform packaged proof and repository-wide CI.
  • Multi-replica durable/redis-backed online presence for the immediate Run now
    availability check; the current connection presence is process-local.
  • Exhaustive reconnect, concurrent-claim, lease-expiry, cancellation, payload
    limit, and fault-injection testing.
  • Broader security review and penetration testing.
  • A future sandbox target and target-selection UI.

Merge, release, and deployment remain separate decisions.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview Aug 4, 2026 1:56pm
openwork-den Ready Ready Preview Aug 4, 2026 1:56pm
openwork-den-worker-proxy Ready Ready Preview Aug 4, 2026 1:56pm
openwork-landing Ready Ready Preview, v0 Aug 4, 2026 1:56pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
openwork-diagnostics Skipped Skipped Aug 4, 2026 1:56pm

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying openwork with  Cloudflare Pages  Cloudflare Pages

Latest commit: 47392f7
Status: ✅  Deploy successful!
Preview URL: https://00524f3d.openwork.pages.dev
Branch Preview URL: https://agent-automations-den-hosted.openwork.pages.dev

View logs

@reachjalil

Copy link
Copy Markdown
Collaborator Author

Engine update: per review direction, v1 will not ship the OpenCode binary. #3477 (stacked on this branch) replaces the execution leg with a native TypeScript engine behind the same AutomationEngineAdapter contract — same durable shell, same conformance suite, binary and @opencode-ai/sdk removed from the Den image. Review that diff for the engine swap; this PR remains the base feature.

@vercel
vercel Bot temporarily deployed to Preview – openwork-diagnostics August 4, 2026 04:45 Inactive
@reachjalil reachjalil changed the title feat(automations): run Automations remotely on Den feat(automations): run Automations remotely on Den with host OpenCode Aug 4, 2026
@reachjalil
reachjalil marked this pull request as ready for review August 4, 2026 04:46
@reachjalil
reachjalil marked this pull request as draft August 4, 2026 08:20
@reachjalil reachjalil changed the title feat(automations): run Automations remotely on Den with host OpenCode feat(automations): execute Den-scheduled Automations on desktop runners Aug 4, 2026
@reachjalil
reachjalil marked this pull request as ready for review August 4, 2026 08:55
@vercel
vercel Bot temporarily deployed to Preview – openwork-diagnostics August 4, 2026 09:59 Inactive
@vercel
vercel Bot temporarily deployed to Preview – openwork-diagnostics August 4, 2026 11:41 Inactive
benjaminshafii
benjaminshafii previously approved these changes Aug 4, 2026
Comment thread ee/apps/den-api/src/server.ts
The scheduler claimed due Automations with their persisted provider/model
selection without checking the owner's current access, so a revoked grant
or removed provider kept dispatching to the owner's desktop runner.

Access is now re-resolved at all three dispatch points: the scheduler
tick and the desktop claim durably skip the occurrence with the authority
failure code (the schedule still advances, and the receipt says why the
run did not execute), and Run now fails with the mapped 409 instead of
queueing. Source-pinned by test so no dispatch path can drop the check.

Identified by Warden diff-security-review L9U-X9E.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread apps/desktop/electron/main.mjs
The automationRunnerConfigure IPC handler accepted any renderer-supplied
baseUrl, so a compromised renderer could point the main-process runner at
an attacker endpoint and leak the bearer token through subsequent SSE and
API requests. configure() now validates the URL before storing it: https
origins only, plain http reserved for loopback development hosts, and
embedded credentials rejected. An invalid URL leaves the runner
disconnected, and a test proves no request ever leaves for a rejected
endpoint.

Identified by Warden diff-security-review JZP-2AT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants