llmist 15.19.0 → 16.0.1

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/index.cjs CHANGED
@@ -360,7 +360,8 @@ var init_execution_tree = __esm({
360
360
  result: params.result ?? "",
361
361
  executionTimeMs: params.executionTimeMs ?? 0,
362
362
  cost: params.cost,
363
- media: params.media
363
+ media: params.media,
364
+ storedMedia: params.storedMedia
364
365
  });
365
366
  }
366
367
  }
@@ -1278,8 +1279,9 @@ Produces: { "items": ["first", "second"] }`);
1278
1279
  * @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
1279
1280
  * @param media - Optional media outputs from the gadget
1280
1281
  * @param mediaIds - Optional IDs for the media outputs
1282
+ * @param storedMedia - Optional stored media info including file paths
1281
1283
  */
1282
- addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
1284
+ addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds, storedMedia) {
1283
1285
  const paramStr = this.formatBlockParameters(parameters, "");
1284
1286
  this.messages.push({
1285
1287
  role: "assistant",
@@ -1288,7 +1290,11 @@ ${paramStr}
1288
1290
  ${this.endPrefix}`
1289
1291
  });
1290
1292
  if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
1291
- const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
1293
+ const idRefs = media.map((m, i) => {
1294
+ const path = storedMedia?.[i]?.path;
1295
+ const pathInfo = path ? ` \u2192 saved to: ${path}` : "";
1296
+ return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
1297
+ }).join("\n");
1292
1298
  const textWithIds = `Result (${invocationId}): ${result}
1293
1299
  ${idRefs}`;
1294
1300
  const parts = [text(textWithIds)];
@@ -2347,7 +2353,7 @@ function createLogger(options = {}) {
2347
2353
  }
2348
2354
  }
2349
2355
  const useFileLogging = Boolean(sharedLogFileStream);
2350
- const logger = new import_tslog.Logger({
2356
+ const logger2 = new import_tslog.Logger({
2351
2357
  name,
2352
2358
  minLevel,
2353
2359
  type: useFileLogging ? "pretty" : defaultType,
@@ -2373,7 +2379,7 @@ function createLogger(options = {}) {
2373
2379
  }
2374
2380
  } : void 0
2375
2381
  });
2376
- return logger;
2382
+ return logger2;
2377
2383
  }
2378
2384
  var import_node_fs, import_node_path2, import_tslog, LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
