@frontera-sdk/chat 1.50.40

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/src/types.ts ADDED
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Request/response shapes for the chat surface, mirrored from the service's
3
+ * TypeBox schemas (chat-router.ts, session-router.ts). Mirrored by hand rather
4
+ * than imported: the SDK publishes source and must not depend on the service
5
+ * package.
6
+ */
7
+
8
+ export type ChatRole = 'user' | 'assistant' | 'system'
9
+
10
+ /**
11
+ * A message as `POST /v1/chat` accepts it. `parts` is deliberately untyped:
12
+ * the server persists parts opaquely (a narrow schema on the wire stripped
13
+ * part fields once — see the comment in chat-router.ts), and the SDK follows.
14
+ */
15
+ export interface ChatInputMessage {
16
+ id: string
17
+ role: ChatRole
18
+ content?: string
19
+ parts: unknown[]
20
+ }
21
+
22
+ export interface ContextAttachment {
23
+ type: string
24
+ id: string
25
+ name?: string
26
+ meta?: Record<string, unknown>
27
+ }
28
+
29
+ export interface FileAttachment {
30
+ /** Publicly reachable URL — under API-key auth there is no upload endpoint. */
31
+ url: string
32
+ storagePath?: string
33
+ filename: string
34
+ mediaType: string
35
+ size: number
36
+ pageCount?: number
37
+ isScanned?: boolean
38
+ }
39
+
40
+ export type ReasoningEffort = 'max' | 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none'
41
+
42
+ export interface StartChatRequest {
43
+ /** 1–5 messages; the server rehydrates earlier history from the conversation. */
44
+ messages: ChatInputMessage[]
45
+ conversationId?: string
46
+ editMessageId?: string
47
+ contextAttachments?: ContextAttachment[]
48
+ fileAttachments?: FileAttachment[]
49
+ analysisMode?: string
50
+ webSearchAugment?: boolean
51
+ modelMode?: string | null
52
+ thinkingEnabled?: boolean
53
+ reasoningEffort?: ReasoningEffort
54
+ /** Ignored under sak_/cht_ auth — the credential's agent always wins. */
55
+ agentName?: string
56
+ surface?: 'web' | 'mobile' | 'console'
57
+ }
58
+
59
+ /** What `startChat` extracts from the response before handing over the stream. */
60
+ export interface StartChatResult {
61
+ conversationId: string
62
+ runId: string
63
+ runtime: string
64
+ quotaWarning?: string
65
+ /** The raw response; `response.body` is the AI SDK v6 UI-message SSE stream. */
66
+ response: Response
67
+ }
68
+
69
+ // --- auth ---
70
+
71
+ export interface ExchangeTokenRequest {
72
+ externalUserId: string
73
+ name?: string
74
+ metadata?: Record<string, unknown>
75
+ ttlSeconds?: number
76
+ /**
77
+ * Which agent the token reaches. Accepted when minting with a workspace key
78
+ * scoped for chat; an agent key is fixed to its own agent and refuses a
79
+ * different one. Omit with a workspace key for a token that reaches every
80
+ * agent, the way a platform user does.
81
+ */
82
+ agentId?: string
83
+ agentName?: string
84
+ /**
85
+ * Limit this token to a SUBSET of the workspace's agents — ids or slugs.
86
+ *
87
+ * Omit and it reaches every agent the minting credential can (an agent key
88
+ * always fixes exactly one). Naming a subset is how a product that exposes
89
+ * two of five agents stops the other three from being reachable by a guessed
90
+ * id, not merely hidden from the picker.
91
+ */
92
+ agentIds?: string[]
93
+ /**
94
+ * What YOUR product authorises this person to do beyond chatting. Everything
95
+ * here is DENIED unless asked for:
96
+ *
97
+ * - `plugin-connect` — connect their own account (Gmail, Drive, …) for a plugin
98
+ * that needs one. All-or-nothing across the plugins the workspace has
99
+ * opened to external users; a per-plugin scope is planned (see the TODO in
100
+ * the service's `chat-token.ts`).
101
+ * - `schedule` — read and manage the agent's scheduled tasks.
102
+ * - `agent-mode` — agent work items, exchanges, handoffs, agent conversations.
103
+ *
104
+ * The base token chats and nothing more: it sends, reads its own history and
105
+ * answers what the agent parks. Decide the rest against your own roles — we
106
+ * never model them. The decision rides on the token and expires with it, so
107
+ * withdrawing it on your side takes effect with nothing to synchronise.
108
+ */
109
+ capabilities?: string[]
110
+ }
111
+
112
+ export interface ChatToken {
113
+ token: string
114
+ expiresAt: string
115
+ }
116
+
117
+ /** What an upload returns — the shape `fileAttachments` expects. */
118
+ export interface UploadedFile {
119
+ url: string
120
+ storagePath?: string
121
+ filename: string
122
+ mediaType: string
123
+ size: number
124
+ pageCount?: number
125
+ isScanned?: boolean
126
+ }
127
+
128
+ /**
129
+ * A document, image or deck the agent produced.
130
+ *
131
+ * The named fields are the ones a viewer reads; the index signature carries
132
+ * whatever else the deployment returns. Naming them matters — under an index
133
+ * signature alone every read is `unknown`, and a renderer cannot tell a title
134
+ * from a MIME type.
135
+ */
136
+ /**
137
+ * One revision of an artifact, as the version picker needs it.
138
+ *
139
+ * Metadata only — no `content`, no `storageKey`. Drawing a menu does not
140
+ * require every revision's bytes, and the preview route serves them when a
141
+ * revision is actually opened.
142
+ */
143
+ export interface ArtifactVersionMeta {
144
+ version: number
145
+ createdAt?: string
146
+ title?: string | null
147
+ /** Who saved it — an agent turn, or the person editing in the viewer. */
148
+ createdBy?: string | null
149
+ sizeBytes?: number | null
150
+ }
151
+
152
+ export interface ChatArtifact {
153
+ id: string
154
+ title?: string | null
155
+ kind?: string | null
156
+ /** Rendered format: markdown, csv, sheet, pdf, image, htmldeck, canvas. */
157
+ format?: string | null
158
+ mimeType?: string | null
159
+ /** The artifact's text, when it has one. */
160
+ content?: string | null
161
+ /** Source language for a code artifact, used to pick a subtitle and lexer. */
162
+ language?: string | null
163
+ conversationId?: string | null
164
+ /** Where the bytes live, for artifacts stored as files rather than text. */
165
+ storageKey?: string | null
166
+ /**
167
+ * A rendered PDF of this artifact, when the format needs one to be shown.
168
+ *
169
+ * Set for `.docx`/`.pptx` and anything else a browser cannot open. Present
170
+ * means `getArtifactPreview(id)` will answer; absent means it will not.
171
+ */
172
+ previewKey?: string | null
173
+ createdAt?: string
174
+ updatedAt?: string
175
+ [key: string]: unknown
176
+ }
177
+
178
+ /** An agent the current credential may talk to — enough to draw a picker. */
179
+ export interface ChatAgent {
180
+ id: string
181
+ agentId: string
182
+ name: string
183
+ description?: string | null
184
+ avatarConfig?: unknown
185
+ }
186
+
187
+ // --- approvals ---
188
+
189
+ /**
190
+ * A tool call the agent has parked, waiting for a person to decide.
191
+ *
192
+ * The run is BLOCKED while this exists: an agent whose approval mode is `ask`
193
+ * (or whose tool the safety reviewer would not clear on its own) streams a
194
+ * `tool-approval-request` chunk carrying `approvalId` and then stops until
195
+ * `respondToApproval` settles it or `expiresAt` passes. An integration that
196
+ * never answers looks like an agent that silently stopped mid-sentence.
197
+ *
198
+ * Named fields are what a card needs to render; the index signature carries
199
+ * whatever else the deployment reports.
200
+ */
201
+ export interface PendingApproval {
202
+ id: string
203
+ /** `pending` while it waits; then `approved`, `denied` or `expired`. */
204
+ status: string
205
+ /** `needs_approval` for a human gate; `needs_connect` for a missing account. */
206
+ pauseReason: string
207
+ /** Which app the agent was reaching for — pass it to `startAppConnection`. */
208
+ installId?: string | null
209
+ toolCallId?: string | null
210
+ toolName?: string | null
211
+ /** Where the tool came from: an app, an MCP server, the platform. */
212
+ toolSource?: string | null
213
+ risk?: string | null
214
+ /** A short, secret-safe description of what the tool is about to do. */
215
+ actionSummary?: unknown
216
+ /** Why the policy asked rather than allowing. */
217
+ policyReason?: string | null
218
+ expiresAt?: string
219
+ [key: string]: unknown
220
+ }
221
+
222
+ export type ApprovalDecision = 'approve' | 'deny'
223
+
224
+ // --- app connections ---
225
+
226
+ /**
227
+ * Starting a connect for a tool that needs the END USER's own account.
228
+ *
229
+ * `redirectUri` is a callback on YOUR domain, registered both with your OAuth
230
+ * client and on the workspace's embed allowlist. It has to be yours: a
231
+ * provider only authorises redirect URIs under a domain the OAuth project's
232
+ * owner has verified, and you cannot verify ours.
233
+ */
234
+ export interface StartAppConnectionRequest {
235
+ /** From the parked call's `installId`; or name the app with `catalogKind`. */
236
+ installId?: string
237
+ catalogKind?: string
238
+ redirectUri: string
239
+ /** Where your page sends the user afterwards — origin must be allowlisted. */
240
+ returnTo?: string
241
+ }
242
+
243
+ export interface StartAppConnectionResult {
244
+ /** Open this in a popup or a full-page redirect. */
245
+ authUrl: string
246
+ installId: string
247
+ }
248
+
249
+ /** What your callback forwards to us: the provider's `code` and our `state`. */
250
+ export interface CompleteAppConnectionRequest {
251
+ code: string
252
+ state: string
253
+ }
254
+
255
+ export interface CompleteAppConnectionResult {
256
+ installId: string
257
+ /** The account's email, when the provider reports one. */
258
+ email: string | null
259
+ }
260
+
261
+ /**
262
+ * The re-run of a parked call. `ok: false` with `reason: 'no_account'` means
263
+ * the connection still is not usable — the row stays parked, so the same card
264
+ * can retry.
265
+ */
266
+ export type ResumeToolCallResult =
267
+ | { ok: true; [key: string]: unknown }
268
+ | { ok: false; reason: string; error?: string }
269
+
270
+ /**
271
+ * The chunk that announces a parked tool call, as it arrives on the stream.
272
+ *
273
+ * Not exported by the AI SDK's own chunk union, so it is declared here for
274
+ * consumers switching on chunk types from `chatChunkStream`.
275
+ */
276
+ export interface ToolApprovalRequestChunk {
277
+ type: 'tool-approval-request'
278
+ approvalId: string
279
+ toolCallId: string
280
+ }
281
+
282
+ // --- sessions ---
283
+
284
+ export interface CreateSessionRequest {
285
+ agentName?: string
286
+ modelMode?: string | null
287
+ approvalMode?: 'ask' | 'auto' | 'full'
288
+ workspaceId?: string
289
+ }
290
+
291
+ export interface UpdateSessionRequest {
292
+ title?: string
293
+ status?: string
294
+ pinned?: boolean
295
+ group?: string | null
296
+ modelMode?: string | null
297
+ approvalMode?: 'ask' | 'auto' | 'full'
298
+ }
299
+
300
+ export interface ListSessionsQuery {
301
+ cursor?: string
302
+ limit?: number
303
+ status?: string
304
+ label?: string
305
+ group?: string
306
+ search?: string
307
+ pinned?: boolean
308
+ source?: 'chat' | 'automation' | 'all'
309
+ agentName?: string
310
+ agentId?: string
311
+ updatedAfter?: string
312
+ updatedBefore?: string
313
+ surface?: string
314
+ }
315
+
316
+ export interface Paginated<T> {
317
+ data: T[]
318
+ nextCursor?: string
319
+ }
320
+
321
+ /**
322
+ * Loose by design: these are the fields the server guarantees today; anything
323
+ * else rides in the index signature until the service exposes a shared
324
+ * contract.
325
+ */
326
+ export interface Session {
327
+ id: string
328
+ title?: string | null
329
+ status?: string
330
+ agentName?: string | null
331
+ pinned?: boolean
332
+ createdAt?: string
333
+ updatedAt?: string
334
+ [key: string]: unknown
335
+ }
336
+
337
+ /** UIMessage-shaped: feed straight into `useChat({ messages })`. */
338
+ export interface SessionMessage {
339
+ id: string
340
+ role: ChatRole
341
+ parts: unknown[]
342
+ metadata?: Record<string, unknown>
343
+ createdAt: string
344
+ }
345
+
346
+ /**
347
+ * Custom data parts the server streams alongside text. Payloads are `unknown`
348
+ * in v1; the names are stable and safe to switch on in `onData`.
349
+ */
350
+ export type ChatDataPartName =
351
+ | 'data-context'
352
+ | 'data-context-estimate'
353
+ | 'data-context-observed'
354
+ | 'data-context-maintenance'
355
+ | 'data-compaction'
356
+ | 'data-work-queued'
357
+ | 'data-desktop'
358
+
359
+ /**
360
+ * Called before any method that touches an existing conversation. Under
361
+ * API-key auth the server skips conversation ownership checks entirely, so a
362
+ * consumer serving multiple end-users MUST decide here whether the current
363
+ * user may touch `conversationId` — typically a lookup in their own
364
+ * user↔conversation store.
365
+ */
366
+ export type AuthorizeConversation = (conversationId: string) => boolean | Promise<boolean>
367
+
368
+ export interface ChatClientOptions {
369
+ authorizeConversation?: AuthorizeConversation
370
+ }
@@ -0,0 +1,49 @@
1
+ import {
2
+ parseJsonEventStream,
3
+ readUIMessageStream,
4
+ type UIMessage,
5
+ type UIMessageChunk,
6
+ } from 'ai'
7
+
8
+ /**
9
+ * Decode a chat SSE `Response` into the AI SDK's typed chunk stream.
10
+ *
11
+ * `readUIMessageStream` consumes chunks, not responses, so this is the bridge
12
+ * for server-side consumers holding the raw `Response` from `startChat`/
13
+ * `resumeStream`. The SSE layer (including the server's `: ping` keepalive
14
+ * comments and the `[DONE]` terminator) is handled by the AI SDK's own parser
15
+ * — the SDK never hand-parses SSE.
16
+ */
17
+ export function chatChunkStream(response: Response): ReadableStream<UIMessageChunk> {
18
+ if (!response.body) {
19
+ throw new Error('chat response has no body to stream')
20
+ }
21
+ // No schema: chunk validation happens in readUIMessageStream's own state
22
+ // machine, and the service can add new data-part chunk types without the SDK
23
+ // rejecting them at the parse layer.
24
+ return parseJsonEventStream({
25
+ stream: response.body,
26
+ schema: undefined,
27
+ } as unknown as Parameters<typeof parseJsonEventStream>[0]).pipeThrough(
28
+ new TransformStream({
29
+ transform(result, controller) {
30
+ if (!result.success) throw result.error
31
+ controller.enqueue(result.value as UIMessageChunk)
32
+ },
33
+ }),
34
+ )
35
+ }
36
+
37
+ /**
38
+ * Iterate assembled `UIMessage` snapshots from a chat response:
39
+ *
40
+ * ```ts
41
+ * const run = await chat.startChat({ messages: [...] })
42
+ * for await (const message of readChatStream(run.response)) {
43
+ * // message.parts grows as the agent streams
44
+ * }
45
+ * ```
46
+ */
47
+ export function readChatStream(response: Response): AsyncIterable<UIMessage> {
48
+ return readUIMessageStream({ stream: chatChunkStream(response) })
49
+ }