Skip to content

Non-deterministic agent/skill ordering in tool descriptions breaks prompt caching #18215

Description

@clempat

Description

The task and skill tool descriptions contain agent/skill lists in non-deterministic order across API calls within the same session. Since Anthropic's prompt caching (the one I tested) is prefix-based, any byte change invalidates the entire cache, resulting in 0% cache hit rate.

Reproducible when custom agents are registered (via plugins like oh-my-opencode-slim or ~/.config/opencode/agents/). The more agents/skills, the more likely the order changes between calls. Bare opencode with only the default agent may not trigger it, but I think the fix still interesting for everyone.

Root cause:

  • packages/opencode/src/tool/task.ts:39: accessibleAgents is mapped without sorting
  • packages/opencode/src/tool/skill.ts:10: Skill.available() result is used unsorted
  • Agent.list() in agent.ts only pins the default agent first via a boolean sort, leaving all others in undefined insertion order from filesystem/plugin loading

Proposed Fix:

Sort before mapping in both files:

// task.ts line 39
[...accessibleAgents].sort((a, b) => a.name.localeCompare(b.name))
// skill.ts line 10
const list = await Skill.available(ctx?.agent).then((x) => [...x].sort((a, b) => a.name.localeCompare(b.name)))

Verified result (3-turn test, ~26 agents + 7 skills):

Turn Before (cache read) Afer (cache read)
1 0 0 (cold start)
2 0 17,837
3 0 17,837

Plugins

oh-my-opencode-slim, opencode-beads, opencode-antigravity-auth, @tarquinen/opencode-dcp, @franlol/opencode-md-table-formatter, opencode-openai-codex-auth, opencode-websearch-cited, @simonwjackson/opencode-direnv

OpenCode version

1.2.26

Steps to reproduce

Setup: register multiple custom agents (e.g. via oh-my-opencode-slim + custom agents in ~/.config/opencode/agents/)

Turn 1

opencode run -m anthropic/claude-sonnet-4-6 "Say hello" --format json

Note session ID, then:

opencode run -m anthropic/claude-sonnet-4-6 --session <ID> --continue "Say goodbye" --format json

Observe cache.read: 0 on every turn. Route through a proxy (I used litellm/langfuse) to compare request payloads, the agent list in the task tool description is in a different order each turn.

Screenshot and/or share link

No response

Operating System

MacOS 26.2 (25C56)

Terminal

Kitty

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcoreAnything pertaining to core functionality of the application (opencode server stuff)perfIndicates a performance issue or need for optimization

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions