@songsid/agend 2.3.0-beta.24 → 2.3.0-beta.26

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 (419) hide show
  1. package/dist/adapter-world.js.map +1 -1
  2. package/dist/agend-home.d.ts +5 -0
  3. package/dist/agend-home.js +7 -0
  4. package/dist/agend-home.js.map +1 -0
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +8 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.js +1 -1
  9. package/dist/agent-file-read.js.map +1 -1
  10. package/dist/agent-whats-new.js.map +1 -1
  11. package/dist/apply-job.js.map +1 -1
  12. package/dist/atomic-write.js.map +1 -1
  13. package/dist/auth-api.d.ts +5 -0
  14. package/dist/auth-api.js +21 -0
  15. package/dist/auth-api.js.map +1 -1
  16. package/dist/authority/authority-state.d.ts +4 -0
  17. package/dist/authority/authority-state.js +17 -8
  18. package/dist/authority/authority-state.js.map +1 -1
  19. package/dist/authority/session-authorizations.js.map +1 -1
  20. package/dist/authority/web-authority.d.ts +14 -6
  21. package/dist/authority/web-authority.js +19 -5
  22. package/dist/authority/web-authority.js.map +1 -1
  23. package/dist/backend/antigravity.d.ts +2 -2
  24. package/dist/backend/antigravity.js +2 -1
  25. package/dist/backend/antigravity.js.map +1 -1
  26. package/dist/backend/claude-code.js.map +1 -1
  27. package/dist/backend/codex-package.js.map +1 -1
  28. package/dist/backend/codex-session-lookup.js.map +1 -1
  29. package/dist/backend/codex.js +1 -1
  30. package/dist/backend/codex.js.map +1 -1
  31. package/dist/backend/effort-metadata.js.map +1 -1
  32. package/dist/backend/grok.js.map +1 -1
  33. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  34. package/dist/backend/kiro-kas-store.js.map +1 -1
  35. package/dist/backend/kiro-v2-store.js.map +1 -1
  36. package/dist/backend/kiro-v3-identity.js.map +1 -1
  37. package/dist/backend/kiro.js.map +1 -1
  38. package/dist/backend/launch-effort.js.map +1 -1
  39. package/dist/backend/mock.js.map +1 -1
  40. package/dist/backend/muse.js.map +1 -1
  41. package/dist/backend/opencode-permission.js.map +1 -1
  42. package/dist/backend/opencode.js.map +1 -1
  43. package/dist/backend/session-signals.js.map +1 -1
  44. package/dist/backend-availability.js.map +1 -1
  45. package/dist/backend-command.js.map +1 -1
  46. package/dist/backend-outage.js.map +1 -1
  47. package/dist/binary-probe.js.map +1 -1
  48. package/dist/bug-report.js.map +1 -1
  49. package/dist/bundled-skills.js.map +1 -1
  50. package/dist/cache-service.js +1 -1
  51. package/dist/cache-service.js.map +1 -1
  52. package/dist/channel/adapters/discord.js +29 -4
  53. package/dist/channel/adapters/discord.js.map +1 -1
  54. package/dist/channel/adapters/telegram.d.ts +6 -0
  55. package/dist/channel/adapters/telegram.js +54 -14
  56. package/dist/channel/adapters/telegram.js.map +1 -1
  57. package/dist/channel/mcp-server.js +10 -1
  58. package/dist/channel/mcp-server.js.map +1 -1
  59. package/dist/channel/mcp-tools.js +2 -2
  60. package/dist/channel/mcp-tools.js.map +1 -1
  61. package/dist/channel/message-queue.js.map +1 -1
  62. package/dist/channel/sticker-download.js.map +1 -1
  63. package/dist/channel/tool-router.js.map +1 -1
  64. package/dist/classic-channel-manager.js +3 -2
  65. package/dist/classic-channel-manager.js.map +1 -1
  66. package/dist/cli-entry.js.map +1 -1
  67. package/dist/cli-flags.js.map +1 -1
  68. package/dist/cli.js +15 -9
  69. package/dist/cli.js.map +1 -1
  70. package/dist/command-table.js.map +1 -1
  71. package/dist/compact-watch.js.map +1 -1
  72. package/dist/completion-install.js.map +1 -1
  73. package/dist/completion.js.map +1 -1
  74. package/dist/config-validator.js +20 -6
  75. package/dist/config-validator.js.map +1 -1
  76. package/dist/config.js +11 -3
  77. package/dist/config.js.map +1 -1
  78. package/dist/connection-secrets.js.map +1 -1
  79. package/dist/context-guardian.js.map +1 -1
  80. package/dist/coredump-filter.js.map +1 -1
  81. package/dist/cpu-profile.js.map +1 -1
  82. package/dist/cross-instance-envelope.js.map +1 -1
  83. package/dist/daemon.d.ts +3 -0
  84. package/dist/daemon.js +73 -3
  85. package/dist/daemon.js.map +1 -1
  86. package/dist/daily-summary.js.map +1 -1
  87. package/dist/darwin-memory.js.map +1 -1
  88. package/dist/dashboard-live.d.ts +8 -0
  89. package/dist/dashboard-live.js +35 -0
  90. package/dist/dashboard-live.js.map +1 -0
  91. package/dist/dashboard-permissions.d.ts +69 -0
  92. package/dist/dashboard-permissions.js +138 -0
  93. package/dist/dashboard-permissions.js.map +1 -0
  94. package/dist/decision-list.js.map +1 -1
  95. package/dist/delivery-consumed-watch.js.map +1 -1
  96. package/dist/delivery-outbox.js.map +1 -1
  97. package/dist/delivery-outbox.test.js.map +1 -1
  98. package/dist/delivery-reconciliation.test.js.map +1 -1
  99. package/dist/diagnostic-redaction.js.map +1 -1
  100. package/dist/doctor.js.map +1 -1
  101. package/dist/e2e-isolation.js.map +1 -1
  102. package/dist/event-log.js.map +1 -1
  103. package/dist/export-import.js +3 -2
  104. package/dist/export-import.js.map +1 -1
  105. package/dist/fleet-control-audit.js.map +1 -1
  106. package/dist/fleet-label.js.map +1 -1
  107. package/dist/fleet-lock.js +3 -2
  108. package/dist/fleet-lock.js.map +1 -1
  109. package/dist/fleet-manager.d.ts +77 -6
  110. package/dist/fleet-manager.js +488 -147
  111. package/dist/fleet-manager.js.map +1 -1
  112. package/dist/fleet-process-handlers.js +3 -2
  113. package/dist/fleet-process-handlers.js.map +1 -1
  114. package/dist/fleet-startup.d.ts +14 -0
  115. package/dist/fleet-startup.js +30 -0
  116. package/dist/fleet-startup.js.map +1 -0
  117. package/dist/fleet-traffic.js.map +1 -1
  118. package/dist/forged-envelope-scan.d.ts +0 -20
  119. package/dist/forged-envelope-scan.js +3 -2
  120. package/dist/forged-envelope-scan.js.map +1 -1
  121. package/dist/full-restart.js.map +1 -1
  122. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +2 -0
  123. package/dist/general-knowledge/skills/web-appearance/SKILL.md +2 -0
  124. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +36 -12
  125. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +45 -14
  126. package/dist/host-memory.js.map +1 -1
  127. package/dist/instance-config-impact.js +6 -0
  128. package/dist/instance-config-impact.js.map +1 -1
  129. package/dist/instance-lifecycle.js +1 -1
  130. package/dist/instance-lifecycle.js.map +1 -1
  131. package/dist/instructions.d.ts +2 -0
  132. package/dist/instructions.js +6 -2
  133. package/dist/instructions.js.map +1 -1
  134. package/dist/kiro-compact-pane.js.map +1 -1
  135. package/dist/kiro-db-reader.js.map +1 -1
  136. package/dist/kiro-engine-status.js.map +1 -1
  137. package/dist/kiro-log-gate.js.map +1 -1
  138. package/dist/kiro-transcript-lane.js.map +1 -1
  139. package/dist/locale.js +22 -0
  140. package/dist/locale.js.map +1 -1
  141. package/dist/log-service.js.map +1 -1
  142. package/dist/log-steps.js.map +1 -1
  143. package/dist/logger.js +3 -2
  144. package/dist/logger.js.map +1 -1
  145. package/dist/login-controller.js.map +1 -1
  146. package/dist/login-manager.js.map +1 -1
  147. package/dist/login-shell.js.map +1 -1
  148. package/dist/memory-pressure.js.map +1 -1
  149. package/dist/muse-usage-relay.js.map +1 -1
  150. package/dist/needs-you-hub.js.map +1 -1
  151. package/dist/needs-you-live.js.map +1 -1
  152. package/dist/network-family.js.map +1 -1
  153. package/dist/operator-control-paths.js +3 -4
  154. package/dist/operator-control-paths.js.map +1 -1
  155. package/dist/outbound-handlers.js.map +1 -1
  156. package/dist/outbound-schemas.d.ts +68 -68
  157. package/dist/outbound-schemas.js +1 -1
  158. package/dist/outbound-schemas.js.map +1 -1
  159. package/dist/outbox-open-error.js.map +1 -1
  160. package/dist/pane-context-cache.js.map +1 -1
  161. package/dist/paths.js.map +1 -1
  162. package/dist/private-dir.js.map +1 -1
  163. package/dist/probe-worker-pool.js.map +1 -1
  164. package/dist/profile-control.js.map +1 -1
  165. package/dist/prompt-file-ref.js.map +1 -1
  166. package/dist/provider-probe.js.map +1 -1
  167. package/dist/provider-secret-registry.js.map +1 -1
  168. package/dist/public-link-auto-open.d.ts +32 -0
  169. package/dist/public-link-auto-open.js +86 -0
  170. package/dist/public-link-auto-open.js.map +1 -0
  171. package/dist/public-link-owner.d.ts +10 -0
  172. package/dist/public-link-owner.js +36 -0
  173. package/dist/public-link-owner.js.map +1 -0
  174. package/dist/public-link-progress.js.map +1 -1
  175. package/dist/public-preview.js.map +1 -1
  176. package/dist/public-web-gateway.js +4 -2
  177. package/dist/public-web-gateway.js.map +1 -1
  178. package/dist/public-web-link.d.ts +35 -1
  179. package/dist/public-web-link.js +142 -43
  180. package/dist/public-web-link.js.map +1 -1
  181. package/dist/quickstart-api.js.map +1 -1
  182. package/dist/quickstart.d.ts +5 -0
  183. package/dist/quickstart.js +25 -16
  184. package/dist/quickstart.js.map +1 -1
  185. package/dist/reply-buttons-controller.js.map +1 -1
  186. package/dist/reply-dedup.js.map +1 -1
  187. package/dist/resource-report.js.map +1 -1
  188. package/dist/restart-guard.js.map +1 -1
  189. package/dist/restart-progress.js.map +1 -1
  190. package/dist/rollout-index.js.map +1 -1
  191. package/dist/runtime-cpu-profile.js.map +1 -1
  192. package/dist/scheduler/db.js.map +1 -1
  193. package/dist/scheduler/scheduler.js.map +1 -1
  194. package/dist/scheduler/task-board-text.js +16 -11
  195. package/dist/scheduler/task-board-text.js.map +1 -1
  196. package/dist/scheduler/task-board.d.ts +50 -10
  197. package/dist/scheduler/task-board.js +138 -44
  198. package/dist/scheduler/task-board.js.map +1 -1
  199. package/dist/scheduler/types.d.ts +1 -1
  200. package/dist/sd-notify.js.map +1 -1
  201. package/dist/search/search-index.js.map +1 -1
  202. package/dist/secret-file.js.map +1 -1
  203. package/dist/secret-store.js.map +1 -1
  204. package/dist/service-installer.d.ts +7 -1
  205. package/dist/service-installer.js +13 -1
  206. package/dist/service-installer.js.map +1 -1
  207. package/dist/service-plan.d.ts +33 -4
  208. package/dist/service-plan.js +177 -8
  209. package/dist/service-plan.js.map +1 -1
  210. package/dist/session-oom.d.ts +21 -0
  211. package/dist/session-oom.js +139 -0
  212. package/dist/session-oom.js.map +1 -0
  213. package/dist/settings-api.js +6 -1
  214. package/dist/settings-api.js.map +1 -1
  215. package/dist/settings-baseline.js.map +1 -1
  216. package/dist/settings-change.d.ts +5 -0
  217. package/dist/settings-change.js +71 -1
  218. package/dist/settings-change.js.map +1 -1
  219. package/dist/settings-confirmation.d.ts +4 -0
  220. package/dist/settings-confirmation.js +2 -0
  221. package/dist/settings-confirmation.js.map +1 -1
  222. package/dist/settings-control.js.map +1 -1
  223. package/dist/settings-effect.js +6 -1
  224. package/dist/settings-effect.js.map +1 -1
  225. package/dist/settings-http-confirmation.js +1 -0
  226. package/dist/settings-http-confirmation.js.map +1 -1
  227. package/dist/settings-instance-patch.js +13 -1
  228. package/dist/settings-instance-patch.js.map +1 -1
  229. package/dist/settings-transaction.js.map +1 -1
  230. package/dist/setup-auth.js.map +1 -1
  231. package/dist/setup-host.d.ts +5 -1
  232. package/dist/setup-host.js +8 -1
  233. package/dist/setup-host.js.map +1 -1
  234. package/dist/setup-wizard.js +5 -2
  235. package/dist/setup-wizard.js.map +1 -1
  236. package/dist/spawn-gate.d.ts +4 -0
  237. package/dist/spawn-gate.js +15 -5
  238. package/dist/spawn-gate.js.map +1 -1
  239. package/dist/startup-memory-deadline.d.ts +23 -0
  240. package/dist/startup-memory-deadline.js +55 -0
  241. package/dist/startup-memory-deadline.js.map +1 -0
  242. package/dist/statusline-watcher.js.map +1 -1
  243. package/dist/storm-window.js.map +1 -1
  244. package/dist/systemd-peer-bus.js.map +1 -1
  245. package/dist/target-queue-worker.js.map +1 -1
  246. package/dist/task-board-summary.d.ts +4 -4
  247. package/dist/task-board-summary.js +4 -4
  248. package/dist/task-board-summary.js.map +1 -1
  249. package/dist/task-board-tool.js +3 -2
  250. package/dist/task-board-tool.js.map +1 -1
  251. package/dist/task-board-waker.js +8 -4
  252. package/dist/task-board-waker.js.map +1 -1
  253. package/dist/tips.js.map +1 -1
  254. package/dist/tmux-control.js.map +1 -1
  255. package/dist/tmux-exec-helper.js.map +1 -1
  256. package/dist/tmux-manager.d.ts +1 -0
  257. package/dist/tmux-manager.js +2 -1
  258. package/dist/tmux-manager.js.map +1 -1
  259. package/dist/tmux-read.js +1 -1
  260. package/dist/tmux-read.js.map +1 -1
  261. package/dist/tool-permissions.js.map +1 -1
  262. package/dist/tool-progress.js.map +1 -1
  263. package/dist/topic-archiver.js.map +1 -1
  264. package/dist/topic-commands.js.map +1 -1
  265. package/dist/topic-link.js.map +1 -1
  266. package/dist/transcript-delta-reader.js.map +1 -1
  267. package/dist/transcript-jsonl.js.map +1 -1
  268. package/dist/transcript-monitor.js.map +1 -1
  269. package/dist/transcript-sources.js.map +1 -1
  270. package/dist/tunnel/cloudflared-install.js.map +1 -1
  271. package/dist/tunnel/cloudflared.js.map +1 -1
  272. package/dist/tunnel/lease.js +3 -2
  273. package/dist/tunnel/lease.js.map +1 -1
  274. package/dist/tunnel/manager.js.map +1 -1
  275. package/dist/tunnel/public-fetch.js.map +1 -1
  276. package/dist/tunnel/purpose-lane.js.map +1 -1
  277. package/dist/tunnel/types.js.map +1 -1
  278. package/dist/types.d.ts +26 -2
  279. package/dist/ui/chat-render.js +1 -15
  280. package/dist/ui/chat-store.js +171 -61
  281. package/dist/ui/chat-strings.js +7 -2
  282. package/dist/ui/chat-thread.js +48 -1
  283. package/dist/ui/fleet-canvas-layout.js +3 -0
  284. package/dist/ui/fleet-canvas.js +23 -20
  285. package/dist/ui/fleet-office-editor.js +149 -0
  286. package/dist/ui/fleet-office-model.js +358 -0
  287. package/dist/ui/fleet-office-pack.js +199 -0
  288. package/dist/ui/fleet-office-skin.js +474 -0
  289. package/dist/ui/fleet-office-store.js +49 -0
  290. package/dist/ui/fleet-office-zones.js +177 -0
  291. package/dist/ui/fleet-office.js +434 -0
  292. package/dist/ui/fleet-org.js +18 -5
  293. package/dist/ui/guest-refused.html +41 -0
  294. package/dist/ui/image-lightbox.js +9 -3
  295. package/dist/ui/panel-chat.js +14 -13
  296. package/dist/ui/panel-settings.js +30 -12
  297. package/dist/ui/settings-dialogs.js +15 -6
  298. package/dist/ui/settings-model.js +40 -3
  299. package/dist/ui/settings-strings.js +38 -0
  300. package/dist/ui/settings-web-authority.js +7 -2
  301. package/dist/ui/shared/agend-auth.js +2 -0
  302. package/dist/ui/shared/app-i18n.js +2 -2
  303. package/dist/ui/shared/app-prefs.js +2 -0
  304. package/dist/ui/shared/app-shell.js +12 -0
  305. package/dist/ui/shared/app-store.js +1 -0
  306. package/dist/ui/shared/app-stream.js +2 -1
  307. package/dist/ui/shared/app.css +132 -3
  308. package/dist/ui/shared/guest-refused.js +35 -0
  309. package/dist/ui/shared/header-tools.js +2 -2
  310. package/dist/ui/shared/instance-nav.js +25 -8
  311. package/dist/ui/shared/ui-dialog.js +2 -2
  312. package/dist/ui/shared/usage-dialog.js +152 -6
  313. package/dist/ui/shared/view-strings.js +20 -0
  314. package/dist/ui/shared/web-authorize.js +3 -1
  315. package/dist/update-check.js.map +1 -1
  316. package/dist/update-dispatch.js.map +1 -1
  317. package/dist/update-install.js.map +1 -1
  318. package/dist/update-launch.js.map +1 -1
  319. package/dist/update-marker.js.map +1 -1
  320. package/dist/update-preflight.js.map +1 -1
  321. package/dist/usage/format-rich.js.map +1 -1
  322. package/dist/usage/providers.d.ts +8 -0
  323. package/dist/usage/providers.js +6 -2
  324. package/dist/usage/providers.js.map +1 -1
  325. package/dist/usage/statusline-usage.js.map +1 -1
  326. package/dist/usage/usage-api.js +2 -1
  327. package/dist/usage/usage-api.js.map +1 -1
  328. package/dist/usage-report.js.map +1 -1
  329. package/dist/usage-rollup.js +1 -1
  330. package/dist/usage-rollup.js.map +1 -1
  331. package/dist/user-systemd.js.map +1 -1
  332. package/dist/view-api.js +1 -1
  333. package/dist/view-api.js.map +1 -1
  334. package/dist/wake-coordinator.js.map +1 -1
  335. package/dist/web-ambient.js.map +1 -1
  336. package/dist/web-api.d.ts +15 -2
  337. package/dist/web-api.js +174 -25
  338. package/dist/web-api.js.map +1 -1
  339. package/dist/web-app-icon.js.map +1 -1
  340. package/dist/web-appearance-policy.js.map +1 -1
  341. package/dist/web-appearance-tools.js.map +1 -1
  342. package/dist/web-appearance-view.js.map +1 -1
  343. package/dist/web-appearance.js.map +1 -1
  344. package/dist/web-auth.js +5 -4
  345. package/dist/web-auth.js.map +1 -1
  346. package/dist/web-backend-login.js.map +1 -1
  347. package/dist/web-card.js.map +1 -1
  348. package/dist/web-channel-echo.js.map +1 -1
  349. package/dist/web-chat-history.d.ts +41 -11
  350. package/dist/web-chat-history.js +93 -22
  351. package/dist/web-chat-history.js.map +1 -1
  352. package/dist/web-commands.js.map +1 -1
  353. package/dist/web-effect-bundle.d.ts +2 -2
  354. package/dist/web-effect-library.js.map +1 -1
  355. package/dist/web-effect-playback.js.map +1 -1
  356. package/dist/web-effect-profiles.d.ts +16 -16
  357. package/dist/web-effect-profiles.js +3 -2
  358. package/dist/web-effect-profiles.js.map +1 -1
  359. package/dist/web-effect-service.js.map +1 -1
  360. package/dist/web-effect-tools.js.map +1 -1
  361. package/dist/web-effect-trials.js.map +1 -1
  362. package/dist/web-effect-upload.js.map +1 -1
  363. package/dist/web-effect.d.ts +7 -7
  364. package/dist/web-effect.js.map +1 -1
  365. package/dist/web-experimental.js.map +1 -1
  366. package/dist/web-guest-access.js +1 -1
  367. package/dist/web-guest-access.js.map +1 -1
  368. package/dist/web-guest-rooms.js.map +1 -1
  369. package/dist/web-host-guard.js.map +1 -1
  370. package/dist/web-icons.js.map +1 -1
  371. package/dist/web-identity-color.js.map +1 -1
  372. package/dist/web-images.js.map +1 -1
  373. package/dist/web-libavoid.js.map +1 -1
  374. package/dist/web-login.js.map +1 -1
  375. package/dist/web-mentions.js.map +1 -1
  376. package/dist/web-mod-agent.js.map +1 -1
  377. package/dist/web-mod-store.js.map +1 -1
  378. package/dist/web-office-store.d.ts +104 -0
  379. package/dist/web-office-store.js +236 -0
  380. package/dist/web-office-store.js.map +1 -0
  381. package/dist/web-owner-css-store.js.map +1 -1
  382. package/dist/web-owner-css.js.map +1 -1
  383. package/dist/web-png.js.map +1 -1
  384. package/dist/web-prefs.js +2 -0
  385. package/dist/web-prefs.js.map +1 -1
  386. package/dist/web-profile.js.map +1 -1
  387. package/dist/web-reactions.d.ts +2 -1
  388. package/dist/web-request-context.d.ts +5 -0
  389. package/dist/web-request-context.js.map +1 -1
  390. package/dist/web-returning-device.js +1 -1
  391. package/dist/web-returning-device.js.map +1 -1
  392. package/dist/web-schedules.js.map +1 -1
  393. package/dist/web-send-dedupe.d.ts +36 -0
  394. package/dist/web-send-dedupe.js +69 -0
  395. package/dist/web-send-dedupe.js.map +1 -0
  396. package/dist/web-session-authorization.js.map +1 -1
  397. package/dist/web-session.js +1 -1
  398. package/dist/web-session.js.map +1 -1
  399. package/dist/web-settings-patch.d.ts +1 -0
  400. package/dist/web-settings-patch.js +6 -4
  401. package/dist/web-settings-patch.js.map +1 -1
  402. package/dist/web-settings-tools.js.map +1 -1
  403. package/dist/web-terminal-http.js.map +1 -1
  404. package/dist/web-terminal.js.map +1 -1
  405. package/dist/web-theme-store.js.map +1 -1
  406. package/dist/web-upload.d.ts +5 -1
  407. package/dist/web-upload.js +6 -4
  408. package/dist/web-upload.js.map +1 -1
  409. package/dist/web-widget.js.map +1 -1
  410. package/dist/webhook-emitter.js.map +1 -1
  411. package/dist/windows-shortcuts.js +1 -1
  412. package/dist/windows-shortcuts.js.map +1 -1
  413. package/dist/workflow-templates/default.md +1 -1
  414. package/dist/ws-server.js.map +1 -1
  415. package/dist/yaml-shared-nodes.js.map +1 -1
  416. package/launcher/agend-install.cjs +281 -6
  417. package/launcher/private-home.cjs +43 -0
  418. package/package.json +3 -2
  419. package/templates/windows-shortcuts.ps1 +1 -1
