@letta-ai/letta-agent-sdk 0.8.53 → 0.8.55

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
@@ -10310,27 +10310,6 @@ The MemFS is a git-backed projection of your memory. Changes affect your future
10310
10310
 
10311
10311
  **Editing memory does NOT change your behavior in the current turn.** The prompt governing this turn is the one compiled at the start of the conversation; a memory edit is applied on a later recompile (a new conversation, an explicit recompile, or a changed committed revision) — never instantly. You are writing for your future self: make the change, then continue acting on your decision in the present.
10312
10312
 
10313
- You can always read and search memory directly. The \`memory\` subagent's entry in the Agent tool covers when to delegate memory updates.
10314
-
10315
- ### Editing memory directly
10316
-
10317
- When memory is the main task, inspect Git status and read the current files before editing. Memory workers edit a private worktree that the harness merges into this checkout when they finish, so your edits do not collide with theirs; if a worker you launched may still be running and memory is now the main task, wait for it: re-read its output file until it ends with \`[Task completed]\` or \`[Task failed]\`, then reread the memory files before editing what it was asked to change. Automatic conflict repair and reflection integration may also commit to this checkout while you work, so stage only the files you changed by explicit path (never \`git add -A\` or \`git add .\`) and commit them in the same shell call.
10318
-
10319
- Memory Markdown files must start with YAML frontmatter containing a non-empty \`description:\` field. Preserve existing frontmatter and discovery links. The MemFS pre-commit hook validates frontmatter and prevents changes to protected \`read_only\` files. Skill \`SKILL.md\` files use their own skill frontmatter format.
10320
-
10321
- Review the diff, stage only the intended files, and commit only those paths, so anything else already staged in the checkout stays out of your commit. Preserve unrelated changes and protected read-only files. Use a non-empty author name fallback if \`$AGENT_NAME\` is unavailable:
10322
-
10323
- \`\`\`bash
10324
- cd "$MEMORY_DIR"
10325
- git status
10326
- git diff
10327
- git add <specific files>
10328
- author_name="\${AGENT_NAME:-$AGENT_ID}"
10329
- git commit --author="$author_name <$AGENT_ID@letta.com>" -m "<type>: <what changed>" -- <specific files>
10330
- \`\`\`
10331
-
10332
- Verify the committed changes before claiming they are saved. Use \`git -C "$MEMORY_DIR" log --oneline\` to inspect memory history. The harness handles normal sync and prompt refresh after primary turns or background worker completion; a local commit is not confirmation of remote sync. Reflection continues independently.
10333
-
10334
10313
  ## Learning from mistakes
10335
10314
 
10336
10315
  You are a long-lived stateful agent expected to operate over a long (potentially infinite) time horizon. Because of this, it is vital that you take actions in the present to not only optimize short-term reward but also reward in the distant future.
@@ -10510,27 +10489,6 @@ The MemFS is a git-backed projection of your memory. Changes affect your future
10510
10489
 
10511
10490
  **Editing memory does NOT change your behavior in the current turn.** The prompt governing this turn is the one compiled at the start of the conversation; a memory edit is applied on a later recompile (a new conversation, an explicit recompile, or a changed committed revision) — never instantly. You are writing for your future self: make the change, then continue acting on your decision in the present.
10512
10491
 
10513
- You can always read and search memory directly. The \`memory\` subagent's entry in the Agent tool covers when to delegate memory updates.
10514
-
10515
- ### Editing memory directly
10516
-
10517
- When memory is the main task, inspect Git status and read the current files before editing. Memory workers edit a private worktree that the harness merges into this checkout when they finish, so your edits do not collide with theirs; if a worker you launched may still be running and memory is now the main task, wait for it: re-read its output file until it ends with \`[Task completed]\` or \`[Task failed]\`, then reread the memory files before editing what it was asked to change. Automatic conflict repair and reflection integration may also commit to this checkout while you work, so stage only the files you changed by explicit path (never \`git add -A\` or \`git add .\`) and commit them in the same shell call.
10518
-
10519
- Memory Markdown files must start with YAML frontmatter containing a non-empty \`description:\` field. Preserve existing frontmatter and discovery links. The MemFS pre-commit hook validates frontmatter and prevents changes to protected \`read_only\` files. Skill \`SKILL.md\` files use their own skill frontmatter format.
10520
-
10521
- Review the diff, stage only the intended files, and commit only those paths, so anything else already staged in the checkout stays out of your commit. Preserve unrelated changes and protected read-only files. Use a non-empty author name fallback if \`$AGENT_NAME\` is unavailable:
10522
-
10523
- \`\`\`bash
10524
- cd "$MEMORY_DIR"
10525
- git status
10526
- git diff
10527
- git add <specific files>
10528
- author_name="\${AGENT_NAME:-$AGENT_ID}"
10529
- git commit --author="$author_name <$AGENT_ID@letta.com>" -m "<type>: <what changed>" -- <specific files>
10530
- \`\`\`
10531
-
10532
- Verify the committed changes before claiming they are saved. Use \`git -C "$MEMORY_DIR" log --oneline\` to inspect memory history. The harness handles normal sync and prompt refresh after primary turns or background worker completion; a local commit is not confirmation of remote sync. Reflection continues independently.
10533
-
10534
10492
  ## Learning from mistakes
10535
10493
 
10536
10494
  You are a long-lived stateful agent expected to operate over a long (potentially infinite) time horizon. Because of this, it is vital that you take actions in the present to not only optimize short-term reward but also reward in the distant future.
@@ -10860,27 +10818,6 @@ The MemFS is a git-backed projection of your memory. Changes affect your future
10860
10818
 
10861
10819
  **Editing memory does NOT change your behavior in the current turn.** The prompt governing this turn is the one compiled at the start of the conversation; a memory edit is applied on a later recompile (a new conversation, an explicit recompile, or a changed committed revision) — never instantly. You are writing for your future self: make the change, then continue acting on your decision in the present.
10862
10820
 
10863
- You can always read and search memory directly. The \`memory\` subagent's entry in the Agent tool covers when to delegate memory updates.
10864
-
10865
- ### Editing memory directly
10866
-
10867
- When memory is the main task, inspect Git status and read the current files before editing. Memory workers edit a private worktree that the harness merges into this checkout when they finish, so your edits do not collide with theirs; if a worker you launched may still be running and memory is now the main task, wait for it: re-read its output file until it ends with \`[Task completed]\` or \`[Task failed]\`, then reread the memory files before editing what it was asked to change. Automatic conflict repair and reflection integration may also commit to this checkout while you work, so stage only the files you changed by explicit path (never \`git add -A\` or \`git add .\`) and commit them in the same shell call.
10868
-
10869
- Preserve the root layout: root and child \`MEMORY.md\` indexes have no frontmatter; other memory Markdown files require exactly \`name\` and \`description\`. Keep discovery links up to date. Skill \`SKILL.md\` files use their own skill frontmatter format. Obey the repository's validation hooks.
10870
-
10871
- Review the diff, stage only the intended files, and commit only those paths, so anything else already staged in the checkout stays out of your commit. Preserve unrelated changes and protected read-only files. Use a non-empty author name fallback if \`$AGENT_NAME\` is unavailable:
10872
-
10873
- \`\`\`bash
10874
- cd "$MEMORY_DIR"
10875
- git status
10876
- git diff
10877
- git add <specific files>
10878
- author_name="\${AGENT_NAME:-$AGENT_ID}"
10879
- git commit --author="$author_name <$AGENT_ID@letta.com>" -m "<type>: <what changed>" -- <specific files>
10880
- \`\`\`
10881
-
10882
- Verify the committed changes before claiming they are saved. Use \`git -C "$MEMORY_DIR" log --oneline\` to inspect memory history. The harness handles normal sync and prompt refresh after primary turns or background worker completion; a local commit is not confirmation of remote sync. Reflection continues independently.
10883
-
10884
10821
  ## Learning from mistakes
10885
10822
 
10886
10823
  You are a long-lived stateful agent expected to operate over a long (potentially infinite) time horizon. Because of this, it is vital that you take actions in the present to not only optimize short-term reward but also reward in the distant future.
@@ -31426,7 +31363,8 @@ var OAuthTokensSchema = object({
31426
31363
  token_type: string2(),
31427
31364
  expires_in: exports_coerce.number().optional(),
31428
31365
  scope: string2().optional(),
31429
- refresh_token: string2().optional()
31366
+ refresh_token: string2().optional(),
31367
+ issuer: string2().optional().catch(undefined)
31430
31368
  }).strip();
31431
31369
  var OAuthErrorResponseSchema = object({
31432
31370
  error: string2(),
@@ -31458,7 +31396,8 @@ var OAuthClientInformationSchema = object({
31458
31396
  client_id: string2(),
31459
31397
  client_secret: string2().optional(),
31460
31398
  client_id_issued_at: number2().optional(),
31461
- client_secret_expires_at: number2().optional()
31399
+ client_secret_expires_at: number2().optional(),
31400
+ issuer: string2().optional().catch(undefined)
31462
31401
  }).strip();
31463
31402
  var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
31464
31403
  var OAuthClientRegistrationErrorSchema = object({
@@ -31595,6 +31534,92 @@ var OAUTH_ERRORS = {
31595
31534
  [InsufficientScopeError.errorCode]: InsufficientScopeError,
31596
31535
  [InvalidTargetError.errorCode]: InvalidTargetError
31597
31536
  };
31537
+ function normalizeHeaders(headers) {
31538
+ if (!headers)
31539
+ return {};
31540
+ if (headers instanceof Headers) {
31541
+ return Object.fromEntries(headers.entries());
31542
+ }
31543
+ if (Array.isArray(headers)) {
31544
+ return Object.fromEntries(headers);
31545
+ }
31546
+ return { ...headers };
31547
+ }
31548
+ function createFetchWithInit(baseFetch = fetch, baseInit) {
31549
+ if (!baseInit) {
31550
+ return baseFetch;
31551
+ }
31552
+ return async (url2, init) => {
31553
+ const mergedInit = {
31554
+ ...baseInit,
31555
+ ...init,
31556
+ headers: init?.headers ? { ...normalizeHeaders(baseInit.headers), ...normalizeHeaders(init.headers) } : baseInit.headers
31557
+ };
31558
+ return baseFetch(url2, mergedInit);
31559
+ };
31560
+ }
31561
+ var MAX_REDIRECTS = 5;
31562
+ var REDIRECT_STATUSES = [301, 302, 303, 307, 308];
31563
+ function isWithinOrigin(from, to) {
31564
+ if (from.protocol === to.protocol && from.hostname === to.hostname && from.port === to.port) {
31565
+ return true;
31566
+ }
31567
+ return from.hostname === to.hostname && from.protocol === "http:" && from.port === "" && to.protocol === "https:" && to.port === "";
31568
+ }
31569
+ function redirectTarget(response, requestUrl) {
31570
+ const location = REDIRECT_STATUSES.includes(response.status) ? response.headers.get("location") : null;
31571
+ try {
31572
+ return location ? new URL(location, requestUrl) : undefined;
31573
+ } catch {
31574
+ return;
31575
+ }
31576
+ }
31577
+ function followWithinOrigin(baseFetch, url2, init, response, followed = 0) {
31578
+ const target = redirectTarget(response, url2);
31579
+ const keepsMethod = response.status === 307 || response.status === 308 || (init?.method ?? "GET").toUpperCase() === "GET";
31580
+ if (!target || followed === MAX_REDIRECTS || !keepsMethod) {
31581
+ return;
31582
+ }
31583
+ const from = new URL(url2);
31584
+ const addsUserinfo = (target.username || target.password) && (target.username !== from.username || target.password !== from.password);
31585
+ if (addsUserinfo || !isWithinOrigin(from, target)) {
31586
+ return;
31587
+ }
31588
+ return Promise.resolve(response.body?.cancel()).then(() => baseFetch(target, { ...init, redirect: "manual" })).then((next) => followWithinOrigin(baseFetch, target, init, next, followed + 1) ?? next);
31589
+ }
31590
+ var followingFetches = new WeakSet;
31591
+ function fetchLeavingRedirects(baseFetch) {
31592
+ const following = (url2, init) => (baseFetch ?? fetch)(url2, init);
31593
+ followingFetches.add(following);
31594
+ return following;
31595
+ }
31596
+ function fetchWithinOrigin(baseFetch = fetch) {
31597
+ if (followingFetches.has(baseFetch)) {
31598
+ return baseFetch;
31599
+ }
31600
+ return (url2, init) => {
31601
+ if (init?.redirect === "error" || init?.redirect === "manual") {
31602
+ return baseFetch(url2, init);
31603
+ }
31604
+ return Promise.resolve(baseFetch(url2, { ...init, redirect: "manual" })).then((response) => followWithinOrigin(baseFetch, url2, init, response) ?? response);
31605
+ };
31606
+ }
31607
+ function unfollowedRedirect(response, requestUrl) {
31608
+ if (response.type === "opaqueredirect") {
31609
+ return "Redirect not followed: this runtime does not expose the redirect target";
31610
+ }
31611
+ const from = response.url || requestUrl;
31612
+ const target = redirectTarget(response, from);
31613
+ if (!target) {
31614
+ return;
31615
+ }
31616
+ target.username = target.password = target.search = target.hash = "";
31617
+ if (target.protocol === "http:" && new URL(from).protocol === "https:") {
31618
+ target.protocol = "https:";
31619
+ return `Redirect to plain http not followed; try ${target.href} instead`;
31620
+ }
31621
+ return `Redirect to ${target.href} not followed`;
31622
+ }
31598
31623
 
31599
31624
  class UnauthorizedError extends Error {
31600
31625
  constructor(message) {
@@ -31666,10 +31691,30 @@ async function parseErrorResponse(input) {
31666
31691
  const errorClass = OAUTH_ERRORS[error2] || ServerError;
31667
31692
  return new errorClass(error_description || "", error_uri);
31668
31693
  } catch (error2) {
31669
- const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}Invalid OAuth error response: ${error2}. Raw body: ${body}`;
31694
+ const redirect = input instanceof Response ? unfollowedRedirect(input, input.url) : undefined;
31695
+ const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}${redirect ?? `Invalid OAuth error response: ${error2}. Raw body: ${body}`}`;
31670
31696
  return new ServerError(errorMessage);
31671
31697
  }
31672
31698
  }
31699
+ function issuersMatch(a, b) {
31700
+ let [x, y] = [a, b];
31701
+ try {
31702
+ [x, y] = [new URL(a).href, new URL(b).href];
31703
+ } catch {}
31704
+ return x === y || x.endsWith("/") && x.slice(0, -1) === y || y.endsWith("/") && y.slice(0, -1) === x;
31705
+ }
31706
+ function discardIfIssuerMismatch(stored, issuer) {
31707
+ if (!stored)
31708
+ return;
31709
+ if (typeof stored.issuer !== "string")
31710
+ return stored.issuer == null ? stored : { ...stored, issuer: undefined };
31711
+ return issuersMatch(stored.issuer, issuer) ? stored : undefined;
31712
+ }
31713
+ var TokenResponseSchema = OAuthTokensSchema.omit({ issuer: true });
31714
+ var RegistrationResponseSchema = OAuthClientInformationFullSchema.omit({ issuer: true });
31715
+ function boundElsewhereError(stored, issuer) {
31716
+ return new Error(`OAuth client information is bound to authorization server ${stored.issuer} and is not presented to ${issuer}. Clear the stored client information, or correct \`expectedIssuer\`, if the authorization server has moved.`);
31717
+ }
31673
31718
  async function auth(provider, options) {
31674
31719
  try {
31675
31720
  return await authInternal(provider, options);
@@ -31722,9 +31767,23 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
31722
31767
  authorizationServerMetadata: metadata
31723
31768
  });
31724
31769
  }
31725
- const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
31770
+ const issuer = String(authorizationServerUrl);
31771
+ const selectedResource = await selectResourceURL(serverUrl, provider, resourceMetadata);
31772
+ const resource = selectedResource && resourceMetadata && !provider.validateResourceURL ? resourceMetadata.resource : selectedResource;
31726
31773
  const resolvedScope = scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope;
31727
- let clientInformation = await Promise.resolve(provider.clientInformation());
31774
+ const storedClientInformation = await Promise.resolve(provider.clientInformation());
31775
+ let clientInformation = discardIfIssuerMismatch(storedClientInformation, issuer);
31776
+ const canRegisterAgain = provider.saveClientInformation !== undefined && !!provider.redirectUrl;
31777
+ if (storedClientInformation && !clientInformation && !canRegisterAgain) {
31778
+ throw boundElsewhereError(storedClientInformation, issuer);
31779
+ }
31780
+ const unstampedClientInformation = clientInformation?.issuer == null ? clientInformation : undefined;
31781
+ const bindClientInformation = async () => {
31782
+ try {
31783
+ if (unstampedClientInformation)
31784
+ await provider.saveClientInformation?.({ ...unstampedClientInformation, issuer });
31785
+ } catch {}
31786
+ };
31728
31787
  if (!clientInformation) {
31729
31788
  if (authorizationCode !== undefined) {
31730
31789
  throw new Error("Existing OAuth client information is required when exchanging an authorization code");
@@ -31736,9 +31795,7 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
31736
31795
  }
31737
31796
  const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
31738
31797
  if (shouldUseUrlBasedClientId) {
31739
- clientInformation = {
31740
- client_id: clientMetadataUrl
31741
- };
31798
+ clientInformation = { client_id: clientMetadataUrl, issuer };
31742
31799
  await provider.saveClientInformation?.(clientInformation);
31743
31800
  } else {
31744
31801
  if (!provider.saveClientInformation) {
@@ -31750,8 +31807,8 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
31750
31807
  scope: resolvedScope,
31751
31808
  fetchFn
31752
31809
  });
31753
- await provider.saveClientInformation(fullInformation);
31754
- clientInformation = fullInformation;
31810
+ clientInformation = { ...fullInformation, issuer };
31811
+ await provider.saveClientInformation(clientInformation);
31755
31812
  }
