daemon: integrate QoL slices 1-5 (model/rename/catalog, history deletes, stats, repo prepare+imageUsed+built) — 95.3% cover green

This commit is contained in:
Raphael Westphal
2026-08-19 10:07:52 +02:00
parent 7287b831e4
commit 698b742cb3
16 changed files with 1506 additions and 36 deletions
+92
View File
@@ -556,6 +556,8 @@ export default function (pi: ExtensionAPI): void {
if (f.type === "welcome") onWelcome(f as Record<string, unknown>);
else if (f.type === "prompt") deliverPrompt(f);
else if (f.type === "abort") doAbort();
else if (f.type === "set_model") applySetModel(f);
else if (f.type === "rename") applyRename(f);
// unknown types are ignored (forward compatibility)
}
@@ -587,6 +589,96 @@ export default function (pi: ExtensionAPI): void {
}
}
/** Daemon-requested model switch. Success is mirrored by the resulting
* model_select event (which emits session_info); every failure path
* emits error_notice so the web sees why nothing changed. */
function applySetModel(frame: {
modelId?: unknown;
provider?: unknown;
sessionId?: unknown;
}): void {
const modelId = frame.modelId;
const provider = frame.provider;
if (
typeof frame.sessionId === "string" &&
frame.sessionId !== currentSessionId
)
return;
if (typeof modelId !== "string" || modelId.length === 0) {
emit("error_notice", { reason: "set_model without modelId" });
return;
}
if (typeof provider !== "string" || provider.length === 0) {
emit("error_notice", { reason: "set_model without provider" });
return;
}
const registry = lastCtx?.modelRegistry as
| { find?: (provider: string, modelId: string) => unknown }
| undefined;
if (registry === undefined || typeof registry.find !== "function") {
emit("error_notice", { reason: "model registry unavailable" });
return;
}
let model: unknown;
try {
model = registry.find(provider, modelId);
} catch (err) {
log(`model lookup failed: ${err}`);
emit("error_notice", {
reason: `model lookup failed: ${String(err)}`,
});
return;
}
if (model === undefined || model === null) {
emit("error_notice", {
reason: `model not found: ${provider}/${modelId}`,
});
return;
}
const maybeSet = (pi as unknown as { setModel?: (m: unknown) => unknown })
.setModel;
if (typeof maybeSet !== "function") {
emit("error_notice", { reason: "pi.setModel unavailable" });
return;
}
try {
Promise.resolve(maybeSet.call(pi, model)).then(
(ok: unknown) => {
if (ok !== true)
emit("error_notice", {
reason: `setModel rejected ${provider}/${modelId} (no API key?)`,
});
},
(err: unknown) => {
log(`setModel failed: ${err}`);
emit("error_notice", {
reason: `setModel failed: ${String(err)}`,
});
},
);
} catch (err) {
log(`setModel threw: ${err}`);
emit("error_notice", { reason: `setModel failed: ${String(err)}` });
}
}
/** Daemon-requested rename. pi has no extension rename API: only the
* mirrored snapshot changes (PROTOCOL.md); the TUI session keeps its
* own name. session_info propagates the change to the daemon/web. */
function applyRename(frame: { name?: unknown; sessionId?: unknown }): void {
const name = frame.name;
if (typeof name !== "string" || name.length === 0) return;
if (
typeof frame.sessionId === "string" &&
frame.sessionId !== currentSessionId
)
return;
if (snapshot === null) return;
if (name === snapshot.name) return;
snapshot.name = name;
emit("session_info", { session: snapshot });
}
function buildSnapshot(
ctx: ExtensionContext,
sessionId: string,