llmist 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-UBPZUVIN.js";
2
+ import "./chunk-F62X5W2G.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-EIE5VRSI.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",
@@ -2941,7 +2941,8 @@ function formatCost(cost) {
2941
2941
  }
2942
2942
  function formatLLMCallLine(info) {
2943
2943
  const parts = [];
2944
- parts.push(`${chalk3.cyan(`#${info.iteration}`)} ${chalk3.magenta(info.model)}`);
2944
+ const callNumber = info.parentCallNumber ? `#${info.parentCallNumber}.${info.iteration}` : `#${info.iteration}`;
2945
+ parts.push(`${chalk3.cyan(callNumber)} ${chalk3.magenta(info.model)}`);
2945
2946
  if (info.contextPercent !== void 0 && info.contextPercent !== null) {
2946
2947
  const formatted = `${Math.round(info.contextPercent)}%`;
2947
2948
  if (info.contextPercent >= 80) {
@@ -3046,7 +3047,7 @@ function getRawValue(value) {
3046
3047
  function truncateValue(str, maxLen) {
3047
3048
  if (maxLen <= 0) return "";
3048
3049
  if (str.length <= maxLen) return str;
3049
- return `${str.slice(0, maxLen)}\u2026`;
3050
+ return `${str.slice(0, maxLen - 1)}\u2026`;
3050
3051
  }
3051
3052
  function formatParametersInline(params, maxWidth) {
3052
3053
  if (!params || Object.keys(params).length === 0) {
@@ -3074,6 +3075,11 @@ function formatParametersInline(params, maxWidth) {
3074
3075
  const proportion = v.length / totalRawLength;
3075
3076
  return Math.max(minPerValue, Math.floor(proportion * availableForValues));
3076
3077
  });
3078
+ const totalLimits = limits.reduce((sum, l) => sum + l, 0);
3079
+ if (totalLimits > availableForValues) {
3080
+ const scale = availableForValues / totalLimits;
3081
+ limits = limits.map((l) => Math.max(1, Math.floor(l * scale)));
3082
+ }
3077
3083
  }
3078
3084
  }
3079
3085
  } else {
@@ -3089,8 +3095,8 @@ function formatGadgetLine(info, maxWidth) {
3089
3095
  const gadgetLabel = chalk3.magenta.bold(info.name);
3090
3096
  const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
3091
3097
  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);
3098
+ const fixedLength = 3 + info.name.length + 2 + 1 + timeStr.length;
3099
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
3094
3100
  const paramsStr = formatParametersInline(info.parameters, availableForParams);
3095
3101
  const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
3096
3102
  if (info.error) {
@@ -3100,17 +3106,21 @@ function formatGadgetLine(info, maxWidth) {
3100
3106
  if (!info.isComplete) {
3101
3107
  return `${chalk3.blue("\u23F5")} ${gadgetLabel}${paramsLabel} ${timeLabel}`;
3102
3108
  }
3103
- let outputStr;
3109
+ let outputLabel;
3104
3110
  if (info.tokenCount !== void 0 && info.tokenCount > 0) {
3105
- outputStr = `${formatTokens(info.tokenCount)} tokens`;
3111
+ outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(info.tokenCount)} `);
3106
3112
  } else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
3107
- outputStr = formatBytes(info.outputBytes);
3113
+ outputLabel = chalk3.green(formatBytes(info.outputBytes)) + " ";
3108
3114
  } else {
3109
- outputStr = "";
3115
+ outputLabel = "";
3110
3116
  }
3111
3117
  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}`;
3118
+ const nameRef = chalk3.magenta(info.name);
3119
+ const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
3120
+ const line2Prefix = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}`;
3121
+ const line2 = `${line2Prefix}${timeLabel}`;
3122
+ return `${line1}
3123
+ ${line2}`;
3114
3124
  }
3115
3125
  function formatBytes(bytes) {
3116
3126
  if (bytes < 1024) {
@@ -3121,6 +3131,11 @@ function formatBytes(bytes) {
3121
3131
  }
3122
3132
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3123
3133
  }
3134
+ function truncateOutputPreview(output, maxWidth) {
3135
+ const normalized = output.replace(/\s+/g, " ").trim();
3136
+ if (normalized.length <= maxWidth) return normalized;
3137
+ return normalized.slice(0, maxWidth - 1) + "\u2026";
3138
+ }
3124
3139
  function getMediaIcon(kind) {
3125
3140
  switch (kind) {
3126
3141
  case "image":
@@ -3148,37 +3163,99 @@ function formatGadgetSummary2(result) {
3148
3163
  const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
3149
3164
  const timeStr = result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`;
3150
3165
  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) {
3166
+ const fixedLength = 3 + result.gadgetName.length + 2;
3167
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
3168
+ const paramsStr = formatParametersInline(result.parameters, availableForParams);
3169
+ const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
3170
+ const icon = result.breaksLoop ? chalk3.yellow("\u23F9") : result.error ? chalk3.red("\u2717") : chalk3.green("\u2713");
3171
+ const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
3172
+ const nameRef = chalk3.magenta(result.gadgetName);
3173
+ const hasSubagentMetrics = result.subagentMetrics && result.subagentMetrics.callCount > 0;
3174
+ let outputLabel;
3175
+ let outputStrRaw;
3176
+ if (!hasSubagentMetrics && result.tokenCount !== void 0 && result.tokenCount > 0) {
3177
+ const tokenStr = formatTokens(result.tokenCount);
3178
+ outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${tokenStr} `);
3179
+ outputStrRaw = `\u2193 ${tokenStr} `;
3180
+ } else if (!hasSubagentMetrics && result.result) {
3155
3181
  const outputBytes = Buffer.byteLength(result.result, "utf-8");
3156
- outputStr = outputBytes > 0 ? formatBytes(outputBytes) : "no output";
3182
+ if (outputBytes > 0) {
3183
+ const bytesStr = formatBytes(outputBytes);
3184
+ outputLabel = chalk3.green(bytesStr) + " ";
3185
+ outputStrRaw = bytesStr + " ";
3186
+ } else {
3187
+ outputLabel = "";
3188
+ outputStrRaw = "";
3189
+ }
3157
3190
  } else {
3158
- outputStr = "no output";
3191
+ outputLabel = "";
3192
+ outputStrRaw = "";
3159
3193
  }
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
3194
  if (result.error) {
3165
3195
  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}`;
3196
+ const line22 = ` ${chalk3.dim("\u2192")} ${nameRef} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
3197
+ return `${line1}
3198
+ ${line22}`;
3199
+ }
3200
+ const previewWidth = Math.floor(terminalWidth * 0.6);
3201
+ const prefixLength = 4 + result.gadgetName.length + 1 + outputStrRaw.length + 1 + timeStr.length + 2;
3202
+ const availablePreview = Math.max(20, previewWidth - prefixLength);
3203
+ let customPreview;
3204
+ if (result.gadgetName === "TodoUpsert" && result.parameters?.content) {
3205
+ const statusEmoji = result.parameters.status === "done" ? "\u2705" : result.parameters.status === "in_progress" ? "\u{1F504}" : "\u2B1C";
3206
+ const content = String(result.parameters.content);
3207
+ customPreview = `${statusEmoji} ${truncateOutputPreview(content, availablePreview - 3)}`;
3208
+ }
3209
+ if (result.gadgetName === "GoogleSearch" && result.parameters?.query) {
3210
+ const query = String(result.parameters.query);
3211
+ const countMatch = result.result?.match(/\((\d+)\s+of\s+[\d,]+\s+results?\)/i) || // "(10 of 36400000 results)"
3212
+ result.result?.match(/(\d+)\s+results?\s+found/i) || // "10 results found"
3213
+ result.result?.match(/found\s+(\d+)\s+results?/i);
3214
+ const count = countMatch?.[1] ?? (result.parameters.maxResults ? String(result.parameters.maxResults) : null);
3215
+ const countStr = count ? ` \u2192 ${count} results` : "";
3216
+ const queryPreview = truncateOutputPreview(query, availablePreview - 5 - countStr.length);
3217
+ customPreview = `\u{1F50D} "${queryPreview}"${countStr}`;
3218
+ }
3219
+ let subagentMetricsStr = "";
3220
+ if (result.subagentMetrics && result.subagentMetrics.callCount > 0) {
3221
+ const parts = [];
3222
+ const m = result.subagentMetrics;
3223
+ if (m.inputTokens > 0) {
3224
+ parts.push(chalk3.dim("\u2191") + chalk3.yellow(` ${formatTokens(m.inputTokens)}`));
3225
+ }
3226
+ if (m.cachedInputTokens > 0) {
3227
+ parts.push(chalk3.dim("\u27F3") + chalk3.blue(` ${formatTokens(m.cachedInputTokens)}`));
3228
+ }
3229
+ if (m.outputTokens > 0) {
3230
+ parts.push(chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(m.outputTokens)}`));
3231
+ }
3232
+ if (m.cost > 0) {
3233
+ parts.push(chalk3.cyan(`$${formatCost(m.cost)}`));
3234
+ }
3235
+ if (parts.length > 0) {
3236
+ subagentMetricsStr = parts.join(chalk3.dim(" | ")) + chalk3.dim(" | ");
3237
+ }
3238
+ }
3239
+ let line2;
3240
+ const previewContent = customPreview ?? (result.result?.trim() ? truncateOutputPreview(result.result, availablePreview) : null);
3241
+ if (previewContent) {
3242
+ line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}${chalk3.dim(":")} ${chalk3.dim(previewContent)}`;
3243
+ } else {
3244
+ line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}`;
3167
3245
  }
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}`;
3246
+ let output = `${line1}
3247
+ ${line2}`;
3171
3248
  if (result.media && result.media.length > 0) {
3172
3249
  const mediaLines = result.media.map(formatMediaLine);
3173
- summaryLine += "\n" + mediaLines.join("\n");
3250
+ output += "\n" + mediaLines.join("\n");
3174
3251
  }
3175
3252
  if (result.gadgetName === "TellUser" && result.parameters?.message) {
3176
3253
  const message = String(result.parameters.message);
3177
3254
  const rendered = renderMarkdownWithSeparators(message);
3178
- return `${summaryLine}
3255
+ return `${output}
3179
3256
  ${rendered}`;
