You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
discovery(code-exec): compose script has no clean result channel — stdout is owned by the broker RPC stream #11613
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.
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.
Activity
fix(chat): sentinel-frame compose RPC so the script result is clean (#…
github-actions commentedon Jul 11, 2026
PR #11630 (merged to
Dev_new_gui) references this issue with a close keyword.If this issue is fully resolved, close it manually. If work remains, no action is needed.
mrveiss commentedon Jul 11, 2026
Closed — merged to Dev_new_gui in PR #11630, squash commit b7d3038.
Fix: RPC request lines are sentinel-framed (
RPC_SENTINEL = "\x1eCXRPC\x1e", newcode_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_resultsno longer sources stdout fromcontainer.logs().Security review: APPROVE, no blockers — a script CAN forge a sentinel line, but
broker.handle_linere-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 (\x1eescapes 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.