feat(FN-1273): support agent assignment in extension task tools

- Add optional agentId parameter to kb_task_create and persist it as assignedAgentId
- Extend kb_task_create output/details to include assignee information when present
- Add kb_task_update support for setting or clearing assignedAgentId via agentId (including null)
- Expand extension tests for create/update/clear assignment flows and add a patch changeset for @gsxdsm/fusion
This commit is contained in:
gsxdsm
2026-04-08 09:57:45 -07:00
parent 431ea68b0a
commit cb90e33853
3 changed files with 120 additions and 4 deletions

View File

@@ -169,6 +169,41 @@ describe("kb pi extension", () => {
expect(result.details.dependencies).toEqual(["FN-001"]);
expect(result.content[0].text).toContain("Dependencies: FN-001");
});
it("creates a task with assigned agent ID", async () => {
const tool = api.tools.get("kb_task_create")!;
const result = await tool.execute(
"call-1",
{ description: "Task with assignee", agentId: "agent-abc123" },
undefined,
undefined,
makeCtx(tmpDir),
);
expect(result.details.taskId).toBe("FN-001");
expect(result.details.assignedAgentId).toBe("agent-abc123");
expect(result.content[0].text).toContain("Assigned to: agent-abc123");
// Verify persistence via show
const showTool = api.tools.get("kb_task_show")!;
const show = await showTool.execute("s1", { id: "FN-001" }, undefined, undefined, makeCtx(tmpDir));
expect(show.details.task.assignedAgentId).toBe("agent-abc123");
});
it("creates a task without assigned agent ID by default", async () => {
const tool = api.tools.get("kb_task_create")!;
const result = await tool.execute(
"call-1",
{ description: "Task without assignee" },
undefined,
undefined,
makeCtx(tmpDir),
);
expect(result.details.taskId).toBe("FN-001");
expect(result.details.assignedAgentId).toBeUndefined();
expect(result.content[0].text).not.toContain("Assigned to:");
});
});
describe("kb_task_update", () => {
@@ -246,6 +281,56 @@ describe("kb pi extension", () => {
expect(result.details.updatedFields).toEqual(["title", "description", "dependencies"]);
});
it("updates task assigned agent ID", async () => {
const createTool = api.tools.get("kb_task_create")!;
await createTool.execute("c1", { description: "Original" }, undefined, undefined, makeCtx(tmpDir));
const updateTool = api.tools.get("kb_task_update")!;
const result = await updateTool.execute(
"u1",
{ id: "FN-001", agentId: "agent-abc123" },
undefined,
undefined,
makeCtx(tmpDir),
);
expect(result.content[0].text).toContain("Updated FN-001");
expect(result.content[0].text).toContain("agentId");
expect(result.details.updatedFields).toEqual(["agentId"]);
const showTool = api.tools.get("kb_task_show")!;
const show = await showTool.execute("s1", { id: "FN-001" }, undefined, undefined, makeCtx(tmpDir));
expect(show.details.task.assignedAgentId).toBe("agent-abc123");
});
it("clears task assigned agent ID with null", async () => {
const createTool = api.tools.get("kb_task_create")!;
await createTool.execute(
"c1",
{ description: "Original", agentId: "agent-abc123" },
undefined,
undefined,
makeCtx(tmpDir),
);
const updateTool = api.tools.get("kb_task_update")!;
const result = await updateTool.execute(
"u1",
{ id: "FN-001", agentId: null },
undefined,
undefined,
makeCtx(tmpDir),
);
expect(result.content[0].text).toContain("Updated FN-001");
expect(result.content[0].text).toContain("agentId");
expect(result.details.updatedFields).toEqual(["agentId"]);
const showTool = api.tools.get("kb_task_show")!;
const show = await showTool.execute("s1", { id: "FN-001" }, undefined, undefined, makeCtx(tmpDir));
expect(show.details.task.assignedAgentId).toBeUndefined();
});
it("returns error when task not found", async () => {
const updateTool = api.tools.get("kb_task_update")!;
const result = await updateTool.execute(

View File

@@ -75,6 +75,11 @@ export default function kbExtension(pi: ExtensionAPI) {
description: "Task IDs this depends on (e.g. ['KB-001', 'KB-002'])",
}),
),
agentId: Type.Optional(
Type.String({
description: "Agent ID to assign this task to (e.g. 'agent-abc123')",
}),
),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -82,6 +87,7 @@ export default function kbExtension(pi: ExtensionAPI) {
const task = await store.createTask({
description: params.description.trim(),
dependencies: params.depends,
assignedAgentId: params.agentId,
});
const label =
@@ -99,10 +105,18 @@ export default function kbExtension(pi: ExtensionAPI) {
(task.dependencies.length
? `Dependencies: ${task.dependencies.join(", ")}\n`
: "") +
(task.assignedAgentId
? `Assigned to: ${task.assignedAgentId}\n`
: "") +
`Path: .fusion/tasks/${task.id}/`,
},
],
details: { taskId: task.id, column: task.column, dependencies: task.dependencies },
details: {
taskId: task.id,
column: task.column,
dependencies: task.dependencies,
assignedAgentId: task.assignedAgentId,
},
};
},
});
@@ -114,10 +128,10 @@ export default function kbExtension(pi: ExtensionAPI) {
label: "KB: Update Task",
description:
"Update fields on an existing task. Supports modifying the title, " +
"description, and dependencies after task creation.",
"description, dependencies, and assigned agent after task creation.",
promptSnippet: "Update fields on an existing Fusion task",
promptGuidelines: [
"Use kb_task_update to modify task title, description, or dependencies after creation.",
"Use kb_task_update to modify task title, description, dependencies, or assigned agent after creation.",
"At least one field must be provided to update.",
],
parameters: Type.Object({
@@ -129,6 +143,14 @@ export default function kbExtension(pi: ExtensionAPI) {
description: "New dependency list — replaces existing dependencies (e.g. ['KB-001', 'KB-002'])",
}),
),
agentId: Type.Optional(
Type.Union([
Type.String(),
Type.Null(),
], {
description: "Agent ID to assign this task to, or null to clear (e.g. 'agent-abc123')",
}),
),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -162,10 +184,14 @@ export default function kbExtension(pi: ExtensionAPI) {
updates.dependencies = params.depends;
updatedFields.push("dependencies");
}
if (params.agentId !== undefined) {
updates.assignedAgentId = params.agentId;
updatedFields.push("agentId");
}
if (updatedFields.length === 0) {
return {
content: [{ type: "text", text: "No fields to update. Provide at least one of: title, description, depends." }],
content: [{ type: "text", text: "No fields to update. Provide at least one of: title, description, depends, agentId." }],
isError: true,
details: { error: "No fields provided" },
};