| 1 | import { noteSessionObservation } from "./sessionObservationDiagnostics"; |
| 2 | import type { InteractionTarget } from "./interactionTarget"; |
| 3 | |
| 4 | export type PromptDiagnosticTarget = Pick<InteractionTarget, "tabId" | "sessionId" | "sessionGeneration"> & |
| 5 | Partial<Pick<InteractionTarget, "promptId" | "turnId" | "runtimeEpoch" | "kind">>; |
| 6 | |
| 7 | /** Closed labels only: host errors may include private paths or tool contents. */ |
| 8 | export function promptFailureClass(error: unknown): string { |
| 9 | const text = (error instanceof Error ? error.message : String(error)).toLowerCase(); |
| 10 | if (/session binding is stale|host binding is stale/.test(text)) return "stale-binding"; |
| 11 | if (/stale runtime|runtime changed/.test(text)) return "stale-runtime"; |
| 12 | if (/stale turn|active turn/.test(text)) return "stale-turn"; |
| 13 | if (/already resolved/.test(text)) return "already-resolved"; |
| 14 | if (/not pending/.test(text)) return "not-pending"; |
| 15 | if (/identity.*(?:unavailable|required)/.test(text)) return "missing-identity"; |
| 16 | if (/unavailable|upgrade/.test(text)) return "unavailable"; |
| 17 | return "other"; |
| 18 | } |
| 19 | |
| 20 | export function notePromptSubmission(target: PromptDiagnosticTarget, stage: "command" | "transport", status: string): void { |
| 21 | if (!target.sessionId) return; |
| 22 | noteSessionObservation(`session-id:${target.sessionId}`, { |
| 23 | action: `prompt-${stage}`, tabId: target.tabId, generation: target.sessionGeneration ?? 0, |
| 24 | sequence: 0, status, |
| 25 | prompt: { id: target.promptId, turnId: target.turnId, runtimeEpoch: target.runtimeEpoch, |
| 26 | kind: target.kind, bindingGeneration: target.sessionGeneration ?? null }, |
| 27 | }); |
| 28 | } |
| 29 |