@distri/core 0.3.1 → 0.3.2

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.js CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
30
30
  DistriClient: () => DistriClient,
31
31
  DistriError: () => DistriError,
32
+ ExternalToolValidationError: () => ExternalToolValidationError,
32
33
  convertA2AMessageToDistri: () => convertA2AMessageToDistri,
33
34
  convertA2APartToDistri: () => convertA2APartToDistri,
34
35
  convertA2AStatusUpdateToDistri: () => convertA2AStatusUpdateToDistri,
@@ -729,6 +730,17 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
729
730
  };
730
731
  return hookRequested;
731
732
  }
733
+ case "browser_session_started": {
734
+ const browserSessionStarted = {
735
+ type: "browser_session_started",
736
+ data: {
737
+ session_id: metadata.session_id || "",
738
+ viewer_url: metadata.viewer_url,
739
+ stream_url: metadata.stream_url
740
+ }
741
+ };
742
+ return browserSessionStarted;
743
+ }
732
744
  default: {
733
745
  console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
734
746
  const defaultResult = {
@@ -908,21 +920,29 @@ var _DistriClient = class _DistriClient {
908
920
  this.tokenRefreshSkewMs = config.tokenRefreshSkewMs ?? 6e4;
909
921
  this.onTokenRefresh = config.onTokenRefresh;
910
922
  this.config = {
911
- baseUrl: config.baseUrl.replace(/\/$/, ""),
923
+ baseUrl: config.baseUrl?.replace(/\/$/, "") || DEFAULT_BASE_URL,
912
924
  apiVersion: config.apiVersion || "v1",
913
- timeout: config.timeout || 3e4,
914
- retryAttempts: config.retryAttempts || 3,
915
- retryDelay: config.retryDelay || 1e3,
916
- debug: config.debug || false,
925
+ timeout: config.timeout ?? 3e4,
926
+ retryAttempts: config.retryAttempts ?? 3,
927
+ retryDelay: config.retryDelay ?? 1e3,
928
+ debug: config.debug ?? false,
917
929
  headers,
918
- interceptor: config.interceptor || ((init) => Promise.resolve(init))
930
+ interceptor: config.interceptor ?? (async (init) => Promise.resolve(init)),
931
+ onTokenRefresh: config.onTokenRefresh,
932
+ clientId: config.clientId
919
933
  };
920
- this.debug("DistriClient initialized with config:", {
921
- baseUrl: this.config.baseUrl,
922
- hasAccessToken: !!this.accessToken,
923
- hasRefreshToken: !!this.refreshToken,
924
- timeout: this.config.timeout
925
- });
934
+ }
935
+ /**
936
+ * Get the configured client ID.
937
+ */
938
+ get clientId() {
939
+ return this.config.clientId;
940
+ }
941
+ /**
942
+ * Set the client ID for embed token issuance.
943
+ */
944
+ set clientId(value) {
945
+ this.config.clientId = value;
926
946
  }
927
947
  /**
928
948
  * Create a client with default cloud configuration.
@@ -955,7 +975,7 @@ var _DistriClient = class _DistriClient {
955
975
  if (expiry) {
956
976
  body.expiry = typeof expiry === "string" ? expiry : expiry.toISOString();
957
977
  }
958
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
978
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values`, {
959
979
  method: "POST",
960
980
  headers: {
961
981
  "Content-Type": "application/json",
@@ -972,7 +992,7 @@ var _DistriClient = class _DistriClient {
972
992
  * Session store: get a single value
973
993
  */
974
994
  async getSessionValue(sessionId, key) {
975
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
995
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
976
996
  method: "GET",
977
997
  headers: {
978
998
  ...this.config.headers
@@ -989,7 +1009,7 @@ var _DistriClient = class _DistriClient {
989
1009
  * Session store: get all values in a session
990
1010
  */
991
1011
  async getSessionValues(sessionId) {
992
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
1012
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values`, {
993
1013
  method: "GET",
994
1014
  headers: {
995
1015
  ...this.config.headers
@@ -1006,7 +1026,7 @@ var _DistriClient = class _DistriClient {
1006
1026
  * Session store: delete a single key
1007
1027
  */
1008
1028
  async deleteSessionValue(sessionId, key) {
1009
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
1029
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
1010
1030
  method: "DELETE",
1011
1031
  headers: {
1012
1032
  ...this.config.headers
@@ -1021,7 +1041,7 @@ var _DistriClient = class _DistriClient {
1021
1041
  * Session store: clear all keys in a session
1022
1042
  */
1023
1043
  async clearSession(sessionId) {
1024
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}`, {
1044
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}`, {
1025
1045
  method: "DELETE",
1026
1046
  headers: {
1027
1047
  ...this.config.headers
@@ -1032,30 +1052,6 @@ var _DistriClient = class _DistriClient {
1032
1052
  throw new ApiError(errorData.error || "Failed to clear session", resp.status);
1033
1053
  }
1034
1054
  }
1035
- /**
1036
- * Set additional user message parts for the next agent iteration.
1037
- * These parts will be appended to the user message in the prompt.
1038
- * @param sessionId - The thread/session ID
1039
- * @param parts - Array of DistriPart objects to append to user message
1040
- */
1041
- async setAdditionalUserParts(sessionId, parts) {
1042
- await this.setSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY, parts);
1043
- }
1044
- /**
1045
- * Get the current additional user message parts.
1046
- * @param sessionId - The thread/session ID
1047
- * @returns Array of DistriPart objects or null if not set
1048
- */
1049
- async getAdditionalUserParts(sessionId) {
1050
- return this.getSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1051
- }
1052
- /**
1053
- * Clear/delete the additional user message parts.
1054
- * @param sessionId - The thread/session ID
1055
- */
1056
- async clearAdditionalUserParts(sessionId) {
1057
- await this.deleteSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1058
- }
1059
1055
  /**
1060
1056
  * Issue an access token + refresh token for temporary authentication.
1061
1057
  * Requires an existing authenticated session (bearer token).
@@ -1085,7 +1081,7 @@ var _DistriClient = class _DistriClient {
1085
1081
  if (!tokens?.access_token || !tokens?.refresh_token || typeof tokens?.expires_at !== "number") {
1086
1082
  throw new ApiError("Invalid token response", response.status);
1087
1083
  }
1088
- this.applyTokens(tokens.access_token, tokens.refresh_token, false);
1084
+ this.applyTokens(tokens.access_token, tokens.refresh_token);
1089
1085
  return tokens;
1090
1086
  }
1091
1087
  /**
@@ -1098,13 +1094,18 @@ var _DistriClient = class _DistriClient {
1098
1094
  * Update the access/refresh tokens in memory.
1099
1095
  */
1100
1096
  setTokens(tokens) {
1101
- if (tokens.accessToken !== void 0) {
1102
- this.accessToken = tokens.accessToken;
1103
- }
1104
- if (tokens.refreshToken !== void 0) {
1097
+ this.accessToken = tokens.accessToken;
1098
+ if (tokens.refreshToken) {
1105
1099
  this.refreshToken = tokens.refreshToken;
1106
1100
  }
1107
1101
  }
1102
+ /**
1103
+ * Reset all authentication tokens.
1104
+ */
1105
+ resetTokens() {
1106
+ this.accessToken = void 0;
1107
+ this.refreshToken = void 0;
1108
+ }
1108
1109
  /**
1109
1110
  * Start streaming speech-to-text transcription via WebSocket
1110
1111
  */
@@ -1322,6 +1323,31 @@ var _DistriClient = class _DistriClient {
1322
1323
  throw new DistriError(`Failed to fetch agent ${agentId}`, "FETCH_ERROR", error);
1323
1324
  }
1324
1325
  }
1326
+ /**
1327
+ * Update an agent's definition (markdown only)
1328
+ */
1329
+ async updateAgent(agentId, update) {
1330
+ try {
1331
+ const response = await this.fetch(`/agents/${agentId}`, {
1332
+ method: "PUT",
1333
+ headers: {
1334
+ "Content-Type": "application/json",
1335
+ ...this.config.headers
1336
+ },
1337
+ body: JSON.stringify(update)
1338
+ });
1339
+ if (!response.ok) {
1340
+ if (response.status === 404) {
1341
+ throw new ApiError(`Agent not found: ${agentId}`, 404);
1342
+ }
1343
+ throw new ApiError(`Failed to update agent: ${response.statusText}`, response.status);
1344
+ }
1345
+ return await response.json();
1346
+ } catch (error) {
1347
+ if (error instanceof ApiError) throw error;
1348
+ throw new DistriError(`Failed to update agent ${agentId}`, "UPDATE_ERROR", error);
1349
+ }
1350
+ }
1325
1351
  /**
1326
1352
  * Get or create A2AClient for an agent
1327
1353
  */
@@ -1407,11 +1433,22 @@ var _DistriClient = class _DistriClient {
1407
1433
  }
1408
1434
  }
1409
1435
  /**
1410
- * Get threads from Distri server
1436
+ * Get threads from Distri server with filtering and pagination
1411
1437
  */
1412
- async getThreads() {
1438
+ async getThreads(params = {}) {
1413
1439
  try {
1414
- const response = await this.fetch(`/threads`);
1440
+ const searchParams = new URLSearchParams();
1441
+ if (params.agent_id) searchParams.set("agent_id", params.agent_id);
1442
+ if (params.external_id) searchParams.set("external_id", params.external_id);
1443
+ if (params.search) searchParams.set("search", params.search);
1444
+ if (params.from_date) searchParams.set("from_date", params.from_date);
1445
+ if (params.to_date) searchParams.set("to_date", params.to_date);
1446
+ if (params.tags?.length) searchParams.set("tags", params.tags.join(","));
1447
+ if (params.limit !== void 0) searchParams.set("limit", params.limit.toString());
1448
+ if (params.offset !== void 0) searchParams.set("offset", params.offset.toString());
1449
+ const queryString = searchParams.toString();
1450
+ const url = queryString ? `/threads?${queryString}` : "/threads";
1451
+ const response = await this.fetch(url);
1415
1452
  if (!response.ok) {
1416
1453
  throw new ApiError(`Failed to fetch threads: ${response.statusText}`, response.status);
1417
1454
  }
@@ -1421,6 +1458,39 @@ var _DistriClient = class _DistriClient {
1421
1458
  throw new DistriError("Failed to fetch threads", "FETCH_ERROR", error);
1422
1459
  }
1423
1460
  }
1461
+ /**
1462
+ * Get agents sorted by thread count (most active first)
1463
+ */
1464
+ async getAgentsByUsage() {
1465
+ try {
1466
+ const response = await this.fetch("/threads/agents");
1467
+ if (!response.ok) {
1468
+ throw new ApiError(`Failed to fetch agents by usage: ${response.statusText}`, response.status);
1469
+ }
1470
+ return await response.json();
1471
+ } catch (error) {
1472
+ if (error instanceof ApiError) throw error;
1473
+ throw new DistriError("Failed to fetch agents by usage", "FETCH_ERROR", error);
1474
+ }
1475
+ }
1476
+ /**
1477
+ * Create a new browser session
1478
+ * Returns session info including viewer_url and stream_url from browsr
1479
+ */
1480
+ async createBrowserSession() {
1481
+ try {
1482
+ const response = await this.fetch("/browser/session", {
1483
+ method: "POST"
1484
+ });
1485
+ if (!response.ok) {
1486
+ throw new ApiError(`Failed to create browser session: ${response.statusText}`, response.status);
1487
+ }
1488
+ return await response.json();
1489
+ } catch (error) {
1490
+ if (error instanceof ApiError) throw error;
1491
+ throw new DistriError("Failed to create browser session", "FETCH_ERROR", error);
1492
+ }
1493
+ }
1424
1494
  async getThread(threadId) {
1425
1495
  try {
1426
1496
  const response = await this.fetch(`/threads/${threadId}`);
@@ -1520,15 +1590,17 @@ var _DistriClient = class _DistriClient {
1520
1590
  get baseUrl() {
1521
1591
  return this.config.baseUrl;
1522
1592
  }
1523
- applyTokens(accessToken, refreshToken, notify) {
1593
+ applyTokens(accessToken, refreshToken) {
1524
1594
  this.accessToken = accessToken;
1525
- this.refreshToken = refreshToken;
1526
- if (notify && this.onTokenRefresh) {
1527
- this.onTokenRefresh({ accessToken, refreshToken });
1595
+ if (refreshToken) {
1596
+ this.refreshToken = refreshToken;
1528
1597
  }
1529
1598
  }
1599
+ /**
1600
+ * Ensure access token is valid, refreshing if necessary
1601
+ */
1530
1602
  async ensureAccessToken() {
1531
- if (!this.refreshToken) {
1603
+ if (!this.refreshToken && !this.onTokenRefresh) {
1532
1604
  return;
1533
1605
  }
1534
1606
  if (!this.accessToken || this.isTokenExpiring(this.accessToken)) {
@@ -1540,7 +1612,7 @@ var _DistriClient = class _DistriClient {
1540
1612
  }
1541
1613
  }
1542
1614
  async refreshTokens() {
1543
- if (!this.refreshToken) {
1615
+ if (!this.refreshToken && !this.onTokenRefresh) {
1544
1616
  return;
1545
1617
  }
1546
1618
  if (!this.refreshPromise) {
@@ -1551,6 +1623,17 @@ var _DistriClient = class _DistriClient {
1551
1623
  return this.refreshPromise;
1552
1624
  }
1553
1625
  async performTokenRefresh() {
1626
+ if (this.onTokenRefresh) {
1627
+ this.accessToken = void 0;
1628
+ const newToken = await this.onTokenRefresh();
1629
+ if (newToken) {
1630
+ this.applyTokens(newToken);
1631
+ return;
1632
+ }
1633
+ }
1634
+ if (!this.refreshToken) {
1635
+ return;
1636
+ }
1554
1637
  const response = await this.fetchAbsolute(
1555
1638
  `${this.config.baseUrl}/token`,
1556
1639
  {
@@ -1574,7 +1657,7 @@ var _DistriClient = class _DistriClient {
1574
1657
  if (!tokens?.access_token || !tokens?.refresh_token) {
1575
1658
  throw new ApiError("Invalid token response", response.status);
1576
1659
  }
1577
- this.applyTokens(tokens.access_token, tokens.refresh_token, true);
1660
+ this.applyTokens(tokens.access_token, tokens.refresh_token);
1578
1661
  }
1579
1662
  isTokenExpiring(token) {
1580
1663
  const expiresAt = this.getTokenExpiry(token);
@@ -1669,7 +1752,7 @@ var _DistriClient = class _DistriClient {
1669
1752
  headers
1670
1753
  });
1671
1754
  clearTimeout(timeoutId);
1672
- if (!skipAuth && retryOnAuth && response.status === 401 && this.refreshToken) {
1755
+ if (!skipAuth && retryOnAuth && response.status === 401 && (this.refreshToken || this.onTokenRefresh)) {
1673
1756
  const refreshed = await this.refreshTokens().then(() => true).catch(() => false);
1674
1757
  if (refreshed) {
1675
1758
  return this.fetchAbsolute(url, initialInit, { skipAuth, retryOnAuth: false });
@@ -1687,7 +1770,8 @@ var _DistriClient = class _DistriClient {
1687
1770
  throw lastError;
1688
1771
  }
1689
1772
  /**
1690
- * Enhanced fetch with retry logic
1773
+ * Enhanced fetch with retry logic and auth headers.
1774
+ * Exposed publicly for extensions like DistriHomeClient.
1691
1775
  */
1692
1776
  async fetch(input, initialInit) {
1693
1777
  const url = `${this.config.baseUrl}${input}`;
@@ -1760,13 +1844,6 @@ var _DistriClient = class _DistriClient {
1760
1844
  }
1761
1845
  };
1762
1846
  // ============================================================
1763
- // Additional User Message Parts API
1764
- // ============================================================
1765
- // These methods allow external tools to append parts (text, images)
1766
- // to the user message in the next agent iteration.
1767
- // The parts are stored under the key "__additional_user_parts".
1768
- _DistriClient.ADDITIONAL_PARTS_KEY = "__additional_user_parts";
1769
- // ============================================================
1770
1847
  // Token API
1771
1848
  // ============================================================
1772
1849
  // Issue access + refresh tokens for temporary authentication (e.g., frontend use)
@@ -1792,6 +1869,25 @@ function uuidv4() {
1792
1869
  }
1793
1870
 
1794
1871
  // src/agent.ts
1872
+ var ExternalToolValidationError = class extends DistriError {
1873
+ constructor(agentName, result) {
1874
+ super(
1875
+ result.message || "Missing required external tools for agent invocation.",
1876
+ "EXTERNAL_TOOL_VALIDATION_ERROR",
1877
+ {
1878
+ agentName,
1879
+ missingTools: result.missingTools,
1880
+ requiredTools: result.requiredTools,
1881
+ providedTools: result.providedTools
1882
+ }
1883
+ );
1884
+ this.name = "ExternalToolValidationError";
1885
+ this.agentName = agentName;
1886
+ this.missingTools = result.missingTools;
1887
+ this.requiredTools = result.requiredTools;
1888
+ this.providedTools = result.providedTools;
1889
+ }
1890
+ };
1795
1891
  var Agent = class _Agent {
1796
1892
  constructor(agentDefinition, client) {
1797
1893
  this.hookHandlers = /* @__PURE__ */ new Map();
@@ -1812,7 +1908,7 @@ var Agent = class _Agent {
1812
1908
  return this.agentDefinition.description;
1813
1909
  }
1814
1910
  get agentType() {
1815
- return this.agentDefinition.agent_type ?? this.agentDefinition.agentType;
1911
+ return this.agentDefinition.agent_type;
1816
1912
  }
1817
1913
  get iconUrl() {
1818
1914
  return this.agentDefinition.icon_url;
@@ -1849,7 +1945,7 @@ var Agent = class _Agent {
1849
1945
  const enhancedParams = this.enhanceParamsWithTools(params, tools);
1850
1946
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
1851
1947
  const self = this;
1852
- return (async function* () {
1948
+ return async function* () {
1853
1949
  for await (const event of a2aStream) {
1854
1950
  const converted = decodeA2AStreamEvent(event);
1855
1951
  if (converted && converted.type === "inline_hook_requested") {
@@ -1870,12 +1966,38 @@ var Agent = class _Agent {
1870
1966
  yield converted;
1871
1967
  }
1872
1968
  }
1873
- })();
1969
+ }();
1970
+ }
1971
+ /**
1972
+ * Validate that required external tools are registered before invoking.
1973
+ */
1974
+ validateExternalTools(tools = []) {
1975
+ const requiredTools = this.getRequiredExternalTools();
1976
+ const providedTools = tools.map((tool) => tool.name);
1977
+ if (requiredTools.length === 0) {
1978
+ return {
1979
+ isValid: true,
1980
+ requiredTools: [],
1981
+ providedTools,
1982
+ missingTools: []
1983
+ };
1984
+ }
1985
+ const providedSet = new Set(providedTools);
1986
+ const missingTools = requiredTools.filter((tool) => !providedSet.has(tool));
1987
+ const isValid = missingTools.length === 0;
1988
+ return {
1989
+ isValid,
1990
+ requiredTools,
1991
+ providedTools,
1992
+ missingTools,
1993
+ message: isValid ? void 0 : this.formatExternalToolValidationMessage(requiredTools, missingTools)
1994
+ };
1874
1995
  }
1875
1996
  /**
1876
1997
  * Enhance message params with tool definitions
1877
1998
  */
1878
1999
  enhanceParamsWithTools(params, tools) {
2000
+ this.assertExternalTools(tools);
1879
2001
  const metadata = {
1880
2002
  ...params.metadata,
1881
2003
  external_tools: tools?.map((tool) => ({
@@ -1890,6 +2012,39 @@ var Agent = class _Agent {
1890
2012
  metadata
1891
2013
  };
1892
2014
  }
2015
+ assertExternalTools(tools) {
2016
+ const result = this.validateExternalTools(tools ?? []);
2017
+ if (!result.isValid) {
2018
+ throw new ExternalToolValidationError(this.agentDefinition.name || this.agentDefinition.id, result);
2019
+ }
2020
+ }
2021
+ getRequiredExternalTools() {
2022
+ const toolConfig = this.resolveToolConfig();
2023
+ if (!toolConfig?.external || !Array.isArray(toolConfig.external)) {
2024
+ return [];
2025
+ }
2026
+ if (toolConfig.external.includes("*")) {
2027
+ return [];
2028
+ }
2029
+ return toolConfig.external.filter((tool) => typeof tool === "string" && tool.trim().length > 0);
2030
+ }
2031
+ resolveToolConfig() {
2032
+ const root = this.agentDefinition;
2033
+ return this.extractToolConfig(root) || this.extractToolConfig(root?.agent) || this.extractToolConfig(root?.definition);
2034
+ }
2035
+ extractToolConfig(candidate) {
2036
+ if (!candidate) return null;
2037
+ const tools = candidate.tools;
2038
+ if (!tools || Array.isArray(tools) || typeof tools !== "object") {
2039
+ return null;
2040
+ }
2041
+ return tools;
2042
+ }
2043
+ formatExternalToolValidationMessage(requiredTools, missingTools) {
2044
+ const requiredList = requiredTools.join(", ");
2045
+ const missingList = missingTools.join(", ");
2046
+ return `Agent has external tools that are not registered: ${missingList}. This is an embedded agent that can run within the parent application. Register DistriWidget for embedding the parent component. Required tools: ${requiredList}.`;
2047
+ }
1893
2048
  /**
1894
2049
  * Register multiple hooks at once.
1895
2050
  */
@@ -1906,12 +2061,13 @@ var Agent = class _Agent {
1906
2061
  */
1907
2062
  static async create(agentIdOrDef, client) {
1908
2063
  const agentDefinition = typeof agentIdOrDef === "string" ? await client.getAgent(agentIdOrDef) : agentIdOrDef;
2064
+ const tools = agentDefinition?.resolved_tools || [];
1909
2065
  console.log("\u{1F916} Agent definition loaded:", {
1910
2066
  id: agentDefinition.id,
1911
2067
  name: agentDefinition.name,
1912
- tools: agentDefinition.tools?.map((t) => ({
2068
+ tools: tools.map((t) => ({
1913
2069
  name: t.name,
1914
- type: t.type || "function"
2070
+ type: "function"
1915
2071
  })) || [],
1916
2072
  toolCount: agentDefinition.tools?.length || 0
1917
2073
  });
@@ -1940,6 +2096,7 @@ var Agent = class _Agent {
1940
2096
  DEFAULT_BASE_URL,
1941
2097
  DistriClient,
1942
2098
  DistriError,
2099
+ ExternalToolValidationError,
1943
2100
  convertA2AMessageToDistri,
1944
2101
  convertA2APartToDistri,
1945
2102
  convertA2AStatusUpdateToDistri,