Files
oh-my-pi/packages/coding-agent/test/issue-1940-repro.test.ts
T
Adryel Dearo ecbc2a3c15 feat(coding-agent): add configurable title system prompt for sessions
- add discovery of `TITLE_SYSTEM.md` and pass it through interactive startup context
- route custom title prompts to online and local tiny title generators via protocol
- update session-title docs and changelog with override behavior
- add tests for prompt discovery, forwarding, and fallback to bundled title prompts
2026-06-10 08:31:32 +02:00

117 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { TinyTitleClient } from "@oh-my-pi/pi-coding-agent/tiny/title-client";
import type { TinyTitleWorkerInbound, TinyTitleWorkerOutbound } from "@oh-my-pi/pi-coding-agent/tiny/title-protocol";
class FakeTinyWorker {
terminated = false;
#messageHandlers = new Set<(message: TinyTitleWorkerOutbound) => void>();
#onSend: (message: TinyTitleWorkerInbound, worker: FakeTinyWorker) => void;
constructor(onSend: (message: TinyTitleWorkerInbound, worker: FakeTinyWorker) => void) {
this.#onSend = onSend;
}
send(message: TinyTitleWorkerInbound): void {
this.#onSend(message, this);
}
onMessage(handler: (message: TinyTitleWorkerOutbound) => void): () => void {
this.#messageHandlers.add(handler);
return () => this.#messageHandlers.delete(handler);
}
onError(): () => void {
return () => {};
}
async terminate(): Promise<void> {
this.terminated = true;
}
emit(message: TinyTitleWorkerOutbound): void {
for (const handler of this.#messageHandlers) handler(message);
}
}
describe("tiny title client prompt options", () => {
it("forwards a custom system prompt on local title requests", async () => {
let sent: TinyTitleWorkerInbound | undefined;
const worker = new FakeTinyWorker((message, worker) => {
sent = message;
if (message.type === "generate") {
worker.emit({ type: "title", id: message.id, title: "custom title" });
}
});
const client = new TinyTitleClient(() => worker);
try {
const title = await client.generate("lfm2-350m", "Investigate routing", {
systemPrompt: "Custom title prompt",
});
expect(title).toBe("custom title");
expect(sent).toMatchObject({
type: "generate",
modelKey: "lfm2-350m",
message: "Investigate routing",
systemPrompt: "Custom title prompt",
});
} finally {
await client.terminate();
}
});
});
describe("issue #1940 — local model failures release the worker process", () => {
it("recycles the tiny-model worker after model execution returns an error", async () => {
const first = new FakeTinyWorker((message, worker) => {
if (message.type === "complete") {
worker.emit({ type: "error", id: message.id, error: "Error: Unknown failure" });
}
});
const second = new FakeTinyWorker((message, worker) => {
if (message.type === "complete") {
worker.emit({ type: "completion", id: message.id, text: "recovered" });
}
});
const workers = [first, second];
let nextWorker = 0;
const client = new TinyTitleClient(() => {
const worker = workers[nextWorker];
if (!worker) throw new Error("unexpected worker spawn");
nextWorker += 1;
return worker;
});
try {
expect(await client.complete("qwen3-1.7b", "long prompt")).toBeNull();
expect(first.terminated).toBe(true);
expect(await client.complete("qwen3-1.7b", "retry prompt")).toBe("recovered");
expect(nextWorker).toBe(2);
} finally {
await client.terminate();
}
});
it("faults queued local completions when the failed worker is recycled", async () => {
let firstRequestId = "";
const worker = new FakeTinyWorker(message => {
if (message.type !== "complete") return;
firstRequestId ||= message.id;
});
const client = new TinyTitleClient(() => worker);
try {
const first = client.complete("qwen3-1.7b", "first prompt");
const second = client.complete("qwen3-1.7b", "second prompt");
worker.emit({ type: "error", id: firstRequestId, error: "Error: Unknown failure" });
expect(await first).toBeNull();
expect(await second).toBeNull();
expect(worker.terminated).toBe(true);
} finally {
await client.terminate();
}
});
});