3180
3257
  }
3181
- return summaryLine;
3258
+ return output;
3182
3259
  }
3183
3260
 
3184
3261
  // src/cli/utils.ts
@@ -3371,18 +3448,60 @@ var StreamProgress = class {
3371
3448
  hasInFlightGadgets() {
3372
3449
  return this.inFlightGadgets.size > 0;
3373
3450
  }
3451
+ /**
3452
+ * Mark a gadget as completed (keeps it visible with ✓ indicator).
3453
+ * Records completion time to freeze the elapsed timer.
3454
+ * The gadget and its nested operations remain visible until clearCompletedGadgets() is called.
3455
+ */
3456
+ completeGadget(invocationId) {
3457
+ const gadget = this.inFlightGadgets.get(invocationId);
3458
+ if (gadget) {
3459
+ gadget.completed = true;
3460
+ gadget.completedTime = Date.now();
3461
+ if (this.isRunning && this.isTTY) {
3462
+ this.render();
3463
+ }
3464
+ }
3465
+ }
3466
+ /**
3467
+ * Clear all completed gadgets from the display.
3468
+ * Called when new text output arrives to clean up the finished gadget section.
3469
+ */
3470
+ clearCompletedGadgets() {
3471
+ for (const [id, gadget] of this.inFlightGadgets) {
3472
+ if (gadget.completed) {
3473
+ this.inFlightGadgets.delete(id);
3474
+ for (const [nestedId, nested] of this.nestedAgents) {
3475
+ if (nested.parentInvocationId === id) {
3476
+ this.nestedAgents.delete(nestedId);
3477
+ }
3478
+ }
3479
+ for (const [nestedId, nested] of this.nestedGadgets) {
3480
+ if (nested.parentInvocationId === id) {
3481
+ this.nestedGadgets.delete(nestedId);
3482
+ }
3483
+ }
3484
+ }
3485
+ }
3486
+ if (this.isRunning && this.isTTY) {
3487
+ this.render();
3488
+ }
3489
+ }
3374
3490
  /**
3375
3491
  * Add a nested agent LLM call (called when nested llm_call_start event received).
3376
3492
  * Used to display hierarchical progress for subagent gadgets.
3493
+ * @param parentCallNumber - Top-level call number for hierarchical display (e.g., #1.2)
3377
3494
  */
3378
- addNestedAgent(id, parentInvocationId, depth, model, iteration, inputTokens) {
3495
+ addNestedAgent(id, parentInvocationId, depth, model, iteration, info, parentCallNumber) {
3379
3496
  this.nestedAgents.set(id, {
3380
3497
  parentInvocationId,
3381
3498
  depth,
3382
3499
  model,
3383
3500
  iteration,
3501
+ parentCallNumber,
3384
3502
  startTime: Date.now(),
3385
- inputTokens
3503
+ inputTokens: info?.inputTokens,
3504
+ cachedInputTokens: info?.cachedInputTokens
3386
3505
  });
3387
3506
  if (this.isRunning && this.isTTY) {
3388
3507
  this.render();
@@ -3396,22 +3515,23 @@ var StreamProgress = class {
3396
3515
  updateNestedAgent(id, info) {
3397
3516
  const agent = this.nestedAgents.get(id);
3398
3517
  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;
3518
+ if (info.inputTokens !== void 0) agent.inputTokens = info.inputTokens;
3519
+ if (info.outputTokens !== void 0) agent.outputTokens = info.outputTokens;
3520
+ if (info.cachedInputTokens !== void 0) agent.cachedInputTokens = info.cachedInputTokens;
3521
+ if (info.cacheCreationInputTokens !== void 0)
3522
+ agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
3523
+ if (info.finishReason !== void 0) agent.finishReason = info.finishReason;
3404
3524
  if (info.cost !== void 0) {
3405
3525
  agent.cost = info.cost;
3406
- } else if (this.modelRegistry && agent.model && info.outputTokens) {
3526
+ } else if (this.modelRegistry && agent.model && agent.outputTokens) {
3407
3527
  try {
3408
3528
  const modelName = agent.model.includes(":") ? agent.model.split(":")[1] : agent.model;
3409
3529
  const costResult = this.modelRegistry.estimateCost(
3410
3530
  modelName,
3411
- info.inputTokens ?? 0,
3412
- info.outputTokens,
3413
- info.cachedInputTokens,
3414
- info.cacheCreationInputTokens
3531
+ agent.inputTokens ?? 0,
3532
+ agent.outputTokens,
3533
+ agent.cachedInputTokens,
3534
+ agent.cacheCreationInputTokens
3415
3535
  );
3416
3536
  agent.cost = costResult?.totalCost;
3417
3537
  } catch {
@@ -3433,6 +3553,27 @@ var StreamProgress = class {
3433
3553
  this.render();
3434
3554
  }
3435
3555
  }
3556
+ /**
3557
+ * Get aggregated metrics from all nested agents for a parent gadget.
3558
+ * Used to show total token counts and cost for subagent gadgets like BrowseWeb.
3559
+ */
3560
+ getAggregatedSubagentMetrics(parentInvocationId) {
3561
+ let inputTokens = 0;
3562
+ let outputTokens = 0;
3563
+ let cachedInputTokens = 0;
3564
+ let cost = 0;
3565
+ let callCount = 0;
3566
+ for (const [, nested] of this.nestedAgents) {
3567
+ if (nested.parentInvocationId === parentInvocationId) {
3568
+ inputTokens += nested.inputTokens ?? 0;
3569
+ outputTokens += nested.outputTokens ?? 0;
3570
+ cachedInputTokens += nested.cachedInputTokens ?? 0;
3571
+ cost += nested.cost ?? 0;
3572
+ callCount++;
3573
+ }
3574
+ }
3575
+ return { inputTokens, outputTokens, cachedInputTokens, cost, callCount };
3576
+ }
3436
3577
  /**
3437
3578
  * Add a nested gadget call (called when nested gadget_call event received).
3438
3579
  */
@@ -3600,20 +3741,23 @@ var StreamProgress = class {
3600
3741
  this.clearRenderedLines();
3601
3742
  const spinner = SPINNER_FRAMES[this.frameIndex++ % SPINNER_FRAMES.length];
3602
3743
  const lines = [];
3603
- if (this.mode === "streaming") {
3604
- lines.push(this.formatStreamingLine(spinner));
3605
- } else {
3606
- lines.push(this.formatCumulativeLine(spinner));
3607
- }
3744
+ const activeNestedStreams = [];
3608
3745
  if (this.isTTY) {
3609
3746
  for (const [gadgetId, gadget] of this.inFlightGadgets) {
3610
- 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
- })}`;
3747
+ const endTime = gadget.completedTime ?? Date.now();
3748
+ const elapsedSeconds = (endTime - gadget.startTime) / 1e3;
3749
+ const termWidth = process.stdout.columns ?? 80;
3750
+ const gadgetIndent = " ";
3751
+ const line = formatGadgetLine(
3752
+ {
3753
+ name: gadget.name,
3754
+ parameters: gadget.params,
3755
+ elapsedSeconds,
3756
+ isComplete: gadget.completed ?? false
3757
+ },
3758
+ termWidth - gadgetIndent.length
3759
+ );
3760
+ const gadgetLine = line.split("\n").map((l) => gadgetIndent + l).join("\n");
3617
3761
  lines.push(gadgetLine);
3618
3762
  const nestedOps = [];
3619
3763
  for (const [_agentId, nested] of this.nestedAgents) {
@@ -3623,6 +3767,7 @@ var StreamProgress = class {
3623
3767
  startTime: nested.startTime,
3624
3768
  depth: nested.depth,
3625
3769
  iteration: nested.iteration,
3770
+ parentCallNumber: nested.parentCallNumber,
3626
3771
  model: nested.model,
3627
3772
  inputTokens: nested.inputTokens,
3628
3773
  cachedInputTokens: nested.cachedInputTokens,
@@ -3632,6 +3777,19 @@ var StreamProgress = class {
3632
3777
  completed: nested.completed,
3633
3778
  completedTime: nested.completedTime
3634
3779
  });
3780
+ if (!nested.completed) {
3781
+ activeNestedStreams.push({
3782
+ depth: nested.depth,
3783
+ iteration: nested.iteration,
3784
+ parentCallNumber: nested.parentCallNumber,
3785
+ model: nested.model,
3786
+ inputTokens: nested.inputTokens,
3787
+ cachedInputTokens: nested.cachedInputTokens,
3788
+ outputTokens: nested.outputTokens,
3789
+ cost: nested.cost,
3790
+ startTime: nested.startTime
3791
+ });
3792
+ }
3635
3793
  }
3636
3794
  }
3637
3795
  for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
@@ -3649,12 +3807,16 @@ var StreamProgress = class {
3649
3807
  }
3650
3808
  nestedOps.sort((a, b) => a.startTime - b.startTime);
3651
3809
  for (const op of nestedOps) {
3652
- const indent = " ".repeat(op.depth + 1);
3653
- const endTime = op.completedTime ?? Date.now();
3654
- const elapsedSeconds2 = (endTime - op.startTime) / 1e3;
3810
+ if (op.type === "agent" && !op.completed) {
3811
+ continue;
3812
+ }
3813
+ const indent = " ".repeat(op.depth + 2);
3814
+ const endTime2 = op.completedTime ?? Date.now();
3815
+ const elapsedSeconds2 = (endTime2 - op.startTime) / 1e3;
3655
3816
  if (op.type === "agent") {
3656
- const line = formatLLMCallLine({
3817
+ const line2 = formatLLMCallLine({
3657
3818
  iteration: op.iteration ?? 0,
3819
+ parentCallNumber: op.parentCallNumber,
3658
3820
  model: op.model ?? "",
3659
3821
  inputTokens: op.inputTokens,
3660
3822
  cachedInputTokens: op.cachedInputTokens,
@@ -3665,21 +3827,49 @@ var StreamProgress = class {
3665
3827
  isStreaming: !op.completed,
3666
3828
  spinner
3667
3829
  });
3668
- lines.push(`${indent}${line}`);
3830
+ lines.push(`${indent}${line2}`);
3669
3831
  } 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}`);
3832
+ const termWidth2 = process.stdout.columns ?? 80;
3833
+ const line2 = formatGadgetLine(
3834
+ {
3835
+ name: op.name ?? "",
3836
+ parameters: op.parameters,
3837
+ elapsedSeconds: elapsedSeconds2,
3838
+ isComplete: op.completed ?? false
3839
+ },
3840
+ termWidth2 - indent.length
3841
+ );
3842
+ const indentedLine = line2.split("\n").map((l) => indent + l).join("\n");
3843
+ lines.push(indentedLine);
3677
3844
  }
3678
3845
  }
3679
3846
  }
3680
3847
  }
3681
- this.lastRenderLineCount = lines.length;
3682
- this.target.write("\r" + lines.join("\n"));
3848
+ for (const stream of activeNestedStreams) {
3849
+ const indent = " ".repeat(stream.depth + 2);
3850
+ const elapsedSeconds = (Date.now() - stream.startTime) / 1e3;
3851
+ const line = formatLLMCallLine({
3852
+ iteration: stream.iteration,
3853
+ parentCallNumber: stream.parentCallNumber,
3854
+ model: stream.model,
3855
+ inputTokens: stream.inputTokens,
3856
+ cachedInputTokens: stream.cachedInputTokens,
3857
+ outputTokens: stream.outputTokens,
3858
+ elapsedSeconds,
3859
+ cost: stream.cost,
3860
+ isStreaming: true,
3861
+ spinner
3862
+ });
3863
+ lines.push(`${indent}${line}`);
3864
+ }
3865
+ if (this.mode === "streaming") {
3866
+ lines.push(this.formatStreamingLine(spinner));
3867
+ } else {
3868
+ lines.push(this.formatCumulativeLine(spinner));
3869
+ }
3870
+ const output = lines.join("\n");
3871
+ this.lastRenderLineCount = (output.match(/\n/g) || []).length + 1;
3872
+ this.target.write("\r" + output);
3683
3873
  this.hasRendered = true;
3684
3874
  }
3685
3875
  /**
@@ -4291,6 +4481,7 @@ async function executeAgent(promptArg, options, env) {
4291
4481
  env.stderr.write(`${summary}
4292
4482
  `);
4293
4483
  }
4484
+ env.stderr.write("\n");
4294
4485
  }
4295
4486
  if (llmSessionDir) {
4296
4487
  const filename = `${formatCallNumber(llmCallCounter)}.response`;
@@ -4397,8 +4588,7 @@ Denied: ${result.reason ?? "by user"}`
4397
4588
  builder.withTrailingMessage(
4398
4589
  (ctx) => [
4399
4590
  `[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."
4591
+ "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
4592
  ].join(" ")
4403
4593
  );
4404
4594
  if (!options.quiet) {
@@ -4411,8 +4601,14 @@ Denied: ${result.reason ?? "by user"}`
4411
4601
  subagentEvent.gadgetInvocationId,
4412
4602
  subagentEvent.depth,
4413
4603
  info.model,
4414
- info.iteration,
4415
- info.inputTokens
4604
+ info.iteration + 1,
4605
+ // Make 1-indexed like main agent
4606
+ {
4607
+ inputTokens: info.usage?.inputTokens ?? info.inputTokens,
4608
+ cachedInputTokens: info.usage?.cachedInputTokens
4609
+ },
4610
+ llmCallCounter
4611
+ // Parent call number for hierarchical display (e.g., #1.2)
4416
4612
  );
4417
4613
  } else if (subagentEvent.type === "llm_call_end") {
4418
4614
  const info = subagentEvent.event;
@@ -4456,6 +4652,9 @@ Denied: ${result.reason ?? "by user"}`
4456
4652
  let textBuffer = "";
4457
4653
  const flushTextBuffer = () => {
4458
4654
  if (textBuffer) {
4655
+ if (!options.quiet) {
4656
+ progress.clearCompletedGadgets();
4657
+ }
4459
4658
  const output = options.quiet ? textBuffer : renderMarkdownWithSeparators(textBuffer);
4460
4659
  printer.write(output);
4461
4660
  textBuffer = "";
@@ -4478,7 +4677,7 @@ Denied: ${result.reason ?? "by user"}`
4478
4677
  } else if (event.type === "gadget_result") {
4479
4678
  flushTextBuffer();
4480
4679
  if (!options.quiet) {
4481
- progress.removeGadget(event.result.invocationId);
4680
+ progress.completeGadget(event.result.invocationId);
4482
4681
  }
4483
4682
  progress.pause();
4484
4683
  if (options.quiet) {
@@ -4489,10 +4688,23 @@ Denied: ${result.reason ?? "by user"}`
4489
4688
  }
4490
4689
  } else {
4491
4690
  const tokenCount = await countGadgetOutputTokens(event.result.result);
4492
- env.stderr.write(
4493
- `${formatGadgetSummary2({ ...event.result, tokenCount, media: event.result.storedMedia })}
4494
- `
4691
+ const subagentMetrics = progress.getAggregatedSubagentMetrics(
4692
+ event.result.invocationId
4495
4693
  );
4694
+ const summary = formatGadgetSummary2({
4695
+ ...event.result,
4696
+ tokenCount,
4697
+ media: event.result.storedMedia,
4698
+ subagentMetrics: subagentMetrics.callCount > 0 ? subagentMetrics : void 0
4699
+ });
4700
+ if (event.result.gadgetName === "TellUser") {
4701
+ env.stderr.write(`${summary}
4702
+ `);
4703
+ } else {
4704
+ const indentedSummary = summary.split("\n").map((line) => " " + line).join("\n");
4705
+ env.stderr.write(`${indentedSummary}
4706
+ `);
4707
+ }
4496
4708
  }
4497
4709
  if (progress.hasInFlightGadgets()) {
4498
4710
  progress.start();