@oberik/sdk 0.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,1614 @@
1
+ /**
2
+ * Agent Framework — single-file TypeScript client.
3
+ *
4
+ * Isomorphic: works server-side (Node 18+) and in the browser. The only runtime
5
+ * requirements are the standard `fetch`, `Blob`, `TextDecoder`, and web
6
+ * `ReadableStream` (all present in Node 18+ and modern browsers). No deps.
7
+ *
8
+ * Handles the tedious parts for you:
9
+ * - Auth via JWT (Bearer) or legacy API key, with optional async token refresh.
10
+ * - Chat streaming over SSE with automatic reconnection — if the connection
11
+ * drops mid-generation it resumes from the last event id (server keeps going).
12
+ * - Client-side tools: register a handler and the client auto-executes tool
13
+ * calls the agent makes and submits the results, looping until the agent ends.
14
+ * - Resumable uploads: presigned S3 multipart with per-part retry + concurrency.
15
+ * - Resumable downloads: presigned URL fetched with ranged GETs that retry.
16
+ *
17
+ * Quick start:
18
+ * const af = createClient({ token: jwt }); // hosted API
19
+ * const af = createClient({ token: jwt, baseUrl: "http://localhost:8000" }); // local
20
+ * const res = await af.chat.send({ message: "hello" });
21
+ *
22
+ * // Client-side tools — the client runs your handler and resumes automatically:
23
+ * af.registerTool({
24
+ * name: "get_weather",
25
+ * description: "Current weather for a city",
26
+ * parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
27
+ * handler: async ({ city }) => ({ tempC: 21, city }),
28
+ * });
29
+ * const done = await af.chat.run({ message: "what's the weather in Paris?" });
30
+ * // ...or streaming, tokens + auto tool dispatch in one call:
31
+ * const handle = af.chat.stream({ message: "weather in Paris?" }, { onToken: t => process.stdout.write(t) });
32
+ * await handle.done;
33
+ */
34
+ /**
35
+ * Where the agent API lives. Override for a self-hosted deployment or local
36
+ * development (`http://localhost:8000`); the hosted API needs no `baseUrl` at all.
37
+ */
38
+ export declare const DEFAULT_BASE_URL = "https://api.oberik.com";
39
+ export interface ClientOptions {
40
+ /** Base URL of the API. Defaults to {@link DEFAULT_BASE_URL}. */
41
+ baseUrl?: string;
42
+ /**
43
+ * Static JWT (sent as `Authorization: Bearer`). Fine for a script; for anything
44
+ * long-lived prefer `getToken`, because end-user tokens are deliberately
45
+ * short-lived and a static one eventually 401s with nothing the SDK can do.
46
+ */
47
+ token?: string;
48
+ /** Legacy API key (sent as `X-API-Key`). Prefer `token`/`getToken`. */
49
+ apiKey?: string;
50
+ /**
51
+ * How the client obtains a token — **the recommended way to authenticate.** Called
52
+ * to get one, and called again with `{ expired: true }` whenever the server rejects
53
+ * the current token (401), so an expiry is handled where it happens instead of
54
+ * everywhere you make a call:
55
+ *
56
+ * const ai = createClient({
57
+ * baseUrl,
58
+ * getToken: async ({ expired }) => {
59
+ * // YOUR endpoint, which mints with your project key server-side.
60
+ * const r = await fetch("/api/ai-token", { cache: expired ? "reload" : "default" });
61
+ * return (await r.json()).access_token;
62
+ * },
63
+ * });
64
+ *
65
+ * On a 401 the failed request is replayed once with the new token, so callers never
66
+ * see the expiry. Concurrent requests share one refresh (no stampede), the replay
67
+ * happens at most once per request (a still-rejected token surfaces as a normal
68
+ * 401 rather than looping), and an interrupted stream re-attaches to the *same* run
69
+ * with the new token — the answer in flight is neither lost nor paid for twice.
70
+ *
71
+ * `{ expired: false }` calls happen per request, so keep your own token cached and
72
+ * only go to the network when `expired` is true.
73
+ */
74
+ getToken?: (ctx: {
75
+ expired: boolean;
76
+ }) => string | Promise<string>;
77
+ /** Operator admin key for tenant-management endpoints (sent as X-Admin-Key). */
78
+ adminKey?: string;
79
+ /** Custom fetch (e.g. for tests or non-global fetch). Defaults to global. */
80
+ fetch?: typeof fetch;
81
+ /** Extra headers merged into every request. */
82
+ headers?: Record<string, string>;
83
+ /** Client-side tools registered up-front; auto-dispatched by chat.run/stream. */
84
+ tools?: ClientToolDef[];
85
+ }
86
+ /** Public tenant view — never includes the API key. */
87
+ export interface TenantOut {
88
+ id: string;
89
+ slug: string;
90
+ name: string;
91
+ created_at: string;
92
+ }
93
+ /** Returned ONCE at creation — the only time the API key is exposed. */
94
+ export interface TenantCreated extends TenantOut {
95
+ api_key: string;
96
+ }
97
+ /** Everything a minted token can be narrowed by. Each field is intersected (or
98
+ * clamped) against the minting credential's own grant — never widened. */
99
+ export interface TokenRequest {
100
+ /** Your own label for the end-user (appears on sessions). */
101
+ user_ref?: string;
102
+ expires_in?: number;
103
+ /** e.g. ["chat", "documents:read"]. Absent = inherit the caller's set. */
104
+ capabilities?: string[];
105
+ roles?: string[];
106
+ groups?: string[];
107
+ /** Visibility boundary for this token (must sit within the caller's). */
108
+ scope?: string;
109
+ /** Hierarchical data-visibility prefix, if you separate it from `scope`. */
110
+ data_scope?: string;
111
+ /** Restrict this token to a subset of models. */
112
+ allowed_models?: string[];
113
+ /** Reasoning-effort ceiling. */
114
+ max_effort?: "minimal" | "low" | "medium" | "high";
115
+ /** Ceiling on how much conversation history is replayed for this token, in tokens.
116
+ * Narrows the project's own context budget (Dashboard → Context management) — it
117
+ * can never widen it. Use it to give a cheap tier a shorter memory. */
118
+ max_context_tokens?: number;
119
+ }
120
+ export interface TokenResponse {
121
+ access_token: string;
122
+ token_type: string;
123
+ expires_in: number;
124
+ tenant_id: string;
125
+ }
126
+ export interface ToolInfo {
127
+ name: string;
128
+ source: "builtin" | "mcp" | string;
129
+ description: string;
130
+ }
131
+ export type DocumentStatus = "awaiting_upload" | "pending" | "processing" | "ready" | "failed" | string;
132
+ export interface DocumentOut {
133
+ id: string;
134
+ filename: string;
135
+ content_type: string | null;
136
+ tags: string[];
137
+ /** The subject that uploaded it — owns it for ACL purposes. */
138
+ owner_subject?: string | null;
139
+ visibility: Visibility | string;
140
+ visibility_scope?: string | null;
141
+ acl_roles: string[];
142
+ acl_groups: string[];
143
+ status: DocumentStatus;
144
+ chunk_count: number;
145
+ error: string | null;
146
+ /** Which model embedded it, and with which pipeline — a document embedded by an
147
+ * older model lives in its own vector collection and needs `reingest` to move. */
148
+ embedding_model?: string | null;
149
+ pipeline_version?: string | null;
150
+ content_hash?: string | null;
151
+ created_at: string;
152
+ }
153
+ export interface RetrievedChunk {
154
+ text: string;
155
+ score: number;
156
+ document_id: string;
157
+ chunk_index: number;
158
+ metadata: Record<string, unknown>;
159
+ }
160
+ export interface RetrieveRequest {
161
+ query: string;
162
+ document_ids?: string[] | null;
163
+ tags?: string[] | null;
164
+ top_k?: number | null;
165
+ top_n?: number | null;
166
+ }
167
+ /** OpenAI-format tool schema for client-executed (non-MCP) tools. */
168
+ export interface ClientTool {
169
+ type: "function";
170
+ function: {
171
+ name: string;
172
+ description?: string;
173
+ parameters: Record<string, unknown>;
174
+ };
175
+ }
176
+ export interface ClientToolResult {
177
+ tool_call_id: string;
178
+ content: string;
179
+ }
180
+ /** A client-side tool: its JSON-schema + a handler the client runs when the agent
181
+ * calls it. Register via `createClient({ tools })` / `client.registerTool(...)`,
182
+ * or pass per-call. `chat.run` / `chat.stream` then dispatch and resume for you. */
183
+ export interface ClientToolDef {
184
+ name: string;
185
+ description?: string;
186
+ /** JSON Schema for the arguments (the `parameters` of the OpenAI tool schema). */
187
+ parameters?: Record<string, unknown>;
188
+ /** Executed with the model-provided args. Return a string, or any JSON value
189
+ * (it's JSON-stringified). Throwing is caught and returned as an error result. */
190
+ handler: (args: Record<string, unknown>, call: PendingToolCall) => unknown | Promise<unknown>;
191
+ }
192
+ /** One step on the agent's plan for a conversation. `number` is stable within the
193
+ * session and never reused, so it keeps meaning the same step as items come and go. */
194
+ export interface TodoItem {
195
+ id: string;
196
+ number: number;
197
+ content: string;
198
+ status: "pending" | "in_progress" | "completed" | string;
199
+ }
200
+ /** One piece of work the agent handed to another agent running beside it.
201
+ *
202
+ * Small on purpose: a UI shows a line per subagent — what it is doing and how far
203
+ * along — not a transcript. The subagent's working conversation is its own, and the
204
+ * only part of it anyone else needs is `result` at the end. */
205
+ export interface SubagentState {
206
+ id: string;
207
+ /** The short handle the agent addresses it by, e.g. "a1". Stable per conversation. */
208
+ ref: string;
209
+ /** What it was asked to do, in the main agent's words. */
210
+ task: string;
211
+ model?: string | null;
212
+ /** `delivered` means the main agent has been told; the work is over either way. */
213
+ status: "running" | "finished" | "failed" | "stopped" | "delivered" | string;
214
+ /** Four or five words on what it is doing right now, written from its own actions
215
+ * rather than asked of the model — so it stays live under load. */
216
+ progress: string;
217
+ /** Agent↔tool steps taken. Use it for movement when `progress` does not change. */
218
+ steps: number;
219
+ /** Set while it is blocked waiting for the main agent to decide something. The main
220
+ * agent answers it; there is nothing for a client to do but show it. */
221
+ question?: string | null;
222
+ /** Its report to the main agent. Empty until it has finished. */
223
+ result: string;
224
+ error?: string | null;
225
+ }
226
+ /** What context management did to fit this turn's history into the model's window.
227
+ *
228
+ * Present only when the conversation did NOT fit as-is — which is exactly when it
229
+ * matters, because it means the model was not shown the whole transcript. The
230
+ * strategy is a project setting (Dashboard → Context management), not a per-call one. */
231
+ export interface ContextReport {
232
+ strategy: "trim" | "summarize" | string;
233
+ /** Present on stream events, absent on `ChatResponse.context`. */
234
+ phase?: "done";
235
+ tokens_before: number;
236
+ tokens_after: number;
237
+ budget: number;
238
+ /** Messages removed from the window entirely. */
239
+ dropped: number;
240
+ /** Messages (usually tool outputs) kept but shortened. */
241
+ truncated: number;
242
+ /** True when the evicted prefix was compacted into a summary. */
243
+ summarized: boolean;
244
+ }
245
+ /** Emitted when compaction starts, before the summarizer has produced anything.
246
+ * `tokens_after` is not known yet — that arrives on the `done` frame. */
247
+ export interface ContextProgress {
248
+ phase: "compacting";
249
+ strategy: "summarize" | string;
250
+ tokens_before: number;
251
+ budget: number;
252
+ /** Images/audio/video being sent to the summarizer to be described before they go. */
253
+ media: number;
254
+ }
255
+ /** A picture of a page the agent has handed to the user, and what a click means. */
256
+ export interface BrowserFrame {
257
+ url: string;
258
+ title: string;
259
+ /** data: URI, ready for an <img src>. */
260
+ image: string;
261
+ width: number;
262
+ height: number;
263
+ /** Where the crop was taken from. Echo this back with a click. */
264
+ origin: {
265
+ x: number;
266
+ y: number;
267
+ };
268
+ /** False when the whole viewport is shown. */
269
+ clipped: boolean;
270
+ /** Whether the agent's OWN selector produced this crop. */
271
+ matched: boolean;
272
+ /** `selector` — the agent's; `detected` — it matched nothing and the widget was found
273
+ * anyway; `viewport` — neither, so this is the whole page. */
274
+ region: "selector" | "detected" | "viewport";
275
+ }
276
+ /** One frame of the live view, as Chrome painted it. */
277
+ export interface BrowserStreamFrame {
278
+ image: string;
279
+ device_width?: number;
280
+ device_height?: number;
281
+ scroll_x?: number;
282
+ scroll_y?: number;
283
+ }
284
+ /** Where to crop the live view, and why. Sent when it MOVES, not per frame — an
285
+ * overlay appearing mid-gesture changes it, a repaint does not. */
286
+ export interface BrowserStreamClip {
287
+ origin: {
288
+ x: number;
289
+ y: number;
290
+ };
291
+ width: number;
292
+ height: number;
293
+ clipped: boolean;
294
+ matched: boolean;
295
+ region: "selector" | "detected" | "viewport";
296
+ url: string;
297
+ }
298
+ /** Whether the person seems to have finished with the page.
299
+ *
300
+ * `state` is one of four fixed words. That is a security property, not brevity: the
301
+ * checker is looking at a CAPTCHA, and a closed vocabulary is what stops it being a
302
+ * way to read one out. `checked: false` means no call was made (rate-limited, off, or
303
+ * nothing handed over) — ask again later; it does not mean "not finished". */
304
+ export interface HandoffCheck {
305
+ /** `gone` (the widget is no longer on the page) and `loading` (mid-navigation) are
306
+ * decided from the page itself, without paying for a model call. */
307
+ state: "solved" | "gone" | "unsolved" | "loading" | "unclear";
308
+ done: boolean;
309
+ checked: boolean;
310
+ }
311
+ export interface BrowserStreamHandlers {
312
+ onFrame: (frame: BrowserStreamFrame) => void;
313
+ onClip?: (clip: BrowserStreamClip) => void;
314
+ onError?: (message: string) => void;
315
+ }
316
+ /** A live view being watched. Call `close()` when the viewer goes away — the page
317
+ * keeps painting for as long as anyone is listening. */
318
+ export interface BrowserStream {
319
+ close: () => void;
320
+ }
321
+ /** The bit of `getBoundingClientRect()` the geometry needs. */
322
+ export interface HandoffRect {
323
+ left: number;
324
+ top: number;
325
+ width: number;
326
+ height: number;
327
+ }
328
+ /** What `attach()` needs of an element — satisfied by a real `<img>`, and by anything
329
+ * else shaped like one. Deliberately not `HTMLImageElement`: the published types would
330
+ * then require a DOM lib in every consumer's tsconfig, including the server-side ones
331
+ * that never open a viewer. */
332
+ export interface HandoffElement {
333
+ getBoundingClientRect(): HandoffRect;
334
+ parentElement?: {
335
+ getBoundingClientRect(): HandoffRect;
336
+ style?: any;
337
+ } | null;
338
+ naturalWidth?: number;
339
+ naturalHeight?: number;
340
+ src?: string;
341
+ style?: any;
342
+ addEventListener(type: string, handler: (event: any) => void, options?: any): void;
343
+ removeEventListener(type: string, handler: (event: any) => void, options?: any): void;
344
+ setPointerCapture?(pointerId: number): void;
345
+ releasePointerCapture?(pointerId: number): void;
346
+ }
347
+ /** Everything needed to render the handed-over page, recomputed as frames arrive.
348
+ *
349
+ * Read it in `onView` and paint however you like — or hand an element to `attach()`
350
+ * and never look at this at all. */
351
+ export interface HandoffView {
352
+ /** False once the hand-off is over; the viewer should come off the screen. */
353
+ active: boolean;
354
+ /** The page's address, for the caption. */
355
+ url: string;
356
+ /** The picture to show, as a data URI. Empty until the first frame lands. */
357
+ image: string;
358
+ /** True when frames are being pushed; false while showing the polled fallback. */
359
+ live: boolean;
360
+ /** The region being shown, in page pixels, and where it was taken from. `origin` is
361
+ * echoed back with every pointer event — never added to a coordinate here. */
362
+ origin: {
363
+ x: number;
364
+ y: number;
365
+ };
366
+ width: number;
367
+ height: number;
368
+ /** False when the whole viewport is being shown rather than a region. */
369
+ clipped: boolean;
370
+ /** True when the frame must be blown up and offset to show only the region — which
371
+ * is what `style` does. */
372
+ cropping: boolean;
373
+ /** CSS for the image and the window it shows through. Applied for you by `attach()`;
374
+ * spread onto your own elements if you render the picture yourself. */
375
+ style: {
376
+ image: Record<string, string>;
377
+ window: Record<string, string>;
378
+ };
379
+ /** False = they may look and not touch. Enforced server-side as well. */
380
+ interactive: boolean;
381
+ /** A "did that finish it?" check is in flight — say so, rather than letting the
382
+ * viewer vanish under them a moment later with no explanation. */
383
+ checking: boolean;
384
+ /** The hand-off as the agent announced it (reason, blocking, auto_done…). */
385
+ handoff: Handoff;
386
+ }
387
+ export interface HandoffOptions {
388
+ /** Called whenever anything about the view changes — a frame, a clip, a check. */
389
+ onView?: (view: HandoffView) => void;
390
+ /** The hand-off is over. `resume` is true when the turn stopped for it and is waiting
391
+ * to be continued: send `handoff_done: true` (via `chat.handoffDone`, or your own
392
+ * streamed turn if you render one). Deliberately yours to do — the SDK owns the
393
+ * viewer, not the conversation. */
394
+ onEnded?: (info: {
395
+ resume: boolean;
396
+ }) => void;
397
+ /** Frame/relay/check failures. Never thrown: a viewer that dies on a dropped frame is
398
+ * worse than one that misses it. */
399
+ onError?: (message: string) => void;
400
+ /** How long after a gesture ends before asking whether it finished the job. A check
401
+ * fired the instant a drag ends asks about a widget still animating. Default 700ms. */
402
+ settleMs?: number;
403
+ /** Minimum gap between relayed pointer moves. A pointermove handler fires far faster
404
+ * than any network will carry. Default 16ms (~60/s). */
405
+ relayMs?: number;
406
+ /** How long to wait for the stream to paint before fetching one frame the slow way,
407
+ * so a viewer shows something even if the stream cannot be opened. Default 1500ms. */
408
+ fallbackMs?: number;
409
+ }
410
+ /** A live hand-off: the stream, the geometry, the gestures and the finished-check. */
411
+ export interface HandoffController {
412
+ /** The current view. Also delivered to `onView` as it changes. */
413
+ readonly view: HandoffView;
414
+ /** Paint into this element and relay what the user does to it. Returns a detach
415
+ * function; call it when the element goes away (a re-render, an unmount). */
416
+ attach: (element: HandoffElement) => () => void;
417
+ /** The agent re-announced the hand-off. Keeps the stream when it is still the same
418
+ * view — each announcement is a new object, and re-opening on every one blanks the
419
+ * picture mid-gesture. */
420
+ update: (handoff: Handoff) => void;
421
+ /** Relay one thing the user did. `selector`, `origin` and `want_frame` are filled in;
422
+ * for a custom viewer, send `x`/`y` in the image space `view` describes. */
423
+ send: (input: BrowserInputEvent) => Promise<void>;
424
+ /** Type into the page, then (optionally) press Enter — what a text box in your UI
425
+ * should call. */
426
+ type: (text: string, opts?: {
427
+ submit?: boolean;
428
+ }) => Promise<void>;
429
+ key: (key: string, modifiers?: string[]) => Promise<void>;
430
+ scroll: (pixels: number) => Promise<void>;
431
+ /** A gesture ended: schedule the "did that finish it?" check. `attach()` calls this
432
+ * on release; call it yourself if you relay gestures your own way. */
433
+ settled: () => void;
434
+ /** Finish the hand-off — the "I'm done" button. Fires `onEnded`. */
435
+ done: () => Promise<void>;
436
+ /** Stop watching without ending anything: the viewer went away, the turn did not. */
437
+ close: () => void;
438
+ }
439
+ /** A page the agent has put in front of the user. */
440
+ export interface Handoff {
441
+ active: boolean;
442
+ /** The turn STOPPED and resumes when they say they're done — same shape as a pending
443
+ * client tool call, because it is the same situation. Render a "Done" button and
444
+ * send `handoff_done: true` when it's pressed. */
445
+ blocking: boolean;
446
+ /** False = show it, don't let them touch it. Enforced server-side too. */
447
+ interactive: boolean;
448
+ /** Poll `handoffCheck` after each release and press Done when it reports finished —
449
+ * someone who has just passed a bot check should not then have to report that they
450
+ * passed it. False when the agent gave nothing to judge against, or wants telling. */
451
+ auto_done: boolean;
452
+ reason: string;
453
+ url: string;
454
+ selector: string;
455
+ clipped: boolean;
456
+ }
457
+ /** One thing the user did, relayed into the page.
458
+ *
459
+ * The pointer kinds are what make a hand-off able to do more than tap: press, move,
460
+ * release IS a drag, which is what a slider puzzle asks for. Stream `pointer_move`
461
+ * from a real pointermove handler and the page sees the gesture the person actually
462
+ * made, path and all. */
463
+ export interface BrowserInputEvent {
464
+ type: "click" | "pointer_down" | "pointer_move" | "pointer_up" | "drag" | "wheel" | "type" | "key" | "scroll";
465
+ /** Keep the same crop the agent handed over, so the view doesn't jump. */
466
+ selector?: string;
467
+ x?: number;
468
+ y?: number;
469
+ origin?: {
470
+ x: number;
471
+ y: number;
472
+ };
473
+ text?: string;
474
+ key?: string;
475
+ pixels?: number;
476
+ /** Held for this input: "Shift" | "Control" | "Alt" | "Meta". */
477
+ modifiers?: string[];
478
+ button?: "left" | "right" | "middle" | "back" | "forward";
479
+ /** 2 is a double-click, 3 a triple — a selection gesture, not three clicks. */
480
+ clicks?: number;
481
+ /** `drag`: where it ends, same image space as x/y. */
482
+ to?: {
483
+ x: number;
484
+ y: number;
485
+ };
486
+ /** `drag`: how long the path takes. A drag with no duration is a teleport. */
487
+ hold_ms?: number;
488
+ steps?: number;
489
+ /** `wheel`: for panes that scroll under the pointer rather than the window. */
490
+ delta_x?: number;
491
+ delta_y?: number;
492
+ /** Set FALSE when watching `browserStream` — otherwise every pointer move renders a
493
+ * full JPEG server-side that arrives after the stream has already shown it. */
494
+ want_frame?: boolean;
495
+ }
496
+ export interface QuestionOption {
497
+ label: string;
498
+ description?: string | null;
499
+ }
500
+ /** One decision the agent put to the user. Render `options` as a picker —
501
+ * checkboxes when `multi_select`, radio otherwise — and always offer two escapes
502
+ * the agent cannot take away: a free-text answer, and declining to answer at all
503
+ * (`chat_instead`), which tells the agent to drop the question and keep talking.
504
+ *
505
+ * `options` is empty when the answer is genuinely open-ended: render a text input. */
506
+ export interface AgentQuestion {
507
+ id: string;
508
+ /** Very short label — the tab/chip for this question in a multi-question batch. */
509
+ header: string;
510
+ question: string;
511
+ options: QuestionOption[];
512
+ multi_select: boolean;
513
+ /** Always true today: "type something else" is never withheld. */
514
+ allow_other: boolean;
515
+ }
516
+ /** A paused `ask_user` call: what was asked, and the id that answers it. */
517
+ export interface PendingQuestions {
518
+ tool_call_id: string;
519
+ questions: AgentQuestion[];
520
+ }
521
+ export interface QuestionAnswerItem {
522
+ /** The question's `id` (or `header`). Omit for a single question, or when the
523
+ * answers are in the order they were asked. */
524
+ question_id?: string;
525
+ /** Option labels the user picked. Anything not offered is reported to the agent
526
+ * as text the user typed, not as a selection. */
527
+ selected?: string[];
528
+ /** The "type something else" answer. May accompany selections. */
529
+ text?: string | null;
530
+ }
531
+ /** Resumes a turn paused on a question. Set `chat_instead` when the user declined
532
+ * the picker — the agent is told to drop the question rather than re-ask it. */
533
+ export interface QuestionAnswer {
534
+ /** The paused call. Optional when exactly one question is outstanding. */
535
+ tool_call_id?: string;
536
+ answers?: QuestionAnswerItem[];
537
+ chat_instead?: boolean;
538
+ /** What the user said instead, if anything. */
539
+ message?: string | null;
540
+ }
541
+ /** Answer a batch of questions. Return one entry per question (or a single
542
+ * `{ chat_instead: true }` to decline the whole batch and keep chatting). */
543
+ export type QuestionHandler = (pending: PendingQuestions) => QuestionAnswer | QuestionAnswerItem[] | Promise<QuestionAnswer | QuestionAnswerItem[]>;
544
+ export interface RunOptions extends Omit<ChatRequest, "tool_results" | "client_tools"> {
545
+ /** Extra client tools for this call (merged over any registered on the client). */
546
+ tools?: ClientToolDef[];
547
+ /** Max auto tool-dispatch rounds before giving up (default 10). */
548
+ maxToolRounds?: number;
549
+ /** Fires before each batch of client tools is executed. */
550
+ onToolCalls?: (calls: PendingToolCall[]) => void;
551
+ /** Answer the agent's questions and continue the turn automatically. Without it,
552
+ * `run` returns as soon as the agent asks, with `questions` set. */
553
+ onQuestion?: QuestionHandler;
554
+ signal?: AbortSignal;
555
+ }
556
+ /** A non-text input part (any modality the model supports). `url` is a data: URI or
557
+ * an https URL. Gated by the token's `input:<kind>` capability. */
558
+ export interface Attachment {
559
+ /** Stable identity, set on anything the agent produced. Match on this, not on `url`:
560
+ * a URL is signed per response, so the same file arrives with a different one from
561
+ * the mid-turn `attachments` event and from the response that follows. It is also
562
+ * the handle to pass to a tool that takes a file. */
563
+ id?: string;
564
+ kind: "image" | "audio" | "video" | "file" | string;
565
+ url: string;
566
+ mime_type?: string | null;
567
+ name?: string | null;
568
+ format?: string | null;
569
+ /** Set on outputs (e.g. a file the agent exported from its sandbox). */
570
+ size?: number | null;
571
+ /** Generated/exported media is stored server-side and `url` is signed fresh on each
572
+ * response — download it or show it, but don't persist the URL, it expires. */
573
+ /** Input only: hand the ORIGINAL bytes to the sandbox instead of showing the file
574
+ * to the model. Needs the `computer` capability; the model is told the path it
575
+ * landed at. Use for data files the agent should process with commands. */
576
+ to_sandbox?: boolean;
577
+ }
578
+ /** What this deployment's sandboxes are and what they can do. The sandbox host is the
579
+ * deployment's, not the project's: there is no backend to configure. */
580
+ export interface ComputerHost {
581
+ provider: "firecracker" | "cloudflare" | "e2b" | string;
582
+ /** pause / snapshot / expose_port / network_policy. */
583
+ capabilities: Record<string, boolean>;
584
+ workdir: string;
585
+ /** Applied when a command names no timeout of its own. */
586
+ exec_default_timeout_s: number;
587
+ /** Hard ceiling: past this a command is killed. */
588
+ exec_max_timeout_s: number;
589
+ max_sessions_per_tenant: number;
590
+ }
591
+ export type ComputerSessionStatus = "creating" | "running" | "paused" | "stopped" | "failed" | string;
592
+ /** A sandbox. `id` is stable across pause/resume — pass it as `computer_session_id`
593
+ * on a later turn to reattach the agent to this workspace. */
594
+ export interface ComputerSession {
595
+ id: string;
596
+ name?: string | null;
597
+ provider: string;
598
+ /** The backend's own sandbox id. */
599
+ external_id: string;
600
+ status: ComputerSessionStatus;
601
+ workdir: string;
602
+ chat_session_id?: string | null;
603
+ error?: string | null;
604
+ last_used_at?: string | null;
605
+ expires_at?: string | null;
606
+ created_at: string;
607
+ }
608
+ export interface ComputerExecResult {
609
+ stdout: string;
610
+ stderr: string;
611
+ exit_code: number;
612
+ truncated: boolean;
613
+ timed_out: boolean;
614
+ duration_ms?: number | null;
615
+ }
616
+ export interface ChatRequest {
617
+ session_id?: string | null;
618
+ message?: string | null;
619
+ /** Additional user messages for this turn, in order — someone who kept typing while
620
+ * the agent was busy. Each is stored as its own message and the agent replies once
621
+ * to all of them, so the transcript shows what was actually said rather than one
622
+ * merged prompt. */
623
+ messages?: string[] | null;
624
+ /** Multimodal inputs (images/audio/video/files) for this turn. */
625
+ attachments?: Attachment[] | null;
626
+ /** Modalities the model may return, e.g. ["text","image"]. Bounded by the token's
627
+ * output:<modality> capabilities; ignored by text-only models. */
628
+ output_modalities?: string[] | null;
629
+ tool_results?: ClientToolResult[] | null;
630
+ /** Resume a turn paused on `ask_user`. The wording the agent reads is composed
631
+ * server-side from the question it actually asked, so the transcript can't drift
632
+ * from what was on screen. */
633
+ question_answers?: QuestionAnswer[] | null;
634
+ /** The user pressed Done on a page the agent handed them. Resumes a turn that
635
+ * stopped on a blocking hand-off — the button IS the answer, so nothing else is
636
+ * needed with it. */
637
+ handoff_done?: boolean;
638
+ client_tools?: ClientTool[] | null;
639
+ /** End-user-supplied MCP servers for this turn, merged with the tenant's own
640
+ * project-level servers. Only honored when the token has the `mcp:manage`
641
+ * capability (the project's `allowMcp` ceiling); otherwise ignored. */
642
+ mcp_servers?: Array<{
643
+ name: string;
644
+ url: string;
645
+ transport?: "sse" | "streamable_http";
646
+ headers?: Record<string, string>;
647
+ }> | null;
648
+ allowed_tools?: string[] | null;
649
+ document_ids?: string[] | null;
650
+ tags?: string[] | null;
651
+ enable_rag?: boolean;
652
+ enable_scheduling?: boolean;
653
+ enable_memory?: boolean;
654
+ enable_web_search?: boolean;
655
+ /** Opt-in: bind only the most relevant tools when the action space is large. */
656
+ enable_action_space?: boolean;
657
+ /** Let the agent run commands / edit files in an isolated sandbox. Needs the
658
+ * `computer` capability and a configured backend; the sandbox is only provisioned
659
+ * if the agent actually uses it. */
660
+ enable_computer?: boolean;
661
+ /** Attach a specific existing sandbox to this turn (reconnect to a prior session).
662
+ * Omitted = the sandbox bound to this chat session, else a fresh one. */
663
+ computer_session_id?: string | null;
664
+ /** Let the agent run read-only SQL against connected query-mode data sources. */
665
+ enable_data_query?: boolean;
666
+ /** Let the agent drive a real browser — click, type, scroll, wait, capture — rather
667
+ * than fetching one page at a time. Needs the `browser` capability and the browser
668
+ * service; `web_search`/`browse_url` work without it. */
669
+ enable_browser?: boolean;
670
+ /** Let the agent keep a todo list for this conversation. Needs the `todo` capability. */
671
+ enable_todo?: boolean;
672
+ /** Let the agent hand a self-contained piece of work to another agent that runs
673
+ * alongside it. Needs the `subagents` capability AND models chosen for it in the
674
+ * dashboard (Project → LLM & limits → Subagent models).
675
+ *
676
+ * Turning it off stops new delegation; it does not abandon work already running —
677
+ * a subagent started earlier still reports back into this conversation. */
678
+ enable_subagents?: boolean;
679
+ /** Let the agent pause and ask the user a structured multiple-choice question.
680
+ * Needs the `ask_user` capability. Set false for any caller that cannot answer —
681
+ * a batch job, a webhook, a scheduled task — so the agent never stalls waiting. */
682
+ enable_ask_user?: boolean;
683
+ model?: string | null;
684
+ /** Reasoning effort (minimal|low|medium|high), bounded by the token's max_effort. */
685
+ reasoning_effort?: "minimal" | "low" | "medium" | "high" | null;
686
+ system_prompt?: string | null;
687
+ temperature?: number;
688
+ user_ref?: string | null;
689
+ }
690
+ export interface PendingToolCall {
691
+ id: string;
692
+ name: string;
693
+ args: Record<string, unknown>;
694
+ }
695
+ export interface Citation {
696
+ document_id: string;
697
+ chunk_index: number;
698
+ filename: string | null;
699
+ page: number | null;
700
+ score: number | null;
701
+ quote: string | null;
702
+ }
703
+ /** A unified source for UI rendering (clickable pill) — a document citation, a
704
+ * web result/page, or a database query. `type` = "document" | "web" | "query"
705
+ * (query sources carry the executed SQL in `snippet` for verifiability). */
706
+ export interface Source {
707
+ type: "document" | "web" | "query" | string;
708
+ title?: string | null;
709
+ url?: string | null;
710
+ snippet?: string | null;
711
+ document_id?: string | null;
712
+ page?: number | null;
713
+ score?: number | null;
714
+ screenshot?: string | null;
715
+ }
716
+ export interface ChatResponse {
717
+ session_id: string;
718
+ content: string;
719
+ requires_action: boolean;
720
+ tool_calls: PendingToolCall[];
721
+ /** The agent paused to ask the user something. Arrives with `requires_action` and
722
+ * an EMPTY `tool_calls`: a question is not the client's work to execute, so tool
723
+ * auto-dispatch can never answer it on the user's behalf. Resume by sending
724
+ * `question_answers` (or let `chat.run`/`chat.stream` do it via `onQuestion`). */
725
+ questions: PendingQuestions[];
726
+ /** The agent's plan for this session, when the todo family ran this turn. */
727
+ todos: TodoItem[];
728
+ /** Every subagent of this conversation, including ones still working — a turn ending
729
+ * is not a reason to stop showing work that has not. Empty unless it has delegated. */
730
+ subagents: SubagentState[];
731
+ /** Set only when history had to be trimmed or summarized to fit the window. */
732
+ context?: ContextReport | null;
733
+ citations: Citation[];
734
+ sources: Source[];
735
+ /** Non-text outputs of the turn: generated media, and files the agent exported
736
+ * from its sandbox. `url` is signed fresh per response — don't persist it. */
737
+ attachments: Attachment[];
738
+ /** Guardrail actions taken this turn, e.g. "pii:EMAIL", "ungrounded", "injection". */
739
+ guard_flags: string[];
740
+ /** A reasoning model's thinking for this turn, else "". Live only — it is not stored
741
+ * and is never sent back to the model, so it won't appear in session history. */
742
+ reasoning?: string;
743
+ /** Non-normal stop reason, else null. "max_tool_iterations" when a token's
744
+ * tool-loop ceiling was hit. Language-neutral — localize it yourself. */
745
+ finish_reason?: string | null;
746
+ }
747
+ export interface SessionOut {
748
+ id: string;
749
+ title: string | null;
750
+ user_ref: string | null;
751
+ created_at: string;
752
+ updated_at: string;
753
+ }
754
+ export interface MessageOut {
755
+ id: string;
756
+ role: "user" | "assistant" | "tool" | "system" | string;
757
+ content: string;
758
+ extra: Record<string, unknown>;
759
+ created_at: string;
760
+ }
761
+ export type TaskKind = "once" | "recurring";
762
+ export interface TaskAction {
763
+ type?: "agent" | "webhook";
764
+ session_id?: string | null;
765
+ prompt?: string | null;
766
+ system_prompt?: string | null;
767
+ model?: string | null;
768
+ callback_url?: string | null;
769
+ url?: string | null;
770
+ method?: string;
771
+ headers?: Record<string, string>;
772
+ payload?: Record<string, unknown>;
773
+ }
774
+ export interface TaskCreate {
775
+ name: string;
776
+ kind: TaskKind;
777
+ run_at?: string | null;
778
+ cron?: string | null;
779
+ interval_seconds?: number | null;
780
+ timezone?: string;
781
+ action: TaskAction;
782
+ }
783
+ export interface TaskOut {
784
+ id: string;
785
+ name: string;
786
+ kind: TaskKind;
787
+ status: string;
788
+ created_by: string;
789
+ run_at: string | null;
790
+ cron: string | null;
791
+ interval_seconds: number | null;
792
+ timezone: string;
793
+ action: Record<string, unknown>;
794
+ run_count: number;
795
+ last_run_at: string | null;
796
+ next_run_at: string | null;
797
+ last_error: string | null;
798
+ created_at: string;
799
+ }
800
+ export type ConnectorType = "postgres" | "http" | string;
801
+ export interface SyncConfig {
802
+ name: string;
803
+ connector_type: ConnectorType;
804
+ /**
805
+ * "sync" (default) copies records into the vector store (RAG over the data).
806
+ * "query" is live query-in-place: the agent runs read-only SQL against the source
807
+ * at question time — the right mode for structured/analytical data (postgres only).
808
+ */
809
+ mode?: "sync" | "query";
810
+ /** Connector-specific config (dsn/query, token/repo, ...). Holds secrets. */
811
+ config: Record<string, unknown>;
812
+ /** Auto-refresh cadence in seconds; 0 = manual only. Default 3600. Ignored for query mode. */
813
+ sync_interval_seconds?: number;
814
+ tags?: string[];
815
+ visibility?: "self" | "private" | "shared" | "groups" | "tenant";
816
+ visibility_scope?: string | null;
817
+ acl_roles?: string[];
818
+ acl_groups?: string[];
819
+ }
820
+ export interface SourceOut {
821
+ id: string;
822
+ name: string;
823
+ connector_type: ConnectorType;
824
+ mode: "sync" | "query";
825
+ /** Config key names only — secret values are never returned. */
826
+ config_keys: string[];
827
+ sync_interval_seconds: number;
828
+ tags: string[];
829
+ visibility: string;
830
+ visibility_scope: string | null;
831
+ status: string;
832
+ error: string | null;
833
+ record_count: number;
834
+ last_synced_at: string | null;
835
+ created_at: string;
836
+ }
837
+ export interface AuditEntry {
838
+ id: string;
839
+ subject: string | null;
840
+ action: string;
841
+ resource_type: string | null;
842
+ resource_id: string | null;
843
+ metadata: Record<string, unknown>;
844
+ created_at: string;
845
+ }
846
+ export interface ForgetResult {
847
+ subject: string;
848
+ documents_deleted: number;
849
+ sessions_deleted: number;
850
+ /** Sandboxes destroyed — they hold the subject's files too. */
851
+ sandboxes_destroyed: number;
852
+ vectors_purged: boolean;
853
+ }
854
+ export type StreamEvent = {
855
+ event: "run";
856
+ data: {
857
+ run_id: string;
858
+ };
859
+ } | {
860
+ event: "start";
861
+ data: {
862
+ session_id: string;
863
+ };
864
+ } | {
865
+ event: "token";
866
+ data: {
867
+ delta: string;
868
+ };
869
+ } | {
870
+ event: "tool_start";
871
+ data: {
872
+ name: string;
873
+ input: unknown;
874
+ };
875
+ } | {
876
+ event: "tool_end";
877
+ data: {
878
+ name: string;
879
+ output: string;
880
+ };
881
+ }
882
+ /** A sandbox command's output WHILE it runs, a chunk at a time. Arrives between a
883
+ * `tool_start` for `computer_bash` and its `tool_end`, so a long build or test run
884
+ * can be watched instead of appearing to hang. `command_id` groups the chunks of one
885
+ * command; the same text also arrives whole in `tool_end`, so ignoring these events
886
+ * costs nothing but the liveness. */
887
+ | {
888
+ event: "command_output";
889
+ data: {
890
+ command_id: string;
891
+ command: string;
892
+ stream: "stdout" | "stderr";
893
+ delta: string;
894
+ };
895
+ }
896
+ /** A command the agent started in the background has finished and been reported to it.
897
+ * Arrives at a step boundary — the agent is told between its own moves, never
898
+ * mid-tool. If the turn had already ended when the command finished, the report
899
+ * arrives as a NEW turn in the same session instead (like a scheduled run), which your
900
+ * `onTurn`/webhook path already handles. */
901
+ | {
902
+ event: "command_finished";
903
+ data: {
904
+ job_id: string;
905
+ command: string;
906
+ exit_code: number | null;
907
+ status: string;
908
+ };
909
+ } | {
910
+ event: "citations";
911
+ data: {
912
+ citations: Citation[];
913
+ };
914
+ } | {
915
+ event: "sources";
916
+ data: {
917
+ sources: Source[];
918
+ };
919
+ } | {
920
+ event: "guardrail";
921
+ data: {
922
+ stage: "input" | "output";
923
+ flags: string[];
924
+ content?: string;
925
+ };
926
+ }
927
+ /** Files for the user. Arrives DURING the turn, as each is produced, and once more
928
+ * before `done`; every frame carries the whole list so far. */
929
+ | {
930
+ event: "attachments";
931
+ data: {
932
+ attachments: Attachment[];
933
+ };
934
+ } | {
935
+ event: "todos";
936
+ data: {
937
+ todos: TodoItem[];
938
+ };
939
+ }
940
+ /** One subagent moved: started, changed what it is doing, asked the main agent
941
+ * something, or finished. Carries that ONE subagent — merge it into what you are
942
+ * showing by `ref`. */
943
+ | {
944
+ event: "subagent";
945
+ data: {
946
+ subagent: SubagentState;
947
+ };
948
+ }
949
+ /** Every subagent of the conversation, sent once before `done`. Render from this
950
+ * rather than from accumulated `subagent` frames where you can: a reconnect or a
951
+ * missed frame cannot leave it wrong. */
952
+ | {
953
+ event: "subagents";
954
+ data: {
955
+ subagents: SubagentState[];
956
+ };
957
+ } | {
958
+ event: "questions";
959
+ data: {
960
+ questions: PendingQuestions[];
961
+ };
962
+ }
963
+ /** The agent has handed a page to the user — open a viewer, poll `browserFrame`
964
+ * and relay clicks with `browserInput`. It then asks a question and waits. */
965
+ | {
966
+ event: "browser_handoff";
967
+ data: Handoff;
968
+ }
969
+ /** History management. `compacting` arrives BEFORE the turn produces anything —
970
+ * summarizing a long transcript is a full model round-trip, so show a
971
+ * "compacting conversation…" state rather than a blank screen — and is followed by
972
+ * a `done` frame with the outcome. `compacting` never fires for a plain trim,
973
+ * which is instant. */
974
+ | {
975
+ event: "context";
976
+ data: ContextProgress | ContextReport;
977
+ }
978
+ /** A reasoning model's thinking, streamed as it happens — arrives BEFORE (and
979
+ * between) `token` frames. Render it, or just use the first one to show that the
980
+ * model is working. */
981
+ | {
982
+ event: "reasoning";
983
+ data: {
984
+ delta: string;
985
+ };
986
+ } | {
987
+ event: "done";
988
+ data: {
989
+ session_id: string;
990
+ content: string;
991
+ requires_action: boolean;
992
+ tool_calls: PendingToolCall[];
993
+ questions: PendingQuestions[];
994
+ todos: TodoItem[];
995
+ subagents: SubagentState[];
996
+ handoff?: Handoff | null;
997
+ guard_flags: string[];
998
+ context?: ContextReport | null;
999
+ citations: Citation[];
1000
+ sources: Source[];
1001
+ attachments: Attachment[];
1002
+ reasoning?: string;
1003
+ finish_reason?: string | null;
1004
+ };
1005
+ } | {
1006
+ event: "cancelled";
1007
+ data: {
1008
+ run_id: string;
1009
+ };
1010
+ } | {
1011
+ event: "error";
1012
+ data: {
1013
+ detail: string;
1014
+ };
1015
+ };
1016
+ export interface StreamHandlers {
1017
+ onToken?: (delta: string, full: string) => void;
1018
+ onEvent?: (event: StreamEvent) => void;
1019
+ onToolStart?: (name: string, input: unknown) => void;
1020
+ onToolEnd?: (name: string, output: string) => void;
1021
+ onCitations?: (citations: Citation[]) => void;
1022
+ onSources?: (sources: Source[]) => void;
1023
+ /** A guardrail acted on the turn (input block/redaction or output redaction/refusal). */
1024
+ onGuardrail?: (stage: "input" | "output", flags: string[], content?: string) => void;
1025
+ /** Non-text outputs: a file the agent handed over, a screenshot it took, media it
1026
+ * generated. Fires as soon as one is produced — mid-turn, right after the tool call
1027
+ * that made it — and again before `done` with the complete list.
1028
+ *
1029
+ * Each call carries EVERY file of the turn so far, not just the new one, so render
1030
+ * from the list rather than appending: that way a reconnect or a missed frame cannot
1031
+ * leave a gap, and re-rendering is idempotent. Files the agent kept for itself
1032
+ * (a screenshot taken only to be cropped) are never in it. */
1033
+ onAttachments?: (attachments: Attachment[]) => void;
1034
+ /** A reasoning model is thinking. `delta` is the newest thinking text and `full` the
1035
+ * accumulated trace. With a reasoning model this fires well before the first token,
1036
+ * which is what a "Thinking…" indicator should key off — otherwise the UI looks
1037
+ * frozen for as long as the model reasons. */
1038
+ onReasoning?: (delta: string, full: string) => void;
1039
+ /** The agent's plan changed. Fires whenever the turn touched its todo list —
1040
+ * render it as a live checklist so the user can see where the agent is. */
1041
+ onTodos?: (todos: TodoItem[]) => void;
1042
+ /** A subagent started, moved, asked the main agent something, or finished.
1043
+ *
1044
+ * Called with EVERY subagent of the conversation, not just the one that moved, for
1045
+ * the same reason `onAttachments` is: render from the list and a dropped frame
1046
+ * cannot leave the panel wrong. One that is `running` after the turn ends is still
1047
+ * running — keep showing it, and it will report into the next turn. */
1048
+ onSubagents?: (subagents: SubagentState[]) => void;
1049
+ /** A sandbox command's output as it is produced. Append it to a live pane keyed by
1050
+ * `command_id` — a long build should look like it is working, and a person watching
1051
+ * needs to be able to tell a slow command from a stuck one. */
1052
+ onCommandOutput?: (chunk: {
1053
+ command_id: string;
1054
+ command: string;
1055
+ stream: "stdout" | "stderr";
1056
+ delta: string;
1057
+ }) => void;
1058
+ /** A background command finished and the agent has just been told. Show it: the user
1059
+ * watched the command start and has been waiting longer than the agent has. */
1060
+ onCommandFinished?: (job: {
1061
+ job_id: string;
1062
+ command: string;
1063
+ exit_code: number | null;
1064
+ status: string;
1065
+ }) => void;
1066
+ /** The agent paused on a question. Without `onQuestion` the stream simply ends
1067
+ * with these on `done`, and you resume it yourself with `question_answers`. */
1068
+ onQuestions?: (pending: PendingQuestions[]) => void;
1069
+ /** The agent needs the user to act on a page themselves. */
1070
+ onBrowserHandoff?: (h: Handoff) => void;
1071
+ /** History had to be trimmed or summarized to fit the model's window. Fires only
1072
+ * when it actually happened — useful for spotting a budget set too low. */
1073
+ onContext?: (event: ContextProgress | ContextReport) => void;
1074
+ /** Answer the agent's questions and continue the stream automatically. Show the
1075
+ * picker, resolve with what the user chose (or `{ chat_instead: true }` if they
1076
+ * would rather keep talking), and `done` resolves only once the agent finishes. */
1077
+ onQuestion?: QuestionHandler;
1078
+ /** Called each time the client (re)connects, with the resume attempt count. */
1079
+ onReconnect?: (attempt: number) => void;
1080
+ signal?: AbortSignal;
1081
+ /** Max reconnection attempts after a drop (default 10). */
1082
+ maxRetries?: number;
1083
+ /** Extra client tools for this stream (merged over any registered on the client).
1084
+ * When any tools are available, the stream auto-executes tool calls and resumes,
1085
+ * so `done` only resolves once the agent finishes (no `requires_action`). */
1086
+ tools?: ClientToolDef[];
1087
+ /** Fires before each batch of client tools runs. */
1088
+ onToolCalls?: (calls: PendingToolCall[]) => void;
1089
+ /** Max auto tool-dispatch rounds before giving up (default 10). */
1090
+ maxToolRounds?: number;
1091
+ }
1092
+ /** Handlers for `chat.sessions.watch`. */
1093
+ export interface WatchHandlers {
1094
+ /** A message that appeared in the session which this client did not stream.
1095
+ * A fired reminder arrives as its prompt (`role: "user"`) followed by the
1096
+ * agent's answer (`role: "assistant"`) — filter to what your UI should show. */
1097
+ onMessage: (message: MessageOut) => void;
1098
+ onError?: (error: Error) => void;
1099
+ /** Poll interval in ms for the fallback path (default 4000). */
1100
+ intervalMs?: number;
1101
+ /** Force polling instead of the live stream (mostly for testing). */
1102
+ poll?: boolean;
1103
+ /** Fires when the transport is decided, so a UI can say "live" vs "polling". */
1104
+ onTransport?: (transport: "stream" | "poll") => void;
1105
+ signal?: AbortSignal;
1106
+ }
1107
+ export interface SessionWatch {
1108
+ /** Stop watching. Safe to call more than once. */
1109
+ stop: () => void;
1110
+ /** Treat everything currently in the session as already delivered. Called for
1111
+ * you whenever this client streams a turn on the watched session. */
1112
+ resync: () => Promise<void>;
1113
+ }
1114
+ export interface ChatDone {
1115
+ session_id: string;
1116
+ content: string;
1117
+ requires_action: boolean;
1118
+ tool_calls: PendingToolCall[];
1119
+ /** Questions the agent paused on. Empty once `onQuestion` has answered them. */
1120
+ questions: PendingQuestions[];
1121
+ /** The agent's plan for this session, when the todo family ran this turn. */
1122
+ todos: TodoItem[];
1123
+ /** Every subagent of the conversation. Some may still be `running` — the turn
1124
+ * finishing does not finish them, and they report into the next one. */
1125
+ subagents: SubagentState[];
1126
+ /** Set when the agent put a page in front of the user. */
1127
+ handoff?: Handoff | null;
1128
+ /** Guardrails that acted on this turn (also announced live as a `guardrail` event). */
1129
+ guard_flags: string[];
1130
+ /** Set only when history had to be trimmed or summarized to fit the window. */
1131
+ context?: ContextReport | null;
1132
+ citations: Citation[];
1133
+ sources: Source[];
1134
+ attachments: Attachment[];
1135
+ /** The full thinking trace of a reasoning model, else "" (streamed as `reasoning`). */
1136
+ reasoning?: string;
1137
+ /** Non-normal stop reason, else null (e.g. "max_tool_iterations"). */
1138
+ finish_reason?: string | null;
1139
+ }
1140
+ export interface StreamHandle extends Promise<ChatDone> {
1141
+ /** Resolves with the terminal `done` payload; rejects on server error/cancel/abort.
1142
+ * The handle is itself awaitable — `await af.chat.stream(...)` returns this. */
1143
+ done: Promise<ChatDone>;
1144
+ /** Stop listening locally. Server-side generation KEEPS running (resume later). */
1145
+ disconnect: () => void;
1146
+ /** Terminate server-side generation (stops model spend), then stop listening.
1147
+ * Best-effort: fires POST /chat/stream/{run_id}/cancel once the run id is known. */
1148
+ cancel: () => Promise<void>;
1149
+ /** The run id (available once the first frame arrives). */
1150
+ runId: () => string | undefined;
1151
+ /** Send a message INTO this running turn. Needs the `steer` capability.
1152
+ *
1153
+ * The agent picks it up at its next step — after any tool in flight finishes — and
1154
+ * reads it as the user speaking. Use it to correct work you can already see going
1155
+ * wrong; to simply say the next thing, wait for `done` and send a normal message.
1156
+ *
1157
+ * Resolves `false` if the turn finished first, in which case send it as a normal
1158
+ * message instead — the caller has to know, so it is not swallowed. */
1159
+ steer: (message: string) => Promise<boolean>;
1160
+ }
1161
+ export declare class AgentCancelledError extends Error {
1162
+ constructor();
1163
+ }
1164
+ export type UploadInput = Blob | ArrayBuffer | Uint8Array;
1165
+ /** Who may retrieve a document/source once it's indexed. `self` is the strictest —
1166
+ * not even an admin token sees it. Matched on every search, so a user can't get an
1167
+ * answer grounded in something they may not read. */
1168
+ export type Visibility = "self" | "private" | "shared" | "groups" | "tenant";
1169
+ /** Access control shared by uploads, presigned uploads and patches. */
1170
+ export interface AclFields {
1171
+ visibility?: Visibility;
1172
+ /** Path prefix `shared` visibility applies under, e.g. "acme:finance". */
1173
+ visibility_scope?: string | null;
1174
+ /** Roles/groups (matched against the reader's token) for `groups` visibility. */
1175
+ acl_roles?: string[];
1176
+ acl_groups?: string[];
1177
+ }
1178
+ export interface UploadOptions extends AclFields {
1179
+ filename: string;
1180
+ contentType?: string;
1181
+ tags?: string[];
1182
+ /** Parallel part uploads (default 4). */
1183
+ concurrency?: number;
1184
+ /** Per-part retry attempts (default 5). */
1185
+ maxRetries?: number;
1186
+ /** Part-completion progress: fires after each part finishes (not byte-level;
1187
+ * fetch has no upload-progress event without XHR). */
1188
+ onProgress?: (sent: number, total: number) => void;
1189
+ signal?: AbortSignal;
1190
+ }
1191
+ export interface DownloadOptions {
1192
+ /** Range chunk size in bytes (default 8 MiB). */
1193
+ chunkSize?: number;
1194
+ maxRetries?: number;
1195
+ onProgress?: (received: number, total: number | null) => void;
1196
+ signal?: AbortSignal;
1197
+ }
1198
+ export declare class AgentApiError extends Error {
1199
+ status: number;
1200
+ detail: unknown;
1201
+ constructor(status: number, detail: unknown);
1202
+ }
1203
+ export declare class AgentStreamError extends Error {
1204
+ constructor(message: string);
1205
+ }
1206
+ export declare class AgentFramework {
1207
+ readonly baseUrl: string;
1208
+ private readonly opts;
1209
+ private readonly _fetch;
1210
+ private readonly toolRegistry;
1211
+ /** Active session watchers, so a streamed turn can mark its own messages seen. */
1212
+ private readonly watchers;
1213
+ /** The bearer in use: `opts.token` initially, replaced on refresh. */
1214
+ private currentToken?;
1215
+ /** The last token the server rejected, so we don't keep re-sending it. */
1216
+ private rejectedToken?;
1217
+ /** In-flight refresh, shared so N concurrent 401s mint one token, not N. */
1218
+ private refreshing?;
1219
+ constructor(opts: ClientOptions);
1220
+ /** Seconds before a token's own expiry at which we stop using it.
1221
+ *
1222
+ * Reacting to a 401 covers most requests, because the failed one is replayed. It
1223
+ * cannot cover an upload: a FormData body is a stream that may already have been
1224
+ * consumed, so a multipart request that 401s cannot be replayed and the expiry
1225
+ * surfaces as a failed upload with nothing the caller can do. Nor does it help a
1226
+ * long-running turn that starts with a token about to lapse.
1227
+ *
1228
+ * So a token is retired slightly before it expires, from the `exp` it carries. The
1229
+ * margin covers clock skew between the browser and the server and the round trip
1230
+ * itself. The 401 path stays exactly as it was — this only avoids reaching it. */
1231
+ /** Everything behind `chat.sessions.handoff()`.
1232
+ *
1233
+ * A closure rather than a class because all of it is state that dies with the
1234
+ * hand-off, and because the whole point is that the caller holds one object and
1235
+ * nothing else. See the notes on `HandoffController` for what it takes off them. */
1236
+ private buildHandoff;
1237
+ private static readonly EXPIRY_MARGIN_S;
1238
+ /** Seconds until this JWT expires, or null if it does not say.
1239
+ *
1240
+ * Reads `exp` without verifying anything: the signature is the server's business and
1241
+ * a token we cannot parse is simply used as-is, which is the behaviour that existed
1242
+ * before. Never throws — a malformed token must not break a request that might have
1243
+ * worked. */
1244
+ private tokenLifeLeft;
1245
+ /** The bearer for the next request. */
1246
+ private resolveToken;
1247
+ /** Whether pre-expiry refreshing is still worth attempting; see `resolveToken`. */
1248
+ private proactive;
1249
+ /** Ask `getToken` for a replacement after a 401, at most one call in flight so N
1250
+ * concurrent rejections mint one token rather than N. Returns undefined when there
1251
+ * is no callback (or it failed) — the signal to let the 401 through untouched. */
1252
+ private refreshToken;
1253
+ /** Register a client-side tool (handler run when the agent calls it). */
1254
+ registerTool(tool: ClientToolDef): this;
1255
+ registerTools(tools: ClientToolDef[]): this;
1256
+ /** Merge the client-level registry with any per-call tools (per-call wins). */
1257
+ private resolveTools;
1258
+ /** `bearer` overrides token resolution — used to replay a request with the token a
1259
+ * refresh just produced, instead of asking for one again. */
1260
+ private authHeaders;
1261
+ /** Like `request`, but hands back the raw Response (for binary payloads). */
1262
+ raw(method: string, path: string, init?: {
1263
+ query?: Record<string, unknown>;
1264
+ body?: unknown;
1265
+ form?: FormData;
1266
+ signal?: AbortSignal;
1267
+ headers?: Record<string, string>;
1268
+ }): Promise<Response>;
1269
+ request<T = unknown>(method: string, path: string, init?: {
1270
+ query?: Record<string, unknown>;
1271
+ body?: unknown;
1272
+ form?: FormData;
1273
+ signal?: AbortSignal;
1274
+ headers?: Record<string, string>;
1275
+ }): Promise<T>;
1276
+ /**
1277
+ * Mint a narrower token from the current credential — for a backend holding a
1278
+ * project key that hands short-lived, per-end-user tokens to its frontend. Every
1279
+ * field is intersected/clamped with what the caller already holds, so this can only
1280
+ * ever narrow: a restricted token cannot mint a broader one.
1281
+ */
1282
+ auth: {
1283
+ token: (body?: TokenRequest) => Promise<TokenResponse>;
1284
+ };
1285
+ private adminHeaders;
1286
+ tenants: {
1287
+ create: (body: {
1288
+ slug: string;
1289
+ name: string;
1290
+ settings?: Record<string, unknown>;
1291
+ }) => Promise<TenantCreated>;
1292
+ list: () => Promise<TenantOut[]>;
1293
+ /** Merge keys into a tenant's settings (model routing, retrieval models, allowed
1294
+ * browser origins, guardrail policy...). Merged, not replaced — omitted keys are
1295
+ * left alone. */
1296
+ patchSettings: (id: string, settings: Record<string, unknown>) => Promise<TenantOut>;
1297
+ };
1298
+ /** Liveness (`health`) and dependency readiness (`ready`). No auth required — use
1299
+ * them from a probe or a status page. `ready` reports "degraded" with a per-check
1300
+ * reason rather than failing, so you can tell "up but Postgres is unreachable"
1301
+ * apart from "down". */
1302
+ health: {
1303
+ live: () => Promise<{
1304
+ status: string;
1305
+ }>;
1306
+ ready: () => Promise<{
1307
+ status: string;
1308
+ } & Record<string, string>>;
1309
+ };
1310
+ tools: {
1311
+ list: () => Promise<{
1312
+ tools: ToolInfo[];
1313
+ }>;
1314
+ };
1315
+ chat: {
1316
+ /** One round-trip. Returns `requires_action` + `tool_calls` for you to handle
1317
+ * manually — use `chat.run` to auto-dispatch client tools instead. */
1318
+ send: (body: ChatRequest) => Promise<ChatResponse>;
1319
+ /** Blocking chat that AUTO-EXECUTES registered client tools: it sends the
1320
+ * message, and whenever the agent asks for client tools it runs their
1321
+ * handlers, submits the results, and repeats until the agent is done. */
1322
+ run: (opts?: RunOptions) => Promise<ChatResponse>;
1323
+ /** Streaming chat. When client tools are registered/passed, tool calls are
1324
+ * auto-executed and the stream resumes, so `done` only resolves when the
1325
+ * agent finishes (never with `requires_action`). */
1326
+ stream: (body: ChatRequest, handlers?: StreamHandlers) => StreamHandle;
1327
+ /** Send a message into a turn that is still running (needs `steer`).
1328
+ *
1329
+ * Prefer `handle.steer(...)` when you have the stream handle. This is for a
1330
+ * caller that only kept the run id. Resolves false if the turn already ended. */
1331
+ steer: (runId: string, message: string) => Promise<boolean>;
1332
+ /** Resume a turn the agent paused on a question.
1333
+ *
1334
+ * Use it when you drive the picker yourself (`chat.send` returned `questions`);
1335
+ * with `onQuestion` on `chat.run`/`chat.stream` this happens for you.
1336
+ *
1337
+ * const r = await ai.chat.send({ message: "migrate the fetchers" });
1338
+ * if (r.questions.length) {
1339
+ * const picked = await showPicker(r.questions[0]); // your UI
1340
+ * await ai.chat.answer(r.session_id, {
1341
+ * tool_call_id: r.questions[0].tool_call_id,
1342
+ * answers: [{ question_id: "Scope", selected: [picked] }],
1343
+ * });
1344
+ * }
1345
+ *
1346
+ * Pass `{ chat_instead: true, message }` when the user would rather keep
1347
+ * talking — the agent drops the question instead of re-asking it. */
1348
+ /** Tell the agent the user is finished with a page it handed over, resuming the
1349
+ * turn that stopped on it. */
1350
+ handoffDone: (sessionId: string) => Promise<ChatResponse>;
1351
+ answer: (sessionId: string, ...answers: QuestionAnswer[]) => Promise<ChatResponse>;
1352
+ sessions: {
1353
+ list: (userRef?: string) => Promise<SessionOut[]>;
1354
+ /** The agent's plan for this conversation — what a UI renders on a page load,
1355
+ * or between turns. A turn that touched the list also returns it directly. */
1356
+ todos: (sessionId: string) => Promise<TodoItem[]>;
1357
+ /** What the handed-over page looks like right now. Needs `browser_handoff`.
1358
+ *
1359
+ * Poll this while the user has control, and pass the same `selector` the agent
1360
+ * handed over so the view doesn't jump. Re-read rather than sent once because
1361
+ * the thing a person has to act on — a CAPTCHA challenge grid — often only
1362
+ * appears after their first click. */
1363
+ browserFrame: (sessionId: string, selector?: string) => Promise<BrowserFrame>;
1364
+ /** Watch the handed-over page live, instead of asking for pictures of it.
1365
+ *
1366
+ * Frames are pushed as Chrome repaints. Polling `browserFrame` gives roughly one
1367
+ * frame a second, which is fine for watching a page settle and not enough to act
1368
+ * on one — a slider puzzle at that rate can't be completed.
1369
+ *
1370
+ * `onClip` fires only when the region worth showing MOVES (an overlay appears,
1371
+ * the page scrolls). Crop client-side against the last clip: the frames are the
1372
+ * whole viewport, so `origin` is both where to crop and what to echo back with a
1373
+ * pointer event. */
1374
+ browserStream: (sessionId: string, handlers: BrowserStreamHandlers, selector?: string) => BrowserStream;
1375
+ /** Has the person finished with the page they were handed?
1376
+ *
1377
+ * Call it after they let go of the mouse. A separate small model looks at the
1378
+ * region and reports one of four words; when `done`, press Done for them with
1379
+ * `chat.handoffDone`. Rate-limited server-side, so calling it on every mouse-up
1380
+ * is fine — the extra calls come back `checked: false`. */
1381
+ handoffCheck: (sessionId: string) => Promise<HandoffCheck>;
1382
+ /** Relay one thing the user did into the page, and get the resulting frame.
1383
+ *
1384
+ * Send `x`/`y` in IMAGE space along with the `origin` of the frame they clicked
1385
+ * — echo it back rather than adding it yourself, so a frame that moved between
1386
+ * render and click can't displace the click.
1387
+ *
1388
+ * Watching `browserStream`? Set `want_frame: false` — otherwise every pointer
1389
+ * move renders a JPEG that arrives after the stream already showed it. */
1390
+ browserInput: (sessionId: string, input: BrowserInputEvent) => Promise<BrowserFrame>;
1391
+ /** The whole hand-off, driven for you.
1392
+ *
1393
+ * Call it when a `browser_handoff` event arrives with `active: true`, give it an
1394
+ * `<img>`, and you are done: it opens the live stream, crops each frame to the
1395
+ * region the agent pointed at, maps clicks and drags back into page space, relays
1396
+ * them, and asks the server whether the gesture finished the job — pressing Done
1397
+ * for the user when it did.
1398
+ *
1399
+ * const view = ai.chat.sessions.handoff(sessionId, handoff, {
1400
+ * onView: () => render(),
1401
+ * onEnded: ({ resume }) => resume && ai.chat.handoffDone(sessionId),
1402
+ * });
1403
+ * const detach = view.attach(imgElement); // in your effect
1404
+ * // …later: detach(); view.close();
1405
+ *
1406
+ * Nothing here needs a browser until `attach()`, and `attach()` needs only an
1407
+ * object shaped like an image — so importing the SDK on a server is unaffected. */
1408
+ handoff: (sessionId: string, handoff: Handoff, options?: HandoffOptions) => HandoffController;
1409
+ /** A few things the user might say next, in their voice — render as buttons and
1410
+ * send the clicked one as an ordinary message. Needs the `followups` capability.
1411
+ *
1412
+ * Call it AFTER the turn's `done`, not before: it is a separate request on
1413
+ * purpose, so the answer is never held up by a suggestion nobody asked for.
1414
+ * The agent is not told a message was suggested rather than typed, and nothing
1415
+ * is stored. Returns [] when nothing sensible follows or generation failed —
1416
+ * a missing affordance is not worth an error. */
1417
+ followups: (sessionId: string, count?: number) => Promise<{
1418
+ suggestions: string[];
1419
+ }>;
1420
+ /** One or two sentences on where this conversation got to, for a user coming
1421
+ * back after a while. Needs the `recap` capability.
1422
+ *
1423
+ * Your client decides when to ask — only it knows the tab has been idle. This
1424
+ * is not context compaction: that summarizes FOR the model and is replayed to
1425
+ * it, whereas the agent never sees this. Returns "" on failure. */
1426
+ recap: (sessionId: string) => Promise<{
1427
+ recap: string;
1428
+ }>;
1429
+ messages: (sessionId: string) => Promise<MessageOut[]>;
1430
+ delete: (sessionId: string) => Promise<void>;
1431
+ /**
1432
+ * Deliver messages that appear in a session which this client did not stream.
1433
+ *
1434
+ * A scheduled reminder (`schedule_reminder`, or any task with an `agent`
1435
+ * action) runs server-side and appends its answer to the session — there is no
1436
+ * stream to listen to, so without this a reminder fires and the UI never hears
1437
+ * about it. History is snapshotted on start and this client's own turns are
1438
+ * marked seen automatically, so `onMessage` only fires for genuinely new
1439
+ * messages. For server-to-server delivery, give the task a `callback_url`
1440
+ * instead of polling.
1441
+ */
1442
+ watch: (sessionId: string, handlers: WatchHandlers) => SessionWatch;
1443
+ /** Branch a conversation: create a new chat by copying `sessionId` up to and
1444
+ * including `upToMessageId` (or the whole chat if omitted). */
1445
+ fork: (sessionId: string, opts?: {
1446
+ upToMessageId?: string;
1447
+ title?: string;
1448
+ }) => Promise<SessionOut>;
1449
+ };
1450
+ };
1451
+ documents: {
1452
+ list: (query?: {
1453
+ tag?: string;
1454
+ status_filter?: string;
1455
+ }) => Promise<DocumentOut[]>;
1456
+ get: (id: string) => Promise<DocumentOut>;
1457
+ delete: (id: string) => Promise<void>;
1458
+ retrieve: (body: RetrieveRequest) => Promise<RetrievedChunk[]>;
1459
+ /** Small-file convenience upload via multipart form (server proxies to S3). */
1460
+ uploadSimple: (file: UploadInput, opts: AclFields & {
1461
+ filename: string;
1462
+ contentType?: string;
1463
+ tags?: string[];
1464
+ signal?: AbortSignal;
1465
+ }) => Promise<DocumentOut>;
1466
+ /** Resumable presigned multipart upload (direct to S3). */
1467
+ upload: (file: UploadInput, opts: UploadOptions) => Promise<DocumentOut>;
1468
+ /** Poll a document until ingestion finishes (status "ready" or "failed").
1469
+ * Ingestion is async, so querying a just-uploaded doc may return nothing until
1470
+ * this resolves. Throws on "failed" or timeout. */
1471
+ waitReady: (id: string, opts?: {
1472
+ timeoutMs?: number;
1473
+ intervalMs?: number;
1474
+ signal?: AbortSignal;
1475
+ }) => Promise<DocumentOut>;
1476
+ /** Upload a small file AND wait for it to finish ingesting — the common case. */
1477
+ uploadAndWait: (file: UploadInput, opts: AclFields & {
1478
+ filename: string;
1479
+ contentType?: string;
1480
+ tags?: string[];
1481
+ timeoutMs?: number;
1482
+ signal?: AbortSignal;
1483
+ }) => Promise<DocumentOut>;
1484
+ /** Get a presigned GET URL for the raw file. */
1485
+ downloadUrl: (id: string) => Promise<{
1486
+ url: string;
1487
+ expires_in: number;
1488
+ size: number | null;
1489
+ content_type: string | null;
1490
+ }>;
1491
+ /** Resumable ranged download; returns a Blob. */
1492
+ download: (id: string, opts?: DownloadOptions) => Promise<Blob>;
1493
+ /** Update a document's tags / visibility / ACL. */
1494
+ patch: (id: string, body: AclFields & {
1495
+ tags?: string[];
1496
+ }) => Promise<DocumentOut>;
1497
+ /** Reprocess a document (retry a failed ingest, or re-embed with a new model). */
1498
+ reingest: (id: string) => Promise<DocumentOut>;
1499
+ /** Inspect the stored chunks of a document (text + page + index). */
1500
+ chunks: (id: string) => Promise<{
1501
+ chunk_index: number | null;
1502
+ page: number | null;
1503
+ text: string;
1504
+ }[]>;
1505
+ };
1506
+ tasks: {
1507
+ create: (body: TaskCreate) => Promise<TaskOut>;
1508
+ list: (status?: string) => Promise<TaskOut[]>;
1509
+ get: (id: string) => Promise<TaskOut>;
1510
+ cancel: (id: string) => Promise<TaskOut>;
1511
+ };
1512
+ sources: {
1513
+ list: () => Promise<SourceOut[]>;
1514
+ get: (id: string) => Promise<SourceOut>;
1515
+ types: () => Promise<{
1516
+ types: string[];
1517
+ }>;
1518
+ /** Rotate credentials, retag, change the refresh cadence or the ACL. A new
1519
+ * `config` is tested before it replaces the old one, so a bad DSN fails here
1520
+ * (400) instead of at the next scheduled sync. Changing the interval
1521
+ * reschedules; 0 turns auto-refresh off. */
1522
+ patch: (id: string, body: AclFields & {
1523
+ name?: string;
1524
+ config?: Record<string, unknown>;
1525
+ sync_interval_seconds?: number;
1526
+ tags?: string[];
1527
+ }) => Promise<SourceOut>;
1528
+ /** Trigger a sync now; pass `fullRefresh` to re-pull everything. */
1529
+ sync: (id: string, fullRefresh?: boolean) => Promise<SourceOut>;
1530
+ remove: (id: string) => Promise<void>;
1531
+ };
1532
+ audit: {
1533
+ /** Read the tenant's audit trail (admin). Filter by action/subject. */
1534
+ list: (opts?: {
1535
+ action?: string;
1536
+ subject?: string;
1537
+ limit?: number;
1538
+ }) => Promise<AuditEntry[]>;
1539
+ /** Purge a subject's documents, sessions, and vectors (GDPR erasure; admin). */
1540
+ forget: (subject: string) => Promise<ForgetResult>;
1541
+ };
1542
+ /**
1543
+ * Sandboxed compute — the isolated machines the agent works in. Each session has a
1544
+ * stable id, survives sleep/wake, and can be reattached to a later turn via
1545
+ * `chat({ computer_session_id })`.
1546
+ *
1547
+ * Nothing to configure: the deployment runs one sandbox host, and a project with the
1548
+ * `computer` capability gets a machine. `host()` says what that machine is.
1549
+ *
1550
+ * const s = await ai.computers.create();
1551
+ * await ai.computers.upload(s.id, file); // any file type, straight in
1552
+ * await ai.chat({ message: "summarise inbox/data.csv", computer_session_id: s.id });
1553
+ * await ai.computers.pause(s.id); // sleep now instead of on idle
1554
+ */
1555
+ computers: {
1556
+ /** What this deployment's sandboxes are and can do — use it to hide a feature
1557
+ * (port exposure, say) rather than offering a button that fails. */
1558
+ host: () => Promise<ComputerHost>;
1559
+ /** Start a sandbox. Bind it to a chat session to give that conversation a
1560
+ * persistent workspace. */
1561
+ create: (body?: {
1562
+ name?: string;
1563
+ chat_session_id?: string;
1564
+ }) => Promise<ComputerSession>;
1565
+ list: (opts?: {
1566
+ include_stopped?: boolean;
1567
+ }) => Promise<ComputerSession[]>;
1568
+ get: (id: string) => Promise<ComputerSession>;
1569
+ /** Put it to sleep now rather than waiting for it to go idle; the workspace is
1570
+ * preserved either way. */
1571
+ pause: (id: string) => Promise<ComputerSession>;
1572
+ /** Wake a sleeping sandbox and reattach to its workspace. */
1573
+ resume: (id: string) => Promise<ComputerSession>;
1574
+ destroy: (id: string) => Promise<void>;
1575
+ /** Run a command yourself (same guardrails as the agent's tool). `timeout_s` is
1576
+ * clamped to the host's ceiling — 30 minutes — and then the command is killed. */
1577
+ exec: (id: string, body: {
1578
+ command: string;
1579
+ cwd?: string;
1580
+ timeout_s?: number;
1581
+ stdin?: string;
1582
+ }) => Promise<ComputerExecResult>;
1583
+ /** Push a file (any type) into the sandbox. */
1584
+ upload: (id: string, file: Blob | File, dest?: string) => Promise<{
1585
+ path: string;
1586
+ size: number;
1587
+ }>;
1588
+ /** Pull a file out of the sandbox as bytes. */
1589
+ download: (id: string, path: string) => Promise<Blob>;
1590
+ };
1591
+ /**
1592
+ * Connect an external data source and keep it live in the agent's knowledge.
1593
+ * Validates the connector, runs an initial sync, and (when an interval is set)
1594
+ * schedules recurring auto-refresh so the data never goes stale.
1595
+ *
1596
+ * await ai.sync({ name: "orders", connector_type: "postgres",
1597
+ * config: { dsn, query: "select id, status, total from orders",
1598
+ * cursor_column: "updated_at" }, sync_interval_seconds: 900 });
1599
+ */
1600
+ sync: (config: SyncConfig) => Promise<SourceOut>;
1601
+ private runWithTools;
1602
+ /** Wrap startStream so a `done` carrying `requires_action` auto-executes the
1603
+ * client tools and continues the stream (same session) until the agent ends. */
1604
+ private startStreamWithTools;
1605
+ private resyncWatchers;
1606
+ private watchSession;
1607
+ private startStream;
1608
+ private multipartUpload;
1609
+ private rangedDownload;
1610
+ }
1611
+ /** Factory helper. */
1612
+ export declare function createClient(opts: ClientOptions): AgentFramework;
1613
+ export default AgentFramework;
1614
+ //# sourceMappingURL=index.d.ts.map