@astralbeam/sdk 0.5.0 → 0.7.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.
@@ -3305,6 +3305,47 @@ interface ToolDefinition {
3305
3305
  */
3306
3306
  execute: (input: Record<string, unknown>) => unknown | Promise<unknown>;
3307
3307
  }
3308
+ /**
3309
+ * Limits and accepted types for the composer's file attachments. Every field is optional;
3310
+ * omitting the whole option leaves attachments enabled with the defaults below.
3311
+ */
3312
+ interface AstralBeamChatAttachmentOptions {
3313
+ /** Hides the attach button (and ignores drops and pastes) when `false`. Default `true`. */
3314
+ enabled?: boolean | undefined;
3315
+ /** How many files one message may carry. Default `5`. */
3316
+ maxFiles?: number | undefined;
3317
+ /**
3318
+ * Ceiling for a single file, in bytes. The widget also applies its own per-kind caps
3319
+ * (5 MB image, 10 MB PDF, 1 MB text file), so the smaller of the two wins.
3320
+ */
3321
+ maxFileBytes?: number | undefined;
3322
+ /** Ceiling for all files on one message, in bytes. Default 20 MB. */
3323
+ maxTotalBytes?: number | undefined;
3324
+ /**
3325
+ * Narrows what the composer takes, as MIME types or `type/*` patterns (`["image/*"]` for
3326
+ * images only). Omit to accept everything the chat endpoint supports: PNG, JPEG, WebP and
3327
+ * GIF images, PDFs, and text files (which the endpoint reads as text for the agent).
3328
+ */
3329
+ accept?: readonly string[] | undefined;
3330
+ }
3331
+ /**
3332
+ * Draws host content into `container`, a light-DOM element the widget projects into the
3333
+ * named area. May return a cleanup, called when the slot is replaced and on unmount.
3334
+ */
3335
+ type AstralBeamChatSlotRenderer = (container: HTMLElement) => (() => void) | void;
3336
+ /** Host-rendered replacements for the widget's own chrome; each renders in the host page's style. */
3337
+ interface AstralBeamChatSlots {
3338
+ /** Replaces the header's content (title and reset button); `showHeader: false` still hides the row. */
3339
+ header?: AstralBeamChatSlotRenderer | undefined;
3340
+ /** Replaces the empty-transcript state (icon, headline, and subtitle). */
3341
+ empty?: AstralBeamChatSlotRenderer | undefined;
3342
+ /** Extra controls at the end of the composer's button row, next to send. */
3343
+ composerActions?: AstralBeamChatSlotRenderer | undefined;
3344
+ }
3345
+ /** Color scheme of the chat widget; `"system"` follows the OS `prefers-color-scheme` setting. */
3346
+ type AstralBeamChatColorScheme = "light" | "dark" | "system";
3347
+ /** Overrides for the widget's theming CSS variables, keyed by custom-property name (`"--primary"`). */
3348
+ type AstralBeamChatThemeVariables = Record<`--${string}`, string>;
3308
3349
  /**
3309
3350
  * A token endpoint to call: `{ url, ...init }`, which the widget calls as `fetch(url, init)` with
3310
3351
  * this object's remaining, standard `RequestInit` fields. The init defaults to `POST`,
@@ -3315,18 +3356,91 @@ interface AstralBeamChatAuthTokenRequest extends RequestInit {
3315
3356
  url: string;
3316
3357
  }
3317
3358
  /**
3318
- * Where the widget's short-lived chat JWT comes from: an endpoint to POST, or a function that
3359
+ * Where the widget's short-lived chat auth token comes from: an endpoint to POST, or a function that
3319
3360
  * mints the token in the host page and returns `{ token }`, optionally as a promise.
3320
3361
  *
3321
3362
  * Either form runs again on every renewal — near expiry and after a token is rejected — so a
3322
3363
  * rotating credential stays current rather than being captured once. A function that returns
3323
3364
  * `undefined`, or throws, fails authentication closed; the composer's retry link asks again.
3324
3365
  */
3325
- type AstralBeamChatGenerateAuthToken = AstralBeamChatAuthTokenRequest | (() => {
3366
+ type AstralBeamChatAuthTokenSource = AstralBeamChatAuthTokenRequest | (() => {
3326
3367
  token: string;
3327
3368
  } | undefined | Promise<{
3328
3369
  token: string;
3329
3370
  } | undefined>);
3371
+ /**
3372
+ * Custom values for the CSS variables the widget's shadcn theme exposes (`--background`,
3373
+ * `--primary`, `--radius`, and the `--font-sans`/`--font-heading`/`--font-mono` stacks, ...),
3374
+ * mirroring shadcn's `:root`/`.dark` split: `light` is the base applied in both color schemes,
3375
+ * and `dark` overrides it when the resolved scheme is dark.
3376
+ */
3377
+ interface AstralBeamChatTheme {
3378
+ light?: AstralBeamChatThemeVariables | undefined;
3379
+ dark?: AstralBeamChatThemeVariables | undefined;
3380
+ }
3381
+ /**
3382
+ * Every option of the drop-in chat widget, and the one documented source the React props and the
3383
+ * headless core options are derived from. Each is optional and accepts an explicit `undefined`, so
3384
+ * a host with `exactOptionalPropertyTypes` can pass a value it does not have yet.
3385
+ */
3386
+ interface MountAstralBeamChatOptions {
3387
+ /**
3388
+ * Public ID of the organization-owned agent. Omit it to use the organization's default agent,
3389
+ * which the dashboard's agents page selects. A change answers the next run with the new agent
3390
+ * and keeps the transcript, which that agent then sees as history.
3391
+ */
3392
+ agentId?: string | undefined;
3393
+ /** Name shown in the widget's header. Default `"AstralBeam"`. */
3394
+ title?: string | undefined;
3395
+ /**
3396
+ * Shows the widget's header, which carries the title and the reset button. `false` hides both
3397
+ * and gives the transcript the full height. Default `true`.
3398
+ */
3399
+ showHeader?: boolean | undefined;
3400
+ /** Headline shown on the empty transcript. Default `"Ask the assistant"`. */
3401
+ emptyTitle?: string | undefined;
3402
+ /** Subtitle shown under the empty transcript's headline. Default describes the app's tools and widgets. */
3403
+ emptyDescription?: string | undefined;
3404
+ /**
3405
+ * Base URL of the AstralBeam API; the widget calls `/chat` and its subroutes under it. Read for
3406
+ * every request, so a change moves the next one. Default `"https://app.astralbeam.ai/api"`, the
3407
+ * hosted cloud; self-hosted deployments must set their own origin.
3408
+ */
3409
+ apiUrl?: string | undefined;
3410
+ /**
3411
+ * Where the short-lived chat auth token comes from: `{ url, ...RequestInit }` for a token
3412
+ * endpoint, or a function that mints `{ token }` in the host page. Read for every token, so a
3413
+ * change applies to the next one, which is minted when the cached token nears expiry. Default
3414
+ * `{ url: "/api/astralbeam/token" }`, posted with the page's cookies.
3415
+ */
3416
+ fetchChatAuthToken?: AstralBeamChatAuthTokenSource | undefined;
3417
+ /** Host-defined tools the agent can call, executed in the host page, keyed by tool name. */
3418
+ tools?: Record<string, ToolDefinition> | undefined;
3419
+ /** Host-defined widgets the agent can render inline in the conversation, keyed by identifier. */
3420
+ widgets?: Record<string, WidgetDefinition> | undefined;
3421
+ /** Host-rendered replacements for parts of the widget's chrome; see `AstralBeamChatSlots`. */
3422
+ slots?: AstralBeamChatSlots | undefined;
3423
+ /**
3424
+ * Shows the collected sandbox panel (every file the agent wrote, with downloads, and the full
3425
+ * command log) above the composer once the sandbox has done work. Off by default: the
3426
+ * transcript already shows each step where it happened. Default `false`.
3427
+ */
3428
+ sandboxPanel?: boolean | undefined;
3429
+ /**
3430
+ * File attachments in the composer, on by default. `false` turns them off; an options object
3431
+ * narrows the limits and accepted types.
3432
+ */
3433
+ attachments?: boolean | AstralBeamChatAttachmentOptions | undefined;
3434
+ /** Color scheme of the widget. Default `"system"`. */
3435
+ colorScheme?: AstralBeamChatColorScheme | undefined;
3436
+ /** Custom values for the widget's theming CSS variables, per color scheme. */
3437
+ theme?: AstralBeamChatTheme | undefined;
3438
+ /**
3439
+ * Logs every SDK action to the browser console with UTC timestamps and full payloads,
3440
+ * and asks the endpoint (via the forwarded props) to log its side of the run too.
3441
+ */
3442
+ debug?: boolean | undefined;
3443
+ }
3330
3444
  //#endregion
3331
3445
  //#region src/lib/debug.d.ts
3332
3446
  declare const CATEGORY_COLORS: {
@@ -3608,26 +3722,38 @@ interface WidgetRenderRequest {
3608
3722
  props: Record<string, unknown>;
3609
3723
  /** Keys the render: a repeat of the same call replaces its own render, not another's. */
3610
3724
  toolCallId: string;
3611
- }
3612
- interface AstralBeamChatCoreOptions {
3613
- /** Public ID of the organization-owned agent; omitted, the organization's default answers. */
3614
- agentId?: string | undefined;
3615
- /** Base URL of the AstralBeam API; `/chat` hangs off it. Default the hosted cloud. */
3616
- apiUrl?: string | undefined;
3617
3725
  /**
3618
- * Where short-lived chat JWTs come from: `{ url, ...RequestInit }` for a token endpoint, or a
3619
- * function minting `{ token }` in the host page. Either runs again on every renewal.
3620
- * Default `{ url: "/api/astralbeam/token" }`.
3726
+ * Drops this session's copy of the render's cleanup, for a host that disposed the render itself;
3727
+ * without it the cleanup and the DOM it captures is held until the next reset.
3621
3728
  */
3622
- generateAuthToken?: AstralBeamChatGenerateAuthToken | undefined;
3623
- /** Host tools the agent can call; `execute` runs wherever this session lives. */
3624
- tools?: Record<string, ToolDefinition> | undefined;
3625
- /** Widgets declared to the agent; `onRenderWidget` is asked to draw them. */
3729
+ release: () => void;
3730
+ }
3731
+ /**
3732
+ * Mirrors of the underlying chat client's stream lifecycle, for a consumer that logs or traces the
3733
+ * raw run; the drop-in widget passes its debug console logger here.
3734
+ */
3735
+ interface ChatStreamCallbacks {
3736
+ onChunk?: ((chunk: StreamChunk) => void) | undefined;
3737
+ onResponse?: ((response?: Response) => void) | undefined;
3738
+ onFinish?: ((message: UIMessage) => void) | undefined;
3739
+ onError?: ((error: Error) => void) | undefined;
3740
+ }
3741
+ /**
3742
+ * The transport and tool options of the drop-in widget, minus everything about its UI, plus this
3743
+ * session's own rendering hooks. The shared options are documented on `MountAstralBeamChatOptions`.
3744
+ */
3745
+ interface AstralBeamChatCoreOptions extends Pick<MountAstralBeamChatOptions, "agentId" | "apiUrl" | "fetchChatAuthToken" | "tools" | "debug"> {
3746
+ /** Widgets declared to the agent, without a `render`; `onRenderWidget` is asked to draw them. */
3626
3747
  widgets?: Record<string, WidgetDeclaration> | undefined;
3627
3748
  /** Draws an agent-requested widget however the host wants; may return a cleanup. */
3628
3749
  onRenderWidget?: ((request: WidgetRenderRequest) => (() => void) | void) | undefined;
3629
- /** Logs every action to the console and asks the endpoint to log its side too. */
3630
- debug?: boolean | undefined;
3750
+ /** Stream lifecycle callbacks, read per event so they follow an update. */
3751
+ streamCallbacks?: ChatStreamCallbacks | undefined;
3752
+ }
3753
+ /** One tool as declared to the agent: its name, and the `metadata.title` that labels it. */
3754
+ interface AgentToolInfo {
3755
+ name: string;
3756
+ title: string | undefined;
3631
3757
  }
3632
3758
  interface AstralBeamChatState {
3633
3759
  messages: UIMessage[];
@@ -3639,6 +3765,8 @@ interface AstralBeamChatState {
3639
3765
  capabilities: {
3640
3766
  attachments: boolean;
3641
3767
  };
3768
+ /** The tool set currently declared to the agent, in declaration order. */
3769
+ agentTools: readonly AgentToolInfo[];
3642
3770
  sandboxStatus: SandboxStatus | undefined;
3643
3771
  sandbox: SandboxActivity;
3644
3772
  }
@@ -3657,6 +3785,8 @@ interface AstralBeamChatCore {
3657
3785
  addToolResult: ChatClient["addToolResult"];
3658
3786
  /** Stops the in-flight generation; the transcript keeps what already streamed. */
3659
3787
  stop: () => void;
3788
+ /** Mints a fresh chat auth token, ignoring the cached one; for a retry after a failure. */
3789
+ retryAuthentication: () => void;
3660
3790
  /** Re-runs the last exchange. */
3661
3791
  reload: () => Promise<void>;
3662
3792
  /** Clears the conversation and disposes live widget renders. */
@@ -3724,14 +3854,7 @@ interface TypedToolDefinition<S extends ParametersSchema = JsonSchemaObject> {
3724
3854
  parameters?: S;
3725
3855
  execute: (input: InferParameters<S>) => unknown | Promise<unknown>;
3726
3856
  }
3727
- interface TypedWidgetDefinition<S extends ParametersSchema = JsonSchemaObject> {
3728
- description: string;
3729
- parameters?: S;
3730
- render: (props: InferParameters<S>, container: HTMLElement) => (() => void) | void;
3731
- }
3732
3857
  /** Declares a host tool; a Standard Schema `parameters` types (and validates) `execute`'s input. */
3733
3858
  declare function defineTool<const S extends ParametersSchema = JsonSchemaObject>(tool: TypedToolDefinition<S>): ToolDefinition;
3734
- /** Declares a host widget; a Standard Schema `parameters` types (and validates) `render`'s props. */
3735
- declare function defineWidget<const S extends ParametersSchema = JsonSchemaObject>(widget: TypedWidgetDefinition<S>): WidgetDefinition;
3736
3859
  //#endregion
3737
- export { SandboxActivity as A, AstralBeamChatState as C, WidgetDeclaration as D, ChatAuthenticationState as E, InferParameters as F, JsonSchemaObject as I, ParametersSchema as L, SandboxCommandRun as M, SandboxFileWrite as N, buildAgentTools as O, SandboxStatus as P, StandardSchemaV1 as R, AstralBeamChatCoreOptions as S, createAstralBeamChat as T, sandboxRefusal as _, SANDBOX_LIST_FILES_TOOL as a, lastPartInProgress as b, SANDBOX_RUN_COMMAND_TOOL as c, collectSandboxActivity as d, describeSandboxCommandRun as f, readSandboxFileWrite as g, readSandboxCommandRun as h, RENDER_WIDGET_TOOL as i, SandboxArtifact as j, RenderWidgetInput as k, SANDBOX_STATUS_EVENT as l, readSandboxArtifact as m, defineWidget as n, SANDBOX_PUBLISH_ARTIFACT_TOOL as o, isSandboxTool as p, ASK_QUESTIONNAIRE_TOOL as r, SANDBOX_READ_FILE_TOOL as s, defineTool as t, SANDBOX_WRITE_FILE_TOOL as u, hasPendingToolRun as v, WidgetRenderRequest as w, AstralBeamChatCore as x, isSettledToolCall as y, ToolDefinition as z };
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 };
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-8NUgB59m.js";
1
+ import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-CudekMKM.js";
2
2
  import { ReactNode } from "react";
3
- import { AstralBeamChatAttachmentOptions, AstralBeamChatAuthTokenRequest, AstralBeamChatColorScheme, AstralBeamChatGenerateAuthToken, AstralBeamChatTheme, InferParameters, JsonSchemaObject, ParametersSchema, ToolDefinition, WidgetDefinition as WidgetDefinition$1, defineTool } from "@astralbeam/sdk/client";
3
+ import { AstralBeamChatAttachmentOptions, AstralBeamChatAuthTokenRequest, AstralBeamChatAuthTokenSource, AstralBeamChatColorScheme, AstralBeamChatTheme, InferParameters, JsonSchemaObject, MountAstralBeamChatOptions, ParametersSchema, ToolDefinition, WidgetDefinition as WidgetDefinition$1, defineTool } from "@astralbeam/sdk/client";
4
4
 
5
5
  //#region src/react/index.d.ts
6
6
  interface WidgetDefinition extends Omit<WidgetDefinition$1, "render"> {
@@ -38,60 +38,21 @@ interface AstralBeamChatRef {
38
38
  /** Stops the in-flight generation, if any; the transcript keeps what already streamed. */
39
39
  stop: () => void;
40
40
  }
41
- interface AstralBeamChatProps {
42
- /**
43
- * Public ID of the organization-owned agent. Omit it to use the organization's default agent,
44
- * which the dashboard's agents page selects. A change answers the next run with the new agent
45
- * and keeps the transcript, which that agent then sees as history.
46
- */
47
- agentId?: string;
48
- /** Name shown in the widget's header. Default `"AstralBeam"`. */
49
- title?: string;
50
- /**
51
- * Shows the widget's header with the title and the reset button; `false` hides both and gives
52
- * the transcript the full height. Default `true`.
53
- */
54
- showHeader?: boolean;
41
+ /**
42
+ * Every mount option as a prop, with the DOM-rendering fields replaced by React ones: a widget
43
+ * renders JSX, and the chrome slots take nodes instead of renderers. The shared options are
44
+ * documented once, on `MountAstralBeamChatOptions`.
45
+ */
46
+ interface AstralBeamChatProps extends Omit<MountAstralBeamChatOptions, "widgets" | "slots"> {
47
+ /** Host-defined widgets the agent can render inline in the conversation, keyed by identifier. */
48
+ widgets?: Record<string, WidgetDefinition> | undefined;
55
49
  /** Replaces the header's content with the host's own React content; `showHeader` still applies. */
56
50
  header?: ReactNode;
57
51
  /** Replaces the empty-transcript state with the host's own React content. */
58
52
  empty?: ReactNode;
59
53
  /** Extra host controls at the end of the composer's button row, next to send. */
60
54
  composerActions?: ReactNode;
61
- /** Headline shown on the empty transcript. Default `"Ask the assistant"`. */
62
- emptyTitle?: string;
63
- /** Subtitle under the empty transcript's headline. */
64
- emptyDescription?: string;
65
- /**
66
- * Base URL of the AstralBeam API; the widget calls `/chat` under it. Read per request, so a
67
- * change moves the next one. Default the hosted cloud.
68
- */
69
- apiUrl?: string;
70
- /**
71
- * Where the short-lived chat JWT comes from: `{ url, ...RequestInit }` for a token endpoint, or
72
- * a function minting `{ token }`, optionally a promise, in the host app. Read per token, and
73
- * the function form runs in the host's React tree, so an inline closure over current auth state
74
- * is fine and needs no memoization. Default `{ url: "/api/astralbeam/token" }`.
75
- */
76
- generateAuthToken?: AstralBeamChatGenerateAuthToken;
77
- /** Host-defined tools the agent can call, executed in the host's React app, keyed by name. */
78
- tools?: Record<string, ToolDefinition>;
79
- /** Host-defined widgets the agent can render inline in the conversation, keyed by identifier. */
80
- widgets?: Record<string, WidgetDefinition>;
81
- /** Color scheme of the chat widget. Default `"system"`. */
82
- colorScheme?: AstralBeamChatColorScheme;
83
- /** Custom values for the widget's theming CSS variables, per color scheme. */
84
- theme?: AstralBeamChatTheme | undefined;
85
- /** File attachments in the composer, on by default; `false` turns them off. */
86
- attachments?: boolean | AstralBeamChatAttachmentOptions;
87
- /** Shows the collected sandbox panel (files with downloads, command log) above the composer. Default `false`. */
88
- sandboxPanel?: boolean;
89
- /**
90
- * Logs every SDK action to the browser console with UTC timestamps and full payloads,
91
- * and asks the endpoint to log its side of the run too.
92
- */
93
- debug?: boolean;
94
55
  }
95
56
  declare const AstralBeamChat: import("react").ForwardRefExoticComponent<AstralBeamChatProps & import("react").RefAttributes<AstralBeamChatRef>>;
96
57
  //#endregion
97
- export { AstralBeamChat, type AstralBeamChatAttachmentOptions, type AstralBeamChatAuthTokenRequest, type AstralBeamChatColorScheme, type AstralBeamChatCore, type AstralBeamChatCoreOptions, type AstralBeamChatGenerateAuthToken, AstralBeamChatProps, AstralBeamChatRef, type AstralBeamChatState, type AstralBeamChatTheme, type InferParameters, type ParametersSchema, type ToolDefinition, TypedReactWidgetDefinition, UseAstralBeamChatResult, WidgetDefinition, defineTool, defineWidget, useAstralBeamChat };
58
+ export { AstralBeamChat, type AstralBeamChatAttachmentOptions, type AstralBeamChatAuthTokenRequest, type AstralBeamChatAuthTokenSource, type AstralBeamChatColorScheme, type AstralBeamChatCore, type AstralBeamChatCoreOptions, AstralBeamChatProps, AstralBeamChatRef, type AstralBeamChatState, type AstralBeamChatTheme, type InferParameters, type ParametersSchema, type ToolDefinition, TypedReactWidgetDefinition, UseAstralBeamChatResult, WidgetDefinition, defineTool, defineWidget, useAstralBeamChat };
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as DEFAULT_COLOR_SCHEME, r as createAstralBeamChat } from "./core-BZxnUlwg.js";
1
+ import { C as DEFAULT_COLOR_SCHEME, n as CORE_OPTION_KEYS, r as createAstralBeamChat } from "./core-BF1G1dLe.js";
2
2
  import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { defineTool, mountAstralBeamChat } from "@astralbeam/sdk/client";
@@ -15,19 +15,12 @@ function defineWidget(widget) {
15
15
  * state stay live and nothing needs a remount.
16
16
  */
17
17
  function useAstralBeamChat(options) {
18
- const [core] = useState(() => createAstralBeamChat(options));
18
+ const coreRef = useRef(null);
19
+ coreRef.current ??= createAstralBeamChat(options);
20
+ const core = coreRef.current;
19
21
  useEffect(() => {
20
22
  core.updateOptions(options);
21
- }, [
22
- core,
23
- options.agentId,
24
- options.apiUrl,
25
- options.generateAuthToken,
26
- options.tools,
27
- options.widgets,
28
- options.onRenderWidget,
29
- options.debug
30
- ]);
23
+ }, [core, ...CORE_OPTION_KEYS.map((key) => options[key])]);
31
24
  useEffect(() => () => core.dispose(), [core]);
32
25
  return {
33
26
  ...useSyncExternalStore(core.subscribe, core.getState, core.getState),
@@ -39,7 +32,7 @@ function useAstralBeamChat(options) {
39
32
  core
40
33
  };
41
34
  }
42
- const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription, apiUrl, generateAuthToken, tools, widgets = {}, colorScheme = DEFAULT_COLOR_SCHEME, theme, attachments, sandboxPanel, debug }, ref) {
35
+ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, showHeader, header, empty, composerActions, emptyTitle, emptyDescription, apiUrl, fetchChatAuthToken, tools, widgets = {}, colorScheme = DEFAULT_COLOR_SCHEME, theme, attachments, sandboxPanel, debug }, ref) {
43
36
  const targetRef = useRef(null);
44
37
  const handleRef = useRef(null);
45
38
  const [activeRenders, setActiveRenders] = useState(/* @__PURE__ */ new Map());
@@ -106,7 +99,7 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
106
99
  const live = useMemo(() => ({
107
100
  agentId,
108
101
  apiUrl,
109
- generateAuthToken,
102
+ fetchChatAuthToken,
110
103
  title,
111
104
  showHeader,
112
105
  emptyTitle,
@@ -122,7 +115,7 @@ const AstralBeamChat = forwardRef(function AstralBeamChat({ agentId, title, show
122
115
  }), [
123
116
  agentId,
124
117
  apiUrl,
125
- generateAuthToken,
118
+ fetchChatAuthToken,
126
119
  title,
127
120
  showHeader,
128
121
  emptyTitle,
package/dist/server.d.ts CHANGED
@@ -1,57 +1,42 @@
1
- import * as Schema from "effect/Schema";
2
-
3
1
  //#region src/server/index.d.ts
4
- declare const ASTRALBEAM_TOKEN_AUDIENCE = "astralbeam";
5
- declare const ASTRALBEAM_CHAT_TOKEN_TYPE = "astralbeam+jwt";
6
- declare const ASTRALBEAM_CHAT_TOKEN_VERSION = 4;
7
- declare const ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS = 300;
8
- declare const ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS = 600;
9
- declare const TenantSchema: Schema.Struct<{
10
- readonly id: Schema.String;
11
- readonly name: Schema.optional<Schema.String>;
12
- readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
13
- }>;
14
- declare const TenantUserSchema: Schema.Struct<{
15
- readonly id: Schema.String;
16
- readonly name: Schema.optional<Schema.String>;
17
- readonly admin: Schema.optional<Schema.Boolean>;
18
- readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
19
- }>;
2
+ declare const CHAT_AUTH_TOKEN_AUDIENCE = "astralbeam";
3
+ declare const CHAT_AUTH_TOKEN_TYPE = "astralbeam+jwt";
4
+ declare const CHAT_AUTH_TOKEN_VERSION = 4;
5
+ declare const CHAT_AUTH_TOKEN_LIFETIME_SECONDS = 300;
6
+ declare const CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS = 600;
7
+ /** A JSON value, which is all a `metadata` object may hold: the token carries it verbatim. */
8
+ type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
9
+ readonly [key: string]: JsonValue;
10
+ };
11
+ /** A `metadata` object: caller-owned keys, preserved verbatim in the token's claims. */
12
+ type JsonMetadata = {
13
+ readonly [key: string]: JsonValue;
14
+ };
20
15
  /** Tenant identity from the Organization's application, including JSON metadata. */
21
- type Tenant = typeof TenantSchema.Type;
16
+ interface Tenant {
17
+ readonly id: string;
18
+ readonly name?: string | undefined;
19
+ readonly metadata?: JsonMetadata | undefined;
20
+ }
22
21
  /** User of an Organization's Tenant who interacts with AstralBeam. */
23
- type TenantUser = typeof TenantUserSchema.Type;
24
- interface CreateAstralBeamChatTokenOptions<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
22
+ interface TenantUser {
23
+ readonly id: string;
24
+ readonly name?: string | undefined;
25
+ readonly admin?: boolean | undefined;
26
+ readonly metadata?: JsonMetadata | undefined;
27
+ }
28
+ interface CreateChatAuthTokenOptions<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
25
29
  readonly apiKey: string;
26
30
  readonly user: TTenantUser;
27
31
  readonly tenant: TTenant;
28
32
  readonly expiresInSeconds?: number | undefined;
29
33
  }
30
- interface CreateAstralBeamTokenRouteOptions<TSession extends object = object, TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant> {
31
- /** The full API key, or a thunk read per request; missing or empty answers 503. */
32
- readonly apiKey: string | undefined | (() => string | undefined);
33
- /**
34
- * Authenticates the request against the application's own session. Returning nothing, or
35
- * throwing, answers 401.
36
- */
37
- readonly authenticate: (request: Request) => TSession | null | undefined | Promise<TSession | null | undefined>;
38
- /** Maps the authenticated session to the tenant user minted into the token. */
39
- readonly user: (session: TSession) => TTenantUser;
40
- /** Maps the same authenticated session to the tenant minted into the token. */
41
- readonly tenant: (session: TSession) => TTenant;
42
- readonly expiresInSeconds?: number | undefined;
43
- }
44
- /**
45
- * Builds the fetch-standard `POST` handler for an application's token endpoint, owning the
46
- * method check, the unconfigured-key 503, the unauthenticated 401, and the `no-store` header.
47
- */
48
- declare function createAstralBeamTokenRoute<TSession extends object, TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>(options: CreateAstralBeamTokenRouteOptions<TSession, TTenantUser, TTenant>): (request: Request) => Promise<Response>;
49
34
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
50
- declare function createAstralBeamChatToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
35
+ declare function createChatAuthToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
51
36
  apiKey,
52
37
  user,
53
38
  tenant,
54
39
  expiresInSeconds
55
- }: CreateAstralBeamChatTokenOptions<TTenantUser, TTenant>): Promise<string>;
40
+ }: CreateChatAuthTokenOptions<TTenantUser, TTenant>): Promise<string>;
56
41
  //#endregion
57
- export { ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_TYPE, ASTRALBEAM_CHAT_TOKEN_VERSION, ASTRALBEAM_TOKEN_AUDIENCE, CreateAstralBeamChatTokenOptions, CreateAstralBeamTokenRouteOptions, Tenant, TenantSchema, TenantUser, TenantUserSchema, createAstralBeamChatToken, createAstralBeamTokenRoute };
42
+ export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, CreateChatAuthTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createChatAuthToken };
package/dist/server.js CHANGED
@@ -1,4 +1,3 @@
1
- import * as Schema from "effect/Schema";
2
1
  //#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/lib/buffer_utils.js
3
2
  const encoder = new TextEncoder();
4
3
  const decoder = new TextDecoder();
@@ -638,50 +637,29 @@ var SignJWT = class {
638
637
  };
639
638
  //#endregion
640
639
  //#region src/server/index.ts
641
- const ASTRALBEAM_TOKEN_AUDIENCE = "astralbeam";
642
- const ASTRALBEAM_CHAT_TOKEN_TYPE = "astralbeam+jwt";
643
- const ASTRALBEAM_CHAT_TOKEN_VERSION = 4;
644
- const ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS = 300;
645
- const ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS = 600;
646
- const CHAT_TOKEN_MAX_BYTES = 16384;
640
+ const CHAT_AUTH_TOKEN_AUDIENCE = "astralbeam";
641
+ const CHAT_AUTH_TOKEN_TYPE = "astralbeam+jwt";
642
+ const CHAT_AUTH_TOKEN_VERSION = 4;
643
+ const CHAT_AUTH_TOKEN_LIFETIME_SECONDS = 300;
644
+ const CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS = 600;
645
+ const CHAT_AUTH_TOKEN_MAX_BYTES = 16384;
647
646
  const IDENTITY_MAX_BYTES = 8192;
647
+ const EXTERNAL_ID_MAX_LENGTH = 255;
648
+ const API_KEY_PATTERN = /^key_[0-9a-z-]{1,63}_[0-9a-z-]{1,63}_abo_[A-Za-z]{64}$/;
649
+ const TENANT_FIELDS = [
650
+ "id",
651
+ "name",
652
+ "metadata"
653
+ ];
654
+ const TENANT_USER_FIELDS = [
655
+ "id",
656
+ "name",
657
+ "admin",
658
+ "metadata"
659
+ ];
648
660
  const textEncoder = new TextEncoder();
649
- const SlugSchema = Schema.String.pipe(Schema.check(Schema.isPattern(/^[0-9a-z-]{1,63}$/)));
650
- const ApiKeySecretSchema = Schema.String.pipe(Schema.check(Schema.isPattern(/^abo_[A-Za-z]{64}$/)));
651
- const ApiKeySchema = Schema.TemplateLiteral([
652
- "key_",
653
- SlugSchema,
654
- "_",
655
- SlugSchema,
656
- "_",
657
- ApiKeySecretSchema
658
- ]);
659
- const isApiKey = Schema.is(ApiKeySchema);
660
- const MetadataSchema = Schema.JsonObject.annotate({ message: "metadata must be a JSON object" });
661
- const TenantExternalIdSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length >= 1 && value.length <= 255, { message: "tenant.id must be a 1-255 character string" })));
662
- const TenantUserExternalIdSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length >= 1 && value.length <= 255, { message: "user.id must be a 1-255 character string" })));
663
- const TenantSchema = Schema.Struct({
664
- id: TenantExternalIdSchema,
665
- name: Schema.optional(Schema.String),
666
- metadata: Schema.optional(MetadataSchema)
667
- });
668
- const TenantUserSchema = Schema.Struct({
669
- id: TenantUserExternalIdSchema,
670
- name: Schema.optional(Schema.String),
671
- admin: Schema.optional(Schema.Boolean),
672
- metadata: Schema.optional(MetadataSchema)
673
- });
674
- const IdentitySchema = Schema.Struct({
675
- user: TenantUserSchema,
676
- tenant: TenantSchema
677
- }).pipe(Schema.check(Schema.makeFilter((value) => textEncoder.encode(JSON.stringify(value)).byteLength <= IDENTITY_MAX_BYTES, { message: `user and tenant must not exceed ${IDENTITY_MAX_BYTES} bytes` })));
678
- const decodeIdentity = Schema.decodeUnknownSync(IdentitySchema, {
679
- errors: "all",
680
- onExcessProperty: "error",
681
- reportInput: false
682
- });
683
661
  function parseApiKey(apiKey) {
684
- if (!isApiKey(apiKey)) throw new Error("apiKey must match key_<organization>_<key>_abo_<secret>");
662
+ if (!API_KEY_PATTERN.test(apiKey)) throw new Error("apiKey must match key_<organization>_<key>_abo_<secret>");
685
663
  const separator = apiKey.lastIndexOf("_abo_");
686
664
  const keyId = apiKey.slice(0, separator);
687
665
  const keySecret = apiKey.slice(separator + 1);
@@ -691,53 +669,44 @@ function parseApiKey(apiKey) {
691
669
  keySecret
692
670
  };
693
671
  }
672
+ function isPlainObject(value) {
673
+ if (typeof value !== "object" || value === null) return false;
674
+ const prototype = Object.getPrototypeOf(value);
675
+ return prototype === Object.prototype || prototype === null;
676
+ }
677
+ function isJsonValue(value) {
678
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
679
+ if (typeof value === "number") return Number.isFinite(value);
680
+ if (Array.isArray(value)) return value.every(isJsonValue);
681
+ return isPlainObject(value) && Object.values(value).every(isJsonValue);
682
+ }
683
+ function validateIdentity(label, value, isUser) {
684
+ if (!isPlainObject(value)) throw new Error(`${label} must be an object`);
685
+ const fields = isUser ? TENANT_USER_FIELDS : TENANT_FIELDS;
686
+ for (const key of Object.keys(value)) if (!fields.includes(key)) throw new Error(`${label} has an unknown field "${key}"`);
687
+ const { id, name, admin, metadata } = value;
688
+ if (typeof id !== "string" || id.length < 1 || id.length > EXTERNAL_ID_MAX_LENGTH) throw new Error(`${label}.id must be a 1-${EXTERNAL_ID_MAX_LENGTH} character string`);
689
+ if (name !== void 0 && typeof name !== "string") throw new Error(`${label}.name must be a string`);
690
+ if (isUser && admin !== void 0 && typeof admin !== "boolean") throw new Error(`${label}.admin must be a boolean`);
691
+ if (metadata !== void 0 && !(isPlainObject(metadata) && isJsonValue(metadata))) throw new Error(`${label}.metadata must be a JSON object`);
692
+ }
694
693
  function validatedIdentity(user, tenant) {
695
- return JSON.parse(JSON.stringify(decodeIdentity({
694
+ validateIdentity("user", user, true);
695
+ validateIdentity("tenant", tenant, false);
696
+ const json = JSON.stringify({
696
697
  user,
697
698
  tenant
698
- })));
699
+ });
700
+ if (textEncoder.encode(json).byteLength > IDENTITY_MAX_BYTES) throw new Error(`user and tenant must not exceed ${IDENTITY_MAX_BYTES} bytes`);
701
+ return JSON.parse(json);
699
702
  }
700
703
  async function signingKey(secret) {
701
704
  const digest = await crypto.subtle.digest("SHA-256", textEncoder.encode(secret));
702
705
  return textEncoder.encode(encode(new Uint8Array(digest)));
703
706
  }
704
- function tokenRouteResponse(body, status) {
705
- return Response.json(body, {
706
- status,
707
- headers: { "cache-control": "no-store" }
708
- });
709
- }
710
- /**
711
- * Builds the fetch-standard `POST` handler for an application's token endpoint, owning the
712
- * method check, the unconfigured-key 503, the unauthenticated 401, and the `no-store` header.
713
- */
714
- function createAstralBeamTokenRoute(options) {
715
- return async (request) => {
716
- if (request.method !== "POST") return tokenRouteResponse({ error: "Use POST" }, 405);
717
- const apiKey = typeof options.apiKey === "function" ? options.apiKey() : options.apiKey;
718
- if (!apiKey) return tokenRouteResponse({ error: "The AstralBeam API key is not configured" }, 503);
719
- let session;
720
- try {
721
- session = await options.authenticate(request);
722
- } catch {
723
- session = void 0;
724
- }
725
- if (!session) return tokenRouteResponse({ error: "The session could not be verified" }, 401);
726
- try {
727
- return tokenRouteResponse({ token: await createAstralBeamChatToken({
728
- apiKey,
729
- user: options.user(session),
730
- tenant: options.tenant(session),
731
- ...options.expiresInSeconds === void 0 ? {} : { expiresInSeconds: options.expiresInSeconds }
732
- }) }, 200);
733
- } catch {
734
- return tokenRouteResponse({ error: "The chat token could not be created" }, 500);
735
- }
736
- };
737
- }
738
707
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
739
- async function createAstralBeamChatToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
740
- if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("AstralBeam chat tokens must live for 60-600 seconds");
708
+ async function createChatAuthToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
709
+ if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("chat auth tokens must live for 60-600 seconds");
741
710
  const { keyId, organizationSlug, keySecret } = parseApiKey(apiKey);
