import { randomUUID } from "node:crypto"; interface PendingInteraction { resolve: (result: unknown) => void; timer: ReturnType; } export type TeammateInteractionFailureReason = "unavailable" | "timeout" | "send-failed"; export type TeammateInteractionResult = | { ok: true; result: T } | { ok: false; reason: TeammateInteractionFailureReason; error?: string }; interface RelayState { installed: boolean; pending: Map; } const relayKey = Symbol.for("pi-maestro-flow.teammate-interactions"); export function isTeammateChild(): boolean { return process.env.PI_TEAMMATE_CHILD === "1" && typeof process.send === "function"; } export async function requestTeammateInteraction( interaction: "permission" | "question", payload: Record, timeoutMs = 10 * 60_000, ): Promise> { if (!isTeammateChild()) return { ok: false, reason: "unavailable" }; const state = relayState(); installListener(state); const requestId = randomUUID(); return new Promise>((resolve) => { let settled = false; let timer: ReturnType; const finish = (result: TeammateInteractionResult) => { if (settled) return; settled = true; clearTimeout(timer); state.pending.delete(requestId); resolve(result); }; timer = setTimeout(() => finish({ ok: false, reason: "timeout" }), timeoutMs); timer.unref?.(); state.pending.set(requestId, { timer, resolve: (result) => finish({ ok: true, result: result as T }), }); try { process.send?.({ type: "teammate_interaction_request", requestId, interaction, correlationId: process.env.PI_TEAMMATE_CORRELATION_ID, payload, }, (error) => { if (error) { finish({ ok: false, reason: "send-failed", error: error.message }); } }); } catch (error) { finish({ ok: false, reason: "send-failed", error: error instanceof Error ? error.message : String(error), }); } }); } function relayState(): RelayState { const globals = globalThis as typeof globalThis & Record; const existing = globals[relayKey] as RelayState | undefined; if (existing) return existing; const created: RelayState = { installed: false, pending: new Map() }; globals[relayKey] = created; return created; } function installListener(state: RelayState): void { if (state.installed) return; state.installed = true; process.on("message", (message: unknown) => { const record = message as Record; if (record?.type !== "teammate_interaction_response" || typeof record.requestId !== "string") return; const pending = state.pending.get(record.requestId); if (!pending) return; state.pending.delete(record.requestId); clearTimeout(pending.timer); pending.resolve(record.result); }); }