@tbox.cn/app-template-agent 0.7.5 → 0.8.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 +102 -17
  5. package/README.md +36 -34
  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 +3 -3
  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 -1
  21. package/apps/server/src/app-tools.ts +2 -1
  22. package/apps/server/src/app.ts +178 -110
  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 -13
  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 -15
  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 -83
  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,14 +1,14 @@
1
1
  /**
2
- * integrations v4 装载链单测(deployment 派生自 integrations——单文件真源;base 精简面)。
3
- * 覆盖:投影穿透(attributes 存活)/ 无参考表 → 空平面 + defaultInstanceId 独立在场 /
4
- * stale 防线 / buildDefaultMallContext mallId-only 注入 / deploymentFromIntegrations 装配活性。
2
+ * integrations 装载链单测(W3-1 收窄:控制平面投影/域装配段随 mall 语义迁
3
+ * modules/module-mall-core/tests/assembly.test.ts——本文件只测模板装载壳)。
4
+ * 覆盖:strict 校验 fail-fast / 占位展开(env 通道)/ defaultInstanceId 引用完整性 /
5
+ * stale 防线(deployments/development.json 含 malls → error 迁移指引)。
5
6
  */
6
7
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
7
8
  import { tmpdir } from 'node:os';
8
9
  import { join } from 'node:path';
9
10
  import { afterAll, describe, expect, it } from 'vitest';
10
11
  import { loadIntegrations, assertNoStaleDeploymentControlPlane } from '../src/config/integration.js';
11
- import { deploymentFromIntegrations, buildDefaultMallContext } from '../src/config/deployment.js';
12
12
 
13
13
  const dir = mkdtempSync(join(tmpdir(), 'tbox-deploy-loader-'));
14
14
  afterAll(() => rmSync(dir, { recursive: true, force: true }));
@@ -19,6 +19,7 @@ function writeConfig(name: string, data: unknown): string {
19
19
  return p;
20
20
  }
21
21
 
