@songsid/agend 2.2.0-beta.1 → 2.2.0-beta.10

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 (377) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-cli-instructions.md +16 -9
  4. package/dist/agent-cli.js +15 -7
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +21 -9
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-whats-new.d.ts +47 -0
  9. package/dist/agent-whats-new.js +147 -0
  10. package/dist/agent-whats-new.js.map +1 -0
  11. package/dist/auth-api.d.ts +2 -2
  12. package/dist/auth-api.js +4 -2
  13. package/dist/auth-api.js.map +1 -1
  14. package/dist/backend/antigravity.d.ts +3 -0
  15. package/dist/backend/antigravity.js +9 -0
  16. package/dist/backend/antigravity.js.map +1 -1
  17. package/dist/backend/claude-carry.d.ts +22 -0
  18. package/dist/backend/claude-carry.js +170 -0
  19. package/dist/backend/claude-carry.js.map +1 -0
  20. package/dist/backend/claude-code.d.ts +65 -2
  21. package/dist/backend/claude-code.js +226 -33
  22. package/dist/backend/claude-code.js.map +1 -1
  23. package/dist/backend/codex-metadata.d.ts +33 -0
  24. package/dist/backend/codex-metadata.js +122 -18
  25. package/dist/backend/codex-metadata.js.map +1 -1
  26. package/dist/backend/codex.d.ts +3 -0
  27. package/dist/backend/codex.js +17 -1
  28. package/dist/backend/codex.js.map +1 -1
  29. package/dist/backend/credential-profile.d.ts +40 -3
  30. package/dist/backend/credential-profile.js +113 -5
  31. package/dist/backend/credential-profile.js.map +1 -1
  32. package/dist/backend/effort-metadata.d.ts +1 -1
  33. package/dist/backend/effort-metadata.js +3 -2
  34. package/dist/backend/effort-metadata.js.map +1 -1
  35. package/dist/backend/grok.d.ts +4 -0
  36. package/dist/backend/grok.js +14 -0
  37. package/dist/backend/grok.js.map +1 -1
  38. package/dist/backend/kiro-engine-ledger.d.ts +5 -0
  39. package/dist/backend/kiro-engine-ledger.js +20 -0
  40. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  41. package/dist/backend/kiro-identity.d.ts +6 -0
  42. package/dist/backend/kiro-identity.js +19 -5
  43. package/dist/backend/kiro-identity.js.map +1 -1
  44. package/dist/backend/kiro.d.ts +39 -3
  45. package/dist/backend/kiro.js +143 -19
  46. package/dist/backend/kiro.js.map +1 -1
  47. package/dist/backend/launch-effort.d.ts +33 -0
  48. package/dist/backend/launch-effort.js +123 -0
  49. package/dist/backend/launch-effort.js.map +1 -0
  50. package/dist/backend/muse.d.ts +84 -0
  51. package/dist/backend/muse.js +449 -53
  52. package/dist/backend/muse.js.map +1 -1
  53. package/dist/backend/types.d.ts +33 -4
  54. package/dist/backend/types.js +2 -1
  55. package/dist/backend/types.js.map +1 -1
  56. package/dist/backend-command.d.ts +91 -0
  57. package/dist/backend-command.js +122 -0
  58. package/dist/backend-command.js.map +1 -0
  59. package/dist/background-work-ended.d.ts +18 -0
  60. package/dist/background-work-ended.js +30 -0
  61. package/dist/background-work-ended.js.map +1 -0
  62. package/dist/cache-service.d.ts +2 -0
  63. package/dist/cache-service.js +2 -1
  64. package/dist/cache-service.js.map +1 -1
  65. package/dist/channel/adapters/discord.d.ts +24 -1
  66. package/dist/channel/adapters/discord.js +87 -2
  67. package/dist/channel/adapters/discord.js.map +1 -1
  68. package/dist/channel/adapters/telegram.d.ts +24 -2
  69. package/dist/channel/adapters/telegram.js +118 -20
  70. package/dist/channel/adapters/telegram.js.map +1 -1
  71. package/dist/channel/mcp-server.js +9 -1
  72. package/dist/channel/mcp-server.js.map +1 -1
  73. package/dist/channel/mcp-tools.d.ts +11 -0
  74. package/dist/channel/mcp-tools.js +24 -13
  75. package/dist/channel/mcp-tools.js.map +1 -1
  76. package/dist/channel/message-edit-errors.d.ts +5 -0
  77. package/dist/channel/message-edit-errors.js +24 -0
  78. package/dist/channel/message-edit-errors.js.map +1 -0
  79. package/dist/channel/telegram-markdown.d.ts +6 -0
  80. package/dist/channel/telegram-markdown.js +104 -0
  81. package/dist/channel/telegram-markdown.js.map +1 -0
  82. package/dist/channel/tool-router.js +9 -3
  83. package/dist/channel/tool-router.js.map +1 -1
  84. package/dist/channel/types.d.ts +39 -3
  85. package/dist/cli.js +133 -28
  86. package/dist/cli.js.map +1 -1
  87. package/dist/command-table.d.ts +2 -2
  88. package/dist/command-table.js +6 -3
  89. package/dist/command-table.js.map +1 -1
  90. package/dist/config-validator.js +59 -0
  91. package/dist/config-validator.js.map +1 -1
  92. package/dist/cross-instance-envelope.d.ts +15 -2
  93. package/dist/cross-instance-envelope.js +29 -5
  94. package/dist/cross-instance-envelope.js.map +1 -1
  95. package/dist/daemon.d.ts +156 -1
  96. package/dist/daemon.js +764 -89
  97. package/dist/daemon.js.map +1 -1
  98. package/dist/delivery-outbox.d.ts +62 -7
  99. package/dist/delivery-outbox.js +309 -22
  100. package/dist/delivery-outbox.js.map +1 -1
  101. package/dist/delivery-reconciliation.d.ts +18 -1
  102. package/dist/delivery-reconciliation.js +67 -2
  103. package/dist/delivery-reconciliation.js.map +1 -1
  104. package/dist/fleet-context.d.ts +10 -1
  105. package/dist/fleet-manager.d.ts +345 -10
  106. package/dist/fleet-manager.js +1868 -174
  107. package/dist/fleet-manager.js.map +1 -1
  108. package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
  109. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +92 -21
  110. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +27 -3
  111. package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
  112. package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
  113. package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
  114. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
  115. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
  116. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
  117. package/dist/general-knowledge/skills/model-discovery/SKILL.md +9 -9
  118. package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
  119. package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
  120. package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
  121. package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
  122. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +8 -3
  123. package/dist/general-knowledge/steering/core-rules.md +8 -7
  124. package/dist/host-memory.d.ts +10 -0
  125. package/dist/host-memory.js +33 -1
  126. package/dist/host-memory.js.map +1 -1
  127. package/dist/human-delivery-receipts.d.ts +83 -0
  128. package/dist/human-delivery-receipts.js +194 -0
  129. package/dist/human-delivery-receipts.js.map +1 -0
  130. package/dist/instance-avatar.d.ts +36 -0
  131. package/dist/instance-avatar.js +57 -0
  132. package/dist/instance-avatar.js.map +1 -0
  133. package/dist/instance-config-impact.js +6 -0
  134. package/dist/instance-config-impact.js.map +1 -1
  135. package/dist/instance-lifecycle.d.ts +29 -6
  136. package/dist/instance-lifecycle.js +79 -28
  137. package/dist/instance-lifecycle.js.map +1 -1
  138. package/dist/instructions.d.ts +3 -1
  139. package/dist/instructions.js +11 -8
  140. package/dist/instructions.js.map +1 -1
  141. package/dist/kiro-log-gate.d.ts +42 -0
  142. package/dist/kiro-log-gate.js +39 -0
  143. package/dist/kiro-log-gate.js.map +1 -0
  144. package/dist/locale.js +200 -44
  145. package/dist/locale.js.map +1 -1
  146. package/dist/log-service.d.ts +99 -0
  147. package/dist/log-service.js +243 -0
  148. package/dist/log-service.js.map +1 -0
  149. package/dist/log-steps.d.ts +169 -0
  150. package/dist/log-steps.js +858 -0
  151. package/dist/log-steps.js.map +1 -0
  152. package/dist/login-controller.d.ts +20 -3
  153. package/dist/login-controller.js +71 -19
  154. package/dist/login-controller.js.map +1 -1
  155. package/dist/login-flows.d.ts +5 -2
  156. package/dist/login-flows.js +5 -4
  157. package/dist/login-flows.js.map +1 -1
  158. package/dist/memory-pressure.d.ts +13 -1
  159. package/dist/memory-pressure.js +50 -10
  160. package/dist/memory-pressure.js.map +1 -1
  161. package/dist/needs-you-hub.js +2 -0
  162. package/dist/needs-you-hub.js.map +1 -1
  163. package/dist/needs-you-live.d.ts +6 -0
  164. package/dist/needs-you-live.js +23 -3
  165. package/dist/needs-you-live.js.map +1 -1
  166. package/dist/next-steps.js +1 -1
  167. package/dist/next-steps.js.map +1 -1
  168. package/dist/outbound-handlers.d.ts +100 -0
  169. package/dist/outbound-handlers.js +263 -96
  170. package/dist/outbound-handlers.js.map +1 -1
  171. package/dist/outbound-schemas.d.ts +26 -15
  172. package/dist/outbound-schemas.js +24 -7
  173. package/dist/outbound-schemas.js.map +1 -1
  174. package/dist/public-link-progress.d.ts +1 -1
  175. package/dist/public-link-progress.js +8 -2
  176. package/dist/public-link-progress.js.map +1 -1
  177. package/dist/public-preview.d.ts +101 -0
  178. package/dist/public-preview.js +236 -0
  179. package/dist/public-preview.js.map +1 -0
  180. package/dist/public-web-gateway.js +11 -4
  181. package/dist/public-web-gateway.js.map +1 -1
  182. package/dist/public-web-link.d.ts +24 -1
  183. package/dist/public-web-link.js +63 -9
  184. package/dist/public-web-link.js.map +1 -1
  185. package/dist/reply-buttons-controller.d.ts +3 -0
  186. package/dist/reply-buttons-controller.js +3 -2
  187. package/dist/reply-buttons-controller.js.map +1 -1
  188. package/dist/restart-guard.d.ts +6 -3
  189. package/dist/restart-guard.js +6 -3
  190. package/dist/restart-guard.js.map +1 -1
  191. package/dist/restart-progress.d.ts +33 -0
  192. package/dist/restart-progress.js +30 -0
  193. package/dist/restart-progress.js.map +1 -1
  194. package/dist/service-activation.d.ts +15 -7
  195. package/dist/service-activation.js +46 -29
  196. package/dist/service-activation.js.map +1 -1
  197. package/dist/service-installer.d.ts +31 -0
  198. package/dist/service-installer.js +41 -2
  199. package/dist/service-installer.js.map +1 -1
  200. package/dist/settings-api.js +85 -25
  201. package/dist/settings-api.js.map +1 -1
  202. package/dist/settings-change.js +72 -1
  203. package/dist/settings-change.js.map +1 -1
  204. package/dist/settings-effect.js +28 -4
  205. package/dist/settings-effect.js.map +1 -1
  206. package/dist/settings-http-confirmation.js +7 -0
  207. package/dist/settings-http-confirmation.js.map +1 -1
  208. package/dist/setup-form.d.ts +1 -1
  209. package/dist/setup-form.js +1 -1
  210. package/dist/spawn-gate.js +5 -1
  211. package/dist/spawn-gate.js.map +1 -1
  212. package/dist/spawn-hold-reply.d.ts +12 -0
  213. package/dist/spawn-hold-reply.js +39 -0
  214. package/dist/spawn-hold-reply.js.map +1 -0
  215. package/dist/status-emojis.d.ts +10 -3
  216. package/dist/status-emojis.js +11 -2
  217. package/dist/status-emojis.js.map +1 -1
  218. package/dist/systemd-peer-bus.d.ts +95 -0
  219. package/dist/systemd-peer-bus.js +430 -0
  220. package/dist/systemd-peer-bus.js.map +1 -0
  221. package/dist/systemd-runtime.d.ts +27 -1
  222. package/dist/systemd-runtime.js +69 -7
  223. package/dist/systemd-runtime.js.map +1 -1
  224. package/dist/tips.js +4 -4
  225. package/dist/tips.js.map +1 -1
  226. package/dist/tool-progress.js +3 -2
  227. package/dist/tool-progress.js.map +1 -1
  228. package/dist/topic-commands.d.ts +42 -2
  229. package/dist/topic-commands.js +92 -7
  230. package/dist/topic-commands.js.map +1 -1
  231. package/dist/transcript-delta-reader.d.ts +3 -3
  232. package/dist/transcript-delta-reader.js +8 -8
  233. package/dist/transcript-delta-reader.js.map +1 -1
  234. package/dist/transcript-jsonl.d.ts +80 -3
  235. package/dist/transcript-jsonl.js +147 -10
  236. package/dist/transcript-jsonl.js.map +1 -1
  237. package/dist/transcript-monitor.d.ts +19 -0
  238. package/dist/transcript-monitor.js +87 -15
  239. package/dist/transcript-monitor.js.map +1 -1
  240. package/dist/transcript-sources.d.ts +78 -0
  241. package/dist/transcript-sources.js +375 -64
  242. package/dist/transcript-sources.js.map +1 -1
  243. package/dist/transcript-turns.d.ts +15 -2
  244. package/dist/transcript-turns.js +15 -2
  245. package/dist/transcript-turns.js.map +1 -1
  246. package/dist/tunnel/cloudflared.d.ts +7 -15
  247. package/dist/tunnel/cloudflared.js +36 -1
  248. package/dist/tunnel/cloudflared.js.map +1 -1
  249. package/dist/tunnel/purpose-lane.d.ts +5 -1
  250. package/dist/tunnel/purpose-lane.js +14 -7
  251. package/dist/tunnel/purpose-lane.js.map +1 -1
  252. package/dist/tunnel/types.d.ts +5 -1
  253. package/dist/tunnel/types.js.map +1 -1
  254. package/dist/turn-resume.d.ts +2 -0
  255. package/dist/turn-resume.js +1 -1
  256. package/dist/turn-resume.js.map +1 -1
  257. package/dist/types.d.ts +25 -0
  258. package/dist/ui/chat-commands.js +1 -0
  259. package/dist/ui/chat-emojis.js +33 -0
  260. package/dist/ui/chat-mentions.js +63 -0
  261. package/dist/ui/chat-render.js +42 -2
  262. package/dist/ui/chat-store.js +2 -2
  263. package/dist/ui/chat-strings.js +38 -20
  264. package/dist/ui/chat-thread.js +69 -12
  265. package/dist/ui/chat-time.js +80 -0
  266. package/dist/ui/panel-chat.js +39 -10
  267. package/dist/ui/panel-details.js +2 -0
  268. package/dist/ui/panel-fleet.js +199 -57
  269. package/dist/ui/panel-log.js +428 -0
  270. package/dist/ui/panel-needs.js +6 -2
  271. package/dist/ui/panel-settings.js +137 -32
  272. package/dist/ui/preview-panel.js +55 -15
  273. package/dist/ui/preview-status.js +28 -0
  274. package/dist/ui/preview.js +44 -3
  275. package/dist/ui/settings-dialogs.js +7 -2
  276. package/dist/ui/settings-model.js +2 -1
  277. package/dist/ui/settings-strings.js +46 -4
  278. package/dist/ui/settings-token.js +1 -1
  279. package/dist/ui/shared/app-i18n.js +43 -17
  280. package/dist/ui/shared/app-prefs.js +250 -0
  281. package/dist/ui/shared/app-route.js +7 -2
  282. package/dist/ui/shared/app-shell.js +165 -8
  283. package/dist/ui/shared/app-store.js +5 -1
  284. package/dist/ui/shared/app-stream.js +23 -5
  285. package/dist/ui/shared/app.css +155 -22
  286. package/dist/ui/shared/app.js +11 -1
  287. package/dist/ui/shared/fonts/README.md +1 -1
  288. package/dist/ui/shared/header-tools.js +55 -5
  289. package/dist/ui/shared/instance-nav.js +241 -45
  290. package/dist/ui/shared/instance-switch.js +4 -4
  291. package/dist/ui/shared/panel-view.js +6 -0
  292. package/dist/ui/shared/signin.js +7 -7
  293. package/dist/ui/shared/theme.js +39 -7
  294. package/dist/ui/shared/tokens.css +1 -1
  295. package/dist/ui/shared/ui-icons.js +1 -0
  296. package/dist/ui/shared/usage-dialog.js +23 -5
  297. package/dist/ui/shared/view-strings.js +32 -4
  298. package/dist/ui/signin.html +1 -1
  299. package/dist/unrecognised-prompt.d.ts +23 -0
  300. package/dist/unrecognised-prompt.js +74 -0
  301. package/dist/unrecognised-prompt.js.map +1 -0
  302. package/dist/update-install.d.ts +28 -2
  303. package/dist/update-install.js +22 -4
  304. package/dist/update-install.js.map +1 -1
  305. package/dist/update-marker.d.ts +51 -0
  306. package/dist/update-marker.js +61 -0
  307. package/dist/update-marker.js.map +1 -1
  308. package/dist/update-preflight.d.ts +100 -0
  309. package/dist/update-preflight.js +264 -0
  310. package/dist/update-preflight.js.map +1 -0
  311. package/dist/updater-caller.d.ts +31 -0
  312. package/dist/updater-caller.js +109 -0
  313. package/dist/updater-caller.js.map +1 -0
  314. package/dist/usage/i18n-keys.d.ts +1 -1
  315. package/dist/usage/i18n-keys.js +1 -1
  316. package/dist/usage/i18n-keys.js.map +1 -1
  317. package/dist/usage/providers.d.ts +34 -5
  318. package/dist/usage/providers.js +160 -27
  319. package/dist/usage/providers.js.map +1 -1
  320. package/dist/usage/statusline-usage.d.ts +6 -2
  321. package/dist/usage/statusline-usage.js +10 -4
  322. package/dist/usage/statusline-usage.js.map +1 -1
  323. package/dist/view-api.d.ts +1 -1
  324. package/dist/view-api.js +38 -8
  325. package/dist/view-api.js.map +1 -1
  326. package/dist/wake-coordinator.d.ts +11 -0
  327. package/dist/wake-coordinator.js +38 -4
  328. package/dist/wake-coordinator.js.map +1 -1
  329. package/dist/web-api.d.ts +49 -2
  330. package/dist/web-api.js +421 -31
  331. package/dist/web-api.js.map +1 -1
  332. package/dist/web-auth.d.ts +2 -2
  333. package/dist/web-auth.js +9 -4
  334. package/dist/web-auth.js.map +1 -1
  335. package/dist/web-commands.d.ts +27 -4
  336. package/dist/web-commands.js +19 -4
  337. package/dist/web-commands.js.map +1 -1
  338. package/dist/web-host-guard.d.ts +3 -11
  339. package/dist/web-host-guard.js +3 -12
  340. package/dist/web-host-guard.js.map +1 -1
  341. package/dist/web-images.d.ts +47 -0
  342. package/dist/web-images.js +169 -0
  343. package/dist/web-images.js.map +1 -0
  344. package/dist/web-mentions.d.ts +44 -0
  345. package/dist/web-mentions.js +75 -0
  346. package/dist/web-mentions.js.map +1 -0
  347. package/dist/web-prefs.d.ts +63 -0
  348. package/dist/web-prefs.js +265 -0
  349. package/dist/web-prefs.js.map +1 -0
  350. package/dist/web-preview.d.ts +14 -1
  351. package/dist/web-preview.js +35 -13
  352. package/dist/web-preview.js.map +1 -1
  353. package/dist/web-reply-files.d.ts +45 -0
  354. package/dist/web-reply-files.js +223 -0
  355. package/dist/web-reply-files.js.map +1 -0
  356. package/dist/web-schedules.d.ts +12 -0
  357. package/dist/web-schedules.js +34 -0
  358. package/dist/web-schedules.js.map +1 -0
  359. package/dist/web-server-drain.d.ts +10 -0
  360. package/dist/web-server-drain.js +50 -0
  361. package/dist/web-server-drain.js.map +1 -0
  362. package/dist/web-session.d.ts +36 -1
  363. package/dist/web-session.js +53 -6
  364. package/dist/web-session.js.map +1 -1
  365. package/dist/web-settings-patch.d.ts +23 -0
  366. package/dist/web-settings-patch.js +77 -0
  367. package/dist/web-settings-patch.js.map +1 -0
  368. package/dist/web-shell-routes.d.ts +5 -1
  369. package/dist/web-shell-routes.js +10 -1
  370. package/dist/web-shell-routes.js.map +1 -1
  371. package/dist/web-upload.d.ts +17 -4
  372. package/dist/web-upload.js +64 -13
  373. package/dist/web-upload.js.map +1 -1
  374. package/dist/workflow-templates/default.md +1 -1
  375. package/package.json +1 -1
  376. package/templates/launchd.plist.ejs +3 -1
  377. package/templates/systemd.service.ejs +2 -1
