@lota-sdk/ui 0.1.9 → 0.1.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lota-sdk/ui",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -12,21 +12,6 @@
12
12
  "bun": "./src/index.ts",
13
13
  "import": "./src/index.ts",
14
14
  "types": "./src/index.ts"
15
- },
16
- "./chat/*": {
17
- "bun": "./src/chat/*.ts",
18
- "import": "./src/chat/*.ts",
19
- "types": "./src/chat/*.ts"
20
- },
21
- "./runtime/*": {
22
- "bun": "./src/runtime/*.ts",
23
- "import": "./src/runtime/*.ts",
24
- "types": "./src/runtime/*.ts"
25
- },
26
- "./tools/*": {
27
- "bun": "./src/tools/*.ts",
28
- "import": "./src/tools/*.ts",
29
- "types": "./src/tools/*.ts"
30
15
  }
31
16
  },
32
17
  "scripts": {
@@ -40,7 +25,7 @@
40
25
  "registry": "https://registry.npmjs.org/"
41
26
  },
42
27
  "dependencies": {
43
- "@lota-sdk/shared": "0.1.7",
28
+ "@lota-sdk/shared": "0.1.11",
44
29
  "ai": "^6.0.116"
45
30
  },
46
31
  "peerDependencies": {
@@ -3,7 +3,7 @@ import {
3
3
  ATTACHMENT_MAX_TOTAL_SIZE_BYTES,
4
4
  inferContentType,
5
5
  isSupportedAttachment,
6
- } from '@lota-sdk/shared/constants/attachments'
6
+ } from '@lota-sdk/shared'
7
7
  import type { FileUIPart } from 'ai'
8
8
 