22
+ /** 模拟 real-env 素材形态:root instances 注册表 + defaultInstanceId + services */
22
23
  const fullIntegrations = {
23
24
  provider: 'acme',
24
25
  instances: [
@@ -36,39 +37,29 @@ const fullIntegrations = {
36
37
  services: { 'member.account': {} },
37
38
  };
38
39
 
39
- describe('loadIntegrations:投影穿透', () => {
40
- it('contactPhone/location/miniAppId 经投影存活到 controlPlane(不被 zod strip)', () => {
40
+ describe('loadIntegrations:装载链(strict + 占位展开 + 缺席语义)', () => {
41
+ it('全量素材可载入(config 停留原样;投影消费方 = module-mall-core)', () => {
41
42
  const loaded = loadIntegrations(writeConfig('full.json', fullIntegrations));
42
- expect(loaded?.controlPlane.malls[0]).toMatchObject({
43
- mallId: 'mall-demo',
44
- name: '示例商场',
45
- miniAppId: 'demo-app',
46
- contactPhone: '0571-88888888',
47
- location: { lat: 30.2741, lon: 120.1551, address: '湖滨示范点' },
48
- });
49
- expect(loaded?.controlPlane.defaultMallId).toBe('mall-demo');
43
+ expect(loaded?.config.defaultInstanceId).toBe('mall-demo');
44
+ expect(loaded?.source.endsWith('full.json')).toBe(true);
50
45
  });
51
46
 
52
- it('拼错 attributes 键 → fail-fast(strict——报错含实例 id 与允许键清单)', () => {
47
+ it('attributes 严格校验归投影(W3-1:loader 不过滤——拼错键由 mall-core register 期 fail-fast)', () => {
53
48
  const p = writeConfig('typo-attr.json', {
54
49
  instances: [{ id: 'm1', name: 'x', attributes: { miniAppid: 'typo' } }],
55
50
  services: {},
56
51
  });
57
- expect(() => loadIntegrations(p)).toThrow(/m1.*允许键|允许键.*miniAppId/s);
58
- });
59
-
60
- it('无参考表 → 空平面;defaultInstanceId 独立在场 = 动态默认(env 注入形态)', () => {
61
- const loaded = loadIntegrations(writeConfig('no-table.json', { provider: 'globex', defaultInstanceId: '42020300520', services: {} }));
62
- expect(loaded?.controlPlane.malls).toEqual([]);
63
- expect(loaded?.controlPlane.defaultMallId).toBe('42020300520');
52
+ // 装载壳只做展开+parse;投影(含允许键清单 strict)在 module-mall-core 装配期
53
+ // (module tests/assembly.test.ts 覆盖 fail-fast 语义——启动链同点报错)
54
+ expect(loadIntegrations(p)?.config.instances?.length).toBe(1);
64
55
  });
65
56
 
66
- it('defaultInstanceId 占位 ${DEFAULT_MALL_ID} → 展开后载入;缺 env fail-fast', () => {
57
+ it('defaultInstanceId 占位 ${DEFAULT_MALL_ID} → loader 展开后载入;缺 env fail-fast', () => {
67
58
  const p = writeConfig('env-placeholder.json', { defaultInstanceId: '${DEFAULT_MALL_ID}', services: {} });
68
59
  const saved = process.env.DEFAULT_MALL_ID;
69
60
  process.env.DEFAULT_MALL_ID = '1102588';
70
61
  try {
71
- expect(loadIntegrations(p)?.controlPlane.defaultMallId).toBe('1102588');
62
+ expect(loadIntegrations(p)?.config.defaultInstanceId).toBe('1102588');
72
63
  delete process.env.DEFAULT_MALL_ID;
73
64
  expect(() => loadIntegrations(p)).toThrow(/DEFAULT_MALL_ID/);
74
65
  } finally {
@@ -76,6 +67,19 @@ describe('loadIntegrations:投影穿透', () => {
76
67
  else process.env.DEFAULT_MALL_ID = saved;
77
68
  }
78
69
  });
70
+
71
+ it('defaultInstanceId ∉ instances ids → zod 拒载(SDK 单源防线)', () => {
72
+ const p = writeConfig('bad-default.json', {
73
+ instances: [{ id: 'm1', name: 'x' }],
74
+ defaultInstanceId: '99999',
75
+ services: {},
76
+ });
77
+ expect(() => loadIntegrations(p)).toThrow(/defaultInstanceId/);
78
+ });
79
+
80
+ it('文件缺席 → undefined(槽位解析不启用)', () => {
81
+ expect(loadIntegrations(join(dir, 'absent.json'))).toBeUndefined();
82
+ });
79
83
  });
80
84
 
81
85
  describe('stale 防线(v4:deployments/development.json 含 malls → error 迁移指引)', () => {
@@ -103,38 +107,13 @@ describe('stale 防线(v4:deployments/development.json 含 malls → error
103
107
  rmSync(root, { recursive: true, force: true });
104
108
  }
105
109
  });
106
- });
107
110
 
108
- describe('buildDefaultMallContext:v4 mallId-only 注入', () => {
109
- it('defaultMallId + miniAppId 齐备 → 拼装完整 context;root 优先', () => {
110
- const loaded = loadIntegrations(writeConfig('ctx-ok.json', fullIntegrations))!;
111
- expect(buildDefaultMallContext(loaded.controlPlane, 'root-app')).toEqual({
112
- mall: { mallId: 'mall-demo', miniAppId: 'root-app' },
113
- });
114
- expect(buildDefaultMallContext(loaded.controlPlane)).toEqual({
115
- mall: { mallId: 'mall-demo', miniAppId: 'demo-app' },
116
- });
117
- });
118
-
119
- it('双缺 miniAppId → mallId-only 注入(v4:不再 warn-refuse 静默失效)', () => {
120
- const loaded = loadIntegrations(writeConfig('ctx-nomini.json', { defaultInstanceId: 'm9', services: {} }))!;
121
- expect(buildDefaultMallContext(loaded.controlPlane)).toEqual({ mall: { mallId: 'm9' } });
122
- });
123
-
124
- it('无 defaultMallId → undefined(不兜底)', () => {
125
- const loaded = loadIntegrations(writeConfig('ctx-nodefault.json', { services: {} }))!;
126
- expect(buildDefaultMallContext(loaded.controlPlane)).toBeUndefined();
127
- });
128
- });
129
-
130
- describe('deploymentFromIntegrations:装配活性', () => {
131
- it('assembly 三函数派生自投影控制平面', () => {
132
- const loaded = loadIntegrations(writeConfig('asm.json', fullIntegrations))!;
133
- const d = deploymentFromIntegrations(loaded);
134
- expect(d.controlPlane).toBe(loaded.controlPlane);
135
- expect(d.assembly.validateScope({ mall: { mallId: 'mall-demo' } })).toBe(true);
136
- expect(d.assembly.validateScope({ mall: { miniAppId: 'a' } })).toBe(false);
137
- expect(d.assembly.extractScopeKey({ identity: { userId: 'u', source: 'external' }, attributes: { mall: { mallId: 'mall-demo' } } })).toBe('mall-demo');
138
- expect(d.assembly.buildScope({ mall: { mallId: 'mall-demo' } })).toEqual({ mallId: 'mall-demo' });
111
+ it('文件缺席 → 不阻断', () => {
112
+ const root = mkdtempSync(join(tmpdir(), 'tbox-stale-absent-'));
113
+ try {
114
+ expect(() => assertNoStaleDeploymentControlPlane(root)).not.toThrow();
115
+ } finally {
116
+ rmSync(root, { recursive: true, force: true });
117
+ }
139
118
  });
140
119
  });
@@ -10,7 +10,7 @@ import { registerAll } from '../src/modules.js';
10
10
  import { translateTool } from '../src/tools/translate.js';
11
11
 
12
12
  describe('tool runtime 宿主装配(自注册闭环)', () => {
13
- it('合并模块工具(v2:无 cardResolver——出卡由工具出参 cards 运行期声明)', async () => {
13
+ it('空装配(mall 模板):无模块工具,app 级工具正常注册', async () => {
14
14
  const ctx = createServerContext();
15
15
  await registerAll(ctx);
16
16
 
@@ -18,31 +18,13 @@ describe('tool runtime 宿主装配(自注册闭环)', () => {
18
18
  loadPlugins: async () => ({ tools: {}, references: [] }),
19
19
  });
20
20
 
21
- expect(Object.keys(runtime.tools)).toEqual(
21
+ // mall 模板空装配:模块工具(如 weather 系)不存在,仅 app 级工具
22
+ expect(Object.keys(runtime.tools)).not.toEqual(
22
23
  expect.arrayContaining(['getWeather']),
23
24
  );
24
25
  });
25
26
 
26
- it('weather 参考实现:execute 出参携带 cards 声明(顶层答话事实 + cards 数组)', async () => {
27
- const ctx = createServerContext();
28
- await registerAll(ctx);
29
-
30
- const runtime = await createSdkToolRuntime(ctx, {
31
- loadPlugins: async () => ({ tools: {}, references: [] }),
32
- });
33
- const weather = runtime.tools.getWeather as unknown as {
34
- execute: (input: { city: string }, context: unknown) => Promise<Record<string, unknown>>;
35
- };
36
- const out = await weather.execute({ city: '杭州' }, {} as never);
37
- // 顶层答话事实
38
- expect(out).toMatchObject({ city: '杭州', temp: expect.any(Number), weather: expect.any(String) });
39
- // cards 出卡声明(capture 前原始形态:data = 顶层事实本体,不含 cards 键自身)
40
- // W7 note 契约:主体+关键事实(城市/天气/温度)
41
- const { cards, ...facts } = out;
42
- expect(cards).toEqual([{ cardType: 'weather', data: facts, note: `杭州 ${facts.weather} ${facts.temp}℃` }]);
43
- });
44
-
45
- it('未注册的 tool 不进运行时工具集(仅作为模型上下文面之外)', async () => {
27
+ it('未注册的 tool 不进运行时工具集(v2:无 cardResolver——出卡由工具出参 cards 运行期声明)', async () => {
46
28
  const ctx = createServerContext();
47
29
  await registerAll(ctx);
48
30
 
@@ -81,45 +63,30 @@ describe('tool runtime 宿主装配(自注册闭环)', () => {
81
63
  });
82
64
  });
83
65
 
84
- describe('长期记忆装配(017:默认 inmemory 开启 + off 逃生舱)', () => {
66
+ describe('长期记忆装配(017:mall 域默认 inmemory + mallId 隔离 scope)', () => {
85
67
  it('resolveMemoryStore 缺省 → inmemory(默认开启)', () => {
86
68
  const m = resolveMemoryStore({});
87
69
  expect(m.enabled).toBe(true);
88
70
  expect(m.mode).toBe('inmemory');
89
71
  expect(m.store).toBeDefined();
90
- expect(m.store!.capabilities).toBeUndefined(); // 缺省全真
91
72
  });
92
73
 
93
- it('resolveMemoryStore off → 关闭(enabled=false)', () => {
74
+ it('resolveMemoryStore off → 关闭(enabled=false,路由面与工具零装配)', () => {
94
75
  const m = resolveMemoryStore({ store: 'off' });
95
76
  expect(m.enabled).toBe(false);
96
77
  expect(m.mode).toBe('off');
97
78
  expect(m.store).toBeUndefined();
98
79
  });
99
80
 
100
- it('resolveMemoryStore knowledge 无 kbId → warn 回退 inmemory', () => {
101
- const m = resolveMemoryStore({ store: 'knowledge' });
102
- expect(m.enabled).toBe(true);
103
- expect(m.mode).toBe('inmemory');
104
- });
105
-
106
- it('resolveMemoryStore knowledge 三件套齐备 → knowledge 模式(2026-09-01 翻转:caps 缺省全 true——平台已发布生产)', () => {
81
+ it('resolveMemoryStore knowledge 三缺一回退;齐备 → knowledge(caps 缺省全 true——2026-09-01 翻转)', () => {
82
+ expect(resolveMemoryStore({ store: 'knowledge' }).mode).toBe('inmemory');
83
+ expect(resolveMemoryStore({ store: 'knowledge', kbId: 'kb-1', apiKey: 'k' }).mode).toBe('inmemory');
107
84
  const m = resolveMemoryStore({ store: 'knowledge', kbId: 'kb-1', kbDocId: 'doc-1', apiKey: 'k' });
108
85
  expect(m.mode).toBe('knowledge');
109
86
  expect(m.store!.capabilities).toEqual({ save: true, search: true, list: true, delete: true });
110
87
  });
111
88
 
112
- it('resolveMemoryStore knowledge 缺 kbDocId → warn 回退 inmemory(三缺一)', () => {
113
- const m = resolveMemoryStore({ store: 'knowledge', kbId: 'kb-1', apiKey: 'k' });
114
- expect(m.mode).toBe('inmemory');
115
- });
116
-
117
- it('resolveMemoryStore 非法值 → warn 回退 inmemory', () => {
118
- const m = resolveMemoryStore({ store: 'bogus' });
119
- expect(m.mode).toBe('inmemory');
120
- });
121
-
122
- it('装配态(inmemory)→ 引擎装配链路无异常(chat 完成 + 本地 skills 不受影响)', async () => {
89
+ it('装配态(inmemory)→ 引擎装配链路无异常 + 本地 skills 不受影响', async () => {
123
90
  const ctx = createServerContext();
124
91
  await registerAll(ctx);
125
92
  const engine = await createEngineFromContext(ctx, {
@@ -127,25 +94,22 @@ describe('长期记忆装配(017:默认 inmemory 开启 + off 逃生舱)',
127
94
  model: {} as never,
128
95
  memory: { store: createInMemoryMemoryStore() },
129
96
  });
130
- // 触发一轮 chat 使装配链路完整执行(无 runCtx → 匿名 → 零 recall)
131
- await engine.chat('t-mem', 'hi', () => {});
132
- // 工具名断言在 SDK build-engine 测试覆盖;模板层断言装配链路无异常 + skills 面不受影响
133
- expect(engine.listSkills().length).toBeGreaterThan(0);
97
+ // 触发一轮 chat 使装配链路完整执行(无 runCtx → 匿名 → 零 recall)。
98
+ // skills/preTools 面不做形状断言:模板源空装配为 [],生成应用(module add 后)非空——
99
+ // 属装配环境差异而非记忆特性;本用例只锁记忆装配链路无异常(chat 完成即通过)。
100
+ await engine.chat('t-mem-mall', 'hi', () => {});
134
101
  });
135
102
 
136
- it('off 态 → 引擎无记忆工具装配(零异常 + 零工具)', async () => {
137
- const ctx = createServerContext();
138
- await registerAll(ctx);
139
- const m = resolveMemoryStore({ store: 'off' });
140
- const engine = await createEngineFromContext(
141
- ctx,
142
- {
143
- instructions: 'test',
144
- model: {} as never,
145
- ...(m.enabled && m.store ? { memory: { store: m.store } } : {}),
146
- },
147
- );
148
- await engine.chat('t-mem-off', 'hi', () => {});
149
- expect(m.enabled).toBe(false);
103
+ it('mall 域 resolveScope 语义:attributes.mall → { mallId };无 mall → undefined(用户级全局)', () => {
104
+ // resolveScope 闭包形态对齐模板装配(buildScope 单点);此处按语义直测:
105
+ // 模板内 resolveScope = buildScope(attributes) 取 mallId 单键
106
+ const resolveScope = (attributes: Record<string, unknown>) => {
107
+ const raw = attributes?.mall as { mallId?: unknown } | undefined;
108
+ const mallId = typeof raw?.mallId === 'string' && raw.mallId ? raw.mallId : undefined;
109
+ return mallId ? { mallId } : undefined;
110
+ };
111
+ expect(resolveScope({ mall: { mallId: 'mall-demo', miniAppId: 'demo-app' } })).toEqual({ mallId: 'mall-demo' });
112
+ expect(resolveScope({})).toBeUndefined();
113
+ expect(resolveScope({ mall: { mallId: '' } })).toBeUndefined();
150
114
  });
151
115
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tbox.cn/app-template-agent",
3
- "version": "0.7.5",
4
- "description": "tbox AI Agent 全栈模板(多包 pnpm workspace):apps/client + apps/server + packages/module-*。客户端同时跑在浏览器与支付宝小程序 web-view 壳里。",
3
+ "version": "0.8.0",
4
+ "description": "tbox AI Agent 全栈模板(多包 pnpm workspace):apps/client + apps/server。唯一模板(W4 单一模板统一化:缺省空装配 chat 形态;mall 能力经 profiles/mall 加装),能力经 tbox-app module add 模块扩展(shopping-guide/member 等)。",
5
5
  "engines": {
6
6
  "node": ">=24.0.0 <25.0.0"
7
7
  },
@@ -13,6 +13,7 @@
13
13
  "!apps/**/node_modules",
14
14
  "!apps/**/*.tsbuildinfo",
15
15
  "config",
16
+ "profiles",
16
17
  "packages",
17
18
  "!packages/**/node_modules",
18
19
  "!packages/**/*.tsbuildinfo",
@@ -31,7 +32,7 @@
31
32
  "LICENSE"
32
33
  ],
33
34
  "devDependencies": {
34
- "@tbox.cn/app-sdk": "0.18.0",
35
+ "@tbox.cn/app-sdk": "0.19.0",
35
36
  "eslint": "9.39.5",
36
37
  "typescript-eslint": "8.67.0",
37
38
  "eslint-plugin-react-hooks": "5.2.0",
@@ -47,7 +48,7 @@
47
48
  "build:client": "pnpm check && pnpm --filter ./apps/client build",
48
49
  "typecheck": "pnpm -r --filter \"./{apps,packages}/*\" typecheck",
49
50
  "test": "pnpm -r --filter \"./{apps,packages}/*\" test",
50
- "lint": "eslint apps packages",
51
+ "lint": "eslint apps $(find packages -mindepth 1 -maxdepth 1 -type d -print -quit 2>/dev/null | grep -q . && echo packages)",
51
52
  "verify": "pnpm typecheck && pnpm test && pnpm lint && pnpm build && pnpm verify:generated && pnpm check:inspector",
52
53
  "verify:generated": "node scripts/verify-generated.mjs",
53
54
  "check": "pnpm check:workspace && pnpm check:inspector",
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tbox.cn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ # @app/agent-ui-kit
2
+
3
+ Agent 对话壳 UI 组件包(源码发布)——单一模板统一化 W2-1 自 mall 模板抽取。
4
+
5
+ - **真源**:本包(`packages/agent-ui-kit`)。模板出厂副本经 `scripts/sync-agent-ui-kit.mjs` 派生
6
+ (name 改写 `@app/agent-ui-kit` → `@app/agent-ui-kit`),guard identity 锁定一致。
7
+ - **消费面**:应用 `<ChatPage store={useChatStore} cards={CHAT_REGISTRY} slots={clientCtx.slots}/>`
8
+ 单点装配;域 UI(如商圈主屏)经 home 槽渲染函数自带。
9
+ - **主题**:kit 默认 tokens(`src/theme/tokens.less`);品牌资产包经 `html[data-theme]` 级联覆写 +
10
+ 接线三行(`import '<pkg>/index.css'` + `import '<pkg>/brand.js'`[可选] + `applyTheme(id)`;见 docs/reference/templates.md)。
11
+
12
+ ## 验证
13
+
14
+ ```sh
15
+ pnpm --filter @app/agent-ui-kit typecheck
16
+ pnpm --filter @app/agent-ui-kit test
17
+ ```
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@app/agent-ui-kit",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Agent 对话壳 UI 组件包(源码发布,单一模板统一化 W2-1):ChatPage/壳组件族/主题 tokens。",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "import": "./src/index.ts"
10
+ },
11
+ "./theme": {
12
+ "types": "./src/theme/theme.ts",
13
+ "import": "./src/theme/theme.ts"
14
+ },
15
+ "./package.json": "./package.json",
16
+ "./theme/global.less": "./src/theme/global.less",
17
+ "./theme/contract": "./src/theme/contract.json"
18
+ },
19
+ "scripts": {
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "vitest run"
22
+ },
23
+ "files": [
24
+ "src",
25
+ "assets",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "dependencies": {
30
+ "@tbox.cn/app-sdk": "catalog:",
31
+ "@tbox.cn/app-contracts": "catalog:",
32
+ "@phosphor-icons/react": "catalog:",
33
+ "antd-mobile": "catalog:",
34
+ "react-markdown": "catalog:",
35
+ "remark-gfm": "catalog:",
36
+ "zustand": "catalog:"
37
+ },
38
+ "peerDependencies": {
39
+ "react": "catalog:",
40
+ "react-dom": "catalog:"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "catalog:",
44
+ "@types/react": "catalog:",
45
+ "@types/react-dom": "catalog:",
46
+ "happy-dom": "catalog:",
47
+ "less": "catalog:",
48
+ "react": "catalog:",
49
+ "react-dom": "catalog:",
50
+ "typescript": "catalog:",
51
+ "vite": "catalog:",
52
+ "vitest": "catalog:"
53
+ },
54
+ "engines": {
55
+ "node": ">=20.0.0"
56
+ },
57
+ "license": "MIT"
58
+ }
@@ -0,0 +1,62 @@
1
+ .chat-page {
2
+ position: relative;
3
+ display: flex;
4
+ flex-direction: column;
5
+ width: 100%;
6
+ height: 100vh;
7
+ height: 100dvh;
8
+ min-height: 0;
9
+ overflow: hidden;
10
+ background: var(--tbox-bg-page);
11
+
12
+ &__bg {
13
+ position: absolute;
14
+ inset: 0;
15
+ z-index: 0;
16
+ background: var(--tbox-bg-page);
17
+ pointer-events: none;
18
+ }
19
+
20
+ &__body-wrapper {
21
+ position: relative;
22
+ z-index: 1;
23
+ display: flex;
24
+ flex: 1;
25
+ flex-direction: column;
26
+ min-height: 0;
27
+ }
28
+
29
+ &__body {
30
+ flex: 1;
31
+ width: 100%;
32
+ max-width: var(--tbox-content-max-width);
33
+ min-height: 0;
34
+ margin: 0 auto;
35
+ padding: calc(12PX + env(safe-area-inset-top, 0PX)) 0 24PX;
36
+ overflow-y: auto;
37
+ overflow-x: hidden;
38
+ overflow-anchor: none;
39
+ overscroll-behavior: contain;
40
+ scrollbar-width: none;
41
+ -webkit-overflow-scrolling: touch;
42
+
43
+ &::-webkit-scrollbar {
44
+ display: none;
45
+ }
46
+ }
47
+
48
+ &__orphan-card {
49
+ margin-right: var(--tbox-page-gutter);
50
+ margin-left: var(--tbox-page-gutter);
51
+ }
52
+
53
+ /* 顶部历史入口在场时欢迎区不再叠加自身顶部留白:胶囊到标题区留 4PX(= 欢迎区自带上边距)。 */
54
+ .history-entry ~ .mall-home {
55
+ padding-top: 0;
56
+ }
57
+
58
+ /* 对话开始后首页仍保留,消息接在联单主卡下方。 */
59
+ &--conversation &__body {
60
+ padding-top: calc(12PX + env(safe-area-inset-top, 0PX));
61
+ }
62
+ }