lemma-sdk 0.2.45 → 0.2.46

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 (56) hide show
  1. package/dist/assistant-events.js +12 -2
  2. package/dist/browser/lemma-client.js +56 -84
  3. package/dist/client.js +1 -1
  4. package/dist/hey_client/types.gen.d.ts +2 -14
  5. package/dist/namespaces/conversations.d.ts +2 -2
  6. package/dist/namespaces/integrations.d.ts +2 -4
  7. package/dist/namespaces/integrations.js +2 -2
  8. package/dist/namespaces/records.d.ts +3 -9
  9. package/dist/namespaces/records.js +4 -33
  10. package/dist/openapi_client/index.d.ts +3 -5
  11. package/dist/openapi_client/index.js +2 -0
  12. package/dist/openapi_client/models/AgentToolset.d.ts +2 -1
  13. package/dist/openapi_client/models/AgentToolset.js +1 -0
  14. package/dist/openapi_client/models/AppTriggerResponseSchema.d.ts +2 -0
  15. package/dist/openapi_client/models/ApprovalDecisionResponse.d.ts +6 -0
  16. package/dist/openapi_client/models/AuthProvider.d.ts +4 -0
  17. package/dist/openapi_client/models/AuthProvider.js +9 -0
  18. package/dist/openapi_client/models/FlowRunStatus.d.ts +3 -2
  19. package/dist/openapi_client/models/FlowRunStatus.js +3 -2
  20. package/dist/openapi_client/models/MessageKind.d.ts +14 -0
  21. package/dist/openapi_client/models/MessageKind.js +19 -0
  22. package/dist/openapi_client/models/MessageResponse.d.ts +5 -6
  23. package/dist/openapi_client/models/WorkflowRunResponse.d.ts +2 -2
  24. package/dist/openapi_client/services/AgentConversationsService.d.ts +5 -5
  25. package/dist/openapi_client/services/AgentConversationsService.js +3 -3
  26. package/dist/openapi_client/services/ApplicationsService.d.ts +19 -19
  27. package/dist/openapi_client/services/ApplicationsService.js +48 -44
  28. package/dist/openapi_client/services/RecordsService.d.ts +1 -3
  29. package/dist/openapi_client/services/RecordsService.js +1 -5
  30. package/dist/react/assistant-output.d.ts +6 -0
  31. package/dist/react/assistant-output.js +16 -0
  32. package/dist/react/useAssistantController.d.ts +8 -1
  33. package/dist/react/useAssistantController.js +87 -250
  34. package/dist/react/useAssistantRuntime.js +6 -17
  35. package/dist/react/useAssistantSession.d.ts +4 -2
  36. package/dist/react/useAssistantSession.js +6 -7
  37. package/dist/react/useConversationMessages.d.ts +2 -2
  38. package/dist/react/useConversationMessages.js +3 -5
  39. package/dist/react/useRecords.d.ts +1 -1
  40. package/dist/react/useRecords.js +2 -13
  41. package/dist/react/useReferencingRecords.d.ts +4 -6
  42. package/dist/react/useReferencingRecords.js +5 -5
  43. package/dist/react/useReverseRelatedRecords.d.ts +3 -4
  44. package/dist/react/useReverseRelatedRecords.js +5 -5
  45. package/dist/types.d.ts +12 -6
  46. package/package.json +1 -1
  47. package/dist/openapi_client/models/NotificationContent.d.ts +0 -4
  48. package/dist/openapi_client/models/TextContent.d.ts +0 -4
  49. package/dist/openapi_client/models/TextContent.js +0 -1
  50. package/dist/openapi_client/models/ThinkingContent.d.ts +0 -4
  51. package/dist/openapi_client/models/ThinkingContent.js +0 -1
  52. package/dist/openapi_client/models/ToolCallContent.d.ts +0 -6
  53. package/dist/openapi_client/models/ToolCallContent.js +0 -1
  54. package/dist/openapi_client/models/ToolReturnContent.d.ts +0 -6
  55. package/dist/openapi_client/models/ToolReturnContent.js +0 -1
  56. /package/dist/openapi_client/models/{NotificationContent.js → ApprovalDecisionResponse.js} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { useAssistantRuntime } from "./useAssistantRuntime.js";
3
3
  import { useAssistantSession } from "./useAssistantSession.js";
4
+ const CONVERSATIONS_PAGE_SIZE = 30;
4
5
  const EMPTY_SCOPE_KEY = JSON.stringify({
5
6
  podId: null,
6
7
  assistantName: null,
@@ -10,43 +11,6 @@ const EMPTY_SCOPE_KEY = JSON.stringify({
10
11
  function isRecord(value) {
11
12
  return !!value && typeof value === "object" && !Array.isArray(value);
12
13
  }
13
- function stringifyContent(value) {
14
- if (typeof value === "string")
15
- return value;
16
- if (Array.isArray(value)) {
17
- const text = value
18
- .map((entry) => extractTextFromStructuredContentEntry(entry))
19
- .filter((entry) => entry.length > 0)
20
- .join("\n\n")
21
- .trim();
22
- if (text.length > 0)
23
- return text;
24
- return "";
25
- }
26
- if (!isRecord(value))
27
- return "";
28
- const direct = value.content;
29
- if (typeof direct === "string")
30
- return direct;
31
- if (Array.isArray(direct)) {
32
- const text = direct
33
- .map((entry) => extractTextFromStructuredContentEntry(entry))
34
- .filter((entry) => entry.length > 0)
35
- .join("\n\n")
36
- .trim();
37
- if (text.length > 0)
38
- return text;
39
- }
40
- const text = value.text;
41
- if (typeof text === "string")
42
- return text;
43
- try {
44
- return JSON.stringify(value, null, 2);
45
- }
46
- catch {
47
- return "";
48
- }
49
- }
50
14
  function parseMaybeJsonObject(value) {
51
15
  if (isRecord(value))
52
16
  return value;
@@ -71,37 +35,6 @@ function parseMaybeJsonValue(value) {
71
35
  return value;
72
36
  }
73
37
  }
74
- function extractTextFromStructuredContentEntry(entry) {
75
- if (typeof entry === "string")
76
- return entry.trim();
77
- if (!isRecord(entry))
78
- return "";
79
- if (typeof entry.text === "string")
80
- return entry.text.trim();
81
- if (typeof entry.content === "string")
82
- return entry.content.trim();
83
- if (typeof entry.value === "string")
84
- return entry.value.trim();
85
- if (Array.isArray(entry.content)) {
86
- const nested = entry.content
87
- .map((child) => extractTextFromStructuredContentEntry(child))
88
- .filter((text) => text.length > 0)
89
- .join("\n")
90
- .trim();
91
- if (nested.length > 0)
92
- return nested;
93
- }
94
- if (Array.isArray(entry.summary)) {
95
- const summary = entry.summary
96
- .map((child) => extractTextFromStructuredContentEntry(child))
97
- .filter((text) => text.length > 0)
98
- .join("\n")
99
- .trim();
100
- if (summary.length > 0)
101
- return summary;
102
- }
103
- return "";
104
- }
105
38
  function parseTimestampMs(value) {
106
39
  if (typeof value === "number" && Number.isFinite(value)) {
107
40
  return value;
@@ -137,27 +70,19 @@ function parseThinkingDurationFromRecord(record) {
137
70
  ?? parseDurationMs(record.thought_duration_ms)
138
71
  ?? parseDurationMs(record.thoughtDurationMs);
139
72
  }
140
- function extractThinkingPartFromContent(content) {
141
- if (!isRecord(content))
142
- return null;
143
- const rawType = typeof content.type === "string" ? content.type.toLowerCase() : "";
144
- if (rawType !== "thinking" && rawType !== "reasoning") {
73
+ function extractThinkingPart(msg) {
74
+ if (msg.kind !== "thinking")
145
75
  return null;
146
- }
147
- const text = extractTextFromStructuredContentEntry(content.content ?? content.text ?? content.value ?? content.summary);
76
+ const text = typeof msg.text === "string" ? msg.text.trim() : "";
148
77
  if (!text)
149
78
  return null;
150
- const stateValue = [content.state, content.status, content.phase]
151
- .find((value) => typeof value === "string");
152
- const normalizedState = stateValue?.toLowerCase() || "";
153
- const isStreaming = normalizedState.includes("stream")
154
- || normalizedState.includes("progress")
155
- || normalizedState.includes("running")
156
- || normalizedState.includes("thinking");
79
+ const metadata = getMessageMetadata(msg);
157
80
  return {
158
81
  text,
159
- state: isStreaming ? "streaming" : "done",
160
- durationMs: parseThinkingDurationFromRecord(content),
82
+ state: "done",
83
+ durationMs: metadata
84
+ ? parseThinkingDurationFromRecord(metadata)
85
+ : undefined,
161
86
  };
162
87
  }
163
88
  function normalizeToolResult(value) {
@@ -172,143 +97,33 @@ function normalizeToolResult(value) {
172
97
  function getMessageMetadata(msg) {
173
98
  return (msg.message_metadata || msg.metadata || undefined);
174
99
  }
175
- function getNativeToolPayload(content) {
176
- if (!isRecord(content))
177
- return null;
178
- const toolCallId = typeof content.tool_call_id === "string" ? content.tool_call_id : null;
179
- if (!toolCallId)
180
- return null;
181
- const toolName = typeof content.tool_name === "string" ? content.tool_name : undefined;
182
- if ("tool_output" in content) {
100
+ function getNativeToolPayload(msg) {
101
+ const toolName = typeof msg.tool_name === "string" ? msg.tool_name : undefined;
102
+ if (msg.kind === "tool_call") {
183
103
  return {
184
- kind: "result",
185
- toolCallId,
104
+ kind: "call",
105
+ toolCallId: (typeof msg.tool_call_id === "string" && msg.tool_call_id) || `${msg.id}-tool-call`,
186
106
  toolName,
187
- result: normalizeToolResult(content.tool_output),
107
+ args: parseMaybeJsonObject(parseMaybeJsonValue(msg.tool_args)),
188
108
  };
189
109
  }
190
- if (typeof content.tool_name === "string") {
110
+ if (msg.kind === "tool_return") {
191
111
  return {
192
- kind: "call",
193
- toolCallId,
112
+ kind: "result",
113
+ toolCallId: (typeof msg.tool_call_id === "string" && msg.tool_call_id) || `${msg.id}-tool-result`,
194
114
  toolName,
195
- args: parseMaybeJsonObject(parseMaybeJsonValue(content.tool_input)),
115
+ result: normalizeToolResult(msg.tool_result),
196
116
  };
197
117
  }
198
118
  return null;
199
119
  }
200
- function hasNativeToolPayloadContent(content) {
201
- return getNativeToolPayload(content) !== null;
202
- }
203
120
  function toolInvocationKey(tool) {
204
121
  return `${tool.toolCallId}:${tool.state}`;
205
122
  }
206
- function toolInvocationFromStructuredContentEntry(entry, fallbackId) {
207
- const type = typeof entry.type === "string" ? entry.type.toLowerCase() : "";
208
- const functionObj = isRecord(entry.function) ? entry.function : {};
209
- const hasToolShape = type.includes("tool")
210
- || type.includes("function_call")
211
- || type.includes("function_result")
212
- || typeof entry.tool_call_id === "string"
213
- || typeof entry.call_id === "string"
214
- || typeof entry.tool_name === "string"
215
- || typeof functionObj.name === "string"
216
- || "tool_output" in entry
217
- || ("result" in entry && typeof entry.call_id === "string");
218
- if (!hasToolShape)
219
- return null;
220
- const rawResult = entry.tool_output ?? entry.output ?? entry.result;
221
- const isResultLike = type.includes("result")
222
- || type.includes("output")
223
- || type.includes("return")
224
- || typeof rawResult !== "undefined";
225
- const toolCallId = ((typeof entry.tool_call_id === "string" && entry.tool_call_id)
226
- || (typeof entry.toolCallId === "string" && entry.toolCallId)
227
- || (typeof entry.call_id === "string" && entry.call_id)
228
- || (typeof entry.id === "string" && entry.id)
229
- || fallbackId);
230
- const toolName = ((typeof entry.tool_name === "string" && entry.tool_name)
231
- || (typeof entry.toolName === "string" && entry.toolName)
232
- || (typeof functionObj.name === "string" && functionObj.name)
233
- || (typeof entry.name === "string" && entry.name)
234
- || "tool");
235
- const argsRaw = functionObj.arguments
236
- ?? entry.tool_input
237
- ?? entry.input
238
- ?? entry.args
239
- ?? entry.arguments;
240
- const state = isResultLike ? "result" : "call";
241
- return {
242
- toolCallId,
243
- toolName,
244
- args: parseMaybeJsonObject(parseMaybeJsonValue(argsRaw)),
245
- state,
246
- ...(isResultLike ? { result: normalizeToolResult(rawResult) } : {}),
247
- };
248
- }
249
- function parseStructuredAssistantParts(content) {
250
- if (!Array.isArray(content))
251
- return null;
252
- const parts = [];
253
- const textChunks = [];
254
- const representedToolKeys = new Set();
255
- content.forEach((rawPart, index) => {
256
- if (!isRecord(rawPart))
257
- return;
258
- const partType = typeof rawPart.type === "string" ? rawPart.type.toLowerCase() : "";
259
- const partId = (typeof rawPart.id === "string" && rawPart.id) || `content-part-${index}`;
260
- const toolInvocation = toolInvocationFromStructuredContentEntry(rawPart, `${partId}-tool`);
261
- if (toolInvocation) {
262
- representedToolKeys.add(toolInvocationKey(toolInvocation));
263
- parts.push({
264
- id: `${partId}-tool`,
265
- type: "tool",
266
- toolInvocation,
267
- });
268
- return;
269
- }
270
- const text = extractTextFromStructuredContentEntry(rawPart);
271
- if (!text)
272
- return;
273
- if (partType.includes("reasoning") || partType.includes("thinking")) {
274
- const stateValue = [rawPart.state, rawPart.status, rawPart.phase]
275
- .find((value) => typeof value === "string");
276
- const normalizedState = stateValue?.toLowerCase() || partType;
277
- const isStreaming = normalizedState.includes("stream")
278
- || normalizedState.includes("progress")
279
- || normalizedState.includes("running")
280
- || normalizedState.includes("thinking");
281
- parts.push({
282
- id: `${partId}-reasoning`,
283
- type: "reasoning",
284
- text,
285
- state: isStreaming ? "streaming" : "done",
286
- durationMs: parseThinkingDurationFromRecord(rawPart),
287
- startedAtMs: parseTimestampMs(rawPart.started_at)
288
- ?? parseTimestampMs(rawPart.startedAt)
289
- ?? parseTimestampMs(rawPart.created_at)
290
- ?? parseTimestampMs(rawPart.createdAt)
291
- ?? undefined,
292
- });
293
- return;
294
- }
295
- textChunks.push(text);
296
- parts.push({
297
- id: `${partId}-text`,
298
- type: "text",
299
- text,
300
- });
301
- });
302
- return {
303
- parts,
304
- textContent: textChunks.join("\n\n").trim(),
305
- representedToolKeys,
306
- };
307
- }
308
123
  function mapToolInvocations(msg) {
309
124
  const invocations = [];
310
125
  const metadata = getMessageMetadata(msg);
311
- const nativeToolPayload = getNativeToolPayload(msg.content);
126
+ const nativeToolPayload = getNativeToolPayload(msg);
312
127
  if (metadata?.message_type === "tool_call") {
313
128
  invocations.push({
314
129
  toolCallId: metadata.tool_call_id || `${msg.id}-tool-call`,
@@ -362,20 +177,6 @@ function mapToolInvocations(msg) {
362
177
  result: nativeToolPayload.result || {},
363
178
  });
364
179
  }
365
- const contentObj = isRecord(msg.content) ? msg.content : null;
366
- if (contentObj && nativeToolPayload === null && "tool_output" in contentObj) {
367
- invocations.push({
368
- toolCallId: (typeof contentObj.tool_call_id === "string" && contentObj.tool_call_id)
369
- || metadata?.tool_call_id
370
- || `${msg.id}-tool-output`,
371
- toolName: (typeof contentObj.tool_name === "string" && contentObj.tool_name)
372
- || metadata?.tool_name
373
- || "tool",
374
- args: metadata?.args || {},
375
- state: "result",
376
- result: normalizeToolResult(contentObj.tool_output),
377
- });
378
- }
379
180
  const seen = new Set();
380
181
  return invocations.filter((invocation) => {
381
182
  const key = toolInvocationKey(invocation);
@@ -387,43 +188,34 @@ function mapToolInvocations(msg) {
387
188
  }
388
189
  function mapConversationMessage(msg, options) {
389
190
  const toolInvocations = mapToolInvocations(msg);
390
- const structured = parseStructuredAssistantParts(msg.content);
391
- const explicitThinkingPart = extractThinkingPartFromContent(msg.content);
392
191
  const createdAtMs = parseTimestampMs(msg.created_at) ?? undefined;
393
- const parts = structured
394
- ? structured.parts.map((part) => (part.type === "reasoning"
395
- ? {
396
- ...part,
397
- startedAtMs: part.startedAtMs ?? createdAtMs,
398
- }
399
- : part))
400
- : [];
401
- const representedToolKeys = structured?.representedToolKeys || new Set();
402
- let content = structured
403
- ? structured.textContent
404
- : (hasNativeToolPayloadContent(msg.content) ? "" : stringifyContent(msg.content));
405
- if (explicitThinkingPart) {
406
- content = "";
192
+ const parts = [];
193
+ let content = "";
194
+ // Flat shape: a message is exactly one kind. Thinking renders as a reasoning
195
+ // part; text/notification render as a text part; tool_call/tool_return render
196
+ // via toolInvocations below.
197
+ const thinkingPart = extractThinkingPart(msg);
198
+ if (thinkingPart) {
407
199
  parts.push({
408
200
  id: `${msg.id}-reasoning`,
409
201
  type: "reasoning",
410
- text: explicitThinkingPart.text,
411
- state: explicitThinkingPart.state,
412
- durationMs: explicitThinkingPart.durationMs ?? options?.thinkingDurationMs,
202
+ text: thinkingPart.text,
203
+ state: thinkingPart.state,
204
+ durationMs: thinkingPart.durationMs ?? options?.thinkingDurationMs,
413
205
  startedAtMs: createdAtMs,
414
206
  });
415
207
  }
416
- else if (!structured && content.trim()) {
417
- parts.push({
418
- id: `${msg.id}-text`,
419
- type: "text",
420
- text: content,
421
- });
208
+ else if (msg.kind === "text" || msg.kind === "notification") {
209
+ content = typeof msg.text === "string" ? msg.text.trim() : "";
210
+ if (content) {
211
+ parts.push({
212
+ id: `${msg.id}-text`,
213
+ type: "text",
214
+ text: content,
215
+ });
216
+ }
422
217
  }
423
218
  toolInvocations.forEach((toolInvocation, index) => {
424
- const key = toolInvocationKey(toolInvocation);
425
- if (representedToolKeys.has(key))
426
- return;
427
219
  parts.push({
428
220
  id: `${msg.id}-tool-${index}`,
429
221
  type: "tool",
@@ -442,8 +234,11 @@ function mapConversationMessage(msg, options) {
442
234
  agent_run_id: msg.agent_run_id,
443
235
  metadata: msg.metadata ?? null,
444
236
  message_metadata: msg.message_metadata ?? null,
237
+ kind: msg.kind,
445
238
  tool_call_id: msg.tool_call_id ?? null,
446
239
  tool_name: msg.tool_name ?? null,
240
+ tool_args: msg.tool_args ?? null,
241
+ tool_result: msg.tool_result ?? null,
447
242
  };
448
243
  }
449
244
  function mapConversationMessages(messages) {
@@ -451,7 +246,7 @@ function mapConversationMessages(messages) {
451
246
  const pendingToolCalls = new Map();
452
247
  const estimateThinkingDurationMs = (index) => {
453
248
  const message = messages[index];
454
- if (!message || !extractThinkingPartFromContent(message.content))
249
+ if (!message || !extractThinkingPart(message))
455
250
  return undefined;
456
251
  const startedAtMs = parseTimestampMs(message.created_at);
457
252
  if (!startedAtMs)
@@ -477,7 +272,7 @@ function mapConversationMessages(messages) {
477
272
  pendingToolCalls.set(invocation.toolCallId, invocation);
478
273
  }
479
274
  });
480
- const nativePayload = getNativeToolPayload(rawMessage.content);
275
+ const nativePayload = getNativeToolPayload(rawMessage);
481
276
  const isToolRole = rawMessage.role === "tool";
482
277
  if (isToolRole && nativePayload?.kind === "result" && mappedMessage.toolInvocations && mappedMessage.toolInvocations.length > 0) {
483
278
  let mergedIntoPriorCall = false;
@@ -625,6 +420,8 @@ export function useAssistantController({ client, podId, agentName, assistantName
625
420
  const [conversationRuntime, setConversationRuntimeState] = useState(null);
626
421
  const [isStreaming, setIsStreaming] = useState(false);
627
422
  const [isLoadingConversations, setIsLoadingConversations] = useState(false);
423
+ const [isLoadingMoreConversations, setIsLoadingMoreConversations] = useState(false);
424
+ const [conversationsCursor, setConversationsCursor] = useState(null);
628
425
  const [isLoadingMessages, setIsLoadingMessages] = useState(false);
629
426
  const [isLoadingOlderMessages, setIsLoadingOlderMessages] = useState(false);
630
427
  const [isUploadingFiles, setIsUploadingFiles] = useState(false);
@@ -739,9 +536,10 @@ export function useAssistantController({ client, podId, agentName, assistantName
739
536
  const loadConversations = useCallback(async () => {
740
537
  setIsLoadingConversations(true);
741
538
  try {
742
- const response = await sessionListConversations({ scope });
539
+ const response = await sessionListConversations({ scope, limit: CONVERSATIONS_PAGE_SIZE });
743
540
  const nextConversations = sortConversationsByUpdatedAt(response.items || []);
744
541
  setConversations(nextConversations);
542
+ setConversationsCursor(response.next_page_token ?? null);
745
543
  setActiveConversationId((current) => {
746
544
  if (current && nextConversations.some((conversation) => conversation.id === current)) {
747
545
  return current;
@@ -759,6 +557,36 @@ export function useAssistantController({ client, podId, agentName, assistantName
759
557
  setIsLoadingConversations(false);
760
558
  }
761
559
  }, [scope, sessionListConversations]);
560
+ const loadMoreConversations = useCallback(async () => {
561
+ if (!conversationsCursor || isLoadingConversations || isLoadingMoreConversations) {
562
+ return [];
563
+ }
564
+ setIsLoadingMoreConversations(true);
565
+ try {
566
+ const response = await sessionListConversations({
567
+ scope,
568
+ limit: CONVERSATIONS_PAGE_SIZE,
569
+ pageToken: conversationsCursor,
570
+ });
571
+ const moreConversations = response.items || [];
572
+ setConversations((prev) => {
573
+ const byId = new Map(prev.map((conversation) => [conversation.id, conversation]));
574
+ for (const conversation of moreConversations) {
575
+ byId.set(conversation.id, conversation);
576
+ }
577
+ return sortConversationsByUpdatedAt(Array.from(byId.values()));
578
+ });
579
+ setConversationsCursor(response.next_page_token ?? null);
580
+ return moreConversations;
581
+ }
582
+ catch (err) {
583
+ setLocalError((prev) => prev || (err instanceof Error ? err.message : "Failed to load more conversations"));
584
+ return [];
585
+ }
586
+ finally {
587
+ setIsLoadingMoreConversations(false);
588
+ }
589
+ }, [conversationsCursor, isLoadingConversations, isLoadingMoreConversations, scope, sessionListConversations]);
762
590
  const loadAvailableModels = useCallback(async () => {
763
591
  try {
764
592
  const response = await client.conversations.listModels({
@@ -909,9 +737,11 @@ export function useAssistantController({ client, podId, agentName, assistantName
909
737
  setConversationModelState(null);
910
738
  setConversationRuntimeState(null);
911
739
  setConversations([]);
740
+ setConversationsCursor(null);
912
741
  setLocalError(null);
913
742
  setOlderMessagesCursor(null);
914
743
  setIsLoadingConversations(false);
744
+ setIsLoadingMoreConversations(false);
915
745
  setIsLoadingMessages(false);
916
746
  setIsLoadingOlderMessages(false);
917
747
  return;
@@ -925,6 +755,7 @@ export function useAssistantController({ client, podId, agentName, assistantName
925
755
  setConversationModelState(null);
926
756
  setConversationRuntimeState(null);
927
757
  setConversations([]);
758
+ setConversationsCursor(null);
928
759
  setLocalError(null);
929
760
  clearRuntimeMessages();
930
761
  setOlderMessagesCursor(null);
@@ -1286,6 +1117,8 @@ export function useAssistantController({ client, podId, agentName, assistantName
1286
1117
  isActiveConversationRunning,
1287
1118
  isLoading,
1288
1119
  isLoadingConversations,
1120
+ isLoadingMoreConversations,
1121
+ hasMoreConversations: !!conversationsCursor,
1289
1122
  isLoadingMessages,
1290
1123
  isLoadingOlderMessages,
1291
1124
  hasOlderMessages: !!olderMessagesCursor,
@@ -1303,6 +1136,7 @@ export function useAssistantController({ client, podId, agentName, assistantName
1303
1136
  removePendingFile,
1304
1137
  clearPendingFiles,
1305
1138
  loadOlderMessages,
1139
+ loadMoreConversations,
1306
1140
  resolveUserApproval,
1307
1141
  clearMessages,
1308
1142
  stop,
@@ -1316,12 +1150,15 @@ export function useAssistantController({ client, podId, agentName, assistantName
1316
1150
  conversationModel,
1317
1151
  conversations,
1318
1152
  error,
1153
+ conversationsCursor,
1319
1154
  isActiveConversationRunning,
1320
1155
  isLoading,
1321
1156
  isLoadingConversations,
1157
+ isLoadingMoreConversations,
1322
1158
  isLoadingMessages,
1323
1159
  isLoadingOlderMessages,
1324
1160
  isUploadingFiles,
1161
+ loadMoreConversations,
1325
1162
  loadOlderMessages,
1326
1163
  messages,
1327
1164
  olderMessagesCursor,
@@ -1,18 +1,6 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- function isRecord(value) {
3
- return !!value && typeof value === "object" && !Array.isArray(value);
4
- }
5
- function messageText(content) {
6
- if (typeof content === "string") {
7
- return content.trim();
8
- }
9
- if (isRecord(content)) {
10
- if (typeof content.content === "string")
11
- return content.content.trim();
12
- if (typeof content.text === "string")
13
- return content.text.trim();
14
- }
15
- return "";
2
+ function messageText(message) {
3
+ return typeof message.text === "string" ? message.text.trim() : "";
16
4
  }
17
5
  function messageTime(message) {
18
6
  const timestamp = new Date(message.created_at).getTime();
@@ -30,7 +18,7 @@ function upsertRuntimeMessage(previous, incoming) {
30
18
  return next;
31
19
  }
32
20
  if (incoming.role === "user") {
33
- const incomingText = messageText(incoming.content);
21
+ const incomingText = messageText(incoming);
34
22
  if (incomingText) {
35
23
  const incomingTimestamp = messageTime(incoming);
36
24
  let optimisticIndex = -1;
@@ -38,7 +26,7 @@ function upsertRuntimeMessage(previous, incoming) {
38
26
  next.forEach((message, index) => {
39
27
  if (message.role !== "user"
40
28
  || !isOptimisticId(message.id)
41
- || messageText(message.content) !== incomingText) {
29
+ || messageText(message) !== incomingText) {
42
30
  return;
43
31
  }
44
32
  const distance = Math.abs(messageTime(message) - incomingTimestamp);
@@ -100,7 +88,8 @@ export function useAssistantRuntime({ conversationId = null, sessionConversation
100
88
  const optimistic = {
101
89
  id: buildOptimisticId(),
102
90
  role: "user",
103
- content: { content: trimmed },
91
+ kind: "text",
92
+ text: trimmed,
104
93
  created_at: new Date().toISOString(),
105
94
  metadata: null,
106
95
  ...(optimisticConversationId ? { conversation_id: optimisticConversationId } : {}),
@@ -45,6 +45,8 @@ export interface CreateConversationInput {
45
45
  agentRuntime?: AgentRuntimeConfig | null;
46
46
  podId?: string | null;
47
47
  agentName?: string | null;
48
+ /** Parent conversation id for sub-agent (child) conversations. */
49
+ parentId?: string | null;
48
50
  /**
49
51
  * @deprecated Use agentName instead.
50
52
  */
@@ -82,9 +84,9 @@ export interface UseAssistantSessionResult {
82
84
  status?: string;
83
85
  messages: ConversationMessage[];
84
86
  latestAssistantMessage: ConversationMessage | null;
85
- output: ConversationMessage["content"] | null;
87
+ output: ConversationMessage | null;
86
88
  outputText: string;
87
- finalOutput: ConversationMessage["content"] | null;
89
+ finalOutput: ConversationMessage | null;
88
90
  finalOutputText: string;
89
91
  streamingText: string;
90
92
  streamingTool: AssistantStreamingTool | null;
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { parseSSEJson, readSSE } from "../streams.js";
3
3
  import { parseAssistantStreamEvent, upsertConversationMessage } from "../assistant-events.js";
4
- import { extractConversationMessageText, getLatestAssistantMessage, } from "./assistant-output.js";
4
+ import { conversationMessageText, getLatestAssistantMessage, } from "./assistant-output.js";
5
5
  import { normalizeError } from "./utils.js";
6
6
  function applyPodScope(client, podId) {
7
7
  const resolvedPodId = podId ?? client.podId;
@@ -84,8 +84,8 @@ function parseStreamingToolToken(token) {
84
84
  return null;
85
85
  const rawToolCallId = [parsed.tool_call_id, parsed.toolCallId, parsed.call_id, parsed.id]
86
86
  .find((value) => typeof value === "string" && value.trim().length > 0);
87
- const rawArgs = parsed.tool_input ?? parsed.args ?? parsed.arguments ?? parsed.input;
88
- const rawResult = parsed.tool_output ?? parsed.result ?? parsed.output;
87
+ const rawArgs = parsed.tool_args ?? parsed.tool_input ?? parsed.args ?? parsed.arguments ?? parsed.input;
88
+ const rawResult = parsed.tool_result ?? parsed.tool_output ?? parsed.result ?? parsed.output;
89
89
  const hasResult = typeof rawResult !== "undefined";
90
90
  return {
91
91
  ...(typeof rawToolCallId === "string" ? { toolCallId: rawToolCallId } : {}),
@@ -292,6 +292,7 @@ export function useAssistantSession(options) {
292
292
  agent_runtime: typeof input.agentRuntime === "undefined"
293
293
  ? undefined
294
294
  : input.agentRuntime,
295
+ parent_id: input.parentId ?? undefined,
295
296
  };
296
297
  const created = await scopedClient.conversations.create(payload);
297
298
  if (input.setActive !== false) {
@@ -699,10 +700,8 @@ export function useAssistantSession(options) {
699
700
  };
700
701
  }, [autoLoad, autoResume, conversationId, loadMessages, refreshConversation, resumeIfRunning]);
701
702
  const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
702
- const output = latestAssistantMessage?.content ?? null;
703
- const latestAssistantText = latestAssistantMessage
704
- ? extractConversationMessageText(latestAssistantMessage.content)
705
- : "";
703
+ const output = latestAssistantMessage ?? null;
704
+ const latestAssistantText = conversationMessageText(latestAssistantMessage);
706
705
  const outputText = streamingText.trim() || latestAssistantText;
707
706
  const finalOutput = !isStreaming && !isConversationRunningStatus(status) ? output : null;
708
707
  const finalOutputText = !isStreaming && !isConversationRunningStatus(status) ? latestAssistantText : "";
@@ -40,9 +40,9 @@ export interface UseConversationMessagesResult {
40
40
  nextPageToken: string | null;
41
41
  streamingText: string;
42
42
  latestAssistantMessage: ConversationMessage | null;
43
- output: ConversationMessage["content"] | null;
43
+ output: ConversationMessage | null;
44
44
  outputText: string;
45
- finalOutput: ConversationMessage["content"] | null;
45
+ finalOutput: ConversationMessage | null;
46
46
  finalOutputText: string;
47
47
  error: Error | null;
48
48
  refresh: (options?: {
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import { extractConversationMessageText, getLatestAssistantMessage, isConversationRunningStatus, normalizeConversationStatus, sortConversationMessagesByCreatedAt, } from "./assistant-output.js";
2
+ import { conversationMessageText, getLatestAssistantMessage, isConversationRunningStatus, normalizeConversationStatus, sortConversationMessagesByCreatedAt, } from "./assistant-output.js";
3
3
  import { useAssistantSession, } from "./useAssistantSession.js";
4
4
  function resolveConversationId(preferred, fallback) {
5
5
  return preferred ?? fallback ?? null;
@@ -117,10 +117,8 @@ export function useConversationMessages({ client, podId, agentName, assistantNam
117
117
  }, [autoLoad, autoResume, cancel, clearSessionMessages, conversationId, enabled, limit, refresh, resumeIfRunning]);
118
118
  const messages = useMemo(() => sortConversationMessagesByCreatedAt(sessionMessages), [sessionMessages]);
119
119
  const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
120
- const output = latestAssistantMessage?.content ?? null;
121
- const latestAssistantText = latestAssistantMessage
122
- ? extractConversationMessageText(latestAssistantMessage.content)
123
- : "";
120
+ const output = latestAssistantMessage ?? null;
121
+ const latestAssistantText = conversationMessageText(latestAssistantMessage);
124
122
  const outputText = streamingText.trim() || latestAssistantText;
125
123
  const finalOutput = isSettledStatus(status, isStreaming) ? output : null;
126
124
  const finalOutputText = isSettledStatus(status, isStreaming) ? latestAssistantText : "";
@@ -17,4 +17,4 @@ export interface UseRecordsResult<TRecord extends Record<string, unknown> = Reco
17
17
  refresh: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
18
18
  loadMore: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
19
19
  }
20
- export declare function useRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, filters, sort, limit, pageToken, offset, sortBy, order, params, enabled, autoLoad, }: UseRecordsOptions): UseRecordsResult<TRecord>;
20
+ export declare function useRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, filters, sort, limit, pageToken, offset, enabled, autoLoad, }: UseRecordsOptions): UseRecordsResult<TRecord>;