@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.
|
|
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.
|
|
28
|
+
"@lota-sdk/shared": "0.1.11",
|
|
44
29
|
"ai": "^6.0.116"
|
|
45
30
|
},
|
|
46
31
|
"peerDependencies": {
|
package/src/chat/attachments.ts
CHANGED
package/src/chat/messages.ts
CHANGED
|
@@ -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:
|
|
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 {
|
|
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
|
|
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 '
|
|
28
|
-
return 'Execution
|
|
29
|
-
case '
|
|
30
|
-
return 'Execution
|
|
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
|
|
31
|
+
return 'Execution run loaded'
|
|
33
32
|
default:
|
|
34
|
-
return 'Execution
|
|
33
|
+
return 'Execution run'
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
export function getExecutionPlanStatusLabel(status: string | null | undefined): string {
|
|
39
|
-
if (status === '
|
|
40
|
-
if (status === '
|
|
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
|
-
|
|
48
|
+
node: SerializablePlanNode,
|
|
49
49
|
displayNameByOwnerRef: Record<string, string> = {},
|
|
50
50
|
): string {
|
|
51
|
-
if (
|
|
52
|
-
return displayNameByOwnerRef[
|
|
51
|
+
if (node.owner.executorType === 'agent' && node.owner.ref in displayNameByOwnerRef) {
|
|
52
|
+
return displayNameByOwnerRef[node.owner.ref]
|
|
53
53
|
}
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
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
|
|
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 ? `${
|
|
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
|
|
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(
|
|
30
|
+
parts.push(answer.customText.trim())
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|