fix: harden Pi management readiness

This commit is contained in:
2026-08-05 06:20:25 +02:00
parent 7a8bcacbfe
commit 7df21b5f21
24 changed files with 577 additions and 274 deletions
+46 -46
View File
@@ -10,7 +10,7 @@ import { ComposerFooter, ContextGauge, SteerInput } from "./SteerInput";
beforeEach(() => {
localStorage.clear();
server.use(
http.post("http://localhost:8787/sessions/:id/steer", () =>
http.post("/api/sessions/:id/steer", () =>
new HttpResponse(null, { status: 204 }),
),
);
@@ -22,10 +22,10 @@ test("new sessions send the browser-selected workspace, model, provider, and thi
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "high",
}));
server.use(
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([{
http.get("/api/workspaces", () => HttpResponse.json([{
id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical",
}])),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -49,7 +49,7 @@ test("renders a text input and submit button", () => {
test("submitting typed text POSTs to /steer and clears the input", async () => {
let captured: unknown = null;
server.use(
http.post("http://localhost:8787/sessions/:id/steer", async ({ request, params }) => {
http.post("/api/sessions/:id/steer", async ({ request, params }) => {
captured = { id: params.id, body: await request.json() };
return new HttpResponse(null, { status: 204 });
}),
@@ -67,7 +67,7 @@ test("submitting typed text POSTs to /steer and clears the input", async () => {
test("pressing Enter in the input submits the steer", async () => {
let captured: unknown = null;
server.use(
http.post("http://localhost:8787/sessions/:id/steer", async ({ request }) => {
http.post("/api/sessions/:id/steer", async ({ request }) => {
captured = await request.json();
return new HttpResponse(null, { status: 204 });
}),
@@ -83,7 +83,7 @@ test("pressing Enter in the input submits the steer", async () => {
test("does not POST when input is empty", async () => {
let called = false;
server.use(
http.post("http://localhost:8787/sessions/:id/steer", () => {
http.post("/api/sessions/:id/steer", () => {
called = true;
return new HttpResponse(null, { status: 204 });
}),
@@ -126,15 +126,15 @@ test("the context gauge uses green, yellow, and red at the requested thresholds"
test("footer shows cumulative k-token counters after workspace and context gauge after thinking", async () => {
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({
http.get("/api/settings", () => HttpResponse.json({
workspace: "psd", provider: "zai", model: "glm-5.2", thinking: "medium",
})),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([{ name: "psd" }])),
http.get("http://localhost:8787/workspaces/psd", () => HttpResponse.json({
http.get("/api/workspaces", () => HttpResponse.json([{ name: "psd" }])),
http.get("/api/workspaces/psd", () => HttpResponse.json({
workspace: { llm_policy: { allowed: ["zai/glm-5.2"] } },
revision: { id: "psd", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" },
})),
http.get("http://localhost:8787/models", () => HttpResponse.json({
http.get("/api/models", () => HttpResponse.json({
models: [{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true }],
})),
);
@@ -162,18 +162,18 @@ test("footer shows cumulative k-token counters after workspace and context gauge
test("footer limits model choices to the selected workspace policy", async () => {
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({
http.get("/api/settings", () => HttpResponse.json({
workspace: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "medium",
})),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([{
http.get("/api/workspaces", () => HttpResponse.json([{
id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical",
revision: { id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" },
}])),
http.get("http://localhost:8787/workspaces/psd-clinical", () => HttpResponse.json({
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: { llm_policy: { allowed: ["zai/glm-5.2"] } },
revision: { id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" },
})),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
@@ -195,26 +195,26 @@ test("switching workspaces replaces an out-of-policy model before session creati
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" as const,
});
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({
http.get("/api/settings", () => HttpResponse.json({
workspace: "research", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
})),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([
http.get("/api/workspaces", () => HttpResponse.json([
{ id: "research", name: "research", file: "research.yaml", displayName: "Research", revision: revision("research") },
{ id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical", revision: revision("psd-clinical") },
])),
http.get("http://localhost:8787/workspaces/research", () => HttpResponse.json({
http.get("/api/workspaces/research", () => HttpResponse.json({
workspace: { workspace: { id: "research" }, llm_policy: { allowed: ["deepseek/deepseek-v4-pro"] } },
revision: revision("research"),
})),
http.get("http://localhost:8787/workspaces/psd-clinical", () => HttpResponse.json({
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
workspace: { workspace: { id: "psd-clinical" }, llm_policy: { default: "zai/glm-5.2", allowed: ["zai/glm-5.2"] } },
revision: revision("psd-clinical"),
})),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -248,15 +248,15 @@ test("immediate submit waits for a switched workspace policy before creating a s
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" as const,
});
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({ workspace: "research" })),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([
http.get("/api/settings", () => HttpResponse.json({ workspace: "research" })),
http.get("/api/workspaces", () => HttpResponse.json([
{ id: "research", name: "research", file: "research.yaml", displayName: "Research", revision: revision("research") },
{ id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical", revision: revision("psd-clinical") },
])),
http.get("http://localhost:8787/workspaces/research", () => HttpResponse.json({
http.get("/api/workspaces/research", () => HttpResponse.json({
workspace: { llm_policy: { allowed: ["deepseek/deepseek-v4-pro"] } }, revision: revision("research"),
})),
http.get("http://localhost:8787/workspaces/psd-clinical", async () => {
http.get("/api/workspaces/psd-clinical", async () => {
policyRequestStarted = true;
await policyMayFinish;
return HttpResponse.json({
@@ -264,11 +264,11 @@ test("immediate submit waits for a switched workspace policy before creating a s
revision: revision("psd-clinical"),
});
}),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -304,11 +304,11 @@ test("initial restored workspace waits for its delayed policy before creating a
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" as const,
});
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([
http.get("/api/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
http.get("/api/workspaces", () => HttpResponse.json([
{ id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical", revision: revision("psd-clinical") },
])),
http.get("http://localhost:8787/workspaces/psd-clinical", async () => {
http.get("/api/workspaces/psd-clinical", async () => {
policyRequestStarted = true;
await policyMayFinish;
return HttpResponse.json({
@@ -316,11 +316,11 @@ test("initial restored workspace waits for its delayed policy before creating a
revision: revision("psd-clinical"),
});
}),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -350,18 +350,18 @@ test("initial submit waits for delayed workspace summaries before allowing a con
workspaceId: "legacy-workspace", provider: "deepseek", model: "deepseek-v4-pro", thinking: "medium",
}));
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({ workspace: "legacy-workspace" })),
http.get("http://localhost:8787/workspaces", async () => {
http.get("/api/settings", () => HttpResponse.json({ workspace: "legacy-workspace" })),
http.get("/api/workspaces", async () => {
summaryRequestStarted = true;
await summariesMayFinish;
return HttpResponse.json([{
id: "legacy-workspace", name: "legacy-workspace", file: "legacy-workspace.yaml", displayName: "Legacy workspace",
}]);
}),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -390,15 +390,15 @@ test("failed workspace summaries block creation and report a safe error", async
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "medium",
}));
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
http.get("http://localhost:8787/workspaces", () => {
http.get("/api/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
http.get("/api/workspaces", () => {
summaryRequestFailed = true;
return new HttpResponse(null, { status: 503 });
}),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),
@@ -433,21 +433,21 @@ test("submit follows a rapid workspace switch instead of waiting for an abandone
id, commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot", state: "operational" as const,
});
server.use(
http.get("http://localhost:8787/settings", () => HttpResponse.json({ workspace: "research" })),
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([
http.get("/api/settings", () => HttpResponse.json({ workspace: "research" })),
http.get("/api/workspaces", () => HttpResponse.json([
{ id: "research", name: "research", file: "research.yaml", displayName: "Research", revision: revision("research") },
{ id: "workspace-b", name: "workspace-b", file: "workspace-b.yaml", displayName: "Workspace B", revision: revision("workspace-b") },
{ id: "workspace-c", name: "workspace-c", file: "workspace-c.yaml", displayName: "Workspace C", revision: revision("workspace-c") },
])),
http.get("http://localhost:8787/workspaces/research", () => HttpResponse.json({
http.get("/api/workspaces/research", () => HttpResponse.json({
workspace: { llm_policy: { allowed: ["deepseek/deepseek-v4-pro"] } }, revision: revision("research"),
})),
http.get("http://localhost:8787/workspaces/workspace-b", async () => {
http.get("/api/workspaces/workspace-b", async () => {
bPolicyRequestStarted = true;
await new Promise(() => undefined);
return HttpResponse.json({});
}),
http.get("http://localhost:8787/workspaces/workspace-c", async () => {
http.get("/api/workspaces/workspace-c", async () => {
cPolicyRequestStarted = true;
await cPolicyMayFinish;
return HttpResponse.json({
@@ -455,11 +455,11 @@ test("submit follows a rapid workspace switch instead of waiting for an abandone
revision: revision("workspace-c"),
});
}),
http.get("http://localhost:8787/models", () => HttpResponse.json({ models: [
http.get("/api/models", () => HttpResponse.json({ models: [
{ provider: "zai", id: "glm-5.2", name: "GLM-5.2", reasoning: true },
{ provider: "deepseek", id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true },
] })),
http.post("http://localhost:8787/sessions", async ({ request }) => {
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
}),