feat(KB-184): add model presets for task creation
- Add model preset types and settings fields with API validation - Create model preset utility helpers for size-based auto-selection - Add settings UI for managing model presets (create, edit, delete) - Integrate preset selection into NewTaskModal with custom override option - Add inline preset selection to InlineCreateCard component - Persist modelPresetId in task store and preserve through archive/unarchive - Add changeset and update AGENTS.md with feature documentation
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export { COLUMNS, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS, THINKING_LEVELS, THEME_MODES, COLOR_THEMES } from "./types.js";
|
||||
export type { Column, IssueInfo, IssueState, PrInfo, PrStatus, Task, TaskAttachment, TaskCreateInput, TaskDetail, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, MergeResult, Settings, TaskStep, StepStatus, TaskLogEntry, ActivityLogEntry, ActivityEventType, ThinkingLevel, SteeringComment, ThemeMode, ColorTheme, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult } from "./types.js";
|
||||
export type { Column, IssueInfo, IssueState, PrInfo, PrStatus, Task, TaskAttachment, TaskCreateInput, TaskDetail, AgentLogEntry, AgentLogType, AgentRole, BoardConfig, MergeResult, Settings, TaskStep, StepStatus, TaskLogEntry, ActivityLogEntry, ActivityEventType, ThinkingLevel, SteeringComment, ThemeMode, ColorTheme, PlanningQuestion, PlanningSummary, PlanningResponse, PlanningQuestionType, ArchivedTaskEntry, BatchStatusRequest, BatchStatusResponse, BatchStatusEntry, BatchStatusResult, ModelPreset } from "./types.js";
|
||||
export { TaskStore } from "./store.js";
|
||||
export { canTransition, getValidTransitions, resolveDependencyOrder } from "./board.js";
|
||||
export {
|
||||
|
||||
@@ -121,6 +121,18 @@ describe("TaskStore", () => {
|
||||
expect(detail.breakIntoSubtasks).toBe(true);
|
||||
});
|
||||
|
||||
it("persists modelPresetId when provided during task creation", async () => {
|
||||
const task = await store.createTask({
|
||||
description: "Preset task",
|
||||
modelPresetId: "budget",
|
||||
});
|
||||
|
||||
expect(task.modelPresetId).toBe("budget");
|
||||
|
||||
const detail = await store.getTask(task.id);
|
||||
expect(detail.modelPresetId).toBe("budget");
|
||||
});
|
||||
|
||||
it("leaves breakIntoSubtasks unset by default", async () => {
|
||||
const task = await store.createTask({
|
||||
description: "Regular task",
|
||||
|
||||
@@ -301,6 +301,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
column: input.column || "triage",
|
||||
dependencies: input.dependencies || [],
|
||||
breakIntoSubtasks: input.breakIntoSubtasks === true ? true : undefined,
|
||||
modelPresetId: input.modelPresetId,
|
||||
modelProvider: input.modelProvider,
|
||||
modelId: input.modelId,
|
||||
validatorModelProvider: input.validatorModelProvider,
|
||||
@@ -349,6 +350,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
title: sourceTask.title,
|
||||
description: `${sourceTask.description}\n\n(Duplicated from ${id})`,
|
||||
column: "triage",
|
||||
modelPresetId: sourceTask.modelPresetId,
|
||||
dependencies: [], // Fresh task should have no dependencies
|
||||
steps: [], // Reset execution state
|
||||
currentStep: 0,
|
||||
@@ -1053,6 +1055,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
updatedAt: task.updatedAt,
|
||||
columnMovedAt: task.columnMovedAt,
|
||||
archivedAt: task.columnMovedAt,
|
||||
modelPresetId: task.modelPresetId,
|
||||
modelProvider: task.modelProvider,
|
||||
modelId: task.modelId,
|
||||
validatorModelProvider: task.validatorModelProvider,
|
||||
@@ -1794,6 +1797,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
createdAt: entry.createdAt,
|
||||
updatedAt: new Date().toISOString(),
|
||||
columnMovedAt: entry.columnMovedAt,
|
||||
modelPresetId: entry.modelPresetId,
|
||||
modelProvider: entry.modelProvider,
|
||||
modelId: entry.modelId,
|
||||
validatorModelProvider: entry.validatorModelProvider,
|
||||
|
||||
@@ -29,6 +29,15 @@ export type ColorTheme = (typeof COLOR_THEMES)[number];
|
||||
export type PrStatus = "open" | "closed" | "merged";
|
||||
export type MergeStrategy = "direct" | "pull-request";
|
||||
|
||||
export interface ModelPreset {
|
||||
id: string;
|
||||
name: string;
|
||||
executorProvider?: string;
|
||||
executorModelId?: string;
|
||||
validatorProvider?: string;
|
||||
validatorModelId?: string;
|
||||
}
|
||||
|
||||
export interface PrInfo {
|
||||
url: string;
|
||||
number: number;
|
||||
@@ -166,6 +175,8 @@ export interface Task {
|
||||
log: TaskLogEntry[];
|
||||
size?: "S" | "M" | "L";
|
||||
reviewLevel?: number;
|
||||
/** Model preset selected during task creation. Presets resolve to concrete model overrides at creation time. */
|
||||
modelPresetId?: string;
|
||||
/** AI model provider override for the executor agent (e.g., "anthropic").
|
||||
* Must be set together with `modelId`. When both model fields are undefined,
|
||||
* the executor uses global settings defaults. */
|
||||
@@ -205,6 +216,8 @@ export interface TaskCreateInput {
|
||||
column?: Column;
|
||||
dependencies?: string[];
|
||||
breakIntoSubtasks?: boolean;
|
||||
/** Model preset selected during task creation. Presets resolve to concrete model overrides at creation time. */
|
||||
modelPresetId?: string;
|
||||
/** AI model provider override for the executor agent (e.g., "anthropic").
|
||||
* Must be set together with `modelId`. When both model fields are undefined,
|
||||
* the executor uses global settings defaults. */
|
||||
@@ -306,6 +319,12 @@ export interface Settings {
|
||||
* Must be set together with `validatorProvider`. When both are undefined,
|
||||
* falls back to `defaultProvider`/`defaultModelId`. */
|
||||
validatorModelId?: string;
|
||||
/** Reusable model configuration presets for task creation. */
|
||||
modelPresets?: ModelPreset[];
|
||||
/** When true, task creation UIs automatically recommend/apply a preset based on task size. */
|
||||
autoSelectModelPreset?: boolean;
|
||||
/** Mapping of task sizes to preset IDs used for auto-selection during task creation. */
|
||||
defaultPresetBySize?: { S?: string; M?: string; L?: string };
|
||||
/** Default thinking effort level for AI agent sessions.
|
||||
* Controls how much reasoning effort the model uses — higher levels
|
||||
* produce better results but cost more. When undefined, the engine
|
||||
@@ -362,6 +381,9 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
planningModelId: undefined,
|
||||
validatorProvider: undefined,
|
||||
validatorModelId: undefined,
|
||||
modelPresets: [],
|
||||
autoSelectModelPreset: false,
|
||||
defaultPresetBySize: {},
|
||||
defaultThinkingLevel: undefined,
|
||||
autoResolveConflicts: true,
|
||||
smartConflictResolution: true,
|
||||
@@ -447,7 +469,8 @@ export interface ArchivedTaskEntry {
|
||||
columnMovedAt?: string;
|
||||
/** Timestamp when the task was archived to the log */
|
||||
archivedAt: string;
|
||||
/** Optional: model override fields for executor and validator */
|
||||
/** Optional: model preset and override fields for executor and validator */
|
||||
modelPresetId?: string;
|
||||
modelProvider?: string;
|
||||
modelId?: string;
|
||||
validatorModelProvider?: string;
|
||||
|
||||
Reference in New Issue
Block a user