sideagent 0.3.0 → 0.5.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
@@ -55,6 +55,18 @@ With an agent hosted on sideagent Cloud, pass its publishable key instead of `en
55
55
 
56
56
  Pass `endpoint` as well to send the requests somewhere else, such as a proxy in front of sideagent Cloud. A malformed key, or neither prop, throws during render. `parsePublishableKey` and `publishableEndpoint` are exported from `sideagent/keys`.
57
57
 
58
+ Signed-in users: pass `getHeaders`, which the chat calls before every request, so a login token that expires is fetched fresh each time. Pass `storageKey` (the user's ID) so the last conversation the chat remembers in `localStorage` belongs to that user; on a shared browser another user starts from their own.
59
+
60
+ ```tsx
61
+ <AgentChat
62
+ publishableKey="pk_live_…"
63
+ getHeaders={async () => ({ Authorization: `Bearer ${await getToken()}` })}
64
+ storageKey={user.id}
65
+ />
66
+ ```
67
+
68
+ `getHeaders` works with `endpoint` too. Its headers are merged under `headers`, which wins on a clash, so it cannot replace the publishable key. If it throws, the request is not sent and the chat shows an error.
69
+
58
70
  ### Docked sidebar
59
71
 
60
72
  Wrap your app in `AgentProvider` to dock the chat on the right, side by side with the app instead of over it. It takes the same props as `AgentChat`. The user drags the panel's left edge (or uses the arrow keys on it) to resize; the width is remembered. Outside `AgentProvider`, sidebar mode overlays the page.
@@ -0,0 +1,10 @@
1
+ // src/server/message-text.ts
2
+ function messageText(content) {
3
+ if (typeof content === "string") return content;
4
+ if (!Array.isArray(content)) return "";
5
+ return content.map((p) => p?.type === "text" && typeof p.text === "string" ? p.text : "").join("");
6
+ }
7
+
8
+ export {
9
+ messageText
10
+ };
@@ -0,0 +1,43 @@
1
+ // src/keys.ts
2
+ var PUBLISHABLE_KEY_HEADER = "x-sideagent-key";
3
+ var AGENT_ID = /^agt_[a-z0-9]{16}$/;
4
+ var HOST = /^[a-z0-9.-]+(:\d+)?$/i;
5
+ var LOOPBACK = /^(localhost|127\.0\.0\.1)(:\d+)?$/i;
6
+ function b64url(s) {
7
+ let bin = "";
8
+ for (const b of new TextEncoder().encode(s)) bin += String.fromCharCode(b);
9
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
10
+ }
11
+ function fromB64url(s) {
12
+ const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/"));
13
+ return new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(bin, (c) => c.charCodeAt(0)));
14
+ }
15
+ function publishableKey(environment, host, agentId) {
16
+ return `pk_${environment}_${b64url(`${host}$${agentId}`)}`;
17
+ }
18
+ function parsePublishableKey(key) {
19
+ const m = /^pk_(test|live)_([A-Za-z0-9_-]+)$/.exec(key);
20
+ if (!m) return null;
21
+ let decoded;
22
+ try {
23
+ decoded = fromB64url(m[2]);
24
+ } catch {
25
+ return null;
26
+ }
27
+ const at = decoded.lastIndexOf("$");
28
+ if (at <= 0) return null;
29
+ const host = decoded.slice(0, at);
30
+ const agentId = decoded.slice(at + 1);
31
+ if (!AGENT_ID.test(agentId) || !HOST.test(host)) return null;
32
+ return { environment: m[1], host, agentId };
33
+ }
34
+ function publishableEndpoint({ host, agentId }) {
35
+ return `${LOOPBACK.test(host) ? "http" : "https"}://${host}/v1/agents/${agentId}`;
36
+ }
37
+
38
+ export {
39
+ PUBLISHABLE_KEY_HEADER,
40
+ publishableKey,
41
+ parsePublishableKey,
42
+ publishableEndpoint
43
+ };
package/dist/index.d.ts CHANGED
@@ -32,8 +32,10 @@ interface AgentHooks {
32
32
 
33
33
  type AgentChatMode = "floating" | "sidebar";
34
34
  interface AgentChatProps {
35
- /** URL of your sideagent endpoint, e.g. "/api/agent". */
36
- endpoint: string;
35
+ /** URL of your sideagent endpoint, e.g. "/api/agent". With `publishableKey`, overrides the URL the key names. */
36
+ endpoint?: string;
37
+ /** sideagent Cloud publishable key (`pk_live_…`). Requests go to the agent it names and carry it. */
38
+ publishableKey?: string;
37
39
  /** Starting layout. The user can switch; their choice is remembered. */
38
40
  mode?: AgentChatMode;
39
41
  title?: string;
@@ -47,6 +49,17 @@ interface AgentChatProps {
47
49
  /** Use "include" when the endpoint is on another origin and relies on cookies. */
48
50
  credentials?: RequestCredentials;
49
51
  headers?: Record<string, string>;
52
+ /**
53
+ * Called before every request; its headers are added to `headers` (which wins on a clash). Use it for a
54
+ * login token that expires, e.g. `Authorization: Bearer <token>` from your login SDK. If it throws, the
55
+ * request is not sent and the chat shows an error.
56
+ */
57
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
58
+ /**
59
+ * Scopes what the chat remembers in localStorage (the last conversation) to one user, e.g. their user ID.
60
+ * Changing it starts over with that user's last conversation.
61
+ */
62
+ storageKey?: string;
50
63
  defaultOpen?: boolean;
51
64
  /** Custom result views per operationId. Anything not listed gets the built-in table / fields view. */
52
65
  resultRenderers?: Record<string, (e: {
@@ -59,24 +72,31 @@ interface AgentChatProps {
59
72
  /** Set false to hide the layout switcher. */
60
73
  allowModeSwitch?: boolean;
61
74
  }
62
- interface AgentControls {
63
- open: boolean;
64
- setOpen: (open: boolean) => void;
65
- }
66
- /** Open or close the assistant from anywhere under `AgentProvider`. */
67
- declare function useAgent(): AgentControls;
75
+
76
+ /** The panel shell: open/close, floating or docked layout, resize, history and session loading. */
77
+ declare function AgentChat(props: AgentChatProps): react.JSX.Element;
78
+
68
79
  /** Lays the app and a docked sidebar chat side by side, and shares open state via `useAgent`. Takes the same props as AgentChat. */
69
80
  declare function AgentProvider({ children, className, style, ...chat }: AgentChatProps & {
70
81
  children: ReactNode;
71
82
  className?: string;
72
83
  style?: CSSProperties;
73
84
  }): react.JSX.Element;
74
- declare function AgentChat(props: AgentChatProps): react.JSX.Element;
75
85
 
86
+ interface AgentControls {
87
+ open: boolean;
88
+ setOpen: (open: boolean) => void;
89
+ }
90
+ /** Open or close the assistant from anywhere under `AgentProvider`. */
91
+ declare function useAgent(): AgentControls;
92
+
93
+ type GetHeaders = () => Record<string, string> | Promise<Record<string, string>>;
76
94
  interface TransportOptions {
77
95
  endpoint: string;
78
96
  credentials?: RequestCredentials;
79
97
  headers?: Record<string, string>;
98
+ /** Called before every request, e.g. for a fresh login token. `headers` wins on a clash. */
99
+ getHeaders?: GetHeaders;
80
100
  signal?: AbortSignal;
81
101
  }
82
102
  /** POSTs a chat request and yields the server's SSE events. */