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:
@@ -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(
|
||||
|
||||
@@ -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" },
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user