diff --git a/src/v2-client.ts b/src/v2-client.ts index e70b3c0..fda0878 100644 --- a/src/v2-client.ts +++ b/src/v2-client.ts @@ -119,7 +119,14 @@ export function createV2Client(ctx: V2Context, deps: V2ClientDeps = {}): PluginC // and agent rules apply later), which strips team tools from // read-only agents. Re-assert here: rules() evaluates after the // agent's rules, restoring the tools. OQ-V2-rules. - await ctx.permission.rules({ sessionID: created.id, permissions }) + // + // Host compatibility: opencode 2.0.27+ no longer exposes + // permission.rules, which would otherwise throw on every spawn. + // The create-input permissions above are still applied; this + // re-assert is best-effort, so skip it when absent. + if (typeof ctx.permission.rules === "function") { + await ctx.permission.rules({ sessionID: created.id, permissions }) + } } return { data: { id: created.id } } }, diff --git a/test/v2-client.test.ts b/test/v2-client.test.ts index eac4aec..7265cb8 100644 --- a/test/v2-client.test.ts +++ b/test/v2-client.test.ts @@ -94,6 +94,25 @@ describe("v2-client adapter (issue #36, DRY: reuses all tool logic)", () => { }) }) + test("session.create tolerates hosts without permission.rules (opencode 2.0.27+)", async () => { + const { calls, ctx } = mockCtx() + // Newer hosts dropped the rules() re-assert API entirely. + const ctxWithoutRules = { ...ctx, permission: {} } + const client = createV2Client(ctxWithoutRules as never) + const result = await client.session.create({ + parentID: "ses_lead", + title: "alice (@build teammate)", + permission: [{ permission: "team_message", pattern: "*", action: "allow" }], + }) + // Create still succeeded, and the permission rules ship in the input. + expect(result.data?.id).toBe("ses_new123") + expect(calls[0]?.input).toMatchObject({ + permissions: [{ action: "team_message", resource: "*", effect: "allow" }], + }) + // The absent rules() must not throw or be called. + expect(calls.find((c) => c.method === "rules")).toBeUndefined() + }) + test("session.create maps parentID/title/permissions to V2 shape", async () => { const { calls: calls2, ctx: ctx2 } = mockCtx() const client2 = createV2Client(ctx2 as never)