@joekytc/dsh-swarm 0.2.0 → 0.3.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 (103) hide show
  1. package/README.md +176 -139
  2. package/README.zh-CN.md +172 -125
  3. package/client/ConfigSection.tsx +96 -5
  4. package/client/ConfigSelect.tsx +3 -1
  5. package/client/TaskDrawer.tsx +2 -0
  6. package/client/config-store.ts +48 -7
  7. package/client/kanban.css +69 -0
  8. package/client/timeline-model.ts +2 -0
  9. package/lib/client.js +172 -14
  10. package/lib/config.d.ts +18 -1
  11. package/lib/config.js +14 -0
  12. package/lib/dispatcher/agent-runner.d.ts +13 -5
  13. package/lib/dispatcher/agent-runner.js +78 -38
  14. package/lib/dispatcher/chain-auditor.d.ts +4 -4
  15. package/lib/dispatcher/chain-auditor.js +10 -8
  16. package/lib/dispatcher/dispatcher.d.ts +18 -18
  17. package/lib/dispatcher/dispatcher.js +59 -43
  18. package/lib/dispatcher/event-waker.d.ts +1 -1
  19. package/lib/dispatcher/event-waker.js +2 -2
  20. package/lib/dispatcher/git-credentials.d.ts +2 -2
  21. package/lib/dispatcher/git-credentials.js +2 -2
  22. package/lib/dispatcher/model-candidates.d.ts +1 -1
  23. package/lib/dispatcher/model-candidates.js +1 -1
  24. package/lib/dispatcher/session-events.d.ts +1 -1
  25. package/lib/dispatcher/session-events.js +1 -1
  26. package/lib/dispatcher/target-repo.d.ts +2 -2
  27. package/lib/dispatcher/target-repo.js +3 -3
  28. package/lib/dispatcher/v-orchestrator.d.ts +8 -8
  29. package/lib/dispatcher/v-orchestrator.js +41 -41
  30. package/lib/dispatcher/watchdog.d.ts +1 -1
  31. package/lib/dispatcher/watchdog.js +2 -2
  32. package/lib/domain/config-override.d.ts +14 -0
  33. package/lib/domain/config-override.js +38 -1
  34. package/lib/domain/delivery-contract.d.ts +2 -2
  35. package/lib/domain/delivery-contract.js +5 -5
  36. package/lib/domain/delivery-evidence.d.ts +1 -1
  37. package/lib/domain/delivery-evidence.js +1 -1
  38. package/lib/domain/event-store.js +1 -1
  39. package/lib/domain/im-message.d.ts +15 -0
  40. package/lib/domain/im-message.js +149 -0
  41. package/lib/domain/kanban-service.d.ts +13 -10
  42. package/lib/domain/kanban-service.js +36 -28
  43. package/lib/domain/memory.js +1 -1
  44. package/lib/domain/ocr-review.d.ts +47 -0
  45. package/lib/domain/ocr-review.js +105 -0
  46. package/lib/domain/permissions.js +5 -5
  47. package/lib/domain/projection.js +6 -6
  48. package/lib/domain/state-machine.js +1 -1
  49. package/lib/domain/task-parents.js +1 -1
  50. package/lib/domain/types.d.ts +4 -4
  51. package/lib/index.js +14 -9
  52. package/lib/roles/clean-fs-tools.js +3 -4
  53. package/lib/roles/preset-installer.js +3 -3
  54. package/lib/roles/skill-installer.js +1 -1
  55. package/lib/roles/toolsets.d.ts +60 -16
  56. package/lib/roles/toolsets.js +265 -47
  57. package/lib/roles/wiki-worker.js +3 -3
  58. package/lib/routes/kanban-http.d.ts +25 -2
  59. package/lib/routes/kanban-http.js +171 -11
  60. package/lib/routes/kanban-sse.d.ts +1 -1
  61. package/lib/routes/kanban-sse.js +1 -1
  62. package/lib/routes/planning-driver.d.ts +4 -2
  63. package/lib/routes/planning-driver.js +7 -3
  64. package/lib/routes/prefix-router.d.ts +3 -3
  65. package/lib/routes/prefix-router.js +3 -1
  66. package/lib/services/config-provider.d.ts +0 -4
  67. package/lib/services/config-provider.js +7 -9
  68. package/lib/services/im-delivery.d.ts +94 -0
  69. package/lib/services/im-delivery.js +263 -0
  70. package/lib/services/kanban-provider.d.ts +1 -1
  71. package/lib/services/kanban-provider.js +2 -2
  72. package/lib/services/ocr-cli.d.ts +35 -0
  73. package/lib/services/ocr-cli.js +128 -0
  74. package/lib/tools/kanban-tools.d.ts +2 -2
  75. package/lib/tools/kanban-tools.js +1 -1
  76. package/lib/tools/main-session-tools.d.ts +6 -2
  77. package/lib/tools/main-session-tools.js +58 -20
  78. package/lib/tools/ocr-review-tools.d.ts +11 -0
  79. package/lib/tools/ocr-review-tools.js +63 -0
  80. package/lib/tools/planning-tools.d.ts +5 -2
  81. package/lib/tools/planning-tools.js +11 -9
  82. package/lib/tools/wiki-tools.js +2 -2
  83. package/lib/wiki/kb-linkage.d.ts +1 -1
  84. package/lib/wiki/local-kb-client.js +3 -3
  85. package/lib/wiki/local-kb.d.ts +1 -1
  86. package/lib/wiki/local-kb.js +2 -2
  87. package/lib/wiki/page-path.d.ts +1 -1
  88. package/lib/wiki/page-path.js +6 -3
  89. package/lib/wiki/wiki-vault-client.d.ts +1 -1
  90. package/lib/wiki/wiki-vault-client.js +1 -1
  91. package/package.json +1 -1
  92. package/personas/kanban-d/agent.cordis.yml +5 -5
  93. package/personas/kanban-dt/agent.cordis.yml +23 -5
  94. package/personas/kanban-p/agent.cordis.yml +3 -3
  95. package/personas/kanban-pt/agent.cordis.yml +1 -1
  96. package/personas/kanban-v/agent.cordis.yml +3 -3
  97. package/personas/kanban-w/agent.cordis.yml +5 -5
  98. package/personas/persona-d.md +1 -1
  99. package/personas/persona-dt.md +12 -4
  100. package/personas/persona-p.md +1 -1
  101. package/personas/persona-v.md +2 -2
  102. package/personas/swarm/agent.cordis.yml +43 -0
  103. package/personas/swarm/preset.yml +5 -0
@@ -27,7 +27,7 @@ export function applyTo(state, ev) {
27
27
  next.chains = new Map(state.chains).set(ev.chainId, { ...c, rootTaskId: String(ev.payload['rootTaskId'] ?? '') });
28
28
  break;
29
29
  }
30
- // D23:audit 事件不改 Chain 状态,只写验收核对视图(auditWarnings)
30
+ // audit 事件不改 Chain 状态,只写验收核对视图(auditWarnings)
31
31
  case 'chain/audit-warning': {
32
32
  const c = state.chains.get(ev.chainId);
33
33
  if (!c)
@@ -59,7 +59,7 @@ export function applyTo(state, ev) {
59
59
  });
60
60
  break;
61
61
  }
62
- // T7:链标题改名(非状态转换,只更新 title;快照重放可见)
62
+ // 链标题改名(非状态转换,只更新 title;快照重放可见)
63
63
  case 'chain/title-updated': {
64
64
  const c = state.chains.get(ev.chainId);
65
65
  if (!c)
@@ -67,7 +67,7 @@ export function applyTo(state, ev) {
67
67
  next.chains = new Map(state.chains).set(ev.chainId, { ...c, title: String(ev.payload['to'] ?? '') });
68
68
  break;
69
69
  }
70
- // T7:任务标题改名(非状态转换,只更新 title)
70
+ // 任务标题改名(非状态转换,只更新 title)
71
71
  case 'task/renamed': {
72
72
  if (!ev.taskId)
73
73
  throw new Error('projection: event without taskId');
@@ -81,7 +81,7 @@ export function applyTo(state, ev) {
81
81
  const p = ev.payload;
82
82
  if (!p.id || !p.assignee)
83
83
  throw new Error('projection: malformed task/created');
84
- // 归一化缺省字段,使最小 payload 的事件日志可回放(P0-3 回放为权威)
84
+ // 归一化缺省字段,使最小 payload 的事件日志可回放(回放为权威)
85
85
  const normalized = {
86
86
  ...p,
87
87
  status: p.status ?? 'todo',
@@ -113,11 +113,11 @@ export function applyTo(state, ev) {
113
113
  const updated = { ...t, status: transitionTask(t.status, ev.kind) };
114
114
  if (ev.kind === 'task/heartbeat')
115
115
  updated.heartbeats = [...t.heartbeats, ev.at];
116
- // RC4:infra 失败不计入 attempts(瞬时基础设施错误不烧重试预算);任务质量失败正常 +1
116
+ // infra 失败不计入 attempts(瞬时基础设施错误不烧重试预算);任务质量失败正常 +1
117
117
  if (ev.kind === 'task/failed' && !ev.payload['infra'])
118
118
  updated.attempts = t.attempts + 1;
119
119
  if (ev.kind === 'task/unblocked')
120
- updated.attempts = 0; // RC4:人工解除阻塞 → 重试预算重置
120
+ updated.attempts = 0; // 人工解除阻塞 → 重试预算重置
121
121
  if (ev.kind === 'task/completed') {
122
122
  const p = ev.payload;
123
123
  next.handoffs = new Map(state.handoffs).set(ev.taskId, p);
@@ -15,7 +15,7 @@ const CHAIN_TRANSITIONS = {
15
15
  completed: {},
16
16
  aborted: {},
17
17
  };
18
- // D23:'chain/audit-warning' / 'chain/audit-confirmed' 不是状态转换——
18
+ // 'chain/audit-warning' / 'chain/audit-confirmed' 不是状态转换——
19
19
  // Chain 保持 completed,只向投影写入验收核对视图(auditWarnings)。
20
20
  // projection.ts 对这两类事件专门处理、不调用 transitionChain,故此处不注册。
21
21
  const SPEC_TRANSITIONS = {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 每阶段任务卡的语义输入来源(R20 依赖):第 1 项为主来源。
2
+ * 每阶段任务卡的语义输入来源:第 1 项为主来源。
3
3
  * key = `${assignee}:${mode}`;w:kb 特判(w2→P、w3→D)见 semanticParentDeps。
4
4
  * 未列出的 mode(如旧兼容 align)不设语义 parents(返回空)。
5
5
  */
@@ -40,7 +40,7 @@ export interface ReviewEvidence {
40
40
  kbUrl: string;
41
41
  };
42
42
  }
43
- export type EventKind = 'chain/created' | 'chain/executing' | 'chain/completed' | 'chain/aborted' | 'chain/blocked' | 'chain/root-task-set' | 'chain/audit-warning' | 'chain/audit-confirmed' | 'chain/title-updated' | 'spec-card/created' | 'spec-card/edited' | 'spec-card/approved' | 'task/created' | 'task/claimed' | 'task/heartbeat' | 'task/commented' | 'task/completed' | 'task/blocked' | 'task/unblocked' | 'task/archived' | 'task/gate-passed' | 'task/gate-failed' | 'task/failed' | 'task/renamed' | 'review/passed' | 'review/failed' | 'review/gave-up';
43
+ export type EventKind = 'chain/created' | 'chain/executing' | 'chain/completed' | 'chain/aborted' | 'chain/blocked' | 'chain/root-task-set' | 'chain/audit-warning' | 'chain/audit-confirmed' | 'chain/title-updated' | 'chain/im-delivery-failed' | 'spec-card/created' | 'spec-card/edited' | 'spec-card/approved' | 'task/created' | 'task/claimed' | 'task/heartbeat' | 'task/commented' | 'task/completed' | 'task/blocked' | 'task/unblocked' | 'task/archived' | 'task/gate-passed' | 'task/gate-failed' | 'task/failed' | 'task/renamed' | 'review/passed' | 'review/failed' | 'review/gave-up';
44
44
  export interface SpecCardSections {
45
45
  problem: string;
46
46
  solution: string;
@@ -61,7 +61,7 @@ export interface Chain {
61
61
  rootTaskId: string | null;
62
62
  specCardId: string | null;
63
63
  ownerSessionId: string;
64
- /** 发起 /plan: 的主 agent 会话工作目录(Q5:角色会话统一创建于此,便于管理 profile 会话)。null=未捕获(GUI 建链),回退 kanban 存储。 */
64
+ /** 发起 /plan: 的主 agent 会话工作目录(角色会话统一创建于此,便于管理 profile 会话)。null=未捕获(GUI 建链),回退 kanban 存储。 */
65
65
  workspaceDir: string | null;
66
66
  createdAt: number;
67
67
  }
@@ -105,14 +105,14 @@ export interface Handoff {
105
105
  metadata: Record<string, unknown>;
106
106
  completedAt: number;
107
107
  }
108
- /** D23 链完成验收核对证据:主会话越权写工作区产物的单个线索。 */
108
+ /** 链完成验收核对证据:主会话越权写工作区产物的单个线索。 */
109
109
  export interface AuditEvidence {
110
110
  source: 'main-session-scan' | 'artifact-reconciliation' | string;
111
111
  detail: string;
112
112
  paths: string[];
113
113
  at?: number;
114
114
  }
115
- /** D23 链完成验收核对投影视图:warning 事件 + (可选)用户确认。 */
115
+ /** 链完成验收核对投影视图:warning 事件 + (可选)用户确认。 */
116
116
  export interface ChainAudit {
117
117
  evidence: AuditEvidence[];
118
118
  warnedAt: number;
package/lib/index.js CHANGED
@@ -7,9 +7,10 @@ import { installLlWikiSkill } from './roles/skill-installer.js';
7
7
  import { registerKanbanHttp } from './routes/kanban-http.js';
8
8
  import { startDispatcher } from './dispatcher/dispatcher.js';
9
9
  import { ConfigProvider } from './services/config-provider.js';
10
+ import { wireImDelivery } from './services/im-delivery.js';
10
11
  export const name = 'dsh-swarm';
11
12
  export { Config };
12
- // P1-9:无 inject——KanbanProvider 只用文件系统(FileEventStore),不依赖 ctx.storage 等任何服务。
13
+ // inject——KanbanProvider 只用文件系统(FileEventStore),不依赖 ctx.storage 等任何服务。
13
14
  /** 等待多个服务同时可用后再接线(如 tools 先于 kanban 出现时,单服务轮询会过早执行)。 */
14
15
  function wireAllAvailable(ctx, names, fn, timeoutMs = 60000) {
15
16
  const ready = () => names.every((name) => Boolean(ctx.get(name)));
@@ -29,26 +30,30 @@ function wireAllAvailable(ctx, names, fn, timeoutMs = 60000) {
29
30
  }
30
31
  export function apply(ctx, config) {
31
32
  // cordis 4:Service 构造即注册(super(ctx,'kanban') 调 ctx.reflect.provide),无需手动 provide。
32
- // Task 8:ConfigProvider 在 apply 中创建一次(override 存放于 storageDir,$DSH_HOME 占位符在此解析),
33
+ // ConfigProvider 在 apply 中创建一次(override 存放于 storageDir,$DSH_HOME 占位符在此解析),
33
34
  // 全链路(KanbanProvider/HTTP 桥/主会话工具/调度器)共享同一实例实现配置面板热生效。
34
35
  const storageDir = config.storageDir.replace('$DSH_HOME', process.env.DSH_HOME ?? homedir());
35
36
  const configProvider = new ConfigProvider(ctx, config, storageDir);
36
- // Task 6:KanbanProvider 持 ConfigProvider 引用,kb_url base 经 getter 热读取(配置面板改后无需重建)。
37
+ // KanbanProvider 持 ConfigProvider 引用,kb_url base 经 getter 热读取(配置面板改后无需重建)。
37
38
  const provider = new KanbanProvider(ctx, config, configProvider);
38
- // D22:把包内角色裁剪 preset 组合安装到 $DSH_HOME/.agent-presets/(真实 API 下唯一可发现的自定义根)。
39
+ // 把包内角色裁剪 preset 组合安装到 $DSH_HOME/.agent-presets/(真实 API 下唯一可发现的自定义根)。
39
40
  const installed = installRolePresets();
40
- installLlWikiSkill(); // 尽力而为:失败仅告警不阻断(设计 §7)
41
+ installLlWikiSkill(); // 尽力而为:失败仅告警不阻断
41
42
  console.info('[dsh-swarm] role presets installed: ' + (installed.length ? installed.join(',') : 'none'));
43
+ // IM 投递(企微,2026-09-07 评审决议):W3 收尾/链阻塞时经 dsh-im 投群。
44
+ // imDelivery.enabled=false(默认)时事件处理器零开销早退;dshIm 服务缺失显式降级留痕。
45
+ const disposeImDelivery = wireImDelivery(ctx, provider.service, configProvider);
46
+ ctx.on('dispose', disposeImDelivery);
42
47
  // LLM 运行时延迟取用:registerKanbanHttp 内部消费(llm-catalog 枚举),接线时 llm 服务已就绪。
43
48
  const llm = () => ctx.get('llm');
44
49
  // 可选服务接线均延迟到服务可用后:
45
50
  // - Web GUI 数据桥(GET /kanban/board + POST /kanban/action + 配置读写 + llm-catalog,仅 webServer 存在时挂载)
46
51
  // llm 需在 webServer 就绪且 llm 就绪后取,wireAllAvailable 双服务轮询比单服务更稳。
47
52
  wireAllAvailable(ctx, ['webServer', 'llm'], () => registerKanbanHttp(ctx, provider, configProvider, llm(), config));
48
- // - P1-3 主会话工具面(spec_card_view/edit/approve + kanban 只读子集 + 前缀路由工具)
49
- // Task 8:收 ConfigProvider,wiki 客户端与全部配置读点经 getEffective() 调用时热读取。
53
+ // - 主会话工具面(spec_card_view/edit/approve + kanban 只读子集 + 前缀路由工具)
54
+ // ConfigProvider,wiki 客户端与全部配置读点经 getEffective() 调用时热读取。
50
55
  wireAllAvailable(ctx, ['tools', 'kanban'], () => registerMainSessionTools(ctx, configProvider));
51
- // - 调度层:事件唤醒 V(R20)+ 每任务 agent runner + 看门狗(仅 agents 可用时启动)
52
- // Task 7:startDispatcher 收 ConfigProvider(模型链/wiki 热生效)。
56
+ // - 调度层:事件唤醒 V + 每任务 agent runner + 看门狗(仅 agents 可用时启动)
57
+ // startDispatcher 收 ConfigProvider(模型链/wiki 热生效)。
53
58
  wireAllAvailable(ctx, ['agents', 'kanban'], () => startDispatcher(ctx, configProvider));
54
59
  }
@@ -1,6 +1,5 @@
1
1
  // src/roles/clean-fs-tools.ts
2
- // sandbox-menu-align Task 1(计划 /tmp/sandbox-menu-align-plan.md):danger-full-access 会话
3
- // (P、D(execute))的 write/edit/bash 工具菜单去毒。
2
+ // danger-full-access 会话(P、D(execute))的 write/edit/bash 工具菜单去毒。
4
3
  //
5
4
  // 为什么:宿主 dsh-tool-fs / dsh-tool-bash 在组合层把可选参数 sandbox_permissions / justification
6
5
  // 合入 write/edit/bash schema(dsh-tool-fs lib/index.js:617/771 schemaFields()、dsh-tool-bash
@@ -10,13 +9,13 @@
10
9
  // (2026-09-02 P 会话 30 连败事故根因)。plan-guard 的带参拦截保留为兜底网(toolsets.ts 不动),
11
10
  // 本文件是根治面:agent scope shadow 注册「无带毒参数的干净版」盖住宿主版本 + execute 剥参转发。
12
11
  //
13
- // 宿主探明结论(详见 .superpowers/sdd/sandbox-menu-align-plan/task-1-report.md):
12
+ // 宿主探明结论:
14
13
  // - 遮蔽语义(dsh-tools lib/index.js view/get + dsh-scope ScopedLayers):scope 自身层注册在
15
14
  // inherited(global + preset standing 祖先层)之后覆盖 visible map → agent scope 同名注册
16
15
  // 真实盖住宿主版本;「重复注册 fail」仅指同一层内(NamedEntries.insert),跨层 shadow 合法。
17
16
  // agentPresets.mount 是 bindScopeParent(agent scope 挂为 preset standing scope 的 child),
18
17
  // preset 里 tool-fs/tool-bash 的注册落在 standing 祖先层而非 agent own 层 → 本模块经
19
- // agentCtx.tools.register 落 own 层,无同层冲突(Task 2 的 kanban_comment 遮蔽已实证该通道)。
18
+ // agentCtx.tools.register 落 own 层,无同层冲突(kanban_comment 遮蔽已实证该通道)。
20
19
  // - 转发通道:agentCtx.tools.get(name, agent) 返回该 scope 可见定义(shadow 注册前即宿主原定义,
21
20
  // kanban-p/d 的 fs/bash 在 preset standing 祖先层,全局视角 get(name) 反而可能取不到);
22
21
  // 直接调用 hostDef.execute(cleanArgs, exec) 与注册表 dispatchToolBody 的调用形态逐字一致
@@ -4,9 +4,9 @@ import { homedir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  /**
7
- * D22 角色裁剪 preset 的运行时安装(2026-08-15 取舍记录):
7
+ * 角色裁剪 preset 的运行时安装(2026-08-15 取舍记录):
8
8
  * - 组合文件随包分发(personas/kanban-{v,p,w,d}/agent.cordis.yml,随 npm 包分发)。
9
- * kanban-v 于 2026-08-17 补充(R21 对齐):V=butler·orchestrator 零执行能力,组合仅 persona+instructions。
9
+ * kanban-v 于 2026-08-17 补充:V=butler·orchestrator 零执行能力,组合仅 persona+instructions。
10
10
  * - 官方 dsh-agent-presets 的 roots 在 web 启动的 composeProfile 中被强制覆盖为
11
11
  * 官方 shipped root 单一路径(profile-boot 内 SHIPPED_PRESET_ROOT 硬编码),
12
12
  * 因此 cordis.patch.yml 追加 roots 指向包内目录在真实 API 下无效;
@@ -15,7 +15,7 @@ import { fileURLToPath } from 'node:url';
15
15
  * agentPresets.mount(agentCtx, 'kanban-v'|'kanban-p'|'kanban-w'|'kanban-d') 即可解析。
16
16
  * - 幂等:重复安装覆盖同名文件;缺组合文件(未随包)则跳过并让 runner 降级日志。
17
17
  */
18
- const PRESET_IDS = ['kanban-v', 'kanban-p', 'kanban-w', 'kanban-d', 'kanban-pt', 'kanban-dt'];
18
+ const PRESET_IDS = ['kanban-v', 'kanban-p', 'kanban-w', 'kanban-d', 'kanban-pt', 'kanban-dt', 'swarm'];
19
19
  /** 包内组合目录(随包分发)。src/roles/ 与 lib/roles/ 深度一致,均经 ../../ 回到包根。 */
20
20
  export function packagePresetsDir() {
21
21
  return fileURLToPath(new URL('../../personas/', import.meta.url));
@@ -1,5 +1,5 @@
1
1
  // src/roles/skill-installer.ts
2
- // llm-wiki skill 自动安装(设计 §7 / D6):DSH 的 dsh-tool-skill 只从 ~/.agents/skills/ 发现 skill,
2
+ // llm-wiki skill 自动安装:DSH 的 dsh-tool-skill 只从 ~/.agents/skills/ 发现 skill,
3
3
  // 不扫插件包内 skills 目录(同 agent-presets 只认 $DSH_HOME/.agent-presets 的先例)。
4
4
  // 完整复制(含 deps/assets/tests 全部子目录);幂等(SKILL.md 已存在跳过);尽力而为(失败仅告警)。
5
5
  import { cpSync, existsSync, mkdirSync } from 'node:fs';
@@ -1,19 +1,13 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import { KanbanService } from '../domain/kanban-service.js';
3
- import type { WikiVaultClient } from '../wiki/wiki-vault-client.js';
3
+ import { WikiVaultClient } from '../wiki/wiki-vault-client.js';
4
4
  import type { Role } from '../domain/types.js';
5
+ import type { ConfigProvider } from '../services/config-provider.js';
6
+ import { isRoleComposed } from '../dispatcher/agent-runner.js';
7
+ export { isRoleComposed };
5
8
  /** 判定 wiki 路径是否位于 DT 评审命名空间 projects/<repoSlug>/<chainId>/review/
6
9
  * (repoSlug=[a-z0-9-]+ 通配,chainId 精确匹配;拒绝 ../、绝对路径、跨链、旧格式直挂根)。 */
7
10
  export declare function isReviewNamespacePath(pagePath: string, chainId: string): boolean;
8
- /**
9
- * 评审引擎决策(DT):ocr(open-code-review Delegation 模式)优先;
10
- * 不可用 fallback superpowers code-review;两者都不可用 → review-tool-unavailable(阻塞)。
11
- * 纯函数便于单测;真实可用性探测在 agent-runner 装配(探活 ocr 二进制/失败)。
12
- */
13
- export declare function resolveReviewEngine(available: {
14
- ocr: boolean;
15
- codeReview: boolean;
16
- }): 'ocr' | 'code-review' | 'review-tool-unavailable';
17
11
  /**
18
12
  * DT 写护栏 = PT 只读护栏(源码/git/写标记 bash 拒绝)+ wiki_write 仅 review namespace 收窄。
19
13
  * repoRoot 为 D 目标仓库;chainId 用于 wiki 评审命名空间校验。
@@ -26,28 +20,28 @@ export declare function buildReadOnlyWriteGuard(_repoRoot: string): (execution:
26
20
  name?: string;
27
21
  arguments?: unknown;
28
22
  }) => string | undefined;
29
- /** 本地模式 KB 写护栏(D7/D10):全名只读拦截(base)之上,仅对「实际写目标全部为
23
+ /** 本地模式 KB 写护栏:全名只读拦截(base)之上,仅对「实际写目标全部为
30
24
  * 库根内绝对路径」的写操作豁免。目标提取不到 / 相对路径 / 越界 → 维持 base 拒绝(fail-closed)。
31
25
  * 仅 local 模式对 W/DT 装配;remote 模式仍用 buildReadOnlyWriteGuard(库根写也被拒)。
32
- * 审查修订(C2):extractWriteTargets 保持既有双参签名(cmd, redirectRe),按入口分流
26
+ * 审查修订:extractWriteTargets 保持既有双参签名(cmd, redirectRe),按入口分流
33
27
  * 传 BASH_REDIRECT_TARGET_RE / CODE_REDIRECT_TARGET_RE(与 buildPlanWriteGuard 同款)——
34
28
  * 单参调用会在首个写意图命令上 TypeError。 */
35
29
  export declare function buildKbWriteGuard(kbRoot: string): (execution: {
36
30
  name?: string;
37
31
  arguments?: unknown;
38
32
  }) => string | undefined;
39
- /** P 专用写护栏(Q3):读全放行;git mutation 一律拒绝;写仅允许目标仓库 openspec/changes 目录。
33
+ /** P 专用写护栏:读全放行;git mutation 一律拒绝;写仅允许目标仓库 openspec/changes 目录。
40
34
  * 直接 fs 写工具 → 路径经 resolve 归一化后须落在 <workspaceRoot>/openspec/changes/ 之下(相邻段对判定);
41
35
  * bash/run_code 写标记命令 → 命令文本须含 `openspec/changes` 子串,且提取出的实际写目标(重定向
42
- * 目标 / writeFileSync 实参)逐条经 resolve+isPlanPath 校验(I1:杀 openspec/changes/../.. 穿越写源码)。
36
+ * 目标 / writeFileSync 实参)逐条经 resolve+isPlanPath 校验(杀 openspec/changes/../.. 穿越写源码)。
43
37
  * 源码/src/lib/tests 等写不入(不含该子串)——"禁止改动源码"为工具级硬约束,非 prompt 软约束。
44
38
  * execution 以 dsh-tools 形态 { name, arguments } 传入(与 buildReadOnlyWriteGuard 一致)。 */
45
39
  export declare function buildPlanWriteGuard(workspaceRoot: string): (execution: {
46
40
  name?: string;
47
41
  arguments?: unknown;
48
42
  }) => string | undefined;
49
- /** 按角色在 agent scope 注册工具面(P1-3 统一注册策略):
50
- * 所有 kanban 工具从 T9 工厂选取 + getCaller 闭包(actor=role、boundTaskId=taskId)。
43
+ /** 按角色在 agent scope 注册工具面(统一注册策略):
44
+ * 所有 kanban 工具从工具工厂选取 + getCaller 闭包(actor=role、boundTaskId=taskId)。
51
45
  * can() 权限兜底仍保留在工具 execute 内(纵深防御第二道)。 */
52
46
  export declare function installRoleTools(agentCtx: Context, role: Role, deps: {
53
47
  kanban: KanbanService;
@@ -72,3 +66,53 @@ export declare function buildSubagentTreeGuard(deps?: SubagentGuardDeps): (execu
72
66
  arguments?: unknown;
73
67
  agent?: unknown;
74
68
  }) => string | undefined;
69
+ /** 蜂群模式主会话硬闸:全局 guard,按 header.agentPreset==='swarm'
70
+ * 精准判定(先例 buildSubagentTreeGuard)。swarm 会话 = 扩权参数教学拦截(宿主 bash/write/edit
71
+ * schema 广播 sandbox_permissions/justification,模型带参重试会触发天花板会话 approveEscalation
72
+ * 死循环——独立 DT 同款拦截)+ git 反选(与独立 DT 共用 GIT_MUTATION_VERBS/dualVerbGitDenyReason,
73
+ * buildPlanWriteGuard 同款分段提取判定,跳过 git 全局选项、提取不到动词 fail-closed,先行判定——
74
+ * git 变更动词多数同时命中只读基座的写标记,须以 swarm-guard 文案优先返回)+ 只读基座
75
+ * (buildReadOnlyWriteGuard:直接写工具全名拦截 + bash/run_code 写标记)。其余会话恒放行
76
+ * (角色会话自有 agent scope 护栏兜底,双保险不叠加)。 */
77
+ export declare function buildSwarmSessionGuard(): (execution: {
78
+ name?: string;
79
+ arguments?: unknown;
80
+ agent?: unknown;
81
+ }) => string | undefined;
82
+ /** 独立评审(standalone DT)全局硬闸。独立会话判定(同时成立):
83
+ * header.agentPreset === 'kanban-dt'、未经角色组合标记(!isRoleComposed)、parentSession
84
+ * 非 kbn- 前缀(与 buildSubagentTreeGuard 判据对齐:有 kbn- parentSession 的是链上系
85
+ * DT 子代理,交由该 guard 管——其 incarnation 可能无组合标记、宿主 id 也可能非 kbn-
86
+ * 前缀,漏判会把链评审写入误当独立模式拒掉)、且(能从 execution.agent 取到 session
87
+ * id 时)id 非 kbn- 前缀。不满足 → 不走独立规则。
88
+ * 独立模式规则(按序):
89
+ * 1. kanban 写工具(complete/block/comment/heartbeat/create)→ 拒(独立评审不挂任务链);
90
+ * 2. bash/run_code/write/edit → 扩权参数教学拦截(sandbox_permissions/justification 任一
91
+ * 出现即拒并教模型去参——只读会话无扩权场景,宿主沙箱校验报错文案晦涩);bash/run_code
92
+ * 再过 git 反选(swarm-guard 同款分段提取:&&/||/;/|/换行 分段、跳过全局选项、提取不到
93
+ * 动词 fail-closed 拒):仅禁变更动词与双态动词的变更子形态(见 GIT_STANDALONE_DENY_VERBS
94
+ * /standaloneGitDenyReason),查询、clone/fetch、裸 checkout/switch 放行;非 git 段不因
95
+ * git 规则拒绝;最后挂只读基座 buildReadOnlyWriteGuard(写动词/重定向照拒;clone/fetch
96
+ * 落盘是 git 自身行为,BASH_WRITE_RE 无此二动词天然放行,无需特判);
97
+ * 3. wiki_write(独立模式)→ 仅放行 projects/<repo>/reviews/<主题>-<日期>/ 命名空间;
98
+ * 4. wiki_write(非独立会话)→ 链上组合会话(isRoleComposed)放行(自有 agent-scope
99
+ * guard 管,链命名空间写入绝不被本 guard 误拦);其余(main/swarm/未知 GUI)拒绝
100
+ * ——评审工具全局注册后 wiki_write 收紧到评审会话;
101
+ * 5. 其余工具(read/glob/grep/ocr_review/wiki_read/wiki_search 等)→ 放行。 */
102
+ export declare function buildStandaloneDtGuard(): (execution: {
103
+ name?: string;
104
+ arguments?: unknown;
105
+ agent?: unknown;
106
+ }) => string | undefined;
107
+ /** 独立评审工具全局注册:ocr_review + wiki 三原语(wiki_read/wiki_search/wiki_write)。
108
+ * 独立评审不经 agent-runner 角色组合,角色工具面不存在,故在插件全局 ctx 注册;
109
+ * caller 固定 actor='dt'(can('wiki-write','dt')=true),wiki_write 的会话级收紧由
110
+ * buildStandaloneDtGuard 完成(链上组合会话放行,main/swarm/未知 GUI 拒绝)。
111
+ * wiki 客户端按 kbMode 构造(与 registerMainSessionTools 同源:remote 优先 ctx.get('wiki')
112
+ * 注入(测试 mock),生产热读取 getEffective().wikiVault;local 走 LocalWikiClient)。
113
+ * local 模式不注册 wiki_write:LocalWikiClient.abs 只接受 wiki/** 形态,而 wiki_write
114
+ * 工具边界只放行 projects/... 命名空间——双锁死(任何路径都写不进)。与链上 DT local
115
+ * 行为一致(走 skill/fs 工具而非 wiki 原语);wiki_read/wiki_search 不受写路径白名单
116
+ * 约束,照常注册。
117
+ * registry 缺失(测试裸 Context)→ return,与 registerMainSessionTools 同款防御。 */
118
+ export declare function registerStandaloneReviewerTools(ctx: Context, configProvider: ConfigProvider): void;