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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user