@anuma/sdk 6.26.2 → 6.27.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.
@@ -5844,6 +5844,116 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
5844
5844
 
5845
5845
  // src/lib/chat/toolLoop.ts
5846
5846
  init_redactor();
5847
+
5848
+ // src/lib/storage/mcpImages.ts
5849
+ var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
5850
+ "AnumaMediaMCP-anuma_create_image",
5851
+ "anuma_create_image",
5852
+ // Deprecated anuma-image-mcp tools, retained so images in already-stored
5853
+ // conversations still re-materialize on replay.
5854
+ "AnumaImageMCP-generate_cloud_image",
5855
+ "AnumaImageMCP-edit_cloud_image",
5856
+ "generate_cloud_image",
5857
+ "edit_cloud_image"
5858
+ ]);
5859
+ function toolOutputForModel(name, output) {
5860
+ if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
5861
+ try {
5862
+ const {
5863
+ imageUrl: _imageUrl,
5864
+ url: _url,
5865
+ output_images: _outputImages,
5866
+ ...rest
5867
+ } = JSON.parse(output);
5868
+ return JSON.stringify(rest);
5869
+ } catch {
5870
+ return output;
5871
+ }
5872
+ }
5873
+ var VIDEO_TOOL_NAMES = /* @__PURE__ */ new Set([
5874
+ "AnumaMediaMCP-anuma_create_video",
5875
+ "AnumaFalMCP-fal_generate_video",
5876
+ "anuma_create_video",
5877
+ "fal_generate_video"
5878
+ ]);
5879
+ var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
5880
+ var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
5881
+ function videoExtensionOf(value) {
5882
+ return value?.match(VIDEO_EXTENSION_RE)?.[1]?.toLowerCase() ?? null;
5883
+ }
5884
+ function classifyUrl(url) {
5885
+ return VIDEO_EXTENSION_RE.test(url) ? "video" : "image";
5886
+ }
5887
+ function extractMCPImageUrls(content, toolCallEvents, mcpR2Domain) {
5888
+ const urls = [];
5889
+ if (toolCallEvents && toolCallEvents.length > 0) {
5890
+ for (const event of toolCallEvents) {
5891
+ if (!event.name) continue;
5892
+ if (IMAGE_TOOL_NAMES.has(event.name)) {
5893
+ try {
5894
+ const output = JSON.parse(event.output || "{}");
5895
+ const model = output.model || "image";
5896
+ const imageUrls = [
5897
+ ...new Set(
5898
+ [
5899
+ ...output.output_images?.map((i) => i.url) ?? [],
5900
+ output.imageUrl,
5901
+ output.url
5902
+ ].filter((u) => Boolean(u))
5903
+ )
5904
+ ];
5905
+ for (const url of imageUrls) {
5906
+ urls.push({ url, model, mediaType: "image" });
5907
+ }
5908
+ } catch {
5909
+ }
5910
+ } else if (VIDEO_TOOL_NAMES.has(event.name)) {
5911
+ try {
5912
+ const output = JSON.parse(event.output || "{}");
5913
+ const model = output.model || "video";
5914
+ const videoUrls = [
5915
+ ...new Set(
5916
+ [
5917
+ ...output.videos?.map((v) => v.video_url) ?? [],
5918
+ output.videoUrl,
5919
+ output.url
5920
+ ].filter((u) => Boolean(u))
5921
+ )
5922
+ ];
5923
+ for (const url of videoUrls) {
5924
+ urls.push({ url, model, mediaType: "video" });
5925
+ }
5926
+ } catch {
5927
+ }
5928
+ }
5929
+ }
5930
+ }
5931
+ if (urls.length === 0 && content) {
5932
+ const escaped = mcpR2Domain.replace(/\./g, "\\.");
5933
+ const patterns = [
5934
+ new RegExp(`https://${escaped}[^\\s"'<>)\\]]+`, "gi"),
5935
+ // Require the proxy's <svc>/<token> shape (>=2 path segments after
5936
+ // /api/v1/media/) so a third-party URL that only shallowly contains
5937
+ // /api/v1/media/ isn't returned as a portal media asset.
5938
+ new RegExp(`https?://[^\\s"'<>)\\]]+/api/v1/media/[^/\\s"'<>)\\]]+/[^\\s"'<>)\\]]+`, "gi")
5939
+ ];
5940
+ const matches = patterns.flatMap((re) => content.match(re) ?? []);
5941
+ if (matches.length > 0) {
5942
+ const seen = /* @__PURE__ */ new Set();
5943
+ for (const url of matches) {
5944
+ const normalized = url.replace(/[)"'>\s]+$/, "");
5945
+ if (!seen.has(normalized)) {
5946
+ seen.add(normalized);
5947
+ const mediaType = classifyUrl(normalized);
5948
+ urls.push({ url: normalized, model: "image", mediaType });
5949
+ }
5950
+ }
5951
+ }
5952
+ }
5953
+ return urls;
5954
+ }
5955
+
5956
+ // src/lib/chat/toolLoop.ts
5847
5957
  init_endpointOverride();
5848
5958
 
5849
5959
  // src/lib/chat/runHooks.ts
@@ -7977,6 +8087,40 @@ function extractLastUserText(messages) {
7977
8087
  function isToolErrorResult(result) {
7978
8088
  return result !== null && typeof result === "object" && "error" in result && typeof result.error === "string";
7979
8089
  }
8090
+ function serverToolCallMessages(events, messages, skipIds) {
8091
+ const seen = new Set(skipIds);
8092
+ for (const m of messages) {
8093
+ if (m.tool_call_id) seen.add(m.tool_call_id);
8094
+ for (const tc of m.tool_calls ?? []) if (tc.id) seen.add(tc.id);
8095
+ }
8096
+ const executed = [];
8097
+ for (const event of events ?? []) {
8098
+ if (!event.id || event.output === void 0 || event.output === null || seen.has(event.id)) {
8099
+ continue;
8100
+ }
8101
+ seen.add(event.id);
8102
+ executed.push(event);
8103
+ }
8104
+ if (executed.length === 0) return [];
8105
+ return [
8106
+ {
8107
+ role: "assistant",
8108
+ content: void 0,
8109
+ tool_calls: executed.map((event) => ({
8110
+ id: event.id,
8111
+ type: "function",
8112
+ function: { name: event.name, arguments: event.arguments }
8113
+ }))
8114
+ },
8115
+ ...executed.map(
8116
+ (event) => ({
8117
+ role: "tool",
8118
+ tool_call_id: event.id,
8119
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output ?? "") }]
8120
+ })
8121
+ )
8122
+ ];
8123
+ }
7980
8124
  function getToolName(tool) {
7981
8125
  const func = tool.function;
7982
8126
  const nestedName = func?.name;
@@ -8746,7 +8890,14 @@ Executing tool: ${toolInfo}
8746
8890
  }
8747
8891
  }))
8748
8892
  };
8749
- const toolResultMessages = [assistantMessage];
8893
+ const toolResultMessages = [
8894
+ ...serverToolCallMessages(
8895
+ currentAccumulator.toolCallEvents,
8896
+ currentMessages,
8897
+ new Set(toolCallsToExecute.map((tc) => tc.id))
8898
+ ),
8899
+ assistantMessage
8900
+ ];
8750
8901
  for (const execResult of continueResults) {
8751
8902
  const resultContent = execResult.error ? `Error: ${execResult.error}` : safeJsonStringify(execResult.result);
8752
8903
  toolResultMessages.push({
@@ -11423,100 +11574,6 @@ function isSupportedMediaType(mimeType) {
11423
11574
  var import_watermelondb8 = require("@nozbe/watermelondb");
11424
11575
  init_logger();
11425
11576
 
11426
- // src/lib/storage/mcpImages.ts
11427
- var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
11428
- "AnumaMediaMCP-anuma_create_image",
11429
- "anuma_create_image",
11430
- // Deprecated anuma-image-mcp tools, retained so images in already-stored
11431
- // conversations still re-materialize on replay.
11432
- "AnumaImageMCP-generate_cloud_image",
11433
- "AnumaImageMCP-edit_cloud_image",
11434
- "generate_cloud_image",
11435
- "edit_cloud_image"
11436
- ]);
11437
- var VIDEO_TOOL_NAMES = /* @__PURE__ */ new Set([
11438
- "AnumaMediaMCP-anuma_create_video",
11439
- "AnumaFalMCP-fal_generate_video",
11440
- "anuma_create_video",
11441
- "fal_generate_video"
11442
- ]);
11443
- var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
11444
- var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
11445
- function videoExtensionOf(value) {
11446
- return value?.match(VIDEO_EXTENSION_RE)?.[1]?.toLowerCase() ?? null;
11447
- }
11448
- function classifyUrl(url) {
11449
- return VIDEO_EXTENSION_RE.test(url) ? "video" : "image";
11450
- }
11451
- function extractMCPImageUrls(content, toolCallEvents, mcpR2Domain) {
11452
- const urls = [];
11453
- if (toolCallEvents && toolCallEvents.length > 0) {
11454
- for (const event of toolCallEvents) {
11455
- if (!event.name) continue;
11456
- if (IMAGE_TOOL_NAMES.has(event.name)) {
11457
- try {
11458
- const output = JSON.parse(event.output || "{}");
11459
- const model = output.model || "image";
11460
- const imageUrls = [
11461
- ...new Set(
11462
- [
11463
- ...output.output_images?.map((i) => i.url) ?? [],
11464
- output.imageUrl,
11465
- output.url
11466
- ].filter((u) => Boolean(u))
11467
- )
11468
- ];
11469
- for (const url of imageUrls) {
11470
- urls.push({ url, model, mediaType: "image" });
11471
- }
11472
- } catch {
11473
- }
11474
- } else if (VIDEO_TOOL_NAMES.has(event.name)) {
11475
- try {
11476
- const output = JSON.parse(event.output || "{}");
11477
- const model = output.model || "video";
11478
- const videoUrls = [
11479
- ...new Set(
11480
- [
11481
- ...output.videos?.map((v) => v.video_url) ?? [],
11482
- output.videoUrl,
11483
- output.url
11484
- ].filter((u) => Boolean(u))
11485
- )
11486
- ];
11487
- for (const url of videoUrls) {
11488
- urls.push({ url, model, mediaType: "video" });
11489
- }
11490
- } catch {
11491
- }
11492
- }
11493
- }
11494
- }
11495
- if (urls.length === 0 && content) {
11496
- const escaped = mcpR2Domain.replace(/\./g, "\\.");
11497
- const patterns = [
11498
- new RegExp(`https://${escaped}[^\\s"'<>)\\]]+`, "gi"),
11499
- // Require the proxy's <svc>/<token> shape (>=2 path segments after
11500
- // /api/v1/media/) so a third-party URL that only shallowly contains
11501
- // /api/v1/media/ isn't returned as a portal media asset.
11502
- new RegExp(`https?://[^\\s"'<>)\\]]+/api/v1/media/[^/\\s"'<>)\\]]+/[^\\s"'<>)\\]]+`, "gi")
11503
- ];
11504
- const matches = patterns.flatMap((re) => content.match(re) ?? []);
11505
- if (matches.length > 0) {
11506
- const seen = /* @__PURE__ */ new Set();
11507
- for (const url of matches) {
11508
- const normalized = url.replace(/[)"'>\s]+$/, "");
11509
- if (!seen.has(normalized)) {
11510
- seen.add(normalized);
11511
- const mediaType = classifyUrl(normalized);
11512
- urls.push({ url: normalized, model: "image", mediaType });
11513
- }
11514
- }
11515
- }
11516
- }
11517
- return urls;
11518
- }
11519
-
11520
11577
  // src/lib/storage/opfs.ts
11521
11578
  init_logger();
11522
11579
  var FILE_PLACEHOLDER_PREFIX = "__SDKFILE__";
@@ -23976,24 +24033,10 @@ async function storedToLlmapiMessage(stored, encryptionKey, resolveMediaByIds) {
23976
24033
  });
23977
24034
  for (const event of stored.toolCallEvents) {
23978
24035
  if (event.id && event.output !== void 0 && event.output !== null) {
23979
- let toolOutput = event.output;
23980
- if (event.name && IMAGE_TOOL_NAMES.has(event.name)) {
23981
- try {
23982
- const parsed = JSON.parse(toolOutput);
23983
- const {
23984
- imageUrl: _imageUrl,
23985
- url: _url,
23986
- output_images: _outputImages,
23987
- ...rest
23988
- } = parsed;
23989
- toolOutput = JSON.stringify(rest);
23990
- } catch {
23991
- }
23992
- }
23993
24036
  messages.push({
23994
24037
  role: "tool",
23995
24038
  tool_call_id: event.id,
23996
- content: [{ type: "text", text: toolOutput }]
24039
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output) }]
23997
24040
  });
23998
24041
  }
23999
24042
  }
@@ -3986,6 +3986,114 @@ async function chunkAndEmbedAllMessages(ctx, options, filter) {
3986
3986
  return embeddedCount;
3987
3987
  }
3988
3988
 
3989
+ // src/lib/storage/mcpImages.ts
3990
+ var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
3991
+ "AnumaMediaMCP-anuma_create_image",
3992
+ "anuma_create_image",
3993
+ // Deprecated anuma-image-mcp tools, retained so images in already-stored
3994
+ // conversations still re-materialize on replay.
3995
+ "AnumaImageMCP-generate_cloud_image",
3996
+ "AnumaImageMCP-edit_cloud_image",
3997
+ "generate_cloud_image",
3998
+ "edit_cloud_image"
3999
+ ]);
4000
+ function toolOutputForModel(name, output) {
4001
+ if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
4002
+ try {
4003
+ const {
4004
+ imageUrl: _imageUrl,
4005
+ url: _url,
4006
+ output_images: _outputImages,
4007
+ ...rest
4008
+ } = JSON.parse(output);
4009
+ return JSON.stringify(rest);
4010
+ } catch {
4011
+ return output;
4012
+ }
4013
+ }
4014
+ var VIDEO_TOOL_NAMES = /* @__PURE__ */ new Set([
4015
+ "AnumaMediaMCP-anuma_create_video",
4016
+ "AnumaFalMCP-fal_generate_video",
4017
+ "anuma_create_video",
4018
+ "fal_generate_video"
4019
+ ]);
4020
+ var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
4021
+ var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
4022
+ function videoExtensionOf(value) {
4023
+ return value?.match(VIDEO_EXTENSION_RE)?.[1]?.toLowerCase() ?? null;
4024
+ }
4025
+ function classifyUrl(url) {
4026
+ return VIDEO_EXTENSION_RE.test(url) ? "video" : "image";
4027
+ }
4028
+ function extractMCPImageUrls(content, toolCallEvents, mcpR2Domain) {
4029
+ const urls = [];
4030
+ if (toolCallEvents && toolCallEvents.length > 0) {
4031
+ for (const event of toolCallEvents) {
4032
+ if (!event.name) continue;
4033
+ if (IMAGE_TOOL_NAMES.has(event.name)) {
4034
+ try {
4035
+ const output = JSON.parse(event.output || "{}");
4036
+ const model = output.model || "image";
4037
+ const imageUrls = [
4038
+ ...new Set(
4039
+ [
4040
+ ...output.output_images?.map((i) => i.url) ?? [],
4041
+ output.imageUrl,
4042
+ output.url
4043
+ ].filter((u) => Boolean(u))
4044
+ )
4045
+ ];
4046
+ for (const url of imageUrls) {
4047
+ urls.push({ url, model, mediaType: "image" });
4048
+ }
4049
+ } catch {
4050
+ }
4051
+ } else if (VIDEO_TOOL_NAMES.has(event.name)) {
4052
+ try {
4053
+ const output = JSON.parse(event.output || "{}");
4054
+ const model = output.model || "video";
4055
+ const videoUrls = [
4056
+ ...new Set(
4057
+ [
4058
+ ...output.videos?.map((v) => v.video_url) ?? [],
4059
+ output.videoUrl,
4060
+ output.url
4061
+ ].filter((u) => Boolean(u))
4062
+ )
4063
+ ];
4064
+ for (const url of videoUrls) {
4065
+ urls.push({ url, model, mediaType: "video" });
4066
+ }
4067
+ } catch {
4068
+ }
4069
+ }
4070
+ }
4071
+ }
4072
+ if (urls.length === 0 && content) {
4073
+ const escaped = mcpR2Domain.replace(/\./g, "\\.");
4074
+ const patterns = [
4075
+ new RegExp(`https://${escaped}[^\\s"'<>)\\]]+`, "gi"),
4076
+ // Require the proxy's <svc>/<token> shape (>=2 path segments after
4077
+ // /api/v1/media/) so a third-party URL that only shallowly contains
4078
+ // /api/v1/media/ isn't returned as a portal media asset.
4079
+ new RegExp(`https?://[^\\s"'<>)\\]]+/api/v1/media/[^/\\s"'<>)\\]]+/[^\\s"'<>)\\]]+`, "gi")
4080
+ ];
4081
+ const matches = patterns.flatMap((re) => content.match(re) ?? []);
4082
+ if (matches.length > 0) {
4083
+ const seen = /* @__PURE__ */ new Set();
4084
+ for (const url of matches) {
4085
+ const normalized = url.replace(/[)"'>\s]+$/, "");
4086
+ if (!seen.has(normalized)) {
4087
+ seen.add(normalized);
4088
+ const mediaType = classifyUrl(normalized);
4089
+ urls.push({ url: normalized, model: "image", mediaType });
4090
+ }
4091
+ }
4092
+ }
4093
+ }
4094
+ return urls;
4095
+ }
4096
+
3989
4097
  // src/lib/chat/runHooks.ts
3990
4098
  function composeHooks(hooks) {
3991
4099
  const handlers = hooks.filter((h) => h !== void 0 && h !== null);
@@ -6116,6 +6224,40 @@ function extractLastUserText(messages) {
6116
6224
  function isToolErrorResult(result) {
6117
6225
  return result !== null && typeof result === "object" && "error" in result && typeof result.error === "string";
6118
6226
  }
6227
+ function serverToolCallMessages(events, messages, skipIds) {
6228
+ const seen = new Set(skipIds);
6229
+ for (const m of messages) {
6230
+ if (m.tool_call_id) seen.add(m.tool_call_id);
6231
+ for (const tc of m.tool_calls ?? []) if (tc.id) seen.add(tc.id);
6232
+ }
6233
+ const executed = [];
6234
+ for (const event of events ?? []) {
6235
+ if (!event.id || event.output === void 0 || event.output === null || seen.has(event.id)) {
6236
+ continue;
6237
+ }
6238
+ seen.add(event.id);
6239
+ executed.push(event);
6240
+ }
6241
+ if (executed.length === 0) return [];
6242
+ return [
6243
+ {
6244
+ role: "assistant",
6245
+ content: void 0,
6246
+ tool_calls: executed.map((event) => ({
6247
+ id: event.id,
6248
+ type: "function",
6249
+ function: { name: event.name, arguments: event.arguments }
6250
+ }))
6251
+ },
6252
+ ...executed.map(
6253
+ (event) => ({
6254
+ role: "tool",
6255
+ tool_call_id: event.id,
6256
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output ?? "") }]
6257
+ })
6258
+ )
6259
+ ];
6260
+ }
6119
6261
  function getToolName(tool) {
6120
6262
  const func = tool.function;
6121
6263
  const nestedName = func?.name;
@@ -6885,7 +7027,14 @@ Executing tool: ${toolInfo}
6885
7027
  }
6886
7028
  }))
6887
7029
  };
6888
- const toolResultMessages = [assistantMessage];
7030
+ const toolResultMessages = [
7031
+ ...serverToolCallMessages(
7032
+ currentAccumulator.toolCallEvents,
7033
+ currentMessages,
7034
+ new Set(toolCallsToExecute.map((tc) => tc.id))
7035
+ ),
7036
+ assistantMessage
7037
+ ];
6889
7038
  for (const execResult of continueResults) {
6890
7039
  const resultContent = execResult.error ? `Error: ${execResult.error}` : safeJsonStringify(execResult.result);
6891
7040
  toolResultMessages.push({
@@ -9568,100 +9717,6 @@ function isSupportedMediaType(mimeType) {
9568
9717
  // src/lib/db/media/operations.ts
9569
9718
  import { Q as Q4 } from "@nozbe/watermelondb";
9570
9719
 
9571
- // src/lib/storage/mcpImages.ts
9572
- var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
9573
- "AnumaMediaMCP-anuma_create_image",
9574
- "anuma_create_image",
9575
- // Deprecated anuma-image-mcp tools, retained so images in already-stored
9576
- // conversations still re-materialize on replay.
9577
- "AnumaImageMCP-generate_cloud_image",
9578
- "AnumaImageMCP-edit_cloud_image",
9579
- "generate_cloud_image",
9580
- "edit_cloud_image"
9581
- ]);
9582
- var VIDEO_TOOL_NAMES = /* @__PURE__ */ new Set([
9583
- "AnumaMediaMCP-anuma_create_video",
9584
- "AnumaFalMCP-fal_generate_video",
9585
- "anuma_create_video",
9586
- "fal_generate_video"
9587
- ]);
9588
- var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
9589
- var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
9590
- function videoExtensionOf(value) {
9591
- return value?.match(VIDEO_EXTENSION_RE)?.[1]?.toLowerCase() ?? null;
9592
- }
9593
- function classifyUrl(url) {
9594
- return VIDEO_EXTENSION_RE.test(url) ? "video" : "image";
9595
- }
9596
- function extractMCPImageUrls(content, toolCallEvents, mcpR2Domain) {
9597
- const urls = [];
9598
- if (toolCallEvents && toolCallEvents.length > 0) {
9599
- for (const event of toolCallEvents) {
9600
- if (!event.name) continue;
9601
- if (IMAGE_TOOL_NAMES.has(event.name)) {
9602
- try {
9603
- const output = JSON.parse(event.output || "{}");
9604
- const model = output.model || "image";
9605
- const imageUrls = [
9606
- ...new Set(
9607
- [
9608
- ...output.output_images?.map((i) => i.url) ?? [],
9609
- output.imageUrl,
9610
- output.url
9611
- ].filter((u) => Boolean(u))
9612
- )
9613
- ];
9614
- for (const url of imageUrls) {
9615
- urls.push({ url, model, mediaType: "image" });
9616
- }
9617
- } catch {
9618
- }
9619
- } else if (VIDEO_TOOL_NAMES.has(event.name)) {
9620
- try {
9621
- const output = JSON.parse(event.output || "{}");
9622
- const model = output.model || "video";
9623
- const videoUrls = [
9624
- ...new Set(
9625
- [
9626
- ...output.videos?.map((v) => v.video_url) ?? [],
9627
- output.videoUrl,
9628
- output.url
9629
- ].filter((u) => Boolean(u))
9630
- )
9631
- ];
9632
- for (const url of videoUrls) {
9633
- urls.push({ url, model, mediaType: "video" });
9634
- }
9635
- } catch {
9636
- }
9637
- }
9638
- }
9639
- }
9640
- if (urls.length === 0 && content) {
9641
- const escaped = mcpR2Domain.replace(/\./g, "\\.");
9642
- const patterns = [
9643
- new RegExp(`https://${escaped}[^\\s"'<>)\\]]+`, "gi"),
9644
- // Require the proxy's <svc>/<token> shape (>=2 path segments after
9645
- // /api/v1/media/) so a third-party URL that only shallowly contains
9646
- // /api/v1/media/ isn't returned as a portal media asset.
9647
- new RegExp(`https?://[^\\s"'<>)\\]]+/api/v1/media/[^/\\s"'<>)\\]]+/[^\\s"'<>)\\]]+`, "gi")
9648
- ];
9649
- const matches = patterns.flatMap((re) => content.match(re) ?? []);
9650
- if (matches.length > 0) {
9651
- const seen = /* @__PURE__ */ new Set();
9652
- for (const url of matches) {
9653
- const normalized = url.replace(/[)"'>\s]+$/, "");
9654
- if (!seen.has(normalized)) {
9655
- seen.add(normalized);
9656
- const mediaType = classifyUrl(normalized);
9657
- urls.push({ url: normalized, model: "image", mediaType });
9658
- }
9659
- }
9660
- }
9661
- }
9662
- return urls;
9663
- }
9664
-
9665
9720
  // src/lib/storage/opfs.ts
9666
9721
  var FILE_PLACEHOLDER_PREFIX = "__SDKFILE__";
9667
9722
  var FILE_PLACEHOLDER_SUFFIX = "__";
@@ -22053,24 +22108,10 @@ async function storedToLlmapiMessage(stored, encryptionKey, resolveMediaByIds) {
22053
22108
  });
22054
22109
  for (const event of stored.toolCallEvents) {
22055
22110
  if (event.id && event.output !== void 0 && event.output !== null) {
22056
- let toolOutput = event.output;
22057
- if (event.name && IMAGE_TOOL_NAMES.has(event.name)) {
22058
- try {
22059
- const parsed = JSON.parse(toolOutput);
22060
- const {
22061
- imageUrl: _imageUrl,
22062
- url: _url,
22063
- output_images: _outputImages,
22064
- ...rest
22065
- } = parsed;
22066
- toolOutput = JSON.stringify(rest);
22067
- } catch {
22068
- }
22069
- }
22070
22111
  messages.push({
22071
22112
  role: "tool",
22072
22113
  tool_call_id: event.id,
22073
- content: [{ type: "text", text: toolOutput }]
22114
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output) }]
22074
22115
  });
22075
22116
  }
22076
22117
  }
@@ -23358,6 +23358,30 @@ var import_watermelondb20 = __toESM(require_watermelondb());
23358
23358
  init_logger();
23359
23359
 
23360
23360
  // src/lib/storage/mcpImages.ts
23361
+ var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
23362
+ "AnumaMediaMCP-anuma_create_image",
23363
+ "anuma_create_image",
23364
+ // Deprecated anuma-image-mcp tools, retained so images in already-stored
23365
+ // conversations still re-materialize on replay.
23366
+ "AnumaImageMCP-generate_cloud_image",
23367
+ "AnumaImageMCP-edit_cloud_image",
23368
+ "generate_cloud_image",
23369
+ "edit_cloud_image"
23370
+ ]);
23371
+ function toolOutputForModel(name, output) {
23372
+ if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
23373
+ try {
23374
+ const {
23375
+ imageUrl: _imageUrl,
23376
+ url: _url,
23377
+ output_images: _outputImages,
23378
+ ...rest
23379
+ } = JSON.parse(output);
23380
+ return JSON.stringify(rest);
23381
+ } catch {
23382
+ return output;
23383
+ }
23384
+ }
23361
23385
  var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
23362
23386
  var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
23363
23387
  function videoExtensionOf(value) {
@@ -37102,6 +37126,40 @@ function extractLastUserText(messages) {
37102
37126
  function isToolErrorResult(result) {
37103
37127
  return result !== null && typeof result === "object" && "error" in result && typeof result.error === "string";
37104
37128
  }
37129
+ function serverToolCallMessages(events, messages, skipIds) {
37130
+ const seen = new Set(skipIds);
37131
+ for (const m of messages) {
37132
+ if (m.tool_call_id) seen.add(m.tool_call_id);
37133
+ for (const tc of m.tool_calls ?? []) if (tc.id) seen.add(tc.id);
37134
+ }
37135
+ const executed = [];
37136
+ for (const event of events ?? []) {
37137
+ if (!event.id || event.output === void 0 || event.output === null || seen.has(event.id)) {
37138
+ continue;
37139
+ }
37140
+ seen.add(event.id);
37141
+ executed.push(event);
37142
+ }
37143
+ if (executed.length === 0) return [];
37144
+ return [
37145
+ {
37146
+ role: "assistant",
37147
+ content: void 0,
37148
+ tool_calls: executed.map((event) => ({
37149
+ id: event.id,
37150
+ type: "function",
37151
+ function: { name: event.name, arguments: event.arguments }
37152
+ }))
37153
+ },
37154
+ ...executed.map(
37155
+ (event) => ({
37156
+ role: "tool",
37157
+ tool_call_id: event.id,
37158
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output ?? "") }]
37159
+ })
37160
+ )
37161
+ ];
37162
+ }
37105
37163
  function getToolName(tool) {
37106
37164
  const func = tool.function;
37107
37165
  const nestedName = func?.name;
@@ -37871,7 +37929,14 @@ Executing tool: ${toolInfo}
37871
37929
  }
37872
37930
  }))
37873
37931
  };
37874
- const toolResultMessages = [assistantMessage];
37932
+ const toolResultMessages = [
37933
+ ...serverToolCallMessages(
37934
+ currentAccumulator.toolCallEvents,
37935
+ currentMessages,
37936
+ new Set(toolCallsToExecute.map((tc) => tc.id))
37937
+ ),
37938
+ assistantMessage
37939
+ ];
37875
37940
  for (const execResult of continueResults) {
37876
37941
  const resultContent = execResult.error ? `Error: ${execResult.error}` : safeJsonStringify(execResult.result);
37877
37942
  toolResultMessages.push({