feat: add permission request UI
Build Push and Deploy / docker-image (push) Successful in 1m2s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-08 13:32:50 +08:00
parent 5fc1812d53
commit e32823e4b5
9 changed files with 999 additions and 12 deletions
+63 -6
View File
@@ -1,6 +1,8 @@
import {
abortAgentChat,
forkAgentChat,
replyAgentPermission,
type StreamEvent,
resumeAgentChatStream,
streamAgentChat,
} from "./chatStream";
@@ -162,12 +164,7 @@ describe("streamAgentChat", () => {
]),
});
const events: Array<{
type: string;
sessionId?: string;
tool?: string;
params?: Record<string, unknown>;
}> = [];
const events: StreamEvent[] = [];
await streamAgentChat({
message: "hi",
@@ -182,6 +179,43 @@ describe("streamAgentChat", () => {
});
});
it("parses permission request and response events", async () => {
apiFetch.mockResolvedValue({
ok: true,
body: makeStream([
'event: permission_request\ndata: {"session_id":"s1","request_id":"perm-1","permission":"bash","patterns":["rm *"],"metadata":{"command":"rm tmp.txt"},"always":["rm *"],"created_at":123}\n\n',
'event: permission_response\ndata: {"session_id":"s1","request_id":"perm-1","reply":"reject"}\n\n',
]),
});
const events: StreamEvent[] = [];
await streamAgentChat({
message: "hi",
onEvent: (event) => events.push(event),
});
expect(events).toEqual([
{
type: "permission_request",
sessionId: "s1",
requestId: "perm-1",
permission: "bash",
patterns: ["rm *"],
metadata: { command: "rm tmp.txt" },
always: ["rm *"],
tool: undefined,
createdAt: 123,
},
{
type: "permission_response",
sessionId: "s1",
requestId: "perm-1",
reply: "reject",
},
]);
});
it("emits error when response is not ok", async () => {
apiFetch.mockResolvedValue({
ok: false,
@@ -255,6 +289,29 @@ describe("streamAgentChat", () => {
);
});
it("calls permission reply endpoint", async () => {
apiFetch.mockResolvedValue({
ok: true,
status: 202,
text: async () => "",
});
await replyAgentPermission("s1", "perm-1", "once");
expect(apiFetch).toHaveBeenCalledWith(
expect.stringContaining("/api/v1/agent/chat/permission/perm-1/reply"),
expect.objectContaining({
method: "POST",
projectHeaderMode: "include",
skipAuthRedirect: true,
body: JSON.stringify({
session_id: "s1",
reply: "once",
}),
}),
);
});
it("calls fork endpoint and returns new session id", async () => {
apiFetch.mockResolvedValue({
ok: true,