feat(FN-3060): add agent task auto-summarization and droid CLI path reconci
This merge adds droid CLI path reconciliation as a new pi extension, wires agent task auto-summarization for the agent tools layer, makes the UsageIndicator component resizable with improved styling, and updates related tests and documentation. The core changes include a new `reconcile-droid-cli-pat Fusion-Task-Id: FN-3060
This commit is contained in:
@@ -210,7 +210,7 @@ Defaults from `DEFAULT_PROJECT_SETTINGS`; key scope from `PROJECT_SETTINGS_KEYS`
|
|||||||
| `autoBackupSchedule` | `string` | `"0 2 * * *"` | Backup cron schedule. |
|
| `autoBackupSchedule` | `string` | `"0 2 * * *"` | Backup cron schedule. |
|
||||||
| `autoBackupRetention` | `number` | `7` | Number of backups to retain. |
|
| `autoBackupRetention` | `number` | `7` | Number of backups to retain. |
|
||||||
| `autoBackupDir` | `string` | `".fusion/backups"` | Relative backup directory path. |
|
| `autoBackupDir` | `string` | `".fusion/backups"` | Relative backup directory path. |
|
||||||
| `autoSummarizeTitles` | `boolean` | `false` | Auto-generate titles for long untitled descriptions. |
|
| `autoSummarizeTitles` | `boolean` | `false` | Auto-generate titles for long untitled descriptions across dashboard/API task creation and agent/tool-created tasks. |
|
||||||
| `useAiMergeCommitSummary` | `boolean` | `false` | Use AI-generated merge commit summaries instead of raw step-commit subject lists. |
|
| `useAiMergeCommitSummary` | `boolean` | `false` | Use AI-generated merge commit summaries instead of raw step-commit subject lists. |
|
||||||
| `titleSummarizerProvider` | `string` | `undefined` | Provider for title summarization. |
|
| `titleSummarizerProvider` | `string` | `undefined` | Provider for title summarization. |
|
||||||
| `titleSummarizerModelId` | `string` | `undefined` | Model ID for title summarization. |
|
| `titleSummarizerModelId` | `string` | `undefined` | Model ID for title summarization. |
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ Users can apply presets at task creation; manual model selection can override th
|
|||||||
|
|
||||||
## AI Title Summarization
|
## AI Title Summarization
|
||||||
|
|
||||||
When `autoSummarizeTitles` is enabled and a task has a long untitled description, Fusion can auto-generate a concise title.
|
When `autoSummarizeTitles` is enabled and a task has a long untitled description, Fusion can auto-generate a concise title. This applies to tasks created from the dashboard/API as well as tasks created by agents and tooling flows (`fn_task_create`, delegated tasks, and triage-created child tasks).
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
|||||||
@@ -3350,7 +3350,7 @@ describe("HeartbeatMonitor", () => {
|
|||||||
sourceAgentId: "agent-001",
|
sourceAgentId: "agent-001",
|
||||||
sourceRunId: undefined,
|
sourceRunId: undefined,
|
||||||
},
|
},
|
||||||
});
|
}, expect.objectContaining({ settings: { autoSummarizeTitles: false } }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3870,7 +3870,7 @@ describe("HeartbeatMonitor", () => {
|
|||||||
sourceAgentId: "agent-001",
|
sourceAgentId: "agent-001",
|
||||||
sourceRunId: undefined,
|
sourceRunId: undefined,
|
||||||
},
|
},
|
||||||
});
|
}, expect.objectContaining({ settings: { autoSummarizeTitles: false } }));
|
||||||
|
|
||||||
const responseText = result.content[0] && "text" in result.content[0] ? result.content[0].text : "";
|
const responseText = result.content[0] && "text" in result.content[0] ? result.content[0].text : "";
|
||||||
expect(responseText).toContain("Created FN-100");
|
expect(responseText).toContain("Created FN-100");
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ function createMockAgentStore(overrides: Partial<AgentStore> = {}): AgentStore {
|
|||||||
|
|
||||||
function createMockTaskStore(overrides: Partial<TaskStore> = {}): TaskStore {
|
function createMockTaskStore(overrides: Partial<TaskStore> = {}): TaskStore {
|
||||||
return {
|
return {
|
||||||
|
getSettings: vi.fn().mockResolvedValue({ autoSummarizeTitles: false }),
|
||||||
createTask: vi.fn().mockResolvedValue({
|
createTask: vi.fn().mockResolvedValue({
|
||||||
id: "FN-001",
|
id: "FN-001",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -188,7 +189,7 @@ describe("createDelegateTaskTool", () => {
|
|||||||
column: "todo",
|
column: "todo",
|
||||||
assignedAgentId: "agent-001",
|
assignedAgentId: "agent-001",
|
||||||
source: { sourceType: "api" },
|
source: { sourceType: "api" },
|
||||||
});
|
}, expect.objectContaining({ settings: { autoSummarizeTitles: false } }));
|
||||||
|
|
||||||
const text = (result.content[0] as { text: string }).text;
|
const text = (result.content[0] as { text: string }).text;
|
||||||
expect(text).toContain("Delegated to Bob (agent-001)");
|
expect(text).toContain("Delegated to Bob (agent-001)");
|
||||||
@@ -283,7 +284,7 @@ describe("createDelegateTaskTool", () => {
|
|||||||
column: "todo",
|
column: "todo",
|
||||||
assignedAgentId: "agent-001",
|
assignedAgentId: "agent-001",
|
||||||
source: { sourceType: "api" },
|
source: { sourceType: "api" },
|
||||||
});
|
}, expect.objectContaining({ settings: { autoSummarizeTitles: false } }));
|
||||||
|
|
||||||
const text = (result.content[0] as { text: string }).text;
|
const text = (result.content[0] as { text: string }).text;
|
||||||
expect(text).toContain("depends on: FN-010");
|
expect(text).toContain("depends on: FN-010");
|
||||||
@@ -312,6 +313,7 @@ describe("createDelegateTaskTool", () => {
|
|||||||
|
|
||||||
expect(taskStore.createTask).toHaveBeenCalledWith(
|
expect(taskStore.createTask).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ dependencies: undefined }),
|
expect.objectContaining({ dependencies: undefined }),
|
||||||
|
expect.objectContaining({ settings: { autoSummarizeTitles: false } }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const text = (result.content[0] as { text: string }).text;
|
const text = (result.content[0] as { text: string }).text;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
sendMessageParams,
|
sendMessageParams,
|
||||||
readMessagesParams,
|
readMessagesParams,
|
||||||
} from "../agent-tools.js";
|
} from "../agent-tools.js";
|
||||||
|
import * as core from "@fusion/core";
|
||||||
import type { MessageStore, Message } from "@fusion/core";
|
import type { MessageStore, Message } from "@fusion/core";
|
||||||
|
|
||||||
const loggerSpies = vi.hoisted(() => ({
|
const loggerSpies = vi.hoisted(() => ({
|
||||||
@@ -62,6 +63,7 @@ vi.mock("node:child_process", async () => {
|
|||||||
describe("createTaskCreateTool", () => {
|
describe("createTaskCreateTool", () => {
|
||||||
it("returns details.taskId and keeps Created <id> response text", async () => {
|
it("returns details.taskId and keeps Created <id> response text", async () => {
|
||||||
const store = {
|
const store = {
|
||||||
|
getSettings: vi.fn().mockResolvedValue({ autoSummarizeTitles: false }),
|
||||||
createTask: vi.fn().mockResolvedValue({
|
createTask: vi.fn().mockResolvedValue({
|
||||||
id: "PROJ-042",
|
id: "PROJ-042",
|
||||||
description: "Follow-up task",
|
description: "Follow-up task",
|
||||||
@@ -87,6 +89,9 @@ describe("createTaskCreateTool", () => {
|
|||||||
dependencies: ["PROJ-001"],
|
dependencies: ["PROJ-001"],
|
||||||
column: "triage",
|
column: "triage",
|
||||||
source: undefined,
|
source: undefined,
|
||||||
|
}, {
|
||||||
|
settings: { autoSummarizeTitles: false },
|
||||||
|
onSummarize: undefined,
|
||||||
});
|
});
|
||||||
expect(result.details).toEqual({ taskId: "PROJ-042" });
|
expect(result.details).toEqual({ taskId: "PROJ-042" });
|
||||||
const responseText = result.content[0]?.type === "text" ? result.content[0].text : "";
|
const responseText = result.content[0]?.type === "text" ? result.content[0].text : "";
|
||||||
@@ -96,6 +101,7 @@ describe("createTaskCreateTool", () => {
|
|||||||
|
|
||||||
it("passes explicit provenance to store.createTask", async () => {
|
it("passes explicit provenance to store.createTask", async () => {
|
||||||
const store = {
|
const store = {
|
||||||
|
getSettings: vi.fn().mockResolvedValue({ autoSummarizeTitles: false }),
|
||||||
createTask: vi.fn().mockResolvedValue({ id: "PROJ-099", description: "Test", dependencies: [], column: "triage" }),
|
createTask: vi.fn().mockResolvedValue({ id: "PROJ-099", description: "Test", dependencies: [], column: "triage" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,7 +114,7 @@ describe("createTaskCreateTool", () => {
|
|||||||
|
|
||||||
expect(store.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
expect(store.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
source: { sourceType: "agent_heartbeat", sourceAgentId: "agent-123", sourceRunId: undefined },
|
source: { sourceType: "agent_heartbeat", sourceAgentId: "agent-123", sourceRunId: undefined },
|
||||||
}));
|
}), expect.objectContaining({ settings: { autoSummarizeTitles: false } }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,6 +124,7 @@ describe("createDelegateTaskTool", () => {
|
|||||||
getAgent: vi.fn().mockResolvedValue({ id: "agent-1", name: "Worker", role: "executor", state: "idle" }),
|
getAgent: vi.fn().mockResolvedValue({ id: "agent-1", name: "Worker", role: "executor", state: "idle" }),
|
||||||
};
|
};
|
||||||
const taskStore = {
|
const taskStore = {
|
||||||
|
getSettings: vi.fn().mockResolvedValue({ autoSummarizeTitles: false }),
|
||||||
createTask: vi.fn().mockResolvedValue({ id: "FN-100", dependencies: [], description: "Delegated" }),
|
createTask: vi.fn().mockResolvedValue({ id: "FN-100", dependencies: [], description: "Delegated" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,8 +133,34 @@ describe("createDelegateTaskTool", () => {
|
|||||||
|
|
||||||
expect(taskStore.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
expect(taskStore.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
source: { sourceType: "api" },
|
source: { sourceType: "api" },
|
||||||
|
}), expect.objectContaining({
|
||||||
|
settings: { autoSummarizeTitles: false },
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("wires title summarization callback when rootDir is provided", async () => {
|
||||||
|
const summarizeSpy = vi.spyOn(core, "summarizeTitle").mockResolvedValue("Short title");
|
||||||
|
const agentStore = {
|
||||||
|
getAgent: vi.fn().mockResolvedValue({ id: "agent-1", name: "Worker", role: "executor", state: "idle" }),
|
||||||
|
};
|
||||||
|
const taskStore = {
|
||||||
|
getSettings: vi.fn().mockResolvedValue({
|
||||||
|
autoSummarizeTitles: true,
|
||||||
|
titleSummarizerProvider: "openai",
|
||||||
|
titleSummarizerModelId: "gpt-4o-mini",
|
||||||
|
}),
|
||||||
|
createTask: vi.fn().mockResolvedValue({ id: "FN-101", dependencies: [], description: "Delegated" }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const tool = createDelegateTaskTool(agentStore as any, taskStore as any, { rootDir: "/repo" });
|
||||||
|
await tool.execute("call-1", { agent_id: "agent-1", description: "Delegated" } as any, undefined, undefined, {} as any);
|
||||||
|
|
||||||
|
const options = vi.mocked(taskStore.createTask).mock.calls[0]?.[1] as { onSummarize?: (description: string) => Promise<string | null> };
|
||||||
|
expect(options.onSummarize).toBeTypeOf("function");
|
||||||
|
await options.onSummarize?.("Long description");
|
||||||
|
expect(summarizeSpy).toHaveBeenCalledWith("Long description", "/repo", "openai", "gpt-4o-mini");
|
||||||
|
summarizeSpy.mockRestore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("createTaskLogTool", () => {
|
describe("createTaskLogTool", () => {
|
||||||
|
|||||||
@@ -1534,6 +1534,8 @@ describe("taskCreate tool model inheritance", () => {
|
|||||||
expect(store.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
expect(store.createTask).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
title: "Child Task",
|
title: "Child Task",
|
||||||
description: "Child task description",
|
description: "Child task description",
|
||||||
|
}), expect.objectContaining({
|
||||||
|
settings: { autoSummarizeTitles: false },
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1632,6 +1634,7 @@ describe("taskCreate tool model inheritance", () => {
|
|||||||
// The second createTask call should have the resolved sibling id preserved.
|
// The second createTask call should have the resolved sibling id preserved.
|
||||||
expect(createTaskMock).toHaveBeenLastCalledWith(
|
expect(createTaskMock).toHaveBeenLastCalledWith(
|
||||||
expect.objectContaining({ dependencies: ["FN-701"] }),
|
expect.objectContaining({ dependencies: ["FN-701"] }),
|
||||||
|
expect.objectContaining({ settings: { autoSummarizeTitles: false } }),
|
||||||
);
|
);
|
||||||
expect(createdSubtasksRef.current).toEqual(["FN-701", "FN-702"]);
|
expect(createdSubtasksRef.current).toEqual(["FN-701", "FN-702"]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1355,11 +1355,11 @@ export class HeartbeatMonitor {
|
|||||||
sourceType: "agent_heartbeat",
|
sourceType: "agent_heartbeat",
|
||||||
sourceAgentId: agentId,
|
sourceAgentId: agentId,
|
||||||
sourceRunId: runContext?.runId,
|
sourceRunId: runContext?.runId,
|
||||||
}));
|
}, { rootDir: this.rootDir }));
|
||||||
|
|
||||||
// Agent delegation tools
|
// Agent delegation tools
|
||||||
heartbeatTools.push(createListAgentsTool(this.store));
|
heartbeatTools.push(createListAgentsTool(this.store));
|
||||||
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore));
|
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
||||||
|
|
||||||
// Messaging tools — when MessageStore is available
|
// Messaging tools — when MessageStore is available
|
||||||
if (this.messageStore) {
|
if (this.messageStore) {
|
||||||
@@ -1845,7 +1845,7 @@ export class HeartbeatMonitor {
|
|||||||
const baseCreateTool = createTaskCreateTool(taskStore, {
|
const baseCreateTool = createTaskCreateTool(taskStore, {
|
||||||
sourceType: "agent_heartbeat",
|
sourceType: "agent_heartbeat",
|
||||||
sourceAgentId: agentId,
|
sourceAgentId: agentId,
|
||||||
});
|
}, { rootDir: this.rootDir });
|
||||||
const trackedCreateTool: ToolDefinition = {
|
const trackedCreateTool: ToolDefinition = {
|
||||||
...baseCreateTool,
|
...baseCreateTool,
|
||||||
execute: async (id: string, params: Static<typeof taskCreateParams>, signal, onUpdate, ctx) => {
|
execute: async (id: string, params: Static<typeof taskCreateParams>, signal, onUpdate, ctx) => {
|
||||||
@@ -1885,7 +1885,7 @@ export class HeartbeatMonitor {
|
|||||||
tools.push(createTaskDocumentReadTool(taskStore, taskId));
|
tools.push(createTaskDocumentReadTool(taskStore, taskId));
|
||||||
// Agent delegation tools — discover and delegate work to other agents
|
// Agent delegation tools — discover and delegate work to other agents
|
||||||
tools.push(createListAgentsTool(this.store));
|
tools.push(createListAgentsTool(this.store));
|
||||||
tools.push(createDelegateTaskTool(this.store, taskStore));
|
tools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
||||||
|
|
||||||
// Messaging tools — when MessageStore is available, agents can send and receive messages
|
// Messaging tools — when MessageStore is available, agents can send and receive messages
|
||||||
if (messageStore) {
|
if (messageStore) {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/p
|
|||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { AgentStore, AgentState, AgentCapability, TaskDocument, TaskDocumentCreateInput, TaskStore, RunMutationContext, MessageStore, Message, SourceType, Settings, ResearchRun, ResearchRunStatus, Agent } from "@fusion/core";
|
import type { AgentStore, AgentState, AgentCapability, TaskDocument, TaskDocumentCreateInput, TaskStore, RunMutationContext, MessageStore, Message, SourceType, Settings, ResearchRun, ResearchRunStatus, Agent, TaskCreateInput } from "@fusion/core";
|
||||||
import { dailyMemoryPath, ensureOpenClawMemoryFiles, getMemoryBackendCapabilities, getProjectMemory, isEphemeralAgent, memoryLongTermPath, resolveMemoryBackend, resolveResearchSettings, scheduleQmdProjectMemoryRefresh, searchProjectMemory, shouldSkipBackgroundQmdRefresh } from "@fusion/core";
|
import { dailyMemoryPath, ensureOpenClawMemoryFiles, getMemoryBackendCapabilities, getProjectMemory, isEphemeralAgent, memoryLongTermPath, resolveMemoryBackend, resolveResearchSettings, resolveTitleSummarizerSettingsModel, scheduleQmdProjectMemoryRefresh, searchProjectMemory, shouldSkipBackgroundQmdRefresh, summarizeTitle } from "@fusion/core";
|
||||||
import { ResearchOrchestrator } from "./research-orchestrator.js";
|
import { ResearchOrchestrator } from "./research-orchestrator.js";
|
||||||
import { ResearchProviderRegistry } from "./research/provider-registry.js";
|
import { ResearchProviderRegistry } from "./research/provider-registry.js";
|
||||||
import { ResearchStepRunner } from "./research-step-runner.js";
|
import { ResearchStepRunner } from "./research-step-runner.js";
|
||||||
@@ -442,6 +442,31 @@ async function getAgentMemoryWindow(rootDir: string, agentMemory: AgentMemoryCon
|
|||||||
|
|
||||||
// ── Tool factory functions ────────────────────────────────────────────────
|
// ── Tool factory functions ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
type AgentTaskCreationOptions = {
|
||||||
|
rootDir?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createAgentTask(
|
||||||
|
store: TaskStore,
|
||||||
|
input: TaskCreateInput,
|
||||||
|
options?: AgentTaskCreationOptions,
|
||||||
|
): Promise<Awaited<ReturnType<TaskStore["createTask"]>>> {
|
||||||
|
const settings = typeof (store as { getSettings?: unknown }).getSettings === "function"
|
||||||
|
? await store.getSettings()
|
||||||
|
: {} as Settings;
|
||||||
|
const rootDir = options?.rootDir;
|
||||||
|
|
||||||
|
return store.createTask(input, {
|
||||||
|
settings: { autoSummarizeTitles: settings.autoSummarizeTitles === true },
|
||||||
|
onSummarize: rootDir
|
||||||
|
? async (description: string) => {
|
||||||
|
const resolved = resolveTitleSummarizerSettingsModel(settings);
|
||||||
|
return summarizeTitle(description, rootDir, resolved.provider, resolved.modelId);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a `fn_task_create` tool that creates a new task in triage.
|
* Create a `fn_task_create` tool that creates a new task in triage.
|
||||||
*
|
*
|
||||||
@@ -451,6 +476,7 @@ async function getAgentMemoryWindow(rootDir: string, agentMemory: AgentMemoryCon
|
|||||||
export function createTaskCreateTool(
|
export function createTaskCreateTool(
|
||||||
store: TaskStore,
|
store: TaskStore,
|
||||||
provenance?: { sourceType: SourceType; sourceAgentId?: string; sourceRunId?: string },
|
provenance?: { sourceType: SourceType; sourceAgentId?: string; sourceRunId?: string },
|
||||||
|
options?: AgentTaskCreationOptions,
|
||||||
): ToolDefinition {
|
): ToolDefinition {
|
||||||
return {
|
return {
|
||||||
name: "fn_task_create",
|
name: "fn_task_create",
|
||||||
@@ -462,7 +488,7 @@ export function createTaskCreateTool(
|
|||||||
"or the current task should wait for the new one).",
|
"or the current task should wait for the new one).",
|
||||||
parameters: taskCreateParams,
|
parameters: taskCreateParams,
|
||||||
execute: async (_id: string, params: Static<typeof taskCreateParams>) => {
|
execute: async (_id: string, params: Static<typeof taskCreateParams>) => {
|
||||||
const task = await store.createTask({
|
const task = await createAgentTask(store, {
|
||||||
description: params.description,
|
description: params.description,
|
||||||
dependencies: params.dependencies,
|
dependencies: params.dependencies,
|
||||||
column: "triage",
|
column: "triage",
|
||||||
@@ -471,7 +497,7 @@ export function createTaskCreateTool(
|
|||||||
sourceAgentId: provenance.sourceAgentId,
|
sourceAgentId: provenance.sourceAgentId,
|
||||||
sourceRunId: provenance.sourceRunId,
|
sourceRunId: provenance.sourceRunId,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
});
|
}, options);
|
||||||
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
@@ -926,7 +952,11 @@ export function createListAgentsTool(agentStore: AgentStore): ToolDefinition {
|
|||||||
* @param taskStore - TaskStore for task creation
|
* @param taskStore - TaskStore for task creation
|
||||||
* @returns ToolDefinition for the `fn_delegate_task` tool
|
* @returns ToolDefinition for the `fn_delegate_task` tool
|
||||||
*/
|
*/
|
||||||
export function createDelegateTaskTool(agentStore: AgentStore, taskStore: TaskStore): ToolDefinition {
|
export function createDelegateTaskTool(
|
||||||
|
agentStore: AgentStore,
|
||||||
|
taskStore: TaskStore,
|
||||||
|
options?: AgentTaskCreationOptions,
|
||||||
|
): ToolDefinition {
|
||||||
return {
|
return {
|
||||||
name: "fn_delegate_task",
|
name: "fn_delegate_task",
|
||||||
label: "Delegate Task",
|
label: "Delegate Task",
|
||||||
@@ -954,13 +984,13 @@ export function createDelegateTaskTool(agentStore: AgentStore, taskStore: TaskSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create task assigned to the target agent
|
// Create task assigned to the target agent
|
||||||
const task = await taskStore.createTask({
|
const task = await createAgentTask(taskStore, {
|
||||||
description: params.description,
|
description: params.description,
|
||||||
dependencies: params.dependencies,
|
dependencies: params.dependencies,
|
||||||
column: "todo",
|
column: "todo",
|
||||||
assignedAgentId: params.agent_id,
|
assignedAgentId: params.agent_id,
|
||||||
source: { sourceType: "api" },
|
source: { sourceType: "api" },
|
||||||
});
|
}, options);
|
||||||
|
|
||||||
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2426,7 +2426,7 @@ export class TaskExecutor {
|
|||||||
// Agent delegation tools — discover and delegate work to other agents.
|
// Agent delegation tools — discover and delegate work to other agents.
|
||||||
...(this.options.agentStore ? [
|
...(this.options.agentStore ? [
|
||||||
createListAgentsTool(this.options.agentStore),
|
createListAgentsTool(this.options.agentStore),
|
||||||
createDelegateTaskTool(this.options.agentStore, this.store),
|
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir }),
|
||||||
] : []),
|
] : []),
|
||||||
// Messaging tools — allows executor agents to send and receive messages.
|
// Messaging tools — allows executor agents to send and receive messages.
|
||||||
...(this.options.messageStore && assignedAgentId ? [
|
...(this.options.messageStore && assignedAgentId ? [
|
||||||
@@ -3350,7 +3350,7 @@ export class TaskExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createTaskCreateTool(): ToolDefinition {
|
private createTaskCreateTool(): ToolDefinition {
|
||||||
return sharedCreateTaskCreateTool(this.store, { sourceType: "api" });
|
return sharedCreateTaskCreateTool(this.store, { sourceType: "api" }, { rootDir: this.rootDir });
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTaskDocumentWriteTool(taskId: string): ToolDefinition {
|
private createTaskDocumentWriteTool(taskId: string): ToolDefinition {
|
||||||
|
|||||||
@@ -932,14 +932,14 @@ export class StepSessionExecutor {
|
|||||||
? [createTaskLogTool(this.options.store, taskDetail.id)]
|
? [createTaskLogTool(this.options.store, taskDetail.id)]
|
||||||
: [];
|
: [];
|
||||||
const taskCreateTool = this.options.store
|
const taskCreateTool = this.options.store
|
||||||
? [createTaskCreateTool(this.options.store)]
|
? [createTaskCreateTool(this.options.store, undefined, { rootDir: this.options.rootDir })]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Agent delegation tools — discover and delegate work to other agents.
|
// Agent delegation tools — discover and delegate work to other agents.
|
||||||
const delegationTools = this.options.agentStore
|
const delegationTools = this.options.agentStore
|
||||||
? [
|
? [
|
||||||
createListAgentsTool(this.options.agentStore),
|
createListAgentsTool(this.options.agentStore),
|
||||||
createDelegateTaskTool(this.options.agentStore, this.options.store!),
|
createDelegateTaskTool(this.options.agentStore, this.options.store!, { rootDir: this.options.rootDir }),
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import type { StuckTaskDetector } from "./stuck-task-detector.js";
|
|||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import {
|
import {
|
||||||
|
createAgentTask,
|
||||||
createDelegateTaskTool,
|
createDelegateTaskTool,
|
||||||
createListAgentsTool,
|
createListAgentsTool,
|
||||||
createMemoryTools,
|
createMemoryTools,
|
||||||
@@ -930,7 +931,7 @@ export class TriageProcessor {
|
|||||||
// Agent delegation tools — discover and delegate work to other agents.
|
// Agent delegation tools — discover and delegate work to other agents.
|
||||||
...(this.options.agentStore ? [
|
...(this.options.agentStore ? [
|
||||||
createListAgentsTool(this.options.agentStore),
|
createListAgentsTool(this.options.agentStore),
|
||||||
createDelegateTaskTool(this.options.agentStore, this.store),
|
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir }),
|
||||||
] : []),
|
] : []),
|
||||||
this.createReviewSpecTool(
|
this.createReviewSpecTool(
|
||||||
task.id,
|
task.id,
|
||||||
@@ -1642,7 +1643,7 @@ export class TriageProcessor {
|
|||||||
parentTask = undefined;
|
parentTask = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTask = await store.createTask({
|
const newTask = await createAgentTask(store, {
|
||||||
title: params.title,
|
title: params.title,
|
||||||
description: params.description,
|
description: params.description,
|
||||||
dependencies: validDeps,
|
dependencies: validDeps,
|
||||||
@@ -1656,7 +1657,7 @@ export class TriageProcessor {
|
|||||||
sourceType: "agent_heartbeat",
|
sourceType: "agent_heartbeat",
|
||||||
sourceParentTaskId: options.parentTaskId,
|
sourceParentTaskId: options.parentTaskId,
|
||||||
},
|
},
|
||||||
});
|
}, { rootDir: this.rootDir });
|
||||||
|
|
||||||
// Track the created subtask
|
// Track the created subtask
|
||||||
options.createdSubtasksRef.current.push(newTask.id);
|
options.createdSubtasksRef.current.push(newTask.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user