@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.
- package/README.md +79 -0
- package/dist/cjs/index.js +1751 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/index.d.ts +1614 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +1743 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +40 -0
|
@@ -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
|