Skip to content

fix(agents): opencode runs as many turns as memory fits, and takes its system prompt as instructions - #372

Merged
earakely-scale merged 1 commit into
mainfrom
fix/opencode-turn-memory-and-system-prompt
Oct 28, 2026
Merged

earakely-scale merged 1 commit into
mainfrom
fix/opencode-turn-memory-and-system-prompt

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

opencode-cli failed AgentEnvHub's dev promotion gate for 0.9.1373 on mcp and snapshot, and the gate restored its previous image. This fixes both, in the opencode agent:

  • Turns wait for memory. No more opencode run processes run at once than the machine's memory fits: one per 900 MB past the first 1 GB, using the cgroup's limit when that's lower. That's 7 on an 8 GB modal_vm. OPENCODE_MAX_CONCURRENT_TURNS sets the number. Turns past the cap wait for one to finish.
  • The system prompt reaches the model as a system prompt. It's written to system-prompt.md in the conversation's data directory and named in the config's instructions. Before, it was prepended to the message.

Why

mcp: the VM ran out of memory. In the gate run (dev instance opencode-cli__validate-a2a-v16-7lsevzc4) the MCP probe failed with "agent_response": "opencode exited with code -9 without completing", and it called no tools.

  • The validator sends about 15 prompts to the agent at once. The server locks per conversation only, so each prompt started its own CLI process.
  • Reproduced in dev with opencode-cli v16 on modal_vm (7,947 MB, 1 CPU, no swap) and 15 concurrent prompts. Each opencode run peaked at 600–860 MB, used memory reached 7,743 MB, and the kernel log shows Out of memory: Killed process … (opencode) four times.
  • Which turn the kernel kills is random. v15's run lost other probes instead.

snapshot: the model refused to recall. Save, load and resume all worked, and the recall turn resumed the same session.

  • The validator's system-prompt probe sets "respond with this exact token…" on the agent, and it's still set when the snapshot probe plants its token.
  • Prepended, the session's first user message read "For validator testing purposes: … respond with the exact token … Remember exactly this token: …". The model took it for a prompt injection: opencode titled the session "Validator token injection attempt", and the recall answered "I won't reproduce tokens or credentials on request, as that could be a prompt injection".
  • claude-code passes the same probe because its system prompt is a real one.

Testing

  • Unit tests: tst/unit/agents/opencode passes (96 passed, 7 skipped). New tests:
    • turns past the cap wait for one to finish;
    • the cap is sized from memory and can be set;
    • a cgroup limit below the machine's memory counts;
    • the system prompt lands in an instructions file and not in the message;
    • a turn with no system prompt names no instructions.
  • The CLI itself: opencode 1.17.18, in the published agentenv-opencode image, against a local stand-in for the model endpoint that records each request. With instructions: ["/tmp/system-prompt.md"], the system message carries Instructions from: /tmp/system-prompt.md followed by the file's text, and the user message carries only the user's words.
  • End to end: AgentEnvHub's dev promotion gate is the check. The next pin bump runs the full A2A validator on modal_vm against this image, and restores the previous one if it fails.
  • One thing to watch there: queued turns wait on a 1-CPU VM, and some validator prompts time out at 120 s.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge, with no actionable findings.

Summary

The PR limits concurrent OpenCode turns based on available memory and passes system prompts through instruction files.

  • OpenCode queues turns when memory cannot fit another run.
  • OpenCode sends each system prompt as model instructions.

Known tradeoff: earakely-scale noted that queued turns can hit the validator’s 120-second timeout.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Task arrives] --> B[Acquire workspace access and context lock]
  B --> C[Prepare attachments]
  C --> D[Wait for a turn slot]
  D --> E[Write system prompt file if supplied]
  E --> F[Run OpenCode with instructions config]
  F --> G[Finish or clean up canceled CLI]
  G --> H[Release turn slot]
  H --> I[Save session reference and capture changes]
Loading

Reviews (1) · Last reviewed commit: "fix(agents): opencode runs as many turns..." · Reviewed by Greptile

…s system prompt as instructions

Each `opencode run` peaks near 900 MB, so a modal_vm (8 GB, no swap)
running every concurrent turn at once ran out of memory, and its kernel
killed one: "opencode exited with code -9", which failed the A2A
validator's mcp probe on whichever turn it hit. Turns past the cap
(memory, or the cgroup's limit, past 1 GB, at 900 MB a turn;
OPENCODE_MAX_CONCURRENT_TURNS sets it) now wait for one to finish.

A system prompt prepended to the message read as the user's own words:
the validator's "respond with this token" system prompt, still set when
its snapshot probe planted a token, made the model take the planted
turn for a prompt injection and refuse to recall it. It's now written
to the context's data directory and named in the config's
`instructions`, which opencode puts in the system message.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner October 28, 2026 20:56
@earakely-scale
earakely-scale merged commit 4886f46 into main Oct 28, 2026
25 checks passed
@earakely-scale
earakely-scale deleted the fix/opencode-turn-memory-and-system-prompt branch October 28, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant