fix(FN-1572): stabilize fusion agent execution
This commit is contained in:
@@ -237,26 +237,19 @@ export function ModelSelectorTab({ task, addToast, onTaskUpdated, settings }: Mo
|
||||
setSavingTarget(target);
|
||||
|
||||
try {
|
||||
const updatedTask = await updateTask(requestTaskId, {
|
||||
modelProvider: target === "executor"
|
||||
? nextSelection.provider ?? null
|
||||
: previousSavedExecutor.provider ?? null,
|
||||
modelId: target === "executor"
|
||||
? nextSelection.modelId ?? null
|
||||
: previousSavedExecutor.modelId ?? null,
|
||||
validatorModelProvider: target === "validator"
|
||||
? nextSelection.provider ?? null
|
||||
: previousSavedValidator.provider ?? null,
|
||||
validatorModelId: target === "validator"
|
||||
? nextSelection.modelId ?? null
|
||||
: previousSavedValidator.modelId ?? null,
|
||||
planningModelProvider: target === "planning"
|
||||
? nextSelection.provider ?? null
|
||||
: previousSavedPlanning.provider ?? null,
|
||||
planningModelId: target === "planning"
|
||||
? nextSelection.modelId ?? null
|
||||
: previousSavedPlanning.modelId ?? null,
|
||||
});
|
||||
const updates: Parameters<typeof updateTask>[1] = {};
|
||||
if (target === "executor") {
|
||||
updates.modelProvider = nextSelection.provider ?? null;
|
||||
updates.modelId = nextSelection.modelId ?? null;
|
||||
} else if (target === "validator") {
|
||||
updates.validatorModelProvider = nextSelection.provider ?? null;
|
||||
updates.validatorModelId = nextSelection.modelId ?? null;
|
||||
} else {
|
||||
updates.planningModelProvider = nextSelection.provider ?? null;
|
||||
updates.planningModelId = nextSelection.modelId ?? null;
|
||||
}
|
||||
|
||||
const updatedTask = await updateTask(requestTaskId, updates);
|
||||
|
||||
if (activeTaskIdRef.current !== requestTaskId) {
|
||||
return;
|
||||
|
||||
@@ -193,6 +193,19 @@ function truncate(s: string, max: number): string {
|
||||
return s.length > max ? s.slice(0, max) + "…" : s;
|
||||
}
|
||||
|
||||
function sameStringArray(a: string[] = [], b: string[] = []): boolean {
|
||||
return a.length === b.length && a.every((value, index) => value === b[index]);
|
||||
}
|
||||
|
||||
function splitModelSelection(value: string): { provider: string; modelId: string } | null {
|
||||
const slashIdx = value.indexOf("/");
|
||||
if (!value || slashIdx === -1) return null;
|
||||
return {
|
||||
provider: value.slice(0, slashIdx),
|
||||
modelId: value.slice(slashIdx + 1),
|
||||
};
|
||||
}
|
||||
|
||||
const DESCRIPTION_TRUNCATE_LENGTH = 200;
|
||||
|
||||
const EDITABLE_COLUMNS: Set<Column> = new Set(["triage", "todo"]);
|
||||
@@ -529,26 +542,54 @@ export function TaskDetailModal({
|
||||
const handleSave = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
// Build update payload with all changed fields
|
||||
const executorSlashIdx = editExecutorModel.indexOf("/");
|
||||
const validatorSlashIdx = editValidatorModel.indexOf("/");
|
||||
const planningSlashIdx = editPlanningModel.indexOf("/");
|
||||
const updates: Parameters<typeof updateTask>[1] = {};
|
||||
const trimmedTitle = editTitle.trim();
|
||||
const trimmedDescription = editDescription.trim();
|
||||
|
||||
const updates: Parameters<typeof updateTask>[1] = {
|
||||
title: editTitle.trim() || undefined,
|
||||
description: editDescription.trim() || undefined,
|
||||
dependencies: editDependencies,
|
||||
enabledWorkflowSteps: editSelectedWorkflowSteps,
|
||||
modelProvider: editExecutorModel && executorSlashIdx !== -1 ? editExecutorModel.slice(0, executorSlashIdx) : null,
|
||||
modelId: editExecutorModel && executorSlashIdx !== -1 ? editExecutorModel.slice(executorSlashIdx + 1) : null,
|
||||
validatorModelProvider: editValidatorModel && validatorSlashIdx !== -1 ? editValidatorModel.slice(0, validatorSlashIdx) : null,
|
||||
validatorModelId: editValidatorModel && validatorSlashIdx !== -1 ? editValidatorModel.slice(validatorSlashIdx + 1) : null,
|
||||
planningModelProvider: editPlanningModel && planningSlashIdx !== -1 ? editPlanningModel.slice(0, planningSlashIdx) : null,
|
||||
planningModelId: editPlanningModel && planningSlashIdx !== -1 ? editPlanningModel.slice(planningSlashIdx + 1) : null,
|
||||
thinkingLevel: editThinkingLevel !== "" ? (editThinkingLevel as "minimal" | "low" | "medium" | "high") : null,
|
||||
};
|
||||
if (trimmedTitle && trimmedTitle !== (task.title ?? "")) {
|
||||
updates.title = trimmedTitle;
|
||||
}
|
||||
if (trimmedDescription && trimmedDescription !== (task.description ?? "")) {
|
||||
updates.description = trimmedDescription;
|
||||
}
|
||||
if (!sameStringArray(editDependencies, task.dependencies ?? [])) {
|
||||
updates.dependencies = editDependencies;
|
||||
}
|
||||
if (!sameStringArray(editSelectedWorkflowSteps, task.enabledWorkflowSteps ?? [])) {
|
||||
updates.enabledWorkflowSteps = editSelectedWorkflowSteps;
|
||||
}
|
||||
|
||||
await updateTask(task.id, updates, projectId);
|
||||
const executorSelection = splitModelSelection(editExecutorModel);
|
||||
const currentExecutorModel = task.modelProvider && task.modelId ? `${task.modelProvider}/${task.modelId}` : "";
|
||||
if (editExecutorModel !== currentExecutorModel) {
|
||||
updates.modelProvider = executorSelection?.provider ?? null;
|
||||
updates.modelId = executorSelection?.modelId ?? null;
|
||||
}
|
||||
|
||||
const validatorSelection = splitModelSelection(editValidatorModel);
|
||||
const currentValidatorModel = task.validatorModelProvider && task.validatorModelId ? `${task.validatorModelProvider}/${task.validatorModelId}` : "";
|
||||
if (editValidatorModel !== currentValidatorModel) {
|
||||
updates.validatorModelProvider = validatorSelection?.provider ?? null;
|
||||
updates.validatorModelId = validatorSelection?.modelId ?? null;
|
||||
}
|
||||
|
||||
const planningSelection = splitModelSelection(editPlanningModel);
|
||||
const currentPlanningModel = task.planningModelProvider && task.planningModelId ? `${task.planningModelProvider}/${task.planningModelId}` : "";
|
||||
if (editPlanningModel !== currentPlanningModel) {
|
||||
updates.planningModelProvider = planningSelection?.provider ?? null;
|
||||
updates.planningModelId = planningSelection?.modelId ?? null;
|
||||
}
|
||||
|
||||
const currentThinkingLevel = task.thinkingLevel ?? "";
|
||||
if (editThinkingLevel !== currentThinkingLevel) {
|
||||
updates.thinkingLevel = editThinkingLevel !== "" ? (editThinkingLevel as "minimal" | "low" | "medium" | "high") : null;
|
||||
}
|
||||
|
||||
const hasTaskUpdates = Object.keys(updates).length > 0;
|
||||
if (hasTaskUpdates) {
|
||||
const updatedTask = await updateTask(task.id, updates, projectId);
|
||||
onTaskUpdated?.(updatedTask);
|
||||
}
|
||||
|
||||
// Upload pending images as attachments
|
||||
if (editPendingImages.length > 0) {
|
||||
@@ -578,7 +619,7 @@ export function TaskDetailModal({
|
||||
setIsSaving(false);
|
||||
}
|
||||
}
|
||||
}, [task.id, editTitle, editDescription, editDependencies, editExecutorModel, editValidatorModel, editPlanningModel, editThinkingLevel, editSelectedWorkflowSteps, editPendingImages, addToast, projectId]);
|
||||
}, [task, editTitle, editDescription, editDependencies, editExecutorModel, editValidatorModel, editPlanningModel, editThinkingLevel, editSelectedWorkflowSteps, editPendingImages, addToast, projectId, onTaskUpdated]);
|
||||
|
||||
const handleAutoSaveDescription = useCallback(async (description: string) => {
|
||||
try {
|
||||
|
||||
@@ -4134,6 +4134,9 @@ describe("TaskDetailModal", () => {
|
||||
// Enter edit mode
|
||||
fireEvent.click(container.querySelector(".modal-edit-btn")!);
|
||||
|
||||
const titleInput = container.querySelector("#task-form-title") as HTMLInputElement;
|
||||
fireEvent.change(titleInput, { target: { value: "Changed title" } });
|
||||
|
||||
// Click Save
|
||||
fireEvent.click(screen.getByText("Save"));
|
||||
|
||||
@@ -4163,6 +4166,9 @@ describe("TaskDetailModal", () => {
|
||||
// Enter edit mode
|
||||
fireEvent.click(container.querySelector(".modal-edit-btn")!);
|
||||
|
||||
const titleInput = container.querySelector("#task-form-title") as HTMLInputElement;
|
||||
fireEvent.change(titleInput, { target: { value: "Changed title" } });
|
||||
|
||||
// Click Save
|
||||
fireEvent.click(screen.getByText("Save"));
|
||||
|
||||
@@ -4196,6 +4202,9 @@ describe("TaskDetailModal", () => {
|
||||
// Enter edit mode
|
||||
fireEvent.click(container.querySelector(".modal-edit-btn")!);
|
||||
|
||||
const titleInput = container.querySelector("#task-form-title") as HTMLInputElement;
|
||||
fireEvent.change(titleInput, { target: { value: "Changed title" } });
|
||||
|
||||
// Click Save
|
||||
fireEvent.click(screen.getByText("Save"));
|
||||
|
||||
@@ -4276,7 +4285,7 @@ describe("TaskDetailModal", () => {
|
||||
expect(screen.getByText(/Workflow Steps/i)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("save sends all changed fields via updateTask", async () => {
|
||||
it("save sends only changed fields via updateTask", async () => {
|
||||
const { updateTask } = await import("../../api");
|
||||
const mockUpdate = vi.mocked(updateTask);
|
||||
mockUpdate.mockResolvedValueOnce({ id: "FN-001" } as Task);
|
||||
@@ -4296,16 +4305,16 @@ describe("TaskDetailModal", () => {
|
||||
// Enter edit mode
|
||||
fireEvent.click(container.querySelector(".modal-edit-btn")!);
|
||||
|
||||
const descTextarea = container.querySelector("#task-form-description") as HTMLTextAreaElement;
|
||||
fireEvent.change(descTextarea, { target: { value: "Updated desc" } });
|
||||
|
||||
// Click Save
|
||||
fireEvent.click(screen.getByText("Save"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdate).toHaveBeenCalledWith("FN-001", expect.objectContaining({
|
||||
title: "Test",
|
||||
description: "Desc",
|
||||
dependencies: ["FN-002"],
|
||||
enabledWorkflowSteps: [],
|
||||
}), undefined);
|
||||
expect(mockUpdate).toHaveBeenCalledWith("FN-001", {
|
||||
description: "Updated desc",
|
||||
}, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4408,12 +4417,10 @@ describe("TaskDetailModal", () => {
|
||||
expect(mockUpdateTask).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
"FN-001",
|
||||
expect.objectContaining({
|
||||
modelProvider: "anthropic",
|
||||
modelId: "claude-sonnet-4-5",
|
||||
{
|
||||
validatorModelProvider: "openai",
|
||||
validatorModelId: "gpt-4o",
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -303,6 +303,27 @@ describe("ChatManager.sendMessage", () => {
|
||||
expect(assistantCall?.[1].content).toBe("Hello world!");
|
||||
});
|
||||
|
||||
it("creates chat agents with the full coding toolset", async () => {
|
||||
let createOptions: any;
|
||||
__setCreateKbAgent(async (options: any) => {
|
||||
createOptions = options;
|
||||
return {
|
||||
session: {
|
||||
prompt: vi.fn().mockResolvedValue(undefined),
|
||||
dispose: vi.fn(),
|
||||
state: {
|
||||
messages: [{ role: "assistant", content: "Done" }],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const chatManager = createChatManager();
|
||||
await chatManager.sendMessage("chat-001", "Hello");
|
||||
|
||||
expect(createOptions.tools).toBe("coding");
|
||||
});
|
||||
|
||||
it("accumulates thinking output separately from text", async () => {
|
||||
let onThinkingCb: ((delta: string) => void) | undefined;
|
||||
let onTextCb: ((delta: string) => void) | undefined;
|
||||
|
||||
@@ -537,7 +537,7 @@ export class ChatManager {
|
||||
agentResult = await createKbAgent({
|
||||
cwd: this.rootDir,
|
||||
systemPrompt,
|
||||
tools: "readonly",
|
||||
tools: "coding",
|
||||
...(effectiveModelProvider && effectiveModelId
|
||||
? {
|
||||
defaultProvider: effectiveModelProvider,
|
||||
|
||||
@@ -2296,23 +2296,30 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: ["FN-002"],
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
expect(res.body.dependencies).toEqual(["FN-002"]);
|
||||
});
|
||||
|
||||
it("does not clear model or assignee fields when they are omitted", async () => {
|
||||
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({ ...FAKE_TASK_DETAIL, title: "New" });
|
||||
|
||||
const res = await REQUEST(buildApp(), "PATCH", "/api/tasks/KB-001", JSON.stringify({ title: "New" }), {
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { title: "New" });
|
||||
expect(store.updateTask).not.toHaveBeenCalledWith(
|
||||
"KB-001",
|
||||
expect.objectContaining({
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
assigneeUserId: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards title and description without dependencies", async () => {
|
||||
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({ ...FAKE_TASK_DETAIL, title: "New" });
|
||||
|
||||
@@ -2323,18 +2330,6 @@ describe("PATCH /tasks/:id", () => {
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: "New",
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2358,19 +2353,10 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: "anthropic",
|
||||
modelId: "claude-sonnet-4-5",
|
||||
validatorModelProvider: "openai",
|
||||
validatorModelId: "gpt-4o",
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2388,18 +2374,6 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: "requesting-user",
|
||||
});
|
||||
});
|
||||
@@ -2442,19 +2416,8 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2476,19 +2439,8 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: "google",
|
||||
planningModelId: "gemini-2.5-pro",
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2530,19 +2482,8 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2560,19 +2501,7 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: ["browser-verification"],
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: undefined,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2601,19 +2530,7 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: "high",
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2632,19 +2549,7 @@ describe("PATCH /tasks/:id", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("KB-001", {
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
prompt: undefined,
|
||||
dependencies: undefined,
|
||||
enabledWorkflowSteps: undefined,
|
||||
modelProvider: null,
|
||||
modelId: null,
|
||||
validatorModelProvider: null,
|
||||
validatorModelId: null,
|
||||
planningModelProvider: null,
|
||||
planningModelId: null,
|
||||
thinkingLevel: null,
|
||||
assigneeUserId: null,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4618,10 +4618,12 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
try {
|
||||
const { store: scopedStore } = await getProjectContext(req);
|
||||
const { title, description, prompt, dependencies, enabledWorkflowSteps, modelProvider, modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, thinkingLevel, assigneeUserId } = req.body;
|
||||
const hasBodyField = (field: string) => Object.prototype.hasOwnProperty.call(req.body, field);
|
||||
|
||||
// Validate model fields are strings or undefined/null
|
||||
const validateModelField = (value: unknown, name: string): string | null | undefined => {
|
||||
if (value === undefined || value === null) return null;
|
||||
if (value === undefined) return undefined;
|
||||
if (value === null) return null;
|
||||
if (typeof value !== "string") {
|
||||
throw new Error(`${name} must be a string`);
|
||||
}
|
||||
@@ -4648,21 +4650,22 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
}
|
||||
}
|
||||
|
||||
const task = await scopedStore.updateTask(req.params.id, {
|
||||
title,
|
||||
description,
|
||||
prompt,
|
||||
dependencies,
|
||||
enabledWorkflowSteps,
|
||||
modelProvider: validatedModelProvider,
|
||||
modelId: validatedModelId,
|
||||
validatorModelProvider: validatedValidatorModelProvider,
|
||||
validatorModelId: validatedValidatorModelId,
|
||||
planningModelProvider: validatedPlanningModelProvider,
|
||||
planningModelId: validatedPlanningModelId,
|
||||
thinkingLevel: thinkingLevel === null ? null : thinkingLevel,
|
||||
assigneeUserId: validatedAssigneeUserId,
|
||||
});
|
||||
const updates: Parameters<typeof scopedStore.updateTask>[1] = {};
|
||||
if (title !== undefined) updates.title = title;
|
||||
if (description !== undefined) updates.description = description;
|
||||
if (prompt !== undefined) updates.prompt = prompt;
|
||||
if (dependencies !== undefined) updates.dependencies = dependencies;
|
||||
if (enabledWorkflowSteps !== undefined) updates.enabledWorkflowSteps = enabledWorkflowSteps;
|
||||
if (hasBodyField("modelProvider")) updates.modelProvider = validatedModelProvider;
|
||||
if (hasBodyField("modelId")) updates.modelId = validatedModelId;
|
||||
if (hasBodyField("validatorModelProvider")) updates.validatorModelProvider = validatedValidatorModelProvider;
|
||||
if (hasBodyField("validatorModelId")) updates.validatorModelId = validatedValidatorModelId;
|
||||
if (hasBodyField("planningModelProvider")) updates.planningModelProvider = validatedPlanningModelProvider;
|
||||
if (hasBodyField("planningModelId")) updates.planningModelId = validatedPlanningModelId;
|
||||
if (hasBodyField("thinkingLevel")) updates.thinkingLevel = thinkingLevel === null ? null : thinkingLevel;
|
||||
if (hasBodyField("assigneeUserId")) updates.assigneeUserId = validatedAssigneeUserId;
|
||||
|
||||
const task = await scopedStore.updateTask(req.params.id, updates);
|
||||
res.json(task);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof ApiError) {
|
||||
|
||||
Reference in New Issue
Block a user