cs-bots 0.1.1 → 0.7.5

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 (48) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/README.md +65 -10
  3. package/docs/01-/346/236/266/346/236/204/346/226/207/346/241/243.md +364 -0
  4. package/docs/02-/351/234/200/346/261/202/350/247/204/346/240/274/344/271/246.md +284 -0
  5. package/docs/03-/350/257/246/347/273/206/350/256/276/350/256/241.md +567 -0
  6. package/docs/04-/346/223/215/344/275/234/346/211/213/345/206/214.md +569 -0
  7. package/docs/ARCHITECTURE.md +277 -0
  8. package/docs/adr/ADR-001-/351/241/265/347/255/276/347/273/223/346/236/204/345/233/236/345/210/260/344/270/203/351/241/265.md +70 -0
  9. package/docs/steps/step-01-/345/216/237/346/236/266/346/236/204/345/244/215/345/216/237/344/270/216IA/350/257/212/346/226/255.md +80 -0
  10. package/docs/steps/step-02-/351/241/265/347/255/276/346/224/266/346/225/233/344/270/216/345/215/207/347/272/247/351/200/232/351/201/223.md +51 -0
  11. package/docs/steps/step-03-/346/236/266/346/236/204/350/265/204/346/226/231/346/210/220/346/226/207.md +16 -0
  12. package/docs/steps/step-04-/344/270/212/347/272/277/344/270/216/351/252/214/350/257/201.md +46 -0
  13. package/docs//344/272/244/344/273/230/350/257/264/346/230/216-v0.2.0.md +95 -0
  14. package/docs//346/223/215/344/275/234/346/211/213/345/206/214-v1.md +509 -0
  15. package/docs//350/277/201/347/247/273/346/226/271/346/241/210-v1.md +64 -0
  16. package/docs//351/225/277/346/234/237/350/256/260/345/277/206/346/226/271/346/241/210-v1.md +85 -0
  17. package/package.json +4 -2
  18. package/src/admission.js +365 -0
  19. package/src/api.js +4097 -420
  20. package/src/board.js +883 -14
  21. package/src/botprofile.js +190 -3
  22. package/src/client.js +6596 -1082
  23. package/src/cycle.js +516 -0
  24. package/src/delivery-watch.js +255 -0
  25. package/src/dirbrowse.js +20 -0
  26. package/src/discipline.js +1188 -0
  27. package/src/fence.js +142 -0
  28. package/src/index.js +387 -4
  29. package/src/kernel.js +636 -24
  30. package/src/library.js +1224 -11
  31. package/src/memory.js +617 -37
  32. package/src/metrics.js +339 -0
  33. package/src/prompts.js +1318 -0
  34. package/src/reviewer.js +709 -0
  35. package/src/room.js +655 -14
  36. package/src/roster.js +346 -26
  37. package/src/routing/complexity.js +320 -0
  38. package/src/routing/hints.js +707 -0
  39. package/src/scratch.js +477 -0
  40. package/src/settings.js +97 -0
  41. package/src/stage.js +263 -0
  42. package/src/store.js +133 -1
  43. package/src/subscription.js +571 -0
  44. package/src/suggest.js +1203 -0
  45. package/src/teams.js +527 -0
  46. package/src/todo.js +1301 -0
  47. package/src/tools.js +734 -10
  48. package/src/verify.js +356 -0
@@ -0,0 +1,567 @@
1
+ # cs-bots 改造07 + 改造08(需求1–5)详细设计文档
2
+
3
+ > 版本:v0.7.3(改造07 分层 + 改造08 需求1–5)
4
+ > 日期:2026-11-09
5
+ > 状态:已实现,1108 项测试全通过(`bash test/run-all.sh` exit 0)
6
+
7
+ ---
8
+
9
+ ## 1. 文档定位
10
+
11
+ 本文档是**实现级**设计,回答「每个模块的具体接口、数据结构、错误码、边界条件」。架构层面的分层原则见《架构文档》。
12
+
13
+ ---
14
+
15
+ ## 2. 模块一:StageService(工单阶段机)
16
+
17
+ ### 2.1 设计动机
18
+
19
+ 现有任务状态只有 5 个粗粒度值,回答不了「此刻卡在谁身上」。实测痛点:任务显示 in_progress 十几小时纹丝不动,人不知道为什么——是 Bot 在跑?在排队?在等人回答?还是投递失败没人知道?
20
+
21
+ 借 Super-agent 的 stage table 把「在做」切成 8 个可判定阶段,其中 5 个是「等人」阶段,人一眼看出该谁动手。
22
+
23
+ ### 2.2 数据结构
24
+
25
+ ```js
26
+ // 阶段枚举(值即稳定 id,不可改名)
27
+ export const STAGES = Object.freeze({
28
+ PERSON_DELIVERY: 'person/delivery',
29
+ PERSON_ANSWER: 'person/answer',
30
+ PERSON_REVIEW: 'person/review',
31
+ PERSON_BLOCKED: 'person/blocked',
32
+ PERSON_FAILED: 'person/failed',
33
+ WORKING_QUEUED: 'working/queued',
34
+ IDLE: 'idle',
35
+ WORKING: 'working',
36
+ })
37
+
38
+ // 判定优先级(自上而下首个命中)
39
+ export const STAGE_ORDER = [PERSON_DELIVERY, PERSON_ANSWER, PERSON_REVIEW,
40
+ PERSON_BLOCKED, PERSON_FAILED, WORKING_QUEUED,
41
+ IDLE, WORKING]
42
+
43
+ // 人类标签(界面直接展示)
44
+ export const STAGE_LABEL = {
45
+ 'person/delivery': '投递失败',
46
+ 'person/answer': '等你回答',
47
+ 'person/review': '等你验收',
48
+ 'person/blocked': '受阻等你',
49
+ 'person/failed': '失败等你',
50
+ 'working/queued': '排队中',
51
+ 'idle': '空闲等触发',
52
+ 'working': '正在干',
53
+ }
54
+
55
+ // 语义分组(前端配色 + needsPerson 判定)
56
+ export const STAGE_GROUP = {
57
+ 'person/delivery': 'person',
58
+ 'person/answer': 'person',
59
+ 'person/review': 'person',
60
+ 'person/blocked': 'person',
61
+ 'person/failed': 'person',
62
+ 'working/queued': 'working',
63
+ 'idle': 'idle',
64
+ 'working': 'working',
65
+ }
66
+ ```
67
+
68
+ ### 2.3 核心函数
69
+
70
+ ```js
71
+ /**
72
+ * 判定任务执行阶段(纯函数,不修改入参,不落盘)
73
+ * @param {object} task board 的 Task 形状
74
+ * @returns {{ stage, label, group, needsPerson, reason }}
75
+ */
76
+ export function deriveStage(task)
77
+ ```
78
+
79
+ **判定流程(自上而下首个命中)**:
80
+
81
+ | 顺序 | 条件 | 阶段 | 人话 |
82
+ |---|---|---|---|
83
+ | 0 | `status==='deleted'` | idle | 已删除(终态) |
84
+ | 0 | `status==='completed' && accepted!==true` | person/review | 已完成,等验收 |
85
+ | 0 | `status==='completed' && accepted===true` | idle | 已完成且验收 |
86
+ | 1 | `delivery.error` 非空 | person/delivery | 投递失败:xxx |
87
+ | 2 | `latestReport.attention` 非空 | person/answer | 执行者提了问题 |
88
+ | 3 | `latestReport.state==='blocked'` | person/blocked | 执行者报告受阻 |
89
+ | 4 | `latestReport.state==='failed'` | person/failed | 执行者报告失败 |
90
+ | 5 | `latestReport.state==='ready'` | person/review | 交付物就绪 |
91
+ | 6 | 无 owner 且无未过期 lease | working/queued | 尚未被认领 |
92
+ | 7 | `latestReport.state==='waiting'` | idle | 本轮已交付 |
93
+ | 8 | 兜底 | working | 正在推进 |
94
+
95
+ **报告形状的宽容性**:`latestReport` 支持三种历史形状:
96
+ - 字符串:`'ready'`
97
+ - 对象:`{ state: 'ready' }`
98
+ - 对象:`{ status: 'ready' }`
99
+
100
+ ### 2.4 批量与统计
101
+
102
+ ```js
103
+ /** 批量派生,不修改原数组 */
104
+ export function attachStages(tasks) // → [{ ...task, stageInfo }]
105
+
106
+ /** 按语义分组计数(排除 deleted) */
107
+ export function stageSummary(tasks) // → { person, working, idle, total }
108
+ ```
109
+
110
+ ### 2.5 错误码
111
+
112
+ | 码 | 场景 |
113
+ |---|---|
114
+ | 无 | 阶段机不抛错(纯函数,输入非法时保守落到 working/queued) |
115
+
116
+ ---
117
+
118
+ ## 3. 模块二:SubscriptionService(任务订阅扇出)
119
+
120
+ ### 3.1 设计动机
121
+
122
+ 房间里的讨论在推进,但相关任务看不到——人只好手动补一句「刚才房间说……」。借 Super-agent 的 Subscription 解决:订阅后,房间后续消息自动到达任务,不需要再 @。
123
+
124
+ ### 3.2 数据结构
125
+
126
+ ```js
127
+ /** Subscription 记录 */
128
+ {
129
+ id: 'sub-1', // 订阅 id
130
+ rev: 1, // CAS 版本
131
+ taskId: 't-1', // 任务
132
+ roomId: 'room-a', // 房间
133
+ state: 'active', // active | dropped
134
+ context: [ // 滚动上下文窗口
135
+ { from, seq, text, at, messageId }
136
+ ],
137
+ fanoutCount: 0, // 累计扇出次数
138
+ note: '为什么订阅', // 可选
139
+ createdAt, createdBy,
140
+ updatedAt, updatedBy,
141
+ droppedAt, droppedBy, // dropped 时有
142
+ }
143
+ ```
144
+
145
+ ### 3.3 核心接口
146
+
147
+ ```js
148
+ /**
149
+ * 订阅(幂等:同一 taskId+roomId 已有活订阅时返回既有记录)
150
+ * @param {{ taskId, roomId, note?, by, now? }} input
151
+ */
152
+ subscribe(input) → Subscription
153
+
154
+ /**
155
+ * 取消订阅(终态:active → dropped,不可逆)
156
+ * @param {{ id, expectedRev?, by, now? }} input
157
+ */
158
+ unsubscribe(input) → Subscription
159
+
160
+ /**
161
+ * 扇出:把房间消息送达所有订阅该房间的活任务
162
+ * @param {{ roomId, message, wake?, by?, now? }} input
163
+ * @returns {{ fanned, woke, failed, details }}
164
+ */
165
+ fanout(input) → Promise<result>
166
+
167
+ /** 列表(按 taskId/roomId/state 过滤) */
168
+ list(query) → Subscription[]
169
+
170
+ /** 某任务的订阅(任务详情用) */
171
+ listByTask(taskId) → Subscription[]
172
+
173
+ /** 某房间的订阅者任务 id(房间页用) */
174
+ subscribersOf(roomId) → string[]
175
+
176
+ /** 统计 */
177
+ stats() → { total, active, dropped, byState, fanouts, wakeFailures, events }
178
+
179
+ /** 从事件流重建 */
180
+ replay() → { from, events, bad }
181
+ ```
182
+
183
+ ### 3.4 配置项
184
+
185
+ | 配置 | 默认 | 说明 |
186
+ |---|---|---|
187
+ | `fanoutKeep` | 20 | 每个任务保留的上下文条目上限 |
188
+ | `wakeOnFanout` | false | 扇出后是否唤醒 owner |
189
+ | `stateDir` | kernel.stateDir() | 状态目录 |
190
+
191
+ ### 3.5 扇出逻辑
192
+
193
+ ```
194
+ for each active subscription of roomId:
195
+ 1. 构造 context entry(text 截断 2000 字符)
196
+ 2. context = [...old, entry],若超 fanoutKeep 则 slice(-fanoutKeep)
197
+ 3. fanoutCount++
198
+ 4. commit('fanout') —— 先落盘
199
+ 5. 若 shouldWake:
200
+ a. 找 task.ownerId
201
+ b. kernel.followup(ctx, ownerId, text)
202
+ c. 成功 woke++,失败 failed++
203
+ ```
204
+
205
+ ### 3.6 错误码
206
+
207
+ | 码 | 场景 |
208
+ |---|---|
209
+ | `SUBSCRIPTION_ERROR` | 通用 |
210
+ | `SUBSCRIPTION_BAD_FIELD` | 字段类型/缺失 |
211
+ | `SUBSCRIPTION_NOT_FOUND` | 订阅不存在 |
212
+ | `SUBSCRIPTION_REV_CONFLICT` | CAS 冲突 |
213
+ | `SUBSCRIPTION_ALREADY_DROPPED` | 已 dropped 再取消 |
214
+ | `SUBSCRIPTION_APPEND_FAILED` | 落盘失败 |
215
+ | `SUBSCRIPTION_NO_STATE_DIR` | 无状态目录 |
216
+
217
+ ### 3.7 与 Super-agent 的差异
218
+
219
+ | 维度 | Super-agent | cs-bots |
220
+ |---|---|---|
221
+ | 订阅主体 | Ticket ↔ Thread | Task ↔ Room |
222
+ | 扇出后果 | 消息作为上下文 Input | 消息作为任务上下文,可选唤醒 |
223
+ | 授权影响 | 只加 context 不加 authority | 同 |
224
+ | 生成方式 | 模型/人订阅 | 人/模型订阅 |
225
+
226
+ ---
227
+
228
+ ## 4. 模块三:prepare command
229
+
230
+ ### 4.1 设计动机
231
+
232
+ Bot 开工前常需要准备环境(拉代码、建 worktree、装依赖)。借 Super-agent 的 prepare command。
233
+
234
+ ### 4.2 数据结构
235
+
236
+ Bot 记录新增字段:
237
+
238
+ ```js
239
+ {
240
+ // ...既有字段
241
+ prepareCommand: 'git worktree add ../x main', // 可选
242
+ prepareCommandSetAt: 1730000000000,
243
+ prepareCommandSetBy: 'lead',
244
+ }
245
+ ```
246
+
247
+ ### 4.3 接口
248
+
249
+ ```js
250
+ // roster.js
251
+ setPrepareCommand({ id, prepareCommand, expectedRev, byId, now? }) → Bot
252
+ // 传空字符串/null = 清除(三字段一起清)
253
+
254
+ // api.js(内部 helper)
255
+ runPrepareCommand(bot, { timeoutMs? }) → Promise<{
256
+ ok, skipped?, command?, cwd?, exitCode?, stdout?, stderr?, error?
257
+ }>
258
+ ```
259
+
260
+ ### 4.4 claim 时执行流程(POST /tasks/assign)
261
+
262
+ ```
263
+ 1. 读 Bot(roster.get(ownerId))
264
+ 2. 若 bot.prepareCommand 非空:
265
+ a. prepare = await runPrepareCommand(bot)
266
+ b. 若 !prepare.ok:
267
+ - 不 claim(任务保持原状)
268
+ - 返回 { ok:false, code:'PREPARE_FAILED', prepare, reason }
269
+ - return
270
+ 3. claim 任务(board.update action:'claim')
271
+ 4. 唤醒 owner(dispatcher.notify)
272
+ ```
273
+
274
+ ### 4.5 安全边界
275
+
276
+ | 措施 | 说明 |
277
+ |---|---|
278
+ | 白名单 | 首段必须在 `CMD_WHITELIST`:bash/node/npm/pnpm/npx/git/ls/test/python3 |
279
+ | 无 shell | `execFile(cmd, args)` 传参数组,不做 shell 展开 |
280
+ | 硬超时 | 默认 10 分钟(`timeout` 参数) |
281
+ | cwd 固定 | Bot 工作目录(写入边界) |
282
+ | 参数切分 | 只按空白切分,不做引号/管道/重定向 |
283
+
284
+ ### 4.6 错误码
285
+
286
+ | 码 | 场景 |
287
+ |---|---|
288
+ | `prepare-command-not-allowed:xxx` | 首段不在白名单 |
289
+ | `bot-cwd-missing` | Bot 无 cwd |
290
+ | `timeout` | 超时 |
291
+ | `PREPARE_FAILED`(API) | 准备失败,未认领 |
292
+
293
+ ---
294
+
295
+ ## 5. 模块四:穿透面板(DeepDive)
296
+
297
+ ### 5.1 设计动机
298
+
299
+ 下层是 AI 的基础设施,人不该天天看,但排查问题时必须能进。穿透是**显式入口**:顶部「⚙ 穿透」,不是隐藏的。
300
+
301
+ ### 5.2 API 端点
302
+
303
+ ```js
304
+ // GET /deepdive/blocks
305
+ // 返回 7 个区块:registry / wake / store / routing / verify / suggest / subscription
306
+ {
307
+ ok: true,
308
+ blocks: [
309
+ { id, label, description, stats: {...} }
310
+ ]
311
+ }
312
+
313
+ // GET /deepdive/block/:id
314
+ {
315
+ ok: true,
316
+ block: 'registry',
317
+ data: { bots: [...] }, // 区块内容(形状因区块而异)
318
+ operations: [ // 可执行操作
319
+ { id: 'revive', label: '...', body: {} }
320
+ ]
321
+ }
322
+
323
+ // POST /deepdive/op
324
+ // body: { block, op, ...opBody }
325
+ { ok: true, text: '已强制投递:sent=3, failed=0', result: {...} }
326
+ ```
327
+
328
+ ### 5.3 七个区块
329
+
330
+ | 区块 | stats 内容 | 可执行操作 |
331
+ |---|---|---|
332
+ | registry | bots 数量、各状态计数 | — |
333
+ | wake | total/sent/skipped/failed/pending | flush |
334
+ | store | 存在的文件数/总数 | replay |
335
+ | routing | tiers、default | — |
336
+ | verify | verify 服务 stats | — |
337
+ | suggest | suggest 服务 stats | — |
338
+ | subscription | 订阅 stats | — |
339
+
340
+ ### 5.4 前端组件
341
+
342
+ ```js
343
+ function DeepDivePanel({ onClose }) {
344
+ // useDeepDive() → GET /deepdive/blocks
345
+ // openBlock(id) → GET /deepdive/block/:id
346
+ // doOp(block, op, body) → POST /deepdive/op
347
+ }
348
+ ```
349
+
350
+ 渲染结构:
351
+ ```
352
+ Drawer(底层工具)
353
+ ├── 每个区块:Card(label + description + stats + 展开箭头)
354
+ └── 展开时:区块详情(data 字段逐个渲染 + operations 按钮)
355
+ ```
356
+
357
+ ### 5.5 错误处理
358
+
359
+ | 场景 | 处理 |
360
+ |---|---|
361
+ | 区块加载失败 | 显示 `穿透面板加载失败:<原因>` |
362
+ | 区块详情失败 | 显示错误 |
363
+ | 操作失败 | 显示人话 + 原因 |
364
+ | 未知区块 | 404 + `未知区块: xxx` |
365
+ | 不支持的操作 | 400 + `不支持的操作:block/op` |
366
+
367
+ ---
368
+
369
+ ## 6. 模块五:分层导航
370
+
371
+ ### 6.1 TABS 结构
372
+
373
+ ```js
374
+ const TABS = [
375
+ { id: 'workspace', label: '工作台', subs: [board, cycles, todo, scratch, artifacts] },
376
+ { id: 'team', label: '团队', subs: [bots, teams, rooms] },
377
+ { id: 'memory', label: '记忆', subs: [facts, rules, prompts] },
378
+ { id: 'skills', label: '技能库', subs: [skills, workflows] },
379
+ { id: 'data', label: '数据', subs: [files, metrics] },
380
+ { id: 'settings', label: '设置', Comp: SettingsBody },
381
+ ]
382
+ ```
383
+
384
+ ### 6.2 向后兼容映射
385
+
386
+ ```js
387
+ const safeGoTab = (id, opts) => {
388
+ const mapped = ({
389
+ tasks: 'workspace', bots: 'team', rooms: 'team',
390
+ teams: 'team', artifacts: 'workspace',
391
+ })[id] ?? id
392
+ goTab(mapped, opts)
393
+ }
394
+ ```
395
+
396
+ ### 6.3 locate 传参
397
+
398
+ 只有「团队 → 房间」子视图接收 `locate`(跨页签定位):
399
+
400
+ ```js
401
+ locate: current.id === 'team' && activeView.id === 'rooms' ? locate : undefined
402
+ ```
403
+
404
+ ---
405
+
406
+ ## 7. 前端视觉规范
407
+
408
+ ### 7.1 阶段徽章样式
409
+
410
+ ```css
411
+ .csb-stage /* 基础:19px 高,圆角 5px,10.5px 字 */
412
+ .csb-stage-person /* 需人动手:警告色 + 9% 底色 + 加粗 */
413
+ .csb-stage-working /* 机器推进:常规色 */
414
+ .csb-stage-idle /* 空闲:弱化色,opacity .8 */
415
+ ```
416
+
417
+ 使用位置:
418
+ - 任务卡片(看板视图):阶段徽章 + 「需你处理」标记
419
+ - 任务详情抽屉:执行阶段行(徽章 + 原因 title)
420
+
421
+ ### 7.2 移动端适配
422
+
423
+ | 元素 | 适配 |
424
+ |---|---|
425
+ | 页签 | `overflow-x:auto` 横向滚动 |
426
+ | 子页签 | 横向滚动 |
427
+ | 穿透面板 | `Drawer` 全屏抽屉(宽度 min(660px, 100%)) |
428
+ | 任务卡片 | 响应式 flex 布局 |
429
+ | 房间分栏 | `@media(max-width:900px)` 转纵向 |
430
+
431
+ ---
432
+
433
+ ## 8. 测试覆盖
434
+
435
+ | 测试文件 | 用例数 | 覆盖 |
436
+ |---|---|---|
437
+ | `test/stage.test.mjs` | 30 | 阶段判定全路径、优先级、宽容性、纯函数性 |
438
+ | `test/subscription.test.mjs` | 17 | 订阅/幂等/扇出/滚动窗口/CAS/replay |
439
+ | `test/api.test.mjs` | 37 | 穿透面板 8 项、订阅 4 项、阶段 3 项 |
440
+ | `test/roster.test.mjs` | 30 | prepare command 8 项 |
441
+ | `test/client.test.mjs` | 13 | 6 页签 + 穿透入口渲染 |
442
+ | 其余既有测试 | 909 | 全部回归 |
443
+ | **合计** | **1036** | **0 失败** |
444
+
445
+ ---
446
+
447
+ ## 9. 实现清单
448
+
449
+ | 文件 | 改动类型 | 行数 |
450
+ |---|---|---|
451
+ | `src/stage.js` | 新增 | +302 |
452
+ | `src/subscription.js` | 新增 | +497 |
453
+ | `src/roster.js` | 修改 | +44 |
454
+ | `src/api.js` | 修改 | +304 |
455
+ | `src/client.js` | 修改 | +245 |
456
+ | `src/tools.js` | 修改 | +97 |
457
+ | `src/index.js` | 修改 | +10 |
458
+ | `test/stage.test.mjs` | 新增 | +330 |
459
+ | `test/subscription.test.mjs` | 新增 | +226 |
460
+ | `test/api.test.mjs` | 修改 | +150 |
461
+ | `test/roster.test.mjs` | 修改 | +75 |
462
+
463
+ ---
464
+
465
+ *详细设计文档完。*
466
+ ---
467
+
468
+ ## 改造08 · 需求 1–5(2026-11-09)
469
+
470
+ > 这一节的每一条都对应你原话里的一条要求,写完当天就上了线(0.7.3 已部署重启,启动图校验 PASS)。
471
+
472
+ ### 需求1 · 任务页只展示人建的任务,智能体任务单独一页
473
+
474
+ | | 改前 | 改后 |
475
+ |---|---|---|
476
+ | 「我的任务」页 | 人和智能体的任务混在一起 | 只显示 `source=human`(你自建的任务) |
477
+ | 智能体任务 | 混在同一个列表里翻 | 单独的「智能体任务」页签,按状态分类 |
478
+
479
+ - 后端 `GET /tasks?source=human|ai` 早已支持(`src/board.js` 里 `source` 字段),这次是**前端把它用起来**。
480
+ - 「我的任务」页保留「只看需要我处理的」开关;「智能体任务」页不显示这个开关(智能体的活不需要你逐条盯)。
481
+
482
+ ### 需求2 · 派活后能追踪进度、看到产出物、看到对话
483
+
484
+ 这一条挖到了一个**真 bug**,不是你操作的问题:
485
+
486
+ > `eventsToMessages` 只读 `ev.content`,而官方会话事件是 `{ type, seq, time, data }`,
487
+ > 内容在 `data.content` / `data.message.content` 里;`tool/call`、`tool/result` 是顶层事件。
488
+ > 结果:**对任何真实会话都恒定返回 0 条** —— 所以你看到的就是「没有对话内容可看」。
489
+
490
+ 实测同一个真实会话(4795 个事件):
491
+
492
+ | | 修前 | 修后 |
493
+ |---|---|---|
494
+ | 对话片段总数 | **0** | **2625** |
495
+ | 你的发言 | 0 | 110 |
496
+ | 执行者正文 | 0 | 74 |
497
+ | 思维链 | 0 | 465 |
498
+ | 工具调用 | 0 | 1306 |
499
+ | 工具结果 | 0 | 670 |
500
+
501
+ 新增/变更:
502
+
503
+ - 后端 `GET /tasks/conversation?id=&limit=` —— 只读打开执行者会话(`persistence.open(id,'read')`,不抢所有权),返回 `{ bot, sessionId, messages[], total, truncated, available }`。
504
+ - 前端任务详情新增**任务对话**面板:显示执行者身份、消息条数;默认只给正文(手机不被思维链淹没),点「看全部」才展开工具调用与思维链;「去对话」直接打开它的会话,宿主不支持就复制会话 ID 兜底。
505
+ - 无执行者时是**空态**(「还没有执行者」+ 下一步),不是报错。
506
+ - 产出物空态改成说清「为什么现在没有」:未完成时正常;已完成却没带证据时提示可以重开补交。
507
+
508
+ ### 需求3 · 团队页只显示你建的团队,负责人有标识,成员可选
509
+
510
+ - 团队页只拉 `GET /teams?type=big`(后端 `type` 字段保留不动,只是 UI 不再暴露「大小团队」概念)。
511
+ - 负责人带 `👑 负责人` 标识,成员列表里负责人加 `👑 ` 前缀;移出负责人会明确警告「移出后团队将没有负责人」。
512
+ - 建团队时负责人用下拉选择,成员用勾选组(可多选,支持新建 Bot 后再来选)。
513
+ - 「看房间」→ 跳房间页并**按团队名定向筛选**:房间页顶部出现「筛选房间:包含「X」→ N 个房间」和「✕ 清筛选」。
514
+ - 你要的「穿透房间看子团队工作内容」:房间页本来就是超级管理员视角,所有房间都能进、都能看长正文(`withBody=1` 一次取回),筛选只是在大列表里做定向。
515
+
516
+ ### 需求4 · PDCA 人机分治(这条改动最大)
517
+
518
+ 你的原话:「pdca 是对用户长任务进行复盘跟进的,人类主导;智能体的 pdca 流程你优化一下」。
519
+
520
+ 结论是**两种周期,两套纪律**:
521
+
522
+ | | `kind=human`(你的) | `kind=agent`(智能体的) |
523
+ |---|---|---|
524
+ | 谁主导 | **你** | 智能体自己 |
525
+ | 用途 | 跟长任务、防漂移 | 定期批量归纳、自我提升 |
526
+ | 阶段纪律 | 计划→执行→检查→改进→完成,**五步不可跳** | 复盘可**一次性提交**,提交即自动闭环 |
527
+ | UI 位置 | 「📋 我的任务周期」(主区) | 「🤖 智能体归纳周期」(折叠在下方) |
528
+ | 复盘后 | 留在「改进」等你显式推进到完成 | 自动 → 完成(`autoClosed: true`) |
529
+ | 产出 | 经验教训 + 标准化建议 | 同左,但**只进建议队列**,你采纳才落库 |
530
+
531
+ 为什么 agent 允许一次性提交:让它假装走 `planned→doing` 是对事实的虚构。你要的是「结论 + 可采纳的标准」,不是看它表演流程。
532
+
533
+ **「定期」怎么落地**:`reviewer` 只做被动回合计数(每回合 +1,到阈值就 `due`),它不自己起定时器(宿主生命周期不可控)。所以:
534
+
535
+ 1. 周期页读 `GET /review/status`,把到期会话列出来(当前线上实测 3 个会话到期,共 111 回合);
536
+ 2. 你点「让它归纳」→ `POST /review/urge` 唤醒它,并给它明确三步指令(开 `kind=agent` 周期 → 一次性复盘 → 建议进队列);
537
+ 3. 归纳出的纪律**不会自动改自己**,走 `bot_suggestion_propose`,你采纳才生效。
538
+
539
+ 这条暗道是被故意堵死的:智能体不能自己给自己立规矩。
540
+
541
+ ### 需求5 · 表述更正
542
+
543
+ 任务详情 5W 区:
544
+
545
+ - 「谁建的」:`source=human` → **用户**;智能体分发 → 显示 Bot 名。
546
+ - 「来源」:`source=human` → **用户自建**;否则 → 智能体分发。
547
+
548
+ 不再把「`ui`」这种平台伪身份暴露给你看。
549
+
550
+ ### 验证证据(可核查)
551
+
552
+ ```
553
+ test/kernel-conversation.test.mjs 8 项 ✅
554
+ test/client-anchor.test.mjs 39 项 ✅
555
+ 全套 node --test test/*.test.mjs → 1108 / 1108 pass, 0 fail
556
+ bash test/run-all.sh → ✅ 全部通过(exit 0)
557
+ dsh-plugins.sh precheck → OK cs-bots -> src/client.js
558
+ dsh web 重启 → 启动图端到端校验 PASS
559
+
560
+ 线上实测(http://150.158.175.241/)
561
+ GET /cs-bots/api/tasks/conversation?id=t-29 → ok=true(无对话时如实 0 条)
562
+ GET /cs-bots/api/review/status → sessions=24, due=true, dueSessions=3
563
+ GET /cs-bots/api/cycles?kind=human → 只返回 human 周期
564
+ GET /cs-bots/api/cycles?kind=agent → items=[](尚无 agent 周期)
565
+ GET /cs-bots/api/teams?type=big → 1 个团队(Myfree改造,leadId 有值)
566
+ 下发 bundle(362 KB) → 含 TaskConversationPanel / roomFilter / 让它归纳
567
+ ```