@liquidmetal-ai/precip 1.0.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.
Files changed (78) hide show
  1. package/.prettierrc +9 -0
  2. package/CHANGELOG.md +8 -0
  3. package/eslint.config.mjs +28 -0
  4. package/package.json +53 -0
  5. package/src/engine/agent.ts +478 -0
  6. package/src/engine/llm-provider.test.ts +275 -0
  7. package/src/engine/llm-provider.ts +330 -0
  8. package/src/engine/stream-parser.ts +170 -0
  9. package/src/index.ts +142 -0
  10. package/src/mounts/mount-manager.test.ts +516 -0
  11. package/src/mounts/mount-manager.ts +327 -0
  12. package/src/mounts/mount-registry.ts +196 -0
  13. package/src/mounts/zod-to-string.test.ts +154 -0
  14. package/src/mounts/zod-to-string.ts +213 -0
  15. package/src/presets/agent-tools.ts +57 -0
  16. package/src/presets/index.ts +5 -0
  17. package/src/sandbox/README.md +1321 -0
  18. package/src/sandbox/bridges/README.md +571 -0
  19. package/src/sandbox/bridges/actor.test.ts +229 -0
  20. package/src/sandbox/bridges/actor.ts +195 -0
  21. package/src/sandbox/bridges/bridge-fixes.test.ts +614 -0
  22. package/src/sandbox/bridges/bucket.test.ts +300 -0
  23. package/src/sandbox/bridges/cleanup-reproduction.test.ts +225 -0
  24. package/src/sandbox/bridges/console-multiple.test.ts +187 -0
  25. package/src/sandbox/bridges/console.test.ts +157 -0
  26. package/src/sandbox/bridges/console.ts +122 -0
  27. package/src/sandbox/bridges/fetch.ts +93 -0
  28. package/src/sandbox/bridges/index.ts +78 -0
  29. package/src/sandbox/bridges/readable-stream.ts +323 -0
  30. package/src/sandbox/bridges/response.test.ts +154 -0
  31. package/src/sandbox/bridges/response.ts +123 -0
  32. package/src/sandbox/bridges/review-fixes.test.ts +331 -0
  33. package/src/sandbox/bridges/search.test.ts +475 -0
  34. package/src/sandbox/bridges/search.ts +264 -0
  35. package/src/sandbox/bridges/shared/body-methods.ts +93 -0
  36. package/src/sandbox/bridges/shared/cleanup.ts +112 -0
  37. package/src/sandbox/bridges/shared/convert.ts +76 -0
  38. package/src/sandbox/bridges/shared/headers.ts +181 -0
  39. package/src/sandbox/bridges/shared/index.ts +36 -0
  40. package/src/sandbox/bridges/shared/json-helpers.ts +77 -0
  41. package/src/sandbox/bridges/shared/path-parser.ts +109 -0
  42. package/src/sandbox/bridges/shared/promise-helper.ts +108 -0
  43. package/src/sandbox/bridges/shared/registry-setup.ts +84 -0
  44. package/src/sandbox/bridges/shared/response-object.ts +280 -0
  45. package/src/sandbox/bridges/shared/result-builder.ts +130 -0
  46. package/src/sandbox/bridges/shared/scope-helpers.ts +44 -0
  47. package/src/sandbox/bridges/shared/stream-reader.ts +90 -0
  48. package/src/sandbox/bridges/storage-bridge.test.ts +893 -0
  49. package/src/sandbox/bridges/storage.ts +421 -0
  50. package/src/sandbox/bridges/text-decoder.ts +190 -0
  51. package/src/sandbox/bridges/text-encoder.ts +102 -0
  52. package/src/sandbox/bridges/types.ts +39 -0
  53. package/src/sandbox/bridges/utils.ts +123 -0
  54. package/src/sandbox/index.ts +6 -0
  55. package/src/sandbox/quickjs-wasm.d.ts +9 -0
  56. package/src/sandbox/sandbox.test.ts +191 -0
  57. package/src/sandbox/sandbox.ts +831 -0
  58. package/src/sandbox/test-helper.ts +43 -0
  59. package/src/sandbox/test-mocks.ts +154 -0
  60. package/src/sandbox/user-stream.test.ts +77 -0
  61. package/src/skills/frontmatter.test.ts +305 -0
  62. package/src/skills/frontmatter.ts +200 -0
  63. package/src/skills/index.ts +9 -0
  64. package/src/skills/skills-loader.test.ts +237 -0
  65. package/src/skills/skills-loader.ts +200 -0
  66. package/src/tools/actor-storage-tools.ts +250 -0
  67. package/src/tools/code-tools.test.ts +199 -0
  68. package/src/tools/code-tools.ts +444 -0
  69. package/src/tools/file-tools.ts +206 -0
  70. package/src/tools/registry.ts +125 -0
  71. package/src/tools/script-tools.ts +145 -0
  72. package/src/tools/smartbucket-tools.ts +203 -0
  73. package/src/tools/sql-tools.ts +213 -0
  74. package/src/tools/tool-factory.ts +119 -0
  75. package/src/types.ts +512 -0
  76. package/tsconfig.eslint.json +5 -0
  77. package/tsconfig.json +15 -0
  78. package/vitest.config.ts +33 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Tool Factory - Reduces boilerplate for path-based tools
