feat(chat): 支持自动始终允许权限
This commit is contained in:
@@ -34,6 +34,7 @@ const payloadSchema = z.object({
|
|||||||
message: z.string().min(1).max(10000),
|
message: z.string().min(1).max(10000),
|
||||||
session_id: z.string().max(128).optional(),
|
session_id: z.string().max(128).optional(),
|
||||||
model: z.enum(supportedModels).optional(),
|
model: z.enum(supportedModels).optional(),
|
||||||
|
approval_mode: z.enum(["request", "always"]).optional().default("request"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const abortPayloadSchema = z.object({
|
const abortPayloadSchema = z.object({
|
||||||
@@ -968,6 +969,7 @@ export const buildChatRouter = (
|
|||||||
sessionId: binding.sessionId,
|
sessionId: binding.sessionId,
|
||||||
created: created || sessionCreated,
|
created: created || sessionCreated,
|
||||||
model: parsed.data.model,
|
model: parsed.data.model,
|
||||||
|
approvalMode: parsed.data.approval_mode,
|
||||||
traceId: requestContext.traceId,
|
traceId: requestContext.traceId,
|
||||||
projectId: requestContext.projectId,
|
projectId: requestContext.projectId,
|
||||||
},
|
},
|
||||||
@@ -1137,6 +1139,7 @@ export const buildChatRouter = (
|
|||||||
clientSessionId,
|
clientSessionId,
|
||||||
message: preparedMessage,
|
message: preparedMessage,
|
||||||
model: parsed.data.model,
|
model: parsed.data.model,
|
||||||
|
approvalMode: parsed.data.approval_mode,
|
||||||
traceId: requestContext.traceId,
|
traceId: requestContext.traceId,
|
||||||
projectId: requestContext.projectId,
|
projectId: requestContext.projectId,
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const supportedModels = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type SupportedModel = (typeof supportedModels)[number];
|
export type SupportedModel = (typeof supportedModels)[number];
|
||||||
|
export type ApprovalMode = "request" | "always";
|
||||||
|
|
||||||
type StreamPromptOptions = {
|
type StreamPromptOptions = {
|
||||||
runtime: OpencodeRuntimeAdapter;
|
runtime: OpencodeRuntimeAdapter;
|
||||||
@@ -17,6 +18,7 @@ type StreamPromptOptions = {
|
|||||||
clientSessionId: string;
|
clientSessionId: string;
|
||||||
message: string;
|
message: string;
|
||||||
model?: SupportedModel;
|
model?: SupportedModel;
|
||||||
|
approvalMode?: ApprovalMode;
|
||||||
traceId?: string;
|
traceId?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
@@ -345,6 +347,7 @@ export const streamPromptResponse = async ({
|
|||||||
clientSessionId,
|
clientSessionId,
|
||||||
message,
|
message,
|
||||||
model,
|
model,
|
||||||
|
approvalMode = "request",
|
||||||
traceId,
|
traceId,
|
||||||
projectId,
|
projectId,
|
||||||
signal,
|
signal,
|
||||||
@@ -591,10 +594,26 @@ export const streamPromptResponse = async ({
|
|||||||
emitProgress({
|
emitProgress({
|
||||||
id: `permission-${event.properties.id}`,
|
id: `permission-${event.properties.id}`,
|
||||||
phase: "permission",
|
phase: "permission",
|
||||||
status: "running",
|
status: approvalMode === "always" ? "completed" : "running",
|
||||||
title: "等待权限确认",
|
title: approvalMode === "always" ? "已自动允许权限请求" : "等待权限确认",
|
||||||
detail: buildPermissionDetail(event),
|
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", {
|
write("permission_request", {
|
||||||
session_id: clientSessionId,
|
session_id: clientSessionId,
|
||||||
request_id: event.properties.id,
|
request_id: event.properties.id,
|
||||||
@@ -620,10 +639,26 @@ export const streamPromptResponse = async ({
|
|||||||
emitProgress({
|
emitProgress({
|
||||||
id: `permission-${event.properties.id}`,
|
id: `permission-${event.properties.id}`,
|
||||||
phase: "permission",
|
phase: "permission",
|
||||||
status: "running",
|
status: approvalMode === "always" ? "completed" : "running",
|
||||||
title: "等待权限确认",
|
title: approvalMode === "always" ? "已自动允许权限请求" : "等待权限确认",
|
||||||
detail: buildPermissionV2Detail(event),
|
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", {
|
write("permission_request", {
|
||||||
session_id: clientSessionId,
|
session_id: clientSessionId,
|
||||||
request_id: event.properties.id,
|
request_id: event.properties.id,
|
||||||
|
|||||||
@@ -61,6 +61,61 @@ describe("streamPromptResponse", () => {
|
|||||||
} satisfies Partial<PermissionRequestPayload>);
|
} 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 () => {
|
it("forwards opencode v2 permission requests as SSE payloads", async () => {
|
||||||
const runtime = {
|
const runtime = {
|
||||||
subscribeEvents: async () =>
|
subscribeEvents: async () =>
|
||||||
|
|||||||
Reference in New Issue
Block a user