9
9
  type UploadedAttachmentPayload = {
@@ -1,4 +1,4 @@
1
- import { getMessageCreatedAt, withMessageCreatedAt } from '@lota-sdk/shared/runtime/chat-message-metadata'
1
+ import { getMessageCreatedAt, withMessageCreatedAt } from '@lota-sdk/shared'
2
2
 
3
3
  interface TimestampedMessage {
4
4
  id: string
@@ -1,4 +1,4 @@
1
- import { getMessageCreatedAt } from '@lota-sdk/shared/runtime/chat-message-metadata'
1
+ import { getMessageCreatedAt } from '@lota-sdk/shared'
2
2
  import type { FileUIPart, UIMessage } from 'ai'
3
3
  import { useCallback, useEffect, useMemo, useRef } from 'react'
4
4
 
@@ -64,6 +64,8 @@ export function useThreadChat<UI_MESSAGE extends UIMessage>({
64
64
  setThreadRunningStateRef.current = setThreadRunningState
65
65
  }, [setThreadRunningState])
66
66
 
67
+ const chatId = threadId ? `${idPrefix}:${threadId}` : `${idPrefix}:none`
68
+
67
69
  const {
68
70
  messages,
69
71
  status,
@@ -74,13 +76,13 @@ export function useThreadChat<UI_MESSAGE extends UIMessage>({
74
76
  stop: stopChatStream,
75
77
  setMessages,
76
78
  addToolApprovalResponse,
79
+ resumeStream,
77
80
  } = useChat<UI_MESSAGE>({
78
- id: threadId ? `${idPrefix}:${threadId}` : `${idPrefix}:none`,
81
+ id: chatId,
79
82
  transport,
80
83
  messages: initialMessages,
81
84
  messageMetadataSchema,
82
85
  dataPartSchemas,
83
- resume,
84
86
  experimental_throttle: experimentalThrottle,
85
87
  sendAutomaticallyWhen,
86
88
  onFinish: () => {
@@ -96,6 +98,16 @@ export function useThreadChat<UI_MESSAGE extends UIMessage>({
96
98
  },
97
99
  })
98
100
 
101
+ // Manual resume with ref guard — prevents React StrictMode double-fire.
102
+ // We do NOT pass `resume` to useChat because the AI SDK's built-in effect
103
+ // has no StrictMode guard, causing two concurrent GET streams.
104
+ const resumedForChatRef = useRef<string | null>(null)
105
+ useEffect(() => {
106
+ if (!resume || resumedForChatRef.current === chatId) return
107
+ resumedForChatRef.current = chatId
108
+ void resumeStream()
109
+ }, [resume, chatId, resumeStream])
110
+
99
111
  const stop = useCallback(async () => {
100
112
  const stopRequest = threadId && stopRemoteRun ? stopRemoteRun(threadId) : Promise.resolve(null)
101
113
  const [stopResponse] = await Promise.allSettled([stopRequest, stopChatStream()])
@@ -1,5 +1,4 @@
1
- import type { SerializableExecutionPlanTask } from '@lota-sdk/shared/schemas/execution-plan'
2
- import type { ExecutionPlanToolResultData } from '@lota-sdk/shared/schemas/tools'
1
+ import type { ExecutionPlanToolResultData, SerializablePlanNode } from '@lota-sdk/shared'
3
2
 
4
3
  export function getLatestExecutionPlanResult(output: unknown): ExecutionPlanToolResultData | null {
5
4
  if (output && typeof output === 'object' && 'hasPlan' in output) {
@@ -17,42 +16,43 @@ export function getLatestExecutionPlanResult(output: unknown): ExecutionPlanTool
17
16
  }
18
17
 
19
18
  export function getExecutionPlanActionLabel(action: string, isRunning: boolean): string {
20
- if (isRunning) return 'Updating execution plan'
19
+ if (isRunning) return 'Updating execution run'
21
20
 
22
21
  switch (action) {
23
22
  case 'created':
24
23
  return 'Execution plan created'
25
24
  case 'replaced':
26
25
  return 'Execution plan replaced'
27
- case 'task-status-updated':
28
- return 'Execution plan updated'
29
- case 'task-restarted':
30
- return 'Execution task restarted'
26
+ case 'node-result-submitted':
27
+ return 'Execution node updated'
28
+ case 'run-resumed':
29
+ return 'Execution run resumed'
31
30
  case 'loaded':
32
- return 'Execution plan loaded'
31
+ return 'Execution run loaded'
33
32
  default:
34
- return 'Execution plan'
33
+ return 'Execution run'
35
34
  }
36
35
  }
37
36
 
38
37
  export function getExecutionPlanStatusLabel(status: string | null | undefined): string {
39
- if (status === 'draft') return 'Draft'
40
- if (status === 'executing') return 'Executing'
38
+ if (status === 'running') return 'Running'
39
+ if (status === 'awaiting-human') return 'Awaiting Human'
41
40
  if (status === 'blocked') return 'Blocked'
42
41
  if (status === 'completed') return 'Completed'
42
+ if (status === 'failed') return 'Failed'
43
43
  if (status === 'aborted') return 'Aborted'
44
44
  return 'Idle'
45
45
  }
46
46
 
47
47
  export function getExecutionPlanOwnerLabel(
48
- task: SerializableExecutionPlanTask,
48
+ node: SerializablePlanNode,
49
49
  displayNameByOwnerRef: Record<string, string> = {},
50
50
  ): string {
51
- if (task.ownerType === 'agent' && task.ownerRef in displayNameByOwnerRef) {
52
- return displayNameByOwnerRef[task.ownerRef]
51
+ if (node.owner.executorType === 'agent' && node.owner.ref in displayNameByOwnerRef) {
52
+ return displayNameByOwnerRef[node.owner.ref]
53
53
  }
54
- if (task.ownerType === 'user') return 'User'
55
- return task.ownerRef
54
+ if (node.owner.executorType === 'user') return 'User'
55
+ return node.owner.ref
56
56
  }
57
57
 
58
58
  export function buildExecutionPlanToolViewModel(params: { input: unknown; isRunning: boolean; output: unknown }) {
@@ -65,14 +65,15 @@ export function buildExecutionPlanToolViewModel(params: { input: unknown; isRunn
65
65
  typeof params.input.title === 'string'
66
66
  ? params.input.title.trim()
67
67
  : ''
68
- const title = plan && plan.title.trim() ? plan.title.trim() : inputTitle || 'Execution plan'
68
+ const title = plan && plan.title.trim() ? plan.title.trim() : inputTitle || 'Execution run'
69
+ const completedCount = plan ? plan.progress.completed + plan.progress.partial : 0
69
70
 
70
71
  return {
71
72
  actionLabel: getExecutionPlanActionLabel(result?.action ?? 'none', params.isRunning),
72
73
  hasPlan: plan !== null,
73
74
  latestEvent: plan ? plan.recentEvents.at(-1)?.message.trim() : undefined,
74
75
  plan,
75
- progressSummary: plan ? `${plan.progress.completed}/${plan.progress.total} complete` : 'No active plan',
76
+ progressSummary: plan ? `${completedCount}/${plan.progress.total} complete` : 'No active execution run',
76
77
  result,
77
78
  statusLabel: getExecutionPlanStatusLabel(result?.status ?? plan?.status),
78
79
  title,
@@ -1,4 +1,4 @@
1
- import type { UserQuestionItem, UserQuestionsArgs } from '@lota-sdk/shared/schemas/tools'
1
+ import type { UserQuestionItem, UserQuestionsArgs } from '@lota-sdk/shared'
2
2
 
3
3
  export type UserQuestionAnswer = { selected: string[]; customText: string }
4
4
  export type UserQuestionsSubmitPayload = {
@@ -27,7 +27,7 @@ export function formatUserQuestionsAnswerText(
27
27
  }
28
28
 
29
29
  if (answer.customText.trim()) {
30
- parts.push(`custom: "${answer.customText.trim()}"`)
30
+ parts.push(answer.customText.trim())
31
31
  }
32
32
  }
33
33