31756
31813
  }
31757
31814
  const nonInteractiveFlow = !provider.redirectUrl;
@@ -31762,10 +31819,14 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
31762
31819
  authorizationCode,
31763
31820
  fetchFn
31764
31821
  });
31765
- await provider.saveTokens(tokens2);
31822
+ await bindClientInformation();
31823
+ await provider.saveTokens({ ...tokens2, issuer });
31766
31824
  return "AUTHORIZED";
31767
31825
  }
31768
- const tokens = await provider.tokens();
31826
+ const tokens = discardIfIssuerMismatch(await provider.tokens(), issuer);
31827
+ if (tokens?.refresh_token && tokens.issuer == null) {
31828
+ console.warn("[mcp-sdk] stored OAuth tokens have no 'issuer' property (saved by an earlier version, or by a provider that does not keep it) and are used as-is; make sure your OAuthClientProvider stores what saveTokens() and saveClientInformation() receive unchanged.");
31829
+ }
31769
31830
  if (tokens?.refresh_token) {
31770
31831
  try {
31771
31832
  const newTokens = await refreshAuthorization(authorizationServerUrl, {
@@ -31776,7 +31837,8 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
31776
31837
  addClientAuthentication: provider.addClientAuthentication,
31777
31838
  fetchFn
31778
31839
  });
31779
- await provider.saveTokens(newTokens);
31840
+ await bindClientInformation();
31841
+ await provider.saveTokens({ ...newTokens, issuer });
31780
31842
  return "AUTHORIZED";
31781
31843
  } catch (error2) {
31782
31844
  if (!(error2 instanceof OAuthError) || error2 instanceof ServerError) {} else {
@@ -31873,7 +31935,7 @@ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn =
31873
31935
  }
31874
31936
  async function fetchWithCorsRetry(url2, headers, fetchFn = fetch) {
31875
31937
  try {
31876
- return await fetchFn(url2, { headers });
31938
+ return await fetchWithinOrigin(fetchFn)(url2, { headers });
31877
31939
  } catch (error2) {
31878
31940
  if (error2 instanceof TypeError) {
31879
31941
  if (headers) {
@@ -31898,7 +31960,7 @@ async function tryMetadataDiscovery(url2, protocolVersion, fetchFn = fetch) {
31898
31960
  return await fetchWithCorsRetry(url2, headers, fetchFn);
31899
31961
  }
31900
31962
  function shouldAttemptFallback(response, pathname) {
31901
- return !response || response.status >= 400 && response.status < 500 && pathname !== "/";
31963
+ return !response || !response.ok && response.status < 500 && pathname !== "/";
31902
31964
  }
31903
31965
  async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
31904
31966
  const issuer = new URL(serverUrl);
@@ -31964,7 +32026,7 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
31964
32026
  }
31965
32027
  if (!response.ok) {
31966
32028
  await response.body?.cancel();
31967
- if (response.status >= 400 && response.status < 500) {
32029
+ if (response.status < 500) {
31968
32030
  continue;
31969
32031
  }
31970
32032
  throw new Error(`HTTP ${response.status} trying to load ${type === "oauth" ? "OAuth" : "OpenID provider"} metadata from ${endpointUrl}`);
@@ -31996,6 +32058,9 @@ async function discoverOAuthServerInfo(serverUrl, opts) {
31996
32058
  resourceMetadata
31997
32059
  };
31998
32060
  }
32061
+ function resourceIndicatorToString(resource) {
32062
+ return typeof resource === "string" ? resource : resource.href;
32063
+ }
31999
32064
  async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
32000
32065
  let authorizationUrl;
32001
32066
  if (metadata) {
@@ -32027,7 +32092,7 @@ async function startAuthorization(authorizationServerUrl, { metadata, clientInfo
32027
32092
  authorizationUrl.searchParams.append("prompt", "consent");
32028
32093
  }
32029
32094
  if (resource) {
32030
- authorizationUrl.searchParams.set("resource", resource.href);
32095
+ authorizationUrl.searchParams.set("resource", resourceIndicatorToString(resource));
32031
32096
  }
32032
32097
  return { authorizationUrl, codeVerifier };
32033
32098
  }
@@ -32046,7 +32111,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
32046
32111
  Accept: "application/json"
32047
32112
  });
32048
32113
  if (resource) {
32049
- tokenRequestParams.set("resource", resource.href);
32114
+ tokenRequestParams.set("resource", resourceIndicatorToString(resource));
32050
32115
  }
32051
32116
  if (addClientAuthentication) {
32052
32117
  await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
@@ -32055,7 +32120,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
32055
32120
  const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
32056
32121
  applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
32057
32122
  }
32058
- const response = await (fetchFn ?? fetch)(tokenUrl, {
32123
+ const response = await fetchWithinOrigin(fetchFn ?? fetch)(tokenUrl, {
32059
32124
  method: "POST",
32060
32125
  headers,
32061
32126
  body: tokenRequestParams
@@ -32063,7 +32128,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
32063
32128
  if (!response.ok) {
32064
32129
  throw await parseErrorResponse(response);
32065
32130
  }
32066
- return OAuthTokensSchema.parse(await response.json());
32131
+ return TokenResponseSchema.parse(await response.json());
32067
32132
  }
32068
32133
  async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
32069
32134
  const tokenRequestParams = new URLSearchParams({
@@ -32081,6 +32146,15 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
32081
32146
  return { refresh_token: refreshToken, ...tokens };
32082
32147
  }
32083
32148
  async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
32149
+ const readClientInformation = async () => {
32150
+ const storedClientInformation = await provider.clientInformation();
32151
+ const checked = discardIfIssuerMismatch(storedClientInformation, String(authorizationServerUrl));
32152
+ if (storedClientInformation && !checked) {
32153
+ throw boundElsewhereError(storedClientInformation, String(authorizationServerUrl));
32154
+ }
32155
+ return checked;
32156
+ };
32157
+ let clientInformation = await readClientInformation();
32084
32158
  const scope = provider.clientMetadata.scope;
32085
32159
  let tokenRequestParams;
32086
32160
  if (provider.prepareTokenRequest) {
@@ -32096,11 +32170,11 @@ async function fetchToken(provider, authorizationServerUrl, { metadata, resource
32096
32170
  const codeVerifier = await provider.codeVerifier();
32097
32171
  tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
32098
32172
  }
32099
- const clientInformation = await provider.clientInformation();
32173
+ clientInformation ?? (clientInformation = await readClientInformation());
32100
32174
  return executeTokenRequest(authorizationServerUrl, {
32101
32175
  metadata,
32102
32176
  tokenRequestParams,
32103
- clientInformation: clientInformation ?? undefined,
32177
+ clientInformation,
32104
32178
  addClientAuthentication: provider.addClientAuthentication,
32105
32179
  resource,
32106
32180
  fetchFn
@@ -32116,7 +32190,7 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
32116
32190
  } else {
32117
32191
  registrationUrl = new URL("/register", authorizationServerUrl);
32118
32192
  }
32119
- const response = await (fetchFn ?? fetch)(registrationUrl, {
32193
+ const response = await fetchWithinOrigin(fetchFn ?? fetch)(registrationUrl, {
32120
32194
  method: "POST",
32121
32195
  headers: {
32122
32196
  "Content-Type": "application/json"
@@ -32129,7 +32203,7 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
32129
32203
  if (!response.ok) {
32130
32204
  throw await parseErrorResponse(response);
32131
32205
  }
32132
- return OAuthClientInformationFullSchema.parse(await response.json());
32206
+ return RegistrationResponseSchema.parse(await response.json());
32133
32207
  }
32134
32208
  function isZ4Schema(s) {
32135
32209
  const schema = s;
@@ -32253,6 +32327,9 @@ class Protocol {
32253
32327
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
32254
32328
  const handleTaskResult = async () => {
32255
32329
  const taskId = request.params.taskId;
32330
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
32331
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
32332
+ }
32256
32333
  if (this._taskMessageQueue) {
32257
32334
  let queuedMessage;
32258
32335
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -32283,12 +32360,12 @@ class Protocol {
32283
32360
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
32284
32361
  }
32285
32362
  if (!isTerminal(task.status)) {
32286
- await this._waitForTaskUpdate(taskId, extra.signal);
32363
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
32287
32364
  return await handleTaskResult();
32288
32365
  }
32289
32366
  if (isTerminal(task.status)) {
32290
32367
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
32291
- this._clearTaskQueue(taskId);
32368
+ this._clearTaskQueue(taskId, extra.sessionId);
32292
32369
  return {
32293
32370
  ...result,
32294
32371
  _meta: {
@@ -32325,7 +32402,7 @@ class Protocol {
32325
32402
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
32326
32403
  }
32327
32404
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
32328
- this._clearTaskQueue(request.params.taskId);
32405
+ this._clearTaskQueue(request.params.taskId, extra.sessionId);
32329
32406
  const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
32330
32407
  if (!cancelledTask) {
32331
32408
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
@@ -32448,6 +32525,19 @@ class Protocol {
32448
32525
  const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
32449
32526
  const capturedTransport = this._transport;
32450
32527
  const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
32528
+ const sessionId = capturedTransport?.sessionId;
32529
+ const store = this._taskStore;
32530
+ let relatedTaskFound = true;
32531
+ let relatedTaskLookup;
32532
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== undefined) {
32533
+ relatedTaskFound = false;
32534
+ relatedTaskLookup = (async () => {
32535
+ if (!await store.getTask(relatedTaskId, sessionId)) {
32536
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
32537
+ }
32538
+ relatedTaskFound = true;
32539
+ })();
32540
+ }
32451
32541
  if (handler === undefined) {
32452
32542
  const errorResponse = {
32453
32543
  jsonrpc: "2.0",
@@ -32457,7 +32547,10 @@ class Protocol {
32457
32547
  message: "Method not found"
32458
32548
  }
32459
32549
  };
32460
- if (relatedTaskId && this._taskMessageQueue) {
32550
+ if (relatedTaskId && relatedTaskLookup) {
32551
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
32552
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error2) => this._onerror(new Error(`Failed to send an error response: ${error2}`)));
32553
+ } else if (relatedTaskId && this._taskMessageQueue) {
32461
32554
  this._enqueueTaskMessage(relatedTaskId, {
32462
32555
  type: "error",
32463
32556
  message: errorResponse,
@@ -32508,7 +32601,10 @@ class Protocol {
32508
32601
  closeSSEStream: extra?.closeSSEStream,
32509
32602
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
32510
32603
  };
32511
- Promise.resolve().then(() => {
32604
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
32605
+ if (relatedTaskLookup && abortController.signal.aborted) {
32606
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
32607
+ }
32512
32608
  if (taskCreationParams) {
32513
32609
  this.assertTaskHandlerCapability(request.method);
32514
32610
  }
@@ -32543,7 +32639,7 @@ class Protocol {
32543
32639
  ...error2["data"] !== undefined && { data: error2["data"] }
32544
32640
  }
32545
32641
  };
32546
- if (relatedTaskId && this._taskMessageQueue) {
32642
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
32547
32643
  await this._enqueueTaskMessage(relatedTaskId, {
32548
32644
  type: "error",
32549
32645
  message: errorResponse,
@@ -32931,7 +33027,7 @@ class Protocol {
32931
33027
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
32932
33028
  }
32933
33029
  const maxQueueSize = this._options?.maxTaskQueueSize;
32934
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
33030
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
32935
33031
  }
32936
33032
  async _clearTaskQueue(taskId, sessionId) {
32937
33033
  if (this._taskMessageQueue) {
@@ -32950,10 +33046,10 @@ class Protocol {
32950
33046
  }
32951
33047
  }
32952
33048
  }
32953
- async _waitForTaskUpdate(taskId, signal) {
33049
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
32954
33050
  let interval = this._options?.defaultTaskPollInterval ?? 1000;
32955
33051
  try {
32956
- const task = await this._taskStore?.getTask(taskId);
33052
+ const task = await this._taskStore?.getTask(taskId, sessionId);
32957
33053
  if (task?.pollInterval) {
32958
33054
  interval = task.pollInterval;
32959
33055
  }
@@ -33999,30 +34095,6 @@ function getBaseURL() {
33999
34095
  const doc2 = "document" in globalThis ? globalThis.document : undefined;
34000
34096
  return doc2 && typeof doc2 == "object" && "baseURI" in doc2 && typeof doc2.baseURI == "string" ? doc2.baseURI : undefined;
34001
34097
  }
34002
- function normalizeHeaders(headers) {
34003
- if (!headers)
34004
- return {};
34005
- if (headers instanceof Headers) {
34006
- return Object.fromEntries(headers.entries());
34007
- }
34008
- if (Array.isArray(headers)) {
34009
- return Object.fromEntries(headers);
34010
- }
34011
- return { ...headers };
34012
- }
34013
- function createFetchWithInit(baseFetch = fetch, baseInit) {
34014
- if (!baseInit) {
34015
- return baseFetch;
34016
- }
34017
- return async (url2, init) => {
34018
- const mergedInit = {
34019
- ...baseInit,
34020
- ...init,
34021
- headers: init?.headers ? { ...normalizeHeaders(baseInit.headers), ...normalizeHeaders(init.headers) } : baseInit.headers
34022
- };
34023
- return baseFetch(url2, mergedInit);
34024
- };
34025
- }
34026
34098
 
34027
34099
  class SseError extends Error {
34028
34100
  constructor(code, message, event) {
@@ -34042,6 +34114,15 @@ class SSEClientTransport {
34042
34114
  this._authProvider = opts?.authProvider;
34043
34115
  this._fetch = opts?.fetch;
34044
34116
  this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
34117
+ this._followRedirects = opts?.redirectPolicy === "follow";
34118
+ if (this._followRedirects) {
34119
+ this._fetch = fetchLeavingRedirects(this._fetch);
34120
+ this._fetchWithInit = fetchLeavingRedirects(this._fetchWithInit);
34121
+ }
34122
+ }
34123
+ _unfollowedRedirect(response, url2) {
34124
+ const text = unfollowedRedirect(response, url2);
34125
+ return text && !this._followRedirects ? `${text} (redirectPolicy: 'same-origin')` : text;
34045
34126
  }
34046
34127
  async _authThenStart() {
34047
34128
  if (!this._authProvider) {
@@ -34082,7 +34163,9 @@ class SSEClientTransport {
34082
34163
  });
34083
34164
  }
34084
34165
  _startOrAuth() {
34085
- const fetchImpl = this?._eventSourceInit?.fetch ?? this._fetch ?? fetch;
34166
+ const baseFetch = this?._eventSourceInit?.fetch ?? this._fetch ?? fetch;
34167
+ const fetchImpl = this._followRedirects ? baseFetch : fetchWithinOrigin(baseFetch);
34168
+ let redirect;
34086
34169
  return new Promise((resolve3, reject) => {
34087
34170
  this._eventSource = new EventSource(this._url.href, {
34088
34171
  ...this._eventSourceInit,
@@ -34093,6 +34176,7 @@ class SSEClientTransport {
34093
34176
  ...init,
34094
34177
  headers
34095
34178
  });
34179
+ redirect = this._unfollowedRedirect(response, url2);
34096
34180
  if (response.status === 401 && response.headers.has("www-authenticate")) {
34097
34181
  const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
34098
34182
  this._resourceMetadataUrl = resourceMetadataUrl;
@@ -34107,7 +34191,7 @@ class SSEClientTransport {
34107
34191
  this._authThenStart().then(resolve3, reject);
34108
34192
  return;
34109
34193
  }
34110
- const error2 = new SseError(event.code, event.message, event);
34194
+ const error2 = new SseError(event.code, redirect ?? event.message, event);
34111
34195
  reject(error2);
34112
34196
  this.onerror?.(error2);
34113
34197
  };
@@ -34180,7 +34264,7 @@ class SSEClientTransport {
34180
34264
  body: JSON.stringify(message),
34181
34265
  signal: this._abortController?.signal
34182
34266
  };
34183
- const response = await (this._fetch ?? fetch)(this._endpoint, init);
34267
+ const response = await fetchWithinOrigin(this._fetch ?? fetch)(this._endpoint, init);
34184
34268
  if (!response.ok) {
34185
34269
  const text = await response.text().catch(() => null);
34186
34270
  if (response.status === 401 && this._authProvider) {
@@ -34198,7 +34282,7 @@ class SSEClientTransport {
34198
34282
  }
34199
34283
  return this.send(message);
34200
34284
  }
34201
- throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
34285
+ throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${this._unfollowedRedirect(response, this._endpoint) ?? text}`);
34202
34286
  }
34203
34287
  await response.body?.cancel();
34204
34288
  } catch (error2) {
@@ -34459,9 +34543,18 @@ class StreamableHTTPClientTransport {
34459
34543
  this._authProvider = opts?.authProvider;
34460
34544
  this._fetch = opts?.fetch;
34461
34545
  this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
34546
+ this._followRedirects = opts?.redirectPolicy === "follow";
34547
+ if (this._followRedirects) {
34548
+ this._fetch = fetchLeavingRedirects(this._fetch);
34549
+ this._fetchWithInit = fetchLeavingRedirects(this._fetchWithInit);
34550
+ }
34462
34551
  this._sessionId = opts?.sessionId;
34463
34552
  this._reconnectionOptions = opts?.reconnectionOptions ?? DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS;
34464
34553
  }
34554
+ _unfollowedRedirect(response, url2) {
34555
+ const text = unfollowedRedirect(response, url2);
34556
+ return text && !this._followRedirects ? `${text} (redirectPolicy: 'same-origin')` : text;
34557
+ }
34465
34558
  async _authThenStart() {
34466
34559
  if (!this._authProvider) {
34467
34560
  throw new UnauthorizedError("No auth provider");
@@ -34511,11 +34604,13 @@ class StreamableHTTPClientTransport {
34511
34604
  if (resumptionToken) {
34512
34605
  headers.set("last-event-id", resumptionToken);
34513
34606
  }
34514
- const response = await (this._fetch ?? fetch)(this._url, {
34515
- method: "GET",
34516
- headers,
34517
- signal: this._abortController?.signal
34518
- });
34607
+ const fetchFn = this._fetch ?? fetch;
34608
+ const init = { method: "GET", headers, signal: this._abortController?.signal };
34609
+ let response = await fetchFn(this._url, this._followRedirects ? init : { ...init, redirect: "manual" });
34610
+ const followed = this._followRedirects ? undefined : followWithinOrigin(fetchFn, this._url, init, response);
34611
+ if (followed) {
34612
+ response = await followed;
34613
+ }
34519
34614
  if (!response.ok) {
34520
34615
  await response.body?.cancel();
34521
34616
  if (response.status === 401 && this._authProvider) {
@@ -34524,7 +34619,7 @@ class StreamableHTTPClientTransport {
34524
34619
  if (response.status === 405) {
34525
34620
  return;
34526
34621
  }
34527
- throw new StreamableHTTPError(response.status, `Failed to open SSE stream: ${response.statusText}`);
34622
+ throw new StreamableHTTPError(response.status, `Failed to open SSE stream: ${this._unfollowedRedirect(response, this._url) ?? response.statusText}`);
34528
34623
  }
34529
34624
  this._handleSseStream(response.body, options, true);
34530
34625
  } catch (error2) {
@@ -34672,7 +34767,7 @@ class StreamableHTTPClientTransport {
34672
34767
  body: JSON.stringify(message),
34673
34768
  signal: this._abortController?.signal
34674
34769
  };
34675
- const response = await (this._fetch ?? fetch)(this._url, init);
34770
+ const response = await fetchWithinOrigin(this._fetch ?? fetch)(this._url, init);
34676
34771
  const sessionId = response.headers.get("mcp-session-id");
34677
34772
  if (sessionId) {
34678
34773
  this._sessionId = sessionId;
@@ -34724,7 +34819,7 @@ class StreamableHTTPClientTransport {
34724
34819
  return this.send(message);
34725
34820
  }
34726
34821
  }
34727
- throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
34822
+ throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${this._unfollowedRedirect(response, this._url) ?? text}`);
34728
34823
  }
34729
34824
  this._hasCompletedAuthFlow = false;
34730
34825
  this._lastUpscopingHeader = undefined;
@@ -34775,10 +34870,10 @@ class StreamableHTTPClientTransport {
34775
34870
  headers,
34776
34871
  signal: this._abortController?.signal
34777
34872
  };
34778
- const response = await (this._fetch ?? fetch)(this._url, init);
34873
+ const response = await fetchWithinOrigin(this._fetch ?? fetch)(this._url, init);
34779
34874
  await response.body?.cancel();
34780
34875
  if (!response.ok && response.status !== 405) {
34781
- throw new StreamableHTTPError(response.status, `Failed to terminate session: ${response.statusText}`);
34876
+ throw new StreamableHTTPError(response.status, `Failed to terminate session: ${this._unfollowedRedirect(response, this._url) ?? response.statusText}`);
34782
34877
  }
34783
34878
  this._sessionId = undefined;
34784
34879
  } catch (error2) {
@@ -34801,7 +34896,7 @@ class StreamableHTTPClientTransport {
34801
34896
  }
34802
34897
  var DEFAULT_CLIENT_INFO = {
34803
34898
  name: "letta-code",
34804
- version: "0.34.24"
34899
+ version: "0.34.26"
34805
34900
  };
34806
34901
  async function connectMcpServer(config2, options = {}) {
34807
34902
  let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
@@ -35829,4 +35924,4 @@ export {
35829
35924
  CloudManagedSandboxExpiredError
35830
35925
  };
35831
35926
 
35832
- //# debugId=7070C54283A6731164756E2164756E21
35927
+ //# debugId=59E6DEB40A201AA464756E2164756E21