llmist 6.1.0 → 7.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.
@@ -44,6 +44,9 @@ function parseEnvBoolean(value) {
44
44
  if (normalized === "false" || normalized === "0") return false;
45
45
  return void 0;
46
46
  }
47
+ function stripAnsi(str) {
48
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
49
+ }
47
50
  function createLogger(options = {}) {
48
51
  const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
49
52
  const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
@@ -52,36 +55,62 @@ function createLogger(options = {}) {
52
55
  const defaultType = options.type ?? "pretty";
53
56
  const name = options.name ?? "llmist";
54
57
  const logReset = options.logReset ?? envLogReset ?? false;
55
- let logFileStream;
56
- let finalType = defaultType;
57
- if (envLogFile) {
58
+ if (envLogFile && (!logFileInitialized || sharedLogFilePath !== envLogFile)) {
58
59
  try {
60
+ if (sharedLogFileStream) {
61
+ sharedLogFileStream.end();
62
+ sharedLogFileStream = void 0;
63
+ }
59
64
  mkdirSync(dirname(envLogFile), { recursive: true });
60
65
  const flags = logReset ? "w" : "a";
61
- logFileStream = createWriteStream(envLogFile, { flags });
62
- finalType = "hidden";
66
+ sharedLogFileStream = createWriteStream(envLogFile, { flags });
67
+ sharedLogFilePath = envLogFile;
68
+ logFileInitialized = true;
69
+ writeErrorCount = 0;
70
+ writeErrorReported = false;
71
+ sharedLogFileStream.on("error", (error) => {
72
+ writeErrorCount++;
73
+ if (!writeErrorReported) {
74
+ console.error(`[llmist] Log file write error: ${error.message}`);
75
+ writeErrorReported = true;
76
+ }
77
+ if (writeErrorCount >= MAX_WRITE_ERRORS_BEFORE_DISABLE) {
78
+ console.error(
79
+ `[llmist] Too many log file errors (${writeErrorCount}), disabling file logging`
80
+ );
81
+ sharedLogFileStream?.end();
82
+ sharedLogFileStream = void 0;
83
+ }
84
+ });
63
85
  } catch (error) {
64
86
  console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
65
87
  }
66
88
  }
89
+ const useFileLogging = Boolean(sharedLogFileStream);
67
90
  const logger = new Logger({
68
91
  name,
69
92
  minLevel,
70
- type: finalType,
71
- // Optimize for production
72
- hideLogPositionForProduction: finalType !== "pretty",
73
- // Pretty output settings
74
- prettyLogTemplate: finalType === "pretty" ? "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] " : void 0
93
+ type: useFileLogging ? "pretty" : defaultType,
94
+ // Hide log position for file logging and non-pretty types
95
+ hideLogPositionForProduction: useFileLogging || defaultType !== "pretty",
96
+ prettyLogTemplate: LOG_TEMPLATE,
97
+ // Use overwrite to redirect tslog's formatted output to file instead of console
98
+ overwrite: useFileLogging ? {
99
+ transportFormatted: (logMetaMarkup, logArgs, _logErrors) => {
100
+ if (!sharedLogFileStream) return;
101
+ const meta = stripAnsi(logMetaMarkup);
102
+ const args = logArgs.map(
103
+ (arg) => typeof arg === "string" ? stripAnsi(arg) : JSON.stringify(arg)
104
+ );
105
+ const line = `${meta}${args.join(" ")}
106
+ `;
107
+ sharedLogFileStream.write(line);
108
+ }
109
+ } : void 0
75
110
  });
76
- if (logFileStream) {
77
- logger.attachTransport((logObj) => {
78
- logFileStream?.write(`${JSON.stringify(logObj)}
79
- `);
80
- });
81
- }
82
111
  return logger;
83
112
  }
84
- var LEVEL_NAME_TO_ID, defaultLogger;
113
+ var LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
85
114
  var init_logger = __esm({
86
115
  "src/logging/logger.ts"() {
87
116
  "use strict";
@@ -94,6 +123,11 @@ var init_logger = __esm({
94
123
  error: 5,
95
124
  fatal: 6
96
125
  };
126
+ logFileInitialized = false;
127
+ writeErrorCount = 0;
128
+ writeErrorReported = false;
129
+ MAX_WRITE_ERRORS_BEFORE_DISABLE = 5;
130
+ LOG_TEMPLATE = "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] ";
97
131
  defaultLogger = createLogger();
98
132
  }
99
133
  });
@@ -3859,9 +3893,8 @@ var init_cost_reporting_client = __esm({
3859
3893
  */
3860
3894
  reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
3861
3895
  if (inputTokens === 0 && outputTokens === 0) return;
3862
- const modelName = model.includes(":") ? model.split(":")[1] : model;
3863
3896
  const estimate = this.client.modelRegistry.estimateCost(
3864
- modelName,
3897
+ model,
3865
3898
  inputTokens,
3866
3899
  outputTokens,
3867
3900
  cachedInputTokens,
@@ -4144,18 +4177,64 @@ var init_parser = __esm({
4144
4177
  }
4145
4178
  });
4146
4179
 
4180
+ // src/gadgets/typed-gadget.ts
4181
+ function Gadget(config) {
4182
+ class GadgetBase extends AbstractGadget {
4183
+ description = config.description;
4184
+ parameterSchema = config.schema;
4185
+ name = config.name;
4186
+ timeoutMs = config.timeoutMs;
4187
+ examples = config.examples;
4188
+ /**
4189
+ * Type helper property for accessing inferred parameter type.
4190
+ * This is used in the execute method signature: `execute(params: this['params'])`
4191
+ *
4192
+ * Note: This is just for type inference - the actual params in execute()
4193
+ * will be Record<string, unknown> which you can safely cast to this['params']
4194
+ */
4195
+ params;
4196
+ }
4197
+ return GadgetBase;
4198
+ }
4199
+ var init_typed_gadget = __esm({
4200
+ "src/gadgets/typed-gadget.ts"() {
4201
+ "use strict";
4202
+ init_gadget();
4203
+ }
4204
+ });
4205
+
4147
4206
  // src/gadgets/executor.ts
4148
- var GadgetExecutor;
4207
+ import equal from "fast-deep-equal";
4208
+ import { z as z4 } from "zod";
4209
+ function getHostExportsInternal() {
4210
+ if (!cachedHostExports) {
4211
+ cachedHostExports = {
4212
+ AgentBuilder,
4213
+ Gadget,
4214
+ createGadget,
4215
+ ExecutionTree,
4216
+ LLMist,
4217
+ z: z4
4218
+ };
4219
+ }
4220
+ return cachedHostExports;
4221
+ }
4222
+ var cachedHostExports, GadgetExecutor;
4149
4223
  var init_executor = __esm({
4150
4224
  "src/gadgets/executor.ts"() {
4151
4225
  "use strict";
4226
+ init_builder();
4227
+ init_client();
4152
4228
  init_constants();
4229
+ init_execution_tree();
4153
4230
  init_logger();
4154
4231
  init_block_params();
4155
4232
  init_cost_reporting_client();
4233
+ init_create_gadget();
4156
4234
  init_error_formatter();
4157
4235
  init_exceptions();
4158
4236
  init_parser();
4237
+ init_typed_gadget();
4159
4238
  GadgetExecutor = class {
4160
4239
  constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
4161
4240
  this.registry = registry;
@@ -4253,7 +4332,7 @@ var init_executor = __esm({
4253
4332
  try {
4254
4333
  const cleanedRaw = stripMarkdownFences(call.parametersRaw);
4255
4334
  const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
4256
- const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
4335
+ const parametersWereModified = !equal(rawParameters, initialParse);
4257
4336
  if (parametersWereModified) {
4258
4337
  this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
4259
4338
  gadgetName: call.gadgetName
@@ -4328,7 +4407,11 @@ var init_executor = __esm({
4328
4407
  // Tree context for subagent support - use gadget's own node ID
4329
4408
  tree: this.tree,
4330
4409
  nodeId: gadgetNodeId,
4331
- depth: gadgetDepth
4410
+ depth: gadgetDepth,
4411
+ // Host exports for external gadgets to use host's llmist classes
4412
+ hostExports: getHostExportsInternal(),
4413
+ // Logger for structured logging (respects CLI's log level/file config)
4414
+ logger: this.logger
4332
4415
  };
4333
4416
  let rawResult;
4334
4417
  if (timeoutMs && timeoutMs > 0) {
@@ -4498,27 +4581,6 @@ var init_executor = __esm({
4498
4581
  async executeAll(calls) {
4499
4582
  return Promise.all(calls.map((call) => this.execute(call)));
4500
4583
  }
4501
- /**
4502
- * Deep equality check for objects/arrays.
4503
- * Used to detect if parameters were modified by an interceptor.
4504
- */
4505
- deepEquals(a, b) {
4506
- if (a === b) return true;
4507
- if (a === null || b === null) return a === b;
4508
- if (typeof a !== typeof b) return false;
4509
- if (typeof a !== "object") return a === b;
4510
- if (Array.isArray(a) !== Array.isArray(b)) return false;
4511
- if (Array.isArray(a) && Array.isArray(b)) {
4512
- if (a.length !== b.length) return false;
4513
- return a.every((val, i) => this.deepEquals(val, b[i]));
4514
- }
4515
- const aObj = a;
4516
- const bObj = b;
4517
- const aKeys = Object.keys(aObj);
4518
- const bKeys = Object.keys(bObj);
4519
- if (aKeys.length !== bKeys.length) return false;
4520
- return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
4521
- }
4522
4584
  };
4523
4585
  }
4524
4586
  });
@@ -4556,6 +4618,11 @@ var init_stream_processor = __esm({
4556
4618
  inFlightExecutions = /* @__PURE__ */ new Map();
4557
4619
  /** Queue of completed gadget results ready to be yielded (for real-time streaming) */
4558
4620
  completedResultsQueue = [];
4621
+ // Cross-iteration dependency tracking
4622
+ /** Invocation IDs completed in previous iterations (read-only reference from Agent) */
4623
+ priorCompletedInvocations;
4624
+ /** Invocation IDs that failed in previous iterations (read-only reference from Agent) */
4625
+ priorFailedInvocations;
4559
4626
  constructor(options) {
4560
4627
  this.iteration = options.iteration;
4561
4628
  this.registry = options.registry;
@@ -4564,6 +4631,8 @@ var init_stream_processor = __esm({
4564
4631
  this.tree = options.tree;
4565
4632
  this.parentNodeId = options.parentNodeId ?? null;
4566
4633
  this.baseDepth = options.baseDepth ?? 0;
4634
+ this.priorCompletedInvocations = options.priorCompletedInvocations ?? /* @__PURE__ */ new Set();
4635
+ this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
4567
4636
  this.parser = new GadgetCallParser({
4568
4637
  startPrefix: options.gadgetStartPrefix,
4569
4638
  endPrefix: options.gadgetEndPrefix,
@@ -4762,62 +4831,11 @@ var init_stream_processor = __esm({
4762
4831
  }
4763
4832
  return [{ type: "text", content }];
4764
4833
  }
4765
- /**
4766
- * Process a gadget call through the full lifecycle, handling dependencies.
4767
- *
4768
- * Gadgets without dependencies (or with all dependencies satisfied) execute immediately.
4769
- * Gadgets with unsatisfied dependencies are queued for later execution.
4770
- * After each execution, pending gadgets are checked to see if they can now run.
4771
- */
4772
- async processGadgetCall(call) {
4773
- const events = [];
4774
- events.push({ type: "gadget_call", call });
4775
- if (call.dependencies.length > 0) {
4776
- if (call.dependencies.includes(call.invocationId)) {
4777
- this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
4778
- gadgetName: call.gadgetName,
4779
- invocationId: call.invocationId
4780
- });
4781
- this.failedInvocations.add(call.invocationId);
4782
- const skipEvent = {
4783
- type: "gadget_skipped",
4784
- gadgetName: call.gadgetName,
4785
- invocationId: call.invocationId,
4786
- parameters: call.parameters ?? {},
4787
- failedDependency: call.invocationId,
4788
- failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
4789
- };
4790
- events.push(skipEvent);
4791
- return events;
4792
- }
4793
- const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
4794
- if (failedDep) {
4795
- const skipEvents = await this.handleFailedDependency(call, failedDep);
4796
- events.push(...skipEvents);
4797
- return events;
4798
- }
4799
- const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
4800
- if (unsatisfied.length > 0) {
4801
- this.logger.debug("Queueing gadget for later - waiting on dependencies", {
4802
- gadgetName: call.gadgetName,
4803
- invocationId: call.invocationId,
4804
- waitingOn: unsatisfied
4805
- });
4806
- this.gadgetsAwaitingDependencies.set(call.invocationId, call);
4807
- return events;
4808
- }
4809
- }
4810
- const executeEvents = await this.executeGadgetWithHooks(call);
4811
- events.push(...executeEvents);
4812
- const triggeredEvents = await this.processPendingGadgets();
4813
- events.push(...triggeredEvents);
4814
- return events;
4815
- }
4816
4834
  /**
4817
4835
  * Process a gadget call, yielding events in real-time.
4818
4836
  *
4819
- * Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
4820
- * when parsed (before execution), enabling real-time UI feedback.
4837
+ * Yields gadget_call event IMMEDIATELY when parsed (before execution),
4838
+ * enabling real-time UI feedback.
4821
4839
  */
4822
4840
  async *processGadgetCallGenerator(call) {
4823
4841
  yield { type: "gadget_call", call };
@@ -4848,7 +4866,9 @@ var init_stream_processor = __esm({
4848
4866
  yield skipEvent;
4849
4867
  return;
4850
4868
  }
4851
- const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
4869
+ const failedDep = call.dependencies.find(
4870
+ (dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
4871
+ );
4852
4872
  if (failedDep) {
4853
4873
  const skipEvents = await this.handleFailedDependency(call, failedDep);
4854
4874
  for (const evt of skipEvents) {
@@ -4856,7 +4876,9 @@ var init_stream_processor = __esm({
4856
4876
  }
4857
4877
  return;
4858
4878
  }
4859
- const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
4879
+ const unsatisfied = call.dependencies.filter(
4880
+ (dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
4881
+ );
4860
4882
  if (unsatisfied.length > 0) {
4861
4883
  this.logger.debug("Queueing gadget for later - waiting on dependencies", {
4862
4884
  gadgetName: call.gadgetName,
@@ -4878,143 +4900,9 @@ var init_stream_processor = __esm({
4878
4900
  this.inFlightExecutions.set(call.invocationId, executionPromise);
4879
4901
  }
4880
4902
  /**
4881
- * Execute a gadget through the full hook lifecycle.
4882
- * This is the core execution logic, extracted from processGadgetCall.
4883
- * @deprecated Use executeGadgetGenerator for real-time streaming
4884
- */
4885
- async executeGadgetWithHooks(call) {
4886
- const events = [];
4887
- if (call.parseError) {
4888
- this.logger.warn("Gadget has parse error", {
4889
- gadgetName: call.gadgetName,
4890
- error: call.parseError,
4891
- rawParameters: call.parametersRaw
4892
- });
4893
- }
4894
- let parameters = call.parameters ?? {};
4895
- if (this.hooks.interceptors?.interceptGadgetParameters) {
4896
- const context = {
4897
- iteration: this.iteration,
4898
- gadgetName: call.gadgetName,
4899
- invocationId: call.invocationId,
4900
- logger: this.logger
4901
- };
4902
- parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
4903
- }
4904
- call.parameters = parameters;
4905
- let shouldSkip = false;
4906
- let syntheticResult;
4907
- if (this.hooks.controllers?.beforeGadgetExecution) {
4908
- const context = {
4909
- iteration: this.iteration,
4910
- gadgetName: call.gadgetName,
4911
- invocationId: call.invocationId,
4912
- parameters,
4913
- logger: this.logger
4914
- };
4915
- const action = await this.hooks.controllers.beforeGadgetExecution(context);
4916
- validateBeforeGadgetExecutionAction(action);
4917
- if (action.action === "skip") {
4918
- shouldSkip = true;
4919
- syntheticResult = action.syntheticResult;
4920
- this.logger.info("Controller skipped gadget execution", {
4921
- gadgetName: call.gadgetName
4922
- });
4923
- }
4924
- }
4925
- const startObservers = [];
4926
- if (this.hooks.observers?.onGadgetExecutionStart) {
4927
- startObservers.push(async () => {
4928
- const context = {
4929
- iteration: this.iteration,
4930
- gadgetName: call.gadgetName,
4931
- invocationId: call.invocationId,
4932
- parameters,
4933
- logger: this.logger
4934
- };
4935
- await this.hooks.observers?.onGadgetExecutionStart?.(context);
4936
- });
4937
- }
4938
- await this.runObserversInParallel(startObservers);
4939
- let result;
4940
- if (shouldSkip) {
4941
- result = {
4942
- gadgetName: call.gadgetName,
4943
- invocationId: call.invocationId,
4944
- parameters,
4945
- result: syntheticResult ?? "Execution skipped",
4946
- executionTimeMs: 0
4947
- };
4948
- } else {
4949
- result = await this.executor.execute(call);
4950
- }
4951
- const originalResult = result.result;
4952
- if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
4953
- const context = {
4954
- iteration: this.iteration,
4955
- gadgetName: result.gadgetName,
4956
- invocationId: result.invocationId,
4957
- parameters,
4958
- executionTimeMs: result.executionTimeMs,
4959
- logger: this.logger
4960
- };
4961
- result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
4962
- }
4963
- if (this.hooks.controllers?.afterGadgetExecution) {
4964
- const context = {
4965
- iteration: this.iteration,
4966
- gadgetName: result.gadgetName,
4967
- invocationId: result.invocationId,
4968
- parameters,
4969
- result: result.result,
4970
- error: result.error,
4971
- executionTimeMs: result.executionTimeMs,
4972
- logger: this.logger
4973
- };
4974
- const action = await this.hooks.controllers.afterGadgetExecution(context);
4975
- validateAfterGadgetExecutionAction(action);
4976
- if (action.action === "recover" && result.error) {
4977
- this.logger.info("Controller recovered from gadget error", {
4978
- gadgetName: result.gadgetName,
4979
- originalError: result.error
4980
- });
4981
- result = {
4982
- ...result,
4983
- error: void 0,
4984
- result: action.fallbackResult
4985
- };
4986
- }
4987
- }
4988
- const completeObservers = [];
4989
- if (this.hooks.observers?.onGadgetExecutionComplete) {
4990
- completeObservers.push(async () => {
4991
- const context = {
4992
- iteration: this.iteration,
4993
- gadgetName: result.gadgetName,
4994
- invocationId: result.invocationId,
4995
- parameters,
4996
- originalResult,
4997
- finalResult: result.result,
4998
- error: result.error,
4999
- executionTimeMs: result.executionTimeMs,
5000
- breaksLoop: result.breaksLoop,
5001
- cost: result.cost,
5002
- logger: this.logger
5003
- };
5004
- await this.hooks.observers?.onGadgetExecutionComplete?.(context);
5005
- });
5006
- }
5007
- await this.runObserversInParallel(completeObservers);
5008
- this.completedResults.set(result.invocationId, result);
5009
- if (result.error) {
5010
- this.failedInvocations.add(result.invocationId);
5011
- }
5012
- events.push({ type: "gadget_result", result });
5013
- return events;
5014
- }
5015
- /**
5016
- * Execute a gadget and yield the result event.
5017
- * Generator version that yields gadget_result immediately when execution completes.
4903
+ * Execute a gadget through the full hook lifecycle and yield events.
4904
+ * Handles parameter interception, before/after controllers, observers,
4905
+ * execution, result interception, and tree tracking.
5018
4906
  */
5019
4907
  async *executeGadgetGenerator(call) {
5020
4908
  if (call.parseError) {
@@ -5280,8 +5168,9 @@ var init_stream_processor = __esm({
5280
5168
  invocationId: call.invocationId,
5281
5169
  failedDependency: failedDep
5282
5170
  });
5283
- const executeEvents = await this.executeGadgetWithHooks(call);
5284
- events.push(...executeEvents);
5171
+ for await (const evt of this.executeGadgetGenerator(call)) {
5172
+ events.push(evt);
5173
+ }
5285
5174
  } else if (action.action === "use_fallback") {
5286
5175
  const fallbackResult = {
5287
5176
  gadgetName: call.gadgetName,
@@ -5302,90 +5191,9 @@ var init_stream_processor = __esm({
5302
5191
  }
5303
5192
  /**
5304
5193
  * Process pending gadgets whose dependencies are now satisfied.
5305
- * Executes ready gadgets in parallel and continues until no more can be triggered.
5306
- */
5307
- async processPendingGadgets() {
5308
- const events = [];
5309
- let progress = true;
5310
- while (progress && this.gadgetsAwaitingDependencies.size > 0) {
5311
- progress = false;
5312
- const readyToExecute = [];
5313
- const readyToSkip = [];
5314
- for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
5315
- const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
5316
- if (failedDep) {
5317
- readyToSkip.push({ call, failedDep });
5318
- continue;
5319
- }
5320
- const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
5321
- if (allSatisfied) {
5322
- readyToExecute.push(call);
5323
- }
5324
- }
5325
- for (const { call, failedDep } of readyToSkip) {
5326
- this.gadgetsAwaitingDependencies.delete(call.invocationId);
5327
- const skipEvents = await this.handleFailedDependency(call, failedDep);
5328
- events.push(...skipEvents);
5329
- progress = true;
5330
- }
5331
- if (readyToExecute.length > 0) {
5332
- this.logger.debug("Executing ready gadgets in parallel", {
5333
- count: readyToExecute.length,
5334
- invocationIds: readyToExecute.map((c) => c.invocationId)
5335
- });
5336
- for (const call of readyToExecute) {
5337
- this.gadgetsAwaitingDependencies.delete(call.invocationId);
5338
- }
5339
- const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
5340
- const results = await Promise.all(executePromises);
5341
- for (const executeEvents of results) {
5342
- events.push(...executeEvents);
5343
- }
5344
- progress = true;
5345
- }
5346
- }
5347
- if (this.gadgetsAwaitingDependencies.size > 0) {
5348
- const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
5349
- for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
5350
- const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
5351
- const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
5352
- const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
5353
- let errorMessage;
5354
- let logLevel = "warn";
5355
- if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
5356
- errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
5357
- logLevel = "error";
5358
- } else if (circularDeps.length > 0) {
5359
- errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
5360
- } else {
5361
- errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
5362
- }
5363
- this.logger[logLevel]("Gadget has unresolvable dependencies", {
5364
- gadgetName: call.gadgetName,
5365
- invocationId,
5366
- circularDependencies: circularDeps,
5367
- missingDependencies: trulyMissingDeps
5368
- });
5369
- this.failedInvocations.add(invocationId);
5370
- const skipEvent = {
5371
- type: "gadget_skipped",
5372
- gadgetName: call.gadgetName,
5373
- invocationId,
5374
- parameters: call.parameters ?? {},
5375
- failedDependency: missingDeps[0],
5376
- failedDependencyError: errorMessage
5377
- };
5378
- events.push(skipEvent);
5379
- }
5380
- this.gadgetsAwaitingDependencies.clear();
5381
- }
5382
- return events;
5383
- }
5384
- /**
5385
- * Process pending gadgets, yielding events in real-time.
5386
- * Generator version that yields events as gadgets complete.
5194
+ * Yields events in real-time as gadgets complete.
5387
5195
  *
5388
- * Note: Gadgets are still executed in parallel for efficiency,
5196
+ * Gadgets are executed in parallel for efficiency,
5389
5197
  * but results are yielded as they become available.
5390
5198
  */
5391
5199
  async *processPendingGadgetsGenerator() {
@@ -5395,12 +5203,16 @@ var init_stream_processor = __esm({
5395
5203
  const readyToExecute = [];
5396
5204
  const readyToSkip = [];
5397
5205
  for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
5398
- const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
5206
+ const failedDep = call.dependencies.find(
5207
+ (dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
5208
+ );
5399
5209
  if (failedDep) {
5400
5210
  readyToSkip.push({ call, failedDep });
5401
5211
  continue;
5402
5212
  }
5403
- const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
5213
+ const allSatisfied = call.dependencies.every(
5214
+ (dep) => this.completedResults.has(dep) || this.priorCompletedInvocations.has(dep)
5215
+ );
5404
5216
  if (allSatisfied) {
5405
5217
  readyToExecute.push(call);
5406
5218
  }
@@ -5441,7 +5253,9 @@ var init_stream_processor = __esm({
5441
5253
  if (this.gadgetsAwaitingDependencies.size > 0) {
5442
5254
  const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
5443
5255
  for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
5444
- const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
5256
+ const missingDeps = call.dependencies.filter(
5257
+ (dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
5258
+ );
5445
5259
  const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
5446
5260
  const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
5447
5261
  let errorMessage;
@@ -5495,10 +5309,27 @@ var init_stream_processor = __esm({
5495
5309
  */
5496
5310
  async runObserversInParallel(observers) {
5497
5311
  if (observers.length === 0) return;
5498
- const results = await Promise.allSettled(
5312
+ await Promise.allSettled(
5499
5313
  observers.map((observer) => this.safeObserve(observer))
5500
5314
  );
5501
5315
  }
5316
+ // ==========================================================================
5317
+ // Public accessors for cross-iteration dependency tracking
5318
+ // ==========================================================================
5319
+ /**
5320
+ * Get all invocation IDs that completed successfully in this iteration.
5321
+ * Used by Agent to accumulate completed IDs across iterations.
5322
+ */
5323
+ getCompletedInvocationIds() {
5324
+ return new Set(this.completedResults.keys());
5325
+ }
5326
+ /**
5327
+ * Get all invocation IDs that failed in this iteration.
5328
+ * Used by Agent to accumulate failed IDs across iterations.
5329
+ */
5330
+ getFailedInvocationIds() {
5331
+ return new Set(this.failedInvocations);
5332
+ }
5502
5333
  };
5503
5334
  }
5504
5335
  });
@@ -5561,6 +5392,9 @@ var init_agent = __esm({
5561
5392
  onSubagentEvent;
5562
5393
  // Counter for generating synthetic invocation IDs for wrapped text content
5563
5394
  syntheticInvocationCounter = 0;
5395
+ // Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
5396
+ completedInvocationIds = /* @__PURE__ */ new Set();
5397
+ failedInvocationIds = /* @__PURE__ */ new Set();
5564
5398
  // Execution Tree - first-class model for nested subagent support
5565
5399
  tree;
5566
5400
  parentNodeId;
@@ -5870,96 +5704,22 @@ var init_agent = __esm({
5870
5704
  maxIterations: this.maxIterations
5871
5705
  });
5872
5706
  while (currentIteration < this.maxIterations) {
5873
- if (this.signal?.aborted) {
5874
- this.logger.info("Agent loop terminated by abort signal", {
5875
- iteration: currentIteration,
5876
- reason: this.signal.reason
5877
- });
5878
- await this.safeObserve(async () => {
5879
- if (this.hooks.observers?.onAbort) {
5880
- const context = {
5881
- iteration: currentIteration,
5882
- reason: this.signal?.reason,
5883
- logger: this.logger
5884
- };
5885
- await this.hooks.observers.onAbort(context);
5886
- }
5887
- });
5707
+ if (await this.checkAbortAndNotify(currentIteration)) {
5888
5708
  return;
5889
5709
  }
5890
5710
  this.logger.debug("Starting iteration", { iteration: currentIteration });
5891
5711
  try {
5892
- if (this.compactionManager) {
5893
- const compactionEvent = await this.compactionManager.checkAndCompact(
5894
- this.conversation,
5895
- currentIteration
5896
- );
5897
- if (compactionEvent) {
5898
- this.logger.info("Context compacted", {
5899
- strategy: compactionEvent.strategy,
5900
- tokensBefore: compactionEvent.tokensBefore,
5901
- tokensAfter: compactionEvent.tokensAfter
5902
- });
5903
- yield { type: "compaction", event: compactionEvent };
5904
- await this.safeObserve(async () => {
5905
- if (this.hooks.observers?.onCompaction) {
5906
- await this.hooks.observers.onCompaction({
5907
- iteration: currentIteration,
5908
- event: compactionEvent,
5909
- // biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
5910
- stats: this.compactionManager.getStats(),
5911
- logger: this.logger
5912
- });
5913
- }
5914
- });
5915
- }
5712
+ const compactionEvent = await this.checkAndPerformCompaction(currentIteration);
5713
+ if (compactionEvent) {
5714
+ yield compactionEvent;
5916
5715
  }
5917
- let llmOptions = {
5918
- model: this.model,
5919
- messages: this.conversation.getMessages(),
5920
- temperature: this.temperature,
5921
- maxTokens: this.defaultMaxTokens,
5922
- signal: this.signal
5923
- };
5924
- await this.safeObserve(async () => {
5925
- if (this.hooks.observers?.onLLMCallStart) {
5926
- const context = {
5927
- iteration: currentIteration,
5928
- options: llmOptions,
5929
- logger: this.logger
5930
- };
5931
- await this.hooks.observers.onLLMCallStart(context);
5932
- }
5933
- });
5934
- if (this.hooks.controllers?.beforeLLMCall) {
5935
- const context = {
5936
- iteration: currentIteration,
5937
- maxIterations: this.maxIterations,
5938
- options: llmOptions,
5939
- logger: this.logger
5940
- };
5941
- const action = await this.hooks.controllers.beforeLLMCall(context);
5942
- validateBeforeLLMCallAction(action);
5943
- if (action.action === "skip") {
5944
- this.logger.info("Controller skipped LLM call, using synthetic response");
5945
- this.conversation.addAssistantMessage(action.syntheticResponse);
5946
- yield { type: "text", content: action.syntheticResponse };
5947
- break;
5948
- } else if (action.action === "proceed" && action.modifiedOptions) {
5949
- llmOptions = { ...llmOptions, ...action.modifiedOptions };
5950
- }
5716
+ const prepared = await this.prepareLLMCall(currentIteration);
5717
+ const llmOptions = prepared.options;
5718
+ if (prepared.skipWithSynthetic !== void 0) {
5719
+ this.conversation.addAssistantMessage(prepared.skipWithSynthetic);
5720
+ yield { type: "text", content: prepared.skipWithSynthetic };
5721
+ break;
5951
5722
  }
5952
- await this.safeObserve(async () => {
5953
- if (this.hooks.observers?.onLLMCallReady) {
5954
- const context = {
5955
- iteration: currentIteration,
5956
- maxIterations: this.maxIterations,
5957
- options: llmOptions,
5958
- logger: this.logger
5959
- };
5960
- await this.hooks.observers.onLLMCallReady(context);
5961
- }
5962
- });
5963
5723
  this.logger.info("Calling LLM", { model: this.model });
5964
5724
  this.logger.silly("LLM request details", {
5965
5725
  model: llmOptions.model,
@@ -5995,7 +5755,10 @@ var init_agent = __esm({
5995
5755
  tree: this.tree,
5996
5756
  parentNodeId: currentLLMNodeId,
5997
5757
  // Gadgets are children of this LLM call
5998
- baseDepth: this.baseDepth
5758
+ baseDepth: this.baseDepth,
5759
+ // Cross-iteration dependency tracking
5760
+ priorCompletedInvocations: this.completedInvocationIds,
5761
+ priorFailedInvocations: this.failedInvocationIds
5999
5762
  });
6000
5763
  let streamMetadata = null;
6001
5764
  let gadgetCallCount = 0;
@@ -6018,6 +5781,12 @@ var init_agent = __esm({
6018
5781
  if (!streamMetadata) {
6019
5782
  throw new Error("Stream processing completed without metadata event");
6020
5783
  }
5784
+ for (const id of processor.getCompletedInvocationIds()) {
5785
+ this.completedInvocationIds.add(id);
5786
+ }
5787
+ for (const id of processor.getFailedInvocationIds()) {
5788
+ this.failedInvocationIds.add(id);
5789
+ }
6021
5790
  const result = streamMetadata;
6022
5791
  this.logger.info("LLM response completed", {
6023
5792
  finishReason: result.finishReason,
@@ -6041,81 +5810,21 @@ var init_agent = __esm({
6041
5810
  await this.hooks.observers.onLLMCallComplete(context);
6042
5811
  }
6043
5812
  });
6044
- this.tree.completeLLMCall(currentLLMNodeId, {
6045
- response: result.rawResponse,
6046
- usage: result.usage,
6047
- finishReason: result.finishReason
6048
- });
6049
- let finalMessage = result.finalMessage;
6050
- if (this.hooks.controllers?.afterLLMCall) {
6051
- const context = {
6052
- iteration: currentIteration,
6053
- maxIterations: this.maxIterations,
6054
- options: llmOptions,
6055
- finishReason: result.finishReason,
6056
- usage: result.usage,
6057
- finalMessage: result.finalMessage,
6058
- gadgetCallCount,
6059
- logger: this.logger
6060
- };
6061
- const action = await this.hooks.controllers.afterLLMCall(context);
6062
- validateAfterLLMCallAction(action);
6063
- if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
6064
- finalMessage = action.modifiedMessage;
6065
- }
6066
- if (action.action === "append_messages" || action.action === "append_and_modify") {
6067
- for (const msg of action.messages) {
6068
- if (msg.role === "user") {
6069
- this.conversation.addUserMessage(msg.content);
6070
- } else if (msg.role === "assistant") {
6071
- this.conversation.addAssistantMessage(extractMessageText(msg.content));
6072
- } else if (msg.role === "system") {
6073
- this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
6074
- }
6075
- }
6076
- }
6077
- }
6078
- if (result.didExecuteGadgets) {
6079
- if (this.textWithGadgetsHandler) {
6080
- const textContent = textOutputs.join("");
6081
- if (textContent.trim()) {
6082
- const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
6083
- const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
6084
- this.conversation.addGadgetCallResult(
6085
- gadgetName,
6086
- parameterMapping(textContent),
6087
- resultMapping ? resultMapping(textContent) : textContent,
6088
- syntheticId
6089
- );
6090
- }
6091
- }
6092
- for (const output of gadgetResults) {
6093
- if (output.type === "gadget_result") {
6094
- const gadgetResult = output.result;
6095
- this.conversation.addGadgetCallResult(
6096
- gadgetResult.gadgetName,
6097
- gadgetResult.parameters,
6098
- gadgetResult.error ?? gadgetResult.result ?? "",
6099
- gadgetResult.invocationId,
6100
- gadgetResult.media,
6101
- gadgetResult.mediaIds
6102
- );
6103
- }
6104
- }
6105
- } else {
6106
- if (finalMessage.trim()) {
6107
- const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
6108
- this.conversation.addGadgetCallResult(
6109
- "TellUser",
6110
- { message: finalMessage, done: false, type: "info" },
6111
- `\u2139\uFE0F ${finalMessage}`,
6112
- syntheticId
6113
- );
6114
- }
6115
- const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
6116
- if (shouldBreak) {
6117
- break;
6118
- }
5813
+ this.completeLLMCallInTree(currentLLMNodeId, result);
5814
+ const finalMessage = await this.processAfterLLMCallController(
5815
+ currentIteration,
5816
+ llmOptions,
5817
+ result,
5818
+ gadgetCallCount
5819
+ );
5820
+ const shouldBreakFromTextOnly = await this.updateConversationWithResults(
5821
+ result.didExecuteGadgets,
5822
+ textOutputs,
5823
+ gadgetResults,
5824
+ finalMessage
5825
+ );
5826
+ if (shouldBreakFromTextOnly) {
5827
+ break;
6119
5828
  }
6120
5829
  if (result.shouldBreakLoop) {
6121
5830
  this.logger.info("Loop terminated by gadget or processor");
@@ -6268,6 +5977,210 @@ var init_agent = __esm({
6268
5977
  }
6269
5978
  };
6270
5979
  }
5980
+ // ==========================================================================
5981
+ // Agent Loop Helper Methods (extracted from run() for readability)
5982
+ // ==========================================================================
5983
+ /**
5984
+ * Check abort signal and notify observers if aborted.
5985
+ * @returns true if agent should terminate
5986
+ */
5987
+ async checkAbortAndNotify(iteration) {
5988
+ if (!this.signal?.aborted) return false;
5989
+ this.logger.info("Agent loop terminated by abort signal", {
5990
+ iteration,
5991
+ reason: this.signal.reason
5992
+ });
5993
+ await this.safeObserve(async () => {
5994
+ if (this.hooks.observers?.onAbort) {
5995
+ const context = {
5996
+ iteration,
5997
+ reason: this.signal?.reason,
5998
+ logger: this.logger
5999
+ };
6000
+ await this.hooks.observers.onAbort(context);
6001
+ }
6002
+ });
6003
+ return true;
6004
+ }
6005
+ /**
6006
+ * Check and perform context compaction if needed.
6007
+ * @returns compaction stream event if compaction occurred, null otherwise
6008
+ */
6009
+ async checkAndPerformCompaction(iteration) {
6010
+ if (!this.compactionManager) return null;
6011
+ const compactionEvent = await this.compactionManager.checkAndCompact(
6012
+ this.conversation,
6013
+ iteration
6014
+ );
6015
+ if (!compactionEvent) return null;
6016
+ this.logger.info("Context compacted", {
6017
+ strategy: compactionEvent.strategy,
6018
+ tokensBefore: compactionEvent.tokensBefore,
6019
+ tokensAfter: compactionEvent.tokensAfter
6020
+ });
6021
+ await this.safeObserve(async () => {
6022
+ if (this.hooks.observers?.onCompaction) {
6023
+ await this.hooks.observers.onCompaction({
6024
+ iteration,
6025
+ event: compactionEvent,
6026
+ // biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
6027
+ stats: this.compactionManager.getStats(),
6028
+ logger: this.logger
6029
+ });
6030
+ }
6031
+ });
6032
+ return { type: "compaction", event: compactionEvent };
6033
+ }
6034
+ /**
6035
+ * Prepare LLM call options and process beforeLLMCall controller.
6036
+ * @returns options and optional skipWithSynthetic response if controller wants to skip
6037
+ */
6038
+ async prepareLLMCall(iteration) {
6039
+ let llmOptions = {
6040
+ model: this.model,
6041
+ messages: this.conversation.getMessages(),
6042
+ temperature: this.temperature,
6043
+ maxTokens: this.defaultMaxTokens,
6044
+ signal: this.signal
6045
+ };
6046
+ await this.safeObserve(async () => {
6047
+ if (this.hooks.observers?.onLLMCallStart) {
6048
+ const context = {
6049
+ iteration,
6050
+ options: llmOptions,
6051
+ logger: this.logger
6052
+ };
6053
+ await this.hooks.observers.onLLMCallStart(context);
6054
+ }
6055
+ });
6056
+ if (this.hooks.controllers?.beforeLLMCall) {
6057
+ const context = {
6058
+ iteration,
6059
+ maxIterations: this.maxIterations,
6060
+ options: llmOptions,
6061
+ logger: this.logger
6062
+ };
6063
+ const action = await this.hooks.controllers.beforeLLMCall(context);
6064
+ validateBeforeLLMCallAction(action);
6065
+ if (action.action === "skip") {
6066
+ this.logger.info("Controller skipped LLM call, using synthetic response");
6067
+ return { options: llmOptions, skipWithSynthetic: action.syntheticResponse };
6068
+ } else if (action.action === "proceed" && action.modifiedOptions) {
6069
+ llmOptions = { ...llmOptions, ...action.modifiedOptions };
6070
+ }
6071
+ }
6072
+ await this.safeObserve(async () => {
6073
+ if (this.hooks.observers?.onLLMCallReady) {
6074
+ const context = {
6075
+ iteration,
6076
+ maxIterations: this.maxIterations,
6077
+ options: llmOptions,
6078
+ logger: this.logger
6079
+ };
6080
+ await this.hooks.observers.onLLMCallReady(context);
6081
+ }
6082
+ });
6083
+ return { options: llmOptions };
6084
+ }
6085
+ /**
6086
+ * Calculate cost and complete LLM call in execution tree.
6087
+ */
6088
+ completeLLMCallInTree(nodeId, result) {
6089
+ const llmCost = this.client.modelRegistry?.estimateCost?.(
6090
+ this.model,
6091
+ result.usage?.inputTokens ?? 0,
6092
+ result.usage?.outputTokens ?? 0,
6093
+ result.usage?.cachedInputTokens ?? 0,
6094
+ result.usage?.cacheCreationInputTokens ?? 0
6095
+ )?.totalCost;
6096
+ this.tree.completeLLMCall(nodeId, {
6097
+ response: result.rawResponse,
6098
+ usage: result.usage,
6099
+ finishReason: result.finishReason,
6100
+ cost: llmCost
6101
+ });
6102
+ }
6103
+ /**
6104
+ * Process afterLLMCall controller and return modified final message.
6105
+ */
6106
+ async processAfterLLMCallController(iteration, llmOptions, result, gadgetCallCount) {
6107
+ let finalMessage = result.finalMessage;
6108
+ if (!this.hooks.controllers?.afterLLMCall) {
6109
+ return finalMessage;
6110
+ }
6111
+ const context = {
6112
+ iteration,
6113
+ maxIterations: this.maxIterations,
6114
+ options: llmOptions,
6115
+ finishReason: result.finishReason,
6116
+ usage: result.usage,
6117
+ finalMessage: result.finalMessage,
6118
+ gadgetCallCount,
6119
+ logger: this.logger
6120
+ };
6121
+ const action = await this.hooks.controllers.afterLLMCall(context);
6122
+ validateAfterLLMCallAction(action);
6123
+ if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
6124
+ finalMessage = action.modifiedMessage;
6125
+ }
6126
+ if (action.action === "append_messages" || action.action === "append_and_modify") {
6127
+ for (const msg of action.messages) {
6128
+ if (msg.role === "user") {
6129
+ this.conversation.addUserMessage(msg.content);
6130
+ } else if (msg.role === "assistant") {
6131
+ this.conversation.addAssistantMessage(extractMessageText(msg.content));
6132
+ } else if (msg.role === "system") {
6133
+ this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
6134
+ }
6135
+ }
6136
+ }
6137
+ return finalMessage;
6138
+ }
6139
+ /**
6140
+ * Update conversation history with gadget results or text-only response.
6141
+ * @returns true if loop should break (text-only handler requested termination)
6142
+ */
6143
+ async updateConversationWithResults(didExecuteGadgets, textOutputs, gadgetResults, finalMessage) {
6144
+ if (didExecuteGadgets) {
6145
+ if (this.textWithGadgetsHandler) {
6146
+ const textContent = textOutputs.join("");
6147
+ if (textContent.trim()) {
6148
+ const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
6149
+ const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
6150
+ this.conversation.addGadgetCallResult(
6151
+ gadgetName,
6152
+ parameterMapping(textContent),
6153
+ resultMapping ? resultMapping(textContent) : textContent,
6154
+ syntheticId
6155
+ );
6156
+ }
6157
+ }
6158
+ for (const output of gadgetResults) {
6159
+ if (output.type === "gadget_result") {
6160
+ const gadgetResult = output.result;
6161
+ this.conversation.addGadgetCallResult(
6162
+ gadgetResult.gadgetName,
6163
+ gadgetResult.parameters,
6164
+ gadgetResult.error ?? gadgetResult.result ?? "",
6165
+ gadgetResult.invocationId,
6166
+ gadgetResult.media,
6167
+ gadgetResult.mediaIds
6168
+ );
6169
+ }
6170
+ }
6171
+ return false;
6172
+ }
6173
+ if (finalMessage.trim()) {
6174
+ const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
6175
+ this.conversation.addGadgetCallResult(
6176
+ "TellUser",
6177
+ { message: finalMessage, done: false, type: "info" },
6178
+ `\u2139\uFE0F ${finalMessage}`,
6179
+ syntheticId
6180
+ );
6181
+ }
6182
+ return await this.handleTextOnlyResponse(finalMessage);
6183
+ }
6271
6184
  /**
6272
6185
  * Run agent with named event handlers (syntactic sugar).
6273
6186
  *
@@ -9800,11 +9713,13 @@ var init_model_registry = __esm({
9800
9713
  }
9801
9714
  /**
9802
9715
  * Get model specification by model ID
9803
- * @param modelId - Full model identifier (e.g., 'gpt-5', 'claude-sonnet-4-5-20250929')
9716
+ * @param modelId - Full model identifier, optionally with provider prefix
9717
+ * (e.g., 'gpt-5', 'claude-sonnet-4-5-20250929', 'anthropic:claude-sonnet-4-5')
9804
9718
  * @returns ModelSpec if found, undefined otherwise
9805
9719
  */
9806
9720
  getModelSpec(modelId) {
9807
- return this.modelSpecs.find((model) => model.modelId === modelId);
9721
+ const normalizedId = modelId.includes(":") ? modelId.split(":")[1] : modelId;
9722
+ return this.modelSpecs.find((model) => model.modelId === normalizedId);
9808
9723
  }
9809
9724
  /**
9810
9725
  * List all models, optionally filtered by provider
@@ -12222,12 +12137,6 @@ export {
12222
12137
  init_event_handlers,
12223
12138
  GadgetOutputStore,
12224
12139
  init_gadget_output_store,
12225
- GadgetCallParser,
12226
- init_parser,
12227
- GadgetExecutor,
12228
- init_executor,
12229
- StreamProcessor,
12230
- init_stream_processor,
12231
12140
  FALLBACK_CHARS_PER_TOKEN,
12232
12141
  init_constants2 as init_constants,
12233
12142
  AnthropicMessagesProvider,
@@ -12250,6 +12159,14 @@ export {
12250
12159
  init_options,
12251
12160
  LLMist,
12252
12161
  init_client,
12162
+ GadgetCallParser,
12163
+ init_parser,
12164
+ Gadget,
12165
+ init_typed_gadget,
12166
+ GadgetExecutor,
12167
+ init_executor,
12168
+ StreamProcessor,
12169
+ init_stream_processor,
12253
12170
  AgentBuilder,
12254
12171
  init_builder,
12255
12172
  validateAndApplyDefaults,
@@ -12294,4 +12211,4 @@ export {
12294
12211
  createEmptyStream,
12295
12212
  createErrorStream
12296
12213
  };
12297
- //# sourceMappingURL=chunk-VAJLPRJ6.js.map
12214
+ //# sourceMappingURL=chunk-SFZIL2VR.js.map