import { SlashCommand } from '../../shared/constants.js'; import { type ConversationMessage } from './interactiveApplication.js'; import { type SessionContext } from './aiCaller.js'; import type { ConversationPromptConfiguration, SummaryPromptBuilder } from './conversationLoop.js'; import type { WorkflowContext } from './interactive-summary-types.js'; import type { InteractiveMetadata } from '../tasks/execute/types.js'; import type { PermissionMode } from '../../core/models/index.js'; import type { ImageAttachmentReference } from '../../shared/types/image-attachments.js'; import type { StreamCallback } from '../../shared/types/provider.js'; export interface ConversationSessionStrategy { systemPrompt: string; /** Whether formal notation blocks must include natural-language meaning comments. */ formalSpecComments?: boolean; /** Timeout for Quint model checking and Alloy verification stages, in seconds. */ modelCheckTimeoutSeconds: number; allowedTools: string[]; /** Optional permission mode resolved for the conversation's provider. */ permissionMode?: PermissionMode; transformPrompt: (message: string, sourceContext?: string) => string; summaryPromptContext?: string; initialPromptContext?: string; /** * Mode-specific `/go` prompt. Retry and Instruct revise the task's existing * `order.md` instead of writing a new instruction, and that prompt is built * from the canonical order — the same builder the readline loop uses. */ summaryPromptBuilder?: SummaryPromptBuilder; /** Resolve the prompt again immediately before a regular turn or /go summary. */ resolveCurrentPromptConfiguration?: () => ConversationPromptConfiguration | Promise; /** Use the current conversation system prompt as /go's system prompt. */ useCurrentSystemPromptForSummary?: boolean; /** * The commands this mode allows. The front-end refuses the rest before they * reach the session, and the session reads the same list so a line a guarded * mode disabled is text here too — not a command it happens to understand. */ enabledCommands?: readonly SlashCommand[]; /** Task/action content supplied to the first `/verify` generation call. */ formalSpecInitialContext?: string; /** Enable the `/tell` command. */ enableTellCommand?: boolean; /** Run to use as the initial `/tell` choice. */ initialReferenceRunSlug?: string; } export interface ConversationSessionOptions { cwd: string; formalSpec: boolean; /** Resolved setting propagated to this session's summary prompt. */ formalSpecComments?: boolean; /** Timeout for Quint model checking and Alloy verification stages, in seconds. */ modelCheckTimeoutSeconds: number; outputMode?: 'terminal' | 'silent'; ctx: SessionContext; strategy: ConversationSessionStrategy; workflowContext?: WorkflowContext; sourceContext?: string; /** Task text seeded from outside the conversation; enters the history without an AI call. */ initialUserMessage?: string; /** Prior session transcript supplied once as inert reference context after a settings switch. */ handoffHistory?: readonly ConversationMessage[]; /** Whether a provider session returned by regular messages may be saved for `/continue`. */ persistSession?: boolean; /** * Summarize a continued provider session that has no local transcript yet. * Off by default: without it a `/go` with nothing to summarize reports that * there is no conversation, which is what the ACP adapter relies on. */ summarizeResumedSession?: boolean; /** Stream observer for front-ends that render the response themselves (`outputMode: 'silent'`). */ onStream?: StreamCallback; /** Resolves the image placeholders a prompt references into provider attachments. */ resolveImageAttachments?: (prompt: string) => ImageAttachmentReference[]; } /** What one turn is given: how to stop it, and where its own stream goes. */ export interface ConversationTurnInput { abortSignal?: AbortSignal; /** * Receives this turn's chunks. A provider that ignores its abort can still * emit after the user moved on, and those chunks belong to the turn that asked * for them — never to the one on screen now. */ onStream?: StreamCallback; /** * Receives this turn's notices — for example that the provider took the images * as paths rather than as images. Same reason as `onStream`: a notice about a * turn's images belongs to the turn that sent them, never to the one on screen * now. */ onNotice?: (message: string) => void; } /** * Machine-readable cause so front-ends can localize the failure. * `provider_error` carries the provider's own text in `message`. */ export type ConversationSessionErrorCode = 'message_required' | 'task_text_required' | 'empty_ai_response' | 'no_conversation' | 'instruction_failed' | 'unsupported_command' | 'provider_error'; export type ConversationSessionResult = { kind: 'assistant_response'; content: string; sessionId?: string; } | { kind: 'workflow_execution_requested'; task: string; workflowIdentifier?: string; interactiveMetadata: InteractiveMetadata; sessionId?: string; } | { kind: 'error'; /** Absent when a producer only has human-readable text to offer. */ code?: ConversationSessionErrorCode; message: string; }; export interface ConversationSession { handleUserMessage(input: ConversationTurnInput & { text: string; }): Promise; createTaskInstruction(input: ConversationTurnInput & { userNote: string; }): Promise; } /** * Controls only the interactive front-ends need. Kept off `ConversationSession` * so the ACP adapter's dependency contract stays exactly as it was. */ export interface InteractiveConversationSession extends ConversationSession { /** Latest assistant reply, for front-ends that offer /accept. */ getLatestAssistantMessage(): string | null; /** * Put a `/go` draft the user rejected back into the conversation, so the next * revision starts from what was proposed rather than from nothing. */ recordRejectedDraft(task: string): void; /** Continue from a previously recorded provider session (/resume). */ setSessionId(nextSessionId: string): void; /** Apply the prompt configuration resolved for the selected session. */ setPromptConfiguration(configuration: ConversationPromptConfiguration): void; /** Snapshot every user/assistant message a replacement session still needs. */ snapshotHistory(): readonly ConversationMessage[]; /** Apply an effort override to subsequent calls without replacing the session. */ setEffort(effort: string): void; /** Latest run confirmed by a successful task-state lookup in this session. */ getReferenceRunSlug?(): string | undefined; } export declare function createConversationSession(options: ConversationSessionOptions): InteractiveConversationSession; //# sourceMappingURL=conversationSession.d.ts.map