Skip to content

Commit c2b38bb

Browse files
test(opencode): reproduce issue 20885, Full access keeps Auto session permissions
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent 8acbefe commit c2b38bb

1 file changed

Lines changed: 115 additions & 0 deletions

File tree

‎packages/provider-opencode/src/server/adapter.test.ts‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,121 @@ describe("OpenCodeAdapterV2", () => {
21912191
),
21922192
);
21932193

2194+
it.effect("installs Full access rules on a native session it resumes from Auto", () =>
2195+
Effect.gen(function* () {
2196+
const nativeSessions = new Map<string, Record<string, unknown>>();
2197+
const updateInputs: Array<unknown> = [];
2198+
const fakeClient = {
2199+
event: {
2200+
subscribe: async (_input?: unknown, options?: { readonly signal?: AbortSignal }) => ({
2201+
stream: {
2202+
[Symbol.asyncIterator]: () => ({
2203+
next: () =>
2204+
new Promise<IteratorResult<never>>((resolve) => {
2205+
const done = () => resolve({ done: true, value: undefined });
2206+
if (options?.signal?.aborted) return done();
2207+
options?.signal?.addEventListener("abort", done, { once: true });
2208+
}),
2209+
}),
2210+
},
2211+
}),
2212+
},
2213+
session: {
2214+
create: async (input: { readonly permission?: unknown }) => {
2215+
const nativeSession = {
2216+
id: "ses_native_auto",
2217+
permission: input.permission,
2218+
time: { created: 1, updated: 1 },
2219+
};
2220+
nativeSessions.set(nativeSession.id, nativeSession);
2221+
return { data: nativeSession };
2222+
},
2223+
get: async ({ sessionID }: { readonly sessionID: string }) => ({
2224+
data: nativeSessions.get(sessionID),
2225+
}),
2226+
update: async (input: { readonly sessionID: string; readonly permission?: unknown }) => {
2227+
updateInputs.push(input);
2228+
const nativeSession = { ...nativeSessions.get(input.sessionID), ...input };
2229+
nativeSessions.set(input.sessionID, nativeSession);
2230+
return { data: nativeSession };
2231+
},
2232+
},
2233+
} as unknown as OpencodeClient;
2234+
const unused = (operation: string) => () => Effect.die(`${operation} is not used`);
2235+
const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = {
2236+
startOpenCodeServerProcess: unused("startOpenCodeServerProcess"),
2237+
connectToOpenCodeServer: () =>
2238+
Effect.succeed({
2239+
url: "test://opencode",
2240+
version: "test",
2241+
exitCode: null,
2242+
external: true,
2243+
}),
2244+
runOpenCodeCommand: unused("runOpenCodeCommand"),
2245+
createOpenCodeSdkClient: () => fakeClient,
2246+
loadOpenCodeInventory: unused("loadOpenCodeInventory"),
2247+
loadInventoryFromCli: unused("loadInventoryFromCli"),
2248+
loadOpenCodeSkills: unused("loadOpenCodeSkills"),
2249+
loadSkillsFromCli: unused("loadSkillsFromCli"),
2250+
};
2251+
const instanceId = ProviderInstanceId.make("opencode");
2252+
const threadId = ThreadId.make("thread-opencode-mode-switch");
2253+
const modelSelection = { instanceId, model: "default" };
2254+
const auto = runtimePolicy("auto");
2255+
const fullAccess = runtimePolicy("full-access");
2256+
const adapter = yield* makeOpenCodeAdapterV2({
2257+
instanceId,
2258+
settings: OPENCODE_TEST_SETTINGS,
2259+
environment: {},
2260+
}).pipe(Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, runtime));
2261+
const autoSession = yield* adapter.openSession({
2262+
threadId,
2263+
providerSessionId: ProviderSessionId.make("provider-session-opencode-auto"),
2264+
modelSelection,
2265+
runtimePolicy: auto,
2266+
});
2267+
const providerThread = yield* autoSession.ensureThread({
2268+
threadId,
2269+
modelSelection,
2270+
runtimePolicy: auto,
2271+
});
2272+
assert.deepEqual(
2273+
nativeSessions.get("ses_native_auto")?.permission,
2274+
openCodePermissionRules(auto),
2275+
);
2276+
assert.equal(permissionAction(openCodePermissionRules(auto), "edit"), "ask");
2277+
2278+
// Switching the composer to Full access reloads the existing native
2279+
// session under the new policy; its creation-time Auto rules must not
2280+
// survive, or OpenCode keeps asking for edits and shell commands.
2281+
const fullAccessSession = yield* adapter.openSession({
2282+
threadId,
2283+
providerSessionId: ProviderSessionId.make("provider-session-opencode-full-access"),
2284+
modelSelection,
2285+
runtimePolicy: fullAccess,
2286+
});
2287+
yield* fullAccessSession.resumeThread({
2288+
providerThread,
2289+
threadId,
2290+
modelSelection,
2291+
runtimePolicy: fullAccess,
2292+
});
2293+
2294+
assert.deepEqual(updateInputs, [
2295+
{ sessionID: "ses_native_auto", permission: openCodePermissionRules(fullAccess) },
2296+
]);
2297+
}).pipe(
2298+
Effect.scoped,
2299+
Effect.provide(
2300+
Layer.mergeAll(
2301+
IdAllocator.layer,
2302+
McpProviderSessions.layer,
2303+
TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)),
2304+
),
2305+
),
2306+
),
2307+
);
2308+
21942309
it("maps runtime modes to safe OpenCode permission rules", () => {
21952310
const approvalRequired = openCodePermissionRules(runtimePolicy("approval-required"));
21962311
assert.equal(permissionAction(approvalRequired, "read"), "allow");

0 commit comments

Comments
 (0)