feat(frontend): open a live session straight to its pending gate
Opening an in-progress session that has a live Pi runtime now reconnects to its pending gate instead of the empty landing screen that read as "stopped". Cold/completed sessions keep the read-only documents panel with its explicit Resume, so a mere click never spawns a runtime. Backend GET /sessions now reports a per-session `active` flag (live runtime bound) to drive this. Also: - "New session" now closes any open session detail panel (left box). - The model-activity separator can be dragged to a full 50/50 split (was capped at 576px); central-min still guards narrow viewports. Test fixes uncovered along the way: - Node 25 ships an experimental global localStorage that shadows jsdom's and lacks clear(), failing every jsdom test at setup; install a spec-compliant in-memory Storage (feature-detected, inert on CI/LTS). - Fix 4 pre-existing session-mgmt tests that used an ambiguous getByText for a session shown in both nav and header; target the nav item by test id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import type { PiProcessManager } from "../pi/pi-process-manager.js";
|
||||
import type { ThtRunner } from "../tht/tht-runner.js";
|
||||
import type { SessionRow, ThtRunner } from "../tht/tht-runner.js";
|
||||
import type { SseHub } from "../sse/sse-hub.js";
|
||||
import type { Settings } from "../settings/settings-store.js";
|
||||
import { getPrincipal } from "../auth/auth.js";
|
||||
@@ -213,7 +213,12 @@ export function sessionRoutes(
|
||||
const settings = await d.getSettings(principal);
|
||||
// Admin RLS is deliberately disabled for a normal 'mine' listing.
|
||||
const scopedPrincipal = scope === "mine" ? { ...principal, isAdmin: false } : principal;
|
||||
return await runnerFor(scopedPrincipal).sessionList(settings.workspace);
|
||||
const list: SessionRow[] = await runnerFor(scopedPrincipal).sessionList(settings.workspace);
|
||||
// Annotate each row with whether a live Pi runtime is currently bound. The client
|
||||
// opens an `active` session straight into its live view (reconnecting to its pending
|
||||
// gate), while a cold session keeps its explicit Resume affordance — so a mere click
|
||||
// never spawns a runtime.
|
||||
return list.map((row) => ({ ...row, active: d.mgr.get(row.id) !== undefined }));
|
||||
} catch { return storageFailure(reply); }
|
||||
});
|
||||
app.get("/sessions/:id", async (req, reply) => {
|
||||
|
||||
@@ -109,11 +109,11 @@ test("A, B, and admin requests preserve owner isolation through session route mu
|
||||
const adminHeaders = { ...aliceHeaders, "x-thoth-principal-subject": "admin", "x-thoth-is-admin": "1" };
|
||||
|
||||
expect((await app.inject({ method: "GET", url: "/sessions", headers: aliceHeaders })).json())
|
||||
.toEqual([{ id: "a", author: "alice" }]);
|
||||
.toEqual([{ id: "a", author: "alice", active: false }]);
|
||||
expect((await app.inject({ method: "GET", url: "/sessions", headers: bobHeaders })).json())
|
||||
.toEqual([{ id: "b", author: "bob" }]);
|
||||
.toEqual([{ id: "b", author: "bob", active: false }]);
|
||||
expect((await app.inject({ method: "GET", url: "/sessions?scope=all", headers: adminHeaders })).json())
|
||||
.toEqual([{ id: "a", author: "alice" }, { id: "b", author: "bob" }]);
|
||||
.toEqual([{ id: "a", author: "alice", active: false }, { id: "b", author: "bob", active: false }]);
|
||||
|
||||
expect((await app.inject({ method: "POST", url: "/sessions/a/close", headers: bobHeaders })).statusCode)
|
||||
.toBe(404);
|
||||
@@ -174,7 +174,7 @@ test("POST /sessions usa i settings (workspace/provider/model/thinking) e crea+a
|
||||
expect(sessionNewArg.thinking).toBe("high");
|
||||
expect(sessionNewArg.question).toBe("q");
|
||||
const list = await app.inject({ method: "GET", url: "/sessions" });
|
||||
expect(list.json()).toEqual([{ id: "s1" }]);
|
||||
expect(list.json()).toEqual([{ id: "s1", active: expect.any(Boolean) }]);
|
||||
unlinkSync(modelKey);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user