/** * What to do when a run that owes a person an answer produces nothing. * * Owner ruling: "if it is truly worthy of silence then let it be silence. * otherwise, i generally expect a conversation to have a response." * * That splits cleanly in two, and this module owns the conversational half: * * - A CONVERSATIONAL run that ends with empty output is a DEFECT, not an * outcome. Somebody sent a message and is waiting. The model gets one more * attempt at an answer; if that also comes back empty, the person is told * plainly that no reply was generated. Never a bare acknowledgement, never * silence. * - A BACKGROUND run (a schedule, a trigger, agreed work) with nothing to * report is silence, and that half needs no code here, it is the absence of * a notification (channels/reply-pipeline.ts). * * The retry is bounded at ONE, and the bound is derived from the conversation * itself rather than from a counter: the request this module appends is its * own record that the attempt was already spent. A second empty response after * that reaches the notice, so a model that returns nothing forever costs one * extra turn, not a loop. */ import type { AgentRecord } from '../tools/agent/index.js'; /** * What the person receives when the model produced nothing twice. * * Plain language, first person, no error code and no agent id: the reader is * someone who sent a message, and what they need to know is that the silence * is a fault on this side rather than an answer. */ export declare const CONVERSATIONAL_EMPTY_REPLY_NOTICE = "No reply was generated, something went wrong on my side."; /** * The re-prompt. Deliberately concrete about what went wrong and what is * wanted, and identical every time so {@link regenerationAlreadySpent} can * recognise it. */ export declare const CONVERSATIONAL_REGENERATION_REQUEST = "Your last message was empty, so the person you are talking to received nothing. Answer their message now, in plain sentences."; /** A conversation, structurally, only what this module touches. */ export interface RecoveryConversation { addAssistantMessage(content: string, options?: { usage?: unknown; }): void; addUserMessage(content: string): void; getMessageSnapshot(): ReadonlyArray<{ readonly role: string; readonly content: unknown; }>; } /** A model response at the point the turn loop decides the run is finished. */ export interface RecoveryResponse { readonly content: string; readonly usage?: unknown; } /** True when this run's final message is a reply to a person. */ export declare function isConversationalRun(record: Pick): boolean; /** * Record the model's final message, and report whether the turn loop should * keep going. * * Returns true in exactly one case: a conversational run whose answer is empty * and whose one regeneration is unspent. Every other case ends the run, which * is what the loop did unconditionally before this existed. * * `record.progress` is set from the answer ONLY when there is an answer. It * used to fall back to a hardcoded 'Done.', which is a status line that says * nothing and, once progress reached channel surfaces, could be published as * though it were the reply. */ export declare function completeOrRegenerate(record: AgentRecord, conversation: RecoveryConversation, response: RecoveryResponse): boolean; /** * Last stop before a completed run is reported: a conversational run that * still has nothing to say says so. * * Applied at the single completion funnel rather than at the end of the turn * loop, so it also covers the runs that finish some other way, a turn budget * exhausted mid-answer, a loop that broke early, where the regeneration above * never had a chance to fire and the person would otherwise get silence. * * A no-op for every non-conversational run: work with nothing to report is * allowed to report nothing. */ export declare function recoverEmptyConversationalReply(record: AgentRecord): void; //# sourceMappingURL=conversational-reply-recovery.d.ts.map