Skip to content

Commit a480af1

Browse files
committed
fix(opencode): restore grep/glob/bash for dreamer subagent permissions
Audit of the live OpenCode DB showed real-world hidden-subagent tool usage: historian (compartment): read 7, grep 3 dreamer (consolidate / verify / improve / archive-stale / smart-notes): bash 114, ctx_memory 57, grep 26, read 21, glob 4, ctx_reduce 1 The previous commit (571d014) locked dreamer to read + ctx_memory + ctx_search + ctx_note, which would have broken: - smart-note evaluation, which task-prompts.ts explicitly tells the model to perform via gh / git / curl / file reads under bash - the verify task git log --oneline --since=... step (line 243) - the verify task grep-schema-for-defaults instruction (line 81) - the improve task find/grep directory inventory (lines 266-268) Add grep, glob, and bash to DREAMER_ALLOWED_TOOLS. task, edit, write, webfetch, websearch remain denied — dreamer must not spawn subagents or commit changes, and smart-note URL fetches go through bash + curl instead of webfetch to keep one shell surface. Historian 3 historical grep calls were the model improvising rather than following its prompt (which makes no mention of grep). Historian job is summarizing the input it is given, not exploring the repo, so the read-only restriction stands. If a real summarization task ever needs grep we can add it then. Sidekick had no real-world calls captured (it is a recent feature with low usage). Its prompt allows ONLY ctx_search, so the ctx_search + ctx_memory allow-list stands. Tests updated: 1476 pass / 0 fail (+3 dreamer-specific assertions). Comments in permissions.ts now cite the DB-observed call counts so a future audit can verify the allow-list against fresh telemetry.
1 parent 571d014 commit a480af1

2 files changed

Lines changed: 83 additions & 22 deletions

File tree

packages/plugin/src/agents/permissions.test.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,45 @@ describe("HISTORIAN_ALLOWED_TOOLS", () => {
6868
});
6969

7070
describe("DREAMER_ALLOWED_TOOLS", () => {
71-
it("includes read + ctx_memory + ctx_search + ctx_note", () => {
72-
// Dreamer needs: `read` (key-files identification), `ctx_memory`
73-
// (consolidate/verify/archive/merge/update), `ctx_search`
74-
// (smart-note evaluation, retrieval-count checks), `ctx_note`
75-
// (smart-note dismiss/update).
71+
it("includes read + grep + glob + bash for local-repo exploration", () => {
72+
// Verify task prompt explicitly tells the model to grep schema
73+
// files, read source, glob/find for project structure inventory,
74+
// and run `git log --oneline --since=...`. Smart-note evaluation
75+
// routinely needs `gh` / `git` / `curl` via bash. Live DB shows
76+
// >100 bash invocations across dreamer task variants.
7677
expect(DREAMER_ALLOWED_TOOLS).toContain("read");
78+
expect(DREAMER_ALLOWED_TOOLS).toContain("grep");
79+
expect(DREAMER_ALLOWED_TOOLS).toContain("glob");
80+
expect(DREAMER_ALLOWED_TOOLS).toContain("bash");
81+
});
82+
83+
it("includes ctx_memory + ctx_search + ctx_note for memory operations", () => {
84+
// Dreamer's canonical job: consolidate / verify / archive /
85+
// improve memories (`ctx_memory`), and dismiss / surface smart
86+
// notes (`ctx_note`). `ctx_search` for retrieval-count and
87+
// smart-note evidence lookup.
7788
expect(DREAMER_ALLOWED_TOOLS).toContain("ctx_memory");
7889
expect(DREAMER_ALLOWED_TOOLS).toContain("ctx_search");
7990
expect(DREAMER_ALLOWED_TOOLS).toContain("ctx_note");
8091
});
8192

82-
it("does NOT include `task` or edit/bash/web tools", () => {
83-
for (const denied of ["task", "bash", "edit", "write", "webfetch", "websearch"]) {
84-
expect(DREAMER_ALLOWED_TOOLS).not.toContain(denied);
85-
}
93+
it("does NOT include `task` (no subagent fanout from dreamer)", () => {
94+
expect(DREAMER_ALLOWED_TOOLS).not.toContain("task");
95+
});
96+
97+
it("does NOT include `edit` or `write` (no project-file mutations)", () => {
98+
// Dreamer task prompt explicitly says "Do not commit changes" —
99+
// memory consolidation must not alter project source files.
100+
expect(DREAMER_ALLOWED_TOOLS).not.toContain("edit");
101+
expect(DREAMER_ALLOWED_TOOLS).not.toContain("write");
102+
});
103+
104+
it("does NOT include `webfetch` or `websearch` (use bash + curl instead)", () => {
105+
// External URL fetches in smart-note conditions go through
106+
// `bash` + `curl` so dreamer has one consistent shell surface
107+
// instead of two redundant network tools.
108+
expect(DREAMER_ALLOWED_TOOLS).not.toContain("webfetch");
109+
expect(DREAMER_ALLOWED_TOOLS).not.toContain("websearch");
86110
});
87111
});
88112

@@ -113,11 +137,14 @@ describe("integration: full hidden-agent permission shape", () => {
113137
});
114138
});
115139

116-
it("dreamer permission object: `*` denied + read + ctx_* allowed", () => {
140+
it("dreamer permission object: `*` denied + read + grep + glob + bash + ctx_* allowed", () => {
117141
const perm = buildAllowOnlyPermission(DREAMER_ALLOWED_TOOLS);
118142
expect(perm).toEqual({
119143
"*": "deny",
120144
read: "allow",
145+
grep: "allow",
146+
glob: "allow",
147+
bash: "allow",
121148
ctx_memory: "allow",
122149
ctx_search: "allow",
123150
ctx_note: "allow",

packages/plugin/src/agents/permissions.ts

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,17 @@
4545
* instructs the model to read that file. The model's only output
4646
* channel is its text response (the `<output>...</output>` XML).
4747
*
48-
* - **dreamer**: `read` (for the key-files identification task),
49-
* plus the Magic Context MCP tools `ctx_memory`, `ctx_search`,
50-
* `ctx_note`. Dreamer task prompts in
51-
* `features/magic-context/dreamer/task-prompts.ts` explicitly
52-
* call these tools to consolidate, verify, archive, merge, and
53-
* update memories, plus evaluate smart notes.
48+
* - **dreamer**: `read`, `grep`, `glob`, `bash`, plus the Magic
49+
* Context MCP tools `ctx_memory`, `ctx_search`, `ctx_note`.
50+
* Dreamer task prompts in
51+
* `features/magic-context/dreamer/task-prompts.ts` explicitly tell
52+
* the model to grep schema files for defaults, read source to
53+
* confirm claims, run `git log` / `gh` / `curl` for verify and
54+
* smart-note evaluation, and use glob/find for directory
55+
* inventory. Live DB shows >100 bash invocations across all
56+
* dreamer task variants. `task` / `edit` / `write` / `webfetch` /
57+
* `websearch` remain denied — dreamer must not spawn subagents
58+
* or commit changes.
5459
*
5560
* - **sidekick**: `ctx_search` and `ctx_memory` (read-only ops).
5661
* Sidekick's job is augmenting user prompts via memory retrieval
@@ -88,13 +93,42 @@ export function buildAllowOnlyPermission(
8893
export const HISTORIAN_ALLOWED_TOOLS = ["read"] as const;
8994

9095
/**
91-
* Tools the dreamer agent needs. Memory consolidation/verification/
92-
* archive/merge/update flows go through `ctx_memory`; smart-note
93-
* evaluation uses `ctx_search` and `ctx_note`; the key-files
94-
* identification task uses `read` to inspect candidate files before
95-
* picking which ones to pin.
96+
* Tools the dreamer agent needs. This is the broadest hidden-agent
97+
* surface because dreamer's tasks legitimately require local-repo
98+
* exploration plus external command execution:
99+
*
100+
* - `ctx_memory` / `ctx_search` / `ctx_note` — the canonical memory
101+
* CRUD and retrieval path for consolidate / verify / archive /
102+
* improve and smart-note dismissal.
103+
* - `read` / `grep` / `glob` — the verify task prompt
104+
* (`task-prompts.ts`) explicitly tells the model to grep schema
105+
* files for default values, read source to confirm claimed
106+
* behavior, and use glob for project structure inventory.
107+
* - `bash` — required for smart-note condition evaluation (the
108+
* prompt explicitly mentions `gh` / `git` / `curl` / file reads),
109+
* for the verify task's `git log --oneline --since=...` step, and
110+
* for the improve task's `find` / `grep` directory inventory. The
111+
* live OpenCode DB shows over 100 `bash` invocations across
112+
* consolidate / verify / improve / archive-stale / smart-notes
113+
* dreamer child sessions, so removing it would regress real,
114+
* documented dreamer behavior.
115+
*
116+
* Deliberately NOT allowed:
117+
* - `task` — no subagent fanout from dreamer
118+
* - `edit` / `write` — dreamer must not modify project files;
119+
* `task-prompts.ts` explicitly states "Do not commit changes"
120+
* - `webfetch` / `websearch` — out of scope; smart-note URL fetches
121+
* go through `bash` + `curl` instead
96122
*/
97-
export const DREAMER_ALLOWED_TOOLS = ["read", "ctx_memory", "ctx_search", "ctx_note"] as const;
123+
export const DREAMER_ALLOWED_TOOLS = [
124+
"read",
125+
"grep",
126+
"glob",
127+
"bash",
128+
"ctx_memory",
129+
"ctx_search",
130+
"ctx_note",
131+
] as const;
98132

99133
/**
100134
* Tools the sidekick agent needs. Sidekick is a read-only memory

0 commit comments

Comments
 (0)