2379
2385
  var init_logger = __esm({
@@ -3612,14 +3618,15 @@ var init_conversation_manager = __esm({
3612
3618
  addAssistantMessage(content) {
3613
3619
  this.historyBuilder.addAssistant(content);
3614
3620
  }
3615
- addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
3621
+ addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds, storedMedia) {
3616
3622
  this.historyBuilder.addGadgetCallResult(
3617
3623
  gadgetName,
3618
3624
  parameters,
3619
3625
  result,
3620
3626
  invocationId,
3621
3627
  media,
3622
- mediaIds
3628
+ mediaIds,
3629
+ storedMedia
3623
3630
  );
3624
3631
  }
3625
3632
  getMessages() {
@@ -9649,6 +9656,106 @@ var init_openrouter_models = __esm({
9649
9656
  }
9650
9657
  });
9651
9658
 
9659
+ // src/providers/openrouter-speech-models.ts
9660
+ function getOpenRouterSpeechModelSpec(modelId) {
9661
+ return openrouterSpeechModels.find((m) => m.modelId === modelId);
9662
+ }
9663
+ function isOpenRouterSpeechModel(modelId) {
9664
+ return openrouterSpeechModels.some((m) => m.modelId === modelId);
9665
+ }
9666
+ function calculateOpenRouterSpeechCost(modelId, characterCount, estimatedMinutes) {
9667
+ const spec = getOpenRouterSpeechModelSpec(modelId);
9668
+ if (!spec) return void 0;
9669
+ if (spec.pricing.perMinute !== void 0) {
9670
+ const minutes = estimatedMinutes ?? characterCount / 750;
9671
+ return minutes * spec.pricing.perMinute;
9672
+ }
9673
+ if (spec.pricing.perInputToken !== void 0) {
9674
+ const estimatedTokens = Math.ceil(characterCount / 4);
9675
+ const inputCost = estimatedTokens * spec.pricing.perInputToken;
9676
+ if (spec.pricing.perAudioOutputToken !== void 0 && estimatedMinutes !== void 0) {
9677
+ const audioTokens = estimatedMinutes * 60 * 100;
9678
+ return inputCost + audioTokens * spec.pricing.perAudioOutputToken;
9679
+ }
9680
+ return inputCost;
9681
+ }
9682
+ return void 0;
9683
+ }
9684
+ var OPENROUTER_TTS_VOICES, OPENROUTER_TTS_FORMATS, openrouterSpeechModels;
9685
+ var init_openrouter_speech_models = __esm({
9686
+ "src/providers/openrouter-speech-models.ts"() {
9687
+ "use strict";
9688
+ OPENROUTER_TTS_VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
9689
+ OPENROUTER_TTS_FORMATS = ["pcm16"];
9690
+ openrouterSpeechModels = [
9691
+ {
9692
+ provider: "openrouter",
9693
+ modelId: "openai/gpt-4o-audio-preview",
9694
+ displayName: "GPT-4o Audio Preview (via OpenRouter)",
9695
+ pricing: {
9696
+ perInputToken: 25e-7,
9697
+ perAudioOutputToken: 1e-5,
9698
+ perMinute: 0.06
9699
+ },
9700
+ voices: [...OPENROUTER_TTS_VOICES],
9701
+ formats: OPENROUTER_TTS_FORMATS,
9702
+ maxInputLength: 128e3,
9703
+ defaultVoice: "alloy",
9704
+ defaultFormat: "pcm16",
9705
+ features: {
9706
+ voiceInstructions: true
9707
+ }
9708
+ },
9709
+ {
9710
+ provider: "openrouter",
9711
+ modelId: "openai/gpt-audio",
9712
+ displayName: "GPT Audio (via OpenRouter)",
9713
+ pricing: {
9714
+ // Token-based pricing with audio output
9715
+ perInputToken: 25e-7,
9716
+ // $2.50 per 1M
9717
+ perAudioOutputToken: 64e-6,
9718
+ // ~$64 per 1M audio tokens
9719
+ // Approximate per-minute cost for estimation
9720
+ perMinute: 0.08
9721
+ },
9722
+ voices: [...OPENROUTER_TTS_VOICES],
9723
+ formats: OPENROUTER_TTS_FORMATS,
9724
+ maxInputLength: 128e3,
9725
+ // 128K context
9726
+ defaultVoice: "alloy",
9727
+ defaultFormat: "pcm16",
9728
+ features: {
9729
+ voiceInstructions: true
9730
+ }
9731
+ },
9732
+ {
9733
+ provider: "openrouter",
9734
+ modelId: "openai/gpt-audio-mini",
9735
+ displayName: "GPT Audio Mini (via OpenRouter)",
9736
+ pricing: {
9737
+ // More affordable option
9738
+ perInputToken: 6e-7,
9739
+ // $0.60 per 1M
9740
+ perAudioOutputToken: 6e-7,
9741
+ // $0.60 per 1M audio tokens
9742
+ // Approximate per-minute cost for estimation
9743
+ perMinute: 0.015
9744
+ },
9745
+ voices: [...OPENROUTER_TTS_VOICES],
9746
+ formats: OPENROUTER_TTS_FORMATS,
9747
+ maxInputLength: 128e3,
9748
+ // 128K context
9749
+ defaultVoice: "alloy",
9750
+ defaultFormat: "pcm16",
9751
+ features: {
9752
+ voiceInstructions: true
9753
+ }
9754
+ }
9755
+ ];
9756
+ }
9757
+ });
9758
+
9652
9759
  // src/providers/openrouter.ts
9653
9760
  function createOpenRouterProviderFromEnv() {
9654
9761
  const apiKey = readEnvVar("OPENROUTER_API_KEY");
@@ -9669,14 +9776,18 @@ function createOpenRouterProviderFromEnv() {
9669
9776
  });
9670
9777
  return new OpenRouterProvider(client, config);
9671
9778
  }
9672
- var import_openai4, OPENROUTER_EFFORT_MAP, OpenRouterProvider;
9779
+ var import_openai4, logger, OPENROUTER_EFFORT_MAP, OpenRouterProvider;
9673
9780
  var init_openrouter = __esm({
9674
9781
  "src/providers/openrouter.ts"() {
9675
9782
  "use strict";
9676
9783
  import_openai4 = __toESM(require("openai"), 1);
9784
+ init_model_shortcuts();
9785
+ init_logger();
9677
9786
  init_openai_compatible_provider();
9678
9787
  init_openrouter_models();
9788
+ init_openrouter_speech_models();
9679
9789
  init_utils();
9790
+ logger = createLogger({ name: "openrouter" });
9680
9791
  OPENROUTER_EFFORT_MAP = {
9681
9792
  none: "none",
9682
9793
  low: "low",
@@ -9782,6 +9893,128 @@ Original error: ${error.message}`
9782
9893
  }
9783
9894
  return error;
9784
9895
  }
9896
+ // =========================================================================
9897
+ // Speech Generation (TTS via Chat Completions with Audio Modality)
9898
+ // =========================================================================
9899
+ /**
9900
+ * Get speech model specifications for OpenRouter.
9901
+ */
9902
+ getSpeechModelSpecs() {
9903
+ return openrouterSpeechModels;
9904
+ }
9905
+ /**
9906
+ * Check if this provider supports speech generation for a given model.
9907
+ * Handles both prefixed (openrouter:openai/gpt-audio-mini) and unprefixed model IDs.
9908
+ */
9909
+ supportsSpeechGeneration(modelId) {
9910
+ const bareModelId = getModelId(modelId);
9911
+ return isOpenRouterSpeechModel(bareModelId);
9912
+ }
9913
+ /**
9914
+ * Generate speech audio from text using OpenRouter's audio-capable models.
9915
+ *
9916
+ * OpenRouter TTS works via chat completions with audio modality, not a
9917
+ * dedicated TTS endpoint. The model receives a prompt asking it to say
9918
+ * the text, and returns audio data via streaming.
9919
+ *
9920
+ * @param options - Speech generation options
9921
+ * @returns Promise resolving to the generation result with audio and cost
9922
+ * @throws Error if model is unknown, voice/format are invalid, or no audio is returned
9923
+ */
9924
+ async generateSpeech(options) {
9925
+ const client = this.client;
9926
+ const bareModelId = getModelId(options.model);
9927
+ const spec = getOpenRouterSpeechModelSpec(bareModelId);
9928
+ if (!spec) {
9929
+ throw new Error(`Unknown OpenRouter TTS model: ${options.model}`);
9930
+ }
9931
+ const voice = options.voice ?? spec.defaultVoice ?? "alloy";
9932
+ if (!spec.voices.includes(voice)) {
9933
+ throw new Error(
9934
+ `Invalid voice "${voice}" for ${options.model}. Valid voices: ${spec.voices.join(", ")}`
9935
+ );
9936
+ }
9937
+ const format2 = options.responseFormat ?? spec.defaultFormat ?? "mp3";
9938
+ if (!spec.formats.includes(format2)) {
9939
+ throw new Error(
9940
+ `Invalid format "${format2}" for ${options.model}. Valid formats: ${spec.formats.join(", ")}`
9941
+ );
9942
+ }
9943
+ logger.debug("TTS request", {
9944
+ model: bareModelId,
9945
+ voice,
9946
+ format: format2,
9947
+ inputLength: options.input.length
9948
+ });
9949
+ try {
9950
+ const response = await client.chat.completions.create({
9951
+ model: bareModelId,
9952
+ messages: [
9953
+ {
9954
+ role: "user",
9955
+ content: `Please say the following text exactly: "${options.input}"`
9956
+ }
9957
+ ],
9958
+ // OpenRouter-specific parameters for audio output
9959
+ // Note: OpenRouter TTS via chat completions does NOT support the speed parameter
9960
+ // (unlike OpenAI's dedicated /audio/speech endpoint which does)
9961
+ modalities: ["text", "audio"],
9962
+ audio: {
9963
+ voice,
9964
+ format: format2
9965
+ },
9966
+ stream: true
9967
+ });
9968
+ const audioChunks = [];
9969
+ for await (const chunk of response) {
9970
+ const delta = chunk.choices[0]?.delta;
9971
+ if (!delta || typeof delta !== "object") continue;
9972
+ const audioObj = delta.audio;
9973
+ if (!audioObj || typeof audioObj !== "object") continue;
9974
+ const audioData = audioObj.data;
9975
+ if (typeof audioData !== "string" || audioData.length === 0) continue;
9976
+ const decoded = Buffer.from(audioData, "base64");
9977
+ if (decoded.length === 0) {
9978
+ throw new Error("Invalid base64 audio data received from OpenRouter");
9979
+ }
9980
+ audioChunks.push(decoded);
9981
+ }
9982
+ const audioBuffer = Buffer.concat(audioChunks);
9983
+ if (audioBuffer.length === 0) {
9984
+ throw new Error(
9985
+ "OpenRouter TTS returned no audio data. The model may have failed to generate audio or the stream was interrupted."
9986
+ );
9987
+ }
9988
+ const cost = calculateOpenRouterSpeechCost(bareModelId, options.input.length);
9989
+ return {
9990
+ // Use Uint8Array for clean ArrayBuffer extraction (safer than buffer.slice for Node.js Buffer)
9991
+ audio: new Uint8Array(audioBuffer).buffer,
9992
+ model: options.model,
9993
+ usage: {
9994
+ characterCount: options.input.length
9995
+ },
9996
+ cost,
9997
+ format: format2
9998
+ };
9999
+ } catch (error) {
10000
+ const err = error;
10001
+ const apiError = err.error?.message || err.error?.code || "";
10002
+ const bodyInfo = err.body ? JSON.stringify(err.body) : "";
10003
+ const details = apiError || bodyInfo || err.message || "Unknown error";
10004
+ logger.debug("TTS error", {
10005
+ model: bareModelId,
10006
+ voice,
10007
+ format: format2,
10008
+ status: err.status,
10009
+ error: err.error,
10010
+ body: err.body,
10011
+ message: err.message
10012
+ });
10013
+ throw new Error(
10014
+ `OpenRouter TTS failed for model ${bareModelId}: ${details}` + (err.status ? ` (HTTP ${err.status})` : "")
10015
+ );
10016
+ }
10017
+ }
9785
10018
  };
9786
10019
  }
9787
10020
  });
@@ -10760,8 +10993,8 @@ var init_builder = __esm({
10760
10993
  * @param logger - Logger instance
10761
10994
  * @returns This builder for chaining
10762
10995
  */
10763
- withLogger(logger) {
10764
- this.logger = logger;
10996
+ withLogger(logger2) {
10997
+ this.logger = logger2;
10765
10998
  return this;
10766
10999
  }
10767
11000
  /**
@@ -12846,7 +13079,7 @@ var init_executor = __esm({
12846
13079
  init_parser();
12847
13080
  init_typed_gadget();
12848
13081
  GadgetExecutor = class {
12849
- constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, tree, parentNodeId, baseDepth, parentObservers, currentObservers, rateLimitTracker, retryConfig) {
13082
+ constructor(registry, requestHumanInput, logger2, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, tree, parentNodeId, baseDepth, parentObservers, currentObservers, rateLimitTracker, retryConfig) {
12850
13083
  this.registry = registry;
12851
13084
  this.requestHumanInput = requestHumanInput;
12852
13085
  this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
@@ -12861,7 +13094,7 @@ var init_executor = __esm({
12861
13094
  this.currentObservers = currentObservers;
12862
13095
  this.rateLimitTracker = rateLimitTracker;
12863
13096
  this.retryConfig = retryConfig;
12864
- this.logger = logger ?? createLogger({ name: "llmist:executor" });
13097
+ this.logger = logger2 ?? createLogger({ name: "llmist:executor" });
12865
13098
  this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
12866
13099
  this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
12867
13100
  }
@@ -13261,22 +13494,22 @@ function getSubagentContextForNode(tree, nodeId) {
13261
13494
  depth: node.depth
13262
13495
  };
13263
13496
  }
13264
- async function safeObserve(fn, logger, eventType) {
13497
+ async function safeObserve(fn, logger2, eventType) {
13265
13498
  try {
13266
13499
  await fn();
13267
13500
  } catch (error) {
13268
- logger.warn(`Observer error in ${eventType}:`, error);
13501
+ logger2.warn(`Observer error in ${eventType}:`, error);
13269
13502
  }
13270
13503
  }
13271
- function chainObserverCall(chainMap, key, fn, logger, eventType, cleanup = false) {
13504
+ function chainObserverCall(chainMap, key, fn, logger2, eventType, cleanup = false) {
13272
13505
  const previousPromise = chainMap.get(key) ?? Promise.resolve();
13273
- const newPromise = previousPromise.then(() => safeObserve(fn, logger, eventType));
13506
+ const newPromise = previousPromise.then(() => safeObserve(fn, logger2, eventType));
13274
13507
  chainMap.set(key, newPromise);
13275
13508
  if (cleanup) {
13276
13509
  newPromise.finally(() => chainMap.delete(key));
13277
13510
  }
13278
13511
  }
13279
- function bridgeTreeToHooks(tree, hooks, logger) {
13512
+ function bridgeTreeToHooks(tree, hooks, logger2) {
13280
13513
  const gadgetPromiseChains = /* @__PURE__ */ new Map();
13281
13514
  const llmPromiseChains = /* @__PURE__ */ new Map();
13282
13515
  return tree.onAll((event) => {
@@ -13298,14 +13531,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13298
13531
  gadgetName: gadgetEvent.name,
13299
13532
  invocationId: gadgetEvent.invocationId,
13300
13533
  parameters: gadgetNode?.parameters ?? {},
13301
- logger,
13534
+ logger: logger2,
13302
13535
  subagentContext
13303
13536
  };
13304
13537
  chainObserverCall(
13305
13538
  gadgetPromiseChains,
13306
13539
  gadgetEvent.invocationId,
13307
13540
  () => hooks.observers?.onGadgetExecutionStart?.(context),
13308
- logger,
13541
+ logger2,
13309
13542
  "onGadgetExecutionStart",
13310
13543
  false
13311
13544
  // Don't cleanup - wait for completion event
@@ -13325,14 +13558,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13325
13558
  finalResult: gadgetEvent.result,
13326
13559
  executionTimeMs: gadgetEvent.executionTimeMs,
13327
13560
  cost: gadgetEvent.cost,
13328
- logger,
13561
+ logger: logger2,
13329
13562
  subagentContext
13330
13563
  };
13331
13564
  chainObserverCall(
13332
13565
  gadgetPromiseChains,
13333
13566
  gadgetEvent.invocationId,
13334
13567
  () => hooks.observers?.onGadgetExecutionComplete?.(context),
13335
- logger,
13568
+ logger2,
13336
13569
  "onGadgetExecutionComplete",
13337
13570
  true
13338
13571
  // Cleanup after completion
@@ -13351,14 +13584,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13351
13584
  parameters: gadgetNode?.parameters ?? {},
13352
13585
  error: gadgetEvent.error,
13353
13586
  executionTimeMs: gadgetEvent.executionTimeMs,
13354
- logger,
13587
+ logger: logger2,
13355
13588
  subagentContext
13356
13589
  };
13357
13590
  chainObserverCall(
13358
13591
  gadgetPromiseChains,
13359
13592
  gadgetEvent.invocationId,
13360
13593
  () => hooks.observers?.onGadgetExecutionComplete?.(context),
13361
- logger,
13594
+ logger2,
13362
13595
  "onGadgetExecutionComplete",
13363
13596
  true
13364
13597
  // Cleanup after error
@@ -13377,14 +13610,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13377
13610
  parameters: gadgetNode?.parameters ?? {},
13378
13611
  failedDependency: gadgetEvent.failedDependency ?? "",
13379
13612
  failedDependencyError: gadgetEvent.failedDependencyError ?? gadgetEvent.error,
13380
- logger,
13613
+ logger: logger2,
13381
13614
  subagentContext
13382
13615
  };
13383
13616
  chainObserverCall(
13384
13617
  gadgetPromiseChains,
13385
13618
  gadgetEvent.invocationId,
13386
13619
  () => hooks.observers?.onGadgetSkipped?.(context),
13387
- logger,
13620
+ logger2,
13388
13621
  "onGadgetSkipped",
13389
13622
  true
13390
13623
  // Cleanup after skipped
@@ -13409,14 +13642,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13409
13642
  temperature: void 0,
13410
13643
  maxTokens: void 0
13411
13644
  },
13412
- logger,
13645
+ logger: logger2,
13413
13646
  subagentContext
13414
13647
  };
13415
13648
  chainObserverCall(
13416
13649
  llmPromiseChains,
13417
13650
  event.nodeId,
13418
13651
  () => hooks.observers?.onLLMCallStart?.(context),
13419
- logger,
13652
+ logger2,
13420
13653
  "onLLMCallStart",
13421
13654
  false
13422
13655
  // Don't cleanup - wait for completion event
@@ -13441,14 +13674,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13441
13674
  rawResponse: llmEvent.response,
13442
13675
  // Use rawResponse as finalMessage since interceptor modifications aren't available from tree events
13443
13676
  finalMessage: llmEvent.response,
13444
- logger,
13677
+ logger: logger2,
13445
13678
  subagentContext
13446
13679
  };
13447
13680
  chainObserverCall(
13448
13681
  llmPromiseChains,
13449
13682
  event.nodeId,
13450
13683
  () => hooks.observers?.onLLMCallComplete?.(context),
13451
- logger,
13684
+ logger2,
13452
13685
  "onLLMCallComplete",
13453
13686
  true
13454
13687
  // Cleanup after completion
@@ -13470,14 +13703,14 @@ function bridgeTreeToHooks(tree, hooks, logger) {
13470
13703
  },
13471
13704
  error: llmEvent.error,
13472
13705
  recovered: llmEvent.recovered,
13473
- logger,
13706
+ logger: logger2,
13474
13707
  subagentContext
13475
13708
  };
13476
13709
  chainObserverCall(
13477
13710
  llmPromiseChains,
13478
13711
  event.nodeId,
13479
13712
  () => hooks.observers?.onLLMCallError?.(context),
13480
- logger,
13713
+ logger2,
13481
13714
  "onLLMCallError",
13482
13715
  true
13483
13716
  // Cleanup after error
@@ -14116,7 +14349,8 @@ var init_stream_processor = __esm({
14116
14349
  result: result.result,
14117
14350
  executionTimeMs: result.executionTimeMs,
14118
14351
  cost: result.cost,
14119
- media: result.media
14352
+ media: result.media,
14353
+ storedMedia: result.storedMedia
14120
14354
  });
14121
14355
  }
14122
14356
  }
@@ -15709,7 +15943,8 @@ var init_agent = __esm({
15709
15943
  gadgetResult.error ?? gadgetResult.result ?? "",
15710
15944
  gadgetResult.invocationId,
15711
15945
  gadgetResult.media,
15712
- gadgetResult.mediaIds
15946
+ gadgetResult.mediaIds,
15947
+ gadgetResult.storedMedia
15713
15948
  );
15714
15949
  }
15715
15950
  }