llmist 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-UBPZUVIN.js";
2
+ import "./chunk-F5QK5YVI.js";
3
3
  import {
4
4
  AbstractGadget,
5
5
  AgentBuilder,
@@ -34,7 +34,7 @@ import {
34
34
  schemaToJSONSchema,
35
35
  text,
36
36
  validateGadgetSchema
37
- } from "./chunk-3SZIQI45.js";
37
+ } from "./chunk-YJKUWFIC.js";
38
38
 
39
39
  // src/cli/constants.ts
40
40
  var CLI_NAME = "llmist";
@@ -121,7 +121,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
121
121
  // package.json
122
122
  var package_default = {
123
123
  name: "llmist",
124
- version: "5.0.0",
124
+ version: "5.1.0",
125
125
  description: "TypeScript LLM client with streaming tool execution. Tools fire mid-stream. Built-in function calling works with any model\u2014no structured outputs or native tool support required.",
126
126
  type: "module",
127
127
  main: "dist/index.cjs",
@@ -3046,7 +3046,7 @@ function getRawValue(value) {
3046
3046
  function truncateValue(str, maxLen) {
3047
3047
  if (maxLen <= 0) return "";
3048
3048
  if (str.length <= maxLen) return str;
3049
- return `${str.slice(0, maxLen)}\u2026`;
3049
+ return `${str.slice(0, maxLen - 1)}\u2026`;
3050
3050
  }
3051
3051
  function formatParametersInline(params, maxWidth) {
3052
3052
  if (!params || Object.keys(params).length === 0) {
@@ -3074,6 +3074,11 @@ function formatParametersInline(params, maxWidth) {
3074
3074
  const proportion = v.length / totalRawLength;
3075
3075
  return Math.max(minPerValue, Math.floor(proportion * availableForValues));
3076
3076
  });
3077
+ const totalLimits = limits.reduce((sum, l) => sum + l, 0);
3078
+ if (totalLimits > availableForValues) {
3079
+ const scale = availableForValues / totalLimits;
3080
+ limits = limits.map((l) => Math.max(1, Math.floor(l * scale)));
3081
+ }
3077
3082
  }
3078
3083
  }
3079
3084
  } else {
@@ -3089,8 +3094,8 @@ function formatGadgetLine(info, maxWidth) {
3089
3094
  const gadgetLabel = chalk3.magenta.bold(info.name);
3090
3095
  const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
3091
3096
  const timeLabel = chalk3.dim(timeStr);
3092
- const fixedLength = 2 + info.name.length + 2 + 1 + timeStr.length;
3093
- const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
3097
+ const fixedLength = 3 + info.name.length + 2 + 1 + timeStr.length;
3098
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
3094
3099
  const paramsStr = formatParametersInline(info.parameters, availableForParams);
3095
3100
  const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
3096
3101
  if (info.error) {
@@ -3100,17 +3105,21 @@ function formatGadgetLine(info, maxWidth) {
3100
3105
  if (!info.isComplete) {
3101
3106
  return `${chalk3.blue("\u23F5")} ${gadgetLabel}${paramsLabel} ${timeLabel}`;
3102
3107
  }
3103
- let outputStr;
3108
+ let outputLabel;
3104
3109
  if (info.tokenCount !== void 0 && info.tokenCount > 0) {
3105
- outputStr = `${formatTokens(info.tokenCount)} tokens`;
3110
+ outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(info.tokenCount)} `);
3106
3111
  } else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
3107
- outputStr = formatBytes(info.outputBytes);
3112
+ outputLabel = chalk3.green(formatBytes(info.outputBytes)) + " ";
3108
3113
  } else {
3109
- outputStr = "";
3114
+ outputLabel = "";
3110
3115
  }
3111
3116
  const icon = info.breaksLoop ? chalk3.yellow("\u23F9") : chalk3.green("\u2713");
3112
- const outputLabel = outputStr ? ` ${chalk3.dim("\u2192")} ${chalk3.green(outputStr)}` : "";
3113
- return `${icon} ${gadgetLabel}${paramsLabel}${outputLabel} ${timeLabel}`;
3117
+ const nameRef = chalk3.magenta(info.name);
3118
+ const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
3119
+ const line2Prefix = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}`;
3120
+ const line2 = `${line2Prefix}${timeLabel}`;
3121
+ return `${line1}
3122
+ ${line2}`;
3114
3123
  }
3115
3124
  function formatBytes(bytes) {
3116
3125
  if (bytes < 1024) {
@@ -3121,6 +3130,11 @@ function formatBytes(bytes) {
3121
3130
  }
3122
3131
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3123
3132
  }
3133
+ function truncateOutputPreview(output, maxWidth) {
3134
+ const normalized = output.replace(/\s+/g, " ").trim();
3135
+ if (normalized.length <= maxWidth) return normalized;
3136
+ return normalized.slice(0, maxWidth - 1) + "\u2026";
3137
+ }
3124
3138
  function getMediaIcon(kind) {
3125
3139
  switch (kind) {
3126
3140
  case "image":
@@ -3148,37 +3162,99 @@ function formatGadgetSummary2(result) {
3148
3162
  const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
3149
3163
  const timeStr = result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`;
3150
3164
  const timeLabel = chalk3.dim(timeStr);
3151
- let outputStr;
3152
- if (result.tokenCount !== void 0 && result.tokenCount > 0) {
3153
- outputStr = `${formatTokens(result.tokenCount)} tokens`;
3154
- } else if (result.result) {
3165
+ const fixedLength = 3 + result.gadgetName.length + 2;
3166
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
3167
+ const paramsStr = formatParametersInline(result.parameters, availableForParams);
3168
+ const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
3169
+ const icon = result.breaksLoop ? chalk3.yellow("\u23F9") : result.error ? chalk3.red("\u2717") : chalk3.green("\u2713");
3170
+ const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
3171
+ const nameRef = chalk3.magenta(result.gadgetName);
3172
+ const hasSubagentMetrics = result.subagentMetrics && result.subagentMetrics.callCount > 0;
3173
+ let outputLabel;
3174
+ let outputStrRaw;
3175
+ if (!hasSubagentMetrics && result.tokenCount !== void 0 && result.tokenCount > 0) {
3176
+ const tokenStr = formatTokens(result.tokenCount);
3177
+ outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${tokenStr} `);
3178
+ outputStrRaw = `\u2193 ${tokenStr} `;
3179
+ } else if (!hasSubagentMetrics && result.result) {
3155
3180
  const outputBytes = Buffer.byteLength(result.result, "utf-8");
3156
- outputStr = outputBytes > 0 ? formatBytes(outputBytes) : "no output";
3181
+ if (outputBytes > 0) {
3182
+ const bytesStr = formatBytes(outputBytes);
3183
+ outputLabel = chalk3.green(bytesStr) + " ";
3184
+ outputStrRaw = bytesStr + " ";
3185
+ } else {
3186
+ outputLabel = "";
3187
+ outputStrRaw = "";
3188
+ }
3157
3189
  } else {
3158
- outputStr = "no output";
3190
+ outputLabel = "";
3191
+ outputStrRaw = "";
3159
3192
  }
3160
- const fixedLength = 2 + result.gadgetName.length + 2 + 3 + outputStr.length + 1 + timeStr.length;
3161
- const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
3162
- const paramsStr = formatParametersInline(result.parameters, availableForParams);
3163
- const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
3164
3193
  if (result.error) {
3165
3194
  const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
3166
- return `${chalk3.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
3195
+ const line22 = ` ${chalk3.dim("\u2192")} ${nameRef} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
3196
+ return `${line1}
3197
+ ${line22}`;
3198
+ }
3199
+ const previewWidth = Math.floor(terminalWidth * 0.6);
3200
+ const prefixLength = 4 + result.gadgetName.length + 1 + outputStrRaw.length + 1 + timeStr.length + 2;
3201
+ const availablePreview = Math.max(20, previewWidth - prefixLength);
3202
+ let customPreview;
3203
+ if (result.gadgetName === "TodoUpsert" && result.parameters?.content) {
3204
+ const statusEmoji = result.parameters.status === "done" ? "\u2705" : result.parameters.status === "in_progress" ? "\u{1F504}" : "\u2B1C";
3205
+ const content = String(result.parameters.content);
3206
+ customPreview = `${statusEmoji} ${truncateOutputPreview(content, availablePreview - 3)}`;
3207
+ }
3208
+ if (result.gadgetName === "GoogleSearch" && result.parameters?.query) {
3209
+ const query = String(result.parameters.query);
3210
+ const countMatch = result.result?.match(/\((\d+)\s+of\s+[\d,]+\s+results?\)/i) || // "(10 of 36400000 results)"
3211
+ result.result?.match(/(\d+)\s+results?\s+found/i) || // "10 results found"
3212
+ result.result?.match(/found\s+(\d+)\s+results?/i);
3213
+ const count = countMatch?.[1] ?? (result.parameters.maxResults ? String(result.parameters.maxResults) : null);
3214
+ const countStr = count ? ` \u2192 ${count} results` : "";
3215
+ const queryPreview = truncateOutputPreview(query, availablePreview - 5 - countStr.length);
3216
+ customPreview = `\u{1F50D} "${queryPreview}"${countStr}`;
3217
+ }
3218
+ let subagentMetricsStr = "";
3219
+ if (result.subagentMetrics && result.subagentMetrics.callCount > 0) {
3220
+ const parts = [];
3221
+ const m = result.subagentMetrics;
3222
+ if (m.inputTokens > 0) {
3223
+ parts.push(chalk3.dim("\u2191") + chalk3.yellow(` ${formatTokens(m.inputTokens)}`));
3224
+ }
3225
+ if (m.cachedInputTokens > 0) {
3226
+ parts.push(chalk3.dim("\u27F3") + chalk3.blue(` ${formatTokens(m.cachedInputTokens)}`));
3227
+ }
3228
+ if (m.outputTokens > 0) {
3229
+ parts.push(chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(m.outputTokens)}`));
3230
+ }
3231
+ if (m.cost > 0) {
3232
+ parts.push(chalk3.cyan(`$${formatCost(m.cost)}`));
3233
+ }
3234
+ if (parts.length > 0) {
3235
+ subagentMetricsStr = parts.join(chalk3.dim(" | ")) + chalk3.dim(" | ");
3236
+ }
3167
3237
  }
3168
- const outputLabel = outputStr === "no output" ? chalk3.dim(outputStr) : chalk3.green(outputStr);
3169
- const icon = result.breaksLoop ? chalk3.yellow("\u23F9") : chalk3.green("\u2713");
3170
- let summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${chalk3.dim("\u2192")} ${outputLabel} ${timeLabel}`;
3238
+ let line2;
3239
+ const previewContent = customPreview ?? (result.result?.trim() ? truncateOutputPreview(result.result, availablePreview) : null);
3240
+ if (previewContent) {
3241
+ line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}${chalk3.dim(":")} ${chalk3.dim(previewContent)}`;
3242
+ } else {
3243
+ line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}`;
3244
+ }
3245
+ let output = `${line1}
3246
+ ${line2}`;
3171
3247
  if (result.media && result.media.length > 0) {
3172
3248
  const mediaLines = result.media.map(formatMediaLine);
3173
- summaryLine += "\n" + mediaLines.join("\n");
3249
+ output += "\n" + mediaLines.join("\n");
3174
3250
  }
3175
3251
  if (result.gadgetName === "TellUser" && result.parameters?.message) {
3176
3252
  const message = String(result.parameters.message);
3177
3253
  const rendered = renderMarkdownWithSeparators(message);
3178
- return `${summaryLine}
3254
+ return `${output}
3179
3255
  ${rendered}`;
3180
3256
  }
3181
- return summaryLine;
3257
+ return output;
3182
3258
  }
3183
3259
 
3184
3260
  // src/cli/utils.ts
@@ -3375,14 +3451,15 @@ var StreamProgress = class {
3375
3451
  * Add a nested agent LLM call (called when nested llm_call_start event received).
3376
3452
  * Used to display hierarchical progress for subagent gadgets.
3377
3453
  */
3378
- addNestedAgent(id, parentInvocationId, depth, model, iteration, inputTokens) {
3454
+ addNestedAgent(id, parentInvocationId, depth, model, iteration, info) {
3379
3455
  this.nestedAgents.set(id, {
3380
3456
  parentInvocationId,
3381
3457
  depth,
3382
3458
  model,
3383
3459
  iteration,
3384
3460
  startTime: Date.now(),
3385
- inputTokens
3461
+ inputTokens: info?.inputTokens,
3462
+ cachedInputTokens: info?.cachedInputTokens
3386
3463
  });
3387
3464
  if (this.isRunning && this.isTTY) {
3388
3465
  this.render();
@@ -3396,22 +3473,23 @@ var StreamProgress = class {
3396
3473
  updateNestedAgent(id, info) {
3397
3474
  const agent = this.nestedAgents.get(id);
3398
3475
  if (agent) {
3399
- agent.inputTokens = info.inputTokens;
3400
- agent.outputTokens = info.outputTokens;
3401
- agent.cachedInputTokens = info.cachedInputTokens;
3402
- agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
3403
- agent.finishReason = info.finishReason;
3476
+ if (info.inputTokens !== void 0) agent.inputTokens = info.inputTokens;
3477
+ if (info.outputTokens !== void 0) agent.outputTokens = info.outputTokens;
3478
+ if (info.cachedInputTokens !== void 0) agent.cachedInputTokens = info.cachedInputTokens;
3479
+ if (info.cacheCreationInputTokens !== void 0)
3480
+ agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
3481
+ if (info.finishReason !== void 0) agent.finishReason = info.finishReason;
3404
3482
  if (info.cost !== void 0) {
3405
3483
  agent.cost = info.cost;
3406
- } else if (this.modelRegistry && agent.model && info.outputTokens) {
3484
+ } else if (this.modelRegistry && agent.model && agent.outputTokens) {
3407
3485
  try {
3408
3486
  const modelName = agent.model.includes(":") ? agent.model.split(":")[1] : agent.model;
3409
3487
  const costResult = this.modelRegistry.estimateCost(
3410
3488
  modelName,
3411
- info.inputTokens ?? 0,
3412
- info.outputTokens,
3413
- info.cachedInputTokens,
3414
- info.cacheCreationInputTokens
3489
+ agent.inputTokens ?? 0,
3490
+ agent.outputTokens,
3491
+ agent.cachedInputTokens,
3492
+ agent.cacheCreationInputTokens
3415
3493
  );
3416
3494
  agent.cost = costResult?.totalCost;
3417
3495
  } catch {
@@ -3433,6 +3511,27 @@ var StreamProgress = class {
3433
3511
  this.render();
3434
3512
  }
3435
3513
  }
3514
+ /**
3515
+ * Get aggregated metrics from all nested agents for a parent gadget.
3516
+ * Used to show total token counts and cost for subagent gadgets like BrowseWeb.
3517
+ */
3518
+ getAggregatedSubagentMetrics(parentInvocationId) {
3519
+ let inputTokens = 0;
3520
+ let outputTokens = 0;
3521
+ let cachedInputTokens = 0;
3522
+ let cost = 0;
3523
+ let callCount = 0;
3524
+ for (const [, nested] of this.nestedAgents) {
3525
+ if (nested.parentInvocationId === parentInvocationId) {
3526
+ inputTokens += nested.inputTokens ?? 0;
3527
+ outputTokens += nested.outputTokens ?? 0;
3528
+ cachedInputTokens += nested.cachedInputTokens ?? 0;
3529
+ cost += nested.cost ?? 0;
3530
+ callCount++;
3531
+ }
3532
+ }
3533
+ return { inputTokens, outputTokens, cachedInputTokens, cost, callCount };
3534
+ }
3436
3535
  /**
3437
3536
  * Add a nested gadget call (called when nested gadget_call event received).
3438
3537
  */
@@ -3600,20 +3699,22 @@ var StreamProgress = class {
3600
3699
  this.clearRenderedLines();
3601
3700
  const spinner = SPINNER_FRAMES[this.frameIndex++ % SPINNER_FRAMES.length];
3602
3701
  const lines = [];
3603
- if (this.mode === "streaming") {
3604
- lines.push(this.formatStreamingLine(spinner));
3605
- } else {
3606
- lines.push(this.formatCumulativeLine(spinner));
3607
- }
3702
+ const activeNestedStreams = [];
3608
3703
  if (this.isTTY) {
3609
3704
  for (const [gadgetId, gadget] of this.inFlightGadgets) {
3610
3705
  const elapsedSeconds = (Date.now() - gadget.startTime) / 1e3;
3611
- const gadgetLine = ` ${formatGadgetLine({
3612
- name: gadget.name,
3613
- parameters: gadget.params,
3614
- elapsedSeconds,
3615
- isComplete: false
3616
- })}`;
3706
+ const termWidth = process.stdout.columns ?? 80;
3707
+ const gadgetIndent = " ";
3708
+ const line = formatGadgetLine(
3709
+ {
3710
+ name: gadget.name,
3711
+ parameters: gadget.params,
3712
+ elapsedSeconds,
3713
+ isComplete: false
3714
+ },
3715
+ termWidth - gadgetIndent.length
3716
+ );
3717
+ const gadgetLine = line.split("\n").map((l) => gadgetIndent + l).join("\n");
3617
3718
  lines.push(gadgetLine);
3618
3719
  const nestedOps = [];
3619
3720
  for (const [_agentId, nested] of this.nestedAgents) {
@@ -3632,6 +3733,18 @@ var StreamProgress = class {
3632
3733
  completed: nested.completed,
3633
3734
  completedTime: nested.completedTime
3634
3735
  });
3736
+ if (!nested.completed) {
3737
+ activeNestedStreams.push({
3738
+ depth: nested.depth,
3739
+ iteration: nested.iteration,
3740
+ model: nested.model,
3741
+ inputTokens: nested.inputTokens,
3742
+ cachedInputTokens: nested.cachedInputTokens,
3743
+ outputTokens: nested.outputTokens,
3744
+ cost: nested.cost,
3745
+ startTime: nested.startTime
3746
+ });
3747
+ }
3635
3748
  }
3636
3749
  }
3637
3750
  for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
@@ -3649,11 +3762,14 @@ var StreamProgress = class {
3649
3762
  }
3650
3763
  nestedOps.sort((a, b) => a.startTime - b.startTime);
3651
3764
  for (const op of nestedOps) {
3652
- const indent = " ".repeat(op.depth + 1);
3765
+ if (op.type === "agent" && !op.completed) {
3766
+ continue;
3767
+ }
3768
+ const indent = " ".repeat(op.depth + 2);
3653
3769
  const endTime = op.completedTime ?? Date.now();
3654
3770
  const elapsedSeconds2 = (endTime - op.startTime) / 1e3;
3655
3771
  if (op.type === "agent") {
3656
- const line = formatLLMCallLine({
3772
+ const line2 = formatLLMCallLine({
3657
3773
  iteration: op.iteration ?? 0,
3658
3774
  model: op.model ?? "",
3659
3775
  inputTokens: op.inputTokens,
@@ -3665,21 +3781,48 @@ var StreamProgress = class {
3665
3781
  isStreaming: !op.completed,
3666
3782
  spinner
3667
3783
  });
3668
- lines.push(`${indent}${line}`);
3784
+ lines.push(`${indent}${line2}`);
3669
3785
  } else {
3670
- const line = formatGadgetLine({
3671
- name: op.name ?? "",
3672
- parameters: op.parameters,
3673
- elapsedSeconds: elapsedSeconds2,
3674
- isComplete: op.completed ?? false
3675
- });
3676
- lines.push(`${indent}${line}`);
3786
+ const termWidth2 = process.stdout.columns ?? 80;
3787
+ const line2 = formatGadgetLine(
3788
+ {
3789
+ name: op.name ?? "",
3790
+ parameters: op.parameters,
3791
+ elapsedSeconds: elapsedSeconds2,
3792
+ isComplete: op.completed ?? false
3793
+ },
3794
+ termWidth2 - indent.length
3795
+ );
3796
+ const indentedLine = line2.split("\n").map((l) => indent + l).join("\n");
3797
+ lines.push(indentedLine);
3677
3798
  }
3678
3799
  }
3679
3800
  }
3680
3801
  }
3681
- this.lastRenderLineCount = lines.length;
3682
- this.target.write("\r" + lines.join("\n"));
3802
+ for (const stream of activeNestedStreams) {
3803
+ const indent = " ".repeat(stream.depth + 2);
3804
+ const elapsedSeconds = (Date.now() - stream.startTime) / 1e3;
3805
+ const line = formatLLMCallLine({
3806
+ iteration: stream.iteration,
3807
+ model: stream.model,
3808
+ inputTokens: stream.inputTokens,
3809
+ cachedInputTokens: stream.cachedInputTokens,
3810
+ outputTokens: stream.outputTokens,
3811
+ elapsedSeconds,
3812
+ cost: stream.cost,
3813
+ isStreaming: true,
3814
+ spinner
3815
+ });
3816
+ lines.push(`${indent}${line}`);
3817
+ }
3818
+ if (this.mode === "streaming") {
3819
+ lines.push(this.formatStreamingLine(spinner));
3820
+ } else {
3821
+ lines.push(this.formatCumulativeLine(spinner));
3822
+ }
3823
+ const output = lines.join("\n");
3824
+ this.lastRenderLineCount = (output.match(/\n/g) || []).length + 1;
3825
+ this.target.write("\r" + output);
3683
3826
  this.hasRendered = true;
3684
3827
  }
3685
3828
  /**
@@ -4291,6 +4434,7 @@ async function executeAgent(promptArg, options, env) {
4291
4434
  env.stderr.write(`${summary}
4292
4435
  `);
4293
4436
  }
4437
+ env.stderr.write("\n");
4294
4438
  }
4295
4439
  if (llmSessionDir) {
4296
4440
  const filename = `${formatCallNumber(llmCallCounter)}.response`;
@@ -4397,8 +4541,7 @@ Denied: ${result.reason ?? "by user"}`
4397
4541
  builder.withTrailingMessage(
4398
4542
  (ctx) => [
4399
4543
  `[Iteration ${ctx.iteration + 1}/${ctx.maxIterations}]`,
4400
- "Think carefully: what gadget invocations can you make in parallel right now?",
4401
- "Maximize efficiency by batching independent operations in a single response."
4544
+ "Think carefully in two steps: 1. what gadget invocations we should be making next? 2. how do they depend on one another so we can run all of them in the right order? Then respond with all the gadget invocations you are able to do now."
4402
4545
  ].join(" ")
4403
4546
  );
4404
4547
  if (!options.quiet) {
@@ -4411,8 +4554,12 @@ Denied: ${result.reason ?? "by user"}`
4411
4554
  subagentEvent.gadgetInvocationId,
4412
4555
  subagentEvent.depth,
4413
4556
  info.model,
4414
- info.iteration,
4415
- info.inputTokens
4557
+ info.iteration + 1,
4558
+ // Make 1-indexed like main agent
4559
+ {
4560
+ inputTokens: info.usage?.inputTokens ?? info.inputTokens,
4561
+ cachedInputTokens: info.usage?.cachedInputTokens
4562
+ }
4416
4563
  );
4417
4564
  } else if (subagentEvent.type === "llm_call_end") {
4418
4565
  const info = subagentEvent.event;
@@ -4489,10 +4636,23 @@ Denied: ${result.reason ?? "by user"}`
4489
4636
  }
4490
4637
  } else {
4491
4638
  const tokenCount = await countGadgetOutputTokens(event.result.result);
4492
- env.stderr.write(
4493
- `${formatGadgetSummary2({ ...event.result, tokenCount, media: event.result.storedMedia })}
4494
- `
4639
+ const subagentMetrics = progress.getAggregatedSubagentMetrics(
4640
+ event.result.invocationId
4495
4641
  );
4642
+ const summary = formatGadgetSummary2({
4643
+ ...event.result,
4644
+ tokenCount,
4645
+ media: event.result.storedMedia,
4646
+ subagentMetrics: subagentMetrics.callCount > 0 ? subagentMetrics : void 0
4647
+ });
4648
+ if (event.result.gadgetName === "TellUser") {
4649
+ env.stderr.write(`${summary}
4650
+ `);
4651
+ } else {
4652
+ const indentedSummary = summary.split("\n").map((line) => " " + line).join("\n");
4653
+ env.stderr.write(`${indentedSummary}
4654
+ `);
4655
+ }
4496
4656
  }
4497
4657
  if (progress.hasInFlightGadgets()) {
4498
4658
  progress.start();