Skip to content

Custom markdown-defined subagents show "should only be called manually" in Task tool despite having description in frontmatter #27831

Closed as not planned

Description

@timperelman

Description

Subagents defined via markdown files (.opencode/agents/*.md) have their frontmatter.description ignored by the Task tool's agent list. The describeTask function in packages/opencode/src/tool/registry.ts falls through to "This subagent should only be called manually by the user." for agents without a description property, even when the field is present in the YAML frontmatter.
Agents defined via JSON config (opencode.json) work correctly — their description appears in the Task tool. Only markdown-defined agents are affected.

Plugins

@opencode-ai/plugin

OpenCode version

1.15.1

Steps to reproduce

Create .opencode/agents/test.md:

description: "Test agent — does X and Y"
mode: subagent
permission:
read: allow

You are a test agent.
2.
Start a session and inspect the Task tool parameter description. The agent will appear as:

  • test: This subagent should only be called manually by the user.

Expected:

  • test: Test agent — does X and Y
    Root Cause Analysis
    The propagation chain is:

config/agent.ts:load() — calls ConfigMarkdown.parse() (gray-matter) which correctly extracts frontmatter.description into md.data.description. This is packed into { name, ...md.data, prompt } and validated through AgentSchema (which has description: optional(String)).
2.
config/config.ts — merges markdown agents into cfg.agent via mergeDeep.
3.
agent/agent.ts — iterates cfg.agent and sets:
item.description = value.description ?? item.description
For non-built-in agents (no prior entry in the agents record), item.description is undefined, so this should resolve to value.description.
4.
tool/registry.ts:describeTask() — calls agents.list() and renders:
- ${item.name}: ${item.description ?? "This subagent should only be called manually by the user."}
The bug is likely in step 2 or 3 — either mergeDeep drops the field, or the normalize() function in config/agent.ts strips it, or the description is null/undefined in the parsed config despite being present in the frontmatter.
Candidate Fix Locations

packages/opencode/src/agent/agent.ts — line where item.description = value.description ?? item.description is set (ensure it's not using ?? where value.description is an empty string or gets overwritten elsewhere)

packages/opencode/src/config/agent.ts — normalize() function (ensure description isn't being lost during KNOWN_KEYS filtering)

packages/opencode/src/config/markdown.ts — parse() function (verify gray-matter is outputting the description field correctly with multi-line frontmatter containing non-standard YAML keys)
Environment

OpenCode version: 1.15.0 / 1.15.1 (@opencode-ai/plugin also 1.15.1, same behavior)

OS: Linux (WSL2)

Agent location: .opencode/agents/*.md
Additional Context
Built-in subagents (explore, general, scout) and JSON-configured agents work correctly — only markdown-defined agents are affected. The @ autocomplete menu displays the description correctly, suggesting the issue is specific to how agents.list() populates description for non-native agents, not the parsing itself.
Confirmed via compiled binary at ~/.opencode/bin/opencode — the fallback string "This subagent should only be called manually by the user." appears in source at tool/registry.ts as item.description ?? "manual fallback".

Operating System

Linux (WSL2 — kernel 6.6.87.2-microsoft-standard-WSL2)

Terminal

Powershell

Activity

  1. PeterPonyu commented on May 16, 2026

    @PeterPonyu

    This appears to be already resolved on current upstream/dev (verified at f80651fa9 / v1.15.3).

    The propagation chain you traced is intact end-to-end in the current source:

    1. ConfigMarkdown.parse() correctly extracts description from frontmatter via gray-matter (no recent changes to this path).
    2. packages/opencode/src/config/agent.ts:58 — description is in the known-keys allowlist, so normalize() does NOT strip it during KNOWN_KEYS filtering. The schema at line 35 declares description: Schema.optional(Schema.String) with annotation "Description of when to use the agent".
    3. packages/opencode/src/config/agent.ts:30 — schema decode preserves description: Schema.optional(Schema.String).
    4. packages/opencode/src/tool/registry.ts:307 — describeTask renders:
      `- ${item.name}: ${item.description ?? "This subagent should only be called manually by the user."}`
      With item.description correctly populated from the markdown frontmatter, this resolves to the real description, not the fallback.

    I followed your suspected "step 2 or 3" — mergeDeep does not drop the field, and normalize() does not strip it, because description is explicitly in KNOWN_KEYS.

    Could you check whether you're still seeing this on v1.15.x? If you're on v1.15.1 as the report says, please try bunx opencode@latest upgrade (or run with OPENCODE_DEBUG=1 against dist/dev) to confirm. If the bug still reproduces on a current build, a minimal repro markdown file + the exact agents.list() output for that file would help — given the source looks correct now, there may be an environment-specific issue (e.g. file encoding, BOM in the frontmatter, plugin override) that's distinct from the original report.

    Most likely safe to close if you can't reproduce on the latest version.

  2. added a commit that references this issue on May 16, 2026
    4789bfd
  3. github-actions commented on Jul 16, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions