magic-agent 1.0.3 → 1.0.49
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/CLIENT-CONTRACT.md +254 -8
- package/README.md +48 -5
- package/client-ui/index.html +32 -0
- package/client-ui/pc/app.css +89 -1
- package/client-ui/pc/index.html +18 -8
- package/client-ui/pc/js/00-base.js +8 -30
- package/client-ui/pc/js/05-task-list-render.js +4 -1
- package/client-ui/pc/js/07-chat-stream-render.js +2 -85
- package/client-ui/pc/js/10-markdown.js +3 -171
- package/client-ui/pc/js/18-askbox.js +2 -176
- package/client-ui/pc/js/90-glue.js +1 -1
- package/client-ui/pc/js/91-workbench-stubs.js +4 -10
- package/client-ui/pc/js/93-rail.js +10 -3
- package/client-ui/shared/conv/01-dom.js +52 -0
- package/client-ui/shared/conv/02-media-kinds.js +34 -0
- package/client-ui/shared/conv/03-chat-message.js +1561 -0
- package/client-ui/shared/conv/06-ask-answer.js +377 -0
- package/client-ui/shared/conv/07-stream-delegation.js +158 -0
- package/client-ui/{pc/js → shared/conv}/09-stream-apply.js +121 -6
- package/client-ui/shared/conv/10-markdown-render.js +252 -0
- package/client-ui/shared/conv/18-askbox-markup.js +185 -0
- package/client-ui/{pc/js → shared/conv}/19-attachments.js +365 -21
- package/client-ui/shared/conv/27-conv-ledger.js +381 -0
- package/client-ui/{pc/js → shared/conv}/29-msg-variants.js +112 -3
- package/client-ui/shared/conv/README.md +257 -0
- package/client-ui/shared/conv/conv.css +1638 -0
- package/client-ui/shared/conv/dom-map.md +170 -0
- package/client-ui/shared/conv/manifest.json +44 -0
- package/client-ui/shared/conversation.css +368 -190
- package/client-ui/shared/conversation.js +1722 -788
- package/client-ui/shared/host-glue.js +143 -0
- package/client-ui/shared/transport-http.js +130 -6
- package/client-ui-mobile/index.html +2417 -103
- package/cmd/magic-client/clientctl.go +273 -0
- package/cmd/magic-client/main.go +71 -5
- package/go.mod +6 -3
- package/go.sum +4 -1
- package/internal/agent/arkclaw.go +3 -2
- package/internal/agent/arkclaw_stream.go +126 -8
- package/internal/agent/arkclaw_stream_test.go +97 -15
- package/internal/agent/arkclaw_test.go +3 -3
- package/internal/agent/attachments.go +18 -5
- package/internal/agent/attachments_test.go +7 -1
- package/internal/agent/capability_test.go +11 -1
- package/internal/agent/claude.go +96 -158
- package/internal/agent/codebuddy.go +108 -133
- package/internal/agent/codebuddy_gateway.go +8 -4
- package/internal/agent/codebuddy_gateway_stream.go +82 -2
- package/internal/agent/codebuddy_gateway_test.go +236 -1
- package/internal/agent/codebuddy_stream_test.go +262 -0
- package/internal/agent/codex.go +50 -9
- package/internal/agent/codex_stream_test.go +30 -0
- package/internal/agent/codex_test.go +24 -0
- package/internal/agent/custom_models.go +109 -3
- package/internal/agent/custom_models_test.go +74 -0
- package/internal/agent/dsh.go +464 -130
- package/internal/agent/dsh_resume.go +258 -0
- package/internal/agent/dsh_sdk.go +68 -17
- package/internal/agent/dsh_sdk_test.go +102 -2
- package/internal/agent/dsh_test.go +409 -36
- package/internal/agent/engine.go +26 -6
- package/internal/agent/engine_base.go +46 -2
- package/internal/agent/engine_base_test.go +29 -0
- package/internal/agent/engine_test.go +101 -0
- package/internal/agent/env.go +69 -1
- package/internal/agent/itemtracker.go +271 -0
- package/internal/agent/launch.go +192 -0
- package/internal/agent/launch_test.go +206 -0
- package/internal/agent/launch_unix.go +15 -0
- package/internal/agent/launch_windows.go +24 -0
- package/internal/agent/limitnotice.go +103 -0
- package/internal/agent/limitnotice_test.go +211 -0
- package/internal/agent/llmengine.go +2 -2
- package/internal/agent/models.go +3 -2
- package/internal/agent/openclaw.go +4 -3
- package/internal/agent/openclaw_acp.go +7 -0
- package/internal/agent/openclaw_test.go +2 -2
- package/internal/agent/permission.go +100 -11
- package/internal/agent/permission_test.go +100 -36
- package/internal/agent/runcmd.go +13 -3
- package/internal/agent/runner.go +7 -1
- package/internal/agent/runner_test.go +1 -1
- package/internal/agent/stream.go +777 -43
- package/internal/agent/stream_test.go +3 -2
- package/internal/agent/stream_timeline_test.go +905 -0
- package/internal/agent/streamjson.go +36 -2
- package/internal/agent/toolkind.go +20 -1
- package/internal/agent/toolkind_test.go +7 -1
- package/internal/agent/tools_suffix_test.go +10 -3
- package/internal/agent/trae.go +5 -3
- package/internal/agent/workspace_test.go +52 -0
- package/internal/cli/ask.go +260 -33
- package/internal/cli/ask_test.go +15 -11
- package/internal/cli/attach_test.go +32 -20
- package/internal/cli/clientcmd.go +156 -0
- package/internal/cli/events.go +53 -8
- package/internal/cli/events_test.go +230 -0
- package/internal/cli/keepalive_test.go +101 -6
- package/internal/cli/permission_test.go +5 -1
- package/internal/cli/root.go +174 -32
- package/internal/cli/session.go +175 -28
- package/internal/cli/session_test.go +144 -5
- package/internal/cli/sessionlog.go +385 -38
- package/internal/cli/sessionlog_test.go +708 -0
- package/internal/cli/sessionlog_user_test.go +142 -1
- package/internal/cli/sessiontitle.go +141 -0
- package/internal/client/browse.go +456 -0
- package/internal/client/browse_test.go +199 -0
- package/internal/client/cors_test.go +35 -0
- package/internal/client/daemon.go +525 -0
- package/internal/client/daemon_test.go +212 -0
- package/internal/client/detach_unix.go +19 -0
- package/internal/client/detach_windows.go +27 -0
- package/internal/client/pair.go +3 -3
- package/internal/client/plugins.go +376 -0
- package/internal/client/plugins_test.go +223 -0
- package/internal/client/proxy.go +374 -0
- package/internal/client/proxy_test.go +212 -0
- package/internal/client/service.go +1464 -48
- package/internal/client/service_test.go +649 -206
- package/internal/client/session_events_test.go +130 -0
- package/internal/client/wsproxy.go +113 -0
- package/internal/client/wsproxy_test.go +102 -0
- package/internal/session/delete.go +151 -0
- package/internal/session/delete_test.go +103 -0
- package/internal/session/session.go +155 -20
- package/internal/session/session_append.go +79 -21
- package/internal/session/session_test.go +124 -10
- package/internal/session/sessionlog.go +572 -6
- package/internal/session/sessionlog_read_test.go +410 -0
- package/internal/session/title.go +486 -0
- package/internal/session/title_test.go +298 -0
- package/npm/bin/magic-agent.js +101 -10
- package/npm/bin/magic-client.js +118 -0
- package/npm/dist/darwin-arm64/magic-agent +0 -0
- package/npm/dist/darwin-arm64/magic-client +0 -0
- package/npm/dist/darwin-x64/magic-agent +0 -0
- package/npm/dist/darwin-x64/magic-client +0 -0
- package/npm/dist/linux-arm64/magic-agent +0 -0
- package/npm/dist/linux-arm64/magic-client +0 -0
- package/npm/dist/linux-x64/magic-agent +0 -0
- package/npm/dist/linux-x64/magic-client +0 -0
- package/npm/dist/win32-x64/magic-agent.exe +0 -0
- package/npm/dist/win32-x64/magic-client.exe +0 -0
- package/npm/install.js +36 -13
- package/npm/lib/platform.js +27 -5
- package/package.json +8 -3
- package/client-ui/pc/js/03-chat-message.js +0 -880
- package/client-ui/pc/js/06-ask-answer.js +0 -204
- /package/client-ui/{pc/js → shared/conv}/08-ask-event-map.js +0 -0
- /package/client-ui/{pc → shared}/vendor/highlight.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/marked.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/purify.min.js +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# 对话内核 `client-ui/shared/conv/` —— 「一条事件流 → 画成对话」的唯一实现
|
|
2
|
+
|
|
3
|
+
**这里只放一份代码。** PC 宿主、移动端宿主、magic-test 工作台都**只使用**它,
|
|
4
|
+
不再各留一份自己的对话渲染/收敛逻辑(历史上正是三份并存、并且已经漂移:
|
|
5
|
+
同一个「工具组头亮着不放」的毛病在三处是三副样子)。
|
|
6
|
+
|
|
7
|
+
## 边界(加文件/加符号前先读)
|
|
8
|
+
|
|
9
|
+
| 属于内核 | 属于宿主(留在各自仓库,通过 `hostSymbols` 供进来) |
|
|
10
|
+
|---|---|
|
|
11
|
+
| 事件 → 画面:消息气泡、思考块、工具组/工具卡、段与「任务耗时」大组、提问卡、收尾行、标记行、分隔条、diff/文件变更/待办清单、附件与截图、对话产物 | 业务壳:会话列表 / 任务库、需求看板、派发与审查轮、文档与用例页、录屏与短视频模式、内嵌浏览器、右栏与工作目录 |
|
|
12
|
+
| 状态与收敛:item 身份 / revision / status、收尾闸门(`closed`)、计数器与计时器 | 样式令牌、外层布局与顶栏、输入区实例配置、事件订阅与 run 路由(`bindHomeAsk` 那一段) |
|
|
13
|
+
| 事件的归一与切分:`qaEventNormalize` / `qaToolUnits` / `qaAskParse` / `qaAskFromEvent` | 传输:`window.desk.*` 或 transport → CLI |
|
|
14
|
+
| 「本轮产物」的收集 / 合并 / 成卡(`qaTurnArtsTrack` / `qaArtMount`,只认 image / video / file) | 产物路径的**存在性探测**(`window.desk.pathProbe({base, paths})`)与**取件地址**(`window.desk.mediaUrl(abs)`)—— 内核不知道存储在哪;宿主没有这两个能力时产物卡片不出现(组件宿主由 `installDeskProbe()` 从 transport 自建,PC 工作台由 Electron 主进程注入) |
|
|
15
|
+
|
|
16
|
+
判据只有一条:**只跟「一条事件流画成对话」有关的进内核;跟工作台业务有关的一律留宿主。**
|
|
17
|
+
宿主没有那条能力时,给的必须是 `notHere('…')` 那种**诚实桩**(明说),不许假装成功。
|
|
18
|
+
|
|
19
|
+
## 怎么用(宿主侧)
|
|
20
|
+
|
|
21
|
+
分片是**同一个全局作用域里的经典脚本**(无 import/export),按 `manifest.json` 的 `order` 拼接:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// magic-test/tools/build-src.cjs —— 内核分片拼在宿主分片之前,语义与「全在一个包里」一致
|
|
25
|
+
const convDir = path.resolve(REPO, '..', 'magic-agent', 'client-ui', 'shared', 'conv');
|
|
26
|
+
const convShards = JSON.parse(fs.readFileSync(path.join(convDir, 'manifest.json'), 'utf8')).order;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<!-- magic-agent/client-ui/pc/index.html —— 直接按相对路径引 -->
|
|
31
|
+
<script src="../shared/conv/03-chat-message.js"></script>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
宿主必须提供 `manifest.json` 的 `hostSymbols` 那批符号(现在 23 个:`escHtml` / `toast` /
|
|
35
|
+
`qaSend` / `qaTasks` / `qaActiveId` / `qaEngine` / `qaModel` / `qaModelName` / `QA_ICONS` /
|
|
36
|
+
`ASK_CFG` / `ASK_INSTANCES` / `qaModelSet` / `qaModelPick` / `qaTaskSetModel` / `qaReqConvMsg` /
|
|
37
|
+
`wsDirOf` / `qaWorkspace` / `reqHas` …)。另有两个**可选**宿主能力不在清单里(缺了只是不画产物卡):
|
|
38
|
+
`window.desk.pathProbe` 与 `window.desk.mediaUrl`。
|
|
39
|
+
|
|
40
|
+
### 命名与「半个文件」的规矩
|
|
41
|
+
|
|
42
|
+
- 被整份搬走的(`03` `06` `08` `19` `29`):内核分片沿用原文件名。
|
|
43
|
+
- 两侧各占一半的(`01` `07` `09` `10` `18` `27`):内核分片**换名**(`01-dom.js`、
|
|
44
|
+
`07-stream-delegation.js`、`09-stream-apply.js`、`10-markdown-render.js`、
|
|
45
|
+
`18-askbox-markup.js`、`27-conv-ledger.js`),宿主那半留在原文件名。
|
|
46
|
+
⚠️ **两边不能同名**:`shardPath()` 先在内核目录找,同名会让宿主那半被静默顶掉
|
|
47
|
+
—— magic-test 的 `build-src.cjs` 与 `check-conv-shards.mjs` 都拦这条。
|
|
48
|
+
|
|
49
|
+
## 校验(改完必跑)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
node tests/check-conv-shards.mjs # 内核自身:无重复声明 + 宿主符号清单与代码一致
|
|
53
|
+
node tests/check-conv-shards.mjs --host-html=client-ui/pc/index.html # PC 宿主页面加载的脚本是否把这批符号都定义了
|
|
54
|
+
node tests/check-conv-shards.mjs --host-dir=../magic-test/src/js # magic-test 宿主(源码分片那侧)
|
|
55
|
+
node tests/check-conv-shards.mjs --write # 按代码现状重写 hostSymbols(加/删符号后跑一次)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`--write` 之后再跑一次普通校验即可(双向一致)。想查某个符号是从哪一行被认出来的:
|
|
59
|
+
`CONV_DEBUG=qaModelName node tests/check-conv-shards.mjs`。
|
|
60
|
+
|
|
61
|
+
## 已经搬进来的 / 还没搬的
|
|
62
|
+
|
|
63
|
+
- 阶段一:`03-chat-message` / `06-ask-answer` / `08-ask-event-map` / `19-attachments` /
|
|
64
|
+
`29-msg-variants`。magic-agent 的 PC 那份独有的能力(待办清单 `qaTodo*`、图片块
|
|
65
|
+
`appendImage` / `subImage`)已并入内核,换内核不会掉功能。
|
|
66
|
+
- 阶段二(2026-10-16):`01-dom`(`$` / `$$` / `htmlWrite` / `qaImeComposing`)、
|
|
67
|
+
`07-stream-delegation`(`bindQaStreamDelegation`,并入 PC 侧的 `.qa-todo-toggle` 分支)、
|
|
68
|
+
`09-stream-apply`(`qaAskFromEvent` / `qaStreamApply` / `qaStreamApplyOne`,并入 PC 侧的
|
|
69
|
+
「任何带 session_id 的事件当场绑锚点」)、`10-markdown-render`(marked + DOMPurify +
|
|
70
|
+
highlight.js + mermaid)、`18-askbox-markup`(输入框标记 + 胶囊)、`27-conv-ledger`
|
|
71
|
+
(台账 / 静态重画 / 回放)。**切分是逐字搬行**(行集守恒),两侧各留一半:
|
|
72
|
+
DOM 工具之外的那些「宿主半」见各自的说明头。
|
|
73
|
+
- 阶段三(进行中,2026-10-16 起步):移动端 / 组件那条线(`shared/conversation.js`,由 PC 宿主
|
|
74
|
+
`client-ui/index.html`、移动宿主 `client-ui-mobile/index.html`、掌天瓶的 `mobile/www` 加载)。
|
|
75
|
+
- **第一步已落地**:工具归类与人话标题(`toolKindOf` / `toolKindMerge` / `toolHumanTitle`)
|
|
76
|
+
从句组件里删掉,改为转发本内核的 `qaToolKindOf` / `qaToolKindMerge` / `qaToolHumanTitle`。
|
|
77
|
+
- **第二步已落地**(同日):① 事件入口接上 `qaEventNormalize` —— 引擎原生的
|
|
78
|
+
`assistant` / `user`(正文与工具调用装在 `message.content[]` 里)以前在组件这条线会落成
|
|
79
|
+
「未识别的事件」兜底行(手机上看不见正文,PC 上正常),现在被翻译成同一批扁平事件;
|
|
80
|
+
② diff 判定与推导(`looksLikeDiff` / `diffTextToRows` / `derivedDiffRows`)改为转发内核的
|
|
81
|
+
`qaLooksLikeDiff` / `qaDiffTextToRows` / `qaDerivedDiffRows`;③ `escHtml` 从**四个宿主**
|
|
82
|
+
收进本内核(`01-dom.js`,纯函数零宿主依赖);④ 顺手修掉组件缺 `start` 分支的老毛病
|
|
83
|
+
(每轮必多一条「未识别的事件:start」—— 新安全网跑出来的)。
|
|
84
|
+
- **第三步已落地**(同日):**提问卡**这一块的语义收齐 ——
|
|
85
|
+
① 题面归一改走内核的 `qaAskFromEvent`(`question`/`text`、`multiSelect`/`multi_select`
|
|
86
|
+
两套拼写都认,组件以前只认一套);② 组件补上「AskUserQuestion 钻进 `tool_use` 参数」
|
|
87
|
+
这条老路(交内核 `qaIsAskTool` + `qaAskParse` 解析)—— 以前它在手机上是**一张 JSON 工具卡**,
|
|
88
|
+
用户得自己读 JSON 再手打答案(PC 侧 2026-09-18 就修了)。
|
|
89
|
+
渲染与作答(`control({op:'answer'})`)仍归组件,安全网里已把「点选项 → 答案送回会话」
|
|
90
|
+
这条闭环钉住。
|
|
91
|
+
- **第四步已落地**(同日):**产物路径的后缀清单与标记行正则只留一份** ——
|
|
92
|
+
从 `19-attachments.js` 抽出新的纯声明分片 [[02-media-kinds.js]](`QA_IMG_EXT_SRC` /
|
|
93
|
+
`QA_ART_EXT_SRC` / `QA_IMG_EXT` / `QA_ART_EXT` / `QA_SHOT_MARK` / `QA_SHOT_ITEM` /
|
|
94
|
+
`QA_IMG_LINE` / `QA_SHOT_PATH_RE`),组件宿主只引这一片(引整片 19 会被要求提供
|
|
95
|
+
`toast` / `wsDirOf` 那批它用不到的宿主符号);组件侧不再自带后缀清单
|
|
96
|
+
(它以前少了 heic/avif、行内识别也少 svg → 同一张图在 PC 有缩略图、在手机只剩路径)。
|
|
97
|
+
顺带把内核自己的行内识别也补上 svg/heic/avif(`QA_SHOT_PATH_RE` 现由 `QA_IMG_EXT_SRC` 派生)。
|
|
98
|
+
- **第五步已落地**(同日):**markdown 渲染栈归内核** —— 组件删掉自带的手写解析
|
|
99
|
+
(`inlineMd` / `readTable` / `readList` / `codePanelHtml` / `mdToHtml`,约 200 行),
|
|
100
|
+
改调本内核的 `mdToHtmlOnly`(marked + DOMPurify + highlight.js + mermaid);
|
|
101
|
+
渲染之后的两件收尾仍归组件(代码块补复制按钮 → `.md-code-wrap`、调 `highlightIn` 上色 / 出图)。
|
|
102
|
+
配套变化:① 第三方库归口 `client-ui/shared/vendor/`(marked / purify / highlight 一份,
|
|
103
|
+
pc 不再自带副本);② 内核按需加载 highlight.js 的地址改由宿主给
|
|
104
|
+
`window.QA_HLJS_URL`(内核不知道页面的相对根:pc 在 /pc/、移动端在 /m/、magic-test 是整包);
|
|
105
|
+
③ `conversation.css` 里列表 / 引用 / 代码块的规则按 **marked 的真实 DOM** 改写
|
|
106
|
+
(旧规则是照手写解析器的 `<li><span class="mk">…` / `<blockquote><span class="bar">…` 画的)。
|
|
107
|
+
- ⚠️ 因此**组件宿主页必须先引**:`shared/vendor/marked.min.js` + `purify.min.js`(设
|
|
108
|
+
`window.QA_HLJS_URL`),再引内核五片 `01-dom` / `02-media-kinds` / `08-ask-event-map` /
|
|
109
|
+
`10-markdown-render` / `29-msg-variants`,最后才是 `shared/conversation.js`
|
|
110
|
+
(组件 `create()` 开场会自检,缺了当场报错,不会拖到运行中才炸)。路径按 `shared/` 取件 ——
|
|
111
|
+
远程 `/m/shared/*` 与 APK 本地 `www/shared/` 都对得上(`build-mobile-www.cjs` 递归复制)。
|
|
112
|
+
- **安全网**(2026-10-16 起归口本仓库 `magic-agent/tests/`,三端各一条):
|
|
113
|
+
`check-conv-shared-host.mjs`(`npm run check:conv-host`)—— 内核在**组件宿主页**里跑完整一轮
|
|
114
|
+
(含 image / command / control_error 这几支组件独有的形态);`check-conv-component.mjs`
|
|
115
|
+
(`npm run check:conv-comp`)—— 组件 `MagicConversation.create()` 那条线的端到端冒烟
|
|
116
|
+
(断言与 DOM 类名无关,**换渲染路径前后都必须绿**)。
|
|
117
|
+
- **内核已是组件那套语义的超集**(同日):组件 `applyOne` 里那几支移动端独有的分支,
|
|
118
|
+
内核侧逐条核对并补齐 —— `image`(模型出图 → `h.appendImage`)、`command`(斜杠命令
|
|
119
|
+
生命周期 → 标记行)、`control_error`(「打断 / 作答没生效」→ 「控制失败 · …」标记行)。
|
|
120
|
+
以前这三条在内核里会掉进 default,标题写成「未识别事件 · x」(信息在、话说反了)。
|
|
121
|
+
`user`(历史里用户那条)与 `interrupted` / `stopped` 不在内核补:前者由宿主在回放时
|
|
122
|
+
调 `qaRenderMine`,后者由收尾那条(`onDone` → `qaRenderDone`,`stopped:true` → 「已停止」)表达。
|
|
123
|
+
- **换渲染路径已落地**(2026-10-16,块② 后半):组件不再自己画消息流 —— `render(ev)` 现在直接
|
|
124
|
+
走内核(`kernelApply` → `h = qaRenderAnswer(画布)` → `qaStreamApply(ev, ctx)` → `qaRenderDone`),
|
|
125
|
+
画布 = 组件自己的 `.conv__thread`(id 给成内核找的 `#qa-stream`),提问卡挂 `#home-askdock`,
|
|
126
|
+
宿主符号桥 `window.MagicHost` 真转发(重试 / 追问 / 提问卡作答 / 提示)。皮肤
|
|
127
|
+
[[conv.css]](`qa-*`)从 magic-test/app.css 抽出、令牌名映射到组件宿主已有的那套,
|
|
128
|
+
两个组件宿主页引它。**三端从此用同一份脚本画同一批 DOM。**
|
|
129
|
+
⚠️ 组件里那套旧实现(`.ubub` / `.rsn` / `.tool` / `.conv-turn`)已在块③ 整体删除
|
|
130
|
+
(见下面「三端瘦身已落地」)。
|
|
131
|
+
- **三端瘦身已落地**(2026-10-16,块③):组件里那套旧实现(`applyOne` 与它底下整条视图链,
|
|
132
|
+
约 1180 行)连同 `conversation.css` 里只针对那些旧类名的规则(471→209 行)一起删了 ——
|
|
133
|
+
组件从此只剩**外壳**(挂载壳 / 输入区 / 附件 / 选择器 / 传输层)。顺带修掉两个真 bug:
|
|
134
|
+
`.conv__thread` 上那句 `copyBin.length`(那个变量早已不存在,**切会话必抛**)与每轮多出的「完成」行。
|
|
135
|
+
- **定稿已落地**(2026-10-16,块④):
|
|
136
|
+
① `check-ui-spec` 里 `.ubub` / `.md` 两条**改锚到令牌**(`--qa-bubble-r` 气泡圆角 /
|
|
137
|
+
`--qa-body-lh` 正文行高)—— DOM 归内核后页内那条规则不复存在,值改由各端给、
|
|
138
|
+
[[conv.css]] 用 `var(…, 缺省)` 取;页内那两条死规则删掉了。规格仍 32/32。
|
|
139
|
+
② **移动端宿主页有了装配冒烟**(`check:conv-comp` 第二段,按打包后的 `shared/` 关系把
|
|
140
|
+
`client-ui-mobile/index.html` 原样跑一遍):组件挂得上、画布在 `#conv` 里、共用皮肤与
|
|
141
|
+
两个基线令牌都在、零脚本报错。
|
|
142
|
+
③ [[conv.css]] 里 magic-test 独有的结构件规则**刻意保留**(本壳没有那些节点 → 匹配不到,
|
|
143
|
+
无害;内核里有运行时拼出来的类名,静态删会误判)—— 理由写在文件头。
|
|
144
|
+
- **2026-10-17 修的两个「看得见吗 / 画了吗」问题**(移动端报障:「看不到会话历史」):
|
|
145
|
+
① **皮肤是逐字原样 + 一层显式令牌桥**,不再「把名字逐个替换」:两边的令牌名**同形不同义**
|
|
146
|
+
(magic-test 的 `--paper` 是**文字色**,宿主的 `--canvas` 是**页面底色**),
|
|
147
|
+
上一版把 `color:var(--paper)` 换成了 `color:var(--canvas)` = 白字白底 ——
|
|
148
|
+
历史其实回放出来了,屏上什么都没有。桥在 [[conv.css]] 文件末,逐条可核。
|
|
149
|
+
② **历史里用户那条要宿主自己画**:落盘的 `kind:"user"` 不在内核的事件流里
|
|
150
|
+
(内核把它放在忽略清单,免得实时流把用户刚发的那条画两遍)→ 回放时宿主调 `qaRenderMine`
|
|
151
|
+
(magic-test 的 `27-conv-log.js`、pc 工作台的 `93-rail.js`、本组件都这么做)。
|
|
152
|
+
③ 头像两处写死的身份改成**可覆盖默认值**:`QA_BOT_AVATAR_SRC`(默认仍是 magic-test 那张
|
|
153
|
+
应用图标;宿主给空串 = 没有这张图 → 中性机器人头,不再裂图)、`QA_USER_INITIAL`
|
|
154
|
+
(默认空 → 取 `#nu-name` 首字,取不到用「我」,不再顶出别人的姓名首字)。
|
|
155
|
+
- **两条闸门补了「看得见」这一层**(此前只查 textContent,于是白字白底一路绿灯):
|
|
156
|
+
`check:conv-host`(PC 宿主,明/暗两套)与 `check:conv-comp`(移动端真页 + 历史回放 + 明/暗)
|
|
157
|
+
都断言正文前景/背景**对比度 ≥ 3**;量色走 1×1 画布取真实像素(`oklch(…)` 靠正则抠数字会误判)。
|
|
158
|
+
- **2026-10-17 又修三个「看得见 / 排得对」**:⑤「宽度没自适应 100%」—— ① 抽取过滤太窄:正文皮肤是
|
|
159
|
+
`.md-body …` / `.hljs-*`(**不含 `.qa-`**),按「只抽 `.qa-*`」整族被丢,代码块没有
|
|
160
|
+
`overflow-x`、一行长代码把会话区从 414 顶到 1157;抽取过滤已放宽到
|
|
161
|
+
「含 `.qa-` 或 `.md-body` 或 `.hljs` 或 `li.task-li`」并把这一族插回 `.qa-*` 之前;
|
|
162
|
+
② 组件壳 `.conv` / `.conv__thread` 补 `min-width:0`(flex 中间层默认 `min-width:auto`
|
|
163
|
+
会把「内容里最宽的一小块」当成整条链的下限)。闸门也补了:移动端冒烟改**手机视口
|
|
164
|
+
414×896**(此前是 Playwright 默认 1280,等于没量),宽度判据改成「谁比视口宽」
|
|
165
|
+
(`scrollWidth` 会被层层 `overflow:hidden` 夹成假绿)。反证:拿掉 `min-width:0` →
|
|
166
|
+
`stream=835 / viewport=414` 红。
|
|
167
|
+
⑥「用户的文字应该左对齐」:magic-test 原先把右侧两类气泡(我 / 数字分身)的文字统一右对齐
|
|
168
|
+
(M3 口径),中文长句换行时第二行会孤零零甩到右边;[[conv.css]] 里
|
|
169
|
+
`.qa-msg.qa-mine .qa-t` 改 `text-align:left`(数字分身那条没动,它属工作台那套)。
|
|
170
|
+
闸门补断言「用户那条文字左对齐」。
|
|
171
|
+
⑦「工具无法点击展开」:那条「任务耗时 / 工具调用中 · N 项过程」是**原生 `<details>`**,
|
|
172
|
+
桌面新 Chromium 怎么点都翻;皮肤给 `summary` 设了 `display:flex`,而**老 WebView /
|
|
173
|
+
老 Chromium 对 details/summary 上的 `display` 支持不完整**(Chrome 131 才正式支持)——
|
|
174
|
+
手机上那一下原生不发生了。改法:[[07-stream-delegation.js]] 的委托**接管** `#qa-stream`
|
|
175
|
+
里所有 `summary`(`preventDefault()` 掉原生再手动切 `open`,两条路都只翻一次)。
|
|
176
|
+
闸门补断言:派发**可取消**的 click 后 `defaultPrevented === true`(证明是我们在接管,
|
|
177
|
+
不是碰巧原生能开)+ `open` 翻转 + 展开后有内容。
|
|
178
|
+
⑧「同一个会话 pc 与移动端标题不一样」:标题的**唯一口径**是 core 的 `--sessions.title`
|
|
179
|
+
(已把「标题表的归纳结果 → prompt_head」合并好)。逐处核对后,只有**组件详情页顶栏**
|
|
180
|
+
(`conversation.js::setSession`)此前优先 `prompt_head` → 改成 `s.title || s.prompt_head`。
|
|
181
|
+
闸门补断言:给 `setSession` **两个字段不同值**,断言顶栏显示的是 `title`。
|
|
182
|
+
- **2026-10-20 修的两个移动端问题**:
|
|
183
|
+
① **组内工具调用点不开**(用户报障原文):根因是 [[07-stream-delegation.js]] 的委托**幂等标记
|
|
184
|
+
挂错了地方** —— 原先用一个**模块级** `qaStreamDelegated`,第一次 `bindQaStreamDelegation()`
|
|
185
|
+
就置真;而移动端每打开一条会话都是 `destroy()` + 重新 `create()`(宿主页 `mountConversation`),
|
|
186
|
+
`#qa-stream` 是**每次新建的节点** → 之后每个画布都被「已绑过」跳过,那块画布上**零监听**:
|
|
187
|
+
组内工具卡 / 思考条 / 复制按钮 / 折叠组全部点不动(原生 details 那一下在老 WebView 上恰好
|
|
188
|
+
又不发生,于是整个组彻底成了死的)。改法:幂等改按**每个根自己**判(`root.__qaDelegated`),
|
|
189
|
+
新节点照样绑、同一节点不重复绑。闸门补了**二次挂载后**再点折叠组那条断言
|
|
190
|
+
(`check:conv-comp` 末段)—— 原来那条 fold 断言跑在第一次 mount 上,天然抓不到。
|
|
191
|
+
② **运行中的过程行不再合并**(用户报障原文「移动端agent运行中不需要 合并到下一行即可」):
|
|
192
|
+
[[03-chat-message.js]] 的 `SEG_KEEP` 由 **2 收到 1** —— 一段里只留「本段进行中的那一笔」
|
|
193
|
+
在外面,新一笔过程一亮(工具组或下一段思考)上一笔立刻被 `segFold` 收进段行。
|
|
194
|
+
收尾仍由 `wrapTurn` 并成一枚大组,最终形态不变。
|
|
195
|
+
③ **「运行中」并到 bot 头像那一行、动画交给头像**(用户报障原文「agent 运行中这一段和下面的
|
|
196
|
+
bot图标合并到一行 运行中的图标动画也改到bot上显示」):这一行是**宿主壳**的
|
|
197
|
+
(`conversation.js` 的 `setWorking` + `conversation.css` 的 `.working*`),原先是画布里
|
|
198
|
+
**独占一行**的兄弟节点。改法两条:
|
|
199
|
+
· **宿主**(`conversation.js::mountWorking`)在**这一轮消息建出来的那一刻**
|
|
200
|
+
(`qaEnsureAnswer` 里 `qaRenderAnswer` 之后)把那个节点搬进该消息,并盖一枚 `.qa-runlive`;
|
|
201
|
+
收尾(`clearWorking`)连节点带类一起摘。
|
|
202
|
+
⚠️ 消息还没建出来时**什么都不画**(用户看了那行「等待处理… + 蓝圈 + 16.4s」后说
|
|
203
|
+
「不需要这个效果」):一条没有正文、却顶着 spinner 的假 bot 行,读起来像第二条回复。
|
|
204
|
+
那段「还在跑」由输入区的「生成中」(`composer__gen`)承担 —— 节点只活在 `state.working` 里,
|
|
205
|
+
等消息建出来才并进去。
|
|
206
|
+
· **宿主皮肤**(`conversation.css`)把该消息改成 `flex-flow:row wrap`:`.qa-ico` 转 flex、
|
|
207
|
+
引擎名 `.qa-who` 从绝对定位放回流内,三块 `[脸][引擎名][运行中·秒数]` 自然排成一行,
|
|
208
|
+
`.qa-bd` / `.qa-acts` 仍 `flex:0 0 100%` 整行换到下面。
|
|
209
|
+
⚠️ 不用「给 `.working` 一个 `left` 绝对定位:`.qa-who` 宽度随引擎/模型名变化,
|
|
210
|
+
固定 `left` 必然要么压住名字要么留空白。
|
|
211
|
+
**文字不要了**(用户:「agent运行中 的文字就不需要」,后又追认「agent运行中还在」)——
|
|
212
|
+
状态由左边头像的呼吸表达,不再复述一遍。⚠️ 那枚文字节点**在 `setWorking` 里就不建**
|
|
213
|
+
(原先是靠 `.qa-runlive` 下的 `display:none` 隐藏:一旦「并进消息」没生效 —— 消息还没建出来 /
|
|
214
|
+
类没盖上 —— 那串「Agent 运行中」就照旧露出来,用户看到的正是这个)。
|
|
215
|
+
转圈那一枚(`.working__sp`)在运行态同样 `display:none`:并进同一行之后它就多余了。
|
|
216
|
+
**耗时计时(`.working__d`)保留,落在这一行的右端**(用户:「耗时计时也显示在头像这一行右边」)
|
|
217
|
+
—— 它是这一行唯一的读数,也是「没卡死」的佐证;连它一起摘掉的话,运行中就只剩
|
|
218
|
+
「一个头像在动」,看不出跑了多久。
|
|
219
|
+
⚠️ 定位靠 `.working` 的 `justify-content:flex-end`(秒数右端对齐消息右缘),
|
|
220
|
+
那个空占位 `.working__act` 在运行态一并 `display:none`。
|
|
221
|
+
«消息还没建出来那段的空白(起进程 / 探测引擎)**什么都不画** ——
|
|
222
|
+
原先的「转圈 + 秒数」独立行已被用户否掉(「不需要这个效果」),见上面 ③ 第一条。»
|
|
223
|
+
动画 = **左边 bot 头像呼吸**(`qaRunBreath`:scale 1↔1.08 + opacity 1↔.6,1.5s)。
|
|
224
|
+
⚠️ 挂在 **`.qa-av` 里面那张脸**(`svg` / `img`)上、**不挂 `.qa-av` 本身**:
|
|
225
|
+
`.qa-av` 是那格 22px 的圆角**实底方块**,缩放它 = 整块颜色在胀缩,像按钮在闪;
|
|
226
|
+
里面那张描线的机器人头才是「bot」,动它才是它在动。
|
|
227
|
+
⚠️ `prefers-reduced-motion:reduce` 那条通用规则(`animation:none!important`)会把它整条关掉 ——
|
|
228
|
+
文字已删之后那张脸是**唯一**的状态提示,所以本文件在那条媒体查询里**放行一份只改透明度的
|
|
229
|
+
呼吸**(`qaRunBreathSoft`,不做缩放:前庭刺激主要来自位移与缩放)。
|
|
230
|
+
⚠️ `.qa-ico` 一改成宽度自适应(为了让引擎名进流内),它那圈 `inset:0` 的
|
|
231
|
+
`::before` 光晕就会跟着变宽、把引擎名罩进去 → 运行态按脸宽收回来。
|
|
232
|
+
⚠️ `.working__sp` 元素与 `work-spin` 关键帧**已无去处**(独立行不再画),
|
|
233
|
+
但先留着:删掉它们要动三处皮肤,收益仅是少几行 CSS;`.qa-runlive` 下的
|
|
234
|
+
`display:none` 已把它彻底藏住。
|
|
235
|
+
④ **「换行了」这条**(用户:「动画效果没有 只显示模型即可 不然现在换行了」):
|
|
236
|
+
那不是动画没生效,是**这一行放不下** —— `.qa-who` 内核给的是「引擎 · 模型显示名」
|
|
237
|
+
整串(两段都能长)且 `white-space:nowrap`,`flex-flow:row wrap` 下它把秒数那一格
|
|
238
|
+
顶到下一行,头像那行于是散成两截、看着像「动画没了」。两条治法**都要**:
|
|
239
|
+
· **内核**:`qaWhoLabel(engine, model, modelOnly)` 与 `h.setWho(..., modelOnly)`
|
|
240
|
+
加这一档,运行态只留模型名(模型空时退回引擎名)。宿主经 **ctx 的可选键**
|
|
241
|
+
`whoModelOnly` 决定要不要(组件宿主给 `true`、PC 工作台不给 → 仍是整串);
|
|
242
|
+
`start` 分支那条重写路径也读它,否则降级换模型会把长串写回去。
|
|
243
|
+
宿主在 `mountWorking` 里先收短一次(`start` 之前有个窗口),
|
|
244
|
+
`clearWorking` **显式写回整串** —— 摘类不管文字,`.qa-runlive` 只管排版。
|
|
245
|
+
· **宿主皮肤**:`.qa-ico` 与 `.qa-who` 都补 `min-width:0`(flex 子项默认
|
|
246
|
+
`min-width:auto`,小标那格会成为整条链的下限)+ `text-overflow:ellipsis` ——
|
|
247
|
+
就算模型名仍超长,它自己截断,秒数那一格永远留在行内。
|
|
248
|
+
⚠️ **内核全程不知情** `.qa-runlive`(除上面 ④ 那一档可选 ctx 键):`.working` 这一行
|
|
249
|
+
与那个类是**宿主**的 —— 见边界表「谁的状态谁盖类」。闸门在**收尾之前**取样钉七条形制
|
|
250
|
+
(在消息里 / 同一行 / 文字已摘 / 转圈已交棒 / 动画挂在**脸上**而非那行或外层方块 /
|
|
251
|
+
计时可见且贴右缘 / 小标只显示模型 / **计时与头像不换行**)——
|
|
252
|
+
最后一条是 ④ 的**真正判据**:光断言「小标短」不够,宽度不够时照样会被 wrap 挤下去。
|
|
253
|
+
都不钉像素(移动端那个 body zoom 会让像素漂)。
|
|
254
|
+
- **还没做(都只是观感打磨,不是结构问题)**:
|
|
255
|
+
① [[conv.css]] 是首版皮肤(逐字抽出 + 令牌桥);要调就改它,某端要自己的样子就在那页覆盖令牌;
|
|
256
|
+
② 真机 / APK 仍要在打包产物上过一遍(本仓库验不到 WebView 里的实际渲染);
|
|
257
|
+
③ `client-ui/pc/`(完整工作台)还没接共用皮肤,仍用它自己那份 `app.css`(带 `.qa-*` 历史副本)。
|