Skip to content

execute_code is structurally unavailable on the packaged desktop app — strands every code-execution-dependent plugin/skill (and leaves ADR 0092 D1's goal unreached) #2137

Description

@mabry1985

Summary

execute_code never registers a tool in the packaged desktop app. It's a hard, unconditional gate with no config override. The consequence isn't one broken feature — any plugin or skill whose capability routes through code execution is silently inert on our flagship distribution, including the Cowork archetype's entire document half.

This also means ADR 0092 D1 does not achieve its stated outcome: bundling the doc stack made cowork install on desktop, but its doc skills still can't run there.

The gate

plugins/execute_code/__init__.py:35-40

# Frozen desktop build: spawning a Python subprocess needs a standalone
# interpreter the PyInstaller binary doesn't ship. Don't register the tool
# there at all — it simply won't exist (matches the old core behavior).
if getattr(sys, "frozen", False):
    log.info("[execute_code] packaged desktop build — no standalone Python, tool not loaded")
    return

Root mechanism — plugins/execute_code/engine.py:143-155: code runs via create_subprocess_exec(sys.executable, …), and in a PyInstaller build sys.executable is the frozen server binary, not an interpreter. The reasoning is sound; the consequence is what needs a platform answer.

Observed on a live v0.107.0 desktop install

[execute_code] packaged desktop build — no standalone Python, tool not loaded
[plugins] loaded execute_code: 0 tool(s), 0 skill dir(s), 0 route(s), …
[plugins] loaded 7 plugin(s): artifact, delegates, execute_code, notes, careercoach, cowork, github

The agent, asked to produce a .docx:

"I don't have a code execution tool available in my current toolset — the docx skill requires execute_code to run python-docx and produce the .docx file."

Why this is bigger than docx

ADR 0092's own framing:

"The desktop app can't make a .docx" is a credibility gap for that positioning, not a plugin edge case.

and D1's stated effect:

cowork's hard requires_pip is simply satisfied — cowork installs and works on desktop with zero manifest change.

D1 delivered the first half (deps satisfied → installs) but not the second (works). cowork's docx/xlsx/pptx/pdf skills all author documents through execute_code, so on desktop they still cannot produce a file. "The desktop app can't make a .docx" is still true after #2123.

Generalizing — the blast radius is the capability tier, not the feature:

  • ADR 0083 Cowork archetype — a first-party flagship whose headline document half is dead on the very distribution it targets ("own your stack", local-first).
  • ADR 0092 D1 (feat(desktop): bundle the document-generation stack (ADR 0092 D1) #2123) — goal unreachable as-is. (The bundled libs do still earn their keep: save_file_artifact's preview extractors run in-process, so D2/D3 previews work. D1 is not wasted — it's just insufficient for generation.)
  • Any future plugin that computes rather than calls an API hits the same wall on desktop.

Secondary: the limitation is invisible

The plugin reports as loaded, the gate logs at INFO, and the operator-facing copy in engine.py says "turn it off under Settings ▸ Tools ▸ Enable execute_code" — implying it could be on. So a desktop operator can enable an arbitrary-code-execution capability and receive zero signal that it does nothing. That cuts against ADR 0071's "present only the boundary we enforce": here we present a capability we structurally can't provide.

Possible directions (core's call)

  1. In-process execution path for frozen builds — reuse the existing timeout / tools allowlist posture. Worth noting the plugin's own docstring already says subprocess isolation "is isolation, NOT a true sandbox" (the script reaches disk/network as the server user), so in-process may be less of a posture change than it first appears. Still a real security decision.
  2. First-party in-process document tools — build .docx/.xlsx/.pptx directly with the now-bundled libs, no subprocess. Narrower and safer than general code execution, and it directly delivers ADR 0092's goal without granting arbitrary execution. Probably the cheapest path to "the desktop app can make a .docx."
  3. Ship a standalone interpreter for execute_code to spawn (heaviest; likely previously rejected on bundle size).
  4. At minimum — make it visible. Surface "unavailable on desktop" in the plugin list / Settings / archetype catalog, and don't let an operator enable a no-op capability silently. This is worth doing regardless of which of 1-3 is chosen.

How this surfaced

Building the careercoach plugin's render_format: docx path on top of #2123/#2126. Everything verified green — doc libs confirmed extracted and importable in the running v0.107.0 sidecar (docx, openpyxl, pptx, reportlab, PIL), save_file_artifact verified working end-to-end on a real .docx (blob stored, text preview extracted, versioned in place) — and it still can't generate a document on desktop, purely because of this gate. Happy to provide the repro or test against a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions