Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,11 @@ export const layer = Layer.effect(
permissions.push({ permission: t, action: enabled ? "allow" : "deny", pattern: "*" })
}
if (permissions.length > 0) {
session.permission = permissions
yield* sessions.setPermission({ sessionID: session.id, permission: permissions })
// Merge so per-call tool rules don't clobber inherited session rules
// (e.g. external_directory allows from the parent session).
const merged = Permission.merge(session.permission ?? [], permissions)
session.permission = merged
yield* sessions.setPermission({ sessionID: session.id, permission: merged })
}

if (input.noReply === true) return message
Expand Down
27 changes: 27 additions & 0 deletions packages/opencode/test/session/prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,33 @@ it.instance("failed subtask preserves metadata on error tool state", () =>
}),
)

it.instance("subtask child inherits parent session external_directory allow", () =>
Effect.gen(function* () {
const { llm } = yield* useServerConfig(providerCfg)
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const chat = yield* sessions.create({
title: "Parent",
permission: [{ permission: "external_directory", pattern: "/tmp/allowed/*", action: "allow" }],
})
yield* llm.text("done")
const msg = yield* user(chat.id, "hello")
yield* addSubtask(chat.id, msg.id)

yield* prompt.loop({ sessionID: chat.id })

const kids = yield* sessions.children(chat.id)
expect(kids).toHaveLength(1)
const child = kids[0]!
const rules = child.permission ?? []
expect(rules).toEqual(
expect.arrayContaining([{ permission: "external_directory", pattern: "/tmp/allowed/*", action: "allow" }]),
)
expect(Permission.evaluate("external_directory", "/tmp/allowed/file", rules).action).toBe("allow")
expect(Permission.evaluate("task", "anything", rules).action).toBe("deny")
}),
)

it.instance(
"running subtask preserves metadata after tool-call transition",
() =>
Expand Down
Loading