@tbox.cn/app-template-agent 0.7.5 → 0.9.0

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 (152) hide show
  1. package/.gitignore +5 -0
  2. package/.tbox/app.json +9 -1
  3. package/AGENTS.md +12 -4
  4. package/ENV.md +105 -21
  5. package/README.md +40 -38
  6. package/apps/client/VISUAL_RESTORE.md +11 -11
  7. package/apps/client/index.html +3 -1
  8. package/apps/client/package.json +9 -8
  9. package/apps/client/src/business/App.tsx +4 -4
  10. package/apps/client/src/business/README.md +5 -11
  11. package/apps/client/src/cards/registry.ts +23 -8
  12. package/apps/client/src/main.tsx +36 -2
  13. package/apps/client/src/modules.ts +7 -4
  14. package/apps/client/src/pages/index/index.tsx +18 -162
  15. package/apps/client/src/platform/AgentChatApp.tsx +7 -6
  16. package/apps/client/src/platform/agent-runtime.ts +5 -0
  17. package/apps/client/src/store/chat.ts +47 -185
  18. package/apps/client/src/test/setup.ts +7 -0
  19. package/apps/client/vite.config.ts +16 -1
  20. package/apps/server/package.json +0 -3
  21. package/apps/server/src/app-tools.ts +2 -1
  22. package/apps/server/src/app.ts +248 -119
  23. package/apps/server/src/config/integration.ts +2 -6
  24. package/apps/server/src/dev.ts +0 -1
  25. package/apps/server/src/intent-routes.ts +3 -3
  26. package/apps/server/src/modules.ts +4 -4
  27. package/apps/server/src/pre-tools.ts +4 -3
  28. package/apps/server/tests/agent-prompt.test.ts +81 -0
  29. package/apps/server/tests/cards-registry.test.ts +1 -3
  30. package/apps/server/tests/deployment-loader.test.ts +35 -56
  31. package/apps/server/tests/tool-runtime.test.ts +25 -61
  32. package/package.json +5 -4
  33. package/packages/agent-ui-kit/LICENSE +21 -0
  34. package/packages/agent-ui-kit/README.md +17 -0
  35. package/packages/agent-ui-kit/package.json +58 -0
  36. package/packages/agent-ui-kit/src/ChatPage.less +62 -0
  37. package/packages/agent-ui-kit/src/ChatPage.tsx +305 -0
  38. package/packages/agent-ui-kit/src/chat-config.test.ts +82 -0
  39. package/packages/agent-ui-kit/src/chat-config.ts +62 -0
  40. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/DebugButton.tsx +2 -2
  41. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.less +19 -16
  42. package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.tsx +10 -15
  43. package/packages/agent-ui-kit/src/components/CardRenderer.tsx +36 -0
  44. package/packages/agent-ui-kit/src/components/ChatHeader/index.less +148 -0
  45. package/packages/agent-ui-kit/src/components/ChatHeader/index.test.tsx +74 -0
  46. package/{apps/client → packages/agent-ui-kit}/src/components/ChatHeader/index.tsx +27 -15
  47. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.less +47 -39
  48. package/packages/agent-ui-kit/src/components/ChatInput/index.test.tsx +156 -0
  49. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.tsx +13 -4
  50. package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/useVoicePress.ts +2 -2
  51. package/packages/agent-ui-kit/src/components/HistoryEntry/index.less +49 -0
  52. package/packages/agent-ui-kit/src/components/HistoryEntry/index.tsx +42 -0
  53. package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.less +3 -3
  54. package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.tsx +6 -6
  55. package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.less +25 -20
  56. package/packages/agent-ui-kit/src/components/MessageBubble/index.test.tsx +112 -0
  57. package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.tsx +22 -17
  58. package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.less +2 -2
  59. package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.tsx +2 -2
  60. package/packages/agent-ui-kit/src/components/QuickCommands/index.less +63 -0
  61. package/packages/agent-ui-kit/src/components/QuickCommands/index.test.tsx +99 -0
  62. package/packages/agent-ui-kit/src/components/QuickCommands/index.tsx +52 -0
  63. package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.less +1 -1
  64. package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.test.ts +346 -0
  65. package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.ts +204 -0
  66. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.less +312 -0
  67. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.test.tsx +261 -0
  68. package/packages/agent-ui-kit/src/components/ToolCallBlock/index.tsx +212 -0
  69. package/packages/agent-ui-kit/src/components/ToolCallBlock/titles.ts +102 -0
  70. package/packages/agent-ui-kit/src/hooks/use-drag-gesture.test.ts +66 -0
  71. package/packages/agent-ui-kit/src/hooks/use-drag-gesture.ts +129 -0
  72. package/packages/agent-ui-kit/src/hooks/use-pull-down-history.ts +241 -0
  73. package/packages/agent-ui-kit/src/hooks/use-pull-down-wheel-native.test.ts +64 -0
  74. package/{apps/client → packages/agent-ui-kit}/src/hooks/useMediaUpload.ts +3 -3
  75. package/{apps/client → packages/agent-ui-kit}/src/hooks/useTTSAutoPlay.ts +7 -5
  76. package/packages/agent-ui-kit/src/index.ts +69 -0
  77. package/packages/agent-ui-kit/src/store-context.tsx +92 -0
  78. package/packages/agent-ui-kit/src/test/setup.ts +7 -0
  79. package/packages/agent-ui-kit/src/theme/contract.json +72 -0
  80. package/packages/agent-ui-kit/src/theme/global.less +99 -0
  81. package/packages/agent-ui-kit/src/theme/motion.less +19 -0
  82. package/packages/agent-ui-kit/src/theme/theme-contract.test.ts +69 -0
  83. package/packages/agent-ui-kit/src/theme/theme.ts +17 -0
  84. package/packages/agent-ui-kit/src/theme/tokens.less +79 -0
  85. package/packages/agent-ui-kit/src/utils/haptics.test.ts +51 -0
  86. package/packages/agent-ui-kit/src/utils/haptics.ts +44 -0
  87. package/packages/agent-ui-kit/src/utils/pull-down-machine.test.ts +188 -0
  88. package/packages/agent-ui-kit/src/utils/pull-down-machine.ts +157 -0
  89. package/packages/agent-ui-kit/src/utils/tool-call-display.test.ts +240 -0
  90. package/packages/agent-ui-kit/src/utils/tool-call-display.ts +146 -0
  91. package/packages/agent-ui-kit/src/vite-env.d.ts +1 -0
  92. package/packages/agent-ui-kit/tsconfig.json +8 -0
  93. package/packages/agent-ui-kit/vitest.config.ts +7 -0
  94. package/pnpm-lock.template.yaml +177 -111
  95. package/pnpm-workspace.template.yaml +12 -4
  96. package/profiles/mall/config/agent.jsonc +33 -0
  97. package/profiles/mall/config/chat.json +21 -0
  98. package/profiles/mall/config/knowledge.json +5 -0
  99. package/profiles/mall/config/llm_provider.json +5 -0
  100. package/profiles/mall/config/plugins.json +3 -0
  101. package/profiles/mall.json +16 -0
  102. package/scripts/check-workspace.mjs +3 -2
  103. package/apps/client/assets/copy@2x.png +0 -0
  104. package/apps/client/assets/debug.png +0 -0
  105. package/apps/client/assets/dislike@2x.png +0 -0
  106. package/apps/client/assets/like@2x.png +0 -0
  107. package/apps/client/assets/speaker@2x.png +0 -0
  108. package/apps/client/src/cards/CardRenderer.tsx +0 -20
  109. package/apps/client/src/components/ChatHeader/index.less +0 -104
  110. package/apps/client/src/components/ErrorBoundary.tsx +0 -2
  111. package/apps/client/src/components/QuickCommands/index.less +0 -48
  112. package/apps/client/src/components/QuickCommands/index.tsx +0 -29
  113. package/apps/client/src/components/ToolCallBlock/index.less +0 -131
  114. package/apps/client/src/components/ToolCallBlock/index.tsx +0 -125
  115. package/apps/client/src/examples/tourism/TourismLanding.tsx +0 -96
  116. package/apps/client/src/examples/tourism/index.less +0 -192
  117. package/apps/client/src/pages/index/index.less +0 -43
  118. package/apps/client/src/store/chat-config.ts +0 -40
  119. package/apps/client/src/styles/global.less +0 -60
  120. package/apps/client/src/styles/tokens.less +0 -60
  121. package/apps/server/src/config/deployment.ts +0 -47
  122. package/packages/module-weather/package.json +0 -36
  123. package/packages/module-weather/skills/weather-guide/SKILL.md +0 -22
  124. package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +0 -5
  125. package/packages/module-weather/src/client/cards/weather/index.less +0 -22
  126. package/packages/module-weather/src/client/cards/weather/index.tsx +0 -14
  127. package/packages/module-weather/src/client/index.ts +0 -19
  128. package/packages/module-weather/src/index.ts +0 -2
  129. package/packages/module-weather/src/server/cards/weather/meta.ts +0 -27
  130. package/packages/module-weather/src/server/index.ts +0 -45
  131. package/packages/module-weather/src/server/pre-tools.ts +0 -20
  132. package/packages/module-weather/src/server/tool.ts +0 -29
  133. package/packages/module-weather/tbox.module.json +0 -27
  134. package/packages/module-weather/tsconfig.json +0 -14
  135. /package/{apps/client → packages/agent-ui-kit}/assets/keyboard-circle.svg +0 -0
  136. /package/{apps/client → packages/agent-ui-kit}/assets/more-circle.svg +0 -0
  137. /package/{apps/client → packages/agent-ui-kit}/assets/new-chat.svg +0 -0
  138. /package/{apps/client → packages/agent-ui-kit}/assets/quick-command-star.svg +0 -0
  139. /package/{apps/client → packages/agent-ui-kit}/assets/send-gradient.svg +0 -0
  140. /package/{apps/client → packages/agent-ui-kit}/assets/stop@2x.png +0 -0
  141. /package/{apps/client → packages/agent-ui-kit}/assets/voice-circle.svg +0 -0
  142. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoicePressField.tsx +0 -0
  143. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoiceRecordingOverlay.tsx +0 -0
  144. /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/machine.ts +0 -0
  145. /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.less +0 -0
  146. /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.tsx +0 -0
  147. /package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.tsx +0 -0
  148. /package/{apps/client/src/types/chat.ts → packages/agent-ui-kit/src/types.ts} +0 -0
  149. /package/{apps/client → packages/agent-ui-kit}/src/utils/copyText.ts +0 -0
  150. /package/{apps/client → packages/agent-ui-kit}/src/utils/imagePreview.ts +0 -0
  151. /package/{apps/client → packages/agent-ui-kit}/src/utils/session-error.ts +0 -0
  152. /package/{apps/client → packages/agent-ui-kit}/src/utils/toast.ts +0 -0
