| 1 | import type { AppBindings } from "./bridge"; |
| 2 | import type { InteractionTarget } from "./interactionTarget"; |
| 3 | import { hasSessionGeneration } from "./sessionIdentity"; |
| 4 | import { notePromptSubmission, promptFailureClass } from "./promptSubmissionDiagnostics"; |
| 5 | |
| 6 | type ExactPromptBinding = Pick<AppBindings, "ResolvePromptForSession" | "ResolvePromptForTab" | "PendingPromptIdentitiesForTab">; |
| 7 | |
| 8 | export async function resolvePromptForSession( |
| 9 | binding: ExactPromptBinding, |
| 10 | target: InteractionTarget, |
| 11 | answer: Record<string, unknown>, |
| 12 | ): Promise<void> { |
| 13 | if (!binding.ResolvePromptForSession) throw new Error("exact session prompt submission is unavailable; upgrade the host"); |
| 14 | if (!target.sessionId || !hasSessionGeneration(target.sessionGeneration)) throw new Error("prompt session identity is unavailable; refresh the card"); |
| 15 | let turnId = target.turnId; |
| 16 | let runtimeEpoch = target.runtimeEpoch; |
| 17 | if (!turnId) { |
| 18 | if (!binding.PendingPromptIdentitiesForTab) throw new Error("prompt identity is unavailable; refresh or upgrade the host"); |
| 19 | const matches = (await binding.PendingPromptIdentitiesForTab(target.tabId)).filter((candidate) => ( |
| 20 | candidate.promptId === target.promptId && candidate.kind === target.kind && |
| 21 | (!runtimeEpoch || !candidate.runtimeEpoch || candidate.runtimeEpoch === runtimeEpoch) |
| 22 | )); |
| 23 | if (matches.length !== 1) throw new Error("prompt is stale or its exact identity is unavailable"); |
| 24 | turnId = matches[0].turnId; |
| 25 | runtimeEpoch = matches[0].runtimeEpoch; |
| 26 | } |
| 27 | notePromptSubmission(target, "transport", "sent"); |
| 28 | try { |
| 29 | await binding.ResolvePromptForSession({ |
| 30 | tabId: target.tabId, |
| 31 | hostId: target.hostId ?? "local", |
| 32 | sessionId: target.sessionId, |
| 33 | sessionGeneration: target.sessionGeneration, |
| 34 | promptId: target.promptId, |
| 35 | turnId, |
| 36 | runtimeEpoch: runtimeEpoch ?? "", |
| 37 | kind: target.kind, |
| 38 | }, answer); |
| 39 | notePromptSubmission(target, "transport", "accepted"); |
| 40 | } catch (error) { |
| 41 | notePromptSubmission(target, "transport", `rejected:${promptFailureClass(error)}`); |
| 42 | throw error; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | export async function resolvePromptForTab( |
| 47 | binding: ExactPromptBinding, |
| 48 | tabId: string, |
| 49 | promptId: string, |
| 50 | kind: string, |
| 51 | answer: Record<string, unknown>, |
| 52 | knownTurnId?: string, |
| 53 | knownRuntimeEpoch?: string, |
| 54 | ): Promise<void> { |
| 55 | if (!binding.ResolvePromptForTab) throw new Error("exact prompt submission is unavailable; upgrade the host"); |
| 56 | let turnId = knownTurnId; |
| 57 | let runtimeEpoch = knownRuntimeEpoch; |
| 58 | if (!turnId) { |
| 59 | if (!binding.PendingPromptIdentitiesForTab) throw new Error("prompt identity is unavailable; refresh or upgrade the host"); |
| 60 | const matches = (await binding.PendingPromptIdentitiesForTab(tabId)).filter((candidate) => ( |
| 61 | candidate.promptId === promptId && candidate.kind === kind && |
| 62 | (!knownRuntimeEpoch || !candidate.runtimeEpoch || candidate.runtimeEpoch === knownRuntimeEpoch) |
| 63 | )); |
| 64 | if (matches.length !== 1) throw new Error("prompt is stale or its exact identity is unavailable"); |
| 65 | turnId = matches[0].turnId; |
| 66 | runtimeEpoch = matches[0].runtimeEpoch; |
| 67 | } |
| 68 | await binding.ResolvePromptForTab(tabId, promptId, turnId, runtimeEpoch ?? "", kind, answer); |
| 69 | } |
| 70 |