llmist 3.1.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
@@ -3496,7 +3496,7 @@ var init_executor = __esm({
3496
3496
  init_exceptions();
3497
3497
  init_parser();
3498
3498
  GadgetExecutor = class {
3499
- constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
3499
+ constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent) {
3500
3500
  this.registry = registry;
3501
3501
  this.requestHumanInput = requestHumanInput;
3502
3502
  this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
@@ -3504,7 +3504,7 @@ var init_executor = __esm({
3504
3504
  this.mediaStore = mediaStore;
3505
3505
  this.agentConfig = agentConfig;
3506
3506
  this.subagentConfig = subagentConfig;
3507
- this.onNestedEvent = onNestedEvent;
3507
+ this.onSubagentEvent = onSubagentEvent;
3508
3508
  this.logger = logger ?? createLogger({ name: "llmist:executor" });
3509
3509
  this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
3510
3510
  this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
@@ -3652,7 +3652,7 @@ var init_executor = __esm({
3652
3652
  agentConfig: this.agentConfig,
3653
3653
  subagentConfig: this.subagentConfig,
3654
3654
  invocationId: call.invocationId,
3655
- onNestedEvent: this.onNestedEvent
3655
+ onSubagentEvent: this.onSubagentEvent
3656
3656
  };
3657
3657
  let rawResult;
3658
3658
  if (timeoutMs && timeoutMs > 0) {
@@ -3892,7 +3892,7 @@ var init_stream_processor = __esm({
3892
3892
  options.mediaStore,
3893
3893
  options.agentConfig,
3894
3894
  options.subagentConfig,
3895
- options.onNestedEvent
3895
+ options.onSubagentEvent
3896
3896
  );
3897
3897
  }
3898
3898
  /**
@@ -4851,8 +4851,12 @@ var init_agent = __esm({
4851
4851
  // Subagent configuration
4852
4852
  agentContextConfig;
4853
4853
  subagentConfig;
4854
- // Nested event callback for subagent gadgets
4855
- onNestedEvent;
4854
+ // Subagent event callback for subagent gadgets
4855
+ userSubagentEventCallback;
4856
+ // Internal queue for yielding subagent events in run()
4857
+ pendingSubagentEvents = [];
4858
+ // Combined callback that queues events AND calls user callback
4859
+ onSubagentEvent;
4856
4860
  /**
4857
4861
  * Creates a new Agent instance.
4858
4862
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -4930,7 +4934,72 @@ var init_agent = __esm({
4930
4934
  temperature: this.temperature
4931
4935
  };
4932
4936
  this.subagentConfig = options.subagentConfig;
4933
- this.onNestedEvent = options.onNestedEvent;
4937
+ this.userSubagentEventCallback = options.onSubagentEvent;
4938
+ this.onSubagentEvent = (event) => {
4939
+ this.pendingSubagentEvents.push(event);
4940
+ this.userSubagentEventCallback?.(event);
4941
+ const subagentContext = {
4942
+ parentGadgetInvocationId: event.gadgetInvocationId,
4943
+ depth: event.depth
4944
+ };
4945
+ if (event.type === "llm_call_start") {
4946
+ const info = event.event;
4947
+ void this.hooks?.observers?.onLLMCallStart?.({
4948
+ iteration: info.iteration,
4949
+ options: { model: info.model, messages: [] },
4950
+ logger: this.logger,
4951
+ subagentContext
4952
+ });
4953
+ } else if (event.type === "llm_call_end") {
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);
4960
+ void this.hooks?.observers?.onLLMCallComplete?.({
4961
+ iteration: info.iteration,
4962
+ options: { model: info.model, messages: [] },
4963
+ finishReason: info.finishReason ?? null,
4964
+ usage,
4965
+ rawResponse: "",
4966
+ finalMessage: "",
4967
+ logger: this.logger,
4968
+ subagentContext
4969
+ });
4970
+ } else if (event.type === "gadget_call") {
4971
+ const gadgetEvent = event.event;
4972
+ void this.hooks?.observers?.onGadgetExecutionStart?.({
4973
+ iteration: 0,
4974
+ gadgetName: gadgetEvent.call.gadgetName,
4975
+ invocationId: gadgetEvent.call.invocationId,
4976
+ parameters: gadgetEvent.call.parameters ?? {},
4977
+ logger: this.logger,
4978
+ subagentContext
4979
+ });
4980
+ } else if (event.type === "gadget_result") {
4981
+ const resultEvent = event.event;
4982
+ void this.hooks?.observers?.onGadgetExecutionComplete?.({
4983
+ iteration: 0,
4984
+ gadgetName: resultEvent.result.gadgetName ?? "unknown",
4985
+ invocationId: resultEvent.result.invocationId,
4986
+ parameters: {},
4987
+ executionTimeMs: resultEvent.result.executionTimeMs ?? 0,
4988
+ logger: this.logger,
4989
+ subagentContext
4990
+ });
4991
+ }
4992
+ };
4993
+ }
4994
+ /**
4995
+ * Flush pending subagent events as StreamEvents.
4996
+ * Called from run() to yield queued subagent events from subagent gadgets.
4997
+ */
4998
+ *flushPendingSubagentEvents() {
4999
+ while (this.pendingSubagentEvents.length > 0) {
5000
+ const event = this.pendingSubagentEvents.shift();
5001
+ yield { type: "subagent_event", subagentEvent: event };
5002
+ }
4934
5003
  }
4935
5004
  /**
4936
5005
  * Get the gadget registry for this agent.
@@ -5162,7 +5231,7 @@ var init_agent = __esm({
5162
5231
  mediaStore: this.mediaStore,
5163
5232
  agentConfig: this.agentContextConfig,
5164
5233
  subagentConfig: this.subagentConfig,
5165
- onNestedEvent: this.onNestedEvent
5234
+ onSubagentEvent: this.onSubagentEvent
5166
5235
  });
5167
5236
  let streamMetadata = null;
5168
5237
  let gadgetCallCount = 0;
@@ -5180,6 +5249,7 @@ var init_agent = __esm({
5180
5249
  gadgetResults.push(event);
5181
5250
  }
5182
5251
  yield event;
5252
+ yield* this.flushPendingSubagentEvents();
5183
5253
  }
5184
5254
  if (!streamMetadata) {
5185
5255
  throw new Error("Stream processing completed without metadata event");
@@ -6934,6 +7004,9 @@ var init_gemini = __esm({
6934
7004
  async countTokens(messages, descriptor, _spec) {
6935
7005
  const client = this.client;
6936
7006
  const contents = this.convertMessagesToContents(messages);
7007
+ if (!contents || contents.length === 0) {
7008
+ return 0;
7009
+ }
6937
7010
  try {
6938
7011
  const response = await client.models.countTokens({
6939
7012
  model: descriptor.name,
@@ -8756,7 +8829,7 @@ var init_builder = __esm({
8756
8829
  signal;
8757
8830
  trailingMessage;
8758
8831
  subagentConfig;
8759
- nestedEventCallback;
8832
+ subagentEventCallback;
8760
8833
  parentContext;
8761
8834
  constructor(client) {
8762
8835
  this.client = client;
@@ -9255,38 +9328,38 @@ var init_builder = __esm({
9255
9328
  return this;
9256
9329
  }
9257
9330
  /**
9258
- * Set the callback for nested subagent events.
9331
+ * Set the callback for subagent events.
9259
9332
  *
9260
- * Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
9333
+ * Subagent gadgets (like BrowseWeb) can use ExecutionContext.onSubagentEvent
9261
9334
  * to report their internal LLM calls and gadget executions in real-time.
9262
9335
  * This callback receives those events, enabling hierarchical progress display.
9263
9336
  *
9264
- * @param callback - Function to handle nested agent events
9337
+ * @param callback - Function to handle subagent events
9265
9338
  * @returns This builder for chaining
9266
9339
  *
9267
9340
  * @example
9268
9341
  * ```typescript
9269
- * .withNestedEventCallback((event) => {
9342
+ * .withSubagentEventCallback((event) => {
9270
9343
  * if (event.type === "llm_call_start") {
9271
- * console.log(` Nested LLM #${event.event.iteration} starting...`);
9344
+ * console.log(` Subagent LLM #${event.event.iteration} starting...`);
9272
9345
  * } else if (event.type === "gadget_call") {
9273
9346
  * console.log(` ⏵ ${event.event.call.gadgetName}...`);
9274
9347
  * }
9275
9348
  * })
9276
9349
  * ```
9277
9350
  */
9278
- withNestedEventCallback(callback) {
9279
- this.nestedEventCallback = callback;
9351
+ withSubagentEventCallback(callback) {
9352
+ this.subagentEventCallback = callback;
9280
9353
  return this;
9281
9354
  }
9282
9355
  /**
9283
- * Enable automatic nested event forwarding to parent agent.
9356
+ * Enable automatic subagent event forwarding to parent agent.
9284
9357
  *
9285
9358
  * When building a subagent inside a gadget, call this method to automatically
9286
9359
  * forward all LLM calls and gadget events to the parent agent. This enables
9287
9360
  * hierarchical progress display without any manual event handling.
9288
9361
  *
9289
- * The method extracts `invocationId` and `onNestedEvent` from the execution
9362
+ * The method extracts `invocationId` and `onSubagentEvent` from the execution
9290
9363
  * context and sets up automatic forwarding via hooks and event wrapping.
9291
9364
  *
9292
9365
  * @param ctx - ExecutionContext passed to the gadget's execute() method
@@ -9313,10 +9386,10 @@ var init_builder = __esm({
9313
9386
  * ```
9314
9387
  */
9315
9388
  withParentContext(ctx, depth = 1) {
9316
- if (ctx.onNestedEvent && ctx.invocationId) {
9389
+ if (ctx.onSubagentEvent && ctx.invocationId) {
9317
9390
  this.parentContext = {
9318
9391
  invocationId: ctx.invocationId,
9319
- onNestedEvent: ctx.onNestedEvent,
9392
+ onSubagentEvent: ctx.onSubagentEvent,
9320
9393
  depth
9321
9394
  };
9322
9395
  }
@@ -9391,20 +9464,22 @@ ${endPrefix}`
9391
9464
  /**
9392
9465
  * Compose the final hooks, including:
9393
9466
  * - Trailing message injection (if configured)
9394
- * - Nested event forwarding for LLM calls (if parentContext is set)
9467
+ * - Subagent event forwarding for LLM calls (if parentContext is set)
9395
9468
  */
9396
9469
  composeHooks() {
9397
9470
  let hooks = this.hooks;
9398
9471
  if (this.parentContext) {
9399
- const { invocationId, onNestedEvent, depth } = this.parentContext;
9472
+ const { invocationId, onSubagentEvent, depth } = this.parentContext;
9400
9473
  const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
9401
9474
  const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
9475
+ const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
9476
+ const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
9402
9477
  hooks = {
9403
9478
  ...hooks,
9404
9479
  observers: {
9405
9480
  ...hooks?.observers,
9406
9481
  onLLMCallStart: async (context) => {
9407
- onNestedEvent({
9482
+ onSubagentEvent({
9408
9483
  type: "llm_call_start",
9409
9484
  gadgetInvocationId: invocationId,
9410
9485
  depth,
@@ -9418,20 +9493,57 @@ ${endPrefix}`
9418
9493
  }
9419
9494
  },
9420
9495
  onLLMCallComplete: async (context) => {
9421
- onNestedEvent({
9496
+ onSubagentEvent({
9422
9497
  type: "llm_call_end",
9423
9498
  gadgetInvocationId: invocationId,
9424
9499
  depth,
9425
9500
  event: {
9426
9501
  iteration: context.iteration,
9427
9502
  model: context.options.model,
9503
+ // Backward compat fields
9504
+ inputTokens: context.usage?.inputTokens,
9428
9505
  outputTokens: context.usage?.outputTokens,
9429
- 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
9430
9510
  }
9431
9511
  });
9432
9512
  if (existingOnLLMCallComplete) {
9433
9513
  await existingOnLLMCallComplete(context);
9434
9514
  }
9515
+ },
9516
+ onGadgetExecutionStart: async (context) => {
9517
+ onSubagentEvent({
9518
+ type: "gadget_call",
9519
+ gadgetInvocationId: invocationId,
9520
+ depth,
9521
+ event: {
9522
+ call: {
9523
+ invocationId: context.invocationId,
9524
+ gadgetName: context.gadgetName,
9525
+ parameters: context.parameters
9526
+ }
9527
+ }
9528
+ });
9529
+ if (existingOnGadgetExecutionStart) {
9530
+ await existingOnGadgetExecutionStart(context);
9531
+ }
9532
+ },
9533
+ onGadgetExecutionComplete: async (context) => {
9534
+ onSubagentEvent({
9535
+ type: "gadget_result",
9536
+ gadgetInvocationId: invocationId,
9537
+ depth,
9538
+ event: {
9539
+ result: {
9540
+ invocationId: context.invocationId
9541
+ }
9542
+ }
9543
+ });
9544
+ if (existingOnGadgetExecutionComplete) {
9545
+ await existingOnGadgetExecutionComplete(context);
9546
+ }
9435
9547
  }
9436
9548
  }
9437
9549
  };
@@ -9518,11 +9630,11 @@ ${endPrefix}`
9518
9630
  this.client = new LLMistClass();
9519
9631
  }
9520
9632
  const registry = GadgetRegistry.from(this.gadgets);
9521
- let onNestedEvent = this.nestedEventCallback;
9633
+ let onSubagentEvent = this.subagentEventCallback;
9522
9634
  if (this.parentContext) {
9523
- const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
9524
- const existingCallback = this.nestedEventCallback;
9525
- onNestedEvent = (event) => {
9635
+ const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
9636
+ const existingCallback = this.subagentEventCallback;
9637
+ onSubagentEvent = (event) => {
9526
9638
  parentCallback({
9527
9639
  ...event,
9528
9640
  gadgetInvocationId: invocationId,
@@ -9557,7 +9669,7 @@ ${endPrefix}`
9557
9669
  compactionConfig: this.compactionConfig,
9558
9670
  signal: this.signal,
9559
9671
  subagentConfig: this.subagentConfig,
9560
- onNestedEvent
9672
+ onSubagentEvent
9561
9673
  };
9562
9674
  }
9563
9675
  ask(userPrompt) {
@@ -9714,11 +9826,11 @@ ${endPrefix}`
9714
9826
  this.client = new LLMistClass();
9715
9827
  }
9716
9828
  const registry = GadgetRegistry.from(this.gadgets);
9717
- let onNestedEvent = this.nestedEventCallback;
9829
+ let onSubagentEvent = this.subagentEventCallback;
9718
9830
  if (this.parentContext) {
9719
- const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
9720
- const existingCallback = this.nestedEventCallback;
9721
- onNestedEvent = (event) => {
9831
+ const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
9832
+ const existingCallback = this.subagentEventCallback;
9833
+ onSubagentEvent = (event) => {
9722
9834
  parentCallback({
9723
9835
  ...event,
9724
9836
  gadgetInvocationId: invocationId,
@@ -9753,7 +9865,7 @@ ${endPrefix}`
9753
9865
  compactionConfig: this.compactionConfig,
9754
9866
  signal: this.signal,
9755
9867
  subagentConfig: this.subagentConfig,
9756
- onNestedEvent
9868
+ onSubagentEvent
9757
9869
  };
9758
9870
  return new Agent(AGENT_INTERNAL_KEY, options);
9759
9871
  }
@@ -9793,7 +9905,6 @@ var OPTION_FLAGS = {
9793
9905
  docker: "--docker",
9794
9906
  dockerRo: "--docker-ro",
9795
9907
  noDocker: "--no-docker",
9796
- dockerDev: "--docker-dev",
9797
9908
  // Multimodal input options
9798
9909
  inputImage: "--image <path>",
9799
9910
  inputAudio: "--audio <path>",
@@ -9828,7 +9939,6 @@ var OPTION_DESCRIPTIONS = {
9828
9939
  docker: "Run agent in a Docker sandbox container for security isolation.",
9829
9940
  dockerRo: "Run in Docker with current directory mounted read-only.",
9830
9941
  noDocker: "Disable Docker sandboxing (override config).",
9831
- dockerDev: "Run in Docker dev mode (mount local source instead of npm install).",
9832
9942
  // Image generation descriptions
9833
9943
  imageSize: "Image size/aspect ratio, e.g. '1024x1024', '1:1', '16:9'.",
9834
9944
  imageQuality: "Image quality: 'standard', 'hd', 'low', 'medium', 'high'.",
@@ -9848,7 +9958,7 @@ var import_commander2 = require("commander");
9848
9958
  // package.json
9849
9959
  var package_default = {
9850
9960
  name: "llmist",
9851
- version: "3.0.0",
9961
+ version: "5.0.0",
9852
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.",
9853
9963
  type: "module",
9854
9964
  main: "dist/index.cjs",
@@ -9891,7 +10001,8 @@ var package_default = {
9891
10001
  "test:all": "bun run test && bun run test:e2e",
9892
10002
  clean: "rimraf dist",
9893
10003
  prepare: "node scripts/install-hooks.js || true",
9894
- "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'
9895
10006
  },
9896
10007
  bin: {
9897
10008
  llmist: "dist/cli.js"
@@ -11250,20 +11361,17 @@ var DOCKER_CONFIG_KEYS = /* @__PURE__ */ new Set([
11250
11361
  "mounts",
11251
11362
  "env-vars",
11252
11363
  "image-name",
11253
- "dev-mode",
11254
- "dev-source",
11255
11364
  "docker-args"
11256
11365
  ]);
11257
11366
  var DEFAULT_IMAGE_NAME = "llmist-sandbox";
11258
11367
  var DEFAULT_CWD_PERMISSION = "rw";
11259
11368
  var DEFAULT_CONFIG_PERMISSION = "ro";
11369
+ var GADGET_CACHE_VOLUME = "llmist-gadget-cache";
11260
11370
  var FORWARDED_API_KEYS = [
11261
11371
  "ANTHROPIC_API_KEY",
11262
11372
  "OPENAI_API_KEY",
11263
11373
  "GEMINI_API_KEY"
11264
11374
  ];
11265
- var DEV_IMAGE_NAME = "llmist-dev-sandbox";
11266
- var DEV_SOURCE_MOUNT_TARGET = "/llmist-src";
11267
11375
 
11268
11376
  // src/cli/docker/docker-config.ts
11269
11377
  var MOUNT_CONFIG_KEYS = /* @__PURE__ */ new Set(["source", "target", "permission"]);
@@ -11375,12 +11483,6 @@ function validateDockerConfig(raw, section) {
11375
11483
  if ("image-name" in rawObj) {
11376
11484
  result["image-name"] = validateString2(rawObj["image-name"], "image-name", section);
11377
11485
  }
11378
- if ("dev-mode" in rawObj) {
11379
- result["dev-mode"] = validateBoolean2(rawObj["dev-mode"], "dev-mode", section);
11380
- }
11381
- if ("dev-source" in rawObj) {
11382
- result["dev-source"] = validateString2(rawObj["dev-source"], "dev-source", section);
11383
- }
11384
11486
  if ("docker-args" in rawObj) {
11385
11487
  result["docker-args"] = validateStringArray2(rawObj["docker-args"], "docker-args", section);
11386
11488
  }
@@ -11390,7 +11492,6 @@ function validateDockerConfig(raw, section) {
11390
11492
  // src/cli/docker/docker-wrapper.ts
11391
11493
  var import_node_fs5 = require("fs");
11392
11494
  var import_node_os4 = require("os");
11393
- var import_node_path6 = require("path");
11394
11495
 
11395
11496
  // src/cli/docker/dockerfile.ts
11396
11497
  var DEFAULT_DOCKERFILE = `# llmist sandbox image
@@ -11410,6 +11511,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \\
11410
11511
  curl \\
11411
11512
  # ca-certificates for HTTPS
11412
11513
  ca-certificates \\
11514
+ # python3 for native module compilation (node-gyp)
11515
+ python3 \\
11516
+ # build-essential for compiling native modules
11517
+ build-essential \\
11413
11518
  && rm -rf /var/lib/apt/lists/*
11414
11519
 
11415
11520
  # Install ast-grep for code search/refactoring
@@ -11427,37 +11532,8 @@ WORKDIR /workspace
11427
11532
  # Entry point - llmist with all arguments forwarded
11428
11533
  ENTRYPOINT ["llmist"]
11429
11534
  `;
11430
- var DEV_DOCKERFILE = `# llmist DEV sandbox image
11431
- # For development/testing with local source code
11432
-
11433
- FROM oven/bun:1-debian
11434
-
11435
- # Install essential tools (same as production)
11436
- RUN apt-get update && apt-get install -y --no-install-recommends \\
11437
- ed \\
11438
- ripgrep \\
11439
- git \\
11440
- curl \\
11441
- ca-certificates \\
11442
- && rm -rf /var/lib/apt/lists/*
11443
-
11444
- # Install ast-grep for code search/refactoring
11445
- RUN curl -fsSL https://raw.githubusercontent.com/ast-grep/ast-grep/main/install.sh | bash \\
11446
- && mv /root/.local/bin/ast-grep /usr/local/bin/ 2>/dev/null || true \\
11447
- && mv /root/.local/bin/sg /usr/local/bin/ 2>/dev/null || true
11448
-
11449
- # Working directory (host CWD will be mounted here)
11450
- WORKDIR /workspace
11451
-
11452
- # Entry point - run llmist from mounted source
11453
- # Source is mounted at ${DEV_SOURCE_MOUNT_TARGET}
11454
- ENTRYPOINT ["bun", "run", "${DEV_SOURCE_MOUNT_TARGET}/src/cli.ts"]
11455
- `;
11456
- function resolveDockerfile(config, devMode = false) {
11457
- if (config.dockerfile) {
11458
- return config.dockerfile;
11459
- }
11460
- return devMode ? DEV_DOCKERFILE : DEFAULT_DOCKERFILE;
11535
+ function resolveDockerfile(config) {
11536
+ return config.dockerfile ?? DEFAULT_DOCKERFILE;
11461
11537
  }
11462
11538
  function computeDockerfileHash(dockerfile) {
11463
11539
  const encoder = new TextEncoder();
@@ -11519,10 +11595,13 @@ async function buildImage(imageName, dockerfile) {
11519
11595
  ensureCacheDir();
11520
11596
  const dockerfilePath = (0, import_node_path5.join)(CACHE_DIR, "Dockerfile");
11521
11597
  (0, import_node_fs4.writeFileSync)(dockerfilePath, dockerfile);
11522
- const proc = Bun.spawn(["docker", "build", "-t", imageName, "-f", dockerfilePath, CACHE_DIR], {
11523
- stdout: "pipe",
11524
- stderr: "pipe"
11525
- });
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
+ );
11526
11605
  const exitCode = await proc.exited;
11527
11606
  const stdout = await new Response(proc.stdout).text();
11528
11607
  const stderr = await new Response(proc.stderr).text();
@@ -11584,46 +11663,13 @@ function isInsideContainer() {
11584
11663
  }
11585
11664
  return false;
11586
11665
  }
11587
- function autoDetectDevSource() {
11588
- const scriptPath = process.argv[1];
11589
- if (!scriptPath || !scriptPath.endsWith("src/cli.ts")) {
11590
- return void 0;
11591
- }
11592
- const srcDir = (0, import_node_path6.dirname)(scriptPath);
11593
- const projectDir = (0, import_node_path6.dirname)(srcDir);
11594
- const packageJsonPath = (0, import_node_path6.join)(projectDir, "package.json");
11595
- if (!(0, import_node_fs5.existsSync)(packageJsonPath)) {
11596
- return void 0;
11597
- }
11598
- try {
11599
- const pkg = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8"));
11600
- if (pkg.name === "llmist") {
11601
- return projectDir;
11602
- }
11603
- } catch {
11604
- }
11605
- return void 0;
11606
- }
11607
- function resolveDevMode(config, cliDevMode) {
11608
- const enabled = cliDevMode || config?.["dev-mode"] || process.env.LLMIST_DEV_MODE === "1";
11609
- if (!enabled) {
11610
- return { enabled: false, sourcePath: void 0 };
11611
- }
11612
- const sourcePath = config?.["dev-source"] || process.env.LLMIST_DEV_SOURCE || autoDetectDevSource();
11613
- if (!sourcePath) {
11614
- throw new Error(
11615
- "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)."
11616
- );
11617
- }
11618
- return { enabled: true, sourcePath };
11619
- }
11620
11666
  function expandHome(path6) {
11621
11667
  if (path6.startsWith("~")) {
11622
11668
  return path6.replace(/^~/, (0, import_node_os4.homedir)());
11623
11669
  }
11624
11670
  return path6;
11625
11671
  }
11626
- function buildDockerRunArgs(ctx, imageName, devMode) {
11672
+ function buildDockerRunArgs(ctx, imageName) {
11627
11673
  const args = ["run", "--rm"];
11628
11674
  const timestamp = Date.now();
11629
11675
  const random = Math.random().toString(36).slice(2, 8);
@@ -11637,11 +11683,15 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
11637
11683
  args.push("-w", "/workspace");
11638
11684
  const configPermission = ctx.config["config-permission"] ?? DEFAULT_CONFIG_PERMISSION;
11639
11685
  const llmistDir = expandHome("~/.llmist");
11640
- args.push("-v", `${llmistDir}:/root/.llmist:${configPermission}`);
11641
- if (devMode.enabled && devMode.sourcePath) {
11642
- const expandedSource = expandHome(devMode.sourcePath);
11643
- 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}`);
11644
11689
  }
11690
+ const gadgetsDir = `${llmistDir}/gadgets`;
11691
+ if ((0, import_node_fs5.existsSync)(gadgetsDir)) {
11692
+ args.push("-v", `${gadgetsDir}:/root/.llmist/gadgets:${configPermission}`);
11693
+ }
11694
+ args.push("-v", `${GADGET_CACHE_VOLUME}:/root/.llmist/gadget-cache`);
11645
11695
  if (ctx.config.mounts) {
11646
11696
  for (const mount of ctx.config.mounts) {
11647
11697
  const source = expandHome(mount.source);
@@ -11668,7 +11718,7 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
11668
11718
  return args;
11669
11719
  }
11670
11720
  function filterDockerArgs(argv) {
11671
- const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker", "--docker-dev"]);
11721
+ const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker"]);
11672
11722
  return argv.filter((arg) => !dockerFlags.has(arg));
11673
11723
  }
11674
11724
  function resolveDockerEnabled(config, options, profileDocker) {
@@ -11683,22 +11733,16 @@ function resolveDockerEnabled(config, options, profileDocker) {
11683
11733
  }
11684
11734
  return config?.enabled ?? false;
11685
11735
  }
11686
- async function executeInDocker(ctx, devMode) {
11736
+ async function executeInDocker(ctx) {
11687
11737
  if (isInsideContainer()) {
11688
- console.error(
11689
- "Warning: Docker mode requested but already inside a container. Proceeding without re-containerization."
11690
- );
11691
11738
  throw new DockerSkipError();
11692
11739
  }
11693
11740
  const available = await checkDockerAvailable();
11694
11741
  if (!available) {
11695
11742
  throw new DockerUnavailableError();
11696
11743
  }
11697
- const dockerfile = resolveDockerfile(ctx.config, devMode.enabled);
11698
- const imageName = devMode.enabled ? DEV_IMAGE_NAME : ctx.config["image-name"] ?? DEFAULT_IMAGE_NAME;
11699
- if (devMode.enabled) {
11700
- console.error(`[dev mode] Mounting source from ${devMode.sourcePath}`);
11701
- }
11744
+ const dockerfile = resolveDockerfile(ctx.config);
11745
+ const imageName = ctx.config["image-name"] ?? DEFAULT_IMAGE_NAME;
11702
11746
  try {
11703
11747
  await ensureImage(imageName, dockerfile);
11704
11748
  } catch (error) {
@@ -11709,7 +11753,7 @@ async function executeInDocker(ctx, devMode) {
11709
11753
  }
11710
11754
  throw error;
11711
11755
  }
11712
- const dockerArgs = buildDockerRunArgs(ctx, imageName, devMode);
11756
+ const dockerArgs = buildDockerRunArgs(ctx, imageName);
11713
11757
  const proc = Bun.spawn(["docker", ...dockerArgs], {
11714
11758
  stdin: "inherit",
11715
11759
  stdout: "inherit",
@@ -11730,7 +11774,7 @@ function createDockerContext(config, options, argv, cwd, profileCwdPermission) {
11730
11774
 
11731
11775
  // src/cli/file-utils.ts
11732
11776
  var import_promises3 = require("fs/promises");
11733
- var import_node_path7 = require("path");
11777
+ var import_node_path6 = require("path");
11734
11778
  init_input_content();
11735
11779
  var DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024;
11736
11780
  function formatFileSize(bytes) {
@@ -11748,7 +11792,7 @@ async function checkFileSize(absolutePath, filePath, maxSize) {
11748
11792
  }
11749
11793
  }
11750
11794
  async function readImageFile(filePath, options = {}) {
11751
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11795
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11752
11796
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11753
11797
  let buffer;
11754
11798
  try {
@@ -11767,7 +11811,7 @@ async function readImageFile(filePath, options = {}) {
11767
11811
  return imageFromBuffer(buffer, mimeType);
11768
11812
  }
11769
11813
  async function readAudioFile(filePath, options = {}) {
11770
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11814
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11771
11815
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11772
11816
  let buffer;
11773
11817
  try {
@@ -11786,7 +11830,7 @@ async function readAudioFile(filePath, options = {}) {
11786
11830
  return audioFromBuffer(buffer, mimeType);
11787
11831
  }
11788
11832
  async function readFileBuffer(filePath, options = {}) {
11789
- const absolutePath = (0, import_node_path7.resolve)(filePath);
11833
+ const absolutePath = (0, import_node_path6.resolve)(filePath);
11790
11834
  const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
11791
11835
  try {
11792
11836
  await checkFileSize(absolutePath, filePath, maxFileSize);
@@ -11799,7 +11843,7 @@ async function readFileBuffer(filePath, options = {}) {
11799
11843
 
11800
11844
  // src/cli/gadgets.ts
11801
11845
  var import_node_fs11 = __toESM(require("fs"), 1);
11802
- var import_node_path12 = __toESM(require("path"), 1);
11846
+ var import_node_path11 = __toESM(require("path"), 1);
11803
11847
  var import_node_url2 = require("url");
11804
11848
  init_gadget();
11805
11849
 
@@ -11879,7 +11923,7 @@ init_gadget();
11879
11923
 
11880
11924
  // src/cli/builtins/filesystem/utils.ts
11881
11925
  var import_node_fs6 = __toESM(require("fs"), 1);
11882
- var import_node_path8 = __toESM(require("path"), 1);
11926
+ var import_node_path7 = __toESM(require("path"), 1);
11883
11927
  var PathSandboxException = class extends Error {
11884
11928
  constructor(inputPath, reason) {
11885
11929
  super(`Path access denied: ${inputPath}. ${reason}`);
@@ -11888,7 +11932,7 @@ var PathSandboxException = class extends Error {
11888
11932
  };
11889
11933
  function validatePathIsWithinCwd(inputPath) {
11890
11934
  const cwd = process.cwd();
11891
- const resolvedPath = import_node_path8.default.resolve(cwd, inputPath);
11935
+ const resolvedPath = import_node_path7.default.resolve(cwd, inputPath);
11892
11936
  let finalPath;
11893
11937
  try {
11894
11938
  finalPath = import_node_fs6.default.realpathSync(resolvedPath);
@@ -11900,7 +11944,7 @@ function validatePathIsWithinCwd(inputPath) {
11900
11944
  throw error;
11901
11945
  }
11902
11946
  }
11903
- const cwdWithSep = cwd + import_node_path8.default.sep;
11947
+ const cwdWithSep = cwd + import_node_path7.default.sep;
11904
11948
  if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
11905
11949
  throw new PathSandboxException(inputPath, "Path is outside the current working directory");
11906
11950
  }
@@ -12003,15 +12047,15 @@ error: ${message}`;
12003
12047
 
12004
12048
  // src/cli/builtins/filesystem/list-directory.ts
12005
12049
  var import_node_fs7 = __toESM(require("fs"), 1);
12006
- var import_node_path9 = __toESM(require("path"), 1);
12050
+ var import_node_path8 = __toESM(require("path"), 1);
12007
12051
  var import_zod5 = require("zod");
12008
12052
  function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
12009
12053
  const entries = [];
12010
12054
  try {
12011
12055
  const items = import_node_fs7.default.readdirSync(dirPath);
12012
12056
  for (const item of items) {
12013
- const fullPath = import_node_path9.default.join(dirPath, item);
12014
- 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);
12015
12059
  try {
12016
12060
  const stats = import_node_fs7.default.lstatSync(fullPath);
12017
12061
  let type;
@@ -12155,7 +12199,7 @@ ${content}`;
12155
12199
 
12156
12200
  // src/cli/builtins/filesystem/write-file.ts
12157
12201
  var import_node_fs9 = __toESM(require("fs"), 1);
12158
- var import_node_path10 = __toESM(require("path"), 1);
12202
+ var import_node_path9 = __toESM(require("path"), 1);
12159
12203
  var import_zod7 = require("zod");
12160
12204
  var writeFile2 = createGadget({
12161
12205
  name: "WriteFile",
@@ -12190,7 +12234,7 @@ console.log(\`Server running on http://localhost:\${port}\`);`
12190
12234
  ],
12191
12235
  execute: ({ filePath, content }) => {
12192
12236
  const validatedPath = validatePathIsWithinCwd(filePath);
12193
- const parentDir = import_node_path10.default.dirname(validatedPath);
12237
+ const parentDir = import_node_path9.default.dirname(validatedPath);
12194
12238
  let createdDir = false;
12195
12239
  if (!import_node_fs9.default.existsSync(parentDir)) {
12196
12240
  validatePathIsWithinCwd(parentDir);
@@ -12199,7 +12243,7 @@ console.log(\`Server running on http://localhost:\${port}\`);`
12199
12243
  }
12200
12244
  import_node_fs9.default.writeFileSync(validatedPath, content, "utf-8");
12201
12245
  const bytesWritten = Buffer.byteLength(content, "utf-8");
12202
- const dirNote = createdDir ? ` (created directory: ${import_node_path10.default.dirname(filePath)})` : "";
12246
+ const dirNote = createdDir ? ` (created directory: ${import_node_path9.default.dirname(filePath)})` : "";
12203
12247
  return `path=${filePath}
12204
12248
 
12205
12249
  Wrote ${bytesWritten} bytes${dirNote}`;
@@ -12328,10 +12372,10 @@ function isBuiltinGadgetName(name) {
12328
12372
  // src/cli/external-gadgets.ts
12329
12373
  var import_node_child_process = require("child_process");
12330
12374
  var import_node_fs10 = __toESM(require("fs"), 1);
12331
- var import_node_path11 = __toESM(require("path"), 1);
12375
+ var import_node_path10 = __toESM(require("path"), 1);
12332
12376
  var import_node_os5 = __toESM(require("os"), 1);
12333
12377
  var import_node_url = require("url");
12334
- 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");
12335
12379
  function isExternalPackageSpecifier(specifier) {
12336
12380
  if (/^@?[a-z0-9][\w.-]*(?:@[\w.-]+)?(?::[a-z]+)?(?:\/\w+)?$/i.test(specifier)) {
12337
12381
  return true;
@@ -12369,13 +12413,13 @@ function parseGadgetSpecifier(specifier) {
12369
12413
  function getCacheDir(spec) {
12370
12414
  const versionSuffix = spec.version ? `@${spec.version}` : "@latest";
12371
12415
  if (spec.type === "npm") {
12372
- 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}`);
12373
12417
  }
12374
12418
  const sanitizedUrl = spec.package.replace(/[/:]/g, "-").replace(/^-+|-+$/g, "");
12375
- return import_node_path11.default.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
12419
+ return import_node_path10.default.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
12376
12420
  }
12377
12421
  function isCached(cacheDir) {
12378
- const packageJsonPath = import_node_path11.default.join(cacheDir, "package.json");
12422
+ const packageJsonPath = import_node_path10.default.join(cacheDir, "package.json");
12379
12423
  return import_node_fs10.default.existsSync(packageJsonPath);
12380
12424
  }
12381
12425
  async function installNpmPackage(spec, cacheDir) {
@@ -12385,10 +12429,10 @@ async function installNpmPackage(spec, cacheDir) {
12385
12429
  private: true,
12386
12430
  type: "module"
12387
12431
  };
12388
- 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));
12389
12433
  const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
12390
12434
  try {
12391
- (0, import_node_child_process.execSync)(`npm install --prefix "${cacheDir}" "${packageSpec}" --save`, {
12435
+ (0, import_node_child_process.execSync)(`bun add "${packageSpec}"`, {
12392
12436
  stdio: "pipe",
12393
12437
  cwd: cacheDir
12394
12438
  });
@@ -12398,7 +12442,7 @@ async function installNpmPackage(spec, cacheDir) {
12398
12442
  }
12399
12443
  }
12400
12444
  async function installGitPackage(spec, cacheDir) {
12401
- 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 });
12402
12446
  if (import_node_fs10.default.existsSync(cacheDir)) {
12403
12447
  try {
12404
12448
  (0, import_node_child_process.execSync)("git fetch", { cwd: cacheDir, stdio: "pipe" });
@@ -12417,17 +12461,17 @@ async function installGitPackage(spec, cacheDir) {
12417
12461
  const message = error instanceof Error ? error.message : String(error);
12418
12462
  throw new Error(`Failed to clone git repository '${spec.package}': ${message}`);
12419
12463
  }
12420
- 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"))) {
12421
12465
  try {
12422
- (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" });
12423
12467
  } catch (error) {
12424
12468
  const message = error instanceof Error ? error.message : String(error);
12425
12469
  throw new Error(`Failed to install dependencies for '${spec.package}': ${message}`);
12426
12470
  }
12427
12471
  try {
12428
- 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"));
12429
12473
  if (packageJson.scripts?.build) {
12430
- (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" });
12431
12475
  }
12432
12476
  } catch (error) {
12433
12477
  const message = error instanceof Error ? error.message : String(error);
@@ -12437,7 +12481,7 @@ async function installGitPackage(spec, cacheDir) {
12437
12481
  }
12438
12482
  }
12439
12483
  function readManifest(packageDir) {
12440
- const packageJsonPath = import_node_path11.default.join(packageDir, "package.json");
12484
+ const packageJsonPath = import_node_path10.default.join(packageDir, "package.json");
12441
12485
  if (!import_node_fs10.default.existsSync(packageJsonPath)) {
12442
12486
  return null;
12443
12487
  }
@@ -12449,7 +12493,7 @@ function readManifest(packageDir) {
12449
12493
  }
12450
12494
  }
12451
12495
  function getPackagePath(cacheDir, packageName) {
12452
- const nodeModulesPath = import_node_path11.default.join(cacheDir, "node_modules", packageName);
12496
+ const nodeModulesPath = import_node_path10.default.join(cacheDir, "node_modules", packageName);
12453
12497
  if (import_node_fs10.default.existsSync(nodeModulesPath)) {
12454
12498
  return nodeModulesPath;
12455
12499
  }
@@ -12493,7 +12537,7 @@ async function loadExternalGadgets(specifier, forceInstall = false) {
12493
12537
  } else {
12494
12538
  entryPoint = manifest?.gadgets || "./dist/index.js";
12495
12539
  }
12496
- const resolvedEntryPoint = import_node_path11.default.resolve(packagePath, entryPoint);
12540
+ const resolvedEntryPoint = import_node_path10.default.resolve(packagePath, entryPoint);
12497
12541
  if (!import_node_fs10.default.existsSync(resolvedEntryPoint)) {
12498
12542
  throw new Error(
12499
12543
  `Entry point not found: ${resolvedEntryPoint}. Make sure the package is built (run 'npm run build' in the package directory).`
@@ -12562,10 +12606,10 @@ function expandHomePath(input) {
12562
12606
  if (!home) {
12563
12607
  return input;
12564
12608
  }
12565
- return import_node_path12.default.join(home, input.slice(1));
12609
+ return import_node_path11.default.join(home, input.slice(1));
12566
12610
  }
12567
12611
  function isFileLikeSpecifier(specifier) {
12568
- 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);
12569
12613
  }
12570
12614
  function tryResolveBuiltin(specifier) {
12571
12615
  if (specifier.startsWith(BUILTIN_PREFIX)) {
@@ -12588,7 +12632,7 @@ function resolveGadgetSpecifier(specifier, cwd) {
12588
12632
  return specifier;
12589
12633
  }
12590
12634
  const expanded = expandHomePath(specifier);
12591
- const resolvedPath = import_node_path12.default.resolve(cwd, expanded);
12635
+ const resolvedPath = import_node_path11.default.resolve(cwd, expanded);
12592
12636
  if (!import_node_fs11.default.existsSync(resolvedPath)) {
12593
12637
  throw new Error(`Gadget module not found at ${resolvedPath}`);
12594
12638
  }
@@ -12673,12 +12717,12 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
12673
12717
  // src/cli/llm-logging.ts
12674
12718
  var import_promises4 = require("fs/promises");
12675
12719
  var import_node_os6 = require("os");
12676
- var import_node_path13 = require("path");
12720
+ var import_node_path12 = require("path");
12677
12721
  init_messages();
12678
- 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");
12679
12723
  function resolveLogDir(option, subdir) {
12680
12724
  if (option === true) {
12681
- return (0, import_node_path13.join)(DEFAULT_LLM_LOG_DIR, subdir);
12725
+ return (0, import_node_path12.join)(DEFAULT_LLM_LOG_DIR, subdir);
12682
12726
  }
12683
12727
  if (typeof option === "string") {
12684
12728
  return option;
@@ -12696,7 +12740,7 @@ function formatLlmRequest(messages) {
12696
12740
  }
12697
12741
  async function writeLogFile(dir, filename, content) {
12698
12742
  await (0, import_promises4.mkdir)(dir, { recursive: true });
12699
- 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");
12700
12744
  }
12701
12745
  function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
12702
12746
  const pad = (n) => n.toString().padStart(2, "0");
@@ -12710,7 +12754,7 @@ function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
12710
12754
  }
12711
12755
  async function createSessionDir(baseDir) {
12712
12756
  const timestamp = formatSessionTimestamp();
12713
- const sessionDir = (0, import_node_path13.join)(baseDir, timestamp);
12757
+ const sessionDir = (0, import_node_path12.join)(baseDir, timestamp);
12714
12758
  try {
12715
12759
  await (0, import_promises4.mkdir)(sessionDir, { recursive: true });
12716
12760
  return sessionDir;
@@ -12803,6 +12847,45 @@ function formatCost(cost) {
12803
12847
  }
12804
12848
  return cost.toFixed(2);
12805
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
+ }
12806
12889
  function renderSummary(metadata) {
12807
12890
  const parts = [];
12808
12891
  if (metadata.iterations !== void 0) {
@@ -12859,23 +12942,84 @@ function renderOverallSummary(metadata) {
12859
12942
  }
12860
12943
  return parts.join(import_chalk3.default.dim(" | "));
12861
12944
  }
12862
- function formatParametersInline(params) {
12945
+ function getRawValue(value) {
12946
+ if (typeof value === "string") {
12947
+ return value;
12948
+ }
12949
+ if (typeof value === "boolean" || typeof value === "number") {
12950
+ return String(value);
12951
+ }
12952
+ return JSON.stringify(value);
12953
+ }
12954
+ function truncateValue(str, maxLen) {
12955
+ if (maxLen <= 0) return "";
12956
+ if (str.length <= maxLen) return str;
12957
+ return `${str.slice(0, maxLen)}\u2026`;
12958
+ }
12959
+ function formatParametersInline(params, maxWidth) {
12863
12960
  if (!params || Object.keys(params).length === 0) {
12864
12961
  return "";
12865
12962
  }
12866
- return Object.entries(params).map(([key, value]) => {
12867
- let formatted;
12868
- if (typeof value === "string") {
12869
- formatted = value.length > 30 ? `${value.slice(0, 30)}\u2026` : value;
12870
- } else if (typeof value === "boolean" || typeof value === "number") {
12871
- formatted = String(value);
12963
+ const entries = Object.entries(params);
12964
+ const defaultLimit = 30;
12965
+ const rawValues = entries.map(([, value]) => getRawValue(value));
12966
+ const overhead = entries.reduce((sum, [key], i) => {
12967
+ return sum + key.length + 1 + (i > 0 ? 2 : 0);
12968
+ }, 0);
12969
+ let limits;
12970
+ if (maxWidth && maxWidth > overhead) {
12971
+ const availableForValues = maxWidth - overhead;
12972
+ const totalRawLength = rawValues.reduce((sum, v) => sum + v.length, 0);
12973
+ if (totalRawLength <= availableForValues) {
12974
+ limits = rawValues.map(() => Infinity);
12872
12975
  } else {
12873
- const json = JSON.stringify(value);
12874
- formatted = json.length > 30 ? `${json.slice(0, 30)}\u2026` : json;
12976
+ const minPerValue = 10;
12977
+ const minTotal = entries.length * minPerValue;
12978
+ if (availableForValues <= minTotal) {
12979
+ limits = rawValues.map(() => Math.max(1, Math.floor(availableForValues / entries.length)));
12980
+ } else {
12981
+ limits = rawValues.map((v) => {
12982
+ const proportion = v.length / totalRawLength;
12983
+ return Math.max(minPerValue, Math.floor(proportion * availableForValues));
12984
+ });
12985
+ }
12875
12986
  }
12987
+ } else {
12988
+ limits = rawValues.map(() => defaultLimit);
12989
+ }
12990
+ return entries.map(([key, _], i) => {
12991
+ const formatted = truncateValue(rawValues[i], limits[i]);
12876
12992
  return `${import_chalk3.default.dim(key)}${import_chalk3.default.dim("=")}${import_chalk3.default.cyan(formatted)}`;
12877
12993
  }).join(import_chalk3.default.dim(", "));
12878
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
+ }
12879
13023
  function formatBytes(bytes) {
12880
13024
  if (bytes < 1024) {
12881
13025
  return `${bytes} bytes`;
@@ -12908,25 +13052,28 @@ function formatMediaLine(media) {
12908
13052
  return `${import_chalk3.default.dim("[")}${icon} ${id} ${mimeType} ${size}${import_chalk3.default.dim("]")} ${import_chalk3.default.dim("\u2192")} ${path6}`;
12909
13053
  }
12910
13054
  function formatGadgetSummary2(result) {
13055
+ const terminalWidth = process.stdout.columns || 80;
12911
13056
  const gadgetLabel = import_chalk3.default.magenta.bold(result.gadgetName);
12912
- const timeLabel = import_chalk3.default.dim(
12913
- result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`
12914
- );
12915
- const paramsStr = formatParametersInline(result.parameters);
12916
- const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
12917
- if (result.error) {
12918
- const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
12919
- return `${import_chalk3.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.red("error:")} ${errorMsg} ${timeLabel}`;
12920
- }
12921
- let outputLabel;
13057
+ const timeStr = result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`;
13058
+ const timeLabel = import_chalk3.default.dim(timeStr);
13059
+ let outputStr;
12922
13060
  if (result.tokenCount !== void 0 && result.tokenCount > 0) {
12923
- outputLabel = import_chalk3.default.green(`${formatTokens(result.tokenCount)} tokens`);
13061
+ outputStr = `${formatTokens(result.tokenCount)} tokens`;
12924
13062
  } else if (result.result) {
12925
13063
  const outputBytes = Buffer.byteLength(result.result, "utf-8");
12926
- outputLabel = outputBytes > 0 ? import_chalk3.default.green(formatBytes(outputBytes)) : import_chalk3.default.dim("no output");
13064
+ outputStr = outputBytes > 0 ? formatBytes(outputBytes) : "no output";
12927
13065
  } else {
12928
- outputLabel = import_chalk3.default.dim("no output");
13066
+ outputStr = "no output";
13067
+ }
13068
+ const fixedLength = 2 + result.gadgetName.length + 2 + 3 + outputStr.length + 1 + timeStr.length;
13069
+ const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
13070
+ const paramsStr = formatParametersInline(result.parameters, availableForParams);
13071
+ const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
13072
+ if (result.error) {
13073
+ const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
13074
+ return `${import_chalk3.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.red("error:")} ${errorMsg} ${timeLabel}`;
12929
13075
  }
13076
+ const outputLabel = outputStr === "no output" ? import_chalk3.default.dim(outputStr) : import_chalk3.default.green(outputStr);
12930
13077
  const icon = result.breaksLoop ? import_chalk3.default.yellow("\u23F9") : import_chalk3.default.green("\u2713");
12931
13078
  let summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.dim("\u2192")} ${outputLabel} ${timeLabel}`;
12932
13079
  if (result.media && result.media.length > 0) {
@@ -13151,11 +13298,35 @@ var StreamProgress = class {
13151
13298
  }
13152
13299
  /**
13153
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
13154
13303
  */
13155
- updateNestedAgent(id, outputTokens) {
13304
+ updateNestedAgent(id, info) {
13156
13305
  const agent = this.nestedAgents.get(id);
13157
13306
  if (agent) {
13158
- 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();
13159
13330
  if (this.isRunning && this.isTTY) {
13160
13331
  this.render();
13161
13332
  }
@@ -13173,11 +13344,12 @@ var StreamProgress = class {
13173
13344
  /**
13174
13345
  * Add a nested gadget call (called when nested gadget_call event received).
13175
13346
  */
13176
- addNestedGadget(id, depth, parentInvocationId, name) {
13347
+ addNestedGadget(id, depth, parentInvocationId, name, parameters) {
13177
13348
  this.nestedGadgets.set(id, {
13178
13349
  depth,
13179
13350
  parentInvocationId,
13180
13351
  name,
13352
+ parameters,
13181
13353
  startTime: Date.now()
13182
13354
  });
13183
13355
  if (this.isRunning && this.isTTY) {
@@ -13193,6 +13365,20 @@ var StreamProgress = class {
13193
13365
  this.render();
13194
13366
  }
13195
13367
  }
13368
+ /**
13369
+ * Mark a nested gadget as completed (keeps it visible with ✓ indicator).
13370
+ * Records completion time to freeze the elapsed timer.
13371
+ */
13372
+ completeNestedGadget(id) {
13373
+ const gadget = this.nestedGadgets.get(id);
13374
+ if (gadget) {
13375
+ gadget.completed = true;
13376
+ gadget.completedTime = Date.now();
13377
+ if (this.isRunning && this.isTTY) {
13378
+ this.render();
13379
+ }
13380
+ }
13381
+ }
13196
13382
  /**
13197
13383
  * Starts a new LLM call. Switches to streaming mode.
13198
13384
  * @param model - Model name being used
@@ -13329,24 +13515,73 @@ var StreamProgress = class {
13329
13515
  }
13330
13516
  if (this.isTTY) {
13331
13517
  for (const [gadgetId, gadget] of this.inFlightGadgets) {
13332
- const elapsed = ((Date.now() - gadget.startTime) / 1e3).toFixed(1);
13333
- 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
+ })}`;
13334
13525
  lines.push(gadgetLine);
13526
+ const nestedOps = [];
13335
13527
  for (const [_agentId, nested] of this.nestedAgents) {
13336
- if (nested.parentInvocationId !== gadgetId) continue;
13337
- const indent = " ".repeat(nested.depth + 1);
13338
- const nestedElapsed = ((Date.now() - nested.startTime) / 1e3).toFixed(1);
13339
- const tokens = nested.inputTokens ? ` ${import_chalk4.default.dim("\u2191")}${import_chalk4.default.yellow(formatTokens(nested.inputTokens))}` : "";
13340
- const outTokens = nested.outputTokens ? ` ${import_chalk4.default.dim("\u2193")}${import_chalk4.default.green(formatTokens(nested.outputTokens))}` : "";
13341
- 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)}`;
13342
- lines.push(nestedLine);
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
+ }
13343
13544
  }
13344
13545
  for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
13345
13546
  if (nestedGadget.parentInvocationId === gadgetId) {
13346
- const indent = " ".repeat(nestedGadget.depth + 1);
13347
- const nestedElapsed = ((Date.now() - nestedGadget.startTime) / 1e3).toFixed(1);
13348
- const nestedGadgetLine = `${indent}${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.dim(nestedGadget.name + "(...)")} ${import_chalk4.default.dim(nestedElapsed + "s")}`;
13349
- 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}`);
13350
13585
  }
13351
13586
  }
13352
13587
  }
@@ -13368,42 +13603,27 @@ var StreamProgress = class {
13368
13603
  }
13369
13604
  /**
13370
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.
13371
13608
  */
13372
13609
  formatStreamingLine(spinner) {
13373
- const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
13374
13610
  const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
13375
- const parts = [];
13376
- const iterPart = import_chalk4.default.cyan(`#${this.currentIteration}`);
13377
- if (this.model) {
13378
- parts.push(`${iterPart} ${import_chalk4.default.magenta(this.model)}`);
13379
- } else {
13380
- parts.push(iterPart);
13381
- }
13382
- const usagePercent = this.getContextUsagePercent();
13383
- if (usagePercent !== null) {
13384
- const formatted = `${Math.round(usagePercent)}%`;
13385
- if (usagePercent >= 80) {
13386
- parts.push(import_chalk4.default.red(formatted));
13387
- } else if (usagePercent >= 50) {
13388
- parts.push(import_chalk4.default.yellow(formatted));
13389
- } else {
13390
- 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
13391
13625
  }
13392
- }
13393
- if (this.callInputTokens > 0) {
13394
- const prefix = this.callInputTokensEstimated ? "~" : "";
13395
- parts.push(import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
13396
- }
13397
- if (this.isStreaming || outTokens > 0) {
13398
- const prefix = this.callOutputTokensEstimated ? "~" : "";
13399
- parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
13400
- }
13401
- parts.push(import_chalk4.default.dim(`${elapsed}s`));
13402
- const callCost = this.calculateCurrentCallCost(outTokens);
13403
- if (callCost > 0) {
13404
- parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
13405
- }
13406
- return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
13626
+ });
13407
13627
  }
13408
13628
  /**
13409
13629
  * Calculates live cost estimate for the current streaming call.
@@ -13631,7 +13851,7 @@ function addAgentOptions(cmd, defaults) {
13631
13851
  OPTION_FLAGS.logLlmRequests,
13632
13852
  OPTION_DESCRIPTIONS.logLlmRequests,
13633
13853
  defaults?.["log-llm-requests"]
13634
- ).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);
13635
13855
  }
13636
13856
  function configToCompleteOptions(config) {
13637
13857
  const result = {};
@@ -13708,8 +13928,7 @@ async function executeAgent(promptArg, options, env) {
13708
13928
  const dockerOptions = {
13709
13929
  docker: options.docker ?? false,
13710
13930
  dockerRo: options.dockerRo ?? false,
13711
- noDocker: options.noDocker ?? false,
13712
- dockerDev: options.dockerDev ?? false
13931
+ noDocker: options.noDocker ?? false
13713
13932
  };
13714
13933
  const dockerEnabled = resolveDockerEnabled(
13715
13934
  env.dockerConfig,
@@ -13718,7 +13937,6 @@ async function executeAgent(promptArg, options, env) {
13718
13937
  // Profile-level docker: true/false
13719
13938
  );
13720
13939
  if (dockerEnabled) {
13721
- const devMode = resolveDevMode(env.dockerConfig, dockerOptions.dockerDev);
13722
13940
  const ctx = createDockerContext(
13723
13941
  env.dockerConfig,
13724
13942
  dockerOptions,
@@ -13729,7 +13947,7 @@ async function executeAgent(promptArg, options, env) {
13729
13947
  // Profile-level CWD permission override
13730
13948
  );
13731
13949
  try {
13732
- await executeInDocker(ctx, devMode);
13950
+ await executeInDocker(ctx);
13733
13951
  } catch (error) {
13734
13952
  if (error instanceof DockerSkipError) {
13735
13953
  } else {
@@ -13756,9 +13974,18 @@ async function executeAgent(promptArg, options, env) {
13756
13974
  registry.registerByClass(gadget);
13757
13975
  }
13758
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
+ }
13759
13982
  const printer = new StreamPrinter(env.stdout);
13760
13983
  const stderrTTY = env.stderr.isTTY === true;
13761
- const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
13984
+ const progress = new StreamProgress(
13985
+ env.stderr,
13986
+ stderrTTY,
13987
+ client.modelRegistry
13988
+ );
13762
13989
  const abortController = new AbortController();
13763
13990
  let wasCancelled = false;
13764
13991
  let isStreaming = false;
@@ -13768,9 +13995,11 @@ async function executeAgent(promptArg, options, env) {
13768
13995
  wasCancelled = true;
13769
13996
  abortController.abort();
13770
13997
  progress.pause();
13771
- env.stderr.write(import_chalk5.default.yellow(`
13998
+ env.stderr.write(
13999
+ import_chalk5.default.yellow(`
13772
14000
  [Cancelled] ${progress.formatStats()}
13773
- `));
14001
+ `)
14002
+ );
13774
14003
  } else {
13775
14004
  handleQuit();
13776
14005
  }
@@ -13780,7 +14009,11 @@ async function executeAgent(promptArg, options, env) {
13780
14009
  cleanupSigint: null,
13781
14010
  restore: () => {
13782
14011
  if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
13783
- keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
14012
+ keyboard.cleanupEsc = createEscKeyListener(
14013
+ stdinStream,
14014
+ handleCancel,
14015
+ handleCancel
14016
+ );
13784
14017
  }
13785
14018
  }
13786
14019
  };
@@ -13805,7 +14038,11 @@ async function executeAgent(promptArg, options, env) {
13805
14038
  process.exit(130);
13806
14039
  };
13807
14040
  if (stdinIsInteractive && stdinStream.isTTY) {
13808
- keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
14041
+ keyboard.cleanupEsc = createEscKeyListener(
14042
+ stdinStream,
14043
+ handleCancel,
14044
+ handleCancel
14045
+ );
13809
14046
  }
13810
14047
  keyboard.cleanupSigint = createSigintListener(
13811
14048
  handleCancel,
@@ -13831,7 +14068,12 @@ async function executeAgent(promptArg, options, env) {
13831
14068
  gadgetApprovals,
13832
14069
  defaultMode: "allowed"
13833
14070
  };
13834
- const approvalManager = new ApprovalManager(approvalConfig, env, progress, keyboard);
14071
+ const approvalManager = new ApprovalManager(
14072
+ approvalConfig,
14073
+ env,
14074
+ progress,
14075
+ keyboard
14076
+ );
13835
14077
  let usage;
13836
14078
  let iterations = 0;
13837
14079
  const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
@@ -13841,7 +14083,10 @@ async function executeAgent(promptArg, options, env) {
13841
14083
  try {
13842
14084
  return await client.countTokens(model, messages);
13843
14085
  } catch {
13844
- 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
+ );
13845
14090
  return Math.round(totalChars / FALLBACK_CHARS_PER_TOKEN);
13846
14091
  }
13847
14092
  };
@@ -13863,7 +14108,9 @@ async function executeAgent(promptArg, options, env) {
13863
14108
  observers: {
13864
14109
  // onLLMCallStart: Start progress indicator for each LLM call
13865
14110
  // This showcases how to react to agent lifecycle events
14111
+ // Skip for subagent events (tracked separately via nested display)
13866
14112
  onLLMCallStart: async (context) => {
14113
+ if (context.subagentContext) return;
13867
14114
  isStreaming = true;
13868
14115
  llmCallCounter++;
13869
14116
  const inputTokens = await countMessagesTokens(
@@ -13889,7 +14136,9 @@ async function executeAgent(promptArg, options, env) {
13889
14136
  },
13890
14137
  // onStreamChunk: Real-time updates as LLM generates tokens
13891
14138
  // This enables responsive UIs that show progress during generation
14139
+ // Skip for subagent events (tracked separately via nested display)
13892
14140
  onStreamChunk: async (context) => {
14141
+ if (context.subagentContext) return;
13893
14142
  progress.update(context.accumulatedText.length);
13894
14143
  if (context.usage) {
13895
14144
  if (context.usage.inputTokens) {
@@ -13906,7 +14155,9 @@ async function executeAgent(promptArg, options, env) {
13906
14155
  },
13907
14156
  // onLLMCallComplete: Finalize metrics after each LLM call
13908
14157
  // This is where you'd typically log metrics or update dashboards
14158
+ // Skip progress updates for subagent events (tracked separately via nested display)
13909
14159
  onLLMCallComplete: async (context) => {
14160
+ if (context.subagentContext) return;
13910
14161
  isStreaming = false;
13911
14162
  usage = context.usage;
13912
14163
  iterations = Math.max(iterations, context.iteration + 1);
@@ -13935,7 +14186,7 @@ async function executeAgent(promptArg, options, env) {
13935
14186
  }
13936
14187
  const callElapsed = progress.getCallElapsedSeconds();
13937
14188
  progress.endCall(context.usage);
13938
- if (!options.quiet) {
14189
+ if (!options.quiet && !context.subagentContext) {
13939
14190
  const summary = renderSummary({
13940
14191
  iterations: context.iteration + 1,
13941
14192
  model: options.model,
@@ -13993,7 +14244,10 @@ ${ctx.gadgetName} is denied by configuration.`
13993
14244
  }
13994
14245
  return { action: "proceed" };
13995
14246
  }
13996
- const result = await approvalManager.requestApproval(ctx.gadgetName, ctx.parameters);
14247
+ const result = await approvalManager.requestApproval(
14248
+ ctx.gadgetName,
14249
+ ctx.parameters
14250
+ );
13997
14251
  if (!result.approved) {
13998
14252
  return {
13999
14253
  action: "skip",
@@ -14036,11 +14290,11 @@ Denied: ${result.reason ?? "by user"}`
14036
14290
  builder.withSyntheticGadgetCall(
14037
14291
  "TellUser",
14038
14292
  {
14039
- 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?",
14040
14294
  done: false,
14041
14295
  type: "info"
14042
14296
  },
14043
- "\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?"
14044
14298
  );
14045
14299
  builder.withTextOnlyHandler("acknowledge");
14046
14300
  builder.withTextWithGadgetsHandler({
@@ -14056,34 +14310,41 @@ Denied: ${result.reason ?? "by user"}`
14056
14310
  ].join(" ")
14057
14311
  );
14058
14312
  if (!options.quiet) {
14059
- builder.withNestedEventCallback((event) => {
14060
- if (event.type === "llm_call_start") {
14061
- const info = event.event;
14062
- const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
14313
+ builder.withSubagentEventCallback((subagentEvent) => {
14314
+ if (subagentEvent.type === "llm_call_start") {
14315
+ const info = subagentEvent.event;
14316
+ const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
14063
14317
  progress.addNestedAgent(
14064
- nestedId,
14065
- event.gadgetInvocationId,
14066
- event.depth,
14318
+ subagentId,
14319
+ subagentEvent.gadgetInvocationId,
14320
+ subagentEvent.depth,
14067
14321
  info.model,
14068
14322
  info.iteration,
14069
14323
  info.inputTokens
14070
14324
  );
14071
- } else if (event.type === "llm_call_end") {
14072
- const info = event.event;
14073
- const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
14074
- progress.updateNestedAgent(nestedId, info.outputTokens);
14075
- setTimeout(() => progress.removeNestedAgent(nestedId), 100);
14076
- } else if (event.type === "gadget_call") {
14077
- const gadgetEvent = event.event;
14325
+ } else if (subagentEvent.type === "llm_call_end") {
14326
+ const info = subagentEvent.event;
14327
+ const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
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
+ });
14336
+ } else if (subagentEvent.type === "gadget_call") {
14337
+ const gadgetEvent = subagentEvent.event;
14078
14338
  progress.addNestedGadget(
14079
14339
  gadgetEvent.call.invocationId,
14080
- event.depth,
14081
- event.gadgetInvocationId,
14082
- gadgetEvent.call.gadgetName
14340
+ subagentEvent.depth,
14341
+ subagentEvent.gadgetInvocationId,
14342
+ gadgetEvent.call.gadgetName,
14343
+ gadgetEvent.call.parameters
14083
14344
  );
14084
- } else if (event.type === "gadget_result") {
14085
- const resultEvent = event.event;
14086
- progress.removeNestedGadget(resultEvent.result.invocationId);
14345
+ } else if (subagentEvent.type === "gadget_result") {
14346
+ const resultEvent = subagentEvent.event;
14347
+ progress.completeNestedGadget(resultEvent.result.invocationId);
14087
14348
  }
14088
14349
  });
14089
14350
  }
@@ -14144,6 +14405,7 @@ Denied: ${result.reason ?? "by user"}`
14144
14405
  if (progress.hasInFlightGadgets()) {
14145
14406
  progress.start();
14146
14407
  }
14408
+ } else if (event.type === "subagent_event") {
14147
14409
  }
14148
14410
  }
14149
14411
  } catch (error) {
@@ -14177,7 +14439,10 @@ Denied: ${result.reason ?? "by user"}`
14177
14439
  }
14178
14440
  }
14179
14441
  function registerAgentCommand(program, env, config, globalSubagents) {
14180
- 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
+ );
14181
14446
  addAgentOptions(cmd, config);
14182
14447
  cmd.action(
14183
14448
  (prompt, options) => executeAction(() => {
@@ -14287,7 +14552,7 @@ function registerCompleteCommand(program, env, config) {
14287
14552
 
14288
14553
  // src/cli/init-command.ts
14289
14554
  var import_node_fs12 = require("fs");
14290
- var import_node_path14 = require("path");
14555
+ var import_node_path13 = require("path");
14291
14556
  var STARTER_CONFIG = `# ~/.llmist/cli.toml
14292
14557
  # llmist CLI configuration file
14293
14558
  #
@@ -14343,7 +14608,7 @@ var STARTER_CONFIG = `# ~/.llmist/cli.toml
14343
14608
  `;
14344
14609
  async function executeInit(_options, env) {
14345
14610
  const configPath = getConfigPath();
14346
- const configDir = (0, import_node_path14.dirname)(configPath);
14611
+ const configDir = (0, import_node_path13.dirname)(configPath);
14347
14612
  if ((0, import_node_fs12.existsSync)(configPath)) {
14348
14613
  env.stderr.write(`Configuration already exists at ${configPath}
14349
14614
  `);