@astralbeam/sdk 0.6.0 → 0.9.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/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-TbZxlv54.js";
1
+ import { C as AstralBeamChatState, S as AstralBeamChatCoreOptions, x as AstralBeamChatCore } from "./index-BQBdZKyB.js";
2
2
  import { ReactNode } from "react";
3
- import { AstralBeamChatAttachmentOptions, AstralBeamChatAuthTokenRequest, AstralBeamChatAuthTokenSource, AstralBeamChatColorScheme, AstralBeamChatTheme, InferParameters, JsonSchemaObject, ParametersSchema, ToolDefinition, WidgetDefinition as WidgetDefinition$1, defineTool } from "@astralbeam/sdk/client";
3
+ import { AstralBeamChatAttachmentOptions, AstralBeamChatColorScheme, AstralBeamChatTheme, AstralBeamTokenRequest, AstralBeamTokenSource, 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
- fetchChatAuthToken?: AstralBeamChatAuthTokenSource;
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 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 };
58
+ export { AstralBeamChat, type AstralBeamChatAttachmentOptions, type AstralBeamChatColorScheme, type AstralBeamChatCore, type AstralBeamChatCoreOptions, AstralBeamChatProps, AstralBeamChatRef, type AstralBeamChatState, type AstralBeamChatTheme, type AstralBeamTokenRequest, type AstralBeamTokenSource, 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-DYXxodYg.js";
1
+ import { C as DEFAULT_COLOR_SCHEME, n as CORE_OPTION_KEYS, r as createAstralBeamChat } from "./core-Eh2vyhUP.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.fetchChatAuthToken,
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, fetchChatAuthToken, 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, fetchAstralBeamToken, 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
- fetchChatAuthToken,
102
+ fetchAstralBeamToken,
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
- fetchChatAuthToken,
118
+ fetchAstralBeamToken,
126
119
  title,
127
120
  showHeader,
128
121
  emptyTitle,
package/dist/server.d.ts CHANGED
@@ -1,38 +1,42 @@
1
- import * as Schema from "effect/Schema";
2
-
3
1
  //#region src/server/index.d.ts
4
2
  declare const CHAT_AUTH_TOKEN_AUDIENCE = "astralbeam";
5
3
  declare const CHAT_AUTH_TOKEN_TYPE = "astralbeam+jwt";
6
4
  declare const CHAT_AUTH_TOKEN_VERSION = 4;
7
5
  declare const CHAT_AUTH_TOKEN_LIFETIME_SECONDS = 300;
8
6
  declare const CHAT_AUTH_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
- }>;
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 CreateChatAuthTokenOptions<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 CreateAstralBeamTokenOptions<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
34
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
31
- declare function createChatAuthToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
35
+ declare function createAstralBeamToken<TTenantUser extends TenantUser = TenantUser, TTenant extends Tenant = Tenant>({
32
36
  apiKey,
33
37
  user,
34
38
  tenant,
35
39
  expiresInSeconds
36
- }: CreateChatAuthTokenOptions<TTenantUser, TTenant>): Promise<string>;
40
+ }: CreateAstralBeamTokenOptions<TTenantUser, TTenant>): Promise<string>;
37
41
  //#endregion
38
- 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, Tenant, TenantSchema, TenantUser, TenantUserSchema, createChatAuthToken };
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, CreateAstralBeamTokenOptions, JsonMetadata, JsonValue, Tenant, TenantUser, createAstralBeamToken };
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();
@@ -645,43 +644,22 @@ const CHAT_AUTH_TOKEN_LIFETIME_SECONDS = 300;
645
644
  const CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS = 600;
646
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,18 +669,43 @@ 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
707
  /** Creates the short-lived bearer token returned by an application's server auth endpoint. */
705
- async function createChatAuthToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
708
+ async function createAstralBeamToken({ apiKey, user, tenant, expiresInSeconds = 300 }) {
706
709
  if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 600) throw new Error("chat auth tokens must live for 60-600 seconds");
707
710
  const { keyId, organizationSlug, keySecret } = parseApiKey(apiKey);
708
711
  const identity = validatedIdentity(user, tenant);
@@ -720,4 +723,4 @@ async function createChatAuthToken({ apiKey, user, tenant, expiresInSeconds = 30
720
723
  return token;
721
724
  }
722
725
  //#endregion
723
- export { CHAT_AUTH_TOKEN_AUDIENCE, CHAT_AUTH_TOKEN_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_MAX_LIFETIME_SECONDS, CHAT_AUTH_TOKEN_TYPE, CHAT_AUTH_TOKEN_VERSION, TenantSchema, TenantUserSchema, createChatAuthToken };
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, createAstralBeamToken };