feat(automations): native TypeScript engine — remove the OpenCode binary from v1 - #3477
Closed
reachjalil wants to merge 1 commit into
Closed
feat(automations): native TypeScript engine — remove the OpenCode binary from v1#3477reachjalil wants to merge 1 commit into
reachjalil wants to merge 1 commit into
Conversation
…pt engine Remove the engine binary from v1. The Den image no longer downloads opencode, and @opencode-ai/sdk leaves den-api; execution now runs fully in-process behind the same AutomationEngineAdapter contract. The native runtime is small because the isolation contract already stripped everything else: a bounded chat-completions tool loop against the resolved provider, plus the two run-scoped OpenWork Connect tools called through @modelcontextprotocol/sdk (already a dependency). Every model path Den can resolve speaks that one wire protocol - the free zen model, the OpenWork inference proxy, and custom providers, which the provider API restricts to OpenAI-compatible packages at creation time; anything else fails closed before a request is made. The Connect endpoint's tool inventory is verified at admission and the run refuses to start on any mismatch. Credentials become an Authorization header only and never enter prompts, observations, or persisted engine state. The durable adapter shell (receipts, ordered events, reattachment, timeout, cancellation) is unchanged apart from engine-neutral naming and the openwork-den-native-v1 adapter id; the conformance suite still passes, and the new adapter tests exercise the real loop against local mock provider and MCP servers, including credential-rejection mapping, fail-closed inventory, and mid-flight abort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Aug 4, 2026
Collaborator
Author
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.
Removes the OpenCode binary from Automations v1 and ships execution as a native TypeScript engine inside den-api, behind the same provider-neutral
AutomationEngineAdaptercontract that #3466 introduces. Stacked onagent/automations-den-hosted; review this diff alone to see exactly what the engine swap changes.Why remove the binary
The first engine spawned a disposable
opencode servecompiled binary bundled into the Den image. It worked, but it put a third-party executable in the server image, added child-process/port lifecycle management, and tied security patch cadence to opencode releases — for an execution surface that no longer needs any of it. An Automation run has no filesystem, shell, browser, or workspace tools by design. What remains is exactly:search_capabilities/execute_capability) served by Den's own run-scoped Connect endpoint.Every model path Den can resolve speaks one wire protocol — the free zen model, the OpenWork inference proxy (
/api/v1/chat/completions), and custom providers, which the provider API already restricts to@ai-sdk/openai-compatible/@ai-sdk/openaiat creation time. The MCP client (@modelcontextprotocol/sdk) was already a den-api dependency.What this PR does
native-runtime.ts— in-process engine attempt: authority + Connect-token expiry checks, provider endpoint resolution, bounded (32-step) non-streaming chat loop, Connect tool calls via the MCP SDK client with the run bearer token, usage + costMicros accounting, observation emission.tools/listmust be exactly the reviewed pair or admission fails.Authorizationheader only — never in prompts, observations, receipts, or persisted engine state (asserted in tests).execution-adapter.ts— engine-neutral renames only (openwork-den-native-v1); the durable receipt/event/reattachment/timeout/cancellation shell is untouched.opencode-runtime.ts,@opencode-ai/sdkfrom den-api, and the entire binary-download block fromDockerfile.den(AUTOMATIONS_OPENCODE_BINgone; state env renamed toAUTOMATIONS_STATE_DIR, persistent/data/openwork-automationsvolume unchanged — receipts still live there).What deliberately does not change
Schema, routes, scheduler, run lifecycle, thread contract, desktop UI, feature flag.
engineKindon new cloud threads records the new adapter id; the UI derives cloud identity fromexecutionLocationonly (pinned by tests in #3466). The adapter contract keeps engines replaceable — OpenCode or anything else can return later as an alternative implementation without touching the domain.Honest v1 limits
provider_unavailable.ordered_at_least_oncecontract — practically identical to before, since the opencode child process died with den-api anyway.What the binary already supported, and where each capability went
For reviewers weighing the swap — what the OpenCode binary brought to the table, and what happened to each piece:
@ai-sdk/openai-compatible/@ai-sdk/openai; free zen + inference proxy also speak that protocol. The breadth was latent, not usablenpminsidenative-runtime.ts— no contract changestream: true+ SSE parsing; the ordered-events contract already accommodates itordered_at_least_oncetools/listmust equal the reviewed Connect pair)0700cwdNet: the two capabilities with user-visible value that this engine gives up (provider breadth, streaming) were either unreachable through the product or invisible at the event surface — and both have contained, contract-compatible recovery paths. The durability semantics reviewers might worry about (
at_least_oncere-execution after a mid-run crash) are unchanged from the binary; neither engine resumed a half-finished attempt.Checks run on this branch
ee/apps/den-apiautomations tsc (tsconfig.automations-test.json) ✅automations-native-adapter.test.ts✅ 9/9 — includes the real in-process loop against local mock provider + mock Streamable-HTTP MCP servers: happy path with usage/cost accounting, credential-rejection →provider_unavailable, fail-closed unreviewed-tool inventory (provider never called), mid-flight abort, receipt reattachment/replay, cancellation, and no-secret-leak assertionsautomations-model-authority(4/4),automations-run-boundary(3/3) ✅packages/automationsconformance suite ✅ 9/9Deferred to the hardening pass (unchanged from #3466): MySQL-live integration suite, restart-recovery soak, closed-desktop acceptance run.
🤖 Generated with Claude Code