import { Accessor } from 'solid-js'; import { type PredictionHistoryMessage } from './regeneratePlan'; import type { MessageType, FilePreview, FileUpload, IAction } from '@/widget/types'; type SubmitDeps = { deploymentId: string; apiHost: string | undefined; onRequest?: (request: RequestInit) => Promise; chatflowConfig?: Record; errorMessage: string | undefined; /** * Ask Core for the AG-UI wire, which is what carries Dynamic UI components live. * * Core downgrades to the legacy wire on its own when Dynamic UI is disabled server-side, and * the stream layer passes through whichever frames actually arrive — so leaving this on is * safe against any Core version. Set false to pin the legacy wire. */ dynamicUi?: boolean; chatId: Accessor; setChatId: (id: string) => void; setSessionId: (id: string) => void; ensureChatId: () => Promise; messages: Accessor; setMessages: (fn: (prev: MessageType[]) => MessageType[]) => void; isChatFlowAvailableToStream: Accessor; startInputType: Accessor; leadEmail: Accessor; previews: Accessor; clearPreviews: () => void; uploadedFiles: Accessor<{ file: File; type: string; }[]>; setUploadedFiles: (v: { file: File; type: string; }[]) => void; fullFileUpload: Accessor; uploadsConfig: Accessor; updateLastBotMessage: (updater: (msg: MessageType) => MessageType, persist?: boolean) => void; updateLastMessage: (text: string) => void; updateErrorMessage: (msg: string) => void; updateLastMessageSourceDocuments: (docs: any) => void; updateLastMessageUsedTools: (tools: any[]) => void; updateLastMessageFileAnnotations: (annotations: any) => void; updateLastMessageAgentReasoning: (reasoning: any) => void; updateAgentFlowEvent: (event: string) => void; updateAgentFlowExecutedData: (data: any) => void; updateLastMessageArtifacts: (artifacts: FileUpload[]) => void; updateLastMessageAction: (action: IAction) => void; resetSoundFlag: () => void; /** * Receives CORE's `ttsAudio` frame — the whole clip for this turn, base64. * Attach it; do not play it here. See `onTurnComplete`. */ onTtsAudio: (audio: { data: string; contentType: string; }) => void; /** A new turn is starting. Drop anything held over from the previous one. */ onTurnStart?: () => void; /** The turn has ended and the typewriter has drained. Safe to start speaking. */ onTurnComplete?: () => void; stopAllTTS: () => void; scrollToBottom: () => void; playReceiveSound: () => void; setFollowUpPrompts: (prompts: string[]) => void; /** * Fork the persisted conversation at the user turn being replaced, so the superseded exchange * becomes a previous version instead of staying in the history alongside its replacement. * * Returns false to abandon the operation — the caller has not touched the transcript yet at that * point, so refusing leaves the conversation exactly as it was. Omitted where there is no server * session to fork (preview mounts), in which case the transcript is edited locally only. */ forkConversation?: (userIndex: number, wasGenerating: boolean) => Promise; }; /** * Whether a turn built from this text and these attachments is one Core will accept. * * The rule is not "is there text" — it is the rule the submit path has always enforced, lifted out * of `handleSubmit` so the send button can be disabled by exactly the condition that would * otherwise reject the turn. One predicate, two readers; they cannot drift into disagreeing about * whether a click was going to do anything. * * Attachments alone are a message when the attachment *is* the message — an image to look at, or a * finished voice recording, which is dictation and arrives with no text by design. A document is * not: it goes to the vector store to be asked about, so an empty question leaves nothing to * answer and the turn stays blocked until the user types one. * * Whitespace-only text ('', ' ', '\n') is empty; `trim()` is what decides, so it never reaches * Core as a turn on its own. */ export declare function hasSendableContent(value: string, previews: readonly FilePreview[]): boolean; export declare function createChatSubmit(deps: SubmitDeps): { loading: Accessor; setLoading: import("solid-js").Setter; regenerate: (assistantIndex: number) => Promise; editMessage: (userIndex: number, newContent: string) => Promise; userInput: Accessor; setUserInput: import("solid-js").Setter; /** Does the composer hold a turn worth sending? Drives the send button's enabled state. */ canSubmit: Accessor; handleSubmit: (value: string | object, action?: IAction | undefined | null, humanInput?: any, options?: { /** Conversation context this turn is built on. Set when regenerating. */ history?: PredictionHistoryMessage[] | undefined; } | undefined) => Promise; handleError: (message?: string, preventOverride?: boolean) => void; /** User-initiated stop: cancels the run on CORE, then settles the UI. */ stopGeneration: () => void; }; export {};