import { prompt } from "@oh-my-pi/pi-utils";
import btwUserPrompt from "../../prompts/system/btw-user.md" with { type: "text" };
import { BtwPanelComponent } from "../components/btw-panel";
import type { InteractiveModeContext } from "../types";
interface BtwRequest {
component: BtwPanelComponent;
abortController: AbortController;
question: string;
}
export class BtwController {
#activeRequest: BtwRequest | undefined;
constructor(private readonly ctx: InteractiveModeContext) {}
hasActiveRequest(): boolean {
return this.#activeRequest !== undefined;
}
handleEscape(): boolean {
if (!this.#activeRequest) return false;
this.#closeActiveRequest({ abort: this.#activeRequest.abortController.signal.aborted === false });
return true;
}
dispose(): void {
this.#closeActiveRequest({ abort: true });
}
async start(question: string): Promise<void> {
const trimmedQuestion = question.trim();
if (!trimmedQuestion) {
this.ctx.showStatus("Usage: /btw <question>");
return;
}
const model = this.ctx.session.model;
if (!model) {
this.ctx.showError("No active model available for /btw.");
return;
}
this.#closeActiveRequest({ abort: true });
const request: BtwRequest = {
component: new BtwPanelComponent({ question: trimmedQuestion, tui: this.ctx.ui }),
abortController: new AbortController(),
question: trimmedQuestion,
};
this.ctx.btwContainer.clear();
this.ctx.btwContainer.addChild(request.component);
this.ctx.ui.requestRender();
this.#activeRequest = request;
void this.#runRequest(request);
}
async #runRequest(request: BtwRequest): Promise<void> {
try {
const promptText = prompt.render(btwUserPrompt, { question: request.question });
const { replyText } = await this.ctx.session.runEphemeralTurn({
promptText,
onTextDelta: delta => {
if (this.#isActiveRequest(request)) {
request.component.appendText(delta);
}
},
signal: request.abortController.signal,
});
if (!this.#isActiveRequest(request)) {
return;
}
if (replyText) {
request.component.setAnswer(replyText);
}
request.component.markComplete();
} catch (error) {
if (!this.#isActiveRequest(request)) {
return;
}
if (request.abortController.signal.aborted) {
request.component.markAborted();
return;
}
request.component.markError(error instanceof Error ? error.message : String(error));
}
}
#closeActiveRequest(options: { abort: boolean }): void {
const request = this.#activeRequest;
if (!request) return;
this.#activeRequest = undefined;
if (options.abort) {
request.abortController.abort();
}
request.component.close();
this.ctx.btwContainer.clear();
this.ctx.ui.requestRender();
}
#isActiveRequest(request: BtwRequest): boolean {
return this.#activeRequest === request;
}
}