import { spawn } from 'node:child_process' import { isValidTeamMachineSessionId, teamMachineSessionIdFromCliIdentity, } from './agent-identity.ts' import type { AgentPromptEnvelope } from './agent-prompt.ts' export { teamMachineSessionIdFromCliIdentity } from './agent-identity.ts' export function formatTeamMachinePromptText(prompt: AgentPromptEnvelope): string { return typeof prompt.text === 'string' ? prompt.text.trim() : '' } function resolveTeamMachineCommand(explicit?: string): string { const fromArg = explicit?.trim() if (fromArg) return fromArg return 'tm' } export async function sendPromptToTeamMachineSession(input: { sessionId: string prompt: AgentPromptEnvelope command?: string timeoutMs?: number }): Promise { const sessionId = input.sessionId.trim() if (!isValidTeamMachineSessionId(sessionId)) { throw new Error(`invalid Team Machine session id: ${sessionId || ''}`) } const text = formatTeamMachinePromptText(input.prompt) if (!text) { throw new Error('prompt text is empty') } const command = resolveTeamMachineCommand(input.command) const timeoutMs = input.timeoutMs ?? 15_000 await new Promise((resolve, reject) => { const child = spawn(command, ['send', sessionId, text], { stdio: ['ignore', 'ignore', 'pipe'], env: process.env, }) let stderr = '' const timer = setTimeout(() => { try { child.kill('SIGTERM') } catch {} reject(new Error(`tm send timed out after ${Math.round(timeoutMs / 1000)}s`)) }, timeoutMs) child.stderr?.setEncoding('utf8') child.stderr?.on('data', (chunk) => { if (stderr.length < 4000) stderr += String(chunk) }) child.on('error', (err) => { clearTimeout(timer) reject(err) }) child.on('close', (code, signal) => { clearTimeout(timer) if (code === 0) { resolve() return } const detail = stderr.trim() reject( new Error( detail || `tm send exited with ${signal ? `signal ${signal}` : `code ${code ?? 'unknown'}`}`, ), ) }) }) }