lemma-sdk 0.2.24 → 0.2.27
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 +175 -506
- package/dist/browser/lemma-client.js +207 -12
- package/dist/client.d.ts +2 -0
- package/dist/client.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/namespaces/pod-join-requests.d.ts +16 -0
- package/dist/namespaces/pod-join-requests.js +24 -0
- package/dist/namespaces/pod-members.d.ts +1 -0
- package/dist/namespaces/pod-members.js +3 -0
- package/dist/openapi_client/index.d.ts +10 -3
- package/dist/openapi_client/index.js +4 -2
- package/dist/openapi_client/models/ColumnSchema.d.ts +4 -0
- package/dist/openapi_client/models/CreateTableRequest.d.ts +1 -1
- package/dist/openapi_client/models/DataStoreFlowStart.d.ts +3 -6
- package/dist/openapi_client/models/DatastoreOperation.d.ts +5 -0
- package/dist/openapi_client/models/DatastoreOperation.js +10 -0
- package/dist/openapi_client/models/DatastoreQueryRequest.d.ts +1 -1
- package/dist/openapi_client/models/FlowInstallEntity.d.ts +2 -2
- package/dist/openapi_client/models/FlowResponse.d.ts +4 -3
- package/dist/openapi_client/models/{FlowStart.d.ts → FlowStart_Input.d.ts} +1 -1
- package/dist/openapi_client/models/FlowStart_Output.d.ts +14 -0
- package/dist/openapi_client/models/FlowStart_Output.js +1 -0
- package/dist/openapi_client/models/PodCreateRequest.d.ts +0 -4
- package/dist/openapi_client/models/PodJoinRequestApproveRequest.d.ts +6 -0
- package/dist/openapi_client/models/PodJoinRequestApproveRequest.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestCreateResponse.d.ts +17 -0
- package/dist/openapi_client/models/PodJoinRequestCreateResponse.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestListResponse.d.ts +7 -0
- package/dist/openapi_client/models/PodJoinRequestListResponse.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestStatus.d.ts +5 -0
- package/dist/openapi_client/models/PodJoinRequestStatus.js +10 -0
- package/dist/openapi_client/models/PodMemberDetailResponse.d.ts +14 -0
- package/dist/openapi_client/models/PodMemberDetailResponse.js +1 -0
- package/dist/openapi_client/models/PodMemberResponse.d.ts +3 -3
- package/dist/openapi_client/models/PodResponse.d.ts +0 -5
- package/dist/openapi_client/models/PodUpdateRequest.d.ts +0 -4
- package/dist/openapi_client/models/WorkflowCreateRequest.d.ts +7 -6
- package/dist/openapi_client/models/WorkflowGraphUpdateRequest.d.ts +2 -2
- package/dist/openapi_client/models/WorkflowInstallMode.d.ts +7 -0
- package/dist/openapi_client/models/WorkflowInstallMode.js +12 -0
- package/dist/openapi_client/models/WorkflowUpdateRequest.d.ts +5 -4
- package/dist/openapi_client/services/FilesService.d.ts +1 -1
- package/dist/openapi_client/services/FilesService.js +1 -1
- package/dist/openapi_client/services/PodJoinRequestsService.d.ts +44 -0
- package/dist/openapi_client/services/PodJoinRequestsService.js +93 -0
- package/dist/openapi_client/services/PodMembersService.d.ts +14 -4
- package/dist/openapi_client/services/PodMembersService.js +29 -8
- package/dist/openapi_client/services/RecordsService.d.ts +2 -2
- package/dist/openapi_client/services/RecordsService.js +2 -2
- package/dist/openapi_client/services/WorkflowsService.d.ts +1 -1
- package/dist/openapi_client/services/WorkflowsService.js +1 -1
- package/dist/react/AuthGuard.d.ts +5 -2
- package/dist/react/AuthGuard.js +126 -3
- package/dist/react/components/AssistantChrome.js +1 -1
- package/dist/react/components/AssistantExperience.d.ts +7 -2
- package/dist/react/components/AssistantExperience.js +272 -32
- package/dist/react/components/assistant-types.d.ts +1 -0
- package/dist/react/index.d.ts +0 -2
- package/dist/react/index.js +0 -1
- package/dist/react/styles.css +594 -224
- package/dist/react/useAssistantController.js +2 -1
- package/dist/react/useAssistantRuntime.d.ts +2 -1
- package/dist/react/useAssistantRuntime.js +7 -3
- package/dist/react/useTaskSession.js +145 -73
- package/dist/task-events.d.ts +2 -1
- package/dist/task-events.js +38 -1
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- package/dist/openapi_client/models/PodStatus.d.ts +0 -4
- package/dist/openapi_client/models/PodStatus.js +0 -9
- package/dist/openapi_client/models/PodType.d.ts +0 -6
- package/dist/openapi_client/models/PodType.js +0 -11
- package/dist/react/useAgentRun.d.ts +0 -17
- package/dist/react/useAgentRun.js +0 -66
- /package/dist/openapi_client/models/{FlowStart.js → FlowStart_Input.js} +0 -0
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
|
|
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
|
|
30
|
-
authUrl: "https://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,264 @@ const assistants = await client.assistants.list({ limit: 20 });
|
|
|
38
31
|
const supportAssistant = await client.assistants.get("support_assistant");
|
|
39
32
|
```
|
|
40
33
|
|
|
41
|
-
##
|
|
42
|
-
|
|
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.
|
|
49
|
-
|
|
50
|
-
## Table Access Grants (`accessible_tables`)
|
|
51
|
-
|
|
52
|
-
For function, agent, and assistant payloads, `accessible_tables` must be an array of objects:
|
|
53
|
-
|
|
54
|
-
- `table_name`: target table
|
|
55
|
-
- `mode`: `READ` or `WRITE`
|
|
56
|
-
|
|
57
|
-
`accessible_tables: ["table_name"]` is no longer valid.
|
|
58
|
-
|
|
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.
|
|
60
|
-
|
|
61
|
-
Examples:
|
|
62
|
-
|
|
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
|
|
34
|
+
## Configuration
|
|
109
35
|
|
|
110
|
-
|
|
111
|
-
import {
|
|
112
|
-
LemmaClient,
|
|
113
|
-
buildAuthUrl,
|
|
114
|
-
buildFederatedLogoutUrl,
|
|
115
|
-
resolveSafeRedirectUri,
|
|
116
|
-
} from "lemma-sdk";
|
|
36
|
+
`LemmaClient` config resolution order:
|
|
117
37
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
38
|
+
1. Constructor overrides
|
|
39
|
+
2. `window.__LEMMA_CONFIG__`
|
|
40
|
+
3. Environment variables
|
|
41
|
+
4. Defaults
|
|
122
42
|
|
|
123
|
-
|
|
124
|
-
const loginUrl = buildAuthUrl(client.authUrl, { redirectUri: "https://app.asur.work/" });
|
|
125
|
-
const signupUrl = buildAuthUrl(client.authUrl, { mode: "signup", redirectUri: "https://app.asur.work/" });
|
|
43
|
+
Supported env keys:
|
|
126
44
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
fallback: "/",
|
|
131
|
-
});
|
|
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`
|
|
132
48
|
|
|
133
|
-
|
|
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 });
|
|
49
|
+
Defaults when unset:
|
|
139
50
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
redirectUri: safeRedirect,
|
|
143
|
-
});
|
|
51
|
+
- `apiUrl`: `http://localhost:8000`
|
|
52
|
+
- `authUrl`: `http://localhost:3000`
|
|
144
53
|
|
|
145
|
-
|
|
146
|
-
await client.auth.redirectToFederatedLogout({ redirectUri: safeRedirect });
|
|
147
|
-
```
|
|
54
|
+
## Pod Scoping
|
|
148
55
|
|
|
149
|
-
|
|
150
|
-
|
|
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.
|
|
56
|
+
Most namespaces are pod-scoped. You can set pod scope in three ways:
|
|
153
57
|
|
|
154
58
|
```ts
|
|
155
|
-
|
|
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();
|
|
59
|
+
client.setPodId("pod_a");
|
|
166
60
|
|
|
167
|
-
|
|
168
|
-
```
|
|
61
|
+
const podBClient = client.withPod("pod_b");
|
|
169
62
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
localStorage.setItem("lemma_token", "<access-token>");
|
|
174
|
-
window.location.reload();
|
|
63
|
+
const conversations = await client.conversations.list({ pod_id: "pod_c" });
|
|
175
64
|
```
|
|
176
65
|
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
66
|
+
## Namespace Overview
|
|
182
67
|
|
|
183
|
-
|
|
68
|
+
Common pod-scoped namespaces:
|
|
184
69
|
|
|
185
|
-
|
|
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`
|
|
186
81
|
|
|
187
|
-
|
|
82
|
+
Org/user-level namespaces:
|
|
188
83
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
84
|
+
- `client.users`
|
|
85
|
+
- `client.icons`
|
|
86
|
+
- `client.pods`
|
|
87
|
+
- `client.podMembers`
|
|
88
|
+
- `client.podJoinRequests`
|
|
89
|
+
- `client.organizations`
|
|
90
|
+
- `client.podSurfaces`
|
|
194
91
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
If you alias the package to local SDK source in Vite, make sure the alias points at the React source and stylesheet:
|
|
92
|
+
Escape hatch for unmapped endpoints:
|
|
198
93
|
|
|
199
94
|
```ts
|
|
200
|
-
|
|
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
|
-
}
|
|
95
|
+
const result = await client.request("GET", "/models");
|
|
273
96
|
```
|
|
274
97
|
|
|
275
|
-
|
|
98
|
+
## CRUD Examples
|
|
276
99
|
|
|
277
|
-
|
|
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
|
|
100
|
+
### Tables + Records
|
|
285
101
|
|
|
286
|
-
|
|
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.
|
|
102
|
+
```ts
|
|
103
|
+
await client.tables.create({ name: "todos" });
|
|
290
104
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
});
|
|
105
|
+
await client.records.create("todos", {
|
|
106
|
+
title: "Ship docs rewrite",
|
|
107
|
+
status: "todo",
|
|
108
|
+
});
|
|
305
109
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
}
|
|
110
|
+
const page = await client.records.list("todos", {
|
|
111
|
+
limit: 20,
|
|
112
|
+
sort: [{ field: "created_at", direction: "desc" }],
|
|
113
|
+
});
|
|
325
114
|
```
|
|
326
115
|
|
|
327
|
-
|
|
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
|
|
116
|
+
### Files (Datastore)
|
|
342
117
|
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
});
|
|
118
|
+
```ts
|
|
119
|
+
await client.files.folder.create("reports", { directoryPath: "/" });
|
|
120
|
+
await client.files.upload(fileBlob, { directoryPath: "/reports", name: "q1.pdf" });
|
|
370
121
|
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
}
|
|
122
|
+
const listing = await client.files.list({ directoryPath: "/reports" });
|
|
123
|
+
const downloaded = await client.files.download("/reports/q1.pdf");
|
|
426
124
|
```
|
|
427
125
|
|
|
428
|
-
|
|
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:
|
|
126
|
+
### Assistants + Conversations
|
|
445
127
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
#### Theming
|
|
453
|
-
|
|
454
|
-
Use `AssistantThemeScope` around custom assistant layouts:
|
|
128
|
+
```ts
|
|
129
|
+
await client.assistants.create({
|
|
130
|
+
name: "support_assistant",
|
|
131
|
+
instruction: "Help with support triage.",
|
|
132
|
+
});
|
|
455
133
|
|
|
456
|
-
|
|
457
|
-
|
|
134
|
+
const conversation = await client.conversations.createForAssistant("support_assistant", {
|
|
135
|
+
title: "Ticket review",
|
|
136
|
+
});
|
|
458
137
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
138
|
+
await client.conversations.messages.send(conversation.id, {
|
|
139
|
+
content: "Summarize unresolved issues from today.",
|
|
140
|
+
});
|
|
462
141
|
```
|
|
463
142
|
|
|
464
|
-
|
|
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:
|
|
143
|
+
### Pod Join Requests
|
|
482
144
|
|
|
483
145
|
```ts
|
|
484
|
-
|
|
485
|
-
await client.
|
|
486
|
-
await client.assistants.delete("old_assistant");
|
|
487
|
-
```
|
|
146
|
+
// Current user requests access to a pod
|
|
147
|
+
await client.podJoinRequests.create("pod_123");
|
|
488
148
|
|
|
489
|
-
|
|
149
|
+
// Current user's pending request (or null)
|
|
150
|
+
const mine = await client.podJoinRequests.me("pod_123");
|
|
490
151
|
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
limit:
|
|
152
|
+
// Admin view of requests for a pod
|
|
153
|
+
const requests = await client.podJoinRequests.list("pod_123", {
|
|
154
|
+
status: "PENDING",
|
|
155
|
+
limit: 50,
|
|
495
156
|
});
|
|
496
157
|
|
|
497
|
-
|
|
498
|
-
|
|
158
|
+
// Admin approval (defaults: ORG_MEMBER + POD_USER)
|
|
159
|
+
await client.podJoinRequests.approve("pod_123", "join_req_abc", {
|
|
160
|
+
org_role: "ORG_MEMBER",
|
|
161
|
+
pod_role: "POD_USER",
|
|
499
162
|
});
|
|
500
163
|
```
|
|
501
164
|
|
|
502
|
-
|
|
165
|
+
## Streaming (SSE)
|
|
166
|
+
|
|
167
|
+
Use `readSSE` + `parseSSEJson` for incremental events.
|
|
503
168
|
|
|
504
169
|
```ts
|
|
170
|
+
import { readSSE, parseSSEJson } from "lemma-sdk";
|
|
171
|
+
|
|
505
172
|
const stream = await client.conversations.sendMessageStream(conversationId, {
|
|
506
|
-
content: "
|
|
173
|
+
content: "Analyze recent incidents",
|
|
507
174
|
});
|
|
508
175
|
|
|
509
176
|
for await (const event of readSSE(stream)) {
|
|
510
177
|
const payload = parseSSEJson(event);
|
|
511
178
|
if (!payload) continue;
|
|
512
|
-
console.log(payload);
|
|
179
|
+
console.log(event.event, payload);
|
|
513
180
|
}
|
|
514
181
|
```
|
|
515
182
|
|
|
516
|
-
|
|
183
|
+
Task stream example:
|
|
517
184
|
|
|
518
185
|
```ts
|
|
519
186
|
const task = await client.tasks.create({
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
runtimeAccountIds: ["acc_123"],
|
|
187
|
+
agent_name: "triage_agent",
|
|
188
|
+
input_data: { ticketId: "TCK-1042" },
|
|
523
189
|
});
|
|
524
190
|
|
|
525
|
-
const
|
|
526
|
-
for await (const event of readSSE(stream)) {
|
|
527
|
-
const payload = parseSSEJson(event);
|
|
528
|
-
if (!payload) continue;
|
|
529
|
-
console.log(payload);
|
|
530
|
-
}
|
|
191
|
+
const taskStream = await client.tasks.stream(task.id);
|
|
531
192
|
```
|
|
532
193
|
|
|
533
|
-
##
|
|
194
|
+
## Access Grants (`accessible_tables`)
|
|
534
195
|
|
|
535
|
-
|
|
196
|
+
When creating agents/functions/assistants, `accessible_tables` must use object entries:
|
|
536
197
|
|
|
537
|
-
|
|
538
|
-
-
|
|
539
|
-
- `useAgentRunStream(...)`
|
|
540
|
-
- `useAssistantRun(...)`
|
|
541
|
-
- `useAssistantSession(...)`
|
|
542
|
-
- `useTaskSession(...)`
|
|
543
|
-
- `useFunctionSession(...)`
|
|
544
|
-
- `useFlowSession(...)`
|
|
545
|
-
|
|
546
|
-
Core run helpers from `lemma-sdk`:
|
|
547
|
-
|
|
548
|
-
- `normalizeRunStatus(...)`
|
|
549
|
-
- `isTerminalTaskStatus(...)`
|
|
550
|
-
- `isTerminalFunctionStatus(...)`
|
|
551
|
-
- `isTerminalFlowStatus(...)`
|
|
552
|
-
- `parseTaskStreamEvent(...)`
|
|
553
|
-
- `upsertTaskMessage(...)`
|
|
554
|
-
- `parseAssistantStreamEvent(...)`
|
|
555
|
-
- `upsertConversationMessage(...)`
|
|
556
|
-
|
|
557
|
-
Example:
|
|
198
|
+
```ts
|
|
199
|
+
import { TableAccessMode } from "lemma-sdk";
|
|
558
200
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
client,
|
|
564
|
-
podId,
|
|
565
|
-
conversationId,
|
|
566
|
-
onEvent: (event, payload) => {
|
|
567
|
-
console.log(event.event, payload);
|
|
568
|
-
},
|
|
569
|
-
});
|
|
201
|
+
accessible_tables: [
|
|
202
|
+
{ table_name: "expenses", mode: TableAccessMode.READ },
|
|
203
|
+
{ table_name: "expense_notes", mode: TableAccessMode.WRITE },
|
|
204
|
+
];
|
|
570
205
|
```
|
|
571
206
|
|
|
572
|
-
|
|
207
|
+
`["expenses"]` is not valid.
|
|
208
|
+
|
|
209
|
+
## Auth
|
|
573
210
|
|
|
574
|
-
|
|
211
|
+
Default mode is cookie/session auth (`credentials: "include"`).
|
|
575
212
|
|
|
576
|
-
|
|
213
|
+
For local browser testing, token injection is supported via local storage key `lemma_token`:
|
|
577
214
|
|
|
578
215
|
```ts
|
|
579
|
-
|
|
580
|
-
|
|
216
|
+
import { setTestingToken, clearTestingToken } from "lemma-sdk";
|
|
217
|
+
|
|
218
|
+
setTestingToken("<access-token>");
|
|
219
|
+
clearTestingToken();
|
|
581
220
|
```
|
|
582
221
|
|
|
583
|
-
|
|
222
|
+
Auth helpers:
|
|
584
223
|
|
|
585
|
-
|
|
224
|
+
- `buildAuthUrl(...)`
|
|
225
|
+
- `buildFederatedLogoutUrl(...)`
|
|
226
|
+
- `resolveSafeRedirectUri(...)`
|
|
227
|
+
- `client.auth.redirectToAuth(...)`
|
|
228
|
+
- `client.auth.redirectToFederatedLogout(...)`
|
|
586
229
|
|
|
587
|
-
|
|
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`.
|
|
230
|
+
## React Package (`lemma-sdk/react`)
|
|
592
231
|
|
|
593
|
-
|
|
232
|
+
Includes auth helpers, run hooks, and assistant UI primitives.
|
|
594
233
|
|
|
595
|
-
|
|
234
|
+
Install React peer dependency in your app if not already installed:
|
|
596
235
|
|
|
597
236
|
```bash
|
|
598
|
-
|
|
237
|
+
npm i react react-dom
|
|
599
238
|
```
|
|
600
239
|
|
|
601
|
-
|
|
240
|
+
Import stylesheet once:
|
|
602
241
|
|
|
603
|
-
```
|
|
604
|
-
|
|
242
|
+
```tsx
|
|
243
|
+
import "lemma-sdk/react/styles.css";
|
|
605
244
|
```
|
|
606
245
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
- `dist/` npm package artifacts
|
|
610
|
-
- `dist/browser/lemma-client.js` standalone browser bundle
|
|
611
|
-
- `public/lemma-client.js` committed copy for static serving
|
|
246
|
+
Fastest assistant integration:
|
|
612
247
|
|
|
613
|
-
|
|
248
|
+
```tsx
|
|
249
|
+
import { AssistantEmbedded } from "lemma-sdk/react";
|
|
614
250
|
|
|
615
|
-
|
|
616
|
-
|
|
251
|
+
<div style={{ height: 720, minHeight: 0 }}>
|
|
252
|
+
<AssistantEmbedded
|
|
253
|
+
client={client}
|
|
254
|
+
podId="<pod-id>"
|
|
255
|
+
assistantName="support_assistant"
|
|
256
|
+
title="Support Assistant"
|
|
257
|
+
placeholder="Message Support Assistant"
|
|
258
|
+
showConversationList
|
|
259
|
+
/>
|
|
260
|
+
</div>;
|
|
617
261
|
```
|
|
618
262
|
|
|
619
|
-
|
|
263
|
+
Auth guard example:
|
|
620
264
|
|
|
621
|
-
```
|
|
622
|
-
|
|
265
|
+
```tsx
|
|
266
|
+
import { AuthGuard } from "lemma-sdk/react";
|
|
267
|
+
|
|
268
|
+
<AuthGuard client={client}>
|
|
269
|
+
<App />
|
|
270
|
+
</AuthGuard>;
|
|
623
271
|
```
|
|
624
272
|
|
|
625
|
-
|
|
273
|
+
When `client.podId` is set and the signed-in user is not a pod member, `AuthGuard` automatically renders a request-access state and can create/view pod join requests.
|
|
274
|
+
|
|
275
|
+
## Browser Bundle
|
|
276
|
+
|
|
277
|
+
The package also ships a standalone browser bundle:
|
|
278
|
+
|
|
279
|
+
- npm artifact path: `dist/browser/lemma-client.js`
|
|
280
|
+
- export path: `lemma-sdk/browser-bundle`
|
|
281
|
+
- global: `window.LemmaClient.LemmaClient`
|
|
282
|
+
|
|
283
|
+
Example:
|
|
284
|
+
|
|
285
|
+
```html
|
|
286
|
+
<script src="https://unpkg.com/lemma-sdk@latest/dist/browser/lemma-client.js"></script>
|
|
287
|
+
<script>
|
|
288
|
+
const client = new window.LemmaClient.LemmaClient({
|
|
289
|
+
apiUrl: "https://api.lemma.work",
|
|
290
|
+
authUrl: "https://auth.lemma.work/auth",
|
|
291
|
+
podId: "<pod-id>"
|
|
292
|
+
});
|
|
293
|
+
</script>
|
|
294
|
+
```
|