742
711
  const identity = validatedIdentity(user, tenant);
743
712
  const now = Math.floor(Date.now() / 1e3);
@@ -747,11 +716,11 @@ async function createAstralBeamChatToken({ apiKey, user, tenant, expiresInSecond
747
716
  tenant: identity.tenant
748
717
  }).setProtectedHeader({
749
718
  alg: "HS256",
750
- typ: ASTRALBEAM_CHAT_TOKEN_TYPE,
719
+ typ: CHAT_AUTH_TOKEN_TYPE,
751
720
  kid: keyId
752
- }).setIssuer(organizationSlug).setAudience(ASTRALBEAM_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
753
- if (textEncoder.encode(token).byteLength > CHAT_TOKEN_MAX_BYTES) throw new Error(`AstralBeam chat tokens must not exceed ${CHAT_TOKEN_MAX_BYTES} bytes`);
721
+ }).setIssuer(organizationSlug).setAudience(CHAT_AUTH_TOKEN_AUDIENCE).setIssuedAt(now).setExpirationTime(now + expiresInSeconds).sign(await signingKey(keySecret));
722
+ if (textEncoder.encode(token).byteLength > CHAT_AUTH_TOKEN_MAX_BYTES) throw new Error(`chat auth tokens must not exceed ${CHAT_AUTH_TOKEN_MAX_BYTES} bytes`);
754
723
  return token;
755
724
  }
756
725
  //#endregion
757
- export { ASTRALBEAM_CHAT_TOKEN_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_MAX_LIFETIME_SECONDS, ASTRALBEAM_CHAT_TOKEN_TYPE, ASTRALBEAM_CHAT_TOKEN_VERSION, ASTRALBEAM_TOKEN_AUDIENCE, TenantSchema, TenantUserSchema, createAstralBeamChatToken, createAstralBeamTokenRoute };
726
+ export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, createChatAuthToken };