@songsid/agend 2.2.0-alpha.9 → 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 +26 -1
  66. package/dist/channel/adapters/discord.js +98 -3
  67. package/dist/channel/adapters/discord.js.map +1 -1
  68. package/dist/channel/adapters/telegram.d.ts +26 -2
  69. package/dist/channel/adapters/telegram.js +123 -19
  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 +41 -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 +352 -10
  106. package/dist/fleet-manager.js +1925 -175
  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 +204 -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 +159 -23
  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 +10 -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
@@ -12,6 +12,10 @@ h1, h2, h3, p, ul, ol, figure, fieldset { margin: 0; padding: 0; }
12
12
  fieldset { border: 0; min-width: 0; }
13
13
  ul, ol { list-style: none; }
14
14
  button, input, select, textarea { font: inherit; color: inherit; }
15
+ /* #1615: and a field with no size of its own, inside small text, is still never under 16px on a touch screen or the phone
16
+ layout (iOS zooms on focusing one; #1611's tokens only reach a field sized from them). After the reset above, as weak
17
+ as it (an element selector: any rule of a field's own still wins), keeping a larger inherited size. */
18
+ @media (pointer: coarse), (max-width: 899px) { input, select, textarea { font-size: max(16px, 1em); } }
15
19
  a { color: inherit; text-decoration: none; }
16
20
  [hidden] { display: none !important; }
17
21
  :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@@ -64,6 +68,7 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
64
68
 
65
69
  .sidebar {
66
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) */
67
72
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom) env(safe-area-inset-left);
68
73
  }
69
74
  .sb-collapsed .sidebar { display: none; }
@@ -99,11 +104,32 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
99
104
  .inst:hover { background: var(--surface-2); }
100
105
  .inst.active { background: var(--surface-2); }
101
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; }
102
127
  .inst-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
103
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; }
104
131
  .inst.active .inst-name { font-weight: var(--fw-semibold); }
105
132
  .inst-sub { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-2); }
106
- .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%; }
107
133
  .badge-await {
108
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);
109
135
  background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); white-space: nowrap;
@@ -124,6 +150,13 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
124
150
  .s-device { display: flex; flex-direction: column; gap: var(--s-3); }
125
151
  .s-device .sub { margin: 0; color: var(--text-3); font-size: var(--fs-sm); }
126
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; }
127
160
  .prefs { display: flex; gap: var(--s-2); padding: var(--s-1) var(--s-3); }
128
161
  .pref { display: flex; align-items: center; gap: var(--s-1); color: var(--text-2); flex: 1; min-width: 0; }
129
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; }
@@ -175,7 +208,7 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
175
208
  :root { --text-scale: 1; }
176
209
  :root[data-text-size="s"] { --text-scale: 0.9; }
177
210
  :root[data-text-size="l"] { --text-scale: 1.15; }
178
- .thread, .p-details .panel-body, .p-fleet .panel-body {
211
+ .thread, .p-details .panel-body, .p-fleet .panel-body, .p-log .panel-body {
179
212
  --fs-xs: calc(12px * var(--text-scale)); --lh-xs: calc(16px * var(--text-scale));
180
213
  --fs-sm: calc(13px * var(--text-scale)); --lh-sm: calc(18px * var(--text-scale));
181
214
  --fs-md: calc(15px * var(--text-scale)); --lh-md: calc(24px * var(--text-scale));
@@ -184,10 +217,40 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
184
217
  }
185
218
  /* #1523 N2: Details. */
186
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); }
187
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); }
188
251
  @media (max-width: 899px) {
189
252
  .panel-head:has(.inst-switch) { flex-wrap: wrap; }
190
- .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); }
191
254
  .inst-switch .seg-item { justify-content: center; border-radius: var(--r-sm); }
192
255
  }
193
256
  .panel-body { flex: 1; min-height: 0; overflow-y: auto; }
@@ -280,6 +343,16 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
280
343
  .pill.open { background: var(--accent-dim); color: var(--accent); }
281
344
  .pill.claimed { background: var(--warn-bg); color: var(--warn); }
282
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); }
283
356
  .save-row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
284
357
 
285
358
  /* ── Chat ── */
@@ -288,11 +361,24 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
288
361
  .msg { font-size: var(--fs-md); line-height: var(--lh-md); overflow-wrap: anywhere; min-width: 0; }
289
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); }
290
363
  .msg .sender { font-weight: var(--fw-semibold); color: var(--text-2); }
291
- .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; }
292
367
  .msg.user { align-self: flex-end; max-width: min(80%, 620px); }
293
368
  .msg.user .meta { justify-content: flex-end; }
294
- .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; }
295
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); }
296
382
  .msg-tools { display: flex; gap: var(--s-1); margin-top: var(--s-1); opacity: 0; transition: opacity var(--t-fast); }
297
383
  .msg:hover .msg-tools, .msg:focus-within .msg-tools { opacity: 1; }
298
384
  @media (hover: none) { .msg-tools { opacity: 1; } }
@@ -314,10 +400,13 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
314
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); }
315
401
  .text-card .tc-head { display: flex; align-items: center; gap: var(--s-2); }
316
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); }
317
- .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; }
318
407
  .text-card .tc-body[hidden] { display: none; }
319
408
  .text-card .tc-pre { margin: 0; white-space: pre-wrap; word-break: break-word; font-size: var(--fs-xs); }
320
- .text-card .tc-table-wrap { overflow-x: auto; }
409
+ .text-card .tc-table-wrap { overflow-x: auto; padding-bottom: var(--s-3); }
321
410
  .text-card .tc-table { border-collapse: collapse; font-size: var(--fs-xs); }
322
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; }
323
412
  .text-card .tc-more { margin-top: var(--s-2); }
@@ -350,10 +439,17 @@ html.lb-open .scroller { overflow: hidden; }
350
439
  .msg .md ul { list-style: disc; } .msg .md ol { list-style: decimal; }
351
440
  .msg .md li > ul, .msg .md li > ol { margin: 2px 0; }
352
441
  .msg .md hr { border: 0; border-top: 1px solid var(--border); margin: var(--s-3) 0; }
353
- .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); }
354
445
  .msg .md table { border-collapse: collapse; font-size: var(--fs-sm); }
355
446
  .msg .md th, .msg .md td { border: 1px solid var(--border); padding: 5px 9px; text-align: left; vertical-align: top; }
356
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; }
357
453
  .msg .md .al-c { text-align: center; } .msg .md .al-r { text-align: right; }
358
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); }
359
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; }
@@ -518,7 +614,6 @@ html.lb-open .scroller { overflow: hidden; }
518
614
  .thread { padding: var(--s-3) var(--s-3) var(--s-2); gap: var(--s-5); }
519
615
  .col { padding: var(--s-3) var(--s-3) var(--s-8); }
520
616
  .dock { padding: 0 max(var(--s-2), env(safe-area-inset-right)) var(--s-2) max(var(--s-2), env(safe-area-inset-left)); }
521
- .composer textarea { font-size: 16px; } /* under 16px, iOS Safari zooms the page on focus */
522
617
  .hint { display: none; }
523
618
  .msg.user { max-width: 88%; }
524
619
  /* Code wraps on a phone: a long line scrolled sideways inside a scrolling page is unreadable (rough edge 9). */
@@ -563,11 +658,32 @@ html.lb-open .scroller { overflow: hidden; }
563
658
  .caret.folded { transform: rotate(-90deg); }
564
659
  .caret.flipped { transform: rotate(180deg); } /* the card's toggle: up when closed (it opens upwards) */
565
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; } }
566
679
  .v-inst .dot { margin-top: 0; }
567
680
  .v-meta { color: var(--text-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
568
- .cli-icon { width: 10px; height: 10px; flex: 0 0 10px; border-radius: 50%; border: 1px solid var(--border-strong); }
569
- .cli-claude-code { background: #c678dd; } .cli-kiro-cli { background: #58a6ff; } .cli-codex { background: #3fb950; }
570
- .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; } }
571
687
  .dragging { opacity: 0.4; }
572
688
  .drop-before { box-shadow: inset 0 2px 0 var(--accent); }
573
689
  .drop-after { box-shadow: inset 0 -2px 0 var(--accent); }
@@ -578,15 +694,21 @@ html.lb-open .scroller { overflow: hidden; }
578
694
  .v-filter-box input::placeholder { color: var(--text-3); }
579
695
  .v-filter-clear { width: 28px; height: 28px; }
580
696
  .v-filter .note { padding: var(--s-1) var(--s-1) 0; font-variant-numeric: tabular-nums; }
581
- /* alpha.2 (N1): status and CLI chips under the filter box — each a <details> with checkboxes. */
582
- .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; }
583
- .nav-facet { font-size: var(--fs-xs); color: var(--text-2); }
584
- .nav-facet > summary { cursor: pointer; list-style: none; padding: 2px var(--s-2); border-radius: var(--r-sm); }
585
- .nav-facet > summary::-webkit-details-marker { display: none; }
586
- .nav-facet > summary::after { content: " ▾"; color: var(--text-3); }
587
- .nav-facet[open] > summary::after { content: " ▴"; }
588
- .nav-facet > summary:hover { background: var(--hover); }
589
- .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); }
590
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; }
591
713
  .nav-facet-list { display: flex; flex-direction: column; gap: 2px; padding: var(--s-1) var(--s-2); }
592
714
  .nav-chip { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; color: var(--text); }
@@ -616,8 +738,11 @@ html.lb-open .scroller { overflow: hidden; }
616
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); }
617
739
  .err { color: var(--danger); font-size: var(--fs-sm); }
618
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); }
619
- .u-head { display: flex; align-items: center; gap: var(--s-2); }
620
- .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; }
621
746
  .u-order .icon-btn { width: 28px; height: 28px; }
622
747
  .u-order .icon-btn:disabled { opacity: 0.3; }
623
748
  .u-metric { display: flex; flex-direction: column; gap: var(--s-1); }
@@ -634,6 +759,12 @@ html.lb-open .scroller { overflow: hidden; }
634
759
  .u-sub { color: var(--text-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
635
760
  .u-err { color: var(--warn); font-size: var(--fs-sm); }
636
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; } }
637
768
  @media (max-width: 899px) {
638
769
  .hide-narrow { display: none; }
639
770
  .v-card { padding-left: var(--s-3); padding-right: var(--s-3); }
@@ -931,3 +1062,8 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
931
1062
  .cmd-palette .cmd-desc { white-space: normal; flex-basis: 100%; }
932
1063
  .cmd-choices .btn, .quick-actions .btn { min-height: var(--tap); }
933
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
  }