lemma-sdk 0.2.23 → 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 (75) hide show
  1. package/README.md +156 -511
  2. package/dist/auth.js +0 -1
  3. package/dist/browser/lemma-client.js +196 -112
  4. package/dist/namespaces/assistants.d.ts +21 -3
  5. package/dist/namespaces/assistants.js +13 -7
  6. package/dist/namespaces/files.d.ts +9 -4
  7. package/dist/namespaces/files.js +52 -14
  8. package/dist/namespaces/records.d.ts +10 -2
  9. package/dist/namespaces/records.js +15 -9
  10. package/dist/openapi_client/index.d.ts +7 -7
  11. package/dist/openapi_client/index.js +2 -4
  12. package/dist/openapi_client/models/ColumnSchema.d.ts +4 -0
  13. package/dist/openapi_client/models/CreateConversationRequest.d.ts +1 -1
  14. package/dist/openapi_client/models/CreateFolderRequest.d.ts +1 -2
  15. package/dist/openapi_client/models/CreateTableRequest.d.ts +1 -1
  16. package/dist/openapi_client/models/CreateTriggerRequest.d.ts +0 -1
  17. package/dist/openapi_client/models/DatastoreFileUploadRequest.d.ts +1 -1
  18. package/dist/openapi_client/models/DatastoreQueryRequest.d.ts +9 -0
  19. package/dist/openapi_client/models/DatastoreQueryResponse.d.ts +7 -0
  20. package/dist/openapi_client/models/DirectoryTreeNode.d.ts +7 -0
  21. package/dist/openapi_client/models/DirectoryTreeResponse.d.ts +6 -0
  22. package/dist/openapi_client/models/DirectoryTreeResponse.js +1 -0
  23. package/dist/openapi_client/models/FileResponse.d.ts +4 -6
  24. package/dist/openapi_client/models/FileSearchRequest.d.ts +5 -3
  25. package/dist/openapi_client/models/FileSearchResultSchema.d.ts +1 -0
  26. package/dist/openapi_client/models/FileSearchScopeMode.d.ts +4 -0
  27. package/dist/openapi_client/models/FileSearchScopeMode.js +9 -0
  28. package/dist/openapi_client/models/PodCreateRequest.d.ts +0 -4
  29. package/dist/openapi_client/models/PodMemberDetailResponse.d.ts +14 -0
  30. package/dist/openapi_client/models/PodMemberDetailResponse.js +1 -0
  31. package/dist/openapi_client/models/PodMemberResponse.d.ts +3 -3
  32. package/dist/openapi_client/models/PodResponse.d.ts +0 -5
  33. package/dist/openapi_client/models/PodUpdateRequest.d.ts +0 -4
  34. package/dist/openapi_client/models/TableResponse.d.ts +1 -1
  35. package/dist/openapi_client/models/TriggerResponse.d.ts +0 -1
  36. package/dist/openapi_client/models/update.d.ts +2 -2
  37. package/dist/openapi_client/services/ConversationsService.d.ts +3 -2
  38. package/dist/openapi_client/services/ConversationsService.js +5 -3
  39. package/dist/openapi_client/services/FilesService.d.ts +34 -25
  40. package/dist/openapi_client/services/FilesService.js +75 -47
  41. package/dist/openapi_client/services/PodMembersService.d.ts +14 -4
  42. package/dist/openapi_client/services/PodMembersService.js +29 -8
  43. package/dist/openapi_client/services/QueryService.d.ts +14 -0
  44. package/dist/openapi_client/services/QueryService.js +26 -0
  45. package/dist/openapi_client/services/RecordsService.d.ts +7 -13
  46. package/dist/openapi_client/services/RecordsService.js +12 -26
  47. package/dist/react/components/AssistantEmbedded.d.ts +1 -1
  48. package/dist/react/components/AssistantEmbedded.js +2 -1
  49. package/dist/react/index.d.ts +0 -2
  50. package/dist/react/index.js +0 -1
  51. package/dist/react/useAssistantController.d.ts +5 -1
  52. package/dist/react/useAssistantController.js +7 -3
  53. package/dist/react/useAssistantSession.d.ts +12 -0
  54. package/dist/react/useAssistantSession.js +24 -5
  55. package/dist/react/useTaskSession.js +145 -73
  56. package/dist/task-events.d.ts +2 -1
  57. package/dist/task-events.js +38 -1
  58. package/dist/types.d.ts +10 -4
  59. package/package.json +1 -1
  60. package/dist/openapi_client/models/PodStatus.d.ts +0 -4
  61. package/dist/openapi_client/models/PodStatus.js +0 -9
  62. package/dist/openapi_client/models/PodType.d.ts +0 -6
  63. package/dist/openapi_client/models/PodType.js +0 -11
  64. package/dist/openapi_client/models/RecordFilter.d.ts +0 -15
  65. package/dist/openapi_client/models/RecordFilterOperator.d.ts +0 -10
  66. package/dist/openapi_client/models/RecordFilterOperator.js +0 -15
  67. package/dist/openapi_client/models/RecordQueryRequest.d.ts +0 -20
  68. package/dist/openapi_client/models/RecordSort.d.ts +0 -11
  69. package/dist/openapi_client/models/RecordSortDirection.d.ts +0 -4
  70. package/dist/openapi_client/models/RecordSortDirection.js +0 -9
  71. package/dist/react/useAgentRun.d.ts +0 -17
  72. package/dist/react/useAgentRun.js +0 -66
  73. /package/dist/openapi_client/models/{RecordFilter.js → DatastoreQueryRequest.js} +0 -0
  74. /package/dist/openapi_client/models/{RecordQueryRequest.js → DatastoreQueryResponse.js} +0 -0
  75. /package/dist/openapi_client/models/{RecordSort.js → DirectoryTreeNode.js} +0 -0
