@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.
- package/.gitignore +5 -0
- package/.tbox/app.json +9 -1
- package/AGENTS.md +12 -4
- package/ENV.md +102 -17
- package/README.md +36 -34
- package/apps/client/VISUAL_RESTORE.md +11 -11
- package/apps/client/index.html +3 -1
- package/apps/client/package.json +9 -8
- package/apps/client/src/business/App.tsx +4 -4
- package/apps/client/src/business/README.md +5 -11
- package/apps/client/src/cards/registry.ts +23 -8
- package/apps/client/src/main.tsx +36 -2
- package/apps/client/src/modules.ts +7 -4
- package/apps/client/src/pages/index/index.tsx +18 -162
- package/apps/client/src/platform/AgentChatApp.tsx +3 -3
- package/apps/client/src/platform/agent-runtime.ts +5 -0
- package/apps/client/src/store/chat.ts +47 -185
- package/apps/client/src/test/setup.ts +7 -0
- package/apps/client/vite.config.ts +16 -1
- package/apps/server/package.json +0 -1
- package/apps/server/src/app-tools.ts +2 -1
- package/apps/server/src/app.ts +178 -110
- package/apps/server/src/config/integration.ts +2 -6
- package/apps/server/src/dev.ts +0 -1
- package/apps/server/src/intent-routes.ts +3 -3
- package/apps/server/src/modules.ts +4 -4
- package/apps/server/src/pre-tools.ts +4 -3
- package/apps/server/tests/agent-prompt.test.ts +81 -0
- package/apps/server/tests/cards-registry.test.ts +1 -3
- package/apps/server/tests/deployment-loader.test.ts +35 -56
- package/apps/server/tests/tool-runtime.test.ts +25 -61
- package/package.json +5 -4
- package/packages/agent-ui-kit/LICENSE +21 -0
- package/packages/agent-ui-kit/README.md +17 -0
- package/packages/agent-ui-kit/package.json +58 -0
- package/packages/agent-ui-kit/src/ChatPage.less +62 -0
- package/packages/agent-ui-kit/src/ChatPage.tsx +305 -0
- package/packages/agent-ui-kit/src/chat-config.test.ts +82 -0
- package/packages/agent-ui-kit/src/chat-config.ts +62 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/DebugButton.tsx +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.less +19 -16
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.tsx +10 -13
- package/packages/agent-ui-kit/src/components/CardRenderer.tsx +36 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.less +148 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.test.tsx +74 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatHeader/index.tsx +27 -15
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.less +47 -39
- package/packages/agent-ui-kit/src/components/ChatInput/index.test.tsx +156 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.tsx +13 -4
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/useVoicePress.ts +2 -2
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.less +49 -0
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.tsx +42 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.less +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.tsx +6 -6
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.less +25 -20
- package/packages/agent-ui-kit/src/components/MessageBubble/index.test.tsx +112 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.tsx +22 -15
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.less +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.tsx +2 -2
- package/packages/agent-ui-kit/src/components/QuickCommands/index.less +63 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.test.tsx +99 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.tsx +52 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.less +1 -1
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.test.ts +346 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.ts +204 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.less +312 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.test.tsx +261 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.tsx +212 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/titles.ts +102 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.test.ts +66 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.ts +129 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-history.ts +241 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-wheel-native.test.ts +64 -0
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useMediaUpload.ts +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useTTSAutoPlay.ts +7 -5
- package/packages/agent-ui-kit/src/index.ts +69 -0
- package/packages/agent-ui-kit/src/store-context.tsx +92 -0
- package/packages/agent-ui-kit/src/test/setup.ts +7 -0
- package/packages/agent-ui-kit/src/theme/contract.json +72 -0
- package/packages/agent-ui-kit/src/theme/global.less +99 -0
- package/packages/agent-ui-kit/src/theme/motion.less +19 -0
- package/packages/agent-ui-kit/src/theme/theme-contract.test.ts +69 -0
- package/packages/agent-ui-kit/src/theme/theme.ts +17 -0
- package/packages/agent-ui-kit/src/theme/tokens.less +79 -0
- package/packages/agent-ui-kit/src/utils/haptics.test.ts +51 -0
- package/packages/agent-ui-kit/src/utils/haptics.ts +44 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.test.ts +188 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.ts +157 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.test.ts +240 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.ts +146 -0
- package/packages/agent-ui-kit/src/vite-env.d.ts +1 -0
- package/packages/agent-ui-kit/tsconfig.json +8 -0
- package/packages/agent-ui-kit/vitest.config.ts +7 -0
- package/pnpm-lock.template.yaml +177 -83
- package/pnpm-workspace.template.yaml +12 -4
- package/profiles/mall/config/agent.jsonc +33 -0
- package/profiles/mall/config/chat.json +21 -0
- package/profiles/mall/config/knowledge.json +5 -0
- package/profiles/mall/config/llm_provider.json +5 -0
- package/profiles/mall/config/plugins.json +3 -0
- package/profiles/mall.json +16 -0
- package/scripts/check-workspace.mjs +3 -2
- package/apps/client/assets/copy@2x.png +0 -0
- package/apps/client/assets/debug.png +0 -0
- package/apps/client/assets/dislike@2x.png +0 -0
- package/apps/client/assets/like@2x.png +0 -0
- package/apps/client/assets/speaker@2x.png +0 -0
- package/apps/client/src/cards/CardRenderer.tsx +0 -20
- package/apps/client/src/components/ChatHeader/index.less +0 -104
- package/apps/client/src/components/ErrorBoundary.tsx +0 -2
- package/apps/client/src/components/QuickCommands/index.less +0 -48
- package/apps/client/src/components/QuickCommands/index.tsx +0 -29
- package/apps/client/src/components/ToolCallBlock/index.less +0 -131
- package/apps/client/src/components/ToolCallBlock/index.tsx +0 -125
- package/apps/client/src/examples/tourism/TourismLanding.tsx +0 -96
- package/apps/client/src/examples/tourism/index.less +0 -192
- package/apps/client/src/pages/index/index.less +0 -43
- package/apps/client/src/store/chat-config.ts +0 -40
- package/apps/client/src/styles/global.less +0 -60
- package/apps/client/src/styles/tokens.less +0 -60
- package/apps/server/src/config/deployment.ts +0 -47
- package/packages/module-weather/package.json +0 -36
- package/packages/module-weather/skills/weather-guide/SKILL.md +0 -22
- package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +0 -5
- package/packages/module-weather/src/client/cards/weather/index.less +0 -22
- package/packages/module-weather/src/client/cards/weather/index.tsx +0 -14
- package/packages/module-weather/src/client/index.ts +0 -19
- package/packages/module-weather/src/index.ts +0 -2
- package/packages/module-weather/src/server/cards/weather/meta.ts +0 -27
- package/packages/module-weather/src/server/index.ts +0 -45
- package/packages/module-weather/src/server/pre-tools.ts +0 -20
- package/packages/module-weather/src/server/tool.ts +0 -29
- package/packages/module-weather/tbox.module.json +0 -27
- package/packages/module-weather/tsconfig.json +0 -14
- /package/{apps/client → packages/agent-ui-kit}/assets/keyboard-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/more-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/new-chat.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/quick-command-star.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/send-gradient.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/stop@2x.png +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/voice-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoicePressField.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoiceRecordingOverlay.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/machine.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.less +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.tsx +0 -0
- /package/{apps/client/src/types/chat.ts → packages/agent-ui-kit/src/types.ts} +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/copyText.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/imagePreview.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/session-error.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/toast.ts +0 -0
|
@@ -3,9 +3,10 @@ import { translateTool } from './tools/translate.js';
|
|
|
3
3
|
import { clockTool } from './tools/clock.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* apps
|
|
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);
|
package/apps/server/src/app.ts
CHANGED
|
@@ -11,7 +11,6 @@ import path from 'path';
|
|
|
11
11
|
import { existsSync } from 'node:fs';
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
13
|
import { registerAll } from './modules.js';
|
|
14
|
-
|
|
15
14
|
// ===== 模板日志(@tbox.cn/app-sdk/server createLogger:本地时间 + LEVEL + [tag] + req/conv 尾缀) =====
|
|
16
15
|
import { createLogger } from '@tbox.cn/app-sdk/server';
|
|
17
16
|
const serverLogger = createLogger('server');
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
createWsServer,
|
|
25
24
|
createServerContext,
|
|
26
25
|
createEngineFromContext,
|
|
26
|
+
createIntegrationsRegistry,
|
|
27
27
|
loadProviderCatalog,
|
|
28
28
|
tboxTraceExporter,
|
|
29
29
|
extractImageUrls,
|
|
@@ -51,26 +51,35 @@ import { installInspector } from '@tbox.cn/app-sdk/platform';
|
|
|
51
51
|
import { registerAppTools } from './app-tools.js';
|
|
52
52
|
import { APP_PRE_TOOLS } from './pre-tools.js';
|
|
53
53
|
import { INTENT_ROUTES, createAppIntentDispatcher } from './intent-routes.js';
|
|
54
|
-
import { deploymentFromIntegrations, buildDefaultMallContext } from './config/deployment.js';
|
|
55
54
|
import { loadIntegrations } from './config/integration.js';
|
|
55
|
+
// W3-1/W4:mall 域语义(部署投影/scope 校验/默认值/instanceIdOf/mall-directory)归 module-mall-core,
|
|
56
|
+
// 经 ctx.domainAssemblies 注册 + 运行期惰性查表——本文件按 TIER1 契约类型(MallScopeAssembly)
|
|
57
|
+
// 泛型断言读取,零 mall-core 包 import(workspace-dag:应用消费模块经注册表 + 契约)
|
|
58
|
+
import { SCOPE_ASSEMBLY_KIND } from '@tbox.cn/app-contracts';
|
|
59
|
+
import type { MallScopeAssembly } from '@app/contracts-mall';
|
|
56
60
|
// 011 S2/S3:mall 装配原语(条件装配——未配置部署文件时零引用)
|
|
57
61
|
import {
|
|
58
62
|
createScopeSessionRegistry,
|
|
59
63
|
createActionGrantService,
|
|
60
64
|
createModuleActionDispatcher,
|
|
61
65
|
createEffectRuntime,
|
|
62
|
-
createIntegrationsRegistry,
|
|
63
66
|
createAuthStrategyRegistry,
|
|
64
67
|
createEnvironmentSecretProvider,
|
|
65
68
|
attachCardActionTokens,
|
|
66
69
|
} from '@tbox.cn/app-sdk/server';
|
|
67
70
|
|
|
68
71
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
69
|
-
|
|
70
|
-
//
|
|
72
|
+
|
|
73
|
+
// ── 应用级路径唯一锚点区 ──────────────────────────────────────────────
|
|
74
|
+
// entry 即 bundle 实文件:src/dist 平级,dev/prod 恒等。被 tsup 内联的模块禁止
|
|
75
|
+
// 基于自身 import.meta.url 做层级解析(bundle 后层级漂移——config 曾因此解析到
|
|
76
|
+
// 应用根上一级)。同族锚点:../skills、../../client/dist。
|
|
77
|
+
// TBOX_CONFIG_DIR 为 tbox-app dev 的配置面注入锚点(dev 探针常量,guard 守护):
|
|
78
|
+
// 注入时为 real-env 档位目录绝对路径;未注入维持默认相对解析。
|
|
71
79
|
const CONFIG_DIR = process.env.TBOX_CONFIG_DIR
|
|
72
80
|
? path.resolve(process.env.TBOX_CONFIG_DIR)
|
|
73
|
-
: path.resolve(__dirname, '../../../config');
|
|
81
|
+
: path.resolve(__dirname, '../../../config'); // src|dist → server → apps → 应用根
|
|
82
|
+
|
|
74
83
|
const PORT = Number(process.env.PORT) || 8000;
|
|
75
84
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
76
85
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
@@ -81,6 +90,58 @@ const toolDebug = resolveToolDebug(process.env.TBOX_TOOL_DEBUG, process.env.NODE
|
|
|
81
90
|
// 收益依赖 config/llm_provider.json parallelToolCalls 开启;单工具轮行为等价现状
|
|
82
91
|
const earlyToolExecution = resolveEarlyToolExecution(process.env.TBOX_EARLY_TOOL_EXECUTION);
|
|
83
92
|
|
|
93
|
+
// 017:长期记忆(与 base 同源三件套:默认 inmemory;off 逃生舱;knowledge 需 KB_ID)。
|
|
94
|
+
// mall 域措辞段仅装配态拼接(死能力零广告 L1;构造期常量 → 前缀缓存稳定)。
|
|
95
|
+
// 记忆隔离:resolveScope 经域装配 buildScope 单点(mallId 命名空间;W3-1 查表)——
|
|
96
|
+
// 多 mall 单实例下互不可见;未装配控制面/无 mall 上下文 → 用户级全局记忆。
|
|
97
|
+
// 017 M4:KB 校验三态(TBOX_MEMORY_KB_VALIDATE:1=异步降级不阻塞启动 / strict=同步 fail-fast /
|
|
98
|
+
// 未设置不校验)——gated store 统一收口,装配面恒定(INV-D),降级只发生在调用执行时。
|
|
99
|
+
const memoryBase = await resolveMemoryStoreGuarded({
|
|
100
|
+
store: process.env.TBOX_MEMORY_STORE,
|
|
101
|
+
kbId: process.env.TBOX_MEMORY_KB_ID,
|
|
102
|
+
kbDocId: process.env.TBOX_MEMORY_KB_DOC_ID,
|
|
103
|
+
kbBaseUrl: process.env.TBOX_MEMORY_KB_BASE_URL,
|
|
104
|
+
kbCookie: process.env.TBOX_MEMORY_KB_COOKIE,
|
|
105
|
+
validate: process.env.TBOX_MEMORY_KB_VALIDATE,
|
|
106
|
+
validateTimeoutMs: process.env.TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS,
|
|
107
|
+
});
|
|
108
|
+
// B4:recall 缓存装饰器(同 base 模板——组合序 withRecallCache 包 gated 外层;
|
|
109
|
+
// 单实例双消费共享 wrapped 实例,save 写失效钩子生效)
|
|
110
|
+
const memory = {
|
|
111
|
+
...memoryBase,
|
|
112
|
+
...(memoryBase.store ? { store: withRecallCache(memoryBase.store) } : {}),
|
|
113
|
+
};
|
|
114
|
+
// agent 提示词(config/agent.jsonc;未配置 = SDK 默认兜底;memoryInstructions 装配条件归 SDK,
|
|
115
|
+
// 模板无条件透传——读取一次,改文件需重启生效)
|
|
116
|
+
const AGENT_PROMPT = loadAgentPromptConfig(CONFIG_DIR);
|
|
117
|
+
|
|
118
|
+
// 平台能力装配(003/006 平台轴,已下沉 @tbox.cn/app-sdk/server)
|
|
119
|
+
// 011 L1 + F2/F3 + W3-1:装配序 integrations → authStrategies → auth → catalog → ctx——
|
|
120
|
+
// v4:部署拓扑并入 integrations.json(单文件真源);域投影/校验(deployment 语义)迁
|
|
121
|
+
// module-mall-core(ctx.domainAssemblies 惰性查表——auth 构造先于装配,动态判定经闭包后补)。
|
|
122
|
+
// auth 提前构造(登录换码器由 provider 包 register 期经 ctx.authExchanges.register 自供给;
|
|
123
|
+
// 注册表持引用惰性查找,register 晚于构造仍生效);
|
|
124
|
+
// catalog 无条件装载(integrations 槽位解析唯一真源 = config/integrations.json)
|
|
125
|
+
// 011 B2:可变认证策略注册表——模块 register 期注册(厂商签名策略),transportFactory 同引用
|
|
126
|
+
const authStrategies = createAuthStrategyRegistry();
|
|
127
|
+
const integrationsConfig = loadIntegrations();
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 域装配查表(W3-1):module-mall-core register 期注册;全部消费点运行期惰性读取
|
|
131
|
+
* (auth/registry 构造早于 ctx 与 registerAll——查表时刻 ∈ listen 后请求/会话链路,装配必已就位)。
|
|
132
|
+
* 无装配(无 integrations.json / 模块未装)→ undefined,各消费点按"无控制面"降级(语义对齐迁移前)。
|
|
133
|
+
*/
|
|
134
|
+
function mallAssembly(): MallScopeAssembly | undefined {
|
|
135
|
+
return ctx.domainAssemblies.resolve<MallScopeAssembly>(SCOPE_ASSEMBLY_KIND);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 上下文统一化 Stage 2:validateContext 域校验注入(HTTP body.context 伪冒防线:
|
|
139
|
+
// mallId 形状校验(v4 恒动态)→ 403;只在全局面生效——login/dev-login 走 login-routes 独立归一化)。
|
|
140
|
+
// 与 WS validateScope 同核(isValidMallShape);无装配 → true(非 mall 部署放行)。
|
|
141
|
+
const auth = createAuthRuntime(undefined, {
|
|
142
|
+
validateContext: (attributes) => mallAssembly()?.validateScope(attributes) ?? true,
|
|
143
|
+
});
|
|
144
|
+
|
|
84
145
|
/** 应用根探测(apps/server cwd → 逐层向上找 .tbox/app.json;dev/bundle 双态收敛) */
|
|
85
146
|
function appRoot(): string {
|
|
86
147
|
let dir = path.resolve(process.cwd());
|
|
@@ -93,45 +154,38 @@ function appRoot(): string {
|
|
|
93
154
|
return path.resolve(process.cwd());
|
|
94
155
|
}
|
|
95
156
|
|
|
96
|
-
const
|
|
157
|
+
const providerCatalog = loadProviderCatalog(appRoot());
|
|
97
158
|
|
|
98
|
-
//
|
|
99
|
-
|
|
159
|
+
// provider 拆包 F2/F3:integrations 槽位解析轴(config/integrations.json 存在即生效;
|
|
160
|
+
// 缺席 → undefined,模块槽解析与换码链 fail-fast 显性暴露——旧 ProviderRegistry 链已拆除)
|
|
161
|
+
// instanceIdOf 注入 mall 语义(W3-1:沿域装配查表——mallId 派生归 module-mall-core)
|
|
162
|
+
const integrations = integrationsConfig
|
|
163
|
+
? createIntegrationsRegistry({
|
|
164
|
+
integrations: integrationsConfig.config,
|
|
165
|
+
// catalog 真源 = 应用根 .tbox/app.json(上方无条件装载)
|
|
166
|
+
catalog: providerCatalog,
|
|
167
|
+
authStrategies,
|
|
168
|
+
secretProvider: createEnvironmentSecretProvider({
|
|
169
|
+
source: process.env as Record<string, string | undefined>,
|
|
170
|
+
// 文件通道(v2):config/credentials/ 凭据文件优先(ref = 文件名 stem 直引),env 兜底。
|
|
171
|
+
files: path.resolve(CONFIG_DIR, 'credentials'),
|
|
172
|
+
allowedHeaderNames: ['content-type', 'sign', 'timestamp', 'publickey', 'x-api-key'],
|
|
173
|
+
production: isProduction,
|
|
174
|
+
}),
|
|
175
|
+
instanceIdOf: (scope) => mallAssembly()?.instanceIdOf(scope) ?? '',
|
|
176
|
+
// F5 观测轴:provider factory 收到的 transport 统一包外部调用日志(→/← path durMs)
|
|
177
|
+
logger: createLogger('provider'),
|
|
178
|
+
})
|
|
179
|
+
: undefined;
|
|
100
180
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
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 } : {}),
|
|
181
|
+
// LLM 装配(模型名单点:configPath 入列 CONFIG_DIR 注入家族——dev 档位/生成应用 config 同锚):
|
|
182
|
+
// resolveModelName 与 getModel 同链解析(config modelName ?? env),记录名与实际请求恒一致。
|
|
183
|
+
const { getModel, resolveModelName } = createModelProvider({
|
|
184
|
+
configPath: path.join(CONFIG_DIR, 'llm_provider.json'),
|
|
111
185
|
});
|
|
112
|
-
const
|
|
113
|
-
const pluginTools = createPluginIntegration();
|
|
186
|
+
const AI_MODEL_NAME = resolveModelName();
|
|
114
187
|
|
|
115
|
-
|
|
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
|
-
};
|
|
188
|
+
const pluginTools = createPluginIntegration();
|
|
135
189
|
|
|
136
190
|
/** 懒初始化单例 ConversationService */
|
|
137
191
|
let _conversationService: ConversationService | null = null;
|
|
@@ -168,54 +222,21 @@ if (auth.config.devLoginEnabled) {
|
|
|
168
222
|
|
|
169
223
|
// 自注册闭环:模块路由挂载到 /api/m/<moduleId>/(在 apiRouter 之前,避免被 /api 兜底 404 截获)
|
|
170
224
|
// ctx 为模块级单例:registerAll 在 start() 内执行,ApiRouteRegistry 共享 root 保证延迟注册即时生效
|
|
171
|
-
// 011 L1 + F2/F3
|
|
172
|
-
// provider
|
|
173
|
-
//
|
|
174
|
-
|
|
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
|
-
|
|
225
|
+
// 011 L1 + F2/F3:条件 mall 装配——integrations 槽位轴在上方装配段创建;
|
|
226
|
+
// F2:authExchanges 同引用传递(provider 包 register 期自注册换码器)+ scopeDefaults 中立注入
|
|
227
|
+
// W3-1:scopeDefaults 静态注入退役——域装配(含 defaultMallId)经 registry 读取视图供给
|
|
228
|
+
// (SDK ctx.scopeDefaults 惰性优先注册表 → provider getter 登录期求值)
|
|
211
229
|
const ctx = createServerContext({
|
|
212
230
|
authStrategies,
|
|
213
231
|
authExchanges: auth.exchanges,
|
|
214
232
|
...(memory.store ? { memory: memory.store } : {}),
|
|
215
233
|
...(integrations ? { integrations } : {}),
|
|
216
|
-
...(scopeDefaults ? { scopeDefaults } : {}),
|
|
217
234
|
});
|
|
218
235
|
|
|
236
|
+
// real-env 统一配置链:app-config 服务(模块资源消费入口)——由 registerServerModules
|
|
237
|
+
// 自动装配(W1-④:raw 派生 modules 资源节 + 源目录台账锚;无条件注册、与控制面正交,
|
|
238
|
+
// 防无控制面启动静默丢失)。模板显式注册已拆(W3-1:装配默认兜底覆盖同语义)。
|
|
239
|
+
|
|
219
240
|
// 全局 /api 鉴权(H2 挂载顺序契约):除 public(login/dev-login/health)外一律要求 Bearer token;
|
|
220
241
|
// optional 模式缺失 token → 匿名身份(写通道仍被 action gateway 拒绝)
|
|
221
242
|
// ⚠️ 必须先于 /api/m、/api/action、createPlatformApiRouter 挂载,保证全 API 面受鉴权约束
|
|
@@ -227,12 +248,37 @@ app.use('/api/m', ctx.routes.mount());
|
|
|
227
248
|
// 006 D11:匿名写拒绝,不随 AUTH_MODE 变)
|
|
228
249
|
app.use('/api/action', ctx.actions.middleware());
|
|
229
250
|
|
|
230
|
-
//
|
|
251
|
+
// ===== GET /assets/deploy/:file — 部署态静态资产(.real-env/assets;厂商物料不进中立模板) =====
|
|
252
|
+
// ⚠️ 必须在 createPlatformApiRouter 与 vite 中间件之前注册。文件名白名单防目录穿越。
|
|
253
|
+
const deploymentAssetsDir =
|
|
254
|
+
process.env.DEPLOYMENT_ASSETS_DIR ?? path.resolve(appRoot(), '../../.real-env/assets');
|
|
255
|
+
app.get('/assets/deploy/:file', (req, res) => {
|
|
256
|
+
const file = typeof req.params.file === 'string' ? req.params.file : '';
|
|
257
|
+
if (!/^[A-Za-z0-9._-]+$/.test(file)) {
|
|
258
|
+
res.status(400).end();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
res.sendFile(path.resolve(deploymentAssetsDir, file), (err) => {
|
|
262
|
+
if (err) res.status(404).end();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// API 路由(平台路由面:auth/health/tbox/conversation [+memory] / 404 兜底;卡片恢复内部化)
|
|
231
267
|
// 017:装配记忆时挂载 /api/memory 管理面(与引擎同源 store)
|
|
232
268
|
app.use('/api', createPlatformApiRouter({ ctx, auth, ...(memory.store ? { memory: memory.store } : {}) }));
|
|
233
269
|
|
|
234
270
|
// tbox dev 工具族(/_tbox:卡片预览 API + 页面族;dev-only 自门控——NODE_ENV=production
|
|
235
271
|
// 零挂载;import 期注册先于 dev.ts 运行期 vite middleware,路由序恒胜)
|
|
272
|
+
// iframe 嵌入收紧(AI 构建面板 card-preview 预览):现状无该头 = 隐式全允许 → 显式三元组
|
|
273
|
+
// (用户裁决 2026-09-13:不存在其他 origin 嵌入既有 demo,行为收紧确认;'self' + 生产域 + 本地面板 dev 端口)
|
|
274
|
+
app.use((_req, res, next) => {
|
|
275
|
+
res.setHeader(
|
|
276
|
+
'Content-Security-Policy',
|
|
277
|
+
"frame-ancestors 'self' https://tbox.cn https://*.tbox.cn http://localhost:3000",
|
|
278
|
+
);
|
|
279
|
+
next();
|
|
280
|
+
});
|
|
281
|
+
|
|
236
282
|
installTboxDev(app, { ctx, auth });
|
|
237
283
|
|
|
238
284
|
// 从 ServerContext 构造 ChatEngine(自注册闭环组装端)
|
|
@@ -247,10 +293,15 @@ async function createEngine(
|
|
|
247
293
|
|
|
248
294
|
const engine = await createEngineFromContext(ctx, {
|
|
249
295
|
instructions: AGENT_PROMPT.instructions,
|
|
296
|
+
...(AGENT_PROMPT.memoryInstructions ? { memoryInstructions: AGENT_PROMPT.memoryInstructions } : {}),
|
|
250
297
|
model: getModel(AI_MODEL_NAME),
|
|
251
298
|
traceExporter: tboxTraceExporter,
|
|
252
299
|
modelName: AI_MODEL_NAME,
|
|
253
300
|
fileUrlPrefix: TBOX_FILE_URL_PREFIX,
|
|
301
|
+
// 018 tool-debug:工具调试视图(memory/silent preTool 前端表达;缺省关)
|
|
302
|
+
toolDebug,
|
|
303
|
+
// PR-2 尽早工具执行(缺省 off;env TBOX_EARLY_TOOL_EXECUTION=1 开启)
|
|
304
|
+
earlyToolExecution,
|
|
254
305
|
// 009:双模式全局配置(accelerated 默认:本地路由命中 → reminder 注入当前轮,零额外 LLM 轮次;
|
|
255
306
|
// progressive:全量注册 InlineSkill,模型渐进 skill_read)
|
|
256
307
|
skillMode,
|
|
@@ -258,12 +309,19 @@ async function createEngine(
|
|
|
258
309
|
// 010:前置工具加速(应用级声明 + 单工具预算;TBOX_PRE_TOOL_TIMEOUT_MS=0 整体禁用)
|
|
259
310
|
preTools: APP_PRE_TOOLS,
|
|
260
311
|
preToolTimeoutMs,
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
312
|
+
// 017:长期记忆(默认 inmemory;mall 装配时按 mallId 隔离——scope 经 deployment.assembly
|
|
313
|
+
// buildScope 单点,与 scope 会话/授权签发同源;无 mall 上下文 → 用户级全局记忆)
|
|
314
|
+
...(memory.enabled && memory.store
|
|
315
|
+
? {
|
|
316
|
+
memory: {
|
|
317
|
+
store: memory.store,
|
|
318
|
+
resolveScope: (attributes: Record<string, unknown>) => {
|
|
319
|
+
const scope = mallAssembly()?.buildScope(attributes);
|
|
320
|
+
return scope?.mallId ? { mallId: scope.mallId } : undefined;
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
}
|
|
324
|
+
: {}),
|
|
267
325
|
// 011 B3:卡片动作 token 签发器(mall 装配时注入)
|
|
268
326
|
cardActionAttacher: extra?.cardActionAttacher,
|
|
269
327
|
// B21:意图路由接线(业务意图表 + handlerDispatch)
|
|
@@ -326,24 +384,31 @@ export function registerUpgradeDelegate(delegate: UpgradeDelegate): void {
|
|
|
326
384
|
}
|
|
327
385
|
|
|
328
386
|
export async function start() {
|
|
329
|
-
//
|
|
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
|
-
// 注册业务模块(自注册闭环)
|
|
387
|
+
// 注册业务模块(自注册闭环)——mall-directory 服务由 module-mall-core register 期注入(W3-1)
|
|
335
388
|
await registerAll(ctx);
|
|
336
389
|
|
|
337
390
|
// 模板演示工具(apps 级工具,P3 建议迁入模块):不参与模块体系,经统一注册面注册
|
|
338
391
|
// (010:start() 与 cli/collect-registry.ts 双处同调,防注册面失同步)
|
|
339
392
|
registerAppTools(ctx);
|
|
340
393
|
|
|
394
|
+
// W3-1 时序契约:域装配解析必须晚于 registerAll——module-mall-core 在 register 期经
|
|
395
|
+
// ctx.domainAssemblies 注册装配;解析点置于 start() 头部会查空表(W4 引入回归:
|
|
396
|
+
// 默认商圈兜底/卡片动作授权/enhanceContext 全断——见 Agent Note 2026-09-17-mall-assembly-resolution-order)
|
|
397
|
+
const assembly = mallAssembly();
|
|
398
|
+
|
|
399
|
+
// 011 S6:mall 装配 + 生产环境缺动作授权密钥 → 启动期提示(签发路径运行时兜底报错)
|
|
400
|
+
if (assembly && isProduction && !process.env.ACTION_TOKEN_SECRET && !process.env.AUTH_TOKEN_SECRET) {
|
|
401
|
+
actionLogger.warn('生产环境未配置 ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET——卡片动作授权装配将 fail-fast 拒绝启动');
|
|
402
|
+
}
|
|
403
|
+
|
|
341
404
|
// 011 F2/E2:mall 装配(条件)——scope 会话 + 动作授权先行(engine attacher 依赖)
|
|
342
|
-
|
|
405
|
+
// 014 T1:buildScope 回填 mallName(ChatHeader 品牌区服务端真源;三级回退内聚于域装配)
|
|
406
|
+
// W3-1:域装配经查表(assembly 于上方 registerAll 后解析——解析点必须晚于模块注册)
|
|
407
|
+
const scopeSessions: ReturnType<typeof createScopeSessionRegistry> | undefined = assembly
|
|
343
408
|
? createScopeSessionRegistry({
|
|
344
|
-
extractScopeKey:
|
|
345
|
-
validateScope:
|
|
346
|
-
buildScope:
|
|
409
|
+
extractScopeKey: (c) => assembly.extractScopeKey(c),
|
|
410
|
+
validateScope: (a) => assembly.validateScope(a),
|
|
411
|
+
buildScope: (a) => assembly.buildScopeWithMallName(a),
|
|
347
412
|
})
|
|
348
413
|
: undefined;
|
|
349
414
|
const grants = scopeSessions ? createActionGrantService({ store: ctx.state }) : undefined;
|
|
@@ -369,7 +434,7 @@ export async function start() {
|
|
|
369
434
|
|
|
370
435
|
// 011 F2/E2:模块动作分发(依赖 scopeSessions/grants)
|
|
371
436
|
let moduleActions: ReturnType<typeof createModuleActionDispatcher> | undefined;
|
|
372
|
-
if (
|
|
437
|
+
if (assembly && scopeSessions && grants) {
|
|
373
438
|
moduleActions = createModuleActionDispatcher({
|
|
374
439
|
grants,
|
|
375
440
|
sessions: scopeSessions,
|
|
@@ -379,32 +444,35 @@ export async function start() {
|
|
|
379
444
|
// 015 P3:effect 运行时纯投影(payment/authorization 回调补签在 dispatcher step7,
|
|
380
445
|
// 客户端执行在 SDK host-effect-runner——模板零装配效果链)
|
|
381
446
|
effects: createEffectRuntime(),
|
|
382
|
-
extractScopeKey:
|
|
447
|
+
extractScopeKey: (c) => assembly.extractScopeKey(c),
|
|
383
448
|
});
|
|
384
449
|
for (const action of ctx.cardActions.getAll().values()) {
|
|
385
450
|
moduleActions.register(action);
|
|
386
451
|
}
|
|
387
452
|
}
|
|
388
453
|
|
|
389
|
-
// chat.json
|
|
390
|
-
// 配置单一真源 + settings 运行时下发;chatConfig 无条件传递,非 scope 部署亦发 ack)
|
|
454
|
+
// chat.json 单次装载:chatConfig 下发与 /ws/tts 通道门同值同源(改文件需重启进程,新连接生效)
|
|
391
455
|
const loadedChatConfig = loadChatConfig(CONFIG_DIR);
|
|
456
|
+
const defaultContext = assembly?.defaultMallContext();
|
|
392
457
|
const chatWss = createWsServer(engine, {
|
|
393
458
|
auth: auth.wsAuth,
|
|
394
|
-
// 018 tool-debug:HELLO_ACK 旗标(仅开启时携带;与 engine.toolDebug 同值同源)
|
|
395
|
-
toolDebug,
|
|
396
459
|
...(scopeSessions
|
|
397
460
|
? {
|
|
398
461
|
scopeSessions,
|
|
399
462
|
...(moduleActions ? { moduleActions } : {}),
|
|
400
|
-
// 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
463
|
+
// 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context(显式参数优先)
|
|
464
|
+
// W3-1:defaultContext/enhanceContext 双钩内聚于域装配(mall-core;fail-soft 契约不变:
|
|
465
|
+
// 增强是增益不是闸门——抛错/超时原 context 继续)
|
|
466
|
+
...(defaultContext ? { defaultContext } : {}),
|
|
467
|
+
...(assembly ? { enhanceContext: (c) => assembly.enhanceHelloContext(c) } : {}),
|
|
405
468
|
}
|
|
406
469
|
: {}),
|
|
470
|
+
// real-env 统一配置链:chat.json → HELLO chatConfig(query→prompt 映射 + settings 五键,
|
|
471
|
+
// 剥离扩展字段;映射真源已下沉 SDK server)
|
|
472
|
+
// 无条件传递(chat 配置与控制面正交;loadChatConfig 缺文件/坏格式 fail-soft 回默认)
|
|
407
473
|
chatConfig: toWsChatConfig(loadedChatConfig),
|
|
474
|
+
// 018 tool-debug:HELLO_ACK 旗标(仅开启时携带;与 engine.toolDebug 同值同源)
|
|
475
|
+
toolDebug,
|
|
408
476
|
});
|
|
409
477
|
const ttsWss = createTtsProxy();
|
|
410
478
|
|
|
@@ -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
|
-
*
|
|
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
|
|
76
|
+
return { config, source: p };
|
|
81
77
|
}
|
package/apps/server/src/dev.ts
CHANGED
|
@@ -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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
//
|
|
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
|
-
*
|
|
15
|
-
*
|
|
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, {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { loadAgentPromptConfig } from '@tbox.cn/app-sdk/server';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* agent.jsonc 提示词护栏(经 SDK loader 实读——防配置 typo 静默退化 + 厂商中立锚)。
|
|
11
|
+
* W4 单一模板统一化双段语义:
|
|
12
|
+
* - 缺省出厂 = 空装配(config/ 无 agent.jsonc)→ loader 走 SDK 默认兜底单行(chat 形态零域广告);
|
|
13
|
+
* - mall 画像 = profiles/mall/config/agent.jsonc(--profile mall 经 --config-dir 通道注入)——
|
|
14
|
+
* 四章节/禁工具名/承接句/中立锚断言锚定画像资产文件(create 展开/verify-cli --mall 完整档消费)。
|
|
15
|
+
*
|
|
16
|
+
* 部署态让位(2026-09-10):--config-dir 注入真实厂商档(integrations.json root provider 为
|
|
17
|
+
* 真实厂商名而非 mock)时 agent.jsonc 为部署方品牌人设——中立锚仅守卫出厂/mock 态(mock 档
|
|
18
|
+
* 不注入 agent.jsonc,保持出厂中立)。verify-cli --real-env 真实厂商档完整链依赖此让位。
|
|
19
|
+
*
|
|
20
|
+
* 装配态感知(2026-09-17 评审修复):第一段的 config/ 状态在生成应用是装配变量——mall 完整档
|
|
21
|
+
* 素材注入、support 自含 config(出厂在场)均使 agent.jsonc 在场,恒常量断言失配(verify-cli
|
|
22
|
+
* 完整档 94/95 缺口)。用例改为形态感知:缺席 → 兜底断言(出厂守卫保持);在场 → 读文件断言。
|
|
23
|
+
*/
|
|
24
|
+
describe('agent.jsonc(兜底语义:空装配 → SDK 默认单行;装配态注入 → 读文件)', () => {
|
|
25
|
+
const cfg = loadAgentPromptConfig(path.resolve(__dirname, '../../../config'));
|
|
26
|
+
const injected = existsSync(path.resolve(__dirname, '../../../config/agent.jsonc'));
|
|
27
|
+
|
|
28
|
+
it('config/ 无 agent.jsonc → loader 兜底单行(零域广告);在场(素材注入/自含)→ 读文件非兜底', () => {
|
|
29
|
+
if (injected) {
|
|
30
|
+
expect(cfg.instructions).not.toContain('你是智能助手,帮助用户完成日常问答与业务事务');
|
|
31
|
+
expect(cfg.instructions).toBeTruthy();
|
|
32
|
+
} else {
|
|
33
|
+
expect(cfg.instructions).toContain('你是智能助手,帮助用户完成日常问答与业务事务');
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('agent.jsonc(mall 画像资产:profiles/mall/config)', () => {
|
|
39
|
+
const MALL_CONFIG = path.resolve(__dirname, '../../../profiles/mall/config');
|
|
40
|
+
const cfg = loadAgentPromptConfig(MALL_CONFIG);
|
|
41
|
+
|
|
42
|
+
it('loader 成功加载(非兜底:不含 SDK 默认单行)', () => {
|
|
43
|
+
expect(cfg.instructions).not.toContain('你是智能助手,帮助用户完成日常问答与业务事务');
|
|
44
|
+
expect(cfg.instructions).toBeTruthy();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('首行身份句(不以标题开头)+ 四章节结构', () => {
|
|
48
|
+
const lines = cfg.instructions.split('\n');
|
|
49
|
+
expect(lines[0].startsWith('你是')).toBe(true);
|
|
50
|
+
for (const section of ['# 服务范围', '# 业务规则', '# 沟通风格', '# 兜底承接']) {
|
|
51
|
+
expect(lines).toContain(section);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('禁工具名护栏与承接句规则在场(正文只出白话与结果)', () => {
|
|
56
|
+
expect(cfg.instructions).toContain('任何回复正文不出现工具名、接口名、字段名');
|
|
57
|
+
expect(cfg.instructions).toContain('需要提及操作时用业务语言');
|
|
58
|
+
expect(cfg.instructions).toContain('先用一句贴合本轮诉求的话承接再发起');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('示例工具名只出现在禁令行,不作为正面示例扩散到其他行', () => {
|
|
62
|
+
const lines = cfg.instructions.split('\n').filter((line) => line.includes('listVehicles') || line.includes('customer-service-faq'));
|
|
63
|
+
expect(lines).toHaveLength(1);
|
|
64
|
+
expect(lines[0]).toContain('不出现工具名');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('pre-tool 定性覆盖实际注入面(FAQ 与碰一碰)', () => {
|
|
68
|
+
expect(cfg.instructions).toContain('客服 FAQ 条目、碰一碰活动详情');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('厂商中立:无品牌人设名(词表字面量零出现;厂商档部署 = 部署方品牌人设让位)', () => {
|
|
72
|
+
// 字面量拼接拆分:guard-vendor-neutrality 正则匹配源码行——拆开后门禁天然不命中,
|
|
73
|
+
// 免行内豁免标记;勿合并为连续字面量(会触发自家门禁)
|
|
74
|
+
expect(cfg.instructions).not.toContain('万' + '小豆');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('memoryInstructions 在场(记忆域措辞随配置)', () => {
|
|
78
|
+
expect(cfg.memoryInstructions).toBeTruthy();
|
|
79
|
+
expect(cfg.memoryInstructions).toContain('长期记忆');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// 卡片 / 工具 registry
|
|
2
|
-
// 通过 registerAll(ctx) 收集真实注册状态,替代原手写 registry 静态断言。
|
|
1
|
+
// 卡片 / 工具 registry 一致性守护测试(自注册闭环版,mall 模板)。
|
|
3
2
|
// v2 出卡协议:tool→card 绑定(allowedTools/cardResolver)退场——出卡由工具出参
|
|
4
3
|
// cards 字段运行期声明,registry 仅余 cardType 注册与 dataSchema 校验职责。
|
|
5
|
-
// 注:C 方案下卡片渲染在 client(React 组件),server 无 template.html,故移除原 C12(模板占位符 ∈ schema)。
|
|
6
4
|
import { describe, it, expect } from 'vitest';
|
|
7
5
|
import { diagnosePlugins } from '@tbox.cn/agent-plugin/doctor';
|
|
8
6
|
import { createServerContext, createPluginIntegration } from '@tbox.cn/app-sdk/server';
|