@standardagents/cli 0.11.0-next.ab7e1ea → 0.11.0-next.c3b4490

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/chat/src/App.tsx CHANGED
@@ -110,7 +110,7 @@ function AuthenticatedApp() {
110
110
  </button>
111
111
 
112
112
  {currentThreadId ? (
113
- <ThreadProvider key={currentThreadId} threadId={currentThreadId} live>
113
+ <ThreadProvider key={currentThreadId} threadId={currentThreadId} live useWorkblocks>
114
114
  <Chat
115
115
  thread={threads.find(t => t.id === currentThreadId)}
116
116
  onUpdateTitle={(title) => updateThreadTitle(currentThreadId, title)}
@@ -228,62 +228,114 @@ function WorkItemStatus({ status }: { status: 'pending' | 'success' | 'error' |
228
228
  return <SpinnerIcon className="w-3.5 h-3.5 text-[var(--text-secondary)]" />
229
229
  }
230
230
 
231
+ // Chevron icon for collapse/expand
232
+ function ChevronIcon({ className, expanded }: { className?: string; expanded: boolean }) {
233
+ return (
234
+ <svg
235
+ className={`${className} transition-transform duration-200 ${expanded ? 'rotate-90' : ''}`}
236
+ viewBox="0 0 24 24"
237
+ fill="none"
238
+ stroke="currentColor"
239
+ strokeWidth={2}
240
+ strokeLinecap="round"
241
+ strokeLinejoin="round"
242
+ >
243
+ <path d="M9 18l6-6-6-6" />
244
+ </svg>
245
+ )
246
+ }
247
+
231
248
  // Workblock component
232
- function Workblock({ workblock }: { workblock: WorkMessage }) {
249
+ function Workblock({ workblock, isLast, threadLoading }: { workblock: WorkMessage; isLast?: boolean; threadLoading?: boolean }) {
250
+ const [isExpanded, setIsExpanded] = useState(false)
233
251
  const { text } = parseMessageContent(workblock.content)
234
252
  const isPending = workblock.status === 'pending'
235
253
 
236
254
  // Group work items by tool_call_id to pair calls with results
237
255
  const toolCalls = workblock.workItems.filter(item => item.type === 'tool_call')
256
+ const toolResults = workblock.workItems.filter(item => item.type === 'tool_result')
238
257
 
239
- // Count completed vs total
258
+ // Count completed vs total and check for errors
240
259
  const completedCount = toolCalls.filter(item => item.status === 'success' || item.status === 'error').length
260
+ const errorCount = toolCalls.filter(item => item.status === 'error').length
241
261
  const totalCount = toolCalls.length
262
+ const hasErrors = errorCount > 0
263
+
264
+ // Get most recent info for the badge (last tool result or last tool call name)
265
+ const lastResult = toolResults[toolResults.length - 1]
266
+ const lastToolCall = toolCalls[toolCalls.length - 1]
267
+ const badgeText = lastResult?.content
268
+ ? (lastResult.content.length > 40 ? lastResult.content.slice(0, 40) + '...' : lastResult.content)
269
+ : lastToolCall?.name
270
+ ? formatToolName(lastToolCall.name)
271
+ : null
272
+
273
+ // Determine status label and color
274
+ const statusLabel = isPending ? 'Working' : hasErrors ? 'Completed with errors' : 'Completed'
275
+ const statusColor = isPending ? 'text-violet-500' : hasErrors ? 'text-amber-500' : 'text-emerald-500'
276
+
277
+ // Show loader if this is the last workblock and thread is still loading
278
+ const showLoader = isLast && threadLoading && !isPending
242
279
 
243
280
  return (
244
- <div className="animate-fade-in space-y-3">
245
- {/* Tool calls summary */}
281
+ <div className="animate-fade-in">
282
+ {/* Workblock panel */}
246
283
  <div className={`bg-[var(--bg-tertiary)] border border-[var(--border-primary)] rounded-xl overflow-hidden max-w-[85%] ${isPending ? 'workblock-shimmer' : ''}`}>
247
- <div className="px-3 py-2 bg-[var(--bg-hover)] border-b border-[var(--border-primary)] flex items-center justify-between">
248
- <div className="flex items-center gap-2">
249
- <span className={`text-xs font-medium uppercase tracking-wide ${isPending ? 'text-violet-500' : 'text-emerald-500'}`}>
250
- {isPending ? 'Working' : 'Completed'}
284
+ {/* Header - clickable to toggle */}
285
+ <button
286
+ onClick={() => setIsExpanded(!isExpanded)}
287
+ className="w-full px-3 py-2 bg-[var(--bg-hover)] flex items-center gap-2 hover:bg-[var(--bg-tertiary)] transition-colors text-left"
288
+ >
289
+ <ChevronIcon className="w-3.5 h-3.5 text-[var(--text-tertiary)]" expanded={isExpanded} />
290
+ <span className={`text-xs font-medium uppercase tracking-wide ${statusColor}`}>
291
+ {statusLabel}
292
+ </span>
293
+ {(isPending || showLoader) && (
294
+ <SpinnerIcon className="w-3 h-3 text-violet-500" />
295
+ )}
296
+ {/* Badge with most recent info when collapsed */}
297
+ {!isExpanded && badgeText && (
298
+ <span className="ml-auto text-xs text-[var(--text-tertiary)] bg-[var(--bg-secondary)] px-2 py-0.5 rounded truncate max-w-[200px]">
299
+ {badgeText}
251
300
  </span>
252
- {isPending && (
253
- <SpinnerIcon className="w-3 h-3 text-violet-500" />
254
- )}
255
- </div>
256
- {totalCount > 1 && (
257
- <span className="text-xs text-[var(--text-tertiary)]">
301
+ )}
302
+ {isExpanded && totalCount > 1 && (
303
+ <span className="ml-auto text-xs text-[var(--text-tertiary)]">
258
304
  {completedCount}/{totalCount}
259
305
  </span>
260
306
  )}
261
- </div>
307
+ </button>
308
+
309
+ {/* Collapsible content */}
310
+ {isExpanded && (
311
+ <>
312
+ {/* Text content (thinking/reasoning) inside the workblock */}
313
+ {text && text.trim() && (
314
+ <div className="px-3 py-2.5 border-t border-[var(--border-primary)] bg-[var(--bg-secondary)]">
315
+ <p className="text-sm text-[var(--text-secondary)] italic whitespace-pre-wrap break-words">
316
+ {text.trim()}
317
+ </p>
318
+ </div>
319
+ )}
262
320
 
263
- <div className="divide-y divide-[var(--border-primary)]">
264
- {toolCalls.map((item, index) => (
265
- <div
266
- key={item.id}
267
- className="px-3 py-2.5 flex items-center gap-3 animate-slide-in"
268
- style={{ animationDelay: `${index * 50}ms` }}
269
- >
270
- <WorkItemStatus status={item.status} />
271
- <span className="text-sm text-[var(--text-primary)]">
272
- {formatToolName(item.name)}
273
- </span>
321
+ {/* Tool calls list */}
322
+ <div className="divide-y divide-[var(--border-primary)] border-t border-[var(--border-primary)]">
323
+ {toolCalls.map((item, index) => (
324
+ <div
325
+ key={item.id}
326
+ className="px-3 py-2.5 flex items-center gap-3 animate-slide-in"
327
+ style={{ animationDelay: `${index * 50}ms` }}
328
+ >
329
+ <WorkItemStatus status={item.status} />
330
+ <span className="text-sm text-[var(--text-primary)]">
331
+ {formatToolName(item.name)}
332
+ </span>
333
+ </div>
334
+ ))}
274
335
  </div>
275
- ))}
276
- </div>
336
+ </>
337
+ )}
277
338
  </div>
278
-
279
- {/* Text content if present */}
280
- {text && (
281
- <div className="max-w-[85%]">
282
- <div className="whitespace-pre-wrap break-words text-[15px] leading-relaxed text-[var(--text-primary)]">
283
- {text}
284
- </div>
285
- </div>
286
- )}
287
339
  </div>
288
340
  )
289
341
  }
@@ -717,7 +769,7 @@ function ThinkingIndicator() {
717
769
  }
718
770
 
719
771
  export function MessageList() {
720
- const { messages, loading, threadId } = useThread()
772
+ const { workblocks: messages, loading, threadId } = useThread()
721
773
  const bottomRef = useRef<HTMLDivElement>(null)
722
774
 
723
775
  const scrollToBottom = useCallback(() => {
@@ -771,10 +823,12 @@ export function MessageList() {
771
823
 
772
824
  return (
773
825
  <div className="py-6 px-4 space-y-6">
774
- {messages.map((msg) => {
826
+ {messages.map((msg, index) => {
827
+ const isLast = index === messages.length - 1
828
+
775
829
  // Handle workblocks
776
830
  if (isWorkMessage(msg)) {
777
- return <Workblock key={msg.id} workblock={msg} />
831
+ return <Workblock key={msg.id} workblock={msg} isLast={isLast} threadLoading={loading} />
778
832
  }
779
833
 
780
834
  // Skip system and tool messages (tool messages are shown in workblocks)
package/dist/index.js CHANGED
@@ -116,30 +116,32 @@ agents/
116
116
 
117
117
  Files are **auto-discovered** at runtime. No manual registration needed.
118
118
 
119
- ## FlowState
119
+ ## ThreadState
120
120
 
121
- \`FlowState\` is the central state object available in tools and hooks:
121
+ \`ThreadState\` is the state object available in tools and hooks:
122
122
 
123
123
  \`\`\`typescript
124
- interface FlowState {
125
- thread: {
126
- id: string; // Thread identifier
127
- instance: DurableThread; // Durable Object instance
128
- };
129
- agent: AgentConfig; // Current agent configuration
130
- prompt: PromptConfig; // Current prompt configuration
131
- model: ModelConfig; // Current model configuration
132
- messages: Message[]; // Conversation history
133
- env: Env; // Cloudflare Worker environment
134
- rootState: FlowState; // Parent state (for sub-prompts)
124
+ interface ThreadState {
125
+ // Identity (readonly)
126
+ threadId: string; // Thread identifier
127
+ agentId: string; // Agent identifier
128
+ userId: string | null; // User identifier (if set)
129
+ createdAt: number; // Creation timestamp
130
+
131
+ // Methods
132
+ getMessages(options?): Promise<MessagesResult>; // Get conversation history
133
+ injectMessage(input): Promise<Message>; // Add message to thread
134
+ loadModel(name): Promise<ModelConfig>; // Load model definition
135
+ loadPrompt(name): Promise<PromptConfig>; // Load prompt definition
136
+ loadAgent(name): Promise<AgentConfig>; // Load agent definition
135
137
  }
136
138
  \`\`\`
137
139
 
138
140
  Access in tools:
139
141
  \`\`\`typescript
140
142
  export default defineTool('...', schema, async (flow, args) => {
141
- const threadId = flow.thread.id;
142
- const messages = flow.messages;
143
+ const threadId = flow.threadId;
144
+ const { messages } = await flow.getMessages();
143
145
  // ...
144
146
  });
145
147
  \`\`\`
@@ -283,15 +285,12 @@ Prompts define system instructions, model selection, and available tools for LLM
283
285
  | \`toolDescription\` | \`string\` | Yes | Description shown when used as a tool |
284
286
  | \`prompt\` | \`string \\| StructuredPrompt\` | Yes | System instructions |
285
287
  | \`model\` | \`string\` | Yes | Model name to use (references \`agents/models/\`) |
286
- | \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools for LLM |
287
- | \`handoffAgents\` | \`string[]\` | No | Agents this prompt can hand off to |
288
+ | \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools (include ai_human agent names for handoffs) |
288
289
  | \`includeChat\` | \`boolean\` | No | Include full conversation history |
289
290
  | \`includePastTools\` | \`boolean\` | No | Include previous tool results |
290
291
  | \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
291
292
  | \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
292
293
  | \`requiredSchema\` | \`z.ZodObject\` | No | Input validation when called as tool |
293
- | \`beforeTool\` | \`string\` | No | Tool to run before LLM request |
294
- | \`afterTool\` | \`string\` | No | Tool to run after LLM response |
295
294
  | \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
296
295
 
297
296
  ## Basic Example
@@ -491,7 +490,7 @@ export default defineAgent({
491
490
  });
492
491
  \`\`\`
493
492
 
494
- Other prompts can then include it in their \`handoffAgents\` array.
493
+ Other prompts can then include the agent name in their \`tools\` array to enable handoffs.
495
494
 
496
495
  ## Stop Conditions (Priority Order)
497
496
 
@@ -528,12 +527,12 @@ Tools extend agent capabilities beyond text generation. They allow LLMs to fetch
528
527
  ## Function Signature
529
528
 
530
529
  \`\`\`typescript
531
- defineTool(
530
+ defineTool({
532
531
  description: string, // What the tool does (shown to LLM)
533
- args: z.ZodObject, // Input validation schema
534
- handler: (flow, args) => ToolResult, // Implementation
535
- returnSchema?: z.ZodObject // Optional output schema
536
- )
532
+ args?: z.ZodObject, // Input validation schema (optional)
533
+ execute: (state, args) => ToolResult, // Implementation
534
+ tenvs?: z.ZodObject, // Thread environment variables (optional)
535
+ })
537
536
  \`\`\`
538
537
 
539
538
  ## Basic Example
@@ -542,20 +541,20 @@ defineTool(
542
541
  import { defineTool } from '@standardagents/builder';
543
542
  import { z } from 'zod';
544
543
 
545
- export default defineTool(
546
- 'Search the knowledge base for articles matching a query',
547
- z.object({
544
+ export default defineTool({
545
+ description: 'Search the knowledge base for articles matching a query',
546
+ args: z.object({
548
547
  query: z.string().describe('Search query'),
549
548
  limit: z.number().optional().default(10).describe('Max results'),
550
549
  }),
551
- async (flow, args) => {
550
+ execute: async (state, args) => {
552
551
  const results = await searchKB(args.query, args.limit);
553
552
  return {
554
553
  status: 'success',
555
554
  result: JSON.stringify(results),
556
555
  };
557
- }
558
- );
556
+ },
557
+ });
559
558
  \`\`\`
560
559
 
561
560
  ## ToolResult Interface
@@ -565,44 +564,45 @@ export default defineTool(
565
564
  | \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
566
565
  | \`result\` | \`string\` | Tool output (required for success) |
567
566
  | \`error\` | \`string\` | Error message (for error status) |
567
+ | \`attachments\` | \`ToolAttachment[]\` | Files to attach (images, etc.) |
568
568
 
569
- ## FlowState Access
569
+ ## ThreadState Access
570
570
 
571
- The \`flow\` parameter provides access to:
571
+ The \`state\` parameter provides access to:
572
572
 
573
573
  \`\`\`typescript
574
- async (flow, args) => {
574
+ execute: async (state, args) => {
575
575
  // Thread information
576
- const threadId = flow.thread.id;
577
- const thread = flow.thread.instance;
576
+ const threadId = state.thread.id;
577
+ const thread = state.thread.instance;
578
578
 
579
579
  // Configuration
580
- const agentName = flow.agent.name;
581
- const modelName = flow.model.name;
580
+ const agentName = state.agent.name;
581
+ const modelName = state.model.name;
582
582
 
583
583
  // Message history
584
- const messages = flow.messages;
584
+ const messages = state.messages;
585
585
 
586
586
  // Environment bindings
587
- const env = flow.env;
587
+ const env = state.env;
588
588
 
589
589
  // Parent state (for sub-prompts)
590
- const root = flow.rootState;
590
+ const root = state.rootState;
591
591
  }
592
592
  \`\`\`
593
593
 
594
594
  ## Tool Without Arguments
595
595
 
596
596
  \`\`\`typescript
597
- export default defineTool(
598
- 'Get current server time',
599
- async (flow) => {
597
+ export default defineTool({
598
+ description: 'Get current server time',
599
+ execute: async (state) => {
600
600
  return {
601
601
  status: 'success',
602
602
  result: new Date().toISOString(),
603
603
  };
604
- }
605
- );
604
+ },
605
+ });
606
606
  \`\`\`
607
607
 
608
608
  ## Error Handling
@@ -610,10 +610,10 @@ export default defineTool(
610
610
  Return errors gracefully instead of throwing:
611
611
 
612
612
  \`\`\`typescript
613
- export default defineTool(
614
- 'Fetch user data',
615
- z.object({ userId: z.string() }),
616
- async (flow, args) => {
613
+ export default defineTool({
614
+ description: 'Fetch user data',
615
+ args: z.object({ userId: z.string() }),
616
+ execute: async (state, args) => {
617
617
  try {
618
618
  const user = await fetchUser(args.userId);
619
619
  return { status: 'success', result: JSON.stringify(user) };
@@ -623,8 +623,8 @@ export default defineTool(
623
623
  error: \`Failed to fetch user: \${error.message}\`,
624
624
  };
625
625
  }
626
- }
627
- );
626
+ },
627
+ });
628
628
  \`\`\`
629
629
 
630
630
  ## Queueing Additional Tools
@@ -634,21 +634,21 @@ Queue another tool to run after the current one:
634
634
  \`\`\`typescript
635
635
  import { queueTool } from '@standardagents/builder';
636
636
 
637
- export default defineTool(
638
- 'Create order and send confirmation',
639
- z.object({ items: z.array(z.string()) }),
640
- async (flow, args) => {
637
+ export default defineTool({
638
+ description: 'Create order and send confirmation',
639
+ args: z.object({ items: z.array(z.string()) }),
640
+ execute: async (state, args) => {
641
641
  const order = await createOrder(args.items);
642
642
 
643
643
  // Queue email tool to run next
644
- queueTool(flow, 'send_confirmation_email', {
644
+ queueTool(state, 'send_confirmation_email', {
645
645
  orderId: order.id,
646
- email: flow.thread.metadata.userEmail,
646
+ email: state.thread.metadata.userEmail,
647
647
  });
648
648
 
649
649
  return { status: 'success', result: JSON.stringify(order) };
650
- }
651
- );
650
+ },
651
+ });
652
652
  \`\`\`
653
653
 
654
654
  ## Injecting Messages
@@ -658,17 +658,61 @@ Add messages without triggering re-execution:
658
658
  \`\`\`typescript
659
659
  import { injectMessage } from '@standardagents/builder';
660
660
 
661
- export default defineTool(
662
- 'Log audit event',
663
- z.object({ event: z.string() }),
664
- async (flow, args) => {
665
- await injectMessage(flow, {
661
+ export default defineTool({
662
+ description: 'Log audit event',
663
+ args: z.object({ event: z.string() }),
664
+ execute: async (state, args) => {
665
+ await injectMessage(state, {
666
666
  role: 'system',
667
667
  content: \`[AUDIT] \${args.event}\`,
668
668
  });
669
669
  return { status: 'success', result: 'Logged' };
670
- }
671
- );
670
+ },
671
+ });
672
+ \`\`\`
673
+
674
+ ## Thread Environment Variables (tenvs)
675
+
676
+ Tools can declare required thread environment variables:
677
+
678
+ \`\`\`typescript
679
+ export default defineTool({
680
+ description: 'Search uploaded documents',
681
+ args: z.object({ query: z.string() }),
682
+ execute: async (state, args) => {
683
+ const vectorStoreId = state.tenvs.vectorStoreId;
684
+ const results = await searchVectorStore(vectorStoreId, args.query);
685
+ return { status: 'success', result: JSON.stringify(results) };
686
+ },
687
+ tenvs: z.object({
688
+ vectorStoreId: z.string().describe('OpenAI Vector Store ID'),
689
+ }),
690
+ });
691
+ \`\`\`
692
+
693
+ ## Returning Attachments
694
+
695
+ Tools can return file attachments (e.g., generated images):
696
+
697
+ \`\`\`typescript
698
+ export default defineTool({
699
+ description: 'Generate a chart from data',
700
+ args: z.object({ data: z.array(z.number()) }),
701
+ execute: async (state, args) => {
702
+ const chartImage = await generateChart(args.data);
703
+ return {
704
+ status: 'success',
705
+ result: 'Chart generated successfully',
706
+ attachments: [{
707
+ name: 'chart.png',
708
+ mimeType: 'image/png',
709
+ data: chartImage.base64,
710
+ width: 800,
711
+ height: 600,
712
+ }],
713
+ };
714
+ },
715
+ });
672
716
  \`\`\`
673
717
 
674
718
  ## Best Practices
@@ -678,7 +722,7 @@ export default defineTool(
678
722
  - **Handle errors gracefully** - return error status, don't throw
679
723
  - **Use snake_case** for file names (\`search_knowledge_base.ts\`)
680
724
  - **Keep tools focused** - one task per tool
681
- - **Use \`flow.rootState\`** when queueing from sub-prompts
725
+ - **Use \`state.rootState\`** when queueing from sub-prompts
682
726
 
683
727
  ## Supported Zod Types
684
728
 
@@ -982,17 +1026,19 @@ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
982
1026
  `;
983
1027
 
984
1028
  // src/commands/scaffold.ts
985
- var WRANGLER_TEMPLATE = (name) => `{
1029
+ var WRANGLER_TEMPLATE = (name) => {
1030
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1031
+ return `{
986
1032
  "$schema": "node_modules/wrangler/config-schema.json",
987
1033
  "name": "${name}",
988
1034
  "main": "worker/index.ts",
989
- "compatibility_date": "2025-01-01",
1035
+ "compatibility_date": "${today}",
990
1036
  "compatibility_flags": ["nodejs_compat"],
991
1037
  "observability": {
992
1038
  "enabled": true
993
1039
  },
994
1040
  "assets": {
995
- "directory": "dist",
1041
+ "directory": "dist/client",
996
1042
  "not_found_handling": "single-page-application",
997
1043
  "binding": "ASSETS",
998
1044
  "run_worker_first": ["/**"]
@@ -1021,6 +1067,7 @@ var WRANGLER_TEMPLATE = (name) => `{
1021
1067
  ]
1022
1068
  }
1023
1069
  `;
1070
+ };
1024
1071
  var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
1025
1072
 
1026
1073
  export default class Thread extends DurableThread {}
@@ -1085,14 +1132,6 @@ async function modifyViteConfig(configPath, force) {
1085
1132
  logger.info("Vite config already includes Standard Agents plugins");
1086
1133
  return true;
1087
1134
  }
1088
- if (!hasCloudflare || force) {
1089
- addVitePlugin(mod, {
1090
- from: "@cloudflare/vite-plugin",
1091
- imported: "cloudflare",
1092
- constructor: "cloudflare"
1093
- });
1094
- logger.success("Added cloudflare plugin to vite.config");
1095
- }
1096
1135
  if (!hasAgentBuilder || force) {
1097
1136
  addVitePlugin(mod, {
1098
1137
  from: "@standardagents/builder",
@@ -1102,6 +1141,14 @@ async function modifyViteConfig(configPath, force) {
1102
1141
  });
1103
1142
  logger.success("Added agentbuilder plugin to vite.config");
1104
1143
  }
1144
+ if (!hasCloudflare || force) {
1145
+ addVitePlugin(mod, {
1146
+ from: "@cloudflare/vite-plugin",
1147
+ imported: "cloudflare",
1148
+ constructor: "cloudflare"
1149
+ });
1150
+ logger.success("Added cloudflare plugin to vite.config");
1151
+ }
1105
1152
  await writeFile(mod, configPath);
1106
1153
  return true;
1107
1154
  } catch (error) {
@@ -1109,10 +1156,10 @@ async function modifyViteConfig(configPath, force) {
1109
1156
  logger.log("");
1110
1157
  logger.log("Please manually add the following to your vite.config.ts:");
1111
1158
  logger.log("");
1112
- logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1113
1159
  logger.log(` import { agentbuilder } from '@standardagents/builder'`);
1160
+ logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1114
1161
  logger.log("");
1115
- logger.log(` plugins: [cloudflare(), agentbuilder({ mountPoint: '/' })]`);
1162
+ logger.log(` plugins: [agentbuilder({ mountPoint: '/' }), cloudflare()]`);
1116
1163
  logger.log("");
1117
1164
  return false;
1118
1165
  }
@@ -1161,7 +1208,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1161
1208
  }
1162
1209
  if (!config.assets) {
1163
1210
  const edits = modify(result, ["assets"], {
1164
- directory: "dist",
1211
+ directory: "dist/client",
1165
1212
  not_found_handling: "single-page-application",
1166
1213
  binding: "ASSETS",
1167
1214
  run_worker_first: ["/**"]
@@ -1520,19 +1567,14 @@ function detectPackageManager() {
1520
1567
  if (fs3.existsSync("package-lock.json")) return "npm";
1521
1568
  return "npm";
1522
1569
  }
1523
- function getBuilderPackageVersion() {
1570
+ function getCliVersion() {
1524
1571
  try {
1525
1572
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1526
1573
  const pkgPath = path3.resolve(__dirname2, "../../package.json");
1527
1574
  const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1528
- const version = pkg2.version;
1529
- const prereleaseMatch = version.match(/-([a-z]+)\./i);
1530
- if (prereleaseMatch) {
1531
- return `@${prereleaseMatch[1]}`;
1532
- }
1533
- return `@^${version}`;
1575
+ return pkg2.version;
1534
1576
  } catch {
1535
- return "";
1577
+ return "latest";
1536
1578
  }
1537
1579
  }
1538
1580
  async function selectPackageManager(detected) {
@@ -1670,31 +1712,7 @@ export default defineConfig({
1670
1712
  }
1671
1713
  }
1672
1714
  logger.log("");
1673
- logger.info("Step 2: Installing Standard Agents dependencies...");
1674
- logger.log("");
1675
- try {
1676
- const installCmd = pm === "npm" ? "install" : "add";
1677
- const devFlag = pm === "npm" ? "--save-dev" : "-D";
1678
- const builderVersion = getBuilderPackageVersion();
1679
- await runCommand(pm, [
1680
- installCmd,
1681
- devFlag,
1682
- "@cloudflare/vite-plugin",
1683
- `@standardagents/builder${builderVersion}`,
1684
- "wrangler",
1685
- "zod"
1686
- ], projectPath);
1687
- } catch (error) {
1688
- logger.error("Failed to install dependencies");
1689
- process.exit(1);
1690
- }
1691
- logger.log("");
1692
- logger.info("Step 3: Configuring Standard Agents...");
1693
- logger.log("");
1694
- process.chdir(projectPath);
1695
- await scaffold({ force: true });
1696
- logger.log("");
1697
- logger.info("Step 4: Configuring environment variables...");
1715
+ logger.info("Step 2: Configuring environment variables...");
1698
1716
  logger.log("");
1699
1717
  const encryptionKey = crypto.randomBytes(32).toString("hex");
1700
1718
  let openrouterKey = "";
@@ -1729,6 +1747,44 @@ export default defineConfig({
1729
1747
  if (!adminPassword) {
1730
1748
  adminPassword = "admin";
1731
1749
  }
1750
+ logger.log("");
1751
+ logger.info("Step 3: Installing Standard Agents dependencies...");
1752
+ logger.log("");
1753
+ try {
1754
+ const installCmd = pm === "npm" ? "install" : "add";
1755
+ const devFlag = pm === "npm" ? "--save-dev" : "-D";
1756
+ const cliVersion = getCliVersion();
1757
+ await runCommand(pm, [
1758
+ installCmd,
1759
+ devFlag,
1760
+ "@cloudflare/vite-plugin",
1761
+ "wrangler"
1762
+ ], projectPath);
1763
+ const deps = [
1764
+ `@standardagents/builder@${cliVersion}`,
1765
+ `@standardagents/spec@${cliVersion}`,
1766
+ `@standardagents/sip@${cliVersion}`,
1767
+ "zod"
1768
+ ];
1769
+ if (openaiKey) {
1770
+ deps.push(`@standardagents/openai@${cliVersion}`);
1771
+ }
1772
+ if (openrouterKey) {
1773
+ deps.push(`@standardagents/openrouter@${cliVersion}`);
1774
+ }
1775
+ await runCommand(pm, [installCmd, ...deps], projectPath);
1776
+ } catch (error) {
1777
+ logger.error("Failed to install dependencies");
1778
+ process.exit(1);
1779
+ }
1780
+ logger.log("");
1781
+ logger.info("Step 4: Configuring Standard Agents...");
1782
+ logger.log("");
1783
+ process.chdir(projectPath);
1784
+ await scaffold({ force: true });
1785
+ logger.log("");
1786
+ logger.info("Writing environment configuration...");
1787
+ logger.log("");
1732
1788
  const devVarsLines = [
1733
1789
  "# Standard Agents Environment Variables",
1734
1790
  "# This file contains secrets for local development only",