@@ -3,6 +3,7 @@ import { useAssistantRuntime } from "./useAssistantRuntime.js";
3
3
  import { useAssistantSession } from "./useAssistantSession.js";
4
4
  const EMPTY_SCOPE_KEY = JSON.stringify({
5
5
  podId: null,
6
+ assistantName: null,
6
7
  assistantId: null,
7
8
  organizationId: null,
8
9
  });
@@ -538,7 +539,7 @@ function isConversationRunning(status) {
538
539
  }
539
540
  return true;
540
541
  }
541
- export function useAssistantController({ client, podId, assistantId, organizationId, enabled = true, }) {
542
+ export function useAssistantController({ client, podId, assistantName, assistantId, organizationId, enabled = true, }) {
542
543
  const [localError, setLocalError] = useState(null);
543
544
  const [messages, setMessages] = useState([]);
544
545
  const [conversations, setConversations] = useState([]);
@@ -560,20 +561,23 @@ export function useAssistantController({ client, podId, assistantId, organizatio
560
561
  const skipInitialLoadConversationIdsRef = useRef(new Set());
561
562
  const scope = useMemo(() => ({
562
563
  podId: podId ?? null,
564
+ assistantName: assistantName ?? assistantId ?? null,
563
565
  assistantId: assistantId ?? null,
564
566
  organizationId: organizationId ?? null,
565
- }), [assistantId, organizationId, podId]);
567
+ }), [assistantId, assistantName, organizationId, podId]);
566
568
  const scopeKey = useMemo(() => JSON.stringify({
567
569
  podId: scope.podId ?? null,
570
+ assistantName: scope.assistantName ?? null,
568
571
  assistantId: scope.assistantId ?? null,
569
572
  organizationId: scope.organizationId ?? null,
570
- }), [scope.assistantId, scope.organizationId, scope.podId]);
573
+ }), [scope.assistantId, scope.assistantName, scope.organizationId, scope.podId]);
571
574
  const handleAssistantSessionError = useCallback((sessionError) => {
572
575
  setLocalError((prev) => prev || (sessionError instanceof Error ? sessionError.message : "Assistant session failed"));
573
576
  }, []);
