Skip to content

todowrite/todoread cannot be enabled for subagents via agent permission config #19101

@n3m6

Description

@n3m6

Description

Description

The documentation states that todowrite and todoread are "disabled for subagents by default, but you can enable it manually" via the agent's permission field. However, setting todowrite: allow and todoread: allow in a custom subagent's permission config has no effect — the tools remain unavailable at runtime.

Steps to Reproduce

  1. Create a custom subagent markdown file (e.g., ~/.config/opencode/agents/executor.md):
---
description: Executes markdown plans iteratively
mode: subagent
permission:
  edit: deny
  bash:
    "*": deny
  task:
    "*": deny
    "build": allow
  webfetch: deny
  todowrite: allow
  todoread: allow
---

You are a plan executor agent. Use todowrite and todoread to track tasks.
  1. Invoke the agent via @executor or have a primary agent delegate to it via the Task tool.
  2. The agent attempts to call todowrite.

Expected Behavior

The todowrite and todoread tools should be available to the subagent, as configured in the agent's permission block.

Actual Behavior

⚙ invalid [tool=todowrite, error=Model tried to call unavailable tool 'todowrite'. Available tools: invalid, read, glob, grep, task, skill.]

Root Cause

The Task tool implementation in packages/opencode/src/tool/task.ts hardcodes todowrite and todoread as denied in two places, overriding the agent's permission config:

1. Session creation — hardcoded deny permissions on the child session:

return await Session.create({
  // ...
  permission: [
    { permission: "todowrite", pattern: "*", action: "deny" },
    { permission: "todoread",  pattern: "*", action: "deny" },
    // ...
  ],
})

2. SessionPrompt.prompt call — hardcoded false in the tools object:

const result = await SessionPrompt.prompt({
  // ...
  tools: {
    todowrite: false,
    todoread: false,
    // ...
  },
})

In prompt.ts, the deprecated tools parameter is converted to session-level permissions and applied via Session.setPermission, which overwrites the session permissions set during creation. Since Permission.merge(agent.permission, session.permission) is used at evaluation time, the session-level deny always wins over the agent-level allow.

There is an existing pattern for conditionally skipping this override — the task permission uses a hasTaskPermission check:

const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")

// ...
permission: [
  // ...
  ...(hasTaskPermission
    ? []
    : [{ permission: "task", pattern: "*", action: "deny" }]),
],

But no equivalent check exists for todowrite/todoread.

Suggested Fix

Add a similar check for todowrite and todoread that respects the agent's permission config:

const hasTodoWritePermission = agent.permission.some(
  (rule) => rule.permission === "todowrite" && rule.action === "allow"
)
const hasTodoReadPermission = agent.permission.some(
  (rule) => rule.permission === "todoread" && rule.action === "allow"
)

Then conditionally include the deny rules in session creation:

permission: [
  ...(hasTodoWritePermission
    ? []
    : [{ permission: "todowrite", pattern: "*", action: "deny" }]),
  ...(hasTodoReadPermission
    ? []
    : [{ permission: "todoread", pattern: "*", action: "deny" }]),
  // ...
],

And similarly for the tools object passed to SessionPrompt.prompt:

tools: {
  todowrite: hasTodoWritePermission ? undefined : false,
  todoread: hasTodoReadPermission ? undefined : false,
  // ...
},

Environment

  • OpenCode version: latest (dev branch as of 2025-03-25)
  • OS: Linux
  • Relevant files: packages/opencode/src/tool/task.ts, packages/opencode/src/session/prompt.ts

Plugins

no plugins

OpenCode version

1.3.0

Steps to reproduce

  1. Create a custom subagent markdown file (e.g., ~/.config/opencode/agents/executor.md):
---
description: Executes markdown plans iteratively
mode: subagent
permission:
  edit: deny
  bash:
    "*": deny
  task:
    "*": deny
    "build": allow
  webfetch: deny
  todowrite: allow
  todoread: allow
---

You are a plan executor agent. Use todowrite and todoread to track tasks.
  1. Invoke the agent via @executor or have a primary agent delegate to it via the Task tool.
  2. The agent attempts to call todowrite.

Screenshot and/or share link

No response

Operating System

Ubuntu 25.10

Terminal

Alacritty

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcoreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions