@mantyx/sdk 0.9.1 → 0.10.0

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.cjs CHANGED
@@ -32,17 +32,23 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AgentSession: () => AgentSession,
34
34
  DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
35
+ DEFAULT_OAUTH_BASE_URL: () => DEFAULT_OAUTH_BASE_URL,
36
+ DEFAULT_REFRESH_SKEW_MS: () => DEFAULT_REFRESH_SKEW_MS,
35
37
  MantyxAuthError: () => MantyxAuthError,
36
38
  MantyxClient: () => MantyxClient,
37
39
  MantyxError: () => MantyxError,
38
40
  MantyxNetworkError: () => MantyxNetworkError,
41
+ MantyxOAuthClient: () => MantyxOAuthClient,
42
+ MantyxOAuthError: () => MantyxOAuthError,
39
43
  MantyxParseError: () => MantyxParseError,
40
44
  MantyxRunError: () => MantyxRunError,
45
+ MantyxScopeError: () => MantyxScopeError,
41
46
  MantyxToolError: () => MantyxToolError,
42
47
  SDK_VERSION: () => SDK_VERSION,
43
48
  defineLocalA2A: () => defineLocalA2A,
44
49
  defineLocalMcp: () => defineLocalMcp,
45
50
  defineLocalTool: () => defineLocalTool,
51
+ generatePkceVerifier: () => generatePkceVerifier,
46
52
  isLocalA2ATool: () => isLocalA2ATool,
47
53
  isLocalMcpServer: () => isLocalMcpServer,
48
54
  isLocalTool: () => isLocalTool,
@@ -51,6 +57,7 @@ __export(index_exports, {
51
57
  mantyxPluginTool: () => mantyxPluginTool,
52
58
  mantyxTool: () => mantyxTool,
53
59
  parseRunOutput: () => parseRunOutput,
60
+ pkceChallenge: () => pkceChallenge,
54
61
  readSseStream: () => readSseStream,
55
62
  toToolParametersWire: () => toToolParametersWire,
56
63
  zodToJsonSchema: () => zodToJsonSchema
@@ -80,11 +87,23 @@ var MantyxNetworkError = class extends MantyxError {
80
87
  }
81
88
  };
82
89
  var MantyxAuthError = class extends MantyxError {
83
- constructor(message = "Invalid or missing API key") {
90
+ constructor(message = "Invalid or missing API key / OAuth access token") {
84
91
  super(message, { code: "unauthorized", status: 401 });
85
92
  this.name = "MantyxAuthError";
86
93
  }
87
94
  };
95
+ var MantyxScopeError = class extends MantyxError {
96
+ /**
97
+ * Scope(s) the route demanded. Always at least one entry; usually
98
+ * exactly one. New routes may demand more scopes in the future.
99
+ */
100
+ requiredScopes;
101
+ constructor(message, requiredScopes) {
102
+ super(message, { code: "insufficient_scope", status: 403 });
103
+ this.name = "MantyxScopeError";
104
+ this.requiredScopes = [...requiredScopes];
105
+ }
106
+ };
88
107
  var MantyxToolError = class extends MantyxError {
89
108
  toolName;
90
109
  constructor(toolName, message) {
@@ -635,9 +654,7 @@ var DEFAULT_BASE_URL = "https://app.mantyx.io";
635
654
  var MantyxClient = class {
636
655
  options;
637
656
  constructor(opts) {
638
- if (!opts.apiKey || typeof opts.apiKey !== "string") {
639
- throw new MantyxError("apiKey is required");
640
- }
657
+ const { credential, tokenSource } = resolveCredential(opts);
641
658
  if (!opts.workspaceSlug || typeof opts.workspaceSlug !== "string") {
642
659
  throw new MantyxError("workspaceSlug is required");
643
660
  }
@@ -648,11 +665,12 @@ var MantyxClient = class {
648
665
  );
649
666
  }
650
667
  this.options = {
651
- apiKey: opts.apiKey,
668
+ apiKey: credential,
652
669
  workspaceSlug: opts.workspaceSlug,
653
670
  baseUrl: (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
654
671
  fetch: f,
655
- timeoutMs: opts.timeoutMs ?? 6e4
672
+ timeoutMs: opts.timeoutMs ?? 6e4,
673
+ tokenSource
656
674
  };
657
675
  }
658
676
  // -------------------------------------------------------------- Models
@@ -788,19 +806,7 @@ var MantyxClient = class {
788
806
  let lastSeq = 0;
789
807
  while (true) {
790
808
  const reqUrl = lastSeq > 0 ? `${url}?lastSeq=${lastSeq}` : url;
791
- const res = await this.options.fetch(reqUrl, {
792
- method: "GET",
793
- headers: {
794
- ...this.authHeaders(),
795
- Accept: "text/event-stream",
796
- ...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
797
- },
798
- ...signal ? { signal } : {}
799
- }).catch((err) => {
800
- throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
801
- cause: err
802
- });
803
- });
809
+ const res = await this.openSseStream(reqUrl, lastSeq, signal);
804
810
  if (!res.ok) {
805
811
  throw await this.errorFromResponse(res);
806
812
  }
@@ -903,18 +909,72 @@ var MantyxClient = class {
903
909
  absoluteUrl(path) {
904
910
  return `${this.options.baseUrl}/api/v1/workspaces/${encodeURIComponent(this.options.workspaceSlug)}${path}`;
905
911
  }
906
- authHeaders() {
907
- return { Authorization: `Bearer ${this.options.apiKey}` };
912
+ /**
913
+ * Resolve the bearer credential to send on the next request. With a
914
+ * static `apiKey` / `accessToken` this is a synchronous reach into
915
+ * `options.apiKey`; with a {@link TokenSource} it delegates so the
916
+ * source can refresh expired access tokens before we hit the wire.
917
+ *
918
+ * The `reason` is forwarded to the source verbatim. Pass
919
+ * `"unauthorized"` immediately after a 401 so the source forces a
920
+ * refresh rather than handing back its (now-invalid) cached value.
921
+ */
922
+ async resolveBearer(reason = "initial") {
923
+ if (this.options.tokenSource) return this.options.tokenSource(reason);
924
+ return this.options.apiKey;
925
+ }
926
+ /**
927
+ * Open an SSE stream against `reqUrl` with at-most-one refresh +
928
+ * retry on 401. The caller is responsible for the subsequent
929
+ * `readSseStream` loop; this helper only handles the initial GET.
930
+ * Mid-stream 401s propagate as `MantyxNetworkError` from the read
931
+ * loop and trigger a reconnect via the outer `while` in
932
+ * {@link streamRunEvents}.
933
+ */
934
+ async openSseStream(reqUrl, lastSeq, signal) {
935
+ const openOnce = async (reason) => {
936
+ const auth = await this.authHeaders(reason);
937
+ return this.options.fetch(reqUrl, {
938
+ method: "GET",
939
+ headers: {
940
+ ...auth,
941
+ Accept: "text/event-stream",
942
+ ...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
943
+ },
944
+ ...signal ? { signal } : {}
945
+ }).catch((err) => {
946
+ throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
947
+ cause: err
948
+ });
949
+ });
950
+ };
951
+ const res = await openOnce("initial");
952
+ if (res.status === 401 && this.options.tokenSource !== null) {
953
+ try {
954
+ await res.text();
955
+ } catch {
956
+ }
957
+ return openOnce("unauthorized");
958
+ }
959
+ return res;
960
+ }
961
+ async authHeaders(reason = "initial") {
962
+ const bearer = await this.resolveBearer(reason);
963
+ return { Authorization: `Bearer ${bearer}` };
908
964
  }
909
965
  async request(args) {
966
+ return this.requestWithRetry(args, "initial");
967
+ }
968
+ async requestWithRetry(args, reason) {
910
969
  const url = this.absoluteUrl(args.path);
911
970
  const ctrl = new AbortController();
912
971
  const t = setTimeout(() => ctrl.abort(), args.timeoutMs ?? this.options.timeoutMs);
913
972
  try {
973
+ const auth = await this.authHeaders(reason);
914
974
  const res = await this.options.fetch(url, {
915
975
  method: args.method,
916
976
  headers: {
917
- ...this.authHeaders(),
977
+ ...auth,
918
978
  ...args.body !== void 0 ? { "Content-Type": "application/json" } : {},
919
979
  Accept: "application/json"
920
980
  },
@@ -927,6 +987,14 @@ var MantyxClient = class {
927
987
  throw new MantyxNetworkError(`Network error: ${err.message}`, { cause: err });
928
988
  });
929
989
  if (!res.ok) {
990
+ if (res.status === 401 && this.options.tokenSource !== null && reason === "initial") {
991
+ try {
992
+ await res.text();
993
+ } catch {
994
+ }
995
+ clearTimeout(t);
996
+ return this.requestWithRetry(args, "unauthorized");
997
+ }
930
998
  throw await this.errorFromResponse(res);
931
999
  }
932
1000
  const text = await res.text();
@@ -947,7 +1015,12 @@ var MantyxClient = class {
947
1015
  } catch {
948
1016
  }
949
1017
  if (res.status === 401) {
950
- return new MantyxAuthError(body.error ?? "Invalid API key");
1018
+ return new MantyxAuthError(body.error ?? "Invalid API key or OAuth access token");
1019
+ }
1020
+ if (res.status === 403 && (body.error === "insufficient_scope" || body.code === "insufficient_scope")) {
1021
+ const required = parseRequiredScopes(body.required, res.headers.get("WWW-Authenticate"));
1022
+ const msg = required.length > 0 ? `Missing OAuth scope${required.length > 1 ? "s" : ""}: ${required.join(", ")}` : "OAuth access token is missing a required scope";
1023
+ return new MantyxScopeError(msg, required);
951
1024
  }
952
1025
  return new MantyxError(body.error ?? `HTTP ${res.status}`, {
953
1026
  code: body.code ?? `http_${res.status}`,
@@ -1313,24 +1386,342 @@ function parseRunOutput(result, validator) {
1313
1386
  function sleep(ms) {
1314
1387
  return new Promise((r) => setTimeout(r, ms));
1315
1388
  }
1389
+ function resolveCredential(opts) {
1390
+ const apiKey = typeof opts.apiKey === "string" ? opts.apiKey : "";
1391
+ const accessToken = typeof opts.accessToken === "string" ? opts.accessToken : "";
1392
+ const tokenSource = typeof opts.tokenSource === "function" ? opts.tokenSource : null;
1393
+ const provided = [apiKey ? "apiKey" : "", accessToken ? "accessToken" : "", tokenSource ? "tokenSource" : ""].filter((s) => s.length > 0);
1394
+ if (provided.length > 1) {
1395
+ throw new MantyxError(
1396
+ `Pass exactly one of \`apiKey\`, \`accessToken\`, or \`tokenSource\` \u2014 got ${provided.join(" + ")}.`
1397
+ );
1398
+ }
1399
+ if (provided.length === 0) {
1400
+ throw new MantyxError(
1401
+ "One of `apiKey` (workspace API key), `accessToken` (OAuth access token), or `tokenSource` (dynamic credential provider) is required"
1402
+ );
1403
+ }
1404
+ return {
1405
+ credential: apiKey || accessToken,
1406
+ tokenSource
1407
+ };
1408
+ }
1409
+ function parseRequiredScopes(bodyRequired, wwwAuthenticate) {
1410
+ if (Array.isArray(bodyRequired)) {
1411
+ return bodyRequired.filter((s) => typeof s === "string" && s.length > 0);
1412
+ }
1413
+ if (typeof bodyRequired === "string" && bodyRequired.length > 0) {
1414
+ return [bodyRequired];
1415
+ }
1416
+ if (typeof wwwAuthenticate === "string") {
1417
+ const m = /scope="([^"]+)"/i.exec(wwwAuthenticate);
1418
+ if (m && m[1]) {
1419
+ return m[1].split(/\s+/).filter((s) => s.length > 0);
1420
+ }
1421
+ }
1422
+ return [];
1423
+ }
1424
+
1425
+ // src/oauth.ts
1426
+ var import_node_buffer = require("buffer");
1427
+ var import_node_crypto = require("crypto");
1428
+ var DEFAULT_OAUTH_BASE_URL = "https://app.mantyx.io";
1429
+ var DEFAULT_REFRESH_SKEW_MS = 6e4;
1430
+ var MantyxOAuthError = class extends MantyxError {
1431
+ oauthError;
1432
+ oauthErrorDescription;
1433
+ constructor(oauthError, oauthErrorDescription, status) {
1434
+ const message = oauthErrorDescription ? `OAuth ${oauthError}: ${oauthErrorDescription}` : `OAuth ${oauthError}`;
1435
+ super(message, { code: oauthError, status });
1436
+ this.name = "MantyxOAuthError";
1437
+ this.oauthError = oauthError;
1438
+ this.oauthErrorDescription = oauthErrorDescription;
1439
+ }
1440
+ };
1441
+ var MantyxOAuthClient = class {
1442
+ clientId;
1443
+ baseUrl;
1444
+ clientSecret;
1445
+ fetchImpl;
1446
+ timeoutMs;
1447
+ constructor(opts) {
1448
+ if (!opts.clientId) {
1449
+ throw new MantyxError("`clientId` is required for MantyxOAuthClient");
1450
+ }
1451
+ if (!opts.clientSecret) {
1452
+ throw new MantyxError("`clientSecret` is required for MantyxOAuthClient");
1453
+ }
1454
+ const f = opts.fetch ?? globalThis.fetch;
1455
+ if (typeof f !== "function") {
1456
+ throw new MantyxError(
1457
+ "Global fetch is not available; pass a custom `fetch` implementation in MantyxOAuthClientOptions."
1458
+ );
1459
+ }
1460
+ this.clientId = opts.clientId;
1461
+ this.clientSecret = opts.clientSecret;
1462
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_OAUTH_BASE_URL).replace(/\/+$/, "");
1463
+ this.fetchImpl = f;
1464
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
1465
+ }
1466
+ /**
1467
+ * Swap an authorization-code + PKCE verifier for the initial
1468
+ * `{access_token, refresh_token}` pair. Call this exactly once per
1469
+ * sign-in after the browser/native redirect lands back on your
1470
+ * `redirectUri` with a `code` parameter. Persist the returned
1471
+ * `refreshToken` against the user record — it is long-lived and
1472
+ * non-rotating per `docs/oauth.md` §"Token lifetimes & lifecycle".
1473
+ */
1474
+ async exchangeAuthorizationCode(opts) {
1475
+ return this.token({
1476
+ grant_type: "authorization_code",
1477
+ code: opts.code,
1478
+ redirect_uri: opts.redirectUri,
1479
+ code_verifier: opts.codeVerifier
1480
+ });
1481
+ }
1482
+ /**
1483
+ * Mint a fresh access token from a stored refresh token. The
1484
+ * returned `refreshToken` is identical to the input — the field is
1485
+ * surfaced for symmetry with {@link exchangeAuthorizationCode} only.
1486
+ *
1487
+ * On `400 invalid_grant` the refresh token has been revoked (or its
1488
+ * grant / app was deleted); the SDK surfaces a
1489
+ * {@link MantyxOAuthError} and callers must drive a fresh sign-in.
1490
+ */
1491
+ async refresh(opts) {
1492
+ if (!opts.refreshToken) {
1493
+ throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refresh");
1494
+ }
1495
+ const body = {
1496
+ grant_type: "refresh_token",
1497
+ refresh_token: opts.refreshToken
1498
+ };
1499
+ const scope = normalizeScope(opts.scope);
1500
+ if (scope !== void 0) body.scope = scope;
1501
+ return this.token(body);
1502
+ }
1503
+ /**
1504
+ * Request a workspace-scoped access token without a user via the
1505
+ * `client_credentials` grant. Available only on private OAuth apps
1506
+ * that were registered with `allowsClientCredentials: true`. No
1507
+ * refresh token is issued; re-call this method whenever a new
1508
+ * access token is needed.
1509
+ */
1510
+ async clientCredentials(opts = {}) {
1511
+ const body = {
1512
+ grant_type: "client_credentials"
1513
+ };
1514
+ const scope = normalizeScope(opts.scope);
1515
+ if (scope !== void 0) body.scope = scope;
1516
+ return this.token(body);
1517
+ }
1518
+ /**
1519
+ * Revoke an access or refresh token (RFC 7009). The server always
1520
+ * returns 200, even for unknown tokens. Revoking a **refresh**
1521
+ * token kills the refresh and every live access token tied to its
1522
+ * grant; revoking an **access** token kills only that one.
1523
+ */
1524
+ async revoke(opts) {
1525
+ if (!opts.token) {
1526
+ throw new MantyxError("`token` is required for MantyxOAuthClient.revoke");
1527
+ }
1528
+ await this.formPost("/api/oauth/revoke", {
1529
+ token: opts.token
1530
+ });
1531
+ }
1532
+ /**
1533
+ * Build a long-lived {@link TokenSource} that re-mints access
1534
+ * tokens from the supplied refresh token. Pass the returned source
1535
+ * to `new MantyxClient({ tokenSource, workspaceSlug, ... })`. The
1536
+ * source caches the access token in-memory and refreshes
1537
+ * proactively when the cached value is within `refreshSkewMs` of
1538
+ * `expiresAt`, or eagerly when `MantyxClient` reports a 401.
1539
+ */
1540
+ refreshTokenSource(opts) {
1541
+ if (!opts.refreshToken) {
1542
+ throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refreshTokenSource");
1543
+ }
1544
+ const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
1545
+ const cache = { token: opts.initialToken, inflight: null };
1546
+ const refreshToken = opts.refreshToken;
1547
+ return makeTokenSource(cache, skew, async () => {
1548
+ return this.refresh({ refreshToken, scope: opts.scope });
1549
+ });
1550
+ }
1551
+ /**
1552
+ * Build a long-lived {@link TokenSource} backed by the
1553
+ * `client_credentials` grant. On every refresh the source re-mints
1554
+ * a workspace-scoped access token by calling the token endpoint
1555
+ * with `grant_type=client_credentials`. Available only on private
1556
+ * apps with `allowsClientCredentials: true`.
1557
+ */
1558
+ clientCredentialsTokenSource(opts = {}) {
1559
+ const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
1560
+ const cache = { token: void 0, inflight: null };
1561
+ return makeTokenSource(cache, skew, async () => {
1562
+ return this.clientCredentials({ scope: opts.scope });
1563
+ });
1564
+ }
1565
+ // -------------------------------------------------------------- internals
1566
+ /**
1567
+ * POST `application/x-www-form-urlencoded` to `/api/oauth/token` and
1568
+ * decode the {@link OAuthToken} response. Always injects `client_id`
1569
+ * + `client_secret` from the constructor.
1570
+ */
1571
+ async token(body) {
1572
+ const res = await this.formPost("/api/oauth/token", body);
1573
+ let parsed = {};
1574
+ try {
1575
+ parsed = await res.json();
1576
+ } catch {
1577
+ throw new MantyxOAuthError(
1578
+ "invalid_response",
1579
+ "Token endpoint returned a non-JSON response",
1580
+ res.status
1581
+ );
1582
+ }
1583
+ const accessToken = typeof parsed.access_token === "string" ? parsed.access_token : "";
1584
+ if (!accessToken) {
1585
+ throw new MantyxOAuthError(
1586
+ "invalid_response",
1587
+ "Token endpoint response is missing `access_token`",
1588
+ res.status
1589
+ );
1590
+ }
1591
+ const expiresIn = typeof parsed.expires_in === "number" ? parsed.expires_in : 3600;
1592
+ return {
1593
+ accessToken,
1594
+ refreshToken: typeof parsed.refresh_token === "string" ? parsed.refresh_token : void 0,
1595
+ tokenType: typeof parsed.token_type === "string" ? parsed.token_type : "Bearer",
1596
+ expiresIn,
1597
+ expiresAt: Date.now() + expiresIn * 1e3,
1598
+ scope: typeof parsed.scope === "string" ? parsed.scope : void 0
1599
+ };
1600
+ }
1601
+ async formPost(path, body) {
1602
+ const url = `${this.baseUrl}${path}`;
1603
+ const params = new URLSearchParams({
1604
+ ...body,
1605
+ client_id: this.clientId,
1606
+ client_secret: this.clientSecret
1607
+ });
1608
+ const ctrl = new AbortController();
1609
+ const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
1610
+ let res;
1611
+ try {
1612
+ res = await this.fetchImpl(url, {
1613
+ method: "POST",
1614
+ headers: {
1615
+ "Content-Type": "application/x-www-form-urlencoded",
1616
+ Accept: "application/json"
1617
+ },
1618
+ body: params.toString(),
1619
+ signal: ctrl.signal
1620
+ });
1621
+ } catch (err) {
1622
+ if (ctrl.signal.aborted) {
1623
+ throw new MantyxNetworkError(`OAuth request timed out after ${this.timeoutMs}ms`);
1624
+ }
1625
+ throw new MantyxNetworkError(`OAuth network error: ${err.message}`, {
1626
+ cause: err
1627
+ });
1628
+ } finally {
1629
+ clearTimeout(t);
1630
+ }
1631
+ if (!res.ok) {
1632
+ let errBody = {};
1633
+ try {
1634
+ errBody = await res.json();
1635
+ } catch {
1636
+ }
1637
+ const oauthError = typeof errBody.error === "string" ? errBody.error : `http_${res.status}`;
1638
+ const desc = typeof errBody.error_description === "string" ? errBody.error_description : void 0;
1639
+ throw new MantyxOAuthError(oauthError, desc, res.status);
1640
+ }
1641
+ return res;
1642
+ }
1643
+ };
1644
+ function generatePkceVerifier(length = 64) {
1645
+ if (length < 43 || length > 128) {
1646
+ throw new MantyxError("PKCE code_verifier length must be in [43, 128]");
1647
+ }
1648
+ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
1649
+ const bytes = (0, import_node_crypto.randomBytes)(length);
1650
+ let out = "";
1651
+ for (let i = 0; i < length; i++) {
1652
+ out += ALPHABET[bytes[i] % ALPHABET.length];
1653
+ }
1654
+ return out;
1655
+ }
1656
+ function pkceChallenge(verifier) {
1657
+ const hash = (0, import_node_crypto.createHash)("sha256").update(verifier, "utf8").digest();
1658
+ return import_node_buffer.Buffer.from(hash).toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
1659
+ }
1660
+ function makeTokenSource(cache, skewMs, mint) {
1661
+ return async (reason = "initial") => {
1662
+ if (reason !== "unauthorized" && cache.token && !isExpiring(cache.token, skewMs)) {
1663
+ return cache.token.accessToken;
1664
+ }
1665
+ if (cache.inflight) {
1666
+ const t = await cache.inflight;
1667
+ if (reason === "unauthorized" && t === cache.token) {
1668
+ } else {
1669
+ return t.accessToken;
1670
+ }
1671
+ }
1672
+ cache.inflight = mint().then(
1673
+ (t) => {
1674
+ cache.token = t;
1675
+ return t;
1676
+ },
1677
+ (err) => {
1678
+ throw err;
1679
+ }
1680
+ );
1681
+ try {
1682
+ const t = await cache.inflight;
1683
+ return t.accessToken;
1684
+ } finally {
1685
+ cache.inflight = null;
1686
+ }
1687
+ };
1688
+ }
1689
+ function isExpiring(token, skewMs) {
1690
+ return token.expiresAt - Date.now() <= skewMs;
1691
+ }
1692
+ function normalizeScope(scope) {
1693
+ if (scope === void 0) return void 0;
1694
+ if (typeof scope === "string") {
1695
+ const trimmed = scope.trim();
1696
+ return trimmed.length > 0 ? trimmed : void 0;
1697
+ }
1698
+ const joined = scope.filter((s) => typeof s === "string" && s.length > 0).join(" ");
1699
+ return joined.length > 0 ? joined : void 0;
1700
+ }
1316
1701
 
1317
1702
  // src/version.ts
1318
- var SDK_VERSION = "0.9.1";
1703
+ var SDK_VERSION = "0.10.0";
1319
1704
  // Annotate the CommonJS export names for ESM import in node:
1320
1705
  0 && (module.exports = {
1321
1706
  AgentSession,
1322
1707
  DEFAULT_BASE_URL,
1708
+ DEFAULT_OAUTH_BASE_URL,
1709
+ DEFAULT_REFRESH_SKEW_MS,
1323
1710
  MantyxAuthError,
1324
1711
  MantyxClient,
1325
1712
  MantyxError,
1326
1713
  MantyxNetworkError,
1714
+ MantyxOAuthClient,
1715
+ MantyxOAuthError,
1327
1716
  MantyxParseError,
1328
1717
  MantyxRunError,
1718
+ MantyxScopeError,
1329
1719
  MantyxToolError,
1330
1720
  SDK_VERSION,
1331
1721
  defineLocalA2A,
1332
1722
  defineLocalMcp,
1333
1723
  defineLocalTool,
1724
+ generatePkceVerifier,
1334
1725
  isLocalA2ATool,
1335
1726
  isLocalMcpServer,
1336
1727
  isLocalTool,
@@ -1339,6 +1730,7 @@ var SDK_VERSION = "0.9.1";
1339
1730
  mantyxPluginTool,
1340
1731
  mantyxTool,
1341
1732
  parseRunOutput,
1733
+ pkceChallenge,
1342
1734
  readSseStream,
1343
1735
  toToolParametersWire,
1344
1736
  zodToJsonSchema