@astralbeam/sdk 0.22.0 → 0.23.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/README.md CHANGED
@@ -14,22 +14,27 @@ One component in React, one function everywhere else. Full setup, including the
14
14
  import { AstralBeamChat } from "@astralbeam/sdk/react"
15
15
 
16
16
  export function Sidebar() {
17
- return <AstralBeamChat />
17
+ return (
18
+ <aside style={{ height: "100dvh" }}>
19
+ <AstralBeamChat />
20
+ </aside>
21
+ )
18
22
  }
19
23
  ```
20
24
 
21
25
  ```ts
22
26
  import { mountAstralBeamChat } from "@astralbeam/sdk/client"
23
27
 
24
- const handle = mountAstralBeamChat(document.getElementById("sidebar"), {})
28
+ const handle = mountAstralBeamChat(document.getElementById("sidebar")!, {})
25
29
  // Update with handle.update({ colorScheme: "dark" }), then clean up with handle.unmount().
26
30
  ```
27
31
 
28
32
  Without npm or a bundler, import the same entry from jsDelivr in a module script. Pin an exact version and the full `/dist/client.js` path, because the loader imports its lazy chunks relative to itself. See [Script tag](https://astralbeam.ai/docs/sdk/script-tag).
29
33
 
30
34
  ```html
35
+ <div id="sidebar" style="height:100dvh"></div>
31
36
  <script type="module">
32
- import { mountAstralBeamChat } from "https://cdn.jsdelivr.net/npm/@astralbeam/sdk@0.22.0/dist/client.js"
37
+ import { mountAstralBeamChat } from "https://cdn.jsdelivr.net/npm/@astralbeam/sdk@0.23.0/dist/client.js"
33
38
 
34
39
  mountAstralBeamChat(document.getElementById("sidebar"), {})
35
40
  </script>
@@ -86,28 +91,7 @@ After acquiring a token, components call `POST /api/v1/me` and renew before expi
86
91
 
87
92
  ## Options
88
93
 
89
- Every option is also a prop on `<AstralBeamChat>`. `handle.update(options)` applies any subset in place, and no option is fixed at mount. Details in [Configuration](https://astralbeam.ai/docs/sdk/configuration).
90
-
91
- | Option | Default | Meaning |
92
- | --- | --- | --- |
93
- | `agentId` | organization's default | `agent_<orgId>_<id>`, copied from the dashboard |
94
- | `threadId` | `"auto"` | Restore this tab's selection, use `"new"` for a fresh chat, or pass a saved thread UUID |
95
- | `apiUrl` | `https://astralbeam.ai/api` | Base URL of the AstralBeam API. The widget calls `/v1/chat` there |
96
- | `fetchAstralBeamToken` | `{ url: "/api/astralbeam/token" }` | Chat auth token endpoint as `{ url, ...RequestInit }`, or a minter |
97
- | `title`, `showHeader` | `"AstralBeam"`, `true` | Header text, and whether the header with its chat history and new chat buttons shows |
98
- | `onClose` | none | Adds an X button and fullscreen chat at widths up to 900px. The host hides or unmounts chat in this callback |
99
- | `resizable` | `true` | Enables the resize edge and keyboard resizing |
100
- | `defaultWidth` | host's initial width | Width in pixels, or `"100%"` for available width, when no saved override exists |
101
- | `resizeContainer` | mount's parent | `() => HTMLElement \| null` returning the stable container that limits available width |
102
- | `showConversationTitle` | `false` | Bar with a titled conversation's title and a rename, delete, and Copy Markdown menu |
103
- | `emptyHeadline`, `emptyDescription` | generic copy | Headline and subtitle of the empty transcript |
104
- | `colorScheme`, `theme` | `"system"`, built-in palette | Light/dark/system, and shadcn token overrides |
105
- | `customCss` | None | Trusted CSS inside the widget's Shadow DOM |
106
- | `attachments` | `true` | `false` hides the feature, or pass limits |
107
- | `tools`, `widgets` | none | What the agent can do and draw in your app |
108
- | `sandboxPanel` | `false` | Collected sandbox panel: files with downloads, command log |
109
- | `header`, `headerActions`, `empty`, `composerActions` | widget's own chrome | Host-rendered replacements (React props. `slots` on the handle) |
110
- | `debug` | `false` | Log SDK actions in the browser, with server logs in development only |
94
+ Pass options as React props or apply a subset with `handle.update(options)`. Presentation changes preserve chat. Changing `threadId` selects a conversation, and changing `apiUrl` clears the previous deployment's state. See [Configuration](https://astralbeam.ai/docs/sdk/configuration) for all options, defaults, layout, and chrome slots.
111
95
 
112
96
  A `ref` on `<AstralBeamChat>` (and the vanilla handle) exposes `reset()` and `stop()` for hosts that draw their own controls.
113
97
 
@@ -145,14 +129,16 @@ widgets: {
145
129
  - Only a Standard Schema validates input in the browser. With plain JSON Schema, treat input as untrusted.
146
130
  - Return plain JSON values from tools, with no `undefined` fields. If a result cannot be sent, the action may still have happened.
147
131
 
148
- ## Documentation
132
+ ## MCP connections
149
133
 
150
- Remote tools appear under **MCP connections** in the authenticated Tenant widget. Organization admins configure the available MCP servers. Each TenantUser creates their own connection to a server. Public endpoints, static headers and OAuth are supported. Tool results follow the conversation's visibility and retention rules. Disconnect clears credentials locally.
134
+ Remote tools appear under **MCP connections** in the authenticated Tenant widget. Organization admins configure the available MCP servers. Each TenantUser creates their own connection to a server. Public endpoints, static headers and OAuth are supported. Tool results follow the conversation's visibility and retention rules. Disconnect removes the saved connection and its credentials from AstralBeam.
151
135
 
152
136
  Headless consumers can use `chat.getMcpCatalog()`, `chat.manageMcpConnection(operation)` and `chat.authorizeMcpConnection({ mcpServerId, id? })`. Start OAuth from a user gesture to open its popup. Browser binding and completion use the authenticated session. Server tool titles arrive in `state.serverToolTitles`, independently of host tool execution.
153
137
 
154
138
  Reuse the dashboard's server list and form with `AstralBeamMcpServers` from `/react` or `mountAstralBeamMcpServers` from `/client`. Supply the server list and authorized write callbacks. See [MCP server management](https://astralbeam.ai/docs/sdk/configuration#mcp-server-management).
155
139
 
140
+ ## Documentation
141
+
156
142
  | Guide | Covers |
157
143
  | --- | --- |
158
144
  | [API client](https://astralbeam.ai/docs/sdk/api) | Typed resource and chat requests with API keys or JWTs. |
@@ -173,17 +159,13 @@ Reuse the dashboard's server list and form with `AstralBeamMcpServers` from `/re
173
159
 
174
160
  Read-only Tenant, TenantUser and conversation widgets are available from `/client` and `/react`. See [Tenant directories](https://astralbeam.ai/docs/sdk/listings) for setup and embedding examples.
175
161
 
176
- Directory results stay fresh for 30 seconds, then refresh when the tab becomes visible or the network reconnects. Unused data expires after five minutes. The optional `cacheKey` is an opaque host-session object for remount reuse. Keep it stable during the session and replace it on sign-out or account changes. Changing it clears authentication and the view. Each mount owns its authentication, requests, cancellation, and refresh, independently of concurrent directories.
177
-
178
- Directory `refresh()` and `reset()` return `Promise<void>` and wait for renderer loading, authentication, and the current directory rows or open conversation messages. Attachment previews load independently. Refresh may reuse the JWT. Reset clears this mount's state and retained data for its key, then reacquires authentication from a ready host session. Unmount during sign-out. See the <a href="https://astralbeam.ai/docs/sdk/listings#account-changes-and-lifecycle" target="_blank" rel="noopener noreferrer">lifecycle contract</a> for remount reuse, failure, and cancellation.
179
-
180
- **Breaking migration**: React directory refs now expose only `refresh()` and `reset()`. Replace the removed `/react` `*ListHandle` exports with `AstralBeamTenantListRef`, `AstralBeamTenantUserListRef`, or `AstralBeamThreadListRef`. Use props for updates and conditional rendering to unmount. Vanilla `/client` handles retain `update()` and `unmount()`. Await or handle rejection from refresh/reset calls that previously returned `void`.
162
+ React directory refs expose `refresh()` and `reset()`, both returning `Promise<void>`. Use props for updates and conditional rendering to unmount. Vanilla handles also expose `update()` and `unmount()`. Unmount during sign-out or identity changes. See the <a href="https://astralbeam.ai/docs/sdk/listings#account-changes-and-lifecycle" target="_blank" rel="noopener noreferrer">lifecycle contract</a> for authentication, caching, remount reuse, and action completion.
181
163
 
182
164
  There is no root export.
183
165
 
184
166
  | Entry point | Contents | Peer dependency |
185
167
  | ------------------------ | -------------------------------------------- | -------------------- |
186
- | `@astralbeam/sdk/client` | Chat and Tenant directory mounts | none |
168
+ | `@astralbeam/sdk/client` | Chat, directory, and MCP server mounts | none |
187
169
  | `@astralbeam/sdk/core` | `createAstralBeamChat`, the headless session | none |
188
170
  | `@astralbeam/sdk/react` | Chat hooks and isolated UI wrappers | `react`, `react-dom` |
189
171
  | `@astralbeam/sdk/server` | Tenant and organization token minters | none |
package/dist/client.d.ts CHANGED
@@ -110,14 +110,8 @@ type AstralBeamChatThemeVariables = Record<`--${string}`, string>;
110
110
  interface AstralBeamTokenRequest extends RequestInit {
111
111
  url: string;
112
112
  }
113
- /**
114
- * Where the widget's short-lived chat auth token comes from: an endpoint to POST, or a function that
115
- * mints the token in the host page and returns `{ token }`, optionally as a promise.
116
- *
117
- * Either form runs again on every renewal — near expiry and after a token is rejected — so a
118
- * rotating credential stays current rather than being captured once. A function that returns
119
- * `undefined`, or throws, fails authentication closed; the composer's retry link asks again.
120
- */
113
+ /** Retrieves a server-minted token on acquisition and renewal, including after a token rejection.
114
+ * Returning undefined or throwing fails authentication closed. The composer's retry link asks again. */
121
115
  type AstralBeamTokenSource = AstralBeamTokenRequest | (() => {
122
116
  token: string;
123
117
  } | undefined | Promise<{
@@ -177,12 +171,8 @@ interface MountAstralBeamChatOptions {
177
171
  * hosted cloud; self-hosted deployments must set their own origin.
178
172
  */
179
173
  apiUrl?: string | undefined;
180
- /**
181
- * Where the short-lived chat auth token comes from: `{ url, ...RequestInit }` for a token
182
- * endpoint, or a function that mints `{ token }` in the host page. Read for every token, so a
183
- * change applies to the next one, which is minted when the cached token nears expiry. Default
184
- * `{ url: "/api/astralbeam/token" }`, posted with the page's cookies.
185
- */
174
+ /** Endpoint or callback retrieving a server-minted token, read on each acquisition and renewal.
175
+ * Defaults to POST /api/astralbeam/token with the page's cookies. Never expose API keys here. */
186
176
  fetchAstralBeamToken?: AstralBeamTokenSource | undefined;
187
177
  /** Host-defined tools the agent can call, executed in the host page, keyed by tool name. */
188
178
  tools?: Record<string, ToolDefinition> | undefined;
@@ -213,20 +203,15 @@ interface MountAstralBeamChatOptions {
213
203
  */
214
204
  debug?: boolean | undefined;
215
205
  }
216
- /**
217
- * What the handle's `update` takes: every mount option, none of them fixed. The transport options
218
- * are re-read per request rather than captured, so changing them keeps the transcript and the
219
- * chat session instead of forcing a fresh mount.
220
- */
206
+ /** Updates only supplied options. Presentation changes preserve chat.
207
+ * Changing apiUrl clears the previous session, while threadId selects a conversation. */
221
208
  type AstralBeamChatUpdate = Partial<MountAstralBeamChatOptions>;
222
209
  interface AstralBeamChatHandle {
223
210
  unmount: () => void;
224
- /**
225
- * Merges option changes into the live mount options and applies them in place, keeping the
226
- * transcript, the chat session, and live widget renders. Only the keys given are replaced.
227
- */
211
+ /** Applies supplied options in place. Presentation changes preserve chat.
212
+ * Changing apiUrl clears the previous session, while threadId selects a conversation. */
228
213
  update: (options: AstralBeamChatUpdate) => void;
229
- /** Clears the conversation: transcript, drafts, attachments, and live widget renders. */
214
+ /** Starts a fresh conversation and clears its draft, preserving saved conversations and their drafts. */
230
215
  reset: () => void;
231
216
  /** Stops the in-flight generation, if any; the transcript keeps what already streamed. */
232
217
  stop: () => void;
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- import{s as e}from"./constants-C-Yp0Vhg.js";import{t}from"./debug-DLDY8CZb.js";import{t as n}from"./abort-_ZeHSwUG.js";function r(e,t,n,r){let i=n.colorScheme??`system`,a=i===`dark`||i===`system`&&r;e.classList.toggle(`dark`,a);for(let n of t)e.style.removeProperty(n);t.clear();let o={...n.theme?.light,...a?n.theme?.dark:void 0};for(let[n,r]of Object.entries(o))n.startsWith(`--`)&&(e.style.setProperty(n,r),t.add(n));return{colorScheme:i,dark:a}}function i(e){return e}function a(e){return e}function o(e,t){return l(e,t,`tenants`)}function s(e,t){return l(e,t,`users`)}function c(e,t){return l(e,t,`threads`)}function l(t,n,r){let i=t.shadowRoot??t.attachShadow({mode:`open`}),a=document.createElement(`div`);a.className=`${e} bg-transparent`,a.style.height=`100%`,i.append(a);let o=matchMedia(`(prefers-color-scheme: dark)`),s={options:{...n},kind:r,shadow:i,container:a,media:o,variables:/* @__PURE__ */ new Set,disposed:!1},c=()=>u(s);return c(),o.addEventListener(`change`,c),d(s).catch(()=>{}),{update:e=>m(s,e),refresh:()=>p(s,!1),reset:()=>p(s,!0),unmount:()=>h(s,c)}}function u({options:e,media:t,container:n,variables:i}){r(n,i,e,t.matches)}function d(e){return e.loading??=f(e)}async function f(e){e.retryLoad?.remove();try{if(e.runtime=await import(`./listings-CyliP2E5.js`),e.disposed)return;e.renderer=e.runtime.renderListing(e.shadow,e.container,e.options,e.kind,()=>p(e,!1))}catch(t){throw e.loading=void 0,e.disposed||(e.retryLoad=document.createElement(`button`),e.retryLoad.textContent=`Could not load directory. Retry`,e.retryLoad.onclick=()=>void d(e).catch(()=>{}),e.container.append(e.retryLoad)),t}}async function p(e,t){if(e.disposed)throw new DOMException(`Directory unmounted`,`AbortError`);e.action?.abort();let r=new AbortController;e.action=r;try{e.renderer||await n(d(e),r.signal),r.signal.throwIfAborted(),await e.runtime.refreshListing(e.renderer,r.signal,t)}finally{e.action===r&&(e.action=void 0)}}function m(e,t){if(e.disposed)return;let n={...e.options,...t};(n.apiUrl!==e.options.apiUrl||n.cacheKey!==e.options.cacheKey||n.scope!==e.options.scope||n.tenantId!==e.options.tenantId||n.tenantExternalId!==e.options.tenantExternalId)&&e.action?.abort(),e.options=n,u(e),e.renderer&&e.runtime.updateListing(e.renderer,e.options)}function h(e,t){e.disposed||(e.disposed=!0,e.action?.abort(),e.media.removeEventListener(`change`,t),e.renderer&&e.runtime.disposeListing(e.renderer),e.container.remove())}function g(t,n){let i=t.shadowRoot??t.attachShadow({mode:`open`}),a=document.createElement(`div`);a.className=e,i.append(a);let o=matchMedia(`(prefers-color-scheme: dark)`),s=/* @__PURE__ */ new Set,c=n,l=!1,u,d=()=>r(a,s,c,o.matches);d(),o.addEventListener(`change`,d);let f=async()=>{a.replaceChildren();try{let{renderMcpServers:e}=await import(`./mcp-servers-DvCu1KuJ.js`);l||(u=e(a,c))}catch{if(l)return;let e=document.createElement(`button`);e.textContent=`Could not load MCP servers. Retry`,e.onclick=()=>void f(),a.replaceChildren(e)}};return f(),{update(e){l||(c={...c,...e},d(),u?.update(c))},unmount(){l=!0,o.removeEventListener(`change`,d),u?.unmount(),a.remove()}}}function _(n,i){let a={...i},o=t(a.debug);o?.(`mount`,`mounting chat widget`,{agentId:a.agentId??`(organization default)`,title:a.title??`AstralBeam`,showHeader:a.showHeader??!0,emptyHeadline:a.emptyHeadline??`Ask the assistant`,emptyDescription:a.emptyDescription??`Ask a question or describe what you want to do in this app.`,apiUrl:a.apiUrl??`https://astralbeam.ai/api`,authentication:`configured`,colorScheme:a.colorScheme??`system`,theme:a.theme,tools:Object.keys(a.tools??{}),widgets:Object.keys(a.widgets??{}),attachments:a.attachments??!0});let s=n.shadowRoot??n.attachShadow({mode:`open`}),c=document.createElement(`div`);c.className=e,c.style.height=`100%`,s.append(c);let l=document.createElement(`div`);l.style.cssText=`display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px;font:14px system-ui`;let u=document.createElement(`span`);u.setAttribute(`role`,`status`),u.textContent=`Loading chat…`;let d=document.createElement(`button`);d.type=`button`,d.textContent=`Close chat`,d.style.cssText=`min-width:44px;min-height:44px;padding:8px;font:inherit;cursor:pointer`,d.hidden=!a.onClose,d.onclick=e=>{e.detail<2&&a.onClose?.()},l.append(u,d),c.append(l);let f=matchMedia(`(prefers-color-scheme: dark)`),p=/* @__PURE__ */ new Set,m=()=>{let{colorScheme:e,dark:t}=r(c,p,a,f.matches);o?.(`theme`,`color scheme "${e}" resolved to ${t?`dark`:`light`}`,{themeVariables:[...p]})};m(),f.addEventListener(`change`,m);let h=!1,g;return import(`./widget-BOdanf0E.js`).then(({renderChat:e})=>{o?.(`mount`,`chat chunk loaded`),h||(g=e(s,c,a))}).catch(()=>{h||(u.textContent=`Could not load chat. Reload this page to try again.`)}),{update:e=>{a={...a,...e},o=t(a.debug),o?.(`mount`,`options updated`,{changed:Object.keys(e)}),m(),d.hidden=!a.onClose,g?.update(a)},reset:()=>g?.reset(),stop:()=>g?.stop(),unmount:()=>{o?.(`mount`,`unmounting chat widget`),h=!0,f.removeEventListener(`change`,m),g?.dispose(),g=void 0,c.remove()}}}export{i as defineTool,a as defineWidget,_ as mountAstralBeamChat,g as mountAstralBeamMcpServers,o as mountAstralBeamTenantList,s as mountAstralBeamTenantUserList,c as mountAstralBeamThreadList};
1
+ import{s as e}from"./constants-C-Yp0Vhg.js";import{t}from"./debug-DLDY8CZb.js";import{t as n}from"./abort-_ZeHSwUG.js";function r(e,t,n,r){let i=n.colorScheme??`system`,a=i===`dark`||i===`system`&&r;e.classList.toggle(`dark`,a);for(let n of t)e.style.removeProperty(n);t.clear();let o={...n.theme?.light,...a?n.theme?.dark:void 0};for(let[n,r]of Object.entries(o))n.startsWith(`--`)&&(e.style.setProperty(n,r),t.add(n));return{colorScheme:i,dark:a}}function i(e){return e}function a(e){return e}function o(e,t){return l(e,t,`tenants`)}function s(e,t){return l(e,t,`users`)}function c(e,t){return l(e,t,`threads`)}function l(t,n,r){let i=t.shadowRoot??t.attachShadow({mode:`open`}),a=document.createElement(`div`);a.className=`${e} bg-transparent`,a.style.height=`100%`,i.append(a);let o=matchMedia(`(prefers-color-scheme: dark)`),s={options:{...n},kind:r,shadow:i,container:a,media:o,variables:/* @__PURE__ */ new Set,disposed:!1},c=()=>u(s);return c(),o.addEventListener(`change`,c),d(s).catch(()=>{}),{update:e=>m(s,e),refresh:()=>p(s,!1),reset:()=>p(s,!0),unmount:()=>h(s,c)}}function u({options:e,media:t,container:n,variables:i}){r(n,i,e,t.matches)}function d(e){return e.loading??=f(e)}async function f(e){e.retryLoad?.remove();try{if(e.runtime=await import(`./listings-CyliP2E5.js`),e.disposed)return;e.renderer=e.runtime.renderListing(e.shadow,e.container,e.options,e.kind,()=>p(e,!1))}catch(t){throw e.loading=void 0,e.disposed||(e.retryLoad=document.createElement(`button`),e.retryLoad.textContent=`Could not load directory. Retry`,e.retryLoad.onclick=()=>void d(e).catch(()=>{}),e.container.append(e.retryLoad)),t}}async function p(e,t){if(e.disposed)throw new DOMException(`Directory unmounted`,`AbortError`);e.action?.abort();let r=new AbortController;e.action=r;try{e.renderer||await n(d(e),r.signal),r.signal.throwIfAborted(),await e.runtime.refreshListing(e.renderer,r.signal,t)}finally{e.action===r&&(e.action=void 0)}}function m(e,t){if(e.disposed)return;let n={...e.options,...t};(n.apiUrl!==e.options.apiUrl||n.cacheKey!==e.options.cacheKey||n.scope!==e.options.scope||n.tenantId!==e.options.tenantId||n.tenantExternalId!==e.options.tenantExternalId)&&e.action?.abort(),e.options=n,u(e),e.renderer&&e.runtime.updateListing(e.renderer,e.options)}function h(e,t){e.disposed||(e.disposed=!0,e.action?.abort(),e.media.removeEventListener(`change`,t),e.renderer&&e.runtime.disposeListing(e.renderer),e.container.remove())}function g(t,n){let i=t.shadowRoot??t.attachShadow({mode:`open`}),a=document.createElement(`div`);a.className=e,i.append(a);let o=matchMedia(`(prefers-color-scheme: dark)`),s=/* @__PURE__ */ new Set,c=n,l=!1,u,d=()=>r(a,s,c,o.matches);d(),o.addEventListener(`change`,d);let f=async()=>{a.replaceChildren();try{let{renderMcpServers:e}=await import(`./mcp-servers-DvCu1KuJ.js`);l||(u=e(a,c))}catch{if(l)return;let e=document.createElement(`button`);e.textContent=`Could not load MCP servers. Retry`,e.onclick=()=>void f(),a.replaceChildren(e)}};return f(),{update(e){l||(c={...c,...e},d(),u?.update(c))},unmount(){l=!0,o.removeEventListener(`change`,d),u?.unmount(),a.remove()}}}function _(n,i){let a={...i},o=t(a.debug);o?.(`mount`,`mounting chat widget`,{agentId:a.agentId??`(organization default)`,title:a.title??`AstralBeam`,showHeader:a.showHeader??!0,emptyHeadline:a.emptyHeadline??`Ask the assistant`,emptyDescription:a.emptyDescription??`Ask a question or describe what you want to do in this app.`,apiUrl:a.apiUrl??`https://astralbeam.ai/api`,authentication:`configured`,colorScheme:a.colorScheme??`system`,theme:a.theme,tools:Object.keys(a.tools??{}),widgets:Object.keys(a.widgets??{}),attachments:a.attachments??!0});let s=n.shadowRoot??n.attachShadow({mode:`open`}),c=document.createElement(`div`);c.className=e,c.style.height=`100%`,s.append(c);let l=document.createElement(`div`);l.style.cssText=`display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px;font:14px system-ui`;let u=document.createElement(`span`);u.setAttribute(`role`,`status`),u.textContent=`Loading chat…`;let d=document.createElement(`button`);d.type=`button`,d.textContent=`Close chat`,d.style.cssText=`min-width:44px;min-height:44px;padding:8px;font:inherit;cursor:pointer`,d.hidden=!a.onClose,d.onclick=e=>{e.detail<2&&a.onClose?.()},l.append(u,d),c.append(l);let f=matchMedia(`(prefers-color-scheme: dark)`),p=/* @__PURE__ */ new Set,m=()=>{let{colorScheme:e,dark:t}=r(c,p,a,f.matches);o?.(`theme`,`color scheme "${e}" resolved to ${t?`dark`:`light`}`,{themeVariables:[...p]})};m(),f.addEventListener(`change`,m);let h=!1,g;return import(`./widget-BYZdWupG.js`).then(({renderChat:e})=>{o?.(`mount`,`chat chunk loaded`),h||(g=e(s,c,a))}).catch(()=>{h||(u.textContent=`Could not load chat. Reload this page to try again.`)}),{update:e=>{a={...a,...e},o=t(a.debug),o?.(`mount`,`options updated`,{changed:Object.keys(e)}),m(),d.hidden=!a.onClose,g?.update(a)},reset:()=>g?.reset(),stop:()=>g?.stop(),unmount:()=>{o?.(`mount`,`unmounting chat widget`),h=!0,f.removeEventListener(`change`,m),g?.dispose(),g=void 0,c.remove()}}}export{i as defineTool,a as defineWidget,_ as mountAstralBeamChat,g as mountAstralBeamMcpServers,o as mountAstralBeamTenantList,s as mountAstralBeamTenantUserList,c as mountAstralBeamThreadList};
@@ -11184,7 +11184,7 @@ function buildAskQuestionnaireTool() {
11184
11184
  }
11185
11185
  }).client();
11186
11186
  }
11187
- /** Wraps the host's mount-time tools as client tools the agent can call. */
11187
+ /** Wraps declared host tools, resolving current callbacks through the session's execution guard. */
11188
11188
  function buildHostTools(tools, debug, guardHostExecution) {
11189
11189
  return Object.entries(tools).map(([name, tool]) => toolDefinition({
11190
11190
  name,
@@ -11917,6 +11917,21 @@ function createAstralBeamChat(options, deferStart = false) {
11917
11917
  let threadKey;
11918
11918
  let startWithNewThread = false;
11919
11919
  let threadRecords = [];
11920
+ const messagePresentation = /* @__PURE__ */ new Map();
11921
+ let presentationVersion = 0;
11922
+ let projectedMessages = /* @__PURE__ */ new WeakMap();
11923
+ const rememberAttachments = (messages) => {
11924
+ for (const message of messages) {
11925
+ if (message.role !== "user" || messagePresentation.get(message.id)?.attachmentParts || !message.parts.some((part) => "fileId" in part)) continue;
11926
+ const parts = message.parts.map((part) => Object.freeze({ ...part }));
11927
+ Object.freeze(parts);
11928
+ messagePresentation.set(message.id, {
11929
+ ...messagePresentation.get(message.id),
11930
+ attachmentParts: parts
11931
+ });
11932
+ presentationVersion++;
11933
+ }
11934
+ };
11920
11935
  const clientId = newUuid();
11921
11936
  const acceptSubmission = () => {
11922
11937
  if (!submission || !settle(submission, "accepted")) return;
@@ -11927,11 +11942,13 @@ function createAstralBeamChat(options, deferStart = false) {
11927
11942
  submission.callbacks?.onAccepted?.();
11928
11943
  };
11929
11944
  const replaceAcceptedMessage = (record) => {
11930
- const messages = [...client.getMessages()];
11945
+ const messages = [...client.getSnapshot().messages];
11931
11946
  const index = messages.findLastIndex((message) => message.role === "user");
11932
11947
  if (index !== -1) {
11933
11948
  messages[index] = projectThreadMessages([record])[0];
11949
+ rememberAttachments([messages[index]]);
11934
11950
  client.setMessagesManually(messages);
11951
+ if (unsubscribeSnapshot) observeSnapshot();
11935
11952
  }
11936
11953
  };
11937
11954
  const liveToolCalls = /* @__PURE__ */ new Set();
@@ -12056,22 +12073,52 @@ function createAstralBeamChat(options, deferStart = false) {
12056
12073
  rendered: true
12057
12074
  };
12058
12075
  };
12059
- const restoreCompletedWidgets = (messages) => {
12060
- if (state.thread?.role === "viewer") return;
12061
- const renderIds = /* @__PURE__ */ new Map();
12062
- for (const message of state.messages) for (const part of message.parts) {
12063
- if (part.type !== "tool-call" || part.name !== "render_widget") continue;
12076
+ const projectMessages = (messages) => Object.freeze(messages.map((message) => {
12077
+ let presentation = messagePresentation.get(message.id);
12078
+ const cached = projectedMessages.get(message);
12079
+ if (cached && cached.presentation === presentation) return cached.message;
12080
+ let parts = presentation?.attachmentParts ?? message.parts;
12081
+ let renderIds = presentation?.renderIds;
12082
+ const nextParts = parts.map((part) => {
12083
+ if (part.type !== "tool-call" || part.name !== "render_widget" || part.state !== "complete" || !part.input) return part;
12064
12084
  const stored = part;
12065
- if (!stored.widgetRenderId) continue;
12066
- renderIds.set(savedToolCallId(message.id, part.id), stored.widgetRenderId);
12067
- if (stored.applicationPartId) renderIds.set(savedToolCallId(message.id, stored.applicationPartId, stored.responseTargetId), stored.widgetRenderId);
12085
+ const alias = savedToolCallId(message.id, part.id);
12086
+ const canonical = stored.applicationPartId ? savedToolCallId(message.id, stored.applicationPartId, stored.responseTargetId) : alias;
12087
+ const upstream = stored.upstreamToolCallId;
12088
+ const renderId = stored.widgetRenderId ?? renderIds?.get(alias) ?? renderIds?.get(canonical) ?? (upstream && liveToolMessageIds.get(upstream) === message.id && restoredWidgets.has(upstream) ? upstream : part.id);
12089
+ if (renderIds?.get(alias) !== renderId || renderIds.get(canonical) !== renderId) renderIds = new Map(renderIds).set(alias, renderId).set(canonical, renderId);
12090
+ return stored.widgetRenderId === renderId ? part : Object.freeze({
12091
+ ...part,
12092
+ widgetRenderId: renderId
12093
+ });
12094
+ });
12095
+ if (nextParts.some((part, index) => part !== parts[index])) {
12096
+ Object.freeze(nextParts);
12097
+ parts = nextParts;
12098
+ }
12099
+ if (renderIds && renderIds !== presentation?.renderIds) {
12100
+ presentation = {
12101
+ ...presentation,
12102
+ renderIds
12103
+ };
12104
+ messagePresentation.set(message.id, presentation);
12105
+ presentationVersion++;
12068
12106
  }
12107
+ const projected = parts === message.parts ? message : Object.freeze({
12108
+ ...message,
12109
+ parts
12110
+ });
12111
+ projectedMessages.set(message, {
12112
+ presentation,
12113
+ message: projected
12114
+ });
12115
+ return projected;
12116
+ }));
12117
+ const restoreCompletedWidgets = (messages) => {
12118
+ if (state.thread?.role === "viewer") return;
12069
12119
  for (const message of messages) for (const part of message.parts) {
12070
12120
  if (part.type !== "tool-call" || part.name !== "render_widget" || part.state !== "complete" || !part.input) continue;
12071
- const stored = part;
12072
- const upstream = stored.upstreamToolCallId;
12073
- const renderId = stored.widgetRenderId ?? renderIds.get(savedToolCallId(message.id, part.id)) ?? (stored.applicationPartId ? renderIds.get(savedToolCallId(message.id, stored.applicationPartId, stored.responseTargetId)) : void 0) ?? (upstream && liveToolMessageIds.get(upstream) === message.id && restoredWidgets.has(upstream) ? upstream : part.id);
12074
- stored.widgetRenderId = renderId;
12121
+ const renderId = part.widgetRenderId ?? part.id;
12075
12122
  if (restoredWidgets.has(renderId) || restoringWidgets.has(renderId)) continue;
12076
12123
  restoringWidgets.add(renderId);
12077
12124
  const generation = selectionGeneration;
@@ -12297,7 +12344,10 @@ function createAstralBeamChat(options, deferStart = false) {
12297
12344
  });
12298
12345
  const older = options?.before !== void 0;
12299
12346
  const readGeneration = older ? historyGeneration : ++historyGeneration;
12300
- if (!options?.before) update({ history: state.messages.length > 0 || state.thread?.hasMessages === false ? "refreshing" : "loading" });
12347
+ if (!options?.before) update({
12348
+ olderMessagesLoading: false,
12349
+ history: state.messages.length > 0 || state.thread?.hasMessages === false ? "refreshing" : "loading"
12350
+ });
12301
12351
  const read = (async () => {
12302
12352
  let history = await loadThreadMessages(threadId, await requestOptions(), options?.before, liveToolMessageIds);
12303
12353
  while (options?.before && history.messages.every((message) => message.role === "tool") && history.page_after) {
@@ -12312,6 +12362,7 @@ function createAstralBeamChat(options, deferStart = false) {
12312
12362
  threadRecords = older ? [...history.messages, ...threadRecords] : history.messages;
12313
12363
  const pageIds = new Set(history.messages.map((message) => message.id));
12314
12364
  const messages = projectThreadMessages(threadRecords, older ? state.pendingInteractions : history.pendingInteractions, liveToolMessageIds).filter((message) => pageIds.has(message.id));
12365
+ rememberAttachments(messages);
12315
12366
  if (older) update({ messagesCursor: history.page_after ?? void 0 });
12316
12367
  else {
12317
12368
  if (history.thread.role === "viewer") disposeRenders();
@@ -12324,7 +12375,6 @@ function createAstralBeamChat(options, deferStart = false) {
12324
12375
  if (agentChanged) resolveCapabilities();
12325
12376
  if (messages.length === 0) next.setMessagesManually([]);
12326
12377
  }
12327
- restoreCompletedWidgets(messages);
12328
12378
  return {
12329
12379
  messages,
12330
12380
  activeRun: null,
@@ -12337,7 +12387,10 @@ function createAstralBeamChat(options, deferStart = false) {
12337
12387
  })();
12338
12388
  if (!older) {
12339
12389
  hydration = read.then(() => "idle", () => "failed").then((history) => new Promise((resolve) => queueMicrotask(() => {
12340
- if (client === next && readGeneration === historyGeneration) update({ history });
12390
+ if (client === next && readGeneration === historyGeneration) {
12391
+ if (unsubscribeSnapshot) observeSnapshot();
12392
+ update({ history });
12393
+ }
12341
12394
  resolve();
12342
12395
  }))).then(() => read);
12343
12396
  hydration.catch(() => void 0);
@@ -12351,25 +12404,6 @@ function createAstralBeamChat(options, deferStart = false) {
12351
12404
  queue: "drop",
12352
12405
  tools: declareTools(),
12353
12406
  forwardedProps: forwardedProps(),
12354
- onMessagesChange: (messages) => {
12355
- if (client !== next) return;
12356
- restoreCompletedWidgets(messages);
12357
- const savedMessages = new Map(state.messages.map((message) => [message.id, message]));
12358
- for (const message of messages) {
12359
- if (message.role !== "user") continue;
12360
- const saved = savedMessages.get(message.id);
12361
- if (saved?.parts.some((part) => "fileId" in part)) message.parts = saved.parts;
12362
- }
12363
- update({
12364
- messages,
12365
- sandbox: collectSandboxActivity(messages)
12366
- });
12367
- },
12368
- onStatusChange: (status) => {
12369
- if (client !== next) return;
12370
- clientStatus = status;
12371
- update(status === "submitted" ? { usageLimitReached: void 0 } : {});
12372
- },
12373
12407
  onInterruptStateChange: (interrupts) => {
12374
12408
  if (client === next) debug?.("tool", "interrupt state changed", interrupts);
12375
12409
  },
@@ -12444,21 +12478,55 @@ function createAstralBeamChat(options, deferStart = false) {
12444
12478
  };
12445
12479
  let selectedId;
12446
12480
  let client = createClient();
12447
- const replaceClient = () => {
12481
+ let unsubscribeSnapshot;
12482
+ let snapshotMessages;
12483
+ let projectedPresentationVersion = -1;
12484
+ const observeSnapshot = () => {
12485
+ const snapshot = client.getSnapshot();
12486
+ const messagesChanged = snapshotMessages !== snapshot.messages || projectedPresentationVersion !== presentationVersion;
12487
+ if (!messagesChanged && clientStatus === snapshot.status) return;
12488
+ const next = {};
12489
+ if (clientStatus !== snapshot.status && snapshot.status === "submitted") next.usageLimitReached = void 0;
12490
+ clientStatus = snapshot.status;
12491
+ if (messagesChanged) {
12492
+ next.messages = projectMessages(snapshot.messages);
12493
+ next.sandbox = collectSandboxActivity(next.messages);
12494
+ snapshotMessages = snapshot.messages;
12495
+ projectedPresentationVersion = presentationVersion;
12496
+ }
12497
+ update(next);
12498
+ if (messagesChanged) restoreCompletedWidgets(state.messages);
12499
+ };
12500
+ const attachClient = () => {
12501
+ if (!unsubscribeSnapshot) {
12502
+ const observed = client;
12503
+ const generation = selectionGeneration;
12504
+ unsubscribeSnapshot = observed.subscribeSnapshot(() => {
12505
+ if (client === observed && generation === selectionGeneration) observeSnapshot();
12506
+ });
12507
+ observeSnapshot();
12508
+ restoreCompletedWidgets(state.messages);
12509
+ }
12510
+ client.attach();
12511
+ };
12512
+ const replaceClient = (messages) => {
12448
12513
  const previous = client;
12514
+ unsubscribeSnapshot?.();
12515
+ unsubscribeSnapshot = void 0;
12516
+ snapshotMessages = void 0;
12449
12517
  client = createClient();
12518
+ if (messages) client.setMessagesManually(messages);
12450
12519
  clientStatus = "ready";
12451
12520
  previous.stop();
12452
12521
  previous.detach();
12453
12522
  liveToolCalls.clear();
12454
- liveToolMessageIds.clear();
12455
12523
  update({ activeToolCallIds: [] });
12456
- if (started) client.attach();
12524
+ if (started) attachClient();
12457
12525
  previous.dispose();
12458
12526
  };
12459
12527
  const scopeCompletedToolCalls = () => {
12460
12528
  historicalToolIds.clear();
12461
- client.setMessagesManually(state.messages.map((message) => {
12529
+ client.setMessagesManually(client.getSnapshot().messages.map((message) => {
12462
12530
  const aliases = /* @__PURE__ */ new Map();
12463
12531
  const parts = message.parts.map((part) => {
12464
12532
  if (part.type !== "tool-call" || webPartMetadata(part).providerExecuted) return part;
@@ -12471,7 +12539,7 @@ function createAstralBeamChat(options, deferStart = false) {
12471
12539
  ...part,
12472
12540
  upstreamToolCallId,
12473
12541
  id,
12474
- ...part.name === "render_widget" && liveToolMessageIds.get(upstreamToolCallId) === message.id ? { widgetRenderId: stored.widgetRenderId ?? part.id } : {}
12542
+ ...part.name === "render_widget" && liveToolMessageIds.get(upstreamToolCallId) === message.id ? { widgetRenderId: stored.widgetRenderId ?? messagePresentation.get(message.id)?.renderIds?.get(savedToolCallId(message.id, part.id)) ?? part.id } : {}
12475
12543
  };
12476
12544
  });
12477
12545
  if (aliases.size) historicalToolIds.set(message.id, aliases);
@@ -12495,7 +12563,11 @@ function createAstralBeamChat(options, deferStart = false) {
12495
12563
  requestController = new AbortController();
12496
12564
  toolResults = threadToolResults.get(threadId ?? "") ?? /* @__PURE__ */ new Map();
12497
12565
  historicalToolIds.clear();
12566
+ liveToolMessageIds.clear();
12498
12567
  threadRecords = [];
12568
+ messagePresentation.clear();
12569
+ projectedMessages = /* @__PURE__ */ new WeakMap();
12570
+ presentationVersion++;
12499
12571
  disposeRenders();
12500
12572
  update({
12501
12573
  thread: void 0,
@@ -12523,7 +12595,7 @@ function createAstralBeamChat(options, deferStart = false) {
12523
12595
  submission = submissions.get(id);
12524
12596
  if (submission) update({ submission: project(submission) });
12525
12597
  try {
12526
- if (!started) client.attach();
12598
+ if (!started) attachClient();
12527
12599
  await hydration;
12528
12600
  if (generation !== selectionGeneration) return;
12529
12601
  if (threadKey) selectedThread(threadKey, id);
@@ -12561,10 +12633,9 @@ function createAstralBeamChat(options, deferStart = false) {
12561
12633
  const refreshThread = async (explicit = true) => {
12562
12634
  if (!state.thread) return;
12563
12635
  const generation = selectionGeneration;
12564
- update({ olderMessagesLoading: false });
12565
12636
  try {
12566
12637
  client.detach();
12567
- client.attach();
12638
+ attachClient();
12568
12639
  await hydration;
12569
12640
  if (explicit && generation === selectionGeneration) update({ error: void 0 });
12570
12641
  } catch (error) {
@@ -12579,6 +12650,7 @@ function createAstralBeamChat(options, deferStart = false) {
12579
12650
  update({ olderMessagesLoading: true });
12580
12651
  try {
12581
12652
  await client.loadOlderMessages();
12653
+ if (generation === selectionGeneration && unsubscribeSnapshot) observeSnapshot();
12582
12654
  } catch (error) {
12583
12655
  if (generation === selectionGeneration) reportError(error);
12584
12656
  } finally {
@@ -12745,12 +12817,14 @@ function createAstralBeamChat(options, deferStart = false) {
12745
12817
  const start = () => {
12746
12818
  if (started) return;
12747
12819
  started = true;
12820
+ const interruptedHydration = requestController.signal.aborted && client.getIsHydrating();
12748
12821
  client.updateOptions({ tools: declareTools() });
12749
- client.attach();
12750
12822
  if (requestController.signal.aborted) requestController = new AbortController();
12751
12823
  unsubscribeAuthentication = subscribeAuthentication(authentication, observeAuthentication);
12752
12824
  observeAuthentication();
12753
12825
  stopAuthentication = startAuthentication(authentication);
12826
+ if (interruptedHydration) replaceClient(client.getSnapshot().messages);
12827
+ else attachClient();
12754
12828
  };
12755
12829
  if (!deferStart) start();
12756
12830
  return {
@@ -12878,12 +12952,13 @@ function createAstralBeamChat(options, deferStart = false) {
12878
12952
  },
12879
12953
  dispose: () => {
12880
12954
  releaseSubmission("The chat closed before this was sent.");
12955
+ unsubscribeSnapshot?.();
12956
+ unsubscribeSnapshot = void 0;
12881
12957
  client.detach();
12882
12958
  disposeRenders();
12883
12959
  client.stop();
12884
12960
  requestController.abort();
12885
12961
  selectionGeneration++;
12886
- threadKey = void 0;
12887
12962
  capabilitiesGeneration++;
12888
12963
  capabilityIdentity = void 0;
12889
12964
  stopAuthentication?.();
@@ -12891,7 +12966,12 @@ function createAstralBeamChat(options, deferStart = false) {
12891
12966
  disposeChatAuthentication(authentication);
12892
12967
  started = false;
12893
12968
  queueMicrotask(() => {
12894
- if (!started) client.dispose();
12969
+ if (!started) {
12970
+ client.dispose();
12971
+ threadKey = void 0;
12972
+ messagePresentation.clear();
12973
+ projectedMessages = /* @__PURE__ */ new WeakMap();
12974
+ }
12895
12975
  });
12896
12976
  listeners.clear();
12897
12977
  }
package/dist/core.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as ChatSubmissionCallbacks, B as RenderWidgetInput, C as lastPartInProgress, D as AstralBeamChatState, E as AstralBeamChatCoreOptions, F as McpCatalog, G as SandboxStatus, H as SandboxArtifact, I as McpConnectionOperation, J as ParametersSchema, K as InferParameters, L as ChatAuthenticationState, M as createAstralBeamChat, N as ChatPendingInteraction, O as ChatStreamCallbacks, P as ChatThread, R as WidgetDeclaration, S as isSettledToolCall, T as AstralBeamChatCore, U as SandboxCommandRun, V as SandboxActivity, W as SandboxFileWrite, X as ToolDefinition, Y as StandardSchemaV1, _ as readSandboxArtifact, a as defineTool, b as sandboxRefusal, c as SANDBOX_LIST_FILES_TOOL, d as SANDBOX_RUN_COMMAND_TOOL, f as SANDBOX_STATUS_EVENT, g as isSandboxTool, h as describeSandboxCommandRun, i as WebSource, j as WidgetRenderRequest, k as ChatSubmission, l as SANDBOX_PUBLISH_ARTIFACT_TOOL, m as collectSandboxActivity, n as WebEvidence, o as ASK_QUESTIONNAIRE_TOOL, p as SANDBOX_WRITE_FILE_TOOL, q as JsonSchemaObject, r as WebPartMetadata, s as RENDER_WIDGET_TOOL, t as WebCitation, u as SANDBOX_READ_FILE_TOOL, v as readSandboxCommandRun, w as AgentToolInfo, x as hasPendingToolRun, y as readSandboxFileWrite, z as buildAgentTools } from "./index-DatxwZpZ.js";
1
+ import { A as ChatSubmissionCallbacks, B as RenderWidgetInput, C as lastPartInProgress, D as AstralBeamChatState, E as AstralBeamChatCoreOptions, F as McpCatalog, G as SandboxStatus, H as SandboxArtifact, I as McpConnectionOperation, J as ParametersSchema, K as InferParameters, L as ChatAuthenticationState, M as createAstralBeamChat, N as ChatPendingInteraction, O as ChatStreamCallbacks, P as ChatThread, R as WidgetDeclaration, S as isSettledToolCall, T as AstralBeamChatCore, U as SandboxCommandRun, V as SandboxActivity, W as SandboxFileWrite, X as ToolDefinition, Y as StandardSchemaV1, _ as readSandboxArtifact, a as defineTool, b as sandboxRefusal, c as SANDBOX_LIST_FILES_TOOL, d as SANDBOX_RUN_COMMAND_TOOL, f as SANDBOX_STATUS_EVENT, g as isSandboxTool, h as describeSandboxCommandRun, i as WebSource, j as WidgetRenderRequest, k as ChatSubmission, l as SANDBOX_PUBLISH_ARTIFACT_TOOL, m as collectSandboxActivity, n as WebEvidence, o as ASK_QUESTIONNAIRE_TOOL, p as SANDBOX_WRITE_FILE_TOOL, q as JsonSchemaObject, r as WebPartMetadata, s as RENDER_WIDGET_TOOL, t as WebCitation, u as SANDBOX_READ_FILE_TOOL, v as readSandboxCommandRun, w as AgentToolInfo, x as hasPendingToolRun, y as readSandboxFileWrite, z as buildAgentTools } from "./index-DGfDkU_f.js";
2
2
  export { ASK_QUESTIONNAIRE_TOOL, type AgentToolInfo, type AstralBeamChatCore, type AstralBeamChatCoreOptions, type AstralBeamChatState, type ChatAuthenticationState, type ChatPendingInteraction, type ChatStreamCallbacks, type ChatSubmission, type ChatSubmissionCallbacks, type ChatThread, type InferParameters, type JsonSchemaObject, type McpCatalog, type McpConnectionOperation, 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 WebCitation, type WebEvidence, type WebPartMetadata, type WebSource, type WidgetDeclaration, type WidgetRenderRequest, buildAgentTools, collectSandboxActivity, createAstralBeamChat, defineTool, describeSandboxCommandRun, hasPendingToolRun, isSandboxTool, isSettledToolCall, lastPartInProgress, readSandboxArtifact, readSandboxCommandRun, readSandboxFileWrite, sandboxRefusal };
package/dist/core.js CHANGED
@@ -1 +1 @@
1
- export { h as ASK_QUESTIONNAIRE_TOOL, g as RENDER_WIDGET_TOOL, _ as SANDBOX_LIST_FILES_TOOL, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, y as SANDBOX_READ_FILE_TOOL, b as SANDBOX_RUN_COMMAND_TOOL, x as SANDBOX_STATUS_EVENT, S as SANDBOX_WRITE_FILE_TOOL, m as buildAgentTools, i as collectSandboxActivity, r as createAstralBeamChat, t as defineTool, a as describeSandboxCommandRun, d as hasPendingToolRun, o as isSandboxTool, f as isSettledToolCall, p as lastPartInProgress, s as readSandboxArtifact, c as readSandboxCommandRun, l as readSandboxFileWrite, u as sandboxRefusal } from "./core-CWFT6Db_.js";
1
+ export { h as ASK_QUESTIONNAIRE_TOOL, g as RENDER_WIDGET_TOOL, _ as SANDBOX_LIST_FILES_TOOL, v as SANDBOX_PUBLISH_ARTIFACT_TOOL, y as SANDBOX_READ_FILE_TOOL, b as SANDBOX_RUN_COMMAND_TOOL, x as SANDBOX_STATUS_EVENT, S as SANDBOX_WRITE_FILE_TOOL, m as buildAgentTools, i as collectSandboxActivity, r as createAstralBeamChat, t as defineTool, a as describeSandboxCommandRun, d as hasPendingToolRun, o as isSandboxTool, f as isSettledToolCall, p as lastPartInProgress, s as readSandboxArtifact, c as readSandboxCommandRun, l as readSandboxFileWrite, u as sandboxRefusal } from "./core-Bn3w2Q44.js";
@@ -6132,14 +6132,8 @@ type AstralBeamChatThemeVariables = Record<`--${string}`, string>;
6132
6132
  interface AstralBeamTokenRequest extends RequestInit {
6133
6133
  url: string;
6134
6134
  }
6135
- /**
6136
- * Where the widget's short-lived chat auth token comes from: an endpoint to POST, or a function that
6137
- * mints the token in the host page and returns `{ token }`, optionally as a promise.
6138
- *
6139
- * Either form runs again on every renewal — near expiry and after a token is rejected — so a
6140
- * rotating credential stays current rather than being captured once. A function that returns
6141
- * `undefined`, or throws, fails authentication closed; the composer's retry link asks again.
6142
- */
6135
+ /** Retrieves a server-minted token on acquisition and renewal, including after a token rejection.
6136
+ * Returning undefined or throwing fails authentication closed. The composer's retry link asks again. */
6143
6137
  type AstralBeamTokenSource = AstralBeamTokenRequest | (() => {
6144
6138
  token: string;
6145
6139
  } | undefined | Promise<{
@@ -6199,12 +6193,8 @@ interface MountAstralBeamChatOptions {
6199
6193
  * hosted cloud; self-hosted deployments must set their own origin.
6200
6194
  */
6201
6195
  apiUrl?: string | undefined;
6202
- /**
6203
- * Where the short-lived chat auth token comes from: `{ url, ...RequestInit }` for a token
6204
- * endpoint, or a function that mints `{ token }` in the host page. Read for every token, so a
6205
- * change applies to the next one, which is minted when the cached token nears expiry. Default
6206
- * `{ url: "/api/astralbeam/token" }`, posted with the page's cookies.
6207
- */
6196
+ /** Endpoint or callback retrieving a server-minted token, read on each acquisition and renewal.
6197
+ * Defaults to POST /api/astralbeam/token with the page's cookies. Never expose API keys here. */
6208
6198
  fetchAstralBeamToken?: AstralBeamTokenSource | undefined;
6209
6199
  /** Host-defined tools the agent can call, executed in the host page, keyed by tool name. */
6210
6200
  tools?: Record<string, ToolDefinition> | undefined;
@@ -6610,7 +6600,7 @@ interface ChatSubmission {
6610
6600
  error?: Error | undefined;
6611
6601
  }
6612
6602
  interface AstralBeamChatState {
6613
- messages: UIMessage[];
6603
+ messages: ChatClientSnapshot["messages"];
6614
6604
  /** The run status: "ready", "submitted", "streaming", or "error". */
6615
6605
  status: ChatClientState;
6616
6606
  /** The latest failure outside a submission, such as history loading or a failed reply. */
@@ -6708,8 +6698,8 @@ declare function isSettledToolCall(part: {
6708
6698
  state: string;
6709
6699
  output?: unknown;
6710
6700
  }): boolean;
6711
- declare function lastPartInProgress(messages: UIMessage[]): boolean;
6712
- declare function hasPendingToolRun(messages: UIMessage[], toolNames: ReadonlySet<string>): boolean;
6701
+ declare function lastPartInProgress(messages: readonly UIMessage[]): boolean;
6702
+ declare function hasPendingToolRun(messages: readonly UIMessage[], toolNames: ReadonlySet<string>): boolean;
6713
6703
  //#endregion
6714
6704
  //#region src/core/sandbox.d.ts
6715
6705
  /**
@@ -6734,7 +6724,7 @@ declare function readSandboxCommandRun(part: ToolCallPart): SandboxCommandRun |
6734
6724
  * The whole conversation's sandbox work, for the panel. A path written more than once appears
6735
6725
  * once, holding its latest content, because the panel answers "what is in the sandbox now".
6736
6726
  */
6737
- declare function collectSandboxActivity(messages: UIMessage[]): SandboxActivity;
6727
+ declare function collectSandboxActivity(messages: readonly UIMessage[]): SandboxActivity;
6738
6728
  /** The one-line summary a finished command carries, in its row's label and under its log. */
6739
6729
  declare function describeSandboxCommandRun(run: SandboxCommandRun): string;
6740
6730
  //#endregion
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { D as AstralBeamChatState, E as AstralBeamChatCoreOptions, T as AstralBeamChatCore } from "./index-DatxwZpZ.js";
1
+ import { D as AstralBeamChatState, E as AstralBeamChatCoreOptions, T as AstralBeamChatCore } from "./index-DGfDkU_f.js";
2
2
  import { CSSProperties, ReactNode } from "react";
3
3
  import { AstralBeamChatAttachmentOptions, AstralBeamChatColorScheme, AstralBeamChatTheme, AstralBeamListingHandle, AstralBeamTokenRequest, AstralBeamTokenSource, InferParameters, JsonSchemaObject, MountAstralBeamChatOptions, MountAstralBeamMcpServersOptions, MountAstralBeamTenantListOptions, MountAstralBeamTenantUserListOptions, MountAstralBeamThreadListOptions, ParametersSchema, ToolDefinition, WidgetDefinition as WidgetDefinition$1, defineTool } from "@astralbeam/sdk/client";
4
4
  //#region src/react/listings.d.ts
@@ -58,7 +58,7 @@ export interface UseAstralBeamChatResult extends AstralBeamChatState {
58
58
  export declare function useAstralBeamChat(options: AstralBeamChatCoreOptions): UseAstralBeamChatResult;
59
59
  /** Imperative surface of a mounted `<AstralBeamChat>`, for hosts that draw their own controls. */
60
60
  export interface AstralBeamChatRef {
61
- /** Clears the conversation: transcript, drafts, attachments, and live widget renders. */
61
+ /** Starts a fresh conversation and clears its draft, preserving saved conversations and their drafts. */
62
62
  reset: () => void;
63
63
  /** Stops the in-flight generation, if any; the transcript keeps what already streamed. */
64
64
  stop: () => void;
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as CORE_OPTION_KEYS, r as createAstralBeamChat } from "./core-CWFT6Db_.js";
1
+ import { n as CORE_OPTION_KEYS, r as createAstralBeamChat } from "./core-Bn3w2Q44.js";
2
2
  import { Ut as DEFAULT_COLOR_SCHEME } from "./api-SpkUzEv3.js";
3
3
  import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";