@astralbeam/sdk 0.7.0 → 0.10.1

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
@@ -1,6 +1,6 @@
1
1
  # @astralbeam/sdk
2
2
 
3
- A drop-in agent chat sidebar for your web app, from [AstralBeam](https://astralbeam.ai), with a headless core underneath when you want to own the UI. The widget renders in a shadow root so its styles never mix with yours, and it streams from an AstralBeam chat endpoint.
3
+ Embed an agent chat sidebar in your web app, or build your own UI with the headless core. The widget isolates its styles in a shadow root.
4
4
 
5
5
  ```sh
6
6
  npm install @astralbeam/sdk
@@ -22,29 +22,29 @@ export function Sidebar() {
22
22
  import { mountAstralBeamChat } from "@astralbeam/sdk/client"
23
23
 
24
24
  const handle = mountAstralBeamChat(document.getElementById("sidebar"), {})
25
- // handle.update({ colorScheme: "dark" }) handle.unmount()
25
+ // Update with handle.update({ colorScheme: "dark" }), then clean up with handle.unmount().
26
26
  ```
27
27
 
28
28
  - The widget fills its container, so give it a parent with a definite height (`min-h-0` in a flex column).
29
- - Two origins by design: chat streams to the hosted cloud by default, while the token comes from your own app's endpoint. Self-hosted deployments set `apiUrl` to their own origin.
30
- - `@astralbeam/sdk/client` ships no React; the chat loads as a lazy chunk with its own bundled copy.
31
- - No runtime dependencies; `react` and `react-dom` are optional peers used only by `@astralbeam/sdk/react`.
29
+ - Chat uses the hosted cloud by default. Tokens come from your application. For self-hosting, set `apiUrl` to your deployment’s `/api` base.
30
+ - `@astralbeam/sdk/client` ships no React. The chat loads as a lazy chunk with its own bundled copy.
31
+ - No runtime dependencies. `react` and `react-dom` are optional peers used only by `@astralbeam/sdk/react`.
32
32
  - Mount it above your router if the transcript should survive page navigation.
33
33
 
34
34
  ## Authentication
35
35
 
36
- The widget will not chat until your app mints it a short-lived chat auth token; it never sees your API key. That token is the credential your server signs for AstralBeam, never your app's own session cookie or access token. See [Authentication](https://app.astralbeam.ai/docs/sdk/authentication).
36
+ Your server must authenticate the host session and mint a chat token before the widget can chat. Keep the API key server-only. See [Authentication](https://app.astralbeam.ai/docs/sdk/authentication).
37
37
 
38
38
  ```ts
39
- import { createChatAuthToken } from "@astralbeam/sdk/server"
39
+ import { createAstralBeamToken } from "@astralbeam/sdk/server"
40
40
 
41
- const apiKey = process.env.ASTRALBEAM_API_KEY // key_<organization>_<key>_abo_<secret>
41
+ const apiKey = process.env.ASTRALBEAM_API_KEY // key_<organizationId>_<id>_abo_<secret>
42
42
 
43
43
  export async function POST(request: Request) {
44
44
  if (!apiKey) return Response.json({ error: "Not configured" }, { status: 503 })
45
45
  const session = await getApplicationSession(request)
46
46
  if (!session) return Response.json({ error: "Unauthenticated" }, { status: 401 })
47
- const token = await createChatAuthToken({
47
+ const token = await createAstralBeamToken({
48
48
  apiKey,
49
49
  user: {
50
50
  id: session.user.id,
@@ -61,36 +61,28 @@ export async function POST(request: Request) {
61
61
  }
62
62
  ```
63
63
 
64
- - Add one endpoint, `/api/astralbeam/token` by default, that authenticates your own session first.
65
- - Your handler owns the response: answer `cache-control: no-store`, and fail closed with a 401 or 503.
66
- - Authenticate once, then derive `user` and `tenant` separately from that same application session.
67
- - Derive `user` and `tenant` from trusted server-side state, never from anything the browser sent.
68
- - Provide stable tenant-local `user.id` and stable `tenant.id` values; names are optional, and set `user.admin` only from trusted state.
69
- - Put custom tenant and tenant-user fields in their respective `metadata` JSON objects; never include secrets.
70
- - SDK fields use camelCase; AstralBeam-owned JWT claims use snake_case, while `metadata` keys are preserved verbatim.
71
- - Tokens use the API key's organization slug as issuer and the platform audience `astralbeam`; AstralBeam does not require or interpret `sub`.
72
- - Tokens are signed, not encrypted: never put a secret in them.
73
- - Lifetimes are 60–600 seconds; the SDK renews in memory before expiry.
74
- - `fetchChatAuthToken` says where the chat auth token comes from: `{ url, ...init }`, which the widget calls as `fetch(url, init)` with a standard `RequestInit`, or a function returning `{ token }` (or a promise of it, or `undefined` when it cannot mint one).
75
- - It defaults to `{ url: "/api/astralbeam/token" }`, posted with the page's cookies, and runs again on every renewal, so a rotating credential stays current.
64
+ - Authenticate once and derive stable `user.id` and `tenant.id` values from that trusted session.
65
+ - Keep API keys server-only. Tokens are signed, not encrypted, so their claims must contain no secrets.
66
+ - Return `Cache-Control: no-store` and fail closed when configuration or authentication is missing.
67
+ - For employee-facing Tenant management, use `createAstralBeamOrganizationToken`. The [API client guide](https://app.astralbeam.ai/docs/sdk/api) covers database-backed roles and browser integration.
76
68
 
77
69
  ## Options
78
70
 
79
- 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://app.astralbeam.ai/docs/sdk/configuration).
80
-
81
- | Option | Default | Meaning |
82
- | ------------------------------------ | ---------------------------------- | ------------------------------------------------------------------ |
83
- | `agentId` | organization's default | `agent_<uuid>`, copied from the dashboard |
84
- | `apiUrl` | `https://app.astralbeam.ai/api` | Base URL of the AstralBeam API; the widget calls `/chat` there |
85
- | `fetchChatAuthToken` | `{ url: "/api/astralbeam/token" }` | Chat auth token endpoint as `{ url, ...RequestInit }`, or a minter |
86
- | `title`, `showHeader` | `"AstralBeam"`, `true` | Header text, and whether the header and reset button show |
87
- | `emptyTitle`, `emptyDescription` | generic copy | Headline and subtitle of the empty transcript |
88
- | `colorScheme`, `theme` | `"system"`, built-in palette | Light/dark/system, and shadcn token overrides |
89
- | `attachments` | `true` | `false` hides the feature, or pass limits |
90
- | `tools`, `widgets` | none | What the agent can do and draw in your app |
91
- | `sandboxPanel` | `false` | Collected sandbox panel: files with downloads, command log |
92
- | `header`, `empty`, `composerActions` | widget's own chrome | Host-rendered replacements (React props; `slots` on the handle) |
93
- | `debug` | `false` | Log every SDK action in the browser and on the server |
71
+ 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://app.astralbeam.ai/docs/sdk/configuration).
72
+
73
+ | Option | Default | Meaning |
74
+ | ------------------------------------ | ---------------------------------- | -------------------------------------------------------------------- |
75
+ | `agentId` | organization's default | `agent_<orgId>_<id>`, copied from the dashboard |
76
+ | `apiUrl` | `https://app.astralbeam.ai/api` | Base URL of the AstralBeam API. The widget calls `/v1/chat` there |
77
+ | `fetchAstralBeamToken` | `{ url: "/api/astralbeam/token" }` | Chat auth token endpoint as `{ url, ...RequestInit }`, or a minter |
78
+ | `title`, `showHeader` | `"AstralBeam"`, `true` | Header text, and whether the header and reset button show |
79
+ | `emptyTitle`, `emptyDescription` | generic copy | Headline and subtitle of the empty transcript |
80
+ | `colorScheme`, `theme` | `"system"`, built-in palette | Light/dark/system, and shadcn token overrides |
81
+ | `attachments` | `true` | `false` hides the feature, or pass limits |
82
+ | `tools`, `widgets` | none | What the agent can do and draw in your app |
83
+ | `sandboxPanel` | `false` | Collected sandbox panel: files with downloads, command log |
84
+ | `header`, `empty`, `composerActions` | widget's own chrome | Host-rendered replacements (React props. `slots` on the handle) |
85
+ | `debug` | `false` | Log SDK actions in the browser, with server logs in development only |
94
86
 
95
87
  A `ref` on `<AstralBeamChat>` (and the vanilla handle) exposes `reset()` and `stop()` for hosts that draw their own controls.
96
88
 
@@ -117,24 +109,23 @@ widgets: {
117
109
  ```
118
110
 
119
111
  - Schemas are plain JSON Schema, or any [Standard Schema](https://standardschema.dev) validator (Zod, Valibot, ArkType).
120
- - Only a Standard Schema validates input in the browser; with plain JSON Schema, treat input as untrusted.
121
- - `defineTool` and `defineWidget` type `execute`/`render` input from a Standard Schema's output.
122
- - In React, `render` returns JSX in your own tree, so state, context, and handlers keep working.
123
- - New tools and widgets reach the agent on its next run.
112
+ - Only a Standard Schema validates input in the browser. With plain JSON Schema, treat input as untrusted.
124
113
 
125
114
  ## Documentation
126
115
 
127
- Each guide is short and self-contained.
128
-
129
- - [Getting started](https://app.astralbeam.ai/docs/sdk/getting-started) install, mount, layout requirements.
130
- - [Authentication](https://app.astralbeam.ai/docs/sdk/authentication) the token endpoint and its security rules.
131
- - [Configuration](https://app.astralbeam.ai/docs/sdk/configuration) every option, and what `update` can change.
132
- - [Theming](https://app.astralbeam.ai/docs/sdk/theming) color schemes, CSS tokens, the shadow-root boundary.
133
- - [Tools and widgets](https://app.astralbeam.ai/docs/sdk/tools-and-widgets) schemas, live state, rendering into the transcript.
134
- - [Attachments](https://app.astralbeam.ai/docs/sdk/attachments) file kinds, limits, what the endpoint enforces.
135
- - [Sandbox](https://app.astralbeam.ai/docs/sdk/sandbox) steps, the opt-in panel, downloads, inline images.
136
- - [Headless](https://app.astralbeam.ai/docs/sdk/headless) own the whole chat UI on the same session.
137
- - [Security model](https://app.astralbeam.ai/docs/sdk/security) who grants, who enforces, what the client can change.
116
+ | Guide | Covers |
117
+ | ------------------------------------------------------------------------- | ------------------------------------------------------- |
118
+ | [API client](https://app.astralbeam.ai/docs/sdk/api) | Typed resource and chat requests with API keys or JWTs. |
119
+ | [Getting started](https://app.astralbeam.ai/docs/sdk/getting-started) | install, mount, layout requirements. |
120
+ | [Authentication](https://app.astralbeam.ai/docs/sdk/authentication) | the token endpoint and its security rules. |
121
+ | [Configuration](https://app.astralbeam.ai/docs/sdk/configuration) | every option, and what `update` can change. |
122
+ | [Theming](https://app.astralbeam.ai/docs/sdk/theming) | color schemes, CSS tokens, the shadow-root boundary. |
123
+ | [Tools and widgets](https://app.astralbeam.ai/docs/sdk/tools-and-widgets) | schemas, live state, rendering into the transcript. |
124
+ | [Attachments](https://app.astralbeam.ai/docs/sdk/attachments) | file kinds, limits, what the endpoint enforces. |
125
+ | [Limits](https://app.astralbeam.ai/docs/sdk/limits) | request, attachment, and sandbox limits. |
126
+ | [Sandbox](https://app.astralbeam.ai/docs/sdk/sandbox) | steps, the opt-in panel, downloads, inline images. |
127
+ | [Headless](https://app.astralbeam.ai/docs/sdk/headless) | own the whole chat UI on the same session. |
128
+ | [Security model](https://app.astralbeam.ai/docs/sdk/security) | who grants, who enforces, what the client can change. |
138
129
 
139
130
  ## Entry points
140
131
 
@@ -145,9 +136,10 @@ There is no root export. Conversation history is not built yet.
145
136
  | `@astralbeam/sdk/client` | `mountAstralBeamChat`, the vanilla loader | none |
146
137
  | `@astralbeam/sdk/core` | `createAstralBeamChat`, the headless session | none |
147
138
  | `@astralbeam/sdk/react` | `<AstralBeamChat>`, `useAstralBeamChat` | `react`, `react-dom` |
148
- | `@astralbeam/sdk/server` | `createChatAuthToken`, the token minter | none |
139
+ | `@astralbeam/sdk/server` | Tenant and organization token minters | none |
140
+ | `@astralbeam/sdk/api` | Resource and chat HTTP helpers | none |
149
141
 
150
- Types resolve under every TypeScript module resolution mode, including the classic `"moduleResolution": "node"` that Ionic, Capacitor, and Create React App templates still ship. TypeScript 5.0 or later is required, because the declarations use `const` type parameters; on TypeScript 4.x the `.d.ts` files fail to parse.
142
+ Types resolve under every TypeScript module resolution mode, including classic `"moduleResolution": "node"`. Requires TypeScript 5.0 or later because declarations use `const` type parameters, which fail to parse on TypeScript 4.x.
151
143
 
152
144
  ## Example
153
145
 
@@ -0,0 +1,296 @@
1
+ //#region src/lib/constants.ts
2
+ /** Base URL of the AstralBeam API when the mount options give none; versioned routes hang off it. */
3
+ const DEFAULT_API_URL = "https://app.astralbeam.ai/api";
4
+ /** Host endpoint that mints chat JWTs when the mount options give none. */
5
+ const DEFAULT_CHAT_AUTH_TOKEN_URL = "/api/astralbeam/token";
6
+ /** Color scheme used when the mount options and the React prop give none. */
7
+ const DEFAULT_COLOR_SCHEME = "system";
8
+ //#endregion
9
+ //#region src/api/api.ts
10
+ function isAstralBeamApiError(error) {
11
+ return error instanceof Error && error.name === "AstralBeamApiError" && "status" in error && typeof error.status === "number";
12
+ }
13
+ function resolveApiUrl(path, apiUrl = DEFAULT_API_URL) {
14
+ return `${apiUrl.replace(/\/+$/, "")}${path.replace(/^\/api(?=\/)/, "")}`;
15
+ }
16
+ function isApiErrorBody(value, status) {
17
+ if (!value || typeof value !== "object") return false;
18
+ const body = value;
19
+ return body.status === status && typeof body.type === "string" && typeof body.title === "string" && typeof body.detail === "string" && (body.issues === void 0 || Array.isArray(body.issues) && body.issues.every((issue) => issue && typeof issue.path === "string" && typeof issue.message === "string"));
20
+ }
21
+ async function apiResponse(path, options) {
22
+ const { apiUrl, apiKey, astralBeamToken, fetchClient = globalThis.fetch, ...init } = options;
23
+ const headers = new Headers();
24
+ const entries = init.headers instanceof Headers ? init.headers.entries() : Array.isArray(init.headers) ? init.headers : Object.entries(init.headers ?? {});
25
+ for (const [name, value] of entries) headers.set(name, value);
26
+ headers.delete("authorization");
27
+ headers.delete("x-api-key");
28
+ if (apiKey) headers.set("x-api-key", apiKey);
29
+ if (astralBeamToken) headers.set("authorization", `Bearer ${astralBeamToken}`);
30
+ const response = await fetchClient(resolveApiUrl(path, apiUrl), {
31
+ ...init,
32
+ headers
33
+ });
34
+ if (response.ok) return response;
35
+ const value = await response.json().catch((error) => {
36
+ if (!(error instanceof SyntaxError)) throw error;
37
+ });
38
+ const body = isApiErrorBody(value, response.status) ? value : void 0;
39
+ throw Object.assign(new Error(body?.detail ?? `AstralBeam API returned HTTP ${response.status}`), {
40
+ name: "AstralBeamApiError",
41
+ status: response.status,
42
+ headers: response.headers,
43
+ body
44
+ });
45
+ }
46
+ async function astralBeamApiFetch(path, options) {
47
+ return await (await apiResponse(path, options)).json();
48
+ }
49
+ function astralBeamJwtFetch(path, options) {
50
+ return astralBeamApiFetch(path, options);
51
+ }
52
+ function astralBeamChatFetch(path, options) {
53
+ return apiResponse(path, options);
54
+ }
55
+ function astralBeamFileFetch(path, options = {}) {
56
+ return apiResponse(path, options);
57
+ }
58
+ //#endregion
59
+ //#region src/api/generated/api.ts
60
+ /**
61
+ * Generated by Orval. Do not edit by hand.
62
+ */
63
+ const ListUsersForTenantFilterAdmin = {
64
+ true: "true",
65
+ false: "false"
66
+ };
67
+ const getListTenantsUrl = (params) => {
68
+ const normalizedParams = new URLSearchParams();
69
+ Object.entries(params || {}).forEach(([key, value]) => {
70
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : String(value));
71
+ });
72
+ const stringifiedParams = normalizedParams.toString();
73
+ return stringifiedParams.length > 0 ? `/api/v1/tenants?${stringifiedParams}` : `/api/v1/tenants`;
74
+ };
75
+ /**
76
+ * List Tenants in internal ID order. q searches name or external ID as a case-insensitive literal substring. filter[external_id] adds an exact match. Organization keys and organization-management JWTs see their organization; admin tenant JWTs see only their signed Tenant. Keep filters unchanged when reusing cursors. Live listing, not a snapshot.
77
+ * @summary List Tenants
78
+ */
79
+ const listTenants = (params, options) => {
80
+ return astralBeamApiFetch(getListTenantsUrl(params), {
81
+ ...options,
82
+ method: "GET"
83
+ });
84
+ };
85
+ const getCreateTenantUrl = () => {
86
+ return `/api/v1/tenants`;
87
+ };
88
+ /**
89
+ * Create a Tenant with an exact customer-provided external_id. Requires an organization API key or organization-management JWT with a current owner/developer role. An external_id already used in this organization returns 409; creation never upserts.
90
+ * @summary Create a Tenant
91
+ */
92
+ const createTenant = (createTenantInput, options) => {
93
+ const getHeaders = (h) => {
94
+ if (!h) return {};
95
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
96
+ if (Symbol.iterator in h) return Object.fromEntries(Array.from(h, (entry) => Array.from(entry)));
97
+ const headers = {};
98
+ for (const [name, value] of Object.entries(h)) if (value !== void 0) headers[name] = value;
99
+ return headers;
100
+ };
101
+ return astralBeamApiFetch(getCreateTenantUrl(), {
102
+ ...options,
103
+ method: "POST",
104
+ headers: {
105
+ "Content-Type": "application/json",
106
+ ...getHeaders(options?.headers)
107
+ },
108
+ body: JSON.stringify(createTenantInput)
109
+ });
110
+ };
111
+ const getGetTenantUrl = (id) => {
112
+ return `/api/v1/tenants/${encodeURIComponent(String(id))}`;
113
+ };
114
+ /**
115
+ * Get a Tenant by internal UUID, not external_id.
116
+ * @summary Get a Tenant
117
+ */
118
+ const getTenant = (id, options) => {
119
+ return astralBeamApiFetch(getGetTenantUrl(id), {
120
+ ...options,
121
+ method: "GET"
122
+ });
123
+ };
124
+ const getUpdateTenantUrl = (id) => {
125
+ return `/api/v1/tenants/${encodeURIComponent(String(id))}`;
126
+ };
127
+ /**
128
+ * Update supplied name/metadata fields only. Requires an organization API key or organization-management JWT with a current owner/developer role. name:null clears the name; metadata replaces the object. Last-write-wins; no upsert.
129
+ * @summary Update a Tenant
130
+ */
131
+ const updateTenant = (id, updateTenantInput, options) => {
132
+ const getHeaders = (h) => {
133
+ if (!h) return {};
134
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
135
+ if (Symbol.iterator in h) return Object.fromEntries(Array.from(h, (entry) => Array.from(entry)));
136
+ const headers = {};
137
+ for (const [name, value] of Object.entries(h)) if (value !== void 0) headers[name] = value;
138
+ return headers;
139
+ };
140
+ return astralBeamApiFetch(getUpdateTenantUrl(id), {
141
+ ...options,
142
+ method: "PATCH",
143
+ headers: {
144
+ "Content-Type": "application/json",
145
+ ...getHeaders(options?.headers)
146
+ },
147
+ body: JSON.stringify(updateTenantInput)
148
+ });
149
+ };
150
+ const getListUsersForTenantUrl = (tenantId, params) => {
151
+ const normalizedParams = new URLSearchParams();
152
+ Object.entries(params || {}).forEach(([key, value]) => {
153
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : String(value));
154
+ });
155
+ const stringifiedParams = normalizedParams.toString();
156
+ return stringifiedParams.length > 0 ? `/api/v1/tenants/${encodeURIComponent(String(tenantId))}/tenant_users?${stringifiedParams}` : `/api/v1/tenants/${encodeURIComponent(String(tenantId))}/tenant_users`;
157
+ };
158
+ /**
159
+ * List users of one Tenant in internal ID order. q searches name or external ID as a case-insensitive literal substring. Exact filter[external_id] and filter[admin] combine with AND. No matching user returns an empty page; missing and out-of-scope Tenants return 404. Cursors cannot be reused for another Tenant or filter. Live listing, not a snapshot.
160
+ * @summary List TenantUsers
161
+ */
162
+ const listUsersForTenant = (tenantId, params, options) => {
163
+ return astralBeamApiFetch(getListUsersForTenantUrl(tenantId, params), {
164
+ ...options,
165
+ method: "GET"
166
+ });
167
+ };
168
+ const getCreateTenantUserUrl = (tenantId) => {
169
+ return `/api/v1/tenants/${encodeURIComponent(String(tenantId))}/tenant_users`;
170
+ };
171
+ /**
172
+ * Create a TenantUser under the internal tenant_id path identifier, with a customer-provided tenant-local external_id. An external_id already used in this Tenant returns 409; the same external_id in another Tenant is allowed. Stored admin does not change signed JWT authority.
173
+ * @summary Create a TenantUser
174
+ */
175
+ const createTenantUser = (tenantId, createTenantUserInput, options) => {
176
+ const getHeaders = (h) => {
177
+ if (!h) return {};
178
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
179
+ if (Symbol.iterator in h) return Object.fromEntries(Array.from(h, (entry) => Array.from(entry)));
180
+ const headers = {};
181
+ for (const [name, value] of Object.entries(h)) if (value !== void 0) headers[name] = value;
182
+ return headers;
183
+ };
184
+ return astralBeamApiFetch(getCreateTenantUserUrl(tenantId), {
185
+ ...options,
186
+ method: "POST",
187
+ headers: {
188
+ "Content-Type": "application/json",
189
+ ...getHeaders(options?.headers)
190
+ },
191
+ body: JSON.stringify(createTenantUserInput)
192
+ });
193
+ };
194
+ const getGetTenantUserUrl = (tenantId, id) => {
195
+ return `/api/v1/tenants/${encodeURIComponent(String(tenantId))}/tenant_users/${encodeURIComponent(String(id))}`;
196
+ };
197
+ /**
198
+ * Get a TenantUser by the internal tenant_id and id pair within the authorized scope. No identity upsert.
199
+ * @summary Get a TenantUser
200
+ */
201
+ const getTenantUser = (tenantId, id, options) => {
202
+ return astralBeamApiFetch(getGetTenantUserUrl(tenantId, id), {
203
+ ...options,
204
+ method: "GET"
205
+ });
206
+ };
207
+ const getUpdateTenantUserUrl = (tenantId, id) => {
208
+ return `/api/v1/tenants/${encodeURIComponent(String(tenantId))}/tenant_users/${encodeURIComponent(String(id))}`;
209
+ };
210
+ /**
211
+ * Update supplied name/metadata/admin fields only. Stored admin does not grant or revoke JWT authority. name:null clears the name; metadata replaces the object.
212
+ * @summary Update a TenantUser
213
+ */
214
+ const updateTenantUser = (tenantId, id, updateTenantUserInput, options) => {
215
+ const getHeaders = (h) => {
216
+ if (!h) return {};
217
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
218
+ if (Symbol.iterator in h) return Object.fromEntries(Array.from(h, (entry) => Array.from(entry)));
219
+ const headers = {};
220
+ for (const [name, value] of Object.entries(h)) if (value !== void 0) headers[name] = value;
221
+ return headers;
222
+ };
223
+ return astralBeamApiFetch(getUpdateTenantUserUrl(tenantId, id), {
224
+ ...options,
225
+ method: "PATCH",
226
+ headers: {
227
+ "Content-Type": "application/json",
228
+ ...getHeaders(options?.headers)
229
+ },
230
+ body: JSON.stringify(updateTenantUserInput)
231
+ });
232
+ };
233
+ const getRunChatUrl = () => {
234
+ return `/api/v1/chat`;
235
+ };
236
+ /**
237
+ * Stream an AG-UI agent run using a tenant user JWT. No admin claim required. HTTP failures before streaming use AstralBeamApiError. Once streaming starts, failures use RUN_ERROR events. Tool results continue in a subsequent request. Disconnecting cancels the run. Limited to 20 requests per minute per organization, tenant, and user.
238
+ * @summary Run chat
239
+ */
240
+ const runChat = (chatRunInput, options) => {
241
+ const getHeaders = (h) => {
242
+ if (!h) return {};
243
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
244
+ if (Symbol.iterator in h) return Object.fromEntries(Array.from(h, (entry) => Array.from(entry)));
245
+ const headers = {};
246
+ for (const [name, value] of Object.entries(h)) if (value !== void 0) headers[name] = value;
247
+ return headers;
248
+ };
249
+ return astralBeamChatFetch(getRunChatUrl(), {
250
+ ...options,
251
+ method: "POST",
252
+ headers: {
253
+ "Content-Type": "application/json",
254
+ ...getHeaders(options?.headers)
255
+ },
256
+ body: JSON.stringify(chatRunInput)
257
+ });
258
+ };
259
+ const getGetChatConfigUrl = (params) => {
260
+ const normalizedParams = new URLSearchParams();
261
+ Object.entries(params || {}).forEach(([key, value]) => {
262
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : String(value));
263
+ });
264
+ const stringifiedParams = normalizedParams.toString();
265
+ return stringifiedParams.length > 0 ? `/api/v1/chat/config?${stringifiedParams}` : `/api/v1/chat/config`;
266
+ };
267
+ /**
268
+ * Read the selected agent's attachment grant using a tenant user JWT. Omit agentId to use the organization's default agent. Client settings may narrow this grant, never widen it.
269
+ * @summary Get chat capabilities
270
+ */
271
+ const getChatConfig = (params, options) => {
272
+ return astralBeamJwtFetch(getGetChatConfigUrl(params), {
273
+ ...options,
274
+ method: "GET"
275
+ });
276
+ };
277
+ const getGetChatFileUrl = (params) => {
278
+ const normalizedParams = new URLSearchParams();
279
+ Object.entries(params || {}).forEach(([key, value]) => {
280
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : String(value));
281
+ });
282
+ const stringifiedParams = normalizedParams.toString();
283
+ return stringifiedParams.length > 0 ? `/api/v1/chat/files?${stringifiedParams}` : `/api/v1/chat/files`;
284
+ };
285
+ /**
286
+ * Use the signed ticket returned when chat publishes an artifact. No bearer token is required. Returns the original bytes with a content-sniffed Content-Type and Content-Disposition filename. Invalid or expired tickets, a missing sandbox, and rejected artifact checks return 404. Provider or file-read failures return 500.
287
+ * @summary Download a chat artifact
288
+ */
289
+ const getChatFile = (params, options) => {
290
+ return astralBeamFileFetch(getGetChatFileUrl(params), {
291
+ ...options,
292
+ method: "GET"
293
+ });
294
+ };
295
+ //#endregion
296
+ export { updateTenantUser as C, DEFAULT_CHAT_AUTH_TOKEN_URL as D, resolveApiUrl as E, DEFAULT_COLOR_SCHEME as O, updateTenant as S, isAstralBeamApiError as T, getUpdateTenantUrl as _, getChatFile as a, listUsersForTenant as b, getGetChatConfigUrl as c, getGetTenantUserUrl as d, getListTenantsUrl as f, getTenantUser as g, getTenant as h, getChatConfig as i, getGetChatFileUrl as l, getRunChatUrl as m, createTenant as n, getCreateTenantUrl as o, getListUsersForTenantUrl as p, createTenantUser as r, getCreateTenantUserUrl as s, ListUsersForTenantFilterAdmin as t, getGetTenantUrl as u, getUpdateTenantUserUrl as v, astralBeamChatFetch as w, runChat as x, listTenants as y };