Просмотр исходного кода

fix(session): merge per-call tool rules into session permission (#30529)

Co-authored-by: Simon Klee <hello@simonklee.dk>
Tommy D. Rossi 3 месяцев назад
Родитель
Сommit
0050134d9e

+ 5 - 2
packages/opencode/src/session/prompt.ts

@@ -1198,8 +1198,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

+ 27 - 0
packages/opencode/test/session/prompt.test.ts

@@ -797,6 +797,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",
   () =>