lemma-sdk 0.2.24 → 0.2.25

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.
Files changed (29) hide show
  1. package/README.md +156 -511
  2. package/dist/browser/lemma-client.js +31 -10
  3. package/dist/openapi_client/index.d.ts +1 -2
  4. package/dist/openapi_client/index.js +0 -2
  5. package/dist/openapi_client/models/ColumnSchema.d.ts +4 -0
  6. package/dist/openapi_client/models/CreateTableRequest.d.ts +1 -1
  7. package/dist/openapi_client/models/DatastoreQueryRequest.d.ts +1 -1
  8. package/dist/openapi_client/models/PodCreateRequest.d.ts +0 -4
  9. package/dist/openapi_client/models/PodMemberDetailResponse.d.ts +14 -0
  10. package/dist/openapi_client/models/PodMemberDetailResponse.js +1 -0
  11. package/dist/openapi_client/models/PodMemberResponse.d.ts +3 -3
  12. package/dist/openapi_client/models/PodResponse.d.ts +0 -5
  13. package/dist/openapi_client/models/PodUpdateRequest.d.ts +0 -4
  14. package/dist/openapi_client/services/PodMembersService.d.ts +14 -4
  15. package/dist/openapi_client/services/PodMembersService.js +29 -8
  16. package/dist/openapi_client/services/RecordsService.d.ts +2 -2
  17. package/dist/openapi_client/services/RecordsService.js +2 -2
  18. package/dist/react/index.d.ts +0 -2
  19. package/dist/react/index.js +0 -1
  20. package/dist/react/useTaskSession.js +145 -73
  21. package/dist/task-events.d.ts +2 -1
  22. package/dist/task-events.js +38 -1
  23. package/package.json +1 -1
  24. package/dist/openapi_client/models/PodStatus.d.ts +0 -4
  25. package/dist/openapi_client/models/PodStatus.js +0 -9
  26. package/dist/openapi_client/models/PodType.d.ts +0 -6
  27. package/dist/openapi_client/models/PodType.js +0 -11
  28. package/dist/react/useAgentRun.d.ts +0 -17
  29. package/dist/react/useAgentRun.js +0 -66
package/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Lemma TypeScript SDK (`lemma-sdk`)
2
2
 
3
- Official TypeScript SDK for Lemma APIs with pod-scoped namespaces, auth helpers, streaming support, and reusable React hooks.
3
+ Official TypeScript SDK for Lemma APIs with:
4
+
5
+ - Pod-scoped namespaces (`tables`, `records`, `files`, `assistants`,`agents` , `workflows`, `tasks`, .)
6
+ - Built-in auth/session handling
7
+ - SSE streaming helpers
8
+ - React hooks and assistant UI components
4
9
 
5
10
  ## Install
6
11
 
@@ -8,26 +13,14 @@ Official TypeScript SDK for Lemma APIs with pod-scoped namespaces, auth helpers,
8
13
  npm i lemma-sdk
9
14
  ```
10
15
 
11
- For local workspace development against the checked-out SDK instead of npm:
12
-
13
- ```bash
14
- npm i file:../lemma-typescript
15
- ```
16
-
17
- If you want to import as `lemma`, use npm aliasing:
18
-
19
- ```bash
20
- npm i lemma@npm:lemma-sdk
21
- ```
22
-
23
16
  ## Quick Start
24
17
 
25
18
  ```ts
26
19
  import { LemmaClient } from "lemma-sdk";
27
20
 
28
21
  const client = new LemmaClient({
29
- apiUrl: "https://api-next.asur.work",
30
- authUrl: "https://auth.asur.work/auth",
22
+ apiUrl: "https://api.lemma.work",
23
+ authUrl: "https://auth.lemma.work/auth",
31
24
  podId: "<pod-id>",
32
25
  });
33
26
 
@@ -38,588 +31,240 @@ const assistants = await client.assistants.list({ limit: 20 });
38
31
  const supportAssistant = await client.assistants.get("support_assistant");
39
32
  ```
40
33
 
41
- ## Core Concepts
34
+ ## Configuration
42
35
 
43
- - `LemmaClient`: entrypoint with auth + API transport.
44
- - Namespace APIs (`client.agents`, `client.tasks`, `client.conversations`, etc.) for typed operations.
45
- - `client.request(method, path, options)` escape hatch for endpoints not yet modeled.
46
- - `client.resources` for generic file resource APIs (`conversation`, `assistant`, `task`, etc.).
47
- - Ergonomic type aliases exported at top level: `Agent`, `Assistant`, `Conversation`, `Task`, `TaskMessage`, `CreateAgentInput`, `CreateAssistantInput`, etc.
48
- - `client.withPod(podId)` returns a pod-scoped client that shares auth state with the parent client.
36
+ `LemmaClient` config resolution order:
49
37
 
50
- ## Table Access Grants (`accessible_tables`)
38
+ 1. Constructor overrides
39
+ 2. `window.__LEMMA_CONFIG__`
40
+ 3. Environment variables
41
+ 4. Defaults
51
42
 
52
- For function, agent, and assistant payloads, `accessible_tables` must be an array of objects:
43
+ Supported env keys:
53
44
 
54
- - `table_name`: target table
55
- - `mode`: `READ` or `WRITE`
45
+ - `VITE_LEMMA_API_URL`, `REACT_APP_LEMMA_API_URL`, `LEMMA_API_URL`
46
+ - `VITE_LEMMA_AUTH_URL`, `REACT_APP_LEMMA_AUTH_URL`, `LEMMA_AUTH_URL`
47
+ - `VITE_LEMMA_POD_ID`, `REACT_APP_LEMMA_POD_ID`, `LEMMA_POD_ID`
56
48
 
57
- `accessible_tables: ["table_name"]` is no longer valid.
49
+ Defaults when unset:
58
50
 
59
- You do not pass a datastore name in SDK calls. Table and file operations are pod-scoped (`client.tables`, `client.records`, `client.files`) and take table/file identifiers directly.
51
+ - `apiUrl`: `http://localhost:8000`
52
+ - `authUrl`: `http://localhost:3000`
60
53
 
61
- Examples:
54
+ ## Pod Scoping
62
55
 
63
- ```ts
64
- import {
65
- TableAccessMode,
66
- type CreateFunctionRequest,
67
- type CreateAgentInput,
68
- type CreateAssistantInput,
69
- } from "lemma-sdk";
70
-
71
- const functionPayload: CreateFunctionRequest = {
72
- name: "expense_summary",
73
- code: "def handler(ctx):\n return {'ok': True}",
74
- config: {},
75
- accessible_tables: [
76
- { table_name: "expenses", mode: TableAccessMode.READ },
77
- { table_name: "expense_summaries", mode: TableAccessMode.WRITE },
78
- ],
79
- accessible_folders: ["/reports"],
80
- accessible_applications: [],
81
- };
82
-
83
- const agentPayload: CreateAgentInput = {
84
- name: "expense-summarizer",
85
- instruction: "Summarize expenses without mutating data.",
86
- tool_sets: [],
87
- accessible_tables: [
88
- { table_name: "expenses", mode: TableAccessMode.READ },
89
- { table_name: "expense_notes", mode: TableAccessMode.WRITE },
90
- ],
91
- accessible_folders: [],
92
- accessible_applications: [],
93
- };
94
-
95
- const assistantPayload: CreateAssistantInput = {
96
- name: "expense_assistant",
97
- instruction: "Answer expense questions and save approved notes.",
98
- tool_sets: [],
99
- accessible_tables: [
100
- { table_name: "expenses", mode: TableAccessMode.READ },
101
- { table_name: "expense_notes", mode: TableAccessMode.WRITE },
102
- ],
103
- accessible_folders: ["/notes"],
104
- accessible_applications: [],
105
- };
106
- ```
107
-
108
- ## Auth Helpers
56
+ Most namespaces are pod-scoped. You can set pod scope in three ways:
109
57
 
110
58
  ```ts
111
- import {
112
- LemmaClient,
113
- buildAuthUrl,
114
- buildFederatedLogoutUrl,
115
- resolveSafeRedirectUri,
116
- } from "lemma-sdk";
117
-
118
- const client = new LemmaClient({
119
- apiUrl: "https://api-next.asur.work",
120
- authUrl: "https://auth.asur.work/auth",
121
- });
122
-
123
- // Build auth URLs (server/client)
124
- const loginUrl = buildAuthUrl(client.authUrl, { redirectUri: "https://app.asur.work/" });
125
- const signupUrl = buildAuthUrl(client.authUrl, { mode: "signup", redirectUri: "https://app.asur.work/" });
126
-
127
- // Redirect safety helper for auth route handlers
128
- const safeRedirect = resolveSafeRedirectUri("/pod/123", {
129
- siteOrigin: "https://app.asur.work",
130
- fallback: "/",
131
- });
59
+ client.setPodId("pod_a");
132
60
 
133
- // Browser helpers
134
- await client.auth.checkAuth();
135
- await client.auth.signOut();
136
- const token = await client.auth.getAccessToken();
137
- const refreshed = await client.auth.refreshAccessToken();
138
- client.auth.redirectToAuth({ mode: "signup", redirectUri: safeRedirect });
61
+ const podBClient = client.withPod("pod_b");
139
62
 
140
- // Build upstream logout URL (server/client)
141
- const federatedLogoutUrl = buildFederatedLogoutUrl(client.authUrl, {
142
- redirectUri: safeRedirect,
143
- });
144
-
145
- // Browser: sign out locally, then clear upstream SSO and return to app
146
- await client.auth.redirectToFederatedLogout({ redirectUri: safeRedirect });
63
+ const conversations = await client.conversations.list({ pod_id: "pod_c" });
147
64
  ```
148
65
 
149
- ### Browser Testing With Injected Token
66
+ ## Namespace Overview
150
67
 
151
- For desk and app testing, the SDK supports a fixed bearer token injected through localStorage.
152
- This is the only supported browser token-injection path.
68
+ Common pod-scoped namespaces:
153
69
 
154
- ```ts
155
- import { LemmaClient, setTestingToken, clearTestingToken } from "lemma-sdk";
156
-
157
- setTestingToken("<access-token>");
158
-
159
- const client = new LemmaClient({
160
- apiUrl: "/api",
161
- authUrl: "http://localhost:4173",
162
- podId: "<pod-id>",
163
- });
164
-
165
- await client.initialize();
166
-
167
- clearTestingToken();
168
- ```
169
-
170
- Equivalent manual browser setup:
171
-
172
- ```js
173
- localStorage.setItem("lemma_token", "<access-token>");
174
- window.location.reload();
175
- ```
70
+ - `client.tables`
71
+ - `client.records`
72
+ - `client.files`
73
+ - `client.functions`
74
+ - `client.agents`
75
+ - `client.tasks`
76
+ - `client.assistants`
77
+ - `client.workflows`
78
+ - `client.desks`
79
+ - `client.integrations`
80
+ - `client.resources`
176
81
 
177
- Notes:
82
+ Org/user-level namespaces:
178
83
 
179
- - do not pass testing tokens in query parameters
180
- - prefer a same-origin dev proxy such as Vite `/api` during local browser testing to avoid CORS on `/users/me`
181
- - production auth should use the normal cookie/session flow
84
+ - `client.users`
85
+ - `client.icons`
86
+ - `client.pods`
87
+ - `client.podMembers`
88
+ - `client.organizations`
89
+ - `client.podSurfaces`
182
90
 
183
- ## Assistants + Agent Runs
184
-
185
- ### React assistant UI
186
-
187
- `lemma-sdk/react` ships the assistant controller, the default assistant experience, and the lower-level UI primitives used to build custom shells.
188
-
189
- Import the bundled stylesheet once anywhere in your app:
190
-
191
- ```tsx
192
- import "lemma-sdk/react/styles.css";
193
- ```
194
-
195
- The stylesheet includes the SDK theme tokens and the complete semantic assistant UI. The assistant components do not depend on the host app's Tailwind version or Tailwind content scanning.
196
-
197
- If you alias the package to local SDK source in Vite, make sure the alias points at the React source and stylesheet:
91
+ Escape hatch for unmapped endpoints:
198
92
 
199
93
  ```ts
200
- // vite.config.ts
201
- import path from "node:path";
202
-
203
- export default {
204
- resolve: {
205
- alias: {
206
- "lemma-sdk/react/styles.css": path.resolve(__dirname, "../lemma-typescript/src/react/styles.css"),
207
- "lemma-sdk/react": path.resolve(__dirname, "../lemma-typescript/src/react/index.ts"),
208
- "lemma-sdk": path.resolve(__dirname, "../lemma-typescript/src/index.ts"),
209
- },
210
- },
211
- };
212
- ```
213
-
214
- Quick checklist for developers:
215
-
216
- - import `lemma-sdk/react/styles.css` once
217
- - give the assistant container a real height
218
- - if the assistant is inside flex/grid, add `min-height: 0` on the relevant parent
219
- - if you use `AssistantEmbedded`, pass `theme` directly there
220
- - if you use `AssistantExperienceView`, wrap it in `AssistantThemeScope`
221
-
222
- The assistant UI renders markdown by default:
223
-
224
- - GitHub-flavored markdown is enabled for assistant and user messages
225
- - raw HTML is not rendered
226
- - links open safely in a new tab by default
227
- - lists, tables, blockquotes, inline code, and fenced code blocks are styled out of the box
228
-
229
- #### Recommended path
230
-
231
- For most apps, start with `AssistantEmbedded`.
232
-
233
- - use `AssistantEmbedded` when you want the SDK to handle the controller lifecycle and render the ready-made assistant UI
234
- - use `AssistantExperienceView` when you still want the SDK's default assistant UI, but you need to own the controller lifecycle yourself
235
- - use `useAssistantController` plus primitives only when you are intentionally building a custom shell or custom layout
236
-
237
- If you are unsure, use `AssistantEmbedded` first. It is the path we recommend and the one we expect most SDK consumers to ship.
238
-
239
- #### Choose an integration level
240
-
241
- ##### 1. `AssistantEmbedded` for the fastest setup
242
-
243
- Use `AssistantEmbedded` when you want a ready-made assistant surface with the SDK defaults.
244
- This is the recommended integration for most users.
245
-
246
- ```tsx
247
- import "lemma-sdk/react/styles.css";
248
- import { AssistantEmbedded } from "lemma-sdk/react";
249
-
250
- function SupportAssistant() {
251
- return (
252
- <div style={{ height: 720, minHeight: 0 }}>
253
- <AssistantEmbedded
254
- client={client}
255
- podId="pod_123"
256
- assistantName="support_assistant"
257
- title="Support Assistant"
258
- subtitle="Ask questions about this pod."
259
- placeholder="Message Support Assistant"
260
- emptyStateSuggestions={[
261
- { text: "Summarize this conversation", icon: "✦" },
262
- { text: "Help me draft a response", icon: "✎" },
263
- { text: "List the next steps", icon: "→" },
264
- ]}
265
- showConversationList
266
- showModelPicker={false}
267
- radius="lg"
268
- theme="auto"
269
- />
270
- </div>
271
- );
272
- }
94
+ const result = await client.request("GET", "/models");
273
95
  ```
274
96
 
275
- Important notes:
276
-
277
- - `theme` accepts `"auto" | "light" | "dark"`
278
- - `radius` lets you pick the built-in rounding scale from `"none"` through `"xl"`
279
- - `showModelPicker={false}` hides the built-in model chooser when you do not want model controls visible
280
- - `theme="auto"` follows the host app when it uses common selectors like `.dark`, `[data-theme="dark"]`, `[data-mode="dark"]`, and also falls back to `prefers-color-scheme`
281
- - the parent container must have a real height; if it lives inside flex/grid, `min-height: 0` is usually needed too
282
- - attachments are queued into the composer and sent with the next message by default
283
- - `emptyStateSuggestions` lets you replace the built-in prompt chips shown before the first message
284
- - prefer this component unless you specifically need to own controller state or replace the built-in layout
285
-
286
- ##### 2. `AssistantExperienceView` for the default UI with your own controller
287
-
288
- Use `AssistantExperienceView` when you want the built-in assistant layout, but you need to own the controller lifecycle yourself.
289
- This is the second-best default when `AssistantEmbedded` is too opinionated for your integration.
97
+ ## CRUD Examples
290
98
 
291
- ```tsx
292
- import "lemma-sdk/react/styles.css";
293
- import {
294
- AssistantExperienceView,
295
- AssistantThemeScope,
296
- useAssistantController,
297
- } from "lemma-sdk/react";
298
-
299
- function ControlledAssistant() {
300
- const assistant = useAssistantController({
301
- client,
302
- podId: "pod_123",
303
- assistantName: "support_assistant",
304
- });
99
+ ### Tables + Records
305
100
 
306
- return (
307
- <AssistantThemeScope theme="dark" style={{ height: 720 }}>
308
- <AssistantExperienceView
309
- controller={assistant}
310
- title="Support Assistant"
311
- subtitle="Direct use of the default assistant experience."
312
- placeholder="Message Support Assistant"
313
- emptyStateSuggestions={[
314
- { text: "Summarize the current context" },
315
- { text: "Help me write a reply" },
316
- { text: "What should I do next?" },
317
- ]}
318
- showConversationList
319
- chromeStyle="subtle"
320
- statusPlacement="inline"
321
- />
322
- </AssistantThemeScope>
323
- );
324
- }
325
- ```
326
-
327
- Useful props on `AssistantExperienceView`:
328
-
329
- - `showConversationList`: show the built-in conversation sidebar
330
- - `chromeStyle`: `"elevated" | "subtle" | "flat"`
331
- - `statusPlacement`: `"inline" | "composer" | "none"`
332
- - `radius`: `"none" | "sm" | "md" | "lg" | "xl"`
333
- - `showModelPicker`: show or hide the built-in model selector
334
- - `showNewConversationButton`: show or hide the built-in reset/new-conversation button
335
- - `emptyStateSuggestions`: replace the built-in generic prompt suggestions used by the default empty state
336
- - `renderMessageContent`: override markdown rendering for custom message content
337
- - `renderToolInvocation`: replace the default tool activity renderer
338
- - `renderPresentedFile` and `renderPendingFile`: customize attachment rendering
339
- - prefer this over building from primitives if you still want the SDK's default assistant experience
340
-
341
- ##### 3. `useAssistantController` + primitives for a custom shell
342
-
343
- Use the primitives when you want full control over layout and app chrome.
344
- This is the advanced path and should be the exception, not the starting point.
345
-
346
- ```tsx
347
- import "lemma-sdk/react/styles.css";
348
- import {
349
- AssistantComposer,
350
- AssistantHeader,
351
- AssistantMessageViewport,
352
- AssistantShellLayout,
353
- AssistantThemeScope,
354
- EmptyState,
355
- MessageGroup,
356
- PlanSummaryStrip,
357
- ThinkingIndicator,
358
- buildDisplayMessageRows,
359
- getActiveToolBanner,
360
- latestPlanSummary,
361
- useAssistantController,
362
- } from "lemma-sdk/react";
363
-
364
- function CustomAssistantShell() {
365
- const assistant = useAssistantController({
366
- client,
367
- podId: "pod_123",
368
- assistantName: "support_assistant",
369
- });
370
-
371
- const rows = buildDisplayMessageRows(assistant.messages);
372
- const plan = latestPlanSummary(assistant.messages);
373
- const activeToolBanner = getActiveToolBanner(assistant.messages);
374
-
375
- return (
376
- <AssistantThemeScope theme="auto" style={{ height: 720 }}>
377
- <AssistantShellLayout
378
- main={(
379
- <div className="flex min-h-0 flex-1 flex-col gap-3">
380
- <AssistantHeader
381
- title="Lemma Assistant"
382
- subtitle="Ask anything"
383
- />
384
-
385
- {plan ? <PlanSummaryStrip plan={plan} onHide={() => {}} /> : null}
386
- {activeToolBanner ? <div>{activeToolBanner.summary}</div> : null}
387
-
388
- <AssistantMessageViewport>
389
- {assistant.messages.length === 0 ? (
390
- <EmptyState
391
- suggestions={[
392
- { text: "Summarize this for me" },
393
- { text: "Help me draft a reply" },
394
- { text: "Brainstorm next steps" },
395
- ]}
396
- onSendMessage={(text) => {
397
- void assistant.sendMessage(text);
398
- }}
399
- />
400
- ) : null}
401
-
402
- {rows.map((row, index) => (
403
- <MessageGroup
404
- key={row.id}
405
- message={row.message}
406
- conversationId={assistant.activeConversationId}
407
- onWidgetSendPrompt={(text) => assistant.sendMessage(text)}
408
- isStreaming={assistant.isActiveConversationRunning && row.sourceIndexes.includes(assistant.messages.length - 1)}
409
- showAssistantHeader={index === 0 || rows[index - 1]?.message.role !== "assistant"}
410
- renderMessageContent={({ message }) => <div>{message.content}</div>}
411
- />
412
- ))}
413
-
414
- {assistant.isActiveConversationRunning ? <ThinkingIndicator /> : null}
415
- </AssistantMessageViewport>
416
-
417
- <AssistantComposer>
418
- <textarea placeholder="Message Lemma Assistant" />
419
- </AssistantComposer>
420
- </div>
421
- )}
422
- />
423
- </AssistantThemeScope>
424
- );
425
- }
426
- ```
427
-
428
- Useful primitives exported from `lemma-sdk/react`:
429
-
430
- - `AssistantThemeScope`
431
- - `AssistantHeader`
432
- - `AssistantConversationList`
433
- - `AssistantModelPicker`
434
- - `AssistantShellLayout`
435
- - `AssistantComposer`
436
- - `AssistantMessageViewport`
437
- - `AssistantAskOverlay`
438
- - `AssistantPendingFileChip`
439
- - `AssistantStatusPill`
440
- - `MessageGroup`
441
- - `PlanSummaryStrip`
442
- - `ThinkingIndicator`
443
-
444
- Guidance:
445
-
446
- - prefer `AssistantEmbedded` over this path when the SDK layout is acceptable
447
- - prefer `AssistantExperienceView` over this path when you only need controller ownership, theming control, or a few render overrides
448
- - reach for primitives only when you are replacing the layout itself or deeply integrating the assistant into app-specific chrome
449
-
450
- Default empty-state suggestions are intentionally generic so they work across support, internal tools, content, and general assistant use cases. Override them with `emptyStateSuggestions` when you want task-specific prompts.
451
-
452
- #### Theming
453
-
454
- Use `AssistantThemeScope` around custom assistant layouts:
101
+ ```ts
102
+ await client.tables.create({ name: "todos" });
455
103
 
456
- ```tsx
457
- import { AssistantThemeScope } from "lemma-sdk/react";
104
+ await client.records.create("todos", {
105
+ title: "Ship docs rewrite",
106
+ status: "todo",
107
+ });
458
108
 
459
- <AssistantThemeScope theme="light">
460
- <YourAssistant />
461
- </AssistantThemeScope>
109
+ const page = await client.records.list("todos", {
110
+ limit: 20,
111
+ sort: [{ field: "created_at", direction: "desc" }],
112
+ });
462
113
  ```
463
114
 
464
- Theme behavior:
465
-
466
- - `theme="auto"`: follows host dark-mode selectors and system color scheme
467
- - `theme="light"`: forces the light SDK palette
468
- - `theme="dark"`: forces the dark SDK palette
469
-
470
- If you use `AssistantEmbedded`, pass `theme` directly on that component instead of wrapping it again.
471
-
472
- #### What belongs in the SDK vs your app
473
-
474
- The intended split is:
475
-
476
- - SDK: `useAssistantController`, message/tool normalization, markdown rendering, plan parsing, tool rollups, and reusable assistant UI primitives
477
- - App: modal shell, fullscreen/window controls, route navigation, workspace/file viewers, and product-specific renderers
478
-
479
- ### Assistant names (resource key)
480
-
481
- Assistant CRUD is name-based:
115
+ ### Files (Datastore)
482
116
 
483
117
  ```ts
484
- await client.assistants.get("support_assistant");
485
- await client.assistants.update("support_assistant", { description: "Handles support triage" });
486
- await client.assistants.delete("old_assistant");
118
+ await client.files.folder.create("reports", { directoryPath: "/" });
119
+ await client.files.upload(fileBlob, { directoryPath: "/reports", name: "q1.pdf" });
120
+
121
+ const listing = await client.files.list({ directoryPath: "/reports" });
122
+ const downloaded = await client.files.download("/reports/q1.pdf");
487
123
  ```
488
124
 
489
- ### Conversation scoping by assistant name
125
+ ### Assistants + Conversations
490
126
 
491
127
  ```ts