3
+ *
4
+ * This factory handles common patterns:
5
+ * - Mount validation and resolution
6
+ * - Path parsing
7
+ * - Mount type checking
8
+ * - Permission (mode) checking
9
+ * - Consistent error messages
10
+ */
11
+
12
+ import type { Tool, ToolContext, ToolParameter } from '../types.js';
13
+ import type { MountInfo, MountProvider, ParsedPath } from '../types.js';
14
+
15
+ /**
16
+ * Configuration for creating a path-based tool
17
+ */
18
+ export interface PathBasedToolConfig<TConfig extends Record<string, any>, TResult> {
19
+ name: string;
20
+ description: string;
21
+ parameters: Record<string, ToolParameter>;
22
+ allowedMountTypes: MountInfo['type'][];
23
+ mode?: 'read' | 'write';
24
+ /** Name of the parameter containing the path (default: 'path', also checks 'database') */
25
+ pathParameterName?: string;
26
+ executor: (mount: MountInfo, parsed: ParsedPath, params: TConfig) => Promise<TResult>;
27
+ }
28
+
29
+ /**
30
+ * Mount access validation utilities
31
+ */
32
+ export class MountAccess {
33
+ /**
34
+ * Resolve a mount path with validation
35
+ */
36
+ static async getWithValidation(
37
+ mountManager: MountProvider,
38
+ path: string,
39
+ allowedTypes: MountInfo['type'][],
40
+ mode?: 'read' | 'write'
41
+ ): Promise<{ mount: MountInfo; parsed: ParsedPath }> {
42
+ const parsed = mountManager.parsePath(path);
43
+ const mount = mountManager.getMount(parsed.mountName);
44
+
45
+ if (!mount) {
46
+ throw this.createMountNotFoundError(parsed.mountName, mountManager);
47
+ }
48
+
49
+ if (!allowedTypes.includes(mount.type)) {
50
+ throw this.createTypeError(mount, allowedTypes);
51
+ }
52
+
53
+ if (mode === 'write' && mount.mode !== 'rw') {
54
+ throw new Error(`Cannot write to read-only mount: ${mount.name}. Mount must have mode 'rw'.`);
55
+ }
56
+
57
+ return { mount, parsed };
58
+ }
59
+
60
+ /**
61
+ * Create a "mount not found" error with helpful message
62
+ */
63
+ static createMountNotFoundError(name: string, mountManager: MountProvider): Error {
64
+ const available = Array.from(mountManager.getAllMounts().keys()).join(', ');
65
+ return new Error(`Mount not found: ${name}. Available: ${available}`);
66
+ }
67
+
68
+ /**
69
+ * Create a mount type mismatch error
70
+ */
71
+ static createTypeError(
72
+ mount: MountInfo,
73
+ allowedTypes: MountInfo['type'][]
74
+ ): Error {
75
+ const typeList = allowedTypes.join(' or ');
76
+ return new Error(
77
+ `Mount '${mount.name}' is not ${typeList} (it's ${mount.type})`
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create a path-based tool with automatic mount validation
84
+ *
85
+ * This eliminates ~80% of boilerplate from tools that operate on mounted resources.
86
+ */
87
+ export function createPathBasedTool<TConfig extends Record<string, any>, TResult>(
88
+ config: PathBasedToolConfig<TConfig, TResult>
89
+ ): Tool {
90
+ return {
91
+ definition: {
92
+ name: config.name,
93
+ description: config.description,
94
+ parameters: config.parameters
95
+ },
96
+
97
+ async execute(params: any, context: ToolContext) {
98
+ const mountManager = context.mounts;
99
+
100
+ // Extract path from params using configured parameter name
101
+ const pathParam = config.pathParameterName || 'path';
102
+ const path = params[pathParam] || (pathParam === 'path' ? params.database : undefined);
103
+ if (!path) {
104
+ throw new Error(`Parameter '${pathParam}' is required`);
105
+ }
106
+
107
+ // Validate mount and get resource
108
+ const { mount, parsed } = await MountAccess.getWithValidation(
109
+ mountManager,
110
+ path,
111
+ config.allowedMountTypes,
112
+ config.mode
113
+ );
114
+
115
+ // Execute the tool's logic with properly typed params
116
+ return config.executor(mount, parsed, params);
117
+ }
118
+ };
119
+ }
package/src/types.ts ADDED
@@ -0,0 +1,512 @@
1
+ /**
2
+ * Core type definitions for Precip Agent SDK
3
+ */
4
+
5
+ // Use Raindrop framework types
6
+ import type {
7
+ Ai,
8
+ Bucket,
9
+ SmartBucket,
10
+ KvCache,
11
+ SqlDatabase,
12
+ Actor,
13
+ ActorNamespace,
14
+ ActorStorage
15
+ } from '@liquidmetal-ai/raindrop-framework';
16
+ import type { BridgeInstaller } from './sandbox/bridges/types.js';
17
+ import type { z } from 'zod';
18
+
19
+ // ============================================================================
20
+ // LLM Provider Types
21
+ // ============================================================================
22
+
23
+ export type LLMProvider = 'openai' | 'anthropic' | 'liquidmetal';
24
+
25
+ export interface LLMConfig {
26
+ provider: LLMProvider;
27
+ model: string;
28
+ apiKey?: string;
29
+ baseUrl?: string;
30
+ temperature?: number;
31
+ maxTokens?: number;
32
+ streaming?: boolean;
33
+ aiBinding?: Ai;
34
+ }
35
+
36
+ export interface LLMMessage {
37
+ role: 'system' | 'user' | 'assistant' | 'tool';
38
+ content: string | null;
39
+ name?: string;
40
+ tool_call_id?: string;
41
+ tool_calls?: LLMToolCall[];
42
+ }
43
+
44
+ export interface LLMToolCall {
45
+ id: string;
46
+ type: 'function';
47
+ function: {
48
+ name: string;
49
+ arguments: string; // JSON string
50
+ };
51
+ }
52
+
53
+ export interface LLMResponse {
54
+ content: string | null;
55
+ toolCalls: LLMToolCall[];
56
+ finishReason: string | null;
57
+ usage?: {
58
+ promptTokens: number;
59
+ completionTokens: number;
60
+ totalTokens: number;
61
+ };
62
+ }
63
+
64
+ // ============================================================================
65
+ // Mount Types
66
+ // ============================================================================
67
+
68
+ /** Mount types */
69
+ export type MountType = 'bucket' | 'smartbucket' | 'kv' | 'database' | 'actor' | 'actor-storage';
70
+
71
+ // ----------------------------------------------------------------------------
72
+ // Actor Method Schema (Zod-based)
73
+ // ----------------------------------------------------------------------------
74
+
75
+ /**
76
+ * Schema definition for an actor RPC method.
77
+ * Uses Zod for type-safe, self-documenting method signatures.
78
+ *
79
+ * Arguments are represented as a tuple (z.tuple), matching the actual
80
+ * method signature. For example, a method `doSomething(a: string, b: number)`
81
+ * should have `params: z.tuple([z.string().describe("a"), z.number().describe("b")])`.
82
+ * A method with no arguments uses `params: z.tuple([])`.
83
+ *
84
+ * Use `.describe()` on each tuple element to name the parameter — these names
85
+ * appear in generated documentation and system prompts for LLMs.
86
+ */
87
+ export interface ActorMethodSchema {
88
+ /** Human-readable description of what this method does */
89
+ description: string;
90
+ /**
91
+ * Zod tuple schema for method arguments.
92
+ * Each element in the tuple corresponds to a positional argument.
93
+ * Use `.describe("name")` on each element to provide a parameter name.
94
+ *
95
+ * Examples:
96
+ * - No args: `z.tuple([])`
97
+ * - Single arg: `z.tuple([z.object({ theme: z.string() }).describe("prefs")])`
98
+ * - Multiple args: `z.tuple([z.string().describe("userId"), z.number().describe("limit")])`
99
+ */
100
+ params: z.ZodTuple<[z.ZodTypeAny, ...z.ZodTypeAny[]] | [], z.ZodTypeAny | null>;
101
+ /** Zod schema for return type */
102
+ returns: z.ZodType;
103
+ }
104
+
105
+ // ----------------------------------------------------------------------------
106
+ // Discriminated Mount Configurations
107
+ // ----------------------------------------------------------------------------
108
+
109
+ /** Mount access mode */
110
+ export type MountMode = 'ro' | 'rw';
111
+
112
+ /** Base config for all mount types */
113
+ interface BaseMountConfig {
114
+ /** Human-readable description of what this resource is for */
115
+ description?: string;
116
+ /** Access mode: 'ro' (read-only) or 'rw' (read-write). Default: 'ro' */
117
+ mode?: MountMode;
118
+ }
119
+
120
+ /** Config for Bucket mounts */
121
+ export interface BucketMountConfig extends BaseMountConfig {
122
+ type: 'bucket';
123
+ resource: Bucket;
124
+ }
125
+
126
+ /** Config for SmartBucket mounts */
127
+ export interface SmartBucketMountConfig extends BaseMountConfig {
128
+ type: 'smartbucket';
129
+ resource: SmartBucket;
130
+ }
131
+
132
+ /** Config for KvCache mounts */
133
+ export interface KvMountConfig extends BaseMountConfig {
134
+ type: 'kv';
135
+ resource: KvCache;
136
+ }
137
+
138
+ /** Config for SqlDatabase mounts */
139
+ export interface SqlMountConfig extends BaseMountConfig {
140
+ type: 'database';
141
+ resource: SqlDatabase;
142
+ }
143
+
144
+ /** Config for Actor mounts - requires method schemas */
145
+ export interface ActorMountConfig<T extends Actor<unknown> = Actor<unknown>>
146
+ extends BaseMountConfig {
147
+ type: 'actor';
148
+ resource: ActorNamespace<T>;
149
+ /** Zod schemas for each RPC method the actor exposes */
150
+ methods: Record<string, ActorMethodSchema>;
151
+ }
152
+
153
+ /** Config for ActorStorage mounts - exposes actor's local state.storage as a filesystem-like mount */
154
+ export interface ActorStorageMountConfig extends BaseMountConfig {
155
+ type: 'actor-storage';
156
+ resource: ActorStorage;
157
+ }
158
+
159
+ /** Discriminated union of all mount configs */
160
+ export type MountConfigType =
161
+ | BucketMountConfig
162
+ | SmartBucketMountConfig
163
+ | KvMountConfig
164
+ | SqlMountConfig
165
+ | ActorMountConfig
166
+ | ActorStorageMountConfig;
167
+
168
+ /** Mount configuration map - requires explicit type for each mount */
169
+ export interface MountConfig {
170
+ [mountName: string]: MountConfigType;
171
+ }
172
+
173
+ // ----------------------------------------------------------------------------
174
+ // Internal Mount Info (normalized)
175
+ // ----------------------------------------------------------------------------
176
+
177
+ /** Base mount info after normalization */
178
+ interface BaseMountInfo {
179
+ name: string;
180
+ type: MountType;
181
+ description?: string;
182
+ /** Access mode: 'ro' (read-only) or 'rw' (read-write). Default: 'ro' */
183
+ mode?: MountMode;
184
+ }
185
+
186
+ /** Normalized info for Bucket mounts */
187
+ export interface BucketMountInfo extends BaseMountInfo {
188
+ type: 'bucket';
189
+ resource: Bucket;
190
+ }
191
+
192
+ /** Normalized info for SmartBucket mounts */
193
+ export interface SmartBucketMountInfo extends BaseMountInfo {
194
+ type: 'smartbucket';
195
+ resource: SmartBucket;
196
+ }
197
+
198
+ /** Normalized info for KvCache mounts */
199
+ export interface KvMountInfo extends BaseMountInfo {
200
+ type: 'kv';
201
+ resource: KvCache;
202
+ }
203
+
204
+ /** Normalized info for SqlDatabase mounts */
205
+ export interface SqlMountInfo extends BaseMountInfo {
206
+ type: 'database';
207
+ resource: SqlDatabase;
208
+ }
209
+
210
+ /** Normalized info for Actor mounts */
211
+ export interface ActorMountInfo extends BaseMountInfo {
212
+ type: 'actor';
213
+ resource: ActorNamespace<Actor<unknown>>;
214
+ methods: Record<string, ActorMethodSchema>;
215
+ }
216
+
217
+ /** Normalized info for ActorStorage mounts */
218
+ export interface ActorStorageMountInfo extends BaseMountInfo {
219
+ type: 'actor-storage';
220
+ resource: ActorStorage;
221
+ }
222
+
223
+ /** Union of all normalized mount info types (discriminated by type) */
224
+ export type MountInfo =
225
+ | BucketMountInfo
226
+ | SmartBucketMountInfo
227
+ | KvMountInfo
228
+ | SqlMountInfo
229
+ | ActorMountInfo
230
+ | ActorStorageMountInfo;
231
+
232
+ export interface ParsedPath {
233
+ mountName: string;
234
+ path: string;
235
+ fullPath: string;
236
+ }
237
+
238
+ // ============================================================================
239
+ // Tool Types
240
+ // ============================================================================
241
+
242
+ export interface ToolParameter {
243
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
244
+ description: string;
245
+ required?: boolean;
246
+ enum?: string[];
247
+ items?: ToolParameter;
248
+ properties?: Record<string, ToolParameter>;
249
+ }
250
+
251
+ export interface ToolDefinition {
252
+ name: string;
253
+ description: string;
254
+ parameters: Record<string, ToolParameter>;
255
+ }
256
+
257
+ /**
258
+ * Interface for mount management in tool contexts.
259
+ * Provides path-based access to mounted resources.
260
+ * Implemented by MountManager.
261
+ */
262
+ export interface MountProvider {
263
+ /** Get mount by name */
264
+ getMount(name: string): MountInfo | undefined;
265
+ /** Get all mounts as a map */
266
+ getAllMounts(): Map<string, MountInfo>;
267
+ /** Parse a full path into mount name and resource path */
268
+ parsePath(fullPath: string): ParsedPath;
269
+ /** Resolve a path to a mount and resource path */
270
+ resolvePath(fullPath: string): { mount: MountInfo; path: string };
271
+ /** List all mounts of a given type */
272
+ getMountsByType<T extends MountInfo['type']>(type: T): MountInfo[];
273
+ /** Get all actor mounts with their schemas */
274
+ getActorMounts(): Map<string, ActorMountInfo>;
275
+ /** Generate a formatted description of all mounts for system prompts */
276
+ getMountsDescription(): string;
277
+ /** Create sandbox globals from mounts */
278
+ createSandboxGlobals(): Record<string, unknown>;
279
+ }
280
+
281
+ export interface ToolContext {
282
+ mounts: MountProvider;
283
+ logger?: Logger;
284
+ env?: unknown;
285
+ }
286
+
287
+ export interface Tool {
288
+ definition: ToolDefinition;
289
+ execute: (params: any, context: ToolContext) => Promise<any>;
290
+ }
291
+
292
+ export interface ToolResult {
293
+ success: boolean;
294
+ result?: any;
295
+ error?: string;
296
+ }
297
+
298
+ // ============================================================================
299
+ // Agent Types
300
+ // ============================================================================
301
+
302
+ export interface AgentConfig {
303
+ llm: LLMConfig;
304
+ system?: string;
305
+ mounts?: MountConfig;
306
+ tools?: (Tool | ToolClass)[];
307
+ /**
308
+ * Agent Skills configuration.
309
+ * Scans a mount path for skills and injects their metadata into the system prompt.
310
+ * @see https://agentskills.io/specification
311
+ */
312
+ skills?: {
313
+ /** Mount path where skills are stored, e.g. "/knowledge/skills/" */
314
+ path: string;
315
+ };
316
+ maxTurns?: number;
317
+ /**
318
+ * Retry configuration for transient LLM failures.
319
+ * When set, LLM calls that throw are retried with exponential backoff.
320
+ * By default retries on any error — use `retryOn` to filter.
321
+ */
322
+ retry?: RetryConfig;
323
+ logger?: Logger;
324
+ }
325
+
326
+ /**
327
+ * Configuration for retrying failed LLM calls.
328
+ *
329
+ * Defaults retry on every error. Provide `retryOn` to be selective
330
+ * (e.g. only retry HTTP 5xx but not 4xx auth errors).
331
+ */
332
+ export interface RetryConfig {
333
+ /**
334
+ * Maximum number of retry attempts after the initial call (default: 2).
335
+ * Total attempts = maxRetries + 1.
336
+ * For example, maxRetries=2 means the initial call plus 2 retries (3 attempts total).
337
+ */
338
+ maxRetries?: number;
339
+ /** Initial delay in ms before first retry (default: 1000) */
340
+ initialDelayMs?: number;
341
+ /** Backoff multiplier applied after each retry (default: 2) */
342
+ backoffMultiplier?: number;
343
+ /** Maximum delay cap in ms (default: 30000) */
344
+ maxDelayMs?: number;
345
+ /**
346
+ * Predicate to decide whether a given error should be retried.
347
+ * Return true to retry, false to fail immediately.
348
+ * Default: retry on any error.
349
+ */
350
+ retryOn?: (error: Error) => boolean;
351
+ }
352
+
353
+ export interface AgentResponse {
354
+ message: string;
355
+ reasoning?: string;
356
+ toolCalls?: ToolCallResult[];
357
+ turns: number;
358
+ finishReason: string | null;
359
+ usage?: {
360
+ promptTokens: number;
361
+ completionTokens: number;
362
+ totalTokens: number;
363
+ };
364
+ }
365
+
366
+ export interface ToolCallResult {
367
+ turn: number;
368
+ toolCallId: string;
369
+ toolName: string;
370
+ arguments: any;
371
+ result: any;
372
+ success: boolean;
373
+ error?: string;
374
+ }
375
+
376
+ export interface ConversationState {
377
+ messages: LLMMessage[];
378
+ turnCount: number;
379
+ totalTokens: number;
380
+ }
381
+
382
+ // ============================================================================
383
+ // Sandbox Types
384
+ // ============================================================================
385
+
386
+ export interface SandboxResult {
387
+ success: boolean;
388
+ result?: any;
389
+ error?: string;
390
+ executionTime?: number;
391
+ consoleOutput?: string[];
392
+ }
393
+
394
+ export interface SandboxOptions {
395
+ timeoutMs?: number;
396
+ memoryLimitBytes?: number;
397
+ logger?: Logger;
398
+ /** Additional bridge installers to run after standard bridges */
399
+ bridgeInstallers?: BridgeInstaller[];
400
+ }
401
+
402
+ // ----------------------------------------------------------------------------
403
+ // Typed Sandbox Globals
404
+ // ----------------------------------------------------------------------------
405
+
406
+ /** An async function to expose in the sandbox — always returns a promise to sandbox code */
407
+ export interface AsyncSandboxGlobal {
408
+ kind: 'async';
409
+ fn: (...args: any[]) => Promise<any>;
410
+ }
411
+
412
+ /** A sync function to expose in the sandbox — returns the value directly */
413
+ export interface SyncSandboxGlobal {
414
+ kind: 'sync';
415
+ fn: (...args: any[]) => any;
416
+ }
417
+
418
+ /** An object with typed methods and optional static properties */
419
+ export interface ObjectSandboxGlobal {
420
+ kind: 'object';
421
+ methods: Record<string, AsyncSandboxGlobal | SyncSandboxGlobal>;
422
+ properties?: Record<string, any>;
423
+ }
424
+
425
+ /** A single sandbox global entry */
426
+ export type SandboxGlobal =
427
+ | AsyncSandboxGlobal
428
+ | SyncSandboxGlobal
429
+ | ObjectSandboxGlobal
430
+ | number
431
+ | string
432
+ | boolean
433
+ | null
434
+ | undefined
435
+ | any[]
436
+ | Record<string, unknown>;
437
+
438
+ /** Map of named globals to inject into the sandbox */
439
+ export interface SandboxGlobals {
440
+ [key: string]: SandboxGlobal;
441
+ }
442
+
443
+ // Helper constructors for typed globals
444
+
445
+ /** Wrap an async function for sandbox injection */
446
+ export function sandboxAsync(fn: (...args: any[]) => Promise<any>): AsyncSandboxGlobal {
447
+ return { kind: 'async', fn };
448
+ }
449
+
450
+ /** Wrap a sync function for sandbox injection */
451
+ export function sandboxSync(fn: (...args: any[]) => any): SyncSandboxGlobal {
452
+ return { kind: 'sync', fn };
453
+ }
454
+
455
+ /** Wrap an object with typed methods for sandbox injection */
456
+ export function sandboxObject(
457
+ methods: Record<string, AsyncSandboxGlobal | SyncSandboxGlobal>,
458
+ properties?: Record<string, any>
459
+ ): ObjectSandboxGlobal {
460
+ return { kind: 'object', methods, properties };
461
+ }
462
+
463
+ // ============================================================================
464
+ // Agent Streaming Event Types
465
+ // ============================================================================
466
+
467
+ export type AgentEventType =
468
+ | 'tool_call_start'
469
+ | 'tool_call_end'
470
+ | 'llm_response'
471
+ | 'turn_complete';
472
+
473
+ export interface AgentEvent {
474
+ type: AgentEventType;
475
+ turn: number;
476
+ toolCall?: {
477
+ id: string;
478
+ name: string;
479
+ arguments: Record<string, unknown>;
480
+ };
481
+ toolResult?: {
482
+ id: string;
483
+ name: string;
484
+ result: unknown;
485
+ success: boolean;
486
+ error?: string;
487
+ };
488
+ message?: string;
489
+ finishReason?: string | null;
490
+ usage?: { promptTokens: number; completionTokens: number; totalTokens: number };
491
+ }
492
+
493
+ // ============================================================================
494
+ // Logger Types
495
+ // ============================================================================
496
+
497
+ export interface Logger {
498
+ info(message: string, data?: any): void;
499
+ warn(message: string, data?: any): void;
500
+ error(message: string, data?: any): void;
501
+ debug(message: string, data?: any): void;
502
+ with?(data: any): Logger;
503
+ withError?(error: Error): Logger;
504
+ }
505
+
506
+ // ============================================================================
507
+ // Tool Class Interface
508
+ // ============================================================================
509
+
510
+ export interface ToolClass {
511
+ new (context: ToolContext): Tool;
512
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": ["src/**/*"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@liquidmetal-ai/typescript-config/base.json",
4
+ "compilerOptions": {
5
+ "lib": ["ESNext", "WebWorker"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "bundler",
8
+ "noEmit": false,
9
+ "outDir": "./dist",
10
+ "strict": true,
11
+ "types": ["@liquidmetal-ai/raindrop-framework"]
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["eslint.config.js", "node_modules", "dist", "examples", "src/**/*.test.ts"]
15
+ }
@@ -0,0 +1,33 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ testTimeout: 10000,
8
+ passWithNoTests: true
9
+ },
10
+ plugins: [
11
+ {
12
+ name: 'wasm-stub',
13
+ enforce: 'pre',
14
+ resolveId(id) {
15
+ // Stub the /wasm export from @jitl packages.
16
+ // In Raindrop this resolves to a WebAssembly.Module,
17
+ // but in Node.js tests the quickjs library loads WASM from disk
18
+ // automatically, so we just need to prevent the import from failing.
19
+ if (id.includes('/wasm') && id.includes('@jitl')) {
20
+ return '\0virtual:wasm-stub';
21
+ }
22
+ },
23
+ load(id) {
24
+ if (id === '\0virtual:wasm-stub') {
25
+ return 'export default undefined;';
26
+ }
27
+ }
28
+ }
29
+ ],
30
+ build: {
31
+ target: 'esnext'
32
+ }
33
+ });