feat(chat): 支持自动始终允许权限

This commit is contained in:
2026-06-08 14:14:52 +08:00
parent 05d36aa8ca
commit f61389ab07
3 changed files with 99 additions and 6 deletions
+3
View File
@@ -34,6 +34,7 @@ const payloadSchema = z.object({
message: z.string().min(1).max(10000),
session_id: z.string().max(128).optional(),
model: z.enum(supportedModels).optional(),
approval_mode: z.enum(["request", "always"]).optional().default("request"),
});
const abortPayloadSchema = z.object({
@@ -968,6 +969,7 @@ export const buildChatRouter = (
sessionId: binding.sessionId,
created: created || sessionCreated,
model: parsed.data.model,
approvalMode: parsed.data.approval_mode,
traceId: requestContext.traceId,
projectId: requestContext.projectId,
},
@@ -1137,6 +1139,7 @@ export const buildChatRouter = (
clientSessionId,
message: preparedMessage,
model: parsed.data.model,
approvalMode: parsed.data.approval_mode,
traceId: requestContext.traceId,
projectId: requestContext.projectId,
signal: abortController.signal,
+41 -6
View File
@@ -10,6 +10,7 @@ export const supportedModels = [
] as const;
export type SupportedModel = (typeof supportedModels)[number];
export type ApprovalMode = "request" | "always";
type StreamPromptOptions = {
runtime: OpencodeRuntimeAdapter;
@@ -17,6 +18,7 @@ type StreamPromptOptions = {
clientSessionId: string;
message: string;
model?: SupportedModel;
approvalMode?: ApprovalMode;
traceId?: string;
projectId?: string;
signal?: AbortSignal;
@@ -345,6 +347,7 @@ export const streamPromptResponse = async ({
clientSessionId,
message,
model,
approvalMode = "request",
traceId,
projectId,
signal,
@@ -591,10 +594,26 @@ export const streamPromptResponse = async ({
emitProgress({
id: `permission-${event.properties.id}`,
phase: "permission",
status: "running",
title: "等待权限确认",
detail: buildPermissionDetail(event),
status: approvalMode === "always" ? "completed" : "running",
title: approvalMode === "always" ? "已自动允许权限请求" : "等待权限确认",
detail:
approvalMode === "always"
? "当前批准模式为始终允许,已自动允许本次权限请求。"
: buildPermissionDetail(event),
});
if (approvalMode === "always") {
await runtime.replyPermission({
requestId: event.properties.id,
sessionId,
reply: "always",
});
write("permission_response", {
session_id: clientSessionId,
request_id: event.properties.id,
reply: "always" satisfies PermissionReply,
});
continue;
}
write("permission_request", {
session_id: clientSessionId,
request_id: event.properties.id,
@@ -620,10 +639,26 @@ export const streamPromptResponse = async ({
emitProgress({
id: `permission-${event.properties.id}`,
phase: "permission",
status: "running",
title: "等待权限确认",
detail: buildPermissionV2Detail(event),
status: approvalMode === "always" ? "completed" : "running",
title: approvalMode === "always" ? "已自动允许权限请求" : "等待权限确认",
detail:
approvalMode === "always"
? "当前批准模式为始终允许,已自动允许本次权限请求。"
: buildPermissionV2Detail(event),
});
if (approvalMode === "always") {
await runtime.replyPermission({
requestId: event.properties.id,
sessionId,
reply: "always",
});
write("permission_response", {
session_id: clientSessionId,
request_id: event.properties.id,
reply: "always" satisfies PermissionReply,
});
continue;
}
write("permission_request", {
session_id: clientSessionId,
request_id: event.properties.id,
+55
View File
@@ -61,6 +61,61 @@ describe("streamPromptResponse", () => {
} satisfies Partial<PermissionRequestPayload>);
});
it("auto replies always when approval mode is always", async () => {
const replies: Array<Record<string, unknown>> = [];
const runtime = {
subscribeEvents: async () =>
createEventStream([
{
type: "permission.asked",
properties: {
id: "perm-1",
sessionID: "runtime-session-1",
permission: "bash",
patterns: ["npm test"],
metadata: { command: "npm test" },
always: ["npm test"],
},
},
{
type: "session.idle",
properties: {
sessionID: "runtime-session-1",
},
},
]),
prompt: async () => undefined,
messages: async () => [],
replyPermission: async (options: Record<string, unknown>) => {
replies.push(options);
},
} as unknown as OpencodeRuntimeAdapter;
const events: Array<{ event: string; data: Record<string, unknown> }> = [];
await streamPromptResponse({
runtime,
sessionId: "runtime-session-1",
clientSessionId: "client-session-1",
message: "run tests",
approvalMode: "always",
write: (event, data) => events.push({ event, data }),
});
expect(replies).toEqual([
{
requestId: "perm-1",
sessionId: "runtime-session-1",
reply: "always",
},
]);
expect(events.some((item) => item.event === "permission_request")).toBe(false);
expect(events.find((item) => item.event === "permission_response")?.data).toEqual({
session_id: "client-session-1",
request_id: "perm-1",
reply: "always",
});
});
it("forwards opencode v2 permission requests as SSE payloads", async () => {
const runtime = {
subscribeEvents: async () =>