Skip to content

discovery(code-exec): compose script has no clean result channel — stdout is owned by the broker RPC stream #11613

Closed

Description

@mrveiss

Found writing the #11596 smoke gate (PR pending), from reading secure_sandbox_executor._pump_broker_io/_drain_lines + tool_handler._compose_result_message.

Problem

The pump treats EVERY stdout line from the sandboxed script as a JSON-RPC tool request (broker.handle_line). The script's shim writes RPC requests to stdout and reads replies from stdin. But the script's FINAL result has nowhere clean to go:

  • If the script print()s its answer, that line hits stdout → the pump feeds it to broker.handle_line → parsed as a bogus RPC (tool empty/unknown → tool not injectable error reply written to stdin the script no longer reads).
  • _compose_result_message then surfaces result.stdout (from container.logs() after exit), which contains ALL the intermediate RPC-request JSON lines intermixed with any script prints. The LLM sees RPC noise, not a clean return value.

The design doc (§2.2) shows scripts doing return {action: 'done', value: ...}, but a bare python3 file.py has no return channel and stdout is owned by the RPC protocol.

Impact

Feature is flag-off (AUTOBOT_CODEEXEC_ENABLED=false), so not live. But the compose result delivered to the chat loop is polluted/ambiguous — a functional gap the #11596 on-box smoke (test_smoke_two_readonly_tools_merge_in_one_dispatch) will fail on if run, by design.

Fix options

  • Dedicated result channel: a sentinel line prefix the pump recognizes and routes to a result buffer (not the broker), OR a result file the script writes and the executor reads post-exit, OR fd 3 for RPC leaving stdout free for the script.
  • Whichever: _compose_result_message must surface ONLY the script's declared result, not the RPC transcript.

Gate: resolve before #11596 can go green and before AUTOBOT_CODEEXEC_ENABLED=1.

Activity

  1. github-actions commented on Jul 11, 2026

    @github-actions
    Contributor

    PR #11630 (merged to Dev_new_gui) references this issue with a close keyword.

    fix(chat): sentinel-frame compose RPC so the script result is clean (#11613)

    If this issue is fully resolved, close it manually. If work remains, no action is needed.

  2. mrveiss commented on Jul 11, 2026

    @mrveiss
    OwnerAuthor

    Closed — merged to Dev_new_gui in PR #11630, squash commit b7d3038.

    Fix: RPC request lines are sentinel-framed (RPC_SENTINEL = "\x1eCXRPC\x1e", new code_exec/protocol.py); the executor pump routes only sentinel lines to the broker and captures every other stdout line as the script's clean result. The LLM script returns its answer by printing to stdout — RPC is transparent. _collect_broker_results no longer sources stdout from container.logs().
    Security review: APPROVE, no blockers — a script CAN forge a sentinel line, but broker.handle_line re-validates every call against the injectable allowlist + forbidden_work + per-run budget regardless of source, so forgery gains nothing (documented in _drain_lines). No sentinel/JSON collision (\x1e escapes in json.dumps); cross-recv-chunk partial lines reassemble; per-broker budget.
    Tests: 67 code-exec (4 new incl. regression 'a script print must NOT reach broker.handle_line') + 129 regression pass; sabotage/flag-off verified. Discovery #11629 (_parse_logs never splits stderr — pre-existing).

    This CLEARS the first of the two code-exec enablement gates. Remaining gate: the #11596 on-box Docker smoke passing against a real sandbox image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions