@distri/core 0.3.2 → 0.3.4

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.mjs CHANGED
@@ -6,8 +6,14 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
6
6
  function isArrayParts(result) {
7
7
  return Array.isArray(result) && result[0].part_type;
8
8
  }
9
- function createSuccessfulToolResult(toolCallId, toolName, result) {
10
- const parts = isArrayParts(result) ? result : [{
9
+ function createSuccessfulToolResult(toolCallId, toolName, result, explicitPartsMetadata) {
10
+ console.log("[createSuccessfulToolResult] toolName:", toolName);
11
+ console.log("[createSuccessfulToolResult] isArrayParts:", isArrayParts(result));
12
+ console.log("[createSuccessfulToolResult] result type:", typeof result, Array.isArray(result) ? `array[${result.length}]` : "");
13
+ if (isArrayParts(result)) {
14
+ console.log("[createSuccessfulToolResult] parts:", result.map((p) => ({ part_type: p.part_type, hasMetadata: !!p.__metadata })));
15
+ }
16
+ const rawParts = isArrayParts(result) ? result : [{
11
17
  part_type: "data",
12
18
  data: {
13
19
  result,
@@ -15,10 +21,22 @@ function createSuccessfulToolResult(toolCallId, toolName, result) {
15
21
  error: void 0
16
22
  }
17
23
  }];
24
+ const parts_metadata = { ...explicitPartsMetadata };
25
+ const parts = rawParts.map((part, index) => {
26
+ if ("__metadata" in part && part.__metadata) {
27
+ parts_metadata[index] = { ...parts_metadata[index], ...part.__metadata };
28
+ }
29
+ if (part.part_type === "image" && !parts_metadata[index]) {
30
+ parts_metadata[index] = { save: false };
31
+ }
32
+ const { __metadata, ...cleanPart } = part;
33
+ return cleanPart;
34
+ });
18
35
  return {
19
36
  tool_call_id: toolCallId,
20
37
  tool_name: toolName,
21
- parts
38
+ parts,
39
+ parts_metadata: Object.keys(parts_metadata).length > 0 ? parts_metadata : void 0
22
40
  };
23
41
  }
24
42
  function createFailedToolResult(toolCallId, toolName, error, result) {
@@ -494,11 +512,22 @@ var A2AClient = class {
494
512
  // src/encoder.ts
495
513
  function convertA2AMessageToDistri(a2aMessage) {
496
514
  const role = a2aMessage.role === "agent" ? "assistant" : "user";
515
+ let agent_id;
516
+ let agent_name;
517
+ if (a2aMessage.metadata) {
518
+ const metadata = a2aMessage.metadata;
519
+ if (metadata.agent) {
520
+ agent_id = metadata.agent.agent_id;
521
+ agent_name = metadata.agent.agent_name;
522
+ }
523
+ }
497
524
  return {
498
525
  id: a2aMessage.messageId,
499
526
  role,
500
527
  parts: a2aMessage.parts.map(convertA2APartToDistri),
501
- created_at: a2aMessage.createdAt
528
+ created_at: a2aMessage.createdAt,
529
+ agent_id,
530
+ agent_name
502
531
  };
503
532
  }
504
533
  function convertA2AStatusUpdateToDistri(statusUpdate) {
@@ -521,8 +550,8 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
521
550
  const runErrorResult = {
522
551
  type: "run_error",
523
552
  data: {
524
- message: statusUpdate.error,
525
- code: statusUpdate.code
553
+ message: metadata.message || statusUpdate.status?.message || "Unknown error",
554
+ code: metadata.code
526
555
  }
527
556
  };
528
557
  return runErrorResult;
@@ -692,6 +721,19 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
692
721
  };
693
722
  return browserSessionStarted;
694
723
  }
724
+ case "todos_updated": {
725
+ const todos = parseTodosFromFormatted(metadata.formatted_todos || "");
726
+ const todosUpdated = {
727
+ type: "todos_updated",
728
+ data: {
729
+ formatted_todos: metadata.formatted_todos || "",
730
+ action: metadata.action || "write_todos",
731
+ todo_count: metadata.todo_count || 0,
732
+ todos
733
+ }
734
+ };
735
+ return todosUpdated;
736
+ }
695
737
  default: {
696
738
  console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
697
739
  const defaultResult = {
@@ -743,7 +785,7 @@ function convertA2APartToDistri(a2aPart) {
743
785
  const fileUrl = { type: "url", mime_type: a2aPart.file.mimeType || "application/octet-stream", url: a2aPart.file.uri || "" };
744
786
  return { part_type: "image", data: fileUrl };
745
787
  } else {
746
- const fileBytes = { type: "bytes", mime_type: a2aPart.file.mimeType || "application/octet-stream", data: a2aPart.file.bytes || "" };
788
+ const fileBytes = { type: "bytes", mime_type: a2aPart.file.mimeType || "application/octet-stream", bytes: a2aPart.file.bytes || "" };
747
789
  return { part_type: "image", data: fileBytes };
748
790
  }
749
791
  case "data":
@@ -770,6 +812,7 @@ function convertDistriMessageToA2A(distriMessage, context) {
770
812
  break;
771
813
  case "system":
772
814
  case "tool":
815
+ case "developer":
773
816
  role = "user";
774
817
  break;
775
818
  default:
@@ -781,7 +824,8 @@ function convertDistriMessageToA2A(distriMessage, context) {
781
824
  parts: distriMessage.parts.map(convertDistriPartToA2A),
782
825
  kind: "message",
783
826
  contextId: context.thread_id,
784
- taskId: context.task_id || context.run_id || void 0
827
+ taskId: context.task_id || context.run_id || void 0,
828
+ metadata: distriMessage.metadata
785
829
  };
786
830
  }
787
831
  function convertDistriPartToA2A(distriPart) {
@@ -795,7 +839,7 @@ function convertDistriPartToA2A(distriPart) {
795
839
  const fileUri = { mimeType: distriPart.data.mime_type, uri: distriPart.data.url };
796
840
  result = { kind: "file", file: fileUri };
797
841
  } else {
798
- const fileBytes = { mimeType: distriPart.data.mime_type, bytes: distriPart.data.data };
842
+ const fileBytes = { mimeType: distriPart.data.mime_type, bytes: distriPart.data.bytes };
799
843
  result = { kind: "file", file: fileBytes };
800
844
  }
801
845
  break;
@@ -860,12 +904,41 @@ function extractToolCallsFromDistriMessage(message) {
860
904
  function extractToolResultsFromDistriMessage(message) {
861
905
  return message.parts.filter((part) => part.part_type === "tool_result").map((part) => part.data);
862
906
  }
907
+ function parseTodosFromFormatted(formatted) {
908
+ if (!formatted || formatted === "\u25A1 No todos") {
909
+ return [];
910
+ }
911
+ const lines = formatted.split("\n").filter((line) => line.trim());
912
+ return lines.map((line, index) => {
913
+ const trimmed = line.trim();
914
+ let status = "open";
915
+ let content = trimmed;
916
+ if (trimmed.startsWith("\u25A0")) {
917
+ status = "done";
918
+ content = trimmed.slice(1).trim();
919
+ } else if (trimmed.startsWith("\u25D0")) {
920
+ status = "in_progress";
921
+ content = trimmed.slice(1).trim();
922
+ } else if (trimmed.startsWith("\u25A1")) {
923
+ status = "open";
924
+ content = trimmed.slice(1).trim();
925
+ }
926
+ return {
927
+ id: `todo_${index}`,
928
+ content,
929
+ status
930
+ };
931
+ });
932
+ }
863
933
 
864
934
  // src/distri-client.ts
865
935
  var _DistriClient = class _DistriClient {
866
936
  constructor(config) {
867
937
  this.agentClients = /* @__PURE__ */ new Map();
868
938
  const headers = { ...config.headers };
939
+ if (config.workspaceId) {
940
+ headers["X-Workspace-Id"] = config.workspaceId;
941
+ }
869
942
  this.accessToken = config.accessToken;
870
943
  this.refreshToken = config.refreshToken;
871
944
  this.tokenRefreshSkewMs = config.tokenRefreshSkewMs ?? 6e4;
@@ -880,7 +953,8 @@ var _DistriClient = class _DistriClient {
880
953
  headers,
881
954
  interceptor: config.interceptor ?? (async (init) => Promise.resolve(init)),
882
955
  onTokenRefresh: config.onTokenRefresh,
883
- clientId: config.clientId
956
+ clientId: config.clientId,
957
+ workspaceId: config.workspaceId
884
958
  };
885
959
  }
886
960
  /**
@@ -895,6 +969,24 @@ var _DistriClient = class _DistriClient {
895
969
  set clientId(value) {
896
970
  this.config.clientId = value;
897
971
  }
972
+ /**
973
+ * Get the configured workspace ID.
974
+ */
975
+ get workspaceId() {
976
+ return this.config.workspaceId;
977
+ }
978
+ /**
979
+ * Set the workspace ID for multi-tenant support.
980
+ * Updates the X-Workspace-Id header for all subsequent requests.
981
+ */
982
+ set workspaceId(value) {
983
+ this.config.workspaceId = value;
984
+ if (value) {
985
+ this.config.headers["X-Workspace-Id"] = value;
986
+ } else {
987
+ delete this.config.headers["X-Workspace-Id"];
988
+ }
989
+ }
898
990
  /**
899
991
  * Create a client with default cloud configuration.
900
992
  *
@@ -1347,8 +1439,51 @@ var _DistriClient = class _DistriClient {
1347
1439
  yield* await client.sendMessageStream(params);
1348
1440
  } catch (error) {
1349
1441
  console.error(error);
1350
- throw new DistriError(`Failed to stream message to agent ${agentId}`, "STREAM_MESSAGE_ERROR", error);
1442
+ const errorMessage = this.extractErrorMessage(error);
1443
+ throw new DistriError(errorMessage, "STREAM_MESSAGE_ERROR", error);
1444
+ }
1445
+ }
1446
+ /**
1447
+ * Extract a user-friendly error message from potentially nested errors
1448
+ */
1449
+ extractErrorMessage(error) {
1450
+ if (!error) return "Unknown error occurred";
1451
+ if (typeof error === "object" && error !== null) {
1452
+ const err = error;
1453
+ if (err.error && typeof err.error === "object") {
1454
+ const jsonRpcError = err.error;
1455
+ if (typeof jsonRpcError.message === "string") {
1456
+ return jsonRpcError.message;
1457
+ }
1458
+ }
1459
+ if (err.message && typeof err.message === "string") {
1460
+ return err.message;
1461
+ }
1462
+ if (err.details && typeof err.details === "object") {
1463
+ const details = err.details;
1464
+ if (details.message && typeof details.message === "string") {
1465
+ return details.message;
1466
+ }
1467
+ if (details.error && typeof details.error === "object") {
1468
+ const nestedError = details.error;
1469
+ if (typeof nestedError.message === "string") {
1470
+ return nestedError.message;
1471
+ }
1472
+ }
1473
+ }
1474
+ if (err.cause && typeof err.cause === "object") {
1475
+ return this.extractErrorMessage(err.cause);
1476
+ }
1351
1477
  }
1478
+ if (error instanceof Error) {
1479
+ const msg = error.message;
1480
+ const sseMatch = msg.match(/SSE event contained an error:\s*(.+?)\s*\(Code:/);
1481
+ if (sseMatch) return sseMatch[1];
1482
+ const rpcMatch = msg.match(/RPC Error:\s*(.+?)\s*\(Code:/);
1483
+ if (rpcMatch) return rpcMatch[1];
1484
+ return msg;
1485
+ }
1486
+ return String(error);
1352
1487
  }
1353
1488
  /**
1354
1489
  * Get task details
@@ -1410,11 +1545,19 @@ var _DistriClient = class _DistriClient {
1410
1545
  }
1411
1546
  }
1412
1547
  /**
1413
- * Get agents sorted by thread count (most active first)
1548
+ * Get agents sorted by thread count (most active first).
1549
+ * Includes all registered agents, even those with 0 threads.
1550
+ * Optionally filter by name with search parameter.
1414
1551
  */
1415
- async getAgentsByUsage() {
1552
+ async getAgentsByUsage(options) {
1416
1553
  try {
1417
- const response = await this.fetch("/threads/agents");
1554
+ const params = new URLSearchParams();
1555
+ if (options?.search) {
1556
+ params.set("search", options.search);
1557
+ }
1558
+ const query = params.toString();
1559
+ const url = query ? `/threads/agents?${query}` : "/threads/agents";
1560
+ const response = await this.fetch(url);
1418
1561
  if (!response.ok) {
1419
1562
  throw new ApiError(`Failed to fetch agents by usage: ${response.statusText}`, response.status);
1420
1563
  }
@@ -1479,6 +1622,138 @@ var _DistriClient = class _DistriClient {
1479
1622
  const messages = await this.getThreadMessages(threadId);
1480
1623
  return messages.map(convertA2AMessageToDistri);
1481
1624
  }
1625
+ // ========== Message Read Status Methods ==========
1626
+ /**
1627
+ * Mark a message as read
1628
+ */
1629
+ async markMessageRead(threadId, messageId) {
1630
+ try {
1631
+ const response = await this.fetch(
1632
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/read`,
1633
+ { method: "POST" }
1634
+ );
1635
+ if (!response.ok) {
1636
+ throw new ApiError(`Failed to mark message as read: ${response.statusText}`, response.status);
1637
+ }
1638
+ return await response.json();
1639
+ } catch (error) {
1640
+ if (error instanceof ApiError) throw error;
1641
+ throw new DistriError(`Failed to mark message ${messageId} as read`, "MARK_READ_ERROR", error);
1642
+ }
1643
+ }
1644
+ /**
1645
+ * Get read status for a specific message
1646
+ */
1647
+ async getMessageReadStatus(threadId, messageId) {
1648
+ try {
1649
+ const response = await this.fetch(
1650
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/read`
1651
+ );
1652
+ if (response.status === 404) {
1653
+ return null;
1654
+ }
1655
+ if (!response.ok) {
1656
+ throw new ApiError(`Failed to get message read status: ${response.statusText}`, response.status);
1657
+ }
1658
+ return await response.json();
1659
+ } catch (error) {
1660
+ if (error instanceof ApiError) throw error;
1661
+ throw new DistriError(`Failed to get read status for message ${messageId}`, "FETCH_ERROR", error);
1662
+ }
1663
+ }
1664
+ /**
1665
+ * Get read status for all messages in a thread
1666
+ */
1667
+ async getThreadReadStatus(threadId) {
1668
+ try {
1669
+ const response = await this.fetch(
1670
+ `/threads/${encodeURIComponent(threadId)}/read-status`
1671
+ );
1672
+ if (!response.ok) {
1673
+ throw new ApiError(`Failed to get thread read status: ${response.statusText}`, response.status);
1674
+ }
1675
+ return await response.json();
1676
+ } catch (error) {
1677
+ if (error instanceof ApiError) throw error;
1678
+ throw new DistriError(`Failed to get read status for thread ${threadId}`, "FETCH_ERROR", error);
1679
+ }
1680
+ }
1681
+ // ========== Message Voting Methods ==========
1682
+ /**
1683
+ * Vote on a message (upvote or downvote)
1684
+ * Downvotes require a comment explaining the issue
1685
+ */
1686
+ async voteMessage(threadId, messageId, request) {
1687
+ try {
1688
+ const response = await this.fetch(
1689
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/vote`,
1690
+ {
1691
+ method: "POST",
1692
+ headers: { "Content-Type": "application/json" },
1693
+ body: JSON.stringify(request)
1694
+ }
1695
+ );
1696
+ if (!response.ok) {
1697
+ const errorData = await response.json().catch(() => ({}));
1698
+ throw new ApiError(errorData.error || `Failed to vote on message: ${response.statusText}`, response.status);
1699
+ }
1700
+ return await response.json();
1701
+ } catch (error) {
1702
+ if (error instanceof ApiError) throw error;
1703
+ throw new DistriError(`Failed to vote on message ${messageId}`, "VOTE_ERROR", error);
1704
+ }
1705
+ }
1706
+ /**
1707
+ * Remove vote from a message
1708
+ */
1709
+ async removeVote(threadId, messageId) {
1710
+ try {
1711
+ const response = await this.fetch(
1712
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/vote`,
1713
+ { method: "DELETE" }
1714
+ );
1715
+ if (!response.ok && response.status !== 204) {
1716
+ throw new ApiError(`Failed to remove vote: ${response.statusText}`, response.status);
1717
+ }
1718
+ } catch (error) {
1719
+ if (error instanceof ApiError) throw error;
1720
+ throw new DistriError(`Failed to remove vote from message ${messageId}`, "VOTE_ERROR", error);
1721
+ }
1722
+ }
1723
+ /**
1724
+ * Get vote summary for a message (counts + current user's vote)
1725
+ */
1726
+ async getMessageVoteSummary(threadId, messageId) {
1727
+ try {
1728
+ const response = await this.fetch(
1729
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/vote`
1730
+ );
1731
+ if (!response.ok) {
1732
+ throw new ApiError(`Failed to get vote summary: ${response.statusText}`, response.status);
1733
+ }
1734
+ return await response.json();
1735
+ } catch (error) {
1736
+ if (error instanceof ApiError) throw error;
1737
+ throw new DistriError(`Failed to get vote summary for message ${messageId}`, "FETCH_ERROR", error);
1738
+ }
1739
+ }
1740
+ /**
1741
+ * Get all votes for a message (admin/analytics use)
1742
+ */
1743
+ async getMessageVotes(threadId, messageId) {
1744
+ try {
1745
+ const response = await this.fetch(
1746
+ `/threads/${encodeURIComponent(threadId)}/messages/${encodeURIComponent(messageId)}/votes`
1747
+ );
1748
+ if (!response.ok) {
1749
+ throw new ApiError(`Failed to get message votes: ${response.statusText}`, response.status);
1750
+ }
1751
+ return await response.json();
1752
+ } catch (error) {
1753
+ if (error instanceof ApiError) throw error;
1754
+ throw new DistriError(`Failed to get votes for message ${messageId}`, "FETCH_ERROR", error);
1755
+ }
1756
+ }
1482
1757
  /**
1483
1758
  * Send a DistriMessage to a thread
1484
1759
  */
@@ -1504,7 +1779,12 @@ var _DistriClient = class _DistriClient {
1504
1779
  },
1505
1780
  body: JSON.stringify({
1506
1781
  tool_call_id: result.tool_call_id,
1507
- tool_response: result
1782
+ tool_response: {
1783
+ tool_call_id: result.tool_call_id,
1784
+ tool_name: result.tool_name,
1785
+ parts: result.parts,
1786
+ parts_metadata: result.parts_metadata
1787
+ }
1508
1788
  })
1509
1789
  });
1510
1790
  if (!response.ok) {
@@ -1768,9 +2048,17 @@ var _DistriClient = class _DistriClient {
1768
2048
  };
1769
2049
  }
1770
2050
  /**
1771
- * Helper method to create message send parameters
1772
- */
1773
- static initMessageParams(message, configuration, metadata) {
2051
+ * Helper method to create message send parameters.
2052
+ *
2053
+ * Pass `dynamicMetadata` to inject `dynamic_sections` and/or `dynamic_values`
2054
+ * into the metadata so the server can apply them to prompt templates.
2055
+ */
2056
+ static initMessageParams(message, configuration, metadata, dynamicMetadata) {
2057
+ const mergedMetadata = {
2058
+ ...metadata,
2059
+ ...dynamicMetadata?.dynamic_sections ? { dynamic_sections: dynamicMetadata.dynamic_sections } : {},
2060
+ ...dynamicMetadata?.dynamic_values ? { dynamic_values: dynamicMetadata.dynamic_values } : {}
2061
+ };
1774
2062
  return {
1775
2063
  message,
1776
2064
  configuration: {
@@ -1779,18 +2067,26 @@ var _DistriClient = class _DistriClient {
1779
2067
  // Default to non-blocking for streaming
1780
2068
  ...configuration
1781
2069
  },
1782
- metadata
2070
+ metadata: Object.keys(mergedMetadata).length > 0 ? mergedMetadata : metadata
1783
2071
  };
1784
2072
  }
1785
2073
  /**
1786
- * Create MessageSendParams from a DistriMessage using InvokeContext
2074
+ * Create MessageSendParams from a DistriMessage using InvokeContext.
2075
+ *
2076
+ * Pass `dynamicMetadata` to inject `dynamic_sections` and/or `dynamic_values`
2077
+ * into the metadata so the server can apply them to prompt templates.
1787
2078
  */
1788
- static initDistriMessageParams(message, context) {
2079
+ static initDistriMessageParams(message, context, dynamicMetadata) {
1789
2080
  const a2aMessage = convertDistriMessageToA2A(message, context);
1790
2081
  const contextMetadata = context.getMetadata?.() || {};
2082
+ const mergedMetadata = {
2083
+ ...contextMetadata,
2084
+ ...dynamicMetadata?.dynamic_sections ? { dynamic_sections: dynamicMetadata.dynamic_sections } : {},
2085
+ ...dynamicMetadata?.dynamic_values ? { dynamic_values: dynamicMetadata.dynamic_values } : {}
2086
+ };
1791
2087
  return {
1792
2088
  message: a2aMessage,
1793
- metadata: contextMetadata
2089
+ metadata: Object.keys(mergedMetadata).length > 0 ? mergedMetadata : contextMetadata
1794
2090
  };
1795
2091
  }
1796
2092
  };
@@ -1896,28 +2192,40 @@ var Agent = class _Agent {
1896
2192
  const enhancedParams = this.enhanceParamsWithTools(params, tools);
1897
2193
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
1898
2194
  const self = this;
1899
- return async function* () {
1900
- for await (const event of a2aStream) {
1901
- const converted = decodeA2AStreamEvent(event);
1902
- if (converted && converted.type === "inline_hook_requested") {
1903
- const hookReq = converted.data;
1904
- const handler = self.hookHandlers.get(hookReq.hook) || self.defaultHookHandler;
1905
- if (handler) {
1906
- try {
1907
- const mutation = await handler(hookReq);
1908
- await self.client.completeInlineHook(hookReq.hook_id, mutation);
1909
- } catch (err) {
2195
+ return (async function* () {
2196
+ try {
2197
+ for await (const event of a2aStream) {
2198
+ const converted = decodeA2AStreamEvent(event);
2199
+ if (converted && converted.type === "inline_hook_requested") {
2200
+ const hookReq = converted.data;
2201
+ const handler = self.hookHandlers.get(hookReq.hook) || self.defaultHookHandler;
2202
+ if (handler) {
2203
+ try {
2204
+ const mutation = await handler(hookReq);
2205
+ await self.client.completeInlineHook(hookReq.hook_id, mutation);
2206
+ } catch (err) {
2207
+ await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
2208
+ }
2209
+ } else {
1910
2210
  await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
1911
2211
  }
1912
- } else {
1913
- await self.client.completeInlineHook(hookReq.hook_id, { dynamic_values: {} });
2212
+ yield converted;
2213
+ } else if (converted) {
2214
+ yield converted;
1914
2215
  }
1915
- yield converted;
1916
- } else if (converted) {
1917
- yield converted;
1918
2216
  }
2217
+ } catch (error) {
2218
+ const message = error instanceof Error ? error.message : String(error);
2219
+ const runError = {
2220
+ type: "run_error",
2221
+ data: {
2222
+ message,
2223
+ code: "STREAM_ERROR"
2224
+ }
2225
+ };
2226
+ yield runError;
1919
2227
  }
1920
- }();
2228
+ })();
1921
2229
  }
1922
2230
  /**
1923
2231
  * Validate that required external tools are registered before invoking.
@@ -1945,12 +2253,16 @@ var Agent = class _Agent {
1945
2253
  };
1946
2254
  }
1947
2255
  /**
1948
- * Enhance message params with tool definitions
2256
+ * Enhance message params with tool definitions and dynamic metadata.
2257
+ *
2258
+ * When `dynamic_sections` or `dynamic_values` are present in `params.metadata`,
2259
+ * they are forwarded so the server injects them into the prompt template.
1949
2260
  */
1950
2261
  enhanceParamsWithTools(params, tools) {
1951
2262
  this.assertExternalTools(tools);
2263
+ const existingMeta = params.metadata ?? {};
1952
2264
  const metadata = {
1953
- ...params.metadata,
2265
+ ...existingMeta,
1954
2266
  external_tools: tools?.map((tool) => ({
1955
2267
  name: tool.name,
1956
2268
  description: tool.description,