Repository navigation
Codemode scripts: confined multi-tool scripts on Den, savable and reusable, behind an org flag - #3645
Conversation
…nput bindings Vendored from anomalyco/opencode@38e10eb (MIT; NOTICE + UPSTREAM.md record provenance and divergences). Drops the OpenAPI adapter. Adds a local divergence: CodeMode.execute({ bindings }) seeds read-only root-scope values through the plain-data boundary, enabling saved parameterized scripts. 244 upstream + bindings tests pass via bun test.
…nd org flag - codemodeScripts org capability (metadata.capabilities, default off), wired like mcpConnections: admin routes, GET /v1/org, rollout helper. - /mcp/agent registers execute_capability_script only when enabled: builds a per-call tool tree (tools.den.<op> via invokeMcpOperation in-process + tools.<connection>.<tool> via executeExternalCapability, tool policy applied at build), runs the confined interpreter with host limits, and search_capabilities matches now carry scriptPath. - New 'script' config object type: executeMarketplaceCapability executes saved scripts with Ajv-validated typed input, least-authority tree restricted to declared requiredCapabilities, and fail-closed capability_unavailable (providerCallAttempted: false) when a declared tool is missing or policy-disabled. - codemode_run receipts for every run (source adhoc|plugin:<id>) + GET /v1/codemode-runs (admin: org-wide, member: own) with safe MCP tag. - drizzle migration 0057: codemode_run table + object_type enum value.
- den-admin-panel: codemodeScripts capability checkbox beside Connect. - New Script runs screen (nav gated by capabilities.codemodeScripts): status, truncated source, tool calls, duration, when.
- codemode-scripts.slow.test.ts: the approved 7-frame narration as observable assertions — org flag gating, deterministic rail probes, a real model turn answering idle-workers as one script step, parallel cross-connection fan-out with witness proof, save through the capability rail, teammate discovery + typed re-run, receipts, dashboard vision check, and fail-closed tool-policy proof. - testkit: locally booted Den seeds DEN_BOOTSTRAP_ADMIN_EMAILS so specs can flip /v1/admin capabilities; MySQL probe honors OPENWORK_EVAL_MYSQL_URL instead of hardcoding 3306.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Photo roll — codemode-scripts-a-40-step-question-becomes-one-step-gets-saved-and-a-teammate-r — 2/2 frames passed · 13 facts✅ 2/2 frames passed · 13 facts · 16 expectations passed · 0 failed ℹ️ FACT — 1. With the org flag off, execute_capability_script is not even discoverabletools/list before enabling: ["search_capabilities","execute_capability"]
ℹ️ FACT — 2. Flipping the Codemode scripts org capability registers the script tooltools/list after enabling: ["search_capabilities","execute_capability","execute_capability_script"]
ℹ️ FACT — 3. The script runtime exposes the Den catalog and both org connections as namespacesObject.keys(tools): [
ℹ️ FACT — 4. In-program tool search finds the Den workers operationsearch("list workers") paths: ["tools.den.getWorkers","tools.den.deleteWorkersById","tools.den.getWorkersById","tools.den.getWorkersByIdRuntime","tools.den.listAutomationRuns","tools.den.listAutomations","tools.den.patchWorkersById","tools.den.postWorkers","tools.den.postWorkersByIdActivityHeartbeat","tools.den.postWorkersByIdRu
ℹ️ FACT — 5. A script can call a Den catalog operation and see the seeded workerstools.den.getWorkers({}) → {
ℹ️ FACT — 6. The idle-workers question ran as a single script step, not one call per workerSession tool calls: {"script":2,"single":0}; assistant reply: builder-1, builder-2, builder-3
✅ PASS — 7. A chat conversation answers which workers are idle, naming builder workers
ℹ️ FACT — 8. One script reached both org connections in parallel and returned only the combined resultResult: {
ℹ️ FACT — 9. The idle-workers script is saved as an org capability through the capability railexecute_capability(postPlugins) → {
ℹ️ FACT — 10. The agent saved the script as an org config object of type scriptconfig-objects?type=script returned 1 row(s); idle-workers-report present: true
ℹ️ FACT — 11. A teammate's agent discovers the saved script through capability searchMember search matches: [{"name":"plugin:plg_01kzkr90xpe64v2xh383fxqhrt:cob_01kzkr90xseaxa4b1ttmxzgdsc","kind":"script"},{"name":"deleteWorkersById"},{"name":"getWorkersById"},{"name":"getWorkersByIdRuntime"},{"name":"patchWorkersById"}]
ℹ️ FACT — 12. The teammate runs the saved script by name with typed input and gets fresh data from the same programstatus: executed; value: ["builder-1","builder-2","builder-3"]
ℹ️ FACT — 13. Every script run left a receipt with its tool calls and durationruns: 7 (adhoc: 6, saved: 1); all carry toolCalls+durationMs: true
✅ PASS — 14. A dashboard screen lists script runs with status and duration information
ℹ️ FACT — 15. Disabling a connection tool makes the dependent saved script fail closed without reaching the providerBlocked run payload: {"error":"capability_unavailable","message":"Required capability tools.drive_mock.mock_echo (mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo) is unavailable or disabled for this organization.","providerCallAttempted":false,"missing":[{"capabilityName":"mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo","scriptPath":"tools.drive_mock.mock_echo"}]}; provider calls after policy flip: 0
Roll created 2026-08-09T17:06:10.392Z · Source: |
…r lockfile churn Warden findings on #3645: - buildExternalMcpToolTree listed tools for connections pending admin OAuth issuer review, constructing the OAuth provider (and possible token refresh) against a configuration an admin had blocked. Filtering is now the exported pure predicate isCodemodeEligibleConnection, which mirrors probeExternalMcpConnection: skip allDisabled AND oauthIssuerReviewRequiredAt. - restrictCodemodeToolTree used `tools[ns] ??= {}`, so a connection named __proto__ wrote a tool definition onto Object.prototype. Namespaces now reserve __proto__/constructor/prototype, and the tree is accumulated in Maps and materialized with Object.fromEntries (defineProperty semantics). CI repair — the earlier pnpm install churned the lockfile: - restored the xlsx CDN tarball integrity hash (ERR_PNPM_MISSING_TARBALL_INTEGRITY broke openwork-tests on both runners under --frozen-lockfile) - pinned codemode's acorn to 8.16.0, the version already resolved in the workspace, removing an unrelated 8.16.0 -> 8.18.0 bump; lockfile is now additions only (167/0, was 184/15) - reverted an unrelated msgpackr-extract allowBuilds entry; pnpm-workspace.yaml is now identical to dev - Dockerfile.den copies packages/codemode (den-api depends on it) Simplification: deleted buildCodemodeManifest, which was dead outside its own test — both tree builders already return their own manifest.
effect -> msgpackr -> msgpackr-extract ships an optional native accelerator with a build script. Without an explicit allowBuilds decision, CI's `pnpm install --frozen-lockfile` fails with ERR_PNPM_IGNORED_BUILDS. msgpackr falls back to pure JS, so this is declared false rather than compiling native code in every CI run and image build. Reverting this in the previous commit was my error: it is a direct consequence of adding effect, not unrelated churn.
Review comments addressed + CI statusBoth Warden findings fixed (
Also simplified: deleted CI: 4 of 5 failures were mine and are fixed. They all traced to lockfile churn from my
The one remaining red check,
Net: the image builds fine; den-api just doesn't survive an unreachable DB at boot because that Better Auth plugin init is unguarded. Happy to fix it in a separate PR (guard Verification tape re-published for the current head: 16/16 expectations, all 7 narrated frames. |
The PR was opened as a draft, so Warden skipped it and never re-analyzed the head that fixes its two findings (OAuth-review bypass, prototype pollution). Warden does not support the ready_for_review event by design — .github/workflows/warden.yml notes that draft-to-ready PRs get analyzed on their next push — so this empty commit is that push.
Deploying openwork with
|
| Latest commit: |
ea0613e
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b8726742.openwork.pages.dev |
| Branch Preview URL: | https://feat-codemode-scripts.openwork.pages.dev |
There was a problem hiding this comment.
Warden security clearance: clear. No blocking findings (security or desktop↔den sync) in this diff (ea0613e356b2f75feabb1beac5ca042c886284c8). 1 advisory desktop↔den sync note(s) were posted as review comments; they do not affect clearance. Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run
…bjects on desktop Smoke health endpoint ran the image with DATABASE_URL pointing at 127.0.0.1 inside the container, so the DB was never reachable. den-api binds :8788, then Better Auth's oauth-provider seedResources queries oauthResource during createAuthContext and the unhandled rejection kills the process — so docker port never resolved and the failure surfaced as 'No such container'. Pre-existing on dev (verified by building the origin/dev image and reproducing the identical DrizzleQueryError) and on PRs #3648/#3640; dev pushes look green only because the step is gated on pull_request. Not fixed by suppressing the crash: better-auth caches the context promise (auth/base.mjs:8 initFn(options), awaited per request), so a rejected init stays rejected for the process lifetime. Swallowing it would yield a container that passes health checks with permanently dead auth, when crashing lets the orchestrator restart into a healthy state. The test premise was wrong, not the app. The den-api smoke now stands up mysql:8.4 on a dedicated network, waits for a real mysqladmin ping, applies the schema with the image's own bootstrap.js (the asset the preceding step already asserts exists), and only then boots den-api against it — so the smoke additionally proves migrations apply, including 0057. Containers are no longer --rm, so logs survive a crash and get dumped on failure. Other matrix images keep the original path. Also fixes Warden's third finding: the desktop parser rejected the new 'script' config object type, silently dropping saved scripts from the Connect inventory.
Warden flagged mysql:8.4 as a mutable tag: a registry-side replacement would execute unreviewed code inside the PR workflow, sharing the smoke test's docker network. Pinned to the current multi-arch index digest (verified to contain linux/amd64 for CI runners, and confirmed to pull, boot, and pass the same mysqladmin readiness probe the step uses). Worth noting the tag had already moved: the digest cached locally from an earlier pull no longer matched the registry's 8.4, which is exactly the drift being closed.


What
Organizations can flip on Codemode scripts (
metadata.capabilities.codemodeScripts, default off) and get:execute_capability_scripton/mcp/agent— a confined JavaScript interpreter over the org's capabilities:tools.den.<operation>(Den REST catalog, in-process dispatch) andtools.<connection>.<tool>(external MCP connections, policy applied at tree build). One model step replaces N sequentialexecute_capabilityround trips;Promise.allfan-out works across connections; only the returned value enters model context.scriptconfig object type on plugin-arch.executeMarketplaceCapabilityexecutes them with Ajv-validated typed input (bindingsdivergence in the vendored interpreter), a least-authority tree restricted to declaredrequiredCapabilities, and fail-closedcapability_unavailable(providerCallAttempted: false) when a declared tool is missing or policy-disabled. Saved scripts inherit versioning, team grants, search discovery, and marketplace sharing.codemode_run(sourceadhoc|plugin:<id>, tool calls, duration, digest) +GET /v1/codemode-runs+ a Script runs dashboard screen.@openwork/codemodefrom anomalyco/opencode@38e10eb (MIT, NOTICE/UPSTREAM.md; OpenAPI adapter dropped; input-bindings divergence). 244 upstream+bindings tests pass.Why
The two-tool facade (
search_capabilities/execute_capability) makes every multi-step task N model round trips with N full payloads through context. This collapses chains into one call, makes the proven program a reusable org capability, and runs it fully server-side (cloud path from day one). Sibling groundwork for #3601 (its "replaceable port" runtime can be this adapter).Verification (agent-first)
evals/specs/codemode-scripts.slow.test.ts— the approved voiceover narration encoded frame by frame; 16/16 evidence facts pass on a local stack (real Den boot, real Electron desktop, real Sonnet model turn, two mock MCP witnesses):tools/list; platform-admin flip → present$codemode.search, catalog dispatchpostPluginswith ascriptcomponent) →config-objects?type=scriptshows itsearch_capabilitiesand runs it by name with{days: 14}→status: executed, fresh datamock_echovia tool policy → saved script fails closed, provider witness records zero callsAlso ran:
packages/codemode:bun test→ 244 passbun test(agent-codemode-script, codemode-tools, codemode-rollout, organization-capabilities, agent-mcp-routes, marketplace-codemode-scripts, marketplace-codemode-readiness, codemode-runs, marketplace-capabilities) → 65 pass, 0 failtsc --noEmitclean on den-api, den-web, evals;pnpm --filter @openwork-ee/den-api buildexit 0mcp-agent-config-policy.test.tsreproduce on cleanorigin/dev(stale expectations vs OpenAPIanyOf), not touched hereEvidence tape publishes to this PR next (fraimz roll
2026-08-09T16-11-01-887Z-codemode-scripts-…, 16 facts + chat and dashboard frames).Notes for review
invokeMcpOperation/executeExternalCapability; member/policy checks run per call. Sandbox = vendored tree-walking interpreter (no eval/vm), plain-data boundary, blocked prototype members, host-owned limits (170s/50 calls/64KB output).0057is additive (new table + enum value).