@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
@@ -4,14 +4,12 @@
4
4
  * import 本模块即完成装配(与旧单文件入口时序等价:listen 仍在 start() 内最后发生)。
5
5
  */
6
6
  import express from 'express';
7
- import cors from 'cors';
8
7
  import { createServer, type IncomingMessage } from 'http';
9
8
  import type { Duplex } from 'node:stream';
10
9
  import path from 'path';
11
10
  import { existsSync } from 'node:fs';
12
11
  import { fileURLToPath } from 'url';
13
12
  import { registerAll } from './modules.js';
14
-
15
13
  // ===== 模板日志(@tbox.cn/app-sdk/server createLogger:本地时间 + LEVEL + [tag] + req/conv 尾缀) =====
16
14
  import { createLogger } from '@tbox.cn/app-sdk/server';
17
15
  const serverLogger = createLogger('server');
@@ -24,6 +22,7 @@ import {
24
22
  createWsServer,
25
23
  createServerContext,
26
24
  createEngineFromContext,
25
+ createIntegrationsRegistry,
27
26
  loadProviderCatalog,
28
27
  tboxTraceExporter,
29
28
  extractImageUrls,
@@ -51,26 +50,35 @@ import { installInspector } from '@tbox.cn/app-sdk/platform';
51
50
  import { registerAppTools } from './app-tools.js';
52
51
  import { APP_PRE_TOOLS } from './pre-tools.js';
53
52
  import { INTENT_ROUTES, createAppIntentDispatcher } from './intent-routes.js';
54
- import { deploymentFromIntegrations, buildDefaultMallContext } from './config/deployment.js';
55
53
  import { loadIntegrations } from './config/integration.js';
54
+ // W3-1/W4:mall 域语义(部署投影/scope 校验/默认值/instanceIdOf/mall-directory)归 module-mall-core,
55
+ // 经 ctx.domainAssemblies 注册 + 运行期惰性查表——本文件按 TIER1 契约类型(MallScopeAssembly)
56
+ // 泛型断言读取,零 mall-core 包 import(workspace-dag:应用消费模块经注册表 + 契约)
57
+ import { SCOPE_ASSEMBLY_KIND } from '@tbox.cn/app-contracts';
58
+ import type { MallScopeAssembly } from '@app/contracts-mall';
56
59
  // 011 S2/S3:mall 装配原语(条件装配——未配置部署文件时零引用)
57
60
  import {
58
61
  createScopeSessionRegistry,
59
62
  createActionGrantService,
60
63
  createModuleActionDispatcher,
61
64
  createEffectRuntime,
62
- createIntegrationsRegistry,
63
65
  createAuthStrategyRegistry,
64
66
  createEnvironmentSecretProvider,
65
67
  attachCardActionTokens,
66
68
  } from '@tbox.cn/app-sdk/server';
67
69
 
68
70
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
69
- // chat.json 配置目录(credentials 同款锚定三元式):dev 态 TBOX_CONFIG_DIR 指档位目录,
70
- // 否则应用根 config/。src|dist → server → apps → 应用根(与 mall entry 锚点同构)。
71
+
72
+ // ── 应用级路径唯一锚点区 ──────────────────────────────────────────────
73
+ // entry 即 bundle 实文件:src/dist 平级,dev/prod 恒等。被 tsup 内联的模块禁止
74
+ // 基于自身 import.meta.url 做层级解析(bundle 后层级漂移——config 曾因此解析到
75
+ // 应用根上一级)。同族锚点:../skills、../../client/dist。
76
+ // TBOX_CONFIG_DIR 为 tbox-app dev 的配置面注入锚点(dev 探针常量,guard 守护):
77
+ // 注入时为 real-env 档位目录绝对路径;未注入维持默认相对解析。
71
78
  const CONFIG_DIR = process.env.TBOX_CONFIG_DIR
72
79
  ? path.resolve(process.env.TBOX_CONFIG_DIR)
73
- : path.resolve(__dirname, '../../../config');
80
+ : path.resolve(__dirname, '../../../config'); // src|dist → server → apps → 应用根
81
+
74
82
  const PORT = Number(process.env.PORT) || 8000;
75
83
  const HOST = process.env.HOST || '0.0.0.0';
76
84
  const isProduction = process.env.NODE_ENV === 'production';
@@ -81,6 +89,58 @@ const toolDebug = resolveToolDebug(process.env.TBOX_TOOL_DEBUG, process.env.NODE
81
89
  // 收益依赖 config/llm_provider.json parallelToolCalls 开启;单工具轮行为等价现状
82
90
  const earlyToolExecution = resolveEarlyToolExecution(process.env.TBOX_EARLY_TOOL_EXECUTION);
83
91
 
92
+ // 017:长期记忆(与 base 同源三件套:默认 inmemory;off 逃生舱;knowledge 需 KB_ID)。
93
+ // mall 域措辞段仅装配态拼接(死能力零广告 L1;构造期常量 → 前缀缓存稳定)。
94
+ // 记忆隔离:resolveScope 经域装配 buildScope 单点(mallId 命名空间;W3-1 查表)——
95
+ // 多 mall 单实例下互不可见;未装配控制面/无 mall 上下文 → 用户级全局记忆。
96
+ // 017 M4:KB 校验三态(TBOX_MEMORY_KB_VALIDATE:1=异步降级不阻塞启动 / strict=同步 fail-fast /
97
+ // 未设置不校验)——gated store 统一收口,装配面恒定(INV-D),降级只发生在调用执行时。
98
+ const memoryBase = await resolveMemoryStoreGuarded({
99
+ store: process.env.TBOX_MEMORY_STORE,
100
+ kbId: process.env.TBOX_MEMORY_KB_ID,
101
+ kbDocId: process.env.TBOX_MEMORY_KB_DOC_ID,
102
+ kbBaseUrl: process.env.TBOX_MEMORY_KB_BASE_URL,
103
+ kbCookie: process.env.TBOX_MEMORY_KB_COOKIE,
104
+ validate: process.env.TBOX_MEMORY_KB_VALIDATE,
105
+ validateTimeoutMs: process.env.TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS,
106
+ });
107
+ // B4:recall 缓存装饰器(同 base 模板——组合序 withRecallCache 包 gated 外层;
108
+ // 单实例双消费共享 wrapped 实例,save 写失效钩子生效)
109
+ const memory = {
110
+ ...memoryBase,
111
+ ...(memoryBase.store ? { store: withRecallCache(memoryBase.store) } : {}),
112
+ };
113
+ // agent 提示词(config/agent.jsonc;未配置 = SDK 默认兜底;memoryInstructions 装配条件归 SDK,
114
+ // 模板无条件透传——读取一次,改文件需重启生效)
115
+ const AGENT_PROMPT = loadAgentPromptConfig(CONFIG_DIR);
116
+
117
+ // 平台能力装配(003/006 平台轴,已下沉 @tbox.cn/app-sdk/server)
118
+ // 011 L1 + F2/F3 + W3-1:装配序 integrations → authStrategies → auth → catalog → ctx——
119
+ // v4:部署拓扑并入 integrations.json(单文件真源);域投影/校验(deployment 语义)迁
120
+ // module-mall-core(ctx.domainAssemblies 惰性查表——auth 构造先于装配,动态判定经闭包后补)。
121
+ // auth 提前构造(登录换码器由 provider 包 register 期经 ctx.authExchanges.register 自供给;
122
+ // 注册表持引用惰性查找,register 晚于构造仍生效);
123
+ // catalog 无条件装载(integrations 槽位解析唯一真源 = config/integrations.json)
124
+ // 011 B2:可变认证策略注册表——模块 register 期注册(厂商签名策略),transportFactory 同引用
125
+ const authStrategies = createAuthStrategyRegistry();
126
+ const integrationsConfig = loadIntegrations();
127
+
128
+ /**
129
+ * 域装配查表(W3-1):module-mall-core register 期注册;全部消费点运行期惰性读取
130
+ * (auth/registry 构造早于 ctx 与 registerAll——查表时刻 ∈ listen 后请求/会话链路,装配必已就位)。
131
+ * 无装配(无 integrations.json / 模块未装)→ undefined,各消费点按"无控制面"降级(语义对齐迁移前)。
132
+ */
133
+ function mallAssembly(): MallScopeAssembly | undefined {
134
+ return ctx.domainAssemblies.resolve<MallScopeAssembly>(SCOPE_ASSEMBLY_KIND);
135
+ }
136
+
137
+ // 上下文统一化 Stage 2:validateContext 域校验注入(HTTP body.context 伪冒防线:
138
+ // mallId 形状校验(v4 恒动态)→ 403;只在全局面生效——login 走 login-routes 独立归一化)。
139
+ // 与 WS validateScope 同核(isValidMallShape);无装配 → true(非 mall 部署放行)。
140
+ const auth = createAuthRuntime(undefined, {
141
+ validateContext: (attributes) => mallAssembly()?.validateScope(attributes) ?? true,
142
+ });
143
+
84
144
  /** 应用根探测(apps/server cwd → 逐层向上找 .tbox/app.json;dev/bundle 双态收敛) */
85
145
  function appRoot(): string {
86
146
  let dir = path.resolve(process.cwd());
@@ -93,45 +153,38 @@ function appRoot(): string {
93
153
  return path.resolve(process.cwd());
94
154
  }
95
155
 
96
- const AI_MODEL_NAME = process.env.APP_AI_MODEL_NAME || '';
156
+ const providerCatalog = loadProviderCatalog(appRoot());
97
157
 
98
- // agent 提示词(config/agent.jsonc;未配置 = SDK 默认兜底;读取一次,改文件需重启生效)
99
- const AGENT_PROMPT = loadAgentPromptConfig(CONFIG_DIR);
158
+ // provider 拆包 F2/F3:integrations 槽位解析轴(config/integrations.json 存在即生效;
159
+ // 缺席 → undefined,模块槽解析与换码链 fail-fast 显性暴露——旧 ProviderRegistry 链已拆除)
160
+ // instanceIdOf 注入 mall 语义(W3-1:沿域装配查表——mallId 派生归 module-mall-core)
161
+ const integrations = integrationsConfig
162
+ ? createIntegrationsRegistry({
163
+ integrations: integrationsConfig.config,
164
+ // catalog 真源 = 应用根 .tbox/app.json(上方无条件装载)
165
+ catalog: providerCatalog,
166
+ authStrategies,
167
+ secretProvider: createEnvironmentSecretProvider({
168
+ source: process.env as Record<string, string | undefined>,
169
+ // 文件通道(v2):config/credentials/ 凭据文件优先(ref = 文件名 stem 直引),env 兜底。
170
+ files: path.resolve(CONFIG_DIR, 'credentials'),
171
+ allowedHeaderNames: ['content-type', 'sign', 'timestamp', 'publickey', 'x-api-key'],
172
+ production: isProduction,
173
+ }),
174
+ instanceIdOf: (scope) => mallAssembly()?.instanceIdOf(scope) ?? '',
175
+ // F5 观测轴:provider factory 收到的 transport 统一包外部调用日志(→/← path durMs)
176
+ logger: createLogger('provider'),
177
+ })
178
+ : undefined;
100
179
 
101
- // 平台能力装配(003/006 平台轴,已下沉 @tbox.cn/app-sdk/server)。
102
- // 011 L1 + F2/F3:装配序 integrations → deployment 派生 → auth(v4:部署拓扑并入
103
- // integrations.json 单文件真源——integrations 先装,deployment 由其投影派生;auth 构造期
104
- // 直引 validateContext)。上下文统一化 Stage 2:auth 的 validateContext 域校验注入(HTTP
105
- // body.context 伪冒防线:mallId 形状校验 → 403)与 scopeDefaults 同源。
106
- const integrationsConfig = loadIntegrations();
107
- const deployment = integrationsConfig ? deploymentFromIntegrations(integrationsConfig) : undefined;
108
- const auth = createAuthRuntime(undefined, {
109
- // validateContext 只在全局面生效(login/dev-login 的 body.context 走 login-routes 独立归一化)
110
- ...(deployment ? { validateContext: deployment.assembly.validateScope } : {}),
180
+ // LLM 装配(模型名单点:configPath 入列 CONFIG_DIR 注入家族——dev 档位/生成应用 config 同锚):
181
+ // resolveModelName 与 getModel 同链解析(config modelName ?? env),记录名与实际请求恒一致。
182
+ const { getModel, resolveModelName } = createModelProvider({
183
+ configPath: path.join(CONFIG_DIR, 'llm_provider.json'),
111
184
  });
112
- const { getModel } = createModelProvider();
113
- const pluginTools = createPluginIntegration();
185
+ const AI_MODEL_NAME = resolveModelName();
114
186
 
115
- // 017:长期记忆(默认 inmemory 开启;TBOX_MEMORY_STORE=off 逃生舱 / knowledge 需 KB_ID+DOC_ID+API_KEY)。
116
- // 单实例双消费:engine(recall + 三工具)与 /api/memory 管理面共享同一存储。
117
- // 017 M4:KB 校验三态(TBOX_MEMORY_KB_VALIDATE:1=异步降级不阻塞启动 / strict=同步 fail-fast /
118
- // 未设置不校验)——gated store 统一收口,装配面恒定(INV-D),降级只发生在调用执行时。
119
- const memoryBase = await resolveMemoryStoreGuarded({
120
- store: process.env.TBOX_MEMORY_STORE,
121
- kbId: process.env.TBOX_MEMORY_KB_ID,
122
- kbDocId: process.env.TBOX_MEMORY_KB_DOC_ID,
123
- kbBaseUrl: process.env.TBOX_MEMORY_KB_BASE_URL,
124
- kbCookie: process.env.TBOX_MEMORY_KB_COOKIE,
125
- validate: process.env.TBOX_MEMORY_KB_VALIDATE,
126
- validateTimeoutMs: process.env.TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS,
127
- });
128
- // B4:recall 缓存装饰器(list 60s TTL + 空结果负缓存短路——零记忆用户 recall <10ms)。
129
- // 组合序:withRecallCache 包 gated 外层(gate 失败穿透不污染缓存);单实例双消费
130
- // (engine recall + 三工具 + /api/memory 管理面)共享 wrapped 实例——save 写失效钩子生效。
131
- const memory = {
132
- ...memoryBase,
133
- ...(memoryBase.store ? { store: withRecallCache(memoryBase.store) } : {}),
134
- };
187
+ const pluginTools = createPluginIntegration();
135
188
 
136
189
  /** 懒初始化单例 ConversationService */
137
190
  let _conversationService: ConversationService | null = null;
@@ -153,70 +206,67 @@ process.on('uncaughtException', (err) => {
153
206
  });
154
207
 
155
208
  export const app = express();
156
- app.use(cors());
209
+
210
+ // ===== CORS allowlist(统一登录改造 T20:全开 cors() 退役) =====
211
+ // 双轴分离:iframe 嵌入走 CSP frame-ancestors(下方中间件),跨源 fetch 走本 allowlist。
212
+ // 三元组与 frame-ancestors 对齐(用户裁决 2026-09-13);credentials 恒 false(token 走
213
+ // Authorization 头,不依赖 cookie——跨源带凭据面直接关死)。
214
+ // 生成应用可编辑此常量(长尾 origin 逃生通道)——刻意不加 env(决策权归部署面代码)。
215
+ const CORS_ALLOWED_ORIGINS = ['https://tbox.cn', 'https://*.tbox.cn', 'http://localhost:3000'];
216
+ function originAllowed(origin: string): boolean {
217
+ return CORS_ALLOWED_ORIGINS.some((pattern) =>
218
+ pattern.includes('*')
219
+ ? origin.endsWith(pattern.slice(pattern.indexOf('*') + 1))
220
+ : origin === pattern,
221
+ );
222
+ }
223
+ app.use((req, res, next) => {
224
+ const origin = req.headers.origin;
225
+ if (typeof origin === 'string' && originAllowed(origin)) {
226
+ res.setHeader('Access-Control-Allow-Origin', origin);
227
+ res.setHeader('Vary', 'Origin');
228
+ }
229
+ res.setHeader('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');
230
+ res.setHeader('Access-Control-Allow-Headers', 'Authorization,Content-Type,X-Request-ID');
231
+ res.setHeader('Access-Control-Allow-Credentials', 'false');
232
+ if (req.method === 'OPTIONS') {
233
+ res.sendStatus(204);
234
+ return;
235
+ }
236
+ next();
237
+ });
238
+
157
239
  app.use(express.json());
158
240
 
159
241
  // 🔒 平台基础设施 — 禁止 AI 删除或修改(@infrastructure-locked)
160
242
  installInspector(app);
161
243
 
162
- // 非 prod 默认开启 dev-login:对暴露公网的实例给出可见警示(生产必须 NODE_ENV=production)
163
- if (auth.config.devLoginEnabled) {
244
+ // 浏览器登录形态提示:非 prod 裸跑(无 mall 控制面)默认开放 guest 登录(内置换码器
245
+ // allowsGuestLogin 声明);公网实例应以 NODE_ENV=production + 换码器档位收敛(browserLoginMode: off)
246
+ if (!isProduction) {
164
247
  authLogger.warn(
165
- 'dev-login 已开启(非 production 默认)。若本实例暴露公网,请设 NODE_ENV=production 或 AUTH_DEV_LOGIN_DISABLED=true。',
248
+ '浏览器登录(browser 轴)按换码器档位开放:裸跑/demo 默认 guest。若本实例暴露公网,请设 NODE_ENV=production 并在登录槽 config 收敛 browserLoginMode。',
166
249
  );
167
250
  }
168
251
 
169
252
  // 自注册闭环:模块路由挂载到 /api/m/<moduleId>/(在 apiRouter 之前,避免被 /api 兜底 404 截获)
170
253
  // ctx 为模块级单例:registerAll 在 start() 内执行,ApiRouteRegistry 共享 root 保证延迟注册即时生效
171
- // 011 L1 + F2/F3:控制面纯两键(malls + defaultMallId)——旧 ProviderRegistry 链已拆除,
172
- // provider 绑定唯一真源 = config/integrations.json(槽位轴;与 mall 模板同构);
173
- // F2:scopeDefaults 中立注入 + authExchanges 同引用(provider 包 register 期自注册换码器)
174
- const providerCatalog = loadProviderCatalog(appRoot());
175
- const scopeDefaults = deployment?.controlPlane.defaultMallId
176
- ? { defaultMallId: deployment.controlPlane.defaultMallId }
177
- : undefined;
178
- // 011 B2:可变认证策略注册表——模块 register 期注册(厂商签名策略),transportFactory 同引用
179
- const authStrategies = createAuthStrategyRegistry();
180
-
181
- // provider 拆包 F2/F3:integrations 槽位解析轴(config/integrations.json 存在即生效;
182
- // 缺席 → undefined,模块槽解析与换码链 fail-fast 显性暴露)
183
- // instanceIdOf 注入 mall 语义(SDK 零领域铁律——mallId 派生归模板层)
184
- // root config.miniAppId(016 默认商圈兜底拼装用;与 mall 模板同构——env 注入形态无参考表可查)
185
- const rootMiniAppId =
186
- typeof integrationsConfig?.config.config?.miniAppId === 'string' && integrationsConfig.config.config.miniAppId
187
- ? integrationsConfig.config.config.miniAppId
188
- : undefined;
189
- const integrations = integrationsConfig
190
- ? createIntegrationsRegistry({
191
- integrations: integrationsConfig.config,
192
- // catalog 真源 = 应用根 .tbox/app.json(上方无条件装载)
193
- catalog: providerCatalog,
194
- authStrategies,
195
- secretProvider: createEnvironmentSecretProvider({
196
- source: process.env as Record<string, string | undefined>,
197
- // 文件通道(v2):config/credentials/ 凭据文件优先(ref = 文件名 stem 直引),env 兜底。
198
- // dev 态 TBOX_CONFIG_DIR 指档位目录 → files 指档位凭据目录;PR-2 前目录缺席零行为。
199
- ...(process.env.TBOX_CONFIG_DIR
200
- ? { files: path.resolve(process.env.TBOX_CONFIG_DIR, 'credentials') }
201
- : { files: path.resolve(__dirname, '../../../config/credentials') }),
202
- allowedHeaderNames: ['content-type', 'sign', 'timestamp', 'publickey', 'x-api-key'],
203
- production: isProduction,
204
- }),
205
- instanceIdOf: (scope) => scope.mallId ?? '',
206
- // F5 观测轴:provider factory 收到的 transport 统一包外部调用日志(→/← path durMs)
207
- logger: createLogger('provider'),
208
- })
209
- : undefined;
210
-
254
+ // 011 L1 + F2/F3:条件 mall 装配——integrations 槽位轴在上方装配段创建;
255
+ // F2:authExchanges 同引用传递(provider 包 register 期自注册换码器)+ scopeDefaults 中立注入
256
+ // W3-1:scopeDefaults 静态注入退役——域装配(含 defaultMallId)经 registry 读取视图供给
257
+ // (SDK ctx.scopeDefaults 惰性优先注册表 → provider getter 登录期求值)
211
258
  const ctx = createServerContext({
212
259
  authStrategies,
213
260
  authExchanges: auth.exchanges,
214
261
  ...(memory.store ? { memory: memory.store } : {}),
215
262
  ...(integrations ? { integrations } : {}),
216
- ...(scopeDefaults ? { scopeDefaults } : {}),
217
263
  });
218
264
 
219
- // 全局 /api 鉴权(H2 挂载顺序契约):除 public(login/dev-login/health)外一律要求 Bearer token;
265
+ // real-env 统一配置链:app-config 服务(模块资源消费入口)——由 registerServerModules
266
+ // 自动装配(W1-④:raw 派生 modules 资源节 + 源目录台账锚;无条件注册、与控制面正交,
267
+ // 防无控制面启动静默丢失)。模板显式注册已拆(W3-1:装配默认兜底覆盖同语义)。
268
+
269
+ // 全局 /api 鉴权(H2 挂载顺序契约):除 public(login/health)外一律要求 Bearer token;
220
270
  // optional 模式缺失 token → 匿名身份(写通道仍被 action gateway 拒绝)
221
271
  // ⚠️ 必须先于 /api/m、/api/action、createPlatformApiRouter 挂载,保证全 API 面受鉴权约束
222
272
  app.use('/api', auth.middleware);
@@ -227,12 +277,37 @@ app.use('/api/m', ctx.routes.mount());
227
277
  // 006 D11:匿名写拒绝,不随 AUTH_MODE 变)
228
278
  app.use('/api/action', ctx.actions.middleware());
229
279
 
230
- // API 路由(平台路由面:auth/health/tbox/conversation [+memory] + 404 兜底;卡片恢复内部化)
280
+ // ===== GET /assets/deploy/:file — 部署态静态资产(.real-env/assets;厂商物料不进中立模板) =====
281
+ // ⚠️ 必须在 createPlatformApiRouter 与 vite 中间件之前注册。文件名白名单防目录穿越。
282
+ const deploymentAssetsDir =
283
+ process.env.DEPLOYMENT_ASSETS_DIR ?? path.resolve(appRoot(), '../../.real-env/assets');
284
+ app.get('/assets/deploy/:file', (req, res) => {
285
+ const file = typeof req.params.file === 'string' ? req.params.file : '';
286
+ if (!/^[A-Za-z0-9._-]+$/.test(file)) {
287
+ res.status(400).end();
288
+ return;
289
+ }
290
+ res.sendFile(path.resolve(deploymentAssetsDir, file), (err) => {
291
+ if (err) res.status(404).end();
292
+ });
293
+ });
294
+
295
+ // API 路由(平台路由面:auth/health/tbox/conversation [+memory] / 404 兜底;卡片恢复内部化)
231
296
  // 017:装配记忆时挂载 /api/memory 管理面(与引擎同源 store)
232
297
  app.use('/api', createPlatformApiRouter({ ctx, auth, ...(memory.store ? { memory: memory.store } : {}) }));
233
298
 
234
299
  // tbox dev 工具族(/_tbox:卡片预览 API + 页面族;dev-only 自门控——NODE_ENV=production
235
300
  // 零挂载;import 期注册先于 dev.ts 运行期 vite middleware,路由序恒胜)
301
+ // iframe 嵌入收紧(AI 构建面板 card-preview 预览):现状无该头 = 隐式全允许 → 显式三元组
302
+ // (用户裁决 2026-09-13:不存在其他 origin 嵌入既有 demo,行为收紧确认;'self' + 生产域 + 本地面板 dev 端口)
303
+ app.use((_req, res, next) => {
304
+ res.setHeader(
305
+ 'Content-Security-Policy',
306
+ "frame-ancestors 'self' https://tbox.cn https://*.tbox.cn http://localhost:3000",
307
+ );
308
+ next();
309
+ });
310
+
236
311
  installTboxDev(app, { ctx, auth });
237
312
 
238
313
  // 从 ServerContext 构造 ChatEngine(自注册闭环组装端)
@@ -247,10 +322,15 @@ async function createEngine(
247
322
 
248
323
  const engine = await createEngineFromContext(ctx, {
249
324
  instructions: AGENT_PROMPT.instructions,
325
+ ...(AGENT_PROMPT.memoryInstructions ? { memoryInstructions: AGENT_PROMPT.memoryInstructions } : {}),
250
326
  model: getModel(AI_MODEL_NAME),
251
327
  traceExporter: tboxTraceExporter,
252
328
  modelName: AI_MODEL_NAME,
253
329
  fileUrlPrefix: TBOX_FILE_URL_PREFIX,
330
+ // 018 tool-debug:工具调试视图(memory/silent preTool 前端表达;缺省关)
331
+ toolDebug,
332
+ // PR-2 尽早工具执行(缺省 off;env TBOX_EARLY_TOOL_EXECUTION=1 开启)
333
+ earlyToolExecution,
254
334
  // 009:双模式全局配置(accelerated 默认:本地路由命中 → reminder 注入当前轮,零额外 LLM 轮次;
255
335
  // progressive:全量注册 InlineSkill,模型渐进 skill_read)
256
336
  skillMode,
@@ -258,12 +338,19 @@ async function createEngine(
258
338
  // 010:前置工具加速(应用级声明 + 单工具预算;TBOX_PRE_TOOL_TIMEOUT_MS=0 整体禁用)
259
339
  preTools: APP_PRE_TOOLS,
260
340
  preToolTimeoutMs,
261
- // 018 tool-debug:工具调试视图(memory/silent preTool 前端表达;缺省关)
262
- toolDebug,
263
- // PR-2 尽早工具执行(缺省 off;env TBOX_EARLY_TOOL_EXECUTION=1 开启)
264
- earlyToolExecution,
265
- // 017:长期记忆(默认 inmemory;off 时 enabled=false 不装配)
266
- ...(memory.enabled && memory.store ? { memory: { store: memory.store } } : {}),
341
+ // 017:长期记忆(默认 inmemory;mall 装配时按 mallId 隔离——scope 经 deployment.assembly
342
+ // buildScope 单点,与 scope 会话/授权签发同源;无 mall 上下文 → 用户级全局记忆)
343
+ ...(memory.enabled && memory.store
344
+ ? {
345
+ memory: {
346
+ store: memory.store,
347
+ resolveScope: (attributes: Record<string, unknown>) => {
348
+ const scope = mallAssembly()?.buildScope(attributes);
349
+ return scope?.mallId ? { mallId: scope.mallId } : undefined;
350
+ },
351
+ },
352
+ }
353
+ : {}),
267
354
  // 011 B3:卡片动作 token 签发器(mall 装配时注入)
268
355
  cardActionAttacher: extra?.cardActionAttacher,
269
356
  // B21:意图路由接线(业务意图表 + handlerDispatch)
@@ -326,24 +413,31 @@ export function registerUpgradeDelegate(delegate: UpgradeDelegate): void {
326
413
  }
327
414
 
328
415
  export async function start() {
329
- // 011 S6:mall 装配 + 生产环境缺动作授权密钥 → 启动期提示(签发路径运行时兜底报错)
330
- if (deployment && isProduction && !process.env.ACTION_TOKEN_SECRET && !process.env.AUTH_TOKEN_SECRET) {
331
- actionLogger.warn('生产环境未配置 ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET——卡片动作授权装配将 fail-fast 拒绝启动');
332
- }
333
-
334
- // 注册业务模块(自注册闭环)
416
+ // 注册业务模块(自注册闭环)——mall-directory 服务由 module-mall-core register 期注入(W3-1)
335
417
  await registerAll(ctx);
336
418
 
337
419
  // 模板演示工具(apps 级工具,P3 建议迁入模块):不参与模块体系,经统一注册面注册
338
420
  // (010:start() 与 cli/collect-registry.ts 双处同调,防注册面失同步)
339
421
  registerAppTools(ctx);
340
422
 
423
+ // W3-1 时序契约:域装配解析必须晚于 registerAll——module-mall-core 在 register 期经
424
+ // ctx.domainAssemblies 注册装配;解析点置于 start() 头部会查空表(W4 引入回归:
425
+ // 默认商圈兜底/卡片动作授权/enhanceContext 全断——见 Agent Note 2026-09-17-mall-assembly-resolution-order)
426
+ const assembly = mallAssembly();
427
+
428
+ // 011 S6:mall 装配 + 生产环境缺动作授权密钥 → 启动期提示(≥32B TBOX_API_KEY 在场回退 apiKey 档;整链空装配期 fail-fast)
429
+ if (assembly && isProduction && !process.env.ACTION_TOKEN_SECRET && !process.env.AUTH_TOKEN_SECRET) {
430
+ actionLogger.warn('生产环境未配置 ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET——TBOX_API_KEY(≥32B) 缺席时装配将 fail-fast 拒绝启动;在场则回退 apiKey 档(平台运维可伪 action token)');
431
+ }
432
+
341
433
  // 011 F2/E2:mall 装配(条件)——scope 会话 + 动作授权先行(engine attacher 依赖)
342
- const scopeSessions: ReturnType<typeof createScopeSessionRegistry> | undefined = deployment
434
+ // 014 T1:buildScope 回填 mallName(ChatHeader 品牌区服务端真源;三级回退内聚于域装配)
435
+ // W3-1:域装配经查表(assembly 于上方 registerAll 后解析——解析点必须晚于模块注册)
436
+ const scopeSessions: ReturnType<typeof createScopeSessionRegistry> | undefined = assembly
343
437
  ? createScopeSessionRegistry({
344
- extractScopeKey: deployment.assembly.extractScopeKey,
345
- validateScope: deployment.assembly.validateScope,
346
- buildScope: deployment.assembly.buildScope,
438
+ extractScopeKey: (c) => assembly.extractScopeKey(c),
439
+ validateScope: (a) => assembly.validateScope(a),
440
+ buildScope: (a) => assembly.buildScopeWithMallName(a),
347
441
  })
348
442
  : undefined;
349
443
  const grants = scopeSessions ? createActionGrantService({ store: ctx.state }) : undefined;
@@ -369,7 +463,7 @@ export async function start() {
369
463
 
370
464
  // 011 F2/E2:模块动作分发(依赖 scopeSessions/grants)
371
465
  let moduleActions: ReturnType<typeof createModuleActionDispatcher> | undefined;
372
- if (deployment && scopeSessions && grants) {
466
+ if (assembly && scopeSessions && grants) {
373
467
  moduleActions = createModuleActionDispatcher({
374
468
  grants,
375
469
  sessions: scopeSessions,
@@ -379,32 +473,48 @@ export async function start() {
379
473
  // 015 P3:effect 运行时纯投影(payment/authorization 回调补签在 dispatcher step7,
380
474
  // 客户端执行在 SDK host-effect-runner——模板零装配效果链)
381
475
  effects: createEffectRuntime(),
382
- extractScopeKey: deployment.assembly.extractScopeKey,
476
+ extractScopeKey: (c) => assembly.extractScopeKey(c),
383
477
  });
384
478
  for (const action of ctx.cardActions.getAll().values()) {
385
479
  moduleActions.register(action);
386
480
  }
387
481
  }
388
482
 
389
- // chat.json 真读(原 scope 分支内 demo 硬编码移除,快捷指令迁入 config/chat.json——与 mall 同构:
390
- // 配置单一真源 + settings 运行时下发;chatConfig 无条件传递,非 scope 部署亦发 ack)
483
+ // chat.json 单次装载:chatConfig 下发与 /ws/tts 通道门同值同源(改文件需重启进程,新连接生效)
391
484
  const loadedChatConfig = loadChatConfig(CONFIG_DIR);
485
+ const defaultContext = assembly?.defaultMallContext();
392
486
  const chatWss = createWsServer(engine, {
393
487
  auth: auth.wsAuth,
394
- // 018 tool-debug:HELLO_ACK 旗标(仅开启时携带;与 engine.toolDebug 同值同源)
395
- toolDebug,
488
+ // 会话 β:会话建立服务(懒代理——TBOX_API_KEY 缺席时 NEW/RESUME 失败 notice,RUN 恒严格拒绝)
489
+ conversationService: {
490
+ createConversation: (agentId, userId) => {
491
+ const service = getConversationService();
492
+ if (!service) return Promise.reject(new Error('TBOX_API_KEY not configured'));
493
+ return service.createConversation(agentId, userId);
494
+ },
495
+ getOrCreateConversation: (agentId, userId) => {
496
+ const service = getConversationService();
497
+ if (!service) return Promise.reject(new Error('TBOX_API_KEY not configured'));
498
+ return service.getOrCreateConversation(agentId, userId);
499
+ },
500
+ },
396
501
  ...(scopeSessions
397
502
  ? {
398
503
  scopeSessions,
399
504
  ...(moduleActions ? { moduleActions } : {}),
400
- // 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context(显式参数优先);
401
- // root config.miniAppId 优先(读源统一,回落 malls[].miniAppId;与 mall 模板同构)
402
- ...(deployment
403
- ? { defaultContext: buildDefaultMallContext(deployment.controlPlane, rootMiniAppId) }
404
- : {}),
505
+ // 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context(显式参数优先)
506
+ // W3-1:defaultContext/enhanceContext 双钩内聚于域装配(mall-core;fail-soft 契约不变:
507
+ // 增强是增益不是闸门——抛错/超时原 context 继续)
508
+ ...(defaultContext ? { defaultContext } : {}),
509
+ ...(assembly ? { enhanceContext: (c) => assembly.enhanceHelloContext(c) } : {}),
405
510
  }
406
511
  : {}),
512
+ // real-env 统一配置链:chat.json → HELLO chatConfig(query→prompt 映射 + settings 五键,
513
+ // 剥离扩展字段;映射真源已下沉 SDK server)
514
+ // 无条件传递(chat 配置与控制面正交;loadChatConfig 缺文件/坏格式 fail-soft 回默认)
407
515
  chatConfig: toWsChatConfig(loadedChatConfig),
516
+ // 018 tool-debug:HELLO_ACK 旗标(仅开启时携带;与 engine.toolDebug 同值同源)
517
+ toolDebug,
408
518
  });
409
519
  const ttsWss = createTtsProxy();
410
520
 
@@ -419,7 +529,7 @@ export async function start() {
419
529
  process.exit(1);
420
530
  });
421
531
 
422
- httpServer.on('upgrade', (request, socket, head) => {
532
+ httpServer.on('upgrade', async (request, socket, head) => {
423
533
  socket.on('error', (e) => upgradeLogger.warn('socket error:', e.message));
424
534
  if (upgradeDelegate?.(request, socket, head)) return;
425
535
  const { pathname } = new URL(request.url || '/', 'http://localhost');
@@ -429,8 +539,27 @@ export async function start() {
429
539
  chatWss.emit('connection', ws, request);
430
540
  });
431
541
  } else if (pathname === '/ws/tts') {
432
- // 通道门与 UI 同值同源(chat.json enableTTS 单源):关态硬拒 upgrade,
433
- // 防服务端开着 TTS 代理但 UI 已隐藏按钮的僵尸路径;destroy 于 handleUpgrade 之前
542
+ // 双层门 1(S5 修复,pre-101 鉴权):token 校验先行——TTS 代理原为全开放面。
543
+ // 双形态载体(IP-2):Node 客户端 = Authorization Bearer;浏览器 WebSocket 无法
544
+ // 自定义 header = ?token= query。无效/缺失 token 于 upgrade 前 destroy
545
+ // (对齐 Terminal channel 先例)。
546
+ const headerToken = (request.headers.authorization ?? '').replace(/^Bearer\s+/i, '');
547
+ const queryToken = new URL(request.url || '/', 'http://localhost').searchParams.get('token') ?? '';
548
+ const ttsToken = headerToken || queryToken;
549
+ if (!ttsToken) {
550
+ upgradeLogger.warn('TTS upgrade rejected: missing token');
551
+ socket.destroy();
552
+ return;
553
+ }
554
+ try {
555
+ await auth.tokenService.verify(ttsToken);
556
+ } catch {
557
+ upgradeLogger.warn('TTS upgrade rejected: invalid token');
558
+ socket.destroy();
559
+ return;
560
+ }
561
+ // 双层门 2(通道门与 UI 同值同源):chat.json enableTTS 单源——关态硬拒 upgrade,
562
+ // 防服务端开着 TTS 代理但 UI 已隐藏按钮的僵尸路径
434
563
  if (!loadedChatConfig.enableTTS) {
435
564
  upgradeLogger.warn('TTS disabled (chat.json enableTTS:false) — /ws/tts upgrade rejected');
436
565
  socket.destroy();
@@ -6,7 +6,7 @@
6
6
  * 旧 `config/deployments/development.json` 已退役,stale 防线在装载链 fail-fast
7
7
  * (mall 家族形态判定:含 malls 键;support 独立体系不受影响)。
8
8
  * - zod 单源 = SDK parseIntegrationsConfig(断言 → strict 校验;拼错键启动即报);
9
- * 控制平面投影 = contracts-mall mallControlPlaneOf(恒返回——无表 = 空平面)。
9
+ * 控制平面投影归 module-mall-core(W3-1:消费 ctx.integrations.raw 自持计算)。
10
10
  * - 本壳只保留 I/O:占位展开(real-env 统一配置链——缺变量 fail-fast,错误含文件与
11
11
  * 缺失清单)+ DEFAULT_PATH 双态锚点探测(打包形态私有知识,不入 SDK)。
12
12
  * - 与 base 模板同构(双生无同步机制——公共能力先行落位,漂移由生成应用 verify 守护)。
@@ -16,8 +16,6 @@ import path from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
  import { expandEnvVars, parseIntegrationsConfig } from '@tbox.cn/app-sdk/server';
18
18
  import type { IntegrationsConfig } from '@tbox.cn/app-contracts';
19
- import { mallControlPlaneOf } from '@app/contracts-mall/server';
20
- import type { MallDeploymentControlPlane } from '@app/contracts-mall';
21
19
 
22
20
  // 默认路径 = 应用根 config/integrations.json。双态锚点探测(bundle 打平后 import.meta.url 层级漂移):
23
21
  // dev 态本文件在 src/config/(四层到根);tsup bundle 态打平进 dist/(三层到根)。
@@ -37,8 +35,6 @@ const DEFAULT_PATH = (() => {
37
35
  export interface LoadedIntegrations {
38
36
  config: IntegrationsConfig;
39
37
  source: string;
40
- /** mall 控制平面投影(恒在场——无参考表 = 空平面;装配链零拓扑分支) */
41
- controlPlane: MallDeploymentControlPlane;
42
38
  }
43
39
 
44
40
  /**
@@ -77,5 +73,5 @@ export function loadIntegrations(envPath?: string): LoadedIntegrations | undefin
77
73
  // 顺序固定:展开(占位符)→ 断言 → zod(后两步在 parseIntegrationsConfig 内,防线单点)
78
74
  const raw = JSON.parse(expandEnvVars(readFileSync(p, 'utf8'), process.env, p));
79
75
  const config = parseIntegrationsConfig(raw);
80
- return { config, source: p, controlPlane: mallControlPlaneOf(config) };
76
+ return { config, source: p };
81
77
  }
@@ -39,7 +39,6 @@ const vite = await createViteServer({
39
39
  },
40
40
  // custom:不加 Vite 自己的 SPA fallback,由下方 app.use('*') 统一处理 HTML 路由
41
41
  appType: 'custom',
42
- optimizeDeps: { entries: [] },
43
42
  });
44
43
 
45
44
  // 升级委托:与 vite hmrServerWsListener 同条件(协议整串匹配 + pathname === 认领路径)。
@@ -5,9 +5,9 @@ import type { IntentRoute, HandlerResult, ServerContext } from '@tbox.cn/app-sdk
5
5
  * chat() 先 matchIntent(统一检索层 BM25 竞争:长词 OR ≥2 token 规则,score 降序 + id 字典序);
6
6
  * handler 缺省直接出卡/文字,也可用 continueWith 续接完整 Agent。
7
7
  *
8
- * 示例(商圈应用启用积分抵扣停车意图,handlerId 对应已装模块的 handler id):
9
- * { id: 'points-parking', keywords: ['积分抵扣停车', '停车抵扣'], handlerId: 'points-parking', action: 'run' }
10
- * 本模板默认空表(保持 LLM + tool calling 主路径);业务方在此声明意图路由。
8
+ * W3-3(单一模板统一化):停车缴费硬路由迁 module-parking handler `intents` 声明
9
+ * (意图与处理器同处定义,011 D7;collectIntentRoutes 装配端自动汇集)——
10
+ * 本表归零,业务方在此声明应用级意图路由(handlerId 对应已装模块的 handler id)。
11
11
  */
12
12
  export const INTENT_ROUTES: IntentRoute[] = [];
13
13
 
@@ -10,16 +10,16 @@ import type { ServerContext, InferModuleCards } from '@tbox.cn/app-sdk/server';
10
10
  // @tbox:imports-begin
11
11
  // SDK 装配框架(非模块行,CLI 增量维护保留非标行)
12
12
  import { registerServerModules } from '@tbox.cn/app-sdk/server';
13
- import { serverModule as moduleWeather } from '@app/module-weather/server';
14
13
  import { serverModule as providerCustom } from '@app/provider-custom/server';
15
14
  // @tbox:imports-end
16
15
 
17
16
  // @tbox:register-begin
18
- // base 模板出厂预装 provider-custom(自定义 API 落点)+ weather 演示模块
17
+ // W4 单一模板统一化:缺省出厂 = 空装配(仅 provider-custom 自定义 API 落点 + contracts-mall shim)——
18
+ // mall 能力(mall-core/mall-home + 业务模块)经 profiles/mall 加装(tbox-app create --profile mall,W5 接线)
19
+ // footer `] as const` 为空数组类型锚(CLI 增删模块时保留变体)
19
20
  const modules = [
20
- moduleWeather,
21
21
  providerCustom,
22
- ];
22
+ ] as const;
23
23
  // @tbox:register-end
24
24
 
25
25
  // 自由区:可做装配级定制——条件注册、顺序调整、包裹中间件
@@ -3,7 +3,7 @@ import { definePreTool } from '@tbox.cn/app-sdk/server';
3
3
  import { clockTool } from './tools/clock.js';
4
4
 
5
5
  /**
6
- * 应用级前置工具声明(010):BuildEngineOptions.preTools 通道。
6
+ * 应用级前置工具声明(010):BuildEngineOptions.preTools 通道(与模块声明合并、应用覆盖模块)。
7
7
  *
8
8
  * 声明形态(详见 README「前置 Tool 加速执行」节):
9
9
  * ① tool —— 工具实例引用(单一真源,id/description 派生)
@@ -11,8 +11,9 @@ import { clockTool } from './tools/clock.js';
11
11
  * 或 { mode: 'always' } 必选候选(decide 门控;慎用防每轮噪音)
12
12
  * ③ decide —— 决策即构造:{ decision: 'run', args } = 预执行 / { decision: 'skip' } = 不生效
13
13
  *
14
- * 演示形态:零参工程化——词表命中即生效(decide 恒 run 空参)。
15
- * 词表刻意排除「今天/时间」等泛词,防止与天气类 preTool 叠加误触发(词表精度示范)。
14
+ * getCurrentTime —— 零参工程化演示(keywords 词表命中即执行,decide 恒 run 空参)。
15
+ * 词表刻意排除「今天/时间」等泛词,防止与业务模块 preTool(如 member 的 always 预执行)语义叠加误触发。
16
+ * W3-1:mall-info preTool 迁 module-mall-core(模块声明式 preTools 通道)。
16
17
  */
17
18
  export const APP_PRE_TOOLS = [
18
19
  definePreTool(clockTool, {