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.
Files changed (153) hide show
  1. package/CLIENT-CONTRACT.md +254 -8
  2. package/README.md +48 -5
  3. package/client-ui/index.html +32 -0
  4. package/client-ui/pc/app.css +89 -1
  5. package/client-ui/pc/index.html +18 -8
  6. package/client-ui/pc/js/00-base.js +8 -30
  7. package/client-ui/pc/js/05-task-list-render.js +4 -1
  8. package/client-ui/pc/js/07-chat-stream-render.js +2 -85
  9. package/client-ui/pc/js/10-markdown.js +3 -171
  10. package/client-ui/pc/js/18-askbox.js +2 -176
  11. package/client-ui/pc/js/90-glue.js +1 -1
  12. package/client-ui/pc/js/91-workbench-stubs.js +4 -10
  13. package/client-ui/pc/js/93-rail.js +10 -3
  14. package/client-ui/shared/conv/01-dom.js +52 -0
  15. package/client-ui/shared/conv/02-media-kinds.js +34 -0
  16. package/client-ui/shared/conv/03-chat-message.js +1561 -0
  17. package/client-ui/shared/conv/06-ask-answer.js +377 -0
  18. package/client-ui/shared/conv/07-stream-delegation.js +158 -0
  19. package/client-ui/{pc/js → shared/conv}/09-stream-apply.js +121 -6
  20. package/client-ui/shared/conv/10-markdown-render.js +252 -0
  21. package/client-ui/shared/conv/18-askbox-markup.js +185 -0
  22. package/client-ui/{pc/js → shared/conv}/19-attachments.js +365 -21
  23. package/client-ui/shared/conv/27-conv-ledger.js +381 -0
  24. package/client-ui/{pc/js → shared/conv}/29-msg-variants.js +112 -3
  25. package/client-ui/shared/conv/README.md +257 -0
  26. package/client-ui/shared/conv/conv.css +1638 -0
  27. package/client-ui/shared/conv/dom-map.md +170 -0
  28. package/client-ui/shared/conv/manifest.json +44 -0
  29. package/client-ui/shared/conversation.css +368 -190
  30. package/client-ui/shared/conversation.js +1722 -788
  31. package/client-ui/shared/host-glue.js +143 -0
  32. package/client-ui/shared/transport-http.js +130 -6
  33. package/client-ui-mobile/index.html +2417 -103
  34. package/cmd/magic-client/clientctl.go +273 -0
  35. package/cmd/magic-client/main.go +71 -5
  36. package/go.mod +6 -3
  37. package/go.sum +4 -1
  38. package/internal/agent/arkclaw.go +3 -2
  39. package/internal/agent/arkclaw_stream.go +126 -8
  40. package/internal/agent/arkclaw_stream_test.go +97 -15
  41. package/internal/agent/arkclaw_test.go +3 -3
  42. package/internal/agent/attachments.go +18 -5
  43. package/internal/agent/attachments_test.go +7 -1
  44. package/internal/agent/capability_test.go +11 -1
  45. package/internal/agent/claude.go +96 -158
  46. package/internal/agent/codebuddy.go +108 -133
  47. package/internal/agent/codebuddy_gateway.go +8 -4
  48. package/internal/agent/codebuddy_gateway_stream.go +82 -2
  49. package/internal/agent/codebuddy_gateway_test.go +236 -1
  50. package/internal/agent/codebuddy_stream_test.go +262 -0
  51. package/internal/agent/codex.go +50 -9
  52. package/internal/agent/codex_stream_test.go +30 -0
  53. package/internal/agent/codex_test.go +24 -0
  54. package/internal/agent/custom_models.go +109 -3
  55. package/internal/agent/custom_models_test.go +74 -0
  56. package/internal/agent/dsh.go +464 -130
  57. package/internal/agent/dsh_resume.go +258 -0
  58. package/internal/agent/dsh_sdk.go +68 -17
  59. package/internal/agent/dsh_sdk_test.go +102 -2
  60. package/internal/agent/dsh_test.go +409 -36
  61. package/internal/agent/engine.go +26 -6
  62. package/internal/agent/engine_base.go +46 -2
  63. package/internal/agent/engine_base_test.go +29 -0
  64. package/internal/agent/engine_test.go +101 -0
  65. package/internal/agent/env.go +69 -1
  66. package/internal/agent/itemtracker.go +271 -0
  67. package/internal/agent/launch.go +192 -0
  68. package/internal/agent/launch_test.go +206 -0
  69. package/internal/agent/launch_unix.go +15 -0
  70. package/internal/agent/launch_windows.go +24 -0
  71. package/internal/agent/limitnotice.go +103 -0
  72. package/internal/agent/limitnotice_test.go +211 -0
  73. package/internal/agent/llmengine.go +2 -2
  74. package/internal/agent/models.go +3 -2
  75. package/internal/agent/openclaw.go +4 -3
  76. package/internal/agent/openclaw_acp.go +7 -0
  77. package/internal/agent/openclaw_test.go +2 -2
  78. package/internal/agent/permission.go +100 -11
  79. package/internal/agent/permission_test.go +100 -36
  80. package/internal/agent/runcmd.go +13 -3
  81. package/internal/agent/runner.go +7 -1
  82. package/internal/agent/runner_test.go +1 -1
  83. package/internal/agent/stream.go +777 -43
  84. package/internal/agent/stream_test.go +3 -2
  85. package/internal/agent/stream_timeline_test.go +905 -0
  86. package/internal/agent/streamjson.go +36 -2
  87. package/internal/agent/toolkind.go +20 -1
  88. package/internal/agent/toolkind_test.go +7 -1
  89. package/internal/agent/tools_suffix_test.go +10 -3
  90. package/internal/agent/trae.go +5 -3
  91. package/internal/agent/workspace_test.go +52 -0
  92. package/internal/cli/ask.go +260 -33
  93. package/internal/cli/ask_test.go +15 -11
  94. package/internal/cli/attach_test.go +32 -20
  95. package/internal/cli/clientcmd.go +156 -0
  96. package/internal/cli/events.go +53 -8
  97. package/internal/cli/events_test.go +230 -0
  98. package/internal/cli/keepalive_test.go +101 -6
  99. package/internal/cli/permission_test.go +5 -1
  100. package/internal/cli/root.go +174 -32
  101. package/internal/cli/session.go +175 -28
  102. package/internal/cli/session_test.go +144 -5
  103. package/internal/cli/sessionlog.go +385 -38
  104. package/internal/cli/sessionlog_test.go +708 -0
  105. package/internal/cli/sessionlog_user_test.go +142 -1
  106. package/internal/cli/sessiontitle.go +141 -0
  107. package/internal/client/browse.go +456 -0
  108. package/internal/client/browse_test.go +199 -0
  109. package/internal/client/cors_test.go +35 -0
  110. package/internal/client/daemon.go +525 -0
  111. package/internal/client/daemon_test.go +212 -0
  112. package/internal/client/detach_unix.go +19 -0
  113. package/internal/client/detach_windows.go +27 -0
  114. package/internal/client/pair.go +3 -3
  115. package/internal/client/plugins.go +376 -0
  116. package/internal/client/plugins_test.go +223 -0
  117. package/internal/client/proxy.go +374 -0
  118. package/internal/client/proxy_test.go +212 -0
  119. package/internal/client/service.go +1464 -48
  120. package/internal/client/service_test.go +649 -206
  121. package/internal/client/session_events_test.go +130 -0
  122. package/internal/client/wsproxy.go +113 -0
  123. package/internal/client/wsproxy_test.go +102 -0
  124. package/internal/session/delete.go +151 -0
  125. package/internal/session/delete_test.go +103 -0
  126. package/internal/session/session.go +155 -20
  127. package/internal/session/session_append.go +79 -21
  128. package/internal/session/session_test.go +124 -10
  129. package/internal/session/sessionlog.go +572 -6
  130. package/internal/session/sessionlog_read_test.go +410 -0
  131. package/internal/session/title.go +486 -0
  132. package/internal/session/title_test.go +298 -0
  133. package/npm/bin/magic-agent.js +101 -10
  134. package/npm/bin/magic-client.js +118 -0
  135. package/npm/dist/darwin-arm64/magic-agent +0 -0
  136. package/npm/dist/darwin-arm64/magic-client +0 -0
  137. package/npm/dist/darwin-x64/magic-agent +0 -0
  138. package/npm/dist/darwin-x64/magic-client +0 -0
  139. package/npm/dist/linux-arm64/magic-agent +0 -0
  140. package/npm/dist/linux-arm64/magic-client +0 -0
  141. package/npm/dist/linux-x64/magic-agent +0 -0
  142. package/npm/dist/linux-x64/magic-client +0 -0
  143. package/npm/dist/win32-x64/magic-agent.exe +0 -0
  144. package/npm/dist/win32-x64/magic-client.exe +0 -0
  145. package/npm/install.js +36 -13
  146. package/npm/lib/platform.js +27 -5
  147. package/package.json +8 -3
  148. package/client-ui/pc/js/03-chat-message.js +0 -880
  149. package/client-ui/pc/js/06-ask-answer.js +0 -204
  150. /package/client-ui/{pc/js → shared/conv}/08-ask-event-map.js +0 -0
  151. /package/client-ui/{pc → shared}/vendor/highlight.min.js +0 -0
  152. /package/client-ui/{pc → shared}/vendor/marked.min.js +0 -0
  153. /package/client-ui/{pc → shared}/vendor/purify.min.js +0 -0
@@ -0,0 +1,170 @@
1
+ # 消息流本体的 DOM/类名映射表(阶段三最后一块 · 动手前的底稿)
2
+
3
+ **这份文件是「换 DOM」前的比对底稿,不代表已经换。** 前面四步收敛的都是**逻辑**
4
+ (事件语义、工具归类、提问卡、markdown、附件与产物判定)—— 它们已经只剩一份实现。
5
+ 最后这一块不一样:它要动的是**皮**(消息流的 DOM 结构与 CSS 类名),也就是「UI 不同」的那部分。
6
+
7
+ ## 一、两套类名的对应
8
+
9
+ | 区块 | 组件(`shared/conversation.js` + `conversation.css`) | 内核(`shared/conv/*.js` 画、宿主 CSS 上色) | 迁移代价 |
10
+ |---|---|---|---|
11
+ | 用户气泡 | `.urow` / `.ubub` / `.ubub__t` / `.ubub__meta` | `.qa-msg.qa-mine` / `.qa-bd` / `.qa-t` | 中(页内 `.ubub` 规则要跟着走) |
12
+ | AI 正文块 | `.msg` / `.agent` / `.md` | `.qa-msg` / `.qa-bd` / `.qa-t` | 中 |
13
+ | 思考块 | `.rsn` / `.rsn__head` / `.rsn__title` / `.rsn__body` | `.qa-think` / `.qa-think-toggle` / `.qa-think-bd` / `.qa-think-dur` | 中(折叠器的 JS 已在内核) |
14
+ | 工具组(一轮一行) | `.tgroup` / `.wait` / `.conv-turn__*` | `.qa-turn` / `.qa-run` / `.qa-done` / `.qa-dots` | 大(含「任务耗时」大组与段壳) |
15
+ | 单张工具卡 | `.tool` / `.tool__head` / `.tool__ico` / `.tool__t` / `.tool__detail` | `.qa-tool` / `.qa-tool-head` / `.qa-tool-ico` / `.qa-tool-dur` / `.qa-tool-bd` | 大 |
16
+ | 工具明细段 | `.cmdbar` / `.casc` / `.fc` | `.qa-tool-sec` / `.v-in` / `.v-out` / `.qa-cf*` | 大 |
17
+ | 标记行 / 分隔条 | `.mk` / `.sep` / `.notice` | `.qa-mk*` / `.qa-sep` / `.qa-hist-note` | 小 |
18
+ | 状态行 / 底部动作 | `.actpill` / `.acts` | `.qa-acts` / `.qa-act` / `.qa-done` | 中 |
19
+ | 产物(图 / 文件) | `.shots` / `.shot` / `.arts` / `.artspop` / `.artsdrawer` | `.qa-atts` / `.qa-img` / `.qa-arts` / `.qa-art` | 大 |
20
+ | 提问卡 | `.askcard*` | `.qa-ask*` / `.qa-askdock` | 中 |
21
+ | 输入区 | `.composer*` / `.attachbar` / `.attachchip` / `.attachgrid` | `.qa-askbox` 系 | 大(宿主壳的一部分) |
22
+
23
+ ## 二、为什么这一步不能照前四步那样直接搬
24
+
25
+ 1. **皮是各宿主自己的**:内核只画结构(`qa-*`),上色分别由 magic-test 的 `app.css`、
26
+ pc 的 app.css、移动端的 `conversation.css` 提供。换 DOM ≠ 搬一份 CSS,是**重画一遍移动端观感**。
27
+ 2. **AA 规格(32 条)里有两条直接绑在组件 DOM 类上**:
28
+ `client-ui-mobile/index.html:325` 的 `.ubub{border-radius:12px 4px 12px 12px;…}` 与
29
+ `:327` 的 `.md{line-height:28px}` —— 它们是「移动端观感基线」的一部分。
30
+ 换 DOM 之后这两条会**静默失效**(`check-ui-spec.mjs` 只比对 CSS 文本是否存在,不看 DOM),
31
+ 也就是说**测试不会红**,但观感已经不符基线 —— 属于「不能用测试兜底」的那类改动。
32
+ 3. **收益与代价不对称**:换掉能省下的是「CSS 两份」;付出的是移动端观感重做 + 基线更新 +
33
+ 逐屏人工验收。而**逻辑**(最容易漂移、也最难查的那部分)已经是一份了。
34
+
35
+ ## 三、三条路
36
+
37
+ - **A. 整体换 DOM**:组件改为画 `qa-*`,`conversation.css` 重写成 `qa-*` 的移动视觉,
38
+ AA 规格基线(含 `index.html:325/327` 两条)同步更新,逐屏人工过一遍。
39
+ 收益最大(CSS 也一份),代价最大(动观感 + 破基线)。
40
+ - **B. 保持现状(逻辑一份、皮两套)**:这正是当前状态,也字面实现了最初那句
41
+ 「显示逻辑和事件能统一,只是 UI 不同」。
42
+ - **C. 折中(建议)**:只换**不影响观感的结构件** —— 标记行 / 分隔条 / 状态行这类纯尺寸件,
43
+ 以及内核里已经带完整折叠逻辑的**思考块**;每换一块就把对应的页内规则搬进
44
+ `conversation.css`,AA 基线只在真正改观感时才动。工具卡与产物卡(观感差异最大的两块)
45
+ 留到最后,且必须先有人拍板观感。
46
+
47
+ ## 五、走 A 的实测底稿(2026-10-16 侦察结果,动手前必读)
48
+
49
+ **目标形态(2026-10-16 用户更正)**:**三个客户端共用同一份脚本** —— 共用脚本自带
50
+ 「事件 → 画面」与**皮肤**,三端只出「外壳 + 令牌 + 宿主适配」,不是「把某一端改造成另一端的样子」。
51
+
52
+ ```
53
+ 共用脚本(唯一源) 三端各自只留
54
+ ──────────────────────── ──────────────────────────────────────────
55
+ shared/conv/* 内核 外壳:顶栏 / 侧栏 / 需求看板 / 输入区布局
56
+ shared/conversation.js 门面 create({hostApi, shell}) —— 对外只有这一个入口
57
+ shared/conv/conv.css 皮肤 令牌:契约 §3.3 那组 CSS 变量(各端给自己的值)
58
+ 宿主适配:transport、模型选择器、文件回读、任务表(走 hostApi)
59
+ ```
60
+
61
+ 于是「观感以谁为准」这个问题**不存在**了 —— 三端本来就用同一份 DOM 与同一份 CSS,
62
+ 差别只在令牌;也不再需要「迁移某一端的观感 / 改 AA 基线去迁就另一段 DOM」。
63
+ AA 那 32 条要改的只有一件事:`.ubub` / `.md` 两条从**移动端页内**移进**共用 CSS**(跟着皮肤搬家)。
64
+
65
+ **1. 内核的 handle 契约是干净的、被 feature-detect 的**(`09-stream-apply.js` 全文按
66
+ `typeof h.x === "function"` 判能力,注释里明写「旧手柄无 agentCall → 退普通卡」),
67
+ 一共 15 个方法:
68
+
69
+ ```
70
+ appendText appendThinking endThinking // 正文 / 思考(累积 + 增量两种都吃)
71
+ beginTools endTools toolCall agentCall // 工具组与单卡、委派卡
72
+ askCard marker note sep // 提问卡 / 标记行 / 注脚 / 分隔条
73
+ setWho setRunLabel setResult // 角色标识 / 轮次标签 / 收尾行
74
+ ```
75
+
76
+ **1b. `qaStreamApply(ev, ctx)` 的 ctx 契约同样是实测的**(不是猜的:少了就地 TypeError)——
77
+ 一共 12 个键,组件接这条路径时要给全:
78
+ `h`(handle)· `tools`(Map,工具卡登记表)· `agents`(Map,委派卡)· `taskId` · `rid` ·
79
+ `onSession(id)` · `onStep(...)` · `onResult(...)` · `onText(...)` · `onError(...)` · `onDone(...)` · `setMs(ms)`。
80
+
81
+ **可选键(给了才生效,缺了走默认,不算进契约)**:
82
+ - `whoModelOnly`(布尔)—— 头像右边那枚小标只显示**模型名**,不与引擎名拼一串。
83
+ 内核只在 `start` 分支读它(`ctx && ctx.whoModelOnly`),给别的宿主一个「按自己的排布取舍」的口子:
84
+ 组件宿主那一行并着秒数、整串太长会换行,所以它给 `true`;PC 工作台不给 → 仍是整串。
85
+ 与 `qaWhoLabel(engine, model, modelOnly)` 和 `h.setWho(engine, model, modelOnly)` 是一套。
86
+
87
+ **1c. 已经跑通的证据**(两条闸门,都在 `magic-agent/tests/`):
88
+ - `node tests/check-conv-shared-host.mjs`(`npm run check:conv-host`)—— 在**组件宿主页**里直接用
89
+ 内核画完两轮(用户气泡 → 思考 → 工具组 → 正文 → 收尾行;外加 `image` / `command` /
90
+ `control_error` 三支),断言事件确实走过了 `qaStreamApply`(`onSession`/`onText`/`onDone` 都被回调)、
91
+ 且**没有 `未识别事件` 标记行**、全程零脚本报错。**15/15 通过**。
92
+ - `node tests/check-conv-component.mjs`(`npm run check:conv-comp`)—— 组件那条线
93
+ (`MagicConversation.create()` + 传输层桩)的端到端冒烟:`type` 与 `kind` 两种拼写各喂一轮,
94
+ 断言用户那句话 / 正文 / 工具命令 / 收尾行都画出来了、没有兜底噪音。**11/11 通过**。
95
+ 它的断言**刻意与 DOM 类名无关** —— 换渲染路径前后都必须绿。
96
+
97
+ **2. 内核自己那套 handle / 圆点轨 / 头像 / 动作行**(`qaRuns`、`qaDotsSync`/`qaDotsMark`、
98
+ `qaUserInitial`/`qaAvatarHtml`/`qaWhoHtml`、`qaMsgActsHtml`)**全部在 `03-chat-message.js` 里**,
99
+ 即内核自带,宿主**不需要**实现(我第一版写反了,这里更正)。
100
+ 宿主真正要给的只有两样:
101
+
102
+ - 一个消息流容器 **`#qa-stream`**(`pc/index.html:124` 已经有 `<div class="qa-stream" id="qa-stream" hidden>`;
103
+ 组件宿主得把 `#thread` 换成它,或者让内核接 holder);
104
+ - 内核契约里那批宿主符号(`QA_ICONS` 等,组件宿主已经全给了 ✓)。
105
+
106
+ **2b. 宿主缺口是实测出来的(2026-10-16,把 11 片内核全挂到组件宿主页跑 `check-conv-shards`)**:
107
+ 组件宿主还差 **12 个符号**,且它们分两类 —— 这是块① 的真正内容:
108
+
109
+ | 类型 | 符号 | 怎么给 |
110
+ |---|---|---|
111
+ | **诚实桩就够**(组件壳压根没这个业务件,内核按返回值降级) | `qaTasks`、`qaTaskSetModel`、`qaWorkspace`、`wsDirOf`、`reqHas`、`reqBoardFetch`、`qaReqConvMsg` | 返回空值 / `false` / `""`,并**如实说明「这里没有」**(AGENTS 的硬规则:不许假装成功) |
112
+ | **必须走桥**(组件自己有对应能力,但状态在闭包里,宿主拿不到) | `qaSend`(发送)、`qaModelName` / `qaModelPick` / `qaModelSet`(模型选择器)、`toast`(提示) | 组件要开一个 `hostApi` 桥(`create({hostApi:{send, model:{name,pick,set}, toast}})`),宿主页实现这几个方法转发给内核 |
113
+
114
+ → 所以块① 不是「写 12 个空函数」,而是**先给组件加 `hostApi` 桥**(约 30 行 + 两个宿主页接线),
115
+ 再补那 7 个诚实桩。桥这一层同时也是块④(提问作答、产物回读)要用的 —— 一次做好,后面都省。
116
+
117
+ **3. 组件那份 `applyOne`(`conversation.js`)不是内核的复制品,它多长了几支移动端专属分支**:
118
+
119
+ `image`(模型出图)、`notice`(max_tokens 截断提示)、`command`(斜杠命令生命周期)、
120
+ `control_error`(打断 / 作答没生效)、`answered` / `answer_queued`(答案去处)、
121
+ 以及 `type` 与 `kind` **双拼写**兼容(落盘 jsonl 用 `kind`)。
122
+ → 换 handle 时这几支**必须逐条确认内核侧也有**,否则会出现「手机上少了某条提示」
123
+ (历史教训:缺 `image` / `start` 分支时,事件直接消失,界面上什么都没发生)。
124
+
125
+ **已逐条核对完毕(2026-10-16)** —— 内核现在是组件那套语义的**超集**:
126
+
127
+ | 组件独有分支 | 内核侧 | 处置 |
128
+ |---|---|---|
129
+ | `image`(KindImage,text = data URI) | 以前走 default(「未识别事件 · image」) | **已补**:`case "image"` → `h.appendImage(text)`(旧手柄无此方法 → 静默略过) |
130
+ | `command`(斜杠命令生命周期) | 以前走 default | **已补**:`case "command"` → 标记行(图标 terminal + 进行中/已完成/失败 + 可展开原始载荷) |
131
+ | `control_error` | 以前走 default(话说反了) | **已补**:`case "control_error"` → 「控制失败 · …」失败标记行 |
132
+ | `notice` | 已有 `case "notice"` ✓ | 无需动 |
133
+ | `answered` / `answer_queued` | 在内核的**刻意忽略**清单里 ✓(画面由收尾那条表达) | 无需动 |
134
+ | `user`(历史里用户那条) | 不在内核:宿主在回放时调 `qaRenderMine`(magic-test 就是这么做的) | 归宿主 |
135
+ | `interrupted` / `stopped` | 不在内核:收尾那条 `onDone({stopped:true})` → `qaRenderDone(…, "已停止")` | 归宿主 |
136
+ | `type` / `kind` 双拼写 | 内核只认 `type`;宿主喂之前做 `kind → type`(magic-test `27-conv-log.js` 就是这么做的) | 归宿主(换渲染时组件这边加一行) |
137
+
138
+ 证据:`npm run check:conv-host` 里第二块事件(`image` / `command` / `control_error`)——
139
+ **15/15 通过**,且断言「没有 `未识别事件` 标记行」。
140
+
141
+ **4. 因此执行顺序(共用脚本为源,三端同时受益;每块单独可验收)**:
142
+
143
+ | 块 | 内容 | 观感风险 |
144
+ |---|---|---|
145
+ | ① | ~~**门面 + 桥**:三端补齐内核要的宿主符号~~ **已落地 2026-10-16**:`shared/host-glue.js`(23 个符号:任务表 / 需求板 / 工作区 / 发送 / 模型引擎六件 / 提示 / 图标等,没有的一律**如实回答**);两个组件宿主页按 manifest 顺序引全套 12 片内核 + `#qa-stream` 回落容器。验收:**四个视角装配契约全绿**(组件 PC 宿主按 12 片算需 248 个宿主符号,页内 409 个定义覆盖)+ UI 规格 32/32 不变 | 无(不改变渲染) |
146
+ | ② | ~~**换渲染路径(三端都用内核画)**~~ **已落地 2026-10-16**。前半:内核补齐组件那几支独有语义(`image` / `command` / `control_error`,见 §5-3 表)+ 两条闸门。后半:组件改成四个调用(`qaRenderMine` / `h = qaRenderAnswer(画布)` / `qaStreamApply(ev, ctx)` / `qaRenderDone`,喂之前 `kind → type`),画布 = 组件自己的 `.conv__thread`(id 给成内核找的 `#qa-stream`),提问卡挂 `#home-askdock`,宿主符号桥 `window.MagicHost` 落地(重试 / 追问 / 作答 / 提示真转发);皮肤 `shared/conv/conv.css`(`qa-*`)从 magic-test/app.css 抽出、令牌名换成组件宿主已有的那套,两个组件宿主页引它。验收:`check:conv-comp` **12/12**(新增一条「画面是内核画的」断言)+ `check:conv-host` **15/15** + UI 规格 32/32 | 中(观感换了一套,是**首版皮肤**,观感微调归各端) |
147
+ | ③ | ~~**三端瘦身**:删掉各自的对话渲染~~ **已落地 2026-10-16**:组件里那套旧实现(约 1180 行 —— `applyOne` 与它底下整条视图链:气泡 / 思考块 / 工具卡 / diff / 标记行 / 产物卡与抽屉 / 提问卡 / 重试)连同 `conversation.css` 里只针对那些旧类名的规则(471→209 行)一起删了;`runTurn` 里补的那几笔收尾(`finalizeRsn/finalizeAgent/stopPrevTool/wrapTurn` + 「完成」分隔条)也删了 —— 那是旧渲染器的活儿,留着会往内核画的画布上多添一行。顺带修掉两个真 bug:`.conv__thread` 上的 `copyBin.length`(那个变量早已不存在,切会话必抛)与每轮多出一条「完成」。验收:`check:conv-comp` 12/12 + `check:conv-host` 15/15 + UI 规格 32/32 + 五端装配契约 | 低(组件只剩外壳) |
148
+ | ④ | ~~**删除与定稿**~~ **已落地 2026-10-16**:① 组件里 `el()` 那批随旧视图链一起删了(见 ③);② `check-ui-spec` 里 `.ubub` / `.md` 两条**改锚到令牌**(气泡圆角 `--qa-bubble-r`、正文行高 `--qa-body-lh`):DOM 归内核之后,页内那条规则已经不存在了,值改由各端给、共用皮肤用 `var(--qa-bubble-r, …)` 取(移动端 = ZCode 基线,桌面 = 整圆角 + 更紧的行高),页内两条死规则删掉;③ **移动端宿主页有了装配冒烟**(`check:conv-comp` 第二段:按打包后的 `shared/` 关系把那一页原样跑一遍 —— 组件挂得上、画布在 `#conv` 里、共用皮肤与基线令牌都在、零报错);④ `conv.css` 里 magic-test 独有的结构件规则**刻意保留**(本壳没有那些节点 → 匹配不到东西,无害;而内核里有运行时拼出来的类名,静态删会误判 —— 理由写在文件头) | — |
149
+ | ⑤ | **回归修复 + 验收补强**(2026-10-17,用户报「移动端看不到会话历史」):① [[conv.css]] 改成「逐字抽出的皮肤 + 一层**显式令牌桥**」—— 上一版把 magic-test 的调色板名字逐个替换,而两边**同形不同义**(`--paper` 是文字色 vs `--canvas` 是页面底色),于是 `color` 被换成了白色,白字白底 → 历史其实画出来了、屏上什么都没有;② 历史里 `kind:"user"` 那条补上(内核把 user 放在忽略清单里,免实时流画两遍 → 回放时宿主要自己调 `qaRenderMine`);③ 头像两处写死的身份改成**可覆盖默认值**(`QA_BOT_AVATAR_SRC` / `QA_USER_INITIAL`,移动端不再裂图、不再顶出别人的姓名首字);④ 两条闸门补「**看得见**」断言(正文前景/背景对比度 ≥ 3、明暗两套;量色走 1×1 画布取真实像素 —— `oklch(…)` 靠正则抠数字会误判)+ 移动端宿主页的**历史回放**冒烟(真页面/真皮肤/真令牌)。验收:`check:conv-host` **17/17**、`check:conv-comp` **22/22**、UI 规格 32/32、五端装配契约 | — |
150
+ | ⑥ | **宽度撑破**(2026-10-17,用户报「宽度没自适应 100%」):两个原因叠在一起 —— ① 抽取过滤太窄:正文的皮肤是 `.md-body …` / `.hljs-*` 选择器(**不含 `.qa-`**),按「只抽 `.qa-*`」整族被丢掉 → 代码块没有 `overflow-x`、标题/列表/引用/表格/行内码全无样式;已放宽到「含 `.qa-` 或 `.md-body` 或 `.hljs` 或 `li.task-li`」并把这一族插回 `.qa-*` 之前(与原文件同序);② 组件壳缺 `min-width:0`:flex 链上的中间层默认 `min-width:auto`,一行长代码就能把会话区从 414 顶到 1157(`#conv` 是 414、`.conv` 却是 1157)。闸门也补了:移动端那段冒烟改成**手机视口 414×896**(此前是 Playwright 默认 1280,等于没量),宽度判据改成「**谁比视口宽**」(`scrollWidth` / `documentElement.scrollWidth` 会被层层 `overflow:hidden` 夹成假绿)+ 「长代码块在自己盒子里横滚」。反证:拿掉 `min-width:0` → `stream=835 / viewport=414` 红。验收:`check:conv-comp` **24/24** | — |
151
+ | ⑦ | **用户那条文字改左对齐**(2026-10-17 用户要求):magic-test 原先把右侧两类气泡(我 / 数字分身)的文字统一右对齐(M3 口径),中文长句换行时第二行会孤零零甩到右边。[[conv.css]] 里把 `.qa-msg.qa-mine .qa-t` 改成 `text-align:left`(数字分身那条**没动**,它属工作台那套;要与不要一起改,改下一条即可)。闸门补断言「用户那条的文字左对齐」。验收:`check:conv-comp` **25/25** | — |
152
+ | ⑧ | **折叠组点不动**(2026-10-18 用户报「工具无法点击展开」):那条「任务耗时 / 工具调用中 · N 项过程」是**原生 `<details>`**(03-chat-message 特意如此:零脚本、键盘可达、开合态进 HTML 不怕快照重建),本来点 `<summary>` 就该翻。手机上不翻 —— 排查:桌面(新 Chromium)程序点击 / 鼠标 / 触摸都正常;皮肤给这些 `summary` 都设了 `display:flex`,而**老 WebView / 老 Chromium 对 details/summary 上的 `display` 支持不完整**(Chrome 131 才正式支持,见 Chrome 官方博客「More options for styling `<details>`」)。改法:[[07-stream-delegation.js]] 的委托里接管 `#qa-stream` 内**所有** `summary` —— `preventDefault()` 掉原生那一下再手动切 `open`(原生能翻的机器上同样只翻一次)。闸门补断言:派发**可取消**的 click 后 `defaultPrevented === true`(= 确实是我们接管,而不是碰巧原生能开)且 `open` 翻转、展开后有内容。验收:`check:conv-comp` **27/27**;并用**用户那条真实会话日志**(`~/.magic-agent/sessions/run-*.jsonl`)回放 + 真触摸验证过 | — |
153
+ | ⑨ | **详情页标题与列表/PC 不一致**(2026-10-18 用户:「同一个会话 pc 端还是跟移动端标题不一样 需一致」):core 早已把「标题表的归纳结果 → prompt_head」合并成 `--sessions` 的 `title`(2026-10-15 定稿「magic-agent 统一归纳作为会话标题」),移动端**列表**(`titleOf`)与 PC(掌天瓶 `cliRows`、PC 工作台 `93-rail`)读的都是它;唯独**组件详情页顶栏**(`conversation.js` 的 `setSession`)此前优先 `prompt_head` → 同一条会话:PC 显示归纳要点、移动端详情页显示首问原文。改法:`s.title \|\| s.prompt_head`(老 core 没 title 字段时仍回落首问)。闸门补断言:给 `setSession` **两个字段不同值**,断言顶栏显示的是 `title`。验收:`check:conv-comp` **28/28** | — |
154
+
155
+ 每块都要过:两条对话闸门(`npm run check:conv-host` / `check:conv-comp`)+ `npm run check:ui` + `check-conv-shards` 三端视角。
156
+
157
+ **⚠️ 这条线到此收工**(①②③④ 全部落地)。剩下的是**观感打磨**,不再是结构问题:
158
+ 1. `conv.css` 是「从 magic-test/app.css 抽出 + 令牌名映射」的**首版皮肤**。要调观感就改它
159
+ (三端同时变);某个端要有自己的样子,就在那一页**覆盖令牌**(皮肤只用不定义)。
160
+ 2. 真机 / APK 那一层仍要在**打包产物**上过一遍:本仓库能验的到「页面装配 + 组件挂载 + 一圈事件」
161
+ (`check:conv-comp` 两段),验不到 WebView 里的实际渲染。
162
+ 3. `client-ui/pc/`(完整工作台)走的是它自己那份 `app.css`,没接共用皮肤 —— 要不要收进来是**另一个决定**
163
+ (它现在还带着 `.qa-*` 的历史副本)。
164
+
165
+
166
+ ## 四、动手的前提(缺一不可)
167
+
168
+ 1. 明确 **AA 规格能不能改**(`tests/check-ui-spec.mjs` 的 32 条是外部基线还是可协商的);
169
+ 2. 有一份**移动端观感的对照基准**(现网截图 / 设计稿)—— 否则「换完是否算对」无从判断;
170
+ 3. 换哪一块、留哪一块,按上面的映射表逐块确认(每块都能单独上线,不需要一次做完)。
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "conv-core",
3
+ "version": 1,
4
+ "note": "「一条事件流 → 画成对话」的唯一实现。宿主(PC / 移动 / magic-test 工作台)只使用它:宿主提供样式令牌、自己的壳(顶栏 / 侧栏 / 需求看板 / 输入区)与下列 hostSymbols;内核只用不定义。校验:node tests/check-conv-shards.mjs(--write 按代码现状重写 hostSymbols)。",
5
+ "order": [
6
+ "01-dom.js",
7
+ "02-media-kinds.js",
8
+ "03-chat-message.js",
9
+ "06-ask-answer.js",
10
+ "07-stream-delegation.js",
11
+ "08-ask-event-map.js",
12
+ "09-stream-apply.js",
13
+ "10-markdown-render.js",
14
+ "18-askbox-markup.js",
15
+ "19-attachments.js",
16
+ "27-conv-ledger.js",
17
+ "29-msg-variants.js"
18
+ ],
19
+ "hostSymbols": [
20
+ "ASK_CFG",
21
+ "ASK_INSTANCES",
22
+ "QA_ICONS",
23
+ "prdBackendBase",
24
+ "qaActiveId",
25
+ "qaEngHintSync",
26
+ "qaEngine",
27
+ "qaEngines",
28
+ "qaModel",
29
+ "qaModelChainLoad",
30
+ "qaModelFor",
31
+ "qaModelName",
32
+ "qaModelPick",
33
+ "qaModelSet",
34
+ "qaReqConvMsg",
35
+ "qaSend",
36
+ "qaTaskSetModel",
37
+ "qaTasks",
38
+ "qaWorkspace",
39
+ "reqBoardFetch",
40
+ "reqHas",
41
+ "toast",
42
+ "wsDirOf"
43
+ ]
44
+ }