import { Context } from 'cordis' import type { Terminal } from '@earendil-works/pi-tui' import AgentRegistry, { type Agent, type AgentStatus } from '@deepseek-ai/dsh-agent' import type { ContentBlock } from '@deepseek-ai/dsh-llm' import SessionStore, { SessionId, type Session } from '@deepseek-ai/dsh-session' import type { ToolDefinition } from '@deepseek-ai/dsh-tools' import UserInteractionService from '@deepseek-ai/dsh-user-interaction' import { createTuiChat, type Config } from '../src/index.ts' interface FakeAgent extends Agent { status: AgentStatus sent: ContentBlock[][] steered: ContentBlock[][] cancelled: string[] } export interface TuiHarnessOptions { status?: AgentStatus config?: Config tools?: Record configureContext?: (ctx: Context) => Promise beforeMount?: (session: Session) => void cwd?: string | null } export interface TuiHarness void> { ctx: Context session: Session agent: FakeAgent terminal: TerminalType exit: Exit controller: ReturnType } /** * Compose the production TUI around an in-memory session and controllable agent. * @param terminal - Terminal boundary driven by the test. * @param exit - Process-exit observer. * @param options - Initial session, agent, tool, and TUI configuration. * @returns The mounted TUI and every boundary the test may drive or inspect. */ export async function createTuiTestHarness void>( terminal: TerminalType, exit: Exit, options: TuiHarnessOptions = {}, ): Promise> { const ctx = new Context() await ctx.plugin(SessionStore) await ctx.plugin(AgentRegistry) await ctx.plugin(UserInteractionService) if (options.configureContext === undefined) { const tools = options.tools ?? {} ctx.provide('tools', { get(name: string) { return tools[name] }, } as never) } else { await options.configureContext(ctx) } const sessionId = SessionId('main-session') const session = ctx.sessions.create( sessionId, options.cwd === null ? undefined : { meta: { cwd: options.cwd ?? '/workspace' } }, ) options.beforeMount?.(session) const sent: ContentBlock[][] = [] const steered: ContentBlock[][] = [] const cancelled: string[] = [] const agent: FakeAgent = { id: sessionId, options: { model: 'deepseek-v4-flash' }, session, status: options.status ?? 'idle', ctx, sent, steered, cancelled, send(content) { sent.push(content) }, steer(content) { steered.push(content) }, inject() {}, cancel(reason) { cancelled.push(reason ?? '') }, whenIdle() { return Promise.resolve() }, } ctx.agents.register(agent) const controller = createTuiChat(ctx, Object.assign({ welcome: 'Coding agent ready.', sessionId, color: false, }, options.config), { terminal, exit }) return { ctx, session, agent, terminal, exit, controller } } /** Dispose the mounted TUI before its owning Cordis context. */ export async function disposeTuiTestHarness( setup: Pick void>, 'controller' | 'ctx'>, ): Promise { await setup.controller.dispose() await setup.ctx.fiber.dispose() } /** Append a production-shaped user message to the active session surface. */ export function appendUser(session: Session, text: string): void { session.append('user/message', { content: [{ type: 'text', text }], source: { kind: 'user' }, }, { surfaceOp: 'append' }) } /** Append a production-shaped assistant message to the active session surface. */ export function appendAssistant( session: Session, content: ContentBlock[], usage?: { inputTokens: number; outputTokens: number }, position: { turn: number; step: number } = { turn: 1, step: 0 }, ): void { session.append('assistant/message', { turn: position.turn, step: position.step, provenance: { provider: 'mock', model: 'deepseek-v4-flash' }, content, ...usage === undefined ? {} : { usage }, }, { surfaceOp: 'append' }) }