@agentuity/core 1.0.37 → 1.0.39

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.
Files changed (49) hide show
  1. package/dist/services/keyvalue/service.d.ts +2 -0
  2. package/dist/services/keyvalue/service.d.ts.map +1 -1
  3. package/dist/services/keyvalue/service.js +4 -0
  4. package/dist/services/keyvalue/service.js.map +1 -1
  5. package/dist/services/sandbox/client.d.ts +56 -5
  6. package/dist/services/sandbox/client.d.ts.map +1 -1
  7. package/dist/services/sandbox/client.js +120 -51
  8. package/dist/services/sandbox/client.js.map +1 -1
  9. package/dist/services/sandbox/pause.js +1 -1
  10. package/dist/services/sandbox/pause.js.map +1 -1
  11. package/dist/services/sandbox/types.d.ts +30 -0
  12. package/dist/services/sandbox/types.d.ts.map +1 -1
  13. package/dist/services/sandbox/types.js +28 -0
  14. package/dist/services/sandbox/types.js.map +1 -1
  15. package/dist/services/stream/delete.d.ts +27 -0
  16. package/dist/services/stream/delete.d.ts.map +1 -0
  17. package/dist/services/stream/delete.js +58 -0
  18. package/dist/services/stream/delete.js.map +1 -0
  19. package/dist/services/stream/index.d.ts +3 -0
  20. package/dist/services/stream/index.d.ts.map +1 -1
  21. package/dist/services/stream/index.js +3 -0
  22. package/dist/services/stream/index.js.map +1 -1
  23. package/dist/services/stream/namespaces.d.ts +75 -0
  24. package/dist/services/stream/namespaces.d.ts.map +1 -0
  25. package/dist/services/stream/namespaces.js +99 -0
  26. package/dist/services/stream/namespaces.js.map +1 -0
  27. package/dist/services/stream/search.d.ts +34 -0
  28. package/dist/services/stream/search.d.ts.map +1 -0
  29. package/dist/services/stream/search.js +49 -0
  30. package/dist/services/stream/search.js.map +1 -0
  31. package/dist/services/task/service.d.ts +154 -18
  32. package/dist/services/task/service.d.ts.map +1 -1
  33. package/dist/services/task/service.js +255 -10
  34. package/dist/services/task/service.js.map +1 -1
  35. package/dist/services/vector/service.d.ts +3 -0
  36. package/dist/services/vector/service.d.ts.map +1 -1
  37. package/dist/services/vector/service.js +7 -0
  38. package/dist/services/vector/service.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/services/keyvalue/service.ts +4 -0
  41. package/src/services/sandbox/client.ts +215 -84
  42. package/src/services/sandbox/pause.ts +1 -1
  43. package/src/services/sandbox/types.ts +52 -0
  44. package/src/services/stream/delete.ts +87 -0
  45. package/src/services/stream/index.ts +3 -0
  46. package/src/services/stream/namespaces.ts +160 -0
  47. package/src/services/stream/search.ts +80 -0
  48. package/src/services/task/service.ts +375 -13
  49. package/src/services/vector/service.ts +8 -0
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  ExecuteOptionsSchema as CoreExecuteOptionsSchema,
3
3
  type SandboxCreateOptions,
4
+ type SandboxFileInfo,
4
5
  type SandboxInfo,
6
+ type SandboxStatus,
5
7
  type Execution,
6
8
  type FileToWrite,
7
9
  type SandboxRunOptions,
@@ -12,11 +14,21 @@ import type { Readable, Writable } from 'node:stream';
12
14
  import { z } from 'zod';
13
15
  import { APIClient } from '../api.ts';
14
16
  import { getEnv } from '../env.ts';
15
- import { sandboxCreate, type SandboxCreateResponse } from './create.ts';
17
+ import { sandboxCreate } from './create.ts';
16
18
  import { sandboxDestroy } from './destroy.ts';
17
19
  import { sandboxGet } from './get.ts';
