@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
@@ -11,22 +11,33 @@ import { useLease } from "/assets/app-ctx.js";
11
11
  import { PanelHeader, setTitle } from "/assets/app-shell.js";
12
12
  import { HeaderTools } from "/assets/header-tools.js";
13
13
  import { renavigate } from "/assets/app-nav.js";
14
- import { chatPath, fleetPath, FLEET_TABS } from "/assets/app-route.js";
14
+ import { chatPath, fleetPath, FLEET_TABS, rememberFleetTab } from "/assets/app-route.js";
15
15
  import { Dialog } from "/assets/ui-dialog.js";
16
16
  import { Empty, ErrorState, Skeleton } from "/assets/ui-states.js";
17
17
  import { Icon } from "/assets/ui-icons.js";
18
18
  import { toast } from "/assets/ui-toast.js";
19
19
  import { confirmDialog } from "/assets/ui-confirm.js";
20
20
  import { confirmedWrite } from "./settings-confirm.js";
21
- import { OrgChart } from "./fleet-org.js";
21
+ import { OrgCanvas } from "./fleet-canvas.js";
22
22
  import { labelOf } from "/assets/instance-nav.js";
23
23
  import { CacheReport } from "./fleet-cache.js";
24
+ import { UsageTab } from "./fleet-usage.js";
25
+ import { BackendSelect, ModelSelect } from "./settings-dialogs.js";
26
+
27
+ /** #2015: one of the largest processes as text: "python3 18.1 GB (dev-a)". */
28
+ export function memoryHogText(p) {
29
+ const size = p.rssBytes >= 1024 ** 3 ? `${(p.rssBytes / 1024 ** 3).toFixed(1)} GB` : `${Math.round(p.rssBytes / 1024 ** 2)} MB`;
30
+ const owner = p.owner === "instance" && p.instance ? p.instance
31
+ : t(p.owner === "fleet" ? "fleet.hostMemory_ownerFleet" : p.owner === "tmux" ? "fleet.hostMemory_ownerTmux" : "fleet.hostMemory_ownerOther");
32
+ return `${p.name} ${size} (${owner})`;
33
+ }
24
34
 
25
35
  register("fleet", {
26
36
  en: {
27
37
  hostMemory_elevated: "Host memory pressure: elevated — new agents start one at a time, at least 5 s apart",
28
38
  hostMemory_critical: "Host memory pressure: critical — new agents wait for memory to recover",
29
- title: "Fleet", tasks: "Tasks", schedules: "Schedules", teams: "Teams", org: "Org chart", cache: "Cache", config: "Config",
39
+ hostMemory_top: "Largest: {0}", hostMemory_ownerFleet: "the fleet", hostMemory_ownerTmux: "tmux", hostMemory_ownerOther: "not an instance",
40
+ title: "Fleet", tasks: "Tasks", schedules: "Schedules", teams: "Teams", org: "Org chart", cache: "Cache", usage: "Usage", config: "Config",
30
41
  docs: "Docs", docsNewTab: "Docs (opens in a new tab)",
31
42
  newTask: "New task", newSchedule: "New schedule", newTeam: "New team",
32
43
  noTasks: "No tasks yet", noTasksHint: "Create one to coordinate fleet work.", noSchedules: "No schedules yet", noSchedulesHint: "Automate recurring work with cron.",
@@ -52,12 +63,13 @@ register("fleet", {
52
63
  newInstance: "New instance", directory: "Directory", directoryHint: "Optional — a workspace is created if empty", topic: "Name", topicHint: "From the directory name",
53
64
  topicRequired: "Name is required when Directory is empty", description: "Description", descriptionHint: "What this instance does", model: "Model", modelHint: "e.g. sonnet, opus, gpt-5",
54
65
  branch: "Branch (git worktree)", branchHint: "e.g. feature-x", tags: "Tags", tagsHint: "comma-separated, e.g. dev, review", systemPrompt: "System prompt", systemPromptHint: "Custom instructions for this instance",
55
- fleetDefault: "Use the fleet default", notInstalled: "(not installed)", deprecated: "(deprecated)", instanceCreated: "Instance created",
66
+ fleetDefault: "Use the fleet default", notInstalled: "(not installed)", notSignedIn: "(not signed in — use /login)", tooOld: "(needs {0} or newer)", unchecked: "(not checked yet)", deprecated: "(deprecated)", instanceCreated: "Instance created",
56
67
  },
57
68
  "zh-TW": {
58
69
  hostMemory_elevated: "主機記憶體壓力偏高:新的 agent 一次啟動一個,至少間隔 5 秒",
59
70
  hostMemory_critical: "主機記憶體壓力嚴重:新的 agent 會等記憶體恢復再啟動",
60
- title: "Fleet", tasks: "Tasks", schedules: "排程", teams: "Teams", org: "組織圖", cache: "快取", config: "設定",
71
+ hostMemory_top: "最大:{0}", hostMemory_ownerFleet: "fleet 本身", hostMemory_ownerTmux: "tmux", hostMemory_ownerOther: "不屬於任何 instance",
72
+ title: "Fleet", tasks: "Tasks", schedules: "排程", teams: "Teams", org: "組織圖", cache: "快取", usage: "用量", config: "設定",
61
73
  docs: "文件", docsNewTab: "文件(在新分頁開啟)",
62
74
  newTask: "新增 task", newSchedule: "新增排程", newTeam: "新增 team",
63
75
  noTasks: "還沒有 task", noTasksHint: "建立一個來協調 fleet 的工作。", noSchedules: "還沒有排程", noSchedulesHint: "用 cron 自動執行例行工作。",
@@ -82,7 +94,7 @@ register("fleet", {
82
94
  newInstance: "新增 instance", directory: "目錄", directoryHint: "可留空——留空會自動建立工作區", topic: "名稱", topicHint: "預設取自目錄名稱",
83
95
  topicRequired: "目錄留空時必須填寫名稱", description: "說明", descriptionHint: "這個 instance 做什麼", model: "模型", modelHint: "例如 sonnet、opus、gpt-5",
84
96
  branch: "分支(git worktree)", branchHint: "例如 feature-x", tags: "標籤", tagsHint: "以逗號分隔,例如 dev, review", systemPrompt: "System prompt", systemPromptHint: "這個 instance 的自訂指示",
85
- fleetDefault: "使用 fleet 預設", notInstalled: "(未安裝)", deprecated: "(已淘汰)", instanceCreated: "Instance 已建立",
97
+ fleetDefault: "使用 fleet 預設", notInstalled: "(未安裝)", notSignedIn: "(未登入,請用 /login)", tooOld: "(需要 {0} 以上版本)", unchecked: "(尚未檢查)", deprecated: "(已淘汰)", instanceCreated: "Instance 已建立",
86
98
  },
87
99
  });
88
100
 
@@ -91,7 +103,7 @@ const DOCS_SITE = "https://songsid.github.io/AgEnD";
91
103
  export function docsUrl(language = lang()) {
92
104
  return `${DOCS_SITE}${language === "zh-TW" ? "/zh-tw" : ""}/docs/web-guide/`;
93
105
  }
94
- const ICONS = { tasks: "tasks", schedules: "clock", teams: "team", org: "org", cache: "chart", config: "sliders" };
106
+ const ICONS = { tasks: "tasks", schedules: "clock", teams: "team", org: "org", cache: "chart", usage: "chart", config: "sliders" };
95
107
  const BACKENDS = ["claude-code", "codex", "opencode", "kiro-cli", "antigravity", "grok", "muse"];
96
108
 
97
109
  async function api(method, path, body, lease) {
@@ -130,15 +142,18 @@ export function FleetPanel({ route, navKey }) {
130
142
  const lease = useLease(navKey);
131
143
  const tab = route.tab;
132
144
  useEffect(() => { setTitle(`${t("fleet.title")} · ${t(`fleet.${tab}`)}`); }, [tab, navKey]);
133
- const Body = { tasks: Tasks, schedules: Schedules, teams: Teams, org: Org, cache: Cache, config: Config }[tab] || Tasks;
145
+ useEffect(() => { rememberFleetTab(tab); }, [tab]); // #2183: the sidebar's and the phone's Fleet reopen it
146
+ const Body = { tasks: Tasks, schedules: Schedules, teams: Teams, org: Org, cache: Cache, usage: UsageTab, config: Config }[tab] || Tasks;
134
147
  // #1685: host memory pressure slowing or holding new agents — shown here, where macOS's routine "elevated" is not a chat notice.
135
- const { hostMemory } = useStore(appStore);
136
- const sub = hostMemory ? html`<span class=${`status host-mem ${hostMemory}`} role="status"><span class=${`dot ${hostMemory === "critical" ? "bad" : "warn"}`} aria-hidden="true"></span>${t(`fleet.hostMemory_${hostMemory}`)}</span>` : null;
137
- return html`<div class="panel p-fleet">
148
+ const { hostMemory, hostMemoryTop } = useStore(appStore);
149
+ // #2015: and which processes hold the most, and whose — text only (never chat, #1712).
150
+ const top = hostMemory && hostMemoryTop?.length ? t("fleet.hostMemory_top", hostMemoryTop.map(memoryHogText).join(" · ")) : "";
151
+ const sub = hostMemory ? html`<span class=${`status host-mem ${hostMemory}`} role="status"><span class=${`dot ${hostMemory === "critical" ? "bad" : "warn"}`} aria-hidden="true"></span>${t(`fleet.hostMemory_${hostMemory}`)}${top ? html`<span class="host-mem-top">${top}</span>` : null}</span>` : null;
152
+ return html`<div data-effect-panel="fleet" class="panel p-fleet">
138
153
  <${PanelHeader} title=${t("fleet.title")} sub=${sub}><${DocsLink} /><${HeaderTools} navKey=${navKey} /></${PanelHeader}>
139
- <nav class="seg" aria-label=${t("fleet.title")}>${FLEET_TABS.map(k => html`<a key=${k} href=${fleetPath(k)} class=${`seg-item${k === tab ? " active" : ""}`}
154
+ <nav data-trust="panel-navigation" class="seg" aria-label=${t("fleet.title")}>${FLEET_TABS.map(k => html`<a key=${k} href=${fleetPath(k)} class=${`seg-item${k === tab ? " active" : ""}`}
140
155
  aria-current=${k === tab ? "page" : undefined}><${Icon} name=${ICONS[k]} size=${16} /><span>${t(`fleet.${k}`)}</span></a>`)}</nav>
141
- <div class="panel-body"><div class=${`col${tab === "org" || tab === "cache" ? " col-wide" : ""}`}><${Body} lease=${lease} /></div></div>
156
+ <div data-trust="panel-content" class="panel-body"><div class=${`col${tab === "org" || tab === "cache" || tab === "usage" ? " col-wide" : ""}`}><${Body} lease=${lease} /></div></div>
142
157
  </div>`;
143
158
  }
144
159
 
@@ -255,11 +270,11 @@ function Teams({ lease }) {
255
270
  ${editing ? html`<${CreateTeam} team=${editing} onClose=${() => setEditing(null)} onDone=${() => setVersion(v => v + 1)} />` : null}`;
256
271
  }
257
272
 
258
- /** #1389: the org chart — its structure read once per opening, its live state from the app store (fleet-org.js). */
273
+ /** #1389: the org chart — its structure read once per opening, its live state from the app store. #1918: a canvas, or the list (fleet-canvas.js). */
259
274
  function Org({ lease }) {
260
275
  const state = useLoad(lease, "/ui/org", 0);
261
276
  const app = useStore(appStore);
262
- return html`<${Loaded} state=${state}>${(org) => html`<${OrgChart} org=${org} app=${app} />`}</${Loaded}>`;
277
+ return html`<${Loaded} state=${state}>${(org) => html`<${OrgCanvas} org=${org} app=${app} lease=${lease} />`}</${Loaded}>`;
263
278
  }
264
279
 
265
280
  /** #1468: the prompt-cache analysis — one read per opening and per window chosen; Refresh reads again (fleet-cache.js). */
@@ -322,8 +337,7 @@ function Config({ lease }) {
322
337
  <div class="field"><label for="cfg-users">${t("fleet.allowedUsers")}</label><${Secret} id="cfg-users" value=${form.users} onInput=${set("users")} label=${t("fleet.allowedUsers")} />
323
338
  <p class="note">${t("fleet.allowedUsersNote")}</p></div></section>
324
339
  <section class="card"><h3>${t("fleet.defaults")}</h3>
325
- <label class="field"><span>${t("fleet.backend")}</span><select value=${form.backend} onChange=${set("backend")}>
326
- ${[...(BACKENDS.includes(form.backend) ? [] : [form.backend]), ...BACKENDS].map(b => html`<option key=${b} value=${b}>${b}</option>`)}</select></label></section>
340
+ <div class="field"><label for="cfg-be">${t("fleet.backend")}</label><${BackendSelect} id="cfg-be" value=${form.backend} onChange=${(v) => setForm({ ...form, backend: v })} /></div></section>
327
341
  <section class="card"><h3>${t("fleet.roots")}</h3>
328
342
  ${form.roots.map((r, i) => html`<div key=${i} class="field-row"><input value=${r} placeholder="/path/to/projects" aria-label=${t("fleet.roots")}
329
343
  onInput=${(e) => { const roots = [...form.roots]; roots[i] = e.target.value; setForm({ ...form, roots }); }} />
@@ -510,6 +524,8 @@ function CreateTeam({ onClose, onDone, team = null }) {
510
524
  export function CreateInstanceDialog({ onClose }) {
511
525
  const lease = useLease("create-instance");
512
526
  const [backends, setBackends] = useState(null);
527
+ // The backend "fleet default" means (#2098 review P3-3): its models are the ones the model list offers then.
528
+ const [defaultBackend, setDefaultBackend] = useState("claude-code");
513
529
  const [f, setF] = useState({ dir: "", topic: "", desc: "", backend: "", model: "", branch: "", tags: "", prompt: "" });
514
530
  useEffect(() => {
515
531
  (async () => {
@@ -518,6 +534,7 @@ export function CreateInstanceDialog({ onClose }) {
518
534
  if (!lease.current()) return;
519
535
  const list = bd.backends || [];
520
536
  const first = list.find(b => b.installed && !b.deprecated) || list.find(b => b.installed);
537
+ if (typeof bd.defaultBackend === "string" && bd.defaultBackend) setDefaultBackend(bd.defaultBackend);
521
538
  setBackends(list);
522
539
  setF(x => ({ ...x, backend: first ? first.name : "" }));
523
540
  } catch {
@@ -547,14 +564,23 @@ export function CreateInstanceDialog({ onClose }) {
547
564
  ${field(`${t("fleet.topic")}${f.dir.trim() ? "" : " *"}`, html`<input value=${f.topic} onInput=${set("topic")} placeholder=${t("fleet.topicHint")} />`)}
548
565
  ${field(t("fleet.description"), html`<input value=${f.desc} onInput=${set("desc")} placeholder=${t("fleet.descriptionHint")} />`)}
549
566
  ${field(t("fleet.backend"), backends ? html`<select value=${f.backend} onChange=${set("backend")}><option value="">${t("fleet.fleetDefault")}</option>
550
- ${backends.map(b => html`<option key=${b.name} value=${b.name} disabled=${!b.installed}>${b.name}${b.deprecated ? ` ${t("fleet.deprecated")}` : ""}${b.installed ? "" : ` ${t("fleet.notInstalled")}`}</option>`)}</select>` : html`<${Skeleton} lines=${1} />`)}
551
- ${field(t("fleet.model"), html`<input value=${f.model} onInput=${set("model")} placeholder=${t("fleet.modelHint")} />`)}
567
+ ${backends.map(b => html`<option key=${b.name} value=${b.name} disabled=${backendBlocked(b)}>${b.name}${b.deprecated ? ` ${t("fleet.deprecated")}` : ""}${backendNote(b)}</option>`)}</select>` : html`<${Skeleton} lines=${1} />`)}
568
+ ${field(t("fleet.model"), html`<${ModelSelect} value=${f.model} onChange=${(v) => setF(x => ({ ...x, model: v }))} backend=${f.backend || defaultBackend} inherit=${t("fleet.fleetDefault")} />`)}
552
569
  ${field(t("fleet.branch"), html`<input value=${f.branch} onInput=${set("branch")} placeholder=${t("fleet.branchHint")} />`)}
553
570
  ${field(t("fleet.tags"), html`<input value=${f.tags} onInput=${set("tags")} placeholder=${t("fleet.tagsHint")} />`)}
554
571
  ${field(t("fleet.systemPrompt"), html`<textarea rows="3" value=${f.prompt} onInput=${set("prompt")} placeholder=${t("fleet.systemPromptHint")}></textarea>`)}
555
572
  </${FormDialog}>`;
556
573
  }
557
574
 
575
+ // #2072: a backend this host cannot run is shown, disabled, with why; one not checked yet stays selectable.
576
+ const backendState = (b) => b.state ?? (b.installed ? "usable" : b.unknown ? "unknown" : "not_installed");
577
+ function backendBlocked(b) { return ["not_installed", "not_signed_in", "too_old"].includes(backendState(b)); }
578
+ function backendNote(b) {
579
+ const s = backendState(b);
580
+ return s === "not_installed" ? ` ${t("fleet.notInstalled")}` : s === "not_signed_in" ? ` ${t("fleet.notSignedIn")}`
581
+ : s === "too_old" ? ` ${t("fleet.tooOld", b.minVersion ?? "?")}` : s === "unknown" ? ` ${t("fleet.unchecked")}` : "";
582
+ }
583
+
558
584
  // ── Cron in words (#1724: in the page's language) ──
559
585
  /**
560
586
  * A cron expression as a person says it — "Weekdays 18:00", 「每週一到五 18:00」, "Every 15 minutes" — or the expression
@@ -200,10 +200,10 @@ export function LogPanel({ route, navKey }) {
200
200
  const name = route.instance;
201
201
  const inst = app.instances.find((i) => i.name === name) || null;
202
202
  useEffect(() => { setTitle(`${name} · ${t("app.log")}`); }, [name, navKey]);
203
- if (!app.ready) return html`<div class="panel p-log"><${PanelHeader} title=${name} /><div class="panel-body"><${Skeleton} lines=${6} /></div></div>`;
203
+ if (!app.ready) return html`<div data-effect-panel="log" class="panel p-log"><${PanelHeader} title=${name} /><div data-trust="panel-content" class="panel-body"><${Skeleton} lines=${6} /></div></div>`;
204
204
  if (!inst) {
205
- return html`<div class="panel p-log"><${PanelHeader} title=${name} />
206
- <div class="panel-body center"><${Empty} icon="alert" title=${t("chat.notFound", name)} hint=${t("chat.notFoundHint")}
205
+ return html`<div data-effect-panel="log" class="panel p-log"><${PanelHeader} title=${name} />
206
+ <div data-trust="panel-content" class="panel-body center"><${Empty} icon="alert" title=${t("chat.notFound", name)} hint=${t("chat.notFoundHint")}
207
207
  action=${html`<button type="button" class="btn only-narrow" onClick=${openDrawer}>${t("chat.backToList")}</button>`} /></div></div>`;
208
208
  }
209
209
  return html`<${LogView} key=${name} name=${name} inst=${inst} navKey=${navKey} exec=${app.exec[name]}
@@ -372,13 +372,13 @@ function LogView({ name, inst, navKey, exec, awaiting }) {
372
372
  ${items.length ? html`<ol class="log-list" role="log" aria-label=${t("app.log")}>${items.map((x, i) => html`<${Row} key=${`${x.kind}:${x.id}`} x=${x} prev=${items[i - 1]} />`)}</ol>`
373
373
  : html`<p class="note log-empty">${tl("empty")}</p>`}`;
374
374
  }
375
- const tools = state.phase === "on" ? html`<div class="log-bar" role="group" aria-label=${tl("filter")}>
375
+ const tools = state.phase === "on" ? html`<div data-trust="panel-controls" class="log-bar" role="group" aria-label=${tl("filter")}>
376
376
  ${["all", "tools", "errors"].map((k) => html`<button key=${k} type="button" class=${`chip-btn${filter === k ? " on" : ""}`} aria-pressed=${filter === k ? "true" : "false"} onClick=${() => setFilter(k)}>${tl(k)}</button>`)}
377
377
  <span class="grow"></span><span class="log-live" title=${tl("live")}><span class="dot running" aria-hidden="true"></span>${tl("live")}</span></div>` : null;
378
- return html`<div class="panel p-log">
378
+ return html`<div data-effect-panel="log" class="panel p-log">
379
379
  <${PanelHeader} title=${name} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="log" />`}><${HeaderTools} navKey=${navKey} /><${HeaderSlot} /></${PanelHeader}>
380
380
  ${tools}
381
- <div class="panel-body log-body" ref=${scroller} onScroll=${onScroll}><div class="col log-col">${content}</div></div>
381
+ <div data-trust="panel-content" class="panel-body log-body" ref=${scroller} onScroll=${onScroll}><div class="col log-col">${content}</div></div>
382
382
  </div>`;
383
383
  }
384
384
 
@@ -123,9 +123,9 @@ export function NeedsPanel({ navKey }) {
123
123
  useEffect(() => { if (owner) return owner.subscribe(() => tick((n) => n + 1)); return undefined; }, [owner, lease]);
124
124
  const groups = groupNeeds(items);
125
125
  const nowMs = Date.now();
126
- return html`<div class="panel p-needs">
126
+ return html`<div data-effect-panel="needs" class="panel p-needs">
127
127
  <${PanelHeader} title=${tn("title")} sub=${items.length ? t("app.needsCount", items.length) : null} />
128
- <div class="panel-body"><div class="col">
128
+ <div data-trust="panel-content" class="panel-body"><div class="col">
129
129
  ${!items.length ? html`<${Empty} icon="check" title=${tn("empty")} hint=${tn(chatApp ? "emptyHint" : "emptyHintWeb")} />`
130
130
  : groups.map((g) => html`<section key=${g.instance} class="n-group" aria-label=${g.instance}>
131
131
  <h2 class="n-group-head"><a href=${chatPath(g.instance)} title=${g.instance}>${nameFor(g.instance)}</a></h2>
@@ -175,7 +175,7 @@ function NeedsItem({ item, nowMs, owner }) {
175
175
  setClaim("acks", item.id, res && res.ok ? "done" : null);
176
176
  toast((body && (body.message || body.error)) || t("app.failed"), !!(res && res.ok));
177
177
  };
178
- return html`<article class=${`n-item${BAD.has(item.reason) ? " bad" : ""}`}>
178
+ return html`<article data-trust="permission" class=${`n-item${BAD.has(item.reason) ? " bad" : ""}`}>
179
179
  <span class="n-icon"><${Icon} name=${ICON[item.type] || "alert"} size=${18} /></span>
180
180
  <div class="n-body">
181
181
  <div class="n-title"><strong>${reasonText(item)}</strong><span class="n-age">${age(item.since, nowMs)}</span></div>
@@ -20,6 +20,7 @@ import { Empty, ErrorState, Skeleton } from "/assets/ui-states.js";
20
20
  import { Icon } from "/assets/ui-icons.js";
21
21
  import { Menu } from "/assets/ui-menu.js";
22
22
  import { toast } from "/assets/ui-toast.js";
23
+ import { GroupMark, groupLabel, groupOf as navGroupOf, modelText } from "/assets/instance-nav.js";
23
24
  import "./settings-strings.js";
24
25
  import { api, attach, confirmedWrite, newKey } from "./settings-confirm.js";
25
26
  import { dismissOperation, operationActive, restartFleet, startOperation, takeLeftover } from "./settings-apply.js";
@@ -30,7 +31,7 @@ import {
30
31
  visibilityDefault, worstImpact,
31
32
  } from "./settings-model.js";
32
33
  import {
33
- AgentDialog, BackendSelect, BotDialog, ChipList, ClassicDialog, Drawer, Feedback, Impact, Label, NewBotDialog, Override,
34
+ AgentDialog, BackendSelect, BotDialog, ChipList, ClassicDialog, Drawer, Feedback, Impact, Label, ModelSelect, NewBotDialog, Override,
34
35
  Select, ask, impactText,
35
36
  } from "./settings-dialogs.js";
36
37
  import { SetupWizard } from "./settings-wizard.js";
@@ -41,9 +42,16 @@ import { takeAgentRequest } from "./settings-request.js";
41
42
  import { setPreviewOptIn } from "./panel-chat.js";
42
43
  import { startTour } from "./chat-tour.js";
43
44
  import "./chat-strings.js";
45
+ import { AppearanceSection } from "./settings-appearance.js";
46
+ import { GuestsCard } from "./settings-guests.js";
47
+ import { WebAuthorityCard } from "./settings-web-authority.js";
44
48
 
45
49
  const tn = (k, ...v) => t(`settings.${k}`, ...v);
46
- const ICONS = { device: "user", agents: "bot", bots: "plug", classic: "room", general: "sliders", web: "globe", advanced: "code" };
50
+ /** What a refused change says to a person: the server's code in words (the code itself only for one not listed). */
51
+ const APPLY_ERRORS = new Set(["unsupported_sensitive_effect", "confirmation_diff_too_large", "session_required", "admin_required", "settings_changed",
52
+ "request_no_longer_current", "too_many_pending_changes", "fleet_unavailable", "invalid_settings_effect", "settings_confirmation_unavailable", "failed"]);
53
+ export const applyErrorText = (code) => (APPLY_ERRORS.has(code) ? tn(`applyError_${code}`) : tn("applyErrorOther", String(code)));
54
+ const ICONS = { device: "user", appearance: "palette", agents: "bot", bots: "plug", classic: "room", general: "sliders", web: "globe", advanced: "code" };
47
55
  const RELEASES = "https://github.com/songsid/AgEnD/releases";
48
56
 
49
57
  /** GET under the panel's lease → { ok, status, body }. */
@@ -86,7 +94,7 @@ async function loadLive(lease) {
86
94
  if (!lease.current()) return null;
87
95
  const live = {}, classicLive = {};
88
96
  if (f.ok && f.body && Array.isArray(f.body.instances)) for (const i of f.body.instances) {
89
- live[i.name] = { status: i.status, state: i.state, backend: i.backend, model: i.model, model_display: i.model_display, effort: i.effort, effort_supported: i.effort_supported };
97
+ live[i.name] = { status: i.status, state: i.state, backend: i.backend, model: i.model, model_display: modelText(i, i.model_display), effort: i.effort, effort_supported: i.effort_supported };
90
98
  if (i.classic) classicLive[i.name] = { status: i.status, state: i.state };
91
99
  }
92
100
  // /api/fleet carries live state for ClassicBot rooms; the profiles are the fallback for one not listed there yet.
@@ -224,6 +232,8 @@ export function SettingsPanel({ route, navKey }) {
224
232
  }, [!!ctx, navKey]);
225
233
  // #1604: This device needs no configuration: shown at once, whatever the configuration read does.
226
234
  const sectionBody = section === "device" ? html`<${DeviceSection} />`
235
+ // #1629 r4 §3: Appearance is the person's own (no configuration read): shown at once, like Preferences.
236
+ : section === "appearance" ? html`<${AppearanceSection} />`
227
237
  : !data ? html`<${Skeleton} lines=${6} />`
228
238
  : data.error ? html`<${ErrorState} message=${tn("configLoadFailed")} onRetry=${reload} />`
229
239
  : section === "agents" ? html`<${Agents} ctx=${ctx} search=${search} openDialog=${(d) => setDialog({ ...d, key: navKey })} />`
@@ -236,14 +246,14 @@ export function SettingsPanel({ route, navKey }) {
236
246
  const open = dialog && dialog.key === navKey ? dialog : null;
237
247
  const close = () => setDialog(null);
238
248
  const searchable = section === "agents" || section === "bots" || section === "classic";
239
- return html`<div class="panel p-settings">
249
+ return html`<div data-effect-panel="settings" class="panel p-settings">
240
250
  <${PanelHeader} title=${tn("title")} sub=${data && data.version ? html`<a class="pill ver" href=${data.version.includes("-") ? `${RELEASES}/latest` : `${RELEASES}/tag/v${data.version}`} target="_blank" rel="noopener">v${data.version}</a>` : null}>
241
251
  <button type="button" class="btn btn-ghost btn-sm" disabled=${!ctx} onClick=${() => setDialog({ kind: "wizard", key: navKey })}><${Icon} name="wand" size=${16} /><span class="hide-narrow">${tn("wizardButton")}</span></button>
242
252
  <button type="button" class="icon-btn" aria-label=${tn("helpButton")} title=${tn("helpButton")} onClick=${() => setDialog({ kind: "help", key: navKey })}><${Icon} name="info" /></button>
243
253
  </${PanelHeader}>
244
- <nav class="seg" aria-label=${tn("title")}>${SETTINGS_SECTIONS.map((k) => html`<a key=${k} href=${settingsPath(k)} class=${`seg-item${k === section ? " active" : ""}`}
254
+ <nav data-trust="panel-navigation" class="seg" aria-label=${tn("title")}>${SETTINGS_SECTIONS.map((k) => html`<a key=${k} href=${settingsPath(k)} class=${`seg-item${k === section ? " active" : ""}`}
245
255
  aria-current=${k === section ? "page" : undefined}><${Icon} name=${ICONS[k]} size=${16} /><span>${tn(`sec_${k}`)}</span></a>`)}</nav>
246
- <div class="panel-body"><div class="col">
256
+ <div data-trust="panel-content" class="panel-body"><div class="col">
247
257
  ${searchable ? html`<div class="s-toolbar"><label class="s-search"><${Icon} name="search" size=${14} /><span class="sr-only">${tn("searchAll")}</span>
248
258
  <input type="search" value=${search} placeholder=${tn("searchAll")} onInput=${(e) => setSearch(e.target.value)} /></label></div>` : null}
249
259
  ${op ? html`<${OperationCard} op=${op} schema=${data && data.schema} />` : null}
@@ -454,7 +464,9 @@ function Agents({ ctx, search, openDialog }) {
454
464
  const [collapsed, setCollapsed] = useState(() => new Set());
455
465
  const [busy, setBusy] = useState(() => new Map()); // name → the action under way
456
466
  const chs = channelsOf(ctx.fleet);
457
- const groups = groupAgents(insts, ctx.live, search);
467
+ // #2172: the sidebar's grouping — an agent in a team under its team, else under its first tag.
468
+ const teamsOf = (name) => Object.keys(ctx.fleet.teams || {}).filter((t) => Array.isArray(ctx.fleet.teams[t]?.members) && ctx.fleet.teams[t].members.includes(name));
469
+ const groups = groupAgents(insts, ctx.live, search, (name) => navGroupOf({ tags: insts[name]?.tags, teams: teamsOf(name) }), groupLabel);
458
470
  // One action per agent at a time: the whole row is off (Settings and ⋯ too), and the pressed button says it is working.
459
471
  const run = async (name, action, work) => {
460
472
  if (busy.has(name)) return;
@@ -470,7 +482,7 @@ function Agents({ ctx, search, openDialog }) {
470
482
  return html`<section key=${group} class="s-group">
471
483
  <button type="button" class="s-group-head" aria-expanded=${folded ? "false" : "true"}
472
484
  onClick=${() => setCollapsed((c) => { const n = new Set(c); n.has(group) ? n.delete(group) : n.add(group); return n; })}>
473
- <${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><span class="grow">${group}</span><span class="count">${names.length}</span></button>
485
+ <${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><${GroupMark} group=${group} /><span class="grow">${groupLabel(group)}</span><span class="count">${names.length}</span></button>
474
486
  ${folded ? null : names.map((name) => {
475
487
  const inst = insts[name];
476
488
  const st = (ctx.live[name] && ctx.live[name].status) || "stopped";
@@ -499,7 +511,30 @@ function Agents({ ctx, search, openDialog }) {
499
511
  })}</div>`;
500
512
  }
501
513
 
514
+ function ConnectionRetry({ id, ctx }) {
515
+ const lease = useLease(`connection-retry:${id}`);
516
+ const claimed = useRef(false);
517
+ const [busy, setBusy] = useState(false);
518
+ const retry = async () => {
519
+ if (claimed.current) return;
520
+ claimed.current = true; setBusy(true);
521
+ try {
522
+ const response = await lease.fetch(`/api/settings/connections/${encodeURIComponent(id)}/retry`, { method: "POST" });
523
+ if (!lease.current()) return;
524
+ toast(response.ok ? tn("connRetryQueued") : tn("connRetryFailed"));
525
+ await ctx.reload();
526
+ } catch {
527
+ if (lease.current()) toast(tn("connRetryFailed"));
528
+ } finally {
529
+ claimed.current = false;
530
+ if (lease.current()) setBusy(false);
531
+ }
532
+ };
533
+ return html`<button type="button" class="btn btn-sm" disabled=${busy} onClick=${retry}>${tn("connRetryNow")}</button>`;
534
+ }
535
+
502
536
  function Bots({ ctx, search, openDialog }) {
537
+ const overPublicLink = globalThis.document?.body?.dataset?.publicLink === "1";
503
538
  const all = channelsOf(ctx.fleet);
504
539
  const needle = search.trim().toLowerCase();
505
540
  const rows = all.map((ch, i) => ({ ch, i })).filter(({ ch }) => !needle || [ch.id, ch.type, ch.group_id, ch.bot_token_env].some((v) => String(v ?? "").toLowerCase().includes(needle)));
@@ -528,6 +563,7 @@ function Bots({ ctx, search, openDialog }) {
528
563
  <span class="s-actions">
529
564
  <span class=${`tag${token ? "" : " warn"}`}>${token ? tn("tokenConfigured") : tn("tokenMissing")}</span>
530
565
  <span class=${`s-state ${state.cls}`} title=${stateText}>${stateText}</span>
566
+ ${!overPublicLink && ctx.fleetUp && meta.status !== "connected" ? html`<${ConnectionRetry} id=${channelId(ch, i)} ctx=${ctx} />` : null}
531
567
  <button type="button" class="btn btn-sm" onClick=${() => openDialog({ kind: "bot", id: channelId(ch, i) })}>${tn("settingsButton")}</button></span>
532
568
  </div>`;
533
569
  })}</div>`}
@@ -703,7 +739,8 @@ function General({ ctx }) {
703
739
  <div class="field">${L(tn("language"), "defaults.locale", "g-locale")}<${Select} id="g-locale" value=${f.locale} onChange=${set("locale")}
704
740
  options=${localeOptions(f.locale).map((o) => (o.auto ? { value: "", label: tn("localeAuto") } : o))} /></div>
705
741
  <div class="field">${L(tn("defaultBackend"), "defaults.backend", "g-be")}<${BackendSelect} id="g-be" value=${f.backend} onChange=${set("backend")} /></div>
706
- <div class="field">${L(tn("defaultModel"), "defaults.model", "g-model")}<input id="g-model" type="text" placeholder="e.g. claude-opus-4-8" value=${f.model} onInput=${(e) => set("model")(e.target.value)} /></div>
742
+ <div class="field">${L(tn("defaultModel"), "defaults.model", "g-model")}<${ModelSelect} id="g-model" value=${f.model} onChange=${set("model")}
743
+ backend=${f.backend || "claude-code"} inherit=${tn("modelCliDefault")} /></div>
707
744
  <div class="field">${L(`${tn("autoPause")} (${tn("minutes")})`, "defaults.auto_pause_after", "g-auto")}<input id="g-auto" type="number" min="0" step="1" class=${errors.auto ? "invalid" : ""} value=${f.auto} onInput=${(e) => set("auto")(e.target.value)} /><${Feedback} error=${errors.auto} /></div>
708
745
  <div class="field">${L(tn("toolProgress"), "defaults.tool_progress", "g-tp")}<${Select} id="g-tp" value=${f.tool_progress} onChange=${set("tool_progress")} options=${TOOL_PROGRESS} /></div>
709
746
  <div class="field">${L(tn("crossInstanceVisibility"), "defaults.cross_instance_visibility", "g-vis")}<${Select} id="g-vis" value=${f.visibility} onChange=${set("visibility")} options=${VISIBILITY_MODES} />
@@ -766,17 +803,20 @@ export const WEB_READ_ONLY = [
766
803
  function WebSection({ ctx, staged }) {
767
804
  const { schema } = ctx;
768
805
  const web = ctx.fleet.web || {}, local = (web.session && web.session.local) || {}, term = ctx.fleet.web_terminal || {};
806
+ // Over the public link the fields read only; the Guests and Web authority cards are not offered (the server never
807
+ // serves their reads there).
769
808
  const overPublicLink = globalThis.document?.body?.dataset?.publicLink === "1";
770
809
  const original = useMemo(() => ({
771
810
  echo: web.echo_to_channel ?? true,
772
811
  log: web.log_tab === true, // #1218: off by default
812
+ usagePub: web.usage_report_on_public_link === true, // #2122: off by default
773
813
  pub: { allow_public: (web.public_link && web.public_link.allow_public) ?? true, ttl_minutes: (web.public_link && web.public_link.ttl_minutes) ?? 120, protocol: (web.public_link && web.public_link.protocol) ?? "http2" },
774
814
  session: { absolute_hours: local.absolute_hours ?? null, idle_hours: local.idle_hours ?? null },
775
815
  terminal: { enabled: term.enabled ?? null, ttl_minutes: term.ttl_minutes ?? null },
776
816
  }), []);
777
817
  const text = (v) => (v === null || v === undefined ? "" : String(v));
778
818
  const [f, setF] = useState(() => ({
779
- echo: original.echo, log: original.log, pub: original.pub.allow_public, ttl: String(original.pub.ttl_minutes), protocol: original.pub.protocol,
819
+ echo: original.echo, log: original.log, usagePub: original.usagePub, pub: original.pub.allow_public, ttl: String(original.pub.ttl_minutes), protocol: original.pub.protocol,
780
820
  abs: text(original.session.absolute_hours), idle: text(original.session.idle_hours),
781
821
  termOn: original.terminal.enabled !== false, termTtl: text(original.terminal.ttl_minutes),
782
822
  }));
@@ -805,6 +845,7 @@ function WebSection({ ctx, staged }) {
805
845
  const body = {
806
846
  ...(f.echo !== original.echo ? { echo_to_channel: f.echo } : {}),
807
847
  ...(f.log !== original.log ? { log_tab: f.log } : {}),
848
+ ...(f.usagePub !== original.usagePub ? { usage_report_on_public_link: f.usagePub } : {}),
808
849
  ...(Object.keys(publicPatch).length ? { public_link: publicPatch } : {}),
809
850
  ...(Object.keys(sessionPatch).length ? { session: { local: sessionPatch } } : {}),
810
851
  ...(Object.keys(terminalPatch).length ? { web_terminal: terminalPatch } : {}),
@@ -812,6 +853,7 @@ function WebSection({ ctx, staged }) {
812
853
  const fields = [
813
854
  ...(body.echo_to_channel !== undefined ? ["web.echo_to_channel"] : []),
814
855
  ...(body.log_tab !== undefined ? ["web.log_tab"] : []),
856
+ ...(body.usage_report_on_public_link !== undefined ? ["web.usage_report_on_public_link"] : []),
815
857
  ...Object.keys(publicPatch).map((k) => `web.public_link.${k}`),
816
858
  ...Object.keys(sessionPatch).map((k) => `web.session.local.${k}`),
817
859
  ...Object.keys(terminalPatch).map((k) => `web_terminal.${k}`),
@@ -853,6 +895,8 @@ function WebSection({ ctx, staged }) {
853
895
  <p class="note">${tn("webEchoHint")}</p>
854
896
  <label class="check"><input id="webLogTab" type="checkbox" checked=${f.log} onChange=${(e) => set("log")(e.target.checked)} /><span>${tn("webLogTab")}</span><${Impact} schema=${schema} field="web.log_tab" /></label>
855
897
  <p class="note">${tn("webLogTabHint")}</p>
898
+ <label class="check"><input id="webUsagePublic" type="checkbox" checked=${f.usagePub} onChange=${(e) => set("usagePub")(e.target.checked)} /><span>${tn("webUsagePublic")}</span><${Impact} schema=${schema} field="web.usage_report_on_public_link" /></label>
899
+ <p class="note">${tn("webUsagePublicHint")}</p>
856
900
  <label class="check"><input id="publicWebLink" type="checkbox" checked=${f.pub} onChange=${(e) => set("pub")(e.target.checked)} /><span>${tn("publicLink")}</span><${Impact} schema=${schema} field="web.public_link.allow_public" /></label>
857
901
  <p class="note">${tn("publicLinkHint")}</p>
858
902
  <div class="grid2">
@@ -861,6 +905,7 @@ function WebSection({ ctx, staged }) {
861
905
  </div>
862
906
  </section>
863
907
  </fieldset>
908
+ ${overPublicLink ? null : html`<${GuestsCard} ctx=${ctx} staged=${staged} /><${WebAuthorityCard} ctx=${ctx} staged=${staged} />`}
864
909
  <section class="card s-web-readonly"><h3>${tn("webReadOnlyTitle")}</h3>
865
910
  <p class="note">${tn("webReadOnlyLead")}</p>
866
911
  <dl class="s-web-ro">${WEB_READ_ONLY.map((k) => html`<div key=${k}><dt><code>${k}</code></dt><dd>${shown(k)}</dd></div>`)}</dl>
@@ -915,7 +960,7 @@ function Developer({ ctx }) {
915
960
  /** Staged, not applied: the count, what it will cost, Discard and Apply. */
916
961
  function PendingBar({ staged, busy, onApply, onDiscard }) {
917
962
  const kinds = [...new Set([...staged.values()].map((c) => c.impact))];
918
- return html`<div class="s-pending" role="region" aria-label=${tn("pendingRegion")}>
963
+ return html`<div data-trust="panel-controls" class="s-pending" role="region" aria-label=${tn("pendingRegion")}>
919
964
  <strong>${tn("changesPending", staged.size)}</strong>
920
965
  <span class="note">${kinds.map(impactText).join(" · ")}</span>
921
966
  <span class="grow"></span>
@@ -938,7 +983,7 @@ function OperationCard({ op, schema }) {
938
983
  const mismatch = schema && schema.fleet_signature_mismatch;
939
984
  const title = op.phase === "failed" ? tn("applyPartial") : fleetRestarting ? tn("restartFleetTitle") : busy ? tn("applying")
940
985
  : failedRows ? tn("applyPartial") : needsRestart ? tn("applyRestartNeeded") : tn("applyDone");
941
- const note = op.phase === "failed" ? (op.error === "busy" ? tn("applyBusy") : op.error ? tn("applyStepFailed", op.error) : "")
986
+ const note = op.phase === "failed" ? (op.error === "busy" ? tn("applyBusy") : op.error ? tn("applyStepFailed", applyErrorText(op.error)) : "")
942
987
  : fleetRestarting ? tn("restartFleetWaiting") : op.lostJob ? tn("applyLostJob")
943
988
  : job && job.overdue ? tn("applyStillWorking", Math.round((job.elapsed_ms || 0) / 1000))
944
989
  : mismatch && needsRestart ? tn("signatureMismatch", mismatch.join(", ")) : needsRestart ? tn("applyRestartHint") : "";
@@ -965,7 +1010,7 @@ function OperationCard({ op, schema }) {
965
1010
  ${op.steps.length ? html`<ul class="s-op-steps">${op.steps.map((s, i) => html`<li key=${i} class=${`s-step ${s.status}`}>
966
1011
  <span class="s-step-icon" aria-hidden="true">${icon(s.status) ? html`<${Icon} name=${icon(s.status)} size=${14} />` : "·"}</span>
967
1012
  <span class="grow">${s.label}</span>
968
- <span class="note">${s.status === "waiting" ? tn("stepWaiting") : s.status === "skipped" ? tn("stepSkipped") : s.status === "failed" ? (s.error || tn("applyFailed")) : s.note || impactText(s.impact)}</span></li>`)}</ul>` : null}
1013
+ <span class="note">${s.status === "waiting" ? tn("stepWaiting") : s.status === "skipped" ? tn("stepSkipped") : s.status === "failed" ? (s.error ? applyErrorText(s.error) : tn("applyFailed")) : s.note || impactText(s.impact)}</span></li>`)}</ul>` : null}
969
1014
  ${rows.length ? html`<ul class="s-op-steps">${rows.map((r) => html`<li key=${r.target} class=${`s-step ${r.status}`}>
970
1015
  <span class="s-step-icon" aria-hidden="true">${r.status === "done" ? html`<${Icon} name="check" size=${14} />` : r.status === "failed" ? html`<${Icon} name="alert" size=${14} />` : r.status === "restart-required" ? html`<${Icon} name="restart" size=${14} />` : "·"}</span>
971
1016
  <span class="grow">${r.target === "fleet" ? "AgEnD" : shortName(r.target, {})}</span>
@@ -16,6 +16,7 @@ import { Icon } from "/assets/ui-icons.js";
16
16
  import "./chat-render.js";
17
17
  import "./preview.js";
18
18
  import { loadHtmlAttachment } from "./html-attachment.js";
19
+ import { EffectTrialPanel } from "./effect-trial-panel.js";
19
20
  import { chatTime, onDayChange } from "./chat-time.js";
20
21
 
21
22
  const R = () => globalThis.AgendChatRender;
@@ -43,14 +44,25 @@ export const panelClaims = () => claims;
43
44
  /** The frame's key in preview.js: its own, so the card that opened it keeps its own state. */
44
45
  export const frameKey = (key) => `panel:${key}`;
45
46
  /** Show `spec` in the panel; `run`: start it there (the card's "Open in panel" click). The block shown before stops. */
47
+ function retire(open) {
48
+ if (!open) return;
49
+ if (open.kind === "trial") { try { open.handle.stop(); } catch { /* host controller owns cleanup */ } }
50
+ else if (P()) P().stop(frameKey(open.key), "stopped", "");
51
+ }
46
52
  export function openPanel(spec, { run = false } = {}) {
47
- if (state.open && P()) P().stop(frameKey(state.open.key), "stopped", "");
48
- set({ open: { ...spec }, run: run ? ++runs : 0 });
53
+ const old = state.open;
54
+ set({ open: { ...spec, kind: "html" }, run: run ? ++runs : 0 });
55
+ retire(old); // State changes first: cleanup reentry cannot close a newer panel.
56
+ }
57
+ /** A checked controller handle enters the existing component slot; no HTML run token is issued. */
58
+ export function openTrialPanel(spec) {
59
+ const old = state.open;
60
+ set({ open: { ...spec, kind: "trial" }, run: 0 }); retire(old);
49
61
  }
50
- /** Close the panel: its frame stops first. */
62
+ /** Close publishes the empty slot before old-owner callbacks can reenter. */
51
63
  export function closePanel() {
52
- if (state.open && P()) P().stop(frameKey(state.open.key), "stopped", "");
53
- if (state.open) set({ open: null, run: 0 });
64
+ const old = state.open; if (!old) return;
65
+ set({ open: null, run: 0 }); retire(old);
54
66
  }
55
67
  /** The card key the panel shows, or null. */
56
68
  export const shownKey = () => (state.open ? state.open.key : null);
@@ -67,7 +79,7 @@ export function htmlTitle(code) {
67
79
  * without one, the newest block does. `msgs` in order; `keyOf` makes a message's key.
68
80
  */
69
81
  export function newerVersion(open, msgs, keyOf) {
70
- if (!open || !Array.isArray(msgs)) return null;
82
+ if (!open || open.kind === "trial" || !Array.isArray(msgs)) return null;
71
83
  const at = msgs.findIndex(x => keyOf(x) === open.msgKey);
72
84
  if (at < 0) return null;
73
85
  // An attached file: a later agent reply attaching a file of the same name is its newer version. Its HTML is read
@@ -132,7 +144,8 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
132
144
  const pending = useRef(0); // #1704: the card's click, waiting for this device's Allow (the st.run it came with)
133
145
  const ownAllow = useRef(0); // while this panel's own Allow commits its choice: the click it answers (not cancelled by it)
134
146
  const holder = useRef(null), divider = useRef(null), panel = useRef(null);
135
- const open = st.open && st.open.instance === name ? st.open : null;
147
+ const trial = st.open?.kind === "trial" && st.open.instance === name ? st.open : null;
148
+ const open = st.open?.kind !== "trial" && st.open?.instance === name ? st.open : null;
136
149
 
137
150
  useEffect(() => panelStore.subscribe(setSt), []);
138
151
  // This device's choice (here or in another tab) decides whether Preview is offered: re-read it when it changes.
@@ -231,6 +244,9 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
231
244
  // Esc in the panel closes it (and is not the chat's "stop the reply").
232
245
  const onPanelKey = (e) => { if (e.key === "Escape" && !e.isComposing) { e.preventDefault(); closePanel(); } };
233
246
 
247
+ if (trial) return html`<${EffectTrialPanel} binding=${trial.binding} title=${trial.handle.reply.offer.title}
248
+ current=${trial.handle.current} ownerCss=${trial.ownerCss} onStage=${trial.onStage}
249
+ onStop=${trial.handle.stop} onClose=${() => { if (panelStore.get().open === trial) closePanel(); }} />`;
234
250
  if (!open) return null;
235
251
  const going = run.state === "starting" || run.state === "running";
236
252
  const a = P().availability();
@@ -250,9 +266,9 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
250
266
  if (want && pending.current === want && panelStore.get().run === want) start();
251
267
  };
252
268
  const source = t("chat.pvSource", open.sender || name, timeOf(open.ts));
253
- return html`<div class="pv-divider" ref=${divider} role="separator" aria-orientation="vertical" tabindex="0" aria-label=${t("chat.pvResize")}
269
+ return html`<div data-trust="preview" class="pv-divider" ref=${divider} role="separator" aria-orientation="vertical" tabindex="0" aria-label=${t("chat.pvResize")}
254
270
  aria-valuemin=${String(MIN_PANEL)} onKeyDown=${onKey} onPointerDown=${onDown}></div>
255
- <aside class="pv-panel" ref=${panel} aria-label=${t("chat.pvPanel")} onKeyDown=${onPanelKey}>
271
+ <aside data-trust="preview" class="pv-panel" ref=${panel} aria-label=${t("chat.pvPanel")} onKeyDown=${onPanelKey}>
256
272
  <div class="pv-panel-head">
257
273
  <button type="button" class="icon-btn only-narrow pv-panel-close" title=${t("chat.pvBack")} aria-label=${t("chat.pvBack")} onClick=${closePanel}><${Icon} name="back" size=${18} /></button>
258
274
  <div class="pv-panel-title"><strong>${t("chat.pvPanel")}</strong>
@@ -100,7 +100,7 @@
100
100
  notify();
101
101
  return true;
102
102
  }
103
- if (j && (j.code === "publicLinkPreviewFailed" || j.code === "publicLink" || j.code === "fleetOff")) {
103
+ if (j && (j.code === "publicLinkPreviewFailed" || j.code === "publicLink" || j.code === "fleetOff" || j.code === "listenFailed" || j.code === "notListening")) {
104
104
  cfg.reasonCode = j.code; cfg.reason = ""; cfg.detail = typeof j.detail === "string" ? j.detail : "";
105
105
  notify();
106
106
  return true;
@@ -191,7 +191,8 @@
191
191
  if (!cfg.previewOrigin || !cfg.boot) {
192
192
  // The server's reason, in the page's language when the server named it (a code), else as the server wrote it.
193
193
  // #1723: a failed public-link preview names why ({0} is the reason, in the page's words).
194
- var arg = cfg.reasonCode === "publicLinkPreviewFailed" ? say("pvFail_" + (cfg.detail || "unknown"), Object.prototype.hasOwnProperty.call(FAIL_EN, cfg.detail) ? FAIL_EN[cfg.detail] : "an unexpected error") : cfg.dashboardOrigin;
194
+ var arg = cfg.reasonCode === "publicLinkPreviewFailed" ? say("pvFail_" + (cfg.detail || "unknown"), Object.prototype.hasOwnProperty.call(FAIL_EN, cfg.detail) ? FAIL_EN[cfg.detail] : "an unexpected error")
195
+ : cfg.reasonCode === "listenFailed" ? cfg.detail : cfg.dashboardOrigin;
195
196
  var english = cfg.reasonCode === "publicLinkPreviewFailed" ? "Previews are unavailable on this public link: {0}. The link itself keeps working."
196
197
  : cfg.reason || "Previews are not available here.";
197
198
  var server = cfg.reasonCode ? say("pvServer_" + cfg.reasonCode, english, arg) : "";