@nelsonlongxiang/dsh-open-a2a-net 0.7.0 → 0.8.1-preview.1

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -94
  3. package/README_zh.md +64 -66
  4. package/THIRD_PARTY_NOTICES.md +12 -0
  5. package/assets/nexusDist/assets/index-BuidhKVr.css +1 -0
  6. package/assets/nexusDist/assets/index-YtYrkDJi.js +9 -0
  7. package/assets/nexusDist/index.html +2 -2
  8. package/cli/a2a.mjs +349 -20
  9. package/cordis.patch.yml +167 -0
  10. package/docs/licenses/dsh-superagent-MIT.txt +21 -0
  11. package/lib/a2a-client.js +12 -4
  12. package/lib/card.js +2 -0
  13. package/lib/cli-deposit.js +75 -4
  14. package/lib/config.js +1 -2
  15. package/lib/direct-forward.js +23 -1
  16. package/lib/faces/admin.js +31 -48
  17. package/lib/faces/wire.js +110 -71
  18. package/lib/followup-admission.js +47 -0
  19. package/lib/followup-delivery.js +19 -2
  20. package/lib/index.js +618 -389
  21. package/lib/node-self.js +1 -1
  22. package/lib/outbound-scope.js +10 -0
  23. package/lib/peer-directory.js +3 -1
  24. package/lib/queue-guard.js +1 -1
  25. package/lib/receipt.js +24 -2
  26. package/lib/skill/SKILL.md +1 -0
  27. package/lib/skill/references/cli.md +193 -21
  28. package/lib/skill/references/collaboration.md +3 -2
  29. package/lib/skill/references/superagent.md +106 -0
  30. package/lib/superagent/a2a-watch.js +587 -0
  31. package/lib/superagent/contract.js +1 -0
  32. package/lib/superagent/host/a2a-source.js +251 -0
  33. package/lib/superagent/host/executor.js +520 -0
  34. package/lib/superagent/host/index.js +1107 -0
  35. package/lib/superagent/host/policy.js +308 -0
  36. package/lib/superagent/host/pr-follow.js +158 -0
  37. package/lib/superagent/host/review-requests.js +77 -0
  38. package/lib/superagent/host/role-tools.js +137 -0
  39. package/lib/superagent/host/store.js +136 -0
  40. package/lib/superagent/host/tools.js +297 -0
  41. package/lib/superagent/source-types.js +22 -0
  42. package/lib/superagent/upstream/dsh-superagent/src/core/assignments.js +160 -0
  43. package/lib/superagent/upstream/dsh-superagent/src/core/engine.js +244 -0
  44. package/lib/superagent/upstream/dsh-superagent/src/core/errors.js +63 -0
  45. package/lib/superagent/upstream/dsh-superagent/src/core/evals.js +381 -0
  46. package/lib/superagent/upstream/dsh-superagent/src/core/ids.js +5 -0
  47. package/lib/superagent/upstream/dsh-superagent/src/core/index.js +9 -0
  48. package/lib/superagent/upstream/dsh-superagent/src/core/intake.js +68 -0
  49. package/lib/superagent/upstream/dsh-superagent/src/core/memory.js +51 -0
  50. package/lib/superagent/upstream/dsh-superagent/src/core/model.js +79 -0
  51. package/lib/superagent/upstream/dsh-superagent/src/core/part.js +71 -0
  52. package/lib/superagent/upstream/dsh-superagent/src/core/ports.js +1 -0
  53. package/lib/superagent/upstream/dsh-superagent/src/core/reasons.js +35 -0
  54. package/lib/superagent/upstream/dsh-superagent/src/core/replies.js +94 -0
  55. package/lib/superagent/upstream/dsh-superagent/src/core/stage.js +27 -0
  56. package/lib/superagent/upstream/dsh-superagent/src/core/tickets.js +487 -0
  57. package/lib/superagent/upstream/snapshot-8c32a9d.js +62 -0
  58. package/lib/task-auth.js +21 -0
  59. package/lib/task-ledger-rows.js +4 -0
  60. package/lib/task-ledger.js +69 -3
  61. package/lib/tools/info.js +13 -7
  62. package/lib/types/a2a-client.d.ts +0 -7
  63. package/lib/types/cli-deposit.d.ts +65 -1
  64. package/lib/types/config.d.ts +6 -20
  65. package/lib/types/direct-forward.d.ts +14 -0
  66. package/lib/types/faces/admin.d.ts +5 -0
  67. package/lib/types/faces/wire.d.ts +20 -57
  68. package/lib/types/followup-admission.d.ts +13 -0
  69. package/lib/types/followup-delivery.d.ts +1 -1
  70. package/lib/types/index.d.ts +0 -2
  71. package/lib/types/outbound-scope.d.ts +1 -0
  72. package/lib/types/queue-guard.d.ts +2 -0
  73. package/lib/types/receipt.d.ts +21 -1
  74. package/lib/types/superagent/a2a-watch.d.ts +227 -0
  75. package/lib/types/superagent/contract.d.ts +36 -0
  76. package/lib/types/superagent/host/a2a-source.d.ts +90 -0
  77. package/lib/types/superagent/host/executor.d.ts +132 -0
  78. package/lib/types/superagent/host/index.d.ts +340 -0
  79. package/lib/types/superagent/host/policy.d.ts +128 -0
  80. package/lib/types/superagent/host/pr-follow.d.ts +51 -0
  81. package/lib/types/superagent/host/review-requests.d.ts +36 -0
  82. package/lib/types/superagent/host/role-tools.d.ts +85 -0
  83. package/lib/types/superagent/host/store.d.ts +61 -0
  84. package/lib/types/superagent/host/tools.d.ts +32 -0
  85. package/lib/types/superagent/source-types.d.ts +218 -0
  86. package/lib/types/superagent/upstream/dsh-superagent/src/core/assignments.d.ts +53 -0
  87. package/lib/types/superagent/upstream/dsh-superagent/src/core/engine.d.ts +191 -0
  88. package/lib/types/superagent/upstream/dsh-superagent/src/core/errors.d.ts +58 -0
  89. package/lib/types/superagent/upstream/dsh-superagent/src/core/evals.d.ts +99 -0
  90. package/lib/types/superagent/upstream/dsh-superagent/src/core/ids.d.ts +1 -0
  91. package/lib/types/superagent/upstream/dsh-superagent/src/core/index.d.ts +9 -0
  92. package/lib/types/superagent/upstream/dsh-superagent/src/core/intake.d.ts +15 -0
  93. package/lib/types/superagent/upstream/dsh-superagent/src/core/memory.d.ts +40 -0
  94. package/lib/types/superagent/upstream/dsh-superagent/src/core/model.d.ts +432 -0
  95. package/lib/types/superagent/upstream/dsh-superagent/src/core/part.d.ts +49 -0
  96. package/lib/types/superagent/upstream/dsh-superagent/src/core/ports.d.ts +217 -0
  97. package/lib/types/superagent/upstream/dsh-superagent/src/core/reasons.d.ts +36 -0
  98. package/lib/types/superagent/upstream/dsh-superagent/src/core/replies.d.ts +55 -0
  99. package/lib/types/superagent/upstream/dsh-superagent/src/core/stage.d.ts +18 -0
  100. package/lib/types/superagent/upstream/dsh-superagent/src/core/tickets.d.ts +151 -0
  101. package/lib/types/superagent/upstream/snapshot-8c32a9d.d.ts +317 -0
  102. package/lib/types/task-auth.d.ts +9 -0
  103. package/lib/types/task-ledger-rows.d.ts +21 -4
  104. package/lib/types/task-ledger.d.ts +9 -0
  105. package/lib/types/types.d.ts +7 -4
  106. package/nexus-stage/scripts/mockhost.py +2 -2
  107. package/nexus-stage/src/main.ts +27 -0
  108. package/nexus-stage/src/planning-card-text.ts +1 -1
  109. package/nexus-stage/src/planning-team-roles.ts +80 -0
  110. package/nexus-stage/src/planning-types.ts +2 -0
  111. package/nexus-stage/src/planning-view.ts +1 -0
  112. package/nexus-stage/src/planning.css +29 -9
  113. package/nexus-stage/src/wb-superagent.ts +518 -0
  114. package/nexus-stage/src/workbench.css +32 -0
  115. package/nexus-stage/src/workbench.ts +25 -3
  116. package/nexus-stage/src/world.ts +1 -1
  117. package/package.json +8 -2
  118. package/assets/nexusDist/assets/index-C7HagaGy.css +0 -1
  119. package/assets/nexusDist/assets/index-CA_YZTWG.js +0 -9
  120. package/assets/stageDist/assets/index-Bj7S2pA1.css +0 -1
  121. package/assets/stageDist/assets/index-C-28k_o5.js +0 -974
  122. package/assets/stageDist/assets/interiors-CQGBjffT.png +0 -0
  123. package/assets/stageDist/assets/office-tileset-CHQT-uYL.png +0 -0
  124. package/assets/stageDist/index.html +0 -13
  125. package/lib/legacy/teams-bridge-inbound.js +0 -156
  126. package/lib/teams-bridge.js +0 -18
  127. package/lib/types/legacy/teams-bridge-inbound.d.ts +0 -94
  128. package/lib/types/teams-bridge.d.ts +0 -112
package/README_zh.md CHANGED
@@ -20,16 +20,16 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
20
20
 
21
21
  重启 `dsh web`,打开侧栏网络面板(底部入口)。
22
22
 
23
- (支持包名、本地路径或 Git URL;包内 `prepare` 会在安装时执行 `pnpm build`。注意:脱离 DSH profile 的独立 Git 安装目前无法构建——官方 `@deepseek-ai/*` npm 快照落后于本插件编译所对照的 harness 源码,请从 registry 安装,或在由 harness 提供当前 `@deepseek-ai` peers 的 profile 内安装。)插件组合在标准 webserver 行旁边:在共享监听器上注册卡片、状态与控制路由,在共享工具运行时上注册模型工具。
23
+ (支持包名、本地路径或 Git URL,参数原样交给 pnpm。包的 `prepare` 脚本是 `pnpm build`,但只有 Git URL 安装会在安装时触发它,而且 pnpm 11 会拦住 Git 包的构建脚本:`dsh plugin` 会报错并打印确切的键,须把它加到 profile 的 `pnpm-workspace.yaml` 的 `allowBuilds` 下再重跑。registry 安装的包已带构建产物,不再构建;本地路径安装不会构建,须先在该目录执行 `pnpm build`。构建需要 pnpm ≥11.24 且 <12(见 `package.json` 的 `engines`)。脱离 DSH profile 的独立 Git 安装现在可以构建(`@deepseek-ai/*` peers 取自 npm;0.7.0 实测通过),不再需要限定 registry 或 profile 安装。)插件组合在标准 webserver 行旁边:在共享监听器上注册状态与控制路由(`/__dsh_a2a/*`,`sessionNodes` 开启时,默认开启),在共享工具运行时上注册模型工具;卡片路由 `/.well-known/agent-card.json` 只在 `announce: true` 时注册(默认关闭)。
24
24
 
25
25
  ## 你能得到什么
26
26
 
27
- - **模型工具**(共十个,见下表)—— `a2a_teams` 列出本节点可见的团队(本机行,加上已加入团队作用域内的 peer 团队);`a2a_route` 向一个团队发送消息,用 `context_id` 继续远端会话,peer 不可达时在候选间故障切换。`a2a_tasks` 按三层对账回执契约:每个让任务欠回复的路由(异步投递、已释放的等待)都以 pending 留在欠账本里,直到其 `[A2A receipt] task <task_id>` 消息关联上——超过 stale TTL 的逾期行自动转死信(复活的目标仍可用迟到回执结清),每次结清都落入有界归档而不是被静默淘汰,每行保留续聊用的 `context_id`,跨重启持久化。`a2a_probe` 测量受跟踪舰队的可达性与往返延迟(每个 peer 一次验签取卡,可收窄到单个 url),供派单前健康检查,并标明每次失败的阶段——`unreachable`(传输/HTTP)或 `rejected`(卡片不可信)。
28
- - **侧栏控制** —— Web 侧栏底部的操作入口,把本宿主的会话列为可加入的网络节点:标题、近期活动摘录、每行所属团队,就地加入/退出,冷行(已持久化加入意图但会话未加载)可唤醒——打开会话即重挂节点。会话团队名为 `<team>/<id8>`;Web 会话取 id 前 8 位,导入会话(`import-<uuid>`)保留 `import-` 前缀加 uuid 前 8 位十六进制,避免导入 id 塌缩成少数几个团队。面板的活动区还列出欠回执——已投递但尚未被其 `[A2A receipt]` 消息关联的异步任务——与在途等待并列,跨轮等待无需询问模型即可看见。
29
- - **归档即离网** —— 归档会话(workspace 注册表状态)会剪除其加入意图并卸载其节点:唤醒前的启动结算、每次状态读取(会话中归档在一个面板轮询周期内消失)、以及永不唤醒已归档目标的路由时守卫。
30
- - **诚实的等待** —— 目标始终不应答的同步路由,在 180s 回复等待期限后以已投递形态与回执契约释放调用方(目标按自己的节奏作答);`async: true` 完全跳过等待。网络面板对超过 120s 的在途行降亮度标记为过期等待而非暗示活跃进度,标题带包版本徽标。
31
- - **A2A 工作台** —— `/__dsh_a2a_nexus/` 全页 DOM 工作台(0.6.0 取代 Three.js 3D 观测;0.6.2 合并:规划板即主舞台,目录/台账/活动/规划乘可折叠侧栏,响应式三档——窄屏侧栏变抽屉、详情变底部浮层;canvas 面掉线时面板自动回落主区):目录(本机现场/冷启动、canvas 队、peers、远端节点)、台账(待回执/死信+就地清账)、活动环、规划(衍生行动清单+催办/派单起草)、空间规划板(canvas 队组建/成员编排/网络加入退出,布局持久化,`?mode=plan` 直达)。
32
- - **通告** —— `announce: true` 发布本节点卡片(团队、能力、引荐、已加入会话团队),peers 无需任何目录即可发现它。
27
+ - **模型工具**(共十二个,见下表)—— `a2a_teams` 列出本节点可见的团队(本机行,加上已加入团队作用域内的 peer 团队);`a2a_route` 向一个团队发送消息,用 `context_id` 继续远端会话,peer 不可达时在候选间故障切换。`a2a_tasks` 按三层对账回执契约:每个让任务欠回复的路由(异步投递、到 180s 期限被释放的同步等待;调用方自行中止的同步等待不入账,没有可路由回调地址的投递只登记为「无回执债」的可见行)都以 pending 留在欠账本里,直到其 `[A2A receipt] task <task_id>` 消息关联上——超过 stale TTL 的逾期行自动转死信(复活的目标仍可用迟到回执结清),每次结清都落入有界归档而不是被静默淘汰,每行保留续聊用的 `context_id`,跨重启持久化。`a2a_probe` 测量受跟踪舰队的可达性与往返延迟(每个 peer 一次验签取卡,可收窄到单个 url),供派单前健康检查,并标明每次失败的阶段——`unreachable`(传输/HTTP)或 `rejected`(卡片不可信)。
28
+ - **侧栏面板** —— Web 侧栏底部的网络入口,已瘦身为本宿主的最近网络动态(`docs/design/panel-activity-feed.md`):在途路由、欠回执(只列可结算的欠账,对话式已送达的陈旧尾巴只计数)与路由活动环并列,跨轮等待无需询问模型即可看见;同机会话的活动行点击可在 GUI 内跳转到该会话;入站未读徽标,标题带包版本徽标,并提供工作台入口。会话的入网/退网、画布队与成员编排不再在侧栏里做,已迁入下面的工作台规划板。会话团队名为 `<team>/<id8>`;Web 会话取 id 前 8 位(先去掉 `session-` 前缀),导入会话(`import-<uuid>`)保留 `import-` 前缀加 uuid 前 8 位十六进制,避免导入 id 塌缩成少数几个团队。
29
+ - **归档即离网** —— 归档会话(workspace 注册表状态)会离开节点网络:卸载其节点、撤回其 roster 声明、从列表与卡片中隐藏,并永不被唤醒——发生在唤醒前的启动结算、每次状态读取(会话中归档在一个状态轮询周期内消失)、以及路由时守卫三处。**加入意图保留**在 `joined.json`(曾因瞬时读到归档态而抹掉入网姿态,导致冷会话再也唤不醒),剪除事件记入状态面 `registry.teamAudit` 的 `archive-prune`。
30
+ - **诚实的等待** —— 目标始终不应答的同步路由:本机会话目标在 180s 回复等待期限后以已投递形态(`TASK_STATE_DELIVERED`)与回执契约释放调用方(目标按自己的节奏作答);经直连的远端 peer 受 15s 传输预算约束,超时先经任务查询补偿(对端已完成则升级为真实结果),否则以 `-32005`「已投递未结清」失败返回,须按任务查询对账、不要盲目重发。`async: true` 完全跳过等待。网络面板对超过 120s 的在途行降亮度标记为过期等待而非暗示活跃进度。
31
+ - **A2A 工作台** —— `/__dsh_a2a_nexus/` 全页 DOM 工作台(0.6.0 取代 Three.js 3D 观测;0.6.2 合并:规划板即主舞台,目录/主机/台账/活动/规划乘可折叠侧栏,宿主运行 superagent 团队时多一个「团队」页;响应式——宽屏 ≥1440px 加宽侧栏,≤1023px 侧栏变抽屉、详情变底部浮层,≤680px 页头折行;canvas 面掉线时面板自动回落主区):目录(本机会话现场/冷启动、画布队、远端节点、本机日志)、主机(tracked peers 的健康与 linked 徽章)、台账(待回执/死信+就地清账)、活动环、规划(衍生行动清单+催办/派单起草,均生成可复制文本)、空间规划板(canvas 队组建/成员编排/网络加入退出,布局持久化;`?mode=plan` 仅为旧链接兼容,canvas 面在线时本就默认进入规划板)。
32
+ - **通告** —— `announce: true`(默认 `false`,需要组合了 web server)发布本节点签名卡片,peers 无需任何目录即可发现它。引荐 `peers` 只给经签名验证、且已配种子或已链接的调用方;已加入会话团队 `sessionTeams`、roster 与画布组织只给已链接的调用方;其余调用方只得到最小发现卡(名称、团队、能力、记录与签名)。未入网的会话节点不在卡片里公布。
33
33
 
34
34
  ## 模型工具一览
35
35
 
@@ -42,13 +42,13 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
42
42
  | `a2a_route` | 向一个团队发消息。 | `context_id` 续聊;`async: true` 投递不等待;在候选间故障切换。 |
43
43
  | `a2a_tasks` | 哪些异步任务还欠回执? | 三层:欠账行保留续聊 `context_id`;过 TTL 的行自动转死信(仍可结清);结局落入有界归档;跨重启持久化。 |
44
44
  | `a2a_probe` | 舰队健康吗? | 每个作用域内 peer 一次验签取卡(或指定一个 url);`N reachable, M down`;失败分为 `unreachable` 与 `rejected`。 |
45
- | `a2a_team_join` | 把一个已加入节点声明为某团队成员。 | 白名单门禁:只有 `teamJoinAllowlist` 里的**精确名**才授权加入(空列表拒绝一切加入)。通配/前缀条目会在 `a2a_whoami` 里显示,但不授权任何加入(`[PATTERN_NO_MINT]`);画布队伍在工作台分配,从不经此工具(`[CANVAS_TEAM]`)。尝试记录进状态面的 `registry.teamAudit`。 |
46
- | `a2a_team_leave` | 撤回一个团队成员关系。 | 总是允许——退出只会缩小暴露面。 |
47
- | `a2a_exposure` | 本会话有多可见? | 设置会话自己的暴露档:`linked`(默认下限)或 `fleet`(自愿上调);组的可见性取最严成员。需会话已加入网络。 |
48
- | `a2a_report` | 不在网时向本宿主监督面报告。 | 唯一不受加入门约束的入口:单向,落入状态面的 reports 环,不授予在场或路由权。每会话 30s 一次、≤4000 字,同文 10 分钟内 3 次按循环拒绝。 |
45
+ | `a2a_team_join` | 把一个已加入节点声明为某团队成员。 | 白名单门禁:只有 `teamJoinAllowlist` 里的**精确名**才授权加入(空列表拒绝一切加入)。通配/前缀条目会在 `a2a_whoami` 里显示,但不授权任何加入(`[PATTERN_NO_MINT]`);画布队伍在工作台分配,从不经此工具(`[CANVAS_TEAM]`)。尝试记录进状态面的 `registry.teamAudit`。模型工具只能为调用会话自己的节点声明(否则 `[NOT_SELF]`),且该节点须已入网(否则 `[NOT_JOINED]`)。 |
46
+ | `a2a_team_leave` | 撤回一个团队成员关系。 | 不受白名单门禁——退出只会缩小暴露面;仍只能撤回调用会话自己节点已声明过的成员关系(`[NOT_SELF]`、`[NOT_JOINED]`,未声明过则无事可退)。 |
47
+ | `a2a_exposure` | 本会话有多可见? | 设置会话自己的暴露档:`linked`(默认下限)或 `fleet`(自愿上调),可随时升降;需会话已加入网络。**目前档位只持久化(`a2a/exposure.json`)并在状态面会话行的 `exposure` 字段展示,尚不改变卡片或目录的可见性**;「组的可见性取最严成员」是已成文的规则,v1 既无组级接口也无执行点。 |
48
+ | `a2a_report` | 不在网时向本宿主监督面报告。 | 唯一能在未入网时向本宿主写入的入口(只读的 `a2a_whoami` 同样不受加入门约束):单向,落入状态面的 reports 环(保留最近 64 条),不授予在场或路由权。每会话 30s 一次、≤4000 字;同会话同文在 10 分钟内已上报 3 次后,第 4 次起按循环熔断拒绝。 |
49
49
  | `a2a_status` | 本节点此刻在做什么? | 作用域内受跟踪 peers 及质量分、在途路由、近期路由活动,以及幂等窗口汇总(占用 + claim/replay/conflict 计数)。 |
50
- | `a2a_file_send` | 把文件交给一个团队,又不把文件塞进消息。 | 每个文件(每次 ≤16 个,单个 ≤64 MiB)暂存在本宿主、授权给该团队,然后只路由**一条**带 `[A2A file]` 引用行的消息;字节从不随消息走(#91)。 |
51
- | `a2a_file_fetch` | 拉取别人引用的文件。 | 以本会话身份签名,从暂存它的宿主拉取一条 `[A2A file]` 引用;先核对大小和 sha256,再存到 `<cwd>/a2a-inbox/`。授权只对一个会话(或整台宿主、或某画布队的成员);拉取总会把本会话标为读过对端内容。 |
50
+ | `a2a_file_send` | 把文件交给一个团队,又不把文件塞进消息。 | 每个文件(每次 ≤16 个,单个 ≤64 MiB)暂存在本宿主、授权给该团队 24 小时,然后只路由**一条**带 `[A2A file]` 引用行的消息;字节从不随消息走(#91)。需本宿主已发布卡片地址(`announce`),否则拒绝发送。 |
51
+ | `a2a_file_fetch` | 拉取别人引用的文件。 | 带本会话身份(`caller_session`)并盖上本宿主节点的 OriginClaim 信封,从暂存它的宿主拉取一条 `[A2A file]` 引用(引用指向本宿主时直接读本机暂存);先核对大小和 sha256,再存到 `<cwd>/a2a-inbox/`。授权只对一个会话(或整台宿主、或某画布队的成员);拉取总会把本会话标为读过对端内容。 |
52
52
 
53
53
  ## a2a-net CLI(会话之外的调用方)
54
54
 
@@ -61,39 +61,28 @@ a2a-net route dsh/1a2b3c4d "status?" # 默认异步;--sync
61
61
  a2a-net inbox --new # 未读来信;--wait 阻塞等待,--hook 供 Claude Code hook 使用
62
62
  ```
63
63
 
64
- 从 0.6.38 起 `forwardDirect` 默认 `true`;本机目标未找到时,home 可替已验签且在线的 CLI 向已链接的对端目标转发一跳。两边运营者必须各自链接:A 链接 B 的接收成员,B 链接 CLI 句柄并将 home 钉为 A。画布目标只需 A 链接画布属主主机(#282 S3,同队即授权):接收成员在转发行出生时从属主签名名片冻结,回执只有来自这些成员、且由该属主公钥签名时才放行进 CLI 信箱并结算。作用域及结算 enforce 开启时,B 成员宿主的自动回执结清 A 的转发行并进入 CLI 信箱;`--sync` 返回 B 的回复。命令、参数、环境变量与退出码见 [`src/skill/references/cli.md`](src/skill/references/cli.md)。
64
+ 从 0.6.38 起 `forwardDirect` 默认 `true`;本机目标未找到时,home 可替已验签且在线的 CLI 向已链接的对端目标转发一跳。两边运营者必须各自链接:A 链接 B 的接收成员,B 链接 CLI 句柄并将 home 钉为 A。画布目标只需 A 链接画布属主主机(#282 S3,同队即授权;属主主机须为 0.6.37 及以上):接收成员在转发行出生时从属主签名名片冻结,回执只有来自这些成员、且由该属主公钥签名时才放行进 CLI 信箱并结算。作用域及结算 enforce 开启时,B 成员宿主的自动回执结清 A 的转发行并进入 CLI 信箱;`--sync` 返回 B 的回复,但转发路径上同步等待只有约 15 秒的传输窗口,超时返回 `-32005`(已投递未结清):对端做完后仍会自动回执,请按 task id / `inbox` 对账,不要盲目重发;长任务用默认的异步 `route`,再 `inbox --wait --renew`。命令、参数、环境变量与退出码见 [`src/skill/references/cli.md`](src/skill/references/cli.md)。
65
65
 
66
66
 
67
67
  ### 独立网络节点(不依赖 DSH 实例)
68
68
 
69
69
  部署手册(安装、常驻服务、与 DSH 宿主互联、公网安全):[docs/guides/standalone-node.md](docs/guides/standalone-node.md)。
70
70
 
71
- `a2a-net serve --config node.json` 在一个独立的 Node 进程里运行网络层:名片与签名、对端目录、CLI 户口与信箱、一跳转发、canvas 归属、台账、状态面和工作台。它不承载 AI 会话,发往会话的消息照常转发给 DSH 主机。把它部署在一台稳定的机器上作为 CLI 的 home,DSH 重启就不会让 CLI 户口、信箱和 canvas 目录掉线(节点自身重启后也保留)。安装与配置见 [`src/skill/references/cli.md`](src/skill/references/cli.md) 的 `a2a-net serve` 一节。
71
+ `a2a-net serve --config node.json` 在一个独立的 Node 进程里运行网络层:名片与签名、对端目录、CLI 户口与信箱、一跳转发、canvas 归属、台账、状态面和工作台。它不承载 AI 会话,CLI 发往会话的消息按上述一跳转发规则交给已链接的 DSH 主机。「不依赖 DSH 实例」指不需要运行中的 DSH 主机;运行时仍需 `@deepseek-ai/*` 的 npm 包(缺失时 `serve` 会打印要安装的固定版本集合)。把它部署在一台稳定的机器上作为 CLI 的 home,DSH 重启就不会让 CLI 户口、信箱和 canvas 目录掉线(节点自身重启后也保留)。安装与配置见 [`src/skill/references/cli.md`](src/skill/references/cli.md) 的 `a2a-net serve` 一节。
72
72
 
73
73
  ## 画布队伍(任意组队,v0.5.25)
74
74
 
75
75
  主机/成员的 linked 徽章按主机事实显示:双向 URL 种子,或该主机卡唯一列出的精确非本机画布成员句柄。同名 zone(例如全为 `dsh`)不再推断已链接;最小卡缺少会话列表时,除非双向种子能证明该主机,否则保守显示 stranger。该显示规则不改变调用方准入或 CLI home 校验。
76
76
 
77
- 画布队伍是用户自建的有名多成员路由组:**一个原子会话节点可同时属于多个队伍**,路由到 `<team>/canvas/<name>` 解析**第一个活成员**(成员顺序即路由优先级),无活成员则唤醒**第一个冷 joined 成员**(wake-on-route)。`canvas/` 路径段与节点别名(`<team>/<id8>`)结构性无冲突,两个命名空间互不干扰。
77
+ 画布队伍是用户自建的有名多成员路由组:**一个原子会话节点可同时属于多个队伍**,路由到 `<team>/canvas/<name>` 解析**第一个活成员**(成员顺序即路由优先级),无活成员则唤醒**第一个冷 joined 成员**(wake-on-route);本机成员都不可用时,再依次落到远端成员(经目录解析其句柄,远端节点不会在这里被唤醒)。发起路由的会话自己不会占用本机两档(自路由规则,#41)。`canvas/` 路径段与节点别名(`<team>/<id8>`)结构性无冲突,两个命名空间互不干扰。
78
78
 
79
- - 存储:`<dsh-home>/a2a/canvas.json`(有序队伍条目;上限:64 队 × 32 成员)
80
- - 控制 API:`POST /__dsh_a2a/canvas`,`action: create | remove | add-member | remove-member`;`cli-home-options` 是仅查询已有 CLI 成员候选的属主操作。
79
+ - 存储:`<dsh-home>/a2a/canvas.json`(有序队伍条目;上限:64 队 × 32 成员;文件另带版本号、成员属主锚、暂停记录与 CLI home 钉)
80
+ - 控制 API:`POST /__dsh_a2a/canvas`,`action: create | remove | add-member | remove-member`;`add-member` 对不存在的队伍会先建队。另有属主的成员来源与暂停治理操作 `confirm-member`、`restore-suspended`、`delete-suspended`(#334/#335),以及仅查询已有 CLI 成员候选的 `cli-home-options`。队名 ≤40 字,不含 `/`,不是纯数字,不以 `@<8 位小写十六进制>` 结尾,且不能被 roster 保留(`roster:` 前缀,或等于白名单/roster 团队名);含暂停成员的队不能 `remove`。
81
81
  - CLI 成员标「home 未确定」时,打开其画布队详情并点「指定 home」。宿主现读名片,只列已链接且列出该句柄的主机,并显示不可读名片数;不会自动选中。确认后复用本机 `add-member` 的显式 `home`,提交时再次现读校验,不放宽自动钉或准入。本机持有在线户口或离线墓碑时显示「本机持有,无需指定」,候选返回 `held:true` 且为空,显式 home 写入提示先撤销本机户口;回执优先投在线本机信箱;本机 publish 成功会清除冲突旧钉并在面板提示,户口离线不复活旧钉;本地投递中途到期即报错,不回落对端目录。
82
- - 成员必须是 joined 会话(活节点或已记意图)——未加入会话没有路由后门;退出网络(leave/archive)自动清空全部画布成员关系
83
- - 成员也可以是操作者手工加入的**远端**带 zone 团队句柄(`zone/a1b2c3d4`)——这就是让该远端调用方在本机获准入、并有权拿完整卡片的链接
82
+ - 成员必须是 joined 会话(活节点或已记意图)——未加入会话没有路由后门;退出网络(leave/archive)自动把它移出全部画布队(旧式裸句柄槽位改记为 suspended:不再路由、不计入有效成员,由属主 `restore-suspended` 或 `delete-suspended`,见 `docs/design/canvas-member-provenance.md`)
83
+ - 成员也可以是操作者手工加入的**远端**带 zone 团队句柄(`zone/a1b2c3d4`)——这就是让该远端调用方在本机获准入、并有权拿完整卡片的链接。写入时宿主现读已链接的种子/委托主机的验签名片,把句柄钉成带属主指纹的 `zone/a1b2c3d4@<8 位指纹>`;找不到唯一可验证的属主就拒写(#334)。不带指纹的旧式裸句柄只作兼容保留(`canvasLegacyBareMembers` 默认 `true` 时仍按旧读法计入、授权和路由),需属主 `confirm-member` 确认
84
84
  - `a2a_teams` 列出本地和作用域内远端画布队,带属主 URL 和成员数;CLI `teams` 包含本地 canvas 和 peerOrg,同名不同属主各列一行。`a2a_whoami.teams.canvasDetails` 和 CLI `whoami --json` 列本身份本地/远端归属,旧 `teams.canvas` 本地名字数组兼容保留。URL 只作发现事实,不要求改 `--home`。失败提示区分可见目录无此队、已知属主但缺前提、同名多属主(列候选不猜);信息作用域、路由选择与准入不变;`/__dsh_a2a/state` 对本地成员提供 `joined`/`live` 标记,对远端成员提供 `{ id, team, remote: true }`
85
85
 
86
- ## Native-teams 桥(节点统一,P1)——已弃用
87
-
88
- > **已弃用。** `@nelsonlongxiang/dsh-native-teams` 已弃用。桥接代码仍随包发布(`src/teams-bridge.ts`;`nativeTeamsInbound` 默认 `false`),仅为既有部署保留;新部署不应依赖它。下文按其现状描述。
89
-
90
- 通往 `@nelsonlongxiang/dsh-native-teams` 的两条半桥(结构契约镜像:`src/teams-bridge.ts`;冻结契约原文在该包 `src/a2a-face.ts`):
91
-
92
- - **出站传输面**——本插件挂载 `nativeTeamsA2a` 服务:`resolve` 与 `submit` 复用同一次目录遍历(单一匹配器,无漂移风险),`submit` 走直连路由派发器(逐候选 async 门禁,镜像 `a2a_route`;对端幂等 409 回放判为终态 accepted,绝不转投造成重复执行;accepted 提交进欠账账本;提交会话为 joined 节点时,其节点地址随 wire `callback` 字段携带——对端回执据此路由回发起会话,冷会话由 wake-on-route 物化;父会话未 joined 时不携带 callback,回执回落 caller label),`cancel` 经 wire 向持有团队投递协作式 `[A2A cancel]` 停止通知(对端不跟踪入站任务 id,走其账本路由必然 unknown)并清除本端欠账行。它不暴露任何新东西——只有 peer 网络已发布的团队可解析。
93
- - **入站分发**(配置 `nativeTeamsInbound`,默认 `false`)——兄弟注册表判定为无歧义本地主张的团队名,经其权威路由缝(`describeTarget`/`startRound`)发起一轮路由,由本节点活 initiator 会话担任 parent,A2A 信封随轮消息携带。注册表存在本身绝不是暴露:操作者显式开启才算。仅分发到 dispatcher 层——入站调用方寻址团队,不能寻址成员(成员保持可见不可寻)。轮与 steer 路径同样有界:180s 回复死线(`nativeRoundWaitMs`)以诚实的 delivered-unsettled 形态应答,轮自身继续运行;调用方 abort(存在时)经其 signal 取消轮。`wait: false` 在 prepare-first 检查(主张/seam/initiator——幻影派发绝不回应成功)之后脱离派发;本切片 native-teams 轮不发 A2A 回执,其结果携带 `bridge` 标记且绝不记为欠回执行(回 `callbackTarget` 的回执回流属 P2 切片)。
94
-
95
- 完整映射与范围说明见 `docs/native-teams-bridge.md`。
96
-
97
86
  ## 配置
98
87
 
99
88
  行配置叠加在 profile 的 patch 层;每个键都有 schema 默认值。
@@ -103,24 +92,23 @@ a2a-net inbox --new # 未读来信;--wait
103
92
  | `announce` | `false` | 发布本节点 agent 卡片供 peer 发现。 |
104
93
  | `isolated` | `false` | 不向任何对端发请求:取卡、目录扫描、路由、远端日志读取、canvas 变更推送、舰队 canvas-op 中继一律拒绝(gh#193)。`a2a/peers.json` 不动,被拒的请求也不给对端扣分;入站面照常应答;state 面显示 `isolated: true`。供测试与验收主机使用。 |
105
94
  | `forwardDirect` | `true` | 从 0.6.38 起默认开启,本机目标未找到时,允许替已验签且在线的本地CLI转发一跳;仍检查发送作用域和跳数预算。A 链接 B 接收成员,B 链接 CLI 并钉住 A;对端不会再转发;显式设 `forwardDirect: false` 恢复 CLI 仅投本机。 |
95
+ | `superagent` | `false` | superagent(设计 v4,团队模型 v5):在本主机运行 superagent 自己的领域引擎(原样引入的上游 `src/core`)。团队就是一支画布团队:属主在画布上组队(总工与成员,本机或其他主机),再在 `/__dsh_a2a/superagent` 绑定画布、指定总工和允许的发送者;团队的 A2A 地址就是画布地址 `<zone>/canvas/<名字>`。总工与成员分别使用插件自带的 `superagent-router` / `superagent-member` 智能体预设;见 [`superagent.md`](src/skill/references/superagent.md)。 |
106
96
  | `peers` | `[]` | 种子 base URL;被引荐的 peers 从其卡片学习。 |
107
97
  | `delegates` | `[]` | 区域委托:`{ name, url, publicKey }` 发布在卡片上。 |
108
98
  | `team` | `'dsh'` | 本节点暴露给直连路由的团队名。 |
109
99
  | `session` | `''` | 调用方标签;`''` 为每个 home 派生稳定的 `dsh-host-<8hex>`。 |
110
100
  | `agentName` | `'DeepSeek Harness A2A node'` | 卡片上的人类可读名称。 |
111
- | `apiKey` | `''` | peer 请求携带的 `X-API-Key`;非空时同时门禁控制路由。 |
101
+ | `apiKey` | `''` | 发往 peer 的请求携带的 `X-API-Key`(配了 `linkKeys` 的对端改带链路 key);非空时同时门禁控制路由和本机对端面(`/a2a/direct`、`/a2a/log`、`/a2a/blob`,链路 key 的接受条件见 `linkKeys`)。为空时对端面保持开放姿态,按调用方身份准入。 |
112
102
  | `sessionNodes` | `true` | 把主会话暴露为可加入的网络节点。 |
113
- | `nativeTeamsInbound` | `false` | 把入站直连路由(及出站 A2A 工具的本地候选)经 native-teams 路由缝分发到其注册表团队;需兄弟插件已组合。 |
114
- | `nativeRoundWaitMs` | `180000` | 单轮 native-teams 路由的回复等待预算(桥的死线,对齐 steer 路径的 180s)。超时轮以诚实的 delivered-unsettled 形态应答并继续运行。 |
115
- | `wakeJoinedOnBoot` | `false` | 挂载后预热冷加入会话的 agent(需要 session controller 唤醒面;无它时路由唤醒与侧栏唤醒按钮仍可用)。预热是延迟启动、前台让路、可取消的——不会阻塞启动窗口(见下方两个参数)。 |
103
+ | `wakeJoinedOnBoot` | `false` | 挂载后预热冷加入会话的 agent(需要 session controller 唤醒面,缺失时跳过并在状态面记 `skipped:no-wake-face`;关闭时冷会话仍会被路由时唤醒(wake-on-route)和 `wakeReconcile` 对账唤醒,二者同样需要唤醒面)。预热是延迟启动、前台让路、可取消的——不会阻塞启动窗口(见下方两个参数)。 |
116
104
  | `wakePrewarmDelayMs` | `10000` | loader 树就绪到第一次预热唤醒之间的空闲延迟;`0` 恢复就绪即唤醒的旧行为。 |
117
- | `wakePrewarmQuietMs` | `5000` | 前台静默窗口:窗口内有唤醒/路由需求(或有出站路由在途)则推迟下一步预热;`0` 关闭让路。 |
105
+ | `wakePrewarmQuietMs` | `5000` | 前台静默窗口:窗口内有唤醒/路由需求(或有出站路由在途)则推迟下一步预热;`0` 关闭时间窗,但有出站路由在途时仍会让路。对账循环(`wakeReconcile`)也按此窗口让路,每轮最多让路 5 次。 |
118
106
  | `wakeBootStaggerMs` | `3000` | 启动预热串行唤醒之间的停顿(压住重启后的日志解码饱和;路由唤醒不排此队)。 |
119
107
  | `wakeReconcile` | `true` | 持续的期望态对账:每个 tick 重新推导到期的冷加入意图,每轮唤醒一个(覆盖启动后漂移、唤醒失败、晚挂载的网关)。由 `wakeReconcileIntervalMs`(`60000`)、`wakeReconcileBackoffBaseMs`(`5000`)、`wakeReconcileMaxBackoffMs`(`600000`)调节。 |
120
- | `seatNames` | `{}` | 按完整本机会话 id 保存的人工席位展示名;拒绝 Cc/Cf 控制/格式字符、行段分隔符、无可见内容的名字,以及形似路由地址的名字(如 `dsh/66abc2cf`、`zone/canvas/x`)。启动配置逐条丢弃非法项并警告,不拖垮插件;属主写入仍整次校验。工作台主人设置中选择会话、输入并显式保存;留空保存清除。即时用于卡面、whoami、CLI teams 和画布,不改标题、句柄或准入;A2A 模型工具携带命名字段会被拒。 |
108
+ | `seatNames` | `{}` | 按完整本机会话 id(`session-…` 形式,至多 1024 条)保存的人工席位展示名(≤64 字);拒绝 Cc/Cf 控制/格式字符、行段分隔符、无可见内容的名字,以及形似路由地址的名字(如 `dsh/66abc2cf`、`zone/canvas/x`)。启动配置逐条丢弃非法项并警告,不拖垮插件;属主写入仍整次校验。工作台主人设置中选择会话、输入并显式保存;留空保存清除。即时用于卡面、whoami、CLI teams 和画布,不改标题、句柄或准入;A2A 模型工具携带命名字段会被拒。 |
121
109
  | `teamJoinAllowlist` | `[]` | `a2a_team_join` 可声明的团队:**只认精确名**(`*`/前缀条目不授权任何加入)。空列表拒绝一切加入。属主也可在工作台属主面板(`/__dsh_a2a/owner-settings`)编辑。 |
122
110
  | `teamScopeRouting` | `true` | S3 准入:路由检查调用方与目标是否共享团队;未声明任何团队的会话没有网络。`false` 恢复开放出站缝。 |
123
- | `teamScopeInfo` | `true` | S5 信息面作用域,作用于 `a2a_teams` / `a2a_status` / `a2a_probe`(见工具一览)。`false` 恢复全舰队披露。 |
111
+ | `teamScopeInfo` | `true` | S5 信息面作用域,作用于 `a2a_teams` / `a2a_status` / `a2a_probe`(见工具一览)。`false` 恢复全舰队披露;属主面板热改后三个工具都即时生效。 |
124
112
  | `peerAllowlist` | `[]` | 非空时,引荐只有在主机匹配某条目时才被接纳(精确 `host[:port]`、裸域名后缀或完整 URL)。种子从不过滤。 |
125
113
  | `referralLearning` | `true` | `false` 把跟踪集冻结为配置的种子:既不学习也不提供引荐。 |
126
114
  | `referralTeamResolution` | `false` | gh#8:团队名只在种子 + 委托上解析;`true` 恢复旧的全 store 基础(含引荐学到的 peers)。 |
@@ -133,11 +121,11 @@ a2a-net inbox --new # 未读来信;--wait
133
121
  | `linkKeys` | `{}` | 链路 key(#317):`{ "<对端 origin>": "<key>" }`。发往该对端的请求改带这把 key;本机对端面(`/a2a/direct`、`/a2a/log`、`/a2a/blob`)只在调用方 OriginClaim 验签通过、且签名者正是该 origin 时接受它(POST 须 Sig3 绑定请求体);控制路由不接受。用于公网节点与舰队互联而不持有舰队 apiKey,两端配同一把链路 key。 |
134
122
  | `gateAgentLessCalls` | `'audit'` | 宿主侧(无 agent)路由调用:审计,或 `refuse` 拒绝。 |
135
123
  | `settlementAuth` | `'observe'` | 结算授权(#114):`off`、`observe`(记下本会拒绝的,照旧结算)或 `enforce`。 |
136
- | `asyncNudgeDelayMs` | `120000` | 已投递但未被消费的异步路由,在此延迟后用一行催办重新唤醒目标;`0` 关闭。 |
124
+ | `asyncNudgeDelayMs` | `120000` | 经 `/a2a/direct` 入站、已投递但目标未消费的异步任务,在此延迟后用一行催办重新唤醒目标(目标忙时先让一轮,再升级为排队提示);`0` 关闭。 |
137
125
  | `targetQueueCap` | `3` | 本宿主上同一个目标最多排队这么多条目标尚未回应且等回执的任务;再来新投递就可见拒收(`-32009 target-queue-full`,不投递)。本宿主自己记过账的任务的回执、以及 CLI 信箱从不拦;一个画布队算一个目标。被拒的 task id 记为失败,重试要换新 id。目标消费本任务后的 final 或目标自己发回执会记 `answeredAt` 并释放名额,即使回执投递失败;行仍欠回执,死信、迟到回执和守恒不变,状态面/画布排队数采用同一口径。`0` 关闭(#224/#253)。 |
138
126
  | `taskStaleTtlMs` | `86400000` | 欠账任务的 stale TTL(24h):过期后 pending 行自动转死信(迟到回执仍可结清)。 |
139
127
  | `dshHome` | `''` | 插件状态所用的 DSH home 目录覆盖(`<home>/a2a/…`);`''` 解析默认 home。 |
140
- | `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | 见 schema | 卡片生命周期(48h)与路由时序预算。 |
128
+ | `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | `172800000` / `300000` / `1800000` | 卡片生命周期(48h,每 1/4 周期重签一次);会话在此窗口内没有最终回复时,回复等待者以占位应答释放(5 分钟);`a2a_route` 工具的单次调用超时(30 分钟)。 |
141
129
  | `cardCacheTtlMs` / `cardCacheNegativeTtlMs` / `remoteRowsTtlMs` / `stateColdRowsTtlMs` | `60000` / `30000` / `15000` / `5000` | peer 卡片缓存(命中/未命中)、远端会话行刷新窗口、状态面冷行刷新窗口。 |
142
130
 
143
131
  示例——一个带种子的通告节点:
@@ -152,66 +140,74 @@ a2a-net inbox --new # 未读来信;--wait
152
140
 
153
141
  ## 退出语义(有界的陈旧期)
154
142
 
155
- `leave` 是按会话、按宿主的动作:在本地移除加入意图并卸载节点。**没有全网撤销广播**——peers 会继续列出该团队,直到它们缓存的本节点卡片刷新(`cardCacheTtlMs`,默认 60s;`sessionTeams` 是未签名的现读字段,不受卡片自身 48h TTL 约束),这段时间内的入站投递会诚实失败(宿主已不再解析该团队)。已实现回执契约的远端目标不受影响。正式的撤销广播协议记录在上游笔记 `2026-08-27-a2a-leave-no-revocation-broadcast.md`(提案)。
143
+ `leave` 是按会话、按宿主的动作(`POST /__dsh_a2a/leave`,控制路由):在本地移除加入意图并卸载节点,同时把该会话移出全部画布队(旧式裸句柄槽位记为 suspended)、撤回它的 roster 声明;本机发布的卡片里 `sessionTeams` 立刻不再列出它。**没有全网撤销广播**——peers 会继续列出该团队,直到它们缓存的本节点卡片刷新(对端自己的 `cardCacheTtlMs`,默认 60s;`sessionTeams` 是未签名的现读字段,不受卡片自身 48h TTL 约束),这段时间内的入站投递会诚实失败(宿主已不再解析该团队)。这个上界只在本节点仍可达时成立:若 peer 此后读不到本节点的卡片,会在 `cardCacheNegativeTtlMs`(默认 30s)窗口内沿用旧卡并重试,最迟留到旧卡自身的到期(默认 48h,行标记为 unreachable)。正式的撤销广播协议记录在上游笔记 `2026-08-27-a2a-leave-no-revocation-broadcast.md`(提案)。
156
144
 
157
145
  ## 信任模型
158
146
 
159
- 卡片经 Ed25519 签名且会过期;卡片上的 `peers` 引荐、`sessionTeams` 列表、`lanIp` 与 `version` 未签名、每次现读——后两项让运维无需逐台登录即可核对每个舰队节点实际运行的构建。若某引荐 URL 自身的取卡刚证明它提供的就是本节点的卡片,该 URL 会被记住,抑制窗口内再次被引荐时直接忽略,不会在 peer store 里反复闪现。
147
+ 卡片经 Ed25519 签名且会过期;签名核心只含 `name`、`session`、`team`、`capabilities`、`expiresAt` 与区域委托记录 `records`。卡片上的 `peers` 引荐、`sessionTeams` 列表、`teamMemberships`、`canvasOrg`、`lanIp` 与 `version` 未签名、每次现读——后两项让运维无需逐台登录即可核对每个舰队节点实际运行的构建。若某引荐 URL 自身的取卡刚证明它提供的是本节点的卡片(或某个已跟踪 peer 的另一种地址写法),该 URL 会被记住 6 小时,窗口内再次被引荐时直接忽略,不会在 peer store 里反复闪现。
160
148
 
161
- 每个 `/__dsh_a2a/*` 控制路由(读也算)在配置了 `apiKey` 时都要求之(`X-API-Key`,常数时间比较)。密钥为空时只信任 loopback socket;`Sec-Fetch-Site` 头只能否决、不能授权(跨站页面即使在 loopback 上也会被拒)。把监听器暴露到 loopback 之外前请先设置 `apiKey`。
149
+ 每个 `/__dsh_a2a/*` 控制路由(读也算)在配置了 `apiKey` 时都要求之(`X-API-Key` 头,非浏览器客户端也可用 `api_key` 查询参数;常数时间比较)。密钥为空时只信任 loopback socket;`Sec-Fetch-Site` 头只能否决、不能授权(跨站页面即使在 loopback 上也会被拒),带 `X-Forwarded-For`、`Forwarded` 或 `X-Real-IP` 的请求同样不享受 loopback 信任(经本机反向代理或隧道进来的远端调用方不能借此免密钥)。把监听器暴露到 loopback 之外前请先设置 `apiKey`。
162
150
 
163
151
  | 控制路由 | 用途 |
164
152
  | --- | --- |
165
153
  | `GET /__dsh_a2a/state` | 面板与工作台轮询的状态面(会话、画布、peers、远端行、活动、账本)。 |
166
154
  | `POST /__dsh_a2a/join` · `/leave` | 加入或退出一个会话节点。 |
167
- | `/__dsh_a2a/canvas` · `/canvas-layout` | 画布队伍(create/remove/add-member/remove-member)与规划板布局。 |
168
- | `POST /__dsh_a2a/roster-op` | 属主在画布创建、加减成员、解散 roster 队,并维护精确允许名单。 |
155
+ | `/__dsh_a2a/canvas` · `/canvas-layout` | 画布队伍(create/remove/add-member/remove-member,另有属主的 confirm-member / restore-suspended / delete-suspended / cli-home-options)与规划板布局。 |
156
+ | `POST /__dsh_a2a/roster-op` | 属主在规划板创建(至少两个已加入的本机会话)、加减成员、解散 roster 队(`op`:create / add-member / remove-member / dissolve),并同步维护 `teamJoinAllowlist` 里的精确名;不能接纳远端或未加入的会话。 |
169
157
  | `/__dsh_a2a/groups` | 手工会话分组——自 0.6.6 起弃用(被画布队伍取代),为旧面板保留。 |
170
158
  | `POST /__dsh_a2a/dispatch` | 工作台快速派单到一个团队。 |
171
- | `POST /__dsh_a2a/tasks/abandon` · `/tasks/cancel` | 从调用方一侧结清一个欠账任务(cancel 还会向在线的本地目标转达停止通知);该行落入 `archived[]`。 |
159
+ | `POST /__dsh_a2a/tasks/abandon` · `/tasks/cancel` | 从调用方一侧结清一个欠账任务(cancel 还会尽力向目标转达停止通知:本机目标直接 steer,远端目标经 wire 投递,卡片声明 `queuedTaskCancellation` 时另带 `cancel_task_id` 撤回队列中的原任务;回复里带 `steered`、`removed`);该行落入 `archived[]`。 |
160
+ | `GET`/`POST /__dsh_a2a/superagent` | `superagent: true` 时:引擎状态(线程、决定、回复、带阶段的工单、绑定的画布及绑定问题),以及属主操作 `settings`(绑定 `canvas` 与 `router`)· `request`(工作台 Source)· `serve`(按画布 id 为其他主机的团队服务)· `stop` · `retry`。 |
172
161
  | `/__dsh_a2a/session-log` · `/remote-session-log` | 供工作台读取会话日志。 |
173
- | `/__dsh_a2a/owner-settings` | 属主面板:读取(GET)或写入(POST)`teamJoinAllowlist`、`teamScopeInfo`、`seatNames` 与 `announce` 到 home patch 层(除 `announce` 外即时生效)。`seatNames` 为完整映射,删去会话 id 即清除。复用属主鉴权,不提供模型命名工具;本机 shell/配置写入者仍处于既有运营者信任边界内。 |
162
+ | `/__dsh_a2a/owner-settings` | 属主面板:读取(GET)或写入(POST)`teamJoinAllowlist`、`teamScopeInfo`、`seatNames` 与 `announce` 到 home patch 层(`announce` 需重启生效,GET 的 `announceRestartPending` 标出;其余即时生效)。`seatNames` 为完整映射,删去会话 id 即清除。复用属主鉴权,不提供模型命名工具;本机 shell/配置写入者仍处于既有运营者信任边界内。 |
174
163
  | `/__dsh_a2a/fleet-canvas-op` | 把画布操作中继到某 peer 的 `/a2a/canvas-op`,盖上本节点的 OriginClaim;由对端的链接门裁决。 |
175
- | `/__dsh_a2a/cli-deposit` | `a2a-net` CLI 身份的寄存、列表、撤销与信箱读取。 |
176
- | `GET /__dsh_a2a/nodes` · `POST` | 节点身份(docs/design/node-identity.md):GET 读名册或单个节点(`?name=`);POST 是身份写入,即使在 loopback 也要 apiKey。 |
164
+ | `/__dsh_a2a/cli-deposit` | `a2a-net` CLI 身份的寄存、列表、撤销、信箱读取与准入查询(`admission`,须由该身份自己签名)。 |
165
+ | `GET /__dsh_a2a/nodes` · `POST` | 节点身份(docs/design/node-identity.md):GET 读名册或单个节点(`?name=`);POST 是身份写入,即使在 loopback 也要 `X-API-Key`;未配置 `apiKey` 的主机直接拒绝一切身份写入。 |
177
166
  | `POST /__dsh_a2a/blob` | 为接收方暂存一个文件(`?to=&name=`)——文件通道的写入面;对端经 `GET /a2a/blob` 拉取。 |
178
167
 
179
- 状态面的账本:`tasks`(pending,每行带 `settleable`)、`tasksStaleCount`、`tasksDead`(已知时带 `direction`),以及 `archived`——最新 64 条已结清行,最新在前,每行带 `outcome`、`endedBy`(`receipt` | `echo` | `abandoned` | `cancelled`)、回执在调用方结束之后才到时带 `late: true`、已知时带 `direction`——另有 `archivedCount` 与 `archivedSplit`(按结局计数)。
168
+ 状态面的账本:`tasks`(pending,每行带 `settleable`)、`tasksStaleCount`、`tasksDead`(已知时带 `direction`),以及 `archived`——最新 64 条已结清行,最新在前,每行带 `outcome`、`endedBy`(`receipt` | `echo` | `abandoned` | `cancelled` | `evicted`)、回执在调用方结束之后才到时带 `late: true`、已知时带 `direction`——另有 `archivedCount` 与 `archivedSplit`(按结局计数)。
180
169
 
181
170
  ## 舞台(可视化面)
182
171
 
183
- 两个全页舞台与模型工具并列挂载:`/__dsh_a2a_nexus`(A2A 工作台——自 0.6.2 起 2D 空间规划板即舞台,目录/台账/活动/规划面板乘可折叠侧栏,合并为一个响应式外壳;canvas 面掉线时面板回落主区;Three.js 拓扑观测在 0.6.0 退役;`?mode=plan` 仍汇聚到规划板)与 `/__dsh_a2a_canvas`。不带斜杠的挂载路径会 301 重定向到带尾斜杠形式,两种写法都能渲染,外壳的相对资源总在服务树内解析。离线开发时,`nexus-stage/scripts/mockhost.py [port]` 用预置状态服务已构建的舞台;在其上打开 `/?fault=500` 可演练页内故障徽标。新检出的仓库上 `pnpm verify` 无需手工引导:其 `verify:nexus` 步骤会自行安装子包依赖再构建。
172
+ 全页工作台与模型工具并列挂载:`/__dsh_a2a_nexus`(A2A 工作台——自 0.6.2 起 2D 空间规划板即舞台,目录/主机/台账/活动/规划面板(宿主运行 superagent 团队时多一个「团队」页)乘可折叠侧栏,合并为一个响应式外壳;canvas 面掉线时面板回落主区;Three.js 拓扑观测在 0.6.0 退役;`?mode=plan` 仍汇聚到规划板)。不带斜杠的挂载路径会 301 重定向到带尾斜杠形式,两种写法都能渲染,外壳的相对资源总在服务树内解析。此前的办公室舞台 `/__dsh_a2a_canvas` 已移除。离线开发时,`nexus-stage/scripts/mockhost.py [port]`(默认 8780)服务已构建的舞台(`assets/nexusDist`),状态取自 `LIVE_STATE`(默认本机 3080 端口的 `/__dsh_a2a/state`),取不到就用空舰队;在其上打开 `/?fault=500` 可演练页内故障徽标,`/?demo=1` 合并一份演示联邦数据(2 个 peer、2 条在途路由),设 `LAYOUT_FILE=<路径>` 可让布局跨重启保留(默认只存内存)。`pnpm verify` 是包了一层「临时目录零残留」闸门的 `pnpm verify:core`;其中 `verify:nexus` 步骤会自行 `npm ci` 安装舞台子包依赖再构建,无需手工引导子包(仓库根的依赖仍需先 `pnpm install`)。
184
173
 
185
174
  ## 协作 SOP(网络实践,v0.1)
186
175
 
187
- > 现行规则见《A2A 协作规范》([`src/skill/references/collaboration.md`](src/skill/references/collaboration.md),随插件分发到每个节点)。下面的 v0.1 保留作历史,冲突处以新规范为准。
176
+ > 现行规则见《A2A 协作规范》([`src/skill/references/collaboration.md`](src/skill/references/collaboration.md),随插件分发到每个节点)。下面的 v0.1 保留作历史,冲突处以新规范为准;每条后面标明现行规范(v1.18)是否已取代它。
188
177
 
189
178
  由网络研究节点从 0.1.x–0.5.x 交付周期蒸馏;维护者采纳。
190
179
 
191
- - **角色** —— 一个会话一个职能,不越工作区边界:研究节点(协议/设计评审、裁决标准、带 must-fix/recommend 标签的源码行引用)、维护者节点(版本裁决、发布、采纳回执)、测试节点(所有活体验证的唯一执行点;生产宿主绝不用于临时测试)、安装/运维节点(生产 profile 部署、单一 owner、等待维护窗口)。
192
- - **派发纪律** —— 测试任务发测试节点,带版本 + 提交 + 可判定标准 + 回执目标;评审任务发研究节点,带源码指针而非转述。
193
- - **回执契约** —— 超过分钟级的任务用 `async: true`(wait:false)路由;回执读作 `[A2A receipt] task <task_id> <outcome summary>`;task id 由调用方出生、请求携带、peer 回显、steering 头传输——四方一致才关联回执。
194
- - **升级链** —— 测试节点绿 → 维护者回执 → 运维节点申请窗口 → 生产升级 → 维护者浏览器面终检 → 全网通告。
195
- - **复用红线** —— 共享纯逻辑提取库;共享运行时能力提取 Service;插件之间禁止值 import;生产宿主不承担验证职责(禁止开发者自测)。
180
+ - **角色**(已被取代:现行规范 v1.17 起一个项目至多两个角色——开发做、评审审,不再单设研究、维护、测试、运维坐席,见规范第 1 节)—— 一个会话一个职能,不越工作区边界:研究节点(协议/设计评审、裁决标准、带 must-fix/recommend 标签的源码行引用)、维护者节点(版本裁决、发布、采纳回执)、测试节点(所有活体验证的唯一执行点;生产宿主绝不用于临时测试)、安装/运维节点(生产 profile 部署、单一 owner、等待维护窗口)。
181
+ - **派发纪律**(部分沿用:送审要写清提交 SHA、完成标准和输入的路径或链接、不贴内容,见规范第 6 节;「测试任务发测试节点」「评审任务发研究节点」已取代——测试归评审)—— 测试任务发测试节点,带版本 + 提交 + 可判定标准 + 回执目标;评审任务发研究节点,带源码指针而非转述。
182
+ - **回执契约** —— 超过分钟级的任务用 `async: true`(wait:false)路由;回执读作 `[A2A receipt] task <task_id> <outcome summary>`;task id 由调用方出生、随请求携带、由 peer 回显、经 steering 头传输;回执凭这个 id 关联到本端欠账行(老 peer 不回显时调用方回落到自己出生的 id,所以不要求四方都一致),关联后还要过结算授权判定。
183
+ - **升级链**(已被取代:现行规范没有先授权、维护窗口和全网通告;高档事只需一个非自我节点对同一提交 SHA 的评审 PASS,PASS 后自己合并、执行,见规范第 4、7 节)—— 测试节点绿 → 维护者回执 → 运维节点申请窗口 → 生产升级 → 维护者浏览器面终检 → 全网通告。
184
+ - **复用红线**(部分有执行点:「插件之间禁止值 import」由客户端 bundle 的纯度闸门强制,`tsdown.config.ts` 对非平台的 `@deepseek-ai/*` 值 import 直接报构建错误;「生产宿主不承担验证职责」在现行规范里没有对应条文,只是历史说法)—— 共享纯逻辑提取库;共享运行时能力提取 Service;插件之间禁止值 import;生产宿主不承担验证职责(禁止开发者自测)。
196
185
 
197
186
  ## 验证
198
187
 
199
188
  测试按深度分层(契约见 `docs/testing-layers.md`):每层独立目录 + 独立脚本,可按层迭代。
200
189
 
201
190
  ```sh
202
- pnpm install # auto-install-peers 从 npm 拉取 DSH peer 线
203
- pnpm verify # typecheck + 构建 + 下列全部 vitest 层 + 布局门禁 + nexus-stage 子套件
204
- pnpm test:unit # L0 —— 纯逻辑,无 fiber、无 socket(store/codec/jsdom 组件)
191
+ pnpm install # auto-install-peers 从 npm 拉取 DSH peer 线;`prepare` 会顺带跑一次 pnpm build
192
+ pnpm verify # 包在「临时目录零残留」闸门里的 verify:core:typecheck + verify:docs(文档漂移)+ 构建
193
+ # + pnpm test(L0–L3 全部 vitest 层;L4 无 env 时跳过)+ 布局门禁 + nexus-stage 子套件
194
+ pnpm test # L0–L3 全部 vitest 层(含 gates)
195
+ pnpm test:unit # L0 —— 纯逻辑,通常无 fiber、无 socket(store/codec/jsdom 组件);少数例外:
196
+ # cli-args / cli-receipt / cli-thread 起本机回环 HTTP 服务测真实 CLI,
197
+ # receipt-retry / superagent-role-tools-scope 建裸 cordis Context
205
198
  pnpm test:wire # L1 —— 单个真实插件实例 + 真实 HTTP,对端 peer 为 fake
206
199
  pnpm test:mesh # L2 —— ≥2 个真实节点(独立 home/密钥/peers.json)走真实 HTTP:
207
200
  # 跨机器等价层,含故障注入(时钟偏差卡片、篡改卡片、分区/重启、
208
201
  # 别名塌缩、zone 委托、referral 冻结)与回执重放——故障矩阵见
209
202
  # docs/testing-layers.md
203
+ pnpm test:build-layout # L3 —— 产物契约(exports 指向的构建布局存在性),不是行为测试
210
204
  pnpm test:live # L4 —— 真实远端机器,env 门控(DSH_A2A_LIVE_MESH=...),默认跳过、
211
205
  # 不进 CI——运行手册见 docs/testing-layers.md
212
- pnpm build # host lib(含 lib/a2a-client.js)、client 类型与浏览器 bundle(lib/client.js)
206
+ pnpm build # host lib(含 lib/a2a-client.js)、client 类型、浏览器 bundle(lib/client.js)与随包技能(lib/skill/)
213
207
  ```
214
208
 
209
+ 注意:`tsconfig.json` 的 `paths` 优先指向与本仓**同级**的 `../deepseek-harness` 检出(`vendor/`、`packages/`)来取类型;没有这份同级检出时 typecheck/构建回落到 npm 上的 DSH 类型,而 npm 快照可能落后于本插件所对照的源码(见「安装」一节的说明)。
210
+
215
211
  ## 非 DSH 节点互通(协议模块)
216
212
 
217
213
  不运行 DeepSeek Harness 的节点也能接入本协议。四个零依赖的协议模块以子路径导出(gh#13),引入时不会拉起 DSH 的 peer 依赖:
@@ -223,12 +219,12 @@ import { withinRouteBodyCap } from '@nelsonlongxiang/dsh-open-a2a-net/transport-
223
219
  import { A2aClient } from '@nelsonlongxiang/dsh-open-a2a-net/a2a-client'
224
220
  ```
225
221
 
226
- 外部节点需要自己实现三样东西:签名卡片、`GET /.well-known/agent-card.json` 和 `POST /a2a/direct`,这些模块编码的线上格式就是契约。匿名取卡只能拿到最小发现面,**不带 `peers`**(#312)。引荐列表要求 OriginClaim 验签通过,且调用方是本机 config.peers 显式种下的对端(根地址按 origin,挂载地址精确匹配),或原 operator link 已成立。无关系的自签名陌生人拿不到拓扑;显式 seed 可在双边完整名片链接形成前读取引荐。会话、roster、组织字段仍须原完整名片链接门。带签名的 `?discovery=1` 只请求核心与引荐,用于有限次自举,避免递归获取完整卡片。仅匿名取卡的旧爬取者须显式配置 seed 或升级签名请求;缺少 peers 仍是合法签名卡片,委派记录不变。要拿完整卡片,必须带上通过验证的 OriginClaim 信封(即用你自己的卡片密钥签名的 `X-A2A-Caller` / `-Url` / `-Ts` / `-Sig`),**并且**与提供卡片的宿主有链接:你的卡片 URL 与它同源、你的句柄是它的已加入会话句柄之一、它的操作者把你的精确句柄加成了画布远端成员,或双方操作者互相种了对方(它的 `peers` 列有你的源,你卡片的 `peers` 也列有它的源)。验签通过但没有链接的调用方仍只拿到最小卡片。细节见 `docs/protocol/delivery-origin-auth.md` 及 `docs/protocol/` 下其余文档。
222
+ 外部节点需要自己实现三样东西:签名卡片、`GET /.well-known/agent-card.json` 和 `POST /a2a/direct`,这些模块编码的线上格式就是契约。匿名取卡只能拿到最小发现面,**不带 `peers`**(#312)。引荐列表要求 OriginClaim 验签通过,且调用方是本机 config.peers 显式种下的对端(根地址按 origin,挂载地址精确匹配),或原 operator link 已成立。无关系的自签名陌生人拿不到拓扑;显式 seed 可在双边完整名片链接形成前读取引荐。会话、roster、组织字段仍须原完整名片链接门。带签名的 `?discovery=1` 只请求核心与引荐,用于有限次自举,避免递归获取完整卡片。仅匿名取卡的旧爬取者须显式配置 seed 或升级签名请求;缺少 peers 仍是合法签名卡片,委派记录不变。要拿完整卡片,必须带上通过验证的 OriginClaim 信封(即用你自己的卡片密钥签名的 `X-A2A-Caller` / `-Url` / `-Ts` / `-Sig`;这是不带受众的旧信封,宿主只在 `originClaimUnboundWindow` 打开期间接受,默认仍打开,窗口关闭后必须改用绑定受众的形式——另带 `X-A2A-Caller-Aud` 与 `X-A2A-Caller-Sig2`;带请求体的 POST 还应带 `X-A2A-Caller-Sig3`(链路 key、转发回执结算、superagent 的签名面都要求它),见 `docs/protocol/delivery-origin-auth.md` §2.1),**并且**与提供卡片的宿主有链接:你的卡片 URL 与它同源、你的句柄是它的已加入会话句柄之一、它的操作者把你的精确句柄加成了画布远端成员,或双方操作者互相种了对方(它的 `peers` 列有你的源,你卡片的 `peers` 也列有它的源)。验签通过但没有链接的调用方仍只拿到最小卡片。细节见 `docs/protocol/delivery-origin-auth.md` 及 `docs/protocol/` 下其余文档。
227
223
 
228
- **按哪个产物实现。** npm 直装已含四个子路径:自 0.6.25 起(徽章 `latest`)的正式版带齐这批导出,`npm install @nelsonlongxiang/dsh-open-a2a-net` 开箱即用——`examples/external-node/` 有一个可对内嵌 mock peer 跑通全部四个子路径的外部节点参考实现。子路径导出随提交 `9cb3852` 落地;更早的版本(含公开 0.5.15)早于这批导出,也早于上述准入规则和 OriginClaim 信封,从它们引入会报 `ERR_PACKAGE_PATH_NOT_EXPORTED`——历史上的做法(钉提交、`pnpm build`、`npm pack`、依赖 tarball)仅留作 0.6.25 之前部署的历史注:
224
+ **按哪个产物实现。** npm 直装已含四个子路径:npm 上自 0.6.24 起的发布版都带齐这批导出(0.5.15 与 0.6.24 之间没有发布版),当前 `latest` 为 0.7.0,`npm install @nelsonlongxiang/dsh-open-a2a-net` 开箱即用——`examples/external-node/` 有一个可对内嵌 mock peer 跑通全部四个子路径的外部节点参考实现。子路径导出随提交 `9cb3852` 落地(0.6.24 已含),完整卡片的链接门自 0.6.25 起;0.5.15 及更早的发布版没有这批导出,也早于 OriginClaim 信封和上述准入规则,从它们引入会报 `ERR_PACKAGE_PATH_NOT_EXPORTED`——历史上的做法(钉提交、`pnpm build`、`npm pack`、依赖 tarball)仅留作需要从源码构建时的历史注(脱离 DSH profile 的 Git 构建受「安装」一节所述限制,本节的做法未在当前主干上重跑):
229
225
 
230
226
  ```bash
231
- # 历史做法(0.5.15 及更早):从钉住的提交自行构建
227
+ # 历史做法(没有带导出的发布版时):从钉住的提交自行构建
232
228
  git clone https://github.com/NelsonLongxiang/dsh-open-a2a-net && cd dsh-open-a2a-net
233
229
  git checkout <commit> # 钉一个提交;9cb3852 及之后的提交都导出这四个子路径
234
230
  pnpm install --frozen-lockfile # pnpm 版本以 package.json 的 "packageManager" 为准
@@ -238,8 +234,10 @@ npm pack # → nelsonlongxiang-dsh-open-a2a-net-<versi
238
234
  npm install /path/to/nelsonlongxiang-dsh-open-a2a-net-<version>.tgz
239
235
  ```
240
236
 
241
- 安装这个包(npm 或历史 tarball)不会拉入任何 `@deepseek-ai/*` 包:DSH peer 依赖都是可选的,四个子路径只互相引用,外加 `node:crypto`。
237
+ 安装这个包(npm 或历史 tarball)不会拉入任何 `@deepseek-ai/*` 包:DSH peer 依赖都是可选的,四个子路径只互相引用(连同它们引用的内部辅助模块,对 `@deepseek-ai/*` 只有编译后会被擦除的类型引用),外加 `node:crypto`。
242
238
 
243
239
  ## 许可证
244
240
 
245
- MIT
241
+ 本包以 MIT 许可发布,见仓库根的 `LICENSE`;第三方内容见 `THIRD_PARTY_NOTICES.md`,各许可文本在 `docs/licenses/`,三者都随 npm 包分发(#470)。
242
+
243
+ superagent 引擎是原样引入的上游代码(MIT,© DeepSeek Harness Team)。
@@ -0,0 +1,12 @@
1
+ # Third-party notices
2
+
3
+ This package is MIT-licensed (see `LICENSE`). It also contains the following third-party work. Its licence text ships with
4
+ the package under `docs/licenses/`.
5
+
6
+ | Component | Where | Licence | Text |
7
+ |---|---|---|---|
8
+ | [dsh-superagent](https://github.com/deepseek-ai/dsh-superagent-prototype) domain engine (© 2026 DeepSeek Harness Team), vendored unchanged | `src/superagent/upstream/` → `lib/superagent/upstream/` | MIT | `docs/licenses/dsh-superagent-MIT.txt` |
9
+
10
+ Releases up to and including 0.7.0, and git history before the office stage was removed, also contain the office stage
11
+ adapted from [Munder Difflin](https://github.com/chaitanyagiri/munder-difflin) (MIT, © 2026 Chaitanya Giri), with parts from
12
+ [the-office](https://github.com/shahar061/the-office) (ISC, © 2026 shahar061) and tilesets by [LimeZu](https://limezu.itch.io/).
@@ -0,0 +1 @@
1
+ #dsh-plan *,#dsh-plan *:before,#dsh-plan *:after{box-sizing:border-box}#dsh-plan{--uupm-ink-0:var(--n-ink,#dde3f0);--uupm-ink-1:var(--n-ink-muted,#7a86a0);--uupm-bg-0:var(--n-bg,#060a12);--uupm-bg-1:var(--n-panel,#0d1322);--uupm-bg-2:var(--n-raised,#131b2e);--uupm-accent:var(--n-accent,#9673f7);--uupm-live:var(--n-live,#4ade80);--uupm-cold:var(--n-cold,#7a86a0);--uupm-edge-member:var(--n-canvas,#22d3ee);--uupm-warn:var(--n-act,#f59e0b);--uupm-error:var(--n-danger,#f87171);--uupm-indigo:var(--n-federal,#6366f1);--uupm-wash-5:color-mix(in srgb,var(--uupm-accent) 5%,transparent);--uupm-wash-8:color-mix(in srgb,var(--uupm-accent) 8%,transparent);--uupm-wash-12:color-mix(in srgb,var(--uupm-accent) 12%,transparent);--uupm-wash-15:color-mix(in srgb,var(--uupm-accent) 15%,transparent);--uupm-wash-18:color-mix(in srgb,var(--uupm-accent) 18%,transparent);--surface:var(--uupm-bg-1);--ink:var(--uupm-ink-0);--ink-muted:var(--uupm-ink-1);--accent:var(--uupm-accent);--edge-live:var(--uupm-live);--edge-cold:var(--uupm-cold);--federal:var(--uupm-indigo);--card-bg:var(--surface);--card-line:var(--n-card-line,#5c6680);--card-line-hover:var(--uupm-ink-1);--toolbar-bg:color-mix(in srgb,var(--uupm-bg-1) 92%,transparent);--grid-dot:color-mix(in srgb,var(--uupm-ink-1) 22%,transparent);--scrim:color-mix(in srgb,var(--uupm-bg-0) 60%,transparent);--error-ink:var(--n-error-ink,#fecaca);--error-panel:var(--n-error-panel,#2a0d10f2)}#dsh-plan{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden;background:var(--uupm-bg-0);color:var(--ink);font-size:13px;line-height:1.5;outline:none}#dsh-plan .mono{font-family:ui-monospace,Cascadia,Consolas,monospace}#dsh-plan:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}#dsh-plan .p-grid{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0;pointer-events:none;background-image:radial-gradient(circle,var(--grid-dot) 1px,transparent 1px);background-size:24px 24px;background-position:0 0}#dsh-plan .p-world{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;transform-origin:0 0}#dsh-plan .p-layer{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}#dsh-plan .p-edges{position:absolute;overflow:visible;width:100%;height:100%;pointer-events:none}#dsh-plan .p-frame{pointer-events:none}#dsh-plan .p-frame-head,#dsh-plan .p-node{pointer-events:auto}#dsh-plan .e-member{stroke:var(--uupm-edge-member);stroke-opacity:.38;stroke-width:1.5}#dsh-plan .e-member.dashed{stroke-dasharray:3 4}#dsh-plan .p-frame{--frame-ink:var(--frame-line,#8B5CF6)}@media (prefers-color-scheme: light){#dsh-plan .p-frame{--frame-ink:color-mix(in srgb,var(--frame-line,#8B5CF6) 72%,black)}}#dsh-plan .p-frame{position:absolute;border:1px solid var(--frame-ink);border-radius:10px;background:var(--frame-bg,var(--uupm-wash-5));min-width:120px;min-height:60px}#dsh-plan .p-frame-head{position:absolute;top:-13px;left:50%;transform:translate(-50%);height:24px;padding:0 10px;border-radius:6px;display:flex;gap:8px;align-items:center;background:var(--uupm-bg-2);border:1px solid var(--frame-ink);font-size:12px;font-weight:600;letter-spacing:.02em;white-space:nowrap;cursor:grab}#dsh-plan .p-frame-head:active{cursor:grabbing}#dsh-plan .p-frame-head:hover{border-color:var(--card-line-hover)}#dsh-plan .p-frame-head .cnt{color:var(--ink-muted);font-weight:400}#dsh-plan .p-frame-head .route{font-size:10.5px;color:var(--ink-muted)}#dsh-plan .p-frame.is-mirror{border-style:dashed}#dsh-plan .p-frame.is-roster{border-style:double;border-width:3px}#dsh-plan .p-frame.is-roster .p-frame-head{border-style:dashed}#dsh-plan .p-frame.is-syncing .p-frame-head .route{color:var(--accent)}#dsh-plan .p-frame-head .sa-team{font-size:10.5px;padding:0 6px;border-radius:8px;border:1px solid var(--accent);color:var(--accent);white-space:nowrap}#dsh-plan .p-node>.sa-role{order:1;align-self:flex-start;max-width:calc(100% + 26px);margin-top:2px;font-size:10.5px;line-height:14px;padding:0 5px;border-radius:6px;border:1px solid var(--ink-muted);color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-node.is-superagent-router>.sa-role{border-color:var(--accent);color:var(--accent)}#dsh-plan .p-node .sub-row{order:2}#dsh-plan .p-node .sub-row .work{margin-left:auto}#dsh-plan .p-node.remote.is-superagent-member .prio{display:none}#dsh-plan .p-frame.is-unreachable{opacity:.76;border-style:dashed}#dsh-plan .p-frame-unreachable{position:absolute;left:8px;top:20px;max-width:calc(100% - 16px);color:var(--uupm-error);font-size:11px;pointer-events:none}#dsh-plan .p-node.remote.is-unreachable{opacity:.72;border-left-color:var(--uupm-error)}#dsh-plan .p-node.remote.is-unreachable .sub{color:var(--uupm-error)}#dsh-plan .p-node{position:absolute;width:172px;height:68px;padding:0 38px 0 14px;display:flex;flex-direction:column;justify-content:center;background:var(--card-bg);border:1px solid var(--card-line);border-left:3px solid var(--edge-live);border-radius:8px;cursor:grab;-webkit-user-select:none;user-select:none;transition:border-color .12s cubic-bezier(.2,0,0,1),transform .12s cubic-bezier(.2,0,0,1)}#dsh-plan .p-node:hover{border-color:var(--card-line-hover)}#dsh-plan .p-node:active{cursor:grabbing}#dsh-plan .p-node:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-node.selected{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-node.dragging{transform:scale(1.02);border-color:var(--accent)}#dsh-plan .p-node.cold{border-left-color:var(--edge-cold)}#dsh-plan .p-node .nm{font-weight:600;display:flex;align-items:center;gap:7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-node .nm-text{min-width:0;overflow:hidden;white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.2;word-break:break-all}#dsh-plan .p-node.remote .nm-text{overflow-wrap:anywhere}#dsh-plan .p-node .dot{width:8px;height:8px;border-radius:50%;background:var(--edge-live);flex:none}#dsh-plan .p-node.cold .dot{background:transparent;border:1.5px solid var(--edge-cold)}#dsh-plan .p-node .sub-row{display:flex;align-items:baseline;gap:6px;margin:2px -12px 0 0;min-width:0}#dsh-plan .p-node .sub-row .sub{flex:1 1 auto;min-width:0;margin-top:0}#dsh-plan .p-node .work{flex:none;font-size:10.5px;font-weight:500;color:var(--ink-muted);white-space:nowrap}#dsh-plan .p-node .work:empty{display:none}#dsh-plan .p-node.is-working .work{color:var(--edge-live)}#dsh-plan .p-node.is-working .dot{animation:p-working 1.4s ease-in-out infinite}@keyframes p-working{0%,to{box-shadow:0 0 color-mix(in srgb,var(--edge-live) 55%,transparent)}50%{box-shadow:0 0 0 4px transparent}}@media (prefers-reduced-motion: reduce){#dsh-plan .p-node.is-working .dot{animation:none}}#dsh-plan .p-node .sub{font-size:11px;color:var(--ink-muted);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-node.pulse{box-shadow:0 0 0 2px var(--accent),0 0 14px 4px var(--uupm-wash-15);border-color:var(--accent)}#dsh-plan .p-search{position:relative}#dsh-plan .p-search-input{width:170px;min-height:28px;padding:2px 8px;font-size:12px;background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px}#dsh-plan .p-search-input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-search-list{position:absolute;top:100%;left:0;margin-top:4px;display:none;min-width:240px;max-height:220px;overflow:auto;z-index:9;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:6px}#dsh-plan .p-search-hit{padding:5px 10px;font-size:12px;color:var(--ink);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#dsh-plan .p-search-hit:hover,#dsh-plan .p-search-hit.active{background:var(--uupm-wash-15)}#dsh-plan .p-node .prio{position:absolute;top:6px;right:8px;font-size:10px;color:var(--ink-muted);border:1px solid var(--card-line);border-radius:4px;padding:0 5px}#dsh-plan .p-marquee{position:absolute;z-index:2;display:none;pointer-events:none;border:1px solid var(--accent);background:var(--uupm-wash-8)}#dsh-plan .p-toolbar{position:absolute;top:16px;left:16px;z-index:3;display:flex;gap:6px;align-items:center;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:6px 8px}#dsh-plan .p-tabs{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:3;display:flex;gap:4px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:4px}#dsh-plan .p-tabs [role=tab]{background:transparent;color:var(--ink-muted);border:1px solid transparent;border-radius:6px;padding:4px 12px;font:inherit;cursor:pointer}#dsh-plan .p-tabs [role=tab]:hover{color:var(--ink);border-color:var(--card-line)}#dsh-plan .p-tabs [role=tab][aria-selected=true]{color:var(--ink);border-color:var(--accent);background:var(--uupm-wash-12)}#dsh-plan .p-tabs [role=tab]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-toolbar button{background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px;padding:4px 10px;font:inherit;cursor:pointer;white-space:nowrap}#dsh-plan .p-toolbar button:hover{border-color:var(--accent)}#dsh-plan .p-toolbar button:active{background:var(--uupm-wash-15)}#dsh-plan .p-toolbar button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-zoom{min-width:44px;text-align:center;font-size:11px;color:var(--ink-muted)}#dsh-plan .p-sep{align-self:stretch;width:1px;margin:2px 4px;background:var(--card-line)}#dsh-plan .p-lamp{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-muted);padding:0 6px}#dsh-plan .p-lamp i{width:7px;height:7px;border-radius:50%;background:var(--ink-muted);transition:background .14s ease}#dsh-plan .p-lamp.saved i{background:var(--edge-live)}#dsh-plan .p-lamp.pending i{background:var(--uupm-warn)}#dsh-plan .p-lamp.error{color:var(--uupm-error);cursor:pointer}#dsh-plan .p-lamp.error i{background:var(--uupm-error)}#dsh-plan .p-lamp.error:hover{border:1px solid var(--uupm-error);border-radius:4px}#dsh-plan .p-status{position:absolute;top:16px;right:16px;z-index:3;display:flex;gap:14px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:8px 14px;font-size:12px}#dsh-plan .p-status b{font-weight:600}#dsh-plan .ok{color:var(--edge-live)}#dsh-plan .warn{color:var(--uupm-warn)}#dsh-plan .mut{color:var(--ink-muted)}#dsh-plan .p-legend{position:absolute;bottom:16px;left:16px;z-index:3;font-size:11px;color:var(--ink-muted);background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;padding:4px 10px}#dsh-plan .p-legend summary{cursor:pointer;padding:2px 0;-webkit-user-select:none;user-select:none}#dsh-plan .p-legend summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-legend[open]{padding-bottom:8px}#dsh-plan .p-legend-rows{display:grid;gap:4px;margin-top:4px}#dsh-plan .p-legend i{display:inline-block;width:18px;border-top:2px solid var(--uupm-edge-member);vertical-align:middle;margin-right:6px}#dsh-plan .p-legend i.dashed{border-top-style:dashed}@media (max-width:680px){#dsh-plan .p-toolbar{position:relative;top:auto;left:auto;margin:8px 8px 0;flex-wrap:wrap;row-gap:6px}#dsh-plan .p-toolbar>*{flex:none}#dsh-plan .p-toolbar .p-search{order:-1;flex:1 1 100%}#dsh-plan .p-toolbar .p-sep{display:none}#dsh-plan .p-toolbar .p-search-input{width:100%;box-sizing:border-box}#dsh-plan .p-status{position:relative;top:auto;right:auto;left:auto;width:fit-content;margin:6px 8px 0;padding:6px 10px;gap:10px;flex-wrap:wrap}#dsh-plan .p-legend{display:none}}@media (pointer:coarse){#dsh-plan .p-toolbar button{min-height:36px;min-width:36px}}@media (prefers-reduced-motion: reduce){#dsh-plan *{transition:none!important;animation:none!important}}#dsh-plan .p-frame.drop-target{border-color:var(--accent);border-width:2px;background:var(--uupm-wash-12)}#dsh-plan .p-frame-head:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-node.remote{border-left-color:var(--federal);padding-right:12px}#dsh-plan .p-node.remote .sub-row{margin-right:0}#dsh-plan .p-node.remote>.sa-role{max-width:100%}#dsh-plan .p-node.remote:not(.is-superagent-member) .sub-row{padding-right:58px}#dsh-plan .p-node.remote:not(.is-superagent-member) .prio{top:auto;bottom:6px}#dsh-plan .p-node.remote:not(.is-superagent-member) .sub{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.25}#dsh-plan .p-node.remote .dot{background:var(--federal)}#dsh-plan .p-node.remote .prio{color:var(--ink)}#dsh-plan .p-node .prio.lnk-linked{color:var(--live, #2e9e5b);border-color:var(--live, #2e9e5b)}#dsh-plan .p-node .prio.lnk-stranger{color:var(--ink-muted);border-color:var(--card-line)}#dsh-plan .e-activity{stroke:var(--accent);stroke-width:2;stroke-dasharray:8 6;stroke-opacity:.9}#dsh-plan .e-federal{stroke:var(--federal);stroke-width:1.5;stroke-dasharray:6 5;stroke-opacity:.5}#dsh-plan .edge-label{font-size:10.5px;fill:var(--ink-muted)}@media (prefers-reduced-motion: no-preference){#dsh-plan .e-activity{animation:p-activity-flow 1.1s linear infinite}}@keyframes p-activity-flow{to{stroke-dashoffset:-28}}#dsh-plan .p-status{cursor:pointer}#dsh-plan .p-status:hover{border-color:var(--accent)}#dsh-plan .p-status:active{background:var(--uupm-wash-12)}#dsh-plan .p-status:focus-visible{outline:2px solid var(--accent);outline-offset:2px}#dsh-plan .p-menu{position:fixed;z-index:5;min-width:170px;display:grid;padding:4px;gap:2px;background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px}#dsh-plan .p-menu [role=menuitem]{background:transparent;color:var(--ink);border:1px solid transparent;border-radius:6px;padding:6px 10px;font:inherit;text-align:left;cursor:pointer;white-space:nowrap}#dsh-plan .p-menu [role=menuitem]:hover{border-color:var(--card-line);background:var(--uupm-wash-8)}#dsh-plan .p-menu [role=menuitem]:active{background:var(--uupm-wash-18)}#dsh-plan .p-menu [role=menuitem]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-menu [role=menuitem][aria-disabled=true]{color:var(--ink-muted);cursor:not-allowed}#dsh-plan .p-dialog{position:absolute;top:0;right:0;bottom:0;left:0;z-index:6;display:grid;place-items:center;background:var(--scrim)}#dsh-plan .p-dialog-panel{min-width:300px;padding:14px 16px;display:grid;gap:10px;background:var(--uupm-bg-2);border:1px solid var(--card-line);border-radius:10px}#dsh-plan .p-dialog-title{font-weight:600;font-size:12.5px;color:var(--ink)}#dsh-plan .p-dialog-input{font:inherit;color:var(--ink);background:var(--uupm-bg-0);border:1px solid var(--card-line);border-radius:6px;padding:7px 10px}#dsh-plan .p-dialog-input:hover{border-color:color-mix(in srgb,var(--uupm-ink-1) 55%,transparent)}#dsh-plan .p-dialog-input:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}#dsh-plan .p-dialog-err{min-height:1.2em;font-size:11px;color:var(--uupm-error)}#dsh-plan .p-dialog-kind{display:grid;grid-template-columns:1fr 1fr;gap:4px;padding:3px;background:var(--uupm-bg-0);border:1px solid var(--card-line);border-radius:8px}#dsh-plan .p-dialog-kind button{font:inherit;color:var(--ink-muted);background:transparent;border:1px solid transparent;border-radius:6px;padding:6px 10px;cursor:pointer}#dsh-plan .p-dialog-kind button:hover{color:var(--ink);background:var(--uupm-wash-8)}#dsh-plan .p-dialog-kind button[aria-checked=true]{color:var(--ink);font-weight:600;background:var(--uupm-wash-15);border-color:var(--accent)}#dsh-plan .p-dialog-kind button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-dialog-hint{font-size:11px;color:var(--ink-muted);max-width:340px}#dsh-plan .p-dialog-row{display:flex;gap:8px;justify-content:flex-end}#dsh-plan .p-dialog-row button{background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:6px;padding:5px 12px;font:inherit;cursor:pointer}#dsh-plan .p-dialog-row button:hover{border-color:var(--accent)}#dsh-plan .p-dialog-row button:active{background:var(--uupm-wash-15)}#dsh-plan .p-dialog-row button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-notice-stack{position:absolute;bottom:52px;left:50%;transform:translate(-50%);z-index:5;display:grid;gap:6px;justify-items:center;pointer-events:none}#dsh-plan .p-notice{max-width:60ch;padding:7px 12px;font-size:12px;color:var(--ink);background:var(--toolbar-bg);border:1px solid var(--card-line);border-radius:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#dsh-plan .p-notice.error{color:var(--error-ink);border-color:var(--uupm-error);background:var(--error-panel)}#dsh-plan .p-netbtn{color:var(--ink-muted)}#dsh-plan .p-netbtn:not([disabled]){color:var(--ink)}#dsh-plan .p-netmenu{max-height:240px;overflow:auto}#dsh-plan .p-nodebtn{color:var(--ink)}#dsh-plan .p-nodelist{min-width:300px;max-width:340px;max-height:420px;overflow:auto;padding:6px;grid-template-columns:minmax(0,1fr)}#dsh-plan .p-nodelist-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:2px 4px 6px;border-bottom:1px solid var(--card-line)}#dsh-plan .p-nodelist-title{font-size:12px;font-weight:600;color:var(--ink)}#dsh-plan .p-nodesel{font:inherit;font-size:11.5px;color:var(--ink);background:var(--uupm-bg-0);border:1px solid var(--card-line);border-radius:6px;padding:2px 6px;max-width:220px}#dsh-plan .p-nodesel:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-nodelist-group{font-size:11px;color:var(--ink-muted);padding:8px 4px 3px;display:flex;align-items:baseline;gap:8px}#dsh-plan .p-nodelist-note{font-size:10px;color:var(--ink-muted);opacity:.8}#dsh-plan .p-noderow{display:flex;align-items:center;gap:6px;padding:3px 4px;border-radius:6px;font-size:12px;color:var(--ink)}#dsh-plan .p-noderow:hover{background:var(--uupm-wash-8)}#dsh-plan .p-nodedot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--edge-live)}#dsh-plan .p-nodedot.cold{background:var(--ink-muted)}#dsh-plan .p-nodedot.off{background:transparent;border:1px solid var(--ink-muted)}#dsh-plan .p-nodename{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#dsh-plan .p-nonet{flex:none;font-size:10px;color:var(--uupm-warn);border:1px solid var(--uupm-warn);border-radius:4px;padding:0 4px}#dsh-plan .p-teamcount{flex:none;font-size:10px;color:var(--ink-muted);border:1px solid var(--card-line);border-radius:4px;padding:0 4px}#dsh-plan .p-listbtn{flex:none;background:transparent;color:var(--ink);border:1px solid var(--card-line);border-radius:5px;padding:1px 7px;font:inherit;font-size:11px;cursor:pointer}#dsh-plan .p-listbtn:hover{border-color:var(--card-line-hover);background:var(--uupm-wash-8)}#dsh-plan .p-listbtn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}#dsh-plan .p-nodelist-empty{padding:10px 4px;font-size:11.5px;color:var(--ink-muted)}#dsh-plan .p-frame{position:absolute}#dsh-plan .p-frame-handle{position:absolute;pointer-events:auto;width:10px;height:10px;z-index:2;opacity:0;transition:opacity .12s ease}#dsh-plan .p-frame:hover .p-frame-handle{opacity:1}#dsh-plan .p-frame-handle:after{content:"";position:absolute;top:2px;right:2px;bottom:2px;left:2px;background:var(--frame-ink);border-radius:2px}#dsh-plan .p-h-n{top:-5px;left:calc(50% - 12px);width:24px;height:8px;cursor:ns-resize}#dsh-plan .p-h-s{bottom:-5px;left:calc(50% - 12px);width:24px;height:8px;cursor:ns-resize}#dsh-plan .p-h-e{right:-5px;top:calc(50% - 12px);width:8px;height:24px;cursor:ew-resize}#dsh-plan .p-h-w{left:-5px;top:calc(50% - 12px);width:8px;height:24px;cursor:ew-resize}#dsh-plan .p-h-ne{top:-5px;right:-5px;cursor:nesw-resize}#dsh-plan .p-h-nw{top:-5px;left:-5px;cursor:nwse-resize}#dsh-plan .p-h-se{bottom:-5px;right:-5px;cursor:nwse-resize}#dsh-plan .p-h-sw{bottom:-5px;left:-5px;cursor:nesw-resize}#dsh-plan .p-h-ne:after,#dsh-plan .p-h-sw:after{transform:rotate(45deg)}#dsh-plan .p-h-nw:after,#dsh-plan .p-h-se:after{transform:rotate(-45deg)}#dsh-plan .p-frame.selected .p-frame-handle{opacity:1}#dsh-plan .p-frame.selected{outline:1px solid var(--frame-ink)}:root{color-scheme:dark;--n-bg:#060a12;--n-ink:#dde3f0;--n-ink-muted:#7a86a0;--n-panel:#0d1322;--n-live:#4ade80;--n-cold:#7a86a0;--n-border:#223052;--n-act:#f59e0b;--n-danger:#f87171;--n-accent:#9673f7;--n-rail-w:300px;--n-canvas:#22d3ee;--n-line:var(--n-border);--n-raised:#131b2e;--n-federal:#6366f1;--n-card-line:#5c6680;--n-error-ink:#fecaca;--n-error-panel:#2a0d10f2;--n-shadow:rgba(0,0,0,.35)}@media (prefers-color-scheme: light){:root{--n-bg:#eef2fa;--n-ink:#1a2438;--n-ink-muted:#5a6580;--n-panel:#ffffff;--n-live:#157a37;--n-border:#c6d0e6;--n-act:#a84d08;--n-danger:#b91c1c;--n-accent:#6d28d9;--n-cold:#636c81;--n-canvas:#0e7490;--n-raised:#ffffff;--n-federal:#4f46e5;--n-card-line:#8a95ae;--n-error-ink:#b91c1c;--n-error-panel:#fef2f2f2;--n-shadow:rgba(26,36,56,.16)}}@media (min-width:1440px){:root{--n-rail-w:340px }}html,body,#app{height:100%;margin:0}body{background:var(--n-bg);color:var(--n-ink);font:13px/1.45 system-ui,Segoe UI,Roboto,PingFang SC,Microsoft YaHei,sans-serif}.wb{display:flex;flex-direction:column;height:100%}.wb-header{display:flex;align-items:center;gap:12px;padding:8px 14px;border-bottom:1px solid var(--n-border);background:var(--n-panel)}.wb-title{display:flex;align-items:center;gap:10px;font-weight:600;font-size:14px;white-space:nowrap}.wb-badge{font-size:11px;font-weight:400;color:var(--n-ink-muted);border:1px solid var(--n-border);border-radius:99px;padding:1px 8px}.wb-badge[hidden]{display:none}.wb-row-remote{margin-left:6px;padding:0 6px;border:1px solid currentColor;border-radius:99px;font-size:10.5px;color:var(--n-ink-muted);vertical-align:1px}.wb-header-stats{display:flex;gap:16px;font-size:12px;color:var(--n-ink-muted);flex-wrap:wrap;min-width:0}.wb-header-stats .wb-kv{display:flex;gap:6px}.wb-header-stats .wb-kv-k{color:var(--n-ink-muted)}.wb-header-stats .wb-kv-v{color:var(--n-ink)}.wb-header-ok{color:var(--n-ink-muted)}.wb-alert{border:1px solid currentColor;border-radius:99px;padding:0 9px;line-height:20px;white-space:nowrap;cursor:pointer;background:transparent;font:inherit}.wb-alert:hover{background:color-mix(in srgb,currentColor 12%,transparent)}.wb-alert:focus-visible{outline:2px solid currentColor;outline-offset:2px}.wb-alert-danger{color:var(--n-danger)}.wb-alert-warn{color:var(--n-act)}.wb-forensics{font-family:ui-monospace,Consolas,monospace;font-size:11px}.wb-icon-btn{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:8px;width:30px;height:30px;display:none;align-items:center;justify-content:center;font:inherit;font-size:14px;cursor:pointer;flex:none}.wb-icon-btn:hover{border-color:var(--n-accent)}.wb-owner-btn{display:inline-flex}.wb-menu{margin-left:0}.wb-rail-toggle{margin-left:auto}.wb.is-rail-collapsed .wb-rail{display:none}.wb-body{position:relative;display:grid;grid-template-columns:minmax(0,var(--n-rail-w)) minmax(0,1fr);flex:1;min-height:0}.wb-rail{border-right:1px solid var(--n-border);min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;background:color-mix(in srgb,var(--n-panel) 60%,var(--n-bg))}.wb-side-head{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--n-ink-muted);margin:10px 2px 4px}.wb-item{display:flex;gap:7px;align-items:center;width:100%;text-align:left;background:none;border:none;color:var(--n-ink);font:inherit;padding:3px 6px;border-radius:6px;cursor:pointer}.wb-item:hover{background:color-mix(in srgb,var(--n-accent) 14%,transparent)}.wb-dot{font-size:10px}.wb-dot-live{color:var(--n-live)}.wb-dot-cold{color:var(--n-cold)}.wb-dot-canvas{color:var(--n-canvas)}.wb-dot-remote{color:var(--n-act)}.wb-dir-tools{display:flex;flex-direction:column;gap:6px;padding:8px 6px 4px}.wb-search{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:4px 8px;font:inherit;font-size:12.5px;min-width:0}.wb-search:focus{outline:1px solid var(--n-accent)}.wb-filters{display:flex;flex-wrap:wrap;gap:4px}.wb-filter{background:none;border:1px solid var(--n-border);color:var(--n-ink-muted);border-radius:99px;padding:0 10px;min-height:24px;font:inherit;font-size:11.5px;cursor:pointer}.wb-filter:hover{border-color:var(--n-accent);color:var(--n-ink)}.wb-filter.is-active{border-color:var(--n-accent);color:var(--n-ink);background:color-mix(in srgb,var(--n-accent) 16%,transparent)}.wb-item-badge{flex:none;font-size:10.5px;color:var(--n-canvas);border:1px solid color-mix(in srgb,var(--n-canvas) 45%,transparent);border-radius:99px;padding:0 6px;line-height:17px;white-space:nowrap}.wb-item-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-member-row{display:flex;align-items:center;gap:8px;padding:3px 2px;border-radius:6px}.wb-member-row:hover{background:color-mix(in srgb,var(--n-accent) 10%,transparent)}.wb-member-dot{flex:none;font-size:11px;color:var(--n-ink-muted);min-width:44px}.wb-member-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-member-remove,.wb-member-submit{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:1px 8px;font:inherit;font-size:11.5px;cursor:pointer;flex:none}.wb-member-remove:hover:not(:disabled),.wb-member-submit:hover:not(:disabled){border-color:var(--n-danger)}.wb-member-submit:hover:not(:disabled){border-color:var(--n-accent)}.wb-member-add{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.wb-cli-home{display:flex;flex-wrap:wrap;gap:6px;padding:8px;margin:4px 0 10px;border:1px solid var(--n-border);border-radius:6px}.wb-cli-home [hidden]{display:none}.wb-cli-home .wb-hint{flex-basis:100%;margin-top:0}.wb-cli-home .wb-member-pick{min-width:0;max-width:100%}.wb-member-pick,.wb-member-manual{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:3px 8px;font:inherit;font-size:12px;min-width:0}.wb-member-pick{flex:1 1 150px}.wb-member-manual{flex:1 1 130px}.wb-member-pick:focus,.wb-member-manual:focus{outline:1px solid var(--n-accent)}.wb-hint{margin-top:10px;font-size:11px;color:var(--n-ink-muted);line-height:1.5}.wb-dispatch{margin-top:12px;border-top:1px dashed var(--n-border);padding-top:8px}.wb-dispatch-form{display:flex;flex-direction:column;gap:6px;margin-top:6px}.wb-dispatch-text{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:5px 8px;font:inherit;font-size:12px;min-height:52px;resize:vertical}.wb-dispatch-text:focus{outline:1px solid var(--n-accent)}.wb-dispatch-submit{align-self:flex-end;background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:2px 14px;font:inherit;font-size:11.5px;cursor:pointer}.wb-dispatch-submit:hover:not(:disabled){border-color:var(--n-accent)}.wb-dispatch-note{font-size:11px;color:var(--n-ink-muted);line-height:1.4;min-height:0}.wb-stage{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column}.wb-stage-board{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden}.wb-stage-board.is-hidden{display:none}.wb-data{display:flex;flex-direction:column;gap:14px;padding:10px 14px;overflow-y:auto;min-height:0}.wb-data.is-hidden{display:none}.wb-pane-head{font-size:12px;color:var(--n-ink-muted);margin:4px 0 8px}.wb-pane-subhead{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--n-ink-muted);margin:12px 0 6px}.wb-table,.wb-row{display:flex;gap:10px;align-items:baseline}.wb-row{font-family:ui-monospace,Consolas,monospace;font-size:12px;background:color-mix(in srgb,var(--n-panel) 55%,transparent);border:1px solid var(--n-border);border-radius:6px;padding:3px 8px;flex-wrap:wrap}.wb-row-team{color:var(--n-accent);min-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-row-meta{color:var(--n-ink-muted)}.wb-row-badges{color:var(--n-live)}.wb-row-dir{min-width:16px}.wb-row-dir.is-fail{color:var(--n-danger)}.wb-btn{background:none;border:1px solid var(--n-border);color:var(--n-ink);border-radius:6px;padding:2px 10px;font:inherit;font-size:12px;cursor:pointer;margin-left:auto}.wb-btn:hover:not(:disabled){border-color:var(--n-accent)}.wb-btn:disabled{opacity:.5;cursor:default}.wb-btn-primary{border-color:var(--n-accent);color:var(--n-ink)}.wb-action{display:flex;gap:10px;align-items:baseline;font-size:12.5px;padding:5px 8px;border-radius:6px;border:1px solid var(--n-border);margin-bottom:4px}.wb-action-high{border-color:var(--n-danger)}.wb-action-text{flex:1}.wb-draft{display:flex;flex-direction:column;gap:8px;max-width:560px;margin-top:6px}.wb-select,.wb-textarea{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);border-radius:8px;padding:6px 9px;font:inherit}.wb-textarea{resize:vertical}.wb-select:focus,.wb-textarea:focus{outline:1px solid var(--n-accent)}.wb-inspector{position:absolute;top:8px;right:8px;width:280px;max-height:calc(100% - 16px);overflow-y:auto;background:var(--n-panel);border:1px solid var(--n-border);border-radius:10px;padding:10px;z-index:20;box-shadow:0 8px 24px var(--n-shadow);display:none}.wb-inspector.is-open{display:block}.wb-inspector-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.wb-inspector-title{font-weight:600;font-size:12.5px}.wb-inspector .wb-icon-btn{display:inline-flex;width:24px;height:24px;font-size:12px}.wb-card{display:flex;flex-direction:column;gap:6px}.wb-kv{display:flex;gap:8px;font-size:12px}.wb-kv-k{color:var(--n-ink-muted);min-width:58px;flex:none}.wb-kv-v{word-break:break-all}.wb-muted{color:var(--n-ink-muted)}.wb-chat{position:absolute;top:8px;right:8px;bottom:8px;width:400px;max-width:calc(100% - 16px);display:flex;flex-direction:column;background:var(--n-panel);border:1px solid var(--n-border);border-radius:12px;z-index:22;box-shadow:0 8px 24px var(--n-shadow);overflow:hidden;transform:translate(calc(100% + 12px));transition:transform .18s ease;pointer-events:none}.wb-chat.is-open{transform:translate(0);pointer-events:auto}.wb-chat-head{display:flex;align-items:center;gap:8px;padding:10px 10px 10px 12px;border-bottom:1px solid var(--n-border);flex:none}.wb-chat-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--n-ink-muted)}.wb-chat-dot.is-live{background:var(--n-live)}.wb-chat-dot.is-cold{background:transparent;border:1.5px solid var(--n-ink-muted)}.wb-chat-dot.is-peer{background:var(--n-federal)}.wb-chat-titles{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;line-height:1.3}.wb-chat-title{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-chat-sub{font:11px/1.4 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-chat .wb-icon-btn{display:inline-flex;width:28px;height:28px;font-size:13px;flex:none;border-color:transparent}.wb-chat .wb-icon-btn:hover{border-color:var(--n-border)}.wb-chat .wb-icon-btn:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-chat .wb-chat-back[hidden]{display:none}.wb-chat-info-btn[aria-expanded=true]{border-color:var(--n-accent);color:var(--n-accent)}.wb-chat-info{flex:none;display:flex;flex-direction:column;gap:4px;padding:8px 12px;font-size:12px;border-bottom:1px solid var(--n-border);background:color-mix(in srgb,var(--n-bg) 55%,var(--n-panel))}.wb-chat-info[hidden]{display:none}.wb-chat-info .wb-kv-v{font-family:ui-monospace,Consolas,monospace;font-size:11.5px}.wb-chat-thread{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;gap:10px;padding:12px}.wb-chat-thread:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-chat-log,.wb-chat-sent{display:flex;flex-direction:column;gap:10px}.wb-chat-notice{align-self:center;max-width:92%;text-align:center;font-size:11.5px;line-height:1.5;color:var(--n-ink-muted);border:1px dashed var(--n-border);border-radius:8px;padding:6px 10px}.wb-chat-notice[hidden]{display:none}.wb-chat-empty{align-self:center;padding:24px 8px;font-size:12px;color:var(--n-ink-muted);text-align:center}.wb-chat-empty.is-error{color:var(--n-danger)}.wb-msg{display:flex;flex-direction:column;gap:4px;max-width:86%;padding:8px 11px;border-radius:12px;font-size:13px;line-height:1.55}.wb-msg-assistant{align-self:flex-start;background:var(--n-raised);border:1px solid var(--n-border);border-bottom-left-radius:4px}.wb-msg-user{align-self:flex-end;background:color-mix(in srgb,var(--n-accent) 16%,var(--n-panel));border:1px solid color-mix(in srgb,var(--n-accent) 35%,transparent);border-bottom-right-radius:4px}.wb-msg-ledger{align-self:center;max-width:92%;background:transparent;border:1px dashed var(--n-border);font-size:12px}.wb-msg-ledger .wb-msg-text{color:var(--n-ink-muted)}.wb-msg-ledger .wb-msg-meta{justify-content:center}.wb-msg-in .wb-msg-meta{justify-content:flex-start}.wb-msg-who{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.wb-msg-text{color:var(--n-ink);white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}.wb-msg-meta{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px;font-size:11px}.wb-msg-task{font-family:ui-monospace,Consolas,monospace;color:var(--n-ink-muted)}.wb-msg-status{border:1px solid currentColor;border-radius:99px;padding:0 7px;line-height:18px;white-space:nowrap}.wb-msg-status.is-wait,.wb-msg-status.is-muted{color:var(--n-ink-muted)}.wb-msg-status.is-ok{color:var(--n-live)}.wb-msg-status.is-warn{color:var(--n-act)}.wb-msg-status.is-danger{color:var(--n-danger)}.wb-chat-divider{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--n-ink-muted);margin-top:4px}.wb-chat-divider:before,.wb-chat-divider:after{content:"";flex:1;border-top:1px solid var(--n-border)}.wb-chat-sessions{display:flex;flex-direction:column;gap:6px}.wb-chat-session{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;text-align:left;background:var(--n-raised);border:1px solid var(--n-border);border-radius:8px;padding:7px 10px;color:var(--n-ink);font:inherit;cursor:pointer}.wb-chat-session:hover{border-color:var(--n-accent)}.wb-chat-session:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-chat-session-id{font:12px ui-monospace,Consolas,monospace}.wb-chat-session-meta{font-size:11px;color:var(--n-ink-muted)}.wb-chat-foot{flex:none;border-top:1px solid var(--n-border);padding:8px 10px 10px;background:var(--n-panel)}.wb-chat-composer{display:flex;flex-direction:column;gap:4px}.wb-chat-composer-row{display:flex;align-items:flex-end;gap:8px}.wb-chat-composer .wb-dispatch-text{flex:1 1 auto;min-height:38px;max-height:160px;resize:none;border-radius:10px;padding:8px 10px;font-size:13px;line-height:1.5}.wb-chat-composer .wb-dispatch-submit{align-self:auto;height:38px;padding:0 16px;border-radius:10px;background:var(--n-accent);border-color:var(--n-accent);color:var(--n-bg);font-size:13px;font-weight:600}.wb-chat-composer .wb-dispatch-submit:hover:not(:disabled){border-color:var(--n-accent);filter:brightness(1.08)}.wb-chat-composer .wb-dispatch-submit:disabled{opacity:.45;cursor:default}.wb-chat-composer .wb-dispatch-note{font-size:11px;padding-left:2px}.wb-chat-composer .wb-dispatch-note.is-error{color:var(--n-danger)}.wb-chat-readonly{font-size:12px;color:var(--n-ink-muted);text-align:center;padding:4px 0}.nexus-fault{display:none;position:fixed;top:10px;right:12px;z-index:50;max-width:420px;font:12px/1.5 ui-monospace,Consolas,monospace;color:var(--n-error-ink);background:var(--n-error-panel);border:1px solid var(--n-danger);border-radius:8px;padding:6px 10px;pointer-events:none}@media (max-width:1023px){.wb-menu{display:inline-flex}.wb-rail-toggle{display:none}.wb-body{grid-template-columns:minmax(0,1fr)}.wb-rail{position:fixed;left:0;top:0;bottom:0;width:min(86vw,320px);z-index:40;background:var(--n-panel);border-right:1px solid var(--n-border);transform:translate(-102%);transition:transform .18s ease;box-shadow:8px 0 24px var(--n-shadow)}.wb.is-rail-open .wb-rail{transform:translate(0)}.wb-inspector{left:8px;right:8px;width:auto;top:auto;bottom:8px;max-height:45%}.wb-chat{left:8px;right:8px;width:auto;top:auto;bottom:8px;height:85%;max-height:85%;transform:translateY(102%)}.wb-chat.is-open{transform:translateY(0)}}@media (pointer:coarse){.wb-filter,.wb-item,.wb-tab{min-height:36px}.wb-icon-btn,.wb-inspector .wb-icon-btn,.wb-chat .wb-icon-btn{width:36px;height:36px}}@media (max-width:680px){.wb-header{flex-wrap:wrap;row-gap:4px}.wb-header-stats{order:3;width:100%}.wb-row-team{min-width:0;flex:1 1 auto}.wb-data{padding:8px}}.wb-host-row{display:flex;align-items:center;gap:7px;padding:4px 6px;border:1px solid var(--n-border);border-radius:7px;margin-bottom:4px;cursor:pointer;font-size:12px}.wb-host-row:hover{border-color:var(--n-accent);background:color-mix(in srgb,var(--n-accent) 8%,transparent)}.wb-host-row:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-host-lamp{flex:none;width:8px;height:8px;border-radius:50%;background:var(--n-ink-muted)}.wb-host-lamp.is-live{background:var(--n-live)}.wb-host-lamp.is-warm{background:var(--n-act)}.wb-host-lamp.is-cold{background:var(--n-ink-muted)}.wb-host-lamp.is-fail{background:var(--n-danger)}.wb-host-lamp.is-unknown{background:transparent;border:1px solid var(--n-ink-muted)}.wb-host-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--n-ink)}.wb-host-badge{flex:none;font-size:10.5px;border-radius:99px;padding:0 7px;line-height:17px;border:1px solid var(--n-border)}.wb-host-badge.is-linked{color:var(--n-live);border-color:var(--n-live)}.wb-host-badge.is-stranger{color:var(--n-ink-muted);border-color:var(--n-border)}.wb-host-ver{flex:none;font-size:10px;color:var(--n-ink-muted);border:1px solid var(--n-border);border-radius:4px;padding:0 4px}.wb-host-url{flex:none;max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--n-ink-muted);font-size:10.5px}.wb-tabs{flex:none;display:flex;gap:2px;padding:6px 6px 0;border-bottom:1px solid var(--n-border);overflow-x:auto;scrollbar-width:none}.wb-tab{flex:1 1 auto;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-width:0;background:none;border:none;border-bottom:2px solid transparent;color:var(--n-ink-muted);font:inherit;font-size:12.5px;padding:6px 6px 7px;cursor:pointer;white-space:nowrap}.wb-tab:hover{color:var(--n-ink)}.wb-tab.is-active{color:var(--n-ink);border-bottom-color:var(--n-accent);font-weight:600}.wb-tab:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px;border-radius:6px 6px 0 0}.wb-tab[hidden]{display:none}.wb-tab-badge{font:10.5px/16px ui-monospace,Consolas,monospace;color:var(--n-ink-muted);font-weight:400}.wb-tab-badge:empty{display:none}.wb-tab-badge.is-danger,.wb-tab-badge.is-warn,.wb-tab-badge.is-accent{padding:0 6px;border-radius:99px;border:1px solid currentColor}.wb-tab-badge.is-danger{color:var(--n-danger)}.wb-tab-badge.is-warn{color:var(--n-act)}.wb-tab-badge.is-accent{color:var(--n-accent)}.wb-tabpanels{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}.wb-tabpanel{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:8px 10px 16px}.wb-tabpanel[hidden]{display:none}.wb-tabpanel:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-tabpanel .wb-pane{display:flex;flex-direction:column;gap:8px}.wb-tabpanel .wb-dir-tools{padding:0 0 6px}.wb-dir-group{display:flex;flex-direction:column;gap:1px;margin-top:10px}.wb-dir-group:first-child{margin-top:2px}.wb-dir-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:0 6px 4px;font-size:11px;letter-spacing:.04em;color:var(--n-ink-muted)}.wb-dir-head-label{font-weight:600;color:var(--n-ink)}.wb-dir-head-note{font-size:10.5px}.wb-dir-day{padding:6px 6px 2px;font-size:10.5px;color:var(--n-ink-muted)}.wb-dir-empty{padding:24px 8px;text-align:center;font-size:12px;color:var(--n-ink-muted)}.wb-directory .wb-item{gap:9px;padding:5px 6px;border-radius:8px}.wb-directory .wb-item.is-draggable{cursor:grab}.wb-directory .wb-item:focus-visible{outline:2px solid var(--n-accent);outline-offset:-2px}.wb-item-text{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;line-height:1.35}.wb-item-sub{font:11px/1.35 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wb-item.is-untitled .wb-item-label{color:var(--n-ink-muted);font-family:ui-monospace,Consolas,monospace;font-size:12px}.wb-directory .wb-item.is-unreachable{opacity:.78}.wb-unreachable-note,.wb-node-warning{font-size:11px;line-height:1.35;color:var(--n-danger);white-space:normal;overflow-wrap:anywhere}.wb-dot-log{color:var(--n-ink-muted)}.wb-led-tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.wb-led-tile{display:flex;flex-direction:column;align-items:flex-start;gap:1px;padding:7px 8px;border-radius:8px;border:1px solid var(--n-border);background:var(--n-raised);cursor:help;min-width:0}.wb-led-tile-v{font:600 17px/1.15 ui-monospace,Consolas,monospace;color:var(--n-ink)}.wb-led-tile-k{font-size:11px;color:var(--n-ink-muted)}.wb-led-tile.is-warn{border-color:var(--n-act)}.wb-led-tile.is-warn .wb-led-tile-v{color:var(--n-act)}.wb-led-tile.is-danger{border-color:var(--n-danger)}.wb-led-tile.is-danger .wb-led-tile-v{color:var(--n-danger)}.wb-led-split{display:flex;flex-direction:column;gap:4px}.wb-led-split-bar{display:flex;height:6px;border-radius:99px;overflow:hidden;background:var(--n-border)}.wb-led-seg{display:block;height:100%}.wb-led-split-legend{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:11px;color:var(--n-ink-muted)}.wb-led-split-item{display:inline-flex;align-items:center;gap:4px}.wb-led-swatch{display:inline-block;width:8px;height:8px;border-radius:2px}.wb-led-seg.is-completed,.wb-led-swatch.is-completed{background:var(--n-live)}.wb-led-seg.is-rejected,.wb-led-swatch.is-rejected{background:var(--n-act)}.wb-led-seg.is-failed,.wb-led-swatch.is-failed{background:var(--n-danger)}.wb-led-seg.is-abandoned,.wb-led-swatch.is-abandoned{background:var(--n-ink-muted)}.wb-led-seg.is-unclassified,.wb-led-swatch.is-unclassified,.wb-led-seg.is-other,.wb-led-swatch.is-other{background:color-mix(in srgb,var(--n-ink-muted) 45%,var(--n-border))}.wb-led-group{display:flex;flex-direction:column;gap:6px;margin-top:4px}.wb-led-group-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;font-size:11.5px;font-weight:600;color:var(--n-ink)}.wb-led-group-hint{font-size:10.5px;font-weight:400;color:var(--n-ink-muted)}.wb-led-group.is-danger .wb-led-group-head{color:var(--n-danger)}.wb-led-group.is-warn .wb-led-group-head{color:var(--n-act)}.wb-led-card{position:relative;display:flex;flex-direction:column;gap:3px;padding:7px 9px;border-radius:8px;border:1px solid var(--n-border);background:var(--n-raised);min-width:0}.wb-led-card.is-danger{border-color:var(--n-danger)}.wb-led-card.is-warn{border-left:3px solid var(--n-act)}.wb-led-card.is-muted,.wb-led-card.is-quiet{background:transparent}.wb-led-card.is-link{cursor:pointer}.wb-led-card.is-link:hover{border-color:var(--n-accent)}.wb-led-card.is-link:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-led-card-head{display:flex;align-items:flex-start;gap:6px;min-width:0}.wb-led-dir{flex:none;font-size:12px;color:var(--n-ink-muted);line-height:1.45}.wb-led-dir.is-in{color:var(--n-federal)}.wb-led-brief{flex:1 1 auto;min-width:0;font-size:12.5px;line-height:1.45;color:var(--n-ink);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}.wb-led-card.is-muted .wb-led-brief,.wb-led-card.is-quiet .wb-led-brief{color:var(--n-ink-muted)}.wb-led-card.is-danger .wb-led-brief{color:var(--n-danger)}.wb-led-brief.is-missing{font-style:italic}.wb-led-meta{display:flex;flex-wrap:wrap;gap:2px 8px;font:11px/1.45 ui-monospace,Consolas,monospace;color:var(--n-ink-muted);min-width:0}.wb-led-meta>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.wb-led-chip{border:1px solid var(--n-border);border-radius:99px;padding:0 6px;font-family:inherit}.wb-led-abandon{position:absolute;top:6px;right:6px;margin-left:0;padding:1px 9px;font-size:11.5px}.wb-led-abandon.is-quiet{border-color:transparent;background:transparent;color:var(--n-ink-muted)}.wb-led-abandon.is-quiet:hover{border-color:var(--n-border);color:var(--n-ink)}.wb-led-card:has(.wb-led-abandon) .wb-led-card-head{padding-right:52px}.wb-led-empty{padding:24px 8px;text-align:center;font-size:12px;color:var(--n-ink-muted)}.wb-led-forensics{font-size:11px;color:var(--n-ink-muted);border-top:1px dashed var(--n-border);padding-top:6px;margin-top:4px}.wb-led-forensics summary{cursor:pointer}.wb-led-forensics summary:focus-visible{outline:2px solid var(--n-accent);outline-offset:1px}.wb-led-forensics-body{display:flex;flex-direction:column;gap:2px;padding:4px 0 0 12px;font-family:ui-monospace,Consolas,monospace}.wb-sa{display:flex;flex-direction:column;gap:8px}.wb-pane.is-hidden{display:none}.wb-sa-nav{flex-wrap:wrap}.wb-sa-nav .wb-filter.is-active{border-color:var(--n-accent);color:var(--n-ink)}.wb-sa-body{display:flex;flex-direction:column;gap:10px}.wb-sa-note{font-size:11.5px;color:var(--n-ink-muted);min-height:1em}.wb-sa-note.is-danger{color:var(--n-danger)}.wb-sa-hint{font-size:11.5px;color:var(--n-ink-muted)}.wb-sa-form{display:flex;flex-direction:column;gap:6px}.wb-sa-input{width:100%;box-sizing:border-box;resize:vertical;font:inherit;font-size:12.5px;color:var(--n-ink);background:var(--n-bg);border:1px solid var(--n-border);border-radius:6px;padding:6px 8px}.wb-sa-input:focus,.wb-sa-select:focus{outline:2px solid var(--n-accent);outline-offset:1px}.wb-sa-select{font:inherit;font-size:12.5px;color:var(--n-ink);background:var(--n-bg);border:1px solid var(--n-border);border-radius:6px;padding:4px 6px;max-width:100%}.wb-sa-field{display:flex;align-items:flex-start;gap:8px;font-size:12px}.wb-sa-checks{display:flex;flex-direction:column;gap:3px}.wb-sa-check{display:flex;align-items:center;gap:6px;font-size:12px}.wb-sa-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;margin-top:2px}.wb-sa .wb-btn{margin:0}.wb-sa-form>.wb-btn{align-self:flex-end}.wb-sa-ticket-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.wb-sa-title{font-size:12.5px;color:var(--n-ink);overflow-wrap:anywhere}.wb-sa-stage{flex:none;font-size:11px;color:var(--n-ink-muted)}.wb-sa-ticket.is-warn .wb-sa-stage{color:var(--n-act)}.wb-sa-msg{display:flex;gap:6px;align-items:baseline;font-size:12px}.wb-sa-msg.is-reply .wb-sa-who{color:var(--n-accent)}.wb-sa-msg.is-danger .wb-sa-text{color:var(--n-danger)}.wb-sa-who{flex:none;font-size:11px;color:var(--n-ink-muted)}.wb-sa-text{flex:1;white-space:pre-wrap;overflow-wrap:anywhere}.wb-sa-when{flex:none;font-size:10.5px;color:var(--n-ink-muted)}.wb-sa-decision{font-size:11.5px;color:var(--n-ink-muted);overflow-wrap:anywhere}