492
- const conversations = await client.conversations.list({
493
- assistantName: "support_assistant",
494
- limit: 20,
128
+ await client.assistants.create({
129
+ name: "support_assistant",
130
+ instruction: "Help with support triage.",
495
131
  });
496
132
 
497
133
  const conversation = await client.conversations.createForAssistant("support_assistant", {
498
- title: "Ticket triage",
134
+ title: "Ticket review",
135
+ });
136
+
137
+ await client.conversations.messages.send(conversation.id, {
138
+ content: "Summarize unresolved issues from today.",
499
139
  });
500
140
  ```
501
141
 
502
- ### Conversations with SSE streaming
142
+ ## Streaming (SSE)
143
+
144
+ Use `readSSE` + `parseSSEJson` for incremental events.
503
145
 
504
146
  ```ts
147
+ import { readSSE, parseSSEJson } from "lemma-sdk";
148
+
505
149
  const stream = await client.conversations.sendMessageStream(conversationId, {
506
- content: "Find open support tickets from yesterday",
150
+ content: "Analyze recent incidents",
507
151
  });
508
152
 
509
153
  for await (const event of readSSE(stream)) {
510
154
  const payload = parseSSEJson(event);
511
155
  if (!payload) continue;
512
- console.log(payload);
156
+ console.log(event.event, payload);
513
157
  }
514
158
  ```
515
159
 
516
- ### Task runs with SSE streaming
160
+ Task stream example:
517
161
 
518
162
  ```ts
519
163
  const task = await client.tasks.create({
520
- agentId: "triage-agent",
521
- input: { ticketId: "TCK-1042" },
522
- runtimeAccountIds: ["acc_123"],
164
+ agent_name: "triage_agent",
165
+ input_data: { ticketId: "TCK-1042" },
523
166
  });
524
167
 
525
- const stream = await client.tasks.stream(task.id);
526
- for await (const event of readSSE(stream)) {
527
- const payload = parseSSEJson(event);
528
- if (!payload) continue;
529
- console.log(payload);
530
- }
168
+ const taskStream = await client.tasks.stream(task.id);
531
169
  ```
532
170
 
533
- ## React Helpers
534
-
535
- Import from `lemma-sdk/react`:
536
-
537
- - `useAuth(client)`
538
- - `AuthGuard`
539
- - `useAgentRunStream(...)`
540
- - `useAssistantRun(...)`
541
- - `useAssistantSession(...)`
542
- - `useTaskSession(...)`
543
- - `useFunctionSession(...)`
544
- - `useFlowSession(...)`
171
+ ## Access Grants (`accessible_tables`)
545
172
 
546
- Core run helpers from `lemma-sdk`:
173
+ When creating agents/functions/assistants, `accessible_tables` must use object entries:
547
174
 
548
- - `normalizeRunStatus(...)`
549
- - `isTerminalTaskStatus(...)`
550
- - `isTerminalFunctionStatus(...)`
551
- - `isTerminalFlowStatus(...)`
552
- - `parseTaskStreamEvent(...)`
553
- - `upsertTaskMessage(...)`
554
- - `parseAssistantStreamEvent(...)`
555
- - `upsertConversationMessage(...)`
556
-
557
- Example:
175
+ ```ts
176
+ import { TableAccessMode } from "lemma-sdk";
558
177
 
559
- ```tsx
560
- import { useAssistantRun } from "lemma-sdk/react";
561
-
562
- const { sendMessage, stop, isStreaming } = useAssistantRun({
563
- client,
564
- podId,
565
- conversationId,
566
- onEvent: (event, payload) => {
567
- console.log(event.event, payload);
568
- },
569
- });
178
+ accessible_tables: [
179
+ { table_name: "expenses", mode: TableAccessMode.READ },
180
+ { table_name: "expense_notes", mode: TableAccessMode.WRITE },
181
+ ];
570
182
  ```
571
183
 
572
- For the SDK consumption UI roadmap (AssistantChat / FunctionInvokeForm / FlowRunExperience / RunPanel), see:
184
+ `["expenses"]` is not valid.
185
+
186
+ ## Auth
573
187
 
574
- - `docs/sdk-consumption-ui-v2.md`
188
+ Default mode is cookie/session auth (`credentials: "include"`).
575
189
 
576
- ## File Resources
190
+ For local browser testing, token injection is supported via local storage key `lemma_token`:
577
191
 
578
192
  ```ts
579
- await client.resources.upload("conversation", conversationId, file);
580
- const files = await client.resources.list("conversation", conversationId);
193
+ import { setTestingToken, clearTestingToken } from "lemma-sdk";
194
+
195
+ setTestingToken("<access-token>");
196
+ clearTestingToken();
581
197
  ```
582
198
 
583
- ## Migration Tips
199
+ Auth helpers:
584
200
 
585
- When migrating from direct `fetch`/custom API clients:
201
+ - `buildAuthUrl(...)`
202
+ - `buildFederatedLogoutUrl(...)`
203
+ - `resolveSafeRedirectUri(...)`
204
+ - `client.auth.redirectToAuth(...)`
205
+ - `client.auth.redirectToFederatedLogout(...)`
586
206
 
587
- 1. Replace auth/session bootstrapping with `LemmaClient`.
588
- 2. Move pod-scoped calls into namespaces (`tasks`, `assistants`, `conversations`, etc.).
589
- 3. Keep rare/unmodeled endpoints on `client.request(...)` temporarily.
590
- 4. Replace SSE parsing code with `readSSE` + `parseSSEJson`.
591
- 5. Gradually lift app-specific run/chat logic into reusable hooks in `lemma-sdk/react`.
207
+ ## React Package (`lemma-sdk/react`)
592
208
 
593
- ## Development
209
+ Includes auth helpers, run hooks, and assistant UI primitives.
594
210
 
595
- ### Regenerate OpenAPI client
211
+ Install React peer dependency in your app if not already installed:
596
212
 
597
213
  ```bash
598
- bash scripts/generate_openapi_client.sh
214
+ npm i react react-dom
599
215
  ```
600
216
 
601
- ### Build SDK
217
+ Import stylesheet once:
602
218
 
603
- ```bash
604
- npm run build
219
+ ```tsx
220
+ import "lemma-sdk/react/styles.css";
605
221
  ```
606
222
 
607
- Output:
223
+ Fastest assistant integration:
608
224
 
609
- - `dist/` npm package artifacts
610
- - `dist/browser/lemma-client.js` standalone browser bundle
611
- - `public/lemma-client.js` committed copy for static serving
225
+ ```tsx
226
+ import { AssistantEmbedded } from "lemma-sdk/react";
227
+
228
+ <div style={{ height: 720, minHeight: 0 }}>
229
+ <AssistantEmbedded
230
+ client={client}
231
+ podId="<pod-id>"
232
+ assistantName="support_assistant"
233
+ title="Support Assistant"
234
+ placeholder="Message Support Assistant"
235
+ showConversationList
236
+ />
237
+ </div>;
238
+ ```
612
239
 
613
- ### Release check
240
+ Auth guard example:
614
241
 
615
- ```bash
616
- npm run release:check
242
+ ```tsx
243
+ import { AuthGuard } from "lemma-sdk/react";
244
+
245
+ <AuthGuard client={client}>
246
+ <App />
247
+ </AuthGuard>;
617
248
  ```
618
249
 
619
- ### Publish
250
+ ## Browser Bundle
620
251
 
621
- ```bash
622
- npm publish
252
+ The package also ships a standalone browser bundle:
253
+
254
+ - npm artifact path: `dist/browser/lemma-client.js`
255
+ - export path: `lemma-sdk/browser-bundle`
256
+ - global: `window.LemmaClient.LemmaClient`
257
+
258
+ Example:
259
+
260
+ ```html
261
+ <script src="https://unpkg.com/lemma-sdk@latest/dist/browser/lemma-client.js"></script>
262
+ <script>
263
+ const client = new window.LemmaClient.LemmaClient({
264
+ apiUrl: "https://api.lemma.work",
265
+ authUrl: "https://auth.lemma.work/auth",
266
+ podId: "<pod-id>"
267
+ });
268
+ </script>
623
269
  ```
624
270
 
625
- As of March 26, 2026, npm package name `lemma` is already taken. This package publishes as `lemma-sdk`.