# agnostic-ai v0.85.0: agents with a tools list get exact permissions on OpenCode and Kilo

> Source: <https://agnostic-ai.org/updates/2026-10-15-v0.85.0/>
> Published: 2026-10-14 22:00:00+00:00

## Upgrading from v0.84.0

Stop any running `agnostic-ai watch` process before updating the installation or project config.

Install through your existing package manager. For a global npm installation:

```
npm install -g agnostic-ai@0.85.0
```

For an exact project config pin, `agnostic-ai upgrade --requires` updates `requires` and the schema URL to the installed version, then syncs. It also replaces minimum or range requirements and local overrides. To keep a deliberate minimum or range, retain that value, update the schema URL to `v0.85.0`, review `agnostic-ai migrate --dry-run`, and run `agnostic-ai sync`. See [upgrade behavior](https://agnostic-ai.org/docs/cli-reference/maintain/#upgrade).

This release changes generated files for several tools, so `sync --check` reports drift until you run `agnostic-ai sync`. Review the diff, commit it, then run `agnostic-ai sync --check` and restart watch. The sections below say which projects see a change and why.

### Permissions get stricter

**OpenCode: an agent with a `can` or `tools` list now writes a `permission` map.** Before, sync dropped the list without a note, so a reviewer meant to only read ran with OpenCode's base permissions. Now every tool you do not list is denied, and each listed tool keeps the project's `ask` and `deny` rules. A project `allow` never widens the agent. A restricted agent also asks before OpenCode's own temp and skill folders outside the project, which OpenCode allows by default, because those paths depend on the machine. Check each agent that has a list. To override the result for one agent, use `x-opencode.permission` on it. See [OpenCode](https://agnostic-ai.org/docs/targets/opencode/).

**OpenCode: a broad `deny` or `ask` now wins over a narrower `allow`.** OpenCode applies the last matching rule, and sync used to write each tool's patterns in alphabetical order. A `git push *` deny came before a `git push origin *` allow, so it blocked nothing. Patterns now go allow, then ask, then deny. The same spec behaves this way on Claude Code, Devin CLI, Augment and Kilo, and the old order was in place from v0.62.0 to v0.84.0. If you wrote `ask: Bash(*)` with `allow: Bash(git status:*)`, `git status` used to run without asking. It now asks. Expect more prompts if you relied on "ask for everything except a few commands".

**Kilo: a restricted agent gets exactly the stricter of the project's rules and its own list.** Kilo applies an agent's rules after the project's, and the last match wins, so a listed tool used to override a project `ask` or `deny` for that tool. Now the project's rules apply inside what the agent allows. Three visible changes:

- A bare `Bash` in an agent's list now asks, as Kilo itself does, and writes Kilo's read-only command allows (about 40 lines in the agent file). Before, it allowed every command.
- A `Write` rule lands under`edit` , the permission Kilo's write tool checks, instead of an unused`write` key.
- A restricted agent asks for paths outside the project and for `doom_loop` instead of denying them. It asks for directories Kilo allows outside the project, because that list depends on the machine.

A project `bash: ask` stays `ask`. See [Kilo](https://agnostic-ai.org/docs/targets/kilo/).

### prefer-spec picks one winner

This applies only if you set `sync.collision-policy: prefer-spec` and two targets write the same path differently. Before, `sync` wrote every target in parallel, so whichever finished last won, and the winner changed between runs. `sync --check` and `status` compared the file against each target's own version, so they reported drift that no sync could clear. Now the file gets the version of the last target in `targets:` that writes it, and `sync`, `sync --check` and `status` agree. Reordering `targets:` changes the winner. `-t` does not, and `--check --diff` shows only the winning version. Check that the last writer is the target you want. See [`sync.collision-policy`](https://agnostic-ai.org/docs/configuration/#synccollision-policy).

### Rules and instruction files change once

**Rules written with `paths:` now scope on every tool.** Copilot used to write `applyTo: "**"` and Cursor `alwaysApply: true` for such a rule, so it loaded everywhere. It now loads only for the matching files, as it already did on Claude Code, Codex and nested files. `validate` and LINT013 reject a rule whose `paths` and `globs` name different patterns. The migration unit is one rule: keep one of the two fields. See [rule fields](https://agnostic-ai.org/docs/spec-format/rules/#fields).

**Root instruction files get a one-time diff.** When a path-scoped rule cannot become a nested file, sync inlines it into `AGENTS.md`, `GEMINI.md` or `.junie/AGENTS.md`. The section now states `Applies to: \` resources/js/**``under its heading, so it no longer reads as a repo-wide rule, and sync adds a`scope: 1` marker in the source comment. A project synced by v0.84.0 shows that diff once; after you commit it,`sync --check` is clean. The line is left out for a catch-all glob.

**Root instruction files drop the provenance header when every reader opts out.** If every target that reads a root `AGENTS.md`, `CLAUDE.md`, `GEMINI.md` or `.github/copilot-instructions.md` sets `outputs.<target>.provenance-header: false`, sync used to write the "Generated by agnostic-ai" header into the file anyway. It no longer does. One reader that keeps the header keeps it in the file. The next sync removes the header from existing files, so commit that diff. `doctor` still counts these files as managed.

### Copilot writes a VS Code setting

With Copilot and any root `AGENTS.md` target both on, sync writes the full instructions to `.github/copilot-instructions.md` and `AGENTS.md`, and VS Code's local chat applied both to every request. Sync now sets `"chat.useAgentsMdFile": false` in `.vscode/settings.json`, so the local chat loads the instructions once. VS Code reads this setting only for its local agent, and only in a trusted workspace. Commit the file.

- A value you set yourself wins. If you keep it `true` , sync prints a note.
- Turning the setting off also drops nested `AGENTS.md` files in the local chat. Copilot reads scoped rules from its own`.instructions.md` files.
- Merging into a `.vscode/settings.json` that has comments drops them, with a one-time warning.
- GitHub.com code review and the Copilot cloud agent still read both files and have no such setting. See [Copilot](https://agnostic-ai.org/docs/targets/copilot/) .

### Lint and handoff files

- LINT047 is a warning for a skill or command name that Claude Code refuses to load, such as one with a zero-width character, `<` or`>` .`lint --strict` fails until you rename it. See[lint codes](https://agnostic-ai.org/docs/cli-reference/check/#lint) .
- With `handoff` on and Factory as a target, sync adds the shared handoff store to`.factory/settings.local.json` . Sync keeps the file out of Git.
- Turning `handoff` off now removes its grant from`.claude/settings.local.json` and`.qoder/settings.json` in one sync, and deletes the file when the grant was all it held.

### Refresh your Git hooks

The generated hooks now name the install for your package manager when `agnostic-ai` is missing: `bun install --frozen-lockfile`, `pnpm install --frozen-lockfile`, `npm ci` or `yarn install`. Run `agnostic-ai install-hook` again, with the flags you used before (`--post-checkout`, `--shared`), to update a hook you installed earlier. It replaces only the old missing-binary line in the pre-commit, post-checkout and post-merge hooks and leaves the rest of each file alone.

## What agnostic-ai changed

### Restrict an agent once and get the same limit on OpenCode and Kilo

A portable `can` list is the one place to say what an agent may do. This agent reads files and runs one Git command:

```
---
name: reviewer
description: Review a diff without editing
can: [read, shell(git diff *)]
---
```

On OpenCode the list becomes the agent's `permission` map with `"*": deny` first. Each listed tool then replays the project's rules inside what the list allows, and `mcp:<server>` allows only that server's tools. Kilo gets the same result in its own key names. Both are checked by a test that runs sync end to end against a copy of each tool's matching rules and fails if any agent decides looser than the project would.

### Scope a rule once

`paths:` is another name for `globs:` everywhere:

```
---
name: frontend
paths: "resources/js/**"
---
Use the shared form components.
```

Copilot, Cursor and Claude Code load it only for those files. Where a tool cannot take a nested file, the inlined section says `Applies to:` and the path.

### Give a VS Code MCP server its own sandbox

VS Code reads allow rules for an MCP server only from that server's `sandbox` object in `.vscode/mcp.json`. Sync now writes it for stdio servers from the MCP spec's top level (or `x-copilot`, which also carries `cwd`, `envFile`, `sandboxEnabled`, `oauth` and `dev` now):

```
name: builder
description: Build tools in a sandbox.
type: stdio
command: npx
args: ["-y", "example-build-server"]
sandboxEnabled: true
sandbox:
  filesystem:
    allowWrite: ["/tmp/build-cache"]
  network:
    allowedDomains: ["registry.example.com"]
```

The five keys are `filesystem.allowWrite`, `filesystem.denyRead`, `filesystem.denyWrite`, `network.allowedDomains` and `network.deniedDomains`. `import copilot` keeps them. `.github/mcp.json` stays on the plain schema.

### Bootstrap bun projects

`agnostic-ai project --bootstrap` used to refuse a bun project, while the post-checkout hook told you to run it. It now reads the package manager from `packageManager`, `devEngines.packageManager` or the only committed lockfile. For bun it needs `bun.lock` or `bun.lockb` and runs `bun install --frozen-lockfile`. Any other manager gets a refusal that names its own install step. A malformed `devEngines` no longer blocks `project` or `project --check`. See [project](https://agnostic-ai.org/docs/cli-reference/maintain/#project).

### Smaller fixes

- `sync --keep-edits` no longer keeps a skill file that Codex and Amp share as a hand edit after a branch switch.`sync --check` stays clean.
- Codex agent files report `service_tier` as ignored, since Codex follows the session's tier.
- `import ellipsis` keeps a reviewer without an`environment` in Ellipsis's basic sandbox.`x-ellipsis.reviewer.environment: null` does it by hand.
- The note for a dropped `disable-model-invocation` on Factory and Crush names the`outputs.<target>.skills-dir` setting needed beside`.agents/skills` .

Full list: [v0.85.0 release notes](https://github.com/Chemaclass/agnostic-ai/releases/tag/v0.85.0).

## What changed in the targets

**Devin CLI adds `PreCompact` and `PostCompact` hooks.** The [Devin CLI changelog](https://docs.devin.ai/cli/changelog/stable.md) for v3000.11.92, dated October 14, 2026 and checked October 15, says they run before and after context compaction, with `manual` and `auto` matchers, and that a failure does not block compaction. The `windsurf` target now writes both events to `.devin/hooks.v1.json`, and `hook run` accepts the two matchers. Devin documents no payload, so `hook run` needs `--payload`, and a block on either event is not counted. The `handoff-pre-compact` built-in stays off Devin until its payload and output routing are documented. See [Windsurf / Devin](https://agnostic-ai.org/docs/targets/windsurf/).

**OpenCode deprecates an agent's `tools` key in favor of `permission`.** The [OpenCode agents page](https://opencode.ai/docs/agents/), checked October 15, 2026, says `tools` is deprecated and to prefer the agent's `permission` field. Sync now writes that map from a `can` or `tools` list (see the upgrade section).

**Copilot reads instruction files differently by product.** The [GitHub custom instructions table](https://docs.github.com/en/copilot/reference/custom-instructions-support), checked October 15, 2026, lists only the repository-wide `.github/copilot-instructions.md` for Copilot Chat on GitHub.com. Visual Studio reads that file and path-specific files. The Copilot cloud agent and code review also read `AGENTS.md`, `CLAUDE.md` and `GEMINI.md`. The [VS Code custom instructions page](https://code.visualstudio.com/docs/agent-customization/custom-instructions), checked the same day, says that for the Local harness, `chat.useAgentsMdFile` turns `AGENTS.md` support on or off, and the [settings reference](https://code.visualstudio.com/docs/copilot/reference/copilot-settings) lists its default as `true`. Sync keeps the full text in `.github/copilot-instructions.md` for the products that read only it, and turns the setting off so VS Code's local chat does not load the same text twice. Other VS Code agent harnesses follow their own rules, and the setting does not change them.

**Kilo applies the last matching rule, with the agent's rules after the project's.** Kilo's [permissions page](https://kilo.ai/docs/customize/agent-permissions), checked October 15, 2026, says the last matching rule wins and to put broad rules first. Kilo's [agent source](https://github.com/Kilo-Org/kilocode/blob/main/packages/opencode/src/agent/agent.ts), read the same day, merges defaults, then the project's `permission`, then the agent's. That order is why a listed tool could override a project `ask`.

**Claude Code refuses some skill and command names.** The [skills docs](https://code.claude.com/docs/en/skills), checked October 15, 2026, list characters a name cannot hold, such as control and zero-width characters, `<` and `>`. Such a skill never loads, and `lint` now warns (LINT047).

**Factory's sandbox limits writes to the working folder.** The [Factory sandbox page](https://docs.factory.com/autonomy-and-safety/sandbox), checked October 15, 2026, says file writes are denied except the working directory unless `sandbox.filesystem.allowWrite` adds paths. From a linked worktree, saving a handoff writes to the main checkout, which the sandbox blocks. Sync adds the store to `sandbox.filesystem.allowWrite` in `.factory/settings.local.json` and never sets `sandbox.enabled`. This release has not verified whether that local list joins or replaces the one in `.factory/settings.json`, so check that a handoff saves from a linked worktree. See [Factory](https://agnostic-ai.org/docs/targets/factory/).
