@@ -188,10 +188,10 @@ const PREFERRED_EXTRACTION_MODEL_ID = "nemotron-3-super-120b-a12b";
* Resolve the extraction model: prefer a lightweight model from the registry,
* fall back to the current session model.
*/
-function resolveExtractionModel(ctx: ExtensionContext) {
+function resolveExtractionModel(ctx: ExtensionContext, fallback: NonNullable<ExtensionContext["model"]>) {
const available = ctx.modelRegistry.getAvailable();
const preferred = available.find((m) => m.id === PREFERRED_EXTRACTION_MODEL_ID);
- return preferred ?? ctx.model!;
+ return preferred ?? fallback;
}
/**
@@ -272,9 +272,11 @@ class QnAComponent implements Component {
const editorTheme: EditorTheme = {
borderColor: this.dim,
selectList: {
- selectedBg: (s: string) => `\x1b[44m${s}\x1b[0m`,
- matchHighlight: this.cyan,
- itemSecondary: this.gray,
+ selectedPrefix: this.cyan,
+ selectedText: (s: string) => `\x1b[44m${s}\x1b[0m`,
+ description: this.gray,
+ scrollInfo: this.dim,
+ noMatch: this.dim,
},
};
@@ -296,11 +298,6 @@ class QnAComponent implements Component {
};
}
- private allQuestionsAnswered(): boolean {
- this.saveCurrentAnswer();
- return this.answers.every((a) => (a?.trim() || "").length > 0);
- }
-
private saveCurrentAnswer(): void {
this.answers[this.currentIndex] = this.editor.getText();
}
@@ -473,10 +470,10 @@ class QnAComponent implements Component {
};
// Title
- lines.push(padToWidth(this.dim("╭" + horizontalLine(boxWidth - 2) + "╮")));
+ lines.push(padToWidth(this.dim(`╭${horizontalLine(boxWidth - 2)}╮`)));
const title = `${this.bold(this.cyan("Questions"))} ${this.dim(`(${this.currentIndex + 1}/${this.questions.length})`)}`;
lines.push(padToWidth(boxLine(title)));
- lines.push(padToWidth(this.dim("├" + horizontalLine(boxWidth - 2) + "┤")));
+ lines.push(padToWidth(this.dim(`├${horizontalLine(boxWidth - 2)}┤`)));
// Progress indicator
const progressParts: string[] = [];
@@ -525,7 +522,7 @@ class QnAComponent implements Component {
lines.push(padToWidth(boxLine(answerPrefix + editorLines[i])));
} else {
// Subsequent lines get padding to align with the first line
- lines.push(padToWidth(boxLine(" " + editorLines[i])));
+ lines.push(padToWidth(boxLine(` ${editorLines[i]}`)));
}
}
@@ -537,7 +534,7 @@ class QnAComponent implements Component {
const notesEditorWidth = contentWidth - 4;
const notesEditorLines = this.notesEditor.render(notesEditorWidth);
for (let i = 1; i < notesEditorLines.length - 1; i++) {
- lines.push(padToWidth(boxLine(" " + notesEditorLines[i])));
+ lines.push(padToWidth(boxLine(` ${notesEditorLines[i]}`)));
}
} else if (this.notesText) {
lines.push(padToWidth(emptyBoxLine()));
@@ -556,15 +553,15 @@ class QnAComponent implements Component {
// Confirmation dialog or footer with controls
if (this.showingConfirmation) {
- lines.push(padToWidth(this.dim("├" + horizontalLine(boxWidth - 2) + "┤")));
+ lines.push(padToWidth(this.dim(`├${horizontalLine(boxWidth - 2)}┤`)));
const confirmMsg = `${this.yellow("Submit all answers?")} ${this.dim("(Enter/y to confirm, Esc/n to cancel)")}`;
lines.push(padToWidth(boxLine(truncateToWidth(confirmMsg, contentWidth))));
} else {
- lines.push(padToWidth(this.dim("├" + horizontalLine(boxWidth - 2) + "┤")));
+ lines.push(padToWidth(this.dim(`├${horizontalLine(boxWidth - 2)}┤`)));
const controls = `${this.dim("Tab/Enter")} next · ${this.dim("Shift+Tab")} prev · ${this.dim("Shift+Enter")} newline · ${this.dim("Alt+N")} note · ${this.dim("Esc")} cancel`;
lines.push(padToWidth(boxLine(truncateToWidth(controls, contentWidth))));
}
- lines.push(padToWidth(this.dim("╰" + horizontalLine(boxWidth - 2) + "╯")));
+ lines.push(padToWidth(this.dim(`╰${horizontalLine(boxWidth - 2)}╯`)));
this.cachedWidth = width;
this.cachedLines = lines;
@@ -613,6 +610,10 @@ export default function (pi: ExtensionAPI) {
return;
}
+ // Capture narrowed values so closures can use them without non-null assertions.
+ const sessionModel = ctx.model;
+ const assistantText = lastAssistantText;
+
// Run extraction with loader UI.
// The result distinguishes user cancellation from errors so we can
// show the right message after the custom UI closes.
@@ -622,7 +623,7 @@ export default function (pi: ExtensionAPI) {
| { kind: "error"; message: string };
const outcome = await ctx.ui.custom<ExtractionOutcome>((tui, theme, _kb, done) => {
- const extractionModel = resolveExtractionModel(ctx);
+ const extractionModel = resolveExtractionModel(ctx, sessionModel);
const loader = new BorderedLoader(tui, theme, `Extracting questions using ${extractionModel.name}...`);
loader.onAbort = () => done({ kind: "cancelled" });
@@ -640,7 +641,7 @@ export default function (pi: ExtensionAPI) {
content: [
{
type: "text",
- text: `<last_assistant_message>\n${lastAssistantText!}\n</last_assistant_message>\n\n${instructionBlock}`,
+ text: `<last_assistant_message>\n${assistantText}\n</last_assistant_message>\n\n${instructionBlock}`,
},
],
timestamp: Date.now(),
@@ -679,9 +680,9 @@ export default function (pi: ExtensionAPI) {
// If the preferred model errored and it's not already the
// session model, fall back to the session model.
- if (result.kind === "model_error" && extractionModel.id !== ctx.model!.id) {
- ctx.ui.notify(`${extractionModel.name} unavailable, falling back to ${ctx.model!.name}...`, "warning");
- result = await tryExtract(ctx.model!);
+ if (result.kind === "model_error" && extractionModel.id !== sessionModel.id) {
+ ctx.ui.notify(`${extractionModel.name} unavailable, falling back to ${sessionModel.name}...`, "warning");
+ result = await tryExtract(sessionModel);
}
switch (result.kind) {
@@ -739,7 +740,7 @@ export default function (pi: ExtensionAPI) {
pi.sendMessage(
{
customType: "answers",
- content: "I answered your questions in the following way:\n\n" + answersResult,
+ content: `I answered your questions in the following way:\n\n${answersResult}`,
display: true,
},
{ triggerTurn: true },