feat(FN-1044): add thinkingLevel and planning model support to task creation
- Add thinkingLevel field to createTask and updateTask in core store - Accept thinkingLevel and planningModel fields in dashboard task routes - Send thinkingLevel and planningModel in frontend API calls from task forms - Add ThinkingLevel selector to ModelSelectorTab component with tests - Wire up planning model and thinking level in QuickEntryBox, TaskForm, and NewTaskModal - Add unit tests for store, routes, and ModelSelectorTab coverage - Add changeset and update AGENTS.md documentation
This commit is contained in:
@@ -1330,6 +1330,44 @@ describe("TaskStore", () => {
|
||||
expect(updated.missionId).toBe("M-789");
|
||||
expect(updated.sliceId).toBe("SL-789");
|
||||
});
|
||||
|
||||
it("sets thinkingLevel via createTask and updateTask", async () => {
|
||||
const created = await store.createTask({
|
||||
description: "Task with thinking level",
|
||||
thinkingLevel: "high",
|
||||
});
|
||||
expect(created.thinkingLevel).toBe("high");
|
||||
|
||||
const persisted = await store.getTask(created.id);
|
||||
expect(persisted.thinkingLevel).toBe("high");
|
||||
|
||||
const updated = await store.updateTask(created.id, { thinkingLevel: "low" });
|
||||
expect(updated.thinkingLevel).toBe("low");
|
||||
|
||||
const reloaded = await store.getTask(created.id);
|
||||
expect(reloaded.thinkingLevel).toBe("low");
|
||||
});
|
||||
|
||||
it("clears thinkingLevel via null in updateTask", async () => {
|
||||
const task = await store.createTask({
|
||||
description: "Task with thinking level",
|
||||
thinkingLevel: "medium",
|
||||
});
|
||||
expect(task.thinkingLevel).toBe("medium");
|
||||
|
||||
const updated = await store.updateTask(task.id, { thinkingLevel: null });
|
||||
expect(updated.thinkingLevel).toBeUndefined();
|
||||
});
|
||||
|
||||
it("preserves thinkingLevel when updating unrelated fields", async () => {
|
||||
const task = await store.createTask({
|
||||
description: "Task with thinking level",
|
||||
thinkingLevel: "high",
|
||||
});
|
||||
const updated = await store.updateTask(task.id, { title: "Updated title" });
|
||||
expect(updated.thinkingLevel).toBe("high");
|
||||
expect(updated.title).toBe("Updated title");
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateTask — PROMPT.md regeneration", () => {
|
||||
|
||||
@@ -836,6 +836,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
validatorModelId: input.validatorModelId,
|
||||
planningModelProvider: input.planningModelProvider,
|
||||
planningModelId: input.planningModelId,
|
||||
thinkingLevel: input.thinkingLevel,
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [{ timestamp: now, action: "Task created" }],
|
||||
@@ -1123,7 +1124,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
|
||||
async updateTask(
|
||||
id: string,
|
||||
updates: { title?: string; description?: string; prompt?: string; worktree?: string | null; status?: string | null; dependencies?: string[]; blockedBy?: string | null; paused?: boolean; baseBranch?: string | null; branch?: string | null; baseCommitSha?: string | null; size?: "S" | "M" | "L"; reviewLevel?: number; mergeRetries?: number; stuckKillCount?: number | null; recoveryRetryCount?: number | null; nextRecoveryAt?: string | null; enabledWorkflowSteps?: string[]; modelProvider?: string | null; modelId?: string | null; validatorModelProvider?: string | null; validatorModelId?: string | null; planningModelProvider?: string | null; planningModelId?: string | null; error?: string | null; summary?: string | null; sessionFile?: string | null; workflowStepResults?: import("./types.js").WorkflowStepResult[] | null; mergeDetails?: import("./types.js").MergeDetails | null; modifiedFiles?: string[] | null; missionId?: string | null; sliceId?: string | null },
|
||||
updates: { title?: string; description?: string; prompt?: string; worktree?: string | null; status?: string | null; dependencies?: string[]; blockedBy?: string | null; paused?: boolean; baseBranch?: string | null; branch?: string | null; baseCommitSha?: string | null; size?: "S" | "M" | "L"; reviewLevel?: number; mergeRetries?: number; stuckKillCount?: number | null; recoveryRetryCount?: number | null; nextRecoveryAt?: string | null; enabledWorkflowSteps?: string[]; modelProvider?: string | null; modelId?: string | null; validatorModelProvider?: string | null; validatorModelId?: string | null; planningModelProvider?: string | null; planningModelId?: string | null; thinkingLevel?: string | null; error?: string | null; summary?: string | null; sessionFile?: string | null; workflowStepResults?: import("./types.js").WorkflowStepResult[] | null; mergeDetails?: import("./types.js").MergeDetails | null; modifiedFiles?: string[] | null; missionId?: string | null; sliceId?: string | null },
|
||||
): Promise<Task> {
|
||||
return this.withTaskLock(id, async () => {
|
||||
// Validate that task doesn't depend on itself
|
||||
@@ -1241,6 +1242,11 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
} else if (updates.planningModelId !== undefined) {
|
||||
task.planningModelId = updates.planningModelId;
|
||||
}
|
||||
if (updates.thinkingLevel === null) {
|
||||
task.thinkingLevel = undefined;
|
||||
} else if (updates.thinkingLevel !== undefined) {
|
||||
task.thinkingLevel = updates.thinkingLevel as import("./types.js").ThinkingLevel;
|
||||
}
|
||||
if (updates.error === null) {
|
||||
task.error = undefined;
|
||||
} else if (updates.error !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user