llmist 4.0.0 → 5.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.cjs CHANGED
@@ -4952,15 +4952,16 @@ var init_agent = __esm({
4952
4952
  });
4953
4953
  } else if (event.type === "llm_call_end") {
4954
4954
  const info = event.event;
4955
+ const usage = info.usage ?? (info.outputTokens ? {
4956
+ inputTokens: info.inputTokens ?? 0,
4957
+ outputTokens: info.outputTokens,
4958
+ totalTokens: (info.inputTokens ?? 0) + info.outputTokens
4959
+ } : void 0);
4955
4960
  void this.hooks?.observers?.onLLMCallComplete?.({
4956
4961
  iteration: info.iteration,
4957
4962
  options: { model: info.model, messages: [] },
4958
4963
  finishReason: info.finishReason ?? null,
4959
- usage: info.outputTokens ? {
4960
- inputTokens: info.inputTokens ?? 0,
4961
- outputTokens: info.outputTokens,
4962
- totalTokens: (info.inputTokens ?? 0) + info.outputTokens
4963
- } : void 0,
4964
+ usage,
4964
4965
  rawResponse: "",
4965
4966
  finalMessage: "",
4966
4967
  logger: this.logger,
@@ -7003,6 +7004,9 @@ var init_gemini = __esm({
7003
7004
  async countTokens(messages, descriptor, _spec) {
7004
7005
  const client = this.client;
7005
7006
  const contents = this.convertMessagesToContents(messages);
7007
+ if (!contents || contents.length === 0) {
7008
+ return 0;
7009
+ }
7006
7010
  try {
7007
7011
  const response = await client.models.countTokens({
7008
7012
  model: descriptor.name,
@@ -9496,8 +9500,13 @@ ${endPrefix}`
9496
9500
  event: {
9497
9501
  iteration: context.iteration,
9498
9502
  model: context.options.model,
9503
+ // Backward compat fields
9504
+ inputTokens: context.usage?.inputTokens,
9499
9505
  outputTokens: context.usage?.outputTokens,
9500
- finishReason: context.finishReason
9506
+ finishReason: context.finishReason ?? void 0,
9507
+ // Full usage object with cache details (for first-class display)
9508
+ usage: context.usage
9509
+ // Cost will be calculated by parent if it has model registry
9501
9510
  }
9502
9511
  });
9503
9512
  if (existingOnLLMCallComplete) {
@@ -9896,7 +9905,6 @@ var OPTION_FLAGS = {
9896
9905
  docker: "--docker",
9897
9906
  dockerRo: "--docker-ro",
9898
9907
  noDocker: "--no-docker",
9899
- dockerDev: "--docker-dev",
9900
9908
  // Multimodal input options
9901
9909
  inputImage: "--image <path>",
9902
9910
  inputAudio: "--audio <path>",
@@ -9931,7 +9939,6 @@ var OPTION_DESCRIPTIONS = {
9931
9939
  docker: "Run agent in a Docker sandbox container for security isolation.",
9932
9940
  dockerRo: "Run in Docker with current directory mounted read-only.",
9933
9941
  noDocker: "Disable Docker sandboxing (override config).",
9934
- dockerDev: "Run in Docker dev mode (mount local source instead of npm install).",
9935
9942
  // Image generation descriptions
9936
9943
  imageSize: "Image size/aspect ratio, e.g. '1024x1024', '1:1', '16:9'.",
9937
9944
  imageQuality: "Image quality: 'standard', 'hd', 'low', 'medium', 'high'.",
@@ -9951,7 +9958,7 @@ var import_commander2 = require("commander");
9951
9958
  // package.json
9952
9959
  var package_default = {
9953
9960
  name: "llmist",
9954
- version: "3.1.0",
9961
+ version: "5.0.0",
9955
9962
  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.",
9956
9963
  type: "module",
9957
9964
  main: "dist/index.cjs",
@@ -9994,7 +10001,8 @@ var package_default = {
9994
10001
  "test:all": "bun run test && bun run test:e2e",
9995
10002
  clean: "rimraf dist",
9996
10003
  prepare: "node scripts/install-hooks.js || true",
9997
- "release:dry": "bunx semantic-release --dry-run"
10004
+ "release:dry": "bunx semantic-release --dry-run",
10005
+ "release:publish": 'test "$(git branch --show-current)" = "main" && git pull origin main && bun run build && npm publish'
9998
10006
  },
9999
10007
  bin: {
10000
10008
  llmist: "dist/cli.js"
@@ -11353,20 +11361,17 @@ var DOCKER_CONFIG_KEYS = /* @__PURE__ */ new Set([
11353
11361
  "mounts",
11354
11362
  "env-vars",
11355
11363
  "image-name",
11356
- "dev-mode",
11357
- "dev-source",
11358
11364
  "docker-args"
11359
11365
  ]);
11360
11366
  var DEFAULT_IMAGE_NAME = "llmist-sandbox";
11361
11367
  var DEFAULT_CWD_PERMISSION = "rw";
11362
11368
  var DEFAULT_CONFIG_PERMISSION = "ro";
11369
+ var GADGET_CACHE_VOLUME = "llmist-gadget-cache";
11363
11370
  var FORWARDED_API_KEYS = [
11364
11371
  "ANTHROPIC_API_KEY",
11365
11372
  "OPENAI_API_KEY",
11366
11373
  "GEMINI_API_KEY"
11367
11374
  ];
11368
- var DEV_IMAGE_NAME = "llmist-dev-sandbox";
11369
- var DEV_SOURCE_MOUNT_TARGET = "/llmist-src";
11370
11375
 
11371
11376
  // src/cli/docker/docker-config.ts
11372
11377
  var MOUNT_CONFIG_KEYS = /* @__PURE__ */ new Set(["source", "target", "permission"]);
@@ -11478,12 +11483,6 @@ function validateDockerConfig(raw, section) {
11478
11483
  if ("image-name" in rawObj) {
11479
11484
  result["image-name"] = validateString2(rawObj["image-name"], "image-name", section);
11480
11485
  }
11481
- if ("dev-mode" in rawObj) {
11482
- result["dev-mode"] = validateBoolean2(rawObj["dev-mode"], "dev-mode", section);
11483
- }
11484
- if ("dev-source" in rawObj) {
11485
- result["dev-source"] = validateString2(rawObj["dev-source"], "dev-source", section);
11486
- }
11487
11486
  if ("docker-args" in rawObj) {
11488
11487
  result["docker-args"] = validateStringArray2(rawObj["docker-args"], "docker-args", section);
11489
11488
  }
@@ -11493,7 +11492,6 @@ function validateDockerConfig(raw, section) {
11493
11492
  // src/cli/docker/docker-wrapper.ts
11494
11493
  var import_node_fs5 = require("fs");
11495
11494
  var import_node_os4 = require("os");
11496
- var import_node_path6 = require("path");
11497
11495
 
11498
11496
  // src/cli/docker/dockerfile.ts
11499
11497
  var DEFAULT_DOCKERFILE = `# llmist sandbox image
@@ -11513,6 +11511,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \\
11513
11511
  curl \\
11514
11512
  # ca-certificates for HTTPS
11515
11513
  ca-certificates \\
11514
+ # python3 for native module compilation (node-gyp)
11515
+ python3 \\
11516
+ # build-essential for compiling native modules
11517
+ build-essential \\
11516
11518
  && rm -rf /var/lib/apt/lists/*
11517
11519
 
11518
11520
  # Install ast-grep for code search/refactoring
@@ -11530,37 +11532,8 @@ WORKDIR /workspace
11530
11532
  # Entry point - llmist with all arguments forwarded
11531
11533
  ENTRYPOINT ["llmist"]
11532
11534
  `;
11533
- var DEV_DOCKERFILE = `# llmist DEV sandbox image
11534
- # For development/testing with local source code
11535
-
11536
- FROM oven/bun:1-debian
11537
-
11538
- # Install essential tools (same as production)
11539
- RUN apt-get update && apt-get install -y --no-install-recommends \\
11540
- ed \\
11541
- ripgrep \\
11542
- git \\
11543
- curl \\
11544
- ca-certificates \\
11545
- && rm -rf /var/lib/apt/lists/*
11546
-
11547
- # Install ast-grep for code search/refactoring
11548
- RUN curl -fsSL https://raw.githubusercontent.com/ast-grep/ast-grep/main/install.sh | bash \\
11549
- && mv /root/.local/bin/ast-grep /usr/local/bin/ 2>/dev/null || true \\
11550
- && mv /root/.local/bin/sg /usr/local/bin/ 2>/dev/null || true
11551
-
11552
- # Working directory (host CWD will be mounted here)
11553
- WORKDIR /workspace
11554
-
11555
- # Entry point - run llmist from mounted source
11556
- # Source is mounted at ${DEV_SOURCE_MOUNT_TARGET}
11557
- ENTRYPOINT ["bun", "run", "${DEV_SOURCE_MOUNT_TARGET}/src/cli.ts"]
11558
- `;
11559
- function resolveDockerfile(config, devMode = false) {
11560
- if (config.dockerfile) {
11561
- return config.dockerfile;
11562
- }
11563
- return devMode ? DEV_DOCKERFILE : DEFAULT_DOCKERFILE;
11535
+ function resolveDockerfile(config) {
11536
+ return config.dockerfile ?? DEFAULT_DOCKERFILE;
11564
11537
  }
11565
11538
  function computeDockerfileHash(dockerfile) {
11566
11539
  const encoder = new TextEncoder();
@@ -11622,10 +11595,13 @@ async function buildImage(imageName, dockerfile) {
11622
11595
  ensureCacheDir();
11623
11596
  const dockerfilePath = (0, import_node_path5.join)(CACHE_DIR, "Dockerfile");
11624
11597
  (0, import_node_fs4.writeFileSync)(dockerfilePath, dockerfile);
11625
- const proc = Bun.spawn(["docker", "build", "-t", imageName, "-f", dockerfilePath, CACHE_DIR], {
11626
- stdout: "pipe",
11627
- stderr: "pipe"
11628
- });
11598
+ const proc = Bun.spawn(
11599
+ ["docker", "build", "--no-cache", "-t", imageName, "-f", dockerfilePath, CACHE_DIR],
11600
+ {
11601
+ stdout: "pipe",
11602
+ stderr: "pipe"
11603
+ }
11604
+ );
11629
11605
  const exitCode = await proc.exited;
11630
11606
  const stdout = await new Response(proc.stdout).text();
11631
11607
  const stderr = await new Response(proc.stderr).text();
@@ -11687,46 +11663,13 @@ function isInsideContainer() {
11687
11663
  }
11688
11664
  return false;
11689
11665
  }
11690
- function autoDetectDevSource() {
11691
- const scriptPath = process.argv[1];
11692
- if (!scriptPath || !scriptPath.endsWith("src/cli.ts")) {
11693
- return void 0;
11694
- }
11695
- const srcDir = (0, import_node_path6.dirname)(scriptPath);
11696
- const projectDir = (0, import_node_path6.dirname)(srcDir);
11697
- const packageJsonPath = (0, import_node_path6.join)(projectDir, "package.json");
11698
- if (!(0, import_node_fs5.existsSync)(packageJsonPath)) {
11699
- return void 0;
11700
- }
11701
- try {
11702
- const pkg = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8"));
11703
- if (pkg.name === "llmist") {
11704
- return projectDir;
11705
- }
11706
- } catch {
11707
- }
11708
- return void 0;
11709
- }
11710
- function resolveDevMode(config, cliDevMode) {
11711
- const enabled = cliDevMode || config?.["dev-mode"] || process.env.LLMIST_DEV_MODE === "1";
11712
- if (!enabled) {
11713
- return { enabled: false, sourcePath: void 0 };
11714
- }
11715
- const sourcePath = config?.["dev-source"] || process.env.LLMIST_DEV_SOURCE || autoDetectDevSource();
11716
- if (!sourcePath) {
11717
- throw new Error(
11718
- "Docker dev mode enabled but llmist source path not found. Set [docker].dev-source in config, LLMIST_DEV_SOURCE env var, or run from the llmist source directory (bun src/cli.ts)."
11719
- );
11720
- }
11721
- return { enabled: true, sourcePath };
11722
- }
11723
11666
  function expandHome(path6) {
11724
11667
  if (path6.startsWith("~")) {
11725
11668
  return path6.replace(/^~/, (0, import_node_os4.homedir)());
11726
11669
  }
11727
11670
  return path6;
11728
11671
  }
11729
- function buildDockerRunArgs(ctx, imageName, devMode) {
11672
+ function buildDockerRunArgs(ctx, imageName) {
11730
11673
  const args = ["run", "--rm"];
11731
11674
  const timestamp = Date.now();
11732
11675
  const random = Math.random().toString(36).slice(2, 8);
@@ -11740,11 +11683,15 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
11740
11683
  args.push("-w", "/workspace");
11741
11684
  const configPermission = ctx.config["config-permission"] ?? DEFAULT_CONFIG_PERMISSION;
11742
11685
  const llmistDir = expandHome("~/.llmist");
11743
- args.push("-v", `${llmistDir}:/root/.llmist:${configPermission}`);
11744
- if (devMode.enabled && devMode.sourcePath) {
11745
- const expandedSource = expandHome(devMode.sourcePath);
11746
- args.push("-v", `${expandedSource}:${DEV_SOURCE_MOUNT_TARGET}:ro`);
11686
+ const cliTomlPath = `${llmistDir}/cli.toml`;
11687
+ if ((0, import_node_fs5.existsSync)(cliTomlPath)) {
11688
+ args.push("-v", `${cliTomlPath}:/root/.llmist/cli.toml:${configPermission}`);
11689
+ }
11690
+ const gadgetsDir = `${llmistDir}/gadgets`;
11691
+ if ((0, import_node_fs5.existsSync)(gadgetsDir)) {
11692
+ args.push("-v", `${gadgetsDir}:/root/.llmist/gadgets:${configPermission}`);
11747
11693
  }
11694
+ args.push("-v", `${GADGET_CACHE_VOLUME}:/root/.llmist/gadget-cache`);
11748
11695
  if (ctx.config.mounts) {
11749
11696
  for (const mount of ctx.config.mounts) {
11750
11697
  const source = expandHome(mount.source);
@@ -11771,7 +11718,7 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
11771
11718
  return args;
11772
11719
  }
11773
11720
  function filterDockerArgs(argv) {
11774
- const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker", "--docker-dev"]);
11721
+ const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker"]);
11775
11722
  return argv.filter((arg) => !dockerFlags.has(arg));
11776
11723
  }
11777
11724
  function resolveDockerEnabled(config, options, profileDocker) {
@@ -11786,22 +11733,16 @@ function resolveDockerEnabled(config, options, profileDocker) {
11786
11733
  }
11787
11734
  return config?.enabled ?? false;
11788
11735
  }
11789
- async function executeInDocker(ctx, devMode) {
11736
+ async function executeInDocker(ctx) {
11790
11737
  if (isInsideContainer()) {
11791
- console.error(
11792
- "Warning: Docker mode requested but already inside a container. Proceeding without re-containerization."
11793
- );
11794
11738
  throw new DockerSkipError();
11795
11739
  }
11796
11740
  const available = await checkDockerAvailable();
11797
11741
  if (!available) {
11798
11742
  throw new DockerUnavailableError();
11799
11743
  }
11800
- const dockerfile = resolveDockerfile(ctx.config, devMode.enabled);
11801
- const imageName = devMode.enabled ? DEV_IMAGE_NAME : ctx.config["image-name"] ?? DEFAULT_IMAGE_NAME;
11802
- if (devMode.enabled) {
11803
- console.error(`[dev mode] Mounting source from ${devMode.sourcePath}`);
11804
- }
11744
+ const dockerfile = resolveDockerfile(ctx.config);
11745
+ const imageName = ctx.config["image-name"] ?? DEFAULT_IMAGE_NAME;
11805
11746
  try {
11806
11747
  await ensureImage(imageName, dockerfile);
11807
11748
  } catch (error) {
@@ -11812,7 +11753,7 @@ async function executeInDocker(ctx, devMode) {
11812
11753
  }
11813
11754
  throw error;
11814
11755
  }
11815
- const dockerArgs = buildDockerRunArgs(ctx, imageName, devMode);
11756
+ const dockerArgs = buildDockerRunArgs(ctx, imageName);
11816
11757
  const proc = Bun.spawn(["docker", ...dockerArgs], {
11817
11758
  stdin: "inherit",
11818
11759
  stdout: "inherit",
@@ -11833,7 +11774,7 @@ function createDockerContext(config, options, argv, cwd, profileCwdPermission) {
11833
11774
 
11834
11775
  // src/cli/file-utils.ts
11835
11776
  var import_promises3 = require("fs/promises");
11836
- var import_node_path7 = require("path");
11777
+ var import_node_path6 = require("path");
11837
11778
  init_input_content();
11838
11779
  var DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024;
11839
11780
  function formatFileSize(bytes) {
@@ -11851,7 +11792,7 @@ async function checkFileSize(absolutePath, filePath, maxSize) {
11851
11792
  }
11852
11793
  }
11853
11794
  async function readImageFile(filePath, options = {}) {
11854
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11795
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11855
11796
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11856
11797
  let buffer;
11857
11798
  try {
@@ -11870,7 +11811,7 @@ async function readImageFile(filePath, options = {}) {
11870
11811
  return imageFromBuffer(buffer, mimeType);
11871
11812
  }
11872
11813
  async function readAudioFile(filePath, options = {}) {
11873
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11814
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11874
11815
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11875
11816
  let buffer;
11876
11817
  try {
@@ -11889,7 +11830,7 @@ async function readAudioFile(filePath, options = {}) {
11889
11830
  return audioFromBuffer(buffer, mimeType);
11890
11831
  }
11891
11832
  async function readFileBuffer(filePath, options = {}) {
11892
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11833
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11893
11834
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11894
11835
  try {
11895
11836
  await checkFileSize(absolutePath, filePath, maxFileSize);
@@ -11902,7 +11843,7 @@ async function readFileBuffer(filePath, options = {}) {
11902
11843
 
11903
11844
  // src/cli/gadgets.ts
11904
11845
  var import_node_fs11 = __toESM(require("fs"), 1);
11905
- var import_node_path12 = __toESM(require("path"), 1);
11846
+ var import_node_path11 = __toESM(require("path"), 1);
11906
11847
  var import_node_url2 = require("url");
11907
11848
  init_gadget();
11908
11849
 
@@ -11982,7 +11923,7 @@ init_gadget();
11982
11923
 
11983
11924
  // src/cli/builtins/filesystem/utils.ts
11984
11925
  var import_node_fs6 = __toESM(require("fs"), 1);
11985
- var import_node_path8 = __toESM(require("path"), 1);
11926
+ var import_node_path7 = __toESM(require("path"), 1);
11986
11927
  var PathSandboxException = class extends Error {
11987
11928
  constructor(inputPath, reason) {
11988
11929
  super(`Path access denied: ${inputPath}. ${reason}`);
@@ -11991,7 +11932,7 @@ var PathSandboxException = class extends Error {
11991
11932
  };
11992
11933
  function validatePathIsWithinCwd(inputPath) {
11993
11934
  const cwd = process.cwd();
11994
- const resolvedPath = import_node_path8.default.resolve(cwd, inputPath);
11935
+ const resolvedPath = import_node_path7.default.resolve(cwd, inputPath);
11995
11936
  let finalPath;
11996
11937
  try {
11997
11938
  finalPath = import_node_fs6.default.realpathSync(resolvedPath);
@@ -12003,7 +11944,7 @@ function validatePathIsWithinCwd(inputPath) {
12003
11944
  throw error;
12004
11945
  }
12005
11946
  }
12006
- const cwdWithSep = cwd + import_node_path8.default.sep;
11947
+ const cwdWithSep = cwd + import_node_path7.default.sep;
12007
11948
  if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
12008
11949
  throw new PathSandboxException(inputPath, "Path is outside the current working directory");
12009
11950
  }
@@ -12106,15 +12047,15 @@ error: ${message}`;
12106
12047
 
12107
12048
  // src/cli/builtins/filesystem/list-directory.ts
12108
12049
  var import_node_fs7 = __toESM(require("fs"), 1);
12109
- var import_node_path9 = __toESM(require("path"), 1);
12050
+ var import_node_path8 = __toESM(require("path"), 1);
12110
12051
  var import_zod5 = require("zod");
12111
12052
  function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
12112
12053
  const entries = [];
12113
12054
  try {
12114
12055
  const items = import_node_fs7.default.readdirSync(dirPath);
12115
12056
  for (const item of items) {
12116
- const fullPath = import_node_path9.default.join(dirPath, item);
12117
- const relativePath = import_node_path9.default.relative(basePath, fullPath);
12057
+ const fullPath = import_node_path8.default.join(dirPath, item);
12058
+ const relativePath = import_node_path8.default.relative(basePath, fullPath);
12118
12059
  try {
12119
12060
  const stats = import_node_fs7.default.lstatSync(fullPath);
12120
12061
  let type;
@@ -12258,7 +12199,7 @@ ${content}`;
12258
12199
 
12259
12200
  // src/cli/builtins/filesystem/write-file.ts
12260
12201
  var import_node_fs9 = __toESM(require("fs"), 1);
12261
- var import_node_path10 = __toESM(require("path"), 1);
12202
+ var import_node_path9 = __toESM(require("path"), 1);
12262
12203
  var import_zod7 = require("zod");
12263
12204
  var writeFile2 = createGadget({
12264
12205
  name: "WriteFile",
@@ -12293,7 +12234,7 @@ console.log(\`Server running on http://localhost:\${port}\`);`
12293
12234
  ],
12294
12235
  execute: ({ filePath, content }) => {
12295
12236
  const validatedPath = validatePathIsWithinCwd(filePath);
12296
- const parentDir = import_node_path10.default.dirname(validatedPath);
12237
+ const parentDir = import_node_path9.default.dirname(validatedPath);
12297
12238
  let createdDir = false;
12298
12239
  if (!import_node_fs9.default.existsSync(parentDir)) {
12299
12240
  validatePathIsWithinCwd(parentDir);
@@ -12302,7 +12243,7 @@ console.log(\`Server running on http://localhost:\${port}\`);`
12302
12243
  }
12303
12244
  import_node_fs9.default.writeFileSync(validatedPath, content, "utf-8");
12304
12245
  const bytesWritten = Buffer.byteLength(content, "utf-8");
12305
- const dirNote = createdDir ? ` (created directory: ${import_node_path10.default.dirname(filePath)})` : "";
12246
+ const dirNote = createdDir ? ` (created directory: ${import_node_path9.default.dirname(filePath)})` : "";
12306
12247
  return `path=${filePath}
12307
12248
 
12308
12249
  Wrote ${bytesWritten} bytes${dirNote}`;
@@ -12431,10 +12372,10 @@ function isBuiltinGadgetName(name) {
12431
12372
  // src/cli/external-gadgets.ts
12432
12373
  var import_node_child_process = require("child_process");
12433
12374
  var import_node_fs10 = __toESM(require("fs"), 1);
12434
- var import_node_path11 = __toESM(require("path"), 1);
12375
+ var import_node_path10 = __toESM(require("path"), 1);
12435
12376
  var import_node_os5 = __toESM(require("os"), 1);
12436
12377
  var import_node_url = require("url");
12437
- var CACHE_DIR2 = import_node_path11.default.join(import_node_os5.default.homedir(), ".llmist", "gadget-cache");
12378
+ var CACHE_DIR2 = import_node_path10.default.join(import_node_os5.default.homedir(), ".llmist", "gadget-cache");
12438
12379
  function isExternalPackageSpecifier(specifier) {
12439
12380
  if (/^@?[a-z0-9][\w.-]*(?:@[\w.-]+)?(?::[a-z]+)?(?:\/\w+)?$/i.test(specifier)) {
12440
12381
  return true;
@@ -12472,13 +12413,13 @@ function parseGadgetSpecifier(specifier) {
12472
12413
  function getCacheDir(spec) {
12473
12414
  const versionSuffix = spec.version ? `@${spec.version}` : "@latest";
12474
12415
  if (spec.type === "npm") {
12475
- return import_node_path11.default.join(CACHE_DIR2, "npm", `${spec.package}${versionSuffix}`);
12416
+ return import_node_path10.default.join(CACHE_DIR2, "npm", `${spec.package}${versionSuffix}`);
12476
12417
  }
12477
12418
  const sanitizedUrl = spec.package.replace(/[/:]/g, "-").replace(/^-+|-+$/g, "");
12478
- return import_node_path11.default.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
12419
+ return import_node_path10.default.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
12479
12420
  }
12480
12421
  function isCached(cacheDir) {
12481
- const packageJsonPath = import_node_path11.default.join(cacheDir, "package.json");
12422
+ const packageJsonPath = import_node_path10.default.join(cacheDir, "package.json");
12482
12423
  return import_node_fs10.default.existsSync(packageJsonPath);
12483
12424
  }
12484
12425
  async function installNpmPackage(spec, cacheDir) {
@@ -12488,10 +12429,10 @@ async function installNpmPackage(spec, cacheDir) {
12488
12429
  private: true,
12489
12430
  type: "module"
12490
12431
  };
12491
- import_node_fs10.default.writeFileSync(import_node_path11.default.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
12432
+ import_node_fs10.default.writeFileSync(import_node_path10.default.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
12492
12433
  const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
12493
12434
  try {
12494
- (0, import_node_child_process.execSync)(`npm install --prefix "${cacheDir}" "${packageSpec}" --save`, {
12435
+ (0, import_node_child_process.execSync)(`bun add "${packageSpec}"`, {
12495
12436
  stdio: "pipe",
12496
12437
  cwd: cacheDir
12497
12438
  });
@@ -12501,7 +12442,7 @@ async function installNpmPackage(spec, cacheDir) {
12501
12442
  }
12502
12443
  }
12503
12444
  async function installGitPackage(spec, cacheDir) {
12504
- import_node_fs10.default.mkdirSync(import_node_path11.default.dirname(cacheDir), { recursive: true });
12445
+ import_node_fs10.default.mkdirSync(import_node_path10.default.dirname(cacheDir), { recursive: true });
12505
12446
  if (import_node_fs10.default.existsSync(cacheDir)) {
12506
12447
  try {
12507
12448
  (0, import_node_child_process.execSync)("git fetch", { cwd: cacheDir, stdio: "pipe" });
@@ -12520,17 +12461,17 @@ async function installGitPackage(spec, cacheDir) {
12520
12461
  const message = error instanceof Error ? error.message : String(error);
12521
12462
  throw new Error(`Failed to clone git repository '${spec.package}': ${message}`);
12522
12463
  }
12523
- if (import_node_fs10.default.existsSync(import_node_path11.default.join(cacheDir, "package.json"))) {
12464
+ if (import_node_fs10.default.existsSync(import_node_path10.default.join(cacheDir, "package.json"))) {
12524
12465
  try {
12525
- (0, import_node_child_process.execSync)("npm install", { cwd: cacheDir, stdio: "pipe" });
12466
+ (0, import_node_child_process.execSync)("bun install", { cwd: cacheDir, stdio: "inherit" });
12526
12467
  } catch (error) {
12527
12468
  const message = error instanceof Error ? error.message : String(error);
12528
12469
  throw new Error(`Failed to install dependencies for '${spec.package}': ${message}`);
12529
12470
  }
12530
12471
  try {
12531
- const packageJson = JSON.parse(import_node_fs10.default.readFileSync(import_node_path11.default.join(cacheDir, "package.json"), "utf-8"));
12472
+ const packageJson = JSON.parse(import_node_fs10.default.readFileSync(import_node_path10.default.join(cacheDir, "package.json"), "utf-8"));
12532
12473
  if (packageJson.scripts?.build) {
12533
- (0, import_node_child_process.execSync)("npm run build", { cwd: cacheDir, stdio: "pipe" });
12474
+ (0, import_node_child_process.execSync)("bun run build", { cwd: cacheDir, stdio: "inherit" });
12534
12475
  }
12535
12476
  } catch (error) {
12536
12477
  const message = error instanceof Error ? error.message : String(error);
@@ -12540,7 +12481,7 @@ async function installGitPackage(spec, cacheDir) {
12540
12481
  }
12541
12482
  }
12542
12483
  function readManifest(packageDir) {
12543
- const packageJsonPath = import_node_path11.default.join(packageDir, "package.json");
12484
+ const packageJsonPath = import_node_path10.default.join(packageDir, "package.json");
12544
12485
  if (!import_node_fs10.default.existsSync(packageJsonPath)) {
12545
12486
  return null;
12546
12487
  }
@@ -12552,7 +12493,7 @@ function readManifest(packageDir) {
12552
12493
  }
12553
12494
  }
12554
12495
  function getPackagePath(cacheDir, packageName) {
12555
- const nodeModulesPath = import_node_path11.default.join(cacheDir, "node_modules", packageName);
12496
+ const nodeModulesPath = import_node_path10.default.join(cacheDir, "node_modules", packageName);
12556
12497
  if (import_node_fs10.default.existsSync(nodeModulesPath)) {
12557
12498
  return nodeModulesPath;
12558
12499
  }
@@ -12596,7 +12537,7 @@ async function loadExternalGadgets(specifier, forceInstall = false) {
12596
12537
  } else {
12597
12538
  entryPoint = manifest?.gadgets || "./dist/index.js";
12598
12539
  }
12599
- const resolvedEntryPoint = import_node_path11.default.resolve(packagePath, entryPoint);
12540
+ const resolvedEntryPoint = import_node_path10.default.resolve(packagePath, entryPoint);
12600
12541
  if (!import_node_fs10.default.existsSync(resolvedEntryPoint)) {
12601
12542
  throw new Error(
12602
12543
  `Entry point not found: ${resolvedEntryPoint}. Make sure the package is built (run 'npm run build' in the package directory).`
@@ -12665,10 +12606,10 @@ function expandHomePath(input) {
12665
12606
  if (!home) {
12666
12607
  return input;
12667
12608
  }
12668
- return import_node_path12.default.join(home, input.slice(1));
12609
+ return import_node_path11.default.join(home, input.slice(1));
12669
12610
  }
12670
12611
  function isFileLikeSpecifier(specifier) {
12671
- return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path12.default.sep);
12612
+ return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path11.default.sep);
12672
12613
  }
12673
12614
  function tryResolveBuiltin(specifier) {
12674
12615
  if (specifier.startsWith(BUILTIN_PREFIX)) {
@@ -12691,7 +12632,7 @@ function resolveGadgetSpecifier(specifier, cwd) {
12691
12632
  return specifier;
12692
12633
  }
12693
12634
  const expanded = expandHomePath(specifier);
12694
- const resolvedPath = import_node_path12.default.resolve(cwd, expanded);
12635
+ const resolvedPath = import_node_path11.default.resolve(cwd, expanded);
12695
12636
  if (!import_node_fs11.default.existsSync(resolvedPath)) {
12696
12637
  throw new Error(`Gadget module not found at ${resolvedPath}`);
12697
12638
  }
@@ -12776,12 +12717,12 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
12776
12717
  // src/cli/llm-logging.ts
12777
12718
  var import_promises4 = require("fs/promises");
12778
12719
  var import_node_os6 = require("os");
12779
- var import_node_path13 = require("path");
12720
+ var import_node_path12 = require("path");
12780
12721
  init_messages();
12781
- var DEFAULT_LLM_LOG_DIR = (0, import_node_path13.join)((0, import_node_os6.homedir)(), ".llmist", "logs");
12722
+ var DEFAULT_LLM_LOG_DIR = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".llmist", "logs");
12782
12723
  function resolveLogDir(option, subdir) {
12783
12724
  if (option === true) {
12784
- return (0, import_node_path13.join)(DEFAULT_LLM_LOG_DIR, subdir);
12725
+ return (0, import_node_path12.join)(DEFAULT_LLM_LOG_DIR, subdir);
12785
12726
  }
12786
12727
  if (typeof option === "string") {
12787
12728
  return option;
@@ -12799,7 +12740,7 @@ function formatLlmRequest(messages) {
12799
12740
  }
12800
12741
  async function writeLogFile(dir, filename, content) {
12801
12742
  await (0, import_promises4.mkdir)(dir, { recursive: true });
12802
- await (0, import_promises4.writeFile)((0, import_node_path13.join)(dir, filename), content, "utf-8");
12743
+ await (0, import_promises4.writeFile)((0, import_node_path12.join)(dir, filename), content, "utf-8");
12803
12744
  }
12804
12745
  function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
12805
12746
  const pad = (n) => n.toString().padStart(2, "0");
@@ -12813,7 +12754,7 @@ function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
12813
12754
  }
12814
12755
  async function createSessionDir(baseDir) {
12815
12756
  const timestamp = formatSessionTimestamp();
12816
- const sessionDir = (0, import_node_path13.join)(baseDir, timestamp);
12757
+ const sessionDir = (0, import_node_path12.join)(baseDir, timestamp);
12817
12758
  try {
12818
12759
  await (0, import_promises4.mkdir)(sessionDir, { recursive: true });
12819
12760
  return sessionDir;
@@ -12906,6 +12847,45 @@ function formatCost(cost) {
12906
12847
  }
12907
12848
  return cost.toFixed(2);
12908
12849
  }
12850
+ function formatLLMCallLine(info) {
12851
+ const parts = [];
12852
+ parts.push(`${import_chalk3.default.cyan(`#${info.iteration}`)} ${import_chalk3.default.magenta(info.model)}`);
12853
+ if (info.contextPercent !== void 0 && info.contextPercent !== null) {
12854
+ const formatted = `${Math.round(info.contextPercent)}%`;
12855
+ if (info.contextPercent >= 80) {
12856
+ parts.push(import_chalk3.default.red(formatted));
12857
+ } else if (info.contextPercent >= 50) {
12858
+ parts.push(import_chalk3.default.yellow(formatted));
12859
+ } else {
12860
+ parts.push(import_chalk3.default.green(formatted));
12861
+ }
12862
+ }
12863
+ if (info.inputTokens && info.inputTokens > 0) {
12864
+ const prefix = info.estimated?.input ? "~" : "";
12865
+ parts.push(import_chalk3.default.dim("\u2191") + import_chalk3.default.yellow(` ${prefix}${formatTokens(info.inputTokens)}`));
12866
+ }
12867
+ if (info.cachedInputTokens && info.cachedInputTokens > 0) {
12868
+ parts.push(import_chalk3.default.dim("\u27F3") + import_chalk3.default.blue(` ${formatTokens(info.cachedInputTokens)}`));
12869
+ }
12870
+ if (info.outputTokens !== void 0 && info.outputTokens > 0 || info.isStreaming) {
12871
+ const prefix = info.estimated?.output ? "~" : "";
12872
+ parts.push(import_chalk3.default.dim("\u2193") + import_chalk3.default.green(` ${prefix}${formatTokens(info.outputTokens ?? 0)}`));
12873
+ }
12874
+ parts.push(import_chalk3.default.dim(`${info.elapsedSeconds.toFixed(1)}s`));
12875
+ if (info.cost !== void 0 && info.cost > 0) {
12876
+ parts.push(import_chalk3.default.cyan(`$${formatCost(info.cost)}`));
12877
+ }
12878
+ if (info.isStreaming && info.spinner) {
12879
+ parts.push(import_chalk3.default.cyan(info.spinner));
12880
+ } else if (info.finishReason !== void 0) {
12881
+ if (!info.finishReason || info.finishReason === "stop" || info.finishReason === "end_turn") {
12882
+ parts.push(import_chalk3.default.green("\u2713"));
12883
+ } else {
12884
+ parts.push(import_chalk3.default.yellow(info.finishReason));
12885
+ }
12886
+ }
12887
+ return parts.join(import_chalk3.default.dim(" | "));
12888
+ }
12909
12889
  function renderSummary(metadata) {
12910
12890
  const parts = [];
12911
12891
  if (metadata.iterations !== void 0) {
@@ -13012,6 +12992,34 @@ function formatParametersInline(params, maxWidth) {
13012
12992
  return `${import_chalk3.default.dim(key)}${import_chalk3.default.dim("=")}${import_chalk3.default.cyan(formatted)}`;
13013
12993
  }).join(import_chalk3.default.dim(", "));
13014
12994
  }
12995
+ function formatGadgetLine(info, maxWidth) {
12996
+ const terminalWidth = maxWidth ?? process.stdout.columns ?? 80;
12997
+ const gadgetLabel = import_chalk3.default.magenta.bold(info.name);
12998
+ const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
12999
+ const timeLabel = import_chalk3.default.dim(timeStr);
13000
+ const fixedLength = 2 + info.name.length + 2 + 1 + timeStr.length;
13001
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
13002
+ const paramsStr = formatParametersInline(info.parameters, availableForParams);
13003
+ const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
13004
+ if (info.error) {
13005
+ const errorMsg = info.error.length > 50 ? `${info.error.slice(0, 50)}\u2026` : info.error;
13006
+ return `${import_chalk3.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.red("error:")} ${errorMsg} ${timeLabel}`;
13007
+ }
13008
+ if (!info.isComplete) {
13009
+ return `${import_chalk3.default.blue("\u23F5")} ${gadgetLabel}${paramsLabel} ${timeLabel}`;
13010
+ }
13011
+ let outputStr;
13012
+ if (info.tokenCount !== void 0 && info.tokenCount > 0) {
13013
+ outputStr = `${formatTokens(info.tokenCount)} tokens`;
13014
+ } else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
13015
+ outputStr = formatBytes(info.outputBytes);
13016
+ } else {
13017
+ outputStr = "";
13018
+ }
13019
+ const icon = info.breaksLoop ? import_chalk3.default.yellow("\u23F9") : import_chalk3.default.green("\u2713");
13020
+ const outputLabel = outputStr ? ` ${import_chalk3.default.dim("\u2192")} ${import_chalk3.default.green(outputStr)}` : "";
13021
+ return `${icon} ${gadgetLabel}${paramsLabel}${outputLabel} ${timeLabel}`;
13022
+ }
13015
13023
  function formatBytes(bytes) {
13016
13024
  if (bytes < 1024) {
13017
13025
  return `${bytes} bytes`;
@@ -13290,11 +13298,35 @@ var StreamProgress = class {
13290
13298
  }
13291
13299
  /**
13292
13300
  * Update a nested agent with completion info (called when nested llm_call_end event received).
13301
+ * Records completion time to freeze the elapsed timer.
13302
+ * @param info - Full LLM call info including tokens, cache details, and cost
13293
13303
  */
13294
- updateNestedAgent(id, outputTokens) {
13304
+ updateNestedAgent(id, info) {
13295
13305
  const agent = this.nestedAgents.get(id);
13296
13306
  if (agent) {
13297
- agent.outputTokens = outputTokens;
13307
+ agent.inputTokens = info.inputTokens;
13308
+ agent.outputTokens = info.outputTokens;
13309
+ agent.cachedInputTokens = info.cachedInputTokens;
13310
+ agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
13311
+ agent.finishReason = info.finishReason;
13312
+ if (info.cost !== void 0) {
13313
+ agent.cost = info.cost;
13314
+ } else if (this.modelRegistry && agent.model && info.outputTokens) {
13315
+ try {
13316
+ const modelName = agent.model.includes(":") ? agent.model.split(":")[1] : agent.model;
13317
+ const costResult = this.modelRegistry.estimateCost(
13318
+ modelName,
13319
+ info.inputTokens ?? 0,
13320
+ info.outputTokens,
13321
+ info.cachedInputTokens,
13322
+ info.cacheCreationInputTokens
13323
+ );
13324
+ agent.cost = costResult?.totalCost;
13325
+ } catch {
13326
+ }
13327
+ }
13328
+ agent.completed = true;
13329
+ agent.completedTime = Date.now();
13298
13330
  if (this.isRunning && this.isTTY) {
13299
13331
  this.render();
13300
13332
  }
@@ -13312,11 +13344,12 @@ var StreamProgress = class {
13312
13344
  /**
13313
13345
  * Add a nested gadget call (called when nested gadget_call event received).
13314
13346
  */
13315
- addNestedGadget(id, depth, parentInvocationId, name) {
13347
+ addNestedGadget(id, depth, parentInvocationId, name, parameters) {
13316
13348
  this.nestedGadgets.set(id, {
13317
13349
  depth,
13318
13350
  parentInvocationId,
13319
13351
  name,
13352
+ parameters,
13320
13353
  startTime: Date.now()
13321
13354
  });
13322
13355
  if (this.isRunning && this.isTTY) {
@@ -13334,11 +13367,13 @@ var StreamProgress = class {
13334
13367
  }
13335
13368
  /**
13336
13369
  * Mark a nested gadget as completed (keeps it visible with ✓ indicator).
13370
+ * Records completion time to freeze the elapsed timer.
13337
13371
  */
13338
13372
  completeNestedGadget(id) {
13339
13373
  const gadget = this.nestedGadgets.get(id);
13340
13374
  if (gadget) {
13341
13375
  gadget.completed = true;
13376
+ gadget.completedTime = Date.now();
13342
13377
  if (this.isRunning && this.isTTY) {
13343
13378
  this.render();
13344
13379
  }
@@ -13480,25 +13515,73 @@ var StreamProgress = class {
13480
13515
  }
13481
13516
  if (this.isTTY) {
13482
13517
  for (const [gadgetId, gadget] of this.inFlightGadgets) {
13483
- const elapsed = ((Date.now() - gadget.startTime) / 1e3).toFixed(1);
13484
- const gadgetLine = ` ${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.magenta.bold(gadget.name)}${import_chalk4.default.dim("(...)")} ${import_chalk4.default.dim(elapsed + "s")}`;
13518
+ const elapsedSeconds = (Date.now() - gadget.startTime) / 1e3;
13519
+ const gadgetLine = ` ${formatGadgetLine({
13520
+ name: gadget.name,
13521
+ parameters: gadget.params,
13522
+ elapsedSeconds,
13523
+ isComplete: false
13524
+ })}`;
13485
13525
  lines.push(gadgetLine);
13526
+ const nestedOps = [];
13486
13527
  for (const [_agentId, nested] of this.nestedAgents) {
13487
- if (nested.parentInvocationId !== gadgetId) continue;
13488
- const indent = " ".repeat(nested.depth + 1);
13489
- const nestedElapsed = ((Date.now() - nested.startTime) / 1e3).toFixed(1);
13490
- const tokens = nested.inputTokens ? ` ${import_chalk4.default.dim("\u2191")}${import_chalk4.default.yellow(formatTokens(nested.inputTokens))}` : "";
13491
- const outTokens = nested.outputTokens ? ` ${import_chalk4.default.dim("\u2193")}${import_chalk4.default.green(formatTokens(nested.outputTokens))}` : "";
13492
- const nestedLine = `${indent}${import_chalk4.default.cyan(`#${nested.iteration}`)} ${import_chalk4.default.dim(nested.model)}${tokens}${outTokens} ${import_chalk4.default.dim(nestedElapsed + "s")} ${import_chalk4.default.cyan(spinner)}`;
13493
- lines.push(nestedLine);
13494
- }
13495
- for (const [nestedId, nestedGadget] of this.nestedGadgets) {
13528
+ if (nested.parentInvocationId === gadgetId) {
13529
+ nestedOps.push({
13530
+ type: "agent",
13531
+ startTime: nested.startTime,
13532
+ depth: nested.depth,
13533
+ iteration: nested.iteration,
13534
+ model: nested.model,
13535
+ inputTokens: nested.inputTokens,
13536
+ cachedInputTokens: nested.cachedInputTokens,
13537
+ outputTokens: nested.outputTokens,
13538
+ cost: nested.cost,
13539
+ finishReason: nested.finishReason,
13540
+ completed: nested.completed,
13541
+ completedTime: nested.completedTime
13542
+ });
13543
+ }
13544
+ }
13545
+ for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
13496
13546
  if (nestedGadget.parentInvocationId === gadgetId) {
13497
- const indent = " ".repeat(nestedGadget.depth + 1);
13498
- const nestedElapsed = ((Date.now() - nestedGadget.startTime) / 1e3).toFixed(1);
13499
- const icon = nestedGadget.completed ? import_chalk4.default.green("\u2713") : import_chalk4.default.blue("\u23F5");
13500
- const nestedGadgetLine = `${indent}${icon} ${import_chalk4.default.dim(nestedGadget.name + "(...)")} ${import_chalk4.default.dim(nestedElapsed + "s")}`;
13501
- lines.push(nestedGadgetLine);
13547
+ nestedOps.push({
13548
+ type: "gadget",
13549
+ startTime: nestedGadget.startTime,
13550
+ depth: nestedGadget.depth,
13551
+ name: nestedGadget.name,
13552
+ parameters: nestedGadget.parameters,
13553
+ completed: nestedGadget.completed,
13554
+ completedTime: nestedGadget.completedTime
13555
+ });
13556
+ }
13557
+ }
13558
+ nestedOps.sort((a, b) => a.startTime - b.startTime);
13559
+ for (const op of nestedOps) {
13560
+ const indent = " ".repeat(op.depth + 1);
13561
+ const endTime = op.completedTime ?? Date.now();
13562
+ const elapsedSeconds2 = (endTime - op.startTime) / 1e3;
13563
+ if (op.type === "agent") {
13564
+ const line = formatLLMCallLine({
13565
+ iteration: op.iteration ?? 0,
13566
+ model: op.model ?? "",
13567
+ inputTokens: op.inputTokens,
13568
+ cachedInputTokens: op.cachedInputTokens,
13569
+ outputTokens: op.outputTokens,
13570
+ elapsedSeconds: elapsedSeconds2,
13571
+ cost: op.cost,
13572
+ finishReason: op.completed ? op.finishReason ?? "stop" : void 0,
13573
+ isStreaming: !op.completed,
13574
+ spinner
13575
+ });
13576
+ lines.push(`${indent}${line}`);
13577
+ } else {
13578
+ const line = formatGadgetLine({
13579
+ name: op.name ?? "",
13580
+ parameters: op.parameters,
13581
+ elapsedSeconds: elapsedSeconds2,
13582
+ isComplete: op.completed ?? false
13583
+ });
13584
+ lines.push(`${indent}${line}`);
13502
13585
  }
13503
13586
  }
13504
13587
  }
@@ -13520,42 +13603,27 @@ var StreamProgress = class {
13520
13603
  }
13521
13604
  /**
13522
13605
  * Format the streaming mode progress line (returns string, doesn't write).
13606
+ * Uses the shared formatLLMCallLine() function for consistent formatting
13607
+ * between main agent and nested subagent displays.
13523
13608
  */
13524
13609
  formatStreamingLine(spinner) {
13525
- const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
13526
13610
  const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
13527
- const parts = [];
13528
- const iterPart = import_chalk4.default.cyan(`#${this.currentIteration}`);
13529
- if (this.model) {
13530
- parts.push(`${iterPart} ${import_chalk4.default.magenta(this.model)}`);
13531
- } else {
13532
- parts.push(iterPart);
13533
- }
13534
- const usagePercent = this.getContextUsagePercent();
13535
- if (usagePercent !== null) {
13536
- const formatted = `${Math.round(usagePercent)}%`;
13537
- if (usagePercent >= 80) {
13538
- parts.push(import_chalk4.default.red(formatted));
13539
- } else if (usagePercent >= 50) {
13540
- parts.push(import_chalk4.default.yellow(formatted));
13541
- } else {
13542
- parts.push(import_chalk4.default.green(formatted));
13611
+ return formatLLMCallLine({
13612
+ iteration: this.currentIteration,
13613
+ model: this.model ?? "",
13614
+ inputTokens: this.callInputTokens,
13615
+ cachedInputTokens: this.callCachedInputTokens,
13616
+ outputTokens: outTokens,
13617
+ elapsedSeconds: (Date.now() - this.callStartTime) / 1e3,
13618
+ cost: this.calculateCurrentCallCost(outTokens),
13619
+ isStreaming: true,
13620
+ spinner,
13621
+ contextPercent: this.getContextUsagePercent(),
13622
+ estimated: {
13623
+ input: this.callInputTokensEstimated,
13624
+ output: this.callOutputTokensEstimated
13543
13625
  }
13544
- }
13545
- if (this.callInputTokens > 0) {
13546
- const prefix = this.callInputTokensEstimated ? "~" : "";
13547
- parts.push(import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
13548
- }
13549
- if (this.isStreaming || outTokens > 0) {
13550
- const prefix = this.callOutputTokensEstimated ? "~" : "";
13551
- parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
13552
- }
13553
- parts.push(import_chalk4.default.dim(`${elapsed}s`));
13554
- const callCost = this.calculateCurrentCallCost(outTokens);
13555
- if (callCost > 0) {
13556
- parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
13557
- }
13558
- return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
13626
+ });
13559
13627
  }
13560
13628
  /**
13561
13629
  * Calculates live cost estimate for the current streaming call.
@@ -13783,7 +13851,7 @@ function addAgentOptions(cmd, defaults) {
13783
13851
  OPTION_FLAGS.logLlmRequests,
13784
13852
  OPTION_DESCRIPTIONS.logLlmRequests,
13785
13853
  defaults?.["log-llm-requests"]
13786
- ).option(OPTION_FLAGS.inputImage, OPTION_DESCRIPTIONS.inputImage).option(OPTION_FLAGS.inputAudio, OPTION_DESCRIPTIONS.inputAudio).option(OPTION_FLAGS.docker, OPTION_DESCRIPTIONS.docker).option(OPTION_FLAGS.dockerRo, OPTION_DESCRIPTIONS.dockerRo).option(OPTION_FLAGS.noDocker, OPTION_DESCRIPTIONS.noDocker).option(OPTION_FLAGS.dockerDev, OPTION_DESCRIPTIONS.dockerDev);
13854
+ ).option(OPTION_FLAGS.inputImage, OPTION_DESCRIPTIONS.inputImage).option(OPTION_FLAGS.inputAudio, OPTION_DESCRIPTIONS.inputAudio).option(OPTION_FLAGS.docker, OPTION_DESCRIPTIONS.docker).option(OPTION_FLAGS.dockerRo, OPTION_DESCRIPTIONS.dockerRo).option(OPTION_FLAGS.noDocker, OPTION_DESCRIPTIONS.noDocker);
13787
13855
  }
13788
13856
  function configToCompleteOptions(config) {
13789
13857
  const result = {};
@@ -13860,8 +13928,7 @@ async function executeAgent(promptArg, options, env) {
13860
13928
  const dockerOptions = {
13861
13929
  docker: options.docker ?? false,
13862
13930
  dockerRo: options.dockerRo ?? false,
13863
- noDocker: options.noDocker ?? false,
13864
- dockerDev: options.dockerDev ?? false
13931
+ noDocker: options.noDocker ?? false
13865
13932
  };
13866
13933
  const dockerEnabled = resolveDockerEnabled(
13867
13934
  env.dockerConfig,
@@ -13870,7 +13937,6 @@ async function executeAgent(promptArg, options, env) {
13870
13937
  // Profile-level docker: true/false
13871
13938
  );
13872
13939
  if (dockerEnabled) {
13873
- const devMode = resolveDevMode(env.dockerConfig, dockerOptions.dockerDev);
13874
13940
  const ctx = createDockerContext(
13875
13941
  env.dockerConfig,
13876
13942
  dockerOptions,
@@ -13881,7 +13947,7 @@ async function executeAgent(promptArg, options, env) {
13881
13947
  // Profile-level CWD permission override
13882
13948
  );
13883
13949
  try {
13884
- await executeInDocker(ctx, devMode);
13950
+ await executeInDocker(ctx);
13885
13951
  } catch (error) {
13886
13952
  if (error instanceof DockerSkipError) {
13887
13953
  } else {
@@ -13908,9 +13974,18 @@ async function executeAgent(promptArg, options, env) {
13908
13974
  registry.registerByClass(gadget);
13909
13975
  }
13910
13976
  }
13977
+ if (!options.quiet) {
13978
+ const allNames = registry.getAll().map((g) => g.name).join(", ");
13979
+ env.stderr.write(import_chalk5.default.dim(`Gadgets: ${allNames}
13980
+ `));
13981
+ }
13911
13982
  const printer = new StreamPrinter(env.stdout);
13912
13983
  const stderrTTY = env.stderr.isTTY === true;
13913
- const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
13984
+ const progress = new StreamProgress(
13985
+ env.stderr,
13986
+ stderrTTY,
13987
+ client.modelRegistry
13988
+ );
13914
13989
  const abortController = new AbortController();
13915
13990
  let wasCancelled = false;
13916
13991
  let isStreaming = false;
@@ -13920,9 +13995,11 @@ async function executeAgent(promptArg, options, env) {
13920
13995
  wasCancelled = true;
13921
13996
  abortController.abort();
13922
13997
  progress.pause();
13923
- env.stderr.write(import_chalk5.default.yellow(`
13998
+ env.stderr.write(
13999
+ import_chalk5.default.yellow(`
13924
14000
  [Cancelled] ${progress.formatStats()}
13925
- `));
14001
+ `)
14002
+ );
13926
14003
  } else {
13927
14004
  handleQuit();
13928
14005
  }
@@ -13932,7 +14009,11 @@ async function executeAgent(promptArg, options, env) {
13932
14009
  cleanupSigint: null,
13933
14010
  restore: () => {
13934
14011
  if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
13935
- keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
14012
+ keyboard.cleanupEsc = createEscKeyListener(
14013
+ stdinStream,
14014
+ handleCancel,
14015
+ handleCancel
14016
+ );
13936
14017
  }
13937
14018
  }
13938
14019
  };
@@ -13957,7 +14038,11 @@ async function executeAgent(promptArg, options, env) {
13957
14038
  process.exit(130);
13958
14039
  };
13959
14040
  if (stdinIsInteractive && stdinStream.isTTY) {
13960
- keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
14041
+ keyboard.cleanupEsc = createEscKeyListener(
14042
+ stdinStream,
14043
+ handleCancel,
14044
+ handleCancel
14045
+ );
13961
14046
  }
13962
14047
  keyboard.cleanupSigint = createSigintListener(
13963
14048
  handleCancel,
@@ -13983,7 +14068,12 @@ async function executeAgent(promptArg, options, env) {
13983
14068
  gadgetApprovals,
13984
14069
  defaultMode: "allowed"
13985
14070
  };
13986
- const approvalManager = new ApprovalManager(approvalConfig, env, progress, keyboard);
14071
+ const approvalManager = new ApprovalManager(
14072
+ approvalConfig,
14073
+ env,
14074
+ progress,
14075
+ keyboard
14076
+ );
13987
14077
  let usage;
13988
14078
  let iterations = 0;
13989
14079
  const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
@@ -13993,7 +14083,10 @@ async function executeAgent(promptArg, options, env) {
13993
14083
  try {
13994
14084
  return await client.countTokens(model, messages);
13995
14085
  } catch {
13996
- const totalChars = messages.reduce((sum, m) => sum + (m.content?.length ?? 0), 0);
14086
+ const totalChars = messages.reduce(
14087
+ (sum, m) => sum + (m.content?.length ?? 0),
14088
+ 0
14089
+ );
13997
14090
  return Math.round(totalChars / FALLBACK_CHARS_PER_TOKEN);
13998
14091
  }
13999
14092
  };
@@ -14015,7 +14108,9 @@ async function executeAgent(promptArg, options, env) {
14015
14108
  observers: {
14016
14109
  // onLLMCallStart: Start progress indicator for each LLM call
14017
14110
  // This showcases how to react to agent lifecycle events
14111
+ // Skip for subagent events (tracked separately via nested display)
14018
14112
  onLLMCallStart: async (context) => {
14113
+ if (context.subagentContext) return;
14019
14114
  isStreaming = true;
14020
14115
  llmCallCounter++;
14021
14116
  const inputTokens = await countMessagesTokens(
@@ -14041,7 +14136,9 @@ async function executeAgent(promptArg, options, env) {
14041
14136
  },
14042
14137
  // onStreamChunk: Real-time updates as LLM generates tokens
14043
14138
  // This enables responsive UIs that show progress during generation
14139
+ // Skip for subagent events (tracked separately via nested display)
14044
14140
  onStreamChunk: async (context) => {
14141
+ if (context.subagentContext) return;
14045
14142
  progress.update(context.accumulatedText.length);
14046
14143
  if (context.usage) {
14047
14144
  if (context.usage.inputTokens) {
@@ -14058,7 +14155,9 @@ async function executeAgent(promptArg, options, env) {
14058
14155
  },
14059
14156
  // onLLMCallComplete: Finalize metrics after each LLM call
14060
14157
  // This is where you'd typically log metrics or update dashboards
14158
+ // Skip progress updates for subagent events (tracked separately via nested display)
14061
14159
  onLLMCallComplete: async (context) => {
14160
+ if (context.subagentContext) return;
14062
14161
  isStreaming = false;
14063
14162
  usage = context.usage;
14064
14163
  iterations = Math.max(iterations, context.iteration + 1);
@@ -14087,7 +14186,7 @@ async function executeAgent(promptArg, options, env) {
14087
14186
  }
14088
14187
  const callElapsed = progress.getCallElapsedSeconds();
14089
14188
  progress.endCall(context.usage);
14090
- if (!options.quiet) {
14189
+ if (!options.quiet && !context.subagentContext) {
14091
14190
  const summary = renderSummary({
14092
14191
  iterations: context.iteration + 1,
14093
14192
  model: options.model,
@@ -14145,7 +14244,10 @@ ${ctx.gadgetName} is denied by configuration.`
14145
14244
  }
14146
14245
  return { action: "proceed" };
14147
14246
  }
14148
- const result = await approvalManager.requestApproval(ctx.gadgetName, ctx.parameters);
14247
+ const result = await approvalManager.requestApproval(
14248
+ ctx.gadgetName,
14249
+ ctx.parameters
14250
+ );
14149
14251
  if (!result.approved) {
14150
14252
  return {
14151
14253
  action: "skip",
@@ -14188,11 +14290,11 @@ Denied: ${result.reason ?? "by user"}`
14188
14290
  builder.withSyntheticGadgetCall(
14189
14291
  "TellUser",
14190
14292
  {
14191
- message: "\u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?",
14293
+ message: "\u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?",
14192
14294
  done: false,
14193
14295
  type: "info"
14194
14296
  },
14195
- "\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?"
14297
+ "\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?"
14196
14298
  );
14197
14299
  builder.withTextOnlyHandler("acknowledge");
14198
14300
  builder.withTextWithGadgetsHandler({
@@ -14223,15 +14325,22 @@ Denied: ${result.reason ?? "by user"}`
14223
14325
  } else if (subagentEvent.type === "llm_call_end") {
14224
14326
  const info = subagentEvent.event;
14225
14327
  const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
14226
- progress.updateNestedAgent(subagentId, info.outputTokens);
14227
- setTimeout(() => progress.removeNestedAgent(subagentId), 100);
14328
+ progress.updateNestedAgent(subagentId, {
14329
+ inputTokens: info.usage?.inputTokens ?? info.inputTokens,
14330
+ outputTokens: info.usage?.outputTokens ?? info.outputTokens,
14331
+ cachedInputTokens: info.usage?.cachedInputTokens,
14332
+ cacheCreationInputTokens: info.usage?.cacheCreationInputTokens,
14333
+ finishReason: info.finishReason,
14334
+ cost: info.cost
14335
+ });
14228
14336
  } else if (subagentEvent.type === "gadget_call") {
14229
14337
  const gadgetEvent = subagentEvent.event;
14230
14338
  progress.addNestedGadget(
14231
14339
  gadgetEvent.call.invocationId,
14232
14340
  subagentEvent.depth,
14233
14341
  subagentEvent.gadgetInvocationId,
14234
- gadgetEvent.call.gadgetName
14342
+ gadgetEvent.call.gadgetName,
14343
+ gadgetEvent.call.parameters
14235
14344
  );
14236
14345
  } else if (subagentEvent.type === "gadget_result") {
14237
14346
  const resultEvent = subagentEvent.event;
@@ -14330,7 +14439,10 @@ Denied: ${result.reason ?? "by user"}`
14330
14439
  }
14331
14440
  }
14332
14441
  function registerAgentCommand(program, env, config, globalSubagents) {
14333
- const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument("[prompt]", "Prompt for the agent loop. Falls back to stdin when available.");
14442
+ const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument(
14443
+ "[prompt]",
14444
+ "Prompt for the agent loop. Falls back to stdin when available."
14445
+ );
14334
14446
  addAgentOptions(cmd, config);
14335
14447
  cmd.action(
14336
14448
  (prompt, options) => executeAction(() => {
@@ -14440,7 +14552,7 @@ function registerCompleteCommand(program, env, config) {
14440
14552
 
14441
14553
  // src/cli/init-command.ts
14442
14554
  var import_node_fs12 = require("fs");
14443
- var import_node_path14 = require("path");
14555
+ var import_node_path13 = require("path");
14444
14556
  var STARTER_CONFIG = `# ~/.llmist/cli.toml
14445
14557
  # llmist CLI configuration file
14446
14558
  #
@@ -14496,7 +14608,7 @@ var STARTER_CONFIG = `# ~/.llmist/cli.toml
14496
14608
  `;
14497
14609
  async function executeInit(_options, env) {
14498
14610
  const configPath = getConfigPath();
14499
- const configDir = (0, import_node_path14.dirname)(configPath);
14611
+ const configDir = (0, import_node_path13.dirname)(configPath);
14500
14612
  if ((0, import_node_fs12.existsSync)(configPath)) {
14501
14613
  env.stderr.write(`Configuration already exists at ${configPath}
14502
14614
  `);