@@ -68,6 +68,7 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
68
68
 
69
69
  .sidebar {
70
70
  width: var(--sidebar-w); flex-shrink: 0; display: flex; flex-direction: column; background: var(--bg-sidebar);
71
+ position: relative; /* #1722: the resize handle sits on its right edge (the width above: tokens.css, or this device's) */
71
72
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom) env(safe-area-inset-left);
72
73
  }
73
74
  .sb-collapsed .sidebar { display: none; }
@@ -103,11 +104,32 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
103
104
  .inst:hover { background: var(--surface-2); }
104
105
  .inst.active { background: var(--surface-2); }
105
106
  .inst .dot { margin-top: 8px; }
107
+ /* #1630: the avatar (a picture, an emoji or the first character), its status dot, and an optional ring in its bot's colour (--ring, set through the CSSOM). */
108
+ .av { position: relative; flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%; background: var(--surface-2); color: var(--text-2);
109
+ display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: 1; }
110
+ .av img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
111
+ .av.av-emoji { background: transparent; font-size: 18px; font-weight: normal; }
112
+ .av.av-emoji img { width: 22px; height: 22px; border-radius: 0; object-fit: contain; }
113
+ .av.ring { box-shadow: 0 0 0 2px var(--ring, transparent); }
114
+ .av-text { overflow: hidden; }
115
+ .av .av-dot { position: absolute; right: -2px; bottom: -2px; margin: 0; box-shadow: 0 0 0 2px var(--bg-sidebar); }
116
+ /* #1722: the CLI as a letter on the avatar's top-left corner — monochrome (the #1630 ring keeps the colour), cut out like the dot */
117
+ .av-cli { position: absolute; left: -4px; top: -4px; width: 14px; height: 14px; border-radius: 50%; box-sizing: border-box;
118
+ background: var(--surface); border: 1px solid var(--border-strong); box-shadow: 0 0 0 1.5px var(--bg-sidebar);
119
+ color: var(--text-2); font-size: 9px; line-height: 12px; font-weight: var(--fw-semibold); text-align: center; pointer-events: auto; }
120
+ .inst.active .av .av-cli, .inst:hover .av .av-cli { box-shadow: 0 0 0 1.5px var(--surface-2); }
121
+ .inst.active .av .av-dot, .inst:hover .av .av-dot { box-shadow: 0 0 0 2px var(--surface-2); }
122
+ .dot.hollow { background: transparent; border: 1.5px solid var(--text-3); }
123
+ /* On a grey avatar a grey dot needs more contrast: stopped is a stronger grey, paused a clear ring around the sidebar colour. */
124
+ .av .av-dot.off { background: var(--text-2); }
125
+ .av .av-dot.hollow { box-sizing: border-box; width: 10px; height: 10px; background: var(--bg-sidebar); border: 2.5px solid var(--text-2); }
126
+ .inst .av { margin-top: 1px; }
106
127
  .inst-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
107
128
  .inst-name { font-size: var(--fs-sm); line-height: var(--lh-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
129
+ /* #1722: the other name, under the one that leads — smaller, muted, truncated on its own */
130
+ .inst-alt { font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
108
131
  .inst.active .inst-name { font-weight: var(--fw-semibold); }
109
132
  .inst-sub { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-2); }
110
- .inst-alias { font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; max-width: 100%; }
111
133
  .badge-await {
112
134
  flex-shrink: 0; font-size: 11px; line-height: 16px; font-weight: var(--fw-semibold); padding: 0 var(--s-2); border-radius: var(--r-full);
113
135
  background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); white-space: nowrap;
@@ -128,6 +150,13 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
128
150
  .s-device { display: flex; flex-direction: column; gap: var(--s-3); }
129
151
  .s-device .sub { margin: 0; color: var(--text-3); font-size: var(--fs-sm); }
130
152
  .s-device-row { display: flex; gap: var(--s-2); }
153
+ /* #1620: Settings → Web — the lead, and the read-only list of what is set in fleet.yaml. */
154
+ .s-web .sub { margin: 0; color: var(--text-3); font-size: var(--fs-sm); }
155
+ .s-web-fields:disabled select { opacity: 0.6; } /* over the public link: dimmed like the inputs (.field input:disabled) */
156
+ .s-web-ro { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--s-1) var(--s-3); margin: 0; font-size: var(--fs-sm); }
157
+ .s-web-ro > div { display: contents; }
158
+ .s-web-ro dt { color: var(--text-3); }
159
+ .s-web-ro dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
131
160
  .prefs { display: flex; gap: var(--s-2); padding: var(--s-1) var(--s-3); }
132
161
  .pref { display: flex; align-items: center; gap: var(--s-1); color: var(--text-2); flex: 1; min-width: 0; }
133
162
  .pref select { flex: 1; min-width: 0; height: 30px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--text-2); font-size: var(--fs-xs); cursor: pointer; }
@@ -179,7 +208,7 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
179
208
  :root { --text-scale: 1; }
180
209
  :root[data-text-size="s"] { --text-scale: 0.9; }
181
210
  :root[data-text-size="l"] { --text-scale: 1.15; }
182
- .thread, .p-details .panel-body, .p-fleet .panel-body {
211
+ .thread, .p-details .panel-body, .p-fleet .panel-body, .p-log .panel-body {
183
212
  --fs-xs: calc(12px * var(--text-scale)); --lh-xs: calc(16px * var(--text-scale));
184
213
  --fs-sm: calc(13px * var(--text-scale)); --lh-sm: calc(18px * var(--text-scale));
185
214
  --fs-md: calc(15px * var(--text-scale)); --lh-md: calc(24px * var(--text-scale));
@@ -188,10 +217,40 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
188
217
  }
189
218
  /* #1523 N2: Details. */
190
219
  .p-details .d-tags { display: inline-flex; flex-wrap: wrap; gap: var(--s-1); }
220
+ /* #1218: an instance's Log — one column of steps, newest at the bottom; the filter bar above it. Classes only (#1300). */
221
+ .p-log .log-bar { display: flex; align-items: center; gap: var(--s-1); padding: var(--s-2) var(--s-4); border-bottom: 1px solid var(--border); flex: none; }
222
+ .p-log .log-bar .chip-btn.on { color: var(--text); background: var(--surface-2); font-weight: var(--fw-semibold); }
223
+ .p-log .log-live { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-3); }
224
+ .p-log .log-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
225
+ .p-log .log-item { padding: var(--s-2) var(--s-3); border-radius: var(--r-md); background: var(--surface); min-width: 0; overflow-wrap: anywhere; }
226
+ .p-log .log-item.turn-start { margin-top: var(--s-4); }
227
+ .p-log .log-item.k-user { background: var(--user-bubble); }
228
+ .p-log .log-item.k-turn_end, .p-log .log-item.k-state, .p-log .log-item.k-event { background: transparent; color: var(--text-3); padding-block: 2px; font-size: var(--fs-sm); }
229
+ .p-log .log-item.k-error, .p-log .log-item.s-failed { border-left: 3px solid var(--danger); }
230
+ .p-log .log-head { display: flex; align-items: baseline; gap: var(--s-2); min-width: 0; }
231
+ .p-log .log-icon { flex: none; }
232
+ .p-log .log-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
233
+ .p-log .log-cat { font-weight: var(--fw-semibold); }
234
+ .p-log .log-subject { font-size: var(--fs-sm); }
235
+ .p-log .log-time { flex: none; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
236
+ .p-log .log-status { flex: none; font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
237
+ .p-log .log-status.done { color: var(--ok); }
238
+ .p-log .log-status.failed { color: var(--danger); }
239
+ .p-log .log-status.interrupted { color: var(--warn, var(--text-3)); }
240
+ .p-log .log-status .spinner { display: inline-block; width: 8px; height: 8px; margin-right: 4px; border-radius: 50%; background: var(--accent); animation: pulse 1.2s ease-in-out infinite; }
241
+ .p-log .log-repeat { flex: none; font-size: var(--fs-xs); padding: 0 6px; border-radius: 8px; background: var(--surface-2); color: var(--text-2); }
242
+ .p-log .log-text { white-space: pre-wrap; margin-top: var(--s-1); font-size: var(--fs-sm); }
243
+ .p-log .log-text.mono { font-family: var(--font-mono); font-size: var(--fs-xs); }
244
+ .p-log .log-text.folded { max-height: 10.5em; overflow: hidden; mask-image: linear-gradient(to bottom, #000 70%, transparent); -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent); }
245
+ .p-log .link-btn { border: 0; background: none; padding: 2px 0; color: var(--accent); cursor: pointer; font-size: var(--fs-xs); }
246
+ .p-log .log-detail { margin-top: var(--s-1); display: flex; flex-direction: column; gap: var(--s-2); }
247
+ .p-log .log-sec-h { font-size: var(--fs-xs); color: var(--text-3); text-transform: uppercase; letter-spacing: 0.04em; }
248
+ .p-log .log-older, .p-log .log-start { text-align: center; margin: var(--s-2) 0; }
249
+ .p-log .log-off { padding: var(--s-6) var(--s-4); }
191
250
  .p-details .d-edit { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-2); }
192
251
  @media (max-width: 899px) {
193
252
  .panel-head:has(.inst-switch) { flex-wrap: wrap; }
194
- .inst-switch.seg { order: 3; flex-basis: 100%; display: grid; grid-template-columns: repeat(3, 1fr); padding: 0 0 var(--s-1); }
253
+ .inst-switch.seg { order: 3; flex-basis: 100%; display: grid; grid-template-columns: repeat(4, 1fr); padding: 0 0 var(--s-1); }
195
254
  .inst-switch .seg-item { justify-content: center; border-radius: var(--r-sm); }
196
255
  }
197
256
  .panel-body { flex: 1; min-height: 0; overflow-y: auto; }
@@ -284,6 +343,16 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
284
343
  .pill.open { background: var(--accent-dim); color: var(--accent); }
285
344
  .pill.claimed { background: var(--warn-bg); color: var(--warn); }
286
345
  .pill.done { color: var(--ok); }
346
+ /* #1724: a disabled schedule — its own look (not the green of "done"): a dashed, muted badge and a dimmed row */
347
+ .pill.off { background: transparent; color: var(--text-3); border: 1px dashed var(--border-strong); }
348
+ .row-item.is-off { opacity: 0.62; }
349
+ .row-item.sched { flex-wrap: nowrap; }
350
+ .row-item.sched .sched-text { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: 2px; }
351
+ .sched-actions { display: inline-flex; flex: none; gap: var(--s-1); }
352
+ .sched-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
353
+ .sched-sub { font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
354
+ @media (max-width: 899px) { .sched-sub { white-space: normal; } } /* a phone: the run time wraps rather than being cut */
355
+ .sched-mode { margin-bottom: var(--s-2); }
287
356
  .save-row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
288
357
 
289
358
  /* ── Chat ── */
@@ -292,11 +361,24 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
292
361
  .msg { font-size: var(--fs-md); line-height: var(--lh-md); overflow-wrap: anywhere; min-width: 0; }
293
362
  .msg .meta { display: flex; align-items: baseline; gap: var(--s-2); font-size: var(--fs-xs); color: var(--text-3); margin-bottom: var(--s-1); }
294
363
  .msg .sender { font-weight: var(--fw-semibold); color: var(--text-2); }
295
- .msg .time { font-family: var(--font-mono); }
364
+ /* The time never wraps or gives up its width to a long neighbour: a flex child shrinks to its min-content, and with
365
+ the message's overflow-wrap: anywhere that is one character wide (the #1672 status line's time, one glyph per line). */
366
+ .msg .time { font-family: var(--font-mono); white-space: nowrap; flex: none; }
296
367
  .msg.user { align-self: flex-end; max-width: min(80%, 620px); }
297
368
  .msg.user .meta { justify-content: flex-end; }
298
- .msg.user .body { background: var(--user-bubble); border-radius: var(--r-xl); padding: 10px var(--s-4); }
369
+ /* #1773: the bubble hugs its text. The message is as wide as its widest child — usually the meta row (sender, a time
370
+ such as 「昨天 下午03:58」, ticks) — and a block body would stretch to it; fit-content keeps a short message short, on
371
+ the right under its meta, and a long one still wraps at the message's max-width. */
372
+ .msg.user .body { background: var(--user-bubble); border-radius: var(--r-xl); padding: 10px var(--s-4); width: fit-content; max-width: 100%; margin-left: auto; }
299
373
  .msg.agent { align-self: stretch; }
374
+ /* #1672: a status line (another instance's message, a daemon notice): muted plain text, no bubble; a long one folds.
375
+ Its class is `status-line`, never `status`: that is the header's inline-flex badge class, and on a message it turned
376
+ the meta, body and tools into one flex row. */
377
+ .msg.status-line { align-self: stretch; font-size: var(--fs-sm); color: var(--text-3); }
378
+ .msg.status-line .st-text { white-space: pre-wrap; border-left: 2px solid var(--border); padding-left: var(--s-3); }
379
+ .msg.status-line.folded .st-text { max-height: 9em; overflow: hidden; mask-image: linear-gradient(to bottom, #000 60%, transparent); -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent); }
380
+ .msg.status-line .st-more { border: 0; background: none; color: var(--text-2); cursor: pointer; font-size: var(--fs-xs); padding: var(--s-1) 0 0 var(--s-3); }
381
+ .msg.status-line .st-more:hover { color: var(--text); }
300
382
  .msg-tools { display: flex; gap: var(--s-1); margin-top: var(--s-1); opacity: 0; transition: opacity var(--t-fast); }
301
383
  .msg:hover .msg-tools, .msg:focus-within .msg-tools { opacity: 1; }
302
384
  @media (hover: none) { .msg-tools { opacity: 1; } }
@@ -318,10 +400,13 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
318
400
  .text-card { margin-top: var(--s-2); border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--s-2) var(--s-3); }
319
401
  .text-card .tc-head { display: flex; align-items: center; gap: var(--s-2); }
320
402
  .text-card .tc-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-xs); font-family: var(--font-mono); color: var(--text-2); }
321
- .text-card .tc-body { margin-top: var(--s-2); max-height: 480px; overflow: auto; }
403
+ /* #1812: the scroller spans the card's padding box and insets its own content — 12px on both sides, from the border on
404
+ the left and from the scrollbar (or the border, with overlay scrollbars) on the right. A stable gutter keeps a
405
+ classic scrollbar off the content, and the content from shifting when the scrollbar comes and goes. */
406
+ .text-card .tc-body { margin: var(--s-2) calc(-1 * var(--s-3)) 0; padding: 0 var(--s-3); max-height: 480px; overflow: auto; scrollbar-gutter: stable; }
322
407
  .text-card .tc-body[hidden] { display: none; }
323
408
  .text-card .tc-pre { margin: 0; white-space: pre-wrap; word-break: break-word; font-size: var(--fs-xs); }
324
- .text-card .tc-table-wrap { overflow-x: auto; }
409
+ .text-card .tc-table-wrap { overflow-x: auto; padding-bottom: var(--s-3); }
325
410
  .text-card .tc-table { border-collapse: collapse; font-size: var(--fs-xs); }
326
411
  .text-card .tc-table th, .text-card .tc-table td { border: 1px solid var(--border); padding: 2px 6px; text-align: left; vertical-align: top; }
327
412
  .text-card .tc-more { margin-top: var(--s-2); }
@@ -354,10 +439,17 @@ html.lb-open .scroller { overflow: hidden; }
354
439
  .msg .md ul { list-style: disc; } .msg .md ol { list-style: decimal; }
355
440
  .msg .md li > ul, .msg .md li > ol { margin: 2px 0; }
356
441
  .msg .md hr { border: 0; border-top: 1px solid var(--border); margin: var(--s-3) 0; }
357
- .msg .md .tbl { overflow-x: auto; margin: var(--s-2) 0; }
442
+ /* #1812: a table too wide for its message scrolls in this wrapper (see the cell rule below); the bottom padding keeps an
443
+ overlay scrollbar off the last row. */
444
+ .msg .md .tbl { overflow-x: auto; margin: var(--s-2) 0 0; padding-bottom: var(--s-3); }
358
445
  .msg .md table { border-collapse: collapse; font-size: var(--fs-sm); }
359
446
  .msg .md th, .msg .md td { border: 1px solid var(--border); padding: 5px 9px; text-align: left; vertical-align: top; }
360
447
  .msg .md th { background: var(--surface); font-weight: var(--fw-semibold); }
448
+ /* #1812: table cells keep their words whole. The message's `overflow-wrap: anywhere` also shrinks a cell's minimum
449
+ width, so auto table layout squeezed the columns and split words ("directo|r"); `break-word` breaks a word only when
450
+ it alone is wider than its line and leaves the minimum at whole words, so a wide table overflows into its scrolling
451
+ wrapper instead. A column keeps at least 5em: CJK text can break between any two characters. */
452
+ .msg .md th, .msg .md td, .text-card .tc-table th, .text-card .tc-table td { overflow-wrap: break-word; word-break: normal; min-width: 5em; }
361
453
  .msg .md .al-c { text-align: center; } .msg .md .al-r { text-align: right; }
362
454
  .msg .md h3, .msg .md h4, .msg .md h5, .msg .md h6 { margin: var(--s-3) 0 var(--s-2); font-size: var(--fs-md); font-weight: var(--fw-semibold); }
363
455
  .msg .md .tk-k { color: var(--tk-k); } .msg .md .tk-s { color: var(--tk-s); } .msg .md .tk-n { color: var(--tk-n); } .msg .md .tk-c { color: var(--tk-c); font-style: italic; }
@@ -566,11 +658,32 @@ html.lb-open .scroller { overflow: hidden; }
566
658
  .caret.folded { transform: rotate(-90deg); }
567
659
  .caret.flipped { transform: rotate(180deg); } /* the card's toggle: up when closed (it opens upwards) */
568
660
  .v-inst { align-items: center; }
661
+ /* #1664: favorites — the star after a row (on hover or focus on a desktop, always when starred; on a phone in Edit) */
662
+ .inst-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); }
663
+ .inst-head .side-label { flex: 1; min-width: 0; }
664
+ .fav-edit { margin-right: var(--s-2); display: inline-flex; align-items: center; gap: var(--s-1); }
665
+ .v-li { display: flex; align-items: center; gap: 2px; }
666
+ .v-li > .inst { flex: 1; min-width: 0; }
667
+ .inst-star, .inst-move { flex: none; width: 28px; height: 28px; padding: 0; color: var(--text-3); }
668
+ .inst-star.on { color: var(--warn); }
669
+ .inst-star.on svg, .fav-mark { fill: currentColor; }
670
+ .fav-head { cursor: default; }
671
+ .fav-mark { color: var(--warn); }
672
+ .inst-nav:not(.fav-editing) .inst-star:not(.on) { visibility: hidden; }
673
+ /* As specific as the rule that hides it (and later), so hover and focus win. */
674
+ @media (hover: hover) { .inst-nav:not(.fav-editing) .v-li:hover .inst-star, .inst-nav:not(.fav-editing) .v-li:focus-within .inst-star { visibility: visible; } }
675
+ @media (hover: none) { .inst-nav:not(.fav-editing) .inst-star { display: none; } }
676
+ /* Edit: room for ↑ ↓ ☆ — the context and CLI marks step aside so the name keeps its width. */
677
+ .fav-editing .v-li .v-meta { display: none; }
678
+ @media (max-width: 899px) { .fav-editing .inst-star, .fav-editing .inst-move { width: 36px; height: 36px; } }
569
679
  .v-inst .dot { margin-top: 0; }
570
680
  .v-meta { color: var(--text-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
571
- .cli-icon { width: 10px; height: 10px; flex: 0 0 10px; border-radius: 50%; border: 1px solid var(--border-strong); }
572
- .cli-claude-code { background: #c678dd; } .cli-kiro-cli { background: #58a6ff; } .cli-codex { background: #3fb950; }
573
- .cli-grok { background: #0d1117; border-color: #f0f6fc; } .cli-antigravity { background: #f85149; } .cli-opencode { background: #f0f6fc; } .cli-other { background: #6e7681; }
681
+ /* #1722: the sidebar's width — a separator on its right edge (drag, arrows, Home/End, double-click resets); not on a phone */
682
+ .side-resize { position: absolute; top: 0; right: -3px; width: 6px; height: 100%; z-index: 5; cursor: col-resize; touch-action: none; }
683
+ .side-resize:hover, .side-resize:focus-visible, .side-resize.dragging { background: var(--accent-dim, var(--border-strong)); }
684
+ .side-resize:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
685
+ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-select: none; }
686
+ @media (max-width: 899px) { .side-resize { display: none; } }
574
687
  .dragging { opacity: 0.4; }
575
688
  .drop-before { box-shadow: inset 0 2px 0 var(--accent); }
576
689
  .drop-after { box-shadow: inset 0 -2px 0 var(--accent); }
@@ -581,15 +694,21 @@ html.lb-open .scroller { overflow: hidden; }
581
694
  .v-filter-box input::placeholder { color: var(--text-3); }
582
695
  .v-filter-clear { width: 28px; height: 28px; }
583
696
  .v-filter .note { padding: var(--s-1) var(--s-1) 0; font-variant-numeric: tabular-nums; }
584
- /* alpha.2 (N1): status and CLI chips under the filter box — each a <details> with checkboxes. */
585
- .nav-facets { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s-1) var(--s-2); padding: var(--s-2) var(--s-1) 0; }
586
- .nav-facet { font-size: var(--fs-xs); color: var(--text-2); }
587
- .nav-facet > summary { cursor: pointer; list-style: none; padding: 2px var(--s-2); border-radius: var(--r-sm); }
588
- .nav-facet > summary::-webkit-details-marker { display: none; }
589
- .nav-facet > summary::after { content: " ▾"; color: var(--text-3); }
590
- .nav-facet[open] > summary::after { content: " ▴"; }
591
- .nav-facet > summary:hover { background: var(--hover); }
592
- .nav-facet > summary:focus-visible { outline: 2px solid var(--accent); }
697
+ /* alpha.2 (N1): status and CLI chips under the filter box — each a group of checkboxes under its header (#1700). */
698
+ /* #1776: the "N / M" count is on the headers' row, at its right end — not a row of its own. The headers (and an open
699
+ group's list, the reset) wrap in their own box beside it, so a filter's summary or a narrow sidebar moves a header
700
+ down, never the count; the count is as tall as a header, so the row does not grow. */
701
+ .nav-facets { display: flex; align-items: flex-start; gap: var(--s-2); padding: var(--s-2) var(--s-1) 0; }
702
+ .nav-facet-groups { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s-1) var(--s-2); flex: 1 1 auto; min-width: 0; }
703
+ .nav-facets .nav-shown { flex: none; margin: 0 0 0 auto; padding: 2px 0; white-space: nowrap; }
704
+ .nav-facet { font-size: var(--fs-xs); color: var(--text-2); min-width: 0; max-width: 100%; }
705
+ /* #1700: a facet group's header — the whole row toggles its list; folded with a filter set, a short summary. */
706
+ .nav-facet-head { display: inline-flex; align-items: center; gap: var(--s-1); max-width: 100%; padding: 2px var(--s-2); border: 0; border-radius: var(--r-sm);
707
+ background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left; white-space: nowrap; }
708
+ .nav-facet-head:hover { background: var(--hover); }
709
+ .nav-facet-head:focus-visible { outline: 2px solid var(--accent); }
710
+ .nav-facet-head .caret { color: var(--text-3); flex: none; }
711
+ .nav-facet-sum { min-width: 0; max-width: 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--accent); }
593
712
  .nav-facet-n { display: inline-block; min-width: 16px; padding: 0 4px; border-radius: 8px; background: var(--accent); color: var(--on-accent, #fff); text-align: center; font-variant-numeric: tabular-nums; }
594
713
  .nav-facet-list { display: flex; flex-direction: column; gap: 2px; padding: var(--s-1) var(--s-2); }
595
714
  .nav-chip { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; color: var(--text); }
@@ -619,8 +738,11 @@ html.lb-open .scroller { overflow: hidden; }
619
738
  .help-list { list-style: disc; padding-left: var(--s-5); display: flex; flex-direction: column; gap: var(--s-2); font-size: var(--fs-sm); }
620
739
  .err { color: var(--danger); font-size: var(--fs-sm); }
621
740
  .u-provider { padding: var(--s-3) var(--s-4); border-radius: var(--r-md); background: var(--surface); display: flex; flex-direction: column; gap: var(--s-2); }
622
- .u-head { display: flex; align-items: center; gap: var(--s-2); }
623
- .u-order { margin-left: auto; display: flex; gap: 2px; }
741
+ .u-head { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
742
+ /* #1749: one line beside ↑↓ — the name gives way first, then the plan badge (each with an ellipsis); ↑↓ never wrap. */
743
+ .u-head strong { min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
744
+ .u-plan { min-width: 0; flex: none; max-width: 55%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
745
+ .u-order { margin-left: auto; display: flex; gap: 2px; flex: none; }
624
746
  .u-order .icon-btn { width: 28px; height: 28px; }
625
747
  .u-order .icon-btn:disabled { opacity: 0.3; }
626
748
  .u-metric { display: flex; flex-direction: column; gap: var(--s-1); }
@@ -637,6 +759,12 @@ html.lb-open .scroller { overflow: hidden; }
637
759
  .u-sub { color: var(--text-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
638
760
  .u-err { color: var(--warn); font-size: var(--fs-sm); }
639
761
  .u-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); }
762
+ /* #1673: the usage rail beside the page on a wide screen (header-tools.js UsageRail; shown only at min-width 1200px). */
763
+ .u-rail { width: 320px; flex-shrink: 0; display: flex; flex-direction: column; min-height: 0; border-left: 1px solid var(--border); background: var(--bg); }
764
+ .u-rail-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); padding: var(--s-3) var(--s-3) var(--s-2) var(--s-4); }
765
+ .u-rail-head h2 { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-semibold); }
766
+ .u-rail-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--s-3) var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
767
+ @media (max-width: 1199px) { .u-rail { display: none; } }
640
768
  @media (max-width: 899px) {
641
769
  .hide-narrow { display: none; }
642
770
  .v-card { padding-left: var(--s-3); padding-right: var(--s-3); }
@@ -934,3 +1062,8 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
934
1062
  .cmd-palette .cmd-desc { white-space: normal; flex-basis: 100%; }
935
1063
  .cmd-choices .btn, .quick-actions .btn { min-height: var(--tap); }
936
1064
  }
1065
+ /* #1630: a Discord server emoji in a chat message, the size of the text around it. */
1066
+ .md img.emoji { display: inline-block; width: 1.375em; height: 1.375em; object-fit: contain; vertical-align: -0.3em; margin: 0 0.05em; }
1067
+ /* #1682: a mention, as Discord draws one — accent text on a tint of it; the id (or the Telegram handle) on hover. */
1068
+ .md .mention { display: inline; padding: 0 0.2em; border-radius: var(--r-sm); color: var(--accent); background: var(--accent-dim);
1069
+ font-weight: var(--fw-medium); white-space: nowrap; }
@@ -5,6 +5,8 @@
5
5
  // session, and so is View (panel-view.js, loaded on demand). The panels that need a session live under /ui/js/ and are
6
6
  // reached only by the dynamic imports below, in "full" mode. A test walks the static imports over real HTTP and asserts
7
7
  // none of them names /ui/.
8
+ // #1665 first: the host's preferences go into this browser's cache before any module below reads its key.
9
+ import { bootPrefs } from "./app-prefs.js";
8
10
  import { html, render } from "./app-html.js";
9
11
  import { Shell, handleKey, showDialog, closeDrawer, openDrawer, shellStore, retryable, setNewInstanceOpener } from "./app-shell.js";
10
12
  import { installSwipe } from "./app-swipe.js";
@@ -15,11 +17,12 @@ import { chatPath } from "./app-route.js";
15
17
  import { startNeedsNotifier } from "./app-needs.js";
16
18
  import { toast } from "./ui-toast.js";
17
19
  import { t } from "./app-i18n.js";
18
- import { initTextSize } from "./header-tools.js";
20
+ import { initTextSize, initUsageRail } from "./header-tools.js";
19
21
 
20
22
  const boot = document.body.dataset;
21
23
  // #1523 N3: this device's text size, on the root before the first paint of any panel.
22
24
  initTextSize();
25
+ initUsageRail();
23
26
  // "full": a signed-in page. "view-only": an anonymous reader of /view under web.view_access: open — no stream, no
24
27
  // session-only panel, nothing under /ui/ is ever imported or read (#1408 §3).
25
28
  const mode = boot.mode === "view-only" ? "view-only" : "full";
@@ -48,6 +51,8 @@ if (mode === "full") {
48
51
  stream.on("hydration", (hydration) => appStore.set({ hydration }));
49
52
  appStore.set({ retryHydration: () => stream.catchUp() });
50
53
  stream.on("needs", (d) => appStore.set({ needs: Array.isArray(d && d.items) ? d.items : [] }));
54
+ // #1218: a panel's way to hear one stream event (the Log's steps_tick), unsubscribed by its lease.
55
+ appStore.set({ streamOn: (name, fn) => stream.on(name, fn) });
51
56
  appStore.set({ connection: stream.connection() });
52
57
 
53
58
  // The chat's store must hear every message from the first frame, whatever panel is open: its module boots once,
@@ -64,6 +69,7 @@ if (mode === "full") {
64
69
  const loadSettings = retryable((a) => import(retryUrl("/ui/js/panel-settings.js", a)));
65
70
  const loadNeeds = retryable((a) => import(retryUrl("/ui/js/panel-needs.js", a)));
66
71
  const loadDetails = retryable((a) => import(retryUrl("/ui/js/panel-details.js", a)));
72
+ const loadLog = retryable((a) => import(retryUrl("/ui/js/panel-log.js", a)));
67
73
  // The stream opens once the chat listens, so the frames sent on connect (status, open prompts, ticks) reach it too.
68
74
  // If the chat cannot load (a session that just ended), the stream still opens for the sidebar.
69
75
  const chatBoot = loadChat();
@@ -77,8 +83,12 @@ if (mode === "full") {
77
83
  panels.set("needs", { load: () => loadNeeds().then((m) => m.NeedsPanel) });
78
84
  // #1523 N2: one instance's Details (/ui/fleet/agent/<name>).
79
85
  panels.set("details", { load: () => loadDetails().then((m) => m.DetailsPanel) });
86
+ // #1218: one instance's Log (/ui/log/<name>).
87
+ panels.set("log", { load: () => loadLog().then((m) => m.LogPanel) });
80
88
  // #1386 §6.3: desktop notifications for new "Needs you" items, in any panel, while this signed-in page is open.
81
89
  startNeedsNotifier({ open: (instance) => navigate(chatPath(instance)) });
90
+ // #1665: this device's first upload of its own choices (once), then another device's changes at each status frame.
91
+ bootPrefs();
82
92
  onNewInstance = async () => {
83
93
  closeDrawer(); // on a phone the dialog opens from the drawer: the drawer goes first
84
94
  let m;
@@ -8,7 +8,7 @@
8
8
  - The optical-size axis is fixed at its text size (`opsz=14`).
9
9
  - The font is subset to Latin, with the same range as `unicode-range` in `tokens.css`.
10
10
  - It is saved as WOFF2. About 44 KB, against 352 KB for the full variable font.
11
- - Chinese and other scripts fall back to the system fonts named in `--font-ui` (PingFang TC → Noto Sans TC → Microsoft JhengHei → the system stack).
11
+ - Chinese and other scripts fall back to the system fonts named in `--font-ui` (PingFang TC → PingFang SC → Noto Sans TC → Noto Sans SC → Noto Sans CJK TC → Noto Sans CJK SC → Microsoft JhengHei → Microsoft YaHei → the system stack).
12
12
 
13
13
  Rebuilt with fontTools 4.59 (and `brotli` for WOFF2):
14
14
 
@@ -1,13 +1,15 @@
1
1
  // #1523 N3 (§3.4, Q2 = A): the controls every page's header has, in one order on Chat, View, Details and Fleet —
2
2
  // Aa text size, shared per device: S / M / L (View also keeps Fit, its own extra choice);
3
3
  // ◔ AI usage, unless the fleet turned it off (web.usage_panel: false — said by the page itself, no probe read).
4
+ // #1673: on a wide screen it is a right rail beside the page (shown or hidden per device); ◔ toggles it. On a
5
+ // narrower one, ◔ opens the dialog.
4
6
  // The size is one attribute on the root (data-text-size); CSS turns it into one custom property the chat, Details and
5
7
  // Fleet read (#1300: no style attribute). View's terminal multiplies its fitted size by VIEW_SCALE, as its density did.
6
- import { html, useState } from "./app-html.js";
8
+ import { html, useEffect, useState } from "./app-html.js";
7
9
  import { t } from "./app-i18n.js";
8
10
  import { createStore, useStore } from "./app-store.js";
9
11
  import { Icon } from "./ui-icons.js";
10
- import { UsageDialog } from "./usage-dialog.js";
12
+ import { UsageBody, UsageDialog } from "./usage-dialog.js";
11
13
 
12
14
  export const TEXT_SIZE_KEY = "agend_text_size";
13
15
  export const VIEW_FIT_KEY = "agend_view_fit";
@@ -73,16 +75,64 @@ export function usageOffered() {
73
75
  try { return document.body && document.body.dataset ? document.body.dataset.usagePanel !== "0" : true; } catch { return true; }
74
76
  }
75
77
 
78
+ // #1673: the rail. Wide enough for it beside the sidebar and the page: RAIL_MEDIA (followed as it changes). Shown or
79
+ // hidden is this device's choice (agend_usage_rail; "0" = hidden, else shown) — a layout choice, not synced (#1665).
80
+ export const RAIL_MEDIA = "(min-width: 1200px)";
81
+ export const RAIL_KEY = "agend_usage_rail";
82
+ /** The pages whose header has ◔ — the rail is theirs. */
83
+ export const RAIL_PANELS = ["chat", "view", "details", "fleet"];
84
+ export const usageRailStore = createStore({ open: true });
85
+ /** A page load: this device's choice. */
86
+ export function initUsageRail() { usageRailStore.set({ open: read(RAIL_KEY) !== "0" }); }
87
+ export function setUsageRail(open) { write(RAIL_KEY, open ? "1" : "0"); usageRailStore.set({ open: !!open }); }
88
+ const railQuery = () => { try { return typeof matchMedia === "function" ? matchMedia(RAIL_MEDIA) : null; } catch { return null; } };
89
+ /** Whether the screen is wide enough for the rail, now and as it changes. */
90
+ export function useRailWidth() {
91
+ const [wide, setWide] = useState(() => !!railQuery()?.matches);
92
+ useEffect(() => {
93
+ const q = railQuery();
94
+ if (!q) return undefined;
95
+ const on = () => setWide(!!q.matches);
96
+ on();
97
+ if (typeof q.addEventListener === "function") { q.addEventListener("change", on); return () => q.removeEventListener("change", on); }
98
+ if (typeof q.addListener === "function") { q.addListener(on); return () => q.removeListener(on); }
99
+ return undefined;
100
+ }, []);
101
+ return wide;
102
+ }
103
+
104
+ /**
105
+ * The rail: beside the page on a wide screen, on the pages with ◔, while this device shows it. Its reads are its own
106
+ * lease: they go on across navigation and end when it is hidden, the screen narrows, or it goes.
107
+ */
108
+ export function UsageRail({ route }) {
109
+ const wide = useRailWidth();
110
+ const { open } = useStore(usageRailStore);
111
+ if (!wide || !open || !route || !RAIL_PANELS.includes(route.panel) || !usageOffered()) return null;
112
+ return html`<aside class="u-rail" aria-label=${t("view.usage.title_plain")}>
113
+ <div class="u-rail-head"><h2>${t("view.usage.title")}</h2>
114
+ <button type="button" class="icon-btn" onClick=${() => setUsageRail(false)} aria-label=${t("app.usageHide")} title=${t("app.usageHide")}><${Icon} name="close" size=${16} /></button></div>
115
+ <div class="u-rail-body"><${UsageBody} owner="usage-rail" /></div>
116
+ </aside>`;
117
+ }
118
+
76
119
  /**
77
- * ◔: the usage dialog (usage-dialog.js). It belongs to the navigation that opened it (`navKey`, #1408 §4; #1563
120
+ * ◔: on a wide screen, shows or hides the rail (#1673); otherwise the usage dialog (usage-dialog.js). It belongs to the navigation that opened it (`navKey`, #1408 §4; #1563
78
121
  * review): another route, the same route again, another tab of the panel or a language switch closes it, and its
79
122
  * reads and timer end with it — the dialog's lease is that navigation's.
80
123
  */
81
124
  export function UsageButton({ navKey }) {
82
125
  const [openFor, setOpenFor] = useState(null);
126
+ const wide = useRailWidth();
127
+ const rail = useStore(usageRailStore);
128
+ // Widening ends a dialog opened on the narrower screen — closed for good, not hidden while wide: narrowing again
129
+ // (after the rail was hidden, or with this device's rail hidden) never brings it back (#1679 review).
130
+ useEffect(() => { if (wide) setOpenFor(null); }, [wide]);
83
131
  if (!usageOffered()) return null;
84
- const open = openFor !== null && openFor === navKey;
85
- return html`<button type="button" class="btn btn-ghost btn-sm hd-usage" onClick=${() => setOpenFor(navKey)} aria-label=${t("app.usage")} title=${t("app.usage")}>
132
+ const open = !wide && openFor !== null && openFor === navKey;
133
+ const click = () => (wide ? setUsageRail(!rail.open) : setOpenFor(navKey));
134
+ return html`<button type="button" class="btn btn-ghost btn-sm hd-usage" onClick=${click} aria-label=${t("app.usage")} title=${t("app.usage")}
135
+ aria-pressed=${wide ? (rail.open ? "true" : "false") : undefined}>
86
136
  <${Icon} name="chart" size=${16} /><span class="hide-narrow" aria-hidden="true">${t("app.usage")}</span></button>
87
137
  ${open ? html`<${UsageDialog} key=${navKey} owner=${navKey} onClose=${() => setOpenFor(null)} />` : null}`;
88
138
  }