struere 0.12.7 → 0.12.8

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.
@@ -33,11 +33,24 @@ __export(exports_convex, {
33
33
  getSyncState: () => getSyncState,
34
34
  getSiteUrl: () => getSiteUrl,
35
35
  getPullState: () => getPullState,
36
+ fireTrigger: () => fireTrigger,
36
37
  createOrganization: () => createOrganization,
37
38
  compilePrompt: () => compilePrompt,
38
39
  chatWithRouter: () => chatWithRouter,
39
40
  chatWithAgent: () => chatWithAgent
40
41
  });
42
+ function isAuthErrorMessage(error) {
43
+ return error.includes("Unauthenticated") || error.includes("OIDC") || error.includes("token") || error.includes("expired");
44
+ }
45
+ async function withAuthRetry(fn) {
46
+ const result = await fn();
47
+ if (!result.error || !isAuthErrorMessage(result.error))
48
+ return result;
49
+ const refreshed = await refreshToken();
50
+ if (!refreshed)
51
+ return result;
52
+ return fn();
53
+ }
41
54
  async function refreshToken() {
42
55
  const credentials = loadCredentials();
43
56
  if (!credentials?.sessionId)
@@ -211,6 +224,9 @@ async function syncViaHttp(apiKey, payload) {
211
224
  return json;
212
225
  }
213
226
  async function syncOrganization(payload) {
227
+ return withAuthRetry(() => _syncOrganization(payload));
228
+ }
229
+ async function _syncOrganization(payload) {
214
230
  const credentials = loadCredentials();
215
231
  const apiKey = getApiKey();
216
232
  if (apiKey && !credentials?.token) {
@@ -274,6 +290,9 @@ async function syncOrganization(payload) {
274
290
  return { success: false, error: `Unexpected response: ${text}` };
275
291
  }
276
292
  async function getSyncState(organizationId, environment) {
293
+ return withAuthRetry(() => _getSyncState(organizationId, environment));
294
+ }
295
+ async function _getSyncState(organizationId, environment) {
277
296
  const credentials = loadCredentials();
278
297
  const apiKey = getApiKey();
279
298
  if (apiKey && !credentials?.token) {
@@ -330,6 +349,9 @@ async function getSyncState(organizationId, environment) {
330
349
  return { error: `Unexpected response: ${JSON.stringify(result)}` };
331
350
  }
332
351
  async function compilePrompt(options) {
352
+ return withAuthRetry(() => _compilePrompt(options));
353
+ }
354
+ async function _compilePrompt(options) {
333
355
  const credentials = loadCredentials();
334
356
  const apiKey = getApiKey();
335
357
  if (apiKey && !credentials?.token) {
@@ -440,6 +462,9 @@ async function compilePrompt(options) {
440
462
  return { error: `Unexpected response: ${text}` };
441
463
  }
442
464
  async function runTool(options) {
465
+ return withAuthRetry(() => _runTool(options));
466
+ }
467
+ async function _runTool(options) {
443
468
  const credentials = loadCredentials();
444
469
  const apiKey = getApiKey();
445
470
  if (apiKey && !credentials?.token) {
@@ -484,6 +509,43 @@ async function runTool(options) {
484
509
  if (!token) {
485
510
  return { error: "Not authenticated" };
486
511
  }
512
+ if (!options.agentSlug) {
513
+ const response2 = await fetch(`${CONVEX_URL}/api/action`, {
514
+ method: "POST",
515
+ headers: {
516
+ "Content-Type": "application/json",
517
+ Authorization: `Bearer ${token}`
518
+ },
519
+ body: JSON.stringify({
520
+ path: "toolTesting:runTool",
521
+ args: {
522
+ environment: options.environment,
523
+ toolName: options.toolName,
524
+ toolArgs: options.toolArgs,
525
+ organizationId: options.organizationId
526
+ }
527
+ }),
528
+ signal: AbortSignal.timeout(30000)
529
+ });
530
+ const text2 = await response2.text();
531
+ let json2;
532
+ try {
533
+ json2 = JSON.parse(text2);
534
+ } catch {
535
+ return { error: text2 || `HTTP ${response2.status}` };
536
+ }
537
+ if (!response2.ok) {
538
+ const msg = json2.errorData?.message || json2.errorMessage || text2;
539
+ return { error: msg };
540
+ }
541
+ if (json2.status === "success" && json2.value) {
542
+ return { result: json2.value };
543
+ }
544
+ if (json2.status === "error") {
545
+ return { error: json2.errorData?.message || json2.errorMessage || "Unknown error from Convex" };
546
+ }
547
+ return { error: `Unexpected response: ${text2}` };
548
+ }
487
549
  const agentResponse = await fetch(`${CONVEX_URL}/api/query`, {
488
550
  method: "POST",
489
551
  headers: {
@@ -545,7 +607,97 @@ async function runTool(options) {
545
607
  }
546
608
  return { error: `Unexpected response: ${text}` };
547
609
  }
610
+ async function fireTrigger(options) {
611
+ return withAuthRetry(() => _fireTrigger(options));
612
+ }
613
+ async function _fireTrigger(options) {
614
+ const credentials = loadCredentials();
615
+ const apiKey = getApiKey();
616
+ if (apiKey && !credentials?.token) {
617
+ const siteUrl = getSiteUrl();
618
+ try {
619
+ const response2 = await fetch(`${siteUrl}/v1/fire-trigger`, {
620
+ method: "POST",
621
+ headers: {
622
+ "Content-Type": "application/json",
623
+ Authorization: `Bearer ${apiKey}`
624
+ },
625
+ body: JSON.stringify({
626
+ slug: options.slug,
627
+ entityId: options.entityId,
628
+ data: options.data
629
+ }),
630
+ signal: AbortSignal.timeout(60000)
631
+ });
632
+ const text2 = await response2.text();
633
+ let json2;
634
+ try {
635
+ json2 = JSON.parse(text2);
636
+ } catch {
637
+ return { error: text2 || `HTTP ${response2.status}` };
638
+ }
639
+ if (!response2.ok) {
640
+ return { error: json2.error || text2 };
641
+ }
642
+ return { result: json2 };
643
+ } catch (err) {
644
+ if (err instanceof DOMException && err.name === "TimeoutError") {
645
+ return { error: "Request timed out after 60s" };
646
+ }
647
+ return { error: `Network error: ${err instanceof Error ? err.message : String(err)}` };
648
+ }
649
+ }
650
+ if (credentials?.sessionId) {
651
+ await refreshToken();
652
+ }
653
+ const freshCredentials = loadCredentials();
654
+ const token = apiKey || freshCredentials?.token;
655
+ if (!token) {
656
+ return { error: "Not authenticated" };
657
+ }
658
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
659
+ method: "POST",
660
+ headers: {
661
+ "Content-Type": "application/json",
662
+ Authorization: `Bearer ${token}`
663
+ },
664
+ body: JSON.stringify({
665
+ path: "triggers:fire",
666
+ args: {
667
+ slug: options.slug,
668
+ environment: options.environment,
669
+ entityId: options.entityId,
670
+ data: options.data
671
+ }
672
+ }),
673
+ signal: AbortSignal.timeout(60000)
674
+ });
675
+ const text = await response.text();
676
+ let json;
677
+ try {
678
+ json = JSON.parse(text);
679
+ } catch {
680
+ return { error: text || `HTTP ${response.status}` };
681
+ }
682
+ if (!response.ok) {
683
+ const msg = json.errorData?.message || json.errorMessage || text;
684
+ return { error: msg };
685
+ }
686
+ if (json.status === "success" && json.value) {
687
+ return { result: json.value };
688
+ }
689
+ if (json.status === "success" && json.value === null) {
690
+ return { error: `Trigger not found: ${options.slug}` };
691
+ }
692
+ if (json.status === "error") {
693
+ return { error: json.errorData?.message || json.errorMessage || "Unknown error from Convex" };
694
+ }
695
+ return { error: `Unexpected response: ${text}` };
696
+ }
548
697
  async function chatWithAgent(options) {
698
+ return withAuthRetry(() => _chatWithAgent(options));
699
+ }
700
+ async function _chatWithAgent(options) {
549
701
  const credentials = loadCredentials();
550
702
  const apiKey = getApiKey();
551
703
  if (apiKey && !credentials?.token) {
@@ -639,6 +791,9 @@ async function chatWithAgent(options) {
639
791
  }
640
792
  }
641
793
  async function chatWithRouter(options) {
794
+ return withAuthRetry(() => _chatWithRouter(options));
795
+ }
796
+ async function _chatWithRouter(options) {
642
797
  const credentials = loadCredentials();
643
798
  const apiKey = getApiKey();
644
799
  if (credentials?.sessionId) {
@@ -698,6 +853,9 @@ async function chatWithRouter(options) {
698
853
  }
699
854
  }
700
855
  async function getPullState(organizationId, environment = "development") {
856
+ return withAuthRetry(() => _getPullState(organizationId, environment));
857
+ }
858
+ async function _getPullState(organizationId, environment = "development") {
701
859
  const credentials = loadCredentials();
702
860
  const apiKey = getApiKey();
703
861
  if (apiKey && !credentials?.token) {
@@ -803,7 +961,7 @@ async function getValidToken() {
803
961
  if (!credentials?.token)
804
962
  return { error: "Not authenticated. Run `struere login`." };
805
963
  const expiry = getJwtExpiry(credentials.token);
806
- const needsRefresh = !expiry || expiry.getTime() - Date.now() < 60000;
964
+ const needsRefresh = !expiry || expiry.getTime() - Date.now() < 300000;
807
965
  if (!needsRefresh)
808
966
  return { token: credentials.token };
809
967
  const { refreshToken: refreshToken2 } = await Promise.resolve().then(() => (init_convex(), exports_convex));
@@ -1684,7 +1842,7 @@ function defineTrigger(config) {
1684
1842
  if (!config.slug) throw new Error('Trigger slug is required')
1685
1843
  if (!config.on) throw new Error('Trigger "on" configuration is required')
1686
1844
  if (config.on.schedule) {
1687
- const parts = config.on.schedule.trim().split(/s+/)
1845
+ const parts = config.on.schedule.trim().split(/\\s+/)
1688
1846
  if (parts.length !== 5) throw new Error('Invalid cron expression: expected 5 fields, got ' + parts.length)
1689
1847
  } else {
1690
1848
  if (!config.on.entityType) throw new Error('Trigger entityType is required')
@@ -2062,6 +2220,26 @@ async function importUserFile(filePath) {
2062
2220
  ${detail}`);
2063
2221
  }
2064
2222
  }
2223
+ function formatResourceSummary(resources) {
2224
+ const parts = [];
2225
+ if (resources.agents.length > 0)
2226
+ parts.push(`${resources.agents.length} agents`);
2227
+ if (resources.entityTypes.length > 0)
2228
+ parts.push(`${resources.entityTypes.length} data types`);
2229
+ if (resources.roles.length > 0)
2230
+ parts.push(`${resources.roles.length} roles`);
2231
+ if (resources.customTools.length > 0)
2232
+ parts.push(`${resources.customTools.length} custom tools`);
2233
+ if (resources.routers.length > 0)
2234
+ parts.push(`${resources.routers.length} routers`);
2235
+ if (resources.triggers.length > 0)
2236
+ parts.push(`${resources.triggers.length} triggers`);
2237
+ if (resources.evalSuites.length > 0)
2238
+ parts.push(`${resources.evalSuites.length} eval suites`);
2239
+ if (resources.fixtures.length > 0)
2240
+ parts.push(`${resources.fixtures.length} fixtures`);
2241
+ return `Loaded ${parts.join(", ") || "no resources"}`;
2242
+ }
2065
2243
  async function loadAllResources(cwd) {
2066
2244
  const errors = [];
2067
2245
  const agents = await loadTsDirectory(join5(cwd, "agents"));
@@ -2954,6 +3132,59 @@ function extractHandlerCode(handler) {
2954
3132
  return code;
2955
3133
  }
2956
3134
 
3135
+ // src/cli/utils/validator.ts
3136
+ var INTEGRATION_PREFIXES = {
3137
+ "whatsapp.": "WhatsApp (Kapso)",
3138
+ "calendar.": "Google Calendar",
3139
+ "airtable.": "Airtable",
3140
+ "email.": "Resend",
3141
+ "payment.": "Flow / Polar"
3142
+ };
3143
+ function validateResources(payload, resources) {
3144
+ const warnings = [];
3145
+ const errors = [];
3146
+ const agentSlugs = new Set(payload.agents.map((a) => a.slug));
3147
+ if (payload.routers) {
3148
+ for (const router of payload.routers) {
3149
+ for (const agentRef of router.agents) {
3150
+ if (!agentSlugs.has(agentRef.slug)) {
3151
+ errors.push(`Router "${router.name}" references agent "${agentRef.slug}" but no agent with that slug exists`);
3152
+ }
3153
+ }
3154
+ if (!agentSlugs.has(router.fallback)) {
3155
+ errors.push(`Router "${router.name}" has fallback "${router.fallback}" but no agent with that slug exists`);
3156
+ }
3157
+ if (router.rules) {
3158
+ for (const rule of router.rules) {
3159
+ if (!agentSlugs.has(rule.route)) {
3160
+ errors.push(`Router "${router.name}" has rule routing to "${rule.route}" but no agent with that slug exists`);
3161
+ }
3162
+ }
3163
+ }
3164
+ }
3165
+ }
3166
+ const entityTypeSlugs = new Set(payload.entityTypes.map((et) => et.slug));
3167
+ if (payload.triggers) {
3168
+ for (const trigger of payload.triggers) {
3169
+ if (trigger.entityType && !entityTypeSlugs.has(trigger.entityType)) {
3170
+ errors.push(`Trigger "${trigger.name}" references entity type "${trigger.entityType}" but no entity type with that slug exists`);
3171
+ }
3172
+ }
3173
+ }
3174
+ const seenIntegrations = new Set;
3175
+ for (const agent of payload.agents) {
3176
+ for (const toolName of agent.tools) {
3177
+ for (const [prefix, integration] of Object.entries(INTEGRATION_PREFIXES)) {
3178
+ if (toolName.startsWith(prefix) && !seenIntegrations.has(integration)) {
3179
+ seenIntegrations.add(integration);
3180
+ warnings.push(`Agent "${agent.name}" uses ${toolName} \u2014 ensure ${integration} integration is configured`);
3181
+ }
3182
+ }
3183
+ }
3184
+ }
3185
+ return { warnings, errors };
3186
+ }
3187
+
2957
3188
  // src/cli/commands/sync.ts
2958
3189
  async function performDevSync(cwd, organizationId) {
2959
3190
  generateTypeDeclarations(cwd);
@@ -2964,6 +3195,16 @@ ${resources.errors.join(`
2964
3195
  `)}`);
2965
3196
  }
2966
3197
  const payload = extractSyncPayload(resources);
3198
+ const validation = validateResources(payload, resources);
3199
+ for (const warning of validation.warnings) {
3200
+ console.log(chalk6.yellow(`\u26A0 ${warning}`));
3201
+ }
3202
+ if (validation.errors.length > 0) {
3203
+ for (const error of validation.errors) {
3204
+ console.log(chalk6.red(`\u2716 ${error}`));
3205
+ }
3206
+ throw new Error(`${validation.errors.length} validation error(s) \u2014 fix the issues above before syncing`);
3207
+ }
2967
3208
  const devResult = await syncOrganization({
2968
3209
  agents: payload.agents,
2969
3210
  tools: payload.tools,
@@ -3033,6 +3274,7 @@ async function checkForDeletions(resources, organizationId, environment) {
3033
3274
  return deletions;
3034
3275
  }
3035
3276
  async function syncToEnvironment(cwd, organizationId, environment) {
3277
+ generateTypeDeclarations(cwd);
3036
3278
  const resources = await loadAllResources(cwd);
3037
3279
  if (resources.errors.length > 0) {
3038
3280
  throw new Error(`${resources.errors.length} resource loading error(s):
@@ -3040,6 +3282,16 @@ ${resources.errors.join(`
3040
3282
  `)}`);
3041
3283
  }
3042
3284
  const payload = extractSyncPayload(resources);
3285
+ const validation = validateResources(payload, resources);
3286
+ for (const warning of validation.warnings) {
3287
+ console.log(chalk6.yellow(`\u26A0 ${warning}`));
3288
+ }
3289
+ if (validation.errors.length > 0) {
3290
+ for (const error of validation.errors) {
3291
+ console.log(chalk6.red(`\u2716 ${error}`));
3292
+ }
3293
+ throw new Error(`${validation.errors.length} validation error(s) \u2014 fix the issues above before syncing`);
3294
+ }
3043
3295
  if (environment === "eval") {
3044
3296
  const result = await syncOrganization({
3045
3297
  agents: payload.agents,
@@ -3107,6 +3359,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
3107
3359
  output.info(`Environment: ${environment}${syncEval && environment === "development" ? " + eval" : ""}`);
3108
3360
  console.log();
3109
3361
  }
3362
+ generateTypeDeclarations(cwd);
3110
3363
  if (!jsonMode)
3111
3364
  output.start("Loading resources");
3112
3365
  let resources;
@@ -3124,7 +3377,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
3124
3377
  process.exit(1);
3125
3378
  }
3126
3379
  if (!jsonMode && !options.dryRun)
3127
- output.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} data types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools`);
3380
+ output.succeed(formatResourceSummary(resources));
3128
3381
  } catch (error) {
3129
3382
  if (jsonMode) {
3130
3383
  console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
@@ -3233,32 +3486,13 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
3233
3486
  }));
3234
3487
  }
3235
3488
  } catch (error) {
3236
- if (isAuthError(error) && interactive && !jsonMode) {
3237
- output.fail("Session expired - re-authenticating...");
3238
- clearCredentials();
3239
- const newCredentials = await performLogin();
3240
- if (!newCredentials) {
3241
- output.error("Authentication failed");
3242
- process.exit(1);
3243
- }
3244
- output.start("Syncing to Convex");
3245
- try {
3246
- const result = await syncToEnvironment(cwd, project.organization.id, environment);
3247
- output.succeed(`Synced to ${environment}`);
3248
- } catch (retryError) {
3249
- output.fail("Sync failed");
3250
- output.error(retryError instanceof Error ? retryError.message : String(retryError));
3251
- process.exit(1);
3252
- }
3489
+ if (jsonMode) {
3490
+ console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
3253
3491
  } else {
3254
- if (jsonMode) {
3255
- console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
3256
- } else {
3257
- output.fail("Sync failed");
3258
- output.error(error instanceof Error ? error.message : String(error));
3259
- }
3260
- process.exit(1);
3492
+ output.fail("Sync failed");
3493
+ output.error(error instanceof Error ? error.message : String(error));
3261
3494
  }
3495
+ process.exit(1);
3262
3496
  }
3263
3497
  });
3264
3498
 
@@ -3325,7 +3559,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
3325
3559
  spinner.start("Loading resources");
3326
3560
  try {
3327
3561
  loadedResources = await loadAllResources(cwd);
3328
- spinner.succeed(`Loaded ${loadedResources.agents.length} agents, ${loadedResources.entityTypes.length} data types, ${loadedResources.roles.length} roles, ${loadedResources.customTools.length} custom tools, ${loadedResources.evalSuites.length} eval suites, ${loadedResources.triggers.length} triggers, ${loadedResources.routers.length} routers, ${loadedResources.fixtures.length} fixtures`);
3562
+ spinner.succeed(formatResourceSummary(loadedResources));
3329
3563
  for (const err of loadedResources.errors) {
3330
3564
  console.log(chalk7.red(" \u2716"), err);
3331
3565
  }
@@ -3372,28 +3606,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
3372
3606
  await performDevSync(cwd, project.organization.id);
3373
3607
  spinner.succeed("Synced to development");
3374
3608
  } catch (error) {
3375
- if (isAuthError(error) && !nonInteractive) {
3376
- spinner.fail("Session expired - re-authenticating...");
3377
- clearCredentials();
3378
- credentials = await performLogin();
3379
- if (!credentials) {
3380
- console.log(chalk7.red("Authentication failed"));
3381
- process.exit(1);
3382
- }
3383
- spinner.start("Syncing to Convex");
3384
- try {
3385
- await performDevSync(cwd, project.organization.id);
3386
- spinner.succeed("Synced to development");
3387
- } catch (retryError) {
3388
- spinner.fail("Sync failed");
3389
- console.log(chalk7.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
3390
- }
3391
- } else if (isAuthError(error) && nonInteractive) {
3392
- spinner.fail("API key authentication failed");
3393
- console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
3394
- console.log(chalk7.gray("Check that STRUERE_API_KEY is valid and not expired."));
3395
- process.exit(1);
3396
- } else if (isOrgAccessError(error)) {
3609
+ if (isOrgAccessError(error)) {
3397
3610
  spinner.fail("Organization access denied");
3398
3611
  console.log();
3399
3612
  console.log(chalk7.red("You do not have access to organization:"), chalk7.cyan(project.organization.name));
@@ -3443,26 +3656,8 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
3443
3656
  await performDevSync(cwd, project.organization.id);
3444
3657
  syncSpinner.succeed("Synced");
3445
3658
  } catch (error) {
3446
- if (isAuthError(error) && !nonInteractive) {
3447
- syncSpinner.fail("Session expired - re-authenticating...");
3448
- clearCredentials();
3449
- const newCredentials = await performLogin();
3450
- if (!newCredentials) {
3451
- console.log(chalk7.red("Authentication failed"));
3452
- return;
3453
- }
3454
- const retrySyncSpinner = ora6("Syncing...").start();
3455
- try {
3456
- await performDevSync(cwd, project.organization.id);
3457
- retrySyncSpinner.succeed("Synced");
3458
- } catch (retryError) {
3459
- retrySyncSpinner.fail("Sync failed");
3460
- console.log(chalk7.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
3461
- }
3462
- } else {
3463
- syncSpinner.fail("Sync failed");
3464
- console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
3465
- }
3659
+ syncSpinner.fail("Sync failed");
3660
+ console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
3466
3661
  }
3467
3662
  };
3468
3663
  const handleFileChange = (path, action) => {
@@ -3554,13 +3749,14 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
3554
3749
  }
3555
3750
  console.log();
3556
3751
  }
3752
+ generateTypeDeclarations(cwd);
3557
3753
  if (!jsonMode)
3558
3754
  spinner.start("Loading resources");
3559
3755
  let resources;
3560
3756
  try {
3561
3757
  resources = await loadAllResources(cwd);
3562
3758
  if (!jsonMode)
3563
- spinner.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} data types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools, ${resources.evalSuites.length} eval suites`);
3759
+ spinner.succeed(formatResourceSummary(resources));
3564
3760
  for (const err of resources.errors) {
3565
3761
  if (!jsonMode)
3566
3762
  console.log(chalk8.red(" \u2716"), err);
@@ -3593,6 +3789,28 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
3593
3789
  return;
3594
3790
  }
3595
3791
  const payload = extractSyncPayload(resources);
3792
+ const validation = validateResources(payload, resources);
3793
+ if (validation.warnings.length > 0 || validation.errors.length > 0) {
3794
+ if (!jsonMode) {
3795
+ for (const warning of validation.warnings) {
3796
+ console.log(chalk8.yellow(`\u26A0 ${warning}`));
3797
+ }
3798
+ for (const error of validation.errors) {
3799
+ console.log(chalk8.red(`\u2716 ${error}`));
3800
+ }
3801
+ }
3802
+ if (validation.errors.length > 0) {
3803
+ if (jsonMode) {
3804
+ console.log(JSON.stringify({ success: false, error: `${validation.errors.length} validation error(s)`, errors: validation.errors, warnings: validation.warnings }));
3805
+ } else {
3806
+ console.log();
3807
+ console.log(chalk8.red(`${validation.errors.length} validation error(s) \u2014 fix the issues above before deploying`));
3808
+ }
3809
+ process.exit(1);
3810
+ }
3811
+ if (!jsonMode)
3812
+ console.log();
3813
+ }
3596
3814
  if (!jsonMode)
3597
3815
  spinner.start("Checking remote state");
3598
3816
  let deletions = [];
@@ -3731,52 +3949,7 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
3731
3949
  console.log();
3732
3950
  }
3733
3951
  } catch (error) {
3734
- if (isAuthError(error) && !nonInteractive) {
3735
- if (!jsonMode)
3736
- spinner.fail("Session expired - re-authenticating...");
3737
- clearCredentials();
3738
- credentials = await performLogin();
3739
- if (!credentials) {
3740
- if (jsonMode) {
3741
- console.log(JSON.stringify({ success: false, error: "Authentication failed" }));
3742
- } else {
3743
- console.log(chalk8.red("Authentication failed"));
3744
- }
3745
- process.exit(1);
3746
- }
3747
- if (!jsonMode)
3748
- spinner.start("Deploying to production");
3749
- try {
3750
- const syncResult = await syncOrganization({
3751
- agents: payload.agents,
3752
- entityTypes: payload.entityTypes,
3753
- roles: payload.roles,
3754
- triggers: payload.triggers,
3755
- tools: payload.tools,
3756
- organizationId: project.organization.id,
3757
- environment: "production"
3758
- });
3759
- if (!syncResult.success) {
3760
- throw new Error(syncResult.error || "Deploy failed");
3761
- }
3762
- if (!jsonMode) {
3763
- spinner.succeed("Deployed to production");
3764
- console.log();
3765
- console.log(chalk8.green("Success!"), "All resources deployed to production");
3766
- console.log();
3767
- } else {
3768
- console.log(JSON.stringify({ success: true, environment: "production" }));
3769
- }
3770
- } catch (retryError) {
3771
- if (jsonMode) {
3772
- console.log(JSON.stringify({ success: false, error: retryError instanceof Error ? retryError.message : String(retryError) }));
3773
- } else {
3774
- spinner.fail("Deployment failed");
3775
- console.log(chalk8.red("Error:"), retryError instanceof Error ? retryError.message : String(retryError));
3776
- }
3777
- process.exit(1);
3778
- }
3779
- } else if (isOrgAccessError(error)) {
3952
+ if (isOrgAccessError(error)) {
3780
3953
  if (jsonMode) {
3781
3954
  console.log(JSON.stringify({ success: false, error: `Organization access denied: ${project.organization.name}` }));
3782
3955
  } else {
@@ -4110,6 +4283,7 @@ var statusCommand = new Command11("status").description("Compare local vs remote
4110
4283
  }
4111
4284
  console.log();
4112
4285
  }
4286
+ generateTypeDeclarations(cwd);
4113
4287
  if (!jsonMode)
4114
4288
  spinner.start("Loading local resources");
4115
4289
  let localResources;
@@ -7147,6 +7321,7 @@ import ora14 from "ora";
7147
7321
  // src/cli/utils/triggers.ts
7148
7322
  init_credentials();
7149
7323
  init_config();
7324
+ init_convex();
7150
7325
  function getToken3() {
7151
7326
  const credentials = loadCredentials();
7152
7327
  const apiKey = getApiKey();
@@ -7248,8 +7423,8 @@ async function listTriggerExecutions(options) {
7248
7423
  ...options.limit && { limit: options.limit }
7249
7424
  });
7250
7425
  }
7251
- async function getTriggerExecutionDetail(eventId) {
7252
- return convexQuery6("triggers:getExecutionDetail", { eventId });
7426
+ async function getTriggerExecutionDetail(eventId, environment) {
7427
+ return convexQuery6("triggers:getExecutionDetail", { eventId, ...environment && { environment } });
7253
7428
  }
7254
7429
  async function convexAction3(path, args) {
7255
7430
  const token = getToken3();
@@ -7575,7 +7750,7 @@ triggersCommand.command("runs [slug]").description("List trigger runs").option("
7575
7750
  }
7576
7751
  console.log();
7577
7752
  renderTable([
7578
- { key: "id", label: "ID", width: 24 },
7753
+ { key: "id", label: "ID", width: 40 },
7579
7754
  { key: "trigger", label: "Trigger", width: 16 },
7580
7755
  { key: "status", label: "Status", width: 10 },
7581
7756
  { key: "attempts", label: "Attempts", width: 8 },
@@ -7702,7 +7877,7 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
7702
7877
  process.exit(1);
7703
7878
  }
7704
7879
  });
7705
- triggersCommand.command("logs [slug]").description("View trigger execution history").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").action(async (slug, opts) => {
7880
+ triggersCommand.command("logs [slug]").description("View trigger execution history").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (slug, opts) => {
7706
7881
  await ensureAuth5();
7707
7882
  const spinner = ora14();
7708
7883
  try {
@@ -7719,12 +7894,12 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
7719
7894
  }
7720
7895
  console.log();
7721
7896
  renderTable([
7722
- { key: "id", label: "ID", width: 24 },
7897
+ { key: "id", label: "ID", width: 40 },
7723
7898
  { key: "trigger", label: "Trigger", width: 16 },
7724
7899
  { key: "status", label: "Status", width: 8 },
7725
7900
  { key: "steps", label: "Steps", width: 7 },
7726
7901
  { key: "duration", label: "Duration", width: 10 },
7727
- { key: "error", label: "Error", width: 30 },
7902
+ { key: "error", label: "Error", width: 50 },
7728
7903
  { key: "time", label: "Time", width: 10 }
7729
7904
  ], executions.map((e) => {
7730
7905
  const status = e.eventType === "trigger.executed" ? "success" : "failed";
@@ -7737,10 +7912,23 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
7737
7912
  status: statusColor4(status),
7738
7913
  steps: `${passed}/${log.length}`,
7739
7914
  duration: `${totalMs}ms`,
7740
- error: status === "failed" ? (e.payload?.error ?? "").slice(0, 28) : "",
7915
+ error: status === "failed" ? (e.payload?.error ?? "").slice(0, 80) : "",
7741
7916
  time: relativeTime2(e.timestamp ?? Date.now())
7742
7917
  };
7743
7918
  }));
7919
+ if (opts.verbose) {
7920
+ const failures = executions.filter((e) => e.eventType === "trigger.failed");
7921
+ if (failures.length) {
7922
+ console.log();
7923
+ console.log(chalk20.bold("Error Details"));
7924
+ console.log(chalk20.gray("\u2500".repeat(60)));
7925
+ for (const f of failures) {
7926
+ console.log(` ${chalk20.cyan(f.payload?.triggerSlug ?? "")} ${chalk20.gray(relativeTime2(f.timestamp ?? Date.now()))}`);
7927
+ console.log(` ${chalk20.red(f.payload?.error ?? "Unknown error")}`);
7928
+ console.log();
7929
+ }
7930
+ }
7931
+ }
7744
7932
  console.log();
7745
7933
  } catch (err) {
7746
7934
  const message = err instanceof Error ? err.message : String(err);
@@ -7753,12 +7941,36 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
7753
7941
  process.exit(1);
7754
7942
  }
7755
7943
  });
7756
- triggersCommand.command("log <event-id>").description("View detailed trigger execution log").option("--json", "Output raw JSON").option("-v, --verbose", "Show detailed agent tool calls").action(async (eventId, opts) => {
7944
+ triggersCommand.command("log <identifier>").description("View detailed trigger execution log (by event ID or trigger slug)").option("--env <environment>", "Environment", "development").option("--nth <n>", "Show nth most recent execution (when using slug)", "1").option("--json", "Output raw JSON").option("-v, --verbose", "Show detailed agent tool calls").action(async (identifier, opts) => {
7757
7945
  await ensureAuth5();
7758
7946
  const spinner = ora14();
7759
7947
  try {
7948
+ let eventId = identifier;
7949
+ const isConvexId = identifier.includes("_") || identifier.length > 30;
7950
+ if (!isConvexId) {
7951
+ spinner.start("Resolving trigger slug to latest execution");
7952
+ const executions = await listTriggerExecutions({
7953
+ environment: opts.env,
7954
+ triggerSlug: identifier,
7955
+ limit: parseInt(opts.nth, 10)
7956
+ });
7957
+ if (!executions.length) {
7958
+ spinner.fail(`No executions found for trigger "${identifier}" in ${opts.env}`);
7959
+ if (opts.json) {
7960
+ console.log(JSON.stringify({ success: false, error: `No executions found for trigger "${identifier}"` }));
7961
+ }
7962
+ process.exit(1);
7963
+ }
7964
+ const idx = parseInt(opts.nth, 10) - 1;
7965
+ if (idx >= executions.length) {
7966
+ spinner.fail(`Only ${executions.length} executions found, cannot get #${opts.nth}`);
7967
+ process.exit(1);
7968
+ }
7969
+ eventId = executions[idx]._id;
7970
+ spinner.succeed(`Found execution for "${identifier}"`);
7971
+ }
7760
7972
  spinner.start("Fetching execution detail");
7761
- const event = await getTriggerExecutionDetail(eventId);
7973
+ const event = await getTriggerExecutionDetail(eventId, opts.env);
7762
7974
  if (!event) {
7763
7975
  spinner.fail("Execution not found");
7764
7976
  if (opts.json) {
@@ -7964,6 +8176,79 @@ triggersCommand.command("retry-event <event-id>").description("Retry a failed im
7964
8176
  process.exit(1);
7965
8177
  }
7966
8178
  });
8179
+ triggersCommand.command("fire <slug>").description("Manually fire a trigger").option("--env <environment>", "Environment (development|production|eval)", "development").option("--entity <entityId>", "Entity ID to provide as context").option("--data <json>", "JSON data for template context").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").option("-v, --verbose", "Show detailed agent tool calls").action(async (slug, opts) => {
8180
+ await ensureAuth5();
8181
+ const spinner = ora14();
8182
+ const environment = opts.env;
8183
+ if (environment === "production" && !opts.confirm && isInteractive()) {
8184
+ const readline = await import("readline");
8185
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
8186
+ await new Promise((resolve) => {
8187
+ rl.question(chalk20.yellow(`WARNING: Firing trigger in PRODUCTION environment.
8188
+ Press Enter to continue or Ctrl+C to cancel: `), resolve);
8189
+ });
8190
+ rl.close();
8191
+ }
8192
+ let data;
8193
+ if (opts.data) {
8194
+ try {
8195
+ data = JSON.parse(opts.data);
8196
+ } catch {
8197
+ console.log(chalk20.red("Error: --data must be valid JSON"));
8198
+ process.exit(1);
8199
+ }
8200
+ }
8201
+ try {
8202
+ spinner.start(`Firing trigger ${chalk20.cyan(slug)}...`);
8203
+ const { result, error } = await fireTrigger({
8204
+ slug,
8205
+ environment,
8206
+ entityId: opts.entity,
8207
+ data
8208
+ });
8209
+ if (error) {
8210
+ spinner.fail("Trigger execution failed");
8211
+ if (opts.json) {
8212
+ console.log(JSON.stringify({ success: false, error }));
8213
+ } else {
8214
+ console.log(chalk20.red("Error:"), error);
8215
+ }
8216
+ process.exit(1);
8217
+ }
8218
+ if (!result) {
8219
+ spinner.fail("No result returned");
8220
+ process.exit(1);
8221
+ }
8222
+ if (result.success) {
8223
+ spinner.succeed(chalk20.green(`Trigger ${chalk20.cyan(slug)} fired successfully`));
8224
+ } else {
8225
+ spinner.fail(chalk20.red(`Trigger ${chalk20.cyan(slug)} execution failed`));
8226
+ }
8227
+ if (opts.json) {
8228
+ console.log(JSON.stringify(result, null, 2));
8229
+ return;
8230
+ }
8231
+ console.log();
8232
+ console.log(` ${chalk20.gray("Trigger:")} ${chalk20.cyan(result.trigger.name)} (${result.trigger.slug})`);
8233
+ console.log(` ${chalk20.gray("Environment:")} ${chalk20.cyan(result.environment)}`);
8234
+ console.log(` ${chalk20.gray("Status:")} ${result.success ? chalk20.green("success") : chalk20.red("failed")}`);
8235
+ if (result.executionLog?.length) {
8236
+ renderExecutionLog(result.executionLog, opts.verbose);
8237
+ }
8238
+ if (!result.success) {
8239
+ process.exit(1);
8240
+ }
8241
+ } catch (err) {
8242
+ const message = err instanceof Error ? err.message : String(err);
8243
+ spinner.fail("Failed to fire trigger");
8244
+ if (opts.json) {
8245
+ console.log(JSON.stringify({ success: false, error: message }));
8246
+ } else {
8247
+ console.log(chalk20.red("Error:"), message);
8248
+ }
8249
+ process.exit(1);
8250
+ }
8251
+ });
7967
8252
 
7968
8253
  // src/cli/commands/compile-prompt.ts
7969
8254
  init_credentials();
@@ -8054,26 +8339,7 @@ var compilePromptCommand = new Command19("compile-prompt").description("Compile
8054
8339
  threadMetadata: Object.keys(threadMetadata).length > 0 ? threadMetadata : undefined
8055
8340
  });
8056
8341
  };
8057
- let { result, error } = await doCompile();
8058
- if (error && isAuthError(error) && !nonInteractive) {
8059
- if (!jsonMode)
8060
- spinner.fail("Session expired - re-authenticating...");
8061
- clearCredentials();
8062
- credentials = await performLogin();
8063
- if (!credentials) {
8064
- if (jsonMode) {
8065
- console.log(JSON.stringify({ success: false, error: "Authentication failed" }));
8066
- } else {
8067
- console.log(chalk21.red("Authentication failed"));
8068
- }
8069
- process.exit(1);
8070
- }
8071
- const retry = await doCompile();
8072
- result = retry.result;
8073
- error = retry.error;
8074
- if (!jsonMode && !error)
8075
- spinner.succeed("Compiled prompt");
8076
- }
8342
+ const { result, error } = await doCompile();
8077
8343
  if (error) {
8078
8344
  if (jsonMode) {
8079
8345
  console.log(JSON.stringify({ success: false, error }));
@@ -8121,7 +8387,7 @@ import { Command as Command20 } from "commander";
8121
8387
  import chalk22 from "chalk";
8122
8388
  import ora16 from "ora";
8123
8389
  init_convex();
8124
- var runToolCommand = new Command20("run-tool").description("Run a tool as it would execute during a real agent conversation").argument("<agent-slug>", "Agent slug").argument("<tool-name>", "Tool name (e.g., entity.query)").option("--env <environment>", "Environment: development | production | eval", "development").option("--args <json>", "Tool arguments as JSON string", "{}").option("--args-file <path>", "Read tool arguments from a JSON file").option("--json", "Output full JSON result").option("--confirm", "Skip production confirmation prompt").action(async (agentSlug, toolName, options) => {
8390
+ var runToolCommand = new Command20("run-tool").description("Run a tool as it would execute during a real agent conversation").argument("<tool-name>", "Tool name (e.g., entity.query, run_scrapers)").option("--agent <slug>", "Agent slug (optional \u2014 omit to run without agent context)").option("--env <environment>", "Environment: development | production | eval", "development").option("--args <json>", "Tool arguments as JSON string", "{}").option("--args-file <path>", "Read tool arguments from a JSON file").option("--json", "Output full JSON result").option("--confirm", "Skip production confirmation prompt").action(async (toolName, options) => {
8125
8391
  const spinner = ora16();
8126
8392
  const cwd = process.cwd();
8127
8393
  const nonInteractive = !isInteractive();
@@ -8190,6 +8456,9 @@ var runToolCommand = new Command20("run-tool").description("Run a tool as it wou
8190
8456
  process.exit(1);
8191
8457
  }
8192
8458
  const environment = options.env;
8459
+ if (!options.agent && !toolName.includes(".") && !jsonMode) {
8460
+ console.log(chalk22.dim(`Tip: Running without agent context. Use --agent <slug> to run with a specific agent.`));
8461
+ }
8193
8462
  if (environment === "production" && !options.confirm && !nonInteractive) {
8194
8463
  const readline = await import("readline");
8195
8464
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -8201,37 +8470,18 @@ var runToolCommand = new Command20("run-tool").description("Run a tool as it wou
8201
8470
  rl.close();
8202
8471
  }
8203
8472
  if (!jsonMode) {
8204
- spinner.start(`Running ${chalk22.cyan(toolName)} on ${chalk22.cyan(agentSlug)} (${environment})`);
8473
+ spinner.start(`Running ${chalk22.cyan(toolName)}${options.agent ? ` on ${chalk22.cyan(options.agent)}` : ""} (${environment})`);
8205
8474
  }
8206
8475
  const doRunTool = async () => {
8207
8476
  return runTool({
8208
- agentSlug,
8477
+ agentSlug: options.agent,
8209
8478
  toolName,
8210
8479
  toolArgs,
8211
8480
  environment,
8212
8481
  organizationId: project?.organization.id
8213
8482
  });
8214
8483
  };
8215
- let { result, error } = await doRunTool();
8216
- if (error && isAuthError(error) && !nonInteractive) {
8217
- if (!jsonMode)
8218
- spinner.fail("Session expired - re-authenticating...");
8219
- clearCredentials();
8220
- credentials = await performLogin();
8221
- if (!credentials) {
8222
- if (jsonMode) {
8223
- console.log(JSON.stringify({ success: false, error: "Authentication failed" }));
8224
- } else {
8225
- console.log(chalk22.red("Authentication failed"));
8226
- }
8227
- process.exit(1);
8228
- }
8229
- const retry = await doRunTool();
8230
- result = retry.result;
8231
- error = retry.error;
8232
- if (!jsonMode && !error)
8233
- spinner.succeed(`Ran ${chalk22.cyan(toolName)}`);
8234
- }
8484
+ const { result, error } = await doRunTool();
8235
8485
  if (error) {
8236
8486
  if (jsonMode) {
8237
8487
  console.log(JSON.stringify({ success: false, error }));
@@ -8258,7 +8508,7 @@ var runToolCommand = new Command20("run-tool").description("Run a tool as it wou
8258
8508
  process.exit(1);
8259
8509
  }
8260
8510
  if (!jsonMode) {
8261
- spinner.succeed(`Ran ${chalk22.cyan(toolName)} on ${chalk22.cyan(result.agent.slug)} (${result.environment}) in ${result.durationMs}ms`);
8511
+ spinner.succeed(`Ran ${chalk22.cyan(toolName)}${result.agent ? ` on ${chalk22.cyan(result.agent.slug)}` : ""} (${result.environment}) in ${result.durationMs}ms`);
8262
8512
  }
8263
8513
  if (jsonMode) {
8264
8514
  console.log(JSON.stringify(result, null, 2));
@@ -8279,7 +8529,18 @@ import chalk23 from "chalk";
8279
8529
  import ora17 from "ora";
8280
8530
  import readline from "readline";
8281
8531
  init_convex();
8282
- var chatCommand = new Command21("chat").description("Chat with an agent or via a router").argument("<slug>", "Agent slug (or router slug when --router is used)").option("--env <environment>", "Environment: development | production | eval", "development").option("--thread <id>", "Continue an existing thread").option("--message <msg>", "Single message mode (send and exit)").option("--json", "Output JSON").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show detailed response info").option("--confirm", "Skip production warning prompt").option("--router", "Chat via a router instead of directly with an agent").option("--phone <number>", "Sender phone number for routing rules").action(async (slug, options) => {
8532
+ function printExecutionMeta(meta) {
8533
+ console.log(chalk23.dim(`Model: ${meta.model} | Duration: ${meta.durationMs}ms`));
8534
+ if (meta.toolCallSummary.length > 0) {
8535
+ console.log(chalk23.dim("Tool calls:"));
8536
+ for (const tc of meta.toolCallSummary) {
8537
+ const status = tc.status === "success" ? chalk23.green("ok") : chalk23.red(tc.status);
8538
+ const err = tc.errorMessage ? chalk23.red(` \u2014 ${tc.errorMessage}`) : "";
8539
+ console.log(chalk23.dim(` ${tc.name} ${status} ${tc.durationMs}ms${err}`));
8540
+ }
8541
+ }
8542
+ }
8543
+ var chatCommand = new Command21("chat").description("Chat with an agent or via a router").argument("<slug>", "Agent slug (or router slug when --router is used)").option("--env <environment>", "Environment: development | production | eval", "development").option("--thread <id>", "Continue an existing thread").option("--message <msg>", "Single message mode (send and exit)").option("--json", "Output JSON").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show execution metadata (model, duration, tool calls)").option("--confirm", "Skip production warning prompt").option("--router", "Chat via a router instead of directly with an agent").option("--phone <number>", "Sender phone number for routing rules").action(async (slug, options) => {
8283
8544
  const spinner = ora17();
8284
8545
  const cwd = process.cwd();
8285
8546
  const nonInteractive = !isInteractive();
@@ -8427,16 +8688,18 @@ var chatCommand = new Command21("chat").description("Chat with an agent or via a
8427
8688
  console.log("\u2500".repeat(60));
8428
8689
  console.log();
8429
8690
  if (routerResult.routedToAgent) {
8430
- console.log(chalk23.magenta(`Routed to: ${routerResult.routedToAgent} (${routerResult.routedToAgentSlug})`));
8431
- console.log();
8691
+ console.log(chalk23.green(`${routerResult.routedToAgent}`) + chalk23.dim(` (${routerResult.routedToAgentSlug})`) + chalk23.magenta(" \u2190 routed") + chalk23.dim(":"));
8692
+ } else {
8693
+ console.log(chalk23.green("Agent:"));
8432
8694
  }
8433
- console.log(chalk23.green("Agent:"));
8434
8695
  console.log(result.message);
8435
8696
  console.log();
8436
8697
  if (options.verbose) {
8437
8698
  console.log(chalk23.dim(`Thread: ${result.threadId}`));
8438
8699
  console.log(chalk23.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
8439
- console.log(chalk23.dim("Tool call details available in dashboard"));
8700
+ if (result._executionMeta) {
8701
+ printExecutionMeta(result._executionMeta);
8702
+ }
8440
8703
  } else {
8441
8704
  console.log(chalk23.dim(`Thread: ${result.threadId} | Tokens: ${result.usage.totalTokens}`));
8442
8705
  }
@@ -8538,16 +8801,18 @@ var chatCommand = new Command21("chat").description("Chat with an agent or via a
8538
8801
  const interactiveRouterResult = result;
8539
8802
  console.log();
8540
8803
  if (interactiveRouterResult.routedToAgent) {
8541
- console.log(chalk23.magenta(`Routed to: ${interactiveRouterResult.routedToAgent} (${interactiveRouterResult.routedToAgentSlug})`));
8542
- console.log();
8804
+ console.log(chalk23.green(`${interactiveRouterResult.routedToAgent}`) + chalk23.dim(` (${interactiveRouterResult.routedToAgentSlug})`) + chalk23.magenta(" \u2190 routed") + chalk23.dim(":"));
8805
+ } else {
8806
+ console.log(chalk23.green("Agent:"));
8543
8807
  }
8544
- console.log(chalk23.green("Agent:"));
8545
8808
  console.log(result.message);
8546
8809
  console.log();
8547
8810
  if (options.verbose) {
8548
8811
  console.log(chalk23.dim(`Thread: ${result.threadId}`));
8549
8812
  console.log(chalk23.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
8550
- console.log(chalk23.dim("Tool call details available in dashboard"));
8813
+ if (result._executionMeta) {
8814
+ printExecutionMeta(result._executionMeta);
8815
+ }
8551
8816
  } else {
8552
8817
  console.log(chalk23.dim(`Tokens: ${result.usage.totalTokens}`));
8553
8818
  }
@@ -8763,7 +9028,7 @@ whatsappCommand.command("set-agent <connection> <agent-slug>").description("Assi
8763
9028
  // package.json
8764
9029
  var package_default = {
8765
9030
  name: "struere",
8766
- version: "0.12.7",
9031
+ version: "0.12.8",
8767
9032
  description: "Build, test, and deploy AI agents",
8768
9033
  keywords: [
8769
9034
  "ai",