@coral-ai/chat 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,38 +1,30 @@
1
- import React, { ReactNode } from 'react';
1
+ import * as React from 'react';
2
+ import React__default, { ReactNode } from 'react';
2
3
  import { ChatFontSize, ChatFontFamily } from '@coral-ai/markdown';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
5
 
4
- /** Agent registration — keyed by agent name; `"default"` is the catch-all. */
5
- type AgentConfig = {
6
- handler: AgentHandler;
7
- };
8
- interface ChatCanvasProps {
9
- /** Name of the agent from the `agents` map. Falls back to `"default"`. */
10
- agent?: string;
11
- /** Named agent handlers keyed by agent name. Use `"default"` for the catch-all. */
12
- agents?: Record<string, AgentConfig>;
13
- /** Stable conversation identifier, passed back in onMessageComplete. */
14
- conversationId?: string;
15
- /** Pre-load conversation history. */
16
- initialMessages?: ChatMessage[];
17
- /** System prompt prepended to every request (not shown in UI). */
18
- systemPrompt?: string;
19
- /** Called when the user sends a message. Return a string, async iterable, or ChatResponse. */
20
- onSendMessage?: (input: string, history: ChatMessage[], attachments: File[]) => ReturnType<AgentHandler>;
21
- /** Called after every completed assistant response. Use to persist messages. */
22
- onMessageComplete?: (messages: ChatMessage[], conversationId?: string) => void;
23
- /** Called on every message-state update (user append, streaming chunk, final). Use for live persistence. */
24
- onMessagesChange?: (messages: ChatMessage[], conversationId?: string) => void;
25
- /** Called when an error occurs during send. */
26
- onError?: (err: unknown) => void;
27
- /** Automatically sends this message once on mount. */
28
- autoSendMessage?: string;
29
- /** Override the thinking indicator text during generation. */
30
- thinkingStatus?: string;
31
- /** Called when the user clicks Stop. Use to abort your fetch/API call. */
32
- onStop?: () => void;
33
- children?: ReactNode;
6
+ type ErrorCategory = "auth_billing" | "rate_limit" | "overloaded" | "bad_input" | "not_found" | "server_error" | "unknown";
7
+ /**
8
+ * A categorized, user-friendly error. This shape is JSON-serializable so it
9
+ * can be stored on a `ChatMessage` and survive persistence / rehydration.
10
+ */
11
+ interface NormalizedChatError {
12
+ category: ErrorCategory;
13
+ message: string;
34
14
  }
35
- declare const ChatCanvas: React.FC<ChatCanvasProps>;
15
+ /**
16
+ * Normalizes raw errors from Anthropic, OpenAI, and Google Gemini into a
17
+ * common category + user-friendly message. Safe to call with any unknown value.
18
+ * Idempotent — passing an already-normalized error returns it unchanged.
19
+ */
20
+ declare function normalizeChatError$1(err: unknown): NormalizedChatError;
21
+ interface ChatErrorProps {
22
+ /** Raw error thrown by your onSendMessage handler — any provider shape works. */
23
+ error: unknown;
24
+ /** Optional retry callback shown as a button. */
25
+ onRetry?: () => void;
26
+ }
27
+ declare const ChatError: React__default.FC<ChatErrorProps>;
36
28
 
37
29
  type ChatMessage = {
38
30
  /** Stable identifier — generated automatically by Chat.Canvas. Used as React key. */
@@ -43,19 +35,93 @@ type ChatMessage = {
43
35
  type?: string;
44
36
  /** Arbitrary data passed to the custom renderer for this message. */
45
37
  metadata?: Record<string, unknown>;
38
+ /**
39
+ * If present, this assistant message carries a structured approval request.
40
+ * Rendered above Chat.Input as a pinned selector while `answer` is undefined;
41
+ * once answered, it stays in history with `answer` populated for replay.
42
+ */
43
+ approval?: ChatApprovalRequest;
44
+ /**
45
+ * If present, this assistant message carries a multi-step task plan.
46
+ * Rendered inline as a collapsible checklist above the message text and
47
+ * persists in history for replay. Updated in place as the agent streams.
48
+ */
49
+ todos?: ChatTodo[];
50
+ /**
51
+ * If present, the turn that produced this assistant message failed. The
52
+ * message renders as an error card (with a Retry button) instead of normal
53
+ * content. Persisted with the conversation so the error survives reloads.
54
+ */
55
+ error?: NormalizedChatError;
56
+ };
57
+ /**
58
+ * One option in a human-in-the-loop approval / multiple-choice prompt.
59
+ * Boolean approval is just two options; N-choice is the same shape with N options.
60
+ */
61
+ type ApprovalOption = {
62
+ id: string;
63
+ label: string;
64
+ description?: string;
65
+ appearance?: "primary" | "subtle" | "outline" | "secondary";
66
+ /**
67
+ * When true, selecting this option does NOT immediately resolve the approval.
68
+ * The chat input is unlocked so the user can type a free-text reply, which is
69
+ * then sent as the resolution payload (carrying this option's id alongside the text).
70
+ * Convention: a final "Other / something else" choice.
71
+ */
72
+ freeform?: boolean;
73
+ };
74
+ /** Structured approval request attached to an assistant message. */
75
+ type ChatApprovalRequest = {
76
+ /** Stable id used to match the eventual response. */
77
+ id: string;
78
+ question: string;
79
+ description?: string;
80
+ options: ApprovalOption[];
81
+ /**
82
+ * Once the user responds, this is populated and the buttons render in their
83
+ * "answered" terminal state. Distinct from `freeform` so freeform replies
84
+ * still record which option was the escape hatch.
85
+ */
86
+ answer?: {
87
+ optionId: string;
88
+ text?: string;
89
+ };
90
+ };
91
+ /**
92
+ * One step in a multi-step task plan. The agent emits the whole list on each
93
+ * update; the convention is exactly one item `in_progress` at a time.
94
+ */
95
+ type ChatTodo = {
96
+ /** Stable id for React key + diffing. Optional; index used as fallback. */
97
+ id?: string;
98
+ /** Imperative title shown when pending/completed, e.g. "Optimize images". */
99
+ content: string;
100
+ /** Present-tense title shown while in_progress, e.g. "Optimizing images". */
101
+ activeForm?: string;
102
+ status: "pending" | "in_progress" | "completed";
46
103
  };
47
104
  /** A single chunk yielded by a streaming onSendMessage. */
48
105
  type ChatChunk = string | {
49
106
  type: "reasoning";
50
107
  content: string;
51
- } | {
52
- type: "agent";
108
+ }
109
+ /** Sets the thinking-indicator word pool while the handler works. */
110
+ | {
111
+ type: "status";
53
112
  name: string;
113
+ } | {
114
+ type: "approval";
115
+ approval: ChatApprovalRequest;
116
+ } | {
117
+ type: "todos";
118
+ todos: ChatTodo[];
54
119
  };
55
120
  type ChatResponse = string | AsyncIterable<ChatChunk> | {
56
121
  content: string;
57
122
  type?: string;
58
123
  metadata?: Record<string, unknown>;
124
+ todos?: ChatTodo[];
59
125
  };
60
126
  /** The typed handler signature engineers implement. */
61
127
  type ChatAttachment = {
@@ -71,11 +137,20 @@ type ChatMessageRendererProps = {
71
137
  /** Sends a message back into the chat as the user. */
72
138
  onReply: (content: string) => void;
73
139
  };
74
- type ChatComponents = Record<string, React.FC<ChatMessageRendererProps>>;
140
+ type ChatComponents = Record<string, React__default.FC<ChatMessageRendererProps>>;
75
141
  interface ChatProps {
76
- agent?: string;
77
- /** Named agent handlers keyed by agent name. Use `"default"` for the catch-all. */
78
- agents?: Record<string, AgentConfig>;
142
+ /**
143
+ * Backend URL. Coral POSTs `{ text, history, attachments }` here on send and
144
+ * adapts the response to the existing chat pipeline. Used only when no
145
+ * `onSendMessage` is provided. Falls back to `useCoralConfig().chat.api`
146
+ * when omitted.
147
+ */
148
+ api?: string;
149
+ /**
150
+ * Which entries to render in the chat input's `+` menu. Falls back to
151
+ * `useCoralConfig().chat.attachments` when omitted.
152
+ */
153
+ allowedAttachments?: Array<"files" | "photos">;
79
154
  conversationId?: string;
80
155
  initialMessages?: ChatMessage[];
81
156
  systemPrompt?: string;
@@ -85,7 +160,6 @@ interface ChatProps {
85
160
  onMessagesChange?: (messages: ChatMessage[], conversationId?: string) => void;
86
161
  onError?: (err: unknown) => void;
87
162
  autoSendMessage?: string;
88
- onFeedback?: (messageIndex: number, rating: "up" | "down") => void;
89
163
  onCopy?: (content: string) => void;
90
164
  onNavigate?: (path: string) => void;
91
165
  components?: ChatComponents;
@@ -96,20 +170,25 @@ interface ChatProps {
96
170
  /** Disables the entire input. */
97
171
  disabled?: boolean;
98
172
  /** Slot rendered at the left of the input toolbar. */
99
- actionsStart?: React.ReactNode;
173
+ actionsStart?: React__default.ReactNode;
100
174
  /** Slot rendered at the right of the input toolbar, just before Send/Stop. */
101
- actionsEnd?: React.ReactNode;
102
- /** Shown 24px above the input while messages is empty. */
103
- emptyState?: React.ReactNode;
175
+ actionsEnd?: React__default.ReactNode;
104
176
  /**
105
- * Clickable suggestion chips rendered below the input while the chat is empty.
106
- * Clicking a chip sends that string as the user's message. Chips fade out once
107
- * any message has been sent.
177
+ * Shown above the input while messages is empty.
178
+ * - Pass a `string` to render it as a `Title3` heading that hugs its content.
179
+ * - Pass any other `ReactNode` to flex-fill the available height (centered).
108
180
  */
109
- promptStarters?: string[];
181
+ welcomeMessage?: string | React__default.ReactNode;
182
+ /**
183
+ * Slot rendered horizontally centered directly under `Chat.Input`, while
184
+ * the chat is empty (no user/assistant messages yet). Typical use is a row
185
+ * of `PromptCard`s as starter prompts. Children can call `useChatContext()`
186
+ * to invoke `sendMessage(text)` on click.
187
+ */
188
+ promptSuggestions?: React__default.ReactNode;
110
189
  /**
111
190
  * Pass `null` to disable the chat's built-in surface motion (input slide to
112
- * bottom + prompt-starter fade). Omit / leave undefined for default motion.
191
+ * bottom on first message). Omit / leave undefined for default motion.
113
192
  */
114
193
  surfaceMotion?: null;
115
194
  /** Font size for assistant message markdown. */
@@ -117,7 +196,46 @@ interface ChatProps {
117
196
  /** Font family for assistant message markdown. */
118
197
  fontFamily?: ChatFontFamily;
119
198
  }
120
- declare const Chat$1: React.FC<ChatProps>;
199
+ declare const Chat$1: React__default.FC<ChatProps>;
200
+
201
+ interface ChatCanvasProps {
202
+ /**
203
+ * Backend URL Coral POSTs to on send. Used as the built-in handler when
204
+ * `onSendMessage` is not set.
205
+ *
206
+ * Coral serializes attachments to base64 and sends
207
+ * `{ text, history, attachments: [{ name, mediaType, data }] }` as JSON. The
208
+ * response can be `{ type?, reply }` (non-streaming) or a Server-Sent-Events
209
+ * stream of `ChatChunk` JSON lines. The optional `type` field is matched
210
+ * against Coral's built-in task vocabulary to pick the thinking-indicator
211
+ * word pool.
212
+ */
213
+ api?: string;
214
+ /** Which entries to render in the chat input's `+` menu (`Chat.AttachButton`). */
215
+ allowedAttachments?: Array<"files" | "photos">;
216
+ /** Stable conversation identifier, passed back in onMessageComplete. */
217
+ conversationId?: string;
218
+ /** Pre-load conversation history. */
219
+ initialMessages?: ChatMessage[];
220
+ /** System prompt prepended to every request (not shown in UI). */
221
+ systemPrompt?: string;
222
+ /** Called when the user sends a message. Return a string, async iterable, or ChatResponse. */
223
+ onSendMessage?: (input: string, history: ChatMessage[], attachments: File[]) => ReturnType<AgentHandler>;
224
+ /** Called after every completed assistant response. Use to persist messages. */
225
+ onMessageComplete?: (messages: ChatMessage[], conversationId?: string) => void;
226
+ /** Called on every message-state update (user append, streaming chunk, final). Use for live persistence. */
227
+ onMessagesChange?: (messages: ChatMessage[], conversationId?: string) => void;
228
+ /** Called when an error occurs during send. */
229
+ onError?: (err: unknown) => void;
230
+ /** Automatically sends this message once on mount. */
231
+ autoSendMessage?: string;
232
+ /** Override the thinking indicator text during generation. */
233
+ thinkingStatus?: string;
234
+ /** Called when the user clicks Stop. Use to abort your fetch/API call. */
235
+ onStop?: () => void;
236
+ children?: ReactNode;
237
+ }
238
+ declare const ChatCanvas: React__default.FC<ChatCanvasProps>;
121
239
 
122
240
  interface ChatMessagesProps {
123
241
  /**
@@ -125,8 +243,6 @@ interface ChatMessagesProps {
125
243
  * Falls back to default markdown rendering if no match.
126
244
  */
127
245
  components?: ChatComponents;
128
- /** Called when the user clicks thumbs-up on an assistant message. */
129
- onFeedback?: (messageIndex: number, rating: "up" | "down") => void;
130
246
  /** Called when the user copies an assistant message. */
131
247
  onCopy?: (content: string) => void;
132
248
  /** Called when the user clicks an internal link (href starts with "/"). */
@@ -136,7 +252,31 @@ interface ChatMessagesProps {
136
252
  /** Font family for assistant message markdown. */
137
253
  fontFamily?: ChatFontFamily;
138
254
  }
139
- declare const ChatMessages: React.FC<ChatMessagesProps>;
255
+ declare const ChatMessages: React__default.FC<ChatMessagesProps>;
256
+
257
+ type ChatAttachType = "files" | "photos";
258
+ interface ChatAttachButtonProps {
259
+ /**
260
+ * Which entries to render in the `+` menu. Order in the array is the order
261
+ * shown in the menu. Defaults to `['files', 'photos']`.
262
+ */
263
+ types?: ChatAttachType[];
264
+ }
265
+ /**
266
+ * The "+" button rendered by default to the left of the chat input toolbar.
267
+ *
268
+ * Opens a Fluent menu with "Add files" / "Add photos" entries (subset
269
+ * controlled by `types`). Selecting one opens the native OS file picker via a
270
+ * hidden `<input type="file">` and pushes the resulting `File[]` through
271
+ * `addAttachments` on `useChatContext`. From there the existing chat pipeline
272
+ * stages the files, renders preview tiles above the textarea, and hands the
273
+ * raw `File[]` to the agent handler on send.
274
+ *
275
+ * Engineers don't typically render this directly — `<Chat allowedAttachments>`
276
+ * does it for them. It's exported so the same component can be dropped into
277
+ * `actionsStart` for fully custom layouts.
278
+ */
279
+ declare const ChatAttachButton: React__default.FC<ChatAttachButtonProps>;
140
280
 
141
281
  interface ChatInputProps {
142
282
  /** Current input value. Optional inside Chat.Canvas — auto-wired from context. */
@@ -148,51 +288,91 @@ interface ChatInputProps {
148
288
  placeholder?: string;
149
289
  /** Disabled state. */
150
290
  disabled?: boolean;
151
- style?: React.CSSProperties;
291
+ style?: React__default.CSSProperties;
152
292
  /** Slot rendered at the left of the bottom toolbar. */
153
- actionsStart?: React.ReactNode;
293
+ actionsStart?: React__default.ReactNode;
154
294
  /** Slot rendered at the right of the bottom toolbar, just before Send/Stop. */
155
- actionsEnd?: React.ReactNode;
295
+ actionsEnd?: React__default.ReactNode;
156
296
  /** Override attachments when used outside a Chat.Canvas. */
157
297
  attachments?: ChatAttachment[];
158
298
  /** Override remove handler when used outside a Chat.Canvas. */
159
299
  removeAttachment?: (id: string) => void;
300
+ /**
301
+ * Renders the `+` (Chat.AttachButton) menu before `actionsStart` content.
302
+ * When omitted, falls back to the value in `useChatContext().allowedAttachments`
303
+ * (set by Chat / Chat.Canvas via the `allowedAttachments` prop). When both are
304
+ * undefined, no `+` button renders — current behavior preserved.
305
+ */
306
+ allowedAttachments?: ChatAttachType[];
160
307
  }
161
- declare const ChatInput: React.ForwardRefExoticComponent<ChatInputProps & React.RefAttributes<HTMLTextAreaElement>>;
308
+ declare const ChatInput: React__default.ForwardRefExoticComponent<ChatInputProps & React__default.RefAttributes<HTMLTextAreaElement>>;
162
309
 
163
- type ErrorCategory = "auth_billing" | "rate_limit" | "overloaded" | "bad_input" | "not_found" | "server_error" | "unknown";
164
- interface NormalizedError {
165
- category: ErrorCategory;
310
+ interface ChatCalloutProps {
166
311
  message: string;
167
- }
168
- /**
169
- * Normalizes raw errors from Anthropic, OpenAI, and Google Gemini into a
170
- * common category + user-friendly message. Safe to call with any unknown value.
171
- */
172
- declare function normalizeChatError$1(err: unknown): NormalizedError;
173
- interface ChatErrorProps {
174
- /** Raw error thrown by your onSendMessage handler — any provider shape works. */
175
- error: unknown;
176
- /** Optional retry callback shown as a button. */
177
312
  onRetry?: () => void;
313
+ className?: string;
178
314
  }
179
- declare const ChatError: React.FC<ChatErrorProps>;
315
+ declare const ChatCallout: React__default.FC<ChatCalloutProps>;
180
316
 
181
- interface ChatCalloutProps {
182
- message: string;
183
- onRetry?: () => void;
317
+ interface ChatApprovalProps {
318
+ /**
319
+ * Override the approval request to render. Defaults to `pendingApproval`
320
+ * from context. Useful for storybook / standalone previews.
321
+ */
322
+ approval?: ChatApprovalRequest | null;
323
+ /**
324
+ * Override the resolver. Defaults to `resolveApproval` from context.
325
+ */
326
+ onSelect?: (optionId: string) => void;
327
+ /** Disable all buttons. Defaults to false. */
328
+ disabled?: boolean;
184
329
  className?: string;
330
+ style?: React__default.CSSProperties;
185
331
  }
186
- declare const ChatCallout: React.FC<ChatCalloutProps>;
332
+ declare const ChatApproval: React__default.FC<ChatApprovalProps>;
333
+
334
+ interface ChatTodoListProps {
335
+ /** The current task plan. The whole list is replaced on each agent update. */
336
+ todos: ChatTodo[];
337
+ className?: string;
338
+ style?: React__default.CSSProperties;
339
+ }
340
+ declare const ChatTodoList: React__default.FC<ChatTodoListProps>;
341
+
342
+ interface ThinkingIndicatorProps {
343
+ isRetrying?: boolean;
344
+ /** Override the cycling animation with a static status string. The animated icon is retained. */
345
+ status?: string;
346
+ /**
347
+ * Override the pool of cycling words. Falls back to the default `thinkingMessages`
348
+ * pool when omitted. Ignored while `isRetrying` is true (retry uses its own pool).
349
+ */
350
+ messages?: string[];
351
+ }
352
+ declare const ThinkingIndicator: React__default.FC<ThinkingIndicatorProps>;
187
353
 
188
354
  type CreateHandlerOptions = {
189
- /** Your backend endpoint. Receives POST { input, history, context }. */
355
+ /** Your backend endpoint. Receives POST { input, history, attachments }. */
190
356
  endpoint: string;
191
357
  /** Additional headers (e.g. Authorization). */
192
358
  headers?: HeadersInit;
193
359
  };
194
360
  /**
195
361
  * Wraps a fetch endpoint into an AgentHandler.
362
+ *
363
+ * On send it POSTs JSON to `endpoint`:
364
+ *
365
+ * ```json
366
+ * {
367
+ * "input": "the user's latest message",
368
+ * "history": [{ "role": "user", "content": "..." }],
369
+ * "attachments": [{ "name": "cat.jpg", "mediaType": "image/jpeg", "data": "<base64>" }]
370
+ * }
371
+ * ```
372
+ *
373
+ * Attached files are base64-encoded with `fileToBase64` (same encoding as
374
+ * Coral's built-in `api` handler); `attachments` is `[]` when none are staged.
375
+ *
196
376
  * The endpoint should stream newline-delimited responses where each line is either:
197
377
  * - A plain text chunk (yielded as a string)
198
378
  * - A JSON-encoded ChatChunk object (e.g. `{"type":"reasoning","content":"..."}`)
@@ -200,53 +380,282 @@ type CreateHandlerOptions = {
200
380
  * - `[DONE]` to signal end-of-stream
201
381
  *
202
382
  * @example
203
- * // main.tsx
204
- * <CoralProvider agents={{ default: { handler: createHandler({ endpoint: '/api/chat' }) } }}>
383
+ * <Chat onSendMessage={createHandler({ endpoint: '/api/chat' })} />
205
384
  */
206
385
  declare function createHandler$1({ endpoint, headers }: CreateHandlerOptions): AgentHandler;
207
386
 
387
+ /**
388
+ * Model-agnostic agent tool-use runner.
389
+ *
390
+ * `createAgentHandler` turns a provider-neutral `callModel` adapter into a
391
+ * Coral `AgentHandler`. Coral owns the tool-use loop, the built-in
392
+ * `update_todos` / `request_approval` tools, and the approval pause/resume
393
+ * bridge. The engineer owns exactly one function — `callModel` — which is the
394
+ * only provider-specific code (Claude, Azure OpenAI, …).
395
+ */
396
+ /** A tool definition. `parameters` is standard JSON Schema for the tool input. */
397
+ type ToolSpec = {
398
+ name: string;
399
+ description: string;
400
+ parameters: Record<string, unknown>;
401
+ };
402
+ /** A tool invocation emitted by the model. */
403
+ type ToolCall = {
404
+ id: string;
405
+ name: string;
406
+ input: unknown;
407
+ };
408
+ /**
409
+ * One message in the neutral agent conversation. `raw` on assistant messages
410
+ * is an opaque passthrough for provider-native content blocks (e.g. Claude
411
+ * `thinking` + `tool_use` blocks that must replay verbatim across the loop) —
412
+ * the runner never inspects it; the adapter prefers it when rebuilding.
413
+ */
414
+ type AgentMessage = {
415
+ role: "user";
416
+ content: string;
417
+ } | {
418
+ role: "assistant";
419
+ content: string;
420
+ toolCalls?: ToolCall[];
421
+ raw?: unknown;
422
+ } | {
423
+ role: "tool";
424
+ toolCallId: string;
425
+ content: string;
426
+ };
427
+ /** What the runner hands the `callModel` adapter for each model call. */
428
+ type ModelRequest = {
429
+ system?: string;
430
+ messages: AgentMessage[];
431
+ tools: ToolSpec[];
432
+ /** Present only on the first model call of a turn. */
433
+ attachments?: File[];
434
+ };
435
+ /** A streamed event from the `callModel` adapter. */
436
+ type ModelStreamEvent = {
437
+ type: "text";
438
+ delta: string;
439
+ } | {
440
+ type: "reasoning";
441
+ delta: string;
442
+ } | {
443
+ type: "tool_call";
444
+ toolCall: ToolCall;
445
+ }
446
+ /** Opaque provider-native representation of the assistant turn, for replay. */
447
+ | {
448
+ type: "raw";
449
+ raw: unknown;
450
+ };
451
+ /** The one function an engineer implements per provider. */
452
+ type CallModel = (req: ModelRequest) => AsyncIterable<ModelStreamEvent>;
453
+ /** Result of executing a custom tool — fed back to the model. */
454
+ type ToolResult = {
455
+ content: string;
456
+ };
457
+ /** A custom tool the engineer registers alongside the built-ins. */
458
+ type AgentTool = {
459
+ spec: ToolSpec;
460
+ execute: (input: unknown) => ToolResult | Promise<ToolResult>;
461
+ };
462
+ /** Paused-agent state, stashed while waiting for an approval answer. */
463
+ type AgentSnapshot = {
464
+ messages: AgentMessage[];
465
+ pendingToolCallId: string;
466
+ };
467
+ /**
468
+ * Where paused agent state is stored between the approval request and the
469
+ * user's answer. Defaults to an in-memory Map — for a server handler that
470
+ * spans processes, supply a Redis/db-backed implementation.
471
+ */
472
+ type AgentSessionStore = {
473
+ get(id: string): AgentSnapshot | undefined | Promise<AgentSnapshot | undefined>;
474
+ set(id: string, snap: AgentSnapshot): void | Promise<void>;
475
+ delete(id: string): void | Promise<void>;
476
+ };
477
+ /** Built-in tool: render / update the inline task checklist. */
478
+ declare const UPDATE_TODOS_TOOL: ToolSpec;
479
+ /** Built-in tool: pause and ask the user to approve / choose before continuing. */
480
+ declare const REQUEST_APPROVAL_TOOL: ToolSpec;
481
+ type CreateAgentHandlerOptions = {
482
+ /** Provider adapter — the only provider-specific code. */
483
+ callModel: CallModel;
484
+ /** Base system prompt. Merged with any `systemPrompt` Coral injects. */
485
+ system?: string;
486
+ /** Custom tools, registered alongside the built-in update_todos / request_approval. */
487
+ tools?: AgentTool[];
488
+ /** Max tool-loop iterations per turn. Default 8. */
489
+ maxSteps?: number;
490
+ /** Where paused-agent state lives. Default: in-memory Map. */
491
+ sessionStore?: AgentSessionStore;
492
+ };
493
+ /**
494
+ * Builds a Coral `AgentHandler` that runs a provider-agnostic tool-use loop.
495
+ *
496
+ * @example
497
+ * <Chat onSendMessage={createAgentHandler({ callModel: claudeCallModel })} />
498
+ */
499
+ declare function createAgentHandler(options: CreateAgentHandlerOptions): AgentHandler;
500
+
208
501
  interface ChatContextValue {
209
502
  sendMessage: (overrideInput?: string) => Promise<void>;
503
+ /**
504
+ * Re-runs the assistant turn following the matching user message.
505
+ * If `messageId` is omitted, regenerates the last assistant message.
506
+ * If `messageId` targets an assistant message, the matching user prompt is replayed.
507
+ * If `messageId` targets a user message, the existing assistant reply for that turn is discarded and re-generated.
508
+ */
509
+ regenerate: (messageId?: string) => Promise<void>;
510
+ /**
511
+ * Replaces the content of an existing user message and re-runs from that point.
512
+ * Truncates everything after the edited message and re-invokes the handler.
513
+ */
514
+ editMessage: (messageId: string, newContent: string) => Promise<void>;
210
515
  scrollToBottom: () => void;
211
516
  /** Pre-binds navigator.clipboard.writeText + the onCopy prop callback. */
212
517
  handleCopy: (content: string) => void;
213
518
  messages: ChatMessage[];
214
- setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;
519
+ setMessages: React__default.Dispatch<React__default.SetStateAction<ChatMessage[]>>;
215
520
  input: string;
216
- setInput: React.Dispatch<React.SetStateAction<string>>;
521
+ setInput: React__default.Dispatch<React__default.SetStateAction<string>>;
217
522
  isTyping: boolean;
218
523
  isRetrying: boolean;
219
524
  isInterrupted: boolean;
220
525
  streamingReasoning: string;
221
- streamingAgentName?: string;
526
+ /** Name from the latest streamed `status` chunk — picks the thinking-indicator word pool. */
527
+ streamingStatus?: string;
222
528
  /** Forwarded from Chat.Canvas thinkingStatus prop so Chat.Messages can read it. */
223
529
  thinkingStatus?: string;
224
- chatError: unknown | null;
225
530
  showScrollButton: boolean;
226
531
  /** Called by Chat.Input to report its measured height so Canvas positions the scroll button. */
227
532
  setInputHeight: (height: number) => void;
228
- messagesContainerRef: React.RefObject<HTMLDivElement | null>;
533
+ messagesContainerRef: React__default.RefObject<HTMLDivElement | null>;
229
534
  /** Called by Chat.Messages on mount/unmount so Canvas can attach scroll listener lazily. */
230
535
  registerMessagesContainer: (el: HTMLDivElement | null) => void;
231
- bottomRef: React.RefObject<HTMLDivElement | null>;
232
- lastUserInputRef: React.MutableRefObject<string>;
233
- hasEverSentRef: React.MutableRefObject<boolean>;
234
- shouldStopRef: React.MutableRefObject<boolean>;
536
+ bottomRef: React__default.RefObject<HTMLDivElement | null>;
537
+ lastUserInputRef: React__default.MutableRefObject<string>;
538
+ hasEverSentRef: React__default.MutableRefObject<boolean>;
539
+ shouldStopRef: React__default.MutableRefObject<boolean>;
235
540
  attachments: ChatAttachment[];
236
541
  addAttachments: (files: File[]) => void;
237
542
  removeAttachment: (id: string) => void;
543
+ /** Declarative list of allowed attachment kinds; renders the `+` menu in Chat.Input when set. */
544
+ allowedAttachments?: Array<"files" | "photos">;
545
+ /** Latest unanswered approval request from the assistant, or null. */
546
+ pendingApproval: ChatApprovalRequest | null;
547
+ /**
548
+ * Set when the user clicks an option marked `freeform: true`. While this is
549
+ * non-null, Chat.Input is unlocked and the next submission resolves the
550
+ * approval (carrying the typed text) instead of being sent as a normal turn.
551
+ */
552
+ awaitingFreeformApproval: {
553
+ approvalId: string;
554
+ optionId: string;
555
+ } | null;
556
+ /**
557
+ * Resolves an approval request. If the option is `freeform`, transitions
558
+ * into the awaiting-freeform state instead of resolving immediately.
559
+ */
560
+ resolveApproval: (approvalId: string, optionId: string) => void;
238
561
  onStop?: () => void;
239
562
  }
240
563
  declare function useChatContext(): ChatContextValue;
241
564
 
565
+ /**
566
+ * Reads a browser `File` and returns its raw base64 payload (without the
567
+ * `data:<mime>;base64,` prefix).
568
+ *
569
+ * Used by Coral's built-in `api` handler to serialize attachments before
570
+ * POSTing them to the engineer's backend, and exported so consumers writing
571
+ * a custom `onSendMessage` can reuse the same encoding.
572
+ */
573
+ declare function fileToBase64(file: File): Promise<string>;
574
+
575
+ /**
576
+ * Built-in task vocabulary for the thinking indicator's per-task word pools.
577
+ *
578
+ * A handler streams a `{ type: "status", name }` chunk to tell the indicator
579
+ * which pool to cycle while it works. `name` is matched against these keys;
580
+ * anything unrecognized falls back to the `text` pool.
581
+ *
582
+ * - `text` — default text-only response.
583
+ * - `photo` — vision / image-reading responses.
584
+ * - `file` — document / PDF responses.
585
+ * - `voice` — audio transcription responses.
586
+ */
587
+ type CoralTaskType = "text" | "photo" | "file" | "voice";
588
+ declare const DEFAULT_TASK_MESSAGES: Record<CoralTaskType, string[]>;
589
+ /**
590
+ * Picks the thinking-indicator word pool for a streamed status name. If `name`
591
+ * matches a built-in task type, its pool is used; otherwise the `text` pool is
592
+ * the fallback. For bespoke copy, use the `thinkingStatus` prop on `<Chat>`.
593
+ */
594
+ declare function resolveTaskMessages(name?: string): string[];
595
+
596
+ /**
597
+ * A stored conversation. Consumers typically persist this shape via the
598
+ * adapter they pass to `ChatThreadsProvider`.
599
+ */
600
+ interface ChatThread {
601
+ id: string;
602
+ title: string;
603
+ messages: ChatMessage[];
604
+ createdAt?: number;
605
+ updatedAt?: number;
606
+ }
607
+ /**
608
+ * Storage seam. Implement this against your backend (REST, IndexedDB,
609
+ * Firestore, etc.). The provider handles in-memory state so UI updates are
610
+ * synchronous; your adapter only needs to persist.
611
+ */
612
+ interface ChatThreadsAdapter {
613
+ list(): Promise<ChatThread[]> | ChatThread[];
614
+ get?(id: string): Promise<ChatThread | undefined> | ChatThread | undefined;
615
+ create(initial?: Partial<ChatThread>): Promise<ChatThread> | ChatThread;
616
+ save(thread: ChatThread): Promise<void> | void;
617
+ rename(id: string, newTitle: string): Promise<void> | void;
618
+ remove(id: string): Promise<void> | void;
619
+ }
620
+ interface ChatThreadsContextValue {
621
+ threads: ChatThread[];
622
+ activeThreadId?: string;
623
+ activeThread?: ChatThread;
624
+ setActiveThreadId: (id: string | undefined) => void;
625
+ createThread: (initial?: Partial<ChatThread>) => Promise<ChatThread>;
626
+ renameThread: (id: string, newTitle: string) => Promise<void>;
627
+ removeThread: (id: string) => Promise<void>;
628
+ updateThreadMessages: (id: string, messages: ChatMessage[]) => void;
629
+ isLoading: boolean;
630
+ }
631
+ declare function useChatThreads(): ChatThreadsContextValue;
632
+ /** Default adapter — holds threads in a module-less closure. Use for demos or unit tests. */
633
+ declare function createMemoryThreadsAdapter(initialThreads?: ChatThread[]): ChatThreadsAdapter;
634
+ interface ChatThreadsProviderProps {
635
+ /** Storage adapter. Defaults to an in-memory adapter if omitted. */
636
+ adapter?: ChatThreadsAdapter;
637
+ /** Controlled active thread id. */
638
+ activeThreadId?: string;
639
+ /** Uncontrolled initial active thread id. */
640
+ defaultActiveThreadId?: string;
641
+ /** Fired when the active thread changes (both controlled and uncontrolled). */
642
+ onActiveThreadChange?: (id: string | undefined) => void;
643
+ children?: React.ReactNode;
644
+ }
645
+ declare function ChatThreadsProvider({ adapter: providedAdapter, activeThreadId: controlledId, defaultActiveThreadId, onActiveThreadChange, children, }: ChatThreadsProviderProps): react_jsx_runtime.JSX.Element;
646
+
242
647
  declare const Chat: typeof Chat$1 & {
243
648
  Canvas: typeof ChatCanvas;
244
649
  Messages: typeof ChatMessages;
245
650
  Input: typeof ChatInput;
246
651
  Error: typeof ChatError;
247
652
  Callout: typeof ChatCallout;
653
+ Approval: typeof ChatApproval;
654
+ TodoList: typeof ChatTodoList;
655
+ AttachButton: typeof ChatAttachButton;
656
+ ThinkingIndicator: typeof ThinkingIndicator;
248
657
  };
249
658
  declare const normalizeChatError: typeof normalizeChatError$1;
250
659
  declare const createHandler: typeof createHandler$1;
251
660
 
252
- export { type AgentConfig, type AgentHandler, Chat, type ChatAttachment, type ChatCanvasProps, type ChatChunk, type ChatComponents, type ChatErrorProps, type ChatInputProps, type ChatMessage, type ChatMessageRendererProps, type ChatMessagesProps, type ChatResponse, createHandler, normalizeChatError, useChatContext };
661
+ export { type AgentHandler, type AgentMessage, type AgentSessionStore, type AgentSnapshot, type AgentTool, type ApprovalOption, type CallModel, Chat, type ChatApprovalProps, type ChatApprovalRequest, type ChatAttachButtonProps, type ChatAttachType, type ChatAttachment, type ChatCanvasProps, type ChatChunk, type ChatComponents, type ChatErrorProps, type ChatInputProps, type ChatMessage, type ChatMessageRendererProps, type ChatMessagesProps, type ChatResponse, type ChatThread, type ChatThreadsAdapter, type ChatThreadsContextValue, ChatThreadsProvider, type ChatThreadsProviderProps, type ChatTodo, type ChatTodoListProps, type CoralTaskType, type CreateAgentHandlerOptions, DEFAULT_TASK_MESSAGES, type ErrorCategory, type ModelRequest, type ModelStreamEvent, type NormalizedChatError, REQUEST_APPROVAL_TOOL, type ToolCall, type ToolResult, type ToolSpec, UPDATE_TODOS_TOOL, createAgentHandler, createHandler, createMemoryThreadsAdapter, fileToBase64, normalizeChatError, resolveTaskMessages, useChatContext, useChatThreads };