@songsid/agend 2.3.0-beta.2 → 2.3.0-beta.21

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 (502) hide show
  1. package/README.md +7 -1
  2. package/README.zh-TW.md +7 -1
  3. package/dist/agent-cli-instructions.md +9 -2
  4. package/dist/agent-cli.js +63 -2
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +4 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.d.ts +4 -1
  9. package/dist/agent-file-read.js +189 -16
  10. package/dist/agent-file-read.js.map +1 -1
  11. package/dist/agent-whats-new.d.ts +11 -0
  12. package/dist/agent-whats-new.js +29 -5
  13. package/dist/agent-whats-new.js.map +1 -1
  14. package/dist/auth-api.d.ts +30 -1
  15. package/dist/auth-api.js +45 -13
  16. package/dist/auth-api.js.map +1 -1
  17. package/dist/authority/authority-state.d.ts +38 -0
  18. package/dist/authority/authority-state.js +114 -0
  19. package/dist/authority/authority-state.js.map +1 -0
  20. package/dist/authority/session-authorizations.d.ts +43 -0
  21. package/dist/authority/session-authorizations.js +102 -0
  22. package/dist/authority/session-authorizations.js.map +1 -0
  23. package/dist/authority/web-authority.d.ts +120 -0
  24. package/dist/authority/web-authority.js +157 -0
  25. package/dist/authority/web-authority.js.map +1 -0
  26. package/dist/backend/claude-code.d.ts +2 -2
  27. package/dist/backend/claude-code.js +18 -8
  28. package/dist/backend/claude-code.js.map +1 -1
  29. package/dist/backend/codex-metadata.d.ts +8 -0
  30. package/dist/backend/codex-metadata.js +74 -17
  31. package/dist/backend/codex-metadata.js.map +1 -1
  32. package/dist/backend/codex.js +15 -2
  33. package/dist/backend/codex.js.map +1 -1
  34. package/dist/backend/credential-profile.d.ts +2 -0
  35. package/dist/backend/credential-profile.js +2 -0
  36. package/dist/backend/credential-profile.js.map +1 -1
  37. package/dist/backend/types.d.ts +6 -0
  38. package/dist/backend/types.js +15 -0
  39. package/dist/backend/types.js.map +1 -1
  40. package/dist/backend-availability.d.ts +120 -0
  41. package/dist/backend-availability.js +396 -0
  42. package/dist/backend-availability.js.map +1 -0
  43. package/dist/backend-command.d.ts +23 -3
  44. package/dist/backend-command.js +25 -4
  45. package/dist/backend-command.js.map +1 -1
  46. package/dist/bug-report-cli.d.ts +30 -0
  47. package/dist/bug-report-cli.js +138 -0
  48. package/dist/bug-report-cli.js.map +1 -0
  49. package/dist/bug-report.d.ts +47 -0
  50. package/dist/bug-report.js +136 -0
  51. package/dist/bug-report.js.map +1 -0
  52. package/dist/bundled-skills.d.ts +14 -0
  53. package/dist/bundled-skills.js +29 -0
  54. package/dist/bundled-skills.js.map +1 -0
  55. package/dist/cache-ledger.d.ts +16 -3
  56. package/dist/cache-ledger.js +33 -13
  57. package/dist/cache-ledger.js.map +1 -1
  58. package/dist/cache-service.d.ts +55 -3
  59. package/dist/cache-service.js +152 -16
  60. package/dist/cache-service.js.map +1 -1
  61. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  62. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  63. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  64. package/dist/channel/adapters/discord.d.ts +9 -0
  65. package/dist/channel/adapters/discord.js +49 -4
  66. package/dist/channel/adapters/discord.js.map +1 -1
  67. package/dist/channel/mcp-tools.js +22 -11
  68. package/dist/channel/mcp-tools.js.map +1 -1
  69. package/dist/channel/types.d.ts +6 -0
  70. package/dist/classic-channel-manager.d.ts +10 -5
  71. package/dist/classic-channel-manager.js +12 -9
  72. package/dist/classic-channel-manager.js.map +1 -1
  73. package/dist/cli.js +224 -111
  74. package/dist/cli.js.map +1 -1
  75. package/dist/config-validator.d.ts +6 -0
  76. package/dist/config-validator.js +129 -3
  77. package/dist/config-validator.js.map +1 -1
  78. package/dist/config.d.ts +13 -1
  79. package/dist/config.js +14 -5
  80. package/dist/config.js.map +1 -1
  81. package/dist/cross-instance-notice.d.ts +7 -0
  82. package/dist/cross-instance-notice.js +10 -0
  83. package/dist/cross-instance-notice.js.map +1 -1
  84. package/dist/daemon.d.ts +24 -1
  85. package/dist/daemon.js +151 -19
  86. package/dist/daemon.js.map +1 -1
  87. package/dist/decision-list.d.ts +25 -0
  88. package/dist/decision-list.js +30 -0
  89. package/dist/decision-list.js.map +1 -0
  90. package/dist/delivery-outbox.d.ts +10 -0
  91. package/dist/delivery-outbox.js +17 -1
  92. package/dist/delivery-outbox.js.map +1 -1
  93. package/dist/diagnostic-redaction.d.ts +10 -0
  94. package/dist/diagnostic-redaction.js +167 -0
  95. package/dist/diagnostic-redaction.js.map +1 -0
  96. package/dist/fleet-context.d.ts +10 -0
  97. package/dist/fleet-manager.d.ts +505 -15
  98. package/dist/fleet-manager.js +1870 -204
  99. package/dist/fleet-manager.js.map +1 -1
  100. package/dist/fleet-process-handlers.d.ts +18 -0
  101. package/dist/fleet-process-handlers.js +69 -0
  102. package/dist/fleet-process-handlers.js.map +1 -0
  103. package/dist/fleet-traffic.d.ts +74 -0
  104. package/dist/fleet-traffic.js +196 -0
  105. package/dist/fleet-traffic.js.map +1 -0
  106. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  107. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +17 -6
  108. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
  109. package/dist/general-knowledge/skills/fleet-config/SKILL.md +2 -0
  110. package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
  111. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  112. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  113. package/dist/general-knowledge/skills/web-appearance/SKILL.md +190 -47
  114. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +545 -0
  115. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  116. package/dist/human-delivery-receipts.d.ts +24 -1
  117. package/dist/human-delivery-receipts.js +64 -11
  118. package/dist/human-delivery-receipts.js.map +1 -1
  119. package/dist/instance-config-impact.js +7 -1
  120. package/dist/instance-config-impact.js.map +1 -1
  121. package/dist/instance-lifecycle.d.ts +10 -1
  122. package/dist/instance-lifecycle.js +79 -18
  123. package/dist/instance-lifecycle.js.map +1 -1
  124. package/dist/instance-names.d.ts +4 -0
  125. package/dist/instance-names.js +12 -0
  126. package/dist/instance-names.js.map +1 -0
  127. package/dist/instructions.d.ts +12 -1
  128. package/dist/instructions.js +13 -2
  129. package/dist/instructions.js.map +1 -1
  130. package/dist/locale.js +173 -12
  131. package/dist/locale.js.map +1 -1
  132. package/dist/log-steps.d.ts +1 -0
  133. package/dist/log-steps.js +15 -0
  134. package/dist/log-steps.js.map +1 -1
  135. package/dist/login-controller.d.ts +45 -0
  136. package/dist/login-controller.js +101 -5
  137. package/dist/login-controller.js.map +1 -1
  138. package/dist/ls-attach-resolve.d.ts +96 -0
  139. package/dist/ls-attach-resolve.js +180 -0
  140. package/dist/ls-attach-resolve.js.map +1 -0
  141. package/dist/ls-rows.d.ts +12 -0
  142. package/dist/ls-rows.js +82 -3
  143. package/dist/ls-rows.js.map +1 -1
  144. package/dist/memory-hogs.d.ts +27 -0
  145. package/dist/memory-hogs.js +124 -0
  146. package/dist/memory-hogs.js.map +1 -0
  147. package/dist/needs-you.d.ts +10 -0
  148. package/dist/needs-you.js +2 -1
  149. package/dist/needs-you.js.map +1 -1
  150. package/dist/outbound-handlers.d.ts +11 -0
  151. package/dist/outbound-handlers.js +139 -11
  152. package/dist/outbound-handlers.js.map +1 -1
  153. package/dist/outbound-schemas.d.ts +200 -4
  154. package/dist/outbound-schemas.js +124 -11
  155. package/dist/outbound-schemas.js.map +1 -1
  156. package/dist/public-web-gateway.js +17 -4
  157. package/dist/public-web-gateway.js.map +1 -1
  158. package/dist/public-web-link.d.ts +9 -0
  159. package/dist/public-web-link.js +8 -0
  160. package/dist/public-web-link.js.map +1 -1
  161. package/dist/quickstart-api.d.ts +19 -1
  162. package/dist/quickstart-api.js +94 -4
  163. package/dist/quickstart-api.js.map +1 -1
  164. package/dist/reply-buttons-controller.d.ts +4 -0
  165. package/dist/reply-buttons-controller.js +2 -2
  166. package/dist/reply-buttons-controller.js.map +1 -1
  167. package/dist/restart-progress.d.ts +80 -1
  168. package/dist/restart-progress.js +306 -21
  169. package/dist/restart-progress.js.map +1 -1
  170. package/dist/rollout-index.d.ts +5 -0
  171. package/dist/rollout-index.js +26 -5
  172. package/dist/rollout-index.js.map +1 -1
  173. package/dist/scheduler/db.d.ts +40 -13
  174. package/dist/scheduler/db.js +123 -89
  175. package/dist/scheduler/db.js.map +1 -1
  176. package/dist/scheduler/task-board-text.d.ts +26 -0
  177. package/dist/scheduler/task-board-text.js +135 -0
  178. package/dist/scheduler/task-board-text.js.map +1 -0
  179. package/dist/scheduler/task-board.d.ts +650 -0
  180. package/dist/scheduler/task-board.js +1938 -0
  181. package/dist/scheduler/task-board.js.map +1 -0
  182. package/dist/scheduler/types.d.ts +6 -1
  183. package/dist/search/search-docs.d.ts +58 -0
  184. package/dist/search/search-docs.js +63 -0
  185. package/dist/search/search-docs.js.map +1 -0
  186. package/dist/search/search-index.d.ts +141 -0
  187. package/dist/search/search-index.js +440 -0
  188. package/dist/search/search-index.js.map +1 -0
  189. package/dist/search/search-store.d.ts +122 -0
  190. package/dist/search/search-store.js +236 -0
  191. package/dist/search/search-store.js.map +1 -0
  192. package/dist/search/search-worker.d.ts +1 -0
  193. package/dist/search/search-worker.js +163 -0
  194. package/dist/search/search-worker.js.map +1 -0
  195. package/dist/service-installer.js +12 -0
  196. package/dist/service-installer.js.map +1 -1
  197. package/dist/settings-api.d.ts +23 -0
  198. package/dist/settings-api.js +164 -3
  199. package/dist/settings-api.js.map +1 -1
  200. package/dist/settings-change.js +104 -0
  201. package/dist/settings-change.js.map +1 -1
  202. package/dist/settings-confirmation.d.ts +3 -1
  203. package/dist/settings-confirmation.js +2 -0
  204. package/dist/settings-confirmation.js.map +1 -1
  205. package/dist/settings-effect.js +34 -3
  206. package/dist/settings-effect.js.map +1 -1
  207. package/dist/settings-http-confirmation.d.ts +11 -0
  208. package/dist/settings-http-confirmation.js +10 -0
  209. package/dist/settings-http-confirmation.js.map +1 -1
  210. package/dist/setup-form.d.ts +1 -1
  211. package/dist/setup-form.js +23 -7
  212. package/dist/setup-form.js.map +1 -1
  213. package/dist/spawn-gate.d.ts +1 -0
  214. package/dist/spawn-gate.js +13 -3
  215. package/dist/spawn-gate.js.map +1 -1
  216. package/dist/task-board-tool.d.ts +25 -0
  217. package/dist/task-board-tool.js +154 -0
  218. package/dist/task-board-tool.js.map +1 -0
  219. package/dist/task-board-waker.d.ts +44 -0
  220. package/dist/task-board-waker.js +128 -0
  221. package/dist/task-board-waker.js.map +1 -0
  222. package/dist/tmux-exec-helper.d.ts +94 -0
  223. package/dist/tmux-exec-helper.js +407 -0
  224. package/dist/tmux-exec-helper.js.map +1 -0
  225. package/dist/tmux-manager.d.ts +5 -0
  226. package/dist/tmux-manager.js +42 -11
  227. package/dist/tmux-manager.js.map +1 -1
  228. package/dist/tool-permissions.d.ts +0 -7
  229. package/dist/tool-permissions.js +44 -5
  230. package/dist/tool-permissions.js.map +1 -1
  231. package/dist/topic-commands.d.ts +17 -1
  232. package/dist/topic-commands.js +45 -19
  233. package/dist/topic-commands.js.map +1 -1
  234. package/dist/tunnel/cloudflared.js +37 -2
  235. package/dist/tunnel/cloudflared.js.map +1 -1
  236. package/dist/tunnel/types.d.ts +3 -1
  237. package/dist/tunnel/types.js.map +1 -1
  238. package/dist/types.d.ts +63 -1
  239. package/dist/ui/app-effects.js +90 -32
  240. package/dist/ui/backend-signin.js +297 -0
  241. package/dist/ui/chat-ambient.js +307 -0
  242. package/dist/ui/chat-effect-posters.js +8 -8
  243. package/dist/ui/chat-effects.js +110 -15
  244. package/dist/ui/chat-msg-actions.js +176 -0
  245. package/dist/ui/chat-render.js +21 -3
  246. package/dist/ui/chat-store.js +64 -12
  247. package/dist/ui/chat-strings.js +85 -8
  248. package/dist/ui/chat-thread.js +204 -21
  249. package/dist/ui/effect-trial-controller.js +13 -8
  250. package/dist/ui/effect-trial-panel.js +19 -16
  251. package/dist/ui/effects-waker.js +70 -0
  252. package/dist/ui/fleet-canvas-layout.js +117 -0
  253. package/dist/ui/fleet-canvas.js +555 -0
  254. package/dist/ui/fleet-org.js +27 -12
  255. package/dist/ui/fleet-route-scene.js +207 -0
  256. package/dist/ui/fleet-route-worker.js +21 -0
  257. package/dist/ui/fleet-route.js +88 -0
  258. package/dist/ui/fleet-task-card.js +436 -0
  259. package/dist/ui/fleet-task-common.js +448 -0
  260. package/dist/ui/fleet-tasks.js +535 -0
  261. package/dist/ui/fleet-traffic.js +359 -0
  262. package/dist/ui/fleet-usage.js +435 -0
  263. package/dist/ui/panel-chat.js +264 -66
  264. package/dist/ui/panel-details.js +2 -1
  265. package/dist/ui/panel-fleet.js +65 -83
  266. package/dist/ui/panel-needs.js +4 -2
  267. package/dist/ui/panel-settings.js +56 -11
  268. package/dist/ui/preview.js +3 -2
  269. package/dist/ui/search-dialog.js +349 -0
  270. package/dist/ui/settings-appearance.js +44 -19
  271. package/dist/ui/settings-apply.js +3 -3
  272. package/dist/ui/settings-catalog.js +92 -0
  273. package/dist/ui/settings-confirm.js +7 -2
  274. package/dist/ui/settings-dialogs.js +40 -12
  275. package/dist/ui/settings-effects.js +4 -3
  276. package/dist/ui/settings-guests.js +117 -0
  277. package/dist/ui/settings-learn.js +25 -0
  278. package/dist/ui/settings-model.js +15 -6
  279. package/dist/ui/settings-mods.js +23 -9
  280. package/dist/ui/settings-owner-effects.js +15 -13
  281. package/dist/ui/settings-strings.js +122 -42
  282. package/dist/ui/settings-web-authority.js +91 -0
  283. package/dist/ui/shared/agend-auth.js +1 -1
  284. package/dist/ui/shared/app-i18n.js +11 -2
  285. package/dist/ui/shared/app-prefs.js +17 -4
  286. package/dist/ui/shared/app-route.js +19 -1
  287. package/dist/ui/shared/app-session.js +2 -0
  288. package/dist/ui/shared/app-shell.js +174 -30
  289. package/dist/ui/shared/app-store.js +26 -1
  290. package/dist/ui/shared/app-stream.js +5 -1
  291. package/dist/ui/shared/app-themes.js +140 -7
  292. package/dist/ui/shared/app.css +705 -20
  293. package/dist/ui/shared/app.js +26 -15
  294. package/dist/ui/shared/card-strings.js +8 -0
  295. package/dist/ui/shared/effect-app-runtime.js +68 -19
  296. package/dist/ui/shared/effect-chat-runtime.js +16 -3
  297. package/dist/ui/shared/effect-frame.js +26 -7
  298. package/dist/ui/shared/effect-geometry.js +24 -9
  299. package/dist/ui/shared/effect-owner-source.js +8 -1
  300. package/dist/ui/shared/effect-policy.js +21 -7
  301. package/dist/ui/shared/effect-profiler.js +31 -9
  302. package/dist/ui/shared/effect-registry.js +97 -50
  303. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  304. package/dist/ui/shared/effect-stock-trust.js +62 -10
  305. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  306. package/dist/ui/shared/effect-trial-source.js +1 -1
  307. package/dist/ui/shared/effect-trial-stage.js +54 -31
  308. package/dist/ui/shared/effect-trial.css +40 -0
  309. package/dist/ui/shared/fx-motion.js +67 -0
  310. package/dist/ui/shared/identity-card.js +100 -11
  311. package/dist/ui/shared/identity-color.js +14 -10
  312. package/dist/ui/shared/instance-nav.js +179 -23
  313. package/dist/ui/shared/owner-css.js +30 -1
  314. package/dist/ui/shared/panel-view.js +24 -4
  315. package/dist/ui/shared/tokens.css +9 -3
  316. package/dist/ui/shared/ui-icons.js +2 -0
  317. package/dist/ui/shared/ui-menu.js +20 -7
  318. package/dist/ui/shared/ui-toast.js +16 -3
  319. package/dist/ui/shared/usage-dialog.js +34 -3
  320. package/dist/ui/shared/view-strings.js +72 -0
  321. package/dist/ui/shared/web-authorize.js +198 -0
  322. package/dist/ui/web-terminal/terminal.css +4 -4
  323. package/dist/ui/web-terminal/terminal.html +1 -1
  324. package/dist/update-launch.d.ts +9 -0
  325. package/dist/update-launch.js +38 -12
  326. package/dist/update-launch.js.map +1 -1
  327. package/dist/usage/format-rich.js +6 -3
  328. package/dist/usage/format-rich.js.map +1 -1
  329. package/dist/usage/i18n-keys.d.ts +1 -1
  330. package/dist/usage/i18n-keys.js +2 -1
  331. package/dist/usage/i18n-keys.js.map +1 -1
  332. package/dist/usage/i18n.d.ts +13 -1
  333. package/dist/usage/i18n.js +29 -0
  334. package/dist/usage/i18n.js.map +1 -1
  335. package/dist/usage/providers.d.ts +81 -2
  336. package/dist/usage/providers.js +240 -38
  337. package/dist/usage/providers.js.map +1 -1
  338. package/dist/usage/usage-api.d.ts +5 -1
  339. package/dist/usage/usage-api.js +184 -58
  340. package/dist/usage/usage-api.js.map +1 -1
  341. package/dist/usage-report.d.ts +158 -0
  342. package/dist/usage-report.js +226 -0
  343. package/dist/usage-report.js.map +1 -0
  344. package/dist/usage-rollup.d.ts +51 -0
  345. package/dist/usage-rollup.js +168 -0
  346. package/dist/usage-rollup.js.map +1 -0
  347. package/dist/user-systemd.d.ts +24 -0
  348. package/dist/user-systemd.js +75 -0
  349. package/dist/user-systemd.js.map +1 -0
  350. package/dist/view-api.d.ts +39 -0
  351. package/dist/view-api.js +69 -6
  352. package/dist/view-api.js.map +1 -1
  353. package/dist/web-ambient.d.ts +72 -0
  354. package/dist/web-ambient.js +161 -0
  355. package/dist/web-ambient.js.map +1 -0
  356. package/dist/web-api.d.ts +98 -14
  357. package/dist/web-api.js +1027 -83
  358. package/dist/web-api.js.map +1 -1
  359. package/dist/web-appearance-policy.d.ts +4 -6
  360. package/dist/web-appearance-policy.js +5 -12
  361. package/dist/web-appearance-policy.js.map +1 -1
  362. package/dist/web-appearance-tools.d.ts +3 -0
  363. package/dist/web-appearance-tools.js +22 -3
  364. package/dist/web-appearance-tools.js.map +1 -1
  365. package/dist/web-appearance-view.d.ts +27 -2
  366. package/dist/web-appearance-view.js +129 -14
  367. package/dist/web-appearance-view.js.map +1 -1
  368. package/dist/web-appearance.d.ts +17 -3
  369. package/dist/web-appearance.js +85 -8
  370. package/dist/web-appearance.js.map +1 -1
  371. package/dist/web-asset-parts.d.ts +2 -2
  372. package/dist/web-asset-parts.js +3 -3
  373. package/dist/web-asset-parts.js.map +1 -1
  374. package/dist/web-auth.js +3 -1
  375. package/dist/web-auth.js.map +1 -1
  376. package/dist/web-backend-login.d.ts +130 -0
  377. package/dist/web-backend-login.js +225 -0
  378. package/dist/web-backend-login.js.map +1 -0
  379. package/dist/web-card.d.ts +14 -0
  380. package/dist/web-card.js +40 -6
  381. package/dist/web-card.js.map +1 -1
  382. package/dist/web-channel-echo.d.ts +7 -1
  383. package/dist/web-channel-echo.js +6 -2
  384. package/dist/web-channel-echo.js.map +1 -1
  385. package/dist/web-chat-history.d.ts +83 -0
  386. package/dist/web-chat-history.js +137 -5
  387. package/dist/web-chat-history.js.map +1 -1
  388. package/dist/web-commands.d.ts +6 -1
  389. package/dist/web-commands.js +1 -1
  390. package/dist/web-commands.js.map +1 -1
  391. package/dist/web-effect-bootstrap.d.ts +12 -0
  392. package/dist/web-effect-bootstrap.js +49 -0
  393. package/dist/web-effect-bootstrap.js.map +1 -0
  394. package/dist/web-effect-bundle.js +46 -1
  395. package/dist/web-effect-bundle.js.map +1 -1
  396. package/dist/web-effect-playback.d.ts +30 -0
  397. package/dist/web-effect-playback.js +71 -0
  398. package/dist/web-effect-playback.js.map +1 -0
  399. package/dist/web-effect-profiles.d.ts +4 -4
  400. package/dist/web-effect-service.d.ts +11 -4
  401. package/dist/web-effect-service.js +25 -4
  402. package/dist/web-effect-service.js.map +1 -1
  403. package/dist/web-effect-tools.d.ts +1 -0
  404. package/dist/web-effect-tools.js +4 -4
  405. package/dist/web-effect-tools.js.map +1 -1
  406. package/dist/web-effect-trials.d.ts +2 -2
  407. package/dist/web-effect-trials.js +3 -3
  408. package/dist/web-effect-trials.js.map +1 -1
  409. package/dist/web-effect-upload.js +1 -1
  410. package/dist/web-effect-upload.js.map +1 -1
  411. package/dist/web-effect-worker.d.ts +1 -1
  412. package/dist/web-effect-worker.js +2 -2
  413. package/dist/web-effect-worker.js.map +1 -1
  414. package/dist/web-effect.d.ts +1 -1
  415. package/dist/web-guest-access.d.ts +126 -0
  416. package/dist/web-guest-access.js +238 -0
  417. package/dist/web-guest-access.js.map +1 -0
  418. package/dist/web-guest-rooms.d.ts +82 -0
  419. package/dist/web-guest-rooms.js +135 -0
  420. package/dist/web-guest-rooms.js.map +1 -0
  421. package/dist/web-host-guard.d.ts +11 -0
  422. package/dist/web-host-guard.js +11 -0
  423. package/dist/web-host-guard.js.map +1 -1
  424. package/dist/web-identity-color.d.ts +1 -1
  425. package/dist/web-identity-color.js +2 -2
  426. package/dist/web-identity-color.js.map +1 -1
  427. package/dist/web-identity-palette.d.ts +1 -1
  428. package/dist/web-identity-palette.js +2 -2
  429. package/dist/web-identity-palette.js.map +1 -1
  430. package/dist/web-image-meta.d.ts +13 -0
  431. package/dist/web-image-meta.js +62 -0
  432. package/dist/web-image-meta.js.map +1 -1
  433. package/dist/web-libavoid.d.ts +14 -0
  434. package/dist/web-libavoid.js +46 -0
  435. package/dist/web-libavoid.js.map +1 -0
  436. package/dist/web-login.d.ts +47 -0
  437. package/dist/web-login.js +115 -0
  438. package/dist/web-login.js.map +1 -1
  439. package/dist/web-mod-agent.d.ts +1 -1
  440. package/dist/web-mod-agent.js +7 -5
  441. package/dist/web-mod-agent.js.map +1 -1
  442. package/dist/web-mod-bundle.d.ts +9 -1
  443. package/dist/web-mod-bundle.js +62 -3
  444. package/dist/web-mod-bundle.js.map +1 -1
  445. package/dist/web-mod-export.d.ts +9 -0
  446. package/dist/web-mod-export.js +21 -0
  447. package/dist/web-mod-export.js.map +1 -1
  448. package/dist/web-mod-store.d.ts +7 -1
  449. package/dist/web-mod-store.js +19 -6
  450. package/dist/web-mod-store.js.map +1 -1
  451. package/dist/web-owner-css-store.d.ts +44 -1
  452. package/dist/web-owner-css-store.js +70 -9
  453. package/dist/web-owner-css-store.js.map +1 -1
  454. package/dist/web-prefs.d.ts +14 -0
  455. package/dist/web-prefs.js +37 -4
  456. package/dist/web-prefs.js.map +1 -1
  457. package/dist/web-preview.d.ts +6 -2
  458. package/dist/web-preview.js +4 -2
  459. package/dist/web-preview.js.map +1 -1
  460. package/dist/web-returning-device.d.ts +41 -0
  461. package/dist/web-returning-device.js +57 -5
  462. package/dist/web-returning-device.js.map +1 -1
  463. package/dist/web-session-authorization.d.ts +132 -0
  464. package/dist/web-session-authorization.js +193 -0
  465. package/dist/web-session-authorization.js.map +1 -0
  466. package/dist/web-session.d.ts +70 -10
  467. package/dist/web-session.js +137 -22
  468. package/dist/web-session.js.map +1 -1
  469. package/dist/web-settings-patch.d.ts +49 -2
  470. package/dist/web-settings-patch.js +145 -1
  471. package/dist/web-settings-patch.js.map +1 -1
  472. package/dist/web-settings-tools.d.ts +94 -0
  473. package/dist/web-settings-tools.js +301 -0
  474. package/dist/web-settings-tools.js.map +1 -0
  475. package/dist/web-shell-routes.d.ts +1 -1
  476. package/dist/web-shell-routes.js +1 -1
  477. package/dist/web-shell-routes.js.map +1 -1
  478. package/dist/web-terminal-http.js +15 -2
  479. package/dist/web-terminal-http.js.map +1 -1
  480. package/dist/web-theme-store.d.ts +39 -0
  481. package/dist/web-theme-store.js +122 -11
  482. package/dist/web-theme-store.js.map +1 -1
  483. package/dist/web-theme.d.ts +31 -0
  484. package/dist/web-theme.js +99 -2
  485. package/dist/web-theme.js.map +1 -1
  486. package/dist/web-upload.d.ts +13 -0
  487. package/dist/web-upload.js +39 -3
  488. package/dist/web-upload.js.map +1 -1
  489. package/dist/windows-shortcuts.d.ts +78 -0
  490. package/dist/windows-shortcuts.js +203 -0
  491. package/dist/windows-shortcuts.js.map +1 -0
  492. package/dist/workflow-templates/default.md +4 -1
  493. package/dist/yaml-shared-nodes.d.ts +25 -0
  494. package/dist/yaml-shared-nodes.js +185 -0
  495. package/dist/yaml-shared-nodes.js.map +1 -0
  496. package/launcher/agend-install.cjs +553 -0
  497. package/launcher/install-admission.cjs +4 -39
  498. package/launcher/postinstall.cjs +14 -12
  499. package/package.json +4 -2
  500. package/scripts/preinstall-guard.cjs +2 -5
  501. package/templates/windows-shortcuts.ps1 +181 -0
  502. package/launcher/old-updater-note.cjs +0 -48
@@ -60,7 +60,7 @@ export function t(key, ...values) {
60
60
 
61
61
  register("app", {
62
62
  en: {
63
- brand: "AgEnD", fleet: "Fleet", fleetOverview: "Fleet overview", view: "View", settings: "Settings", instances: "Instances", newInstance: "New instance",
63
+ brand: "AgEnD", fleet: "Fleet", fleetOverview: "Fleet", view: "View", settings: "Settings", instances: "Instances", newInstance: "New instance", newAgent: "New agent",
64
64
  collapse: "Hide the sidebar", expand: "Show the sidebar", openMenu: "Open the sidebar", closeMenu: "Close the sidebar",
65
65
  resizeSidebar: "Sidebar width", resizeSidebarHint: "Drag to resize · double-click to reset",
66
66
  names: "Names", namesNickname: "Nickname first", namesInstance: "Real name first",
@@ -74,11 +74,13 @@ register("app", {
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…",
76
76
  noInstances: "No instances yet", noInstancesHint: "Create one to start chatting.",
77
+ guestChip: "Guest", guestAs: "Guest · {0}", guestAsOn: "Guest · {0} ({1})", guestHint: "You see only the agents you can already use in chat.",
77
78
  session: "Session", signIn: "Sign in", signedIn: "Signed in", signedOut: "Not signed in", thisDevice: "This device", devicePrefs: "Theme and language",
78
79
  lastActive: "active {0}", justNow: "just now", minAgo: "{0} min ago", hrAgo: "{0} h ago", signOut: "Sign out",
79
80
  signOutAll: "Sign out everywhere", devices: "Signed-in devices", expires: "Ends by {0} at the latest, or after {1} unused",
80
81
  failed: "That did not work — try again.", close: "Close", more: "More actions", menu: "Menu",
81
82
  pendingTitle: "Changes waiting for confirmation", pending_pending: "Waiting for confirmation", pending_applying: "Confirmed — applying…",
83
+ pendingWeb: "Confirmed by your account (this browser is web-verified). General is told.",
82
84
  pending_applied: "Confirmed and applied", pending_rejected: "Not applied", pending_expired: "Expired — nothing was changed",
83
85
  pending_stale: "Not applied — settings changed meanwhile", pending_failed: "Confirmed, but applying it failed",
84
86
  pendingChat: "A fleet admin confirms this in chat (General topic).", pendingHost: "Confirm it on the host: {0}", pendingLeft: "{0} left", withdraw: "Withdraw",
@@ -86,13 +88,16 @@ register("app", {
86
88
  opDone: "Settings applied", opRestart: "Settings saved — AgEnD needs a restart to use them", opPartial: "Some settings were not applied",
87
89
  opFailed: "Settings were not applied", openSettings: "Open Settings",
88
90
  pendingSent: "Sent for confirmation — a fleet admin confirms it in chat.",
91
+ pendingSelfSent: "Confirmed by your account (this browser is web-verified) — applying.",
89
92
  needsNav: "Needs you", needsTab: "Needs you", needsCount: "{0} waiting",
93
+ // #2293: global search — chats, cross-instance messages and tasks (not the sidebar's filter of agents).
94
+ search: "Search", searchTitle: "Search chats, messages between agents and tasks ({0})",
90
95
  confirmTitle: "Please confirm", confirmOk: "Continue", cancel: "Cancel",
91
96
  // The server's own wording (src/locale.ts needs.reason.*), so the web says what Discord says.
92
97
  needs_reason_hang: "Not responding", needs_reason_exited: "Exited", needs_reason_assist: "Waiting at its terminal", needs_reason_permission: "Permission needed", needs_reason_dangerous_command: "Dangerous command waiting", needs_reason_login: "Sign-in needed", needs_reason_dialog: "Waiting at a dialog", needs_reason_terminal_input: "May be waiting for input", needs_reason_auth_paused: "Paused: sign-in needed", needs_reason_crashed: "Crashed", needs_reason_delivery_uncertain: "Could not confirm delivery", needs_reason_delivery_failed: "Delivery failed",
93
98
  },
94
99
  "zh-TW": {
95
- brand: "AgEnD", fleet: "Fleet", fleetOverview: "Fleet 概覽", view: "檢視", settings: "設定", instances: "Instances", newInstance: "新增 instance",
100
+ brand: "AgEnD", fleet: "Fleet", fleetOverview: "Fleet 概覽", view: "檢視", settings: "設定", instances: "Instances", newInstance: "新增 instance", newAgent: "新增 Agent",
96
101
  collapse: "隱藏側邊欄", expand: "顯示側邊欄", openMenu: "打開側邊欄", closeMenu: "關閉側邊欄",
97
102
  resizeSidebar: "側邊欄寬度", resizeSidebarHint: "拖曳調整寬度 · 按兩下恢復預設",
98
103
  names: "名稱", namesNickname: "暱稱優先", namesInstance: "實際名稱優先",
@@ -106,11 +111,13 @@ register("app", {
106
111
  hydrating: "正在載入目前的狀態…", hydrateFailed: "無法載入目前的狀態(提示、送達標記)。",
107
112
  skip: "跳到對話", loadFailed: "無法載入。", retry: "再試一次", loading: "載入中…",
108
113
  noInstances: "還沒有 instance", noInstancesHint: "建立一個就能開始聊天。",
114
+ guestChip: "訪客", guestAs: "訪客 · {0}", guestAsOn: "訪客 · {0}({1})", guestHint: "你只會看到在聊天中本來就能用的 agent。",
109
115
  session: "登入狀態", signIn: "登入", signedIn: "已登入", signedOut: "尚未登入", thisDevice: "這台裝置", devicePrefs: "主題與語言",
110
116
  lastActive: "{0}活動", justNow: "剛剛", minAgo: "{0} 分鐘前", hrAgo: "{0} 小時前", signOut: "登出",
111
117
  signOutAll: "全部登出", devices: "已登入的裝置", expires: "最晚 {0} 結束;閒置 {1} 也會結束",
112
118
  failed: "沒有成功,請再試一次。", close: "關閉", more: "更多動作", menu: "選單",
113
119
  pendingTitle: "等待確認的變更", pending_pending: "等待確認", pending_applying: "已確認——套用中…",
120
+ pendingWeb: "由你的帳號確認(這個瀏覽器已通過網頁驗證),也會通知 General。",
114
121
  pending_applied: "已確認並套用", pending_rejected: "未套用", pending_expired: "已逾時——沒有任何變更",
115
122
  pending_stale: "未套用——設定在這期間已變動", pending_failed: "已確認,但套用失敗",
116
123
  pendingChat: "由 fleet 管理員在聊天(General topic)中確認。", pendingHost: "請在主機上確認:{0}", pendingLeft: "剩 {0}", withdraw: "撤回",
@@ -118,7 +125,9 @@ register("app", {
118
125
  opDone: "設定已套用", opRestart: "設定已儲存——需要重新啟動 AgEnD 才會生效", opPartial: "部分設定沒有套用",
119
126
  opFailed: "設定沒有套用", openSettings: "開啟設定",
120
127
  pendingSent: "已送出確認——由 fleet 管理員在聊天中確認。",
128
+ pendingSelfSent: "已由你的帳號確認(這個瀏覽器已通過網頁驗證)——套用中。",
121
129
  needsNav: "等你處理", needsTab: "待處理", needsCount: "{0} 件待處理",
130
+ search: "搜尋", searchTitle: "搜尋聊天、agent 之間的訊息與任務({0})",
122
131
  confirmTitle: "請確認", confirmOk: "繼續", cancel: "取消",
123
132
  needs_reason_hang: "沒有回應", needs_reason_exited: "已結束", needs_reason_assist: "在終端機上等待", needs_reason_permission: "需要權限", needs_reason_dangerous_command: "危險指令待確認", needs_reason_login: "需要登入", needs_reason_dialog: "停在對話框", needs_reason_terminal_input: "可能在等輸入", needs_reason_auth_paused: "已暫停:需要登入", needs_reason_crashed: "已崩潰", needs_reason_delivery_uncertain: "無法確認是否送達", needs_reason_delivery_failed: "傳送失敗",
124
133
  },
@@ -18,7 +18,7 @@
18
18
  // - The status frame's prefs_rev: another device changed something; the host's values are read again and applied. A
19
19
  // revision counts as seen only once its read succeeded (a failed one is tried again, at most every few seconds).
20
20
  import { deviceLanguage, lang, setLang } from "./app-i18n.js";
21
- import { appStore } from "./app-store.js";
21
+ import { appStore, guestPage } from "./app-store.js";
22
22
 
23
23
  const SYNCED_FLAG = "agend_prefs_synced";
24
24
  const RETRY_MS = 5000;
@@ -31,6 +31,7 @@ export const SYNCED = {
31
31
  "nav.collapsed": { ls: "agend_instance_collapsed", ...json },
32
32
  "nav.filter": { ls: "agend_instance_filter", ...json },
33
33
  "nav.favorites": { ls: "agend_favorites", ...json }, // #1664
34
+ "nav.sort": { ls: "agend_nav_sort", ...json }, // #2179
34
35
  "preview.optIn": { ls: "agend_html_preview", toLocal: (v) => (v ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
35
36
  "names.primary": { ls: "agend_names_primary", toLocal: (v) => (v === "instance" ? v : null), fromLocal: (s) => (s === "instance" ? s : undefined) }, // #1722
36
37
  // #1629 r2 (d): "Show agents' themes and effects" — on unless turned off (only "off" is cached).
@@ -40,8 +41,9 @@ export const SYNCED = {
40
41
  "card.sizeByInstance": { ls: "agend_card_sizes", ...json },
41
42
  // #1629 r4-4: the person's own sidebar widget ({ spec, data }), drawn by the page's own components.
42
43
  "widget.sidebar": { ls: "agend_widget_sidebar", ...json },
43
- // #1958: where identity colours show by default (ring + bar unless chosen), and the host's switch to turn them off.
44
+ // #1958: where identity colours show by default (#2056: name, bar and ring unless chosen), and the host's switch to turn them off.
44
45
  "color.show": { ls: "agend_color_show", ...json },
46
+ "theme.pageByInstance": { ls: "agend_theme_page", ...json }, // #2196
45
47
  "color.off": { ls: "agend_color_off", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
46
48
  };
47
49
  // #1629: fixed personal scope. A separate session-bound owner-effect store synchronizes these, never this host cache.
@@ -77,6 +79,15 @@ const inflight = new Map(); // key → writes sent and not answered yet
77
79
  let flushTimer = null;
78
80
  let writes = Promise.resolve(); // one write at a time, in the order they were made
79
81
  const listeners = new Set();
82
+ // #2193: whether the signed-in person has a personal effect slot chosen (their fx.* keys: in the page's data-prefs and
83
+ // in every host read, never in this host cache). The effects runtime is loaded only when there is something to play.
84
+ const ownerEffectsIn = (values) => !!values && OWNER_EFFECT_PREF_KEYS.some((k) => own(values, k) && values[k] != null && values[k] !== "");
85
+ let ownerFx = initial ? ownerEffectsIn(initial.values) : false;
86
+ const ownerFxListeners = new Set();
87
+ /** Whether the person has any personal effect (fx.page/header/sidebar/box/cursor) chosen, as this page last read. */
88
+ export function ownerEffectsChosen() { return ownerFx; }
89
+ /** Called when that changes. Returns the unsubscribe. */
90
+ export function onOwnerEffectsChanged(fn) { ownerFxListeners.add(fn); return () => ownerFxListeners.delete(fn); }
80
91
 
81
92
  // The host's values into the cache, now — before the sidebar, the tour or the first-run card read their keys. A key
82
93
  // the host does not have: the default, on a device in step with the host; this device's own (to upload) otherwise.
@@ -88,9 +99,9 @@ if (host) {
88
99
  }
89
100
  }
90
101
 
91
- /** This page may change the host's preferences: the signed-in app, not over the public link. */
102
+ /** This page may change the host's preferences: the signed-in app, not over the public link, not a guest's (#1920). */
92
103
  export function prefsWritable() {
93
- return !!initial && !(typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1");
104
+ return !!initial && !guestPage() && !(typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1");
94
105
  }
95
106
  /**
96
107
  * The value in force for a key, in memory — never only in this browser's storage (a full storage must not bring back
@@ -117,6 +128,8 @@ function take(body) {
117
128
  if (!isView(body) || (rev !== null && body.rev < rev)) return false;
118
129
  rev = body.rev;
119
130
  host = hostValues(body.values);
131
+ const fx = ownerEffectsIn(body.values);
132
+ if (fx !== ownerFx) { ownerFx = fx; for (const fn of [...ownerFxListeners]) { try { fn(fx); } catch { /* the rest still hear */ } } }
120
133
  return true;
121
134
  }
122
135
 
@@ -1,7 +1,7 @@
1
1
  // #1408 §3: the app's routes on the client. The same table as the server's classifier (src/web-shell-routes.ts;
2
2
  // a test runs both over one list of cases). Pure: no DOM, no history.
3
3
 
4
- export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "cache", "config"];
4
+ export const FLEET_TABS = ["tasks", "schedules", "teams", "org", "cache", "usage", "config"];
5
5
  export const SETTINGS_SECTIONS = ["device", "appearance", "agents", "bots", "classic", "general", "web", "advanced"]; // #1604: device first
6
6
  const MAX_INSTANCE_NAME = 128;
7
7
  const PATH_SEPARATOR_OR_CONTROL = /[/\\\u0000-\u001f\u007f]/;
@@ -50,6 +50,24 @@ export function viewPath(instance) { return instance ? `/view/${encodeURICompone
50
50
  export function detailsPath(instance) { return `/ui/fleet/agent/${encodeURIComponent(instance)}`; }
51
51
  export function logPath(instance) { return `/ui/log/${encodeURIComponent(instance)}`; }
52
52
  export function fleetPath(tab) { return tab && tab !== "tasks" ? `/ui/fleet/${tab}` : "/ui/fleet"; }
53
+ /** #2183: this device's last Fleet tab (Fleet remembers it; the Org chart the first time). */
54
+ export const LAST_FLEET_TAB_KEY = "agend_last_fleet_tab";
55
+ export function lastFleetTab() {
56
+ let tab = null;
57
+ try { tab = localStorage.getItem(LAST_FLEET_TAB_KEY); } catch { /* storage blocked: the default */ }
58
+ return FLEET_TABS.includes(tab) ? tab : "org";
59
+ }
60
+ /**
61
+ * #2183: where Fleet (the sidebar's entry, the phone's tab) goes — on a Fleet tab, that tab (the route, not storage: the
62
+ * panel stores it in an effect after this render, and a storage write re-renders nothing); anywhere else, the last one.
63
+ */
64
+ export function fleetEntryPath(route) {
65
+ return fleetPath(route && route.panel === "fleet" ? route.tab : lastFleetTab()); // parseRoute names only FLEET_TABS
66
+ }
67
+ export function rememberFleetTab(tab) {
68
+ if (!FLEET_TABS.includes(tab)) return;
69
+ try { localStorage.setItem(LAST_FLEET_TAB_KEY, tab); } catch { /* this page only */ }
70
+ }
53
71
  export const NEEDS_PATH = "/ui/needs";
54
72
  export function settingsPath(section) { return section && section !== "agents" ? `/settings/${section}` : "/settings"; }
55
73
  export function routePath(route) {
@@ -4,6 +4,7 @@
4
4
  import { html, useEffect, useRef, useState } from "./app-html.js";
5
5
  import { Icon } from "./ui-icons.js";
6
6
  import { t, lang } from "./app-i18n.js";
7
+ import { AuthorizeChip } from "./web-authorize.js";
7
8
 
8
9
  async function getJson(url) {
9
10
  try {
@@ -53,6 +54,7 @@ export function SessionMenu() {
53
54
  ${!me ? html`<h3>${t("app.signedOut")}</h3><a class="btn" href=${`/signin?next=${encodeURIComponent(location.pathname)}`}>${t("app.signIn")}</a>` : html`
54
55
  <h3>${t("app.signedIn")} · ${me.label}</h3>
55
56
  <p class="sub">${t("app.expires", clock(me.expiresAt), span(me.idleMs))}</p>
57
+ <${AuthorizeChip} />
56
58
  ${rows.length > 1 ? html`<h3>${t("app.devices")}</h3><ul class="devices">${rows.map(s => html`<li key=${s.handle}>
57
59
  <span class="grow"><span class="name">${s.label}${s.current ? ` · ${t("app.thisDevice")}` : ""}</span><span class="when">${t("app.lastActive", ago(s.lastSeen))}</span></span>
58
60
  ${s.current ? null : html`<button type="button" class="btn btn-sm danger" onClick=${() => act("DELETE", `/auth/sessions/${encodeURIComponent(s.handle)}`, refresh)}>${t("app.signOut")}</button>`}</li>`)}</ul>` : null}
@@ -7,11 +7,13 @@ import { Toasts } from "./ui-toast.js";
7
7
  import { ConfirmHost } from "./ui-confirm.js";
8
8
  import { ColorDialogHost } from "./identity-color.js";
9
9
  import { t, lang, setLang, onLang } from "./app-i18n.js";
10
- import { appStore, createStore, useStore } from "./app-store.js";
10
+ import { appStore, createStore, guestBadge, guestPage, useStore } from "./app-store.js";
11
11
  import { navStore } from "./app-nav.js";
12
- import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH, detailsPath, fleetPath } from "./app-route.js";
12
+ import { toast } from "./ui-toast.js";
13
+ import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH, fleetEntryPath } from "./app-route.js";
13
14
  import { SessionMenu } from "./app-session.js";
14
- import { savePref } from "./app-prefs.js";
15
+ import { onPrefsChanged, savePref } from "./app-prefs.js";
16
+ import { applyPageTheme, pageNow, resolvedMode, themesStore } from "./app-themes.js";
15
17
  import { ErrorState, Skeleton } from "./ui-states.js";
16
18
  import { InstanceNav, RosterNav, namesStore, setNamesPrimary } from "./instance-nav.js";
17
19
  import { UsageRail, usageRailStore } from "./header-tools.js";
@@ -19,7 +21,7 @@ import { OwnerCssKeepBar } from "./owner-css.js";
19
21
  import { AgentWidget, fleetBinds, sidebarWidget, sidebarWidgetStore } from "./agent-widget.js";
20
22
 
21
23
  /** Shell state shared with panels: the phone drawer, the collapsed sidebar, extensions a panel module adds. */
22
- export const shellStore = createStore({ drawer: false, collapsed: readCollapsed(), keyboard: false, dialog: null, side: null });
24
+ export const shellStore = createStore({ drawer: false, collapsed: readCollapsed(), keyboard: false, dialog: null, side: null, banner: null });
23
25
 
24
26
  /**
25
27
  * The mounted panel's own sidebar section, in place of the instance list (View's roster: its groups, filter and order,
@@ -39,10 +41,66 @@ let newInstanceOpener = null;
39
41
  export function setNewInstanceOpener(fn) { newInstanceOpener = fn; }
40
42
  export function requestNewInstance() { if (newInstanceOpener) newInstanceOpener(); }
41
43
 
44
+ /**
45
+ * #2074: whether this page's session may make an agent — an admin's (/auth/session's tier); a guest's create is refused.
46
+ * Asked once per page; a session that ends takes the buttons away. A display hint only: the create route decides.
47
+ */
48
+ export const canCreateStore = createStore({ value: false });
49
+ let canCreateOf = null; // the AgendAuth asked (one per page)
50
+ export function useCanCreate() {
51
+ const s = useStore(canCreateStore);
52
+ useEffect(() => {
53
+ const auth = typeof window !== "undefined" ? window.AgendAuth : undefined;
54
+ if (!auth || typeof auth.canWrite !== "function" || canCreateOf === auth) return;
55
+ canCreateOf = auth;
56
+ canCreateStore.set({ value: false });
57
+ Promise.resolve(auth.canWrite()).then((v) => { if (canCreateOf === auth && !auth.sessionEnded?.()) canCreateStore.set({ value: v === true }); }, () => {});
58
+ auth.onSessionEnd?.(() => { if (canCreateOf === auth) canCreateStore.set({ value: false }); });
59
+ }, []);
60
+ return s.value;
61
+ }
62
+
42
63
  /** An app-level dialog (New instance): rendered by the shell until closed, whatever panel is showing. */
43
64
  export function showDialog(Component, props = {}) { shellStore.set({ dialog: { Component, props } }); }
44
65
  export function closeDialog() { shellStore.set({ dialog: null }); }
45
66
 
67
+ // ── #2293: global search — an admin's. The dialog lives behind the session gate (/ui/js/search-dialog.js) and loads the
68
+ // first time it opens; the sidebar row, the header's button and the shortcuts only open it. ──
69
+ export const isMacLike = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || "");
70
+ /** What the row and tooltips show: ⌘K on a Mac, Ctrl K elsewhere. */
71
+ export const searchShortcut = () => (isMacLike() ? "⌘K" : "Ctrl K");
72
+ /**
73
+ * Whether this page offers search: an admin's page reached directly. A guest's never, and never over the public link —
74
+ * the server refuses it there (#2298 review), so no entry, no shortcut, and Ctrl+F stays the browser's.
75
+ */
76
+ export function searchOffered(admin) {
77
+ const onLink = typeof document !== "undefined" && !!document.body && document.body.dataset.publicLink === "1";
78
+ return !!admin && !guestPage() && !onLink;
79
+ }
80
+ let searchModule = null;
81
+ /** Open search; `preset.instance`: narrowed to that agent (Ctrl+F in its chat). */
82
+ export function openSearch(preset = {}) {
83
+ closeDrawer();
84
+ (searchModule ??= import("/ui/js/search-dialog.js")).then(
85
+ (m) => showDialog(m.SearchDialog, { preset }),
86
+ () => { searchModule = null; toast(t("app.loadFailed"), false); });
87
+ }
88
+ /**
89
+ * Ctrl+K (⌘K) anywhere opens search; Ctrl+F (⌘F) in an agent's chat opens it narrowed to that agent — elsewhere the
90
+ * browser's own find stays. Not while another dialog is open (search, open already, has its input focused).
91
+ */
92
+ export function handleSearchKey(e) {
93
+ if (!searchOffered(true)) return;
94
+ if (e.isComposing || e.altKey || e.shiftKey || !(isMacLike() ? e.metaKey : e.ctrlKey)) return;
95
+ const key = (e.key || "").toLowerCase();
96
+ if (key !== "k" && key !== "f") return;
97
+ if (typeof document !== "undefined" && document.querySelector("dialog[open]")) return;
98
+ const route = navStore.get().route;
99
+ if (key === "f" && !(route && route.panel === "chat" && route.instance)) return;
100
+ e.preventDefault();
101
+ openSearch(key === "f" ? { instance: route.instance } : {});
102
+ }
103
+
46
104
  function readCollapsed() { try { return localStorage.getItem("agend_sidebar") === "hidden"; } catch { return false; } }
47
105
  const narrow = () => typeof matchMedia === "function" && matchMedia("(max-width: 899px)").matches;
48
106
 
@@ -247,9 +305,8 @@ export function signInHref() {
247
305
  function Sidebar({ route, onNewInstance, viewOnly }) {
248
306
  const app = useStore(appStore);
249
307
  const shell = useStore(shellStore);
250
- // #1675: the Fleet panel is "Fleet overview"'s (one current row); the "Fleet" row is current on an instance's Details.
251
- const on = (panel) => !!route && (panel === "fleet" ? route.panel === "details" : route.panel === panel);
252
- const openInstance = openInstanceOf(route);
308
+ const canCreate = useCanCreate();
309
+ const on = (panel) => !!route && route.panel === panel;
253
310
  // A count badge is hidden at 0; its number is also in the link's accessible name.
254
311
  const navLink = (panel, href, icon, label, count = 0) => html`<a class=${`side-row${on(panel) ? " active" : ""}`} href=${href}
255
312
  aria-current=${on(panel) ? "page" : undefined} aria-label=${count ? `${label} (${t("app.needsCount", count)})` : undefined} onClick=${closeDrawer}>
@@ -271,22 +328,39 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
271
328
  <${SidebarResize} />
272
329
  </aside>`;
273
330
  }
274
- // #1675: the whole fleet, on its Org chart (#1389), from anywhere — top left, so at the top of a phone's drawer too. The
275
- // "Fleet" row under it goes to the open instance's Details (#1523 N2); this one is the Fleet panel, any of its tabs.
331
+ // #1920 phase 3: a guest's sidebar — its own agents to pick from, who it is signed in as, and its session; no fleet,
332
+ // Needs you, View, Settings, new agent or widget (each is an admin's, and the server refuses a guest them anyway).
333
+ if (guestPage()) {
334
+ return html`<aside id="sidebar" class="sidebar" aria-label=${t("app.menu")}>
335
+ <div class="side-head" data-trust="navigation">
336
+ <a class="brand" href="/ui" onClick=${closeDrawer}>${t("app.brand")}</a>
337
+ <button type="button" class="icon-btn side-collapse" onClick=${toggleSidebar} aria-label=${narrow() ? t("app.closeMenu") : t("app.collapse")} title=${narrow() ? t("app.closeMenu") : t("app.collapse")} aria-controls="sidebar"><${Icon} name="sidebar" /></button>
338
+ </div>
339
+ <${InstanceNav} route=${route} items=${app.instances} loaded=${app.ready !== false} exec=${app.exec} awaiting=${app.awaiting} onPick=${closeDrawer} />
340
+ <div class="side-foot" data-trust="navigation">
341
+ <${GuestChip} />
342
+ <${SessionMenu} />
343
+ </div>
344
+ <${SidebarResize} />
345
+ </aside>`;
346
+ }
347
+ // #1675: the whole fleet, from anywhere — top left, so at the top of a phone's drawer too. #2183 (the user's call): the
348
+ // one Fleet entry ("Fleet" / "Fleet 概覽"), on the tab this device used last (the Org chart the first time); an agent's
349
+ // View, Log and Details are its header's tabs, so the sidebar has no "Fleet" (Details) or "View" row of its own.
276
350
  const onFleet = !!route && route.panel === "fleet";
277
- const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${fleetPath("org")}
351
+ const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${fleetEntryPath(route)}
278
352
  aria-current=${onFleet ? "page" : undefined} onClick=${closeDrawer}><${Icon} name="org" /><span class="grow">${t("app.fleetOverview")}</span></a>`;
279
353
  return html`<aside id="sidebar" class="sidebar" aria-label=${t("app.menu")}>
280
354
  <div class="side-head" data-trust="navigation">
281
355
  <a class="brand" href="/ui" onClick=${closeDrawer}>${t("app.brand")}</a>
282
356
  <button type="button" class="icon-btn side-collapse" onClick=${toggleSidebar} aria-label=${narrow() ? t("app.closeMenu") : t("app.collapse")} title=${narrow() ? t("app.closeMenu") : t("app.collapse")} aria-controls="sidebar"><${Icon} name="sidebar" /></button>
283
- <button type="button" class="icon-btn" onClick=${onNewInstance} aria-label=${t("app.newInstance")} title=${t("app.newInstance")}><${Icon} name="plus" /></button>
284
357
  </div>
285
358
  <nav data-trust="navigation" class="side-nav" aria-label=${t("app.menu")}>
286
359
  ${overview}
287
360
  ${navLink("needs", NEEDS_PATH, "inbox", t("app.needsNav"), needsCount)}
288
- ${navLink("fleet", openInstance ? detailsPath(openInstance) : "/ui/fleet", "fleet", t("app.fleet"))}
289
- ${navLink("view", "/view", "view", t("app.view"))}
361
+ ${searchOffered(canCreate) ? html`<button type="button" class="side-row side-search" onClick=${() => openSearch()} title=${t("app.searchTitle", searchShortcut())}
362
+ aria-keyshortcuts=${isMacLike() ? "Meta+K" : "Control+K"}><${Icon} name="search" /><span class="grow">${t("app.search")}</span><kbd class="kbd-hint" aria-hidden="true">${searchShortcut()}</kbd></button>` : null}
363
+ ${canCreate && onNewInstance ? html`<button type="button" class="side-row side-new" onClick=${onNewInstance}><${Icon} name="plus" /><span class="grow">${t("app.newAgent")}</span></button>` : null}
290
364
  </nav>
291
365
  ${shell.side ? html`<${shell.side.Component} />` : html`<${InstanceNav} route=${route} items=${app.instances} loaded=${app.ready !== false}
292
366
  exec=${app.exec} awaiting=${app.awaiting} onPick=${closeDrawer} />`}
@@ -299,6 +373,17 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
299
373
  </aside>`;
300
374
  }
301
375
 
376
+ /** #1920 phase 3: "Guest · <name> (<platform>)" — who this guest's page is signed in as; what a guest sees, in its title. */
377
+ const PLATFORM_NAMES = { discord: "Discord", telegram: "Telegram" };
378
+ function GuestChip() {
379
+ const { name, platform } = guestBadge();
380
+ const where = PLATFORM_NAMES[platform] || platform;
381
+ const label = name ? (where ? t("app.guestAsOn", name, where) : t("app.guestAs", name)) : t("app.guestChip");
382
+ // Inside .side-foot (a marked, stock-trusted element): no trust marker of its own — effect-stock-trust's closed inventory
383
+ // refuses any element it does not know, which would hide a guest's static effect posters (#2101 review).
384
+ return html`<p class="guest-chip" title=${t("app.guestHint")}><${Icon} name="user" size=${14} /><span class="grow">${label}</span></p>`;
385
+ }
386
+
302
387
  /** #1629 r4-4: the person's own sidebar widget (their synced preference), with the fleet's counts. */
303
388
  function SidebarWidget({ app }) {
304
389
  useStore(sidebarWidgetStore);
@@ -347,20 +432,25 @@ function PrefsPopover() {
347
432
  * The header every panel shows: ☰ (phone, or a collapsed sidebar), its title and status, then its actions. `nav`
348
433
  * (#1523 N2): an instance page's view switch — beside the title on a wide screen, a row of tabs under it on a phone.
349
434
  */
350
- export function PanelHeader({ title, sub, children, headingRef, nav }) {
435
+ export function PanelHeader({ title, sub, children, headingRef, nav, notice }) {
351
436
  const shell = useStore(shellStore);
437
+ const admin = useCanCreate();
438
+ const canCreate = admin && !!newInstanceOpener;
352
439
  return html`<header data-trust="header" class=${`panel-head${shell.collapsed ? " collapsed" : ""}`}>
353
440
  <button type="button" id="sbOpen" class="icon-btn sb-open" onClick=${() => (narrow() ? openDrawer() : toggleSidebar())}
354
441
  aria-label=${narrow() ? t("app.openMenu") : t("app.expand")} title=${narrow() ? t("app.openMenu") : t("app.expand")} aria-controls="sidebar" aria-expanded=${shell.drawer ? "true" : "false"}><${Icon} name="menu" /></button>
442
+ ${canCreate && shell.collapsed ? html`<button type="button" class="icon-btn sb-new" onClick=${requestNewInstance} aria-label=${t("app.newAgent")} title=${t("app.newAgent")}><${Icon} name="plus" /></button>` : null}
443
+ ${searchOffered(admin) ? html`<button type="button" class="icon-btn sb-search" onClick=${() => openSearch()} aria-label=${t("app.search")} title=${t("app.searchTitle", searchShortcut())}><${Icon} name="search" /></button>` : null}
355
444
  <div class="panel-title"><h1 ref=${headingRef} tabindex="-1">${title}</h1>${sub ? html`<div class="panel-sub">${sub}</div>` : null}</div>
356
445
  ${nav || null}
357
446
  <div class="panel-actions">${children}</div>
447
+ ${notice || null}
358
448
  </header>`;
359
449
  }
360
450
 
361
451
  /**
362
- * #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null. With one open, Fleet goes to its
363
- * Details (the user's complaint: Fleet always landed on Tasks), and Chat and View keep it; with none, as before.
452
+ * #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null. #2183: with one open, the
453
+ * phone's Chat tab is current and goes to its chat.
364
454
  */
365
455
  export function openInstanceOf(route) {
366
456
  return route && (route.panel === "chat" || route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? route.instance : null;
@@ -371,24 +461,28 @@ function BottomTabs({ route, viewOnly }) {
371
461
  let last = null;
372
462
  try { last = localStorage.getItem("agend_last_instance"); } catch { /* none */ }
373
463
  const open = openInstanceOf(route);
374
- const chatHref = open && route.panel !== "view" ? chatPath(open) : route && route.panel === "chat" && route.instance ? chatPath(route.instance) : last ? chatPath(last) : "/ui";
464
+ // #2183: Chat stands for the open agent's pages (its View, Log and Details are the tabs under its header), so it goes to that agent's chat.
465
+ const chatHref = open ? chatPath(open) : last ? chatPath(last) : "/ui";
375
466
  const anyAwaiting = Object.keys(app.awaiting).length;
376
467
  const tab = (href, icon, label, active, badge, badgeLabel) => html`<a class=${`tab${active ? " active" : ""}`} href=${href} aria-current=${active ? "page" : undefined}>
377
468
  <${Icon} name=${icon} size=${20} /><span>${label}</span>${badge ? html`<span class="tab-badge" aria-label=${badgeLabel || t("app.needsYou")}>${badge}</span>` : null}</a>`;
378
- let lastView = null;
379
- try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
380
- const viewHref = route && (route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? viewPath(route.instance) : viewPath(lastView);
381
469
  if (viewOnly) {
470
+ let lastView = null;
471
+ try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
472
+ const viewHref = route && route.panel === "view" && route.instance ? viewPath(route.instance) : viewPath(lastView);
382
473
  return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
383
474
  ${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
384
475
  ${tab(signInHref(), "user", t("app.signIn"), false)}
385
476
  </nav>`;
386
477
  }
478
+ // #1920: a guest has only its chats (the drawer lists them): no tabs to switch between.
479
+ if (guestPage()) return null;
480
+ // #2183 (the user's call): four tabs — Chat (current on /ui and on any of an agent's pages), Needs you, Fleet (on its
481
+ // last tab), Settings.
387
482
  return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
388
- ${tab(chatHref, "chat", t("app.chat"), route && route.panel === "chat", anyAwaiting)}
483
+ ${tab(chatHref, "chat", t("app.chat"), !!open || (!!route && route.panel === "chat"), anyAwaiting)}
389
484
  ${tab(NEEDS_PATH, "inbox", t("app.needsTab"), route && route.panel === "needs", (app.needs || []).length, t("app.needsCount", (app.needs || []).length))}
390
- ${tab(open ? detailsPath(open) : "/ui/fleet", "fleet", t("app.fleet"), route && (route.panel === "fleet" || route.panel === "details"))}
391
- ${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
485
+ ${tab(fleetEntryPath(route), "fleet", t("app.fleet"), route && route.panel === "fleet")}
392
486
  ${tab(settingsPath(), "settings", t("app.settings"), route && route.panel === "settings")}
393
487
  </nav>`;
394
488
  }
@@ -435,8 +529,21 @@ export function PendingChanges({ inline = false } = {}) {
435
529
  const { pendingChanges } = useStore(appStore);
436
530
  const list = pendingChanges || [];
437
531
  useTick(list.some(p => p.state === "pending"));
532
+ // #2168: inline (Settings' own column), a request that has just started waiting is brought into view — on a phone the
533
+ // person applied far down the list, and the card that says where to confirm it sat above the screen. Once per request.
534
+ const box = useRef(null), shown = useRef(new Set());
535
+ useEffect(() => {
536
+ if (!inline || !box.current) return;
537
+ for (const p of list) {
538
+ if (p.state !== "pending" || shown.current.has(p.id)) continue;
539
+ shown.current.add(p.id);
540
+ const card = box.current.querySelector(`[data-pending="${p.id}"]`);
541
+ const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
542
+ if (card && typeof card.scrollIntoView === "function") card.scrollIntoView({ block: "nearest", behavior: still ? "auto" : "smooth" });
543
+ }
544
+ });
438
545
  if (!list.length) return null;
439
- return html`<div data-trust="confirmation" class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} class=${`pending-card ${p.state}`} role="status">
546
+ return html`<div data-trust="confirmation" ref=${box} class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} data-pending=${p.id} class=${`pending-card ${p.state}`} role="status">
440
547
  <div class="pending-head"><${Icon} name=${p.state === "applied" ? "check" : p.state === "pending" || p.state === "applying" ? "clock" : "alert"} size=${16} />
441
548
  <strong>${t(`app.pending_${p.state}`)}</strong>
442
549
  ${p.state === "pending" ? html`<span class="pending-left">${t("app.pendingLeft", mmss(p.deadline - NOW()))}</span>` : null}
@@ -444,6 +551,7 @@ export function PendingChanges({ inline = false } = {}) {
444
551
  ${p.label ? html`<div class="pending-label">${p.label}</div>` : null}
445
552
  ${Array.isArray(p.summary) && p.summary.length ? html`<ul class="pending-summary">${p.summary.map((line, i) => html`<li key=${i}>${line}</li>`)}</ul>` : null}
446
553
  ${p.state === "pending" ? html`<p class="note">${p.confirmation && p.confirmation.kind === "host_cli" ? t("app.pendingHost", `agend settings confirm ${p.id}`) : t("app.pendingChat")}</p>` : null}
554
+ ${(p.state === "applying" || p.state === "applied") && p.confirmation && p.confirmation.kind === "web" ? html`<p class="note">${t("app.pendingWeb")}</p>` : null}
447
555
  ${p.state !== "pending" && p.state !== "applying" && p.state !== "applied" && p.outcome && p.outcome.message ? html`<p class="note">${p.outcome.message}</p>` : null}
448
556
  ${p.withdraw ? html`<div class="pending-actions"><button type="button" class="btn btn-sm" onClick=${p.withdraw}>${t("app.withdraw")}</button></div>` : null}
449
557
  </section>`)}</div>`;
@@ -493,9 +601,43 @@ function Outlet({ route, seq, panels }) {
493
601
  return html`<${C} route=${route} navKey=${`${routeKey(route)}|${seq}|${lang()}`} />`;
494
602
  }
495
603
 
604
+ /**
605
+ * #2196: the open agent's colours on the whole page (its theme's page section, or a person's choice), drawn on the
606
+ * shell in the same render that opens it — no fetch (the themes are on the page already), no flash, colours only, so
607
+ * no layout moves. Redrawn when the themes, the person's choices, the viewer's switch or the light/dark mode move.
608
+ */
609
+ function usePageTheme(ref, route) {
610
+ useStore(themesStore);
611
+ const [, bump] = useState(0);
612
+ useEffect(() => onPrefsChanged((changed) => { if (changed.has("theme.pageByInstance") || changed.has("fx.show")) bump((n) => n + 1); }), []);
613
+ useEffect(() => {
614
+ const T = globalThis.AgendTheme;
615
+ const off = T && typeof T.onChange === "function" ? T.onChange(() => bump((n) => n + 1)) : null;
616
+ let mq = null;
617
+ try { mq = globalThis.matchMedia ? globalThis.matchMedia("(prefers-color-scheme: light)") : null; } catch { mq = null; }
618
+ const onMq = () => bump((n) => n + 1);
619
+ if (mq && typeof mq.addEventListener === "function") mq.addEventListener("change", onMq);
620
+ return () => { if (typeof off === "function") off(); if (mq && typeof mq.removeEventListener === "function") mq.removeEventListener("change", onMq); };
621
+ }, []);
622
+ const mode = resolvedMode();
623
+ const p = pageNow(mode, route);
624
+ const key = p ? `${mode}|${JSON.stringify(p)}` : "";
625
+ useLayoutEffect(() => { applyPageTheme(ref.current, p, mode); }, [key]);
626
+ // #2199: the agent open now, as body[data-agent] — for a person's own custom CSS to follow (body[data-agent="x"]
627
+ // { --bg-sidebar: … }); set and cleared in the same render as the page colours, whatever the themes switch says.
628
+ const agent = openInstanceOf(route);
629
+ useLayoutEffect(() => {
630
+ const body = typeof document !== "undefined" ? document.body : null;
631
+ if (!body || !body.dataset) return;
632
+ if (agent) body.dataset.agent = agent; else delete body.dataset.agent;
633
+ }, [agent]);
634
+ }
635
+
496
636
  export function Shell({ panels, onNewInstance, viewOnly = false }) {
497
637
  const nav = useStore(navStore);
498
638
  const shell = useStore(shellStore);
639
+ const root = useRef(null);
640
+ usePageTheme(root, nav.route);
499
641
  const [, relang] = useState(0);
500
642
  // A language switch re-renders everything and gives the panel a new lease (its key includes the language).
501
643
  useEffect(() => onLang(() => relang(n => n + 1)), []);
@@ -508,23 +650,25 @@ export function Shell({ panels, onNewInstance, viewOnly = false }) {
508
650
  vv.addEventListener("resize", onResize);
509
651
  return () => vv.removeEventListener("resize", onResize);
510
652
  }, []);
511
- const cls = ["shell", shell.collapsed ? "sb-collapsed" : "", shell.drawer ? "sb-open" : "", shell.keyboard ? "kb-open" : ""].filter(Boolean).join(" ");
512
- return html`<div class=${cls}>
653
+ const guest = guestPage(); // #1920: a guest's page — no operations, usage, pending changes or owner CSS
654
+ const cls = ["shell", shell.collapsed ? "sb-collapsed" : "", shell.drawer ? "sb-open" : "", shell.keyboard ? "kb-open" : "", guest ? "guest" : ""].filter(Boolean).join(" ");
655
+ return html`<div ref=${root} class=${cls}>
513
656
  <a data-trust="navigation" class="skip" href="#main">${t("app.skip")}</a>
514
657
  <${Sidebar} route=${nav.route} onNewInstance=${onNewInstance} viewOnly=${viewOnly} />
515
658
  <div class="scrim" onClick=${closeDrawer} aria-hidden="true"></div>
516
659
  <main id="main" class="main" tabindex="-1" inert=${shell.drawer && narrow() ? true : undefined}>
517
660
  <${ConnectionLine} />
518
- ${viewOnly ? null : html`<${OperationLine} route=${nav.route} />`}
661
+ ${viewOnly || guest ? null : html`<${OperationLine} route=${nav.route} />`}
662
+ ${viewOnly || guest || !shell.banner ? null : html`<${shell.banner} />`}
519
663
  <${Outlet} route=${nav.route} seq=${nav.seq} panels=${panels} />
520
664
  </main>
521
- <${UsageRail} route=${nav.route} />
665
+ ${guest ? null : html`<${UsageRail} route=${nav.route} />`}
522
666
  <${BottomTabs} route=${nav.route} viewOnly=${viewOnly} />
523
667
  ${shell.dialog ? html`<${shell.dialog.Component} ...${shell.dialog.props} onClose=${closeDialog} />` : null}
524
- ${viewOnly || (nav.route && nav.route.panel === "settings") ? null : html`<${PendingChanges} />`}
668
+ ${viewOnly || guest || (nav.route && nav.route.panel === "settings") ? null : html`<${PendingChanges} />`}
525
669
  <${ConfirmHost} />
526
670
  <${ColorDialogHost} />
527
- ${viewOnly ? null : html`<${OwnerCssKeepBar} />`}
671
+ ${viewOnly || guest ? null : html`<${OwnerCssKeepBar} />`}
528
672
  <${Toasts} />
529
673
  </div>`;
530
674
  }
@@ -26,6 +26,20 @@ export function createStore(initial) {
26
26
  export const appStore = createStore({ ready: false, instances: [], uptime: 0, exec: {}, awaiting: {}, connection: "connecting", needs: [], connections: null,
27
27
  hydration: "none", retryHydration: null });
28
28
 
29
+ /**
30
+ * #1920 phase 3: this page is a guest's — the server says so on <body> (data-tier="guest"). A guest sees only its own
31
+ * agents' chats: no fleet, Needs you, View, Settings, filters, commands or new agent, and nothing it may not read is asked
32
+ * for (each would be refused). The server checks every request regardless; this only keeps the page to what works.
33
+ */
34
+ export function guestPage() {
35
+ return typeof document !== "undefined" && !!document.body && document.body.dataset.tier === "guest";
36
+ }
37
+ /** Who a guest's page is signed in as: its web name and its platform (the server's, on <body>). */
38
+ export function guestBadge() {
39
+ const d = typeof document !== "undefined" && document.body ? document.body.dataset : {};
40
+ return { name: d.guestName || "", platform: d.guestPlatform || "" };
41
+ }
42
+
29
43
  /** Fold one status frame (stream or poll) into the store. */
30
44
  export function applyStatus(d) {
31
45
  if (!d || !Array.isArray(d.instances)) return;
@@ -42,7 +56,18 @@ export function applyStatus(d) {
42
56
  // #1629: the agents' themes' revision (app-themes.js re-reads them when it moves).
43
57
  themesRev: typeof d.themes_rev === "number" ? d.themes_rev : null,
44
58
  // #1685: host memory pressure slowing or holding new agents ("elevated" | "critical"), else null.
45
- hostMemory: d.host_memory === "elevated" || d.host_memory === "critical" ? d.host_memory : null });
59
+ hostMemory: d.host_memory === "elevated" || d.host_memory === "critical" ? d.host_memory : null,
60
+ // #2015: while it lasts, the largest processes in the fleet and whose they are (at most 3; anything else dropped).
61
+ hostMemoryTop: hostMemoryTop(d.host_memory_top) });
62
+ }
63
+
64
+ /** #2015: the status frame's host_memory_top, as the dashboard keeps it: well-formed entries only, at most 3. */
65
+ export function hostMemoryTop(raw) {
66
+ if (!Array.isArray(raw)) return [];
67
+ return raw.slice(0, 3).flatMap(p => p && typeof p.name === "string" && Number.isSafeInteger(p.pid) && Number.isSafeInteger(p.rssBytes) && p.rssBytes >= 0
68
+ && ["fleet", "tmux", "instance", "other"].includes(p.owner)
69
+ ? [{ name: p.name.slice(0, 64), pid: p.pid, rssBytes: p.rssBytes, owner: p.owner, ...(p.owner === "instance" && typeof p.instance === "string" ? { instance: p.instance.slice(0, 128) } : {}) }]
70
+ : []);
46
71
  }
47
72
 
48
73
  /** One "activity" event: an instance started or stopped working. */
@@ -30,7 +30,11 @@
30
30
  // - suspend()/resume(): the page is leaving (pagehide) / came back from the back-forward cache.
31
31
 
32
32
  // #1218: steps_tick — an instance's transcript moved while a Log watches it (SSE only: a poll page asks the Log itself).
33
- const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "steps_tick", "clock"];
33
+ // #1918: traffic — a message between instances moved; delegation — a task opened, or its report closed it (SSE only: the
34
+ // org canvas reads /ui/traffic when it has no stream).
35
+ // #2233: backend_login — a dashboard sign-in started, moved or ended (no details: the page reads its own view).
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", "steps_tick", "clock", "traffic", "delegation", "backend_login", "tasks"];
34
38
  const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
35
39
  const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
36
40
  const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed