@beanx/cathygo-web-core 0.1.0 → 0.1.2
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 +30 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +258 -46
- package/dist/styles.css +91 -1
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @beanx/cathygo-web-core
|
|
2
|
+
|
|
3
|
+
Shared CathyGO Web UI and state package.
|
|
4
|
+
|
|
5
|
+
Current active upgrade plan:
|
|
6
|
+
|
|
7
|
+
- [`../../docs/session-runtime-vnext-upgrade-plan.md`](../../docs/session-runtime-vnext-upgrade-plan.md)
|
|
8
|
+
|
|
9
|
+
## vNext Role
|
|
10
|
+
|
|
11
|
+
This package owns the selected-session view model for:
|
|
12
|
+
|
|
13
|
+
- single-session transcript rendering
|
|
14
|
+
- session event reduction
|
|
15
|
+
- optimistic user input reconciliation by `client_input_id`
|
|
16
|
+
- session-scoped stop state
|
|
17
|
+
- replay-safe hydration after refresh or reconnect
|
|
18
|
+
- selectors used by local Web and `beanx-home`
|
|
19
|
+
|
|
20
|
+
Host applications still own account routing, agent selection, transport setup,
|
|
21
|
+
selected session id, lightweight session lists, and product-specific shell UI.
|
|
22
|
+
They should not own a second session execution runtime.
|
|
23
|
+
|
|
24
|
+
## First vNext Deliverables
|
|
25
|
+
|
|
26
|
+
- Keep the current single-session `ChatState` reducer exported.
|
|
27
|
+
- Add `SessionViewState`, session event reducer, and current-session selectors.
|
|
28
|
+
- Add reducer tests for interrupt, replay, stop, and optimistic reconciliation.
|
|
29
|
+
- Prove the implementation in `cathygo-agent/web` before publishing
|
|
30
|
+
`@beanx/cathygo-web-core@0.2.0`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { Dispatch, RefObject } from 'react';
|
|
3
|
-
import { AgentActivity, LearningTurnEvent, ConversationSummary, GatewayModelStatus } from '@beanx/cathygo-protocol';
|
|
3
|
+
import { AgentActivity, SessionRuntimeSnapshot, LearningTurnEvent, ConversationSummary, GatewayModelStatus } from '@beanx/cathygo-protocol';
|
|
4
4
|
|
|
5
5
|
type AgentIdentityStatus = {
|
|
6
6
|
agent_id?: string | null;
|
|
@@ -96,6 +96,7 @@ type ChatState = {
|
|
|
96
96
|
messages: ChatMessage$1[];
|
|
97
97
|
activitiesByTurnId: Record<string, AgentActivity[]>;
|
|
98
98
|
progressByTurnId: Record<string, AgentRunProgress>;
|
|
99
|
+
runtime?: SessionRuntimeSnapshot;
|
|
99
100
|
status: string;
|
|
100
101
|
contextMessageCount?: number;
|
|
101
102
|
error?: string;
|
|
@@ -123,20 +124,32 @@ type AgentRunThinking = {
|
|
|
123
124
|
type ChatAction = {
|
|
124
125
|
type: 'session.created';
|
|
125
126
|
sessionId: string;
|
|
127
|
+
} | {
|
|
128
|
+
type: 'session.loading';
|
|
129
|
+
sessionId: string;
|
|
126
130
|
} | {
|
|
127
131
|
type: 'session.loaded';
|
|
128
132
|
sessionId: string;
|
|
129
133
|
messages: ChatMessage$1[];
|
|
130
134
|
activities?: AgentActivity[];
|
|
135
|
+
runtime?: SessionRuntimeSnapshot;
|
|
136
|
+
} | {
|
|
137
|
+
type: 'session.load.failed';
|
|
138
|
+
sessionId: string;
|
|
139
|
+
error: string;
|
|
140
|
+
code?: string;
|
|
131
141
|
} | {
|
|
132
142
|
type: 'user.sent';
|
|
133
143
|
id: string;
|
|
134
144
|
content: string;
|
|
135
145
|
parts?: ChatMessagePart[];
|
|
136
146
|
} | {
|
|
137
|
-
type: '
|
|
147
|
+
type: 'session.input.accepted';
|
|
138
148
|
sessionId: string;
|
|
139
149
|
turnId: string;
|
|
150
|
+
clientInputId?: string;
|
|
151
|
+
clientTurnId?: string;
|
|
152
|
+
idempotentReplay?: boolean;
|
|
140
153
|
} | {
|
|
141
154
|
type: 'send.failed';
|
|
142
155
|
id: string;
|
|
@@ -174,6 +187,9 @@ type CathyGOChatAppProps = {
|
|
|
174
187
|
gatewayLinked?: boolean;
|
|
175
188
|
model?: GatewayModelStatus;
|
|
176
189
|
agentStatus?: AgentIdentityStatus;
|
|
190
|
+
activeSessionId?: string;
|
|
191
|
+
loadingSessionId?: string;
|
|
192
|
+
debugPanels?: boolean;
|
|
177
193
|
homeAvatarSrc?: string;
|
|
178
194
|
onDraftChange: (value: string) => void;
|
|
179
195
|
onFilesSelected?: (files: File[]) => void;
|
|
@@ -184,15 +200,16 @@ type CathyGOChatAppProps = {
|
|
|
184
200
|
onSuggest?: (text: string) => void;
|
|
185
201
|
onOpenChat: (sessionId: string) => void;
|
|
186
202
|
onNewChat: () => void;
|
|
203
|
+
onSettingsOpen?: () => void;
|
|
187
204
|
};
|
|
188
|
-
declare function CathyGOChatApp({ screen, chat, chats, draft, attachments, attachmentPolicy, composerError, busy, connectionStatus, gatewayLinked, model, agentStatus, homeAvatarSrc, onDraftChange, onFilesSelected, onRemoveAttachment, onSendMessage, onSendHomeMessage, onAbort, onSuggest, onOpenChat, onNewChat, }: CathyGOChatAppProps): react.JSX.Element;
|
|
205
|
+
declare function CathyGOChatApp({ screen, chat, chats, draft, attachments, attachmentPolicy, composerError, busy, connectionStatus, gatewayLinked, model, agentStatus, activeSessionId, loadingSessionId, debugPanels, homeAvatarSrc, onDraftChange, onFilesSelected, onRemoveAttachment, onSendMessage, onSendHomeMessage, onAbort, onSuggest, onOpenChat, onNewChat, onSettingsOpen, }: CathyGOChatAppProps): react.JSX.Element;
|
|
189
206
|
|
|
190
207
|
type UseCathyGOChatResult = {
|
|
191
208
|
chat: ChatState;
|
|
192
209
|
dispatchChat: Dispatch<ChatAction>;
|
|
193
210
|
resetChat: () => void;
|
|
194
211
|
createSession: (sessionId: string) => void;
|
|
195
|
-
loadSession: (sessionId: string, messages: ChatMessage$1[], activities?: ChatState['activitiesByTurnId'][string]) => void;
|
|
212
|
+
loadSession: (sessionId: string, messages: ChatMessage$1[], activities?: ChatState['activitiesByTurnId'][string], runtime?: SessionRuntimeSnapshot) => void;
|
|
196
213
|
applyTurnEvent: (event: LearningTurnEvent) => void;
|
|
197
214
|
};
|
|
198
215
|
declare function useCathyGOChat(initialState?: ChatState): UseCathyGOChatResult;
|
|
@@ -246,8 +263,9 @@ type ChatTopBarProps = {
|
|
|
246
263
|
debugUI?: boolean;
|
|
247
264
|
canAbort?: boolean;
|
|
248
265
|
onAbort?: () => void;
|
|
266
|
+
onSettingsOpen?: () => void;
|
|
249
267
|
};
|
|
250
|
-
declare function ChatTopBar({ agentStatus, connectionStatus, gatewayLinked, model, debugUI, canAbort, onAbort, }: ChatTopBarProps): react.JSX.Element;
|
|
268
|
+
declare function ChatTopBar({ agentStatus, connectionStatus, gatewayLinked, model, debugUI, canAbort, onAbort, onSettingsOpen, }: ChatTopBarProps): react.JSX.Element;
|
|
251
269
|
|
|
252
270
|
type ChatTranscriptProps = {
|
|
253
271
|
messages: ChatMessage$1[];
|
|
@@ -325,19 +343,22 @@ interface ChatHomeViewProps {
|
|
|
325
343
|
model?: GatewayModelStatus;
|
|
326
344
|
agentStatus?: AgentIdentityStatus;
|
|
327
345
|
avatarSrc?: string;
|
|
346
|
+
onSettingsOpen?: () => void;
|
|
328
347
|
onDraftChange: (value: string) => void;
|
|
329
348
|
onFilesSelected?: (files: File[]) => void;
|
|
330
349
|
onRemoveAttachment?: (id: string) => void;
|
|
331
350
|
onSend: (modeId: ChatHomeModeId) => void;
|
|
332
351
|
}
|
|
333
|
-
declare function ChatHomeView({ draft, attachments, attachmentPolicy, error, busy, connectionStatus, gatewayLinked, model, agentStatus, avatarSrc, onDraftChange, onFilesSelected, onRemoveAttachment, onSend, }: ChatHomeViewProps): react.JSX.Element;
|
|
352
|
+
declare function ChatHomeView({ draft, attachments, attachmentPolicy, error, busy, connectionStatus, gatewayLinked, model, agentStatus, avatarSrc, onSettingsOpen, onDraftChange, onFilesSelected, onRemoveAttachment, onSend, }: ChatHomeViewProps): react.JSX.Element;
|
|
334
353
|
|
|
335
354
|
interface ChatListViewProps {
|
|
336
355
|
chats: ConversationSummary[];
|
|
356
|
+
activeSessionId?: string;
|
|
357
|
+
loadingSessionId?: string;
|
|
337
358
|
onOpen: (sessionId: string) => void;
|
|
338
359
|
onNewChat: () => void;
|
|
339
360
|
}
|
|
340
|
-
declare function ChatListView({ chats, onOpen, onNewChat }: ChatListViewProps): react.JSX.Element;
|
|
361
|
+
declare function ChatListView({ chats, activeSessionId, loadingSessionId, onOpen, onNewChat, }: ChatListViewProps): react.JSX.Element;
|
|
341
362
|
|
|
342
363
|
interface ChatViewProps {
|
|
343
364
|
chat: ChatState;
|
|
@@ -345,18 +366,21 @@ interface ChatViewProps {
|
|
|
345
366
|
attachments?: PendingComposerAttachment[];
|
|
346
367
|
attachmentPolicy?: ComposerAttachmentPolicy;
|
|
347
368
|
composerError?: string;
|
|
369
|
+
busy?: boolean;
|
|
348
370
|
connectionStatus: string;
|
|
349
371
|
gatewayLinked?: boolean;
|
|
350
372
|
model?: GatewayModelStatus;
|
|
351
373
|
agentStatus?: AgentIdentityStatus;
|
|
374
|
+
debugPanels?: boolean;
|
|
352
375
|
onDraftChange: (value: string) => void;
|
|
353
376
|
onFilesSelected?: (files: File[]) => void;
|
|
354
377
|
onRemoveAttachment?: (id: string) => void;
|
|
355
378
|
onSend: () => void;
|
|
356
379
|
onAbort: () => void;
|
|
357
380
|
onSuggest?: (text: string) => void;
|
|
381
|
+
onSettingsOpen?: () => void;
|
|
358
382
|
}
|
|
359
|
-
declare function ChatView({ chat, draft, attachments, attachmentPolicy, composerError, connectionStatus, gatewayLinked, model, agentStatus, onDraftChange, onFilesSelected, onRemoveAttachment, onSend, onAbort, onSuggest, }: ChatViewProps): react.JSX.Element;
|
|
383
|
+
declare function ChatView({ chat, draft, attachments, attachmentPolicy, composerError, busy: externalBusy, connectionStatus, gatewayLinked, model, agentStatus, debugPanels, onDraftChange, onFilesSelected, onRemoveAttachment, onSend, onAbort, onSuggest, onSettingsOpen, }: ChatViewProps): react.JSX.Element;
|
|
360
384
|
|
|
361
385
|
declare function modelStatusText(model: GatewayModelStatus): string;
|
|
362
386
|
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
// src/features/chat/ChatHomeView.tsx
|
|
2
2
|
import { useRef as useRef2, useState as useState2 } from "react";
|
|
3
3
|
|
|
4
|
-
// src/chat-ui/agentIdentity.ts
|
|
5
|
-
function agentDisplayName(status) {
|
|
6
|
-
return status?.display_name?.trim() || status?.agent_id?.trim() || "CathyGO Agent";
|
|
7
|
-
}
|
|
8
|
-
function agentShortId(status) {
|
|
9
|
-
const value = status?.agent_short_id?.trim();
|
|
10
|
-
return value || void 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
// src/chat-ui/icons.tsx
|
|
14
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
6
|
function IconNewChat({ className }) {
|
|
@@ -773,6 +764,15 @@ function IconSend({ className }) {
|
|
|
773
764
|
);
|
|
774
765
|
}
|
|
775
766
|
|
|
767
|
+
// src/chat-ui/agentIdentity.ts
|
|
768
|
+
function agentDisplayName(status) {
|
|
769
|
+
return status?.display_name?.trim() || status?.agent_id?.trim() || "CathyGO Agent";
|
|
770
|
+
}
|
|
771
|
+
function agentShortId(status) {
|
|
772
|
+
const value = status?.agent_short_id?.trim();
|
|
773
|
+
return value || void 0;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
776
|
// src/chat-ui/components/ChatAgentIdentity.tsx
|
|
777
777
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
778
778
|
function ChatAgentIdentity({ agentStatus }) {
|
|
@@ -816,7 +816,8 @@ function ChatTopBar({
|
|
|
816
816
|
model,
|
|
817
817
|
debugUI,
|
|
818
818
|
canAbort,
|
|
819
|
-
onAbort
|
|
819
|
+
onAbort,
|
|
820
|
+
onSettingsOpen
|
|
820
821
|
}) {
|
|
821
822
|
const tone = connectionTone(connectionStatus, gatewayLinked);
|
|
822
823
|
const statusLabel = gatewayLinked ? "Connected" : connectionStatus;
|
|
@@ -833,6 +834,17 @@ function ChatTopBar({
|
|
|
833
834
|
}
|
|
834
835
|
),
|
|
835
836
|
debugUI ? /* @__PURE__ */ jsx3("span", { className: "chat-topbar-debug", children: "Debug" }) : null,
|
|
837
|
+
onSettingsOpen ? /* @__PURE__ */ jsx3(
|
|
838
|
+
"button",
|
|
839
|
+
{
|
|
840
|
+
"aria-label": "\u6253\u5F00\u8BBE\u7F6E",
|
|
841
|
+
className: "chat-topbar-icon-btn",
|
|
842
|
+
onClick: onSettingsOpen,
|
|
843
|
+
title: "\u8BBE\u7F6E",
|
|
844
|
+
type: "button",
|
|
845
|
+
children: /* @__PURE__ */ jsx3(IconSettings, {})
|
|
846
|
+
}
|
|
847
|
+
) : null,
|
|
836
848
|
canAbort ? /* @__PURE__ */ jsx3("button", { className: "chat-topbar-abort", onClick: onAbort, type: "button", children: "\u505C\u6B62" }) : null
|
|
837
849
|
] })
|
|
838
850
|
] });
|
|
@@ -1108,6 +1120,7 @@ function ChatHomeView({
|
|
|
1108
1120
|
model,
|
|
1109
1121
|
agentStatus,
|
|
1110
1122
|
avatarSrc,
|
|
1123
|
+
onSettingsOpen,
|
|
1111
1124
|
onDraftChange,
|
|
1112
1125
|
onFilesSelected,
|
|
1113
1126
|
onRemoveAttachment,
|
|
@@ -1130,7 +1143,8 @@ function ChatHomeView({
|
|
|
1130
1143
|
connectionStatus,
|
|
1131
1144
|
gatewayLinked,
|
|
1132
1145
|
model,
|
|
1133
|
-
agentStatus
|
|
1146
|
+
agentStatus,
|
|
1147
|
+
onSettingsOpen
|
|
1134
1148
|
}
|
|
1135
1149
|
),
|
|
1136
1150
|
/* @__PURE__ */ jsxs5("div", { className: "chat-home-center", children: [
|
|
@@ -1188,29 +1202,37 @@ function stripMathForPreview(text, maxLen = 80) {
|
|
|
1188
1202
|
|
|
1189
1203
|
// src/features/chat/ChatListView.tsx
|
|
1190
1204
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1191
|
-
function ChatListView({
|
|
1205
|
+
function ChatListView({
|
|
1206
|
+
chats,
|
|
1207
|
+
activeSessionId,
|
|
1208
|
+
loadingSessionId,
|
|
1209
|
+
onOpen,
|
|
1210
|
+
onNewChat
|
|
1211
|
+
}) {
|
|
1192
1212
|
return /* @__PURE__ */ jsxs6("section", { className: "cathygo-chat-panel list-workspace", children: [
|
|
1193
1213
|
/* @__PURE__ */ jsxs6("header", { className: "page-topbar", children: [
|
|
1194
1214
|
/* @__PURE__ */ jsx6("h1", { className: "page-topbar-title", children: "\u5BF9\u8BDD\u5386\u53F2" }),
|
|
1195
1215
|
/* @__PURE__ */ jsx6("button", { className: "primary-action", onClick: onNewChat, type: "button", children: "\u65B0\u5BF9\u8BDD" })
|
|
1196
1216
|
] }),
|
|
1197
|
-
/* @__PURE__ */ jsx6("div", { className: "chat-column list-column", children: chats.length ? /* @__PURE__ */ jsx6("div", { className: "history-list", children: chats.map((chat) =>
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
"
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1217
|
+
/* @__PURE__ */ jsx6("div", { className: "chat-column list-column", children: chats.length ? /* @__PURE__ */ jsx6("div", { className: "history-list", children: chats.map((chat) => {
|
|
1218
|
+
const active = chat.session_id === activeSessionId;
|
|
1219
|
+
const loading = chat.session_id === loadingSessionId;
|
|
1220
|
+
return /* @__PURE__ */ jsxs6(
|
|
1221
|
+
"button",
|
|
1222
|
+
{
|
|
1223
|
+
"aria-current": active ? "true" : void 0,
|
|
1224
|
+
className: `history-row${active ? " active" : ""}${loading ? " loading" : ""}`,
|
|
1225
|
+
onClick: () => onOpen(chat.session_id),
|
|
1226
|
+
type: "button",
|
|
1227
|
+
children: [
|
|
1228
|
+
/* @__PURE__ */ jsx6("strong", { children: chat.title }),
|
|
1229
|
+
/* @__PURE__ */ jsx6("span", { children: chat.preview ? stripMathForPreview(chat.preview, 120) : "\u6682\u65E0\u9884\u89C8" }),
|
|
1230
|
+
/* @__PURE__ */ jsx6("small", { children: loading ? "\u6B63\u5728\u6253\u5F00" : `${chat.message_count} \u6761\u6D88\u606F` })
|
|
1231
|
+
]
|
|
1232
|
+
},
|
|
1233
|
+
chat.session_id
|
|
1234
|
+
);
|
|
1235
|
+
}) }) : /* @__PURE__ */ jsx6("p", { className: "empty", children: "\u6682\u65E0\u5BF9\u8BDD\u3002\u521B\u5EFA\u65B0\u5BF9\u8BDD\u540E\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }) })
|
|
1214
1236
|
] });
|
|
1215
1237
|
}
|
|
1216
1238
|
|
|
@@ -1631,22 +1653,26 @@ function ChatView({
|
|
|
1631
1653
|
attachments,
|
|
1632
1654
|
attachmentPolicy,
|
|
1633
1655
|
composerError,
|
|
1656
|
+
busy: externalBusy,
|
|
1634
1657
|
connectionStatus,
|
|
1635
1658
|
gatewayLinked,
|
|
1636
1659
|
model,
|
|
1637
1660
|
agentStatus,
|
|
1661
|
+
debugPanels = true,
|
|
1638
1662
|
onDraftChange,
|
|
1639
1663
|
onFilesSelected,
|
|
1640
1664
|
onRemoveAttachment,
|
|
1641
1665
|
onSend,
|
|
1642
1666
|
onAbort,
|
|
1643
|
-
onSuggest
|
|
1667
|
+
onSuggest,
|
|
1668
|
+
onSettingsOpen
|
|
1644
1669
|
}) {
|
|
1645
|
-
const
|
|
1646
|
-
const
|
|
1670
|
+
const loadingSession = chat.status === "Loading";
|
|
1671
|
+
const busy = Boolean(externalBusy) || loadingSession;
|
|
1672
|
+
const ready = Boolean(chat.sessionId) && !loadingSession;
|
|
1647
1673
|
const usingMock = model?.provider === "mock";
|
|
1648
1674
|
const modelLabel = model ? model.model || model.display_name || model.provider : "loading model status";
|
|
1649
|
-
const debugUI = isDebugUIEnabled();
|
|
1675
|
+
const debugUI = debugPanels && isDebugUIEnabled();
|
|
1650
1676
|
return /* @__PURE__ */ jsxs11("section", { className: "cathygo-chat-panel chat-workspace", children: [
|
|
1651
1677
|
/* @__PURE__ */ jsx13(
|
|
1652
1678
|
ChatTopBar,
|
|
@@ -1657,7 +1683,8 @@ function ChatView({
|
|
|
1657
1683
|
agentStatus,
|
|
1658
1684
|
debugUI,
|
|
1659
1685
|
canAbort: Boolean(chat.activeTurnId),
|
|
1660
|
-
onAbort
|
|
1686
|
+
onAbort,
|
|
1687
|
+
onSettingsOpen
|
|
1661
1688
|
}
|
|
1662
1689
|
),
|
|
1663
1690
|
debugUI ? /* @__PURE__ */ jsxs11("div", { className: "chat-debug-panels", children: [
|
|
@@ -1681,16 +1708,17 @@ function ChatView({
|
|
|
1681
1708
|
)
|
|
1682
1709
|
] }) : null,
|
|
1683
1710
|
/* @__PURE__ */ jsxs11("div", { className: "chat-main", children: [
|
|
1684
|
-
/* @__PURE__ */ jsx13(
|
|
1711
|
+
loadingSession ? /* @__PURE__ */ jsx13(ChatSessionLoading, {}) : /* @__PURE__ */ jsx13(
|
|
1685
1712
|
ChatTranscript,
|
|
1686
1713
|
{
|
|
1687
1714
|
activeTurnId: chat.activeTurnId,
|
|
1688
1715
|
activitiesByTurnId: chat.activitiesByTurnId,
|
|
1689
1716
|
progressByTurnId: chat.progressByTurnId,
|
|
1690
1717
|
messages: chat.messages,
|
|
1691
|
-
onSuggest: ready && !busy ? onSuggest : void 0
|
|
1718
|
+
onSuggest: ready && !busy && !chat.activeTurnId ? onSuggest : void 0
|
|
1692
1719
|
}
|
|
1693
1720
|
),
|
|
1721
|
+
!loadingSession && chat.status === "Error" && chat.error ? /* @__PURE__ */ jsx13("p", { className: "chat-session-error", children: chat.error }) : null,
|
|
1694
1722
|
/* @__PURE__ */ jsx13(
|
|
1695
1723
|
MessageComposer,
|
|
1696
1724
|
{
|
|
@@ -1708,6 +1736,17 @@ function ChatView({
|
|
|
1708
1736
|
] })
|
|
1709
1737
|
] });
|
|
1710
1738
|
}
|
|
1739
|
+
function ChatSessionLoading() {
|
|
1740
|
+
return /* @__PURE__ */ jsx13("div", { className: "chat-transcript-wrap", children: /* @__PURE__ */ jsx13("section", { className: "chat-transcript", "aria-busy": "true", "aria-label": "\u6B63\u5728\u52A0\u8F7D\u4F1A\u8BDD", children: /* @__PURE__ */ jsxs11("div", { className: "chat-column session-loading", children: [
|
|
1741
|
+
/* @__PURE__ */ jsx13("div", { className: "session-loading-title" }),
|
|
1742
|
+
/* @__PURE__ */ jsx13("div", { className: "session-loading-line wide" }),
|
|
1743
|
+
/* @__PURE__ */ jsx13("div", { className: "session-loading-line" }),
|
|
1744
|
+
/* @__PURE__ */ jsxs11("div", { className: "session-loading-card", children: [
|
|
1745
|
+
/* @__PURE__ */ jsx13("div", {}),
|
|
1746
|
+
/* @__PURE__ */ jsx13("div", {})
|
|
1747
|
+
] })
|
|
1748
|
+
] }) }) });
|
|
1749
|
+
}
|
|
1711
1750
|
|
|
1712
1751
|
// src/CathyGOChatApp.tsx
|
|
1713
1752
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
@@ -1724,6 +1763,9 @@ function CathyGOChatApp({
|
|
|
1724
1763
|
gatewayLinked,
|
|
1725
1764
|
model,
|
|
1726
1765
|
agentStatus,
|
|
1766
|
+
activeSessionId,
|
|
1767
|
+
loadingSessionId,
|
|
1768
|
+
debugPanels = true,
|
|
1727
1769
|
homeAvatarSrc,
|
|
1728
1770
|
onDraftChange,
|
|
1729
1771
|
onFilesSelected,
|
|
@@ -1733,10 +1775,20 @@ function CathyGOChatApp({
|
|
|
1733
1775
|
onAbort,
|
|
1734
1776
|
onSuggest,
|
|
1735
1777
|
onOpenChat,
|
|
1736
|
-
onNewChat
|
|
1778
|
+
onNewChat,
|
|
1779
|
+
onSettingsOpen
|
|
1737
1780
|
}) {
|
|
1738
1781
|
if (screen === "history") {
|
|
1739
|
-
return /* @__PURE__ */ jsx14(
|
|
1782
|
+
return /* @__PURE__ */ jsx14(
|
|
1783
|
+
ChatListView,
|
|
1784
|
+
{
|
|
1785
|
+
activeSessionId,
|
|
1786
|
+
chats,
|
|
1787
|
+
loadingSessionId,
|
|
1788
|
+
onOpen: onOpenChat,
|
|
1789
|
+
onNewChat
|
|
1790
|
+
}
|
|
1791
|
+
);
|
|
1740
1792
|
}
|
|
1741
1793
|
if (screen === "home") {
|
|
1742
1794
|
return /* @__PURE__ */ jsx14(
|
|
@@ -1752,6 +1804,7 @@ function CathyGOChatApp({
|
|
|
1752
1804
|
gatewayLinked,
|
|
1753
1805
|
model,
|
|
1754
1806
|
agentStatus,
|
|
1807
|
+
onSettingsOpen,
|
|
1755
1808
|
onDraftChange,
|
|
1756
1809
|
onFilesSelected,
|
|
1757
1810
|
onRemoveAttachment,
|
|
@@ -1766,17 +1819,20 @@ function CathyGOChatApp({
|
|
|
1766
1819
|
draft,
|
|
1767
1820
|
attachments,
|
|
1768
1821
|
attachmentPolicy,
|
|
1822
|
+
busy,
|
|
1769
1823
|
composerError,
|
|
1770
1824
|
connectionStatus,
|
|
1771
1825
|
gatewayLinked,
|
|
1772
1826
|
model,
|
|
1773
1827
|
agentStatus,
|
|
1828
|
+
debugPanels,
|
|
1774
1829
|
onDraftChange,
|
|
1775
1830
|
onFilesSelected,
|
|
1776
1831
|
onRemoveAttachment,
|
|
1777
1832
|
onSend: onSendMessage,
|
|
1778
1833
|
onAbort,
|
|
1779
|
-
onSuggest
|
|
1834
|
+
onSuggest,
|
|
1835
|
+
onSettingsOpen
|
|
1780
1836
|
}
|
|
1781
1837
|
);
|
|
1782
1838
|
}
|
|
@@ -1805,19 +1861,46 @@ function reduceChat(state, action) {
|
|
|
1805
1861
|
errorCode: void 0
|
|
1806
1862
|
};
|
|
1807
1863
|
}
|
|
1808
|
-
if (action.type === "session.
|
|
1864
|
+
if (action.type === "session.loading") {
|
|
1809
1865
|
return {
|
|
1810
1866
|
...state,
|
|
1811
1867
|
sessionId: action.sessionId,
|
|
1812
1868
|
activeTurnId: void 0,
|
|
1869
|
+
messages: [],
|
|
1870
|
+
activitiesByTurnId: {},
|
|
1871
|
+
progressByTurnId: {},
|
|
1872
|
+
status: "Loading",
|
|
1873
|
+
error: void 0,
|
|
1874
|
+
errorCode: void 0
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
if (action.type === "session.loaded") {
|
|
1878
|
+
const runtime = action.runtime;
|
|
1879
|
+
const activeTurnId = optionalString(runtime?.active_turn_id);
|
|
1880
|
+
return {
|
|
1881
|
+
...state,
|
|
1882
|
+
sessionId: action.sessionId,
|
|
1883
|
+
activeTurnId,
|
|
1813
1884
|
messages: action.messages,
|
|
1814
1885
|
activitiesByTurnId: groupActivitiesByTurn(action.activities ?? []),
|
|
1815
|
-
progressByTurnId: {},
|
|
1816
|
-
|
|
1886
|
+
progressByTurnId: activeTurnId ? setProgress({}, runtimeProgressFromSnapshot(runtime, activeTurnId)) : {},
|
|
1887
|
+
runtime,
|
|
1888
|
+
status: statusFromRuntime(runtime),
|
|
1817
1889
|
error: void 0,
|
|
1818
1890
|
errorCode: void 0
|
|
1819
1891
|
};
|
|
1820
1892
|
}
|
|
1893
|
+
if (action.type === "session.load.failed") {
|
|
1894
|
+
return {
|
|
1895
|
+
...state,
|
|
1896
|
+
sessionId: action.sessionId,
|
|
1897
|
+
activeTurnId: void 0,
|
|
1898
|
+
progressByTurnId: {},
|
|
1899
|
+
status: "Error",
|
|
1900
|
+
error: action.error,
|
|
1901
|
+
errorCode: action.code
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1821
1904
|
if (action.type === "user.sent") {
|
|
1822
1905
|
return {
|
|
1823
1906
|
...state,
|
|
@@ -1849,11 +1932,19 @@ function reduceChat(state, action) {
|
|
|
1849
1932
|
errorCode: action.code
|
|
1850
1933
|
};
|
|
1851
1934
|
}
|
|
1852
|
-
if (action.type === "
|
|
1935
|
+
if (action.type === "session.input.accepted") {
|
|
1853
1936
|
return {
|
|
1854
1937
|
...state,
|
|
1855
1938
|
sessionId: action.sessionId,
|
|
1856
1939
|
activeTurnId: action.turnId,
|
|
1940
|
+
runtime: {
|
|
1941
|
+
...state.runtime ?? emptyRuntimeSnapshot(),
|
|
1942
|
+
status: "running",
|
|
1943
|
+
active_turn_id: action.turnId,
|
|
1944
|
+
active_turn_ids: [action.turnId],
|
|
1945
|
+
active_client_input_id: action.clientInputId ?? null,
|
|
1946
|
+
active_client_turn_id: action.clientTurnId ?? null
|
|
1947
|
+
},
|
|
1857
1948
|
progressByTurnId: setProgress(state.progressByTurnId, {
|
|
1858
1949
|
turnId: action.turnId,
|
|
1859
1950
|
phase: "queued",
|
|
@@ -1873,8 +1964,50 @@ function reduceChat(state, action) {
|
|
|
1873
1964
|
...state,
|
|
1874
1965
|
eventCount: state.eventCount + 1,
|
|
1875
1966
|
sessionId: payload.session_id ?? state.sessionId,
|
|
1876
|
-
activeTurnId: payload.turn_id ?? state.activeTurnId
|
|
1967
|
+
activeTurnId: payload.turn_id ?? state.activeTurnId,
|
|
1968
|
+
runtime: runtimeFromEvent(state.runtime, event)
|
|
1877
1969
|
};
|
|
1970
|
+
if (event.event === "session.created") {
|
|
1971
|
+
return {
|
|
1972
|
+
...next,
|
|
1973
|
+
status: "Ready",
|
|
1974
|
+
error: void 0,
|
|
1975
|
+
errorCode: void 0
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
if (event.event === "session.deleted") {
|
|
1979
|
+
return {
|
|
1980
|
+
...next,
|
|
1981
|
+
activeTurnId: void 0,
|
|
1982
|
+
progressByTurnId: {},
|
|
1983
|
+
status: "Archived"
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
if (event.event === "session.input.accepted") {
|
|
1987
|
+
const turnId = optionalString(payload.turn_id);
|
|
1988
|
+
if (!turnId) return next;
|
|
1989
|
+
return {
|
|
1990
|
+
...next,
|
|
1991
|
+
activeTurnId: turnId,
|
|
1992
|
+
progressByTurnId: setProgress(next.progressByTurnId, {
|
|
1993
|
+
turnId,
|
|
1994
|
+
phase: "queued",
|
|
1995
|
+
status: "running",
|
|
1996
|
+
summary: "\u6B63\u5728\u63D0\u4EA4\u95EE\u9898",
|
|
1997
|
+
detail: "\u7B49\u5F85 CathyGO \u5F00\u59CB\u5904\u7406",
|
|
1998
|
+
startedAt: nowIso()
|
|
1999
|
+
}),
|
|
2000
|
+
status: "Thinking",
|
|
2001
|
+
error: void 0,
|
|
2002
|
+
errorCode: void 0
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
if (event.event === "session.stop.requested") {
|
|
2006
|
+
return {
|
|
2007
|
+
...next,
|
|
2008
|
+
status: "Stopping"
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
1878
2011
|
if (event.event === "agent.progress.delta") {
|
|
1879
2012
|
return {
|
|
1880
2013
|
...next,
|
|
@@ -1949,6 +2082,7 @@ function reduceChat(state, action) {
|
|
|
1949
2082
|
...next,
|
|
1950
2083
|
activeTurnId: void 0,
|
|
1951
2084
|
progressByTurnId: removeProgress(next.progressByTurnId, payload.turn_id),
|
|
2085
|
+
runtime: markRuntimeIdle(next.runtime),
|
|
1952
2086
|
status: "Ready"
|
|
1953
2087
|
};
|
|
1954
2088
|
}
|
|
@@ -1957,6 +2091,7 @@ function reduceChat(state, action) {
|
|
|
1957
2091
|
...next,
|
|
1958
2092
|
activeTurnId: void 0,
|
|
1959
2093
|
progressByTurnId: removeProgress(next.progressByTurnId, payload.turn_id),
|
|
2094
|
+
runtime: markRuntimeIdle(next.runtime),
|
|
1960
2095
|
status: "Ready"
|
|
1961
2096
|
};
|
|
1962
2097
|
}
|
|
@@ -1966,6 +2101,7 @@ function reduceChat(state, action) {
|
|
|
1966
2101
|
...next,
|
|
1967
2102
|
activeTurnId: void 0,
|
|
1968
2103
|
progressByTurnId: removeProgress(next.progressByTurnId, payload.turn_id),
|
|
2104
|
+
runtime: markRuntimeIdle(next.runtime),
|
|
1969
2105
|
status: "Error",
|
|
1970
2106
|
error: error?.message ?? "CathyGO turn failed",
|
|
1971
2107
|
errorCode: error?.code
|
|
@@ -1988,6 +2124,10 @@ function appendAssistantDelta(messages, messageId, delta, turnId) {
|
|
|
1988
2124
|
}
|
|
1989
2125
|
];
|
|
1990
2126
|
}
|
|
2127
|
+
const existing = messages[index];
|
|
2128
|
+
if (existing?.role === "assistant" && existing.status === "done" && existing.content) {
|
|
2129
|
+
return messages;
|
|
2130
|
+
}
|
|
1991
2131
|
return messages.map(
|
|
1992
2132
|
(message, itemIndex) => itemIndex === index ? {
|
|
1993
2133
|
...message,
|
|
@@ -2137,6 +2277,78 @@ function progressFromActivity(progressByTurnId, activity, turnActivities, messag
|
|
|
2137
2277
|
}
|
|
2138
2278
|
return progressByTurnId;
|
|
2139
2279
|
}
|
|
2280
|
+
function runtimeProgressFromSnapshot(runtime, turnId) {
|
|
2281
|
+
return {
|
|
2282
|
+
turnId,
|
|
2283
|
+
phase: runtime?.status === "stopping" ? "model" : "queued",
|
|
2284
|
+
status: "running",
|
|
2285
|
+
summary: runtime?.status === "stopping" ? "\u6B63\u5728\u505C\u6B62" : "\u6B63\u5728\u6062\u590D\u4F1A\u8BDD\u72B6\u6001",
|
|
2286
|
+
detail: runtime?.active_client_input_id ? `client_input_id=${runtime.active_client_input_id}` : void 0,
|
|
2287
|
+
startedAt: nowIso()
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
function runtimeFromEvent(current, event) {
|
|
2291
|
+
const payload = event.payload;
|
|
2292
|
+
const turnId = optionalString(payload.turn_id);
|
|
2293
|
+
const turnIds = eventTurnIds(payload, turnId);
|
|
2294
|
+
const base = {
|
|
2295
|
+
...current ?? emptyRuntimeSnapshot(),
|
|
2296
|
+
last_event_seq: Math.max(current?.last_event_seq ?? 0, event.seq)
|
|
2297
|
+
};
|
|
2298
|
+
if (event.event === "session.input.accepted" || event.event === "turn.started") {
|
|
2299
|
+
return {
|
|
2300
|
+
...base,
|
|
2301
|
+
status: "running",
|
|
2302
|
+
active_turn_id: turnId ?? base.active_turn_id ?? null,
|
|
2303
|
+
active_turn_ids: turnIds.length > 0 ? turnIds : base.active_turn_ids ?? [],
|
|
2304
|
+
active_client_input_id: optionalString(payload.client_input_id) ?? base.active_client_input_id ?? null,
|
|
2305
|
+
active_client_turn_id: optionalString(payload.client_turn_id) ?? base.active_client_turn_id ?? null
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
if (event.event === "session.stop.requested") {
|
|
2309
|
+
return {
|
|
2310
|
+
...base,
|
|
2311
|
+
status: "stopping",
|
|
2312
|
+
active_turn_id: turnId ?? base.active_turn_id ?? null,
|
|
2313
|
+
active_turn_ids: turnIds.length > 0 ? turnIds : base.active_turn_ids ?? []
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
if (event.event === "turn.completed" || event.event === "turn.cancelled" || event.event === "turn.failed" || event.event === "session.deleted") {
|
|
2317
|
+
return markRuntimeIdle(base);
|
|
2318
|
+
}
|
|
2319
|
+
return base;
|
|
2320
|
+
}
|
|
2321
|
+
function eventTurnIds(payload, turnId) {
|
|
2322
|
+
if (Array.isArray(payload.turn_ids)) {
|
|
2323
|
+
return payload.turn_ids.map((value) => optionalString(value)).filter((value) => Boolean(value));
|
|
2324
|
+
}
|
|
2325
|
+
return turnId ? [turnId] : [];
|
|
2326
|
+
}
|
|
2327
|
+
function emptyRuntimeSnapshot() {
|
|
2328
|
+
return {
|
|
2329
|
+
status: "idle",
|
|
2330
|
+
active_turn_id: null,
|
|
2331
|
+
active_turn_ids: [],
|
|
2332
|
+
active_client_input_id: null,
|
|
2333
|
+
active_client_turn_id: null,
|
|
2334
|
+
last_event_seq: 0
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
function markRuntimeIdle(runtime) {
|
|
2338
|
+
return {
|
|
2339
|
+
...runtime ?? emptyRuntimeSnapshot(),
|
|
2340
|
+
status: "idle",
|
|
2341
|
+
active_turn_id: null,
|
|
2342
|
+
active_turn_ids: [],
|
|
2343
|
+
active_client_input_id: null,
|
|
2344
|
+
active_client_turn_id: null
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
function statusFromRuntime(runtime) {
|
|
2348
|
+
if (runtime?.status === "running") return "Thinking";
|
|
2349
|
+
if (runtime?.status === "stopping") return "Stopping";
|
|
2350
|
+
return "Ready";
|
|
2351
|
+
}
|
|
2140
2352
|
function setProgress(progressByTurnId, progress) {
|
|
2141
2353
|
return {
|
|
2142
2354
|
...progressByTurnId,
|
|
@@ -2234,8 +2446,8 @@ function useCathyGOChat(initialState = initialChatState) {
|
|
|
2234
2446
|
dispatchChat({ type: "session.created", sessionId });
|
|
2235
2447
|
}, []);
|
|
2236
2448
|
const loadSession = useCallback(
|
|
2237
|
-
(sessionId, messages, activities) => {
|
|
2238
|
-
dispatchChat({ type: "session.loaded", sessionId, messages, activities });
|
|
2449
|
+
(sessionId, messages, activities, runtime) => {
|
|
2450
|
+
dispatchChat({ type: "session.loaded", sessionId, messages, activities, runtime });
|
|
2239
2451
|
},
|
|
2240
2452
|
[]
|
|
2241
2453
|
);
|
package/dist/styles.css
CHANGED
|
@@ -240,6 +240,25 @@
|
|
|
240
240
|
padding: 5px 12px;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
.chat-topbar-icon-btn {
|
|
244
|
+
align-items: center;
|
|
245
|
+
background: transparent;
|
|
246
|
+
border: 1px solid var(--border-subtle);
|
|
247
|
+
border-radius: 50%;
|
|
248
|
+
color: var(--text-muted);
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
display: inline-flex;
|
|
251
|
+
flex-shrink: 0;
|
|
252
|
+
height: 32px;
|
|
253
|
+
justify-content: center;
|
|
254
|
+
width: 32px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.chat-topbar-icon-btn:hover {
|
|
258
|
+
background: var(--bg-hover);
|
|
259
|
+
color: var(--text-primary);
|
|
260
|
+
}
|
|
261
|
+
|
|
243
262
|
.chat-topbar-abort:hover {
|
|
244
263
|
background: var(--bg-hover);
|
|
245
264
|
}
|
|
@@ -469,6 +488,68 @@
|
|
|
469
488
|
background: var(--bg-hover);
|
|
470
489
|
}
|
|
471
490
|
|
|
491
|
+
.session-loading {
|
|
492
|
+
gap: 14px;
|
|
493
|
+
margin-top: 72px;
|
|
494
|
+
max-width: 40rem;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.session-loading-title,
|
|
498
|
+
.session-loading-line,
|
|
499
|
+
.session-loading-card {
|
|
500
|
+
animation: session-loading-pulse 1.2s ease-in-out infinite;
|
|
501
|
+
background: linear-gradient(90deg, #ececf1 0%, #f7f7f8 48%, #ececf1 100%);
|
|
502
|
+
background-size: 200% 100%;
|
|
503
|
+
border-radius: 8px;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.session-loading-title {
|
|
507
|
+
height: 28px;
|
|
508
|
+
width: 180px;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.session-loading-line {
|
|
512
|
+
height: 16px;
|
|
513
|
+
width: 68%;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.session-loading-line.wide {
|
|
517
|
+
width: 92%;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.session-loading-card {
|
|
521
|
+
border-radius: 12px;
|
|
522
|
+
display: grid;
|
|
523
|
+
gap: 12px;
|
|
524
|
+
margin-left: auto;
|
|
525
|
+
min-height: 132px;
|
|
526
|
+
padding: 18px;
|
|
527
|
+
width: min(360px, 80%);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.session-loading-card div {
|
|
531
|
+
background: rgba(255, 255, 255, 0.65);
|
|
532
|
+
border-radius: 7px;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.chat-session-error {
|
|
536
|
+
color: #b42318;
|
|
537
|
+
font-size: 14px;
|
|
538
|
+
margin: 0 auto 8px;
|
|
539
|
+
max-width: var(--chat-column-width);
|
|
540
|
+
padding: 0 20px;
|
|
541
|
+
width: 100%;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
@keyframes session-loading-pulse {
|
|
545
|
+
0% {
|
|
546
|
+
background-position: 100% 0;
|
|
547
|
+
}
|
|
548
|
+
100% {
|
|
549
|
+
background-position: -100% 0;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
472
553
|
.empty-chat h2,
|
|
473
554
|
.empty-state h1 {
|
|
474
555
|
color: #1f2937;
|
|
@@ -1293,10 +1374,19 @@
|
|
|
1293
1374
|
width: 100%;
|
|
1294
1375
|
}
|
|
1295
1376
|
|
|
1296
|
-
.history-row:hover
|
|
1377
|
+
.history-row:hover,
|
|
1378
|
+
.history-row.active {
|
|
1297
1379
|
background: var(--bg-hover);
|
|
1298
1380
|
}
|
|
1299
1381
|
|
|
1382
|
+
.history-row.active {
|
|
1383
|
+
box-shadow: inset 3px 0 0 #2563eb;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
.history-row.loading small {
|
|
1387
|
+
color: #2563eb;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1300
1390
|
.list-surface {
|
|
1301
1391
|
align-content: start;
|
|
1302
1392
|
display: grid;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beanx/cathygo-web-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"./styles.css": "./dist/styles.css"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@beanx/cathygo-protocol": "0.1.
|
|
19
|
+
"@beanx/cathygo-protocol": "0.1.1",
|
|
20
20
|
"@streamdown/math": "^1.0.2",
|
|
21
21
|
"katex": "^0.16.47",
|
|
22
22
|
"streamdown": "^2.5.0"
|