feat: Stateless subagents (#127)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { getClient } from "../../agent/client";
|
||||
import {
|
||||
getCurrentAgentId,
|
||||
getCurrentClient,
|
||||
getSkillsDirectory,
|
||||
setHasLoadedSkills,
|
||||
} from "../../agent/context";
|
||||
@@ -128,7 +128,7 @@ async function readSkillContent(
|
||||
* Get skills directory, trying multiple sources
|
||||
*/
|
||||
async function getResolvedSkillsDir(
|
||||
client: ReturnType<typeof getCurrentClient>,
|
||||
client: Awaited<ReturnType<typeof getClient>>,
|
||||
agentId: string,
|
||||
): Promise<string> {
|
||||
let skillsDir = getSkillsDirectory();
|
||||
@@ -176,7 +176,7 @@ export async function skill(args: SkillArgs): Promise<SkillResult> {
|
||||
|
||||
try {
|
||||
// Get current agent context
|
||||
const client = getCurrentClient();
|
||||
const client = await getClient();
|
||||
const agentId = getCurrentAgentId();
|
||||
|
||||
// Handle refresh command
|
||||
|
||||
75
src/tools/impl/Task.ts
Normal file
75
src/tools/impl/Task.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Task tool implementation
|
||||
*
|
||||
* Spawns specialized subagents to handle complex, multi-step tasks autonomously.
|
||||
* Supports both built-in subagent types and custom subagents defined in .letta/agents/.
|
||||
*/
|
||||
|
||||
import { getAllSubagentConfigs } from "../../agent/subagents";
|
||||
import { spawnSubagent } from "../../agent/subagents/manager";
|
||||
import { validateRequiredParams } from "./validation";
|
||||
|
||||
interface TaskArgs {
|
||||
subagent_type: string;
|
||||
prompt: string;
|
||||
description: string;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format args for display (truncate prompt)
|
||||
*/
|
||||
function formatTaskArgs(args: TaskArgs): string {
|
||||
const parts: string[] = [];
|
||||
parts.push(`subagent_type="${args.subagent_type}"`);
|
||||
parts.push(`description="${args.description}"`);
|
||||
// Truncate prompt for display
|
||||
const promptPreview =
|
||||
args.prompt.length > 20 ? `${args.prompt.slice(0, 17)}...` : args.prompt;
|
||||
parts.push(`prompt="${promptPreview}"`);
|
||||
if (args.model) parts.push(`model="${args.model}"`);
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Task tool - Launch a specialized subagent to handle complex tasks
|
||||
*/
|
||||
export async function task(args: TaskArgs): Promise<string> {
|
||||
// Validate required parameters
|
||||
validateRequiredParams(
|
||||
args,
|
||||
["subagent_type", "prompt", "description"],
|
||||
"Task",
|
||||
);
|
||||
|
||||
const { subagent_type, prompt, description, model } = args;
|
||||
|
||||
// Print Task header FIRST so subagent output appears below it
|
||||
console.log(`\n● Task(${formatTaskArgs(args)})\n`);
|
||||
|
||||
// Get all available subagent configs (built-in + custom)
|
||||
const allConfigs = await getAllSubagentConfigs();
|
||||
|
||||
// Validate subagent type
|
||||
if (!(subagent_type in allConfigs)) {
|
||||
const available = Object.keys(allConfigs).join(", ");
|
||||
return `Error: Invalid subagent type "${subagent_type}". Available types: ${available}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await spawnSubagent(
|
||||
subagent_type,
|
||||
prompt,
|
||||
description,
|
||||
model,
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
return `Error: ${result.error || "Subagent execution failed"}`;
|
||||
}
|
||||
|
||||
return result.report;
|
||||
} catch (error) {
|
||||
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user