@x12i/ai 1.0.0 → 1.1.0

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.
@@ -0,0 +1,482 @@
1
+ /**
2
+ * The neutral message shape (spec section 6). Components read typed parts and
3
+ * never import a provider SDK; adapters convert provider formats into this
4
+ * shape.
5
+ *
6
+ * Everything here is plain, JSON-serializable data, so a thread can be stored
7
+ * through `ThreadStore`, sent over the wire or kept in a database.
8
+ */
9
+ type MessageRole = "user" | "assistant" | "system" | "tool";
10
+ interface TextPart {
11
+ type: "text";
12
+ text: string;
13
+ }
14
+ interface ReasoningPart {
15
+ type: "reasoning";
16
+ text: string;
17
+ /** How long the model thought, for the "Thought for 4s" line. */
18
+ durationMs?: number;
19
+ /** Epoch milliseconds when this reasoning began. Set by the thread reducer. */
20
+ startedAt?: number;
21
+ }
22
+ /**
23
+ * - `queued`: the call is planned but has not started.
24
+ * - `running`: executing.
25
+ * - `needs-approval`: waiting for a human decision. Never auto-approved.
26
+ * - `approved`: a human approved; execution has not reported back yet.
27
+ * - `denied`: a human refused. Terminal.
28
+ * - `succeeded` / `failed`: terminal.
29
+ */
30
+ type ToolState = "queued" | "running" | "needs-approval" | "approved" | "denied" | "succeeded" | "failed";
31
+ /** Details of a human-in-the-loop approval on a tool call. */
32
+ interface ToolApproval {
33
+ /** Why the call needs approval, shown above the exact action. */
34
+ prompt?: string;
35
+ /** The reason the human gave when approving or denying. */
36
+ reason?: string;
37
+ }
38
+ interface ToolPart {
39
+ type: "tool";
40
+ id: string;
41
+ name: string;
42
+ state: ToolState;
43
+ input?: unknown;
44
+ output?: unknown;
45
+ /** A human-readable cause when `state` is `failed`. */
46
+ error?: string;
47
+ /** A machine-readable cause, such as `aborted` when the user stopped the run. */
48
+ errorCode?: string;
49
+ durationMs?: number;
50
+ /** Epoch milliseconds when the call started. Set by the thread reducer. */
51
+ startedAt?: number;
52
+ approval?: ToolApproval;
53
+ }
54
+ interface SourcePart {
55
+ type: "source";
56
+ id: string;
57
+ title: string;
58
+ url: string;
59
+ /** Shown in the source list next to the title and domain. */
60
+ snippet?: string;
61
+ }
62
+ interface FilePart {
63
+ type: "file";
64
+ /** Stable id, used by attachment chips. Optional for stored threads. */
65
+ id?: string;
66
+ name: string;
67
+ mediaType: string;
68
+ url: string;
69
+ size?: number;
70
+ }
71
+ /** Structured data a component can render, for example an artifact or a chart spec. */
72
+ interface DataPart {
73
+ type: "data";
74
+ /** Identifies the shape of `data`, such as `chart` or `diagram`. */
75
+ kind: string;
76
+ data: unknown;
77
+ }
78
+ type MessagePart = TextPart | ReasoningPart | ToolPart | SourcePart | FilePart | DataPart;
79
+ type MessagePartType = MessagePart["type"];
80
+ type MessageStatus = "streaming" | "complete" | "error" | "stopped";
81
+ /** Token counts for a response. Providers differ, so every field is optional. */
82
+ interface Usage {
83
+ inputTokens?: number;
84
+ outputTokens?: number;
85
+ totalTokens?: number;
86
+ reasoningTokens?: number;
87
+ cachedInputTokens?: number;
88
+ }
89
+ type FinishReason = "stop" | "length" | "tool-calls" | "content-filter" | "error" | "other";
90
+ /**
91
+ * - `network`: the request never completed (offline, DNS, dropped stream).
92
+ * - `rate-limit`: the provider asked the client to slow down (HTTP 429).
93
+ * - `moderation`: the provider refused the content.
94
+ * - `auth`: missing or rejected credentials.
95
+ * - `unknown`: anything else.
96
+ */
97
+ type ErrorKind = "network" | "rate-limit" | "moderation" | "auth" | "unknown";
98
+ interface ThreadError {
99
+ /** A human-readable cause. Shown as detail under the kind's title. */
100
+ message: string;
101
+ kind?: ErrorKind;
102
+ /** The provider's error code, if any. */
103
+ code?: string;
104
+ /** `false` hides the retry action. Defaults to true. */
105
+ retryable?: boolean;
106
+ }
107
+ interface Message {
108
+ id: string;
109
+ role: MessageRole;
110
+ parts: MessagePart[];
111
+ status?: MessageStatus;
112
+ createdAt?: string;
113
+ /** The message this one replaces or follows, for branches between versions. */
114
+ parentId?: string;
115
+ metadata?: Record<string, unknown>;
116
+ /** Token usage reported when the response finished. */
117
+ usage?: Usage;
118
+ finishReason?: FinishReason;
119
+ /** Set when `status` is `error`. */
120
+ error?: ThreadError;
121
+ }
122
+ interface Thread {
123
+ id: string;
124
+ title?: string;
125
+ messages: Message[];
126
+ updatedAt?: string;
127
+ }
128
+ /** Converts a provider's message format into the neutral shape. */
129
+ type MessageAdapter<Input> = (input: Input) => Message;
130
+ /** Converts a provider's whole message list into neutral messages. */
131
+ type MessagesAdapter<Input> = (input: readonly Input[]) => Message[];
132
+ /**
133
+ * The thread component is stateless. Storage and history belong to the app,
134
+ * through this small interface.
135
+ */
136
+ interface ThreadStore {
137
+ load(threadId: string): Promise<Thread | null>;
138
+ save(thread: Thread): Promise<void>;
139
+ list?(): Promise<Array<Pick<Thread, "id" | "title" | "updatedAt">>>;
140
+ delete?(threadId: string): Promise<void>;
141
+ }
142
+
143
+ /**
144
+ * Thread state and its reducer.
145
+ *
146
+ * `threadReducer(state, action)` is a pure function: it never mutates `state`
147
+ * or an action, it returns the same `state` object when an action changes
148
+ * nothing, and it never reads a clock or generates a random id. Time comes in
149
+ * through the optional `at` (epoch milliseconds) on actions, and ids come in
150
+ * through the actions or, when a provider sends none, a deterministic counter.
151
+ * That makes it safe in React, in tests and in server code.
152
+ */
153
+ /**
154
+ * - `idle`: nothing in flight.
155
+ * - `submitted`: a request was sent and nothing has come back yet.
156
+ * - `streaming`: an assistant message is being written.
157
+ * - `error`: the last request failed; `error` has the cause.
158
+ */
159
+ type ThreadStatus = "idle" | "submitted" | "streaming" | "error";
160
+ /**
161
+ * Versions of one place in the thread, made by editing a message with
162
+ * `mode: "branch"`. `alternatives` holds the versions that are not showing,
163
+ * each one a whole tail of the thread; the showing version is `messages`.
164
+ */
165
+ interface BranchPoint {
166
+ /** The message the versions follow, or `""` when the versions start the thread. */
167
+ parentId: string;
168
+ /** The versions that are not showing. */
169
+ alternatives: Message[][];
170
+ /** Where the showing version sits among all versions, from 0. */
171
+ activeIndex: number;
172
+ }
173
+ interface ThreadState {
174
+ /** The messages on the showing path, oldest first. */
175
+ messages: Message[];
176
+ status: ThreadStatus;
177
+ error: ThreadError | null;
178
+ /** The assistant message being written, or null. */
179
+ activeMessageId: string | null;
180
+ /** Branch points by `parentId`. */
181
+ branches: Record<string, BranchPoint>;
182
+ }
183
+ interface BranchInfo {
184
+ parentId: string;
185
+ /** The showing version, from 0. */
186
+ index: number;
187
+ /** How many versions exist, including the showing one. */
188
+ count: number;
189
+ }
190
+ interface ToolCallPatch {
191
+ name?: string;
192
+ input?: unknown;
193
+ output?: unknown;
194
+ state?: ToolState;
195
+ error?: string;
196
+ errorCode?: string;
197
+ durationMs?: number;
198
+ approval?: ToolApproval;
199
+ }
200
+ type ThreadAction =
201
+ /** Replaces the whole thread, for example after loading from a store. */
202
+ {
203
+ type: "set-messages";
204
+ messages: Message[];
205
+ }
206
+ /** Adds a ready-made message (system notes, restored history). A duplicate id is ignored. */
207
+ | {
208
+ type: "add-message";
209
+ message: Message;
210
+ } | {
211
+ type: "add-user-message";
212
+ id: string;
213
+ text?: string;
214
+ attachments?: FilePart[];
215
+ parts?: MessagePart[];
216
+ createdAt?: string;
217
+ /** Defaults to `submitted`: the user message starts a request. */
218
+ status?: ThreadStatus;
219
+ }
220
+ /** Starts an assistant message. With an existing id it reopens that message. */
221
+ | {
222
+ type: "begin-assistant-message";
223
+ id?: string;
224
+ createdAt?: string;
225
+ metadata?: Record<string, unknown>;
226
+ } | {
227
+ type: "append-text";
228
+ delta: string;
229
+ messageId?: string;
230
+ at?: number;
231
+ } | {
232
+ type: "append-reasoning";
233
+ delta: string;
234
+ messageId?: string;
235
+ at?: number;
236
+ } | {
237
+ type: "end-reasoning";
238
+ messageId?: string;
239
+ durationMs?: number;
240
+ at?: number;
241
+ } | {
242
+ type: "start-tool-call";
243
+ callId: string;
244
+ name: string;
245
+ input?: unknown;
246
+ state?: ToolState;
247
+ requiresApproval?: boolean;
248
+ approvalPrompt?: string;
249
+ messageId?: string;
250
+ at?: number;
251
+ } | {
252
+ type: "update-tool-call";
253
+ callId: string;
254
+ patch: ToolCallPatch;
255
+ messageId?: string;
256
+ at?: number;
257
+ } | {
258
+ type: "finish-tool-call";
259
+ callId: string;
260
+ name?: string;
261
+ output?: unknown;
262
+ error?: string;
263
+ errorCode?: string;
264
+ durationMs?: number;
265
+ messageId?: string;
266
+ at?: number;
267
+ }
268
+ /** A human decision on a tool call that needs approval. */
269
+ | {
270
+ type: "tool-decision";
271
+ callId: string;
272
+ decision: "approved" | "denied";
273
+ reason?: string;
274
+ } | {
275
+ type: "add-source";
276
+ source: Omit<SourcePart, "type">;
277
+ messageId?: string;
278
+ } | {
279
+ type: "add-part";
280
+ part: MessagePart;
281
+ messageId?: string;
282
+ } | {
283
+ type: "attach-files";
284
+ files: FilePart[];
285
+ messageId?: string;
286
+ } | {
287
+ type: "finish";
288
+ messageId?: string;
289
+ usage?: Usage;
290
+ finishReason?: FinishReason;
291
+ at?: number;
292
+ } | {
293
+ type: "error";
294
+ error: ThreadError | string;
295
+ messageId?: string;
296
+ at?: number;
297
+ }
298
+ /** The user stopped the response. Content so far is kept. */
299
+ | {
300
+ type: "abort";
301
+ messageId?: string;
302
+ at?: number;
303
+ }
304
+ /** Drops the assistant reply to the last user message and gets ready to ask again. */
305
+ | {
306
+ type: "regenerate";
307
+ } | {
308
+ type: "edit-message";
309
+ messageId: string;
310
+ text?: string;
311
+ parts?: MessagePart[];
312
+ /**
313
+ * For user messages: `truncate` (default) replaces the message and drops
314
+ * everything after it; `branch` keeps the old version reachable through
315
+ * `switch-branch`. Other roles are edited in place.
316
+ */
317
+ mode?: "truncate" | "branch";
318
+ /** Id of the new version in `branch` mode. Defaults to `<id>~<n>`. */
319
+ newId?: string;
320
+ createdAt?: string;
321
+ } | {
322
+ type: "switch-branch";
323
+ parentId: string;
324
+ index: number;
325
+ } | {
326
+ type: "dismiss-error";
327
+ } | {
328
+ type: "clear";
329
+ } | {
330
+ type: "batch";
331
+ actions: ThreadAction[];
332
+ };
333
+ declare function isTerminalToolState(state: ToolState): boolean;
334
+ declare function isBusy(state: Pick<ThreadState, "status">): boolean;
335
+ declare function createThreadState(messages?: readonly Message[]): ThreadState;
336
+ declare function threadReducer(state: ThreadState, action: ThreadAction): ThreadState;
337
+ /** The branch navigator data for a message, if it is where versions fork. */
338
+ declare function getBranchInfo(state: ThreadState, messageId: string): BranchInfo | undefined;
339
+ /** All branch navigator data for the showing messages, by message id. */
340
+ declare function getBranchInfoMap(state: ThreadState): Record<string, BranchInfo>;
341
+ /** The last user message, which is what "regenerate" answers again. */
342
+ declare function lastUserMessage(state: Pick<ThreadState, "messages">): Message | undefined;
343
+
344
+ /**
345
+ * Stream events: the one shape every provider is adapted to.
346
+ *
347
+ * A provider adapter is an async generator that yields these events. The
348
+ * thread reducer (`applyStreamEvent`) turns each one into a state change, so
349
+ * the UI never sees a provider SDK. Unknown event types are ignored, so an
350
+ * adapter can pass extra events through without breaking older UIs.
351
+ */
352
+ interface StartEvent {
353
+ type: "start";
354
+ /** The assistant message id. Omit it and the hook or reducer makes one. */
355
+ messageId?: string;
356
+ createdAt?: string;
357
+ metadata?: Record<string, unknown>;
358
+ }
359
+ interface TextDeltaEvent {
360
+ type: "text-delta";
361
+ delta: string;
362
+ messageId?: string;
363
+ }
364
+ interface ReasoningDeltaEvent {
365
+ type: "reasoning-delta";
366
+ delta: string;
367
+ messageId?: string;
368
+ }
369
+ /** Ends the open reasoning block. Optional: the first text, tool call or finish also ends it. */
370
+ interface ReasoningEndEvent {
371
+ type: "reasoning-end";
372
+ durationMs?: number;
373
+ messageId?: string;
374
+ }
375
+ interface ToolCallEvent {
376
+ type: "tool-call";
377
+ callId: string;
378
+ name: string;
379
+ input?: unknown;
380
+ /** Set when a human has to approve before the tool runs. */
381
+ requiresApproval?: boolean;
382
+ /** Why approval is needed. */
383
+ approvalPrompt?: string;
384
+ /** Overrides the starting state. Defaults to `running`, or `needs-approval`. */
385
+ state?: ToolState;
386
+ messageId?: string;
387
+ }
388
+ interface ToolUpdateEvent {
389
+ type: "tool-update";
390
+ callId: string;
391
+ state?: ToolState;
392
+ name?: string;
393
+ input?: unknown;
394
+ output?: unknown;
395
+ error?: string;
396
+ approval?: ToolApproval;
397
+ messageId?: string;
398
+ }
399
+ interface ToolResultEvent {
400
+ type: "tool-result";
401
+ callId: string;
402
+ output?: unknown;
403
+ /** Present when the tool failed. */
404
+ error?: string;
405
+ durationMs?: number;
406
+ /** Used only when this result arrives for a call that was never announced. */
407
+ name?: string;
408
+ messageId?: string;
409
+ }
410
+ interface SourceEvent {
411
+ type: "source";
412
+ id: string;
413
+ title: string;
414
+ url: string;
415
+ snippet?: string;
416
+ messageId?: string;
417
+ }
418
+ interface FileEvent {
419
+ type: "file";
420
+ id?: string;
421
+ name: string;
422
+ mediaType: string;
423
+ url: string;
424
+ size?: number;
425
+ messageId?: string;
426
+ }
427
+ interface DataEvent {
428
+ type: "data";
429
+ kind: string;
430
+ data: unknown;
431
+ messageId?: string;
432
+ }
433
+ interface FinishEvent {
434
+ type: "finish";
435
+ usage?: Usage;
436
+ finishReason?: FinishReason;
437
+ messageId?: string;
438
+ }
439
+ interface ErrorEvent {
440
+ type: "error";
441
+ /** A message, or a full `ThreadError`. */
442
+ error: string | ThreadError;
443
+ messageId?: string;
444
+ }
445
+ interface AbortEvent {
446
+ type: "abort";
447
+ messageId?: string;
448
+ }
449
+ type StreamEvent = StartEvent | TextDeltaEvent | ReasoningDeltaEvent | ReasoningEndEvent | ToolCallEvent | ToolUpdateEvent | ToolResultEvent | SourceEvent | FileEvent | DataEvent | FinishEvent | ErrorEvent | AbortEvent;
450
+ interface StreamEventOptions {
451
+ /**
452
+ * The message to write to when an event names none and no message is being
453
+ * written. `useThread` sets this to the id it made for the reply.
454
+ */
455
+ messageId?: string;
456
+ /** Epoch milliseconds, for reasoning and tool durations. */
457
+ at?: number;
458
+ }
459
+ /** Translates one stream event into a reducer action, or null when the event is unknown. */
460
+ declare function streamEventToAction(state: Pick<ThreadState, "activeMessageId">, event: StreamEvent, options?: StreamEventOptions): ThreadAction | null;
461
+ /**
462
+ * Applies one stream event to the thread state. Content that arrives before a
463
+ * `start` event starts the assistant message itself, so a provider that never
464
+ * sends `start` still works.
465
+ */
466
+ declare function applyStreamEvent(state: ThreadState, event: StreamEvent, options?: StreamEventOptions): ThreadState;
467
+ /** Applies a list of stream events in order. Handy in tests and for replaying a recording. */
468
+ declare function applyStreamEvents(state: ThreadState, events: readonly StreamEvent[], options?: StreamEventOptions): ThreadState;
469
+ /** True for events that end a response, so a consumer can flush pending work. */
470
+ declare function isTerminalStreamEvent(event: StreamEvent): boolean;
471
+ /**
472
+ * Wraps a stream so it stops when `signal` aborts: iteration ends quietly,
473
+ * and the source gets `return()` so a generator can clean up (close a socket,
474
+ * cancel a request). Errors from the source are passed through, except the
475
+ * abort error itself when the signal is aborted.
476
+ */
477
+ declare function createAbortableStream<T>(source: AsyncIterable<T>, signal: AbortSignal): AsyncGenerator<T, void, undefined>;
478
+ /** Turns a thrown value into the `error` stream event, so a generator can `catch` and yield it. */
479
+ declare function errorEvent(error: unknown): ErrorEvent;
480
+
481
+ export { streamEventToAction as $, applyStreamEvent as L, applyStreamEvents as N, createAbortableStream as O, createThreadState as P, errorEvent as Q, getBranchInfo as V, getBranchInfoMap as W, isBusy as X, isTerminalStreamEvent as Y, isTerminalToolState as Z, lastUserMessage as _, threadReducer as a0 };
482
+ export type { AbortEvent as A, BranchInfo as B, ToolApproval as C, DataPart as D, ErrorEvent as E, FilePart as F, ToolCallEvent as G, ToolCallPatch as H, ToolResultEvent as I, ToolState as J, ToolUpdateEvent as K, Message as M, ReasoningDeltaEvent as R, SourcePart as S, ToolPart as T, Usage as U, FinishReason as a, ThreadError as b, BranchPoint as c, DataEvent as d, ErrorKind as e, FileEvent as f, FinishEvent as g, MessageAdapter as h, MessagePart as i, MessagePartType as j, MessageRole as k, MessageStatus as l, MessagesAdapter as m, ReasoningEndEvent as n, ReasoningPart as o, SourceEvent as p, StartEvent as q, StreamEvent as r, StreamEventOptions as s, TextDeltaEvent as t, TextPart as u, Thread as v, ThreadAction as w, ThreadState as x, ThreadStatus as y, ThreadStore as z };