@@ -22,6 +22,8 @@ export function verifiedAuthorityBody(original, f) {
22
22
  if (f.enabled !== original.enabled) out.enabled = f.enabled ? null : false; // §0.1: on is the default
23
23
  if (f.loopbackOnly !== original.loopbackOnly) out.loopback_only = f.loopbackOnly ? true : null;
24
24
  if (f.hours !== original.hours) out.max_session_hours = f.hours === null ? null : f.hours;
25
+ // #2453 (§0.2): off by default; turning it on is confirmed in a General or on the host.
26
+ if (f.namedLink !== original.namedLink) out.named_link = f.namedLink ? true : null;
25
27
  return Object.keys(out).length ? out : null;
26
28
  }
27
29
 
@@ -39,7 +41,8 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
39
41
  const o = r && r.ok ? r.body : null;
40
42
  if (!o || typeof o !== "object" || !o.state || !o.hours || !Array.isArray(o.lanHosts) || !Array.isArray(o.authorized)) { setFailed(true); return; }
41
43
  const c = o.configured || {};
42
- const orig = { enabled: c.enabled !== false, loopbackOnly: c.loopback_only === true, hours: typeof c.max_session_hours === "number" ? c.max_session_hours : null };
44
+ const orig = { enabled: c.enabled !== false, loopbackOnly: c.loopback_only === true, hours: typeof c.max_session_hours === "number" ? c.max_session_hours : null,
45
+ namedLink: c.named_link === true };
43
46
  setOverview(o); setFailed(false);
