@aexhq/sdk 0.62.1 → 0.64.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/dist/resources.js DELETED
@@ -1,442 +0,0 @@
1
- import { MAX_INLINE_FILE_BYTES, SessionChildren as BrainChildren, SessionSandbox as BrainSandbox, SessionStorage as BrainStorage, } from "@aexhq/brain";
2
- import { SessionError, abortError } from "./errors.js";
3
- import { randomIdempotencyKey } from "./json.js";
4
- export class SandboxFiles {
5
- #inner;
6
- #transport;
7
- constructor(inner, transport) {
8
- this.#inner = inner;
9
- this.#transport = transport;
10
- }
11
- async list(path, options) {
12
- const page = await this.#inner.list({
13
- path,
14
- generation: options.generation,
15
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
16
- ...(options.limit === undefined ? {} : { limit: options.limit }),
17
- }, request(options));
18
- return filePage(page);
19
- }
20
- async stat(path, options) {
21
- return file(await this.#inner.stat({ path, generation: options.generation }, request(options)));
22
- }
23
- async download(path, options) {
24
- return collect(await this.downloadStream(path, options));
25
- }
26
- async downloadStream(path, options) {
27
- const entry = await this.#inner.stat({ path, generation: options.generation }, request(options));
28
- if (entry.bytes <= MAX_INLINE_FILE_BYTES) {
29
- const result = await this.#inner.readInline({ path, generation: options.generation }, request(options));
30
- return byteStream(decodeBase64(result.content_base64));
31
- }
32
- const ticket = await this.#inner.prepareDownload({ path, generation: options.generation }, request(options));
33
- return this.#transport.downloadTransferStream(ticket, options.signal, entry.bytes);
34
- }
35
- async upload(path, source, options) {
36
- const base = {
37
- path,
38
- generation: options.generation,
39
- ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),
40
- };
41
- let entry;
42
- if (isStreamingSource(source)) {
43
- assertStreamingSource(source);
44
- entry = source.bytes <= MAX_INLINE_FILE_BYTES
45
- ? await this.#inner.writeInline({ ...base, content_base64: encodeBase64(await collectDeclared(source, options.signal)) }, request(options))
46
- : await this.#uploadStream(base, source, options);
47
- }
48
- else {
49
- const content = await bytesOf(source);
50
- entry = content.byteLength <= MAX_INLINE_FILE_BYTES
51
- ? await this.#inner.writeInline({ ...base, content_base64: encodeBase64(content) }, request(options))
52
- : await this.#upload(base, content, options);
53
- }
54
- return file(entry);
55
- }
56
- async #upload(input, content, options) {
57
- const ticket = await this.#inner.prepareUpload({ ...input, bytes: content.byteLength, sha256: await sha256(content) }, request(options));
58
- await this.#transport.uploadTransfer(ticket, content, content.byteLength, options.signal);
59
- // Direct sandbox transfers are intentionally happy-path only. Do not automatically replay an
60
- // ambiguous import: surface the error so the caller can inspect the generation/path and
61
- // prepare a fresh transfer. Durable storage completion has separate retry-safe semantics.
62
- return this.#inner.completeUpload(ticket.transfer_id, request(options));
63
- }
64
- async #uploadStream(input, source, options) {
65
- const ticket = await this.#inner.prepareUpload({ ...input, bytes: source.bytes, sha256: source.sha256 }, request(options));
66
- await this.#transport.uploadTransfer(ticket, () => source.stream(), source.bytes, options.signal);
67
- return this.#inner.completeUpload(ticket.transfer_id, request(options));
68
- }
69
- async find(input, options) {
70
- const page = await this.#inner.find({
71
- ...input,
72
- generation: options.generation,
73
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
74
- ...(options.limit === undefined ? {} : { limit: options.limit }),
75
- }, request(options));
76
- return filePage(page);
77
- }
78
- async grep(input, options) {
79
- const page = await this.#inner.grep({
80
- ...input,
81
- generation: options.generation,
82
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
83
- ...(options.limit === undefined ? {} : { limit: options.limit }),
84
- }, request(options));
85
- return filePage(page);
86
- }
87
- }
88
- export class SessionSandbox {
89
- #inner;
90
- files;
91
- constructor(transport, sessionId, environment) {
92
- this.#inner = new BrainSandbox(transport, sessionId, environment);
93
- this.files = new SandboxFiles(this.#inner.files, transport);
94
- }
95
- /** The declared Environment name this sandbox addresses. */
96
- get environment() {
97
- return this.#inner.environment;
98
- }
99
- async status(options = {}) {
100
- return sandboxStatus(await this.#inner.status(request(options)));
101
- }
102
- async create(options = {}) {
103
- return sandboxStatus(await this.#inner.create(intrinsicRequest(options)));
104
- }
105
- }
106
- export class SessionStorage {
107
- #inner;
108
- #transport;
109
- constructor(transport, sessionId) {
110
- this.#inner = new BrainStorage(transport, sessionId);
111
- this.#transport = transport;
112
- }
113
- async list(options = {}) {
114
- const page = await this.#inner.list({
115
- ...(options.prefix === undefined ? {} : { prefix: options.prefix }),
116
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
117
- ...(options.limit === undefined ? {} : { limit: options.limit }),
118
- }, request(options));
119
- return {
120
- data: page.data.map(storageObject),
121
- hasMore: page.has_more,
122
- ...(page.next_cursor === undefined ? {} : { nextCursor: page.next_cursor }),
123
- };
124
- }
125
- async stat(key, options = {}) {
126
- return storageObject(await this.#inner.stat(key, request(options)));
127
- }
128
- async download(key, options = {}) {
129
- return collect(await this.downloadStream(key, options));
130
- }
131
- async downloadStream(key, options = {}) {
132
- const object = await this.#inner.stat(key, request(options));
133
- if (object.bytes <= MAX_INLINE_FILE_BYTES) {
134
- return byteStream(decodeBase64((await this.#inner.readInline(key, request(options))).content_base64));
135
- }
136
- const ticket = await this.#inner.prepareDownload(key, request(options));
137
- return this.#transport.downloadTransferStream(ticket, options.signal, object.bytes);
138
- }
139
- async upload(key, source, options = {}) {
140
- const base = {
141
- key,
142
- ...(options.contentType === undefined ? {} : { content_type: options.contentType }),
143
- ...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),
144
- };
145
- if (isStreamingSource(source)) {
146
- assertStreamingSource(source);
147
- if (source.bytes <= MAX_INLINE_FILE_BYTES) {
148
- return storageObject(await this.#inner.writeInline({ ...base, content_base64: encodeBase64(await collectDeclared(source, options.signal)) }, request(options)));
149
- }
150
- const ticket = await this.#inner.prepareUpload({ ...base, bytes: source.bytes, sha256: source.sha256 }, request(options));
151
- await this.#transport.uploadTransfer(ticket, () => source.stream(), source.bytes, options.signal);
152
- return storageObject(await this.#inner.completeUpload(ticket.transfer_id, intrinsicRequest(options)));
153
- }
154
- const content = await bytesOf(source);
155
- if (content.byteLength <= MAX_INLINE_FILE_BYTES) {
156
- return storageObject(await this.#inner.writeInline({ ...base, content_base64: encodeBase64(content) }, request(options)));
157
- }
158
- const ticket = await this.#inner.prepareUpload({ ...base, bytes: content.byteLength, sha256: await sha256(content) }, request(options));
159
- await this.#transport.uploadTransfer(ticket, content, content.byteLength, options.signal);
160
- return storageObject(await this.#inner.completeUpload(ticket.transfer_id, intrinsicRequest(options)));
161
- }
162
- delete(key, options = {}) {
163
- return this.#inner.delete(key, request(options));
164
- }
165
- async copyFromSandbox(input, options = {}) {
166
- return storageObject(await this.#inner.copyFromEnvironment(input.environment, {
167
- key: input.key,
168
- path: input.path,
169
- environment_generation: input.sandboxGeneration,
170
- ...(input.overwrite === undefined ? {} : { overwrite: input.overwrite }),
171
- }, request(options)));
172
- }
173
- async copyToSandbox(input, options = {}) {
174
- return file(await this.#inner.copyToEnvironment(input.environment, {
175
- key: input.key,
176
- path: input.path,
177
- environment_generation: input.sandboxGeneration,
178
- ...(input.overwrite === undefined ? {} : { overwrite: input.overwrite }),
179
- }, request(options)));
180
- }
181
- }
182
- export class SessionChild {
183
- #inner;
184
- constructor(inner) {
185
- this.#inner = inner;
186
- }
187
- get id() {
188
- return this.#inner.id;
189
- }
190
- async info(options = {}) {
191
- return child(await this.#inner.get(request(options)));
192
- }
193
- async send(message, options = {}) {
194
- await this.#inner.send(message, idempotentRequest(options));
195
- }
196
- async followUp(message, options = {}) {
197
- return child(await this.#inner.followUp(message, idempotentRequest(options)));
198
- }
199
- async wait(options = {}) {
200
- return child(await this.#inner.wait(options.timeoutMs === undefined ? {} : { timeout_ms: options.timeoutMs }, request(options)));
201
- }
202
- async interrupt(options = {}) {
203
- return child(await this.#inner.interrupt(intrinsicRequest(options)));
204
- }
205
- async end(options = {}) {
206
- return child(await this.#inner.end(intrinsicRequest(options)));
207
- }
208
- events(options = {}) {
209
- return this.#inner.events(options);
210
- }
211
- }
212
- export class SessionChildren {
213
- #inner;
214
- constructor(transport, sessionId) {
215
- this.#inner = new BrainChildren(transport, sessionId);
216
- }
217
- async create(input, options = {}) {
218
- const handle = await this.#inner.create({
219
- prompt: input.prompt,
220
- ...(input.name === undefined ? {} : { name: input.name }),
221
- ...(input.forkTurns === undefined ? {} : { fork_turns: input.forkTurns }),
222
- }, idempotentRequest(options));
223
- return new SessionChild(handle);
224
- }
225
- async list(options = {}) {
226
- const page = await this.#inner.list({
227
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
228
- ...(options.limit === undefined ? {} : { limit: options.limit }),
229
- }, request(options));
230
- return {
231
- data: page.data.map(child),
232
- hasMore: page.has_more,
233
- ...(page.next_cursor === undefined ? {} : { nextCursor: page.next_cursor }),
234
- };
235
- }
236
- get(childId) {
237
- return new SessionChild(this.#inner.get(childId));
238
- }
239
- }
240
- function request(options) {
241
- return options.signal === undefined ? {} : { signal: options.signal };
242
- }
243
- function intrinsicRequest(options) {
244
- return {
245
- ...(options.signal === undefined ? {} : { signal: options.signal }),
246
- retry: true,
247
- };
248
- }
249
- function idempotentRequest(options) {
250
- return {
251
- ...(options.signal === undefined ? {} : { signal: options.signal }),
252
- headers: { "Idempotency-Key": options.idempotencyKey ?? randomIdempotencyKey() },
253
- retry: true,
254
- };
255
- }
256
- function sandboxStatus(value) {
257
- return {
258
- state: value.state,
259
- ...(value.generation == null ? {} : { generation: value.generation }),
260
- ...(value.reason == null ? {} : { reason: value.reason }),
261
- ...(value.changed_at_ms == null ? {} : { changedAt: timestamp(value.changed_at_ms) }),
262
- ...(value.expires_at_ms == null ? {} : { expiresAt: timestamp(value.expires_at_ms) }),
263
- };
264
- }
265
- function file(value) {
266
- return {
267
- path: value.path,
268
- kind: value.kind,
269
- bytes: value.bytes,
270
- ...(value.sha256 == null ? {} : { sha256: value.sha256 }),
271
- modifiedAt: timestamp(value.modified_at_ms),
272
- };
273
- }
274
- function filePage(value) {
275
- return {
276
- data: value.data.map(file),
277
- hasMore: value.has_more,
278
- ...(value.next_cursor === undefined ? {} : { nextCursor: value.next_cursor }),
279
- generation: value.generation,
280
- };
281
- }
282
- function storageObject(value) {
283
- return {
284
- key: value.key,
285
- bytes: value.bytes,
286
- sha256: value.sha256,
287
- ...(value.content_type === undefined ? {} : { contentType: value.content_type }),
288
- createdAt: value.created_at,
289
- updatedAt: value.updated_at,
290
- };
291
- }
292
- function child(value) {
293
- if (value.parent_id === undefined) {
294
- throw new SessionError(`Aex returned root session ${value.id} from a direct-child resource`);
295
- }
296
- return {
297
- id: value.id,
298
- parentId: value.parent_id,
299
- rootId: value.root_id,
300
- ...(value.name === undefined ? {} : { name: value.name }),
301
- depth: value.depth,
302
- state: value.state,
303
- turnState: value.turn_state,
304
- ...(value.turn_phase === undefined ? {} : { turnPhase: value.turn_phase }),
305
- shape: value.shape,
306
- createdAt: value.created_at,
307
- updatedAt: value.updated_at,
308
- };
309
- }
310
- function timestamp(value) {
311
- if (!Number.isSafeInteger(value) || value < 0) {
312
- throw new SessionError("Aex returned an invalid sandbox timestamp");
313
- }
314
- try {
315
- return new Date(value).toISOString();
316
- }
317
- catch (cause) {
318
- throw new SessionError("Aex returned an invalid sandbox timestamp", { cause });
319
- }
320
- }
321
- async function bytesOf(source) {
322
- if (typeof source === "string")
323
- return new TextEncoder().encode(source);
324
- if (source instanceof ArrayBuffer)
325
- return new Uint8Array(source);
326
- if (typeof Blob !== "undefined" && source instanceof Blob) {
327
- return new Uint8Array(await source.arrayBuffer());
328
- }
329
- if (ArrayBuffer.isView(source)) {
330
- return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
331
- }
332
- throw new TypeError("Unsupported binary source");
333
- }
334
- function isStreamingSource(source) {
335
- return typeof source === "object" && source !== null && "stream" in source &&
336
- "bytes" in source && "sha256" in source && !(typeof Blob !== "undefined" && source instanceof Blob);
337
- }
338
- function assertStreamingSource(source) {
339
- if (!Number.isSafeInteger(source.bytes) || source.bytes < 0) {
340
- throw new TypeError("Streaming upload bytes must be a non-negative safe integer");
341
- }
342
- if (!/^[0-9a-f]{64}$/u.test(source.sha256)) {
343
- throw new TypeError("Streaming upload sha256 must be 64 lowercase hexadecimal characters");
344
- }
345
- if (typeof source.stream !== "function") {
346
- throw new TypeError("Streaming upload source must provide stream()");
347
- }
348
- }
349
- function byteStream(bytes) {
350
- return new ReadableStream({
351
- start(controller) {
352
- if (bytes.byteLength !== 0)
353
- controller.enqueue(bytes);
354
- controller.close();
355
- },
356
- });
357
- }
358
- async function collect(stream) {
359
- const chunks = [];
360
- let bytes = 0;
361
- const reader = stream.getReader();
362
- try {
363
- while (true) {
364
- const item = await reader.read();
365
- if (item.done)
366
- break;
367
- chunks.push(item.value);
368
- bytes += item.value.byteLength;
369
- }
370
- }
371
- finally {
372
- reader.releaseLock();
373
- }
374
- const result = new Uint8Array(bytes);
375
- let offset = 0;
376
- for (const chunk of chunks) {
377
- result.set(chunk, offset);
378
- offset += chunk.byteLength;
379
- }
380
- return result;
381
- }
382
- async function collectDeclared(source, signal) {
383
- if (signal?.aborted === true)
384
- throw abortError(signal.reason);
385
- const reader = source.stream().getReader();
386
- const result = new Uint8Array(source.bytes);
387
- let offset = 0;
388
- let aborted = false;
389
- const abort = () => {
390
- aborted = true;
391
- void reader.cancel(signal?.reason).catch(() => undefined);
392
- };
393
- signal?.addEventListener("abort", abort, { once: true });
394
- try {
395
- while (true) {
396
- const item = await reader.read();
397
- if (aborted)
398
- throw abortError(signal?.reason);
399
- if (item.done)
400
- break;
401
- if (!(item.value instanceof Uint8Array) || offset + item.value.byteLength > result.byteLength) {
402
- throw new TypeError("Streaming upload exceeded its declared byte length");
403
- }
404
- result.set(item.value, offset);
405
- offset += item.value.byteLength;
406
- }
407
- }
408
- finally {
409
- signal?.removeEventListener("abort", abort);
410
- reader.releaseLock();
411
- }
412
- if (offset !== result.byteLength) {
413
- throw new TypeError(`Streaming upload produced ${offset} bytes; expected ${result.byteLength}`);
414
- }
415
- if (await sha256(result) !== source.sha256) {
416
- throw new TypeError("Streaming upload content does not match its declared sha256");
417
- }
418
- return result;
419
- }
420
- export function encodeBase64(bytes) {
421
- let binary = "";
422
- for (let offset = 0; offset < bytes.byteLength; offset += 32_768) {
423
- binary += String.fromCharCode(...bytes.subarray(offset, offset + 32_768));
424
- }
425
- return btoa(binary);
426
- }
427
- function decodeBase64(value) {
428
- const binary = atob(value);
429
- const bytes = new Uint8Array(binary.length);
430
- for (let index = 0; index < binary.length; index += 1)
431
- bytes[index] = binary.charCodeAt(index);
432
- return bytes;
433
- }
434
- async function sha256(value) {
435
- const input = value.buffer instanceof ArrayBuffer
436
- ? value
437
- : Uint8Array.from(value);
438
- const digest = await crypto.subtle.digest("SHA-256", input);
439
- return [...new Uint8Array(digest)]
440
- .map((byte) => byte.toString(16).padStart(2, "0"))
441
- .join("");
442
- }
package/dist/session.d.ts DELETED
@@ -1,137 +0,0 @@
1
- import type { Event, Session as BrainSessionData, SessionState } from "@aexhq/brain/session";
2
- import { type ComponentExtension, type SessionTool as ComponentSessionTool } from "@aexhq/brain";
3
- import type { NetworkPolicy, WebSocketFactory } from "@aexhq/brain";
4
- import * as z from "zod";
5
- import type { EventOptions } from "./transport.js";
6
- import { Transport } from "./transport.js";
7
- import { type Tool } from "./tools.js";
8
- import { SessionChildren, SessionSandbox, SessionStorage } from "./resources.js";
9
- type SessionData = BrainSessionData & {
10
- retain_until: string;
11
- };
12
- export type SessionInput = string;
13
- export interface ModelOptions {
14
- /** Imported Model component. */
15
- component: ComponentExtension<"model">;
16
- provider: string;
17
- name: string;
18
- apiKey: string;
19
- baseUrl?: string;
20
- maxOutputTokens?: number;
21
- }
22
- export interface CreateSessionOptions {
23
- model: ModelOptions;
24
- /** The imported agent loop that drives this session and every child unless spawn overrides it. */
25
- agentloop: ComponentExtension<"agentloop">;
26
- /** Omitted or empty grants no tools. A non-empty list is the exact grant. */
27
- environments?: Readonly<Record<string, ComponentExtension<"environment">>>;
28
- tools?: readonly (Tool | ComponentSessionTool)[];
29
- /** Write-only values for components that explicitly consume session secrets. */
30
- secrets?: Record<string, string>;
31
- /** Maximum direct outbound network authority sealed for managed sandboxes. Omission is deny-all. */
32
- network?: NetworkPolicy;
33
- /** Replacement attempts after an unrecoverable provider outcome. Defaults to one. */
34
- providerRecoveryRetries?: 0 | 1;
35
- /** Optional ceilings for durable child sessions. Omitted fields use the hosted defaults. */
36
- children?: {
37
- maxDepth?: number;
38
- maxDirectChildren?: number;
39
- maxDescendants?: number;
40
- };
41
- metadata?: Record<string, string>;
42
- /** Finite durable-history deadline. Omit to use the Brain deployment default. */
43
- retainUntil?: Date | string;
44
- }
45
- export interface RequestOptions {
46
- signal?: AbortSignal;
47
- idempotencyKey?: string;
48
- metadata?: Record<string, string>;
49
- }
50
- export interface OutputOptions<Schema extends z.ZodType = z.ZodType> extends RequestOptions {
51
- output: Schema;
52
- /** Extra attempts after the first invalid candidate. Defaults to 1; maximum 2. */
53
- outputRetries?: 0 | 1 | 2;
54
- }
55
- export interface ListSessionsOptions {
56
- limit?: number;
57
- cursor?: string;
58
- state?: SessionState;
59
- signal?: AbortSignal;
60
- }
61
- export interface SessionList {
62
- data: Session[];
63
- hasMore: boolean;
64
- nextCursor?: string;
65
- }
66
- export interface ModelSummary {
67
- provider: string;
68
- name: string;
69
- baseUrl?: string;
70
- contextWindowTokens: number;
71
- }
72
- export interface SessionSummary {
73
- id: string;
74
- parentId: string | undefined;
75
- rootId: string;
76
- depth: number;
77
- state: SessionState;
78
- turnState: SessionData["turn_state"];
79
- model: ModelSummary;
80
- createdAt: string;
81
- updatedAt: string;
82
- retainUntil: string;
83
- metadata: Readonly<Record<string, string | undefined>>;
84
- }
85
- export declare class Sessions {
86
- #private;
87
- constructor(transport: Transport, webSocketFactory?: WebSocketFactory);
88
- /** @internal Called by `Aex.close()`. */
89
- close(): void;
90
- create(options: CreateSessionOptions, request?: RequestOptions): Promise<Session>;
91
- get(id: string, options?: Pick<RequestOptions, "signal">): Promise<Session>;
92
- list(options?: ListSessionsOptions): Promise<SessionList>;
93
- }
94
- export declare class Session implements SessionSummary {
95
- #private;
96
- readonly storage: SessionStorage;
97
- readonly children: SessionChildren;
98
- constructor(transport: Transport, data: SessionData);
99
- /** The Environment names this session sealed. Only these can be addressed. */
100
- get environments(): readonly string[];
101
- /** One sealed Environment addressed by the name the session declared it under. */
102
- environment(name: string): SessionSandbox;
103
- /** The session's default sandbox: the one Environment it declared. */
104
- get sandbox(): SessionSandbox;
105
- get id(): string;
106
- get state(): SessionState;
107
- get turnState(): SessionData["turn_state"];
108
- get parentId(): string | undefined;
109
- get rootId(): string;
110
- get depth(): number;
111
- get model(): ModelSummary;
112
- get createdAt(): string;
113
- get updatedAt(): string;
114
- get retainUntil(): string;
115
- get metadata(): Readonly<Record<string, string | undefined>>;
116
- refresh(options?: Pick<RequestOptions, "signal">): Promise<this>;
117
- send(input: SessionInput, options?: RequestOptions): Promise<string>;
118
- send<Schema extends z.ZodType>(input: SessionInput, options: OutputOptions<Schema>): Promise<z.output<Schema>>;
119
- /**
120
- * Raw, attempt-aware event stream. Provisional frames have no durable cursor and may later be
121
- * superseded; consumers rendering them must key by `attempt_id` and process
122
- * `model.attempt_superseded`. Use `send()` when only the durable winning answer is needed.
123
- */
124
- events(options?: EventOptions): AsyncGenerator<Event>;
125
- cancel(options?: Pick<RequestOptions, "signal">): Promise<this>;
126
- suspend(options?: Pick<RequestOptions, "signal">): Promise<this>;
127
- resume(options?: Pick<RequestOptions, "signal">): Promise<this>;
128
- setRetention(value: Date | string, options?: Pick<RequestOptions, "signal"> & {
129
- allowShorten?: boolean;
130
- }): Promise<this>;
131
- end(options?: Pick<RequestOptions, "signal">): Promise<this>;
132
- delete(options?: Pick<RequestOptions, "signal"> & {
133
- queue?: boolean;
134
- }): Promise<void>;
135
- private markIdle;
136
- }
137
- export {};