feat(webui): add project workspaces and access controls (#4007)

* feat(webui): add project workspaces and access controls

* feat(webui): add project workspaces and access controls

* refactor(tools): centralize workspace access resolution

* refactor(webui): remove unused workspace host state

* fix(webui): hide estimated file edit label

* fix(webui): clarify file edit deletion feedback

* fix(webui): label deleted file activity

* fix(webui): flatten file edit activity rows

* fix(core): remove path-only patch deletion

* fix(core): keep apply patch non-destructive

* refactor(webui): trim workspace host plumbing

* fix(tools): register exec with tools config
This commit is contained in:
Xubin Ren
2026-05-29 03:42:53 +08:00
committed by GitHub
parent 84428136e6
commit 3a420136bb
111 changed files with 9972 additions and 1822 deletions
+5 -4
View File
@@ -9,7 +9,7 @@ import {
listSessions,
} from "@/lib/api";
import { deriveTitle } from "@/lib/format";
import type { ChatSummary, UIMessage } from "@/lib/types";
import type { ChatSummary, UIMessage, WorkspaceScopePayload } from "@/lib/types";
const EMPTY_MESSAGES: UIMessage[] = [];
@@ -19,7 +19,7 @@ export function useSessions(): {
loading: boolean;
error: string | null;
refresh: () => Promise<void>;
createChat: () => Promise<string>;
createChat: (workspaceScope?: WorkspaceScopePayload | null) => Promise<string>;
deleteChat: (key: string) => Promise<void>;
} {
const { client, token } = useClient();
@@ -66,8 +66,8 @@ export function useSessions(): {
});
}, [client, refresh]);
const createChat = useCallback(async (): Promise<string> => {
const chatId = await client.newChat();
const createChat = useCallback(async (workspaceScope?: WorkspaceScopePayload | null): Promise<string> => {
const chatId = await client.newChat(5_000, workspaceScope);
const key = `websocket:${chatId}`;
optimisticKeysRef.current.add(key);
// Optimistic insert; a subsequent refresh will replace it with the
@@ -81,6 +81,7 @@ export function useSessions(): {
updatedAt: new Date().toISOString(),
title: "",
preview: "",
workspaceScope: workspaceScope ?? null,
},
...prev.filter((s) => s.key !== key),
]);