Skip to content

fix(core): always deny tool calls for system agents - #10091

Merged
marius-kilocode merged 1 commit into
Kilo-Org:mainfrom
shssoichiro:deny-system-agents-tools
Jun 16, 2026
Merged

fix(core): always deny tool calls for system agents#10091
marius-kilocode merged 1 commit into
Kilo-Org:mainfrom
shssoichiro:deny-system-agents-tools

Conversation

@shssoichiro

Copy link
Copy Markdown
Contributor

Context

The Title agent would frequently attempt to make tool calls, even though its system prompt tells it not to do so. This resulted in a broken title generation. This was traced back to top-level user allow rules overriding the deny: "*" rule for these agents.

Implementation

Hardcode deny: "*" for the Title, Summarize, and Compaction agents. Although this issue was most noticeable for the Title agent, it's possible it was impacting Summarize and Compaction as well, and it seems reasonable to deny tools to those agents as well, since they have no reason to be making tool calls in any situation.

Get in Touch

ExpedientFalcon on Discord

Comment thread packages/opencode/src/agent/agent.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

All changes are correct and well-tested. The incremental diff introduces:

  • harden / hardenSystemAgents functions in packages/opencode/src/kilocode/agent/index.ts that enforce deny-all permissions for the title, summary, and compaction system agents, regardless of user config overrides.
  • A second hardenSystemAgents call in packages/opencode/src/agent/agent.ts (line 413) placed after the per-agent config merge loop (lines 297–326), ensuring user-configured permission entries cannot re-open tool access for locked agents.
  • Two new integration tests confirming the hardening survives both per-agent config allows and name overrides.

The double-call pattern (once inside patchAgents, once at line 413) is intentional and correct: the first call hardens before upstream config merges, the second ensures the lock holds after them.

Files Reviewed (4 files)
  • packages/opencode/src/kilocode/agent/index.tsharden/hardenSystemAgents implementation correct; locked set covers all three utility agents
  • packages/opencode/src/agent/agent.tshardenSystemAgents correctly placed after all config merges at line 413
  • packages/opencode/test/kilocode/agent-permission-overrides.test.tsWithInstance import correct; two new tests exercise key-based and name-based hardening paths
  • .changeset/fast-monkeys-smoke.md — no issues

Reviewed by claude-4.6-sonnet-20260217 · 530,833 tokens

Review guidance: REVIEW.md from base branch main

@shssoichiro
shssoichiro force-pushed the deny-system-agents-tools branch from 45221e2 to 1af3ecd Compare May 9, 2026 06:22
@marius-kilocode

marius-kilocode commented May 20, 2026

Copy link
Copy Markdown
Collaborator

@shssoichiro my bot found this:

High: the new deny is applied too early, so agent-specific config can still re-enable tools for title, summary, and compaction. patchAgents() resets them at packages/opencode/src/kilocode/agent/index.ts:440, but cfg.agent..permission is merged afterward at packages/opencode/src/agent/agent.ts:253 and packages/opencode/src/agent/agent.ts:286. Those system agents are explicitly configurable at packages/opencode/src/config/config.ts:214, and permission evaluation is last-match-wins at packages/opencode/src/permission/evaluate.ts:9. Example: agent.title.permission.bash = "allow" wins after the new "": "deny", so the PR does not yet satisfy “always deny tool calls.”

True?

@shssoichiro
shssoichiro force-pushed the deny-system-agents-tools branch from 1af3ecd to 0346863 Compare May 21, 2026 16:34
@shssoichiro

Copy link
Copy Markdown
Contributor Author

Yes, it does seem to be valid. I added a test case for this, and refactored to resolve the issue.

Comment thread packages/opencode/test/kilocode/agent-permission-overrides.test.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

I found one P1 issue in the latest version:

The hardening pass is still bypassable because it keys off the mutable agent name rather than the agent map key. Agent config is merged before the final hardenSystemAgents(agents) call, and config can set agent.title.name, agent.summary.name, or agent.compaction.name to any other value. In that case the object remains the system agent under the title/summary/compaction map key, but harden() sees the overridden item.name, locked.has(item.name) returns false, and any configured tool permissions remain allowed.

Impact: a normal config such as this can keep tools enabled for a hidden system utility agent, preserving the tool-execution risk this PR is meant to remove:

{
  "agent": {
    "title": {
      "name": "custom-title",
      "permission": { "bash": "allow" }
    }
  }
}

Suggested fix: harden by the original agent identity/map key instead of only item.name, e.g. iterate Object.entries(agents) and lock when the key is title, summary, or compaction (optionally also preserving the current item.name check as defense-in-depth). Please also add a regression test that overrides agent.title.name while allowing a tool and verifies the resulting title agent still denies all tool calls.

@shssoichiro
shssoichiro force-pushed the deny-system-agents-tools branch from 2bd296b to be234fa Compare June 5, 2026 03:51
@marius-kilocode

Copy link
Copy Markdown
Collaborator

Thanks for submitting, this can still be bypassed.

resolveTools() merges agent.permission before input.permission, and permission evaluation is last-match-wins:

  • packages/opencode/src/session/llm.ts:676
  • packages/opencode/src/permission/evaluate.ts:9

Sessions can accept permission rules through the public create/update APIs, while prompt input accepts an arbitrary agent name. This means a later session-level allow overrides the wildcard deny installed on title, summary, or compaction.

I reproduced this locally with a fake OpenAI-compatible provider that always requests the read tool:

  1. Start kilo serve with the fake provider and model.
  2. Create a session with a session-level read allow:
SESSION_ID=$(
  curl -fsS -X POST \
    -H 'content-type: application/json' \
    -H "x-kilo-directory: $PWD" \
    --data '{
      "title": "system agent permission repro",
      "permission": [
        {
          "permission": "read",
          "pattern": "*",
          "action": "allow"
        }
      ]
    }' \
    http://127.0.0.1:18081/session |
  jq -r '.id'
)
  1. Prompt that session using the hidden title agent:
curl -fsS -X POST \
  -H 'content-type: application/json' \
  -H "x-kilo-directory: $PWD" \
  --data '{
    "agent": "title",
    "model": {
      "providerID": "fake",
      "modelID": "tool-test"
    },
    "parts": [
      {
        "type": "text",
        "text": "Attempt the model response."
      }
    ]
  }' \
  "http://127.0.0.1:18081/session/$SESSION_ID/prompt_async"
  1. The fake provider received tools: ["read"], emitted a read call for AGENTS.md, and the stored assistant message contained a completed read result.

Without the session permission, the same provider receives no tools and Kilo reports the forced call as unavailable. A normal code agent still executes the same read successfully.

Can we enforce this as an invocation-time hard rule after session permissions are merged, rather than only replacing the configured agent rules?

@marius-kilocode

Copy link
Copy Markdown
Collaborator

On another thought, this does not qualify as an edge case for normal usage. Let's try this.

@marius-kilocode
marius-kilocode merged commit 34f16a5 into Kilo-Org:main Jun 16, 2026
18 checks passed
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…-tools

fix(core): always deny tool calls for system agents
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants