Repository navigation
Description
Summary
Custom agent system prompts currently replace the stock provider/model OpenCode system prompt. For hosted/custom agents that only want to add channel/persona instructions, this drops core coding-agent behavior and can cause visible status narration, premature "I'm going to investigate" replies, and other degraded agent-loop behavior.
This appears relevant to both the current/V1 request path and the V2 core runner. Please tag this for 2.0.
Environment
- opencode version:
0.0.0pinned at194b0615e0e94358e4d04201ac59e9bffdd1437cin hosted OpenCode Agent runtime - OS: Linux gangprompt 7.0.9-arch1-1 x86_64
- Terminal: service/headless Discord runtime;
TERM=xterm-256color - Shell:
/usr/bin/bash - Install/channel: hosted OpenCode Agent release
fc6e486049d48ac63b529fca1f1945b841b82a08_194b0615e0e94358e4d04201ac59e9bffdd1437c - Active plugins: hosted runtime registers an app plugin that defines Discord/Slack agents
Reproduction
- Define a custom agent whose purpose is to add product/channel/persona instructions, not replace core coding-agent behavior.
- Set the agent's system/prompt text.
- Run an autonomous coding/research task with tools.
- Observe that the stock provider/model OpenCode coding prompt is omitted from the final system prompt whenever the agent has a custom system/prompt.
Expected Behavior
There should be a supported way for an agent to add instructions while preserving OpenCode's stock provider/model prompt.
For example, a custom hosted Discord agent should be able to append:
- chat/channel formatting rules
- Discord mention rules
- short persona/tone rules
- safety rules for private chat content
without losing the base prompt that tells the model how to behave as an OpenCode coding agent across tool loops.
Actual Behavior
Current request assembly uses replacement semantics.
In packages/opencode/src/session/llm/request.ts on current/dev:
const system = [
[
...(input.agent.prompt ? [input.agent.prompt] : SystemPrompt.provider(input.model)),
...input.system,
...(input.user.system ? [input.user.system] : []),
]
.filter((x) => x)
.join("\n"),
]If input.agent.prompt exists, SystemPrompt.provider(input.model) is not included.
In the V2 branch, packages/core/src/session/runner/llm.ts has improved fallback but still replacement semantics:
system: [agent.info?.system ? agent.info.system : SessionRunnerSystemPrompt.provider(model), system.baseline]
.filter((part): part is string => part !== undefined && part.length > 0)
.map(SystemPart.make),If agent.info.system exists, SessionRunnerSystemPrompt.provider(model) is not included.
Additional Context
This caused a hosted OpenCode Agent regression in Discord.
The hosted runtime defines an opencode-agent-discord agent to add chat persona/channel instructions. The app code explicitly notes replacement behavior:
// an agent's `system` replaces the stock per-model opencode prompt
// (anthropic.txt, etc.) rather than appending to it
...
agent.system = system;Observed production evidence from a Discord thread:
- Discord thread: https://discord.com/channels/1391832426048651334/1521717083912802557
- Representative message: https://discord.com/channels/1391832426048651334/1521717083912802557/1521723659562123266
- OpenCode session:
ses_agent_06d6fb4d-59b1-42ca-b22f-ef507e4c7442 - Model:
opencode/gpt-5.5, varianthigh - Session event counts: 62
session.next.text.ended, 151session.next.tool.called - No direct Discord send/reply/thread tools were called in that session; visible narrations came from normal assistant text events.
A second self-repro happened in this diagnostic thread:
- Discord thread: https://discord.com/channels/1391832426048651334/1521724663850336336
- OpenCode session:
ses_agent_1a337d2e-dcb6-4179-b7ff-accb81d5eec5 - The assistant emitted a visible status line ("I'm digging through session_message now...") as normal assistant text, then continued tool work. That text was not sent through a Discord tool.
This may be partly model behavior, but losing the stock coding-agent prompt is a plausible root cause because hosted/channel agents only need additive instructions. The model no longer receives the full provider-specific coding-agent operational prompt.
Possible fixes
- Add an explicit additive mode for agents, for example:
{
"agent": {
"discord": {
"system": "additional channel/persona instructions",
"systemMode": "append"
}
}
}Request assembly could then use:
replace:[agent.system, baseline]append:[providerPrompt, agent.system, baseline]
-
Split config into distinct fields, for example
system(replace) andsystemAppend/instructions(append). -
Provide a stable API/plugin helper to access and compose the provider prompt safely, avoiding duplicated prompt-selection logic in embedders.
-
Keep default replacement semantics for backward compatibility, but document the difference and expose an additive path for 2.0.
Activity
opencode-agent commentedon Jul 2, 2026
Tracker update from V2 TODO LIST audit (2026-07-02):
Related design issue:
2.0,gang-grill)Suggested tracker state: use #34774 for the team design discussion / prompt-stack decision. Keep this issue as the concrete bug/implementation record only if we still need an additive custom-system-prompt implementation ticket after that design decision; otherwise close it as duplicate/superseded by the resolved design issue.
opencode-agent commentedon Jul 5, 2026
Consolidated into #34774. That issue now carries the concrete replacement-semantics bug/reproduction and the additive prompt-layer design decision, including applicability to the current/V1 path where relevant.