import type { ComposerImageModelMenu } from "@agent-native/toolkit/composer/TiptapComposer"; import type { ChatModelAdapter, ExportedMessageRepository } from "@assistant-ui/react"; import React from "react"; import type { ReasoningEffort } from "../shared/reasoning-effort.js"; import { type AgentChatSurfaceKind } from "./agent-chat-adapter.js"; import { type AgentChatContextItem } from "./agent-chat.js"; import { type QueuedAttachment } from "./chat/attachment-adapters.js"; import { assistantMessageRunId } from "./chat/message-components.js"; import { type BuilderSetupCardLayout } from "./chat/run-recovery.js"; import { type AgentChatRuntime } from "./chat/runtime.js"; import { type AgentComposerLayoutVariant, type ComposerSubmitIntent, type Reference } from "./composer/index.js"; import { type AgentDynamicSuggestionsOption } from "./dynamic-suggestions.js"; import { type ContentPart } from "./sse-event-processor.js"; import type { ChatThreadScope, ChatThreadSnapshot } from "./use-chat-threads.js"; export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js"; export { displayableUserMessageText } from "./chat/message-components.js"; export type AgentRequestMode = "act" | "plan"; export type AgentRecoveryAction = "continue" | "retry"; export interface AssistantChatSendOptions { trackInRunsTray?: boolean; requestMode?: AgentRequestMode; /** Correlates with `AGENT_CHAT_SUBMIT_RESULT_EVENT` — see agent-chat.ts. */ submitMessageId?: string; } /** * Decide whether a reconnect should give up with "no progress". The signal is * *time since the last streamed event*, NOT total reconnect duration: a healthy * long-running tool (e.g. image generation, which emits `activity` heartbeats * every few seconds) makes continuous progress and must never be aborted just * for running longer than the threshold. Only genuine silence for the full * stuck threshold counts as stuck. Pure + exported so the decision is unit * testable without driving the whole reconnect lifecycle. */ export declare function reconnectProgressTimedOut(args: { lastProgressAt: number; now: number; thresholdMs?: number; }): boolean; export { assistantMessageRunId }; export declare function shouldAcceptRunError(args: { errorRunId?: string; activeRunId?: string; latestAssistantRunId?: string; }): boolean; /** * Keep the assistant-ui repository race recovery installed across runtime-core * replacements. The public ThreadRuntime object stays stable when its binding * swaps to another local runtime (thread activation, reconnect, history load), * but each core owns a different MessageRepository instance. */ export declare function installAssistantUiMessageRepositoryRecovery(threadRuntime: unknown): () => void; export declare function settleInterruptedAssistantToolCallsInRepo(repo: T): { repo: T; changed: boolean; }; export declare function waitForThreadRunToClear(apiUrl: string, threadId?: string): Promise; export declare function reconnectActivityFallbackContent(toolName: string | null | undefined): ContentPart[]; export declare function dedupeReconnectContentAgainstMessages(content: ContentPart[], messages: readonly unknown[], options?: { suppressToolRepeats?: boolean; trimTailTextOverlap?: boolean; }): ContentPart[]; export declare function latestNonRecoveryUserMessageText(messages: readonly unknown[]): string; export declare function resolveAssistantChatSubmitIntent({ isRunning, requestedIntent, }: { isRunning: boolean; requestedIntent?: ComposerSubmitIntent; }): ComposerSubmitIntent; export declare function resolveAssistantChatRunningState({ forceStopped, isRuntimeRunning, isReconnecting, optimisticRunning, isAutoResuming, hasActiveServerRun, }: { forceStopped: boolean; isRuntimeRunning: boolean; isReconnecting: boolean; optimisticRunning: boolean; isAutoResuming: boolean; hasActiveServerRun?: boolean; }): { isRunning: boolean; showRunningInUI: boolean; }; export declare function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, }: { runningActivityLabel: string | null | undefined; isAutoResuming: boolean; isReconnecting: boolean; hasReconnectContent: boolean; }): string; export declare function shouldShowGlobalRunningStatus({ showRunningInUI, runningActivityLabel, runningActivityTool, latestMessage, reconnectContent, }: { showRunningInUI: boolean; runningActivityLabel: string | null | undefined; runningActivityTool?: string | null; latestMessage: unknown; reconnectContent: readonly ContentPart[]; }): boolean; export declare function assistantChatAutoscrollStatusKey({ showGlobalRunningStatus, runningStatusLabel, }: { showGlobalRunningStatus: boolean; runningStatusLabel: string; }): string; type QueuedMessage = { id: string; text: string; images?: string[]; attachments?: QueuedAttachment[]; references?: Reference[]; requestMode?: AgentRequestMode; recoveryAction?: AgentRecoveryAction; trackInRunsTray?: boolean; hideUserMessage?: boolean; }; export declare function queuedMessageImageSources(message: Pick): string[]; export interface AssistantChatHandle { /** Programmatically send a message into this chat */ sendMessage(text: string, images?: string[], options?: AssistantChatSendOptions): void; /** Programmatically prefill the composer without submitting. */ prefillMessage(text: string): void; /** * Add or replace keyed context for the next composer submission. * Focuses the composer by default; pass `{ focus: false }` for passive * context mirroring (e.g. canvas selection) that must not steal focus. */ setComposerContextItem(item: AgentChatContextItem, options?: { focus?: boolean; }): void; /** Remove a keyed context item from the composer. */ removeComposerContextItem(key: string): void; /** Clear all staged context items from the composer. */ clearComposerContextItems(): void; /** Programmatically send a recovery prompt without replacing the original request. */ sendRecoveryMessage(text: string, recoveryAction: AgentRecoveryAction, images?: string[]): void; /** Queue a message to send after the current run finishes */ queueMessage(text: string, images?: string[]): void; /** Whether the chat is currently running */ isRunning(): boolean; /** * Whether the current run has a tool call or sub-agent (A2A) call that * hasn't returned a result yet. Mirrors the server's in-flight-work * tracking client-side so callers (e.g. `RunStuckBanner`) can tell a * genuinely stalled run apart from one still waiting on a long-running * tool/A2A call before treating "no progress" as safe to abort. */ hasInFlightWork(): boolean; /** Focus the composer input */ focusComposer(): void; /** Export the currently visible client-side thread for operations like fork. */ exportThreadSnapshot(): ChatThreadSnapshot | null; } export type AssistantChatThreadFooterSlot = React.ReactNode | ((context: { threadId: string | null; tabId: string | null; }) => React.ReactNode); export interface AssistantChatAdapterContext { apiUrl: string; tabId?: string; threadId?: string; modelRef: { current: string | undefined; }; engineRef: { current: string | undefined; }; effortRef: { current: ReasoningEffort | undefined; }; execModeRef: { current: "build" | "plan" | undefined; }; browserTabId?: string; scopeRef: { current: ChatThreadScope | null | undefined; }; surface: AgentChatSurfaceKind; } export interface AssistantChatProps { /** API endpoint URL. Default: "/_agent-native/agent-chat" */ apiUrl?: string; /** Stable tab identifier passed to the adapter for event correlation */ tabId?: string; /** Stable browser tab id used for tab-scoped app-state context. */ browserTabId?: string; /** Thread ID for SQL-backed persistence. When set, messages are loaded from and saved to the server. */ threadId?: string; /** Resource scope to include with chat requests for server-side context. */ contextScope?: ChatThreadScope | null; /** Whether this chat owns the active visible composer context snapshot. */ isActiveComposer?: boolean; /** * Identifies which surface hosts this chat. Defaults to "app", which keeps * dev filesystem/bash code-editing tools out of in-product sidebars. */ agentChatSurface?: AgentChatSurfaceKind; /** Placeholder text for empty state */ emptyStateText?: string; /** Suggestion prompts shown when no messages */ suggestions?: string[]; /** Context-aware suggestions merged with `suggestions`. Enabled by default. */ dynamicSuggestions?: AgentDynamicSuggestionsOption; /** Optional content rendered at the bottom of the scrollable thread, after messages. */ threadFooterSlot?: AssistantChatThreadFooterSlot; /** Optional content rendered in the empty state, above the suggestion buttons. */ emptyStateAddon?: React.ReactNode; /** Whether to show the header bar. Default: true */ showHeader?: boolean; /** CSS class for the outer container */ className?: string; /** Callback when user clicks "Use CLI" button */ onSwitchToCli?: () => void; /** Callback when message count changes */ onMessageCountChange?: (count: number) => void; /** Callback to save thread data to the server (provided by useChatThreads) */ onSaveThread?: (threadId: string, data: { threadData: string; title: string; preview: string; messageCount: number; }) => void; /** Callback to generate a title from the first user message */ onGenerateTitle?: (threadId: string, message: string) => void; /** Optional content rendered just above the composer input */ composerSlot?: React.ReactNode; /** * Called with the active composer's current plain text as it changes. * Host apps can use this to render contextual, non-destructive affordances * beside the shared composer without replacing the composer stack. */ onComposerTextChange?: (text: string) => void; /** Class applied to the shared composer area for host-specific sizing/skin. */ composerAreaClassName?: string; /** Placeholder for the shared composer in its normal idle state. */ composerPlaceholder?: string; /** Sidebar uses a compact setup CTA above the composer; page chat keeps the default below-composer CTA. */ missingApiKeySetupLayout?: BuilderSetupCardLayout; /** Visual density for the shared composer shell. */ composerLayoutVariant?: AgentComposerLayoutVariant; /** Center the composer on a fresh empty chat instead of pinning it low. */ centerComposerWhenEmpty?: boolean; /** Hide the default empty-state icon/text/suggestions for custom start screens. */ emptyStateDisplay?: "default" | "hidden"; /** Optional content rendered inside the composer toolbar after the attach button. */ composerToolbarSlot?: React.ReactNode; /** Optional action rendered beside the voice/send controls. */ composerExtraActionButton?: React.ReactNode; /** Disable the composer for capability-gated surfaces while still showing history. */ composerDisabled?: boolean; /** Placeholder to show while the composer is disabled by the host surface. */ composerDisabledPlaceholder?: string; /** When true, skip the restore skeleton (used for freshly created threads with no messages) */ isNewThread?: boolean; /** Called when a slash command (e.g. /clear, /help) is executed */ onSlashCommand?: (command: string) => void; /** Current execution mode (build/plan) */ execMode?: "build" | "plan"; /** Callback to change execution mode */ onExecModeChange?: (mode: "build" | "plan") => void; /** Disable Plan mode while leaving Act mode available. */ planModeDisabled?: boolean; /** Explanation shown next to the disabled Plan option. */ planModeDisabledReason?: string; /** Selected model override for this conversation (undefined = use server default) */ selectedModel?: string; /** Default model from server config (shown in picker when no override is set) */ defaultModel?: string; /** Selected engine override for this conversation */ selectedEngine?: string; /** Selected reasoning effort override for this conversation */ selectedEffort?: ReasoningEffort; /** Available engine/model list for the model picker */ availableModels?: Array<{ engine: string; label: string; models: string[]; configured: boolean; }>; /** Whether the model list is still being resolved. */ modelListLoading?: boolean; /** Callback when user picks a model from the picker */ onModelChange?: (model: string, engine: string) => void; /** Callback when user picks a reasoning effort from the picker */ onEffortChange?: (effort: ReasoningEffort) => void; /** * Optional secondary model menu (e.g. an image-generation model) shown inside * the composer's model picker. Opt-in; chat-only apps omit it. */ imageModelMenu?: ComposerImageModelMenu; /** Callback when user clicks "Fork Chat" in the message actions menu */ onForkChat?: () => void | boolean | Promise; /** Override Builder/provider connect routing for embedded hosts. */ onConnectProvider?: () => void; /** Route local runtime setup through the host's native bridge. */ onConnectLocalRuntime?: (engine: string) => void; /** * Controls the shared composer + menu. Sidebar keeps the full menu by default; * hosts without the sidebar provider stack can use upload-only. */ plusMenuMode?: "full" | "upload-only" | "hidden"; /** * Enable framework provider/env status checks. Embedded hosts that provide * model/provider state through another transport can disable these probes. */ providerStatusChecksEnabled?: boolean; /** * Advanced host override for non-HTTP transports. Defaults to the production * sidebar SSE adapter when omitted. */ createAdapter?: (context: AssistantChatAdapterContext) => ChatModelAdapter; /** * Bring-your-own agent runtime. When supplied, AssistantChat keeps the * standard composer/transcript/tool rendering shell but sends turns through * this runtime instead of the built-in Agent-Native SSE endpoint. If * `createAdapter` is also supplied, the adapter override takes precedence. */ runtime?: AgentChatRuntime; /** * Explicitly recreate an injected adapter when the host transport identity * changes. Omit for the production sidebar so parent rerenders do not reset * active chats. */ adapterReloadKey?: unknown; /** * Advanced host override for thread replay. Defaults to SQL thread fetch when * `threadId` is set, or sessionStorage for legacy tab chats. */ loadHistoryRepository?: () => Promise; /** Re-run `loadHistoryRepository` when the host's external transcript changes. */ historyReloadKey?: string | number | null; /** Smooth the last assistant message while an external transcript is updating. */ externalStreaming?: boolean; /** * Optional host hooks for the inline `needsApproval` affordance beyond the * built-in Approve. Additive: omit entirely to keep today's default * behavior (Deny is local-only, no "Always allow" button). Code sessions * pass these through to the same `host.controlRun` commands their * standalone approval banner already uses (see CodeAgentsApp). */ approvalActions?: { onDeny?: (approvalKey: string) => void; onAlwaysAllow?: (approvalKey: string) => void; }; } export declare const CHAT_STORAGE_PREFIX = "agent-chat:"; /** Remove persisted chat for a given tabId (or "default"). */ export declare function clearChatStorage(tabId?: string): void; import { extractThreadMeta } from "../agent/thread-data-builder.js"; export { extractThreadMeta }; /** * Owns the "Resuming…" status shown during the adapter's auto-continuation * window — the gap between the end of one serverless chunk and the POST for * the next. Set true the moment the adapter dispatches * `agent-chat:auto-continue`. Cleared here as soon as the successor chunk * produces real output (`agent-chat:stream-progress` — dispatched by the SSE * processor for non-empty text/reasoning deltas) or the run is force-stopped; * a 30s failsafe timer covers any case neither fires. The caller clears it * via the returned `clearAutoResume` for other real-progress signals (tool * activity, an accepted run error, an explicit stop) that live outside this * hook. * * Deliberately NOT cleared by `agent-chat:activity-clear` or merely-idle * `isRunning`: both also fire for old-chunk `tool_done` replays / server * retries, and clearing on those would re-expose terminal message controls * during the exact gap this state exists to cover. */ export declare function useAutoResumeStatus(tabId: string | undefined, forceStopped: boolean): { isAutoResuming: boolean; clearAutoResume: () => void; }; export declare const AssistantChat: React.ForwardRefExoticComponent>; //# sourceMappingURL=AssistantChat.d.ts.map