574
577
  const assistantSession = useAssistantSession({
575
578
  client,
576
579
  podId: scope.podId ?? undefined,
580
+ assistantName: scope.assistantName ?? undefined,
577
581
  assistantId: scope.assistantId ?? undefined,
578
582
  organizationId: scope.organizationId ?? undefined,
579
583
  conversationId: activeConversationId ?? undefined,
@@ -3,12 +3,20 @@ import { type SseRawEvent } from "../streams.js";
3
3
  import type { Conversation, ConversationMessage, ConversationModel, CursorPage } from "../types.js";
4
4
  interface ConversationScope {
5
5
  podId?: string | null;
6
+ assistantName?: string | null;
7
+ /**
8
+ * @deprecated Use assistantName instead.
9
+ */
6
10
  assistantId?: string | null;
7
11
  organizationId?: string | null;
8
12
  }
9
13
  export interface UseAssistantSessionOptions {
10
14
  client: LemmaClient;
11
15
  podId?: string;
16
+ assistantName?: string;
17
+ /**
18
+ * @deprecated Use assistantName instead.
19
+ */
12
20
  assistantId?: string;
13
21
  organizationId?: string;
14
22
  conversationId?: string | null;
@@ -24,6 +32,10 @@ export interface CreateConversationInput {
24
32
  title?: string | null;
25
33
  model?: ConversationModel | null;
26
34
  podId?: string | null;
35
+ assistantName?: string | null;
36
+ /**
37
+ * @deprecated Use assistantName instead.
38
+ */
27
39
  assistantId?: string | null;
28
40
  organizationId?: string | null;
29
41
  setActive?: boolean;
@@ -23,8 +23,14 @@ function normalizeError(error, fallback) {
23
23
  return new Error(fallback);
24
24
  }
25
25
  function normalizeScope(client, defaults, override) {
26
+ const resolvedAssistantName = override?.assistantName
27
+ ?? override?.assistantId
28
+ ?? defaults.assistantName
29
+ ?? defaults.assistantId
30
+ ?? null;
26
31
  return {
27
32
  podId: override?.podId ?? defaults.podId ?? client.podId ?? null,
33
+ assistantName: resolvedAssistantName,
28
34
  assistantId: override?.assistantId ?? defaults.assistantId ?? null,
29
35
  organizationId: override?.organizationId ?? defaults.organizationId ?? null,
30
36
  };
@@ -48,7 +54,7 @@ function resolveResumeInput(input) {
48
54
  return input ?? {};
49
55
  }
50
56
  export function useAssistantSession(options) {
51
- const { client, podId: defaultPodId, assistantId: defaultAssistantId, organizationId: defaultOrganizationId, conversationId: externalConversationId = null, autoLoad = true, autoResume = false, syncOnTurnEnd = false, onEvent, onStatus, onMessage, onError, } = options;
57
+ const { client, podId: defaultPodId, assistantName: defaultAssistantName, assistantId: defaultAssistantId, organizationId: defaultOrganizationId, conversationId: externalConversationId = null, autoLoad = true, autoResume = false, syncOnTurnEnd = false, onEvent, onStatus, onMessage, onError, } = options;
52
58
  const [conversationId, setConversationIdState] = useState(externalConversationId);
53
59
  const [conversation, setConversation] = useState(null);
54
60
  const [status, setStatus] = useState(undefined);
@@ -129,16 +135,17 @@ export function useAssistantSession(options) {
129
135
  }, []);
130
136
  const defaultScope = useMemo(() => ({
131
137
  podId: defaultPodId ?? null,
138
+ assistantName: defaultAssistantName ?? defaultAssistantId ?? null,
132
139
  assistantId: defaultAssistantId ?? null,
133
140
  organizationId: defaultOrganizationId ?? null,
134
- }), [defaultAssistantId, defaultOrganizationId, defaultPodId]);
141
+ }), [defaultAssistantId, defaultAssistantName, defaultOrganizationId, defaultPodId]);
135
142
  const listConversations = useCallback(async (input = {}) => {
136
143
  try {
137
144
  const scope = normalizeScope(client, defaultScope, input.scope);
138
145
  applyPodScope(client, scope.podId);
139
146
  const response = await client.conversations.list({
140
147
  pod_id: scope.podId ?? undefined,
141
- assistant_id: scope.assistantId ?? undefined,
148
+ assistant_name: scope.assistantName ?? scope.assistantId ?? undefined,
142
149
  organization_id: scope.organizationId ?? undefined,
143
150
  limit: input.limit,
144
151
  page_token: input.pageToken,
@@ -165,7 +172,11 @@ export function useAssistantSession(options) {
165
172
  const payload = {
166
173
  title: input.title ?? undefined,
167
174
  pod_id: input.podId ?? defaultPodId ?? client.podId ?? undefined,
168
- assistant_id: input.assistantId ?? defaultAssistantId ?? undefined,
175
+ assistant_name: input.assistantName
176
+ ?? input.assistantId
177
+ ?? defaultAssistantName
178
+ ?? defaultAssistantId
179
+ ?? undefined,
169
180
  organization_id: input.organizationId ?? defaultOrganizationId ?? undefined,
170
181
  model: typeof input.model === "undefined"
171
182
  ? undefined
@@ -181,7 +192,15 @@ export function useAssistantSession(options) {
181
192
  autoResumedKeyRef.current = null;
182
193
  }
183
194
  return created;
184
- }, [clearStreamingText, client, defaultAssistantId, defaultOrganizationId, defaultPodId, setConversationStatus]);
195
+ }, [
196
+ clearStreamingText,
197
+ client,
198
+ defaultAssistantId,
199
+ defaultAssistantName,
200
+ defaultOrganizationId,
201
+ defaultPodId,
202
+ setConversationStatus,
203
+ ]);
185
204
  const refreshConversation = useCallback(async (explicitConversationId) => {
186
205
  const id = explicitConversationId ?? conversationId;
187
206
  if (!id)
@@ -14,6 +14,9 @@ function normalizeError(error, fallback) {
14
14
  return error;
15
15
  return new Error(fallback);
16
16
  }
17
+ function sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
17
20
  export function useTaskSession({ client, podId, taskId: externalTaskId = null, autoConnect = true, autoConnectOnStart = true, onEvent, onStatus, onMessage, onError, }) {
18
21
  const [taskId, setTaskIdState] = useState(externalTaskId);
19
22
  const [task, setTask] = useState(null);
@@ -22,53 +25,88 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
22
25
  const [isStreaming, setIsStreaming] = useState(false);
23
26
  const [error, setError] = useState(null);
24
27
  const abortRef = useRef(null);
25
- const setTaskId = useCallback((nextTaskId) => {
26
- setTaskIdState(nextTaskId);
27
- if (!nextTaskId) {
28
- setTask(null);
29
- setStatus(undefined);
30
- setMessages([]);
28
+ const previousExternalTaskIdRef = useRef(externalTaskId);
29
+ const taskIdRef = useRef(externalTaskId);
30
+ const statusRef = useRef(undefined);
31
+ const onEventRef = useRef(onEvent);
32
+ const onStatusRef = useRef(onStatus);
33
+ const onMessageRef = useRef(onMessage);
34
+ const onErrorRef = useRef(onError);
35
+ const setTaskStatus = useCallback((nextStatus) => {
36
+ const normalized = normalizeRunStatus(nextStatus);
37
+ setStatus(normalized);
38
+ statusRef.current = normalized;
39
+ if (normalized) {
40
+ onStatusRef.current?.(normalized);
31
41
  }
32
42
  }, []);
43
+ const setTaskId = useCallback((nextTaskId) => {
44
+ abortRef.current?.abort();
45
+ abortRef.current = null;
46
+ setTaskIdState((currentTaskId) => {
47
+ if (currentTaskId === nextTaskId) {
48
+ return currentTaskId;
49
+ }
50
+ setError(null);
51
+ setIsStreaming(false);
52
+ if (!nextTaskId) {
53
+ setTask(null);
54
+ setTaskStatus(undefined);
55
+ setMessages([]);
56
+ }
57
+ return nextTaskId;
58
+ });
59
+ }, [setTaskStatus]);
33
60
  const disconnect = useCallback(() => {
34
61
  abortRef.current?.abort();
35
62
  abortRef.current = null;
63
+ setIsStreaming(false);
36
64
  }, []);
37
65
  useEffect(() => {
38
- if (externalTaskId === taskId)
66
+ taskIdRef.current = taskId;
67
+ }, [taskId]);
68
+ useEffect(() => {
69
+ statusRef.current = status;
70
+ }, [status]);
71
+ useEffect(() => {
72
+ onEventRef.current = onEvent;
73
+ }, [onEvent]);
74
+ useEffect(() => {
75
+ onStatusRef.current = onStatus;
76
+ }, [onStatus]);
77
+ useEffect(() => {
78
+ onMessageRef.current = onMessage;
79
+ }, [onMessage]);
80
+ useEffect(() => {
81
+ onErrorRef.current = onError;
82
+ }, [onError]);
83
+ useEffect(() => {
84
+ if (previousExternalTaskIdRef.current === externalTaskId) {
39
85
  return;
40
- setTaskIdState(externalTaskId);
41
- if (!externalTaskId) {
42
- disconnect();
43
- setTask(null);
44
- setStatus(undefined);
45
- setMessages([]);
46
86
  }
47
- }, [disconnect, externalTaskId, taskId]);
87
+ previousExternalTaskIdRef.current = externalTaskId;
88
+ setTaskId(externalTaskId);
89
+ }, [externalTaskId, setTaskId]);
48
90
  const refreshTask = useCallback(async (explicitTaskId) => {
49
- const id = explicitTaskId ?? taskId;
91
+ const id = explicitTaskId ?? taskIdRef.current;
50
92
  if (!id)
51
93
  return null;
52
94
  try {
53
95
  client.setPodId(resolvePodId(client, podId));
54
96
  const nextTask = await client.tasks.get(id);
55
97
  setTask(nextTask);
56
- const nextStatus = normalizeRunStatus(nextTask.status);
57
- setStatus(nextStatus);
58
- if (nextStatus) {
59
- onStatus?.(nextStatus);
60
- }
98
+ setTaskStatus(nextTask.status);
61
99
  return nextTask;
62
100
  }
63
101
  catch (refreshError) {
64
102
  const normalized = normalizeError(refreshError, "Failed to fetch task.");
65
103
  setError(normalized);
66
- onError?.(refreshError);
104
+ onErrorRef.current?.(refreshError);
67
105
  return null;
68
106
  }
69
- }, [client, onError, onStatus, podId, taskId]);
107
+ }, [client, podId, setTaskStatus]);
70
108
  const loadMessages = useCallback(async (explicitTaskId) => {
71
- const id = explicitTaskId ?? taskId;
109
+ const id = explicitTaskId ?? taskIdRef.current;
72
110
  if (!id)
73
111
  return [];
74
112
  try {
@@ -81,57 +119,86 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
81
119
  catch (messageError) {
82
120
  const normalized = normalizeError(messageError, "Failed to fetch task messages.");
83
121
  setError(normalized);
84
- onError?.(messageError);
122
+ onErrorRef.current?.(messageError);
85
123
  return [];
86
124
  }
87
- }, [client, onError, podId, taskId]);
125
+ }, [client, podId]);
88
126
  const connect = useCallback(async (explicitTaskId) => {
89
- const id = explicitTaskId ?? taskId;
127
+ const id = explicitTaskId ?? taskIdRef.current;
90
128
  if (!id)
91
129
  return;
92
130
  setTaskIdState(id);
131
+ taskIdRef.current = id;
93
132
  disconnect();
94
133
  const controller = new AbortController();
95
134
  abortRef.current = controller;
96
135
  setError(null);
97
136
  setIsStreaming(true);
137
+ let reconnectDelayMs = 1000;
98
138
  try {
99
- client.setPodId(resolvePodId(client, podId));
100
- const stream = await client.tasks.stream(id, { signal: controller.signal });
101
- for await (const event of readSSE(stream)) {
102
- if (controller.signal.aborted) {
139
+ while (!controller.signal.aborted) {
140
+ if (isTerminalTaskStatus(statusRef.current)) {
103
141
  break;
104
142
  }
105
- const payload = parseSSEJson(event);
106
- onEvent?.(event, payload);
107
- const parsed = parseTaskStreamEvent(payload);
108
- if (parsed.message) {
109
- setMessages((previous) => upsertTaskMessage(previous, parsed.message));
110
- onMessage?.(parsed.message);
143
+ try {
144
+ client.setPodId(resolvePodId(client, podId));
145
+ const stream = await client.tasks.stream(id, { signal: controller.signal });
146
+ reconnectDelayMs = 1000;
147
+ for await (const event of readSSE(stream)) {
148
+ if (controller.signal.aborted) {
149
+ break;
150
+ }
151
+ const payload = parseSSEJson(event);
152
+ onEventRef.current?.(event, payload);
153
+ const parsed = parseTaskStreamEvent(payload);
154
+ if (parsed.task?.id === id) {
155
+ setTask(parsed.task);
156
+ setTaskStatus(parsed.task.status);
157
+ }
158
+ if (parsed.message) {
159
+ setMessages((previous) => upsertTaskMessage(previous, parsed.message));
160
+ onMessageRef.current?.(parsed.message);
161
+ }
162
+ if (parsed.status) {
163
+ setTaskStatus(parsed.status);
164
+ setTask((previous) => {
165
+ if (!previous || previous.id !== id)
166
+ return previous;
167
+ return {
168
+ ...previous,
169
+ status: parsed.status,
170
+ };
171
+ });
172
+ }
173
+ if (isTerminalTaskStatus(statusRef.current)) {
174
+ break;
175
+ }
176
+ }
177
+ if (controller.signal.aborted || isTerminalTaskStatus(statusRef.current)) {
178
+ break;
179
+ }
180
+ await sleep(Math.max(reconnectDelayMs, 2000));
181
+ reconnectDelayMs = Math.min(Math.max(reconnectDelayMs * 2, 2000), 6000);
111
182
  }
112
- if (parsed.status) {
113
- setStatus(parsed.status);
114
- onStatus?.(parsed.status);
115
- if (isTerminalTaskStatus(parsed.status)) {
183
+ catch (streamError) {
184
+ if (streamError instanceof Error && streamError.name === "AbortError") {
116
185
  break;
117
186
  }
187
+ const normalized = normalizeError(streamError, "Failed to stream task run.");
188
+ setError(normalized);
189
+ onErrorRef.current?.(streamError);
190
+ await sleep(reconnectDelayMs);
191
+ reconnectDelayMs = Math.min(reconnectDelayMs * 2, 6000);
118
192
  }
119
193
  }
120
194
  }
121
- catch (streamError) {
122
- if (!(streamError instanceof Error && streamError.name === "AbortError")) {
123
- const normalized = normalizeError(streamError, "Failed to stream task run.");
124
- setError(normalized);
125
- onError?.(streamError);
126
- }
127
- }
128
195
  finally {
129
196
  if (abortRef.current === controller) {
130
197
  abortRef.current = null;
131
198
  }
132
199
  setIsStreaming(false);
133
200
  }
134
- }, [client, disconnect, onError, onEvent, onMessage, onStatus, podId, taskId]);
201
+ }, [client, disconnect, podId, setTaskStatus]);
135
202
  const start = useCallback(async (input) => {
136
203
  setError(null);
137
204
  client.setPodId(resolvePodId(client, podId));
@@ -139,55 +206,60 @@ export function useTaskSession({ client, podId, taskId: externalTaskId = null, a
139
206
  agent_name: input.agentName,
140
207
  input_data: input.inputData,
141
208
  });
209
+ taskIdRef.current = created.id;
142
210
  setTask(created);
143
211
  setTaskIdState(created.id);
144
- const nextStatus = normalizeRunStatus(created.status);
145
- setStatus(nextStatus);
146
- if (nextStatus) {
147
- onStatus?.(nextStatus);
148
- }
212
+ setMessages([]);
213
+ setTaskStatus(created.status);
149
214
  if (autoConnectOnStart && !autoConnect) {
150
215
  await connect(created.id);
151
216
  }
152
217
  return created;
153
- }, [autoConnect, autoConnectOnStart, client, connect, onStatus, podId]);
218
+ }, [autoConnect, autoConnectOnStart, client, connect, podId, setTaskStatus]);
154
219
  const stop = useCallback(async () => {
155
- if (!taskId)
220
+ const id = taskIdRef.current;
221
+ if (!id)
156
222
  return null;
157
223
  try {
158
224
  client.setPodId(resolvePodId(client, podId));
159
- const stopped = await client.tasks.stop(taskId);
225
+ const stopped = await client.tasks.stop(id);
160
226
  setTask(stopped);
161
- const nextStatus = normalizeRunStatus(stopped.status);
162
- setStatus(nextStatus);
163
- if (nextStatus) {
164
- onStatus?.(nextStatus);
165
- }
227
+ setTaskStatus(stopped.status);
166
228
  return stopped;
167
229
  }
168
230
  catch (stopError) {
169
231
  const normalized = normalizeError(stopError, "Failed to stop task run.");
170
232
  setError(normalized);
171
- onError?.(stopError);
233
+ onErrorRef.current?.(stopError);
172
234
  return null;
173
235
  }
174
- }, [client, onError, onStatus, podId, taskId]);
236
+ }, [client, podId, setTaskStatus]);
175
237
  const clearMessages = useCallback(() => {
176
238
  setMessages([]);
177
239
  }, []);
178
240
  useEffect(() => {
179
241
  if (!taskId)
180
242
  return;
181
- void refreshTask(taskId);
182
- void loadMessages(taskId);
183
- }, [loadMessages, refreshTask, taskId]);
184
- useEffect(() => {
185
- if (!autoConnect || !taskId) {
186
- return;
187
- }
188
- void connect(taskId);
189
- return () => disconnect();
190
- }, [autoConnect, connect, disconnect, taskId]);
243
+ let cancelled = false;
244
+ const bootstrapTask = async () => {
245
+ const latestTask = await refreshTask(taskId);
246
+ if (cancelled)
247
+ return;
248
+ await loadMessages(taskId);
249
+ if (cancelled || !autoConnect)
250
+ return;
251
+ const latestStatus = normalizeRunStatus(latestTask?.status) ?? normalizeRunStatus(statusRef.current);
252
+ if (isTerminalTaskStatus(latestStatus)) {
253
+ return;
254
+ }
255
+ await connect(taskId);
256
+ };
257
+ void bootstrapTask();
258
+ return () => {
259
+ cancelled = true;
260
+ disconnect();
261
+ };
262
+ }, [autoConnect, connect, disconnect, loadMessages, refreshTask, taskId]);
191
263
  return {
192
264
  taskId,
193
265
  task,
@@ -1,7 +1,8 @@
1
- import type { TaskMessage } from "./types.js";
1
+ import type { Task, TaskMessage } from "./types.js";
2
2
  export interface ParsedTaskStreamEvent {
3
3
  message?: TaskMessage;
4
4
  status?: string;
5
+ task?: Task;
5
6
  }
6
7
  export declare function parseTaskStreamEvent(value: unknown): ParsedTaskStreamEvent;
7
8
  export declare function upsertTaskMessage(messages: TaskMessage[], incoming: TaskMessage): TaskMessage[];
@@ -26,6 +26,37 @@ function toTaskMessage(value) {
26
26
  };
27
27
  return message;
28
28
  }
29
+ function toTask(value) {
30
+ if (!isRecord(value))
31
+ return undefined;
32
+ if (typeof value.id !== "string")
33
+ return undefined;
34
+ const status = normalizeStatus(value.status);
35
+ if (!status)
36
+ return undefined;
37
+ if (typeof value.agent_id !== "string")
38
+ return undefined;
39
+ if (typeof value.pod_id !== "string")
40
+ return undefined;
41
+ if (typeof value.user_id !== "string")
42
+ return undefined;
43
+ if (typeof value.created_at !== "string")
44
+ return undefined;
45
+ if (typeof value.updated_at !== "string")
46
+ return undefined;
47
+ return {
48
+ id: value.id,
49
+ agent_id: value.agent_id,
50
+ pod_id: value.pod_id,
51
+ user_id: value.user_id,
52
+ input_data: isRecord(value.input_data) ? value.input_data : null,
53
+ output_data: isRecord(value.output_data) ? value.output_data : null,
54
+ error: typeof value.error === "string" ? value.error : null,
55
+ status: status,
56
+ created_at: value.created_at,
57
+ updated_at: value.updated_at,
58
+ };
59
+ }
29
60
  function extractPayload(record) {
30
61
  if ("data" in record)
31
62
  return record.data;
@@ -59,8 +90,14 @@ export function parseTaskStreamEvent(value) {
59
90
  || eventType === "task_status"
60
91
  || eventType === "task"
61
92
  || eventType === "task_updated") {
93
+ const task = toTask(payload) ?? (isRecord(payload) ? toTask(payload.task) : undefined);
62
94
  const status = extractStatus(payload);
63
- return status ? { status } : {};
95
+ if (task || status) {
96
+ return {
97
+ task,
98
+ status: status ?? task?.status,
99
+ };
100
+ }
64
101
  }
65
102
  return {};
66
103
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type { RecordFilter as GeneratedRecordFilter } from "./openapi_client/models/RecordFilter.js";
2
- import type { RecordSort as GeneratedRecordSort } from "./openapi_client/models/RecordSort.js";
3
1
  import type { AgentResponse, AssistantResponse, AvailableModels, ConversationMessageResponse, ConversationResponse, CreateAgentRequest, CreateAssistantRequest, CreateTaskRequest, FlowRunEntity, FunctionRunResponse, IconUploadResponse, OrganizationInvitationResponse, OrganizationMemberResponse, OrganizationResponse, PodConfigResponse, PodMemberResponse, PodResponse, TaskMessageResponse, TaskResponse, UpdateAgentRequest, UpdateAssistantRequest, UserResponse } from "./openapi_client/index.js";
4
2
  /** Public ergonomic types. */
5
3
  export interface PageResult<T> {
@@ -7,8 +5,16 @@ export interface PageResult<T> {
7
5
  nextPageToken?: string;
8
6
  total?: number;
9
7
  }
10
- export type RecordFilter = GeneratedRecordFilter;
11
- export type RecordSort = GeneratedRecordSort;
8
+ export interface RecordFilter {
9
+ field: string;
10
+ op: string;
11
+ value?: unknown;
12
+ values?: unknown[];
13
+ }
14
+ export interface RecordSort {
15
+ field: string;
16
+ direction?: "asc" | "desc" | string;
17
+ }
12
18
  export interface ListRecordsOptions {
13
19
  filters?: RecordFilter[];
14
20
  sort?: RecordSort[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lemma-sdk",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Official TypeScript SDK for Lemma pod-scoped APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,4 +0,0 @@
1
- export declare enum PodStatus {
2
- ACTIVE = "ACTIVE",
3
- ARCHIVED = "ARCHIVED"
4
- }
@@ -1,9 +0,0 @@
1
- /* generated using openapi-typescript-codegen -- do not edit */
2
- /* istanbul ignore file */
3
- /* tslint:disable */
4
- /* eslint-disable */
5
- export var PodStatus;
6
- (function (PodStatus) {
7
- PodStatus["ACTIVE"] = "ACTIVE";
8
- PodStatus["ARCHIVED"] = "ARCHIVED";
9
- })(PodStatus || (PodStatus = {}));
@@ -1,6 +0,0 @@
1
- export declare enum PodType {
2
- UI = "UI",
3
- ASSISTANT = "ASSISTANT",
4
- HYBRID = "HYBRID",
5
- AUTOMATION = "AUTOMATION"
6
- }
@@ -1,11 +0,0 @@
1
- /* generated using openapi-typescript-codegen -- do not edit */
2
- /* istanbul ignore file */
3
- /* tslint:disable */
4
- /* eslint-disable */
5
- export var PodType;
6
- (function (PodType) {
7
- PodType["UI"] = "UI";
8
- PodType["ASSISTANT"] = "ASSISTANT";
9
- PodType["HYBRID"] = "HYBRID";
10
- PodType["AUTOMATION"] = "AUTOMATION";
11
- })(PodType || (PodType = {}));
@@ -1,15 +0,0 @@
1
- import type { RecordFilterOperator } from './RecordFilterOperator.js';
2
- export type RecordFilter = {
3
- /**
4
- * Table column name to filter on.
5
- */
6
- field: string;
7
- /**
8
- * Comparison operator to apply.
9
- */
10
- op?: RecordFilterOperator;
11
- /**
12
- * Filter comparison value.
13
- */
14
- value: any;
15
- };
@@ -1,10 +0,0 @@
1
- export declare enum RecordFilterOperator {
2
- EQ = "eq",
3
- NE = "ne",
4
- GT = "gt",
5
- GTE = "gte",
6
- LT = "lt",
7
- LTE = "lte",
8
- LIKE = "like",
9
- ILIKE = "ilike"
10
- }
@@ -1,15 +0,0 @@
1
- /* generated using openapi-typescript-codegen -- do not edit */
2
- /* istanbul ignore file */
3
- /* tslint:disable */
4
- /* eslint-disable */
5
- export var RecordFilterOperator;
6
- (function (RecordFilterOperator) {
7
- RecordFilterOperator["EQ"] = "eq";
8
- RecordFilterOperator["NE"] = "ne";
9
- RecordFilterOperator["GT"] = "gt";
10
- RecordFilterOperator["GTE"] = "gte";
11
- RecordFilterOperator["LT"] = "lt";
12
- RecordFilterOperator["LTE"] = "lte";
13
- RecordFilterOperator["LIKE"] = "like";
14
- RecordFilterOperator["ILIKE"] = "ilike";
15
- })(RecordFilterOperator || (RecordFilterOperator = {}));