18
20
  import { sandboxExecute } from './execute.ts';
19
- import { sandboxWriteFiles, sandboxReadFile } from './files.ts';
21
+ import {
22
+ sandboxWriteFiles,
23
+ sandboxReadFile,
24
+ sandboxListFiles,
25
+ sandboxMkDir,
26
+ sandboxRmFile,
27
+ sandboxRmDir,
28
+ sandboxSetEnv,
29
+ } from './files.ts';
30
+ import { sandboxPause } from './pause.ts';
31
+ import { sandboxResume } from './resume.ts';
20
32
  import { sandboxRun } from './run.ts';
21
33
  import { executionGet, type ExecutionInfo } from './execution.ts';
22
34
  import { createMinimalLogger } from '../logger.ts';
@@ -130,7 +142,7 @@ export const SandboxClientRunIOSchema = z.object({
130
142
  export type SandboxClientRunIO = z.infer<typeof SandboxClientRunIOSchema>;
131
143
 
132
144
  /**
133
- * A sandbox instance returned by SandboxClient.create()
145
+ * A sandbox instance returned by SandboxClient.create() or SandboxClient.connect()
134
146
  */
135
147
  export interface SandboxInstance {
136
148
  /**
@@ -139,9 +151,9 @@ export interface SandboxInstance {
139
151
  id: string;
140
152
 
141
153
  /**
142
- * Sandbox status at creation time
154
+ * Sandbox status at creation or connection time
143
155
  */
144
- status: SandboxCreateResponse['status'];
156
+ status: SandboxStatus;
145
157
 
146
158
  /**
147
159
  * URL to stream stdout output
@@ -173,17 +185,171 @@ export interface SandboxInstance {
173
185
  */
174
186
  readFile(path: string): Promise<ReadableStream<Uint8Array>>;
175
187
 
188
+ /**
189
+ * List files in the sandbox workspace
190
+ */
191
+ listFiles(path?: string): Promise<SandboxFileInfo[]>;
192
+
193
+ /**
194
+ * Create a directory in the sandbox workspace
195
+ */
196
+ mkDir(path: string, recursive?: boolean): Promise<void>;
197
+
198
+ /**
199
+ * Remove a file from the sandbox workspace
200
+ */
201
+ rmFile(path: string): Promise<void>;
202
+
203
+ /**
204
+ * Remove a directory from the sandbox workspace
205
+ */
206
+ rmDir(path: string, recursive?: boolean): Promise<void>;
207
+
208
+ /**
209
+ * Set environment variables on the sandbox. Pass null to delete a variable.
210
+ */
211
+ setEnv(env: Record<string, string | null>): Promise<Record<string, string>>;
212
+
176
213
  /**
177
214
  * Get current sandbox information
178
215
  */
179
216
  get(): Promise<SandboxInfo>;
180
217
 
218
+ /**
219
+ * Pause the sandbox, creating a checkpoint of its current state
220
+ */
221
+ pause(): Promise<void>;
222
+
223
+ /**
224
+ * Resume the sandbox from a paused or evacuated state
225
+ */
226
+ resume(): Promise<void>;
227
+
181
228
  /**
182
229
  * Destroy the sandbox and release all resources
183
230
  */
184
231
  destroy(): Promise<void>;
185
232
  }
186
233
 
234
+ /**
235
+ * Creates the method implementations shared by both create() and connect().
236
+ * Modelled after the similar helper in packages/runtime/src/services/sandbox/http.ts.
237
+ */
238
+ function createSandboxInstanceMethods(
239
+ client: APIClient,
240
+ sandboxId: string,
241
+ orgId?: string
242
+ ): Omit<
243
+ SandboxInstance,
244
+ 'id' | 'status' | 'stdoutStreamUrl' | 'stderrStreamUrl' | 'auditStreamUrl'
245
+ > {
246
+ return {
247
+ async execute(executeOptions: ExecuteOptions): Promise<Execution> {
248
+ const { pipe, ...coreOptions } = executeOptions;
249
+
250
+ const initialResult = await sandboxExecute(client, {
251
+ sandboxId,
252
+ options: coreOptions,
253
+ orgId,
254
+ signal: coreOptions.signal,
255
+ });
256
+
257
+ // If pipe options provided, stream the output to the writable streams
258
+ if (pipe) {
259
+ const streamPromises: Promise<void>[] = [];
260
+
261
+ if (pipe.stdout && initialResult.stdoutStreamUrl) {
262
+ streamPromises.push(
263
+ pipeStreamToWritable(
264
+ initialResult.stdoutStreamUrl,
265
+ pipe.stdout,
266
+ coreOptions.signal
267
+ )
268
+ );
269
+ }
270
+ if (pipe.stderr && initialResult.stderrStreamUrl) {
271
+ streamPromises.push(
272
+ pipeStreamToWritable(
273
+ initialResult.stderrStreamUrl,
274
+ pipe.stderr,
275
+ coreOptions.signal
276
+ )
277
+ );
278
+ }
279
+
280
+ // Wait for all streams to complete
281
+ if (streamPromises.length > 0) {
282
+ await Promise.all(streamPromises);
283
+ }
284
+ }
285
+
286
+ // Wait for execution to complete and get final result with exit code
287
+ const finalResult = await waitForExecution(
288
+ client,
289
+ initialResult.executionId,
290
+ orgId,
291
+ coreOptions.signal
292
+ );
293
+
294
+ return {
295
+ executionId: finalResult.executionId,
296
+ status: finalResult.status,
297
+ exitCode: finalResult.exitCode,
298
+ durationMs: finalResult.durationMs,
299
+ stdoutStreamUrl: initialResult.stdoutStreamUrl,
300
+ stderrStreamUrl: initialResult.stderrStreamUrl,
301
+ };
302
+ },
303
+
304
+ async writeFiles(files: FileToWrite[]): Promise<number> {
305
+ const result = await sandboxWriteFiles(client, { sandboxId, files, orgId });
306
+ return result.filesWritten;
307
+ },
308
+
309
+ async readFile(path: string): Promise<ReadableStream<Uint8Array>> {
310
+ return sandboxReadFile(client, { sandboxId, path, orgId });
311
+ },
312
+
313
+ async listFiles(path?: string): Promise<SandboxFileInfo[]> {
314
+ const result = await sandboxListFiles(client, { sandboxId, path, orgId });
315
+ return result.files;
316
+ },
317
+
318
+ async mkDir(path: string, recursive?: boolean): Promise<void> {
319
+ await sandboxMkDir(client, { sandboxId, path, recursive, orgId });
320
+ },
321
+
322
+ async rmFile(path: string): Promise<void> {
323
+ await sandboxRmFile(client, { sandboxId, path, orgId });
324
+ },
325
+
326
+ async rmDir(path: string, recursive?: boolean): Promise<void> {
327
+ await sandboxRmDir(client, { sandboxId, path, recursive, orgId });
328
+ },
329
+
330
+ async setEnv(env: Record<string, string | null>): Promise<Record<string, string>> {
331
+ const result = await sandboxSetEnv(client, { sandboxId, env, orgId });
332
+ return result.env;
333
+ },
334
+
335
+ async get(): Promise<SandboxInfo> {
336
+ return sandboxGet(client, { sandboxId, orgId });
337
+ },
338
+
339
+ async pause(): Promise<void> {
340
+ return sandboxPause(client, { sandboxId, orgId });
341
+ },
342
+
343
+ async resume(): Promise<void> {
344
+ return sandboxResume(client, { sandboxId, orgId });
345
+ },
346
+
347
+ async destroy(): Promise<void> {
348
+ return sandboxDestroy(client, { sandboxId, orgId });
349
+ },
350
+ };
351
+ }
352
+
187
353
  /**
188
354
  * Convenience client for sandbox operations.
189
355
  *
@@ -284,90 +450,13 @@ export class SandboxClient {
284
450
  orgId: this.#orgId,
285
451
  });
286
452
 
287
- const sandboxId = response.sandboxId;
288
- const client = this.#client;
289
- const orgId = this.#orgId;
290
-
291
453
  return {
292
- id: sandboxId,
454
+ id: response.sandboxId,
293
455
  status: response.status,
294
456
  stdoutStreamUrl: response.stdoutStreamUrl,
295
457
  stderrStreamUrl: response.stderrStreamUrl,
296
458
  auditStreamUrl: response.auditStreamUrl,
297
-
298
- async execute(executeOptions: ExecuteOptions): Promise<Execution> {
299
- const { pipe, ...coreOptions } = executeOptions;
300
-
301
- const initialResult = await sandboxExecute(client, {
302
- sandboxId,
303
- options: coreOptions,
304
- orgId,
305
- signal: coreOptions.signal,
306
- });
307
-
308
- // If pipe options provided, stream the output to the writable streams
309
- if (pipe) {
310
- const streamPromises: Promise<void>[] = [];
311
-
312
- if (pipe.stdout && initialResult.stdoutStreamUrl) {
313
- streamPromises.push(
314
- pipeStreamToWritable(
315
- initialResult.stdoutStreamUrl,
316
- pipe.stdout,
317
- coreOptions.signal
318
- )
319
- );
320
- }
321
- if (pipe.stderr && initialResult.stderrStreamUrl) {
322
- streamPromises.push(
323
- pipeStreamToWritable(
324
- initialResult.stderrStreamUrl,
325
- pipe.stderr,
326
- coreOptions.signal
327
- )
328
- );
329
- }
330
-
331
- // Wait for all streams to complete
332
- if (streamPromises.length > 0) {
333
- await Promise.all(streamPromises);
334
- }
335
- }
336
-
337
- // Wait for execution to complete and get final result with exit code
338
- const finalResult = await waitForExecution(
339
- client,
340
- initialResult.executionId,
341
- orgId,
342
- coreOptions.signal
343
- );
344
-
345
- return {
346
- executionId: finalResult.executionId,
347
- status: finalResult.status,
348
- exitCode: finalResult.exitCode,
349
- durationMs: finalResult.durationMs,
350
- stdoutStreamUrl: initialResult.stdoutStreamUrl,
351
- stderrStreamUrl: initialResult.stderrStreamUrl,
352
- };
353
- },
354
-
355
- async writeFiles(files: FileToWrite[]): Promise<number> {
356
- const result = await sandboxWriteFiles(client, { sandboxId, files, orgId });
357
- return result.filesWritten;
358
- },
359
-
360
- async readFile(path: string): Promise<ReadableStream<Uint8Array>> {
361
- return sandboxReadFile(client, { sandboxId, path, orgId });
362
- },
363
-
364
- async get(): Promise<SandboxInfo> {
365
- return sandboxGet(client, { sandboxId, orgId });
366
- },
367
-
368
- async destroy(): Promise<void> {
369
- return sandboxDestroy(client, { sandboxId, orgId });
370
- },
459
+ ...createSandboxInstanceMethods(this.#client, response.sandboxId, this.#orgId),
371
460
  };
372
461
  }
373
462
 
@@ -432,4 +521,46 @@ export class SandboxClient {
432
521
  signal,
433
522
  });
434
523
  }
524
+
525
+ /**
526
+ * Get a full sandbox instance for an existing sandbox by ID.
527
+ *
528
+ * Unlike `get()` which returns read-only metadata, `connect()` returns
529
+ * a `SandboxInstance` with `execute()`, `writeFiles()`, and all other
530
+ * interaction methods — allowing you to resume working with a sandbox
531
+ * using just its ID.
532
+ *
533
+ * @param sandboxId - The sandbox ID to connect to
534
+ * @returns A sandbox instance with all interaction methods
535
+ */
536
+ async connect(sandboxId: string): Promise<SandboxInstance> {
537
+ const info = await sandboxGet(this.#client, { sandboxId, orgId: this.#orgId });
538
+
539
+ return {
540
+ id: info.sandboxId,
541
+ status: info.status,
542
+ stdoutStreamUrl: info.stdoutStreamUrl,
543
+ stderrStreamUrl: info.stderrStreamUrl,
544
+ auditStreamUrl: info.auditStreamUrl,
545
+ ...createSandboxInstanceMethods(this.#client, info.sandboxId, this.#orgId),
546
+ };
547
+ }
548
+
549
+ /**
550
+ * Pause a running sandbox, creating a checkpoint of its current state
551
+ *
552
+ * @param sandboxId - The sandbox ID to pause
553
+ */
554
+ async pause(sandboxId: string): Promise<void> {
555
+ return sandboxPause(this.#client, { sandboxId, orgId: this.#orgId });
556
+ }
557
+
558
+ /**
559
+ * Resume a paused or evacuated sandbox from its checkpoint
560
+ *
561
+ * @param sandboxId - The sandbox ID to resume
562
+ */
563
+ async resume(sandboxId: string): Promise<void> {
564
+ return sandboxResume(this.#client, { sandboxId, orgId: this.#orgId });
565
+ }
435
566
  }
@@ -25,7 +25,7 @@ export async function sandboxPause(client: APIClient, params: SandboxPauseParams
25
25
  queryParams.set('orgId', orgId);
26
26
  }
27
27
  const queryString = queryParams.toString();
28
- const url = `/sandbox/${sandboxId}/pause${queryString ? `?${queryString}` : ''}`;
28
+ const url = `/sandbox/${encodeURIComponent(sandboxId)}/pause${queryString ? `?${queryString}` : ''}`;
29
29
 
30
30
  const resp = await client.post<z.infer<typeof PauseResponseSchema>>(
31
31
  url,
@@ -339,11 +339,57 @@ export const SandboxSchema = z.object({
339
339
  readFile: z
340
340
  .custom<(path: string) => Promise<ReadableStream<Uint8Array>>>()
341
341
  .describe('Read a file from the sandbox workspace.'),
342
+ /** List files in the sandbox workspace. */
343
+ listFiles: z
344
+ .custom<(path?: string) => Promise<SandboxFileInfo[]>>()
345
+ .describe('List files in the sandbox workspace.'),
346
+ /** Create a directory in the sandbox workspace. */
347
+ mkDir: z
348
+ .custom<(path: string, recursive?: boolean) => Promise<void>>()
349
+ .describe('Create a directory in the sandbox workspace.'),
350
+ /** Remove a file from the sandbox workspace. */
351
+ rmFile: z
352
+ .custom<(path: string) => Promise<void>>()
353
+ .describe('Remove a file from the sandbox workspace.'),
354
+ /** Remove a directory from the sandbox workspace. */
355
+ rmDir: z
356
+ .custom<(path: string, recursive?: boolean) => Promise<void>>()
357
+ .describe('Remove a directory from the sandbox workspace.'),
358
+ /** Set environment variables on the sandbox. Pass null to delete a variable. */
359
+ setEnv: z
360
+ .custom<(env: Record<string, string | null>) => Promise<Record<string, string>>>()
361
+ .describe('Set environment variables on the sandbox. Pass null to delete a variable.'),
362
+ /** Pause the sandbox, creating a checkpoint of its current state. */
363
+ pause: z
364
+ .custom<() => Promise<void>>()
365
+ .describe('Pause the sandbox, creating a checkpoint of its current state.'),
366
+ /** Resume the sandbox from a paused or evacuated state. */
367
+ resume: z
368
+ .custom<() => Promise<void>>()
369
+ .describe('Resume the sandbox from a paused or evacuated state.'),
342
370
  /** Destroy the sandbox */
343
371
  destroy: z.custom<() => Promise<void>>().describe('Destroy the sandbox'),
344
372
  });
345
373
  export type Sandbox = z.infer<typeof SandboxSchema>;
346
374
 
375
+ /**
376
+ * File information returned by sandbox file operations.
377
+ * NOTE: This interface is structurally identical to FileInfo in ./files.ts.
378
+ * It is duplicated here to avoid circular type imports. Keep these in sync.
379
+ */
380
+ export interface SandboxFileInfo {
381
+ /** File path relative to the listed directory */
382
+ path: string;
383
+ /** File size in bytes */
384
+ size: number;
385
+ /** Whether the entry is a directory */
386
+ isDir: boolean;
387
+ /** Unix permissions as octal string (e.g., "0644") */
388
+ mode: string;
389
+ /** Modification time in RFC3339 format */
390
+ modTime: string;
391
+ }
392
+
347
393
  /** Information about a user who created the sandbox */
348
394
  export const SandboxUserInfoSchema = z.object({
349
395
  /** User ID */
@@ -785,9 +831,15 @@ export type SandboxRunResult = z.infer<typeof SandboxRunResultSchema>;
785
831
  export interface SandboxService {
786
832
  run(options: SandboxRunOptions): Promise<SandboxRunResult>;
787
833
  create(options?: SandboxCreateOptions): Promise<Sandbox>;
834
+ /** Get a full Sandbox instance for an existing sandbox by ID. */
835
+ connect(sandboxId: string): Promise<Sandbox>;
788
836
  get(sandboxId: string): Promise<SandboxInfo>;
789
837
  list(params?: ListSandboxesParams): Promise<ListSandboxesResponse>;
790
838
  destroy(sandboxId: string): Promise<void>;
839
+ /** Pause a running sandbox, creating a checkpoint of its current state. */
840
+ pause(sandboxId: string): Promise<void>;
841
+ /** Resume a paused or evacuated sandbox from its checkpoint. */
842
+ resume(sandboxId: string): Promise<void>;
791
843
  snapshot: SnapshotService;
792
844
  }
793
845
 
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { type APIClient } from '../api.ts';
3
+ import { StreamResponseError } from './util.ts';
4
+
5
+ // --- Response schemas matching Pulse format ---
6
+
7
+ const DeleteStreamResponseSchema = z.discriminatedUnion('success', [
8
+ z.object({
9
+ success: z.literal<false>(false),
10
+ message: z.string().optional(),
11
+ }),
12
+ z.object({
13
+ success: z.literal<true>(true),
14
+ }),
15
+ ]);
16
+
17
+ type DeleteStreamResponse = z.infer<typeof DeleteStreamResponseSchema>;
18
+
19
+ const DeleteNamespaceResponseSchema = z.discriminatedUnion('success', [
20
+ z.object({
21
+ success: z.literal<false>(false),
22
+ message: z.string().optional(),
23
+ }),
24
+ z.object({
25
+ success: z.literal<true>(true),
26
+ deleted: z.number(),
27
+ }),
28
+ ]);
29
+
30
+ type DeleteNamespaceResponse = z.infer<typeof DeleteNamespaceResponseSchema>;
31
+
32
+ // --- Return types ---
33
+
34
+ export interface StreamDeleteNamespaceResult {
35
+ deleted: number;
36
+ }
37
+
38
+ // --- Functions ---
39
+
40
+ /**
41
+ * Delete a single stream by ID.
42
+ *
43
+ * @param client - The API client configured for Pulse
44
+ * @param id - The stream ID to delete
45
+ *
46
+ * @example
47
+ * await streamDelete(client, 'strm_abc123');
48
+ */
49
+ export async function streamDelete(client: APIClient, id: string): Promise<void> {
50
+ const resp = await client.delete<DeleteStreamResponse>(
51
+ `/${encodeURIComponent(id)}`,
52
+ DeleteStreamResponseSchema
53
+ );
54
+
55
+ if (resp.success) {
56
+ return;
57
+ }
58
+
59
+ throw new StreamResponseError({ message: resp.message });
60
+ }
61
+
62
+ /**
63
+ * Delete all streams in a namespace (soft delete).
64
+ *
65
+ * @param client - The API client configured for Pulse
66
+ * @param name - The namespace name to delete
67
+ * @returns The number of streams that were deleted
68
+ *
69
+ * @example
70
+ * const result = await streamDeleteNamespace(client, 'old-logs');
71
+ * console.log(`Deleted ${result.deleted} streams`);
72
+ */
73
+ export async function streamDeleteNamespace(
74
+ client: APIClient,
75
+ name: string
76
+ ): Promise<StreamDeleteNamespaceResult> {
77
+ const resp = await client.delete<DeleteNamespaceResponse>(
78
+ `/namespace/${encodeURIComponent(name)}`,
79
+ DeleteNamespaceResponseSchema
80
+ );
81
+
82
+ if (resp.success) {
83
+ return { deleted: resp.deleted };
84
+ }
85
+
86
+ throw new StreamResponseError({ message: resp.message });
87
+ }
@@ -10,5 +10,8 @@ export {
10
10
  type StreamListOptions,
11
11
  streamList,
12
12
  } from './list.ts';
13
+ export * from './namespaces.ts';
14
+ export * from './delete.ts';
15
+ export * from './search.ts';
13
16
  export * from './service.ts';
14
17
  export * from './util.ts';
@@ -0,0 +1,160 @@
1
+ import { z } from 'zod';
2
+ import { type APIClient } from '../api.ts';
3
+ import { StreamResponseError } from './util.ts';
4
+
5
+ // --- Schemas ---
6
+
7
+ export const NamespaceInfoSchema = z.object({
8
+ name: z.string().describe('the namespace name'),
9
+ count: z.number().describe('number of streams in this namespace'),
10
+ total_size: z.number().describe('total size in bytes across all streams'),
11
+ created_at: z.string().describe('ISO 8601 timestamp of the earliest stream'),
12
+ last_used_at: z.string().describe('ISO 8601 timestamp of the most recent activity'),
13
+ internal: z.boolean().describe('whether this namespace contains internal/system streams'),
14
+ });
15
+
16
+ export type NamespaceInfo = z.infer<typeof NamespaceInfoSchema>;
17
+
18
+ // Response schema matching Pulse's format (NO data wrapper)
19
+ const ListNamespacesResponseSchema = z.discriminatedUnion('success', [
20
+ z.object({
21
+ success: z.literal<false>(false),
22
+ message: z.string().optional(),
23
+ }),
24
+ z.object({
25
+ success: z.literal<true>(true),
26
+ namespaces: z.array(NamespaceInfoSchema),
27
+ total: z.number(),
28
+ }),
29
+ ]);
30
+
31
+ type ListNamespacesResponse = z.infer<typeof ListNamespacesResponseSchema>;
32
+
33
+ export const StreamNamespaceEntrySchema = z.object({
34
+ id: z.string().describe('the stream entry id'),
35
+ name: z.string().describe('the stream namespace name'),
36
+ created_at: z.string().nullable().describe('ISO 8601 creation timestamp'),
37
+ updated_at: z.string().nullable().describe('ISO 8601 last update timestamp'),
38
+ org_id: z.string().describe('the organization id'),
39
+ project_id: z.string().nullable().describe('the project id'),
40
+ chunks: z.number().describe('number of chunks'),
41
+ completed: z.boolean().describe('whether the stream upload is completed'),
42
+ size_bytes: z.number().describe('size in bytes'),
43
+ started_at: z.string().nullable().describe('ISO 8601 stream start timestamp'),
44
+ ended_at: z.string().nullable().describe('ISO 8601 stream end timestamp'),
45
+ headers: z.record(z.string(), z.string()).nullable().optional().describe('stream headers'),
46
+ metadata: z.record(z.string(), z.string()).nullable().optional().describe('stream metadata'),
47
+ expires_at: z.string().nullable().describe('ISO 8601 expiration timestamp or null'),
48
+ url: z.string().describe('public URL to access the stream'),
49
+ });
50
+
51
+ export type StreamNamespaceEntry = z.infer<typeof StreamNamespaceEntrySchema>;
52
+
53
+ const GetNamespaceResponseSchema = z.discriminatedUnion('success', [
54
+ z.object({
55
+ success: z.literal<false>(false),
56
+ message: z.string().optional(),
57
+ }),
58
+ z.object({
59
+ success: z.literal<true>(true),
60
+ name: z.string(),
61
+ entries: z.array(StreamNamespaceEntrySchema),
62
+ total: z.number(),
63
+ total_size: z.number(),
64
+ }),
65
+ ]);
66
+
67
+ type GetNamespaceResponse = z.infer<typeof GetNamespaceResponseSchema>;
68
+
69
+ // --- Options ---
70
+
71
+ export interface StreamListNamespacesOptions {
72
+ limit?: number;
73
+ offset?: number;
74
+ }
75
+
76
+ export interface StreamGetNamespaceOptions {
77
+ limit?: number;
78
+ offset?: number;
79
+ }
80
+
81
+ // --- Return types ---
82
+
83
+ export interface StreamNamespacesResult {
84
+ namespaces: NamespaceInfo[];
85
+ total: number;
86
+ }
87
+
88
+ export interface StreamNamespaceResult {
89
+ name: string;
90
+ entries: StreamNamespaceEntry[];
91
+ total: number;
92
+ totalSize: number;
93
+ }
94
+
95
+ // --- Functions ---
96
+
97
+ /**
98
+ * List stream namespaces (aggregated view).
99
+ * Each namespace groups streams by name, showing count, total size, and timestamps.
100
+ *
101
+ * @param client - The API client configured for Pulse
102
+ * @param options - Pagination options
103
+ * @returns Aggregated namespace list with total count
104
+ *
105
+ * @example
106
+ * const result = await streamListNamespaces(client, { limit: 50 });
107
+ * console.log(`Found ${result.total} namespaces`);
108
+ */
109
+ export async function streamListNamespaces(
110
+ client: APIClient,
111
+ options: StreamListNamespacesOptions = {}
112
+ ): Promise<StreamNamespacesResult> {
113
+ const resp = await client.post<ListNamespacesResponse>(
114
+ '/namespaces',
115
+ { limit: options.limit, offset: options.offset },
116
+ ListNamespacesResponseSchema
117
+ );
118
+
119
+ if (resp.success) {
120
+ return { namespaces: resp.namespaces, total: resp.total };
121
+ }
122
+
123
+ throw new StreamResponseError({ message: resp.message });
124
+ }
125
+
126
+ /**
127
+ * Get entries within a specific stream namespace.
128
+ * Returns full detail for each stream entry in the namespace.
129
+ *
130
+ * @param client - The API client configured for Pulse
131
+ * @param name - The namespace name
132
+ * @param options - Pagination options
133
+ * @returns Namespace entries with total count and size
134
+ *
135
+ * @example
136
+ * const result = await streamGetNamespace(client, 'agent-logs', { limit: 100 });
137
+ * console.log(`${result.total} entries, ${result.totalSize} bytes total`);
138
+ */
139
+ export async function streamGetNamespace(
140
+ client: APIClient,
141
+ name: string,
142
+ options: StreamGetNamespaceOptions = {}
143
+ ): Promise<StreamNamespaceResult> {
144
+ const resp = await client.post<GetNamespaceResponse>(
145
+ `/namespace/${encodeURIComponent(name)}/info`,
146
+ { limit: options.limit, offset: options.offset },
147
+ GetNamespaceResponseSchema
148
+ );
149
+
150
+ if (resp.success) {
151
+ return {
152
+ name: resp.name,
153
+ entries: resp.entries,
154
+ total: resp.total,
155
+ totalSize: resp.total_size,
156
+ };
157
+ }
158
+
159
+ throw new StreamResponseError({ message: resp.message });
160
+ }