feat(FN-4045): surface and handle delegate task collision errors

Added delegate collision detection and error surfacing across the task creation and delegation pipeline. The core store now throws a typed `DelegateCollisionError` when `createTask` encounters a duplicate task ID, the engine's `delegate_task` tool propagates these errors with context, and the CLI ex

Fusion-Task-Id: FN-4045
This commit is contained in:
Fusion
2026-05-11 22:58:23 -07:00
committed by gsxdsm
parent c6edf5f49f
commit 7b5ec3d063
7 changed files with 194 additions and 91 deletions

View File

@@ -256,6 +256,22 @@ describe("createDelegateTaskTool", () => {
expect(taskStore.createTask).not.toHaveBeenCalled();
});
it("returns explicit collision error when delegated createTask hits existing id", async () => {
const agent = createAgent({ id: "agent-001", name: "Bob" });
vi.mocked(agentStore.getAgent).mockResolvedValue(agent);
vi.mocked(taskStore.createTask).mockRejectedValue(new Error("Task ID already exists: FN-050"));
const tool = createDelegateTaskTool(agentStore, taskStore);
const result = await tool.execute("session-1", {
agent_id: "agent-001",
description: "Write tests",
}, undefined as any, undefined as any, undefined as any);
expect((result as { isError?: boolean }).isError).toBe(true);
expect((result.content[0] as { text: string }).text).toBe("ERROR: Task ID already exists: FN-050");
expect(result.details).toEqual({});
});
it("allows durable engineer target without override", async () => {
const engineer = createAgent({ id: "agent-009", name: "Eli", role: "engineer" });
vi.mocked(agentStore.getAgent).mockResolvedValue(engineer);

View File

@@ -623,24 +623,35 @@ export function createTaskCreateTool(
"or the current task should wait for the new one).",
parameters: taskCreateParams,
execute: async (_id: string, params: Static<typeof taskCreateParams>) => {
const task = await createAgentTask(store, {
description: params.description,
dependencies: params.dependencies,
column: "triage",
source: provenance ? {
sourceType: provenance.sourceType,
sourceAgentId: provenance.sourceAgentId,
sourceRunId: provenance.sourceRunId,
} : undefined,
}, options);
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
return {
content: [{
type: "text" as const,
text: `Created ${task.id}: ${params.description}${deps}`,
}],
details: { taskId: task.id },
};
try {
const task = await createAgentTask(store, {
description: params.description,
dependencies: params.dependencies,
column: "triage",
source: provenance ? {
sourceType: provenance.sourceType,
sourceAgentId: provenance.sourceAgentId,
sourceRunId: provenance.sourceRunId,
} : undefined,
}, options);
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
return {
content: [{
type: "text" as const,
text: `Created ${task.id}: ${params.description}${deps}`,
}],
details: { taskId: task.id },
};
} catch (err) {
if (err instanceof Error && err.message.startsWith("Task ID already exists:")) {
return {
content: [{ type: "text" as const, text: `ERROR: ${err.message}` }],
details: {},
isError: true,
};
}
throw err;
}
},
};
}
@@ -1761,27 +1772,38 @@ export function createDelegateTaskTool(
};
}
// Create task assigned to the target agent
const task = await createAgentTask(taskStore, {
description: params.description,
dependencies: params.dependencies,
column: "todo",
assignedAgentId: params.agent_id,
source: {
sourceType: "api",
...(override ? { sourceMetadata: { executorRoleOverride: true } } : {}),
},
}, options);
try {
// Create task assigned to the target agent
const task = await createAgentTask(taskStore, {
description: params.description,
dependencies: params.dependencies,
column: "todo",
assignedAgentId: params.agent_id,
source: {
sourceType: "api",
...(override ? { sourceMetadata: { executorRoleOverride: true } } : {}),
},
}, options);
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
return {
content: [{
type: "text" as const,
text: `Delegated to ${agent.name} (${agent.id}): Created ${task.id}${deps}. ` +
`The task will be picked up by ${agent.name} on their next heartbeat cycle.`,
}],
details: { taskId: task.id, agentId: agent.id, agentName: agent.name },
};
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
return {
content: [{
type: "text" as const,
text: `Delegated to ${agent.name} (${agent.id}): Created ${task.id}${deps}. ` +
`The task will be picked up by ${agent.name} on their next heartbeat cycle.`,
}],
details: { taskId: task.id, agentId: agent.id, agentName: agent.name },
};
} catch (err) {
if (err instanceof Error && err.message.startsWith("Task ID already exists:")) {
return {
content: [{ type: "text" as const, text: `ERROR: ${err.message}` }],
details: {},
isError: true,
};
}
throw err;
}
},
};
}