/** * discuss.open/discuss.reply's live:true synchronous human round-trip -- the * one piece of discuss's own retired pi.registerTool() the plain * request/response Vehicle projection can't express on its own, wired in via * vehicle-client-pi's interactiveFollowUps hook (../tools/vehicle-notes-client.ts). * * The primary invoke() always durably records the round first, exactly like * every other Vehicle operation -- this only ever adds an OPTIONAL synchronous * prompt on top when ctx.hasUI and the caller actually asked for one. No * answer (canceled, no UI, live not requested) falls back to the operation's * own default content/details, matching the retired tool's exact fallback * behavior. */ import type { DiscussionAndRounds } from "@danypops/papyrus"; import { readDiscussionExtra } from "@danypops/papyrus"; import type { PiVehicleInteractiveFollowUp } from "@danypops/vehicle-client-pi"; import { askQuestion } from "./discuss-ask-view.ts"; const LIVE_REPLY_PERMISSIONS = ["discuss:read", "discuss:write"] as const; export const discussLiveFollowUp: PiVehicleInteractiveFollowUp = async (request, output, client) => { const live = (request.input as { live?: unknown } | undefined)?.live === true; if (!live || !request.context.hasUI) return undefined; const result = output as DiscussionAndRounds; const pending = (() => { try { return readDiscussionExtra(result.discussion.extra); } catch { return undefined; } })(); // The just-recorded round's own content IS the real question -- a generic "Reply to //