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
Design additive prompt layering for app agent personas #34774
OpenCode V2 currently treats a configured agent system prompt as a replacement for the model-family OpenCode system prompt. That is surprising for embedded/chat products such as OpenCode Agent: a short Discord/Slack persona can accidentally remove the baseline coding-agent behavior.
We should design a clearer prompt layering model before changing behavior.
Current observed behavior
In V2 request assembly, a non-empty agent system replaces the provider/model-family prompt:
So a Discord persona like “be a concise teammate in chat” becomes the privileged system prompt instead of being added after the standard OpenCode prompt.
Design directions to gang-grill
Additive app/persona instructions by default
Always include SessionRunnerSystemPrompt.provider(model).
Add app/channel persona as a separate later system part.
Split replacement from additions
Example fields: systemOverride vs instructions / persona.
Replacement stays possible, but has to be explicit.
Named prompt stack
Model/provider base prompt
Selected agent behavior
App/channel persona
Durable session baseline/context
UI/debug surfaces can show each layer separately.
Why this matters
Embedded agents need product/chat persona without losing core OpenCode behavior.
Debugging “why did the agent behave this way?” needs visibility into the effective prompt stack.
Future app integrations should not have to copy/paste or manually preserve the base coding prompt.
This issue is intentionally tagged gang-grill so we can collect design topics and later run a focused team discussion without pinging everyone immediately.
Replacement semantics are still present and covered by tests, so the design question remains open. #34941 now gives embedders an additive per-session context seam, while #34721 tracks the concrete agent-system bug.
The remaining scope here is the agent configuration contract: explicit override versus additive instructions, ordering relative to provider and durable context, and whether the effective prompt stack needs inspection/debug visibility.
Consolidated #34721 here. Preserve its concrete requirement: hosted/custom agents need a supported additive persona/instruction path without dropping the stock provider/model prompt. Verify both V2 and any still-applicable current/V1 behavior after the design lands.
Drafted with an AI assistant (opencode). The code references and captured request output were checked against this repository; the reasoning and wording are mine to defend.
Update: the fallback changed after this issue was opened, and it changes the tradeoff
This was going to be a separate issue, but this is the right place for it.
#42638 (2026-08-14) replaced the fallback that this discussion assumes. At the time this issue was filed, the fallback was per-family persona — gpt.txt, anthropic.txt, gemini.txt at 100–155 lines each. That is why "replacement" was defensible: an app persona and a 150-line model prompt are two competing answers to the same question, and the author should pick one.
That is no longer true. The fallback is now session/runner/prompt/system.txt, 14 lines, plus per-tool rules derived from the tools the request actually advertises:
This is the part I would add to direction 1 in the summary. The rules SessionSystemPrompt.make splices in are tool contracts, not style:
- Use the edit tool for targeted changes to existing text files. It replaces the exact text
in `oldString` with `newString`, and the values must differ. By default, `oldString` must
occur exactly once. ... set `replaceAll` to true to replace every occurrence.
- Prefer dedicated tools over shell commands; fall back to the shell when a tool cannot do what you need.
- Use the write tool to create files or completely replace their content. Prefer using the edit tool for targeted changes.
These describe how the tools behave. An author adding a persona is not expressing a competing preference about how to write code — they are adding information the model does not otherwise have. Dropping them is a correctness loss, not a tradeoff.
Evidence
Two agents in ~/.config/opencode/agents/, same location, same tools advertised, same model. auditor has a two-line body, runner is frontmatter only.
LLMRequest.system[0] for auditor:
You are an auditor. You inspect code and report findings. You never edit anything.
LLMRequest.system[0] for runner:
You are an AI agent running in OpenCode, a coding agent harness. Help the user accomplish their goals using the tools you have available.
# Harness
- Responses are rendered as GitHub-flavored Markdown.
- `<system-reminder>` blocks are harness instructions, not user-authored content. Read and follow them.
- Prefer parallelizing independent tool calls.
- Prefer dedicated tools over shell commands; fall back to the shell when a tool cannot do what you need.
- Do not chain shell commands with separators like `echo "====";` or `printf '---';` ...
- Use the write tool to create files or completely replace their content. Prefer using the edit tool for targeted changes.
- Use the edit tool for targeted changes to existing text files. It replaces the exact text in `oldString` with `newString`, ...
# Communication
...
# Working in codebases
...
A subagent that writes code has no description of how edit resolves an ambiguous match.
Additive insertion already ships
Direction 1 in the summary — "add app/channel persona as a separate later system part" — has precedent one slot over. packages/core/src/plugin/identity.ts:
Model identity is layered on top of whatever the agent contributed. Agent role can use the same insertion without new machinery.
Ordering, since the summary lists it as open scope
Recommended:
[0] harness + tool guidance derived from the tool snapshot
[1] agent role agent.system
[2] model identity existing splice
[3] durable baseline AGENTS.md, skills, date, <env>
[4] plugin extras
Two reasons:
Cache. With the harness at [0], agents that share a tool snapshot share the prefix. Today a custom agent's system[0] is unique, so the shared prefix diverges at byte zero per agent.
Stable before volatile. The role is stable; the baseline churns daily. Role above baseline means role changes invalidate cache instead of baseline changes invalidating everything below it.
Tool guidance stays folded into [0] rather than becoming its own layer, because it is conditional on the advertised tools. Telling a model about edit while the permission set denies edit would make the prompt lie.
On direction 2, two notes
The opt-out does not need an enum. Replacement is the non-default, so systemMode: "replace" | undefined is enough — "prepend" is the default behavior and naming it adds surface to argue about.
title, summary and compaction are the real consumers of full replacement. They deny all tools, so the tool guidance is already empty for them, but harness prose about "working in codebases" and parallelizing tool calls is noise in a title generator. They would set the opt-out explicitly.
One case that dissolves under additive
An empty body is currently indistinguishable from a missing one. packages/core/src/config/plugin/agent.ts assigns markdown.content.trim() unconditionally, so a frontmatter-only agent file yields system: "", which is falsy at the ternary above — the agent looks configured and silently runs the stock prompt. Under an additive default, "no role" is just a valid empty layer and this needs no separate fix.
Filed separately as #56402 and closed as a duplicate of this one.
Activity
kitlangton commentedon Jul 4, 2026
Replacement semantics are still present and covered by tests, so the design question remains open. #34941 now gives embedders an additive per-session context seam, while #34721 tracks the concrete agent-system bug.
The remaining scope here is the agent configuration contract: explicit override versus additive instructions, ordering relative to provider and durable context, and whether the effective prompt stack needs inspection/debug visibility.
opencode-agent commentedon Jul 5, 2026
Consolidated #34721 here. Preserve its concrete requirement: hosted/custom agents need a supported additive persona/instruction path without dropping the stock provider/model prompt. Verify both V2 and any still-applicable current/V1 behavior after the design lands.
geril07 commentedon Oct 17, 2026
Drafted with an AI assistant (opencode). The code references and captured request output were checked against this repository; the reasoning and wording are mine to defend.
Update: the fallback changed after this issue was opened, and it changes the tradeoff
This was going to be a separate issue, but this is the right place for it.
#42638 (2026-08-14) replaced the fallback that this discussion assumes. At the time this issue was filed, the fallback was per-family persona —
gpt.txt,anthropic.txt,gemini.txtat 100–155 lines each. That is why "replacement" was defensible: an app persona and a 150-line model prompt are two competing answers to the same question, and the author should pick one.That is no longer true. The fallback is now
session/runner/prompt/system.txt, 14 lines, plus per-tool rules derived from the tools the request actually advertises:The fallback is not persona anymore
This is the part I would add to direction 1 in the summary. The rules
SessionSystemPrompt.makesplices in are tool contracts, not style:These describe how the tools behave. An author adding a persona is not expressing a competing preference about how to write code — they are adding information the model does not otherwise have. Dropping them is a correctness loss, not a tradeoff.
Evidence
Two agents in
~/.config/opencode/agents/, same location, same tools advertised, same model.auditorhas a two-line body,runneris frontmatter only.LLMRequest.system[0]for auditor:LLMRequest.system[0]for runner:A subagent that writes code has no description of how
editresolves an ambiguous match.Additive insertion already ships
Direction 1 in the summary — "add app/channel persona as a separate later system part" — has precedent one slot over.
packages/core/src/plugin/identity.ts:Model identity is layered on top of whatever the agent contributed. Agent role can use the same insertion without new machinery.
Ordering, since the summary lists it as open scope
Recommended:
Two reasons:
[0], agents that share a tool snapshot share the prefix. Today a custom agent'ssystem[0]is unique, so the shared prefix diverges at byte zero per agent.Tool guidance stays folded into
[0]rather than becoming its own layer, because it is conditional on the advertised tools. Telling a model abouteditwhile the permission set denieseditwould make the prompt lie.On direction 2, two notes
systemMode: "replace" | undefinedis enough —"prepend"is the default behavior and naming it adds surface to argue about.title,summaryandcompactionare the real consumers of full replacement. They deny all tools, so the tool guidance is already empty for them, but harness prose about "working in codebases" and parallelizing tool calls is noise in a title generator. They would set the opt-out explicitly.One case that dissolves under additive
An empty body is currently indistinguishable from a missing one.
packages/core/src/config/plugin/agent.tsassignsmarkdown.content.trim()unconditionally, so a frontmatter-only agent file yieldssystem: "", which is falsy at the ternary above — the agent looks configured and silently runs the stock prompt. Under an additive default, "no role" is just a valid empty layer and this needs no separate fix.Filed separately as #56402 and closed as a duplicate of this one.