@standardagents/builder 0.8.1

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/plugin.js ADDED
@@ -0,0 +1,3183 @@
1
+ import fs2 from 'fs';
2
+ import path3 from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ // src/plugin.ts
6
+ var TSCONFIG_CONTENT = `{
7
+ "compilerOptions": {
8
+ "composite": true,
9
+ "declaration": true,
10
+ "declarationMap": true,
11
+ "skipLibCheck": true,
12
+ "strict": true,
13
+ "moduleResolution": "bundler",
14
+ "module": "ESNext",
15
+ "target": "ESNext"
16
+ },
17
+ "include": ["types.d.ts", "virtual-module.d.ts"]
18
+ }
19
+ `;
20
+ function scanForNames(dir, useFilename = false) {
21
+ const names = [];
22
+ if (!fs2.existsSync(dir)) {
23
+ return names;
24
+ }
25
+ try {
26
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
27
+ for (const entry of entries) {
28
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
29
+ const filePath = path3.join(dir, entry.name);
30
+ const content = fs2.readFileSync(filePath, "utf-8");
31
+ if (useFilename) {
32
+ const toolName = entry.name.replace(/\.ts$/, "");
33
+ if (content.includes("defineTool")) {
34
+ names.push(toolName);
35
+ }
36
+ } else {
37
+ const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
38
+ if (nameMatch) {
39
+ names.push(nameMatch[1]);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ } catch (error) {
45
+ console.error(`Error scanning directory ${dir}:`, error);
46
+ }
47
+ return names;
48
+ }
49
+ function generateRegistryProperties(names) {
50
+ if (names.length === 0) {
51
+ return "";
52
+ }
53
+ return names.map((n) => `'${n}': true;`).join("\n ");
54
+ }
55
+ function generateTypesContent(config) {
56
+ const models = scanForNames(config.modelsDir);
57
+ const prompts = scanForNames(config.promptsDir);
58
+ const agents = scanForNames(config.agentsDir);
59
+ const tools = scanForNames(config.toolsDir, true);
60
+ const callables = [...prompts, ...agents, ...tools];
61
+ return `// Auto-generated by @standardagents/builder - DO NOT EDIT
62
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
63
+ //
64
+ // This file augments the AgentBuilder namespace declared in @standardagents/builder
65
+ // to provide type-safe references for your models, prompts, agents, and tools.
66
+
67
+ /**
68
+ * Augment the global AgentBuilder namespace with your project's specific types.
69
+ * This provides autocomplete and type checking for model, prompt, agent, and tool references.
70
+ *
71
+ * Uses interface declaration merging with property keys to create union types.
72
+ * Example: interface ModelRegistry { 'gpt-4o': true; } gives type Models = 'gpt-4o'
73
+ */
74
+ declare global {
75
+ namespace AgentBuilder {
76
+ /** Model names from agents/models/ */
77
+ interface ModelRegistry {
78
+ ${generateRegistryProperties(models)}
79
+ }
80
+
81
+ /** Prompt names from agents/prompts/ */
82
+ interface PromptRegistry {
83
+ ${generateRegistryProperties(prompts)}
84
+ }
85
+
86
+ /** Agent names from agents/agents/ */
87
+ interface AgentRegistry {
88
+ ${generateRegistryProperties(agents)}
89
+ }
90
+
91
+ /** Tool names from agents/tools/ */
92
+ interface ToolRegistry {
93
+ ${generateRegistryProperties(tools)}
94
+ }
95
+
96
+ /** All callable items (prompts, agents, tools) that can be used as tools */
97
+ interface CallableRegistry {
98
+ ${generateRegistryProperties(callables)}
99
+ }
100
+ }
101
+ }
102
+
103
+ // This export is required for TypeScript to treat this as a module
104
+ // and allow the declare global to work properly
105
+ export {};
106
+ `;
107
+ }
108
+ function generateVirtualModuleContent() {
109
+ return `// Auto-generated by @standardagents/builder - DO NOT EDIT
110
+ //
111
+ // Type declarations for the virtual:@standardagents/builder module.
112
+ // This file must NOT have any exports to work as an ambient module declaration.
113
+
114
+ /**
115
+ * Type declarations for the consolidated virtual module.
116
+ * This module provides DurableThread and DurableAgentBuilder classes
117
+ * with all virtual module methods (tools, hooks, models, prompts, agents)
118
+ * already implemented, plus the router function.
119
+ */
120
+ declare module 'virtual:@standardagents/builder' {
121
+ import type { DurableThread as BaseDurableThread } from '@standardagents/builder/runtime';
122
+ import type { DurableAgentBuilder as BaseDurableAgentBuilder } from '@standardagents/builder/runtime';
123
+ import type { ThreadEnv } from '@standardagents/builder/runtime';
124
+
125
+ // ============================================================
126
+ // Message Types
127
+ // ============================================================
128
+
129
+ export interface ThreadMessage {
130
+ id: string;
131
+ role: string;
132
+ content: string | null;
133
+ name: string | null;
134
+ tool_calls: string | null;
135
+ tool_call_id: string | null;
136
+ tool_status: 'success' | 'error' | null;
137
+ log_id: string | null;
138
+ created_at: number;
139
+ silent: boolean;
140
+ parent_id: string | null;
141
+ depth: number;
142
+ status: 'pending' | 'completed' | 'failed' | null;
143
+ reasoning_content: string | null;
144
+ reasoning_details: string | null;
145
+ }
146
+
147
+ export interface GetMessagesResult {
148
+ messages: ThreadMessage[];
149
+ total: number;
150
+ hasMore: boolean;
151
+ }
152
+
153
+ // ============================================================
154
+ // Log Types
155
+ // ============================================================
156
+
157
+ export interface ThreadLog {
158
+ id: string;
159
+ message_id: string;
160
+ provider: string;
161
+ model: string;
162
+ model_name: string | null;
163
+ prompt_name: string | null;
164
+ tools_called: string | null;
165
+ parent_log_id: string | null;
166
+ retry_of_log_id: string | null;
167
+ error: string | null;
168
+ cost_total: number | null;
169
+ is_complete: boolean;
170
+ created_at: number;
171
+ request_body: string | null;
172
+ }
173
+
174
+ export interface GetLogsResult {
175
+ logs: ThreadLog[];
176
+ total: number;
177
+ hasMore: boolean;
178
+ }
179
+
180
+ export interface ThreadLogDetails extends ThreadLog {
181
+ endpoint: string | null;
182
+ request_headers: string | null;
183
+ response_body: string | null;
184
+ response_headers: string | null;
185
+ status_code: number | null;
186
+ reasoning_content: string | null;
187
+ input_tokens: number | null;
188
+ cached_tokens: number | null;
189
+ output_tokens: number | null;
190
+ reasoning_tokens: number | null;
191
+ total_tokens: number | null;
192
+ latency_ms: number | null;
193
+ time_to_first_token_ms: number | null;
194
+ finish_reason: string | null;
195
+ error_type: string | null;
196
+ cost_input: number | null;
197
+ cost_output: number | null;
198
+ message_history_length: number | null;
199
+ tools_available: number | null;
200
+ tools_schema: string | null;
201
+ message_history: string | null;
202
+ system_prompt: string | null;
203
+ errors: string | null;
204
+ tool_results: string | null;
205
+ }
206
+
207
+ // ============================================================
208
+ // Thread Metadata Types
209
+ // ============================================================
210
+
211
+ export interface ThreadMetaResult {
212
+ thread: {
213
+ id: string;
214
+ agent_id: string;
215
+ user_id: string | null;
216
+ tags: string[];
217
+ created_at: number;
218
+ };
219
+ agent: {
220
+ id: string;
221
+ title: string;
222
+ type: string;
223
+ side_a_label?: string;
224
+ side_b_label?: string;
225
+ } | null;
226
+ stats: {
227
+ message_count: number;
228
+ log_count: number;
229
+ };
230
+ }
231
+
232
+ // ============================================================
233
+ // DurableThread Class
234
+ // ============================================================
235
+
236
+ /**
237
+ * DurableThread with all virtual module methods pre-implemented.
238
+ * Extend this class in your agents/Thread.ts file.
239
+ *
240
+ * @example
241
+ * \`\`\`typescript
242
+ * import { DurableThread } from 'virtual:@standardagents/builder'
243
+ *
244
+ * export class Thread extends DurableThread {}
245
+ * \`\`\`
246
+ */
247
+ export class DurableThread extends BaseDurableThread {
248
+ // Virtual module registry methods
249
+ tools(): Record<string, () => Promise<any>>;
250
+ hooks(): Record<string, () => Promise<any>>;
251
+ models(): Record<string, () => Promise<any>>;
252
+ prompts(): Record<string, () => Promise<any>>;
253
+ agents(): Record<string, () => Promise<any>>;
254
+
255
+ // Lookup methods
256
+ loadModel(name: string): Promise<any>;
257
+ getModelNames(): string[];
258
+ loadPrompt(name: string): Promise<any>;
259
+ getPromptNames(): string[];
260
+ loadAgent(name: string): Promise<any>;
261
+ getAgentNames(): string[];
262
+
263
+ // Execution methods
264
+ execute(
265
+ threadId: string,
266
+ agentId: string,
267
+ initial_messages?: any[],
268
+ data?: any
269
+ ): Promise<Response>;
270
+ sendMessage(
271
+ threadId: string,
272
+ content: string,
273
+ role?: string
274
+ ): Promise<Response>;
275
+ stop(): Promise<Response>;
276
+ shouldStop(): Promise<boolean>;
277
+
278
+ // Message methods
279
+ getMessages(
280
+ limit?: number,
281
+ offset?: number,
282
+ order?: 'ASC' | 'DESC',
283
+ includeSilent?: boolean,
284
+ maxDepth?: number
285
+ ): Promise<GetMessagesResult>;
286
+ deleteMessage(messageId: string): Promise<{ success: boolean; error?: string }>;
287
+ seedMessages(args: {
288
+ messages: Array<{
289
+ id: string;
290
+ role: string;
291
+ content: string;
292
+ created_at: number;
293
+ }>;
294
+ }): Promise<{ success: boolean; count?: number; error?: string }>;
295
+
296
+ // Log methods
297
+ getLogs(
298
+ limit?: number,
299
+ offset?: number,
300
+ order?: 'ASC' | 'DESC'
301
+ ): Promise<GetLogsResult>;
302
+ getLogDetails(logId: string): Promise<ThreadLogDetails>;
303
+
304
+ // Thread management methods
305
+ getThreadMeta(threadId: string): Promise<ThreadMetaResult>;
306
+ updateThreadMeta(
307
+ threadId: string,
308
+ params: UpdateThreadParams
309
+ ): Promise<{
310
+ success: boolean;
311
+ error?: string;
312
+ thread?: {
313
+ id: string;
314
+ agent_id: string;
315
+ user_id: string | null;
316
+ tags: string[];
317
+ created_at: number;
318
+ };
319
+ }>;
320
+ deleteThread(): Promise<{ success: boolean; message: string }>;
321
+
322
+ // WebSocket/fetch handler
323
+ fetch(request: Request): Promise<Response>;
324
+ }
325
+
326
+ // ============================================================
327
+ // DurableAgentBuilder Types
328
+ // ============================================================
329
+
330
+ export interface ThreadRegistryEntry {
331
+ id: string;
332
+ agent_name: string;
333
+ user_id: string | null;
334
+ tags: string[] | null;
335
+ created_at: number;
336
+ }
337
+
338
+ export interface UpdateThreadParams {
339
+ agent_name?: string;
340
+ user_id?: string | null;
341
+ tags?: string[] | null;
342
+ }
343
+
344
+ export interface User {
345
+ id: string;
346
+ username: string;
347
+ password_hash: string;
348
+ role: 'admin';
349
+ created_at: number;
350
+ updated_at: number;
351
+ }
352
+
353
+ export interface Provider {
354
+ name: string;
355
+ sdk: string;
356
+ api_key: string;
357
+ }
358
+
359
+ // ============================================================
360
+ // DurableAgentBuilder Class
361
+ // ============================================================
362
+
363
+ /**
364
+ * DurableAgentBuilder with all virtual module methods pre-implemented.
365
+ * Extend this class in your agents/AgentBuilder.ts file.
366
+ *
367
+ * @example
368
+ * \`\`\`typescript
369
+ * import { DurableAgentBuilder } from 'virtual:@standardagents/builder'
370
+ *
371
+ * export class AgentBuilder extends DurableAgentBuilder {}
372
+ * \`\`\`
373
+ */
374
+ export class DurableAgentBuilder extends BaseDurableAgentBuilder {
375
+ // Virtual module registry methods
376
+ tools(): Record<string, () => Promise<any>>;
377
+ hooks(): Record<string, () => Promise<any>>;
378
+ models(): Record<string, () => Promise<any>>;
379
+ prompts(): Record<string, () => Promise<any>>;
380
+ agents(): Record<string, () => Promise<any>>;
381
+
382
+ // Lookup methods
383
+ loadModel(name: string): Promise<any>;
384
+ getModelNames(): string[];
385
+ loadPrompt(name: string): Promise<any>;
386
+ getPromptNames(): string[];
387
+ loadAgent(name: string): Promise<any>;
388
+ getAgentNames(): string[];
389
+
390
+ // Thread registry methods
391
+ createThread(params: {
392
+ agent_name: string;
393
+ user_id?: string;
394
+ tags?: string[];
395
+ }): Promise<ThreadRegistryEntry>;
396
+ getThread(id: string): Promise<ThreadRegistryEntry | null>;
397
+ listThreads(params?: {
398
+ agent_name?: string;
399
+ user_id?: string;
400
+ limit?: number;
401
+ offset?: number;
402
+ }): Promise<{ threads: ThreadRegistryEntry[]; total: number }>;
403
+ deleteThread(id: string): Promise<boolean>;
404
+ updateThreadAgent(id: string, agent_name: string): Promise<boolean>;
405
+ updateThread(id: string, params: UpdateThreadParams): Promise<ThreadRegistryEntry | null>;
406
+ getThreadStub(threadId: string): DurableObjectStub;
407
+
408
+ // Provider methods
409
+ getProvider(name: string): Promise<Provider | null>;
410
+ setProvider(provider: Provider): Promise<void>;
411
+ listProviders(): Promise<Provider[]>;
412
+ deleteProvider(name: string): Promise<boolean>;
413
+
414
+ // User methods
415
+ getUserByUsername(username: string): Promise<User | null>;
416
+ getUserById(id: string): Promise<User | null>;
417
+ createUser(params: {
418
+ username: string;
419
+ password_hash: string;
420
+ role?: 'admin';
421
+ }): Promise<User>;
422
+ hasUsers(): Promise<boolean>;
423
+ listUsers(): Promise<Array<{
424
+ id: string;
425
+ username: string;
426
+ role: 'admin';
427
+ created_at: number;
428
+ updated_at: number;
429
+ }>>;
430
+ updateUser(
431
+ id: string,
432
+ params: {
433
+ username?: string;
434
+ password_hash?: string;
435
+ role?: 'admin';
436
+ }
437
+ ): Promise<User | null>;
438
+ deleteUser(id: string): Promise<boolean>;
439
+
440
+ // Session methods
441
+ createSession(params: {
442
+ user_id: string;
443
+ token_hash: string;
444
+ expires_at: number;
445
+ }): Promise<string>;
446
+ validateSession(tokenHash: string): Promise<{ user_id: string; expires_at: number } | null>;
447
+ cleanupSessions(): Promise<number>;
448
+ deleteSession(tokenHash: string): Promise<void>;
449
+
450
+ // API key methods
451
+ createApiKey(params: {
452
+ name: string;
453
+ key_hash: string;
454
+ key_prefix: string;
455
+ last_five: string;
456
+ user_id: string;
457
+ }): Promise<string>;
458
+ validateApiKey(keyHash: string): Promise<{ user_id: string; id: string } | null>;
459
+ listApiKeys(userId: string): Promise<Array<{
460
+ id: string;
461
+ name: string;
462
+ key_prefix: string;
463
+ last_five: string;
464
+ created_at: number;
465
+ last_used_at: number | null;
466
+ }>>;
467
+ deleteApiKey(id: string, userId: string): Promise<boolean>;
468
+
469
+ // OAuth methods
470
+ linkOAuthAccount(params: {
471
+ user_id: string;
472
+ provider: 'github' | 'google';
473
+ provider_user_id: string;
474
+ provider_username?: string;
475
+ }): Promise<void>;
476
+ findUserByOAuth(
477
+ provider: 'github' | 'google',
478
+ providerUserId: string
479
+ ): Promise<User | null>;
480
+
481
+ // Edit lock methods (for GitHub integration)
482
+ acquireEditLock(params: {
483
+ locked_by: string;
484
+ lock_reason: string;
485
+ }): Promise<boolean>;
486
+ releaseEditLock(lockedBy: string): Promise<boolean>;
487
+ getEditLockState(): Promise<{
488
+ locked: boolean;
489
+ locked_by: string | null;
490
+ locked_at: number | null;
491
+ lock_reason: string | null;
492
+ pending_changes: string | null;
493
+ }>;
494
+ setPendingChanges(changes: string): Promise<void>;
495
+
496
+ // WebSocket/fetch handler
497
+ fetch(request: Request): Promise<Response>;
498
+ }
499
+
500
+ // ============================================================
501
+ // Router Function
502
+ // ============================================================
503
+
504
+ /**
505
+ * Main router function for handling incoming requests.
506
+ * Routes requests to the appropriate API endpoint or serves the UI.
507
+ *
508
+ * @example
509
+ * \`\`\`typescript
510
+ * import { router } from 'virtual:@standardagents/builder'
511
+ *
512
+ * export default {
513
+ * async fetch(request: Request, env: Env) {
514
+ * const response = await router(request, env);
515
+ * return response ?? new Response('Not Found', { status: 404 });
516
+ * }
517
+ * }
518
+ * \`\`\`
519
+ */
520
+ export function router<Env extends ThreadEnv = ThreadEnv>(
521
+ request: Request,
522
+ env: Env
523
+ ): Promise<Response | null>;
524
+ }
525
+
526
+ /**
527
+ * Legacy alias for the router module.
528
+ * @deprecated Use \`import { router } from 'virtual:@standardagents/builder'\` instead.
529
+ */
530
+ declare module 'virtual:@standardagents/router' {
531
+ import type { ThreadEnv } from '@standardagents/builder/runtime';
532
+
533
+ export function router<Env extends ThreadEnv = ThreadEnv>(
534
+ request: Request,
535
+ env: Env
536
+ ): Promise<Response | null>;
537
+
538
+ export { DurableThread } from 'virtual:@standardagents/builder';
539
+ }
540
+ `;
541
+ }
542
+ function ensureDir(dir) {
543
+ if (!fs2.existsSync(dir)) {
544
+ fs2.mkdirSync(dir, { recursive: true });
545
+ }
546
+ }
547
+ function generateTypes(config) {
548
+ ensureDir(config.outputDir);
549
+ const typesContent = generateTypesContent(config);
550
+ fs2.writeFileSync(path3.join(config.outputDir, "types.d.ts"), typesContent);
551
+ const virtualModuleContent = generateVirtualModuleContent();
552
+ fs2.writeFileSync(path3.join(config.outputDir, "virtual-module.d.ts"), virtualModuleContent);
553
+ fs2.writeFileSync(path3.join(config.outputDir, "tsconfig.json"), TSCONFIG_CONTENT);
554
+ fs2.writeFileSync(path3.join(config.outputDir, ".gitignore"), "*\n");
555
+ }
556
+ function needsRegeneration(config) {
557
+ const typesPath = path3.join(config.outputDir, "types.d.ts");
558
+ if (!fs2.existsSync(typesPath)) {
559
+ return true;
560
+ }
561
+ const typesMtime = fs2.statSync(typesPath).mtime;
562
+ const dirs = [config.modelsDir, config.promptsDir, config.agentsDir, config.toolsDir];
563
+ for (const dir of dirs) {
564
+ if (!fs2.existsSync(dir)) {
565
+ continue;
566
+ }
567
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
568
+ for (const entry of entries) {
569
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
570
+ const filePath = path3.join(dir, entry.name);
571
+ const fileMtime = fs2.statSync(filePath).mtime;
572
+ if (fileMtime > typesMtime) {
573
+ return true;
574
+ }
575
+ }
576
+ }
577
+ }
578
+ return false;
579
+ }
580
+
581
+ // src/sdk/generators/generateModelFile.ts
582
+ function generateModelFile(data) {
583
+ const lines = [
584
+ `import { defineModel } from '@standardagents/builder';`,
585
+ "",
586
+ `export default defineModel({`,
587
+ ` name: '${escapeString(data.name)}',`,
588
+ ` provider: '${escapeString(data.provider)}',`,
589
+ ` model: '${escapeString(data.model)}',`
590
+ ];
591
+ if (data.includedProviders && data.includedProviders.length > 0) {
592
+ lines.push(` includedProviders: ${JSON.stringify(data.includedProviders)},`);
593
+ }
594
+ if (data.fallbacks && data.fallbacks.length > 0) {
595
+ lines.push(` fallbacks: ${JSON.stringify(data.fallbacks)},`);
596
+ }
597
+ if (data.inputPrice !== void 0) {
598
+ lines.push(` inputPrice: ${data.inputPrice},`);
599
+ }
600
+ if (data.outputPrice !== void 0) {
601
+ lines.push(` outputPrice: ${data.outputPrice},`);
602
+ }
603
+ if (data.cachedPrice !== void 0) {
604
+ lines.push(` cachedPrice: ${data.cachedPrice},`);
605
+ }
606
+ lines.push(`});`);
607
+ lines.push("");
608
+ return lines.join("\n");
609
+ }
610
+ function escapeString(str) {
611
+ return str.replace(/'/g, "\\'").replace(/\\/g, "\\\\");
612
+ }
613
+
614
+ // src/sdk/generators/jsonSchemaToZod.ts
615
+ function jsonSchemaToZod(schema, indent = 0) {
616
+ if (!schema || Object.keys(schema).length === 0) {
617
+ return "z.any()";
618
+ }
619
+ if (schema.anyOf && schema.anyOf.length > 0) {
620
+ const types = schema.anyOf.map((s) => jsonSchemaToZod(s, indent));
621
+ if (types.length === 1) {
622
+ return types[0];
623
+ }
624
+ return `z.union([${types.join(", ")}])`;
625
+ }
626
+ if (schema.oneOf && schema.oneOf.length > 0) {
627
+ const types = schema.oneOf.map((s) => jsonSchemaToZod(s, indent));
628
+ if (types.length === 1) {
629
+ return types[0];
630
+ }
631
+ return `z.union([${types.join(", ")}])`;
632
+ }
633
+ if (schema.allOf && schema.allOf.length > 0) {
634
+ const types = schema.allOf.map((s) => jsonSchemaToZod(s, indent));
635
+ if (types.length === 1) {
636
+ return types[0];
637
+ }
638
+ return types.reduce((acc, t) => `${acc}.and(${t})`);
639
+ }
640
+ if (schema.const !== void 0) {
641
+ return `z.literal(${JSON.stringify(schema.const)})`;
642
+ }
643
+ if (schema.enum && schema.enum.length > 0) {
644
+ const values = schema.enum.map((v) => JSON.stringify(v));
645
+ if (values.length === 1) {
646
+ return `z.literal(${values[0]})`;
647
+ }
648
+ return `z.enum([${values.join(", ")}])`;
649
+ }
650
+ switch (schema.type) {
651
+ case "string":
652
+ return buildStringSchema(schema);
653
+ case "number":
654
+ case "integer":
655
+ return buildNumberSchema(schema);
656
+ case "boolean":
657
+ return addDescription("z.boolean()", schema.description);
658
+ case "null":
659
+ return addDescription("z.null()", schema.description);
660
+ case "array":
661
+ return buildArraySchema(schema, indent);
662
+ case "object":
663
+ return buildObjectSchema(schema, indent);
664
+ default:
665
+ return "z.any()";
666
+ }
667
+ }
668
+ function buildStringSchema(schema) {
669
+ let code = "z.string()";
670
+ if (schema.minLength !== void 0) {
671
+ code += `.min(${schema.minLength})`;
672
+ }
673
+ if (schema.maxLength !== void 0) {
674
+ code += `.max(${schema.maxLength})`;
675
+ }
676
+ if (schema.pattern) {
677
+ code += `.regex(/${escapeRegex(schema.pattern)}/)`;
678
+ }
679
+ if (schema.format) {
680
+ switch (schema.format) {
681
+ case "email":
682
+ code += ".email()";
683
+ break;
684
+ case "uri":
685
+ case "url":
686
+ code += ".url()";
687
+ break;
688
+ case "uuid":
689
+ code += ".uuid()";
690
+ break;
691
+ case "date-time":
692
+ code += ".datetime()";
693
+ break;
694
+ case "date":
695
+ code += ".date()";
696
+ break;
697
+ case "time":
698
+ code += ".time()";
699
+ break;
700
+ case "ipv4":
701
+ code += '.ip({ version: "v4" })';
702
+ break;
703
+ case "ipv6":
704
+ code += '.ip({ version: "v6" })';
705
+ break;
706
+ }
707
+ }
708
+ return addDescription(code, schema.description);
709
+ }
710
+ function buildNumberSchema(schema) {
711
+ let code = schema.type === "integer" ? "z.number().int()" : "z.number()";
712
+ if (schema.minimum !== void 0) {
713
+ code += `.min(${schema.minimum})`;
714
+ }
715
+ if (schema.maximum !== void 0) {
716
+ code += `.max(${schema.maximum})`;
717
+ }
718
+ return addDescription(code, schema.description);
719
+ }
720
+ function buildArraySchema(schema, indent) {
721
+ const itemsSchema = schema.items ? jsonSchemaToZod(schema.items, indent) : "z.any()";
722
+ let code = `z.array(${itemsSchema})`;
723
+ return addDescription(code, schema.description);
724
+ }
725
+ function buildObjectSchema(schema, indent) {
726
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
727
+ if (schema.additionalProperties === false) {
728
+ return addDescription("z.object({})", schema.description);
729
+ }
730
+ return addDescription("z.record(z.any())", schema.description);
731
+ }
732
+ const required = new Set(schema.required || []);
733
+ const spaces = " ".repeat(indent + 1);
734
+ const closingSpaces = " ".repeat(indent);
735
+ const props = Object.entries(schema.properties).map(([key, propSchema]) => {
736
+ let propCode = jsonSchemaToZod(propSchema, indent + 1);
737
+ if (propSchema.nullable) {
738
+ propCode += ".nullable()";
739
+ }
740
+ if (!required.has(key)) {
741
+ propCode += ".optional()";
742
+ }
743
+ if (propSchema.default !== void 0) {
744
+ propCode += `.default(${JSON.stringify(propSchema.default)})`;
745
+ }
746
+ return `${spaces}${safePropertyKey(key)}: ${propCode}`;
747
+ });
748
+ const propsStr = props.join(",\n");
749
+ let code = `z.object({
750
+ ${propsStr}
751
+ ${closingSpaces}})`;
752
+ if (schema.additionalProperties === false) {
753
+ code += ".strict()";
754
+ }
755
+ return addDescription(code, schema.description);
756
+ }
757
+ function addDescription(code, description) {
758
+ if (description) {
759
+ return `${code}.describe(${JSON.stringify(description)})`;
760
+ }
761
+ return code;
762
+ }
763
+ function escapeRegex(pattern) {
764
+ return pattern.replace(/\//g, "\\/");
765
+ }
766
+ function safePropertyKey(key) {
767
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)) {
768
+ return key;
769
+ }
770
+ return JSON.stringify(key);
771
+ }
772
+ function isEmptySchema(schema) {
773
+ if (!schema) return true;
774
+ if (Object.keys(schema).length === 0) return true;
775
+ if (schema.type === "object" && (!schema.properties || Object.keys(schema.properties).length === 0) && (!schema.required || schema.required.length === 0)) {
776
+ return true;
777
+ }
778
+ return false;
779
+ }
780
+
781
+ // src/sdk/generators/generatePromptFile.ts
782
+ function generatePromptFile(data) {
783
+ const hasSchema = data.requiredSchema && !isEmptySchema(data.requiredSchema);
784
+ const lines = [`import { definePrompt } from '@standardagents/builder';`];
785
+ if (hasSchema) {
786
+ lines.push(`import { z } from 'zod';`);
787
+ }
788
+ lines.push("");
789
+ lines.push(`export default definePrompt({`);
790
+ lines.push(` name: '${escapeString2(data.name)}',`);
791
+ if (data.toolDescription) {
792
+ lines.push(` toolDescription: '${escapeString2(data.toolDescription)}',`);
793
+ }
794
+ if (data.prompt !== void 0) {
795
+ const promptCode = formatPromptContent(data.prompt);
796
+ lines.push(` prompt: ${promptCode},`);
797
+ }
798
+ lines.push(` model: '${escapeString2(data.model)}',`);
799
+ if (data.includeChat !== void 0) {
800
+ lines.push(` includeChat: ${data.includeChat},`);
801
+ }
802
+ if (data.includePastTools !== void 0) {
803
+ lines.push(` includePastTools: ${data.includePastTools},`);
804
+ }
805
+ if (data.parallelToolCalls !== void 0) {
806
+ lines.push(` parallelToolCalls: ${data.parallelToolCalls},`);
807
+ }
808
+ if (data.toolChoice && data.toolChoice !== "auto") {
809
+ lines.push(` toolChoice: '${data.toolChoice}',`);
810
+ }
811
+ if (data.tools && data.tools.length > 0) {
812
+ const toolsCode = formatToolsArray(data.tools);
813
+ lines.push(` tools: ${toolsCode},`);
814
+ }
815
+ if (data.handoffAgents && data.handoffAgents.length > 0) {
816
+ const agentsStr = data.handoffAgents.map((a) => `'${escapeString2(a)}'`).join(", ");
817
+ lines.push(` handoffAgents: [${agentsStr}],`);
818
+ }
819
+ if (data.beforeTool) {
820
+ lines.push(` beforeTool: '${escapeString2(data.beforeTool)}',`);
821
+ }
822
+ if (data.afterTool) {
823
+ lines.push(` afterTool: '${escapeString2(data.afterTool)}',`);
824
+ }
825
+ if (data.reasoning && hasNonNullProperties(data.reasoning)) {
826
+ const reasoningCode = formatReasoningConfig(data.reasoning);
827
+ lines.push(` reasoning: ${reasoningCode},`);
828
+ }
829
+ if (hasSchema) {
830
+ const zodCode = jsonSchemaToZod(data.requiredSchema, 1);
831
+ lines.push(` requiredSchema: ${zodCode},`);
832
+ }
833
+ lines.push(`});`);
834
+ lines.push("");
835
+ return lines.join("\n");
836
+ }
837
+ function hasNonNullProperties(obj) {
838
+ return Object.values(obj).some((v) => v !== null && v !== void 0);
839
+ }
840
+ function formatToolsArray(tools) {
841
+ const formatted = tools.map((tool) => {
842
+ if (typeof tool === "string") {
843
+ return `'${escapeString2(tool)}'`;
844
+ }
845
+ return formatToolConfig(tool);
846
+ });
847
+ if (formatted.length === 1) {
848
+ return `[${formatted[0]}]`;
849
+ }
850
+ const indented = formatted.map((t) => ` ${t}`).join(",\n");
851
+ return `[
852
+ ${indented}
853
+ ]`;
854
+ }
855
+ function formatToolConfig(config) {
856
+ const parts = [];
857
+ parts.push(`name: '${escapeString2(config.name)}'`);
858
+ if (config.include_text_response !== void 0 && config.include_text_response !== true) {
859
+ parts.push(`includeTextResponse: ${config.include_text_response}`);
860
+ }
861
+ if (config.include_tool_calls !== void 0 && config.include_tool_calls !== true) {
862
+ parts.push(`includeToolCalls: ${config.include_tool_calls}`);
863
+ }
864
+ if (config.include_errors !== void 0 && config.include_errors !== true) {
865
+ parts.push(`includeErrors: ${config.include_errors}`);
866
+ }
867
+ if (config.init_user_message_property !== void 0 && config.init_user_message_property !== null) {
868
+ parts.push(`initUserMessageProperty: '${escapeString2(config.init_user_message_property)}'`);
869
+ }
870
+ return `{ ${parts.join(", ")} }`;
871
+ }
872
+ function formatReasoningConfig(reasoning) {
873
+ if (!reasoning) return "{}";
874
+ const parts = [];
875
+ if (reasoning.effort !== void 0 && reasoning.effort !== null) {
876
+ parts.push(`effort: '${reasoning.effort}'`);
877
+ }
878
+ if (reasoning.maxTokens !== void 0 && reasoning.maxTokens !== null) {
879
+ parts.push(`maxTokens: ${reasoning.maxTokens}`);
880
+ }
881
+ if (reasoning.exclude !== void 0 && reasoning.exclude !== null) {
882
+ parts.push(`exclude: ${reasoning.exclude}`);
883
+ }
884
+ if (reasoning.include !== void 0 && reasoning.include !== null) {
885
+ parts.push(`include: ${reasoning.include}`);
886
+ }
887
+ if (parts.length === 0) {
888
+ return "{}";
889
+ }
890
+ return `{ ${parts.join(", ")} }`;
891
+ }
892
+ function escapeString2(str) {
893
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
894
+ }
895
+ function escapeTemplateLiteral(str) {
896
+ return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\${/g, "\\${");
897
+ }
898
+ function parsePromptContent(prompt) {
899
+ if (!prompt) return null;
900
+ if (Array.isArray(prompt)) {
901
+ return prompt;
902
+ }
903
+ if (typeof prompt === "string") {
904
+ try {
905
+ const parsed = JSON.parse(prompt);
906
+ if (Array.isArray(parsed)) {
907
+ return parsed;
908
+ }
909
+ } catch {
910
+ }
911
+ }
912
+ return null;
913
+ }
914
+ function formatPromptContent(prompt) {
915
+ if (!prompt) return "''";
916
+ const parts = parsePromptContent(prompt);
917
+ if (parts !== null) {
918
+ return formatStructuredPrompt(parts);
919
+ }
920
+ if (typeof prompt === "string") {
921
+ if (prompt.includes("\n")) {
922
+ return `\`${escapeTemplateLiteral(prompt)}\``;
923
+ }
924
+ return `'${escapeString2(prompt)}'`;
925
+ }
926
+ return "''";
927
+ }
928
+ function formatStructuredPrompt(parts) {
929
+ if (parts.length === 0) {
930
+ return "[]";
931
+ }
932
+ const normalizedParts = parts.map(normalizePart);
933
+ if (normalizedParts.length === 1 && normalizedParts[0].type === "text") {
934
+ const content = normalizedParts[0].content || "";
935
+ if (!content.includes("\n") && content.length < 50) {
936
+ return `[{ type: 'text', content: '${escapeString2(content)}' }]`;
937
+ }
938
+ }
939
+ const formattedParts = normalizedParts.map((part) => {
940
+ if (part.type === "text") {
941
+ const content = part.content || "";
942
+ if (content.includes("\n")) {
943
+ return ` { type: 'text', content: \`${escapeTemplateLiteral(content)}\` }`;
944
+ }
945
+ return ` { type: 'text', content: '${escapeString2(content)}' }`;
946
+ } else if (part.type === "include") {
947
+ const promptName = part.prompt || "";
948
+ return ` { type: 'include', prompt: '${escapeString2(promptName)}' }`;
949
+ }
950
+ return ` // Unknown part type: ${part.type}`;
951
+ });
952
+ return `[
953
+ ${formattedParts.join(",\n")},
954
+ ]`;
955
+ }
956
+ function normalizePart(part) {
957
+ if (part.type === "text") {
958
+ return { type: "text", content: part.content || "" };
959
+ }
960
+ if (part.type === "include") {
961
+ return { type: "include", prompt: part.prompt || "" };
962
+ }
963
+ if (part.type === "string") {
964
+ return { type: "text", content: part.value || "" };
965
+ }
966
+ if (part.type === "prompt") {
967
+ return { type: "include", prompt: part.id || "" };
968
+ }
969
+ if (part.type === "variable") {
970
+ return { type: "text", content: `{{${part.value || ""}}}` };
971
+ }
972
+ return { type: "text", content: "" };
973
+ }
974
+
975
+ // src/sdk/generators/generateAgentFile.ts
976
+ function generateAgentFile(data) {
977
+ const lines = [
978
+ `import { defineAgent } from '@standardagents/builder';`,
979
+ "",
980
+ `export default defineAgent({`,
981
+ ` name: '${escapeString3(data.name)}',`
982
+ ];
983
+ if (data.title) {
984
+ lines.push(` title: '${escapeString3(data.title)}',`);
985
+ }
986
+ if (data.type && data.type !== "ai_human") {
987
+ lines.push(` type: '${data.type}',`);
988
+ }
989
+ if (data.maxSessionTurns !== void 0) {
990
+ lines.push(` maxSessionTurns: ${data.maxSessionTurns},`);
991
+ }
992
+ lines.push(` sideA: ${formatSideConfig(data.sideA)},`);
993
+ if (data.sideB) {
994
+ lines.push(` sideB: ${formatSideConfig(data.sideB)},`);
995
+ }
996
+ if (data.exposeAsTool !== void 0) {
997
+ lines.push(` exposeAsTool: ${data.exposeAsTool},`);
998
+ }
999
+ if (data.toolDescription) {
1000
+ lines.push(` toolDescription: '${escapeString3(data.toolDescription)}',`);
1001
+ }
1002
+ if (data.tags && data.tags.length > 0) {
1003
+ lines.push(` tags: ${JSON.stringify(data.tags)},`);
1004
+ }
1005
+ lines.push(`});`);
1006
+ lines.push("");
1007
+ return lines.join("\n");
1008
+ }
1009
+ function formatSideConfig(config) {
1010
+ const parts = ["{"];
1011
+ if (config.label) {
1012
+ parts.push(` label: '${escapeString3(config.label)}',`);
1013
+ }
1014
+ parts.push(` prompt: '${escapeString3(config.prompt)}',`);
1015
+ if (config.stopOnResponse !== void 0) {
1016
+ parts.push(` stopOnResponse: ${config.stopOnResponse},`);
1017
+ }
1018
+ if (config.stopTool) {
1019
+ parts.push(` stopTool: '${escapeString3(config.stopTool)}',`);
1020
+ }
1021
+ if (config.stopToolResponseProperty) {
1022
+ parts.push(` stopToolResponseProperty: '${escapeString3(config.stopToolResponseProperty)}',`);
1023
+ }
1024
+ if (config.maxTurns !== void 0) {
1025
+ parts.push(` maxTurns: ${config.maxTurns},`);
1026
+ }
1027
+ if (config.endConversationTool) {
1028
+ parts.push(` endConversationTool: '${escapeString3(config.endConversationTool)}',`);
1029
+ }
1030
+ if (config.manualStopCondition !== void 0) {
1031
+ parts.push(` manualStopCondition: ${config.manualStopCondition},`);
1032
+ }
1033
+ parts.push(" }");
1034
+ return parts.join("\n");
1035
+ }
1036
+ function escapeString3(str) {
1037
+ return str.replace(/'/g, "\\'").replace(/\\/g, "\\\\");
1038
+ }
1039
+
1040
+ // src/sdk/persistence/index.ts
1041
+ function nameToFilename(name) {
1042
+ return name.replace(/[/\\]/g, "__").replace(/[:*?"<>|]/g, "_").replace(/-/g, "_");
1043
+ }
1044
+ function getModelFilePath(modelsDir, name) {
1045
+ const filename = nameToFilename(name);
1046
+ return path3.join(modelsDir, `${filename}.ts`);
1047
+ }
1048
+ function modelExists(modelsDir, name) {
1049
+ const filePath = getModelFilePath(modelsDir, name);
1050
+ return fs2.existsSync(filePath);
1051
+ }
1052
+ async function saveModel(modelsDir, data, overwrite = false) {
1053
+ try {
1054
+ if (!fs2.existsSync(modelsDir)) {
1055
+ fs2.mkdirSync(modelsDir, { recursive: true });
1056
+ }
1057
+ const filePath = getModelFilePath(modelsDir, data.name);
1058
+ if (!overwrite && fs2.existsSync(filePath)) {
1059
+ return {
1060
+ success: false,
1061
+ error: `Model file already exists: ${filePath}. Use update to modify existing models.`
1062
+ };
1063
+ }
1064
+ const content = generateModelFile(data);
1065
+ await fs2.promises.writeFile(filePath, content, "utf-8");
1066
+ return {
1067
+ success: true,
1068
+ filePath
1069
+ };
1070
+ } catch (error) {
1071
+ return {
1072
+ success: false,
1073
+ error: error.message || "Failed to save model"
1074
+ };
1075
+ }
1076
+ }
1077
+ async function deleteModel(modelsDir, name) {
1078
+ try {
1079
+ const filePath = getModelFilePath(modelsDir, name);
1080
+ if (!fs2.existsSync(filePath)) {
1081
+ return {
1082
+ success: false,
1083
+ error: `Model file not found: ${filePath}`
1084
+ };
1085
+ }
1086
+ await fs2.promises.unlink(filePath);
1087
+ return {
1088
+ success: true,
1089
+ filePath
1090
+ };
1091
+ } catch (error) {
1092
+ return {
1093
+ success: false,
1094
+ error: error.message || "Failed to delete model"
1095
+ };
1096
+ }
1097
+ }
1098
+ function transformModelData(data) {
1099
+ const transformed = {};
1100
+ const isOpenRouter = data.provider === "openrouter";
1101
+ const fieldMappings = {
1102
+ included_providers: "includedProviders",
1103
+ input_price: "inputPrice",
1104
+ output_price: "outputPrice",
1105
+ cached_price: "cachedPrice"
1106
+ };
1107
+ const openRouterSkipFields = ["inputPrice", "outputPrice", "cachedPrice", "input_price", "output_price", "cached_price"];
1108
+ for (const [key, value] of Object.entries(data)) {
1109
+ if (key === "id") continue;
1110
+ if (isOpenRouter && openRouterSkipFields.includes(key)) continue;
1111
+ if (fieldMappings[key]) {
1112
+ const transformedKey = fieldMappings[key];
1113
+ if (isOpenRouter && openRouterSkipFields.includes(transformedKey)) continue;
1114
+ transformed[transformedKey] = value;
1115
+ } else {
1116
+ transformed[key] = value;
1117
+ }
1118
+ }
1119
+ return transformed;
1120
+ }
1121
+ function validateModelData(data) {
1122
+ if (!data.name || typeof data.name !== "string") {
1123
+ return "Model name is required and must be a string";
1124
+ }
1125
+ if (!data.provider || typeof data.provider !== "string") {
1126
+ return "Model provider is required and must be a string";
1127
+ }
1128
+ const validProviders = ["openai", "openrouter", "anthropic", "google"];
1129
+ if (!validProviders.includes(data.provider)) {
1130
+ return `Invalid provider '${data.provider}'. Must be one of: ${validProviders.join(", ")}`;
1131
+ }
1132
+ if (!data.model || typeof data.model !== "string") {
1133
+ return "Model ID is required and must be a string";
1134
+ }
1135
+ if (data.inputPrice !== void 0 && typeof data.inputPrice !== "number") {
1136
+ return "inputPrice must be a number";
1137
+ }
1138
+ if (data.outputPrice !== void 0 && typeof data.outputPrice !== "number") {
1139
+ return "outputPrice must be a number";
1140
+ }
1141
+ if (data.cachedPrice !== void 0 && typeof data.cachedPrice !== "number") {
1142
+ return "cachedPrice must be a number";
1143
+ }
1144
+ if (data.fallbacks !== void 0) {
1145
+ if (!Array.isArray(data.fallbacks)) {
1146
+ return "fallbacks must be an array";
1147
+ }
1148
+ for (const fallback of data.fallbacks) {
1149
+ if (typeof fallback !== "string") {
1150
+ return "Each fallback must be a string (model name)";
1151
+ }
1152
+ }
1153
+ }
1154
+ if (data.includedProviders !== void 0) {
1155
+ if (!Array.isArray(data.includedProviders)) {
1156
+ return "includedProviders must be an array";
1157
+ }
1158
+ for (const provider of data.includedProviders) {
1159
+ if (typeof provider !== "string") {
1160
+ return "Each includedProvider must be a string";
1161
+ }
1162
+ }
1163
+ }
1164
+ return null;
1165
+ }
1166
+ function transformPromptData(data) {
1167
+ const transformed = {};
1168
+ const fieldMappings = {
1169
+ model_id: "model",
1170
+ tool_description: "toolDescription",
1171
+ expose_as_tool: "exposeAsTool",
1172
+ required_schema: "requiredSchema",
1173
+ include_chat: "includeChat",
1174
+ include_past_tools: "includePastTools",
1175
+ before_tool: "beforeTool",
1176
+ after_tool: "afterTool",
1177
+ parallel_tool_calls: "parallelToolCalls",
1178
+ tool_choice: "toolChoice",
1179
+ handoff_agents: "handoffAgents",
1180
+ reasoning_effort: "reasoningEffort",
1181
+ reasoning_max_tokens: "reasoningMaxTokens",
1182
+ reasoning_exclude: "reasoningExclude",
1183
+ include_reasoning: "includeReasoning"
1184
+ };
1185
+ for (const [key, value] of Object.entries(data)) {
1186
+ if (key === "id") continue;
1187
+ if (fieldMappings[key]) {
1188
+ transformed[fieldMappings[key]] = value;
1189
+ } else {
1190
+ transformed[key] = value;
1191
+ }
1192
+ }
1193
+ if (transformed.reasoningEffort !== void 0 || transformed.reasoningMaxTokens !== void 0 || transformed.reasoningExclude !== void 0 || transformed.includeReasoning !== void 0) {
1194
+ transformed.reasoning = {};
1195
+ if (transformed.reasoningEffort) {
1196
+ transformed.reasoning.effort = transformed.reasoningEffort;
1197
+ }
1198
+ if (transformed.reasoningMaxTokens) {
1199
+ transformed.reasoning.maxTokens = transformed.reasoningMaxTokens;
1200
+ }
1201
+ if (transformed.reasoningExclude !== void 0) {
1202
+ transformed.reasoning.exclude = transformed.reasoningExclude;
1203
+ }
1204
+ if (transformed.includeReasoning !== void 0) {
1205
+ transformed.reasoning.include = transformed.includeReasoning;
1206
+ }
1207
+ delete transformed.reasoningEffort;
1208
+ delete transformed.reasoningMaxTokens;
1209
+ delete transformed.reasoningExclude;
1210
+ delete transformed.includeReasoning;
1211
+ }
1212
+ return transformed;
1213
+ }
1214
+ function getPromptFilePath(promptsDir, name) {
1215
+ const filename = nameToFilename(name);
1216
+ return path3.join(promptsDir, `${filename}.ts`);
1217
+ }
1218
+ function promptExists(promptsDir, name) {
1219
+ const filePath = getPromptFilePath(promptsDir, name);
1220
+ return fs2.existsSync(filePath);
1221
+ }
1222
+ async function savePrompt(promptsDir, data, overwrite = false) {
1223
+ try {
1224
+ if (!fs2.existsSync(promptsDir)) {
1225
+ fs2.mkdirSync(promptsDir, { recursive: true });
1226
+ }
1227
+ const filePath = getPromptFilePath(promptsDir, data.name);
1228
+ if (!overwrite && fs2.existsSync(filePath)) {
1229
+ return {
1230
+ success: false,
1231
+ error: `Prompt file already exists: ${filePath}. Use update to modify existing prompts.`
1232
+ };
1233
+ }
1234
+ const content = generatePromptFile(data);
1235
+ await fs2.promises.writeFile(filePath, content, "utf-8");
1236
+ return {
1237
+ success: true,
1238
+ filePath
1239
+ };
1240
+ } catch (error) {
1241
+ return {
1242
+ success: false,
1243
+ error: error.message || "Failed to save prompt"
1244
+ };
1245
+ }
1246
+ }
1247
+ async function deletePrompt(promptsDir, name) {
1248
+ try {
1249
+ const filePath = getPromptFilePath(promptsDir, name);
1250
+ if (!fs2.existsSync(filePath)) {
1251
+ return {
1252
+ success: false,
1253
+ error: `Prompt file not found: ${filePath}`
1254
+ };
1255
+ }
1256
+ await fs2.promises.unlink(filePath);
1257
+ return {
1258
+ success: true,
1259
+ filePath
1260
+ };
1261
+ } catch (error) {
1262
+ return {
1263
+ success: false,
1264
+ error: error.message || "Failed to delete prompt"
1265
+ };
1266
+ }
1267
+ }
1268
+ async function renamePrompt(promptsDir, oldName, newName) {
1269
+ try {
1270
+ const oldFilePath = getPromptFilePath(promptsDir, oldName);
1271
+ const newFilePath = getPromptFilePath(promptsDir, newName);
1272
+ if (!fs2.existsSync(oldFilePath)) {
1273
+ return {
1274
+ success: false,
1275
+ error: `Prompt file not found: ${oldFilePath}`
1276
+ };
1277
+ }
1278
+ if (fs2.existsSync(newFilePath)) {
1279
+ return {
1280
+ success: false,
1281
+ error: `Prompt file already exists: ${newFilePath}`
1282
+ };
1283
+ }
1284
+ const content = await fs2.promises.readFile(oldFilePath, "utf-8");
1285
+ const updatedContent = content.replace(
1286
+ /name:\s*['"]([^'"]+)['"]/,
1287
+ `name: '${newName}'`
1288
+ );
1289
+ await fs2.promises.writeFile(newFilePath, updatedContent, "utf-8");
1290
+ await fs2.promises.unlink(oldFilePath);
1291
+ return {
1292
+ success: true,
1293
+ filePath: newFilePath
1294
+ };
1295
+ } catch (error) {
1296
+ return {
1297
+ success: false,
1298
+ error: error.message || "Failed to rename prompt"
1299
+ };
1300
+ }
1301
+ }
1302
+ function validatePromptData(data) {
1303
+ if (!data.name || typeof data.name !== "string") {
1304
+ return "Prompt name is required and must be a string";
1305
+ }
1306
+ if (!data.model || typeof data.model !== "string") {
1307
+ return "Prompt model is required and must be a string";
1308
+ }
1309
+ if (data.toolDescription !== void 0 && typeof data.toolDescription !== "string") {
1310
+ return "toolDescription must be a string";
1311
+ }
1312
+ if (data.prompt !== void 0 && typeof data.prompt !== "string") {
1313
+ return "prompt must be a string";
1314
+ }
1315
+ const booleanFields = ["includeChat", "includePastTools", "parallelToolCalls"];
1316
+ for (const field of booleanFields) {
1317
+ if (data[field] !== void 0 && typeof data[field] !== "boolean") {
1318
+ return `${field} must be a boolean`;
1319
+ }
1320
+ }
1321
+ if (data.toolChoice !== void 0) {
1322
+ const validChoices = ["auto", "none", "required"];
1323
+ if (!validChoices.includes(data.toolChoice)) {
1324
+ return `Invalid toolChoice '${data.toolChoice}'. Must be one of: ${validChoices.join(", ")}`;
1325
+ }
1326
+ }
1327
+ if (data.tools !== void 0 && !Array.isArray(data.tools)) {
1328
+ return "tools must be an array";
1329
+ }
1330
+ if (data.handoffAgents !== void 0 && !Array.isArray(data.handoffAgents)) {
1331
+ return "handoffAgents must be an array";
1332
+ }
1333
+ if (data.reasoning !== void 0) {
1334
+ if (typeof data.reasoning !== "object") {
1335
+ return "reasoning must be an object";
1336
+ }
1337
+ if (data.reasoning.effort !== void 0) {
1338
+ const validEfforts = ["low", "medium", "high"];
1339
+ if (!validEfforts.includes(data.reasoning.effort)) {
1340
+ return `Invalid reasoning.effort '${data.reasoning.effort}'. Must be one of: ${validEfforts.join(", ")}`;
1341
+ }
1342
+ }
1343
+ if (data.reasoning.maxTokens !== void 0 && typeof data.reasoning.maxTokens !== "number") {
1344
+ return "reasoning.maxTokens must be a number";
1345
+ }
1346
+ }
1347
+ return null;
1348
+ }
1349
+ function transformAgentData(data) {
1350
+ const transformed = {
1351
+ name: data.name
1352
+ };
1353
+ if (data.title) {
1354
+ transformed.title = data.title;
1355
+ }
1356
+ if (data.type) {
1357
+ transformed.type = data.type;
1358
+ }
1359
+ if (data.max_session_turns !== void 0) {
1360
+ transformed.maxSessionTurns = data.max_session_turns;
1361
+ }
1362
+ transformed.sideA = {
1363
+ prompt: data.side_a_agent_prompt
1364
+ };
1365
+ if (data.side_a_label) {
1366
+ transformed.sideA.label = data.side_a_label;
1367
+ }
1368
+ if (data.side_a_stop_on_response !== void 0) {
1369
+ transformed.sideA.stopOnResponse = data.side_a_stop_on_response;
1370
+ }
1371
+ if (data.side_a_stop_tool) {
1372
+ transformed.sideA.stopTool = data.side_a_stop_tool;
1373
+ }
1374
+ if (data.side_a_stop_tool_response_property) {
1375
+ transformed.sideA.stopToolResponseProperty = data.side_a_stop_tool_response_property;
1376
+ }
1377
+ if (data.side_a_max_turns !== void 0) {
1378
+ transformed.sideA.maxTurns = data.side_a_max_turns;
1379
+ }
1380
+ if (data.side_a_end_conversation_tool) {
1381
+ transformed.sideA.endConversationTool = data.side_a_end_conversation_tool;
1382
+ }
1383
+ if (data.side_a_manual_stop_condition !== void 0) {
1384
+ transformed.sideA.manualStopCondition = data.side_a_manual_stop_condition;
1385
+ }
1386
+ if (data.side_b_agent_prompt) {
1387
+ transformed.sideB = {
1388
+ prompt: data.side_b_agent_prompt
1389
+ };
1390
+ if (data.side_b_label) {
1391
+ transformed.sideB.label = data.side_b_label;
1392
+ }
1393
+ if (data.side_b_stop_on_response !== void 0) {
1394
+ transformed.sideB.stopOnResponse = data.side_b_stop_on_response;
1395
+ }
1396
+ if (data.side_b_stop_tool) {
1397
+ transformed.sideB.stopTool = data.side_b_stop_tool;
1398
+ }
1399
+ if (data.side_b_stop_tool_response_property) {
1400
+ transformed.sideB.stopToolResponseProperty = data.side_b_stop_tool_response_property;
1401
+ }
1402
+ if (data.side_b_max_turns !== void 0) {
1403
+ transformed.sideB.maxTurns = data.side_b_max_turns;
1404
+ }
1405
+ if (data.side_b_end_conversation_tool) {
1406
+ transformed.sideB.endConversationTool = data.side_b_end_conversation_tool;
1407
+ }
1408
+ if (data.side_b_manual_stop_condition !== void 0) {
1409
+ transformed.sideB.manualStopCondition = data.side_b_manual_stop_condition;
1410
+ }
1411
+ }
1412
+ if (data.expose_as_tool !== void 0) {
1413
+ transformed.exposeAsTool = data.expose_as_tool;
1414
+ }
1415
+ if (data.tool_description) {
1416
+ transformed.toolDescription = data.tool_description;
1417
+ }
1418
+ if (data.tags) {
1419
+ transformed.tags = data.tags;
1420
+ }
1421
+ return transformed;
1422
+ }
1423
+ function getAgentFilePath(agentsDir, name) {
1424
+ const filename = nameToFilename(name);
1425
+ return path3.join(agentsDir, `${filename}.ts`);
1426
+ }
1427
+ function agentExists(agentsDir, name) {
1428
+ const filePath = getAgentFilePath(agentsDir, name);
1429
+ return fs2.existsSync(filePath);
1430
+ }
1431
+ async function saveAgent(agentsDir, data, overwrite = false) {
1432
+ try {
1433
+ if (!fs2.existsSync(agentsDir)) {
1434
+ fs2.mkdirSync(agentsDir, { recursive: true });
1435
+ }
1436
+ const filePath = getAgentFilePath(agentsDir, data.name);
1437
+ if (!overwrite && fs2.existsSync(filePath)) {
1438
+ return {
1439
+ success: false,
1440
+ error: `Agent file already exists: ${filePath}. Use update to modify existing agents.`
1441
+ };
1442
+ }
1443
+ const content = generateAgentFile(data);
1444
+ await fs2.promises.writeFile(filePath, content, "utf-8");
1445
+ return {
1446
+ success: true,
1447
+ filePath
1448
+ };
1449
+ } catch (error) {
1450
+ return {
1451
+ success: false,
1452
+ error: error.message || "Failed to save agent"
1453
+ };
1454
+ }
1455
+ }
1456
+ async function deleteAgent(agentsDir, name) {
1457
+ try {
1458
+ const filePath = getAgentFilePath(agentsDir, name);
1459
+ if (!fs2.existsSync(filePath)) {
1460
+ return {
1461
+ success: false,
1462
+ error: `Agent file not found: ${filePath}`
1463
+ };
1464
+ }
1465
+ await fs2.promises.unlink(filePath);
1466
+ return {
1467
+ success: true,
1468
+ filePath
1469
+ };
1470
+ } catch (error) {
1471
+ return {
1472
+ success: false,
1473
+ error: error.message || "Failed to delete agent"
1474
+ };
1475
+ }
1476
+ }
1477
+ async function renameModel(modelsDir, oldName, newName) {
1478
+ try {
1479
+ const oldFilePath = getModelFilePath(modelsDir, oldName);
1480
+ const newFilePath = getModelFilePath(modelsDir, newName);
1481
+ if (!fs2.existsSync(oldFilePath)) {
1482
+ return {
1483
+ success: false,
1484
+ error: `Model file not found: ${oldFilePath}`
1485
+ };
1486
+ }
1487
+ if (fs2.existsSync(newFilePath)) {
1488
+ return {
1489
+ success: false,
1490
+ error: `Model file already exists: ${newFilePath}`
1491
+ };
1492
+ }
1493
+ const content = await fs2.promises.readFile(oldFilePath, "utf-8");
1494
+ const updatedContent = content.replace(
1495
+ /name:\s*['"]([^'"]+)['"]/,
1496
+ `name: '${newName}'`
1497
+ );
1498
+ await fs2.promises.writeFile(newFilePath, updatedContent, "utf-8");
1499
+ await fs2.promises.unlink(oldFilePath);
1500
+ return {
1501
+ success: true,
1502
+ filePath: newFilePath
1503
+ };
1504
+ } catch (error) {
1505
+ return {
1506
+ success: false,
1507
+ error: error.message || "Failed to rename model"
1508
+ };
1509
+ }
1510
+ }
1511
+ async function updateModelReferencesInPrompts(promptsDir, oldModelName, newModelName) {
1512
+ const updatedFiles = [];
1513
+ if (!fs2.existsSync(promptsDir)) {
1514
+ return updatedFiles;
1515
+ }
1516
+ const files = fs2.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1517
+ for (const file of files) {
1518
+ const filePath = path3.join(promptsDir, file);
1519
+ let content = await fs2.promises.readFile(filePath, "utf-8");
1520
+ const modelRegex = new RegExp(`model:\\s*['"]${escapeRegExp(oldModelName)}['"]`, "g");
1521
+ if (modelRegex.test(content)) {
1522
+ content = content.replace(modelRegex, `model: '${newModelName}'`);
1523
+ await fs2.promises.writeFile(filePath, content, "utf-8");
1524
+ updatedFiles.push(filePath);
1525
+ }
1526
+ }
1527
+ return updatedFiles;
1528
+ }
1529
+ async function updatePromptReferencesInPrompts(promptsDir, oldPromptName, newPromptName) {
1530
+ const updatedFiles = [];
1531
+ if (!fs2.existsSync(promptsDir)) {
1532
+ return updatedFiles;
1533
+ }
1534
+ const files = fs2.readdirSync(promptsDir).filter((f) => f.endsWith(".ts"));
1535
+ for (const file of files) {
1536
+ const filePath = path3.join(promptsDir, file);
1537
+ let content = await fs2.promises.readFile(filePath, "utf-8");
1538
+ let modified = false;
1539
+ const toolsArrayRegex = /tools:\s*\[([^\]]*)\]/gs;
1540
+ const newContent = content.replace(toolsArrayRegex, (match) => {
1541
+ const oldMatch = match;
1542
+ const promptRefRegex = new RegExp(`['"]${escapeRegExp(oldPromptName)}['"]`, "g");
1543
+ const replaced = match.replace(promptRefRegex, `'${newPromptName}'`);
1544
+ if (replaced !== oldMatch) {
1545
+ modified = true;
1546
+ }
1547
+ return replaced;
1548
+ });
1549
+ if (modified) {
1550
+ await fs2.promises.writeFile(filePath, newContent, "utf-8");
1551
+ updatedFiles.push(filePath);
1552
+ }
1553
+ }
1554
+ return updatedFiles;
1555
+ }
1556
+ async function updatePromptReferencesInAgents(agentsDir, oldPromptName, newPromptName) {
1557
+ const updatedFiles = [];
1558
+ if (!fs2.existsSync(agentsDir)) {
1559
+ return updatedFiles;
1560
+ }
1561
+ const files = fs2.readdirSync(agentsDir).filter((f) => f.endsWith(".ts"));
1562
+ for (const file of files) {
1563
+ const filePath = path3.join(agentsDir, file);
1564
+ let content = await fs2.promises.readFile(filePath, "utf-8");
1565
+ const promptRegex = new RegExp(`prompt:\\s*['"]${escapeRegExp(oldPromptName)}['"]`, "g");
1566
+ if (promptRegex.test(content)) {
1567
+ content = content.replace(promptRegex, `prompt: '${newPromptName}'`);
1568
+ await fs2.promises.writeFile(filePath, content, "utf-8");
1569
+ updatedFiles.push(filePath);
1570
+ }
1571
+ }
1572
+ return updatedFiles;
1573
+ }
1574
+ function escapeRegExp(string) {
1575
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1576
+ }
1577
+ function validateAgentData(data) {
1578
+ if (!data.name || typeof data.name !== "string") {
1579
+ return "Agent name is required and must be a string";
1580
+ }
1581
+ if (data.title !== void 0 && typeof data.title !== "string") {
1582
+ return "Agent title must be a string if provided";
1583
+ }
1584
+ if (data.type !== void 0) {
1585
+ const validTypes = ["ai_human", "dual_ai"];
1586
+ if (!validTypes.includes(data.type)) {
1587
+ return `Invalid type '${data.type}'. Must be one of: ${validTypes.join(", ")}`;
1588
+ }
1589
+ }
1590
+ if (!data.sideA || typeof data.sideA !== "object") {
1591
+ return "sideA configuration is required";
1592
+ }
1593
+ if (!data.sideA.prompt || typeof data.sideA.prompt !== "string") {
1594
+ return "sideA.prompt is required and must be a string";
1595
+ }
1596
+ if (data.sideA.label !== void 0 && typeof data.sideA.label !== "string") {
1597
+ return "sideA.label must be a string";
1598
+ }
1599
+ if (data.sideA.stopOnResponse !== void 0 && typeof data.sideA.stopOnResponse !== "boolean") {
1600
+ return "sideA.stopOnResponse must be a boolean";
1601
+ }
1602
+ if (data.sideA.stopTool !== void 0 && typeof data.sideA.stopTool !== "string") {
1603
+ return "sideA.stopTool must be a string";
1604
+ }
1605
+ if (data.sideA.stopTool && !data.sideA.stopToolResponseProperty) {
1606
+ return "sideA.stopToolResponseProperty is required when sideA.stopTool is set";
1607
+ }
1608
+ if (data.sideA.maxTurns !== void 0) {
1609
+ if (typeof data.sideA.maxTurns !== "number" || data.sideA.maxTurns <= 0) {
1610
+ return "sideA.maxTurns must be a positive number";
1611
+ }
1612
+ }
1613
+ if (data.type === "dual_ai") {
1614
+ if (!data.sideB || typeof data.sideB !== "object") {
1615
+ return "sideB configuration is required for dual_ai type";
1616
+ }
1617
+ if (!data.sideB.prompt || typeof data.sideB.prompt !== "string") {
1618
+ return "sideB.prompt is required for dual_ai type";
1619
+ }
1620
+ if (data.sideB.stopTool && !data.sideB.stopToolResponseProperty) {
1621
+ return "sideB.stopToolResponseProperty is required when sideB.stopTool is set";
1622
+ }
1623
+ if (data.sideB.maxTurns !== void 0) {
1624
+ if (typeof data.sideB.maxTurns !== "number" || data.sideB.maxTurns <= 0) {
1625
+ return "sideB.maxTurns must be a positive number";
1626
+ }
1627
+ }
1628
+ }
1629
+ if (data.exposeAsTool && !data.toolDescription) {
1630
+ return "toolDescription is required when exposeAsTool is true";
1631
+ }
1632
+ if (data.maxSessionTurns !== void 0) {
1633
+ if (typeof data.maxSessionTurns !== "number" || data.maxSessionTurns <= 0) {
1634
+ return "maxSessionTurns must be a positive number";
1635
+ }
1636
+ }
1637
+ if (data.tags !== void 0) {
1638
+ if (!Array.isArray(data.tags)) {
1639
+ return "tags must be an array";
1640
+ }
1641
+ for (const tag of data.tags) {
1642
+ if (typeof tag !== "string") {
1643
+ return "Each tag must be a string";
1644
+ }
1645
+ }
1646
+ }
1647
+ return null;
1648
+ }
1649
+
1650
+ // src/plugin.ts
1651
+ var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
1652
+ var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
1653
+ var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
1654
+ var RESOLVED_VIRTUAL_ROUTES_ID = "\0" + VIRTUAL_ROUTES_ID;
1655
+ var VIRTUAL_ROUTER_ID = "virtual:@standardagents/router";
1656
+ var RESOLVED_VIRTUAL_ROUTER_ID = "\0" + VIRTUAL_ROUTER_ID;
1657
+ var VIRTUAL_HOOKS_ID = "virtual:@standardagents-hooks";
1658
+ var RESOLVED_VIRTUAL_HOOKS_ID = "\0" + VIRTUAL_HOOKS_ID;
1659
+ var VIRTUAL_CONFIG_ID = "virtual:@standardagents-config";
1660
+ var RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
1661
+ var VIRTUAL_THREAD_API_ID = "virtual:@standardagents-thread-api";
1662
+ var RESOLVED_VIRTUAL_THREAD_API_ID = "\0" + VIRTUAL_THREAD_API_ID;
1663
+ var VIRTUAL_MODELS_ID = "virtual:@standardagents-models";
1664
+ var RESOLVED_VIRTUAL_MODELS_ID = "\0" + VIRTUAL_MODELS_ID;
1665
+ var VIRTUAL_PROMPTS_ID = "virtual:@standardagents-prompts";
1666
+ var RESOLVED_VIRTUAL_PROMPTS_ID = "\0" + VIRTUAL_PROMPTS_ID;
1667
+ var VIRTUAL_AGENTS_ID = "virtual:@standardagents-agents";
1668
+ var RESOLVED_VIRTUAL_AGENTS_ID = "\0" + VIRTUAL_AGENTS_ID;
1669
+ var VIRTUAL_BUILDER_ID = "virtual:@standardagents/builder";
1670
+ var RESOLVED_VIRTUAL_BUILDER_ID = "\0" + VIRTUAL_BUILDER_ID;
1671
+ function scanApiDirectory(dir, baseRoute = "") {
1672
+ const routes = [];
1673
+ if (!fs2.existsSync(dir)) {
1674
+ return routes;
1675
+ }
1676
+ try {
1677
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
1678
+ for (const entry of entries) {
1679
+ const fullPath = path3.join(dir, entry.name);
1680
+ if (entry.isDirectory()) {
1681
+ const convertedDirName = entry.name.replace(/\[([^\]]+)\]/g, ":$1");
1682
+ const subRoute = baseRoute + "/" + convertedDirName;
1683
+ routes.push(...scanApiDirectory(fullPath, subRoute));
1684
+ } else if (entry.isFile() && entry.name.endsWith(".ts")) {
1685
+ const fileName = entry.name.replace(/\.ts$/, "");
1686
+ let method = "GET";
1687
+ let routePath = baseRoute;
1688
+ if (fileName.includes(".")) {
1689
+ const parts = fileName.split(".");
1690
+ const methodPart = parts[parts.length - 1].toUpperCase();
1691
+ if (["GET", "POST", "PUT", "DELETE", "PATCH"].includes(methodPart)) {
1692
+ method = methodPart;
1693
+ const pathPart = parts.slice(0, -1).join(".");
1694
+ if (pathPart !== "index") {
1695
+ const convertedPath = pathPart.replace(/\[([^\]]+)\]/g, ":$1");
1696
+ routePath += "/" + convertedPath;
1697
+ }
1698
+ } else {
1699
+ if (fileName !== "index") {
1700
+ const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
1701
+ routePath += "/" + convertedPath;
1702
+ }
1703
+ }
1704
+ } else {
1705
+ if (fileName !== "index") {
1706
+ const convertedPath = fileName.replace(/\[([^\]]+)\]/g, ":$1");
1707
+ routePath += "/" + convertedPath;
1708
+ }
1709
+ }
1710
+ const importPath = "./" + path3.relative(process.cwd(), fullPath).replace(/\\/g, "/");
1711
+ routes.push({
1712
+ method,
1713
+ route: routePath || "/",
1714
+ importPath
1715
+ });
1716
+ }
1717
+ }
1718
+ return routes;
1719
+ } catch (error) {
1720
+ console.error(`Error scanning API directory ${dir}:`, error);
1721
+ return [];
1722
+ }
1723
+ }
1724
+ function isSnakeCase(str) {
1725
+ return /^[a-z][a-z0-9_]*[a-z0-9]$/.test(str) || /^[a-z]$/.test(str);
1726
+ }
1727
+ function validateToolFile(filePath, fileName) {
1728
+ try {
1729
+ const content = fs2.readFileSync(filePath, "utf-8");
1730
+ const hasDefaultExport = /export\s+default\s+defineTool/.test(content);
1731
+ if (!hasDefaultExport) {
1732
+ return `Tool file '${fileName}.ts' must have a default export using defineTool()`;
1733
+ }
1734
+ return null;
1735
+ } catch (error) {
1736
+ return `Failed to read tool file '${fileName}.ts'`;
1737
+ }
1738
+ }
1739
+ async function scanToolsDirectory(dir) {
1740
+ const tools = [];
1741
+ if (!fs2.existsSync(dir)) {
1742
+ return tools;
1743
+ }
1744
+ const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
1745
+ for (const entry of entries) {
1746
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
1747
+ const fileName = entry.name.replace(".ts", "");
1748
+ const filePath = path3.join(dir, entry.name);
1749
+ const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
1750
+ let toolError;
1751
+ const validationError = validateToolFile(filePath, fileName);
1752
+ if (validationError) {
1753
+ toolError = validationError;
1754
+ console.error(`
1755
+ \u274C Tool validation error: ${validationError}`);
1756
+ }
1757
+ if (!isSnakeCase(fileName)) {
1758
+ const warning = `Tool name should be in snake_case format (e.g., 'log_name', 'send_email')`;
1759
+ if (toolError) {
1760
+ toolError += ` | ${warning}`;
1761
+ } else {
1762
+ toolError = warning;
1763
+ }
1764
+ console.warn(
1765
+ `
1766
+ \u26A0\uFE0F Tool naming warning: '${fileName}' should be in snake_case format (e.g., 'log_name', 'send_email')`
1767
+ );
1768
+ }
1769
+ tools.push({ name: fileName, importPath, error: toolError });
1770
+ }
1771
+ }
1772
+ return tools;
1773
+ }
1774
+ async function scanHooksDirectory(dir) {
1775
+ const hooks = [];
1776
+ if (!fs2.existsSync(dir)) {
1777
+ return hooks;
1778
+ }
1779
+ const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
1780
+ for (const entry of entries) {
1781
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
1782
+ const fileName = entry.name.replace(".ts", "");
1783
+ const filePath = path3.join(dir, entry.name);
1784
+ const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
1785
+ hooks.push({ name: fileName, importPath });
1786
+ }
1787
+ }
1788
+ return hooks;
1789
+ }
1790
+ async function scanConfigDirectory(dir, definePattern) {
1791
+ const items = [];
1792
+ if (!fs2.existsSync(dir)) {
1793
+ return items;
1794
+ }
1795
+ const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
1796
+ for (const entry of entries) {
1797
+ if (entry.isFile() && entry.name.endsWith(".ts")) {
1798
+ const filePath = path3.join(dir, entry.name);
1799
+ const importPath = "./" + path3.relative(process.cwd(), filePath).replace(/\\/g, "/");
1800
+ try {
1801
+ const content = fs2.readFileSync(filePath, "utf-8");
1802
+ const hasDefaultExport = definePattern.test(content);
1803
+ if (!hasDefaultExport) {
1804
+ items.push({
1805
+ name: entry.name.replace(".ts", ""),
1806
+ importPath,
1807
+ error: `File must have a default export using the define function`
1808
+ });
1809
+ continue;
1810
+ }
1811
+ const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
1812
+ if (nameMatch) {
1813
+ items.push({ name: nameMatch[1], importPath });
1814
+ } else {
1815
+ items.push({
1816
+ name: entry.name.replace(".ts", ""),
1817
+ importPath,
1818
+ error: `Could not extract name from definition`
1819
+ });
1820
+ }
1821
+ } catch (error) {
1822
+ items.push({
1823
+ name: entry.name.replace(".ts", ""),
1824
+ importPath,
1825
+ error: `Failed to read file: ${error}`
1826
+ });
1827
+ }
1828
+ }
1829
+ }
1830
+ return items;
1831
+ }
1832
+ async function scanModelsDirectory(dir) {
1833
+ return scanConfigDirectory(dir, /export\s+default\s+defineModel/);
1834
+ }
1835
+ async function scanPromptsDirectory(dir) {
1836
+ return scanConfigDirectory(dir, /export\s+default\s+definePrompt/);
1837
+ }
1838
+ async function scanAgentsDirectory(dir) {
1839
+ return scanConfigDirectory(dir, /export\s+default\s+defineAgent/);
1840
+ }
1841
+ function parseRequestBody(req) {
1842
+ return new Promise((resolve, reject) => {
1843
+ let body = "";
1844
+ req.on("data", (chunk) => {
1845
+ body += chunk.toString();
1846
+ });
1847
+ req.on("end", () => {
1848
+ try {
1849
+ resolve(body ? JSON.parse(body) : {});
1850
+ } catch (e) {
1851
+ reject(new Error("Invalid JSON body"));
1852
+ }
1853
+ });
1854
+ req.on("error", reject);
1855
+ });
1856
+ }
1857
+ function agentbuilder(options = {}) {
1858
+ let mountPoint = options.mountPoint || "/agents";
1859
+ if (!mountPoint.startsWith("/")) {
1860
+ mountPoint = "/" + mountPoint;
1861
+ }
1862
+ if (mountPoint.endsWith("/") && mountPoint.length > 1) {
1863
+ mountPoint = mountPoint.slice(0, -1);
1864
+ }
1865
+ const toolsDir = options.toolsDir ? path3.resolve(process.cwd(), options.toolsDir) : path3.resolve(process.cwd(), "agents/tools");
1866
+ const hooksDir = options.hooksDir ? path3.resolve(process.cwd(), options.hooksDir) : path3.resolve(process.cwd(), "agents/hooks");
1867
+ const threadApiDir = options.apiDir ? path3.resolve(process.cwd(), options.apiDir) : path3.resolve(process.cwd(), "agents/api");
1868
+ const modelsDir = options.modelsDir ? path3.resolve(process.cwd(), options.modelsDir) : path3.resolve(process.cwd(), "agents/models");
1869
+ const promptsDir = options.promptsDir ? path3.resolve(process.cwd(), options.promptsDir) : path3.resolve(process.cwd(), "agents/prompts");
1870
+ const agentsDir = options.agentsDir ? path3.resolve(process.cwd(), options.agentsDir) : path3.resolve(process.cwd(), "agents/agents");
1871
+ const outputDir = path3.resolve(process.cwd(), ".agents");
1872
+ const typeGenConfig = {
1873
+ modelsDir,
1874
+ promptsDir,
1875
+ agentsDir,
1876
+ toolsDir,
1877
+ outputDir
1878
+ };
1879
+ function regenerateTypes() {
1880
+ if (needsRegeneration(typeGenConfig)) {
1881
+ console.log("[vite-plugin-agent] Regenerating types...");
1882
+ generateTypes(typeGenConfig);
1883
+ console.log("[vite-plugin-agent] Types regenerated at .agents/types.d.ts");
1884
+ }
1885
+ }
1886
+ const __filename = fileURLToPath(import.meta.url);
1887
+ const __dirname = path3.dirname(__filename);
1888
+ const rou3Path = path3.join(__dirname, "../dist/rou3.js");
1889
+ let rou3Code = "";
1890
+ try {
1891
+ rou3Code = fs2.readFileSync(rou3Path, "utf-8").replace(/^export \{[^}]+\};?\s*$/gm, "").replace(/\/\/# sourceMappingURL=.+$/gm, "").trim();
1892
+ } catch (err) {
1893
+ console.warn("[vite-plugin-agent] Could not read rou3.js for inlining:", err);
1894
+ }
1895
+ let routesVersion = Math.random().toString(36).substring(7);
1896
+ function getWorkerEnvironment(server) {
1897
+ const envNames = Object.keys(server.environments);
1898
+ const workerEnvName = envNames.find((name) => name !== "client");
1899
+ if (!workerEnvName) {
1900
+ console.warn("[vite-plugin-agent] Could not find worker environment");
1901
+ return null;
1902
+ }
1903
+ return server.environments[workerEnvName];
1904
+ }
1905
+ function reloadToolsModule(server) {
1906
+ const workerEnv = getWorkerEnvironment(server);
1907
+ if (!workerEnv) return false;
1908
+ const toolsModule = workerEnv.moduleGraph.getModuleById(
1909
+ RESOLVED_VIRTUAL_TOOLS_ID
1910
+ );
1911
+ if (toolsModule) {
1912
+ workerEnv.reloadModule(toolsModule);
1913
+ server.ws.send({
1914
+ type: "custom",
1915
+ event: "agent:tools-updated",
1916
+ data: { timestamp: Date.now() }
1917
+ });
1918
+ return true;
1919
+ }
1920
+ return false;
1921
+ }
1922
+ function reloadRoutesModule(server) {
1923
+ const workerEnv = getWorkerEnvironment(server);
1924
+ if (!workerEnv) return false;
1925
+ const routesModule = workerEnv.moduleGraph.getModuleById(
1926
+ RESOLVED_VIRTUAL_ROUTES_ID
1927
+ );
1928
+ if (routesModule) {
1929
+ routesVersion = Math.random().toString(36).substring(7);
1930
+ workerEnv.reloadModule(routesModule);
1931
+ server.ws.send({
1932
+ type: "custom",
1933
+ event: "agent:routes-updated",
1934
+ data: { timestamp: Date.now() }
1935
+ });
1936
+ return true;
1937
+ }
1938
+ return false;
1939
+ }
1940
+ function reloadHooksModule(server) {
1941
+ const workerEnv = getWorkerEnvironment(server);
1942
+ if (!workerEnv) return false;
1943
+ const hooksModule = workerEnv.moduleGraph.getModuleById(
1944
+ RESOLVED_VIRTUAL_HOOKS_ID
1945
+ );
1946
+ if (hooksModule) {
1947
+ workerEnv.reloadModule(hooksModule);
1948
+ server.ws.send({
1949
+ type: "custom",
1950
+ event: "agent:hooks-updated",
1951
+ data: { timestamp: Date.now() }
1952
+ });
1953
+ return true;
1954
+ }
1955
+ return false;
1956
+ }
1957
+ async function reloadModelsModule(server) {
1958
+ const workerEnv = getWorkerEnvironment(server);
1959
+ if (!workerEnv) return false;
1960
+ const modelsModule = workerEnv.moduleGraph.getModuleById(
1961
+ RESOLVED_VIRTUAL_MODELS_ID
1962
+ );
1963
+ if (modelsModule) {
1964
+ await workerEnv.reloadModule(modelsModule);
1965
+ server.ws.send({
1966
+ type: "custom",
1967
+ event: "agent:models-updated",
1968
+ data: { timestamp: Date.now() }
1969
+ });
1970
+ return true;
1971
+ }
1972
+ return false;
1973
+ }
1974
+ async function reloadPromptsModule(server) {
1975
+ const workerEnv = getWorkerEnvironment(server);
1976
+ if (!workerEnv) return false;
1977
+ const promptsModule = workerEnv.moduleGraph.getModuleById(
1978
+ RESOLVED_VIRTUAL_PROMPTS_ID
1979
+ );
1980
+ if (promptsModule) {
1981
+ await workerEnv.reloadModule(promptsModule);
1982
+ server.ws.send({
1983
+ type: "custom",
1984
+ event: "agent:prompts-updated",
1985
+ data: { timestamp: Date.now() }
1986
+ });
1987
+ return true;
1988
+ }
1989
+ return false;
1990
+ }
1991
+ async function reloadAgentsModule(server) {
1992
+ const workerEnv = getWorkerEnvironment(server);
1993
+ if (!workerEnv) return false;
1994
+ const agentsModule = workerEnv.moduleGraph.getModuleById(
1995
+ RESOLVED_VIRTUAL_AGENTS_ID
1996
+ );
1997
+ if (agentsModule) {
1998
+ await workerEnv.reloadModule(agentsModule);
1999
+ server.ws.send({
2000
+ type: "custom",
2001
+ event: "agent:agents-updated",
2002
+ data: { timestamp: Date.now() }
2003
+ });
2004
+ return true;
2005
+ }
2006
+ return false;
2007
+ }
2008
+ async function reloadRouterModule(server) {
2009
+ const workerEnv = getWorkerEnvironment(server);
2010
+ if (!workerEnv) return false;
2011
+ const routerModule = workerEnv.moduleGraph.getModuleById(
2012
+ RESOLVED_VIRTUAL_ROUTER_ID
2013
+ );
2014
+ if (routerModule) {
2015
+ await workerEnv.reloadModule(routerModule);
2016
+ return true;
2017
+ }
2018
+ return false;
2019
+ }
2020
+ function handleFileChange(file, server, action) {
2021
+ const isToolFile = file.startsWith(toolsDir) && file.endsWith(".ts");
2022
+ const isThreadApiFile = file.startsWith(threadApiDir) && file.endsWith(".ts");
2023
+ const isHookFile = file.startsWith(hooksDir) && file.endsWith(".ts");
2024
+ const isModelFile = file.startsWith(modelsDir) && file.endsWith(".ts");
2025
+ const isPromptFile = file.startsWith(promptsDir) && file.endsWith(".ts");
2026
+ const isAgentFile = file.startsWith(agentsDir) && file.endsWith(".ts");
2027
+ const isConfigFile = isModelFile || isPromptFile || isAgentFile;
2028
+ if (!isToolFile && !isThreadApiFile && !isHookFile && !isConfigFile) return;
2029
+ const fileType = isToolFile ? "tool" : isThreadApiFile ? "thread-api" : isHookFile ? "hook" : isModelFile ? "model" : isPromptFile ? "prompt" : "agent";
2030
+ console.log(
2031
+ `[vite-plugin-agent] ${action} ${fileType}: ${path3.relative(
2032
+ process.cwd(),
2033
+ file
2034
+ )}`
2035
+ );
2036
+ if (isConfigFile || isToolFile) {
2037
+ regenerateTypes();
2038
+ }
2039
+ if (isToolFile) {
2040
+ reloadToolsModule(server);
2041
+ }
2042
+ if (isModelFile) {
2043
+ reloadModelsModule(server);
2044
+ }
2045
+ if (isPromptFile) {
2046
+ reloadPromptsModule(server);
2047
+ }
2048
+ if (isAgentFile) {
2049
+ reloadAgentsModule(server);
2050
+ }
2051
+ if (isThreadApiFile) {
2052
+ reloadRoutesModule(server);
2053
+ }
2054
+ if (isHookFile) {
2055
+ reloadHooksModule(server);
2056
+ }
2057
+ }
2058
+ return {
2059
+ name: "vite-plugin-agent",
2060
+ config() {
2061
+ return {
2062
+ optimizeDeps: {
2063
+ // Exclude @standardagents/builder/mcp from pre-bundling (used for MCP server, not needed in worker)
2064
+ exclude: [
2065
+ "@standardagents/builder/mcp"
2066
+ ]
2067
+ },
2068
+ ssr: {
2069
+ // Mark as external for SSR/worker builds to prevent bundling
2070
+ // Note: @standardagents/builder, @standardagents/builder/runtime, built-in-routes, and rou3
2071
+ // are NOT external - they must be bundled into the worker for Cloudflare Workers runtime
2072
+ external: [
2073
+ "@standardagents/builder/mcp"
2074
+ ]
2075
+ },
2076
+ build: {
2077
+ rollupOptions: {
2078
+ // Also mark as external for Rollup/build (used by Cloudflare Workers plugin)
2079
+ // Note: @standardagents/builder, @standardagents/builder/runtime, built-in-routes, and rou3
2080
+ // are NOT external - they must be bundled into the worker for Cloudflare Workers runtime
2081
+ external: [
2082
+ "@standardagents/builder/mcp"
2083
+ ]
2084
+ }
2085
+ }
2086
+ };
2087
+ },
2088
+ resolveId(id) {
2089
+ if (id.includes("virtual:") || id.includes("agent")) {
2090
+ console.log("[plugin.resolveId]", id);
2091
+ }
2092
+ if (id === VIRTUAL_TOOLS_ID) {
2093
+ console.log("[plugin.resolveId] \u2713 Resolving tools");
2094
+ return RESOLVED_VIRTUAL_TOOLS_ID;
2095
+ }
2096
+ if (id === VIRTUAL_ROUTES_ID || id === VIRTUAL_ROUTER_ID) {
2097
+ console.log("[plugin.resolveId] \u2713 Resolving routes");
2098
+ return RESOLVED_VIRTUAL_ROUTES_ID;
2099
+ }
2100
+ if (id === VIRTUAL_HOOKS_ID) {
2101
+ console.log("[plugin.resolveId] \u2713 Resolving hooks");
2102
+ return RESOLVED_VIRTUAL_HOOKS_ID;
2103
+ }
2104
+ if (id === VIRTUAL_CONFIG_ID) {
2105
+ console.log("[plugin.resolveId] \u2713 Resolving config");
2106
+ return RESOLVED_VIRTUAL_CONFIG_ID;
2107
+ }
2108
+ if (id === VIRTUAL_THREAD_API_ID) {
2109
+ console.log("[plugin.resolveId] \u2713 Resolving thread API");
2110
+ return RESOLVED_VIRTUAL_THREAD_API_ID;
2111
+ }
2112
+ if (id === VIRTUAL_MODELS_ID) {
2113
+ console.log("[plugin.resolveId] \u2713 Resolving models");
2114
+ return RESOLVED_VIRTUAL_MODELS_ID;
2115
+ }
2116
+ if (id === VIRTUAL_PROMPTS_ID) {
2117
+ console.log("[plugin.resolveId] \u2713 Resolving prompts");
2118
+ return RESOLVED_VIRTUAL_PROMPTS_ID;
2119
+ }
2120
+ if (id === VIRTUAL_AGENTS_ID) {
2121
+ console.log("[plugin.resolveId] \u2713 Resolving agents");
2122
+ return RESOLVED_VIRTUAL_AGENTS_ID;
2123
+ }
2124
+ if (id === VIRTUAL_BUILDER_ID) {
2125
+ console.log("[plugin.resolveId] \u2713 Resolving consolidated builder module");
2126
+ return RESOLVED_VIRTUAL_BUILDER_ID;
2127
+ }
2128
+ },
2129
+ async load(id) {
2130
+ if (id.includes("\0virtual:") || id.includes("agent")) {
2131
+ console.log("[plugin.load]", id);
2132
+ }
2133
+ if (id === RESOLVED_VIRTUAL_TOOLS_ID) {
2134
+ const tools = await scanToolsDirectory(toolsDir);
2135
+ const toolsCode = tools.map(({ name, importPath, error }) => {
2136
+ if (error) {
2137
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2138
+ return ` "${name}": async () => ["${escapedError}", null, async () => ({ status: "error", error: "Tool validation failed" })],`;
2139
+ } else {
2140
+ return ` "${name}": async () => {
2141
+ try {
2142
+ return (await import("${importPath}")).default;
2143
+ } catch (error) {
2144
+ console.error('Failed to import tool ${name}:', error);
2145
+ return ["Failed to load tool: " + error.message, null, async () => ({ status: "error", error: "Import failed" })];
2146
+ }
2147
+ },`;
2148
+ }
2149
+ }).join("\n");
2150
+ const moduleCode = `// Virtual agent tools module
2151
+ export const tools = {
2152
+ ${toolsCode}
2153
+ };`;
2154
+ return moduleCode;
2155
+ }
2156
+ if (id === RESOLVED_VIRTUAL_ROUTES_ID) {
2157
+ const threadRoutes = scanApiDirectory(threadApiDir);
2158
+ const threadRouteCode = threadRoutes.map(({ method, route, importPath }) => {
2159
+ const apiRoute = `/api/threads/:id${route}`;
2160
+ return ` addRoute(
2161
+ router,
2162
+ "${method}",
2163
+ "${apiRoute}",
2164
+ /* v${routesVersion} */ async () => (await import("${importPath}")).default
2165
+ );`;
2166
+ }).join("\n");
2167
+ return `// Inline rou3 router code (no external imports)
2168
+ ${rou3Code}
2169
+
2170
+ import { registerBuiltInRoutes } from "@standardagents/builder/built-in-routes";
2171
+ import { config } from "virtual:@standardagents-config";
2172
+ import { tools } from "virtual:@standardagents-tools";
2173
+ import { hooks } from "virtual:@standardagents-hooks";
2174
+ import { models, modelNames } from "virtual:@standardagents-models";
2175
+ import { prompts, promptNames } from "virtual:@standardagents-prompts";
2176
+ import { agents, agentNames } from "virtual:@standardagents-agents";
2177
+ import { DurableThread as _DurableThread } from "@standardagents/builder/runtime";
2178
+ import { requireAuth } from "@standardagents/builder/runtime";
2179
+
2180
+ console.log('[Router] Virtual modules loaded - tools:', Object.keys(tools || {}), 'hooks:', Object.keys(hooks || {}), 'models:', modelNames, 'prompts:', promptNames, 'agents:', agentNames);
2181
+
2182
+ const MOUNT_POINT = "${mountPoint}";
2183
+
2184
+ // Routes that don't require authentication
2185
+ const PUBLIC_ROUTES = [
2186
+ '/api/auth/login',
2187
+ '/api/auth/config',
2188
+ '/api/auth/oauth/github',
2189
+ '/api/auth/oauth/google',
2190
+ '/api/auth/oauth/github/callback',
2191
+ '/api/auth/oauth/google/callback'
2192
+ ];
2193
+
2194
+ // Check if a route is public (no auth required)
2195
+ function isPublicRoute(routePath) {
2196
+ // Exact match for auth routes
2197
+ if (PUBLIC_ROUTES.includes(routePath)) {
2198
+ return true;
2199
+ }
2200
+
2201
+ // Thread routes are always public
2202
+ if (routePath.startsWith('/api/threads/') || routePath === '/api/threads') {
2203
+ return true;
2204
+ }
2205
+
2206
+ return false;
2207
+ }
2208
+
2209
+ export async function router(request, env) {
2210
+ const url = new URL(request.url);
2211
+ const pathname = url.pathname;
2212
+
2213
+ // Check if request is under mount point
2214
+ if (!pathname.startsWith(MOUNT_POINT)) {
2215
+ return null;
2216
+ }
2217
+
2218
+ // Strip mount point prefix for route matching, ensuring we keep the leading slash
2219
+ let routePath = pathname.slice(MOUNT_POINT.length) || "/";
2220
+ if (!routePath.startsWith('/')) {
2221
+ routePath = '/' + routePath;
2222
+ }
2223
+
2224
+ // Handle API routes
2225
+ const router = createRouter();
2226
+
2227
+ // Register built-in API routes with runtime data
2228
+ registerBuiltInRoutes(router, { config, tools, models, modelNames, prompts, promptNames, agents, agentNames });
2229
+
2230
+ // Register user thread API routes
2231
+ ${threadRouteCode}
2232
+
2233
+ const routeMatch = findRoute(
2234
+ router,
2235
+ request.method.toUpperCase(),
2236
+ routePath
2237
+ );
2238
+
2239
+ if (routeMatch) {
2240
+ // Check if authentication is required for this route
2241
+ const publicRoute = isPublicRoute(routePath);
2242
+ const isApiRoute = routePath.startsWith('/api/');
2243
+
2244
+ let authContext = null;
2245
+
2246
+ // Require authentication for all API routes except public ones
2247
+ if (isApiRoute && !publicRoute) {
2248
+ const authResult = await requireAuth(request, env);
2249
+
2250
+ // If requireAuth returns a Response, it's an error (401)
2251
+ if (authResult instanceof Response) {
2252
+ return authResult;
2253
+ }
2254
+
2255
+ authContext = authResult;
2256
+ }
2257
+
2258
+ const controller = await routeMatch.data();
2259
+ const context = {
2260
+ req: request,
2261
+ params: routeMatch.params || {},
2262
+ env: env,
2263
+ url: url,
2264
+ config,
2265
+ tools,
2266
+ auth: authContext, // Add auth context to controller context
2267
+ };
2268
+ const result = await controller(context);
2269
+
2270
+ if (result instanceof Response) {
2271
+ return result;
2272
+ }
2273
+ if (typeof result === "string") {
2274
+ return new Response(result, {
2275
+ headers: {
2276
+ "Content-Type": "text/plain",
2277
+ },
2278
+ });
2279
+ }
2280
+ return Response.json(result);
2281
+ }
2282
+
2283
+ // Serve UI for all other routes (SPA fallback)
2284
+ return serveUI(routePath, env);
2285
+ }
2286
+
2287
+ async function serveUI(pathname, env) {
2288
+ try {
2289
+ // Use Cloudflare Workers Assets binding (automatically created from wrangler assets config)
2290
+ if (env.ASSETS) {
2291
+ try {
2292
+ // Create a proper request for the asset path
2293
+ // Use a dummy origin since we only care about the path
2294
+ // Re-add mount point since pathname was stripped by router
2295
+ const assetUrl = \`http://localhost\${MOUNT_POINT}\${pathname}\`;
2296
+ let response = await env.ASSETS.fetch(assetUrl);
2297
+
2298
+ // If not found, fall back to index.html for SPA routing
2299
+ const isIndexHtml = response.status === 404 || pathname === "/" || !pathname.includes(".");
2300
+ if (isIndexHtml) {
2301
+ response = await env.ASSETS.fetch(\`http://localhost\${MOUNT_POINT}/index.html\`);
2302
+
2303
+ // Transform HTML to use configured mount point
2304
+ if (response.status === 200) {
2305
+ const html = await response.text();
2306
+ // Replace default /agentbuilder/ paths with configured mount point
2307
+ const modifiedHtml = html.replace(/\\/agentbuilder\\//g, \`\${MOUNT_POINT}/\`);
2308
+ return new Response(modifiedHtml, {
2309
+ headers: {
2310
+ "Content-Type": "text/html; charset=utf-8",
2311
+ },
2312
+ });
2313
+ }
2314
+ }
2315
+
2316
+ return response;
2317
+ } catch (assetError) {
2318
+ console.error("Error fetching from ASSETS:", assetError);
2319
+ }
2320
+ }
2321
+
2322
+ // Fallback: proxy to UI dev server if explicitly configured
2323
+ if (env.UI_DEV_SERVER) {
2324
+ const assetPath = pathname === "/" ? "/index.html" : pathname;
2325
+ const response = await fetch(\`\${env.UI_DEV_SERVER}\${assetPath}\`);
2326
+ return response;
2327
+ }
2328
+
2329
+ // In production/dev, assets should be served by Cloudflare Workers automatically
2330
+ // This function only handles fallback cases
2331
+ return null;
2332
+ } catch (error) {
2333
+ console.error("Error serving UI:", error);
2334
+ return null;
2335
+ }
2336
+ }
2337
+
2338
+ // Export DurableThread so users can re-export it from virtual module
2339
+ export { _DurableThread as DurableThread };
2340
+ `;
2341
+ }
2342
+ if (id === RESOLVED_VIRTUAL_HOOKS_ID) {
2343
+ console.log("[plugin.load] \u2713 Loading HOOKS virtual module");
2344
+ console.log("[plugin.load] hooksDir:", hooksDir);
2345
+ const hooks = await scanHooksDirectory(hooksDir);
2346
+ console.log(`[plugin.load] Scanned hooks directory, found ${hooks.length} hooks:`, hooks.map((h) => h.name));
2347
+ const hooksCode = hooks.map(({ name, importPath }) => {
2348
+ return ` "${name}": async () => {
2349
+ try {
2350
+ return (await import("${importPath}")).default;
2351
+ } catch (error) {
2352
+ console.error('[Hooks] Failed to import hook ${name}:', error);
2353
+ return null;
2354
+ }
2355
+ },`;
2356
+ }).join("\n");
2357
+ return `// Virtual agent hooks module
2358
+ console.log('[virtual:agent-hooks] Module loaded with hooks:', Object.keys({${hooks.map((h) => `'${h.name}': null`).join(", ")}}));
2359
+ export const hooks = {
2360
+ ${hooksCode}
2361
+ };`;
2362
+ }
2363
+ if (id === RESOLVED_VIRTUAL_CONFIG_ID) {
2364
+ const relativeToolsDir = path3.relative(process.cwd(), toolsDir).replace(/\\/g, "/");
2365
+ const relativeHooksDir = path3.relative(process.cwd(), hooksDir).replace(/\\/g, "/");
2366
+ const relativeApiDir = path3.relative(process.cwd(), threadApiDir).replace(/\\/g, "/");
2367
+ const relativeModelsDir = path3.relative(process.cwd(), modelsDir).replace(/\\/g, "/");
2368
+ const relativePromptsDir = path3.relative(process.cwd(), promptsDir).replace(/\\/g, "/");
2369
+ const relativeAgentsDir = path3.relative(process.cwd(), agentsDir).replace(/\\/g, "/");
2370
+ return `// Virtual agent config module
2371
+ export const config = {
2372
+ toolsDir: "${relativeToolsDir}",
2373
+ hooksDir: "${relativeHooksDir}",
2374
+ apiDir: "${relativeApiDir}",
2375
+ modelsDir: "${relativeModelsDir}",
2376
+ promptsDir: "${relativePromptsDir}",
2377
+ agentsDir: "${relativeAgentsDir}",
2378
+ mountPoint: "${mountPoint}",
2379
+ };`;
2380
+ }
2381
+ if (id === RESOLVED_VIRTUAL_MODELS_ID) {
2382
+ const models = await scanModelsDirectory(modelsDir);
2383
+ const modelsCode = models.map(({ name, importPath, error }) => {
2384
+ if (error) {
2385
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2386
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2387
+ } else {
2388
+ return ` "${name}": async () => {
2389
+ try {
2390
+ return (await import("${importPath}")).default;
2391
+ } catch (error) {
2392
+ console.error('Failed to import model ${name}:', error);
2393
+ throw error;
2394
+ }
2395
+ },`;
2396
+ }
2397
+ }).join("\n");
2398
+ return `// Virtual agent models module
2399
+ export const models = {
2400
+ ${modelsCode}
2401
+ };
2402
+
2403
+ export const modelNames = ${JSON.stringify(models.filter((m) => !m.error).map((m) => m.name))};
2404
+ `;
2405
+ }
2406
+ if (id === RESOLVED_VIRTUAL_PROMPTS_ID) {
2407
+ const prompts = await scanPromptsDirectory(promptsDir);
2408
+ const promptsCode = prompts.map(({ name, importPath, error }) => {
2409
+ if (error) {
2410
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2411
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2412
+ } else {
2413
+ return ` "${name}": async () => {
2414
+ try {
2415
+ return (await import("${importPath}")).default;
2416
+ } catch (error) {
2417
+ console.error('Failed to import prompt ${name}:', error);
2418
+ throw error;
2419
+ }
2420
+ },`;
2421
+ }
2422
+ }).join("\n");
2423
+ return `// Virtual agent prompts module
2424
+ export const prompts = {
2425
+ ${promptsCode}
2426
+ };
2427
+
2428
+ export const promptNames = ${JSON.stringify(prompts.filter((p) => !p.error).map((p) => p.name))};
2429
+ `;
2430
+ }
2431
+ if (id === RESOLVED_VIRTUAL_AGENTS_ID) {
2432
+ const agents = await scanAgentsDirectory(agentsDir);
2433
+ const agentsCode = agents.map(({ name, importPath, error }) => {
2434
+ if (error) {
2435
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2436
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2437
+ } else {
2438
+ return ` "${name}": async () => {
2439
+ try {
2440
+ return (await import("${importPath}")).default;
2441
+ } catch (error) {
2442
+ console.error('Failed to import agent ${name}:', error);
2443
+ throw error;
2444
+ }
2445
+ },`;
2446
+ }
2447
+ }).join("\n");
2448
+ return `// Virtual agent agents module
2449
+ export const agents = {
2450
+ ${agentsCode}
2451
+ };
2452
+
2453
+ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a) => a.name))};
2454
+ `;
2455
+ }
2456
+ if (id === RESOLVED_VIRTUAL_BUILDER_ID) {
2457
+ const tools = await scanToolsDirectory(toolsDir);
2458
+ const hooks = await scanHooksDirectory(hooksDir);
2459
+ const models = await scanModelsDirectory(modelsDir);
2460
+ const prompts = await scanPromptsDirectory(promptsDir);
2461
+ const agents = await scanAgentsDirectory(agentsDir);
2462
+ const toAbsolutePath = (relativePath) => {
2463
+ if (relativePath.startsWith("./")) {
2464
+ return path3.resolve(process.cwd(), relativePath).replace(/\\/g, "/");
2465
+ }
2466
+ return relativePath;
2467
+ };
2468
+ const toolsCode = tools.map(({ name, importPath, error }) => {
2469
+ const absPath = toAbsolutePath(importPath);
2470
+ if (error) {
2471
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2472
+ return ` "${name}": async () => ["${escapedError}", null, async () => ({ status: "error", error: "Tool validation failed" })],`;
2473
+ } else {
2474
+ return ` "${name}": async () => {
2475
+ try {
2476
+ return (await import("${absPath}")).default;
2477
+ } catch (error) {
2478
+ console.error('Failed to import tool ${name}:', error);
2479
+ return ["Failed to load tool: " + error.message, null, async () => ({ status: "error", error: "Import failed" })];
2480
+ }
2481
+ },`;
2482
+ }
2483
+ }).join("\n");
2484
+ const hooksCode = hooks.map(({ name, importPath }) => {
2485
+ const absPath = toAbsolutePath(importPath);
2486
+ return ` "${name}": async () => {
2487
+ try {
2488
+ return (await import("${absPath}")).default;
2489
+ } catch (error) {
2490
+ console.error('[Hooks] Failed to import hook ${name}:', error);
2491
+ return null;
2492
+ }
2493
+ },`;
2494
+ }).join("\n");
2495
+ const modelsCode = models.map(({ name, importPath, error }) => {
2496
+ const absPath = toAbsolutePath(importPath);
2497
+ if (error) {
2498
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2499
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2500
+ } else {
2501
+ return ` "${name}": async () => {
2502
+ try {
2503
+ return (await import("${absPath}")).default;
2504
+ } catch (error) {
2505
+ console.error('Failed to import model ${name}:', error);
2506
+ throw error;
2507
+ }
2508
+ },`;
2509
+ }
2510
+ }).join("\n");
2511
+ const promptsCode = prompts.map(({ name, importPath, error }) => {
2512
+ const absPath = toAbsolutePath(importPath);
2513
+ if (error) {
2514
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2515
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2516
+ } else {
2517
+ return ` "${name}": async () => {
2518
+ try {
2519
+ return (await import("${absPath}")).default;
2520
+ } catch (error) {
2521
+ console.error('Failed to import prompt ${name}:', error);
2522
+ throw error;
2523
+ }
2524
+ },`;
2525
+ }
2526
+ }).join("\n");
2527
+ const agentsCode = agents.map(({ name, importPath, error }) => {
2528
+ const absPath = toAbsolutePath(importPath);
2529
+ if (error) {
2530
+ const escapedError = error.replace(/"/g, '\\"').replace(/\n/g, "\\n");
2531
+ return ` "${name}": async () => { throw new Error("${escapedError}"); },`;
2532
+ } else {
2533
+ return ` "${name}": async () => {
2534
+ try {
2535
+ return (await import("${absPath}")).default;
2536
+ } catch (error) {
2537
+ console.error('Failed to import agent ${name}:', error);
2538
+ throw error;
2539
+ }
2540
+ },`;
2541
+ }
2542
+ }).join("\n");
2543
+ return `// Consolidated virtual module: virtual:@standardagents/builder
2544
+ // Provides DurableThread, DurableAgentBuilder, and router
2545
+ import { DurableThread as _BaseDurableThread } from '@standardagents/builder/runtime';
2546
+ import { DurableAgentBuilder as _BaseDurableAgentBuilder } from '@standardagents/builder/runtime';
2547
+
2548
+ // Re-export router from virtual:@standardagents-routes
2549
+ export { router } from 'virtual:@standardagents-routes';
2550
+
2551
+ // Registry objects
2552
+ const _tools = {
2553
+ ${toolsCode}
2554
+ };
2555
+
2556
+ const _hooks = {
2557
+ ${hooksCode}
2558
+ };
2559
+
2560
+ const _models = {
2561
+ ${modelsCode}
2562
+ };
2563
+
2564
+ const _prompts = {
2565
+ ${promptsCode}
2566
+ };
2567
+
2568
+ const _agents = {
2569
+ ${agentsCode}
2570
+ };
2571
+
2572
+ /**
2573
+ * DurableThread with all virtual module methods already implemented.
2574
+ * Simply extend this class in your agents/Thread.ts file.
2575
+ */
2576
+ export class DurableThread extends _BaseDurableThread {
2577
+ tools() {
2578
+ return _tools;
2579
+ }
2580
+
2581
+ hooks() {
2582
+ return _hooks;
2583
+ }
2584
+
2585
+ models() {
2586
+ return _models;
2587
+ }
2588
+
2589
+ prompts() {
2590
+ return _prompts;
2591
+ }
2592
+
2593
+ agents() {
2594
+ return _agents;
2595
+ }
2596
+ }
2597
+
2598
+ /**
2599
+ * DurableAgentBuilder with all virtual module methods already implemented.
2600
+ * Simply extend this class in your agents/AgentBuilder.ts file.
2601
+ */
2602
+ export class DurableAgentBuilder extends _BaseDurableAgentBuilder {
2603
+ tools() {
2604
+ return _tools;
2605
+ }
2606
+
2607
+ hooks() {
2608
+ return _hooks;
2609
+ }
2610
+
2611
+ models() {
2612
+ return _models;
2613
+ }
2614
+
2615
+ prompts() {
2616
+ return _prompts;
2617
+ }
2618
+
2619
+ agents() {
2620
+ return _agents;
2621
+ }
2622
+ }
2623
+ `;
2624
+ }
2625
+ },
2626
+ buildStart() {
2627
+ regenerateTypes();
2628
+ this.addWatchFile(toolsDir);
2629
+ this.addWatchFile(threadApiDir);
2630
+ this.addWatchFile(hooksDir);
2631
+ this.addWatchFile(modelsDir);
2632
+ this.addWatchFile(promptsDir);
2633
+ this.addWatchFile(agentsDir);
2634
+ },
2635
+ configureServer(server) {
2636
+ server.watcher.on("add", async (file) => {
2637
+ handleFileChange(file, server, "New file detected");
2638
+ });
2639
+ server.watcher.on("unlink", (file) => {
2640
+ handleFileChange(file, server, "File removed");
2641
+ });
2642
+ server.middlewares.use(async (req, res, next) => {
2643
+ const url = req.url;
2644
+ if (!url || !url.startsWith(mountPoint)) {
2645
+ next();
2646
+ return;
2647
+ }
2648
+ let pathWithoutMount = url.slice(mountPoint.length) || "/";
2649
+ if (!pathWithoutMount.startsWith("/")) {
2650
+ pathWithoutMount = "/" + pathWithoutMount;
2651
+ }
2652
+ const method = req.method?.toUpperCase();
2653
+ if (pathWithoutMount === "/api/models" && method === "GET") {
2654
+ try {
2655
+ const fs4 = await import('fs');
2656
+ const files = fs4.existsSync(modelsDir) ? fs4.readdirSync(modelsDir).filter((f) => f.endsWith(".ts")) : [];
2657
+ const modelList = files.map((file) => {
2658
+ try {
2659
+ const filePath = path3.join(modelsDir, file);
2660
+ const content = fs4.readFileSync(filePath, "utf-8");
2661
+ const getName = (c) => c.match(/name:\s*['"]([^'"]+)['"]/)?.[1];
2662
+ const getProvider = (c) => c.match(/provider:\s*['"]([^'"]+)['"]/)?.[1];
2663
+ const getModel = (c) => c.match(/model:\s*['"]([^'"]+)['"]/)?.[1];
2664
+ const getInputPrice = (c) => {
2665
+ const match = c.match(/inputPrice:\s*([\d.]+)/);
2666
+ return match ? parseFloat(match[1]) : void 0;
2667
+ };
2668
+ const getOutputPrice = (c) => {
2669
+ const match = c.match(/outputPrice:\s*([\d.]+)/);
2670
+ return match ? parseFloat(match[1]) : void 0;
2671
+ };
2672
+ const getCachedPrice = (c) => {
2673
+ const match = c.match(/cachedPrice:\s*([\d.]+)/);
2674
+ return match ? parseFloat(match[1]) : void 0;
2675
+ };
2676
+ const getIncludedProviders = (c) => {
2677
+ const match = c.match(/includedProviders:\s*\[([^\]]*)\]/);
2678
+ if (!match) return void 0;
2679
+ const items = match[1].match(/['"]([^'"]+)['"]/g);
2680
+ return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
2681
+ };
2682
+ const getFallbacks = (c) => {
2683
+ const match = c.match(/fallbacks:\s*\[([^\]]*)\]/);
2684
+ if (!match) return [];
2685
+ const items = match[1].match(/['"]([^'"]+)['"]/g);
2686
+ return items ? items.map((s) => s.replace(/['"]/g, "")) : [];
2687
+ };
2688
+ const name = getName(content);
2689
+ if (!name) return null;
2690
+ const fallbacks = getFallbacks(content);
2691
+ const fallbackObjects = fallbacks.map((fallbackName, index) => ({
2692
+ id: fallbackName,
2693
+ name: fallbackName,
2694
+ order: index
2695
+ }));
2696
+ return {
2697
+ id: name,
2698
+ name,
2699
+ provider: getProvider(content),
2700
+ model: getModel(content),
2701
+ input_price: getInputPrice(content),
2702
+ output_price: getOutputPrice(content),
2703
+ cached_price: getCachedPrice(content),
2704
+ included_providers: getIncludedProviders(content),
2705
+ fallbacks: fallbackObjects,
2706
+ created_at: Math.floor(Date.now() / 1e3)
2707
+ };
2708
+ } catch (error) {
2709
+ console.error(`Error loading model ${file}:`, error);
2710
+ return null;
2711
+ }
2712
+ });
2713
+ const validModels = modelList.filter(Boolean);
2714
+ res.statusCode = 200;
2715
+ res.setHeader("Content-Type", "application/json");
2716
+ res.end(JSON.stringify({ models: validModels }));
2717
+ return;
2718
+ } catch (error) {
2719
+ res.statusCode = 500;
2720
+ res.setHeader("Content-Type", "application/json");
2721
+ res.end(JSON.stringify({ error: error.message || "Failed to list models" }));
2722
+ return;
2723
+ }
2724
+ }
2725
+ if (pathWithoutMount === "/api/models" && method === "POST") {
2726
+ try {
2727
+ const rawBody = await parseRequestBody(req);
2728
+ const body = transformModelData(rawBody);
2729
+ const validationError = validateModelData(body);
2730
+ if (validationError) {
2731
+ res.statusCode = 400;
2732
+ res.setHeader("Content-Type", "application/json");
2733
+ res.end(JSON.stringify({ error: validationError }));
2734
+ return;
2735
+ }
2736
+ if (modelExists(modelsDir, body.name)) {
2737
+ res.statusCode = 409;
2738
+ res.setHeader("Content-Type", "application/json");
2739
+ res.end(JSON.stringify({ error: `Model '${body.name}' already exists. Use PUT to update.` }));
2740
+ return;
2741
+ }
2742
+ const result = await saveModel(modelsDir, body, false);
2743
+ if (result.success) {
2744
+ await reloadModelsModule(server);
2745
+ await reloadRouterModule(server);
2746
+ res.statusCode = 201;
2747
+ res.setHeader("Content-Type", "application/json");
2748
+ res.end(JSON.stringify({
2749
+ success: true,
2750
+ model: body,
2751
+ filePath: result.filePath
2752
+ }));
2753
+ } else {
2754
+ res.statusCode = 500;
2755
+ res.setHeader("Content-Type", "application/json");
2756
+ res.end(JSON.stringify({ error: result.error }));
2757
+ }
2758
+ return;
2759
+ } catch (error) {
2760
+ res.statusCode = 500;
2761
+ res.setHeader("Content-Type", "application/json");
2762
+ res.end(JSON.stringify({ error: error.message || "Failed to create model" }));
2763
+ return;
2764
+ }
2765
+ }
2766
+ const modelPutMatch = pathWithoutMount.match(/^\/api\/models\/(.+)$/);
2767
+ if (modelPutMatch && method === "PUT") {
2768
+ try {
2769
+ const urlModelName = decodeURIComponent(modelPutMatch[1]);
2770
+ const rawBody = await parseRequestBody(req);
2771
+ const body = transformModelData(rawBody);
2772
+ const newName = body.name;
2773
+ const isNameChange = newName && newName !== urlModelName;
2774
+ const validationError = validateModelData(body);
2775
+ if (validationError) {
2776
+ res.statusCode = 400;
2777
+ res.setHeader("Content-Type", "application/json");
2778
+ res.end(JSON.stringify({ error: validationError }));
2779
+ return;
2780
+ }
2781
+ let updatedPrompts = [];
2782
+ if (isNameChange) {
2783
+ const renameResult = await renameModel(modelsDir, urlModelName, newName);
2784
+ if (!renameResult.success) {
2785
+ res.statusCode = 400;
2786
+ res.setHeader("Content-Type", "application/json");
2787
+ res.end(JSON.stringify({ error: renameResult.error }));
2788
+ return;
2789
+ }
2790
+ updatedPrompts = await updateModelReferencesInPrompts(promptsDir, urlModelName, newName);
2791
+ }
2792
+ const result = await saveModel(modelsDir, body, true);
2793
+ if (result.success) {
2794
+ await reloadModelsModule(server);
2795
+ if (updatedPrompts.length > 0) {
2796
+ await reloadPromptsModule(server);
2797
+ }
2798
+ await reloadRouterModule(server);
2799
+ await new Promise((resolve) => setTimeout(resolve, 100));
2800
+ res.statusCode = 200;
2801
+ res.setHeader("Content-Type", "application/json");
2802
+ res.end(JSON.stringify({
2803
+ success: true,
2804
+ model: body,
2805
+ filePath: result.filePath,
2806
+ ...isNameChange && { renamed: { from: urlModelName, to: newName }, updatedPrompts }
2807
+ }));
2808
+ } else {
2809
+ res.statusCode = 500;
2810
+ res.setHeader("Content-Type", "application/json");
2811
+ res.end(JSON.stringify({ error: result.error }));
2812
+ }
2813
+ return;
2814
+ } catch (error) {
2815
+ res.statusCode = 500;
2816
+ res.setHeader("Content-Type", "application/json");
2817
+ res.end(JSON.stringify({ error: error.message || "Failed to update model" }));
2818
+ return;
2819
+ }
2820
+ }
2821
+ const modelDeleteMatch = pathWithoutMount.match(/^\/api\/models\/(.+)$/);
2822
+ if (modelDeleteMatch && method === "DELETE") {
2823
+ try {
2824
+ const modelName = decodeURIComponent(modelDeleteMatch[1]);
2825
+ const result = await deleteModel(modelsDir, modelName);
2826
+ if (result.success) {
2827
+ await reloadModelsModule(server);
2828
+ await reloadRouterModule(server);
2829
+ res.statusCode = 200;
2830
+ res.setHeader("Content-Type", "application/json");
2831
+ res.end(JSON.stringify({
2832
+ success: true,
2833
+ deleted: modelName,
2834
+ filePath: result.filePath
2835
+ }));
2836
+ } else {
2837
+ res.statusCode = 404;
2838
+ res.setHeader("Content-Type", "application/json");
2839
+ res.end(JSON.stringify({ error: result.error }));
2840
+ }
2841
+ return;
2842
+ } catch (error) {
2843
+ res.statusCode = 500;
2844
+ res.setHeader("Content-Type", "application/json");
2845
+ res.end(JSON.stringify({ error: error.message || "Failed to delete model" }));
2846
+ return;
2847
+ }
2848
+ }
2849
+ if (pathWithoutMount === "/api/prompts" && method === "POST") {
2850
+ try {
2851
+ const rawBody = await parseRequestBody(req);
2852
+ const body = transformPromptData(rawBody);
2853
+ const validationError = validatePromptData(body);
2854
+ if (validationError) {
2855
+ res.statusCode = 400;
2856
+ res.setHeader("Content-Type", "application/json");
2857
+ res.end(JSON.stringify({ error: validationError }));
2858
+ return;
2859
+ }
2860
+ if (promptExists(promptsDir, body.name)) {
2861
+ res.statusCode = 409;
2862
+ res.setHeader("Content-Type", "application/json");
2863
+ res.end(JSON.stringify({ error: `Prompt '${body.name}' already exists. Use PUT to update.` }));
2864
+ return;
2865
+ }
2866
+ const result = await savePrompt(promptsDir, body, false);
2867
+ if (result.success) {
2868
+ await reloadPromptsModule(server);
2869
+ await reloadRouterModule(server);
2870
+ res.statusCode = 201;
2871
+ res.setHeader("Content-Type", "application/json");
2872
+ res.end(JSON.stringify({
2873
+ success: true,
2874
+ prompt: body,
2875
+ filePath: result.filePath
2876
+ }));
2877
+ } else {
2878
+ res.statusCode = 500;
2879
+ res.setHeader("Content-Type", "application/json");
2880
+ res.end(JSON.stringify({ error: result.error }));
2881
+ }
2882
+ return;
2883
+ } catch (error) {
2884
+ res.statusCode = 500;
2885
+ res.setHeader("Content-Type", "application/json");
2886
+ res.end(JSON.stringify({ error: error.message || "Failed to create prompt" }));
2887
+ return;
2888
+ }
2889
+ }
2890
+ const promptPutMatch = pathWithoutMount.match(/^\/api\/prompts\/([^/]+)$/);
2891
+ if (promptPutMatch && method === "PUT") {
2892
+ try {
2893
+ const urlPromptName = decodeURIComponent(promptPutMatch[1]);
2894
+ const rawBody = await parseRequestBody(req);
2895
+ const body = transformPromptData(rawBody);
2896
+ const newName = body.name;
2897
+ const isNameChange = newName && newName !== urlPromptName;
2898
+ const validationError = validatePromptData(body);
2899
+ if (validationError) {
2900
+ res.statusCode = 400;
2901
+ res.setHeader("Content-Type", "application/json");
2902
+ res.end(JSON.stringify({ error: validationError }));
2903
+ return;
2904
+ }
2905
+ let updatedPrompts = [];
2906
+ let updatedAgents = [];
2907
+ if (isNameChange) {
2908
+ const renameResult = await renamePrompt(promptsDir, urlPromptName, newName);
2909
+ if (!renameResult.success) {
2910
+ res.statusCode = 400;
2911
+ res.setHeader("Content-Type", "application/json");
2912
+ res.end(JSON.stringify({ error: renameResult.error }));
2913
+ return;
2914
+ }
2915
+ updatedPrompts = await updatePromptReferencesInPrompts(promptsDir, urlPromptName, newName);
2916
+ updatedAgents = await updatePromptReferencesInAgents(agentsDir, urlPromptName, newName);
2917
+ }
2918
+ const result = await savePrompt(promptsDir, body, true);
2919
+ if (result.success) {
2920
+ await reloadPromptsModule(server);
2921
+ if (updatedAgents.length > 0) {
2922
+ await reloadAgentsModule(server);
2923
+ }
2924
+ await reloadRouterModule(server);
2925
+ res.statusCode = 200;
2926
+ res.setHeader("Content-Type", "application/json");
2927
+ res.end(JSON.stringify({
2928
+ success: true,
2929
+ prompt: body,
2930
+ filePath: result.filePath,
2931
+ ...isNameChange && { renamed: { from: urlPromptName, to: newName }, updatedPrompts, updatedAgents }
2932
+ }));
2933
+ } else {
2934
+ res.statusCode = 500;
2935
+ res.setHeader("Content-Type", "application/json");
2936
+ res.end(JSON.stringify({ error: result.error }));
2937
+ }
2938
+ return;
2939
+ } catch (error) {
2940
+ res.statusCode = 500;
2941
+ res.setHeader("Content-Type", "application/json");
2942
+ res.end(JSON.stringify({ error: error.message || "Failed to update prompt" }));
2943
+ return;
2944
+ }
2945
+ }
2946
+ const promptDeleteMatch = pathWithoutMount.match(/^\/api\/prompts\/([^/]+)$/);
2947
+ if (promptDeleteMatch && method === "DELETE") {
2948
+ try {
2949
+ const promptName = decodeURIComponent(promptDeleteMatch[1]);
2950
+ const result = await deletePrompt(promptsDir, promptName);
2951
+ if (result.success) {
2952
+ await reloadPromptsModule(server);
2953
+ await reloadRouterModule(server);
2954
+ res.statusCode = 200;
2955
+ res.setHeader("Content-Type", "application/json");
2956
+ res.end(JSON.stringify({
2957
+ success: true,
2958
+ deleted: promptName,
2959
+ filePath: result.filePath
2960
+ }));
2961
+ } else {
2962
+ res.statusCode = 404;
2963
+ res.setHeader("Content-Type", "application/json");
2964
+ res.end(JSON.stringify({ error: result.error }));
2965
+ }
2966
+ return;
2967
+ } catch (error) {
2968
+ res.statusCode = 500;
2969
+ res.setHeader("Content-Type", "application/json");
2970
+ res.end(JSON.stringify({ error: error.message || "Failed to delete prompt" }));
2971
+ return;
2972
+ }
2973
+ }
2974
+ if (pathWithoutMount === "/api/agents" && method === "POST") {
2975
+ try {
2976
+ const rawBody = await parseRequestBody(req);
2977
+ const body = transformAgentData(rawBody);
2978
+ const validationError = validateAgentData(body);
2979
+ if (validationError) {
2980
+ res.statusCode = 400;
2981
+ res.setHeader("Content-Type", "application/json");
2982
+ res.end(JSON.stringify({ error: validationError }));
2983
+ return;
2984
+ }
2985
+ if (agentExists(agentsDir, body.name)) {
2986
+ res.statusCode = 409;
2987
+ res.setHeader("Content-Type", "application/json");
2988
+ res.end(JSON.stringify({ error: `Agent '${body.name}' already exists. Use PUT to update.` }));
2989
+ return;
2990
+ }
2991
+ const result = await saveAgent(agentsDir, body, false);
2992
+ if (result.success) {
2993
+ await reloadAgentsModule(server);
2994
+ await reloadRouterModule(server);
2995
+ res.statusCode = 201;
2996
+ res.setHeader("Content-Type", "application/json");
2997
+ res.end(JSON.stringify({
2998
+ success: true,
2999
+ agent: body,
3000
+ filePath: result.filePath
3001
+ }));
3002
+ } else {
3003
+ res.statusCode = 500;
3004
+ res.setHeader("Content-Type", "application/json");
3005
+ res.end(JSON.stringify({ error: result.error }));
3006
+ }
3007
+ return;
3008
+ } catch (error) {
3009
+ res.statusCode = 500;
3010
+ res.setHeader("Content-Type", "application/json");
3011
+ res.end(JSON.stringify({ error: error.message || "Failed to create agent" }));
3012
+ return;
3013
+ }
3014
+ }
3015
+ const agentPutMatch = pathWithoutMount.match(/^\/api\/agents\/([^/]+)$/);
3016
+ if (agentPutMatch && method === "PUT") {
3017
+ try {
3018
+ const agentName = decodeURIComponent(agentPutMatch[1]);
3019
+ const rawBody = await parseRequestBody(req);
3020
+ const body = transformAgentData(rawBody);
3021
+ body.name = agentName;
3022
+ const validationError = validateAgentData(body);
3023
+ if (validationError) {
3024
+ res.statusCode = 400;
3025
+ res.setHeader("Content-Type", "application/json");
3026
+ res.end(JSON.stringify({ error: validationError }));
3027
+ return;
3028
+ }
3029
+ const result = await saveAgent(agentsDir, body, true);
3030
+ if (result.success) {
3031
+ await reloadAgentsModule(server);
3032
+ await reloadRouterModule(server);
3033
+ res.statusCode = 200;
3034
+ res.setHeader("Content-Type", "application/json");
3035
+ res.end(JSON.stringify({
3036
+ success: true,
3037
+ agent: body,
3038
+ filePath: result.filePath
3039
+ }));
3040
+ } else {
3041
+ res.statusCode = 500;
3042
+ res.setHeader("Content-Type", "application/json");
3043
+ res.end(JSON.stringify({ error: result.error }));
3044
+ }
3045
+ return;
3046
+ } catch (error) {
3047
+ res.statusCode = 500;
3048
+ res.setHeader("Content-Type", "application/json");
3049
+ res.end(JSON.stringify({ error: error.message || "Failed to update agent" }));
3050
+ return;
3051
+ }
3052
+ }
3053
+ const agentDeleteMatch = pathWithoutMount.match(/^\/api\/agents\/([^/]+)$/);
3054
+ if (agentDeleteMatch && method === "DELETE") {
3055
+ try {
3056
+ const agentName = decodeURIComponent(agentDeleteMatch[1]);
3057
+ const result = await deleteAgent(agentsDir, agentName);
3058
+ if (result.success) {
3059
+ await reloadAgentsModule(server);
3060
+ await reloadRouterModule(server);
3061
+ res.statusCode = 200;
3062
+ res.setHeader("Content-Type", "application/json");
3063
+ res.end(JSON.stringify({
3064
+ success: true,
3065
+ deleted: agentName,
3066
+ filePath: result.filePath
3067
+ }));
3068
+ } else {
3069
+ res.statusCode = 404;
3070
+ res.setHeader("Content-Type", "application/json");
3071
+ res.end(JSON.stringify({ error: result.error }));
3072
+ }
3073
+ return;
3074
+ } catch (error) {
3075
+ res.statusCode = 500;
3076
+ res.setHeader("Content-Type", "application/json");
3077
+ res.end(JSON.stringify({ error: error.message || "Failed to delete agent" }));
3078
+ return;
3079
+ }
3080
+ }
3081
+ if (pathWithoutMount.startsWith("/api/")) {
3082
+ next();
3083
+ return;
3084
+ }
3085
+ const isStaticAsset = pathWithoutMount.startsWith("/assets/") || pathWithoutMount.startsWith("/vendor.js") || pathWithoutMount.startsWith("/vue.js") || pathWithoutMount.startsWith("/monaco.js") || pathWithoutMount.startsWith("/index.js") || pathWithoutMount.startsWith("/index.css") || pathWithoutMount.match(/\.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)$/);
3086
+ {
3087
+ const currentDir = path3.dirname(fileURLToPath(import.meta.url));
3088
+ const isInDist = currentDir.endsWith("dist");
3089
+ const clientDir = path3.resolve(
3090
+ currentDir,
3091
+ isInDist ? "./client" : "../dist/client"
3092
+ );
3093
+ let filePath;
3094
+ if (isStaticAsset) {
3095
+ const cleanUrl = pathWithoutMount.split("?")[0];
3096
+ filePath = path3.join(clientDir, cleanUrl);
3097
+ } else {
3098
+ filePath = path3.join(clientDir, "index.html");
3099
+ }
3100
+ try {
3101
+ if (fs2.existsSync(filePath)) {
3102
+ let content = fs2.readFileSync(filePath);
3103
+ const ext = path3.extname(filePath).toLowerCase();
3104
+ if (ext === ".html") {
3105
+ const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { mountPoint: "${mountPoint}" };</script>`;
3106
+ let htmlContent = content.toString();
3107
+ const assetPrefix = mountPoint === "/" ? "/" : `${mountPoint}/`;
3108
+ htmlContent = htmlContent.replace(/\/agentbuilder\//g, assetPrefix);
3109
+ htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
3110
+ content = Buffer.from(htmlContent);
3111
+ }
3112
+ const mimeTypes = {
3113
+ ".html": "text/html",
3114
+ ".js": "application/javascript",
3115
+ ".css": "text/css",
3116
+ ".ttf": "font/ttf",
3117
+ ".woff": "font/woff",
3118
+ ".woff2": "font/woff2",
3119
+ ".svg": "image/svg+xml",
3120
+ ".png": "image/png",
3121
+ ".jpg": "image/jpeg"
3122
+ };
3123
+ const contentType = mimeTypes[ext] || "application/octet-stream";
3124
+ res.setHeader("Content-Type", contentType);
3125
+ res.end(content);
3126
+ return;
3127
+ }
3128
+ } catch (error) {
3129
+ console.error("Error serving AgentBuilder UI:", error);
3130
+ }
3131
+ }
3132
+ next();
3133
+ });
3134
+ },
3135
+ handleHotUpdate({ file, server }) {
3136
+ handleFileChange(file, server, "File modified");
3137
+ },
3138
+ writeBundle(options2, bundle) {
3139
+ const outDir = options2.dir || "dist";
3140
+ const mountPath = mountPoint.slice(1);
3141
+ const mountDir = mountPath ? path3.join(outDir, "../client", mountPath) : path3.join(outDir, "../client");
3142
+ const currentDir = path3.dirname(fileURLToPath(import.meta.url));
3143
+ const isInDist = currentDir.endsWith("dist");
3144
+ const clientDir = path3.resolve(
3145
+ currentDir,
3146
+ isInDist ? "./client" : "../dist/client"
3147
+ );
3148
+ if (!fs2.existsSync(clientDir)) {
3149
+ console.warn(`[agentbuilder] Client directory not found at ${clientDir}`);
3150
+ return;
3151
+ }
3152
+ fs2.mkdirSync(mountDir, { recursive: true });
3153
+ function copyRecursive(src, dest) {
3154
+ const entries = fs2.readdirSync(src, { withFileTypes: true });
3155
+ for (const entry of entries) {
3156
+ const srcPath = path3.join(src, entry.name);
3157
+ const destPath = path3.join(dest, entry.name);
3158
+ if (entry.isDirectory()) {
3159
+ fs2.mkdirSync(destPath, { recursive: true });
3160
+ copyRecursive(srcPath, destPath);
3161
+ } else {
3162
+ let content = fs2.readFileSync(srcPath);
3163
+ if (entry.name === "index.html") {
3164
+ const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { mountPoint: "${mountPoint}" };</script>`;
3165
+ let htmlContent = content.toString();
3166
+ const assetPrefix = mountPoint === "/" ? "/" : `${mountPoint}/`;
3167
+ htmlContent = htmlContent.replace(/\/agentbuilder\//g, assetPrefix);
3168
+ htmlContent = htmlContent.replace("</head>", `${configScript}</head>`);
3169
+ content = Buffer.from(htmlContent);
3170
+ }
3171
+ fs2.writeFileSync(destPath, content);
3172
+ }
3173
+ }
3174
+ }
3175
+ copyRecursive(clientDir, mountDir);
3176
+ console.log(`[agentbuilder] Copied client files to ${mountDir}`);
3177
+ }
3178
+ };
3179
+ }
3180
+
3181
+ export { agentbuilder };
3182
+ //# sourceMappingURL=plugin.js.map
3183
+ //# sourceMappingURL=plugin.js.map