44
47
  setOriginal((prev) => prev || orig);
45
48
  setF((prev) => prev || { ...orig });
@@ -78,6 +81,8 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
78
81
  <label class="check"><input id="vaLoopbackOnly" type="checkbox" checked=${f.loopbackOnly} onChange=${(e) => set({ loopbackOnly: e.target.checked })} /><span>${tn("vaLoopbackOnly")}</span></label>
79
82
  <p class="note">${tn("vaLoopbackOnlyHint")}</p>
80
83
  ${warnHosts ? html`<p class="note s-va-warn" role="status" id="vaLanWarning">${tn("vaLanWarning", overview.lanHosts.join(", "))}</p>` : null}
84
+ ${overview.namedHost || original.namedLink ? html`<label class="check"><input id="vaNamedLink" type="checkbox" checked=${f.namedLink} onChange=${(e) => set({ namedLink: e.target.checked })} /><span>${tn("vaNamedLink", overview.namedHost || "—")}</span></label>
85
+ <p class="note">${tn("vaNamedLinkHint")}</p>` : null}
81
86
  <div class="field"><label for="vaHours">${tn("vaHours", overview.hours.min, overview.hours.max)}</label>
82
87
  <input id="vaHours" type="number" min=${overview.hours.min} max=${overview.hours.max} step="any" placeholder=${tn("defaultHours", overview.hours.default)}
83
88
  class=${hoursError ? "invalid" : ""} value=${hoursText} onInput=${(e) => set({ hours: onHoursText(e.target.value) })} />
@@ -85,7 +90,7 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
85
90
  <h4>${tn("vaSessionsTitle")}</h4>
86
91
  ${overview.authorized.length
87
92
  ? html`<ul class="s-va-sessions" id="vaSessions">${overview.authorized.map((a) => html`<li key=${a.handle} data-handle=${a.handle}>
88
- <strong>${a.label}</strong> <span class="note">${a.account}</span><br /><span class="note">${tn("vaSessionUntil", when(a.authorizedAt), when(a.until))}</span></li>`)}</ul>`
93
+ <strong>${a.label}</strong> <span class="note">${a.account}</span>${a.named ? html` <span class="note s-va-named">${tn("vaSessionNamed")}</span>` : null}<br /><span class="note">${tn("vaSessionUntil", when(a.authorizedAt), when(a.until))}</span></li>`)}</ul>`
89
94
  : html`<p class="note" id="vaSessionsNone">${tn("vaSessionsNone")}</p>`}
90
95
  </fieldset>
91
96
  <div class="save-row"><button type="button" class="btn btn-primary" id="vaReview" disabled=${disabled || !!hoursError} onClick=${review}>${tn("vaReview")}</button>
@@ -106,6 +106,8 @@
106
106
  /** Display hint from the same server session response; Start still reauthorizes. No CSRF is exposed here. */
107
107
  trialSession() { return session().then(s => !ended && s?.handle && /^[a-f0-9]{16}$/.test(s.handle) ? Object.freeze({ epoch: s.handle, localAdmin: s.localAdmin === true }) : null); },
108
108
  sessionEnded() { return ended !== null; },
109
+ /** A same-origin request made outside fetch (an upload's XHR, for its progress) was answered 401: the same end. */
110
+ unauthorized() { endSession("unauthorized"); sessionEndedBanner(); },
109
111
  /** #1568: the session handle of this page's sign-in, or null when there is none. */
110
112
  sessionHandle() { return session().then((s) => (s && s.handle ? s.handle : null)); },
111
113
  async signOut() {
@@ -69,7 +69,7 @@ register("app", {
69
69
  needsYou: "needs you", approxNote: "Read from the terminal, so approximate", chat: "Chat", details: "Details", log: "Log", instanceViews: "Views of {0}",
70
70
  textSize: "Text size: {0}", textSize_s: "S", textSize_m: "M", textSize_l: "L", textSize_fit: "Fit", usage: "Usage", usageHide: "Hide usage",
71
71
  ownerCssKeep: "Keep this custom CSS?", ownerCssKeepLeft: "Reverts in {0} s", ownerCssKeepYes: "Keep", ownerCssKeepNo: "Revert",
72
- statusRunning: "running", statusStopped: "stopped", statusPaused: "paused", statusCrashed: "crashed", statusWorking: "working", statusStuck: "looks stuck", statusIdle: "idle",
72
+ statusQueued: "queued", statusStarting: "starting", fleetStarting: "Fleet starting {0}/{1}…", statusRunning: "running", statusStopped: "stopped", statusPaused: "paused", statusCrashed: "crashed", statusWorking: "working", statusStuck: "looks stuck", statusIdle: "idle",
73
73
  connReconnecting: "Can't reach AgEnD — it may be restarting. Reconnecting…", connReconnectingIn: "Can't reach AgEnD — it may be restarting. Trying again in {0} s.",
74
74
  hydrating: "Loading what is open now…", hydrateFailed: "Could not load what is open now (prompts, ticks).",
75
75
  skip: "Skip to the conversation", loadFailed: "This could not be loaded.", retry: "Try again", loading: "Loading…",
@@ -106,7 +106,7 @@ register("app", {
106
106
  needsYou: "等你回應", approxNote: "從終端畫面判讀,僅供參考", chat: "聊天", details: "詳細資訊", log: "紀錄", instanceViews: "{0} 的檢視",
107
107
  textSize: "字級:{0}", textSize_s: "小", textSize_m: "中", textSize_l: "大", textSize_fit: "填滿", usage: "用量", usageHide: "隱藏用量",
108
108
  ownerCssKeep: "保留這份自訂 CSS?", ownerCssKeepLeft: "{0} 秒後還原", ownerCssKeepYes: "保留", ownerCssKeepNo: "還原",
109
- statusRunning: "執行中", statusStopped: "已停止", statusPaused: "已暫停", statusCrashed: "已當掉", statusWorking: "工作中", statusStuck: "似乎卡住", statusIdle: "閒置",
109
+ statusQueued: "等待啟動", statusStarting: "啟動中", fleetStarting: "Fleet 啟動中 {0}/{1}…", statusRunning: "執行中", statusStopped: "已停止", statusPaused: "已暫停", statusCrashed: "已當掉", statusWorking: "工作中", statusStuck: "似乎卡住", statusIdle: "閒置",
110
110
  connReconnecting: "連不上 AgEnD——可能正在重新啟動。正在重新連線…", connReconnectingIn: "連不上 AgEnD——可能正在重新啟動。{0} 秒後再試。",
111
111
  hydrating: "正在載入目前的狀態…", hydrateFailed: "無法載入目前的狀態(提示、送達標記)。",
112
112
  skip: "跳到對話", loadFailed: "無法載入。", retry: "再試一次", loading: "載入中…",
@@ -45,6 +45,8 @@ export const SYNCED = {
45
45
  "color.show": { ls: "agend_color_show", ...json },
46
46
  "theme.pageByInstance": { ls: "agend_theme_page", ...json }, // #2196
47
47
  "color.off": { ls: "agend_color_off", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
48
+ // The AI usage view's compact mode: one line per login (usage-dialog.js), in the dialog and the rail alike.
49
+ "usage.compact": { ls: "agend_usage_compact", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
48
50
  };
49
51
  // #1629: fixed personal scope. A separate session-bound owner-effect store synchronizes these, never this host cache.
50
52
  export const OWNER_EFFECT_PREF_KEYS = Object.freeze(["fx.sidebar", "fx.header", "fx.page", "fx.box", "fx.cursor"]);
@@ -284,6 +284,8 @@ appStore.subscribe((s) => {
284
284
 
285
285
  function statusLabel(i, exec, awaiting) {
286
286
  if (awaiting != null) return t("app.needsYou");
287
+ if (i.startup_phase === "queued") return t("app.statusQueued");
288
+ if (i.startup_phase === "starting") return t("app.statusStarting");
287
289
  if (i.status !== "running") return i.status === "crashed" ? t("app.statusCrashed") : t("app.statusStopped");
288
290
  if (exec === "working") return t("app.statusWorking");
289
291
  if (exec === "stuck") return t("app.statusStuck");
@@ -291,6 +293,8 @@ function statusLabel(i, exec, awaiting) {
291
293
  }
292
294
  export function statusClass(i, exec, awaiting) {
293
295
  if (awaiting != null) return "warn";
296
+ if (i?.startup_phase === "starting") return "busy";
297
+ if (i?.startup_phase === "queued") return "off";
294
298
  if (!i || i.status !== "running") return i && i.status === "crashed" ? "bad" : "off";
295
299
  return exec === "working" ? "busy" : exec === "stuck" ? "warn" : "ok";
296
300
  }
@@ -500,6 +504,13 @@ function ConnectionLine() {
500
504
  // (agend-auth's banner says that) draw nothing either; only reconnecting and a failed catch-up are shown.
501
505
  return null;
502
506
  }
507
+
508
+ /** A connected fleet is still bringing up agents; this is distinct from the reconnect banner. */
509
+ export function StartupLine() {
510
+ const { startup } = useStore(appStore);
511
+ if (!startup) return null;
512
+ return html`<div data-trust="status" class="conn fleet-starting" role="status">${t("app.fleetStarting", startup.ready, startup.total)}</div>`;
513
+ }
503
514
  /** #1580: the fleet is not answering (a restart): said, with when the next try is — never a silent stale page. */
504
515
  function Reconnecting() {
505
516
  const { retryAt } = useStore(appStore);
@@ -658,6 +669,7 @@ export function Shell({ panels, onNewInstance, viewOnly = false }) {
658
669
  <div class="scrim" onClick=${closeDrawer} aria-hidden="true"></div>
659
670
  <main id="main" class="main" tabindex="-1" inert=${shell.drawer && narrow() ? true : undefined}>
660
671
  <${ConnectionLine} />
672
+ <${StartupLine} />
661
673
  ${viewOnly || guest ? null : html`<${OperationLine} route=${nav.route} />`}
662
674
  ${viewOnly || guest || !shell.banner ? null : html`<${shell.banner} />`}
663
675
  <${Outlet} route=${nav.route} seq=${nav.seq} panels=${panels} />
@@ -50,6 +50,7 @@ export function applyStatus(d) {
50
50
  if (i.state === "awaiting_input") awaiting[i.name] = typeof i.interaction_summary === "string" ? i.interaction_summary : "";
51
51
  }
52
52
  appStore.set({ canvasWorker: d.canvas_worker === true, ready: true, instances: d.instances, uptime: d.uptime ?? 0, exec, awaiting,
53
+ startup: d.startup?.phase === "starting" ? d.startup : null,
53
54
  connections: typeof d.connections === "number" ? d.connections : null,
54
55
  // #1665: the host preferences' revision (app-prefs.js re-reads them when it moves).
55
56
  prefsRev: typeof d.prefs_rev === "number" ? d.prefs_rev : null,
@@ -34,7 +34,8 @@
34
34
  // org canvas reads /ui/traffic when it has no stream).
35
35
  // #2233: backend_login — a dashboard sign-in started, moved or ended (no details: the page reads its own view).
36
36
  // tasks — the task board changed: the card numbers a write touched (SSE only: the board reads again when it is shown).
37
- const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "reactions", "steps_tick", "clock", "traffic", "delegation", "backend_login", "tasks"];
37
+ // offices — the stored offices changed ({ rev }; SSE only: the Office view reads /ui/offices again).
38
+ const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "reactions", "steps_tick", "clock", "traffic", "delegation", "backend_login", "tasks", "offices"];
38
39
  const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
39
40
  const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
40
41
  const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed
@@ -470,6 +470,36 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
470
470
  such as 「昨天 下午03:58」, ticks) — and a block body would stretch to it; fit-content keeps a short message short, on
471
471
  the right under its meta, and a long one still wraps at the message's max-width. */
472
472
  .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; }
473
+ /* A message this page is sending (chat-thread.js outgoingHtml): shown at once, its text dimmed with "Sending…" until the
474
+ server takes it; failed, a straight red bar with Retry and Delete. Each file: a picture as itself (or a document mark),
475
+ its name, a square-ended bar and its percentage, moved in place. */
476
+ .msg.user.out .meta { align-items: center; gap: var(--s-2); }
477
+ .msg.out.out-queued .body, .msg.out.out-sending .body { color: var(--text-3); }
478
+ .msg.out.out-queued .body .md, .msg.out.out-sending .body .md { opacity: .72; }
479
+ .out-state { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-3); }
480
+ .out-spin { width: 10px; height: 10px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: out-spin .8s linear infinite; }
481
+ @keyframes out-spin { to { transform: rotate(360deg); } }
482
+ .out-files { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-2); }
483
+ .msg.out .body .out-files:first-child { margin-top: 0; }
484
+ .out-file { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
485
+ .out-pic { width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-md); flex: none; background: var(--surface-2); }
486
+ .msg.out.out-queued .out-pic, .msg.out.out-sending .out-pic { opacity: .7; }
487
+ .out-doc { width: 32px; height: 40px; flex: none; border: 1px solid var(--border); border-radius: 4px; background: var(--surface-2); }
488
+ .out-file-info { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px var(--s-2); min-width: 0; flex: 1; min-width: 140px; }
489
+ .out-name { font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
490
+ .out-pct { font-size: var(--fs-xs); color: var(--text-3); font-variant-numeric: tabular-nums; }
491
+ .out-bar { grid-column: 1 / -1; height: 3px; background: var(--border); overflow: hidden; }
492
+ .out-fill { display: block; height: 100%; background: var(--accent); transform: scaleX(0); transform-origin: left; transition: transform .2s linear; }
493
+ .out-file[data-state="done"] .out-bar, .out-file[data-state="done"] .out-pct { visibility: hidden; }
494
+ .out-file[data-state="failed"] .out-fill { background: var(--danger); }
495
+ .out-file[data-state="failed"] .out-pct { color: var(--danger); }
496
+ .msg.out.out-sent .out-file .out-bar, .msg.out.out-sent .out-file .out-pct { visibility: hidden; }
497
+ /* Hugs its words under the bubble's right edge, as the bubble does: the bar stays beside them, never across the row. */
498
+ .out-note { --lbar-color: var(--danger); display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); width: fit-content; max-width: 100%;
499
+ margin: var(--s-2) 0 0 auto; padding: var(--s-1) 0 var(--s-1) calc(var(--s-2) + 3px); font-size: var(--fs-xs); color: var(--danger); }
500
+ .out-error { overflow-wrap: anywhere; }
501
+ .out-actions { display: inline-flex; gap: var(--s-1); }
502
+ @media (prefers-reduced-motion: reduce) { .out-spin { animation: none; border-right-color: currentColor; opacity: .6; } .out-fill { transition: none; } }
473
503
  /* A person's click on a reply button: the bubble shows 🔘 and the button's label (never the value the agent was given);
474
504
  live, the label is a button that shows the reply its buttons were on. */
475
505
  .click-chip { display: flex; }
@@ -795,6 +825,8 @@ img.amb-p { object-fit: contain; }
795
825
  /* #1939 S2a: Settings → Web → Web authority. */
796
826
  .s-web-authority .s-va-sessions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
797
827
  .s-web-authority .s-va-sessions li { padding: var(--s-2) var(--s-3); border: 1px solid var(--border); border-radius: var(--r-sm); overflow-wrap: anywhere; }
828
+ /* #2453: "over your named link" stays one phrase (the list item may break anywhere, CJK included). */
829
+ .s-web-authority .s-va-named { white-space: nowrap; }
798
830
  .s-web-authority .s-va-warn { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); border-radius: var(--r-sm); padding: var(--s-1) var(--s-2); }
799
831
  .s-web-authority .save-row { flex-wrap: wrap; }
800
832
  /* #1920: a guest's note under the composer — on a phone too, where the key hints are hidden. */
@@ -1032,7 +1064,21 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
1032
1064
  down, never the count; the count is as tall as a header, so the row does not grow. */
1033
1065
  .nav-facets { display: flex; align-items: flex-start; gap: var(--s-2); padding: var(--s-2) var(--s-1) 0; }
1034
1066
  .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; }
1035
- .nav-facets .nav-shown { flex: none; margin: 0 0 0 auto; padding: 2px 0; white-space: nowrap; }
1067
+ /* #2401: the count, "shown" under it and Clear under that. The word's width is held by ::before (its text from
1068
+ data-reserve) and idle Clear's by the button itself, both at no height, unseen and out of reach (visibility): the column —
1069
+ and the headers' box beside it — is as wide with a facet set as without. */
1070
+ .nav-facet-end { flex: none; margin: 0 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; }
1071
+ .nav-facets .nav-shown { margin: 0; padding: 2px 0; white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }
1072
+ .nav-shown-word { display: block; }
1073
+ .nav-facet-end::before { content: attr(data-reserve); display: block; height: 0; overflow: hidden; visibility: hidden; font-size: var(--fs-xs); white-space: nowrap; }
1074
+ .nav-facet-end .is-idle { visibility: hidden; height: 0; min-height: 0; margin: 0; padding-top: 0; padding-bottom: 0; border-top-width: 0; border-bottom-width: 0; overflow: hidden; }
1075
+ /* No open list beside it (.compact): one cell of one line, as wide as the count or Clear; a facet set, Clear in the count's
1076
+ place (the count transparent, still read; the word for a screen reader only, so its width is not held here). */
1077
+ .nav-facets.compact .nav-facet-end { display: grid; justify-items: end; align-items: center; }
1078
+ .nav-facets.compact .nav-facet-end > * { grid-area: 1 / 1; margin: 0; }
1079
+ .nav-facets.compact .nav-facet-end::before { display: none; }
1080
+ .nav-facets.compact .nav-shown.is-set { opacity: 0; pointer-events: none; }
1081
+ .nav-facets.compact .nav-shown-word { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
1036
1082
  .nav-facet { font-size: var(--fs-xs); color: var(--text-2); min-width: 0; max-width: 100%; }
1037
1083
  /* #1700: a facet group's header — the whole row toggles its list; folded with a filter set, a short summary. */
1038
1084
  .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);
@@ -1041,10 +1087,15 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
1041
1087
  .nav-facet-head:focus-visible { outline: 2px solid var(--accent); }
1042
1088
  .nav-facet-head .caret { color: var(--text-3); flex: none; }
1043
1089
  .nav-facet-sum { min-width: 0; max-width: 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--accent); }
1090
+ /* #2401: the summary takes only the room its line has left — never counted when the headers wrap (contain: inline-size,
1091
+ its gap taken back), so a facet set never sends a header to a line of its own; the header holding it grows into the rest. */
1092
+ .nav-facet.has-sum { flex-grow: 1; }
1093
+ .nav-facet.has-sum .nav-facet-head { width: 100%; }
1094
+ .nav-facet-sum { contain: inline-size; flex: 1 1 0; margin-left: calc(-1 * var(--s-1)); padding-left: var(--s-1); }
1044
1095
  .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; }
1045
1096
  .nav-facet-list { display: flex; flex-direction: column; gap: 2px; padding: var(--s-1) var(--s-2); }
1046
1097
  .nav-chip { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; color: var(--text); }
1047
- .nav-facet-reset { font-size: var(--fs-xs); }
1098
+ .nav-facet-reset { font-size: var(--fs-xs); flex: none; white-space: nowrap; margin-top: 2px; padding: 2px var(--s-1); }
1048
1099
 
1049
1100
  /* One gap on every page (View's, #1448): the header's controls are the same size on every tab. */
1050
1101
  .panel-actions .btn { gap: var(--s-1); }
@@ -1102,7 +1153,42 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
1102
1153
  /* #1673: the usage rail beside the page on a wide screen (header-tools.js UsageRail; shown only at min-width 1200px). */
1103
1154
  .u-rail { width: 320px; flex-shrink: 0; display: flex; flex-direction: column; min-height: 0; border-left: 1px solid var(--border); background: var(--bg); }
1104
1155
  .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); }
1105
- .u-rail-head h2 { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-semibold); }
1156
+ .u-rail-head h2 { margin: 0; flex: 1; min-width: 0; font-size: var(--fs-md); font-weight: var(--fw-semibold); }
1157
+ /* The usage view's compact mode (usage-dialog.js CompactGroup / CompactRow), the user's pick (2026-10-22): the logins
1158
+ grouped by provider (its name once, in small capitals), each in three short lines — its name and plan with the
1159
+ soonest reset on the right, then each window as a labelled full-width bar and the percent. */
1160
+ .dlg-tools { display: inline-flex; align-items: center; flex: none; }
1161
+ .u-compact-toggle { flex: none; }
1162
+ /* flex: none — a group never shrinks to fit the rail (it would clip its accounts): the rail scrolls instead. */
1163
+ .u-cgroup { flex: none; display: flex; flex-direction: column; border-radius: var(--r-md); background: var(--surface); overflow: hidden; }
1164
+ .u-cg-head { display: flex; align-items: baseline; gap: 4px; padding: 7px var(--s-3) 3px; font-size: 11px; font-weight: var(--fw-semibold);
1165
+ letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-3); }
1166
+ .u-cg-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1167
+ .u-cg-count { flex: none; }
1168
+ .u-crow { display: flex; flex-direction: column; gap: 3px; width: 100%; min-width: 0; padding: 6px var(--s-3); border: 0; border-top: 1px solid var(--border);
1169
+ background: transparent; color: inherit; font: inherit; line-height: 1.3; text-align: left; cursor: pointer; }
1170
+ .u-cg-head + .u-crow { border-top: 0; }
1171
+ .u-crow:hover { background: var(--surface-2); }
1172
+ .u-crow:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--focus); }
1173
+ /* The name never gives way to the plan (#2415); the reset keeps its room on the right. */
1174
+ .u-crow-head { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--fs-sm); }
1175
+ .u-crow-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--fw-semibold); color: var(--text); }
1176
+ .u-crow .u-plan { flex: 0 100 auto; min-width: 22px; max-width: max-content; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1177
+ padding-block: 0; line-height: 16px; font-size: 10.5px; }
1178
+ .u-crow-reset { margin-left: auto; flex: none; padding-left: 6px; font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
1179
+ .u-cm { display: grid; grid-template-columns: 2.6em minmax(0, 1fr) 3.4em; align-items: center; gap: 7px; min-width: 0; }
1180
+ .u-cm-tag { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1181
+ /* #2240: an account of several pools (agy) names each pool's line: a wider label, ending in an ellipsis if it must. */
1182
+ .u-crow.is-pooled .u-cm { grid-template-columns: minmax(0, 7.5em) minmax(0, 1fr) 3.4em; }
1183
+ .u-cm-bar { height: 6px; border-radius: var(--r-full); background: var(--surface-2); overflow: hidden; }
1184
+ .u-cm-fill { display: block; height: 100%; width: 0; min-width: 2px; border-radius: var(--r-full); background: var(--accent); }
1185
+ .u-cm-fill.warn { background: var(--warn); } .u-cm-fill.crit { background: var(--danger); }
1186
+ .u-cm-pct { text-align: right; font-size: 13px; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; color: var(--text); }
1187
+ .u-cm-pct.warn { color: var(--warn); } .u-cm-pct.crit { color: var(--danger); }
1188
+ .u-crow-note { font-size: var(--fs-xs); color: var(--text-3); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1189
+ .u-crow-note.is-alert::before { content: "●"; margin-right: 5px; color: var(--danger); }
1190
+ .u-crow.is-stale .u-crow-note.is-alert::before { color: var(--warn); }
1191
+ @media (prefers-reduced-motion: no-preference) { .u-crow { transition: background-color 0.15s var(--ease); } }
1106
1192
  .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); }
1107
1193
  @media (max-width: 1199px) { .u-rail { display: none; } }
1108
1194
  @media (max-width: 899px) {
@@ -1460,6 +1546,49 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
1460
1546
  .oc-tools { margin-left: 0; width: 100%; flex-wrap: wrap; row-gap: var(--s-2); }
1461
1547
  .oc-mini { display: none; }
1462
1548
  }
1549
+ /* ── Fleet → Org chart → Office (fleet-office.js): the fleet as a pixel office on one <canvas>. Its size is set through
1550
+ the CSSOM (never a style attribute, #1300); the stage scrolls, the page around it too. ── */
1551
+ .oo { display: flex; flex-direction: column; gap: var(--s-2); }
1552
+ .oo-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
1553
+ .oo-pick { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-2); }
1554
+ .oo-pick .tb-select { max-width: 200px; }
1555
+ .oo-people { position: relative; }
1556
+ /* #2399: the offices' minimal editor (fleet-office-editor.js) — one row per office, its members unfolded under it. */
1557
+ .oe-form { border: 0; margin: 0; padding: 0; min-width: 0; }
1558
+ /* #2431: after a 409, the two ways on (keep my edits, load theirs), side by side; they wrap on a phone. */
1559
+ .oe-conflict { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
1560
+ .oe-list { list-style: none; margin: var(--s-3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
1561
+ .oe-row { padding: var(--s-3); border: 1px solid var(--border); border-radius: var(--r-md); background: var(--surface); display: flex; flex-direction: column; gap: var(--s-2); }
1562
+ .oe-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s-2); }
1563
+ .oe-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
1564
+ .oe-label { font-size: var(--fs-xs); color: var(--text-2); }
1565
+ .oe-name { min-width: 0; width: 16em; max-width: 100%; }
1566
+ .oe-remove { margin-left: auto; }
1567
+ .oe-members { display: flex; flex-wrap: wrap; gap: var(--s-3); }
1568
+ .oe-group { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; min-width: 12em; max-height: 240px; overflow-y: auto; }
1569
+ .oe-group legend { font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-2); padding: 0; margin-bottom: 4px; }
1570
+ .oo-people > .btn.on { border-color: var(--accent); }
1571
+ .oo-people-pop { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; width: min(280px, calc(100vw - 32px)); max-height: 340px; overflow-y: auto;
1572
+ display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-2); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: var(--shadow-2); }
1573
+ .oo-people-pop input[type="search"] { width: 100%; height: 32px; }
1574
+ .oo-people-list { list-style: none; margin: 0; padding: 0; }
1575
+ .oo-people-list label { display: flex; align-items: center; gap: 8px; padding: 3px 2px; font-size: var(--fs-xs); color: var(--text); cursor: pointer; overflow-wrap: anywhere; }
1576
+ .oo-note { font-size: var(--fs-xs); }
1577
+ .oo-stage { position: relative; height: clamp(420px, calc(100dvh - 280px), 960px); overflow: auto; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--bg); }
1578
+ .oo-canvas { display: block; margin: 0 auto; image-rendering: pixelated; }
1579
+ .oo-canvas.hot { cursor: pointer; }
1580
+ .oo-tip { position: absolute; left: 0; top: 0; z-index: 5; max-width: 240px; pointer-events: none; padding: 6px 10px; font-size: var(--fs-xs); line-height: 1.4;
1581
+ color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: var(--shadow-2); overflow-wrap: anywhere; }
1582
+ /* The desks as a list: for a screen reader, and shown (over the stage's corner) while the keyboard is in it. */
1583
+ .oo-desks { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; list-style: none; }
1584
+ .oo-desks:focus-within { position: sticky; left: var(--s-2); bottom: var(--s-2); width: auto; height: auto; max-height: 40%; margin: 0; padding: var(--s-1); clip: auto; overflow-y: auto;
1585
+ display: inline-flex; flex-wrap: wrap; gap: 4px; max-width: calc(100% - 2 * var(--s-2)); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); white-space: normal; }
1586
+ .oo-desk { font-size: var(--fs-xs); padding: 2px 6px; border-radius: var(--r-sm); background: var(--bg); color: var(--text); border: 1px solid var(--border); }
1587
+ .oo-desk:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
1588
+ @media (max-width: 640px) {
1589
+ .oo-stage { height: calc(100dvh - 300px); min-height: 340px; }
1590
+ .oo-pick .tb-select { max-width: 150px; }
1591
+ }
1463
1592
  /* A phone: Fleet's five tabs fit when only the open one shows its label (the others keep theirs for screen readers). */
1464
1593
  @media (max-width: 479px) {
1465
1594
  .p-fleet .seg-item:not(.active) > span, .p-settings .seg-item:not(.active) > span { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@@ -0,0 +1,35 @@
1
+ // What a guest's browser is shown for a page it cannot open. Loaded from /assets/guest-refused.js.
2
+ //
3
+ // The gate answers a guest's navigation with this page instead of its JSON (#2399 smoke): "members" for a page that is
4
+ // not a guest's (Fleet, Settings…), "ended" when no chat is shared with the guest any more. The page's words are in
5
+ // the browser's language, as the sign-in page's are; the HTML carries the English for a browser without scripts.
6
+ (() => {
7
+ "use strict";
8
+ const T = {
9
+ en: {
10
+ members_title: "This page is not open to guests",
11
+ members_lead: "This page is for the fleet's members. As a guest you can use the chats shared with you.",
12
+ members_go: "Open your chats",
13
+ ended_title: "No chats are shared with you",
14
+ ended_lead: "Your guest access on this fleet has no chat now — you may have left the room it came from. Ask whoever invited you to share a chat with you again.",
15
+ },
16
+ "zh-TW": {
17
+ members_title: "訪客無法開啟這個頁面",
18
+ members_lead: "這個頁面只給 fleet 的成員使用。身為訪客,你可以使用分享給你的聊天。",
19
+ members_go: "打開你的聊天",
20
+ ended_title: "目前沒有分享給你的聊天",
21
+ ended_lead: "你在這個 fleet 的訪客權限目前沒有任何聊天——你可能已經離開當初的頻道。請聯絡邀請你的人,再分享聊天給你。",
22
+ },
23
+ };
24
+ const lang = /^zh/i.test(navigator.language || "") ? "zh-TW" : "en";
25
+ const t = (k) => T[lang][k] || T.en[k] || k;
26
+ document.documentElement.lang = lang === "zh-TW" ? "zh-Hant" : "en";
27
+ const main = document.getElementById("refused");
28
+ const why = main && main.dataset.why === "ended" ? "ended" : "members";
29
+ document.getElementById("title").textContent = t(`${why}_title`);
30
+ document.getElementById("lead").textContent = t(`${why}_lead`);
31
+ // Nothing to open when no chat is shared (the chats would answer this page again): no button then.
32
+ const go = document.getElementById("go");
33
+ if (go) { if (why === "ended") go.remove(); else go.textContent = t("members_go"); }
34
+ document.title = `AgEnD — ${t(`${why}_title`)}`;
35
+ })();
@@ -9,7 +9,7 @@ import { html, useEffect, useState } from "./app-html.js";
9
9
  import { t } from "./app-i18n.js";
10
10
  import { createStore, useStore } from "./app-store.js";
11
11
  import { Icon } from "./ui-icons.js";
12
- import { UsageBody, UsageDialog } from "./usage-dialog.js";
12
+ import { UsageBody, UsageCompactToggle, UsageDialog } from "./usage-dialog.js";
13
13
 
14
14
  export const TEXT_SIZE_KEY = "agend_text_size";
15
15
  export const VIEW_FIT_KEY = "agend_view_fit";
@@ -126,7 +126,7 @@ export function UsageRail({ route }) {
126
126
  const { open } = useStore(usageRailStore);
127
127
  if (!wide || !open || !route || !RAIL_PANELS.includes(route.panel) || !usageOffered()) return null;
128
128
  return html`<aside data-trust="usage" class="u-rail" aria-label=${t("view.usage.title_plain")}>
129
- <div class="u-rail-head"><h2>${t("view.usage.title")}</h2>
129
+ <div class="u-rail-head"><h2>${t("view.usage.title")}</h2><${UsageCompactToggle} />
130
130
  <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>
131
131
  <div class="u-rail-body"><${UsageBody} owner="usage-rail" /></div>
132
132
  </aside>`;
@@ -82,11 +82,12 @@ export function backendLabel(backend) { return BACKEND_LABELS[backend] || backen
82
82
  export const KNOWN_BACKENDS = new Set(Object.keys(BACKEND_LABELS));
83
83
  export const STATUS_KEYS = { running: "statusRunning", paused: "statusPaused", stopped: "statusStopped", crashed: "statusCrashed" };
84
84
 
85
- /** The status chips, in order (design Q5): what the row's dot says, in five words. */
86
- export const STATUS_FACETS = ["working", "needs", "idle", "stopped", "crashed"];
85
+ /** The status chips, in order: what the row's dot says, including agents queued for this boot. */
86
+ export const STATUS_FACETS = ["working", "needs", "starting", "idle", "stopped", "crashed"];
87
87
  /** Which status chip a row falls under: needs you first, then crashed, not running, working (or stuck), idle. */
88
88
  export function statusFacet(it, exec, awaiting) {
89
89
  if (awaiting != null) return "needs";
90
+ if (it?.startup_phase === "queued" || it?.startup_phase === "starting") return "starting";
90
91
  if (!it || it.status === "crashed") return "crashed";
91
92
  if (it.status !== "running") return "stopped";
92
93
  return exec === "working" || exec === "stuck" ? "working" : "idle";
@@ -127,7 +128,8 @@ export function filterSidebar(groupNames, instByGroup, rosterByName, query, labe
127
128
  }
128
129
  export function instanceTooltip(it) {
129
130
  const context = it.context_pct != null && Number.isFinite(it.context_pct) ? `${Math.round(Math.min(100, Math.max(0, it.context_pct)))}%` : tn("tooltipUnavailable");
130
- const status = STATUS_KEYS[it.status] ? tn(STATUS_KEYS[it.status]) : it.status || tn("statusUnknown");
131
+ const status = it.startup_phase === "queued" ? t("app.statusQueued") : it.startup_phase === "starting" ? t("app.statusStarting")
132
+ : STATUS_KEYS[it.status] ? tn(STATUS_KEYS[it.status]) : it.status || tn("statusUnknown");
131
133
  const line = (key, value) => tn(key, { value });
132
134
  const lines = [];
133
135
  const name = nameOf(it);
@@ -361,12 +363,15 @@ export function avatarOf(it) {
361
363
  /** The status dot on the avatar: needs you, crashed, paused, stopped, looks stuck, working, idle — colour AND a label. */
362
364
  export function dotState(it, exec, awaiting) {
363
365
  if (awaiting != null) return "needs";
366
+ if (it?.startup_phase === "queued") return "queued";
367
+ if (it?.startup_phase === "starting") return "starting";
364
368
  if (!it || it.status === "crashed") return "crashed";
365
369
  if (it.status === "paused") return "paused";
366
370
  if (it.status !== "running") return "stopped";
367
371
  return exec === "stuck" ? "stuck" : exec === "working" ? "working" : "idle";
368
372
  }
369
373
  const DOT = {
374
+ queued: ["off", () => t("app.statusQueued")], starting: ["busy", () => t("app.statusStarting")],
370
375
  needs: ["warn", () => t("app.needsYou")], crashed: ["bad", () => t("app.statusCrashed")], paused: ["off hollow", () => t("app.statusPaused")],
371
376
  stopped: ["off", () => t("app.statusStopped")], stuck: ["warn", () => t("app.statusStuck")], working: ["busy", () => t("app.statusWorking")],
372
377
  idle: ["ok", () => t("app.statusIdle")],
@@ -393,7 +398,7 @@ export function InstanceAvatar({ it, state, cli = false }) {
393
398
 
394
399
  // ── The list ──
395
400
 
396
- const statusText = (facet) => (facet === "needs" ? t("app.needsYou") : facet === "working" ? t("app.statusWorking") : facet === "idle" ? t("app.statusIdle")
401
+ const statusText = (facet) => (facet === "needs" ? t("app.needsYou") : facet === "starting" ? t("app.statusStarting") : facet === "working" ? t("app.statusWorking") : facet === "idle" ? t("app.statusIdle")
397
402
  : facet === "crashed" ? t("app.statusCrashed") : t("app.statusStopped"));
398
403
 
399
404
  // ── #2179: when each instance last replied to a person (the status frame's last_reply) ──
@@ -571,7 +576,7 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
571
576
  const sel = f[key];
572
577
  const summary = !open && sel.length ? (sel.length <= 2 ? sel.map(text).join(", ") : tn("facetSelected", { n: sel.length })) : "";
573
578
  const hint = tn(open ? "facetHide" : "facetShow", { group: label });
574
- return html`<div class=${`nav-facet${open ? " open" : ""}`}>
579
+ return html`<div class=${`nav-facet${open ? " open" : ""}${summary ? " has-sum" : ""}`}>
575
580
  <button type="button" class="nav-facet-head" aria-expanded=${open ? "true" : "false"} aria-controls=${`nav-facet-${key}`}
576
581
  title=${summary ? `${hint} (${label} · ${summary})` : hint} onClick=${() => toggleFacet(key)}>
577
582
  <${Icon} name="chevron" size=${12} cls=${open ? "caret" : "caret folded"} /><span class="nav-facet-label">${label}</span>
@@ -614,6 +619,9 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
614
619
  aria-label=${tn(starred ? "favRemove" : "favAdd", { name: label })} title=${full ? tn("favFull", { max: MAX_FAVORITES }) : tn(starred ? "favRemove" : "favAdd", { name: label })}
615
620
  onClick=${() => toggleFavorite(name, known)}><${Icon} name="star" size=${14} /></button>` : null}</li>`;
616
621
  };
622
+ // #2401: an open list of two or more lines leaves room under the count for "shown" and Clear (stacked); otherwise compact.
623
+ const facetSet = f.status.length > 0 || f.cli.length > 0;
624
+ const stacked = (!viewOnly && facetOpen.status) || (facetOpen.cli && backends.length > 1);
617
625
  // The list scrolls; the filter under it stays in view (#999), so it sits outside the scrolling section.
618
626
  return html`<div class=${`view-roster inst-nav${editing && canStar ? " fav-editing" : ""}`}>
619
627
  <div data-trust="navigation" class="side-section" id="instanceList" ref=${list}>
@@ -653,13 +661,22 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
653
661
  ${f.q ? html`<button type="button" class="icon-btn v-filter-clear" aria-label=${tn("filterClear")} title=${tn("filterClear")}
654
662
  onClick=${() => { recordEdit(filterRef.current); setNavFilter({ q: "" }); filterRef.current && filterRef.current.focus(); }}><${Icon} name="close" size=${14} /></button>` : null}
655
663
  </label>
656
- <div class="nav-facets">
664
+ <div class=${`nav-facets${stacked ? "" : " compact"}`}>
657
665
  <div class="nav-facet-groups">
658
666
  ${viewOnly ? null : chips("status", STATUS_FACETS, tn("filterStatus"), statusText)}
659
667
  ${backends.length > 1 || f.cli.length ? chips("cli", backends, tn("filterCli"), backendLabel) : null}
660
- ${f.status.length || f.cli.length ? html`<button type="button" class="chip-btn nav-facet-reset" onClick=${() => setNavFilter({ status: [], cli: [] })}>${tn("filterFacetsReset")}</button>` : null}
661
668
  </div>
662
- <p class="note nav-shown" aria-live="polite">${shown.shown} / ${shown.total}${shown.active ? ` ${tn("filterShown")}` : ""}</p>
669
+ ${/* #2401: the count, its "shown" and Clear stack at the row's right end — beside an open list there is room under
670
+ the count, so setting a facet adds no row (a word beside the count, or Clear beside it, wrapped CLI under the
671
+ list). With no such list (.compact) they share one cell of one line: a facet set, Clear takes the count's place
672
+ (the count kept for a screen reader and in Clear's title). Either way the column keeps Clear's width (idle, there
673
+ unseen at no height: .is-idle, a class — never a style attribute, #1300), and stacked the word's too
674
+ (data-reserve, drawn unseen at no height), so the headers' box beside it never narrows. */ ""}
675
+ <div class="nav-facet-end" data-reserve=${tn("filterShown")}>
676
+ <p class=${`note nav-shown${facetSet ? " is-set" : ""}`} aria-live="polite">${shown.shown} / ${shown.total}${shown.active ? html`<span class="nav-shown-word"> ${tn("filterShown")}</span>` : ""}</p>
677
+ <button type="button" class=${`chip-btn nav-facet-reset${facetSet ? "" : " is-idle"}`} title=${facetSet ? `${shown.shown} / ${shown.total} ${tn("filterShown")}` : undefined}
678
+ onClick=${() => setNavFilter({ status: [], cli: [] })}>${tn("filterFacetsReset")}</button>
679
+ </div>
663
680
  </div>
664
681
  </div>`}
665
682
  </div>`;
@@ -10,7 +10,7 @@ import { t } from "./app-i18n.js";
10
10
  * <${Dialog} title="Create task" onClose=${…} actions=${html`…`}>…body…</${Dialog}>
11
11
  * Rendered = open; the parent unmounts it to close. `busy` keeps Esc and the backdrop from closing it mid-request.
12
12
  */
13
- export function Dialog({ title, onClose, actions, children, busy = false, wide = false, drawer = false, label }) {
13
+ export function Dialog({ title, onClose, actions, children, busy = false, wide = false, drawer = false, label, tools = null }) {
14
14
  const ref = useRef(null);
15
15
  const opener = useRef(null);
16
16
  const busyRef = useRef(busy);
@@ -38,7 +38,7 @@ export function Dialog({ title, onClose, actions, children, busy = false, wide =
38
38
  }, [onClose]);
39
39
  return html`<dialog data-trust="dialog" ref=${ref} class=${`dlg${wide ? " dlg-wide" : ""}${drawer ? " dlg-drawer" : ""}`} aria-label=${label || title}>
40
40
  <div class="dlg-box">
41
- <div class="dlg-head"><h2 class="dlg-title">${title}</h2>
41
+ <div class="dlg-head"><h2 class="dlg-title">${title}</h2>${tools ? html`<span class="dlg-tools">${tools}</span>` : null}
42
42
  <button type="button" class="icon-btn dlg-x" aria-label=${t("app.close")} title=${t("app.close")} onClick=${() => { if (!busyRef.current) onClose(); }}><${Icon} name="close" /></button></div>
43
43
  <div class="dlg-body">${children}</div>
44
44
  ${actions ? html`<div class="dlg-actions">${actions}</div>` : null}