dsh-agent-toolkit 0.2.4 → 0.2.6

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/lib/client.js CHANGED
@@ -67,15 +67,15 @@ window.__ModuleLoader__.load({
67
67
  document.head.appendChild(tag);
68
68
  }
69
69
  var delegate_card_module_css_default = {
70
+ "summary": "_8wQUq_summary",
70
71
  "prompt": "_8wQUq_prompt",
71
72
  "row": "_8wQUq_row",
72
- "result": "_8wQUq_result",
73
- "body": "_8wQUq_body",
74
- "visuallyHidden": "_8wQUq_visuallyHidden",
75
73
  "childLink": "_8wQUq_childLink",
76
- "chip": "_8wQUq_chip",
77
74
  "root": "_8wQUq_root",
78
- "summary": "_8wQUq_summary"
75
+ "result": "_8wQUq_result",
76
+ "chip": "_8wQUq_chip",
77
+ "visuallyHidden": "_8wQUq_visuallyHidden",
78
+ "body": "_8wQUq_body"
79
79
  };
80
80
  //#endregion
81
81
  //#region src/client/delegate/delegate-card.tsx
@@ -311,8 +311,8 @@ window.__ModuleLoader__.load({
311
311
  document.head.appendChild(tag);
312
312
  }
313
313
  var entry_module_css_default$1 = {
314
- "rail": "VJWWsa_rail",
315
- "trigger": "VJWWsa_trigger"
314
+ "trigger": "VJWWsa_trigger",
315
+ "rail": "VJWWsa_rail"
316
316
  };
317
317
  //#endregion
318
318
  //#region src/client/shared/entry.tsx
@@ -411,33 +411,33 @@ window.__ModuleLoader__.load({
411
411
  document.head.appendChild(tag);
412
412
  }
413
413
  var agents_module_css_default = {
414
- "readonlyId": "KsfSLW_readonlyId",
414
+ "hint": "KsfSLW_hint",
415
415
  "listPane": "KsfSLW_listPane",
416
+ "agentRow": "KsfSLW_agentRow",
416
417
  "agentRowActive": "KsfSLW_agentRowActive",
417
- "agentDesc": "KsfSLW_agentDesc",
418
- "split": "KsfSLW_split",
419
- "toolGrid": "KsfSLW_toolGrid",
420
- "block": "KsfSLW_block",
421
- "toolCheck": "KsfSLW_toolCheck",
418
+ "createButton": "KsfSLW_createButton",
419
+ "editorPane": "KsfSLW_editorPane",
420
+ "editor": "KsfSLW_editor",
422
421
  "blockTitle": "KsfSLW_blockTitle",
423
- "hint": "KsfSLW_hint",
422
+ "toolGrid": "KsfSLW_toolGrid",
423
+ "split": "KsfSLW_split",
424
+ "actions": "KsfSLW_actions",
424
425
  "dangerButton": "KsfSLW_dangerButton",
425
- "input": "KsfSLW_input",
426
- "textarea": "KsfSLW_textarea",
427
- "editorPane": "KsfSLW_editorPane",
426
+ "toolGroupTitle": "KsfSLW_toolGroupTitle",
427
+ "toolCheck": "KsfSLW_toolCheck",
428
+ "readonlyId": "KsfSLW_readonlyId",
429
+ "select": "KsfSLW_select",
430
+ "toolMode": "KsfSLW_toolMode",
428
431
  "field": "KsfSLW_field",
429
- "dialog": "KsfSLW_dialog",
432
+ "toolGroupSet": "KsfSLW_toolGroupSet",
430
433
  "error": "KsfSLW_error",
431
- "agentRow": "KsfSLW_agentRow",
434
+ "agentDesc": "KsfSLW_agentDesc",
432
435
  "agentName": "KsfSLW_agentName",
433
- "editor": "KsfSLW_editor",
434
- "select": "KsfSLW_select",
435
- "actions": "KsfSLW_actions",
436
- "createButton": "KsfSLW_createButton",
437
- "toolMode": "KsfSLW_toolMode",
438
436
  "builtinBadge": "KsfSLW_builtinBadge",
439
- "toolGroupSet": "KsfSLW_toolGroupSet",
440
- "toolGroupTitle": "KsfSLW_toolGroupTitle"
437
+ "textarea": "KsfSLW_textarea",
438
+ "dialog": "KsfSLW_dialog",
439
+ "block": "KsfSLW_block",
440
+ "input": "KsfSLW_input"
441
441
  };
442
442
  //#endregion
443
443
  //#region src/client/agents/AgentEditor.tsx
@@ -951,24 +951,24 @@ window.__ModuleLoader__.load({
951
951
  document.head.appendChild(tag);
952
952
  }
953
953
  var prompt_module_css_default = {
954
- "editor": "VZk1fa_editor",
955
- "error": "VZk1fa_error",
956
- "editorPane": "VZk1fa_editorPane",
957
954
  "tab": "VZk1fa_tab",
958
- "layerOrder": "VZk1fa_layerOrder",
959
955
  "field": "VZk1fa_field",
960
- "dialog": "VZk1fa_dialog",
956
+ "textarea": "VZk1fa_textarea",
957
+ "error": "VZk1fa_error",
958
+ "layerRowActive": "VZk1fa_layerRowActive",
959
+ "layerOrder": "VZk1fa_layerOrder",
961
960
  "listPane": "VZk1fa_listPane",
962
- "hint": "VZk1fa_hint",
963
- "tabs": "VZk1fa_tabs",
964
- "layerRow": "VZk1fa_layerRow",
961
+ "dialog": "VZk1fa_dialog",
962
+ "editorPane": "VZk1fa_editorPane",
963
+ "editor": "VZk1fa_editor",
965
964
  "split": "VZk1fa_split",
965
+ "actions": "VZk1fa_actions",
966
+ "layerRow": "VZk1fa_layerRow",
967
+ "readonlyTag": "VZk1fa_readonlyTag",
966
968
  "tabActive": "VZk1fa_tabActive",
967
969
  "layerName": "VZk1fa_layerName",
968
- "textarea": "VZk1fa_textarea",
969
- "actions": "VZk1fa_actions",
970
- "layerRowActive": "VZk1fa_layerRowActive",
971
- "readonlyTag": "VZk1fa_readonlyTag"
970
+ "tabs": "VZk1fa_tabs",
971
+ "hint": "VZk1fa_hint"
972
972
  };
973
973
  //#endregion
974
974
  //#region src/client/prompt/PromptLayersModal.tsx
@@ -1452,13 +1452,13 @@ window.__ModuleLoader__.load({
1452
1452
  document.head.appendChild(tag);
1453
1453
  }
1454
1454
  var chart_module_css_default = {
1455
- "swatchCached": "_8LXSJW_swatchCached",
1455
+ "chartTheme": "_8LXSJW_chartTheme",
1456
+ "tooltipTotal": "_8LXSJW_tooltipTotal",
1456
1457
  "legend": "_8LXSJW_legend",
1457
- "swatchFresh": "_8LXSJW_swatchFresh",
1458
1458
  "tooltipTitle": "_8LXSJW_tooltipTitle",
1459
- "tooltip": "_8LXSJW_tooltip",
1460
- "tooltipTotal": "_8LXSJW_tooltipTotal",
1461
- "chartTheme": "_8LXSJW_chartTheme"
1459
+ "swatchFresh": "_8LXSJW_swatchFresh",
1460
+ "swatchCached": "_8LXSJW_swatchCached",
1461
+ "tooltip": "_8LXSJW_tooltip"
1462
1462
  };
1463
1463
  //#endregion
1464
1464
  //#region \0dsh-css:D:\work\github\dsh\dsh-agent-toolkit\packages\usage\src\client\usage\ActivityHeatmap.module.css.mjs
@@ -1472,14 +1472,14 @@ window.__ModuleLoader__.load({
1472
1472
  document.head.appendChild(tag);
1473
1473
  }
1474
1474
  var ActivityHeatmap_module_css_default = {
1475
+ "level3": "hjrYrq_level3",
1475
1476
  "level2": "hjrYrq_level2",
1476
- "level4": "hjrYrq_level4",
1477
1477
  "level0": "hjrYrq_level0",
1478
1478
  "grid": "hjrYrq_grid",
1479
- "months": "hjrYrq_months",
1479
+ "level4": "hjrYrq_level4",
1480
+ "level1": "hjrYrq_level1",
1480
1481
  "week": "hjrYrq_week",
1481
- "level3": "hjrYrq_level3",
1482
- "level1": "hjrYrq_level1"
1482
+ "months": "hjrYrq_months"
1483
1483
  };
1484
1484
  //#endregion
1485
1485
  //#region ../usage/src/client/usage/ActivityHeatmap.tsx
@@ -52516,19 +52516,19 @@ ${te.current.stack}
52516
52516
  document.head.appendChild(tag);
52517
52517
  }
52518
52518
  var UsageModal_module_css_default = {
52519
- "dialog": "MODeXa_dialog",
52520
- "dateLabel": "MODeXa_dateLabel",
52521
- "pagerButton": "MODeXa_pagerButton",
52522
52519
  "compaction": "MODeXa_compaction",
52520
+ "dateLabel": "MODeXa_dateLabel",
52521
+ "tab": "MODeXa_tab",
52522
+ "total": "MODeXa_total",
52523
52523
  "tabActive": "MODeXa_tabActive",
52524
+ "pagerButton": "MODeXa_pagerButton",
52525
+ "pager": "MODeXa_pager",
52524
52526
  "rowName": "MODeXa_rowName",
52527
+ "dialog": "MODeXa_dialog",
52525
52528
  "sectionTitle": "MODeXa_sectionTitle",
52526
52529
  "tabs": "MODeXa_tabs",
52527
52530
  "row": "MODeXa_row",
52528
- "total": "MODeXa_total",
52529
- "rowCalls": "MODeXa_rowCalls",
52530
- "pager": "MODeXa_pager",
52531
- "tab": "MODeXa_tab"
52531
+ "rowCalls": "MODeXa_rowCalls"
52532
52532
  };
52533
52533
  //#endregion
52534
52534
  //#region ../usage/src/client/usage/UsageModal.tsx
@@ -55153,29 +55153,29 @@ ${te.current.stack}
55153
55153
  document.head.appendChild(tag);
55154
55154
  }
55155
55155
  var bots_module_css_default = {
55156
- "botRow": "vTbXga_botRow",
55157
- "tabActive": "vTbXga_tabActive",
55158
- "dialog": "vTbXga_dialog",
55156
+ "formActions": "vTbXga_formActions",
55157
+ "steps": "vTbXga_steps",
55158
+ "tab": "vTbXga_tab",
55159
55159
  "error": "vTbXga_error",
55160
- "group": "vTbXga_group",
55161
- "createButton": "vTbXga_createButton",
55162
55160
  "tabs": "vTbXga_tabs",
55161
+ "dialog": "vTbXga_dialog",
55162
+ "step": "vTbXga_step",
55163
+ "scanSection": "vTbXga_scanSection",
55164
+ "textarea": "vTbXga_textarea",
55165
+ "createButton": "vTbXga_createButton",
55166
+ "groupTitle": "vTbXga_groupTitle",
55163
55167
  "stepSeparator": "vTbXga_stepSeparator",
55164
- "stepActive": "vTbXga_stepActive",
55168
+ "status": "vTbXga_status",
55165
55169
  "hint": "vTbXga_hint",
55170
+ "botRow": "vTbXga_botRow",
55171
+ "channelBadge": "vTbXga_channelBadge",
55172
+ "group": "vTbXga_group",
55173
+ "tabActive": "vTbXga_tabActive",
55174
+ "stepActive": "vTbXga_stepActive",
55166
55175
  "botName": "vTbXga_botName",
55167
55176
  "select": "vTbXga_select",
55168
- "channelBadge": "vTbXga_channelBadge",
55169
- "scanSection": "vTbXga_scanSection",
55170
- "textarea": "vTbXga_textarea",
55171
- "steps": "vTbXga_steps",
55172
- "field": "vTbXga_field",
55173
- "formActions": "vTbXga_formActions",
55174
- "tab": "vTbXga_tab",
55175
- "step": "vTbXga_step",
55176
- "groupTitle": "vTbXga_groupTitle",
55177
55177
  "input": "vTbXga_input",
55178
- "status": "vTbXga_status"
55178
+ "field": "vTbXga_field"
55179
55179
  };
55180
55180
  //#endregion
55181
55181
  //#region src/client/bots/BotForm.tsx
package/lib/index.js CHANGED
@@ -1354,6 +1354,15 @@ function createDelegateTool(toolName, deps) {
1354
1354
  /** 团队名册段在 prompt 中的位置:紧随内置 subagent 段(116.5)之后。 */
1355
1355
  const TEAM_SECTION_ORDER = 116.6;
1356
1356
  /**
1357
+ * 团队名册段文本:仅当该 agent scope 的可见工具里确有委派工具时渲染,否则空
1358
+ * (空段渲染时被丢弃)。工具白名单 restrict 只作用于 tools 视图,名册段必须
1359
+ * 自行按 `tools.get(name, scope)` 联动,否则没勾委派的 bot 提示词仍会出现委派段落。
1360
+ */
1361
+ function teamSectionText(toolName, roles, toolVisible) {
1362
+ if (!toolVisible) return "";
1363
+ return `你有一组可委派的成员:用 ${toolName} 把自包含的子任务委派给合适的成员,成员结果会作为工具返回值回到本对话。\n可用成员:\n${roles.filter((r) => r.id !== "main").map((r) => `${r.id}: ${r.description ?? r.name}`).join("\n")}`;
1364
+ }
1365
+ /**
1357
1366
  * 挂载 team_delegate 工具与函数式团队提示段。
1358
1367
  * provider 能力守卫与生命周期镜像同归档 tool-subagent:persona/depthLimit 缺失响亮报错,
1359
1368
  * 工具随 provider 在场与否挂载/摘除。名册来自注册表(main 排除),工具与提示段都经
@@ -1400,10 +1409,7 @@ function setupDelegate(ctx, config, registry, channels) {
1400
1409
  ctx.systemPrompt.section({
1401
1410
  name: "plugin:dsh-agent-toolkit:team",
1402
1411
  order: TEAM_SECTION_ORDER,
1403
- text: () => {
1404
- const rosterText = registry.list().filter((r) => r.id !== "main").map((r) => `${r.id}: ${r.description ?? r.name}`).join("\n");
1405
- return `你有一组可委派的成员:用 ${config.toolName} 把自包含的子任务委派给合适的成员,成员结果会作为工具返回值回到本对话。\n可用成员:\n${rosterText}`;
1406
- }
1412
+ text: (context) => teamSectionText(config.toolName, registry.list(), ctx.tools.get(config.toolName, context.scope) !== void 0)
1407
1413
  });
1408
1414
  }
1409
1415
  //#endregion
@@ -1692,6 +1698,22 @@ async function setupAgentScope(agentCtx, hooks, toolsScope) {
1692
1698
  }
1693
1699
  //#endregion
1694
1700
  //#region src/channels/feishu/api.ts
1701
+ /** 附件服务接受的图片媒体类型(与宿主 attachment v1 一致)。 */
1702
+ const IMAGE_MEDIA_TYPES = [
1703
+ "image/png",
1704
+ "image/jpeg",
1705
+ "image/webp",
1706
+ "image/gif"
1707
+ ];
1708
+ /** 按魔数判定图片媒体类型;未知字节时回退响应头 content-type(限接受集合),否则 undefined。 */
1709
+ function sniffImageMediaType(data, contentType) {
1710
+ if (data.length >= 4 && data[0] === 137 && data[1] === 80 && data[2] === 78 && data[3] === 71) return "image/png";
1711
+ if (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255) return "image/jpeg";
1712
+ if (data.length >= 4 && data[0] === 71 && data[1] === 73 && data[2] === 70 && data[3] === 56) return "image/gif";
1713
+ if (data.length >= 12 && data[0] === 82 && data[1] === 73 && data[2] === 70 && data[3] === 70 && data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80) return "image/webp";
1714
+ const declared = typeof contentType === "string" ? contentType.split(";")[0].trim().toLowerCase() : "";
1715
+ return IMAGE_MEDIA_TYPES.includes(declared) ? declared : void 0;
1716
+ }
1695
1717
  /** SDK 薄封装:tenant_access_token 由 SDK 自动管理;错误带 code/msg 上下文。 */
1696
1718
  function createFeishuApi(client) {
1697
1719
  return {
@@ -1788,6 +1810,31 @@ function createFeishuApi(client) {
1788
1810
  message_id: messageId,
1789
1811
  reaction_id: reactionId
1790
1812
  } });
1813
+ },
1814
+ async downloadImage(messageId, fileKey) {
1815
+ const res = await client.im.messageResource.get({
1816
+ params: { type: "image" },
1817
+ path: {
1818
+ message_id: messageId,
1819
+ file_key: fileKey
1820
+ }
1821
+ });
1822
+ const chunks = [];
1823
+ await new Promise((resolve, reject) => {
1824
+ const stream = res.getReadableStream();
1825
+ stream.on("data", (chunk) => {
1826
+ chunks.push(chunk);
1827
+ });
1828
+ stream.on("end", () => resolve());
1829
+ stream.on("error", (error) => reject(error instanceof Error ? error : new Error(String(error))));
1830
+ });
1831
+ const data = new Uint8Array(Buffer.concat(chunks));
1832
+ const mediaType = sniffImageMediaType(data, res.headers?.["content-type"]);
1833
+ if (mediaType === void 0) throw new Error(`图片资源格式不受支持(file_key=${fileKey},content-type=${String(res.headers?.["content-type"] ?? "未知")})`);
1834
+ return {
1835
+ data,
1836
+ mediaType
1837
+ };
1791
1838
  }
1792
1839
  };
1793
1840
  }
@@ -1808,9 +1855,39 @@ function stripMentionPlaceholders(text) {
1808
1855
  //#endregion
1809
1856
  //#region src/channels/feishu/parse.ts
1810
1857
  /** im.message.receive_v1 事件解析:窄化为渠道无关的 ParsedMessage;message_id 去重。 */
1858
+ /** 从 post content(段落数组)抽取文本与图片 key;段落间以换行连接。 */
1859
+ function parsePostContent(content) {
1860
+ const imageKeys = [];
1861
+ const paragraphs = [];
1862
+ try {
1863
+ const parsed = JSON.parse(content);
1864
+ const list = Array.isArray(parsed.content) ? parsed.content : [];
1865
+ for (const paragraph of list) {
1866
+ if (!Array.isArray(paragraph)) continue;
1867
+ const parts = [];
1868
+ for (const node of paragraph) {
1869
+ if (node === null || typeof node !== "object") continue;
1870
+ if (node.tag === "img") {
1871
+ if (typeof node.image_key === "string" && node.image_key.length > 0) imageKeys.push(node.image_key);
1872
+ } else if ((node.tag === "text" || node.tag === "a") && typeof node.text === "string") parts.push(node.text);
1873
+ }
1874
+ paragraphs.push(parts.join(""));
1875
+ }
1876
+ } catch {
1877
+ return {
1878
+ text: "",
1879
+ imageKeys: []
1880
+ };
1881
+ }
1882
+ return {
1883
+ text: paragraphs.join("\n"),
1884
+ imageKeys
1885
+ };
1886
+ }
1811
1887
  /**
1812
1888
  * SDK handler 收到的 data 即事件体(README 示例 `data.message` 直接解构);
1813
- * 兼容包一层 { event } 的形态。过滤:机器人消息、非文本、群内未 @机器人、空文本。
1889
+ * 兼容包一层 { event } 的形态。过滤:机器人消息、非 text/post/image 类型、
1890
+ * 群内未 @机器人、无文本且无图片。
1814
1891
  */
1815
1892
  function parseMessageEvent(data) {
1816
1893
  const wrapped = data;
@@ -1819,25 +1896,38 @@ function parseMessageEvent(data) {
1819
1896
  const userId = event.sender.sender_id?.open_id;
1820
1897
  const msg = event.message;
1821
1898
  if (typeof userId !== "string" || msg === void 0) return null;
1822
- if (msg.message_type !== "text" || typeof msg.content !== "string") return null;
1899
+ if (msg.message_type !== "text" && msg.message_type !== "post" && msg.message_type !== "image") return null;
1900
+ if (typeof msg.content !== "string") return null;
1823
1901
  if (typeof msg.message_id !== "string" || typeof msg.chat_id !== "string") return null;
1824
1902
  if (msg.chat_type !== "p2p" && msg.chat_type !== "group") return null;
1825
1903
  if (msg.chat_type === "group" && !(msg.mentions ?? []).some((m) => m.mentioned_type === "bot")) return null;
1826
- let text;
1827
- try {
1904
+ let text = "";
1905
+ let imageKeys = [];
1906
+ if (msg.message_type === "text") try {
1828
1907
  const parsed = JSON.parse(msg.content);
1829
1908
  if (typeof parsed.text !== "string") return null;
1830
1909
  text = stripMentionPlaceholders(parsed.text);
1831
1910
  } catch {
1832
1911
  return null;
1833
1912
  }
1834
- if (text.length === 0) return null;
1913
+ else if (msg.message_type === "post") {
1914
+ const post = parsePostContent(msg.content);
1915
+ text = stripMentionPlaceholders(post.text);
1916
+ imageKeys = post.imageKeys;
1917
+ } else try {
1918
+ const parsed = JSON.parse(msg.content);
1919
+ if (typeof parsed.image_key === "string" && parsed.image_key.length > 0) imageKeys.push(parsed.image_key);
1920
+ } catch {
1921
+ return null;
1922
+ }
1923
+ if (text.length === 0 && imageKeys.length === 0) return null;
1835
1924
  return {
1836
1925
  messageId: msg.message_id,
1837
1926
  chatId: msg.chat_id,
1838
1927
  chatType: msg.chat_type,
1839
1928
  userId,
1840
- text
1929
+ text,
1930
+ imageKeys
1841
1931
  };
1842
1932
  }
1843
1933
  /** message_id 去重(飞书会重推);FIFO 容量淘汰。 */
@@ -2270,12 +2360,14 @@ const feishuChannel = {
2270
2360
  const parsed = parseMessageEvent(data);
2271
2361
  if (parsed === null || !dedup.check(parsed.messageId)) return;
2272
2362
  const reply = new FeishuReplyHandle(api, parsed.chatId, tunables, log);
2363
+ const loadImages = parsed.imageKeys.length > 0 ? async () => Promise.all(parsed.imageKeys.map(async (key) => api.downloadImage(parsed.messageId, key))) : void 0;
2273
2364
  io.onMessage({
2274
2365
  botId: bot.record.id,
2275
2366
  chatId: parsed.chatId,
2276
2367
  userId: parsed.userId,
2277
2368
  messageId: parsed.messageId,
2278
2369
  text: parsed.text,
2370
+ ...loadImages !== void 0 ? { loadImages } : {},
2279
2371
  reply,
2280
2372
  ackProcessing: makeAck(api, parsed.messageId, tunables.processingReactionEmoji)
2281
2373
  });
@@ -2461,7 +2553,7 @@ var Outbound = class {
2461
2553
  };
2462
2554
  //#endregion
2463
2555
  //#region src/channels/inbound.ts
2464
- /** 入站:指令分流 → 路由 → 单 in-flight 准入 → 表情回复 → followup 投递。 */
2556
+ /** 入站:指令分流 → 路由 → 单 in-flight 准入 → 表情回复 → 图片落附件库 → followup 投递。 */
2465
2557
  var Inbound = class {
2466
2558
  deps;
2467
2559
  constructor(deps) {
@@ -2503,11 +2595,23 @@ var Inbound = class {
2503
2595
  }
2504
2596
  rt.inflight = { ack: void 0 };
2505
2597
  rt.inflight.ack = await msg.ackProcessing().catch(() => void 0) ?? void 0;
2598
+ const imageRefs = [];
2599
+ if (msg.loadImages !== void 0) {
2600
+ const images = await msg.loadImages();
2601
+ if (images.length > 0) {
2602
+ const attachments = this.deps.attachments?.();
2603
+ if (attachments === void 0) await msg.reply.notice("当前环境暂不支持图片消息(附件服务不可用),已按文字部分处理");
2604
+ else imageRefs.push(...await attachments.saveImages(images));
2605
+ }
2606
+ }
2506
2607
  const message = createUserMessage({
2507
- content: [{
2608
+ content: [...msg.text.length > 0 ? [{
2508
2609
  type: "text",
2509
2610
  text: msg.text
2510
- }],
2611
+ }] : [], ...imageRefs.map((ref) => ({
2612
+ type: "image",
2613
+ attachment: ref
2614
+ }))],
2511
2615
  source: { kind: "user" }
2512
2616
  });
2513
2617
  try {
@@ -2662,6 +2766,7 @@ var BotRuntime = class {
2662
2766
  router: this.router,
2663
2767
  bots: deps.bots,
2664
2768
  maxErrorDetailChars: deps.maxErrorDetailChars,
2769
+ ...deps.attachments !== void 0 ? { attachments: deps.attachments } : {},
2665
2770
  onError: (m) => deps.log.warn(m)
2666
2771
  });
2667
2772
  this.outbound = new Outbound(this.sessions, (m) => deps.log.warn(m), deps.maxErrorDetailChars);
@@ -3169,6 +3274,18 @@ function setupBots(ctx, config, deps) {
3169
3274
  if (workspaceRegistry === void 0) throw new Error("workspaceRegistry 服务不可用");
3170
3275
  await (await workspaceRegistry.create(cwd)).attachSession(SessionId(sessionId));
3171
3276
  } };
3277
+ const attachmentsOf = () => {
3278
+ if (ctx.get("attachments", false) === void 0) return void 0;
3279
+ return { async saveImages(inputs) {
3280
+ const current = ctx.get("attachments", false);
3281
+ if (current === void 0) throw new Error("attachments 服务不可用");
3282
+ return current.saveImages(inputs.map(({ data, mediaType, name }) => ({
3283
+ data,
3284
+ mediaType,
3285
+ ...name !== void 0 ? { name } : {}
3286
+ })));
3287
+ } };
3288
+ };
3172
3289
  let botsTable;
3173
3290
  let bindingsTable;
3174
3291
  let runtime;
@@ -3203,6 +3320,7 @@ function setupBots(ctx, config, deps) {
3203
3320
  channels,
3204
3321
  tunables,
3205
3322
  maxErrorDetailChars: config.errorDetailMaxChars,
3323
+ attachments: attachmentsOf,
3206
3324
  resolveSecret: async (ref) => (await ctx.credentials.resolve(credentialRef(ref)))?.value,
3207
3325
  validateProject: (path) => existsSync(path),
3208
3326
  log