UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

104 lines 4.82 kB
import { z } from 'zod'; import { v4 as uuidv4 } from 'uuid'; import { run } from './engine.js'; import { createRunId, } from './types.js'; import { ToolResponse } from './tool-results.js'; import { getToolRuntime } from './tool-runtime.js'; /** * Wrap an Agent as a Tool so it can be invoked by another agent. * The child agent runs in an isolated sub-run, receives only the input text, * and returns its final output as a string (or via custom extractor). */ export function agentAsTool(childAgent, options = {}) { const { toolName = childAgent.name, toolDescription = `Run the '${childAgent.name}' agent on the supplied input and return its result`, customOutputExtractor, maxTurns = 5, registry, propagateEvents = 'summary', memoryMode = 'none', } = options; const paramsSchema = z.object({ input: z.string().describe('Input text passed to the sub-agent as a user message') }); return { schema: { name: toolName, description: toolDescription, parameters: paramsSchema, }, execute: async (args, context) => { // Retrieve current runtime (state + config) from engine bridge const runtime = getToolRuntime(context); if (!runtime) { return ToolResponse.error('EXECUTION_FAILED', 'Agent tool cannot access runtime. Ensure engine installs tool runtime before execution.'); } const parentState = runtime.state; const parentConfig = runtime.config; // Build child run state: new runId, same traceId, single user message const childState = { runId: createRunId(uuidv4()), traceId: parentState.traceId, messages: [ { role: 'user', content: args.input } ], currentAgentName: childAgent.name, context: parentState.context, turnCount: 0, approvals: new Map(), }; // Build child config derived from parent const childRegistry = registry ?? new Map([[childAgent.name, childAgent]]); const childConfig = { ...parentConfig, agentRegistry: childRegistry, maxTurns, // Memory isolation by default ...(memoryMode === 'none' ? { memory: undefined, conversationId: undefined } : {}), onEvent: (event) => { if (propagateEvents === 'all') { parentConfig.onEvent?.(event); } else if (propagateEvents === 'summary') { // For summary, we still forward important boundaries if (event.type === 'run_start' || event.type === 'run_end' || event.type === 'final_output' || event.type === 'handoff') { parentConfig.onEvent?.(event); } } } }; try { const result = await run(childState, childConfig); const childRunId = childState.runId; if (result.outcome.status === 'completed') { const output = result.outcome.output; let text; if (customOutputExtractor) { text = await customOutputExtractor(output, result.finalState); } else { // Default conversion: strings as-is, objects to JSON text = typeof output === 'string' ? output : JSON.stringify(output); } return ToolResponse.success(text, { toolName, childRunId, childAgent: childAgent.name, turns: result.finalState.turnCount, }); } // Error path let message; if (result.outcome.status === 'error') { const err = result.outcome.error; message = `${err._tag}${'detail' in err ? `: ${err.detail}` : ''}`; } else { message = 'Sub-agent interrupted'; } return ToolResponse.error('EXECUTION_FAILED', message, { toolName, childRunId, childAgent: childAgent.name, }); } catch (e) { return ToolResponse.error('EXECUTION_FAILED', e instanceof Error ? e.message : String(e), { toolName, childAgent: childAgent.name }); } } }; } //# sourceMappingURL=agent-as-tool.js.map