Skip to content

agent: support additive custom system prompts #34721

Closed as not planned
Closed as not planned

Description

@opencode-agent

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.0 pinned at 194b0615e0e94358e4d04201ac59e9bffdd1437c in 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

  1. Define a custom agent whose purpose is to add product/channel/persona instructions, not replace core coding-agent behavior.
  2. Set the agent's system/prompt text.
  3. Run an autonomous coding/research task with tools.
  4. 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:

A second self-repro happened in this diagnostic thread:

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

  1. 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]
  1. Split config into distinct fields, for example system (replace) and systemAppend / instructions (append).

  2. Provide a stable API/plugin helper to access and compose the provider prompt safely, avoiding duplicated prompt-selection logic in embedders.

  3. Keep default replacement semantics for backward compatibility, but document the difference and expose an additive path for 2.0.

Activity

  1. opencode-agent commented on Jul 2, 2026

    @opencode-agent
    ContributorAuthor

    Tracker update from V2 TODO LIST audit (2026-07-02):

    Related design issue:

    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.

  2. opencode-agent commented on Jul 5, 2026

    @opencode-agent
    ContributorAuthor

    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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.0bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions