@tbox.cn/app-template-agent 0.1.0 → 0.7.4
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 +6 -0
- package/.tbox/app.json +12 -3
- package/AGENTS.md +17 -6
- package/ENV.md +38 -5
- package/README.md +131 -5
- package/apps/client/package.json +3 -2
- package/apps/client/src/app.tsx +28 -0
- package/apps/client/src/cards/CardRenderer.tsx +12 -2
- package/apps/client/src/components/ActionBar/DebugButton.tsx +5 -1
- package/apps/client/src/components/ActionBar/index.tsx +7 -1
- package/apps/client/src/components/ChatInput/index.less +54 -0
- package/apps/client/src/components/ChatInput/index.tsx +33 -9
- package/apps/client/src/components/ChatInput/voice/useVoicePress.ts +20 -1
- package/apps/client/src/components/MessageBubble/index.less +12 -1
- package/apps/client/src/components/MessageBubble/index.tsx +16 -8
- package/apps/client/src/components/PullDownIndicator/index.less +1 -1
- package/apps/client/src/components/PullDownIndicator/index.tsx +3 -4
- package/apps/client/src/components/QuickCommands/index.tsx +9 -2
- package/apps/client/src/components/ToolCallBlock/index.less +40 -0
- package/apps/client/src/components/ToolCallBlock/index.tsx +84 -22
- package/apps/client/src/hooks/useMediaUpload.ts +7 -2
- package/apps/client/src/main.tsx +4 -1
- package/apps/client/src/pages/index/index.tsx +35 -13
- package/apps/client/src/platform/AgentChatApp.tsx +5 -1
- package/apps/client/src/store/chat-config.ts +12 -1
- package/apps/client/src/store/chat.ts +72 -71
- package/apps/client/vite.config.ts +46 -26
- package/apps/server/package.json +5 -10
- package/apps/server/src/app-tools.ts +13 -0
- package/apps/server/src/app.ts +452 -0
- package/apps/server/src/cli/collect-registry.ts +11 -9
- package/apps/server/src/config/deployment.ts +47 -0
- package/apps/server/src/config/integration.ts +81 -0
- package/apps/server/src/dev.ts +81 -0
- package/apps/server/src/index.ts +24 -219
- package/apps/server/src/intent-routes.ts +11 -4
- package/apps/server/src/modules.ts +12 -5
- package/apps/server/src/pre-tools.ts +25 -0
- package/apps/server/src/tools/clock.ts +24 -0
- package/apps/server/src/tools/translate.ts +10 -4
- package/apps/server/tests/cards-registry.test.ts +9 -17
- package/apps/server/tests/deployment-loader.test.ts +140 -0
- package/apps/server/tests/intent-dispatcher.test.ts +58 -0
- package/apps/server/tests/tool-runtime.test.ts +124 -22
- package/apps/server/tsup.config.ts +15 -2
- package/config/chat.json +4 -1
- package/config/llm_provider.json +3 -1
- package/package.json +19 -11
- package/packages/contracts-mall/package.json +30 -0
- package/packages/contracts-mall/src/index.ts +1 -0
- package/packages/contracts-mall/src/runtime.ts +1 -0
- package/packages/contracts-mall/src/server/index.ts +1 -0
- package/packages/contracts-mall/tsconfig.json +7 -0
- package/packages/module-weather/package.json +6 -5
- package/packages/module-weather/skills/weather-guide/SKILL.md +22 -0
- package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +5 -0
- package/packages/module-weather/src/server/cards/weather/meta.ts +0 -1
- package/packages/module-weather/src/server/index.ts +27 -1
- package/packages/module-weather/src/server/pre-tools.ts +20 -0
- package/packages/module-weather/src/server/tool.ts +24 -24
- package/packages/provider-custom/AGENTS.md +33 -0
- package/packages/provider-custom/LICENSE +21 -0
- package/packages/provider-custom/README.md +8 -0
- package/packages/provider-custom/package.json +62 -0
- package/packages/provider-custom/src/index.ts +5 -0
- package/packages/provider-custom/src/server/index.ts +40 -0
- package/packages/provider-custom/tbox.module.json +29 -0
- package/packages/provider-custom/tests/provider-custom.test.ts +17 -0
- package/packages/provider-custom/tsconfig.json +7 -0
- package/packages/provider-custom/tsup.config.ts +18 -0
- package/pnpm-lock.template.yaml +6297 -0
- package/pnpm-workspace.template.yaml +45 -45
- package/scripts/build.sh +6 -12
- package/scripts/check-workspace.mjs +11 -10
- package/scripts/stop.sh +48 -14
- package/scripts/verify-generated.mjs +4 -4
- package/tsconfig.base.json +1 -0
- package/apps/client/babel-plugins/code-inspect.cjs +0 -110
- package/apps/client/src/hooks/usePullDownHistory.ts +0 -97
- package/scripts/check-inspector.sh +0 -91
- /package/packages/module-weather/{tbox.component.json → tbox.module.json} +0 -0
package/.gitignore
CHANGED
package/.tbox/app.json
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"templateVersion": "7.
|
|
3
|
-
"npmModules": [
|
|
4
|
-
|
|
2
|
+
"templateVersion": "7.1.0",
|
|
3
|
+
"npmModules": [
|
|
4
|
+
{
|
|
5
|
+
"id": "provider-custom",
|
|
6
|
+
"package": "@tbox.cn/app-provider-custom",
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"mode": "codegen",
|
|
9
|
+
"baseVersion": "0.1.0"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"miniappContainers": []
|
|
13
|
+
}
|
package/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
template-agent 是多包 pnpm workspace:`apps/` 薄组装端 + `packages/` 模块。客户端同时运行在浏览器和支付宝小程序 web-view 壳中。
|
|
4
4
|
|
|
5
5
|
> **工作区上下文(发布态消费,005 v0.4.0)**:仓库内本目录是**根 workspace 成员子树**(无独立 `pnpm-workspace.yaml`),
|
|
6
6
|
> 平台引用 `catalog:` + 根 `pnpm-workspace.yaml` `overrides: workspace:*` 回指根 `packages/` 真源;`pnpm` 命令在根上下文执行。
|
|
@@ -26,7 +26,7 @@ tbox-agent-app 是多包 pnpm workspace:`apps/` 薄组装端 + `packages/` 模
|
|
|
26
26
|
`register` 内经 `ctx.cards.registerMap(cards)` 注册卡片组件。
|
|
27
27
|
- `cards` map 的 **key 约定 = meta.cardType**(server 端 meta 的 cardType;doctor 会校验),无卡片模块写 `cards: {}`;
|
|
28
28
|
模块对象必须用 `satisfies`(不能 `: ServerModule` 标注——标注会把 cards 泛化导致类型推导失效)。
|
|
29
|
-
- 每个模块根目录有 `tbox.
|
|
29
|
+
- 每个模块根目录有 `tbox.module.json`(schema v1)声明 contributes(tools/cards/handlers/routes/pages/tabs)。
|
|
30
30
|
跨模块写操作走 `ctx.services.register/resolve` + 类型化事件总线,业务代码只 import `@tbox.cn/app-contracts`(或领域契约包),不 import `apps/*`。
|
|
31
31
|
|
|
32
32
|
## 装配文件(双锚点,双端逐行同构)
|
|
@@ -55,7 +55,7 @@ apps/server/src/index.ts # 服务端入口(HTTP + WS + Vi
|
|
|
55
55
|
apps/server/src/modules.ts # 服务端模块装配(registerAll)
|
|
56
56
|
packages/app-sdk/src/server/chat-engine.ts # ChatEngine 工厂
|
|
57
57
|
packages/app-sdk/src/server/context.ts # ServerContext(注册表聚合 + createServerContext)
|
|
58
|
-
packages/app-sdk/src/server/build-engine.ts # createEngineFromContext / createSdkToolRuntime
|
|
58
|
+
packages/app-sdk/src/server/build-engine.ts # createEngineFromContext / createSdkToolRuntime(出卡 capture 在 engine 构造期包装,见 tool-cards.ts)
|
|
59
59
|
packages/app-sdk/src/client/chat-store.ts # ChatStore 工厂
|
|
60
60
|
packages/contracts/src/ # 跨端类型契约
|
|
61
61
|
config/ # chat/model/plugin 配置
|
|
@@ -75,11 +75,11 @@ pnpm plugin:doctor
|
|
|
75
75
|
pnpm plugin:inspect <pluginId> <toolId>
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
starter 文档:`tbox-app starter --type agent`;可用类型:`tbox-app starter --list`。在沙箱中使用 `tbox-app create . --name <app-name> --source @tbox.cn/app-template-agent` 原地初始化,应用模板的 `AGENTS.md` 会覆盖 starter seed。模块安装/装配由 `tbox-app` CLI(`@tbox.cn/app-cli`)管理:`module add --mode local|codegen|sdk --source <路径|tgz|registry包> / remove / list / sync / diff / doctor / update / publish`。add/remove/sync 后需 `pnpm install`。未安装 CLI 时:`pnpm add -g @tbox.cn/app-cli`(registry 安装)。
|
|
79
79
|
|
|
80
80
|
## 平台基础设施
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
以下链路受保护,不得删除或绕过(守卫随 `@tbox.cn/app-sdk` 分发:`pnpm check:inspector` / `tbox-inspector-check`,已入 `pnpm verify` 链):
|
|
83
83
|
|
|
84
84
|
- `@tbox.cn/app-sdk/platform` 的 `installInspector`(`@infrastructure-locked`;`apps/server/src/index.ts` 从 `@tbox.cn/app-sdk/platform` import 并调用 `installInspector(app)`)
|
|
85
85
|
- `apps/client/index.html` 中的 `/__vs_inspector__.js`(`RUNTIME_PATH` 约定不变)
|
|
@@ -95,7 +95,18 @@ pnpm plugin:inspect <pluginId> <toolId>
|
|
|
95
95
|
- 平台"插件"页添加插件时只改 `config/plugins.json`。应用代码不复制插件鉴权、参数注入、runtime ID 或 SDK 客户端。
|
|
96
96
|
- 插件结果出卡时按 `packages/contracts/src/cards.ts` 注册 schema,在模块包内注册 adapter 和 client 组件。
|
|
97
97
|
|
|
98
|
+
## web-view 样式约定(014,POC 沉淀)
|
|
99
|
+
|
|
100
|
+
客户端同时跑浏览器与支付宝小程序 web-view 壳(组件挂载于 H5 视口,非小程序原生渲染),样式遵守:
|
|
101
|
+
|
|
102
|
+
1. **长度单位 PX 大写**(如 `800PX`)——web-view 内联字体放大:小写 `px` 被浏览器 rem 缩放改写,大写跳过改写保持视觉尺寸。
|
|
103
|
+
2. **flex 子项设 `flex-shrink: 0`**——壳内内容宽度不定,防元素被压缩变形。
|
|
104
|
+
3. **滚动容器隐藏滚动条**(`::-webkit-scrollbar { display: none }`)——web-view 无原生滚动条,出现即破相。
|
|
105
|
+
4. **颜色用 `rgba()` 传统语法**——避免 `color-mix`/`oklch` 等新语法在低版本 web-view 内核不渲染。
|
|
106
|
+
|
|
98
107
|
## 工程边界(check-workspace 校验)
|
|
99
108
|
|
|
100
|
-
- `packages/*` 业务包必须有 `tbox.
|
|
109
|
+
- `packages/*` 业务包必须有 `tbox.module.json`;SDK/契约(`app-sdk` / `contracts*` / `shared`)豁免。
|
|
110
|
+
- `scripts/` 下所有文件保持可执行位(git 100755;CLI create 后 chmod 兜底);双模板 scripts 文件集合镜像全等(guard-workspace R4)。
|
|
101
111
|
- 装配文件不得裸 import(全部 import 行在锚区内);模块包不得 import SDK/契约内部路径(仅公开入口 `@tbox.cn/app-sdk{/server,/client,/platform}` 与 `@tbox.cn/app-contracts`)。
|
|
112
|
+
- 构建序列单源 = 根 `build` / `build:client` script(守卫经 `check` 组合注入);`build.sh` 只管环境 + install + 委托——新守卫改 `check` 组合,不改 build.sh;零依赖守卫恒可 install 前(`check:workspace` 已占此位)。
|
package/ENV.md
CHANGED
|
@@ -4,11 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
## 配置方式
|
|
6
6
|
|
|
7
|
-
- **本地开发**:shell export 注入(每次新终端需重新执行;见文末命令块)
|
|
7
|
+
- **本地开发**:shell export 注入(每次新终端需重新执行;见文末命令块)。生成应用请直接 shell export 或平台配置。
|
|
8
8
|
- **生产**:建议在 tbox 平台页面上配置环境变量(平台托管 env,密钥不进代码仓库、不进 git)。
|
|
9
9
|
生产部署必须 `NODE_ENV=production`(dev-login 强制关闭 + JWT 密钥链 fail-fast 的依据)。
|
|
10
|
+
入口分离后 dist 恒静态供给(vite dev 仅存在于源码 `src/dev.ts` 本地开发入口);`NODE_ENV` 不再决定
|
|
11
|
+
客户端供给形态——测试环境可用 `node dist/index.js`(不设 NODE_ENV)获得「静态产物 + dev-login」。
|
|
10
12
|
- 以下变量缺失时服务可启动,但对话/会话/上传链路会 503。
|
|
11
13
|
|
|
14
|
+
## 配置文件:config/chat.json(会话 UI 设置)
|
|
15
|
+
|
|
16
|
+
六字段(`quickCommands`/`enableImageUpload`/`enableVideoUpload`/`enableTTS`/`enableVoiceInput`/
|
|
17
|
+
`defaultInputMode`)由 server 经 `loadChatConfig` 读取(zod fail-soft),WS HELLO chatConfig
|
|
18
|
+
**服务端运行时下发单一真源**:`quickCommands` 映射 query→prompt,其余五键经 `settings` 下发 →
|
|
19
|
+
client 水合覆盖构建期快照;`/ws/tts` 通道门同源(`enableTTS:false` 时 upgrade 硬拒)。
|
|
20
|
+
dev 态 `TBOX_CONFIG_DIR` 可指档位目录替换默认 `config/`。**改 chat.json 需重启 server,新连接生效**
|
|
21
|
+
(client 构建期静态 import 仅作 ack 未达/键缺席的回落快照)。
|
|
22
|
+
|
|
23
|
+
## 配置文件:config/agent.jsonc(agent 提示词)
|
|
24
|
+
|
|
25
|
+
`instructions` 业务段行数组(首行身份句)+ `memoryInstructions` 可选(装配长期记忆时由 SDK 追加)。
|
|
26
|
+
未配置时 SDK 默认提示词兜底;解析失败 ERROR 日志 + 兜底。改文件需重启 server。(本模板出厂不携带
|
|
27
|
+
该文件——即 SDK 默认形态)
|
|
28
|
+
|
|
12
29
|
## 必需
|
|
13
30
|
|
|
14
31
|
### 模型服务(OpenAI 兼容接口)
|
|
@@ -17,7 +34,9 @@
|
|
|
17
34
|
|---|---|
|
|
18
35
|
| `APP_AI_BASE_URL` | 模型服务地址(OpenAI 兼容 `/v1`) |
|
|
19
36
|
| `APP_AI_API_KEY` | API 密钥 |
|
|
20
|
-
| `APP_AI_MODEL_NAME` | 模型名称;可留空:`config/llm_provider.json` 的 `modelName`
|
|
37
|
+
| `APP_AI_MODEL_NAME` | 模型名称;可留空:`config/llm_provider.json` 的 `modelName` 优先。同文件 `thinking` 字段控制模型思考,优先级高于下方 env 开关 |
|
|
38
|
+
| `APP_AI_THINKING` | 模型思考开关 `true`/`false`/`auto`,缺省 `false`(代码级默认——删除配置文件/字段≠开启思考);`config/llm_provider.json` 的 `thinking` 字段优先级更高,且额外支持 object 形态(字面量合并进请求体顶层,可覆盖 `model`/`messages` 等键——慎用);`"auto"` 为不注入逃生舱;每请求热读,改配置即生效无需重启 |
|
|
39
|
+
| `APP_AI_PARALLEL_TOOL_CALLS` | 并行工具调用 `true`/`false`/`auto`,缺省 `auto`(不注入,走网关按模型默认);`config/llm_provider.json` 的 `parallelToolCalls` 字段优先级更高;每请求热读,改配置即生效无需重启 |
|
|
21
40
|
|
|
22
41
|
### tbox 平台凭证
|
|
23
42
|
|
|
@@ -25,16 +44,26 @@
|
|
|
25
44
|
|---|---|
|
|
26
45
|
| `TBOX_API_KEY` | 平台 API Key:插件直接使用;语音/上传/反馈/TTS 靠它换 session。缺失时插件不加载,`GET /api/tbox/session` 返回 503 → 前端平台能力适配器建不起来 |
|
|
27
46
|
| `TBOX_APP_ID` | 应用 ID |
|
|
47
|
+
| `TBOX_MEMORY_KB_ID` | 长期记忆知识库 ID(`TBOX_MEMORY_STORE=knowledge` 时与 `TBOX_MEMORY_KB_DOC_ID`/平台凭证三缺一回退 inmemory;SDK `bootstrapMemoryKnowledgeBase()` 幂等建库产出固化(生成应用内 `node -e` 调用或联系平台)) |
|
|
48
|
+
| `TBOX_MEMORY_KB_DOC_ID` | 记忆知识库文档 ID(同上必填;addSegment/容量淘汰通道入参) |
|
|
49
|
+
| `TBOX_MEMORY_KB_BASE_URL` | 知识库通道地址(缺省 https://o.tbox.cn;预发联调 https://agtgw-pre.alipay.com) |
|
|
50
|
+
| `TBOX_MEMORY_KB_COOKIE` | 预发访问管控 Cookie 透传(仅预发联调;生产不设——配置会触发 warn。多应用同租户共用知识库时勿共用缺省 KB 名,建库脚本已按 appId 命名) |
|
|
51
|
+
| `TBOX_MEMORY_KB_VALIDATE` | knowledge 启动校验开关(`=1` 异步校验——不阻塞启动;KB 回读不符/超时 → 记忆功能降级:调用时报错+HTTP 503,防 kbId 误配静默击穿跨用户隔离且不拖垮系统;`strict` 同步阻塞校验,不符即启动失败——零窗口合规场景;未设置不校验。仅 knowledge 三件套齐备时生效;生产启用建议 `=1`) |
|
|
52
|
+
| `TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS` | 校验整体超时毫秒数(缺省 10000;`>=1` 才生效,非法回退缺省。超时按校验失败定性——降级态修复 KB 配置后重启恢复) |
|
|
53
|
+
| `TBOX_MEMORY_RECALL_TIMEOUT_MS` | recall 整体 deadline 毫秒数(缺省 200;`0` = 禁用召回注入。B4 起 withRecallCache 装饰器缺省装配——零记忆用户负缓存短路 recall <10ms,真查询单 RTT 约 150-300ms,超时降级跳过不拖首字) |
|
|
54
|
+
| `TBOX_LLM_AFFINITY` | 会话亲和大容量旋钮(缺省 on;`=off` 关闭——fetch 头 `X-Affinity-Session` 与 body `prompt_cache_key` 双通道均不注入。best-effort 语义:网关负载可覆盖亲和;排障时若网关对未知 body 字段报错可关) |
|
|
55
|
+
| `TBOX_LLM_CONN_BIND` | per-session 连接绑定旋钮(缺省 on;`=off` 关闭——LLM fetch 回退全局连接池。连接=网关缓存池路由单位:per-session 单连接(120s keep-alive)钉死集群分配,保活跨 run 提升前缀缓存命中;排障时可关) |
|
|
56
|
+
| `TBOX_LLM_COOKIE_AFFINITY` | 网关 cookie 亲和旋钮(缺省 on;`=off` 关闭——不回传网关 set-cookie。回传激活 LB 粘性:连接重建后恢复原集群的通道;best-effort) |
|
|
28
57
|
|
|
29
58
|
## 统一鉴权(006)
|
|
30
59
|
|
|
31
60
|
| 变量 | 说明 |
|
|
32
61
|
|---|---|
|
|
33
|
-
| `AUTH_TOKEN_SECRET` | 自有 JWT 密钥(≥32 字节随机串)
|
|
62
|
+
| `AUTH_TOKEN_SECRET` | 自有 JWT 密钥(≥32 字节随机串),生产由平台注入。006 鉴权与 011 模块动作授权(action-grant)共用统一密钥链:未配置回退 `TBOX_API_KEY`(须 ≥32 字节)→ dev 临时随机;prod 整链空则 fail-fast(拒绝启动)。011 另有 `ACTION_TOKEN_SECRET` 覆盖档。本地一般无需设置 |
|
|
34
63
|
| `AUTH_MODE` | `required`(默认,缺失 token 一律 401)/ `optional`(缺失 token → 匿名,写通道仍拒绝) |
|
|
35
64
|
| `AUTH_DEV_LOGIN_DISABLED` | 非生产环境需关闭 dev-login 时设 `true`(dev-login 非 production 默认开启,生产强制关闭) |
|
|
36
65
|
| `AUTH_TOKEN_TTL_MS` | token 有效期(毫秒,默认 2h) |
|
|
37
|
-
| `AUTH_TOKEN_ISSUER` | token issuer(默认 `
|
|
66
|
+
| `AUTH_TOKEN_ISSUER` | token issuer(默认 `agent`) |
|
|
38
67
|
| `AUTH_EMBED_EXTERNAL_TOKEN` | 是否把外部原始 token 内嵌进自有 JWT(external claim,默认 `true`;`false` 则上游调用需另取) |
|
|
39
68
|
| `AUTH_EXTERNAL_TOKEN_MAX_BYTES` | external 原始 token 上限(bytes,默认 16KB=Node http header 上限)。external claim 使自有 JWT 膨胀 ~1.4x,生产网关 header 上限需 ≥16KB |
|
|
40
69
|
| `AUTH_EXTERNAL_EXCHANGE_URL` | 外部 provider 换发地址;配置后 `/api/auth/login(platform=external)` 走 HMAC 转发换发 |
|
|
@@ -60,6 +89,10 @@
|
|
|
60
89
|
| `ALIPAY_APP_ID` / `ALIPAY_APP_PRIVATE_KEY` | 支付宝小程序 Auth(login code 换 openId;按需)。私钥为支付宝应用私钥 RSA2 PEM |
|
|
61
90
|
| `WX_APP_ID` / `WX_APP_SECRET` | weapp 换码登录(本地 provider,按需) |
|
|
62
91
|
| `TTS_AUTH_URL` / `TTS_APPKEY` | TTS 鉴权必需:AUTH_URL 为鉴权网关端点(POST `{appkey}` → `timestamp+digest`);APPKEY 为 TTS 应用密钥。**缺失时 TTS 播放直接 CONFIGURATION_ERROR(无「走平台」回退)**,前端报 `[TTS] server error` |
|
|
92
|
+
| `TBOX_SKILL_MODE` | 本地 Skill 模式(009):`accelerated`(默认,本地路由命中 → SOP 注入当前轮 user 消息,零额外 LLM 轮次)/ `progressive`(全量注册 InlineSkill,模型渐进 skill_read)。非法值回退默认 |
|
|
93
|
+
| `TBOX_PRE_TOOL_TIMEOUT_MS` | 前置工具(010)单工具总预算 ms(decide+execute 共享):默认 `2000`;`0` = 整体禁用(预执行全关,行为与未启用逐字节一致)。非法值回退默认 |
|
|
94
|
+
| `TBOX_TOOL_DEBUG` | 工具调试视图(018):`1` 开 / `0` 关;缺省 = 非 `NODE_ENV=production` 开启。开启时 memory 召回/silent 前置工具以伪工具事件下发前端(不持久化)且 HELLO_ACK 携带旗标解锁客户端 dev 工具视图(出入参展开+紧凑格式)。**生产勿开**——测试环境开启会使 silent 检索 query 与用户记忆预览(截断 200 字符)对页面访问者可见。非法值回退缺省 |
|
|
95
|
+
| `TBOX_EARLY_TOOL_EXECUTION` | 尽早工具执行(缺省 `off`):`1`/`true` 开启。模型流中白名单工具(只读查询类,`withEarlyExecution` 标记)参数流完即预执行,多工具轮延迟优化;收益依赖 `parallelToolCalls` 开启;参数漂移自动作废重执行(正确性无损);单工具轮行为等价现状。仅显式 opt-in,观测 journal `tool_preexec` |
|
|
63
96
|
| `HOST` | 服务监听地址(默认 `0.0.0.0`) |
|
|
64
97
|
| `PORT` | 服务监听端口(默认 `8000`) |
|
|
65
98
|
|
|
@@ -73,5 +106,5 @@ export TBOX_API_KEY=...
|
|
|
73
106
|
export TBOX_APP_ID=...
|
|
74
107
|
# TTS(可选;缺失时 TTS 播放报 CONFIGURATION_ERROR):
|
|
75
108
|
export TTS_AUTH_URL=https://open.tbox.alipay.com/agent/v1/homepage/secret/get
|
|
76
|
-
export TTS_APPKEY=your-tts-appkey #
|
|
109
|
+
export TTS_APPKEY=your-tts-appkey # 敏感值勿提交;本地 shell export 注入(生产由平台注入)
|
|
77
110
|
```
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# template-agent
|
|
2
2
|
|
|
3
3
|
tbox AI Agent 全栈模板(npm 包名 `@tbox.cn/app-template-agent`),多包 pnpm workspace:`apps/` 薄组装端 + `packages/` 模块。
|
|
4
4
|
客户端同时支持浏览器和支付宝小程序 web-view;平台能力(鉴权/会话/TTS/ASR/上传/反馈)经 `@tbox.cn/app-sdk` 统一提供(007 下沉)。
|
|
@@ -14,11 +14,11 @@ pnpm verify # typecheck + test + build
|
|
|
14
14
|
> 环境变量:本地开发用 shell export 注入(见 `ENV.md`,含必需变量命令块;每次新终端需重新执行),
|
|
15
15
|
> 生产建议在 tbox 平台页面上配置(平台托管 env,密钥不进代码仓库)。
|
|
16
16
|
|
|
17
|
-
> 平台包消费(005 v0.4.0
|
|
17
|
+
> 平台包消费(005 v0.4.0 发布态):生成应用初始通过 `@app/contracts-mall` shim 使用 `catalog:` 引用的发布包;添加依赖 contracts-mall 的 codegen 模块后,CLI 将 shim 替换为本地源码包——
|
|
18
18
|
> 生产(registry 已发布)直接 `pnpm install`;平台包未发布 npm 时,由 `tbox-app create --local-deps <平台 tgz 目录>`
|
|
19
19
|
> 注入 overrides(`file:` 本地发布态产物)后 install 离线可用。
|
|
20
20
|
>
|
|
21
|
-
> 依赖链说明(007):`@tbox.cn/app-sdk` 新增 dependencies `@tbox-dev-js/sdk`(平台能力客户端)、`@ai-sdk/openai`(LLM)——
|
|
21
|
+
> 依赖链说明(007):`@tbox.cn/app-sdk` 新增 dependencies `@tbox-dev-js/sdk`(平台能力客户端)、`@ai-sdk/openai-compatible`(LLM)——
|
|
22
22
|
> 生成应用 install 时由 pnpm 自动解析(`@tbox-dev-js/sdk` 已发布 npm;内网/离线环境需 registry 镜像或 store 缓存兜底)。
|
|
23
23
|
|
|
24
24
|
> 🔐 鉴权(006):非 production 默认开启 dev-login(浏览器 H5 测试登录,开箱即用);
|
|
@@ -28,10 +28,11 @@ pnpm verify # typecheck + test + build
|
|
|
28
28
|
## 工程结构
|
|
29
29
|
|
|
30
30
|
```text
|
|
31
|
-
|
|
31
|
+
template-agent/
|
|
32
32
|
apps/
|
|
33
33
|
client/ React/Vite 装配端(业务页面/组件/hooks/样式;平台能力经 @tbox.cn/app-sdk/client)
|
|
34
34
|
server/ Express + WS 装配端(~120 行装配:auth 中间件 + 模块挂载 + 平台路由面/TTS/WS)
|
|
35
|
+
server/skills/ 本地 Skill(SKILL.md 目录格式,随模板复制/pack)
|
|
35
36
|
packages/
|
|
36
37
|
module-weather/ 示例业务模块(双端单包)
|
|
37
38
|
config/ chat/model/plugin 配置
|
|
@@ -39,13 +40,138 @@ tbox-agent-app/
|
|
|
39
40
|
pnpm-workspace.template.yaml 应用 workspace 模板(create 生成应用 pnpm-workspace.yaml 的来源)
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
## 本地 Skill(009)
|
|
44
|
+
|
|
45
|
+
应用与模块可为 Agent 提供受控技能(SOP + 参考资料),支持双模式(全局配置,运行期恒定):
|
|
46
|
+
|
|
47
|
+
| 模式 | 环境变量 | 行为 | 适用 |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| **accelerated(默认)** | `TBOX_SKILL_MODE=accelerated` | 本地打分路由(零 LLM)命中 → `<system-reminder skill="...">SOP+references</system-reminder>` 注入**当前轮 user 消息前部**;同 skill 会话内最多一次;system prompt 与工具列表恒定 → 前缀缓存稳定、零 skill 轮次 | 大多数业务场景 |
|
|
50
|
+
| **progressive** | `TBOX_SKILL_MODE=progressive` | Agent 全量注册 InlineSkill,模型按需 skill_read 渐进读取(mastra 原生) | 需要模型自主挑选技能的复杂场景 |
|
|
51
|
+
|
|
52
|
+
### 应用级 Skill(目录格式)
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
apps/server/skills/<skill-name>/
|
|
56
|
+
├── SKILL.md # frontmatter(name/description/metadata.routingTerms)+ SOP 正文
|
|
57
|
+
└── references/ # 可选,递归 *.md(随 SKILL.md 内存化加载,运行期零文件系统暴露)
|
|
58
|
+
├── a.md
|
|
59
|
+
└── sub/b.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
模板已内置 `shopping-guide` 示例(含子目录 references)。新增步骤:建目录 → 写 SKILL.md(`name` 须匹配 `^[a-z0-9-]{1,64}$`)→ references 可选 → 启动加载(无需改代码)。
|
|
63
|
+
|
|
64
|
+
### 模块级 Skill(文件加载,推荐示例)
|
|
65
|
+
|
|
66
|
+
模块包内放 `skills/` 目录(与 `src/` 平级),顶层 await 加载后填回 `serverModule.skills` 声明式通道(装配端 `registerAll` 自动注册,register 内零代码):
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
packages/module-weather/
|
|
70
|
+
├── skills/weather-guide/
|
|
71
|
+
│ ├── SKILL.md # frontmatter(name/description/metadata.routingTerms)+ SOP
|
|
72
|
+
│ └── references/dressing-tips.md
|
|
73
|
+
├── src/server/index.ts
|
|
74
|
+
└── package.json # exports 须含 "./package.json": "./package.json"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// packages/module-weather/src/server/index.ts
|
|
79
|
+
import { loadModuleSkills } from '@tbox.cn/app-sdk/server';
|
|
80
|
+
|
|
81
|
+
const skills = Object.fromEntries(
|
|
82
|
+
(await loadModuleSkills('@app/module-weather', import.meta.url)).map((s) => [s.name, s]),
|
|
83
|
+
) satisfies Record<string, LocalSkill>;
|
|
84
|
+
|
|
85
|
+
export const serverModule = { cards, skills, register(ctx) { /* ... */ } } satisfies ServerModule;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
底层能力(SDK `loadModuleSkills(packageName, import.meta.url)`)**单策略**(createRequire 包名解析)定位模块包根,**dev / build dist / 发布态路径恒等**:以调用文件为基准解析 `${packageName}/package.json`(workspace link / 模块 dist / 应用 tsup bundle dist / node_modules 全形态命中)。前置:模块 exports 须含 `"./package.json"`。加载失败降级 `[]` + 可行动 warn(提示 exports 键/安装问题,不阻断启动)。生成应用内业务模块 codegen 源码调用名由 CLI 自动改写为本地装配包名(`@app/<id>`);contracts-mall 作为平台 codegen 依赖时,模块 import 与依赖键由同一归一化器改写为 `@app/contracts-mall`。
|
|
89
|
+
|
|
90
|
+
inline 声明(`serverModule.skills` 直接内联 instructions/references)仍是发布态模块的轻量选择(无文件系统依赖),两种形态可并存。
|
|
91
|
+
|
|
92
|
+
### 冒烟
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
# 启动日志会打印装配结果:mode + skill 名列表(不含内容)
|
|
96
|
+
[SKILL] mode=accelerated skills=weather-guide,shopping-guide
|
|
97
|
+
# 提问触发词(accelerated):"今天天气怎么样"(weather-guide → getWeather 工具+卡片)/ "3楼有什么"(shopping-guide → SOP)
|
|
98
|
+
# 切换模式:TBOX_SKILL_MODE=progressive
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 前置 Tool 加速执行(010)
|
|
102
|
+
|
|
103
|
+
对「参数可工程化获取、不依赖模型抽参」的**只读工具**,模型请求前先执行、结果注入上下文——正式回答首字从「模型①生成 tool-call → 工具执行 → 模型②作答」缩短为「工具执行 → 模型①直接作答」,**每个预执行工具省一整轮 loop**。
|
|
104
|
+
|
|
105
|
+
### 三步声明法
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// 模块:serverModule.preTools(key = tool.id;装配端自动注册,register 内零代码)
|
|
109
|
+
// 应用:BuildEngineOptions.preTools(apps/server/src/pre-tools.ts)
|
|
110
|
+
// 统一经 definePreTool(tool, spec) 构造——tool 实例引用 = 单一真源(id/description 派生)
|
|
111
|
+
import { definePreTool } from '@tbox.cn/app-sdk/server';
|
|
112
|
+
definePreTool(weatherTool, {
|
|
113
|
+
routing: { mode: 'keywords', terms: ['天气', '气温'] }, // ② 结构化路由:词表打分 / { mode:'always' } 必选
|
|
114
|
+
decide: ({ text, identity }) => { // ③ 决策即构造:run(args) = 预执行 / skip = 不生效
|
|
115
|
+
const city = CITIES.find((c) => text.includes(c));
|
|
116
|
+
return city ? { decision: 'run', args: { city } } : { decision: 'skip' };
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
铁律:仅只读/幂等工具(`requireApproval`/交互式工具不参与);decide 不得抛(SDK 兜底 warn + skip);args 必须 JSON 可序列化(坏形状/非法参数 → warn + skip);身份经入参 `identity`、领域属性经入参 `attributes` 获取(per-run 并发安全——显式入参是唯一通道,`ctx.auth` 全局单例已拆除);`always` 路由每轮进入 decide(慎用,防每轮噪音);框架层**无跨轮去重**(同参可重入,执行策略完全由 decide 工程化自控)。
|
|
122
|
+
|
|
123
|
+
### 行为
|
|
124
|
+
|
|
125
|
+
- **注入**:每预执行工具一个 `<system-reminder pre-tool="...">` 数据块(`<input>`/`<output>` 内嵌 JSON,user 消息前部;**无合成工具消息**——模型不会把结果误判为「已调用过该工具」,可跨轮正常再调用);server-only(不进事件/落库/历史恢复)。
|
|
126
|
+
- **前端**:thinking 气泡内实时显示「自动查询 xx」徽标(执行前即出现 spinner)→「已自动获取 xx」+ 可展开结果详情;刷新后历史消息同样可见。
|
|
127
|
+
- **失败降级**:decide skip / 执行失败 / 超时 → 该工具按普通 LLM 工具路径走,不影响本轮回答;意图路由命中 → 零预执行。
|
|
128
|
+
- **预算**:单工具 decide+execute 共享 `TBOX_PRE_TOOL_TIMEOUT_MS`(默认 2000ms;`0` = 整体禁用);单轮最多 3 个工具并行预执行。
|
|
129
|
+
|
|
130
|
+
### 模板演示
|
|
131
|
+
|
|
132
|
+
| 示例 | 位置 | 形态 |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `getWeather` | `packages/module-weather/src/server/pre-tools.ts` | 文本抽取(城市词表,抽不到 skip)——与 weather-guide skill SOP 共存,单轮完成 |
|
|
135
|
+
| `getCurrentTime` | `apps/server/src/pre-tools.ts` + `tools/clock.ts` | 零参工程化(词表命中即执行) |
|
|
136
|
+
| `queryPoints` | `modules/module-member`(`tbox-app module add` 后) | `always` 必选 + 身份门控(identity 取 userId,匿名 skip) |
|
|
137
|
+
| `translate` | 反例(不改代码) | 参数依赖语义抽取,不适配——保持 LLM 工具路径 |
|
|
138
|
+
|
|
139
|
+
### 冒烟
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
# 启动日志:timeout + 预执行工具清单
|
|
143
|
+
[PRETOOL] timeoutMs=2000 preTools=getCurrentTime,getWeather
|
|
144
|
+
# ①"杭州天气怎么样" → thinking 气泡出现「自动查询 查询城市天气信息」(label = 工具自身 description 派生)→ 直接作答(单轮 1 次模型请求,无 getWeather 二次调用)
|
|
145
|
+
# ②"现在几点了" → 「自动查询 查询当前日期和时间」→ 作答(零参形态)
|
|
146
|
+
# ③"帮我翻译 hello" → 无「自动查询」徽标(反例:正常 LLM 工具路径)
|
|
147
|
+
# ④ TBOX_PRE_TOOL_TIMEOUT_MS=0 复测① → 回退为模型调用工具两轮(对比基准)
|
|
148
|
+
# ⑤ 刷新页面 → 历史消息仍显示「已自动获取」徽标(inputs.preTools 重建)
|
|
149
|
+
# ⑥"杭州天气怎么样"后再问"那纽约呢"(词表外城市) → decide 不命中 → 模型正常调用 getWeather(city=纽约)(无合成工具消息,跨轮不再泛化「已调用过」)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 深链自动发送
|
|
153
|
+
|
|
154
|
+
打开页面时 URL 携带 `?query=...`(或 `?prompt=...` 兜底,`query` 优先)即自动发送该消息一次,
|
|
155
|
+
与手动输入同通道。参数读取后立即从地址栏剥离(replaceState):刷新不重发、分享不带参;
|
|
156
|
+
socket 5 秒内未就绪则静默放弃。SDK 单点实现(`@tbox.cn/app-sdk/client` 的
|
|
157
|
+
`consumeAutoSendQuery`/`runAutoSend`),模板侧仅 3 行接线(IndexPage init effect)。
|
|
158
|
+
|
|
159
|
+
### 冒烟
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
# 浏览器访问 http://127.0.0.1:8000/?query=今天天气怎么样
|
|
163
|
+
# 预期:自动发送一次;地址栏 query 消失(其余参数保留);刷新不重发;dev 模式(StrictMode)不双发
|
|
164
|
+
```
|
|
165
|
+
|
|
42
166
|
## 平台能力定制(007 扩展点)
|
|
43
167
|
|
|
44
168
|
平台轴(安全管道/能力管道/协议客户端)已下沉 `@tbox.cn/app-sdk`,模板保留 3 个注入点 + 模板源码扩展:
|
|
45
169
|
|
|
46
170
|
| 口子 | 位置 | 场景 |
|
|
47
171
|
|---|---|---|
|
|
48
|
-
| `
|
|
172
|
+
| `ctx.authExchanges.register(...)` | `@tbox.cn/app-sdk/server` | 自定义登录换码器(F2 注册制:scoped 谓词路由 / 无条件兜底,机制见 docs/reference/auth.md;静态 `exchanges` 注入仍兼容) |
|
|
173
|
+
| `getToolRequestContext` / `requireToolRequestContext` | `@tbox.cn/app-sdk/server` | 工具 execute 第二参读取运行上下文(fail-soft / 缺失抛 AUTH_FORBIDDEN;上下文统一化) |
|
|
174
|
+
| `createToolExecContext(reqCtx)` | `@tbox.cn/app-sdk/server` | 测试/直调助手:构造工具 execute 第二参(散键与引擎同构) |
|
|
49
175
|
| `createPlatformAdapters(session, overrides)` | `@tbox.cn/app-sdk/client` | 自定义 ASR/TTS/upload/feedback 实现(contracts 接口,UI 零改动) |
|
|
50
176
|
| `createAuthClient({ loginPlatform, devUser })` | `@tbox.cn/app-sdk/client` | 客户端登录配置(`main.tsx`/`platform/agent-runtime.ts`);**应用级单例**——重复调用幂等返回首建实例,首建选项生效 |
|
|
51
177
|
| `configureRuntime({ apiBaseUrl })` | `@tbox.cn/app-sdk/client` | API/WS 地址注入(`main.tsx`;对应 `VITE_API_URL`) |
|
package/apps/client/package.json
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"react-dom": "catalog:",
|
|
20
20
|
"react-markdown": "catalog:",
|
|
21
21
|
"remark-gfm": "catalog:",
|
|
22
|
-
"zustand": "catalog:"
|
|
22
|
+
"zustand": "catalog:",
|
|
23
|
+
"@app/provider-custom": "workspace:*"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@types/node": "catalog:",
|
|
@@ -31,4 +32,4 @@
|
|
|
31
32
|
"typescript": "catalog:",
|
|
32
33
|
"vite": "catalog:"
|
|
33
34
|
}
|
|
34
|
-
}
|
|
35
|
+
}
|
package/apps/client/src/app.tsx
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
+
import { lazy, Suspense } from 'react';
|
|
1
2
|
import BusinessApp from '@/business/App';
|
|
2
3
|
|
|
4
|
+
// dev 卡片预览页(/_tbox/card-preview):
|
|
5
|
+
// - 守卫用**三元**而非 if——`import.meta.env.DEV` 在生产构建被静态替换为 false,
|
|
6
|
+
// lazy 分支成为可摇除死代码,动态 import 与页面 chunk 一并消失(若用顶层 if,
|
|
7
|
+
// lazy() 调用本身活过 treeshaking,chunk 仍会产出);
|
|
8
|
+
// - lazy 双动态 import 组装(SDK 页面 + 模板 registry/authClient 单例注入——
|
|
9
|
+
// authClient 含模板选项,不经预览页自建)。
|
|
10
|
+
const CARD_PREVIEW_PATH = '/_tbox/card-preview';
|
|
11
|
+
const DevCardPreview = import.meta.env.DEV
|
|
12
|
+
? lazy(async () => {
|
|
13
|
+
const [{ CardPreviewPage }, { CARD_REGISTRY }, { authClient }] = await Promise.all([
|
|
14
|
+
import('@tbox.cn/app-sdk/client/card-preview'),
|
|
15
|
+
import('@/cards/registry'),
|
|
16
|
+
import('@/platform/agent-runtime'),
|
|
17
|
+
]);
|
|
18
|
+
return {
|
|
19
|
+
default: () => <CardPreviewPage registry={CARD_REGISTRY} authClient={authClient} />,
|
|
20
|
+
};
|
|
21
|
+
})
|
|
22
|
+
: null;
|
|
23
|
+
|
|
3
24
|
export default function App() {
|
|
25
|
+
if (import.meta.env.DEV && DevCardPreview && window.location.pathname === CARD_PREVIEW_PATH) {
|
|
26
|
+
return (
|
|
27
|
+
<Suspense fallback={<div>加载卡片预览…</div>}>
|
|
28
|
+
<DevCardPreview />
|
|
29
|
+
</Suspense>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
4
32
|
return <BusinessApp />;
|
|
5
33
|
}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 卡片渲染入口 — 注入本地 registry 后委托 SDK 的 CardRenderer。
|
|
3
|
+
* 011 B3:动作按钮 → sendUiAction(token 来自 payload.actionTokens)。
|
|
4
|
+
* P1:onAction 升级为完整 UiAction(CardRenderer 按 kind 分发后回传,桥一行透传)。
|
|
3
5
|
*/
|
|
4
6
|
import { CardRenderer as SdkCardRenderer } from '@tbox.cn/app-sdk/client';
|
|
5
|
-
import type { TboxCardPayload } from '@tbox.cn/app-contracts';
|
|
7
|
+
import type { TboxCardPayload, UiAction } from '@tbox.cn/app-contracts';
|
|
6
8
|
import { CARD_REGISTRY } from './registry';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
7
10
|
|
|
8
11
|
export function CardRenderer({ card }: { card: TboxCardPayload }) {
|
|
9
|
-
|
|
12
|
+
const sendUiAction = useChatStore((s) => s.sendUiAction);
|
|
13
|
+
return (
|
|
14
|
+
<SdkCardRenderer
|
|
15
|
+
card={card}
|
|
16
|
+
registry={CARD_REGISTRY}
|
|
17
|
+
onAction={(action: UiAction) => sendUiAction(card.id, action)}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
10
20
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { createLogger } from '@tbox.cn/app-sdk/client';
|
|
2
|
+
|
|
3
|
+
const debugLogger = createLogger('DebugButton');
|
|
4
|
+
|
|
1
5
|
import debugIcon from '../../../assets/debug.png';
|
|
2
6
|
|
|
3
7
|
/**
|
|
@@ -36,7 +40,7 @@ export function DebugButton({
|
|
|
36
40
|
'*',
|
|
37
41
|
);
|
|
38
42
|
} catch (err) {
|
|
39
|
-
|
|
43
|
+
debugLogger.warn('postMessage failed:', err);
|
|
40
44
|
}
|
|
41
45
|
};
|
|
42
46
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
2
|
+
import { createLogger } from '@tbox.cn/app-sdk/client';
|
|
3
|
+
|
|
4
|
+
const ttsActionLogger = createLogger('tts');
|
|
2
5
|
import { useChatStore } from '@/store/chat';
|
|
3
6
|
import { useChatConfigStore } from '@/store/chat-config';
|
|
4
7
|
import { useFeedbackAdapter, useRefreshSession, useTTSAdapter } from '@tbox.cn/app-sdk/client';
|
|
@@ -98,7 +101,10 @@ export function ActionBar({ message }: { message: ChatMessage }) {
|
|
|
98
101
|
}
|
|
99
102
|
_isPlaying = true;
|
|
100
103
|
setPlayingId(message.id);
|
|
101
|
-
tts.speak(message.content, message.id).catch(() =>
|
|
104
|
+
tts.speak(message.content, message.id).catch((err: unknown) => {
|
|
105
|
+
ttsActionLogger.warn(`tts.speak 失败:err=${(err as Error)?.message ?? String(err)}`);
|
|
106
|
+
setPlayingId(null);
|
|
107
|
+
});
|
|
102
108
|
_isPlaying = false;
|
|
103
109
|
};
|
|
104
110
|
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
.tbox-voice-page-lock,
|
|
2
|
+
.tbox-voice-page-lock * {
|
|
3
|
+
user-select: none !important;
|
|
4
|
+
-webkit-user-select: none !important;
|
|
5
|
+
-webkit-touch-callout: none !important;
|
|
6
|
+
-webkit-user-drag: none !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
.tbox-chat-input {
|
|
2
10
|
position: relative;
|
|
3
11
|
z-index: 1;
|
|
@@ -12,6 +20,36 @@
|
|
|
12
20
|
display: flex;
|
|
13
21
|
align-items: flex-end;
|
|
14
22
|
gap: 16px;
|
|
23
|
+
flex-wrap: wrap;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* 连接终态阻断提示条(禁用输入 + 重试入口;配色沿用既有错误语义,不硬编码新色值) */
|
|
27
|
+
&__blocked {
|
|
28
|
+
flex-basis: 100%;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: 12px;
|
|
32
|
+
padding: 12px 20px;
|
|
33
|
+
border-radius: 12px;
|
|
34
|
+
background: rgba(255, 241, 241, 0.9);
|
|
35
|
+
color: #d5492f;
|
|
36
|
+
font-size: 24px;
|
|
37
|
+
line-height: 1.4;
|
|
38
|
+
|
|
39
|
+
&-msg {
|
|
40
|
+
flex: 1;
|
|
41
|
+
min-width: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&-retry {
|
|
45
|
+
flex-shrink: 0;
|
|
46
|
+
padding: 6px 20px;
|
|
47
|
+
border: none;
|
|
48
|
+
border-radius: 999px;
|
|
49
|
+
background: #d5492f;
|
|
50
|
+
color: #fff;
|
|
51
|
+
font-size: 22px;
|
|
52
|
+
}
|
|
15
53
|
}
|
|
16
54
|
|
|
17
55
|
/* 半透明白色药丸 */
|
|
@@ -44,6 +82,11 @@
|
|
|
44
82
|
&::placeholder {
|
|
45
83
|
color: rgba(16, 41, 83, 0.6);
|
|
46
84
|
}
|
|
85
|
+
|
|
86
|
+
/* 连接终态阻断(登录失败/scope 拒绝):禁用态弱化输入框 */
|
|
87
|
+
&:disabled {
|
|
88
|
+
opacity: 0.45;
|
|
89
|
+
}
|
|
47
90
|
}
|
|
48
91
|
|
|
49
92
|
/* 发送 / 停止(图片图标) */
|
|
@@ -212,6 +255,9 @@
|
|
|
212
255
|
box-sizing: border-box;
|
|
213
256
|
overflow: hidden;
|
|
214
257
|
user-select: none;
|
|
258
|
+
-webkit-user-select: none;
|
|
259
|
+
-webkit-touch-callout: none;
|
|
260
|
+
touch-action: none;
|
|
215
261
|
|
|
216
262
|
&--disabled {
|
|
217
263
|
opacity: 0.35;
|
|
@@ -249,6 +295,10 @@
|
|
|
249
295
|
position: fixed;
|
|
250
296
|
inset: 0;
|
|
251
297
|
z-index: 300;
|
|
298
|
+
user-select: none;
|
|
299
|
+
-webkit-user-select: none;
|
|
300
|
+
-webkit-touch-callout: none;
|
|
301
|
+
touch-action: none;
|
|
252
302
|
|
|
253
303
|
&__mask {
|
|
254
304
|
position: absolute;
|
|
@@ -264,6 +314,9 @@
|
|
|
264
314
|
justify-content: center;
|
|
265
315
|
padding: 0 24px calc(56px + env(safe-area-inset-bottom, 16px));
|
|
266
316
|
box-sizing: border-box;
|
|
317
|
+
user-select: none;
|
|
318
|
+
-webkit-user-select: none;
|
|
319
|
+
touch-action: none;
|
|
267
320
|
}
|
|
268
321
|
|
|
269
322
|
&__panel {
|
|
@@ -328,5 +381,6 @@
|
|
|
328
381
|
background-image: url('https://mdn.alipayobjects.com/huamei_frp8ut/afts/img/A*5PDvR4j7fBgAAAAAVBAAAAgAet-KAQ/original');
|
|
329
382
|
background-size: 100% 100%;
|
|
330
383
|
background-repeat: no-repeat;
|
|
384
|
+
pointer-events: none;
|
|
331
385
|
}
|
|
332
386
|
}
|