Skip to content

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

@clempat

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

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