feat: add answer extraction replay commands

This commit is contained in:
Erik Kotsiuba
2026-05-02 22:47:55 +02:00
parent 288caf49de
commit 5b70eca306
36 changed files with 1636 additions and 98 deletions
+69 -4
View File
@@ -1,7 +1,20 @@
import { DEFAULT_ASK_KEYMAPS } from "../constants/keymaps.ts";
import type { AskConfig, AskConfigFileV1 } from "./schema.ts";
import type { AskConfig, AskConfigFileV2 } from "./schema.ts";
const DEFAULT_EXTRACTION_RETRIES = 1;
const MAX_EXTRACTION_RETRIES = 3;
const DEFAULT_EXTRACTION_TIMEOUT_MS = 30_000;
export const DEFAULT_ASK_CONFIG: AskConfig = {
answer: {
extractionModels: [
{ provider: "openai-codex", id: "gpt-5.4-mini" },
{ provider: "github-copilot", id: "gpt-5.4-mini" },
{ provider: "anthropic", id: "claude-haiku-4-5" },
],
extractionRetries: DEFAULT_EXTRACTION_RETRIES,
extractionTimeoutMs: DEFAULT_EXTRACTION_TIMEOUT_MS,
},
behaviour: {
autoSubmitWhenAnsweredWithoutNotes: false,
confirmDismissWhenDirty: true,
@@ -14,9 +27,24 @@ export const DEFAULT_ASK_CONFIG: AskConfig = {
};
export function normalizeAskConfig(
config?: Partial<AskConfigFileV1> | AskConfig
config?: Partial<AskConfigFileV2> | AskConfig
): AskConfig {
return {
answer: {
extractionModels:
config?.answer?.extractionModels?.filter(isValidModelPreference) ??
DEFAULT_ASK_CONFIG.answer.extractionModels,
extractionRetries: clampInteger(
config?.answer?.extractionRetries,
0,
MAX_EXTRACTION_RETRIES,
DEFAULT_EXTRACTION_RETRIES
),
extractionTimeoutMs: positiveNumberOrDefault(
config?.answer?.extractionTimeoutMs,
DEFAULT_EXTRACTION_TIMEOUT_MS
),
},
behaviour: {
autoSubmitWhenAnsweredWithoutNotes:
config?.behaviour?.autoSubmitWhenAnsweredWithoutNotes ??
@@ -38,10 +66,15 @@ export function normalizeAskConfig(
};
}
export function toAskConfigFileV1(config: AskConfig): AskConfigFileV1 {
export function toAskConfigFileV2(config: AskConfig): AskConfigFileV2 {
const normalized = normalizeAskConfig(config);
return {
schemaVersion: 1,
schemaVersion: 2,
answer: {
extractionModels: normalized.answer.extractionModels,
extractionRetries: normalized.answer.extractionRetries,
extractionTimeoutMs: normalized.answer.extractionTimeoutMs,
},
behaviour: {
autoSubmitWhenAnsweredWithoutNotes:
normalized.behaviour.autoSubmitWhenAnsweredWithoutNotes,
@@ -60,3 +93,35 @@ export function toAskConfigFileV1(config: AskConfig): AskConfigFileV1 {
},
};
}
function isValidModelPreference(value: unknown): value is {
id: string;
provider: string;
} {
return (
!!value &&
typeof value === "object" &&
typeof (value as { id?: unknown }).id === "string" &&
(value as { id: string }).id.trim().length > 0 &&
typeof (value as { provider?: unknown }).provider === "string" &&
(value as { provider: string }).provider.trim().length > 0
);
}
function clampInteger(
value: unknown,
min: number,
max: number,
fallback: number
): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
return fallback;
}
return Math.max(min, Math.min(max, Math.trunc(value)));
}
function positiveNumberOrDefault(value: unknown, fallback: number): number {
return typeof value === "number" && Number.isFinite(value) && value > 0
? value
: fallback;
}