@nelsonlongxiang/dsh-open-a2a-net 0.6.33 → 0.6.34

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 (51) hide show
  1. package/README.md +5 -2
  2. package/README_zh.md +5 -2
  3. package/assets/nexusDist/assets/index-D2BGWOeU.js +9 -0
  4. package/assets/nexusDist/assets/index-z33lHtLw.css +1 -0
  5. package/assets/nexusDist/index.html +2 -2
  6. package/lib/a2a-client.js +9 -2
  7. package/lib/auth-observer.js +1 -1
  8. package/lib/canvas-store.js +44 -6
  9. package/lib/card.js +2 -1
  10. package/lib/cli-home.js +44 -0
  11. package/lib/client/A2aControl.js +1 -1
  12. package/lib/client.js +65 -65
  13. package/lib/client.js.map +1 -1
  14. package/lib/direct-forward.js +61 -0
  15. package/lib/faces/admin.js +41 -5
  16. package/lib/faces/wire.js +55 -16
  17. package/lib/index.js +110 -64
  18. package/lib/outbound-scope.js +25 -0
  19. package/lib/queue-guard.js +37 -0
  20. package/lib/skill/references/cli.md +5 -4
  21. package/lib/skill/references/pitfalls.md +1 -4
  22. package/lib/task-auth.js +52 -3
  23. package/lib/task-ledger.js +4 -1
  24. package/lib/tools/info.js +19 -3
  25. package/lib/types/a2a-client.d.ts +1 -1
  26. package/lib/types/auth-observer.d.ts +2 -2
  27. package/lib/types/canvas-store.d.ts +10 -1
  28. package/lib/types/cli-home.d.ts +24 -0
  29. package/lib/types/client/A2aControl.d.ts +1 -1
  30. package/lib/types/direct-forward.d.ts +32 -0
  31. package/lib/types/faces/admin.d.ts +8 -1
  32. package/lib/types/faces/wire.d.ts +12 -4
  33. package/lib/types/index.d.ts +4 -0
  34. package/lib/types/outbound-scope.d.ts +21 -0
  35. package/lib/types/queue-guard.d.ts +32 -0
  36. package/lib/types/runtime.d.ts +1 -1
  37. package/lib/types/task-auth.d.ts +17 -2
  38. package/lib/types/task-ledger.d.ts +6 -1
  39. package/lib/types/types.d.ts +8 -0
  40. package/nexus-stage/src/cli-home-picker.ts +42 -0
  41. package/nexus-stage/src/main.ts +9 -2
  42. package/nexus-stage/src/planning-card.ts +61 -0
  43. package/nexus-stage/src/planning-menus.ts +42 -7
  44. package/nexus-stage/src/planning-view.ts +14 -32
  45. package/nexus-stage/src/planning.css +17 -0
  46. package/nexus-stage/src/workbench.css +4 -0
  47. package/nexus-stage/src/workbench.ts +22 -5
  48. package/nexus-stage/src/world.ts +5 -1
  49. package/package.json +2 -1
  50. package/assets/nexusDist/assets/index-BACVZe0j.css +0 -1
  51. package/assets/nexusDist/assets/index-Bbxnpf2p.js +0 -9
@@ -91,12 +91,46 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
91
91
  title.id = 'p-dialog-title'
92
92
  title.className = 'p-dialog-title'
93
93
  title.textContent = `组建团队(${ids.length} 个节点,自上而下即优先级)`
94
- const kind = document.createElement('select')
95
- kind.className = 'p-dialog-kind'
96
- kind.setAttribute('aria-label', '团队类型')
97
- for (const [value, label] of [['canvas', 'canvas 队'], ['roster', 'roster 网络队']]) {
98
- const option = document.createElement('option'); option.value = value; option.textContent = label; kind.appendChild(option)
94
+ // Team type: a visible two-way toggle (a bare <select> read as a label and roster went unnoticed).
95
+ const kind = { value: 'canvas' as 'canvas' | 'roster' }
96
+ const kindGroup = document.createElement('div')
97
+ kindGroup.className = 'p-dialog-kind'
98
+ kindGroup.setAttribute('role', 'radiogroup')
99
+ kindGroup.setAttribute('aria-label', '团队类型')
100
+ const kindHint = document.createElement('div')
101
+ kindHint.className = 'p-dialog-hint'
102
+ const KIND_HINT = {
103
+ canvas: 'canvas 队:画布上的协作组,可以加入远端成员。',
104
+ roster: 'roster 网络队:只收本机已入网的会话;队名会写进属主的精确允许名单。',
105
+ } as const
106
+ const kindButtons = (['canvas', 'roster'] as const).map(value => {
107
+ const b = document.createElement('button')
108
+ b.type = 'button'
109
+ b.dataset.kind = value
110
+ b.setAttribute('role', 'radio')
111
+ b.textContent = value === 'canvas' ? 'canvas 队' : 'roster 网络队'
112
+ b.addEventListener('click', () => setKind(value))
113
+ kindGroup.appendChild(b)
114
+ return b
115
+ })
116
+ function setKind(value: 'canvas' | 'roster'): void {
117
+ kind.value = value
118
+ for (const b of kindButtons) {
119
+ const on = b.dataset.kind === value
120
+ b.setAttribute('aria-checked', String(on))
121
+ b.tabIndex = on ? 0 : -1
122
+ }
123
+ kindHint.textContent = KIND_HINT[value]
124
+ err.textContent = ''
99
125
  }
126
+ // Arrow keys move between the two options (radiogroup pattern).
127
+ kindGroup.addEventListener('keydown', (ev) => {
128
+ const e = ev as KeyboardEvent
129
+ if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) return
130
+ e.preventDefault()
131
+ setKind(kind.value === 'canvas' ? 'roster' : 'canvas')
132
+ kindButtons.find(b => b.dataset.kind === kind.value)?.focus()
133
+ })
100
134
  const input = document.createElement('input')
101
135
  input.className = 'p-dialog-input'
102
136
  input.maxLength = 40
@@ -113,7 +147,8 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
113
147
  cancelBtn.type = 'button'
114
148
  cancelBtn.textContent = '取消'
115
149
  row.append(okBtn, cancelBtn)
116
- panel.append(title, kind, input, err, row)
150
+ setKind('canvas')
151
+ panel.append(title, kindGroup, kindHint, input, err, row)
117
152
  wrap.appendChild(panel)
118
153
  root.appendChild(wrap)
119
154
 
@@ -161,7 +196,7 @@ export function createPlanningMenus(d: MenusDeps): PlanningMenus {
161
196
  if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); close(); return }
162
197
  if (e.key !== 'Tab') return
163
198
  e.preventDefault()
164
- const focusables = Array.from(panel.querySelectorAll<HTMLElement>('input, select, button'))
199
+ const focusables = Array.from(panel.querySelectorAll<HTMLElement>('input, select, button:not([tabindex="-1"])'))
165
200
  const idx = focusables.indexOf(document.activeElement as HTMLElement)
166
201
  const next = e.shiftKey
167
202
  ? focusables[(idx - 1 + focusables.length) % focusables.length]
@@ -51,6 +51,7 @@ import { settleBoard, tidyMembers } from './planning-overlap'
51
51
  import { createLayoutUndo, type LayoutSnap } from './planning-undo'
52
52
  import { createNodeList } from './planning-nodelist'
53
53
  import { cardText } from './planning-card-text'
54
+ import { createCardElement, paintPrio, workLabel } from './planning-card'
54
55
  import { createLegend } from './planning-legend'
55
56
  import { UNREACHABLE_HINT, showFrameReachability, unreachableRefs } from './planning-reachability'
56
57
  import { viewportBucket, type PlanningDeps, type PlanningInput, type PlanningSession, type PlanningTeam, type PlanningView, type SeamKey, type SeamPointer, type ViewportBucket } from './planning-types'
@@ -68,6 +69,9 @@ type Gesture =
68
69
  | { readonly kind: 'pan'; last: { x: number; y: number }; moved: boolean }
69
70
  | { readonly kind: 'marquee'; origin: { x: number; y: number }; last: { x: number; y: number }; additive: boolean }
70
71
 
72
+ // The card skeleton, its 工作中/空闲 chip and the right-hand chip live in planning-card (module-size cap).
73
+ export { workLabel } from './planning-card'
74
+
71
75
  export function createPlanningView(deps: PlanningDeps): PlanningView {
72
76
  const model = new WorldModel()
73
77
  let layoutDoc: LayoutDoc | undefined
@@ -398,22 +402,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
398
402
  seen.add(n.id)
399
403
  let el = nodeEls.get(n.id)
400
404
  if (el === undefined) {
401
- el = document.createElement('div')
402
- el.className = 'p-node'
403
- el.dataset.id = n.id
404
- el.tabIndex = 0
405
- const nm = document.createElement('div')
406
- nm.className = 'nm'
407
- const dot = document.createElement('span')
408
- dot.className = 'dot'
409
- const name = document.createElement('span')
410
- name.className = 'nm-text'
411
- nm.append(dot, name)
412
- const sub = document.createElement('div')
413
- sub.className = 'sub mono'
414
- const prio = document.createElement('span')
415
- prio.className = 'prio mono'
416
- el.append(nm, sub, prio)
405
+ el = createCardElement(n.id)
417
406
  nodeLayer.appendChild(el)
418
407
  nodeEls.set(n.id, el)
419
408
  }
@@ -422,6 +411,12 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
422
411
  el.style.top = `${r.y}px`
423
412
  el.classList.toggle('cold', !n.live)
424
413
  el.classList.toggle('remote', n.remote === true)
414
+ // 工作中/空闲: live local sessions only; the host reports nothing for cold or remote nodes.
415
+ const work = n.live && n.remote !== true ? lastWork.get(n.id) : undefined
416
+ el.classList.toggle('is-working', work?.status === 'running')
417
+ const workText = workLabel(work)
418
+ const workEl = el.querySelector<HTMLElement>('.work')!
419
+ if (workEl.textContent !== workText) workEl.textContent = workText
425
420
  el.classList.toggle('selected', model.isSelected(n.id))
426
421
  el.setAttribute('aria-selected', String(model.isSelected(n.id)))
427
422
  const host = n.id.slice('peer-'.length)
@@ -436,22 +431,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
436
431
  if (subEl.textContent !== subText) subEl.textContent = subText
437
432
  const tip = unavailable ? `${txt.tip} · ${UNREACHABLE_HINT}` : txt.tip
438
433
  if (el.title !== tip) el.title = tip
439
- const prioEl = el.querySelector<HTMLElement>('.prio')!
440
- if (n.remote === true) {
441
- // gh#20 W-A: the peer badge — link class colors the chip (linked
442
- // green / stranger gray), the verified tick/cross rides after it.
443
- // An absent link field (older host) renders the neutral 'peer'.
444
- const info = peerInfoByHost.get(host)
445
- const cls = info?.link === 'linked' ? 'lnk-linked' : info?.link === 'stranger' ? 'lnk-stranger' : ''
446
- if (prioEl.className !== `prio mono ${cls}`.trim()) prioEl.className = `prio mono ${cls}`.trim()
447
- const mark = info === undefined ? '' : info.verified === true ? '✓' : '✗'
448
- const badge = (info?.link ?? 'peer') + mark
449
- if (prioEl.textContent !== badge) prioEl.textContent = badge
450
- } else {
451
- if (prioEl.className !== 'prio mono') prioEl.className = 'prio mono'
452
- const prioText = n.memberships[0] !== undefined ? `P${n.memberships[0]!.index}` : ''
453
- if (prioEl.textContent !== prioText) prioEl.textContent = prioText
454
- }
434
+ paintPrio(el.querySelector<HTMLElement>('.prio')!, n, n.remote === true ? peerInfoByHost.get(host) : undefined)
455
435
  }
456
436
  for (const [id, el] of [...nodeEls]) {
457
437
  if (!seen.has(id)) { el.remove(); nodeEls.delete(id) }
@@ -470,6 +450,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
470
450
  const federation = createFederationOverlay(svg, model)
471
451
  let lastPeers: ReadonlyArray<PeerRow> = []
472
452
  let lastRemoteTeams: ReadonlyMap<string, RemoteTeamRow[]> = new Map()
453
+ let lastWork: ReadonlyMap<string, Pick<PlanningSession, 'status' | 'queued'>> = new Map()
473
454
  const peerScoreByText = new Map<string, number | undefined>()
474
455
  /** gh#20 W-A: per-host link/verified info for the peer badges. */
475
456
  const peerInfoByHost = new Map<string, { link?: 'linked' | 'stranger'; verified?: boolean }>()
@@ -1221,6 +1202,7 @@ export function createPlanningView(deps: PlanningDeps): PlanningView {
1221
1202
  // excluded from bounds, so the column cannot feed back into itself).
1222
1203
  const peerSeen = new Set<string>()
1223
1204
  lastRemoteTeams = groupRemoteTeams(input.remoteTeams ?? [])
1205
+ lastWork = new Map(input.sessions.map(s => [s.id, { status: s.status, queued: s.queued }]))
1224
1206
  reachability = unreachableRefs(input)
1225
1207
  peerScoreByText.clear()
1226
1208
  for (const p of input.peers ?? []) {
@@ -116,6 +116,14 @@
116
116
  #dsh-plan .p-node.remote .nm-text{overflow-wrap:anywhere}
117
117
  #dsh-plan .p-node .dot{width:8px; height:8px; border-radius:50%; background:var(--edge-live); flex:none}
118
118
  #dsh-plan .p-node.cold .dot{background:transparent; border:1.5px solid var(--edge-cold)}
119
+ #dsh-plan .p-node .sub-row{display:flex; align-items:baseline; gap:6px; margin-top:2px; min-width:0}
120
+ #dsh-plan .p-node .sub-row .sub{flex:1 1 auto; min-width:0; margin-top:0}
121
+ #dsh-plan .p-node .work{flex:none; font-size:10.5px; font-weight:500; color:var(--ink-muted); white-space:nowrap}
122
+ #dsh-plan .p-node .work:empty{display:none}
123
+ #dsh-plan .p-node.is-working .work{color:var(--edge-live)}
124
+ #dsh-plan .p-node.is-working .dot{animation:p-working 1.4s ease-in-out infinite}
125
+ @keyframes p-working{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--edge-live) 55%,transparent)}50%{box-shadow:0 0 0 4px transparent}}
126
+ @media (prefers-reduced-motion: reduce){#dsh-plan .p-node.is-working .dot{animation:none}}
119
127
  #dsh-plan .p-node .sub{font-size:11px; color:var(--ink-muted); margin-top:2px;
120
128
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
121
129
  /* W1: a picked search hit pulses its card for 1.2s (class removed on timer). */
@@ -252,6 +260,15 @@
252
260
  #dsh-plan .p-dialog-input:hover{border-color:color-mix(in srgb,var(--uupm-ink-1) 55%,transparent)}
253
261
  #dsh-plan .p-dialog-input:focus-visible{outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent)}
254
262
  #dsh-plan .p-dialog-err{min-height:1.2em; font-size:11px; color:var(--uupm-error)}
263
+ #dsh-plan .p-dialog-kind{display:grid; grid-template-columns:1fr 1fr; gap:4px; padding:3px;
264
+ background:var(--uupm-bg-0); border:1px solid var(--card-line); border-radius:8px}
265
+ #dsh-plan .p-dialog-kind button{font:inherit; color:var(--ink-muted); background:transparent; border:1px solid transparent;
266
+ border-radius:6px; padding:6px 10px; cursor:pointer}
267
+ #dsh-plan .p-dialog-kind button:hover{color:var(--ink); background:var(--uupm-wash-8)}
268
+ #dsh-plan .p-dialog-kind button[aria-checked=true]{color:var(--ink); font-weight:600; background:var(--uupm-wash-15);
269
+ border-color:var(--accent)}
270
+ #dsh-plan .p-dialog-kind button:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
271
+ #dsh-plan .p-dialog-hint{font-size:11px; color:var(--ink-muted); max-width:340px}
255
272
  #dsh-plan .p-dialog-row{display:flex; gap:8px; justify-content:flex-end}
256
273
  #dsh-plan .p-dialog-row button{background:transparent; color:var(--ink); border:1px solid var(--card-line);
257
274
  border-radius:6px; padding:5px 12px; font:inherit; cursor:pointer}
@@ -93,6 +93,10 @@ body{background:var(--n-bg);color:var(--n-ink);font:13px/1.45 system-ui,"Segoe U
93
93
  .wb-member-remove:hover:not(:disabled),.wb-member-submit:hover:not(:disabled){border-color:var(--n-danger)}
94
94
  .wb-member-submit:hover:not(:disabled){border-color:var(--n-accent)}
95
95
  .wb-member-add{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
96
+ .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}
97
+ .wb-cli-home [hidden]{display:none}
98
+ .wb-cli-home .wb-hint{flex-basis:100%;margin-top:0}
99
+ .wb-cli-home .wb-member-pick{min-width:0;max-width:100%}
96
100
  .wb-member-pick,.wb-member-manual{background:var(--n-panel);border:1px solid var(--n-border);color:var(--n-ink);
97
101
  border-radius:6px;padding:3px 8px;font:inherit;font-size:12px;min-width:0}
98
102
  .wb-member-pick{flex:1 1 150px}
@@ -19,6 +19,7 @@
19
19
  * @module nexus-stage/workbench
20
20
  */
21
21
  import { createChatDrawer, type ChatTarget } from './wb-chat'
22
+ import { createCliHomePicker, type CliHomeOptions } from './cli-home-picker'
22
23
  import { createRailTabs } from './wb-rail'
23
24
  import { DIRECTORY_FILTERS, learnLogTitle, loadLogTitles, renderDirectory, type DirFilter } from './wb-directory'
24
25
  import { ledgerFacts, renderLedger as paintLedger } from './wb-ledger'
@@ -130,6 +131,9 @@ export interface CanvasMemberRow {
130
131
  readonly joined?: boolean
131
132
  readonly live?: boolean
132
133
  readonly remote?: boolean
134
+ /** Present only for CLI refs: null means the linked home is ambiguous or unknown. */
135
+ readonly cliHome?: string | null
136
+ readonly cliHeld?: boolean
133
137
  }
134
138
 
135
139
  /** One canvas team from /state `canvas.teams`. */
@@ -142,7 +146,7 @@ export interface CanvasTeamRow {
142
146
  /** One activity-ring entry. */
143
147
  export interface ActivityRow {
144
148
  readonly ts: number
145
- readonly dir: 'in' | 'out'
149
+ readonly dir: 'in' | 'out' | 'fwd'
146
150
  readonly team: string
147
151
  readonly peer: string
148
152
  readonly ok: boolean
@@ -212,7 +216,8 @@ export interface WorkbenchDeps {
212
216
  abandon(taskId: string, reason: string): Promise<boolean>
213
217
  /** Canvas team member change via the control route (0.6.6): `id` is a
214
218
  * local session id or a zone-qualified remote handle. True on success. */
215
- canvasOp(action: 'add-member' | 'remove-member', name: string, id: string): Promise<boolean>
219
+ canvasOp(action: 'add-member' | 'remove-member', name: string, id: string, home?: string): Promise<boolean>
220
+ cliHomeOptions?(name: string, id: string): Promise<CliHomeOptions>
216
221
  /** Quick dispatch (0.6.9): POST /__dsh_a2a/dispatch as a human gesture —
217
222
  * one message to a routing team, async delivery, receipt lands in the
218
223
  * ledger. Optional — without it the 快派 form hides. */
@@ -333,6 +338,7 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
333
338
  // Canvas add-member form cache (static DOM re-mounted across polls; keyed
334
339
  // by team name — same trick as draftForm so inputs survive the 3s poll).
335
340
  let canvasForm: { name: string; form: HTMLFormElement; pick: HTMLSelectElement; manual: HTMLInputElement } | null = null
341
+ const cliHomeForms = new Map<string, HTMLFormElement>()
336
342
  // Quick-dispatch form (0.6.9), cached per target routing team the same
337
343
  // draftForm way: a textarea's draft must survive the 3s poll remount.
338
344
  let dispatchForm: { team: string; form: HTMLFormElement; text: HTMLTextAreaElement; note: HTMLElement } | null = null
@@ -690,7 +696,7 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
690
696
  for (const row of ring) {
691
697
  const line = el('div', 'wb-row')
692
698
  const dir = el('span', 'wb-row-dir')
693
- dir.textContent = row.dir === 'out' ? '→' : '←'
699
+ dir.textContent = row.dir === 'fwd' ? '⇢' : row.dir === 'out' ? '→' : '←'
694
700
  if (row.ok !== true) dir.classList.add('is-fail')
695
701
  line.appendChild(dir)
696
702
  teamCell(line, last, row.team)
@@ -962,12 +968,12 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
962
968
  }
963
969
  for (const member of fresh.members) {
964
970
  const row = el('div', 'wb-member-row')
965
- const dot = member.remote === true ? '◇ 远端' : member.live === true ? '● 现场' : member.joined === true ? '○ 冷启' : '· 未入网'
971
+ const dot = member.cliHeld === true ? '本机持有,无需指定' : member.remote === true ? (member.cliHome === null ? '◇ 远端 · home 未确定' : '◇ 远端') : member.live === true ? '● 现场' : member.joined === true ? '○ 冷启' : '· 未入网'
966
972
  row.appendChild(el('span', 'wb-member-dot')).textContent = dot
967
973
  const label = el('span', 'wb-member-label')
968
974
  const session = (last?.sessions ?? []).find(s => s.id === member.id)
969
975
  label.textContent = member.remote === true ? member.id : (session !== undefined ? displayName(session) : member.id)
970
- label.title = member.team ?? member.id
976
+ label.title = member.cliHeld === true ? '本机持有,无需指定' : member.cliHome !== undefined ? (member.cliHome ?? 'home 未确定') : (member.team ?? member.id)
971
977
  row.appendChild(label)
972
978
  const remove = el('button', 'wb-member-remove')
973
979
  remove.type = 'button'
@@ -978,6 +984,15 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
978
984
  })
979
985
  row.appendChild(remove)
980
986
  card.appendChild(row)
987
+ if (member.cliHome === null && member.cliHeld !== true && deps.cliHomeOptions !== undefined) {
988
+ const key = JSON.stringify([fresh.name, member.id])
989
+ let form = cliHomeForms.get(key)
990
+ if (form === undefined) {
991
+ form = createCliHomePicker(() => deps.cliHomeOptions!(fresh.name, member.id), home => deps.canvasOp('add-member', fresh.name, member.id, home))
992
+ cliHomeForms.set(key, form)
993
+ }
994
+ card.appendChild(form)
995
+ }
981
996
  }
982
997
  // Add-member form: static DOM re-mounted per poll (draftForm trick) —
983
998
  // rebuilt only when the inspected team changes.
@@ -1196,6 +1211,8 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
1196
1211
  }
1197
1212
 
1198
1213
  function update(state: StateBody | null): void {
1214
+ const unknown = new Set((state?.canvas?.teams ?? []).flatMap(team => team.members.filter(member => member.cliHome === null && member.cliHeld !== true).map(member => JSON.stringify([team.name, member.id]))))
1215
+ for (const key of cliHomeForms.keys()) if (!unknown.has(key)) cliHomeForms.delete(key)
1199
1216
  last = state
1200
1217
  // 0.6.12: refresh the readable-log list opportunistically (single flight;
1201
1218
  // the sidebar re-renders when rows land, mid-poll remount is idempotent).
@@ -153,7 +153,11 @@ export function planSeatClear(sid: string, occupied: ReadonlyArray<{ readonly x:
153
153
  }
154
154
 
155
155
  /** Minimal session row shape the model needs (topology.SessionRow satisfies it). */
156
- export interface SessionLite { id: string; label: string; team: string; name?: string; description?: string; joined: boolean; live?: boolean; teams?: readonly string[] }
156
+ export interface SessionLite {
157
+ id: string; label: string; team: string; name?: string; description?: string; joined: boolean; live?: boolean; teams?: readonly string[]
158
+ /** Live local sessions only: mid-turn ('running') or idle, and how many receipt-owing tasks wait on it. */
159
+ status?: 'running' | 'idle'; queued?: number
160
+ }
157
161
 
158
162
  /** Minimal team shape (topology.CanvasTeam satisfies it). `team` is the
159
163
  * mono route alias `<host>/canvas/<name>` when the payload carries it. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nelsonlongxiang/dsh-open-a2a-net",
3
- "version": "0.6.33",
3
+ "version": "0.6.34",
4
4
  "description": "Open A2A network plugin for DeepSeek Harness: signed agent cards, decentralized peer/zone discovery, direct routing model tools, and joinable session nodes with a sidebar control",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -85,6 +85,7 @@
85
85
  "test:live": "vitest run tests/live",
86
86
  "test:build-layout": "vitest run tests/gates/build-layout.spec.ts",
87
87
  "verify:docs": "node scripts/doc-drift.mjs",
88
+ "reanchor:arch": "node scripts/reanchor-architecture.mjs",
88
89
  "verify:nexus": "node scripts/verify-nexus-ci.mjs && npm --prefix nexus-stage run build && npm --prefix nexus-stage run test && git diff --exit-code -- assets/nexusDist/assets && git diff --ignore-cr-at-eol --exit-code -- assets/nexusDist/index.html",
89
90
  "verify": "pnpm typecheck && pnpm verify:docs && pnpm build && pnpm test && pnpm test:build-layout && pnpm verify:nexus"
90
91
  },
@@ -1 +0,0 @@
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.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:56px;padding:8px 26px 8px 14px;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.25}#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{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:46px}#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-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}#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-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-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: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}