/** * The durable half of an ask-user question, as the Session reports it. * * A question used to exist only as a live request: the dialog the answerer opened * and the row it pushed. Nothing rebuilt it from the log, so `--resume` showed the * `ask_user_question` *tool* card and nothing that said what was asked or answered — * the same session read differently depending on whether it was lived or replayed. * * The Session already records the truth: `tool/call` carries the questions and * `tool/result` (or a late `user-question-reply` message) carries the answers, and * `@deepseek-ai/dsh-user-questions` folds both into a projection every Client can * read (`ctx.sessionProjections.stateOf(session, 'userQuestions')`). This module * turns that view into the records the transcript paints, and it is deliberately * pure: the caller supplies the questions, because the projection's *settled* half * names only the call and the answers. * * Two limits are the Harness's, not this module's, and both are why the caller * keeps its live path as well: * * - the projection tracks only **timed** `ask_user_question` calls — a call made * while the blocking legacy schema was in effect appears in neither half, so a * session that only ever used it folds to an empty view; * - it needs the projection service to be registered at all, which a bare Context * in a test (or an embedder without `dsh-session-projection`) does not provide. * * @module dsh-ssh-tui/question-state */ import type { AskUserQuestionAnswerItem, AskUserQuestionItem } from '@deepseek-ai/dsh-user-questions'; /** * The projection's two halves, declared here as the *shape this plugin reads* * rather than imported by name. * * `PendingUserQuestion`, `SettledUserQuestion` and `UserQuestionProjectionView` are * named exports of `dsh-user-questions` only from 0.2.0-rc.2 on: the same module on * 0.1.7-rc.x and 0.2.0-rc.1 exports a different surface, and importing the names made * the plugin fail to *compile* there — which is how three CI legs went red at 0.8.2's * first push. Nothing here needs the host's names: every field below is already read * defensively (`questionViewOf` takes `unknown` and parses it), and the two item types * are exported by every line the plugin declares. */ export interface QuestionViewPending { readonly callId: string; readonly questions: readonly AskUserQuestionItem[]; /** `open` while the window is live, `continued` once it closed but still accepts. */ readonly state?: string; } export interface QuestionViewSettled { readonly callId: string; readonly answers?: readonly AskUserQuestionAnswerItem[]; } /** Both halves of one Session's timed `ask_user_question` state. */ export interface QuestionView { readonly active: readonly QuestionViewPending[]; readonly settled: readonly QuestionViewSettled[]; } /** One question as the transcript should show it, live or replayed. */ export interface DurableQuestionRecord { /** The `ask_user_question` call this question belongs to. */ callId: string; question: AskUserQuestionItem; /** * `open` is a foreground wait nobody has answered yet; `continued` is one whose * window closed but which the Session still accepts an answer for; the other two * are terminal. */ state: 'open' | 'continued' | 'answered' | 'cancelled'; /** What the human chose; empty while the question is unanswered. */ answers: readonly AskUserQuestionAnswerItem[]; } /** The projection view, or the empty one when nothing is registered. */ export declare function questionViewOf(value: unknown): QuestionView | undefined; /** Whether the projection has anything to say about this session's questions. */ export declare function questionViewIsEmpty(view: QuestionView | undefined): boolean; /** * Every question the Session log can still account for, in ask order then * settlement order. * * A settled call names only its answers, so a call whose questions this process * never saw (a resumed session whose `tool/call` predates the log window it read) * contributes nothing rather than a half-record: an answer with no question is not * something the transcript can draw. * @param view - the projection view, or undefined when nothing is registered. * @param questionsOf - the questions recorded for one call, when known. * @returns one record per question, newest last. */ export declare function durableQuestionRecords(view: QuestionView | undefined, questionsOf: (callId: string) => readonly AskUserQuestionItem[] | undefined): DurableQuestionRecord[]; /** * How one answered question reads on its card. * * Mirrors what the live dialog writes when it settles a card, so a session that * was replayed shows the same sentence as one that was lived: the typed answer * when there was one, the chosen labels otherwise. * @param answers - the recorded answers for this question. * @returns the summary text. */ export declare function answerSummaryText(answers: readonly AskUserQuestionAnswerItem[]): string;