@infinisys/is-fabric-team 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +215 -2
  2. package/client.js +4245 -0
  3. package/cordis.patch.yml +462 -0
  4. package/icon.svg +14 -0
  5. package/index.js +1816 -0
  6. package/lib/actor-resolver.js +29 -0
  7. package/lib/attention.js +320 -0
  8. package/lib/binding-route.js +98 -0
  9. package/lib/binding-service.js +85 -0
  10. package/lib/blocker-gate.js +74 -0
  11. package/lib/check-run.js +398 -0
  12. package/lib/continuation.js +259 -0
  13. package/lib/criterion-amend.js +109 -0
  14. package/lib/cycle-close.js +174 -0
  15. package/lib/descriptions.js +118 -0
  16. package/lib/gitfox-gate.js +130 -0
  17. package/lib/local-route.js +92 -0
  18. package/lib/member-tools.js +328 -0
  19. package/lib/mode-overlay.js +151 -0
  20. package/lib/mode-route.js +61 -0
  21. package/lib/mode-state.js +132 -0
  22. package/lib/mode-switch.js +168 -0
  23. package/lib/model-route.js +98 -0
  24. package/lib/model-switch.js +387 -0
  25. package/lib/open-work.js +252 -0
  26. package/lib/positions.js +204 -0
  27. package/lib/problem-gate.js +952 -0
  28. package/lib/problem-host.js +230 -0
  29. package/lib/publish-action.js +76 -0
  30. package/lib/report-gate.js +225 -0
  31. package/lib/review-gate.js +122 -0
  32. package/lib/review-host.js +336 -0
  33. package/lib/scope-install.js +212 -0
  34. package/lib/session-log-heal.js +428 -0
  35. package/lib/session-title-heal.js +437 -0
  36. package/lib/shell-git.js +332 -0
  37. package/lib/state-route.js +139 -0
  38. package/lib/status-host.js +95 -0
  39. package/lib/status-view.js +129 -0
  40. package/lib/stop-route.js +61 -0
  41. package/lib/team-core.js +1746 -0
  42. package/lib/team-discovery.js +189 -0
  43. package/lib/team-error.js +21 -0
  44. package/lib/team-journal.js +408 -0
  45. package/lib/team-model.js +676 -0
  46. package/lib/team-runtime.js +642 -0
  47. package/lib/team-stop.js +166 -0
  48. package/lib/team-store.js +723 -0
  49. package/lib/team-withdraw.js +93 -0
  50. package/lib/tool-output.js +37 -0
  51. package/lib/tool-schema.js +132 -0
  52. package/lib/tracking.js +64 -0
  53. package/lib/watchdog.js +798 -0
  54. package/lib/work-record-gate.js +357 -0
  55. package/lib/work-record-host.js +206 -0
  56. package/lib/workspace-catalog.js +101 -0
  57. package/lib/workspace-scope.js +20 -0
  58. package/locale/en.json +7 -0
  59. package/locale/zh.json +7 -0
  60. package/mode.js +43 -0
  61. package/package.json +51 -4
package/README.md CHANGED
@@ -1,3 +1,216 @@
1
- # Temporary Holding Version
1
+ # @infinisys/is-fabric-team
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Infinity Fabric 的 **AI 团队插件**(v2):新增 **团队协作模式(Team Collaboration Mode)** 的四个 Agent 预设。会话根身份是 **assistant**;插件自动拉起 `lead` 和 8 个角色;章程/名册/身份是本地状态;GitFox 只跟踪工作项。工具面是**结构性**的:每个角色跑只挂载它该有能力的那套工具的预设,助手和 Lead 根本没有 shell / 写文件工具可"揽活"。
4
+
5
+ ## 预设与工具面
6
+
7
+ | 预设 id | 席位 | bash | write/edit |
8
+ | --- | --- | --- | --- |
9
+ | `fabric-team` | assistant、lead | — | 不挂载(`read` 只读) |
10
+ | `fabric-team-worker` | dev-backend、dev-frontend、cicd、test | ✅ | 挂载,工作区内任意路径 |
11
+ | `fabric-team-readonly` | qa、secure、hr | — | 不挂载(`read` 只读) |
12
+ | `fabric-team-docs` | docs | — | 挂载,工作区内任意路径 |
13
+
14
+ 角色 → 预设的映射是 `lib/team-model.js` 的 `ROLE_PRESET`(单一事实源);`spawn_member` 用 `agentPreset` 拉起独立预设,不依赖父会话继承。
15
+
16
+ ## HR 定义职位
17
+
18
+ 章程只有 10 个席位,但团队需要的能力不是 10 个:HR 用 `define_role` 建出章程里没有的职位,再用普通的 `enroll` + `spawn_member` 把人放上去。职位写进本地账本 `state.roles`,不是代码常量。
19
+
20
+ - **由 HR 决定挂什么工具**:`capabilities` 是一对布尔值(`shell`、`write`),可组合出四种工具面——shell+write、只 write、只 shell、都不挂。Host 把它映射到最窄的预设再收紧:shell+write 与只 shell 走 `fabric-team-worker`(只 shell 的职位由写守卫摘掉 write/edit),只 write 走 `fabric-team-docs`,都不挂走 `fabric-team-readonly`。映射函数是 `presetForCapabilities()`。
21
+ - **两条线不能碰**:`assistant` / `lead` / `hr` 是拉起链本身,不可定义、不可重定义、不可退休(`reserved-role`);新职位 `votingSeat` 恒为 `null`,四席一致的门槛不会因为加人而被稀释。
22
+ - **职位 id 永不复用**:`retire_role` 只在无人持有该职位身份、名册上也没有在用成员时成功;退休后同一 id 再 `define_role` / `enroll` 一律 `role-retired`。
23
+ - **退休身份同时离开名册**:`retire` 会把该成员的名册行标成 `retired`,否则职位会一直显示"有人在用",`retire_role` 永远走不到。
24
+
25
+ ## 与 v1 的关键区别
26
+
27
+ | 主题 | v2 |
28
+ | --- | --- |
29
+ | 会话身份 | root = `assistant`;`lead` 是第一个队员,不再复用原生 Agent Teams 的 lead |
30
+ | 团队运行时 | 使用 `subagents` 拉起 `lead` + 8 个队员;原生 Agent Teams 工具/UI 在本 profile 禁用 |
31
+ | 建队 | 进入模式自动建队(10 个角色),不需要 `bootstrap` |
32
+ | 章程/名册 | 本地账本;初始化、身份新增/退休都不创建 GitFox 议题 |
33
+ | GitFox | 只跟踪需求、会议、决策/投票;工作流推进只写本地账本,不再为每个阶段开议题;`configure` 只在本地的固定项目 |
34
+ | 身份权限 | 初始身份由系统 seed;之后只有 HR 能 `define_role` / `retire_role` / `enroll` / `retire` / `spawn_member`;assistant/lead 不能建身份或职位 |
35
+ | 工具面 | 结构性:角色用哪个预设就有什么工具;协调席位没有 shell/write,不存在"先给权限再 deny"的窗口 |
36
+ | 失败策略 | fail-closed:团队工具装不上时,协调席位拒绝所有工具调用并上报,而不是退化成单人开发 |
37
+
38
+ ## 团队名册
39
+
40
+ | 角色 | 成员 | 关系 | 投票 |
41
+ | --- | --- | --- | --- |
42
+ | assistant | `assistant` | 会话根、用户唯一接口 | — |
43
+ | lead | `lead` | assistant 的孩子;协调 8 个队员 | — |
44
+ | dev-backend | `dev-backend` | lead 的孩子 | ✅ |
45
+ | dev-frontend | `dev-frontend` | lead 的孩子 | ✅ |
46
+ | qa | `qa` | lead 的孩子 | ✅ |
47
+ | secure | `secure` | lead 的孩子 | ✅ |
48
+ | docs | `docs` | lead 的孩子 | — |
49
+ | cicd | `cicd` | lead 的孩子 | — |
50
+ | hr | `hr` | lead 的孩子;身份管理 | — |
51
+ | test | `test` | lead 的孩子 | — |
52
+
53
+ 消息是层级制:`assistant ↔ lead`、`lead ↔ 8 个队员`。队员之间不能直连,必须经 lead(`team_send_message` 会以 `not-adjacent` 拒绝)。
54
+
55
+ **拉起链固定,不能跳级**:assistant 只拉起 `lead`;`lead` 只拉起 `hr`;`hr` 通过 `infinity_team` 的 `spawn_member` 拉起其余工作组席位,并负责长期身份的 `enroll`/`retire`。`spawn_member` 对 assistant/lead/hr 一律返回 `role-not-allowed`。HR 还可以用 `define_role` 先建出章程里没有的职位,再按同样两步(`enroll` → `spawn_member`)把人放上去——新职位同样是 lead 的孩子,同样只走 `team_send_message`。
56
+
57
+ **名册齐备后不重建团队**:子 Agent 是进程内的,重启后就没了,但名册行里的 session id 指向持久会话。团队下一次给某个席位发消息时,`subagents` 运行时从那一段会话里冷恢复**同一个** Agent——同一个 Lead、同一段历史、不发新回合。所以重启既不会换掉 Lead,也不会在启动时自动跑一轮;只有持久会话确实无法恢复时才会替换该行并重拉一个席位(`lib/team-runtime.js` 的 `deliverToMember` / `respawnMember`)。
58
+
59
+ **重启时"没有行"才算缺**:启动只补名册里**根本没有行**的角色,而且只在它的上级席位此刻活着时才拉;上级不在(刚重启、没人给它发过消息)就把这件事写进 `diagnostics` 等上级冷恢复后再补,绝不为了补一个席位去唤醒上级——那等于自己发起一轮模型回合。账本里已有的行一律保留,`kept` 里有几个角色就是几个"没被动过"的席位。同理,重启后的冷恢复不会创建或重新武装目标(`agent/created` 不 arm),只有回合结束(`agent/turn-stopping`)才把目标接回去,所以没人说话时会话不会自己跑起来。
60
+
61
+ ## 能力边界
62
+
63
+ | 角色 | bash | write/edit |
64
+ | --- | --- | --- |
65
+ | assistant / lead / qa / secure / hr | ❌ 不挂载 | ❌ 不挂载(只读,`read` 可用) |
66
+ | dev-backend / dev-frontend / test / cicd | ✅ | ✅ 工作区任意路径 |
67
+ | docs | ❌ 不挂载 | ✅ 工作区任意路径 |
68
+ | HR 定义的职位 | 按 `capabilities.shell` | 按 `capabilities.write`(无 write 时由守卫摘掉 write/edit) |
69
+
70
+ **没有目录白名单**:工作区可能是 `packages/` 布局,也可能是 `src/`、`ggml/`、`tests/` 的 llama.cpp fork 布局,写席位要能改到改动真正落在的文件。边界只有一条硬规则——读席位不能写;评审看实际 diff。
71
+
72
+ GitFox 通过 `web_fetch` 或 shell `curl`/`wget` 访问会被硬拒绝:议题、MR、提交、分支只能走 `infinity_devops`,本地仓库只能走 `infinity_git`。拒绝原因指向对应工具,绕道另一个工具也不会生效。
73
+
74
+ 工具面由预设决定,Host 只补一个预设表达不了的东西:Git 词法门禁:
75
+
76
+ - 协调席位(assistant/lead)的预设不挂 `tool-bash`,`tool-fs` 配置 `write: false, edit: false`,所以它们没有"自己揽活"的能力可言。
77
+ - 团队模式禁止通过 `bash` / `pwsh` 直接调用 `git`;所有 Git 读写都走 `infinity_git`。
78
+ - `infinity_git` 的提交/推送使用当前角色的 `infinsys-<role>` 身份;只读角色被拒绝写操作,可写角色可暂存/提交工作区内任意路径,推送项目必须是团队 `configure` 固定的项目。
79
+ - `is-fabric-team` 通过 `infinityDevOps.registerActorResolver()` 把 live 角色注入 `is-devops`;模型不能自行声明 actor。
80
+ - 如果团队 scope 没装上(会话恢复早于插件订阅、Host 行缺失),Host 的 provisioning guard 会拒绝该 Agent 的工具调用并触发补装;补装失败则一律 deny 并写入 `diagnostics`,不会静默变成单人开发。
81
+
82
+ ### 共享状态评审(强门禁)
83
+
84
+ 团队里的共享状态变更不是私有操作,一律进入评审队列:
85
+
86
+ | 事件 | 处理 |
87
+ | --- | --- |
88
+ | `infinity_devops` `merge_merge_request` | 评审通过前**拒绝**;首次尝试自动开启 `merge` 评审(qa + secure) |
89
+ | `infinity_git` `rebase` / `rebase_continue` | 本地执行(可在冲突处停下),完成后立即开启 `rebase` 评审(qa + secure) |
90
+ | `infinity_git` `push`(发布已变基的分支) | 该分支的 `rebase` 评审通过前**拒绝**;没有 rebase 的普通推送不受影响 |
91
+ | `create_merge_request` | 成功后自动开启 `merge_request` 评审(qa + secure);其批准同时授权该 MR 的合并 |
92
+ | `create_issue` | 成功后自动开启 `issue` 评审(lead + qa) |
93
+
94
+ 拒绝信息里直接给出评审 id 与必需席位;通过 `infinity_team` action `reviews` / `review` 处理。拦截点在 Host 的 `tools/pre-execute`(门禁)与 `tools/result`(自动开评审),对非团队 Agent 不生效。
95
+
96
+ 队列必须可读,否则评审 id 无从发现。`reviews` 按**最新在前**分页返回:第一页不带 `cursor`,之后的页把上一页的 `next_cursor` 原样传回;`total` 是账本里的总数,因此“读到最后一页”是机械可判定的。`status` 把 `activeDecision` 与 `problem` 排在历史表之前,且默认不再序列化整本账本——24000 字符的工具输出预算只会截掉点名要的历史,不会截掉当前决策或最新评审。
97
+
98
+ ### 门禁是工作的视图,不是并行的任务(硬门禁)
99
+
100
+ 形式主义不是“形式的问题”。砍掉形式,形式主义照旧;把形式本身当成目标,才是病根。第一版门禁犯过这个错:它要求团队报 `E-####`、调 `verify`,于是又造出一个可以被“完成”的形式。这一版**没有任何要做的动作**。
101
+
102
+ 规则只有一条:门禁读的是已经发生的工作,不是模型写下的话。
103
+
104
+ | 环节 | 门禁读什么 |
105
+ | --- | --- |
106
+ | `intake` | 每条验收标准必须配一条**可执行 check**(同序、一行一条)。没有 check 的标准只是观点。门禁不看命令的措辞,只看它的运行记录:`true` / `echo` / `ls` 不会被拒绝,它们只会永远“从未失败过”,这会被标记出来而不是当成罪证。**一轮没结题时不会拒收**:新需求照样登记,进队列等当前周期结束(`abandon` 或走到 `retro`)再接过舞台,先来的先上——请求不会因为前面卡住而丢掉 |
107
+ | `implementation` | Host 亲眼见过的 `infinity_git commit`。没有提交,阶段不动 |
108
+ | `test` | 每条标准的 check 至少真跑过一次 |
109
+ | `review` / `delivery` | 每条 check **至少真跑过一次**(`problem-unresolved` 只在有标准一次都没跑过时出现)。红、旧都不再挡住阶段:绿不绿照样写进 `problem`,由席位判断;“从未失败过”仍然在 `problem.neverFailed` 里点名 |
110
+ | 关议题 / 合 MR | **只在本轮需求自己的那张议题上、且本轮确实有可执行 check 时才管**,门槛与阶段一致:每条 check 至少跑过一次就放行,红的也算跑过(关闭是记账,不是“修好了”的声明)。一次都没跑过的标准仍然拦住,因为那是没看过,不是没通过。会议、决策、别的项目的议题一律放行;本轮没有需求在跑时也一律放行 |
111
+ | 工作流推进 | 只写本地账本,不再为每个阶段开议题 |
112
+
113
+ 所以“通过门禁”不是一个可以单独做的动作:先让那条 check 跑起来,修好,然后在最终提交上把那条命令跑成绿色。形式(标准 + check)只是实质(行为)的可执行描述——正如形式化验证的目标是行为一致,而不是形式化验证本身。删掉门禁,工作仍然成立;删掉工作,门禁无法通过。
114
+
115
+ 三道墙曾经互相咬合:评审阶段要求“先红过才准绿”(天生为绿的 check 永远过不了)、每个需求最多三个决策(第四个只能弃周期重开)、`vote` 阶段又必须先有一个**未决**决策(决策在别处通过过就再也进不去)。三条合起来,工作做得再完整也只能以 `abandon` 收场。现在三条都撤了,留下的是治理核心:决策只能由四席一致投票产生,实现阶段必须有已通过的决策。
116
+
117
+ 后来又有三道墙同样把人卡死,一并撤掉:**新鲜度**(提交一次就把所有跑绿的验收作废,于是任何一边干活一边提交的团队永远 0 绿,只能弃周期——现在绿的跑就作数,之后的提交只在 `criterionStatus` 里记成 `staleAgainst` 这个事实)、**登记口**(周期的验收没全绿就不许登记新需求,请求直接丢掉——现在进队列)、**结题全绿**(关议题/合 MR/进 review、delivery 都要求全绿——现在只要每条 check 都真跑过一次)。三道墙的共同点是把“仪器测得的结果”当成“工作能不能继续”的开关;放宽后,红与旧都只是写进状态的事实,交给人去判断。
118
+
119
+ 门槛是刻意压低的:门槛太高,团队会为了关掉一张讨论议题而绕过账本。记账动作只要不冒充“完成”(`review` / `delivery` 仍然要求每条 check 跑过一次,绿不绿写在状态里),就不该被未完成的需求挡住。
120
+
121
+ Host 侧实现:`lib/problem-gate.js`(从账本推导状态;`problemStatus` 与 `closureStatus` 读同一批运行,后者只数有 check 的标准)、`lib/problem-host.js`(`tools/result` 记录真实 commit/命令,`tools/pre-execute` 只拦本轮需求自己的议题与合并)。账本只新增 `evidence` 表与需求的 `criteria[].check`;旧账本加载自动补空表,没有 check 的旧标准不会把议题永久锁死。
122
+
123
+ ### 缺件门禁(硬门禁)
124
+
125
+ “没有硬件 / 缺内核 / 没有对端”不是状态,是任务。把缺件当借口停下,和把流程当目标一样,都是形式主义:维护性支持本来就能编译,缺目标设备可以交叉编译或 JIT,缺 IB 对端可以先在 loopback 上把传输测出来。
126
+
127
+ | 规则 | 执行点 |
128
+ | --- | --- |
129
+ | 裸的阻塞声明被拒绝 | 团队消息(`team_send_message`)与 `workflow` 的 `note` 命中阻塞语句,且账本里**还没有任何真实尝试**时返回 `bare-blocker` |
130
+ | 放行不靠标签 | 不需要写 `WORKAROUND`、不需要引用 id。只要 Host 已经观察到一次 commit 或命令运行,同一个阻塞报告就成立;没有尝试之前,标签和 id 都不算数 |
131
+ | 变通由 Host 识别 | 命令里出现 `HSA_OVERRIDE_GFX_VERSION`、`LD_PRELOAD`、`--workaround` 时,该次运行被标记为 workaround,并在状态里露出;它不会被当成修复 |
132
+ | 章程第 12 节固定做法 | 目标设备缺席 → 交叉编译 / PTX JIT + CPU 对照(“compiled and inspected, not executed”是合法一行);维护性内核缺失 → 源码编译并报 cmake/build rc、产物路径、SONAME 与 ldd 证明;没有 IB 对端 → 先起 rxe/siw 在 loopback 上测 perftest;传输四类(硬件 IB / soft-RoCE / siw / TCP)必须分四行 |
133
+
134
+ 实现:`lib/blocker-gate.js`(语句识别 + 账本里的尝试判定)与 `index.js` 的消息 / note 校验。
135
+
136
+ ### 回合结束必须回报(硬约束)
137
+
138
+ 队员的回合绝不能“悄悄结束”:Lead 必须回报 `assistant`,其余 8 个席位必须回报 `lead`(`assistant` 是会话根,它对用户的答复本身就是回报)。这是结构性约束,不是提示词建议:
139
+
140
+ - Host 订阅 DSH 的 `agent/turn-stopping`——回合提交前被 await 的停止边界。若本回合最后一次动作不是发给上级的 `team_send_message`,Host 用 `agent.steer()` 投递一条 `STOP REFUSED` 通知:回合被保持打开,模型必须再走一步把回报发出去。
141
+ - “最后一次动作”是字面意义:`team_send_message` 之后再调用任何工具(哪怕是 `team_list_members`),停止时依然会被拒绝;工具调用通过每个成员 scope 的 observation guard 清掉“已回报”标记。
142
+ - 只有发给上级的消息才算回报:发给下级、同级或别的席位不计数(同级本来就会被 `not-adjacent` 拒绝)。
143
+ - 每个回合最多拒绝 2 次;仍然静默停止时回合放行,并在账本 `diagnostics` 写入 `unreported-stop`(角色、回合号、上级),AI Team 面板可见。这样团队不会卡死,也不会出现无声的漏报。
144
+ - 新的回合(`agent/inbox/claimed`)重置计数,Agent 释放时清理状态;`assistant` 根与普通会话不受影响。
145
+ - 协议段(第 11 节)与每个席位的身份段都写明同一条规则,并明确“等待中 / 被阻塞 / 出错”也必须回报。
146
+
147
+ 残余风险:Git 词法门禁不是 OS 沙箱,有 `bash` 的角色仍可能通过写入脚本再执行来绕过;该行为是策略违规。推送 token 只存在于 Host 子进程环境,不进入 shell,因此绕过门禁也无法读取 Fabric 保存的凭据。
148
+
149
+ ## Agent 工具
150
+
151
+ `infinity_team`(每个成员 scope 安装):
152
+
153
+ | action | 权限 | 说明 |
154
+ | --- | --- | --- |
155
+ | `status` | 成员 | 只读:默认只给核心字段(`activeDecision` 与 `problem` 排在最前);历史表用 `include` 点名追加 |
156
+ | `roster` | 成员 | 只读:本地名册、角色、阶段 |
157
+ | `configure` | assistant / lead | 本地固定 GitFox 项目,不创建 issue |
158
+ | `intake` | assistant(或 lead) | 创建 GitFox 需求并登记;未 configure 则失败 |
159
+ | `abandon` | assistant / lead | 关闭卡死的周期但不伪装完成:必须给 `reason`;需求标记 `abandoned`、议题追加“未完成即关闭”的记录、stage 回到 `intake`,下一轮 `intake` 因此可以开启;需求、证据、决策、会议全部保留 |
160
+ | `meeting` | 成员 | 创建带纪要的 GitFox 会议;retro 只能 docs/hr |
161
+ | `propose_decision` | lead | 四席一致投票的决策。一个需求可以提任意多个决策——没有上限,因为每个新提案都要重新拿四票,投票本身才是那道闸 |
162
+ | `vote` / `decide` | 投票席位 / lead | 只在 `vote` 阶段;不可改票;缺票/reject/abstain 阻断 |
163
+ | `workflow` | 阶段 owner / lead | 逐级推进;门禁读 Host 观察到的工作(commit、check 运行),不创建议题 |
164
+ | `reviews` | 成员 | 共享状态评审队列(合并 / 变基 / MR / issue),最新在前;`per_page`(默认 20,最大 100)+ 不透明 `cursor`,返回 `total` / `next_cursor` |
165
+ | `review` | qa、secure、lead | 必需席位投票;merge 需 qa + secure,issue 需 lead + qa |
166
+ | `define_role` | HR | 定义章程外的职位:`role_id` / `role_name` / `mandate` + `capabilities`(`shell`、`write` 的任意组合);不可定义 assistant/lead/hr,且不获得投票席位 |
167
+ | `retire_role` | HR | 永久关闭一个 HR 定义的职位(无人持有、无在用名册成员);id 不再复用 |
168
+ | `enroll` / `retire` | HR | 本地身份变更,无 GitFox issue;`retire` 同时把该成员的名册行标记为 retired |
169
+ | `spawn_member` | HR | 为已登记身份拉起成员(host 执行,assistant 无此权限);职位由 `define_role` 定义时按该职位的 `capabilities` 选预设 |
170
+
171
+ 模型路由不是工具动作:`infinity_team` 没有 `set_model`,任何成员(HR 包括在内)传入 `provider` / `model` / `reasoning_effort` 都会被拒绝。模型与思考强度只由人类在 AI Team 面板选择。
172
+
173
+ 另有团队工具:`team_list_members`、`team_send_message`、`team_interrupt`。
174
+
175
+ 团队的 Git 操作统一使用全局 `infinity_git`(`status`、`diff`、`log`、`stage`、`unstage`、`commit`、`fetch`、`rebase`、`rebase_continue`、`rebase_abort`、`push`);`infinity_team` 创建的追踪议题自动带 `infinsys-<role>` bot 身份。
176
+
177
+ ## Web 协作界面
178
+
179
+ - **AI Team 主工作台**:左侧成员/阶段 rail,中间 Activity / Team / Decisions / Meetings / Workflow 协作流,右侧聚焦成员、最新决策、诊断与原则。
180
+ - **右侧团队侧栏**:会话头部 “AI Team” 按钮打开原生右栏 tab,显示实时名册与最近协作。
181
+ - **设置页卡片**:Infinity 设置页中的 Fabric AI Team 只读摘要。
182
+ - **模型路由与思考强度(人类专属)**:主工作台头部右侧设团队默认模型和它的思考强度;Team 标签页的每个角色卡还有自己的席位模型与思考强度下拉。席位固定优先于团队默认,选中「跟随团队默认」即取消固定。选择写入账本(`model` 与 `roleModels`,每条路由可带 `reasoningEffort`),之后拉起的成员按 `roleModels[role] ?? model ?? Host 默认` 继承。模型目录会带出每个模型可选的思考等级和供应商推荐等级:没有等级的模型不显示该下拉,换模型时自动改用新模型的推荐等级,选「模型默认」即清空存储的等级。当前正在跑的那一步保留它起步时的模型与等级,下一步生效:切换直接写 `agent.model.select()`(DSH 控制器 `selectModel` 最后落到的方法),不经过那个按 `sessionId` 收会话、还会顺带改 Host 新会话默认值的 RPC;写之前先用 `llm.resolveCallConfig()` 校验,模型不支持的等级在碰任何 Agent 之前就报错(`model-switch-rejected`),个别席位不接受选择时按席位报失败(`model-switch-unavailable`),不会只写账本假装成功。这是浏览器唯一拥有的写操作,只能改模型路由;Agent 工具与 HR 都无法改。
183
+ - 其余状态变更仍走 Agent 工具。
184
+ - 右栏与设置页入口用 `ctx.inject` 按需挂载:`is-settings` 或右栏插件缺失时只是少一个入口,不会让 Web 引导停在 `pending (waiting for service: infinitySettings)`。
185
+
186
+ ## 安装
187
+
188
+ 要求 profile 已启用官方 `@deepseek-ai/dsh-experimental-agent-team-profile`(提供 Agent Teams 的基础服务行),并按顺序安装本仓库的四个包。本包 patch 会:
189
+
190
+ - 禁用原生 `tool-agent-team` 与 `ui-agent-team`;
191
+ - 把 `subagent` 的 `maxActiveSubagents` 提高到 16、`maxDepth` 到 3;
192
+ - 注册四个预设 `fabric-team` / `fabric-team-worker` / `fabric-team-readonly` / `fabric-team-docs`,工具面按角色结构性隔离。
193
+
194
+ 顺序:`is-identity → is-settings → is-devops → is-fabric-team`。
195
+
196
+ ## 数据与边界
197
+
198
+ - 账本:`$DSH_HOME/infinity/fabric-team/team-state.json`(完整快照)+ `team-state.json.journal`(增量日志)。每次改动只追加一行补丁并 fsync,不再重写整份文档;日志累计到 256 KiB 或超过 5 分钟时折回快照。日志头记录所属快照的摘要,世代不符的日志会被忽略而不是重复回放。**坏行不再等于"后面的都没了"**:最后一行写坏(崩溃留下的半行)丢弃该行并在下次写入前截断;中间某一行写坏时跳过它、保留它后面的完整补丁,并在下一次写入时把恢复后的状态折回新快照、在 `diagnostics` 里记下跳过了几行(跳过的那一行自身的改动无法恢复,所以可能缺一条消息或一个席位——但只缺它,不是整份名册)。**快照丢了但日志还在**时拒绝启动(`corrupt-state`),不做"从空团队开始":那样会把还在磁盘上的名册直接抹掉。跨进程锁 + 心跳、原子写、损坏可重试都保留。
199
+ - 本地账本是部署级的;每个 fabric-team 会话初始化自己的名册,共享身份与决策。
200
+ - `/infinity/team/state` 只读,本机对端或 Harness 监听地址 + `x-infinity-request: 1`。
201
+ - `/infinity/team/models`(GET)返回按 provider 分组的模型目录,每个模型可带 `reasoning`(可选思考等级 + 推荐等级,来自 `resolveModelInfo()`,解析失败只是缺少该字段);`/infinity/team/model`(POST)是人类的模型路由入口:`{provider, model, reasoningEffort?}` 设团队默认,`{role, provider, model, reasoningEffort?}` 固定单席,`{role, clear: true}` 取消固定。两者都只接受本机对端或 Harness 监听地址 + `x-infinity-request: 1`,且 POST 只能改模型路由,不能改投票、身份或 GitFox 跟踪。
202
+ - 自动建队是异步的:失败会写入 `diagnostics`,不会阻塞会话创建;面板会显示缺失成员。协调席位的 scope 装不上时该 Agent 会 fail-closed(拒绝所有工具调用),避免以单人身份继续干活。
203
+ - **会话日志自愈**:进入团队模式写的是会话日志里的一条 `team/mode` 记录,而 Harness 只接受它认识的类型,别的类型必须带 `ignorable: true`。能写这个标记的 `appendPluginRecord` 属于 DSH 自己的依赖,Fabric 的包引用不到,所以早期日志里的 `team/mode` 是裸写的:DSH 重启后再打开这个会话,整份日志会被拒绝(`contains event type "team/mode" (seq N) unknown to this harness and not marked ignorable`),用户看到的历史像是丢了。Host 因此接管了 `sessionPersistence.open`:读到这类拒绝时,只给本插件自己的事件补上 `ignorable: true`(事件顺序、seq、payload 与其余每一行都不变),原子替换并留下 `.bak-<时间戳>` 备份,然后再读一次;补写失败、或替换后仍然读不出来,就回滚备份并原样抛出最初的错误。同一会话的并发读取共用一次修复,正在跑的会话不碰;修好的日志永久带标记,一个日志只会被修一次。以后再有本插件自己的事件类型需要跨版本存活,加进 `HEALABLE_EVENT_TYPES`(`lib/session-log-heal.js`)即可。
204
+ - **会话标题自愈**:侧边栏的名字不读日志,只读 `~/.dsh/storages/session_projcache/sessions/<会话>.json` 里那份投影检查点;列表这一路即使读缓存失败也只会「这一行不带列」,界面于是显示「未命名」。异常重启没走干净的 dispose,检查点就可能落后于日志或整份被判无效,名字就从侧边栏消失——点开会话时冷读会重建缓存,名字又回来了,这正是用户看到的现象。Host 因此做两件事:启动后在后台过一遍存盘的会话(只读不在运行的那种),缓存里没有字符串标题的就读一次它的日志,用缓存自己的 `coldSnapshot(…)` 把检查点重建并写回,写回是它自己的 fail-soft 路径;同时把握到的标题记在 `~/.dsh/infinity/session-titles.json`,并给 `sessionProjectionCache.cachedSnapshot` 包一层,只补空着的标题,让启动后的第一次列表就带上名字。日志里本来就没有标题的会话(还没生成过)按日志的 revision(dev/ino/size/mtime 指纹)记住,下次启动不再重读;一轮修复有时间预算(默认 2 秒),超时就停下留一行日志,剩下的下次启动接着做;单个会话读不出来不影响其它会话。
205
+
206
+ ## 测试
207
+
208
+ ```bash
209
+ pnpm run check
210
+ pnpm test
211
+ pnpm run verify
212
+ ```
213
+
214
+ ## License
215
+
216
+ MIT