diff --git a/web/adapter/pi-adapter.ts b/web/adapter/pi-adapter.ts index fb4c0bc9..66c93395 100644 --- a/web/adapter/pi-adapter.ts +++ b/web/adapter/pi-adapter.ts @@ -284,6 +284,14 @@ export class PiWebAdapter { }); } + async unarchiveSession(path: string) { + await this.ensureArchivesLoaded(); + await this.enqueueArchiveMutation(async (draft) => { + const session = await this.requireSession(path); + draft.delete(resolve(session.path)); + }); + } + async removeWorkspace(path: string) { await this.ensureWorkspaceStateLoaded(); const canonical = resolve(path); @@ -490,6 +498,7 @@ export class PiWebAdapter { status: this.runtime.isIdle() ? ("idle" as const) : ("running" as const), + trust: this.runtime.projectTrustState ?? "unknown", capabilities: webCapabilitySnapshot(this.runtime.sessionManager), }, truncation: { diff --git a/web/host/web-host.ts b/web/host/web-host.ts index 6203e647..b1f0597d 100644 --- a/web/host/web-host.ts +++ b/web/host/web-host.ts @@ -425,6 +425,13 @@ export class WebHost { this.publish("session_archived", { sessionPath: path }); return this.json(response, 200, { path, archived: true }); } + if (url.pathname === "/api/sessions/unarchive" && request.method === "POST") { + const path = url.searchParams.get("path"); + if (!path) return this.json(response, 400, { error: "session path is required" }); + await this.adapter.unarchiveSession(path); + this.publish("session_unarchived", { sessionPath: path }); + return this.json(response, 200, { path, archived: false }); + } if (url.pathname === "/api/sessions/select" && request.method === "POST") { const body = await this.readJson(request); if (typeof body.path !== "string" || body.path.trim().length === 0) { diff --git a/web/protocol/types.ts b/web/protocol/types.ts index a7429b6e..90181b4e 100644 --- a/web/protocol/types.ts +++ b/web/protocol/types.ts @@ -113,6 +113,7 @@ export interface WebSnapshot { models: WebModelSummary[]; runtime: { status: "idle" | "running" | "unknown"; + trust: "trusted" | "untrusted" | "unknown"; capabilities: WebCapabilitySnapshot; }; truncation: WebSnapshotTruncation; diff --git a/web/runtime/pi-runtime.ts b/web/runtime/pi-runtime.ts index cf792d39..1ea69196 100644 --- a/web/runtime/pi-runtime.ts +++ b/web/runtime/pi-runtime.ts @@ -86,6 +86,17 @@ export class PiWebRuntime implements WebRuntimeController { private disposePromise?: Promise; private hasSelectedWorkspace: boolean; + get projectTrustState() { + if (!this.hasSelectedWorkspace) return "unknown" as const; + try { + return this.runtime.session.settingsManager.isProjectTrusted() + ? ("trusted" as const) + : ("untrusted" as const); + } catch { + return "unknown" as const; + } + } + private constructor( runtime: AgentSessionRuntime, webSessionDirectory: string, diff --git a/web/runtime/types.ts b/web/runtime/types.ts index 2b66c3f0..11f2f0d2 100644 --- a/web/runtime/types.ts +++ b/web/runtime/types.ts @@ -53,6 +53,7 @@ export interface WebRuntimeController { readonly workspaceSelected: boolean; readonly sessionDirectory: string; readonly sessionManager: SessionManager; + readonly projectTrustState?: "trusted" | "untrusted" | "unknown"; isIdle(): boolean; sendPrompt(content: string, options?: WebPromptOptions): Promise; newSession(