Skip to content
Closed
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
9 changes: 8 additions & 1 deletion src/v2-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } }
},
Expand Down
19 changes: 19 additions & 0 deletions test/v2-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading