From 8394086446c9aad28c1b37b3956389df75aacf8b Mon Sep 17 00:00:00 2001 From: William Valentin Date: Tue, 17 Feb 2026 11:03:55 -0800 Subject: [PATCH] Wire agent.delegate into TUI tool registry --- docs/plans/state.json | 13 ++++++++ src/cli/tui.ts | 41 +++++++++++++++++++++++- src/tools/builtin/agent-delegate.test.ts | 11 ++++--- src/tools/builtin/agent-delegate.ts | 17 ++++++++-- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/docs/plans/state.json b/docs/plans/state.json index 41a6772..4458443 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -3754,6 +3754,19 @@ "docs/plans/state.json" ], "test_status": "pnpm test:run src/frontends/tui/minimal.test.ts src/models/gemini.test.ts + pnpm typecheck passing" + }, + "tui-agent-delegate-registration": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Wired minimal/fullscreen TUI tool registry to register `agents.list` and `agent.delegate` when `agent_configs` exist. Added a TUI delegation bridge that executes delegated single-turn tasks through `ModelRouter` while preserving per-agent tier/system prompt behavior and keeping existing TUI `NativeAgent` flow unchanged.", + "files_modified": [ + "src/cli/tui.ts", + "src/tools/builtin/agent-delegate.ts", + "src/tools/builtin/agent-delegate.test.ts", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/tools/builtin/agent-delegate.test.ts + pnpm typecheck passing" } }, "overall_progress": { diff --git a/src/cli/tui.ts b/src/cli/tui.ts index d2ec737..d3e38e1 100644 --- a/src/cli/tui.ts +++ b/src/cli/tui.ts @@ -102,9 +102,25 @@ export function registerTuiCommand(program: Command): void { setLogLevel(tuiLogLevel); const { MinimalTui, startFullscreenTui } = await import('../frontends/tui/index.js'); const { NativeAgent } = await import('../backends/index.js'); - const { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, createGmailTools, createGcalTools, createGdocsTools, createGdriveTools, createGtasksTools } = await import('../tools/index.js'); + const { + ToolRegistry, + ToolExecutor, + ToolPolicy, + allBuiltinTools, + createWebSearchTools, + createProcessTools, + ProcessManager, + createGmailTools, + createGcalTools, + createGdocsTools, + createGdriveTools, + createGtasksTools, + createAgentsListTool, + createAgentDelegateTool, + } = await import('../tools/index.js'); const { HookEngine } = await import('../hooks/index.js'); const { createModelRouter } = await import('../daemon/index.js'); + const { AgentConfigRegistry } = await import('../agents/index.js'); const dataDir = process.env.FLYNN_DATA_DIR ?? resolve(homedir(), '.local/share/flynn'); mkdirSync(dataDir, { recursive: true }); @@ -194,6 +210,29 @@ export function registerTuiCommand(program: Command): void { } } + const agentConfigRegistry = new AgentConfigRegistry(); + agentConfigRegistry.loadFromConfig(config.agent_configs); + if (agentConfigRegistry.list().length > 0) { + toolRegistry.register(createAgentsListTool(agentConfigRegistry)); + toolRegistry.register(createAgentDelegateTool({ + registry: agentConfigRegistry, + orchestrator: { + async delegate(request) { + const response = await modelRouter.chat({ + messages: [{ role: 'user', content: request.message }], + system: request.systemPrompt, + maxTokens: request.maxTokens, + }, request.tier); + return { + content: response.content, + usage: response.usage, + tier: request.tier, + }; + }, + }, + })); + } + toolRegistry.setPolicy(new ToolPolicy(config.tools)); const toolExecutor = new ToolExecutor(toolRegistry, hookEngine); diff --git a/src/tools/builtin/agent-delegate.test.ts b/src/tools/builtin/agent-delegate.test.ts index be0019a..9fdb332 100644 --- a/src/tools/builtin/agent-delegate.test.ts +++ b/src/tools/builtin/agent-delegate.test.ts @@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createAgentDelegateTool } from './agent-delegate.js'; import type { AgentDelegateDeps } from './agent-delegate.js'; import type { AgentConfigRegistry } from '../../agents/registry.js'; -import type { AgentOrchestrator } from '../../backends/native/orchestrator.js'; function createMockRegistry(configs: Record): AgentConfigRegistry { const entries = Object.entries(configs).map(([name, cfg]) => ({ @@ -17,19 +16,21 @@ function createMockRegistry(configs: Record { let deps: AgentDelegateDeps; - let mockOrchestrator: AgentOrchestrator; + let mockOrchestrator: AgentDelegateDeps['orchestrator']; beforeEach(() => { mockOrchestrator = createMockOrchestrator(); @@ -127,7 +128,7 @@ describe('agent.delegate tool', () => { it('handles orchestrator errors gracefully', async () => { const failingOrchestrator = { delegate: vi.fn().mockRejectedValue(new Error('Model provider unavailable')), - } as unknown as AgentOrchestrator; + } as AgentDelegateDeps['orchestrator']; const tool = createAgentDelegateTool({ ...deps, orchestrator: failingOrchestrator }); const result = await tool.execute({ agent: 'research', task: 'This will fail' }); diff --git a/src/tools/builtin/agent-delegate.ts b/src/tools/builtin/agent-delegate.ts index 59b4924..90864e4 100644 --- a/src/tools/builtin/agent-delegate.ts +++ b/src/tools/builtin/agent-delegate.ts @@ -1,11 +1,24 @@ import type { Tool, ToolResult } from '../types.js'; import type { AgentConfigRegistry } from '../../agents/registry.js'; -import type { AgentOrchestrator } from '../../backends/native/orchestrator.js'; import type { ModelTier } from '../../models/router.js'; +import type { TokenUsage } from '../../models/types.js'; + +interface AgentDelegateRunner { + delegate(request: { + tier: ModelTier; + systemPrompt: string; + message: string; + maxTokens?: number; + }): Promise<{ + content: string; + usage: TokenUsage; + tier: ModelTier; + }>; +} export interface AgentDelegateDeps { registry: AgentConfigRegistry; - orchestrator: AgentOrchestrator; + orchestrator: AgentDelegateRunner; } /**