@astralbeam/sdk 0.9.0 → 0.11.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.
@@ -1,19 +1,30 @@
1
+ import { D as isAstralBeamApiError, E as astralBeamChatFetch, O as resolveApiUrl, c as getCurrentUser, g as getRunChatUrl, i as getChatConfig } from "./api-DrKgAIBT.js";
2
+ import { d as JWTInvalid, h as strictDecoder, r as isObject, s as decode } from "./type_checks-C-WTHaWs.js";
1
3
  //#region \0rolldown/runtime.js
2
4
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
3
- /**
4
- * The chat API's URLs under an API base: the stream itself, the agent capability handshake,
5
- * and artifact downloads. Chat is one API under the base; others will sit beside it.
6
- */
7
- function chatApiUrls(apiUrl) {
8
- const chat = `${(apiUrl ?? "https://app.astralbeam.ai/api").replace(/\/+$/, "")}/v1/chat`;
9
- return {
10
- chat,
11
- config: `${chat}/config`,
12
- files: `${chat}/files`
13
- };
5
+ //#endregion
6
+ //#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/util/decode_jwt.js
7
+ function decodeJwt(jwt) {
8
+ if (typeof jwt !== "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
9
+ const { 1: payload, length } = jwt.split(".");
10
+ if (length === 5) throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
11
+ if (length !== 3) throw new JWTInvalid("Invalid JWT");
12
+ if (!payload) throw new JWTInvalid("JWTs must contain a payload");
13
+ let decoded;
14
+ try {
15
+ decoded = decode(payload);
16
+ } catch {
17
+ throw new JWTInvalid("Failed to base64url decode the payload");
18
+ }
19
+ let result;
20
+ try {
21
+ result = JSON.parse(strictDecoder.decode(decoded));
22
+ } catch {
23
+ throw new JWTInvalid("Failed to parse the decoded payload as JSON");
24
+ }
25
+ if (!isObject(result)) throw new JWTInvalid("Invalid JWT Claims Set");
26
+ return result;
14
27
  }
15
- /** Color scheme used when the mount options and the React prop give none. */
16
- const DEFAULT_COLOR_SCHEME = "system";
17
28
  //#endregion
18
29
  //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/response-stream.js
19
30
  var UnsupportedResponseStreamError = class extends Error {
@@ -7841,29 +7852,24 @@ function buildHostTools(tools, debug) {
7841
7852
  //#region src/core/auth.ts
7842
7853
  const REFRESH_SKEW_MS = 6e4;
7843
7854
  const MAX_TOKEN_LENGTH = 16384;
7844
- function tokenExpiry(token) {
7855
+ function tokenTiming(token) {
7845
7856
  if (token.length > MAX_TOKEN_LENGTH) throw new Error("The chat auth token is too large");
7846
- const parts = token.split(".");
7847
- if (parts.length !== 3 || !parts[1]) throw new Error("The chat auth token is not a JWT");
7848
- const encoded = parts[1].replaceAll("-", "+").replaceAll("_", "/");
7849
- const padded = encoded.padEnd(Math.ceil(encoded.length / 4) * 4, "=");
7850
- let payload;
7851
- try {
7852
- payload = JSON.parse(atob(padded));
7853
- } catch {
7854
- throw new Error("The chat auth token has an invalid payload");
7855
- }
7856
- const exp = payload?.exp;
7857
+ const { exp, iat, user } = decodeJwt(token);
7857
7858
  if (!Number.isInteger(exp) || Number(exp) <= 0) throw new Error("The chat auth token has no valid expiry");
7858
- return Number(exp) * 1e3;
7859
- }
7860
- function bearerToken(headers) {
7861
- const authorization = headers.get("authorization");
7862
- return authorization?.startsWith("Bearer ") ? authorization.slice(7) : void 0;
7859
+ const expiresAt = Number(exp) * 1e3;
7860
+ const lifetime = iat === void 0 ? 3e5 : expiresAt - iat * 1e3;
7861
+ const margin = Math.min(REFRESH_SKEW_MS, lifetime * .2);
7862
+ const tenantAdmin = user?.admin === true;
7863
+ return {
7864
+ expiresAt,
7865
+ refreshAt: expiresAt - margin,
7866
+ tenantAdmin
7867
+ };
7863
7868
  }
7864
7869
  async function requestChatAuthToken(options, signal) {
7865
7870
  const { fetchAstralBeamToken, fetchClient } = options;
7866
7871
  if (typeof fetchAstralBeamToken === "function") return (await fetchAstralBeamToken())?.token;
7872
+ if (!fetchAstralBeamToken) throw new Error("Provide fetchAstralBeamToken.");
7867
7873
  const { url, ...init } = fetchAstralBeamToken;
7868
7874
  const headers = new Headers(init.headers);
7869
7875
  if (!headers.has("accept")) headers.set("accept", "application/json");
@@ -7875,43 +7881,120 @@ async function requestChatAuthToken(options, signal) {
7875
7881
  headers,
7876
7882
  signal: init.signal ? AbortSignal.any([signal, init.signal]) : signal
7877
7883
  });
7878
- if (!response.ok) throw new Error(`Authentication endpoint returned HTTP ${response.status}`);
7884
+ if (!response.ok) throw Object.assign(/* @__PURE__ */ new Error(`Authentication endpoint returned HTTP ${response.status}`), {
7885
+ status: response.status,
7886
+ headers: response.headers
7887
+ });
7879
7888
  return (await response.json())?.token;
7880
7889
  }
7890
+ const initialAuthenticationState = { status: "loading" };
7891
+ function createChatAuthentication(options) {
7892
+ return {
7893
+ ...options,
7894
+ fetchAstralBeamToken: options.fetchAstralBeamToken,
7895
+ session: {
7896
+ cached: void 0,
7897
+ refreshPromise: void 0,
7898
+ abortController: new AbortController(),
7899
+ state: initialAuthenticationState,
7900
+ listeners: /* @__PURE__ */ new Set()
7901
+ },
7902
+ fetchClient: globalThis.fetch.bind(globalThis),
7903
+ debug: void 0
7904
+ };
7905
+ }
7906
+ function authenticationState(options) {
7907
+ return options.session.state;
7908
+ }
7909
+ function subscribeAuthentication(options, listener) {
7910
+ const { listeners } = options.session;
7911
+ listeners.add(listener);
7912
+ return () => {
7913
+ listeners.delete(listener);
7914
+ };
7915
+ }
7916
+ function publishAuthentication(options, state) {
7917
+ options.session.state = state;
7918
+ for (const listener of options.session.listeners) listener();
7919
+ }
7920
+ function updateAuthentication(options, next) {
7921
+ if (options.apiUrl !== next.apiUrl) {
7922
+ disposeChatAuthentication(options);
7923
+ options.session.abortController = new AbortController();
7924
+ publishAuthentication(options, { status: "loading" });
7925
+ }
7926
+ options.apiUrl = next.apiUrl;
7927
+ options.fetchAstralBeamToken = next.fetchAstralBeamToken;
7928
+ }
7929
+ function authenticationIdentity(current, tenantAdmin) {
7930
+ return JSON.stringify([
7931
+ current.scope,
7932
+ current.organization.id,
7933
+ current.scope === "tenant" ? current.tenant.id : null,
7934
+ current.user.id,
7935
+ tenantAdmin !== void 0 ? current.scope === "tenant" ? tenantAdmin : current.user.role : null
7936
+ ]);
7937
+ }
7881
7938
  async function loadChatAuthToken(options) {
7882
- const { session, onStateChange, debug } = options;
7939
+ const { session, debug, apiUrl } = options;
7883
7940
  const { signal } = session.abortController;
7884
7941
  const source = typeof options.fetchAstralBeamToken === "function" ? "fetchAstralBeamToken" : "Authentication endpoint";
7885
7942
  try {
7886
- const token = await requestChatAuthToken(options, signal);
7887
- if (typeof token !== "string" || !token) throw new Error(`${source} did not return a token`);
7888
- const expiresAt = tokenExpiry(token);
7889
- if (expiresAt <= Date.now()) throw new Error(`${source} returned an expired token`);
7890
- session.cached = {
7891
- value: token,
7892
- expiresAt
7893
- };
7894
- onStateChange({ status: "ready" });
7895
- debug?.("auth", "chat authentication ready", { expiresAt: new Date(expiresAt) });
7896
- return token;
7897
- } catch (cause) {
7898
- const error = cause instanceof Error ? cause : /* @__PURE__ */ new Error("Chat authentication failed");
7899
- if (!signal.aborted) {
7900
- onStateChange({
7901
- status: "error",
7902
- error
7943
+ for (let attempt = 0; attempt < 2; attempt++) {
7944
+ const token = await requestChatAuthToken(options, signal);
7945
+ signal.throwIfAborted();
7946
+ if (typeof token !== "string" || !token) throw new Error(`${source} did not return a token`);
7947
+ const { expiresAt, refreshAt, tenantAdmin } = tokenTiming(token);
7948
+ if (expiresAt <= Date.now()) throw new Error(`${source} returned an expired token`);
7949
+ if (!Number.isFinite(refreshAt) || refreshAt <= Date.now() || expiresAt - Date.now() < 5e3) throw new Error(`${source} returned a token too close to expiry`);
7950
+ let currentUser;
7951
+ try {
7952
+ currentUser = await getCurrentUser({
7953
+ apiUrl,
7954
+ astralBeamToken: token,
7955
+ fetchClient: options.fetchClient,
7956
+ signal,
7957
+ cache: "no-store",
7958
+ credentials: "omit"
7959
+ });
7960
+ } catch (error) {
7961
+ if (!attempt && options.retryUnauthorized !== false && isAstralBeamApiError(error) && error.status === 401) continue;
7962
+ throw error;
7963
+ }
7964
+ signal.throwIfAborted();
7965
+ if (options.scope && options.scope !== currentUser.scope) throw new Error(`Expected ${options.scope} authentication`);
7966
+ session.cached = {
7967
+ value: token,
7968
+ expiresAt,
7969
+ refreshAt,
7970
+ synchronizedAt: Date.now(),
7971
+ tenantAdmin
7972
+ };
7973
+ publishAuthentication(options, {
7974
+ status: "ready",
7975
+ currentUser
7903
7976
  });
7904
- debug?.("error", `chat authentication failed: ${error.message}`);
7977
+ debug?.("auth", "authentication ready", { expiresAt: new Date(expiresAt) });
7978
+ return token;
7905
7979
  }
7980
+ throw new Error("Authentication failed");
7981
+ } catch (cause) {
7982
+ signal.throwIfAborted();
7983
+ const error = cause instanceof Error ? cause : /* @__PURE__ */ new Error("Authentication failed");
7984
+ session.cached = void 0;
7985
+ publishAuthentication(options, {
7986
+ status: "error",
7987
+ error
7988
+ });
7906
7989
  throw error;
7907
7990
  }
7908
7991
  }
7909
7992
  async function getValidChatAuthToken(options) {
7910
- const { session, force = false, onStateChange } = options;
7911
- const now = Date.now();
7912
- if (!force && session.cached && session.cached.expiresAt - now > REFRESH_SKEW_MS) return session.cached.value;
7993
+ const { session, force = false } = options;
7994
+ session.abortController.signal.throwIfAborted();
7913
7995
  if (session.refreshPromise) return await session.refreshPromise;
7914
- onStateChange({ status: "loading" });
7996
+ if (!force && session.cached && session.cached.refreshAt > Date.now()) return session.cached.value;
7997
+ if (!session.cached || session.cached.expiresAt <= Date.now()) publishAuthentication(options, { status: "loading" });
7915
7998
  const refresh = loadChatAuthToken(options);
7916
7999
  session.refreshPromise = refresh;
7917
8000
  try {
@@ -7920,6 +8003,15 @@ async function getValidChatAuthToken(options) {
7920
8003
  if (session.refreshPromise === refresh) session.refreshPromise = void 0;
7921
8004
  }
7922
8005
  }
8006
+ function refreshRejectedAuthentication(options, token) {
8007
+ const rejectedCurrentToken = !token || options.session.cached?.value === token;
8008
+ if (rejectedCurrentToken) options.session.cached = void 0;
8009
+ return getValidChatAuthToken({
8010
+ ...options,
8011
+ force: rejectedCurrentToken,
8012
+ retryUnauthorized: false
8013
+ });
8014
+ }
7923
8015
  async function initializeChatAuthentication(options) {
7924
8016
  if (options.session.abortController.signal.aborted) options.session.abortController = new AbortController();
7925
8017
  await getValidChatAuthToken(options);
@@ -7927,28 +8019,86 @@ async function initializeChatAuthentication(options) {
7927
8019
  function disposeChatAuthentication({ session }) {
7928
8020
  session.abortController.abort();
7929
8021
  session.refreshPromise = void 0;
8022
+ session.cached = void 0;
8023
+ session.state = initialAuthenticationState;
7930
8024
  }
7931
8025
  async function fetchAuthenticatedChat(options) {
7932
8026
  const { input, init, session, fetchClient, debug } = options;
8027
+ const previous = authenticationState(options);
7933
8028
  const response = await fetchClient(input, init);
7934
8029
  if (response.status !== 401 || session.abortController.signal.aborted) return response;
7935
- const usedToken = bearerToken(new Headers(init?.headers));
7936
- const rejectedCurrentToken = !usedToken || session.cached?.value === usedToken;
7937
- if (rejectedCurrentToken) session.cached = void 0;
7938
- debug?.("auth", "chat auth token was rejected; refreshing once");
7939
- const token = await getValidChatAuthToken({
7940
- ...options,
7941
- force: rejectedCurrentToken
7942
- });
7943
8030
  const headers = new Headers(init?.headers);
7944
- headers.set("authorization", `Bearer ${token}`);
8031
+ const authorization = headers.get("authorization");
8032
+ const usedToken = authorization?.startsWith("Bearer ") ? authorization.slice(7) : void 0;
8033
+ debug?.("auth", "chat auth token was rejected; refreshing once");
7945
8034
  await response.body?.cancel();
8035
+ const token = await refreshRejectedAuthentication(options, usedToken);
8036
+ const current = authenticationState(options);
8037
+ if (previous.status !== "ready" || current.status !== "ready" || authenticationIdentity(previous.currentUser) !== authenticationIdentity(current.currentUser)) throw new DOMException("Identity changed", "AbortError");
8038
+ init?.signal?.throwIfAborted();
8039
+ headers.set("authorization", `Bearer ${token}`);
7946
8040
  return await fetchClient(input, {
7947
8041
  ...init,
7948
8042
  headers
7949
8043
  });
7950
8044
  }
7951
8045
  //#endregion
8046
+ //#region src/core/auth-lifecycle.ts
8047
+ function startAuthentication(options) {
8048
+ let timer;
8049
+ const events = new AbortController();
8050
+ let retries = 0;
8051
+ const visible = () => typeof document === "undefined" || document.visibilityState !== "hidden" && globalThis.navigator?.onLine !== false;
8052
+ const refresh = () => {
8053
+ if (events.signal.aborted || !visible()) return;
8054
+ getValidChatAuthToken({
8055
+ ...options,
8056
+ force: true
8057
+ }).catch(() => {});
8058
+ };
8059
+ const schedule = () => {
8060
+ clearTimeout(timer);
8061
+ if (events.signal.aborted || typeof document === "undefined" || !visible()) return;
8062
+ const state = authenticationState(options);
8063
+ if (state.status === "ready") {
8064
+ retries = 0;
8065
+ timer = setTimeout(refresh, Math.max(1e3, options.session.cached.refreshAt - Date.now()));
8066
+ } else if (state.status === "error" && retries < 3) {
8067
+ const error = state.error;
8068
+ if (error.status !== void 0 && error.status !== 429 && error.status < 500) return;
8069
+ if (error.status === void 0 && !(error instanceof TypeError)) return;
8070
+ const retryAfter = error.headers?.get("retry-after");
8071
+ const delay = retryAfter ? /^\d+$/.test(retryAfter) ? Number(retryAfter) * 1e3 : Date.parse(retryAfter) - Date.now() : 0;
8072
+ timer = setTimeout(refresh, Math.max(1e3 * 2 ** retries++, Number.isFinite(delay) ? delay : 0));
8073
+ }
8074
+ };
8075
+ const foreground = () => {
8076
+ if (!visible()) {
8077
+ clearTimeout(timer);
8078
+ return;
8079
+ }
8080
+ const cached = options.session.cached;
8081
+ if (!cached || Date.now() - cached.synchronizedAt >= 6e4 || cached.refreshAt <= Date.now()) refresh();
8082
+ else schedule();
8083
+ };
8084
+ const unsubscribe = subscribeAuthentication(options, schedule);
8085
+ if (typeof document !== "undefined") {
8086
+ for (const event of [
8087
+ "focus",
8088
+ "online",
8089
+ "offline"
8090
+ ]) globalThis.addEventListener(event, foreground, { signal: events.signal });
8091
+ document.addEventListener("visibilitychange", foreground, { signal: events.signal });
8092
+ }
8093
+ initializeChatAuthentication(options).catch(() => {});
8094
+ schedule();
8095
+ return () => {
8096
+ events.abort();
8097
+ clearTimeout(timer);
8098
+ unsubscribe();
8099
+ };
8100
+ }
8101
+ //#endregion
7952
8102
  //#region src/core/messages.ts
7953
8103
  function isSettledToolCall(part) {
7954
8104
  return part.state === "complete" || part.state === "error" || part.output !== void 0;
@@ -8094,7 +8244,7 @@ function sameAgentTools(current, next) {
8094
8244
  * transcript state, with no markup. The drop-in widget is one consumer; a host that owns its
8095
8245
  * whole UI is another.
8096
8246
  */
8097
- function createAstralBeamChat(options) {
8247
+ function createAstralBeamChat(options, deferStart = false) {
8098
8248
  let live = { ...options };
8099
8249
  let debug = createDebugLogger(live.debug);
8100
8250
  const listeners = /* @__PURE__ */ new Set();
@@ -8118,28 +8268,32 @@ function createAstralBeamChat(options) {
8118
8268
  };
8119
8269
  for (const listener of listeners) listener();
8120
8270
  };
8121
- const authentication = {
8122
- fetchAstralBeamToken: live.fetchAstralBeamToken ?? { url: "/api/astralbeam/token" },
8123
- session: {
8124
- cached: void 0,
8125
- refreshPromise: void 0,
8126
- abortController: new AbortController()
8127
- },
8128
- onStateChange: (auth) => update({ auth }),
8129
- fetchClient: globalThis.fetch.bind(globalThis),
8130
- debug
8131
- };
8132
- initializeChatAuthentication(authentication).catch(() => void 0);
8271
+ const authentication = createChatAuthentication({
8272
+ apiUrl: live.apiUrl,
8273
+ scope: "tenant",
8274
+ fetchAstralBeamToken: live.fetchAstralBeamToken ?? { url: "/api/astralbeam/token" }
8275
+ });
8276
+ let stopAuthentication;
8277
+ let unsubscribeAuthentication;
8278
+ let identity;
8279
+ let started = false;
8133
8280
  let capabilitiesGeneration = 0;
8281
+ let capabilityIdentity;
8134
8282
  const resolveCapabilities = async () => {
8135
8283
  const generation = ++capabilitiesGeneration;
8136
8284
  try {
8137
- const url = new URL(chatApiUrls(live.apiUrl).config, globalThis.location?.href);
8138
- if (live.agentId) url.searchParams.set("agentId", live.agentId);
8139
8285
  const token = await getValidChatAuthToken(authentication);
8140
- const response = await fetch(url, { headers: { authorization: `Bearer ${token}` } });
8141
- if (!response.ok) throw new Error(`The config request answered ${response.status}`);
8142
- const body = await response.json();
8286
+ if (generation !== capabilitiesGeneration) return;
8287
+ const body = await getChatConfig(live.agentId ? { agentId: live.agentId } : {}, {
8288
+ apiUrl: authentication.apiUrl,
8289
+ astralBeamToken: token,
8290
+ signal: authentication.session.abortController.signal,
8291
+ fetchClient: (input, init) => fetchAuthenticatedChat({
8292
+ ...authentication,
8293
+ input,
8294
+ init
8295
+ })
8296
+ });
8143
8297
  if (generation !== capabilitiesGeneration) return;
8144
8298
  const attachments = body.capabilities?.attachments !== false;
8145
8299
  update({ capabilities: { attachments } });
@@ -8148,7 +8302,6 @@ function createAstralBeamChat(options) {
8148
8302
  debug?.("error", "agent capabilities could not be resolved; keeping the defaults", error);
8149
8303
  }
8150
8304
  };
8151
- resolveCapabilities();
8152
8305
  const renderCleanups = /* @__PURE__ */ new Map();
8153
8306
  const disposeRenders = () => {
8154
8307
  for (const cleanup of renderCleanups.values()) cleanup();
@@ -8198,15 +8351,31 @@ function createAstralBeamChat(options) {
8198
8351
  ...live.agentId ? { agentId: live.agentId } : {},
8199
8352
  ...live.debug ? { debug: true } : {}
8200
8353
  });
8201
- const client = new ChatClient({
8202
- connection: fetchServerSentEvents(() => chatApiUrls(live.apiUrl).chat, async () => ({
8203
- headers: { authorization: `Bearer ${await getValidChatAuthToken(authentication)}` },
8204
- fetchClient: (input, init) => fetchAuthenticatedChat({
8354
+ const connection = fetchServerSentEvents(() => resolveApiUrl(getRunChatUrl(), authentication.apiUrl), async () => {
8355
+ const previousIdentity = identity;
8356
+ const token = await getValidChatAuthToken(authentication);
8357
+ if (previousIdentity !== void 0 && previousIdentity !== identity) throw new DOMException("Identity changed", "AbortError");
8358
+ return { fetchClient: (_input, init) => astralBeamChatFetch(getRunChatUrl(), {
8359
+ ...init,
8360
+ apiUrl: authentication.apiUrl,
8361
+ astralBeamToken: token,
8362
+ fetchClient: (input, request) => fetchAuthenticatedChat({
8205
8363
  ...authentication,
8206
8364
  input,
8207
- init
8365
+ init: request
8208
8366
  })
8209
- })),
8367
+ }) };
8368
+ });
8369
+ const connect = connection.connect;
8370
+ connection.connect = async function* (...args) {
8371
+ try {
8372
+ yield* connect(...args);
8373
+ } catch (error) {
8374
+ throw error instanceof Error && isAstralBeamApiError(error.cause) ? error.cause : error;
8375
+ }
8376
+ };
8377
+ const client = new ChatClient({
8378
+ connection,
8210
8379
  tools: declareTools(),
8211
8380
  forwardedProps: forwardedProps(),
8212
8381
  onMessagesChange: (messages) => {
@@ -8259,7 +8428,41 @@ function createAstralBeamChat(options) {
8259
8428
  }
8260
8429
  }
8261
8430
  };
8431
+ const observeAuthentication = () => {
8432
+ const auth = authenticationState(authentication);
8433
+ if (auth.status === "ready") {
8434
+ const nextIdentity = authenticationIdentity(auth.currentUser);
8435
+ if (identity !== void 0 && identity !== nextIdentity) {
8436
+ client.stop();
8437
+ client.clear();
8438
+ disposeRenders();
8439
+ update({
8440
+ messages: [],
8441
+ sandbox: {
8442
+ files: [],
8443
+ commands: []
8444
+ },
8445
+ sandboxStatus: void 0
8446
+ });
8447
+ }
8448
+ identity = nextIdentity;
8449
+ if (capabilityIdentity !== nextIdentity) {
8450
+ capabilityIdentity = nextIdentity;
8451
+ resolveCapabilities();
8452
+ }
8453
+ }
8454
+ update({ auth });
8455
+ };
8456
+ const start = () => {
8457
+ if (started) return;
8458
+ started = true;
8459
+ unsubscribeAuthentication = subscribeAuthentication(authentication, observeAuthentication);
8460
+ observeAuthentication();
8461
+ stopAuthentication = startAuthentication(authentication);
8462
+ };
8463
+ if (!deferStart) start();
8262
8464
  return {
8465
+ start,
8263
8466
  getState: () => state,
8264
8467
  subscribe: (listener) => {
8265
8468
  listeners.add(listener);
@@ -8273,13 +8476,16 @@ function createAstralBeamChat(options) {
8273
8476
  ...next
8274
8477
  };
8275
8478
  debug = createDebugLogger(live.debug);
8276
- authentication.fetchAstralBeamToken = live.fetchAstralBeamToken ?? { url: "/api/astralbeam/token" };
8479
+ updateAuthentication(authentication, {
8480
+ apiUrl: live.apiUrl,
8481
+ fetchAstralBeamToken: live.fetchAstralBeamToken ?? { url: "/api/astralbeam/token" }
8482
+ });
8277
8483
  authentication.debug = debug;
8278
8484
  client.updateOptions({
8279
8485
  tools: declareTools(),
8280
8486
  forwardedProps: forwardedProps()
8281
8487
  });
8282
- if (live.agentId !== agent || live.apiUrl !== apiUrl) resolveCapabilities();
8488
+ if (started && (live.agentId !== agent || live.apiUrl !== apiUrl)) resolveCapabilities();
8283
8489
  },
8284
8490
  sendMessage: (content) => {
8285
8491
  settleDanglingToolCalls();
@@ -8310,8 +8516,16 @@ function createAstralBeamChat(options) {
8310
8516
  },
8311
8517
  dispose: () => {
8312
8518
  disposeRenders();
8313
- client.dispose();
8519
+ client.stop();
8520
+ capabilitiesGeneration++;
8521
+ capabilityIdentity = void 0;
8522
+ stopAuthentication?.();
8523
+ unsubscribeAuthentication?.();
8314
8524
  disposeChatAuthentication(authentication);
8525
+ started = false;
8526
+ queueMicrotask(() => {
8527
+ if (!started) client.dispose();
8528
+ });
8315
8529
  listeners.clear();
8316
8530
  }
8317
8531
  };
@@ -8323,4 +8537,4 @@ function defineTool(tool) {
8323
8537
  return tool;
8324
8538
  }
8325
8539
  //#endregion
8326
- export { DEFAULT_COLOR_SCHEME as C, SANDBOX_WRITE_FILE_TOOL as S, SANDBOX_LIST_FILES_TOOL as _, describeSandboxCommandRun as a, SANDBOX_RUN_COMMAND_TOOL as b, readSandboxCommandRun as c, hasPendingToolRun as d, isSettledToolCall as f, RENDER_WIDGET_TOOL as g, ASK_QUESTIONNAIRE_TOOL as h, collectSandboxActivity as i, readSandboxFileWrite as l, buildAgentTools as m, CORE_OPTION_KEYS as n, isSandboxTool as o, lastPartInProgress as p, createAstralBeamChat as r, readSandboxArtifact as s, defineTool as t, sandboxRefusal as u, SANDBOX_PUBLISH_ARTIFACT_TOOL as v, SANDBOX_STATUS_EVENT as x, SANDBOX_READ_FILE_TOOL as y };
8540
+ export { SANDBOX_WRITE_FILE_TOOL as S, SANDBOX_LIST_FILES_TOOL as _, describeSandboxCommandRun as a, SANDBOX_RUN_COMMAND_TOOL as b, readSandboxCommandRun as c, hasPendingToolRun as d, isSettledToolCall as f, RENDER_WIDGET_TOOL as g, ASK_QUESTIONNAIRE_TOOL as h, collectSandboxActivity as i, readSandboxFileWrite as l, buildAgentTools as m, CORE_OPTION_KEYS as n, isSandboxTool as o, lastPartInProgress as p, createAstralBeamChat as r, readSandboxArtifact as s, defineTool as t, sandboxRefusal as u, SANDBOX_PUBLISH_ARTIFACT_TOOL as v, SANDBOX_STATUS_EVENT as x, SANDBOX_READ_FILE_TOOL as y };
package/dist/core.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as RenderWidgetInput, B as ToolDefinition, C as AstralBeamChatState, D as ChatAuthenticationState, E as createAstralBeamChat, F as SandboxStatus, I as InferParameters, L as JsonSchemaObject, M as SandboxArtifact, N as SandboxCommandRun, O as WidgetDeclaration, P as SandboxFileWrite, R as ParametersSchema, S as AstralBeamChatCoreOptions, T as WidgetRenderRequest, _ as hasPendingToolRun, a as SANDBOX_PUBLISH_ARTIFACT_TOOL, b as AgentToolInfo, c as SANDBOX_STATUS_EVENT, d as describeSandboxCommandRun, f as isSandboxTool, g as sandboxRefusal, h as readSandboxFileWrite, i as SANDBOX_LIST_FILES_TOOL, j as SandboxActivity, k as buildAgentTools, l as SANDBOX_WRITE_FILE_TOOL, m as readSandboxCommandRun, n as ASK_QUESTIONNAIRE_TOOL, o as SANDBOX_READ_FILE_TOOL, p as readSandboxArtifact, r as RENDER_WIDGET_TOOL, s as SANDBOX_RUN_COMMAND_TOOL, t as defineTool, u as collectSandboxActivity, v as isSettledToolCall, w as ChatStreamCallbacks, x as AstralBeamChatCore, y as lastPartInProgress, z as StandardSchemaV1 } from "./index-BQBdZKyB.js";
1
+ import { A as RenderWidgetInput, B as JsonSchemaObject, C as AstralBeamChatState, D as ChatAuthenticationState, E as createAstralBeamChat, F as SandboxStatus, H as StandardSchemaV1, M as SandboxArtifact, N as SandboxCommandRun, O as WidgetDeclaration, P as SandboxFileWrite, S as AstralBeamChatCoreOptions, T as WidgetRenderRequest, U as ToolDefinition, V as ParametersSchema, _ as hasPendingToolRun, a as SANDBOX_PUBLISH_ARTIFACT_TOOL, b as AgentToolInfo, c as SANDBOX_STATUS_EVENT, d as describeSandboxCommandRun, f as isSandboxTool, g as sandboxRefusal, h as readSandboxFileWrite, i as SANDBOX_LIST_FILES_TOOL, j as SandboxActivity, k as buildAgentTools, l as SANDBOX_WRITE_FILE_TOOL, m as readSandboxCommandRun, n as ASK_QUESTIONNAIRE_TOOL, o as SANDBOX_READ_FILE_TOOL, p as readSandboxArtifact, r as RENDER_WIDGET_TOOL, s as SANDBOX_RUN_COMMAND_TOOL, t as defineTool, u as collectSandboxActivity, v as isSettledToolCall, w as ChatStreamCallbacks, x as AstralBeamChatCore, y as lastPartInProgress, z as InferParameters } from "./index-C4uH8CV1.js";
2
2
  export { ASK_QUESTIONNAIRE_TOOL, type AgentToolInfo, type AstralBeamChatCore, type AstralBeamChatCoreOptions, type AstralBeamChatState, type ChatAuthenticationState, type ChatStreamCallbacks, type InferParameters, type JsonSchemaObject, type ParametersSchema, RENDER_WIDGET_TOOL, type RenderWidgetInput, SANDBOX_LIST_FILES_TOOL, SANDBOX_PUBLISH_ARTIFACT_TOOL, SANDBOX_READ_FILE_TOOL, SANDBOX_RUN_COMMAND_TOOL, SANDBOX_STATUS_EVENT, SANDBOX_WRITE_FILE_TOOL, type SandboxActivity, type SandboxArtifact, type SandboxCommandRun, type SandboxFileWrite, type SandboxStatus, type StandardSchemaV1, type ToolDefinition, type WidgetDeclaration, type WidgetRenderRequest, buildAgentTools, collectSandboxActivity, createAstralBeamChat, defineTool, describeSandboxCommandRun, hasPendingToolRun, isSandboxTool, isSettledToolCall, lastPartInProgress, readSandboxArtifact, readSandboxCommandRun, readSandboxFileWrite, sandboxRefusal };
package/dist/core.js CHANGED
@@ -1,2 +1,2 @@
1
- import { S as SANDBOX_WRITE_FILE_TOOL, _ as SANDBOX_LIST_FILES_TOOL, a as describeSandboxCommandRun, b as SANDBOX_RUN_COMMAND_TOOL, c as readSandboxCommandRun, d as hasPendingToolRun, f as isSettledToolCall, g as RENDER_WIDGET_TOOL, h as ASK_QUESTIONNAIRE_TOOL, i as collectSandboxActivity, l as readSandboxFileWrite, m as buildAgentTools, o as isSandboxTool, p as lastPartInProgress, r as createAstralBeamChat, s as readSandboxArtifact, t as defineTool, u as sandboxRefusal, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, x as SANDBOX_STATUS_EVENT, y as SANDBOX_READ_FILE_TOOL } from "./core-Eh2vyhUP.js";
1
+ import { S as SANDBOX_WRITE_FILE_TOOL, _ as SANDBOX_LIST_FILES_TOOL, a as describeSandboxCommandRun, b as SANDBOX_RUN_COMMAND_TOOL, c as readSandboxCommandRun, d as hasPendingToolRun, f as isSettledToolCall, g as RENDER_WIDGET_TOOL, h as ASK_QUESTIONNAIRE_TOOL, i as collectSandboxActivity, l as readSandboxFileWrite, m as buildAgentTools, o as isSandboxTool, p as lastPartInProgress, r as createAstralBeamChat, s as readSandboxArtifact, t as defineTool, u as sandboxRefusal, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, x as SANDBOX_STATUS_EVENT, y as SANDBOX_READ_FILE_TOOL } from "./core-xh4NLb1l.js";
2
2
  export { ASK_QUESTIONNAIRE_TOOL, RENDER_WIDGET_TOOL, SANDBOX_LIST_FILES_TOOL, SANDBOX_PUBLISH_ARTIFACT_TOOL, SANDBOX_READ_FILE_TOOL, SANDBOX_RUN_COMMAND_TOOL, SANDBOX_STATUS_EVENT, SANDBOX_WRITE_FILE_TOOL, buildAgentTools, collectSandboxActivity, createAstralBeamChat, defineTool, describeSandboxCommandRun, hasPendingToolRun, isSandboxTool, isSettledToolCall, lastPartInProgress, readSandboxArtifact, readSandboxCommandRun, readSandboxFileWrite, sandboxRefusal };
@@ -0,0 +1 @@
1
+ const e=e=>`background:${e};color:#fff;border-radius:3px;padding:1px 5px`,t=`${e(`#7c3aed`)};font-weight:600`,n={mount:`#7c3aed`,auth:`#be185d`,theme:`#8b5cf6`,send:`#2563eb`,run:`#0891b2`,stream:`#0e7490`,text:`#16a34a`,reasoning:`#64748b`,tool:`#d97706`,widget:`#db2777`,attachment:`#0d9488`,sandbox:`#0369a1`,questionnaire:`#9333ea`,status:`#475569`,error:`#dc2626`};function r(r){if(r)return(r,i,a)=>{console.log(`%cAstralBeam%c ${new Date().toISOString().slice(11,19)} %c${r}%c ${i}`,t,`color:#94a3b8;font-weight:400`,e(n[r]),``,...a===void 0?[]:[a])}}export{r as t};
@@ -0,0 +1 @@
1
+ export { };
@@ -1,3 +1,5 @@
1
+ import { f as CurrentUser } from "./api-CWgQSDaI.js";
2
+
1
3
  //#region node_modules/.deno/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
2
4
  /** The Standard Typed interface. This is a base type extended by other specs. */
3
5
  interface StandardTypedV1<Input = unknown, Output = Input> {
@@ -3262,7 +3264,7 @@ interface StandardSchemaV1<Input = unknown, Output = Input> {
3262
3264
  * Input type `defineTool`/`defineWidget` derive from a `parameters` schema: a Standard Schema's
3263
3265
  * validated output, or untyped props for a plain JSON Schema, which nothing validates in the browser.
3264
3266
  */
3265
- type InferParameters<S extends ParametersSchema> = S extends StandardSchemaV1<any, infer O> ? O : Record<string, unknown>;
3267
+ type InferParameters<S extends ParametersSchema> = S extends StandardSchemaV1<unknown, infer O> ? O : Record<string, unknown>;
3266
3268
  /** A plain JSON Schema object, the same shape tool definitions use for their parameters. */
3267
3269
  interface JsonSchemaObject {
3268
3270
  type: "object";
@@ -3303,7 +3305,7 @@ interface ToolDefinition {
3303
3305
  * Runs in the host page with the agent-chosen input. The resolved value is returned
3304
3306
  * to the agent as the tool result; a thrown error is returned as a tool error.
3305
3307
  */
3306
- execute: (input: Record<string, unknown>) => unknown | Promise<unknown>;
3308
+ execute: (input: Record<string, unknown>) => unknown;
3307
3309
  }
3308
3310
  /**
3309
3311
  * Limits and accepted types for the composer's file attachments. Every field is optional;
@@ -3435,6 +3437,8 @@ interface MountAstralBeamChatOptions {
3435
3437
  colorScheme?: AstralBeamChatColorScheme | undefined;
3436
3438
  /** Custom values for the widget's theming CSS variables, per color scheme. */
3437
3439
  theme?: AstralBeamChatTheme | undefined;
3440
+ /** Trusted application CSS, scoped to this widget's shadow root. */
3441
+ customCss?: string | undefined;
3438
3442
  /**
3439
3443
  * Logs every SDK action to the browser console with UTC timestamps and full payloads,
3440
3444
  * and asks the endpoint (via the forwarded props) to log its side of the run too.
@@ -3710,6 +3714,7 @@ type ChatAuthenticationState = {
3710
3714
  status: "loading";
3711
3715
  } | {
3712
3716
  status: "ready";
3717
+ currentUser: CurrentUser;
3713
3718
  } | {
3714
3719
  status: "error";
3715
3720
  error: Error;
@@ -3771,6 +3776,8 @@ interface AstralBeamChatState {
3771
3776
  sandbox: SandboxActivity;
3772
3777
  }
3773
3778
  interface AstralBeamChatCore {
3779
+ /** Start browser side effects after a React commit. Vanilla sessions start automatically. */
3780
+ start: () => void;
3774
3781
  getState: () => AstralBeamChatState;
3775
3782
  /** Notifies on every state change; returns the unsubscribe. */
3776
3783
  subscribe: (listener: () => void) => () => void;
@@ -3799,7 +3806,7 @@ interface AstralBeamChatCore {
3799
3806
  * transcript state, with no markup. The drop-in widget is one consumer; a host that owns its
3800
3807
  * whole UI is another.
3801
3808
  */
3802
- declare function createAstralBeamChat(options: AstralBeamChatCoreOptions): AstralBeamChatCore;
3809
+ declare function createAstralBeamChat(options: AstralBeamChatCoreOptions, deferStart?: boolean): AstralBeamChatCore;
3803
3810
  //#endregion
3804
3811
  //#region src/core/messages.d.ts
3805
3812
  declare function isSettledToolCall(part: {
@@ -3852,9 +3859,9 @@ interface TypedToolDefinition<S extends ParametersSchema = JsonSchemaObject> {
3852
3859
  description: string;
3853
3860
  metadata?: Record<string, unknown> | undefined;
3854
3861
  parameters?: S;
3855
- execute: (input: InferParameters<S>) => unknown | Promise<unknown>;
3862
+ execute: (input: InferParameters<S>) => unknown;
3856
3863
  }
3857
3864
  /** Declares a host tool; a Standard Schema `parameters` types (and validates) `execute`'s input. */
3858
3865
  declare function defineTool<const S extends ParametersSchema = JsonSchemaObject>(tool: TypedToolDefinition<S>): ToolDefinition;
3859
3866
  //#endregion
3860
- export { RenderWidgetInput as A, ToolDefinition as B, AstralBeamChatState as C, ChatAuthenticationState as D, createAstralBeamChat as E, SandboxStatus as F, InferParameters as I, JsonSchemaObject as L, SandboxArtifact as M, SandboxCommandRun as N, WidgetDeclaration as O, SandboxFileWrite as P, ParametersSchema as R, AstralBeamChatCoreOptions as S, WidgetRenderRequest as T, hasPendingToolRun as _, SANDBOX_PUBLISH_ARTIFACT_TOOL as a, AgentToolInfo as b, SANDBOX_STATUS_EVENT as c, describeSandboxCommandRun as d, isSandboxTool as f, sandboxRefusal as g, readSandboxFileWrite as h, SANDBOX_LIST_FILES_TOOL as i, SandboxActivity as j, buildAgentTools as k, SANDBOX_WRITE_FILE_TOOL as l, readSandboxCommandRun as m, ASK_QUESTIONNAIRE_TOOL as n, SANDBOX_READ_FILE_TOOL as o, readSandboxArtifact as p, RENDER_WIDGET_TOOL as r, SANDBOX_RUN_COMMAND_TOOL as s, defineTool as t, collectSandboxActivity as u, isSettledToolCall as v, ChatStreamCallbacks as w, AstralBeamChatCore as x, lastPartInProgress as y, StandardSchemaV1 as z };
3867
+ export { RenderWidgetInput as A, JsonSchemaObject as B, AstralBeamChatState as C, ChatAuthenticationState as D, createAstralBeamChat as E, SandboxStatus as F, StandardSchemaV1 as H, AstralBeamChatColorScheme as I, AstralBeamChatTheme as L, SandboxArtifact as M, SandboxCommandRun as N, WidgetDeclaration as O, SandboxFileWrite as P, AstralBeamTokenSource as R, AstralBeamChatCoreOptions as S, WidgetRenderRequest as T, ToolDefinition as U, ParametersSchema as V, hasPendingToolRun as _, SANDBOX_PUBLISH_ARTIFACT_TOOL as a, AgentToolInfo as b, SANDBOX_STATUS_EVENT as c, describeSandboxCommandRun as d, isSandboxTool as f, sandboxRefusal as g, readSandboxFileWrite as h, SANDBOX_LIST_FILES_TOOL as i, SandboxActivity as j, buildAgentTools as k, SANDBOX_WRITE_FILE_TOOL as l, readSandboxCommandRun as m, ASK_QUESTIONNAIRE_TOOL as n, SANDBOX_READ_FILE_TOOL as o, readSandboxArtifact as p, RENDER_WIDGET_TOOL as r, SANDBOX_RUN_COMMAND_TOOL as s, defineTool as t, collectSandboxActivity as u, isSettledToolCall as v, ChatStreamCallbacks as w, AstralBeamChatCore as x, lastPartInProgress as y, InferParameters as z };