@songsid/agend 2.2.0 → 2.2.5

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 (447) hide show
  1. package/dist/agent-cli-instructions.md +5 -2
  2. package/dist/agent-cli.js +284 -2
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/agent-endpoint.d.ts +2 -0
  5. package/dist/agent-endpoint.js +29 -0
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-file-read.d.ts +38 -0
  8. package/dist/agent-file-read.js +317 -0
  9. package/dist/agent-file-read.js.map +1 -0
  10. package/dist/agent-whats-new.d.ts +11 -0
  11. package/dist/agent-whats-new.js +34 -2
  12. package/dist/agent-whats-new.js.map +1 -1
  13. package/dist/atomic-write.js +9 -1
  14. package/dist/atomic-write.js.map +1 -1
  15. package/dist/auth-api.d.ts +30 -1
  16. package/dist/auth-api.js +61 -12
  17. package/dist/auth-api.js.map +1 -1
  18. package/dist/authority/authority-state.d.ts +38 -0
  19. package/dist/authority/authority-state.js +114 -0
  20. package/dist/authority/authority-state.js.map +1 -0
  21. package/dist/authority/session-authorizations.d.ts +43 -0
  22. package/dist/authority/session-authorizations.js +102 -0
  23. package/dist/authority/session-authorizations.js.map +1 -0
  24. package/dist/authority/web-authority.d.ts +120 -0
  25. package/dist/authority/web-authority.js +157 -0
  26. package/dist/authority/web-authority.js.map +1 -0
  27. package/dist/backend/antigravity.js +3 -3
  28. package/dist/backend/antigravity.js.map +1 -1
  29. package/dist/backend/claude-code.d.ts +2 -2
  30. package/dist/backend/claude-code.js +10 -5
  31. package/dist/backend/claude-code.js.map +1 -1
  32. package/dist/backend/launch-effort.js +1 -1
  33. package/dist/backend/launch-effort.js.map +1 -1
  34. package/dist/backend/muse.js +10 -2
  35. package/dist/backend/muse.js.map +1 -1
  36. package/dist/backend/types.d.ts +14 -0
  37. package/dist/backend/types.js +25 -0
  38. package/dist/backend/types.js.map +1 -1
  39. package/dist/backend-availability.d.ts +120 -0
  40. package/dist/backend-availability.js +396 -0
  41. package/dist/backend-availability.js.map +1 -0
  42. package/dist/backend-command.d.ts +21 -3
  43. package/dist/backend-command.js +23 -4
  44. package/dist/backend-command.js.map +1 -1
  45. package/dist/bundled-skills.d.ts +14 -0
  46. package/dist/bundled-skills.js +29 -0
  47. package/dist/bundled-skills.js.map +1 -0
  48. package/dist/cache-analysis.d.ts +47 -1
  49. package/dist/cache-analysis.js +68 -8
  50. package/dist/cache-analysis.js.map +1 -1
  51. package/dist/cache-ledger.d.ts +16 -3
  52. package/dist/cache-ledger.js +33 -13
  53. package/dist/cache-ledger.js.map +1 -1
  54. package/dist/cache-report-tool.d.ts +17 -0
  55. package/dist/cache-report-tool.js +40 -0
  56. package/dist/cache-report-tool.js.map +1 -0
  57. package/dist/cache-service.d.ts +70 -3
  58. package/dist/cache-service.js +178 -17
  59. package/dist/cache-service.js.map +1 -1
  60. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  61. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  62. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  63. package/dist/channel/adapters/discord.d.ts +11 -0
  64. package/dist/channel/adapters/discord.js +66 -6
  65. package/dist/channel/adapters/discord.js.map +1 -1
  66. package/dist/channel/adapters/telegram.d.ts +8 -1
  67. package/dist/channel/adapters/telegram.js +74 -4
  68. package/dist/channel/adapters/telegram.js.map +1 -1
  69. package/dist/channel/mcp-tools.js +48 -2
  70. package/dist/channel/mcp-tools.js.map +1 -1
  71. package/dist/channel/types.d.ts +21 -0
  72. package/dist/classic-channel-manager.d.ts +10 -5
  73. package/dist/classic-channel-manager.js +12 -9
  74. package/dist/classic-channel-manager.js.map +1 -1
  75. package/dist/cli.js +142 -110
  76. package/dist/cli.js.map +1 -1
  77. package/dist/config-validator.js +119 -4
  78. package/dist/config-validator.js.map +1 -1
  79. package/dist/daemon.d.ts +10 -0
  80. package/dist/daemon.js +327 -31
  81. package/dist/daemon.js.map +1 -1
  82. package/dist/decision-list.d.ts +25 -0
  83. package/dist/decision-list.js +30 -0
  84. package/dist/decision-list.js.map +1 -0
  85. package/dist/delivery-outbox.d.ts +10 -0
  86. package/dist/delivery-outbox.js +12 -1
  87. package/dist/delivery-outbox.js.map +1 -1
  88. package/dist/fleet-context.d.ts +15 -0
  89. package/dist/fleet-manager.d.ts +514 -12
  90. package/dist/fleet-manager.js +1885 -163
  91. package/dist/fleet-manager.js.map +1 -1
  92. package/dist/fleet-process-handlers.d.ts +18 -0
  93. package/dist/fleet-process-handlers.js +69 -0
  94. package/dist/fleet-process-handlers.js.map +1 -0
  95. package/dist/fleet-traffic.d.ts +74 -0
  96. package/dist/fleet-traffic.js +196 -0
  97. package/dist/fleet-traffic.js.map +1 -0
  98. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
  99. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
  100. package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
  101. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  102. package/dist/general-knowledge/skills/web-appearance/SKILL.md +492 -0
  103. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +482 -0
  104. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  105. package/dist/human-delivery-receipts.d.ts +24 -1
  106. package/dist/human-delivery-receipts.js +64 -11
  107. package/dist/human-delivery-receipts.js.map +1 -1
  108. package/dist/instance-avatar.js +3 -0
  109. package/dist/instance-avatar.js.map +1 -1
  110. package/dist/instance-config-impact.js +6 -1
  111. package/dist/instance-config-impact.js.map +1 -1
  112. package/dist/instance-lifecycle.d.ts +2 -0
  113. package/dist/instance-lifecycle.js +20 -10
  114. package/dist/instance-lifecycle.js.map +1 -1
  115. package/dist/instance-names.d.ts +4 -0
  116. package/dist/instance-names.js +12 -0
  117. package/dist/instance-names.js.map +1 -0
  118. package/dist/instructions.d.ts +12 -1
  119. package/dist/instructions.js +13 -2
  120. package/dist/instructions.js.map +1 -1
  121. package/dist/locale.js +100 -8
  122. package/dist/locale.js.map +1 -1
  123. package/dist/ls-attach-resolve.d.ts +95 -0
  124. package/dist/ls-attach-resolve.js +174 -0
  125. package/dist/ls-attach-resolve.js.map +1 -0
  126. package/dist/memory-hogs.d.ts +27 -0
  127. package/dist/memory-hogs.js +124 -0
  128. package/dist/memory-hogs.js.map +1 -0
  129. package/dist/needs-you-hub.d.ts +4 -0
  130. package/dist/needs-you-hub.js +31 -6
  131. package/dist/needs-you-hub.js.map +1 -1
  132. package/dist/needs-you.d.ts +1 -13
  133. package/dist/needs-you.js +3 -37
  134. package/dist/needs-you.js.map +1 -1
  135. package/dist/outbound-handlers.d.ts +15 -0
  136. package/dist/outbound-handlers.js +152 -11
  137. package/dist/outbound-handlers.js.map +1 -1
  138. package/dist/outbound-schemas.d.ts +228 -4
  139. package/dist/outbound-schemas.js +147 -1
  140. package/dist/outbound-schemas.js.map +1 -1
  141. package/dist/public-web-gateway.js +21 -1
  142. package/dist/public-web-gateway.js.map +1 -1
  143. package/dist/public-web-link.d.ts +9 -0
  144. package/dist/public-web-link.js +8 -0
  145. package/dist/public-web-link.js.map +1 -1
  146. package/dist/quickstart-api.d.ts +10 -1
  147. package/dist/quickstart-api.js +12 -4
  148. package/dist/quickstart-api.js.map +1 -1
  149. package/dist/reply-buttons-controller.d.ts +4 -0
  150. package/dist/reply-buttons-controller.js +2 -2
  151. package/dist/reply-buttons-controller.js.map +1 -1
  152. package/dist/repo-worktree-guard.d.ts +36 -0
  153. package/dist/repo-worktree-guard.js +143 -0
  154. package/dist/repo-worktree-guard.js.map +1 -0
  155. package/dist/restart-progress.d.ts +65 -1
  156. package/dist/restart-progress.js +236 -10
  157. package/dist/restart-progress.js.map +1 -1
  158. package/dist/rollout-index.d.ts +5 -0
  159. package/dist/rollout-index.js +26 -5
  160. package/dist/rollout-index.js.map +1 -1
  161. package/dist/scheduler/scheduler.d.ts +70 -1
  162. package/dist/scheduler/scheduler.js +256 -17
  163. package/dist/scheduler/scheduler.js.map +1 -1
  164. package/dist/service-installer.d.ts +2 -1
  165. package/dist/service-installer.js +18 -3
  166. package/dist/service-installer.js.map +1 -1
  167. package/dist/settings-api.d.ts +23 -0
  168. package/dist/settings-api.js +233 -2
  169. package/dist/settings-api.js.map +1 -1
  170. package/dist/settings-change.js +112 -0
  171. package/dist/settings-change.js.map +1 -1
  172. package/dist/settings-effect.js +32 -2
  173. package/dist/settings-effect.js.map +1 -1
  174. package/dist/settings-http-confirmation.js +5 -0
  175. package/dist/settings-http-confirmation.js.map +1 -1
  176. package/dist/tmux-manager.d.ts +5 -0
  177. package/dist/tmux-manager.js +16 -0
  178. package/dist/tmux-manager.js.map +1 -1
  179. package/dist/tool-permissions.d.ts +4 -6
  180. package/dist/tool-permissions.js +100 -2
  181. package/dist/tool-permissions.js.map +1 -1
  182. package/dist/topic-commands.d.ts +17 -7
  183. package/dist/topic-commands.js +39 -21
  184. package/dist/topic-commands.js.map +1 -1
  185. package/dist/topic-link.d.ts +128 -0
  186. package/dist/topic-link.js +269 -0
  187. package/dist/topic-link.js.map +1 -0
  188. package/dist/types.d.ts +61 -1
  189. package/dist/ui/app-effects.js +245 -0
  190. package/dist/ui/chat-ambient.js +307 -0
  191. package/dist/ui/chat-effect-posters.js +42 -0
  192. package/dist/ui/chat-effects.js +160 -0
  193. package/dist/ui/chat-live-replies.js +58 -0
  194. package/dist/ui/chat-render.js +25 -7
  195. package/dist/ui/chat-store.js +41 -13
  196. package/dist/ui/chat-strings.js +86 -2
  197. package/dist/ui/chat-thread.js +204 -37
  198. package/dist/ui/chat-tour.js +1 -1
  199. package/dist/ui/effect-trial-controller.js +124 -0
  200. package/dist/ui/effect-trial-offer.js +31 -0
  201. package/dist/ui/effect-trial-panel.js +40 -0
  202. package/dist/ui/fleet-cache.js +23 -1
  203. package/dist/ui/fleet-canvas-layout.js +111 -0
  204. package/dist/ui/fleet-canvas.js +525 -0
  205. package/dist/ui/fleet-org.js +27 -12
  206. package/dist/ui/fleet-traffic.js +343 -0
  207. package/dist/ui/fleet-usage.js +401 -0
  208. package/dist/ui/image-lightbox.js +1 -1
  209. package/dist/ui/panel-chat.js +252 -58
  210. package/dist/ui/panel-details.js +20 -6
  211. package/dist/ui/panel-fleet.js +45 -19
  212. package/dist/ui/panel-log.js +6 -6
  213. package/dist/ui/panel-needs.js +3 -3
  214. package/dist/ui/panel-settings.js +58 -13
  215. package/dist/ui/preview-panel.js +25 -9
  216. package/dist/ui/preview.js +3 -2
  217. package/dist/ui/settings-appearance.js +349 -0
  218. package/dist/ui/settings-catalog.js +92 -0
  219. package/dist/ui/settings-dialogs.js +40 -12
  220. package/dist/ui/settings-effects.js +202 -0
  221. package/dist/ui/settings-guests.js +117 -0
  222. package/dist/ui/settings-learn.js +25 -0
  223. package/dist/ui/settings-model.js +15 -6
  224. package/dist/ui/settings-mods.js +271 -0
  225. package/dist/ui/settings-owner-effects.js +80 -0
  226. package/dist/ui/settings-strings.js +335 -10
  227. package/dist/ui/settings-web-authority.js +91 -0
  228. package/dist/ui/shared/agend-auth.js +28 -4
  229. package/dist/ui/shared/agent-widget.js +248 -0
  230. package/dist/ui/shared/app-i18n.js +6 -2
  231. package/dist/ui/shared/app-prefs.js +34 -13
  232. package/dist/ui/shared/app-route.js +42 -2
  233. package/dist/ui/shared/app-session.js +3 -1
  234. package/dist/ui/shared/app-shell.js +119 -41
  235. package/dist/ui/shared/app-store.js +28 -1
  236. package/dist/ui/shared/app-stream.js +14 -7
  237. package/dist/ui/shared/app-themes.js +206 -0
  238. package/dist/ui/shared/app.css +566 -5
  239. package/dist/ui/shared/app.js +23 -10
  240. package/dist/ui/shared/card-strings.js +32 -0
  241. package/dist/ui/shared/effect-app-runtime.js +297 -0
  242. package/dist/ui/shared/effect-baseline.js +64 -0
  243. package/dist/ui/shared/effect-chat-runtime.js +167 -0
  244. package/dist/ui/shared/effect-cursor.js +125 -0
  245. package/dist/ui/shared/effect-frame.js +146 -0
  246. package/dist/ui/shared/effect-geometry.js +137 -0
  247. package/dist/ui/shared/effect-host-style.js +10 -0
  248. package/dist/ui/shared/effect-owner-regions.js +74 -0
  249. package/dist/ui/shared/effect-owner-source.js +122 -0
  250. package/dist/ui/shared/effect-policy.js +151 -0
  251. package/dist/ui/shared/effect-poster-reader.js +98 -0
  252. package/dist/ui/shared/effect-posters.js +117 -0
  253. package/dist/ui/shared/effect-profiler.js +184 -0
  254. package/dist/ui/shared/effect-profiles.js +65 -0
  255. package/dist/ui/shared/effect-registry.js +543 -0
  256. package/dist/ui/shared/effect-runtime-loader.js +234 -0
  257. package/dist/ui/shared/effect-safety.js +153 -0
  258. package/dist/ui/shared/effect-stock-snapshot.js +43 -0
  259. package/dist/ui/shared/effect-stock-trust.js +217 -0
  260. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  261. package/dist/ui/shared/effect-trial-source.js +103 -0
  262. package/dist/ui/shared/effect-trial-stage.js +170 -0
  263. package/dist/ui/shared/effect-trial.css +40 -0
  264. package/dist/ui/shared/fx-motion.js +67 -0
  265. package/dist/ui/shared/header-tools.js +1 -1
  266. package/dist/ui/shared/identity-card.js +273 -0
  267. package/dist/ui/shared/identity-color.js +261 -0
  268. package/dist/ui/shared/instance-nav.js +215 -41
  269. package/dist/ui/shared/owner-css.js +250 -0
  270. package/dist/ui/shared/owner-effect-prefs.js +117 -0
  271. package/dist/ui/shared/panel-view.js +103 -10
  272. package/dist/ui/shared/signin.js +2 -0
  273. package/dist/ui/shared/tokens.css +9 -3
  274. package/dist/ui/shared/ui-dialog.js +1 -1
  275. package/dist/ui/shared/ui-icons.js +3 -0
  276. package/dist/ui/shared/ui-menu.js +1 -1
  277. package/dist/ui/shared/ui-toast.js +1 -1
  278. package/dist/ui/shared/usage-dialog.js +22 -2
  279. package/dist/ui/shared/view-strings.js +98 -2
  280. package/dist/ui/shared/web-authorize.js +151 -0
  281. package/dist/ui/signin.html +1 -0
  282. package/dist/ui/web-terminal/terminal.css +4 -4
  283. package/dist/usage/providers.d.ts +28 -0
  284. package/dist/usage/providers.js +126 -36
  285. package/dist/usage/providers.js.map +1 -1
  286. package/dist/usage-report.d.ts +158 -0
  287. package/dist/usage-report.js +226 -0
  288. package/dist/usage-report.js.map +1 -0
  289. package/dist/usage-rollup.d.ts +51 -0
  290. package/dist/usage-rollup.js +168 -0
  291. package/dist/usage-rollup.js.map +1 -0
  292. package/dist/view-api.d.ts +68 -0
  293. package/dist/view-api.js +249 -8
  294. package/dist/view-api.js.map +1 -1
  295. package/dist/web-ambient.d.ts +72 -0
  296. package/dist/web-ambient.js +161 -0
  297. package/dist/web-ambient.js.map +1 -0
  298. package/dist/web-api.d.ts +117 -7
  299. package/dist/web-api.js +1283 -70
  300. package/dist/web-api.js.map +1 -1
  301. package/dist/web-appearance-limits.d.ts +82 -0
  302. package/dist/web-appearance-limits.js +83 -0
  303. package/dist/web-appearance-limits.js.map +1 -0
  304. package/dist/web-appearance-policy.d.ts +64 -0
  305. package/dist/web-appearance-policy.js +87 -0
  306. package/dist/web-appearance-policy.js.map +1 -0
  307. package/dist/web-appearance-tools.d.ts +53 -0
  308. package/dist/web-appearance-tools.js +124 -0
  309. package/dist/web-appearance-tools.js.map +1 -0
  310. package/dist/web-appearance-view.d.ts +124 -0
  311. package/dist/web-appearance-view.js +278 -0
  312. package/dist/web-appearance-view.js.map +1 -0
  313. package/dist/web-appearance.d.ts +93 -0
  314. package/dist/web-appearance.js +373 -0
  315. package/dist/web-appearance.js.map +1 -0
  316. package/dist/web-asset-parts.d.ts +93 -0
  317. package/dist/web-asset-parts.js +133 -0
  318. package/dist/web-asset-parts.js.map +1 -0
  319. package/dist/web-auth.js +20 -2
  320. package/dist/web-auth.js.map +1 -1
  321. package/dist/web-card.d.ts +110 -0
  322. package/dist/web-card.js +266 -0
  323. package/dist/web-card.js.map +1 -0
  324. package/dist/web-channel-echo.d.ts +7 -1
  325. package/dist/web-channel-echo.js +6 -2
  326. package/dist/web-channel-echo.js.map +1 -1
  327. package/dist/web-chat-history.d.ts +63 -0
  328. package/dist/web-chat-history.js +109 -6
  329. package/dist/web-chat-history.js.map +1 -1
  330. package/dist/web-commands.d.ts +1 -0
  331. package/dist/web-effect-bootstrap.d.ts +12 -0
  332. package/dist/web-effect-bootstrap.js +49 -0
  333. package/dist/web-effect-bootstrap.js.map +1 -0
  334. package/dist/web-effect-bundle.d.ts +28 -0
  335. package/dist/web-effect-bundle.js +171 -0
  336. package/dist/web-effect-bundle.js.map +1 -0
  337. package/dist/web-effect-canonical.d.ts +8 -0
  338. package/dist/web-effect-canonical.js +27 -0
  339. package/dist/web-effect-canonical.js.map +1 -0
  340. package/dist/web-effect-library.d.ts +114 -0
  341. package/dist/web-effect-library.js +530 -0
  342. package/dist/web-effect-library.js.map +1 -0
  343. package/dist/web-effect-playback.d.ts +30 -0
  344. package/dist/web-effect-playback.js +71 -0
  345. package/dist/web-effect-playback.js.map +1 -0
  346. package/dist/web-effect-poster.d.ts +8 -0
  347. package/dist/web-effect-poster.js +52 -0
  348. package/dist/web-effect-poster.js.map +1 -0
  349. package/dist/web-effect-profiles.d.ts +118 -0
  350. package/dist/web-effect-profiles.js +180 -0
  351. package/dist/web-effect-profiles.js.map +1 -0
  352. package/dist/web-effect-service.d.ts +93 -0
  353. package/dist/web-effect-service.js +335 -0
  354. package/dist/web-effect-service.js.map +1 -0
  355. package/dist/web-effect-tools.d.ts +48 -0
  356. package/dist/web-effect-tools.js +129 -0
  357. package/dist/web-effect-tools.js.map +1 -0
  358. package/dist/web-effect-trials.d.ts +86 -0
  359. package/dist/web-effect-trials.js +204 -0
  360. package/dist/web-effect-trials.js.map +1 -0
  361. package/dist/web-effect-upload.d.ts +27 -0
  362. package/dist/web-effect-upload.js +185 -0
  363. package/dist/web-effect-upload.js.map +1 -0
  364. package/dist/web-effect-worker.d.ts +8 -0
  365. package/dist/web-effect-worker.js +170 -0
  366. package/dist/web-effect-worker.js.map +1 -0
  367. package/dist/web-effect.d.ts +103 -0
  368. package/dist/web-effect.js +153 -0
  369. package/dist/web-effect.js.map +1 -0
  370. package/dist/web-guest-access.d.ts +125 -0
  371. package/dist/web-guest-access.js +234 -0
  372. package/dist/web-guest-access.js.map +1 -0
  373. package/dist/web-guest-rooms.d.ts +82 -0
  374. package/dist/web-guest-rooms.js +135 -0
  375. package/dist/web-guest-rooms.js.map +1 -0
  376. package/dist/web-identity-color.d.ts +87 -0
  377. package/dist/web-identity-color.js +209 -0
  378. package/dist/web-identity-color.js.map +1 -0
  379. package/dist/web-identity-palette.d.ts +113 -0
  380. package/dist/web-identity-palette.js +84 -0
  381. package/dist/web-identity-palette.js.map +1 -0
  382. package/dist/web-image-meta.d.ts +38 -0
  383. package/dist/web-image-meta.js +262 -0
  384. package/dist/web-image-meta.js.map +1 -0
  385. package/dist/web-login.d.ts +47 -0
  386. package/dist/web-login.js +115 -0
  387. package/dist/web-login.js.map +1 -1
  388. package/dist/web-mod-agent.d.ts +50 -0
  389. package/dist/web-mod-agent.js +207 -0
  390. package/dist/web-mod-agent.js.map +1 -0
  391. package/dist/web-mod-bundle.d.ts +157 -0
  392. package/dist/web-mod-bundle.js +529 -0
  393. package/dist/web-mod-bundle.js.map +1 -0
  394. package/dist/web-mod-export.d.ts +68 -0
  395. package/dist/web-mod-export.js +100 -0
  396. package/dist/web-mod-export.js.map +1 -0
  397. package/dist/web-mod-store.d.ts +289 -0
  398. package/dist/web-mod-store.js +638 -0
  399. package/dist/web-mod-store.js.map +1 -0
  400. package/dist/web-org.d.ts +3 -3
  401. package/dist/web-org.js +11 -17
  402. package/dist/web-org.js.map +1 -1
  403. package/dist/web-owner-css-store.d.ts +222 -0
  404. package/dist/web-owner-css-store.js +440 -0
  405. package/dist/web-owner-css-store.js.map +1 -0
  406. package/dist/web-owner-css.d.ts +58 -0
  407. package/dist/web-owner-css.js +552 -0
  408. package/dist/web-owner-css.js.map +1 -0
  409. package/dist/web-prefs.d.ts +62 -0
  410. package/dist/web-prefs.js +232 -36
  411. package/dist/web-prefs.js.map +1 -1
  412. package/dist/web-preview.d.ts +15 -3
  413. package/dist/web-preview.js +28 -9
  414. package/dist/web-preview.js.map +1 -1
  415. package/dist/web-returning-device.d.ts +41 -0
  416. package/dist/web-returning-device.js +57 -5
  417. package/dist/web-returning-device.js.map +1 -1
  418. package/dist/web-session-authorization.d.ts +132 -0
  419. package/dist/web-session-authorization.js +193 -0
  420. package/dist/web-session-authorization.js.map +1 -0
  421. package/dist/web-session.d.ts +70 -10
  422. package/dist/web-session.js +137 -22
  423. package/dist/web-session.js.map +1 -1
  424. package/dist/web-settings-patch.d.ts +67 -2
  425. package/dist/web-settings-patch.js +196 -1
  426. package/dist/web-settings-patch.js.map +1 -1
  427. package/dist/web-settings-tools.d.ts +89 -0
  428. package/dist/web-settings-tools.js +273 -0
  429. package/dist/web-settings-tools.js.map +1 -0
  430. package/dist/web-shell-routes.d.ts +2 -2
  431. package/dist/web-shell-routes.js +2 -2
  432. package/dist/web-shell-routes.js.map +1 -1
  433. package/dist/web-theme-store.d.ts +257 -0
  434. package/dist/web-theme-store.js +627 -0
  435. package/dist/web-theme-store.js.map +1 -0
  436. package/dist/web-theme.d.ts +123 -0
  437. package/dist/web-theme.js +416 -0
  438. package/dist/web-theme.js.map +1 -0
  439. package/dist/web-upload.d.ts +13 -0
  440. package/dist/web-upload.js +39 -3
  441. package/dist/web-upload.js.map +1 -1
  442. package/dist/web-widget.d.ts +72 -0
  443. package/dist/web-widget.js +245 -0
  444. package/dist/web-widget.js.map +1 -0
  445. package/dist/workflow-templates/default.md +1 -1
  446. package/package.json +1 -1
  447. package/templates/systemd.service.ejs +8 -0
@@ -31,7 +31,7 @@ export function Menu({ items, label, icon = "more", cls = "" }) {
31
31
  return html`<div class=${`menu ${cls}`}>
32
32
  <button ref=${btn} type="button" class="icon-btn" aria-haspopup="menu" aria-expanded=${open ? "true" : "false"}
33
33
  aria-label=${label || t("app.more")} title=${label || t("app.more")} onClick=${() => setOpen(!open)}><${Icon} name=${icon} /></button>
34
- ${open ? html`<div ref=${list} class="menu-list" role="menu" aria-label=${label || t("app.more")} onKeyDown=${onKey}>
34
+ ${open ? html`<div data-trust="menu" ref=${list} class="menu-list" role="menu" aria-label=${label || t("app.more")} onKeyDown=${onKey}>
35
35
  ${shown.map(item => html`<button key=${item.key} type="button" role="menuitem" class=${`menu-item${item.danger ? " danger" : ""}`}
36
36
  disabled=${!!item.disabled} onClick=${() => choose(item)}>${item.icon ? html`<${Icon} name=${item.icon} size=${16} />` : null}<span>${item.label}</span></button>`)}
37
37
  </div>` : null}
@@ -14,5 +14,5 @@ export function toast(text, ok = true) {
14
14
  export function Toasts() {
15
15
  const [list, setList] = useState(toasts.get());
16
16
  useEffect(() => toasts.subscribe(setList), []);
17
- return html`<div class="toasts" role="status" aria-live="polite">${list.map(x => html`<div key=${x.id} class=${`toast ${x.ok ? "ok" : "err"}`}>${x.text}</div>`)}</div>`;
17
+ return html`<div data-trust="toast" class="toasts" role="status" aria-live="polite">${list.map(x => html`<div key=${x.id} class=${`toast ${x.ok ? "ok" : "err"}`}>${x.text}</div>`)}</div>`;
18
18
  }
@@ -4,6 +4,7 @@
4
4
  import { html, useEffect, useLayoutEffect, useMemo, useRef, useState } from "./app-html.js";
5
5
  import { t } from "./app-i18n.js";
6
6
  import { useLease } from "./app-ctx.js";
7
+ import { guestPage } from "./app-store.js";
7
8
  import { readStream } from "./read-stream.js";
8
9
  import { Dialog } from "./ui-dialog.js";
9
10
  import { ErrorState, Skeleton } from "./ui-states.js";
@@ -66,7 +67,17 @@ export function UsageMetric({ m }) {
66
67
  const sub = [note, expiryText(m.expiresAt)].filter(Boolean).join(" · ");
67
68
  return html`<div class="u-metric"><div class="u-row"><span class="u-label">${label}</span><span class="u-val">${val}</span></div>${sub ? html`<div class="u-sub">${sub}</div>` : null}</div>`;
68
69
  }
69
- /** "Updated 3 min ago" (#1585): how old the numbers are — the panel may show the last snapshot while a fresh one comes. */
70
+ /**
71
+ * #2014: a row's title. A credential profile's row (`profile`; its API `name` is "Claude (<profile>)") shows the
72
+ * provider name on the title line beside the plan badge, and the profile on its own full-width muted line under it;
73
+ * the full name is the tooltip, and the dialog shows the profile unclipped. Any other row: its name.
74
+ */
75
+ export function providerTitle(p) {
76
+ const name = String((p && p.name) || ""), profile = p && typeof p.profile === "string" && p.profile ? p.profile : "";
77
+ const suffix = ` (${profile})`;
78
+ return profile && name.endsWith(suffix) ? { base: name.slice(0, -suffix.length), profile } : { base: name, profile };
79
+ }
80
+
70
81
  /**
71
82
  * #1749: a plan as its badge reads — one short line. "Name [Tier] (N×)" (Claude's "Team Premium (6.25x)", "Max (20x)")
72
83
  * becomes "Name ×N"; anything else (Pro, Codex's and muse's labels) as it is. The full name stays on the badge's
@@ -76,6 +87,7 @@ export function planBadge(plan) {
76
87
  const m = /^(\S+)(?: [^()]+)? \((\d+(?:\.\d+)?)x\)$/i.exec(String(plan || "").trim());
77
88
  return m ? `${m[1]} ×${m[2]}` : String(plan || "");
78
89
  }
90
+ /** "Updated 3 min ago" (#1585): how old the numbers are — the panel may show the last snapshot while a fresh one comes. */
79
91
  export function updatedAgo(fetchedAt, now = Date.now()) {
80
92
  const ms = now - new Date(fetchedAt).getTime();
81
93
  if (!Number.isFinite(ms) || ms < 60_000) return tn("usageUpdatedJustNow");
@@ -134,11 +146,12 @@ export function UsageBody({ owner }) {
134
146
  const providers = ordered(data.providers);
135
147
  content = html`${!providers.length ? html`<p class="note">${tn("usage.empty_active")}</p>` : null}
136
148
  ${providers.map((p, i) => html`<section key=${key(p)} class="u-provider">
137
- <div class="u-head"><strong title=${p.name}>${p.name}</strong>${p.plan ? html`<span class="pill u-plan" title=${p.plan}><span aria-hidden="true">${planBadge(p.plan)}</span><span class="sr-only">${p.plan}</span></span>` : null}
149
+ <div class="u-head"><strong title=${p.name}>${providerTitle(p).base}</strong>${p.plan ? html`<span class="pill u-plan" title=${p.plan}><span aria-hidden="true">${planBadge(p.plan)}</span><span class="sr-only">${p.plan}</span></span>` : null}
138
150
  <span class="u-order">
139
151
  <button type="button" class="icon-btn" disabled=${i === 0} aria-label=${tn("usageMoveUp")} title=${tn("usageMoveUp")} onClick=${() => move(key(p), -1, data.providers)}><${Icon} name="up" size=${14} /></button>
140
152
  <button type="button" class="icon-btn" disabled=${i === providers.length - 1} aria-label=${tn("usageMoveDown")} title=${tn("usageMoveDown")} onClick=${() => move(key(p), 1, data.providers)}><${Icon} name="down" size=${14} /></button>
141
153
  </span></div>
154
+ ${providerTitle(p).profile ? html`<div class="u-profile" title=${p.name}>${providerTitle(p).profile}</div>` : null}
142
155
  ${p.stale ? html`<p class="note">${usageText(p.hint || "", p.hintI18n)}</p>` : null}
143
156
  ${p.status === "error" && !p.transient ? html`<p class="u-err">${usageText(p.error || tn("usage.error_fallback"), p.errorI18n)}</p>`
144
157
  : p.status === "error" && p.transient ? html`<p class="note">${usageText(p.error || tn("usage.error_fallback"), p.errorI18n)}</p>`
@@ -147,11 +160,18 @@ export function UsageBody({ owner }) {
147
160
  ${!p.stale && p.hint ? html`<p class="note">${usageText(p.hint, p.hintI18n)}</p>` : !(p.metrics || []).length ? html`<p class="note">${tn("usage.no_data")}</p>` : null}`}
148
161
  </section>`)}
149
162
  <div class="u-foot"><span class="note" title=${new Date(data.fetchedAt).toLocaleString()}>${updatedAgo(data.fetchedAt)}${data.refreshing ? html` · <span class="u-refreshing">${tn("usageRefreshing")}</span>` : null}</span>
163
+ ${byAgent() ? html`<a class="btn btn-ghost btn-sm u-by-agent" href="/ui/fleet/usage" title=${tn("usageByAgentTitle")}>${tn("usageByAgent")}</a>` : null}
150
164
  <button type="button" class="btn btn-sm" onClick=${() => load(true)}>${tn("usageRefresh")}</button></div>`;
151
165
  }
152
166
  return content;
153
167
  }
154
168
 
169
+ /**
170
+ * #2122: the plans above are per login; each agent's own tokens and API-equivalent are Fleet → Usage — linked from the
171
+ * app (not View's anonymous reader, not a guest, who has no Fleet).
172
+ */
173
+ const byAgent = () => !guestPage() && typeof location !== "undefined" && location.pathname.startsWith("/ui");
174
+
155
175
  /** `owner`: the navigation that opened it — its reads and the minute's timer live and end with it. */
156
176
  export function UsageDialog({ onClose, owner = "" }) {
157
177
  return html`<${Dialog} title=${tn("usage.title")} onClose=${onClose} wide=${true}><${UsageBody} owner=${`usage-dialog:${owner}`} /></${Dialog}>`;
@@ -40,6 +40,8 @@ register("view", {
40
40
  "usageMinutes": "{n} min",
41
41
  "usageHours": "{n} h",
42
42
  "usageRefresh": "Refresh",
43
+ "usageByAgent": "By agent →",
44
+ "usageByAgentTitle": "Each agent's tokens and API-equivalent (Fleet → Usage)",
43
45
  "usageHigh": "high",
44
46
  "usageNear": "near limit",
45
47
  "usageMoveUp": "Move up",
@@ -51,6 +53,16 @@ register("view", {
51
53
  "tooltipEffort": "Effort: {value}",
52
54
  "tooltipCost": "Cost: {value}",
53
55
  "tooltipUnavailable": "Unavailable",
56
+ "srcConfigured": " (configured)",
57
+ "srcFleetDefault": " (fleet default)",
58
+ "modelDefault": "default",
59
+ "modelDefaultWhy": "default ({reason})",
60
+ "modelCliDefault": "{model} (default)",
61
+ "modelWhyStart": "detected when it starts",
62
+ "modelWhyCli": "this CLI does not report a default",
63
+ "groupOther": "Other",
64
+ "groupTeam": "Team",
65
+ "groupTag": "Tag",
54
66
  "statusRunning": "Running",
55
67
  "statusPaused": "Paused",
56
68
  "statusStopped": "Stopped",
@@ -156,6 +168,24 @@ register("view", {
156
168
  "removePicture": "Remove picture",
157
169
  "keepPicture": "Keep picture",
158
170
  "removePictureNote": "Removed when you save. The bot's avatar, the persona emoji or the first letter shows instead.",
171
+ "cardSection": "Identity card",
172
+ "cardLead": "Yours wins over the agent's, field by field. Leave a field empty to show the agent's.",
173
+ "cardTagline": "Tagline (one line, up to 60 characters)",
174
+ "cardBio": "Bio (up to 280 characters, 3 line breaks)",
175
+ "cardPortrait": "Portrait",
176
+ "cardBanner": "Banner",
177
+ "cardBannerFocus": "Banner focus",
178
+ "cardBannerFocusNote": "What stays in view when the banner is cropped: the dashed area shows on every screen and card size; the yellow one is a phone's medium card.",
179
+ "cardFocusFollow": "Follow the agent's",
180
+ "cardRemove": "Remove yours",
181
+ "cardKeep": "Keep yours",
182
+ "clearCard": "Clear card",
183
+ "keepCard": "Keep card",
184
+ "clearCardNote": "Removed when you save: your tagline, bio, portrait and banner. The agent's own card shows instead.",
185
+ "resizeAsk": "This picture is {w}×{h}; it will be resized to {nw}×{nh}.",
186
+ "resizeGo": "Resize",
187
+ "cardFailed": "Card not saved: {error}",
188
+ "pictureTooLarge": "This picture can't be made small enough to upload.",
159
189
  "saved": "Saved",
160
190
  "saveFailed": "Save failed: {error}",
161
191
  "signInToEdit": "Sign in to edit",
@@ -182,7 +212,25 @@ register("view", {
182
212
  "favUp": "Move {name} up",
183
213
  "favDown": "Move {name} down",
184
214
  "favFull": "Favorites are full ({max})",
185
- "favFiltered": "Clear the filter to reorder favorites"
215
+ "favFiltered": "Clear the filter to reorder favorites",
216
+ "favSorted": "Favorites are sorted by last reply — set them back to your order to move them",
217
+ "replyJustNow": "just now",
218
+ "replyMinutes": "{n} min ago",
219
+ "replyHours": "{n} h ago",
220
+ "replyYesterday": "yesterday",
221
+ "replyDays": "{n} d ago",
222
+ "replyTitle": "Last replied: {time}",
223
+ "sortTitle": "Sort the list",
224
+ "sortCustom": "Custom",
225
+ "sortCustomHint": "Your own order (drag rows); replies never move them",
226
+ "sortRecent": "Recent replies",
227
+ "sortRecentHint": "No groups: the last reply first",
228
+ "sortFavs": "Favorites, in Recent replies",
229
+ "sortFavsFixed": "Pinned on top, in your order",
230
+ "sortFavsRecent": "Sorted by last reply too",
231
+ "recentOthers": "Recent replies first",
232
+ "pinnedCurrent": "Current",
233
+ "pinnedLabel": "The instance you have open"
186
234
  },
187
235
  "zh-TW": {
188
236
  "editProfile": "編輯個人資料",
@@ -220,6 +268,8 @@ register("view", {
220
268
  "usageMinutes": "{n} 分鐘",
221
269
  "usageHours": "{n} 小時",
222
270
  "usageRefresh": "重新整理",
271
+ "usageByAgent": "依 agent →",
272
+ "usageByAgentTitle": "每個 agent 的 token 與 API 等值(Fleet → 用量)",
223
273
  "usageHigh": "偏高",
224
274
  "usageNear": "接近上限",
225
275
  "usageMoveUp": "上移",
@@ -231,6 +281,16 @@ register("view", {
231
281
  "tooltipEffort": "推理強度:{value}",
232
282
  "tooltipCost": "費用:{value}",
233
283
  "tooltipUnavailable": "無資料",
284
+ "srcConfigured": "(設定值)",
285
+ "srcFleetDefault": "(fleet 預設)",
286
+ "modelDefault": "預設",
287
+ "modelDefaultWhy": "預設({reason})",
288
+ "modelCliDefault": "{model}(預設)",
289
+ "modelWhyStart": "啟動時偵測",
290
+ "modelWhyCli": "這個 CLI 不提供預設值",
291
+ "groupOther": "其他",
292
+ "groupTeam": "團隊",
293
+ "groupTag": "標籤",
234
294
  "statusRunning": "運行中",
235
295
  "statusPaused": "已暫停",
236
296
  "statusStopped": "已停止",
@@ -336,6 +396,24 @@ register("view", {
336
396
  "removePicture": "移除圖片",
337
397
  "keepPicture": "保留圖片",
338
398
  "removePictureNote": "儲存後移除,改顯示 Bot 的頭像、persona emoji 或名稱第一個字。",
399
+ "cardSection": "身分卡",
400
+ "cardLead": "你設定的欄位優先於 agent 的,逐欄比較。欄位留空就顯示 agent 的。",
401
+ "cardTagline": "標語(一行,最多 60 個字)",
402
+ "cardBio": "簡介(最多 280 個字、3 次換行)",
403
+ "cardPortrait": "肖像",
404
+ "cardBanner": "橫幅",
405
+ "cardBannerFocus": "橫幅焦點",
406
+ "cardBannerFocusNote": "橫幅被裁切時保留在畫面中的部分:虛線框在所有螢幕與卡片大小都看得到;黃色框是手機上中卡片的範圍。",
407
+ "cardFocusFollow": "跟隨 agent 的設定",
408
+ "cardRemove": "移除你的",
409
+ "cardKeep": "保留你的",
410
+ "clearCard": "清除卡片",
411
+ "keepCard": "保留卡片",
412
+ "clearCardNote": "儲存後移除你的標語、簡介、肖像與橫幅,改顯示 agent 自己的卡片。",
413
+ "resizeAsk": "這張圖片是 {w}×{h},會縮小為 {nw}×{nh}。",
414
+ "resizeGo": "縮小",
415
+ "cardFailed": "卡片未儲存:{error}",
416
+ "pictureTooLarge": "這張圖片無法縮到可上傳的大小。",
339
417
  "saved": "已儲存",
340
418
  "saveFailed": "儲存失敗:{error}",
341
419
  "signInToEdit": "登入後才能編輯",
@@ -362,6 +440,24 @@ register("view", {
362
440
  "favUp": "把 {name} 上移",
363
441
  "favDown": "把 {name} 下移",
364
442
  "favFull": "最愛已滿({max} 個)",
365
- "favFiltered": "清除篩選後才能調整最愛順序"
443
+ "favFiltered": "清除篩選後才能調整最愛順序",
444
+ "favSorted": "最愛目前依最近回覆排序;改回「照你的順序」才能移動",
445
+ "replyJustNow": "剛剛",
446
+ "replyMinutes": "{n} 分鐘前",
447
+ "replyHours": "{n} 小時前",
448
+ "replyYesterday": "昨天",
449
+ "replyDays": "{n} 天前",
450
+ "replyTitle": "最後回覆:{time}",
451
+ "sortTitle": "排序方式",
452
+ "sortCustom": "自訂",
453
+ "sortCustomHint": "你拖曳排好的順序,有回覆也不會跳動",
454
+ "sortRecent": "最近回覆",
455
+ "sortRecentHint": "不分組,最後回覆的在上面",
456
+ "sortFavs": "最愛(最近回覆模式時)",
457
+ "sortFavsFixed": "固定在最上面,照你的順序",
458
+ "sortFavsRecent": "也依最近回覆排序",
459
+ "recentOthers": "最近回覆在上",
460
+ "pinnedCurrent": "目前",
461
+ "pinnedLabel": "你目前開著的 instance"
366
462
  }
367
463
  });
@@ -0,0 +1,151 @@
1
+ // #1939 S2b (design §0.1): "Authorize this browser". The app shows this browser's state — verified (until when), a
2
+ // Confirm card waiting in the account's Discord or Telegram DM (with its countdown), or why it can't be authorized here —
3
+ // and asks for the card. The card itself is the gate: nothing here can authorize anything.
4
+ import { html, useEffect, useRef, useState } from "./app-html.js";
5
+ import { register, t } from "./app-i18n.js";
6
+ import { guestPage } from "./app-store.js";
7
+
8
+ register("webauth", {
9
+ en: {
10
+ verified: "Verified · speaks as you until {0}",
11
+ notVerified: "Not verified",
12
+ authorize: "Authorize this browser",
13
+ waiting: "Waiting for your Confirm in Discord or Telegram ({0})",
14
+ sent: "Sent — confirm it in your DM within 5 minutes.",
15
+ publicLink: "Not on the temporary public link: open the dashboard directly to authorize.",
16
+ reason_disabled: "Web authority is turned off.",
17
+ reason_loopback_only: "Only this computer can authorize, and this browser is not on it.",
18
+ reason_no_account: "This browser was not signed in as an account (agend web): sign in with /web to authorize.",
19
+ reason_not_admin: "Your account is not a fleet admin here.",
20
+ reason_not_admin_session: "Guests can't authorize.",
21
+ reason_token_rotated: "The dashboard's token changed: sign in again.",
22
+ reason_public_link: "Not on the temporary public link.",
23
+ reason_not_session: "Sign in to authorize.",
24
+ reason_other: "This browser can't be authorized here.",
25
+ refusal_pending: "A request is already waiting in your DM.",
26
+ refusal_rate_limited: "Three requests this hour — try again in {0} min.",
27
+ refusal_dm_unavailable: "Couldn't send you a DM. Open your DMs with the bot, then try again.",
28
+ refusal_other: "That didn't work: {0}",
29
+ settings: "Settings",
30
+ },
31
+ "zh-TW": {
32
+ verified: "已驗證 · 以你的身分發言到 {0}",
33
+ notVerified: "未驗證",
34
+ authorize: "授權這個瀏覽器",
35
+ waiting: "等你在 Discord 或 Telegram 按下確認({0})",
36
+ sent: "已送出——請在 5 分鐘內到私訊確認。",
37
+ publicLink: "臨時公開連結上無法授權:請直接開啟儀表板再授權。",
38
+ reason_disabled: "網頁授權已關閉。",
39
+ reason_loopback_only: "只有這台電腦可以授權,而這個瀏覽器不在這台電腦上。",
40
+ reason_no_account: "這個瀏覽器不是用帳號登入的(agend web):請用 /web 登入後再授權。",
41
+ reason_not_admin: "你的帳號在這裡不是 fleet 管理者。",
42
+ reason_not_admin_session: "訪客無法授權。",
43
+ reason_token_rotated: "儀表板的 token 已變更:請重新登入。",
44
+ reason_public_link: "臨時公開連結上無法授權。",
45
+ reason_not_session: "登入後才能授權。",
46
+ reason_other: "這個瀏覽器在這裡無法授權。",
47
+ refusal_pending: "已經有一個請求在你的私訊中等待。",
48
+ refusal_rate_limited: "這一小時已請求三次——請 {0} 分鐘後再試。",
49
+ refusal_dm_unavailable: "無法私訊給你。請開放與 bot 的私訊後再試一次。",
50
+ refusal_other: "沒有成功:{0}",
51
+ settings: "設定",
52
+ },
53
+ });
54
+ const tn = (k, ...v) => t(`webauth.${k}`, ...v);
55
+ const REASONS = ["disabled", "loopback_only", "no_account", "not_admin", "not_admin_session", "token_rotated", "public_link", "not_session"];
56
+ // Until when: the time, with the day when it isn't today (an authorization lasts up to 48 h).
57
+ const clock = (ms) => {
58
+ const d = new Date(ms), today = new Date().toDateString() === d.toDateString();
59
+ return today ? d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
60
+ : d.toLocaleString([], { weekday: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
61
+ };
62
+ const mmss = (ms) => { const s = Math.max(0, Math.ceil(ms / 1000)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; };
63
+ const publicLink = () => globalThis.document?.body?.dataset?.publicLink === "1";
64
+
65
+ // One read per page load, shared by every line that shows it (the composer's, the session menu's): coming back to a chat
66
+ // reads nothing (#1374). It is read again only after an ask, and while a card is pending.
67
+ // An answer of any kind counts as this page's read (one that isn't a state hides the line); only a network failure lets a
68
+ // later mount try again. Signed out (401: the session ended, e.g. Not me), the line goes.
69
+ let shared = null, answered = false, inflight = null, signedOut = false;
70
+ async function readStatus() {
71
+ inflight ??= (async () => {
72
+ try {
73
+ const r = await fetch("/ui/authorization", { credentials: "same-origin" });
74
+ answered = true;
75
+ if (r.status === 401) { signedOut = true; shared = null; }
76
+ const body = r.ok ? await r.json().catch(() => null) : null;
77
+ if (body && typeof body.state === "string") shared = body;
78
+ } catch { /* the line stays as it was; a later mount may try again */ }
79
+ finally { inflight = null; }
80
+ return shared;
81
+ })();
82
+ return inflight;
83
+ }
84
+
85
+ /** This browser's authorization, kept fresh while a card is pending (every 3 s, until it is decided or expires). */
86
+ export function useWebAuthorization() {
87
+ const [st, setSt] = useState(shared), [notice, setNotice] = useState(null);
88
+ const [deadline, setDeadline] = useState(0), [now, setNow] = useState(() => Date.now());
89
+ const alive = useRef(true);
90
+ const load = async () => {
91
+ if (publicLink() || guestPage()) return;
92
+ const body = await readStatus();
93
+ if (!alive.current) return;
94
+ if (signedOut) { setSt(null); setNotice(null); return; }
95
+ if (!body) return;
96
+ setSt(body);
97
+ if (body.state !== "pending") setNotice((n) => (n === tn("sent") ? null : n)); // "Sent —" was about the card
98
+ if (body.state === "pending" && typeof body.expiresInMs === "number") setDeadline(Date.now() + body.expiresInMs);
99
+ };
100
+ useEffect(() => { alive.current = true; if (!answered) load(); return () => { alive.current = false; }; }, []);
101
+ useEffect(() => {
102
+ if (!st || st.state !== "pending") return undefined;
103
+ const tick = setInterval(() => setNow(Date.now()), 1000), poll = setInterval(load, 3000);
104
+ return () => { clearInterval(tick); clearInterval(poll); };
105
+ }, [st && st.state]);
106
+ // "Verified · until X" is read again at X (a page left open must not keep saying verified after it ends).
107
+ useEffect(() => {
108
+ if (!st || st.state !== "verified" || typeof st.until !== "number") return undefined;
109
+ const at = setTimeout(() => { resetWebAuthorization(); load(); }, Math.max(0, Math.min(st.until - Date.now() + 1000, 2 ** 31 - 1)));
110
+ return () => clearTimeout(at);
111
+ }, [st && st.state, st && st.until]);
112
+ const ask = async () => {
113
+ setNotice(null);
114
+ let r = null, body = null;
115
+ try { r = await fetch("/ui/authorization", { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json" }, body: "{}" }); body = await r.json(); } catch { body = null; }
116
+ if (r && r.ok && body && body.ok) { setNotice(tn("sent")); setSt(shared = { state: "pending", expiresInMs: body.expiresInMs }); setDeadline(Date.now() + body.expiresInMs); return; }
117
+ const refusal = body && body.refusal;
118
+ if (refusal === "rate_limited") setNotice(tn("refusal_rate_limited", Math.max(1, Math.ceil((body.retryAfterMs || 0) / 60_000))));
119
+ else if (refusal === "pending" || refusal === "dm_unavailable") setNotice(tn(`refusal_${refusal}`));
120
+ else if (REASONS.includes(refusal)) setNotice(tn(`reason_${refusal}`));
121
+ else setNotice(tn("refusal_other", String(refusal || (r && r.status) || "?")));
122
+ load();
123
+ };
124
+ return { st, notice, ask, left: deadline - now };
125
+ }
126
+
127
+ /**
128
+ * Forget the page's one read, so the next line that mounts reads again: Settings → Web calls it when it shows (a revoke
129
+ * or a changed switch there changes what the line should say). Tests use it too: each case is its own page load.
130
+ */
131
+ export function resetWebAuthorization() { shared = null; answered = false; inflight = null; signedOut = false; }
132
+
133
+ /** The line under the composer: verified, waiting, or "Not verified" with the button (or why not, with the fix). */
134
+ export function AuthorizeChip() {
135
+ const a = useWebAuthorization();
136
+ if (guestPage()) return null;
137
+ if (publicLink()) return html`<p class="webauth-chip note" data-state="public">${tn("publicLink")}</p>`;
138
+ if (!a.st) return null;
139
+ return html`<div class="webauth-chip" data-state=${a.st.state}>${body(a)}${a.notice ? html` <span class="note" role="status">${a.notice}</span>` : null}</div>`;
140
+ }
141
+
142
+ function body(a) {
143
+ const st = a.st;
144
+ if (st.state === "verified") return html`<span class="webauth-ok">✓ ${tn("verified", clock(st.until))}</span>`;
145
+ if (st.state === "pending") return html`<span class="note">${tn("waiting", mmss(a.left))}</span>`;
146
+ if (st.reason === "not_authorized" || st.reason === "authorization_expired")
147
+ return html`<span class="note">${tn("notVerified")} ·</span> <button type="button" class="btn btn-sm" onClick=${a.ask}>${tn("authorize")}</button>`;
148
+ const why = REASONS.includes(st.reason) ? tn(`reason_${st.reason}`) : tn("reason_other");
149
+ // The fix, where there is one the person can take here: the switch lives in Settings → Web.
150
+ return html`<span class="note">${tn("notVerified")}: ${why}</span>${st.reason === "disabled" || st.reason === "loopback_only" ? html` <a href="/settings/web">${tn("settings")}</a>` : null}`;
151
+ }
@@ -55,6 +55,7 @@
55
55
  <div id="msg" role="alert" aria-live="polite"></div>
56
56
  <div class="hint" data-i18n="hint">No code? Send /web in General, then press a sign-in button to get one.</div>
57
57
  <details class="help"><summary data-i18n="nobot">No chat bot?</summary><p data-i18n="nobot_body">Ask the operator to run <code>agend web --code</code> on the AgEnD computer.</p></details>
58
+ <details class="help"><summary data-i18n="safe">Page looks wrong after signing in?</summary><p data-i18n="safe_body">Your custom CSS may be hiding something. Add ?safe=1 to the end of the address to load the page without it, then fix it or turn it off in Settings → Web appearance.</p></details>
58
59
  </form>
59
60
  <noscript>This page needs JavaScript to sign you in.</noscript>
60
61
  </main>
@@ -1,4 +1,4 @@
1
- html, body { margin: 0; height: 100%; background: #111; color: #ddd; font: 14px system-ui, sans-serif; }
1
+ html, body { margin: 0; height: 100%; background: #111; color: #ddd; font: 14px system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", emoji; }
2
2
  body { display: flex; flex-direction: column; }
3
3
  #bar { display: flex; align-items: center; gap: 12px; padding: 6px 10px; background: #1c1c1c; border-bottom: 1px solid #333; }
4
4
  #bar #title { font-weight: 600; }
@@ -6,18 +6,18 @@ body { display: flex; flex-direction: column; }
6
6
  #bar button { background: #402; color: #fbb; border: 1px solid #733; border-radius: 4px; padding: 3px 10px; cursor: pointer; }
7
7
  #gate { max-width: 560px; margin: 40px auto; padding: 0 16px; line-height: 1.5; }
8
8
  #gate form { display: flex; gap: 8px; margin-top: 12px; }
9
- #gate input { flex: 1; font: 16px ui-monospace, monospace; letter-spacing: 1px; padding: 8px; background: #000; color: #fff; border: 1px solid #555; border-radius: 4px; }
9
+ #gate input { flex: 1; font: 16px ui-monospace, monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", emoji; letter-spacing: 1px; padding: 8px; background: #000; color: #fff; border: 1px solid #555; border-radius: 4px; }
10
10
  #gate button { padding: 8px 16px; background: #246; color: #fff; border: 0; border-radius: 4px; cursor: pointer; }
11
11
  #gate-msg { color: #f88; min-height: 1.5em; }
12
12
  #term { flex: 1; min-height: 0; padding: 4px; overflow: hidden; } /* never drawn over the rows below it (#1242) */
13
13
  #code-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 10px; background: #1c1c1c; border-top: 1px solid #333; }
14
14
  #code-row[hidden] { display: none; }
15
- #code-row input { flex: 1; min-width: 12em; font: 16px ui-monospace, monospace; padding: 8px; background: #000; color: #fff; border: 1px solid #555; border-radius: 4px; }
15
+ #code-row input { flex: 1; min-width: 12em; font: 16px ui-monospace, monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", emoji; padding: 8px; background: #000; color: #fff; border: 1px solid #555; border-radius: 4px; }
16
16
  #code-row button { padding: 8px 16px; background: #246; color: #fff; border: 0; border-radius: 4px; cursor: pointer; }
17
17
  #code-msg { color: #9ad; min-height: 1.2em; }
18
18
  #code-hint { flex-basis: 100%; margin: 0; color: #aaa; font-size: 13px; }
19
19
  #keys { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px; background: #1c1c1c; border-top: 1px solid #333; }
20
- #keys button { background: #222; color: #ddd; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; font: 14px ui-monospace, monospace; cursor: pointer; }
20
+ #keys button { background: #222; color: #ddd; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; font: 14px ui-monospace, monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", emoji; cursor: pointer; }
21
21
  #done { padding: 10px; background: #1c1c1c; border-top: 1px solid #333; }
22
22
  #done.ok { color: #8d8; } #done.bad { color: #f88; }
23
23
  @media (min-width: 900px) { #keys { display: none; } }
@@ -34,6 +34,8 @@ export interface UsageMetric {
34
34
  export interface ProviderUsage {
35
35
  id: string;
36
36
  name: string;
37
+ /** #2014: the credential profile this row reads (its `name` ends in ` (<profile>)`); the page shows it on its own line. */
38
+ profile?: string;
37
39
  status: "ok" | "error" | "no-credentials";
38
40
  /** A non-metered subscription whose entitlement is unlimited (not 0/100%). */
39
41
  unlimited?: boolean;
@@ -155,6 +157,8 @@ interface AgyBucket {
155
157
  remainingFraction?: number;
156
158
  resetTime?: string;
157
159
  group?: string;
160
+ /** Not on the summary's wire: the model's `modelProvider` (MODEL_PROVIDER_GOOGLE …), copied on by agyModelBuckets. */
161
+ provider?: string;
158
162
  }
159
163
  interface AgyQuotaSummary {
160
164
  groups?: {
@@ -166,6 +170,28 @@ interface AgyQuotaSummary {
166
170
  /** The summary's buckets, each tagged with its group's name. The top-level list (deprecated in the proto) only counts
167
171
  * when no group carries any bucket. */
168
172
  export declare function agyQuotaBuckets(body: AgyQuotaSummary): AgyBucket[];
173
+ /** `fetchAvailableModels`: each model's own quota (agy's proto: ModelDetails.quotaInfo). */
174
+ interface AgyModels {
175
+ models?: Record<string, {
176
+ displayName?: string;
177
+ modelProvider?: string;
178
+ quotaInfo?: {
179
+ remainingFraction?: number;
180
+ resetTime?: string;
181
+ };
182
+ }>;
183
+ }
184
+ /**
185
+ * Each model's quota as a per-model bucket. Only models with a reset time count: agy's internal models (tab completion,
186
+ * `chat_…`) report a bare 1 with none, which is no limit anyone runs into.
187
+ */
188
+ export declare function agyModelBuckets(body: AgyModels): AgyBucket[];
189
+ /**
190
+ * The buckets to show: the summary's, then — for each pool the summary does not name — the models' own quotas
191
+ * (#2174). agy 1.4 (2026-10-19, this machine's live account) answered the summary with only the "Claude and GPT
192
+ * models" group while every Gemini model still had a quota of its own, so the Gemini pool vanished from the row.
193
+ */
194
+ export declare function agyUsageBuckets(summary: AgyQuotaSummary, models: AgyModels | null): AgyBucket[];
169
195
  /**
170
196
  * Pool the summary's buckets into one metric per pool and limit.
171
197
  *
@@ -197,6 +223,7 @@ interface AgyStoredToken {
197
223
  * uses) is read alongside `expiry` (RFC3339) for the same reason.
198
224
  */
199
225
  export declare function readAgyToken(file: unknown): AgyStoredToken;
226
+ export declare function fetchAntigravityUsage(): Promise<Omit<ProviderUsage, "id" | "name">>;
200
227
  /**
201
228
  * When the soonest of the still-available reset tickets expires (#1244): min(expires_at) over the tickets whose status
202
229
  * is "available", skipping ones with no expiry or one already past. null when there is none — or when the list cannot
@@ -221,6 +248,7 @@ export declare function fetchKiroUsage(storeHome?: string): Promise<Omit<Provide
221
248
  type UsageProvider = {
222
249
  id: string;
223
250
  name: string;
251
+ profile?: string;
224
252
  fetch: (storeHome?: string, scope?: ClaudeUsageScope) => Promise<Omit<ProviderUsage, "id" | "name">>;
225
253
  };
226
254
  type UsageFleetConfig = Parameters<typeof listConfiguredProfiles>[0];