@@ -1,202 +1,64 @@
1
1
  /**
2
- * 对话状态(Zustand)。
3
- * 使用 createCoreChatState 在同一个 create() 中合并核心状态与业务方法,
4
- * 共用同一份 set/get,避免双 store 断裂。
2
+ * 对话状态(Zustand)薄壳(单一模板统一化 W2-5):
3
+ * 会话泳道/历史分页/凭证升级调度全部下沉 SDK createCoreChatState——
4
+ * 本壳只做域参数注入(HELLO context 采集)+ 排水订阅装配(应用侧一行)。
5
5
  */
6
6
  import { create } from 'zustand';
7
- import { createLogger } from '@tbox.cn/app-sdk/client';
8
-
9
- const conversationLogger = createLogger('CONVERSATION');
10
- const historyLogger = createLogger('HISTORY');
11
- const feedbackLogger = createLogger('FEEDBACK');
12
- import { createCoreChatState, getWsUrl, configureHelloContext } from '@tbox.cn/app-sdk/client';
7
+ import {
8
+ createCoreChatState,
9
+ getWsUrl,
10
+ getLocation,
11
+ configureHelloContext,
12
+ subscribeSessionUpgradeDrain,
13
+ } from '@tbox.cn/app-sdk/client';
13
14
  import type { CoreChatState } from '@tbox.cn/app-sdk/client';
14
- import { convertHistoryMessages } from '@tbox.cn/app-sdk/client';
15
- import type { PlatformHistoryMessage } from '@tbox.cn/app-sdk/client';
16
- import type { ChatMessage } from '@/types/chat';
17
- import type { IFeedbackAdapter } from '@tbox.cn/app-sdk/client';
18
- import type { AdapterContextValue } from '@tbox.cn/app-sdk/client';
19
- import { isSessionExpiredError } from '@/utils/session-error';
20
- import { toast } from '@/utils/toast';
15
+ import { toast } from '@app/agent-ui-kit';
16
+ import { readMallIdFromUrl, readMiniAppIdFromUrl } from '@app/contracts-mall';
21
17
  import { authClient, platformApi } from '@/platform/agent-runtime';
22
18
 
23
- // ── 历史加载代际标志(模块级):newConversation 递增作废在途 loadMoreHistory(stale 响应整段丢弃)──
24
- let historyEpoch = 0;
25
- // ── 新建会话 in-flight 标志(模块级):双击「新对话」防双建会话 ──
26
- let newConversationInFlight = false;
27
- // ── 终态阻断重试 in-flight 标志(模块级):防并发重试 ──
28
- let retryBootstrapInFlight: Promise<void> | null = null;
29
-
30
- interface ChatState extends CoreChatState {
31
- historyMessages: ChatMessage[];
32
- historyHasMore: boolean;
33
- isLoadingHistory: boolean;
34
- initConversation: () => Promise<void>;
35
- /** 新建会话:清会话态(连接复用)+ 重置历史态 + create 新会话 */
36
- newConversation: () => Promise<void>;
37
- loadMoreHistory: () => Promise<void>;
38
- /** 终态阻断后重试:清匿名降级缓存 → 拆除连接 → 重走 initConversation(重复点击防抖) */
39
- retryBootstrap: () => Promise<void>;
40
- submitFeedback: (
41
- messageId: string,
42
- type: 'like' | 'dislike' | 'cancel',
43
- adapter: IFeedbackAdapter,
44
- refreshSession?: () => Promise<AdapterContextValue>,
45
- ) => Promise<void>;
46
- }
47
-
48
- export const useChatStore = create<ChatState>((set, get) => ({
49
- // 核心状态:所有 set/get 闭包绑定到此 store(006:不再创建客户端 sessionId)
19
+ export const useChatStore = create<CoreChatState>()((set, get) => ({
20
+ // 核心状态:所有 set/get 闭包绑定到此 store(006:不再创建客户端 sessionId);
21
+ // 会话泳道 deps(platformApi/authClient)注入 SDK 默认实现
50
22
  ...createCoreChatState(
51
- { getWsUrl: () => getWsUrl(), toast },
23
+ {
24
+ getWsUrl: () => getWsUrl(),
25
+ toast,
26
+ platformApi,
27
+ authClient,
28
+ // session.credentials-refreshed effect → 升级调度(清缓存冷登 + 幂等重连;守卫在 scheduleSessionUpgrade 内)
29
+ onSessionCredentialsRefreshed: () => void get().scheduleSessionUpgrade(),
30
+ },
52
31
  set as (partial: any) => void,
53
32
  get as () => any,
54
- ), // 业务扩展状态
55
- historyMessages: [],
56
- historyHasMore: true,
57
- isLoadingHistory: false,
58
-
59
- initConversation: async () => {
60
- const userId = await authClient.resolveUserId();
61
- set({ userId });
62
- try {
63
- let conversationId: string | null = null;
64
- // 006:create 的 userId 只来自 token 身份(body 废弃);匿名(无 token)不可建会话 → 直接走 no-conv 分支
65
- if (!authClient.getAuthState()?.token) {
66
- conversationLogger.warn('anonymous: skip create conversation (no-conv)');
67
- } else {
68
- for (let i = 0; i < 2 && !conversationId; i++) {
69
- try {
70
- const { status, data } = await platformApi.createConversation();
71
- if (status === 200 && data?.conversationId) conversationId = data.conversationId;
72
- } catch (e) {
73
- conversationLogger.warn(`createConversation 失败(重试 ${i + 1}/2):err=${(e as Error).message}`);
74
- }
75
- }
76
- }
77
- if (conversationId) set({ conversationId });
78
- get().init();
79
- } catch (err) {
80
- conversationLogger.error('init error:', err);
81
- get().init();
82
- }
83
- },
84
-
85
- // 终态阻断(登录失败/scope 拒绝)后重试:清匿名降级缓存(不清则必复败)→ 拆连接 → 重走完整引导
86
- retryBootstrap: () => {
87
- if (retryBootstrapInFlight) return retryBootstrapInFlight; // 并发重试防抖
88
- const task = (async () => {
89
- authClient.clearAuth(); // 匿名降级结果被缓存,不清则重试必然复败
90
- get().destroy();
91
- await get().initConversation();
92
- })();
93
- const tracked = task.finally(() => {
94
- if (retryBootstrapInFlight === tracked) retryBootstrapInFlight = null;
95
- });
96
- retryBootstrapInFlight = tracked;
97
- return tracked;
98
- },
99
-
100
- // 新建会话:连接复用(clearChat 不断连——连接/身份是身份域),create 拿新会话键
101
- newConversation: async () => {
102
- if (newConversationInFlight) return; // 双击防双建会话
103
- newConversationInFlight = true;
104
- try {
105
- historyEpoch++; // 作废在途历史加载(stale 响应整段丢弃,含 isLoadingHistory 复位)
106
- get().clearChat(); // SDK:内聚取消运行中 run + 清会话态
107
- set({ historyMessages: [], historyHasMore: true, isLoadingHistory: false });
108
- await get().initConversation(); // create 新会话;init 对 OPEN 连接幂等早返
109
- } finally {
110
- newConversationInFlight = false;
111
- }
112
- },
113
-
114
- loadMoreHistory: async () => {
115
- const { isLoadingHistory, historyHasMore, historyMessages } = get();
116
- if (isLoadingHistory || !historyHasMore) return;
117
- const epoch = historyEpoch; // 快照:newConversation 递增后本响应作废
118
- set({ isLoadingHistory: true });
119
-
120
- const pageSize = 5;
121
- const earliestRequestId =
122
- historyMessages.length > 0
123
- ? historyMessages[0].requestId
124
- : get().messages.find((m) => m.requestId)?.requestId;
125
-
126
- // 006:有 token 身份时服务端按 token 取归属;无 token(匿名/兼容)保留 query userId
127
- try {
128
- const { status, data } = await platformApi.listMessages({
129
- pageSize,
130
- ...(authClient.getAuthState()?.token
131
- ? {}
132
- : { userId: get().userId }),
133
- ...(earliestRequestId ? { beforeId: earliestRequestId } : {}),
134
- });
135
- // stale 响应(newConversation 已重置态):整段丢弃——含 isLoadingHistory 复位也不落
136
- if (epoch !== historyEpoch) return;
137
- if (status === 200 && data) {
138
- const rawList: PlatformHistoryMessage[] = (data.data as PlatformHistoryMessage[]) || [];
139
- const currentRequestIds = new Set(get().messages.map((m) => m.requestId).filter(Boolean));
140
- const filtered = rawList.filter(
141
- (item) => !item.outerBusinessId || !currentRequestIds.has(item.outerBusinessId),
142
- );
143
- // SDK 单真源:消息对 + 历史卡片桶同 key 派生(平台 id 字段归一;isHistory 已由服务端标记)
144
- const { messages: newMessages, cardsByMessageId } = convertHistoryMessages(filtered.reverse());
145
- set((s) => ({
146
- historyMessages: [...newMessages, ...s.historyMessages],
147
- historyHasMore: rawList.length >= pageSize,
148
- isLoadingHistory: false,
149
- cardsByMessageId: { ...s.cardsByMessageId, ...cardsByMessageId },
150
- }));
151
- } else {
152
- set({ isLoadingHistory: false });
153
- }
154
- } catch (err) {
155
- historyLogger.error('load more error:', err);
156
- if (epoch !== historyEpoch) return; // stale:newConversation 已重置 isLoadingHistory
157
- set({ isLoadingHistory: false });
158
- }
159
- },
160
-
161
- submitFeedback: async (messageId, type, adapter, refreshSession) => {
162
- const oldMsg = get().messages.find((m) => m.id === messageId);
163
- const oldFeedbackState = oldMsg?.feedbackState ?? 'none';
164
- const feedbackRequestId = oldMsg?.requestId;
165
- if (!feedbackRequestId) {
166
- feedbackLogger.warn('no requestId for message', messageId);
167
- return;
168
- }
169
- const newFeedbackState = type === 'cancel' ? 'none' : type;
170
- get().updateMessage(messageId, { feedbackState: newFeedbackState as 'none' | 'like' | 'dislike' });
171
-
172
- const feedbackMap = { like: 'LIKE' as const, dislike: 'DISLIKE' as const, cancel: 'CANCEL' as const };
173
- try {
174
- const result = await adapter.addTag(feedbackRequestId, feedbackMap[type]);
175
- if (!result.success) get().updateMessage(messageId, { feedbackState: oldFeedbackState });
176
- } catch (err) {
177
- if (isSessionExpiredError(err) && refreshSession) {
178
- try {
179
- const newAdapters = await refreshSession();
180
- if (newAdapters.feedback) {
181
- const retry = await newAdapters.feedback.addTag(feedbackRequestId, feedbackMap[type]);
182
- if (!retry.success) get().updateMessage(messageId, { feedbackState: oldFeedbackState });
183
- return;
184
- }
185
- } catch (retryErr) {
186
- feedbackLogger.error('retry failed:', retryErr);
187
- }
188
- }
189
- get().updateMessage(messageId, { feedbackState: oldFeedbackState });
190
- }
191
- },
33
+ ),
192
34
  }));
193
35
 
194
36
  // 011 L4:HELLO context 采集——mallId 经 URL query 采集(mall 部署;无 mallId 时不携带 context)
37
+ // 停车缴费按用户就近车场查(服务端 #ensureParkingId 用 lbs 反查 cityId→最近车场),
38
+ // 故伴采集实时 lbs(best-effort:H5/无定位权限时静默丢弃,服务端自行降级 plaza)。
39
+ // getter 同步签名,不能现 await getLocation() → 装配期先发起,缓存供 getter 同步读取。
40
+ let cachedLbs: { latitude: string; longitude: string } | undefined;
41
+ void getLocation()
42
+ .then((loc) => {
43
+ if (Number.isFinite(loc.latitude) && Number.isFinite(loc.longitude)) {
44
+ cachedLbs = { latitude: String(loc.latitude), longitude: String(loc.longitude) };
45
+ }
46
+ })
47
+ .catch(() => {
48
+ // 定位不可用(H5 沙箱/未授权)→ 不带 location,服务端走 plaza 降级
49
+ });
195
50
  configureHelloContext(() => {
196
- const mallId = new URLSearchParams(window.location.search).get('mallId');
197
- const miniAppId = new URLSearchParams(window.location.search).get('miniAppId');
51
+ // 读源收敛(CS-8):mallId ?? plazaId 单一 helper(contracts-mall runtime 唯一真源)
52
+ const mallId = readMallIdFromUrl();
53
+ const miniAppId = readMiniAppIdFromUrl();
198
54
  if (!mallId) return undefined;
199
- return { mall: { mallId, ...(miniAppId ? { miniAppId } : {}) } };
55
+ return {
56
+ mall: { mallId, ...(miniAppId ? { miniAppId } : {}) },
57
+ ...(cachedLbs ? { location: cachedLbs } : {}),
58
+ };
200
59
  });
201
60
 
202
61
  // 4002 scope 拒绝 / 4001 鉴权耗尽的终态提示与禁用由 SDK 统一出站(chat-store configureWsFatalCloseHandler)
62
+
63
+ // 凭证升级排水(W2-2 SDK 装配位):生成中排队的升级在 isGenerating true→false 跃迁时执行
64
+ subscribeSessionUpgradeDrain(useChatStore);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * vitest 全局 setup:React act() 环境声明单一真源。
3
+ * 此前散落各用例内联声明、多数漏声明(act 告警刷屏 CI 日志);
4
+ * 统一收敛后新用例自动继承,无需逐文件记忆。node 环境纯逻辑用例设此全局无副作用。
5
+ */
6
+ (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean })
7
+ .IS_REACT_ACT_ENVIRONMENT = true;
@@ -1,3 +1,4 @@
1
+ /// <reference types="vitest" />
1
2
  import { defineConfig, type Plugin } from 'vite';
2
3
  import react from '@vitejs/plugin-react';
3
4
  import pxToRem from 'postcss-pxtorem';
@@ -31,7 +32,9 @@ export default defineConfig(async ({ command }) => {
31
32
  // __dirname 基准 = apps/client:构件区在模板根(上一级×2)
32
33
  const devOverridePath = resolve(__dirname, '../../.tbox-dev.local/vite-override.mjs');
33
34
  const devPlugins: Plugin[] = [];
34
- if (existsSync(devOverridePath)) {
35
+ // 作用域门:override 仅 dev-serve 生效——build/vitest 不受 dev 会话残留影响
36
+ // (G2 防线;门与探针标记同文件,guard 另断言门条件在场)
37
+ if (command === 'serve' && !process.env.VITEST && existsSync(devOverridePath)) {
35
38
  const { default: tboxDevOverride } = await import(pathToFileURL(devOverridePath).href);
36
39
  devPlugins.push(tboxDevOverride());
37
40
  }
@@ -48,6 +51,18 @@ export default defineConfig(async ({ command }) => {
48
51
  fs: resolve(__dirname, 'src/shims/node-stub.ts'),
49
52
  path: resolve(__dirname, 'src/shims/node-stub.ts'),
50
53
  },
54
+ // dev 域 React 单实例(G1/G2 防线):双 dev 入口经本文件合并生效(middleware
55
+ // dev 加载本文件且 command==='serve');build 恒不继承——产物位稳定性。
56
+ ...(command === 'serve' ? { dedupe: ['react', 'react-dom'] } : {}),
57
+ },
58
+ // vitest 并入同一 config(vitest 无专属 config 文件时本就加载本文件):
59
+ // act() 环境声明统一入口——此前散落各用例内联、多数漏声明 → act 告警刷屏 CI 日志。
60
+ // environment 保持每文件 @vitest-environment pragma(纯逻辑用例不付 DOM 开销)。
61
+ test: {
62
+ setupFiles: ['src/test/setup.ts'],
63
+ // W3-2:client 测试随域迁 module-mall-home(页级集成测试 = 模块 tests/index.test.tsx);
64
+ // 模板仅留薄页装配,零测试文件合法(新增客户端测试时移除本旗标)
65
+ passWithNoTests: true,
51
66
  },
52
67
  css: {
53
68
  postcss: { plugins: [pxToRemPlugin] },
@@ -11,19 +11,16 @@
11
11
  "typecheck": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@app/module-weather": "workspace:*",
15
14
  "@tbox.cn/agent-plugin": "catalog:",
16
15
  "@tbox.cn/app-sdk": "catalog:",
17
16
  "@tbox.cn/app-contracts": "catalog:",
18
17
  "@app/contracts-mall": "workspace:*",
19
18
  "@tbox-dev-js/sdk": "catalog:",
20
- "cors": "catalog:",
21
19
  "express": "catalog:",
22
20
  "zod": "catalog:",
23
21
  "@app/provider-custom": "workspace:*"
24
22
  },
25
23
  "devDependencies": {
26
- "@types/cors": "catalog:",
27
24
  "@types/express": "catalog:",
28
25
  "@types/node": "catalog:",
29
26
  "tsx": "catalog:",
@@ -3,9 +3,10 @@ import { translateTool } from './tools/translate.js';
3
3
  import { clockTool } from './tools/clock.js';
4
4
 
5
5
  /**
6
- * apps 级演示工具统一注册面(010:双注册面同步):
6
+ * apps 级工具统一注册面(010:双注册面同步):
7
7
  * start()(运行时)与 cli/collect-registry.ts(doctor/inspect 视图)两处同调本函数,
8
8
  * 未来新增 apps 级工具只改此处,避免两处注册面失同步(R 评审修正)。
9
+ * W3-1:mall-info(含 preTool/缓存)迁 module-mall-core(模块注册面自持)。
9
10
  */
10
11
  export function registerAppTools(ctx: ServerContext): void {
11
12
  ctx.tools.register(translateTool);