@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.mjs CHANGED
@@ -681,6 +681,17 @@ function convertA2AStatusUpdateToDistri(statusUpdate) {
681
681
  };
682
682
  return hookRequested;
683
683
  }
684
+ case "browser_session_started": {
685
+ const browserSessionStarted = {
686
+ type: "browser_session_started",
687
+ data: {
688
+ session_id: metadata.session_id || "",
689
+ viewer_url: metadata.viewer_url,
690
+ stream_url: metadata.stream_url
691
+ }
692
+ };
693
+ return browserSessionStarted;
694
+ }
684
695
  default: {
685
696
  console.warn(`Unhandled status update metadata type: ${metadata.type}`, metadata);
686
697
  const defaultResult = {
@@ -860,21 +871,29 @@ var _DistriClient = class _DistriClient {
860
871
  this.tokenRefreshSkewMs = config.tokenRefreshSkewMs ?? 6e4;
861
872
  this.onTokenRefresh = config.onTokenRefresh;
862
873
  this.config = {
863
- baseUrl: config.baseUrl.replace(/\/$/, ""),
874
+ baseUrl: config.baseUrl?.replace(/\/$/, "") || DEFAULT_BASE_URL,
864
875
  apiVersion: config.apiVersion || "v1",
865
- timeout: config.timeout || 3e4,
866
- retryAttempts: config.retryAttempts || 3,
867
- retryDelay: config.retryDelay || 1e3,
868
- debug: config.debug || false,
876
+ timeout: config.timeout ?? 3e4,
877
+ retryAttempts: config.retryAttempts ?? 3,
878
+ retryDelay: config.retryDelay ?? 1e3,
879
+ debug: config.debug ?? false,
869
880
  headers,
870
- interceptor: config.interceptor || ((init) => Promise.resolve(init))
881
+ interceptor: config.interceptor ?? (async (init) => Promise.resolve(init)),
882
+ onTokenRefresh: config.onTokenRefresh,
883
+ clientId: config.clientId
871
884
  };
872
- this.debug("DistriClient initialized with config:", {
873
- baseUrl: this.config.baseUrl,
874
- hasAccessToken: !!this.accessToken,
875
- hasRefreshToken: !!this.refreshToken,
876
- timeout: this.config.timeout
877
- });
885
+ }
886
+ /**
887
+ * Get the configured client ID.
888
+ */
889
+ get clientId() {
890
+ return this.config.clientId;
891
+ }
892
+ /**
893
+ * Set the client ID for embed token issuance.
894
+ */
895
+ set clientId(value) {
896
+ this.config.clientId = value;
878
897
  }
879
898
  /**
880
899
  * Create a client with default cloud configuration.
@@ -907,7 +926,7 @@ var _DistriClient = class _DistriClient {
907
926
  if (expiry) {
908
927
  body.expiry = typeof expiry === "string" ? expiry : expiry.toISOString();
909
928
  }
910
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
929
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values`, {
911
930
  method: "POST",
912
931
  headers: {
913
932
  "Content-Type": "application/json",
@@ -924,7 +943,7 @@ var _DistriClient = class _DistriClient {
924
943
  * Session store: get a single value
925
944
  */
926
945
  async getSessionValue(sessionId, key) {
927
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
946
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
928
947
  method: "GET",
929
948
  headers: {
930
949
  ...this.config.headers
@@ -941,7 +960,7 @@ var _DistriClient = class _DistriClient {
941
960
  * Session store: get all values in a session
942
961
  */
943
962
  async getSessionValues(sessionId) {
944
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values`, {
963
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values`, {
945
964
  method: "GET",
946
965
  headers: {
947
966
  ...this.config.headers
@@ -958,7 +977,7 @@ var _DistriClient = class _DistriClient {
958
977
  * Session store: delete a single key
959
978
  */
960
979
  async deleteSessionValue(sessionId, key) {
961
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
980
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}/values/${encodeURIComponent(key)}`, {
962
981
  method: "DELETE",
963
982
  headers: {
964
983
  ...this.config.headers
@@ -973,7 +992,7 @@ var _DistriClient = class _DistriClient {
973
992
  * Session store: clear all keys in a session
974
993
  */
975
994
  async clearSession(sessionId) {
976
- const resp = await this.fetch(`/session/${encodeURIComponent(sessionId)}`, {
995
+ const resp = await this.fetch(`/sessions/${encodeURIComponent(sessionId)}`, {
977
996
  method: "DELETE",
978
997
  headers: {
979
998
  ...this.config.headers
@@ -984,30 +1003,6 @@ var _DistriClient = class _DistriClient {
984
1003
  throw new ApiError(errorData.error || "Failed to clear session", resp.status);
985
1004
  }
986
1005
  }
987
- /**
988
- * Set additional user message parts for the next agent iteration.
989
- * These parts will be appended to the user message in the prompt.
990
- * @param sessionId - The thread/session ID
991
- * @param parts - Array of DistriPart objects to append to user message
992
- */
993
- async setAdditionalUserParts(sessionId, parts) {
994
- await this.setSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY, parts);
995
- }
996
- /**
997
- * Get the current additional user message parts.
998
- * @param sessionId - The thread/session ID
999
- * @returns Array of DistriPart objects or null if not set
1000
- */
1001
- async getAdditionalUserParts(sessionId) {
1002
- return this.getSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1003
- }
1004
- /**
1005
- * Clear/delete the additional user message parts.
1006
- * @param sessionId - The thread/session ID
1007
- */
1008
- async clearAdditionalUserParts(sessionId) {
1009
- await this.deleteSessionValue(sessionId, _DistriClient.ADDITIONAL_PARTS_KEY);
1010
- }
1011
1006
  /**
1012
1007
  * Issue an access token + refresh token for temporary authentication.
1013
1008
  * Requires an existing authenticated session (bearer token).
@@ -1037,7 +1032,7 @@ var _DistriClient = class _DistriClient {
1037
1032
  if (!tokens?.access_token || !tokens?.refresh_token || typeof tokens?.expires_at !== "number") {
1038
1033
  throw new ApiError("Invalid token response", response.status);
1039
1034
  }
1040
- this.applyTokens(tokens.access_token, tokens.refresh_token, false);
1035
+ this.applyTokens(tokens.access_token, tokens.refresh_token);
1041
1036
  return tokens;
1042
1037
  }
1043
1038
  /**
@@ -1050,13 +1045,18 @@ var _DistriClient = class _DistriClient {
1050
1045
  * Update the access/refresh tokens in memory.
1051
1046
  */
1052
1047
  setTokens(tokens) {
1053
- if (tokens.accessToken !== void 0) {
1054
- this.accessToken = tokens.accessToken;
1055
- }
1056
- if (tokens.refreshToken !== void 0) {
1048
+ this.accessToken = tokens.accessToken;
1049
+ if (tokens.refreshToken) {
1057
1050
  this.refreshToken = tokens.refreshToken;
1058
1051
  }
1059
1052
  }
1053
+ /**
1054
+ * Reset all authentication tokens.
1055
+ */
1056
+ resetTokens() {
1057
+ this.accessToken = void 0;
1058
+ this.refreshToken = void 0;
1059
+ }
1060
1060
  /**
1061
1061
  * Start streaming speech-to-text transcription via WebSocket
1062
1062
  */
@@ -1274,6 +1274,31 @@ var _DistriClient = class _DistriClient {
1274
1274
  throw new DistriError(`Failed to fetch agent ${agentId}`, "FETCH_ERROR", error);
1275
1275
  }
1276
1276
  }
1277
+ /**
1278
+ * Update an agent's definition (markdown only)
1279
+ */
1280
+ async updateAgent(agentId, update) {
1281
+ try {
1282
+ const response = await this.fetch(`/agents/${agentId}`, {
1283
+ method: "PUT",
1284
+ headers: {
1285
+ "Content-Type": "application/json",
1286
+ ...this.config.headers
1287
+ },
1288
+ body: JSON.stringify(update)
1289
+ });
1290
+ if (!response.ok) {
1291
+ if (response.status === 404) {
1292
+ throw new ApiError(`Agent not found: ${agentId}`, 404);
1293
+ }
1294
+ throw new ApiError(`Failed to update agent: ${response.statusText}`, response.status);
1295
+ }
1296
+ return await response.json();
1297
+ } catch (error) {
1298
+ if (error instanceof ApiError) throw error;
1299
+ throw new DistriError(`Failed to update agent ${agentId}`, "UPDATE_ERROR", error);
1300
+ }
1301
+ }
1277
1302
  /**
1278
1303
  * Get or create A2AClient for an agent
1279
1304
  */
@@ -1359,11 +1384,22 @@ var _DistriClient = class _DistriClient {
1359
1384
  }
1360
1385
  }
1361
1386
  /**
1362
- * Get threads from Distri server
1387
+ * Get threads from Distri server with filtering and pagination
1363
1388
  */
1364
- async getThreads() {
1389
+ async getThreads(params = {}) {
1365
1390
  try {
1366
- const response = await this.fetch(`/threads`);
1391
+ const searchParams = new URLSearchParams();
1392
+ if (params.agent_id) searchParams.set("agent_id", params.agent_id);
1393
+ if (params.external_id) searchParams.set("external_id", params.external_id);
1394
+ if (params.search) searchParams.set("search", params.search);
1395
+ if (params.from_date) searchParams.set("from_date", params.from_date);
1396
+ if (params.to_date) searchParams.set("to_date", params.to_date);
1397
+ if (params.tags?.length) searchParams.set("tags", params.tags.join(","));
1398
+ if (params.limit !== void 0) searchParams.set("limit", params.limit.toString());
1399
+ if (params.offset !== void 0) searchParams.set("offset", params.offset.toString());
1400
+ const queryString = searchParams.toString();
1401
+ const url = queryString ? `/threads?${queryString}` : "/threads";
1402
+ const response = await this.fetch(url);
1367
1403
  if (!response.ok) {
1368
1404
  throw new ApiError(`Failed to fetch threads: ${response.statusText}`, response.status);
1369
1405
  }
@@ -1373,6 +1409,39 @@ var _DistriClient = class _DistriClient {
1373
1409
  throw new DistriError("Failed to fetch threads", "FETCH_ERROR", error);
1374
1410
  }
1375
1411
  }
1412
+ /**
1413
+ * Get agents sorted by thread count (most active first)
1414
+ */
1415
+ async getAgentsByUsage() {
1416
+ try {
1417
+ const response = await this.fetch("/threads/agents");
1418
+ if (!response.ok) {
1419
+ throw new ApiError(`Failed to fetch agents by usage: ${response.statusText}`, response.status);
1420
+ }
1421
+ return await response.json();
1422
+ } catch (error) {
1423
+ if (error instanceof ApiError) throw error;
1424
+ throw new DistriError("Failed to fetch agents by usage", "FETCH_ERROR", error);
1425
+ }
1426
+ }
1427
+ /**
1428
+ * Create a new browser session
1429
+ * Returns session info including viewer_url and stream_url from browsr
1430
+ */
1431
+ async createBrowserSession() {
1432
+ try {
1433
+ const response = await this.fetch("/browser/session", {
1434
+ method: "POST"
1435
+ });
1436
+ if (!response.ok) {
1437
+ throw new ApiError(`Failed to create browser session: ${response.statusText}`, response.status);
1438
+ }
1439
+ return await response.json();
1440
+ } catch (error) {
1441
+ if (error instanceof ApiError) throw error;
1442
+ throw new DistriError("Failed to create browser session", "FETCH_ERROR", error);
1443
+ }
1444
+ }
1376
1445
  async getThread(threadId) {
1377
1446
  try {
1378
1447
  const response = await this.fetch(`/threads/${threadId}`);
@@ -1472,15 +1541,17 @@ var _DistriClient = class _DistriClient {
1472
1541
  get baseUrl() {
1473
1542
  return this.config.baseUrl;
1474
1543
  }
1475
- applyTokens(accessToken, refreshToken, notify) {
1544
+ applyTokens(accessToken, refreshToken) {
1476
1545
  this.accessToken = accessToken;
1477
- this.refreshToken = refreshToken;
1478
- if (notify && this.onTokenRefresh) {
1479
- this.onTokenRefresh({ accessToken, refreshToken });
1546
+ if (refreshToken) {
1547
+ this.refreshToken = refreshToken;
1480
1548
  }
1481
1549
  }
1550
+ /**
1551
+ * Ensure access token is valid, refreshing if necessary
1552
+ */
1482
1553
  async ensureAccessToken() {
1483
- if (!this.refreshToken) {
1554
+ if (!this.refreshToken && !this.onTokenRefresh) {
1484
1555
  return;
1485
1556
  }
1486
1557
  if (!this.accessToken || this.isTokenExpiring(this.accessToken)) {
@@ -1492,7 +1563,7 @@ var _DistriClient = class _DistriClient {
1492
1563
  }
1493
1564
  }
1494
1565
  async refreshTokens() {
1495
- if (!this.refreshToken) {
1566
+ if (!this.refreshToken && !this.onTokenRefresh) {
1496
1567
  return;
1497
1568
  }
1498
1569
  if (!this.refreshPromise) {
@@ -1503,6 +1574,17 @@ var _DistriClient = class _DistriClient {
1503
1574
  return this.refreshPromise;
1504
1575
  }
1505
1576
  async performTokenRefresh() {
1577
+ if (this.onTokenRefresh) {
1578
+ this.accessToken = void 0;
1579
+ const newToken = await this.onTokenRefresh();
1580
+ if (newToken) {
1581
+ this.applyTokens(newToken);
1582
+ return;
1583
+ }
1584
+ }
1585
+ if (!this.refreshToken) {
1586
+ return;
1587
+ }
1506
1588
  const response = await this.fetchAbsolute(
1507
1589
  `${this.config.baseUrl}/token`,
1508
1590
  {
@@ -1526,7 +1608,7 @@ var _DistriClient = class _DistriClient {
1526
1608
  if (!tokens?.access_token || !tokens?.refresh_token) {
1527
1609
  throw new ApiError("Invalid token response", response.status);
1528
1610
  }
1529
- this.applyTokens(tokens.access_token, tokens.refresh_token, true);
1611
+ this.applyTokens(tokens.access_token, tokens.refresh_token);
1530
1612
  }
1531
1613
  isTokenExpiring(token) {
1532
1614
  const expiresAt = this.getTokenExpiry(token);
@@ -1621,7 +1703,7 @@ var _DistriClient = class _DistriClient {
1621
1703
  headers
1622
1704
  });
1623
1705
  clearTimeout(timeoutId);
1624
- if (!skipAuth && retryOnAuth && response.status === 401 && this.refreshToken) {
1706
+ if (!skipAuth && retryOnAuth && response.status === 401 && (this.refreshToken || this.onTokenRefresh)) {
1625
1707
  const refreshed = await this.refreshTokens().then(() => true).catch(() => false);
1626
1708
  if (refreshed) {
1627
1709
  return this.fetchAbsolute(url, initialInit, { skipAuth, retryOnAuth: false });
@@ -1639,7 +1721,8 @@ var _DistriClient = class _DistriClient {
1639
1721
  throw lastError;
1640
1722
  }
1641
1723
  /**
1642
- * Enhanced fetch with retry logic
1724
+ * Enhanced fetch with retry logic and auth headers.
1725
+ * Exposed publicly for extensions like DistriHomeClient.
1643
1726
  */
1644
1727
  async fetch(input, initialInit) {
1645
1728
  const url = `${this.config.baseUrl}${input}`;
@@ -1712,13 +1795,6 @@ var _DistriClient = class _DistriClient {
1712
1795
  }
1713
1796
  };
1714
1797
  // ============================================================
1715
- // Additional User Message Parts API
1716
- // ============================================================
1717
- // These methods allow external tools to append parts (text, images)
1718
- // to the user message in the next agent iteration.
1719
- // The parts are stored under the key "__additional_user_parts".
1720
- _DistriClient.ADDITIONAL_PARTS_KEY = "__additional_user_parts";
1721
- // ============================================================
1722
1798
  // Token API
1723
1799
  // ============================================================
1724
1800
  // Issue access + refresh tokens for temporary authentication (e.g., frontend use)
@@ -1744,6 +1820,25 @@ function uuidv4() {
1744
1820
  }
1745
1821
 
1746
1822
  // src/agent.ts
1823
+ var ExternalToolValidationError = class extends DistriError {
1824
+ constructor(agentName, result) {
1825
+ super(
1826
+ result.message || "Missing required external tools for agent invocation.",
1827
+ "EXTERNAL_TOOL_VALIDATION_ERROR",
1828
+ {
1829
+ agentName,
1830
+ missingTools: result.missingTools,
1831
+ requiredTools: result.requiredTools,
1832
+ providedTools: result.providedTools
1833
+ }
1834
+ );
1835
+ this.name = "ExternalToolValidationError";
1836
+ this.agentName = agentName;
1837
+ this.missingTools = result.missingTools;
1838
+ this.requiredTools = result.requiredTools;
1839
+ this.providedTools = result.providedTools;
1840
+ }
1841
+ };
1747
1842
  var Agent = class _Agent {
1748
1843
  constructor(agentDefinition, client) {
1749
1844
  this.hookHandlers = /* @__PURE__ */ new Map();
@@ -1764,7 +1859,7 @@ var Agent = class _Agent {
1764
1859
  return this.agentDefinition.description;
1765
1860
  }
1766
1861
  get agentType() {
1767
- return this.agentDefinition.agent_type ?? this.agentDefinition.agentType;
1862
+ return this.agentDefinition.agent_type;
1768
1863
  }
1769
1864
  get iconUrl() {
1770
1865
  return this.agentDefinition.icon_url;
@@ -1801,7 +1896,7 @@ var Agent = class _Agent {
1801
1896
  const enhancedParams = this.enhanceParamsWithTools(params, tools);
1802
1897
  const a2aStream = this.client.sendMessageStream(this.agentDefinition.id, enhancedParams);
1803
1898
  const self = this;
1804
- return (async function* () {
1899
+ return async function* () {
1805
1900
  for await (const event of a2aStream) {
1806
1901
  const converted = decodeA2AStreamEvent(event);
1807
1902
  if (converted && converted.type === "inline_hook_requested") {
@@ -1822,12 +1917,38 @@ var Agent = class _Agent {
1822
1917
  yield converted;
1823
1918
  }
1824
1919
  }
1825
- })();
1920
+ }();
1921
+ }
1922
+ /**
1923
+ * Validate that required external tools are registered before invoking.
1924
+ */
1925
+ validateExternalTools(tools = []) {
1926
+ const requiredTools = this.getRequiredExternalTools();
1927
+ const providedTools = tools.map((tool) => tool.name);
1928
+ if (requiredTools.length === 0) {
1929
+ return {
1930
+ isValid: true,
1931
+ requiredTools: [],
1932
+ providedTools,
1933
+ missingTools: []
1934
+ };
1935
+ }
1936
+ const providedSet = new Set(providedTools);
1937
+ const missingTools = requiredTools.filter((tool) => !providedSet.has(tool));
1938
+ const isValid = missingTools.length === 0;
1939
+ return {
1940
+ isValid,
1941
+ requiredTools,
1942
+ providedTools,
1943
+ missingTools,
1944
+ message: isValid ? void 0 : this.formatExternalToolValidationMessage(requiredTools, missingTools)
1945
+ };
1826
1946
  }
1827
1947
  /**
1828
1948
  * Enhance message params with tool definitions
1829
1949
  */
1830
1950
  enhanceParamsWithTools(params, tools) {
1951
+ this.assertExternalTools(tools);
1831
1952
  const metadata = {
1832
1953
  ...params.metadata,
1833
1954
  external_tools: tools?.map((tool) => ({
@@ -1842,6 +1963,39 @@ var Agent = class _Agent {
1842
1963
  metadata
1843
1964
  };
1844
1965
  }
1966
+ assertExternalTools(tools) {
1967
+ const result = this.validateExternalTools(tools ?? []);
1968
+ if (!result.isValid) {
1969
+ throw new ExternalToolValidationError(this.agentDefinition.name || this.agentDefinition.id, result);
1970
+ }
1971
+ }
1972
+ getRequiredExternalTools() {
1973
+ const toolConfig = this.resolveToolConfig();
1974
+ if (!toolConfig?.external || !Array.isArray(toolConfig.external)) {
1975
+ return [];
1976
+ }
1977
+ if (toolConfig.external.includes("*")) {
1978
+ return [];
1979
+ }
1980
+ return toolConfig.external.filter((tool) => typeof tool === "string" && tool.trim().length > 0);
1981
+ }
1982
+ resolveToolConfig() {
1983
+ const root = this.agentDefinition;
1984
+ return this.extractToolConfig(root) || this.extractToolConfig(root?.agent) || this.extractToolConfig(root?.definition);
1985
+ }
1986
+ extractToolConfig(candidate) {
1987
+ if (!candidate) return null;
1988
+ const tools = candidate.tools;
1989
+ if (!tools || Array.isArray(tools) || typeof tools !== "object") {
1990
+ return null;
1991
+ }
1992
+ return tools;
1993
+ }
1994
+ formatExternalToolValidationMessage(requiredTools, missingTools) {
1995
+ const requiredList = requiredTools.join(", ");
1996
+ const missingList = missingTools.join(", ");
1997
+ 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}.`;
1998
+ }
1845
1999
  /**
1846
2000
  * Register multiple hooks at once.
1847
2001
  */
@@ -1858,12 +2012,13 @@ var Agent = class _Agent {
1858
2012
  */
1859
2013
  static async create(agentIdOrDef, client) {
1860
2014
  const agentDefinition = typeof agentIdOrDef === "string" ? await client.getAgent(agentIdOrDef) : agentIdOrDef;
2015
+ const tools = agentDefinition?.resolved_tools || [];
1861
2016
  console.log("\u{1F916} Agent definition loaded:", {
1862
2017
  id: agentDefinition.id,
1863
2018
  name: agentDefinition.name,
1864
- tools: agentDefinition.tools?.map((t) => ({
2019
+ tools: tools.map((t) => ({
1865
2020
  name: t.name,
1866
- type: t.type || "function"
2021
+ type: "function"
1867
2022
  })) || [],
1868
2023
  toolCount: agentDefinition.tools?.length || 0
1869
2024
  });
@@ -1891,6 +2046,7 @@ export {
1891
2046
  DEFAULT_BASE_URL,
1892
2047
  DistriClient,
1893
2048
  DistriError,
2049
+ ExternalToolValidationError,
1894
2050
  convertA2AMessageToDistri,
1895
2051
  convertA2APartToDistri,
1896
2052
  convertA2AStatusUpdateToDistri,