@songsid/agend 2.2.1 → 2.2.6

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 (455) 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 +8 -0
  80. package/dist/daemon.js +298 -22
  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 +118 -10
  122. package/dist/locale.js.map +1 -1
  123. package/dist/login-controller.js +35 -2
  124. package/dist/login-controller.js.map +1 -1
  125. package/dist/ls-attach-resolve.d.ts +95 -0
  126. package/dist/ls-attach-resolve.js +174 -0
  127. package/dist/ls-attach-resolve.js.map +1 -0
  128. package/dist/memory-hogs.d.ts +27 -0
  129. package/dist/memory-hogs.js +124 -0
  130. package/dist/memory-hogs.js.map +1 -0
  131. package/dist/needs-you-hub.d.ts +4 -0
  132. package/dist/needs-you-hub.js +31 -6
  133. package/dist/needs-you-hub.js.map +1 -1
  134. package/dist/needs-you.d.ts +1 -13
  135. package/dist/needs-you.js +3 -37
  136. package/dist/needs-you.js.map +1 -1
  137. package/dist/outbound-handlers.d.ts +15 -0
  138. package/dist/outbound-handlers.js +152 -11
  139. package/dist/outbound-handlers.js.map +1 -1
  140. package/dist/outbound-schemas.d.ts +228 -4
  141. package/dist/outbound-schemas.js +147 -1
  142. package/dist/outbound-schemas.js.map +1 -1
  143. package/dist/public-web-gateway.js +21 -1
  144. package/dist/public-web-gateway.js.map +1 -1
  145. package/dist/public-web-link.d.ts +9 -0
  146. package/dist/public-web-link.js +8 -0
  147. package/dist/public-web-link.js.map +1 -1
  148. package/dist/quickstart-api.d.ts +10 -1
  149. package/dist/quickstart-api.js +12 -4
  150. package/dist/quickstart-api.js.map +1 -1
  151. package/dist/reply-buttons-controller.d.ts +4 -0
  152. package/dist/reply-buttons-controller.js +2 -2
  153. package/dist/reply-buttons-controller.js.map +1 -1
  154. package/dist/repo-worktree-guard.d.ts +36 -0
  155. package/dist/repo-worktree-guard.js +143 -0
  156. package/dist/repo-worktree-guard.js.map +1 -0
  157. package/dist/restart-progress.d.ts +65 -1
  158. package/dist/restart-progress.js +236 -10
  159. package/dist/restart-progress.js.map +1 -1
  160. package/dist/rollout-index.d.ts +5 -0
  161. package/dist/rollout-index.js +26 -5
  162. package/dist/rollout-index.js.map +1 -1
  163. package/dist/scheduler/scheduler.d.ts +70 -1
  164. package/dist/scheduler/scheduler.js +256 -17
  165. package/dist/scheduler/scheduler.js.map +1 -1
  166. package/dist/service-installer.d.ts +2 -1
  167. package/dist/service-installer.js +18 -3
  168. package/dist/service-installer.js.map +1 -1
  169. package/dist/settings-api.d.ts +23 -0
  170. package/dist/settings-api.js +233 -2
  171. package/dist/settings-api.js.map +1 -1
  172. package/dist/settings-change.js +112 -0
  173. package/dist/settings-change.js.map +1 -1
  174. package/dist/settings-effect.js +32 -2
  175. package/dist/settings-effect.js.map +1 -1
  176. package/dist/settings-http-confirmation.js +5 -0
  177. package/dist/settings-http-confirmation.js.map +1 -1
  178. package/dist/tmux-manager.d.ts +5 -0
  179. package/dist/tmux-manager.js +16 -0
  180. package/dist/tmux-manager.js.map +1 -1
  181. package/dist/tool-permissions.d.ts +4 -6
  182. package/dist/tool-permissions.js +100 -2
  183. package/dist/tool-permissions.js.map +1 -1
  184. package/dist/topic-commands.d.ts +17 -7
  185. package/dist/topic-commands.js +39 -21
  186. package/dist/topic-commands.js.map +1 -1
  187. package/dist/topic-link.d.ts +128 -0
  188. package/dist/topic-link.js +269 -0
  189. package/dist/topic-link.js.map +1 -0
  190. package/dist/tunnel/cloudflared.js +37 -2
  191. package/dist/tunnel/cloudflared.js.map +1 -1
  192. package/dist/tunnel/types.d.ts +3 -1
  193. package/dist/tunnel/types.js.map +1 -1
  194. package/dist/types.d.ts +61 -1
  195. package/dist/ui/app-effects.js +245 -0
  196. package/dist/ui/chat-ambient.js +307 -0
  197. package/dist/ui/chat-effect-posters.js +42 -0
  198. package/dist/ui/chat-effects.js +160 -0
  199. package/dist/ui/chat-live-replies.js +58 -0
  200. package/dist/ui/chat-render.js +25 -7
  201. package/dist/ui/chat-store.js +41 -13
  202. package/dist/ui/chat-strings.js +86 -2
  203. package/dist/ui/chat-thread.js +204 -37
  204. package/dist/ui/chat-tour.js +1 -1
  205. package/dist/ui/effect-trial-controller.js +124 -0
  206. package/dist/ui/effect-trial-offer.js +31 -0
  207. package/dist/ui/effect-trial-panel.js +40 -0
  208. package/dist/ui/fleet-cache.js +23 -1
  209. package/dist/ui/fleet-canvas-layout.js +111 -0
  210. package/dist/ui/fleet-canvas.js +525 -0
  211. package/dist/ui/fleet-org.js +27 -12
  212. package/dist/ui/fleet-traffic.js +343 -0
  213. package/dist/ui/fleet-usage.js +401 -0
  214. package/dist/ui/image-lightbox.js +1 -1
  215. package/dist/ui/panel-chat.js +252 -58
  216. package/dist/ui/panel-details.js +20 -6
  217. package/dist/ui/panel-fleet.js +45 -19
  218. package/dist/ui/panel-log.js +6 -6
  219. package/dist/ui/panel-needs.js +3 -3
  220. package/dist/ui/panel-settings.js +58 -13
  221. package/dist/ui/preview-panel.js +25 -9
  222. package/dist/ui/preview.js +3 -2
  223. package/dist/ui/settings-appearance.js +349 -0
  224. package/dist/ui/settings-catalog.js +92 -0
  225. package/dist/ui/settings-dialogs.js +40 -12
  226. package/dist/ui/settings-effects.js +202 -0
  227. package/dist/ui/settings-guests.js +117 -0
  228. package/dist/ui/settings-learn.js +25 -0
  229. package/dist/ui/settings-model.js +15 -6
  230. package/dist/ui/settings-mods.js +271 -0
  231. package/dist/ui/settings-owner-effects.js +80 -0
  232. package/dist/ui/settings-strings.js +335 -10
  233. package/dist/ui/settings-web-authority.js +91 -0
  234. package/dist/ui/shared/agend-auth.js +28 -4
  235. package/dist/ui/shared/agent-widget.js +248 -0
  236. package/dist/ui/shared/app-i18n.js +6 -2
  237. package/dist/ui/shared/app-prefs.js +34 -13
  238. package/dist/ui/shared/app-route.js +42 -2
  239. package/dist/ui/shared/app-session.js +3 -1
  240. package/dist/ui/shared/app-shell.js +119 -41
  241. package/dist/ui/shared/app-store.js +28 -1
  242. package/dist/ui/shared/app-stream.js +14 -7
  243. package/dist/ui/shared/app-themes.js +206 -0
  244. package/dist/ui/shared/app.css +566 -5
  245. package/dist/ui/shared/app.js +23 -10
  246. package/dist/ui/shared/card-strings.js +32 -0
  247. package/dist/ui/shared/effect-app-runtime.js +297 -0
  248. package/dist/ui/shared/effect-baseline.js +64 -0
  249. package/dist/ui/shared/effect-chat-runtime.js +167 -0
  250. package/dist/ui/shared/effect-cursor.js +125 -0
  251. package/dist/ui/shared/effect-frame.js +146 -0
  252. package/dist/ui/shared/effect-geometry.js +137 -0
  253. package/dist/ui/shared/effect-host-style.js +10 -0
  254. package/dist/ui/shared/effect-owner-regions.js +74 -0
  255. package/dist/ui/shared/effect-owner-source.js +122 -0
  256. package/dist/ui/shared/effect-policy.js +151 -0
  257. package/dist/ui/shared/effect-poster-reader.js +98 -0
  258. package/dist/ui/shared/effect-posters.js +117 -0
  259. package/dist/ui/shared/effect-profiler.js +184 -0
  260. package/dist/ui/shared/effect-profiles.js +65 -0
  261. package/dist/ui/shared/effect-registry.js +543 -0
  262. package/dist/ui/shared/effect-runtime-loader.js +234 -0
  263. package/dist/ui/shared/effect-safety.js +153 -0
  264. package/dist/ui/shared/effect-stock-snapshot.js +43 -0
  265. package/dist/ui/shared/effect-stock-trust.js +217 -0
  266. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  267. package/dist/ui/shared/effect-trial-source.js +103 -0
  268. package/dist/ui/shared/effect-trial-stage.js +170 -0
  269. package/dist/ui/shared/effect-trial.css +40 -0
  270. package/dist/ui/shared/fx-motion.js +67 -0
  271. package/dist/ui/shared/header-tools.js +1 -1
  272. package/dist/ui/shared/identity-card.js +273 -0
  273. package/dist/ui/shared/identity-color.js +261 -0
  274. package/dist/ui/shared/instance-nav.js +215 -41
  275. package/dist/ui/shared/owner-css.js +250 -0
  276. package/dist/ui/shared/owner-effect-prefs.js +117 -0
  277. package/dist/ui/shared/panel-view.js +103 -10
  278. package/dist/ui/shared/signin.js +2 -0
  279. package/dist/ui/shared/tokens.css +9 -3
  280. package/dist/ui/shared/ui-dialog.js +1 -1
  281. package/dist/ui/shared/ui-icons.js +3 -0
  282. package/dist/ui/shared/ui-menu.js +1 -1
  283. package/dist/ui/shared/ui-toast.js +1 -1
  284. package/dist/ui/shared/usage-dialog.js +22 -2
  285. package/dist/ui/shared/view-strings.js +98 -2
  286. package/dist/ui/shared/web-authorize.js +151 -0
  287. package/dist/ui/signin.html +1 -0
  288. package/dist/ui/web-terminal/terminal.css +4 -4
  289. package/dist/usage/providers.d.ts +28 -0
  290. package/dist/usage/providers.js +126 -36
  291. package/dist/usage/providers.js.map +1 -1
  292. package/dist/usage-report.d.ts +158 -0
  293. package/dist/usage-report.js +226 -0
  294. package/dist/usage-report.js.map +1 -0
  295. package/dist/usage-rollup.d.ts +51 -0
  296. package/dist/usage-rollup.js +168 -0
  297. package/dist/usage-rollup.js.map +1 -0
  298. package/dist/view-api.d.ts +68 -0
  299. package/dist/view-api.js +249 -8
  300. package/dist/view-api.js.map +1 -1
  301. package/dist/web-ambient.d.ts +72 -0
  302. package/dist/web-ambient.js +161 -0
  303. package/dist/web-ambient.js.map +1 -0
  304. package/dist/web-api.d.ts +117 -7
  305. package/dist/web-api.js +1283 -70
  306. package/dist/web-api.js.map +1 -1
  307. package/dist/web-appearance-limits.d.ts +82 -0
  308. package/dist/web-appearance-limits.js +83 -0
  309. package/dist/web-appearance-limits.js.map +1 -0
  310. package/dist/web-appearance-policy.d.ts +64 -0
  311. package/dist/web-appearance-policy.js +87 -0
  312. package/dist/web-appearance-policy.js.map +1 -0
  313. package/dist/web-appearance-tools.d.ts +53 -0
  314. package/dist/web-appearance-tools.js +124 -0
  315. package/dist/web-appearance-tools.js.map +1 -0
  316. package/dist/web-appearance-view.d.ts +124 -0
  317. package/dist/web-appearance-view.js +278 -0
  318. package/dist/web-appearance-view.js.map +1 -0
  319. package/dist/web-appearance.d.ts +93 -0
  320. package/dist/web-appearance.js +373 -0
  321. package/dist/web-appearance.js.map +1 -0
  322. package/dist/web-asset-parts.d.ts +93 -0
  323. package/dist/web-asset-parts.js +133 -0
  324. package/dist/web-asset-parts.js.map +1 -0
  325. package/dist/web-auth.js +20 -2
  326. package/dist/web-auth.js.map +1 -1
  327. package/dist/web-card.d.ts +110 -0
  328. package/dist/web-card.js +266 -0
  329. package/dist/web-card.js.map +1 -0
  330. package/dist/web-channel-echo.d.ts +7 -1
  331. package/dist/web-channel-echo.js +6 -2
  332. package/dist/web-channel-echo.js.map +1 -1
  333. package/dist/web-chat-history.d.ts +63 -0
  334. package/dist/web-chat-history.js +109 -6
  335. package/dist/web-chat-history.js.map +1 -1
  336. package/dist/web-commands.d.ts +1 -0
  337. package/dist/web-effect-bootstrap.d.ts +12 -0
  338. package/dist/web-effect-bootstrap.js +49 -0
  339. package/dist/web-effect-bootstrap.js.map +1 -0
  340. package/dist/web-effect-bundle.d.ts +28 -0
  341. package/dist/web-effect-bundle.js +171 -0
  342. package/dist/web-effect-bundle.js.map +1 -0
  343. package/dist/web-effect-canonical.d.ts +8 -0
  344. package/dist/web-effect-canonical.js +27 -0
  345. package/dist/web-effect-canonical.js.map +1 -0
  346. package/dist/web-effect-library.d.ts +114 -0
  347. package/dist/web-effect-library.js +530 -0
  348. package/dist/web-effect-library.js.map +1 -0
  349. package/dist/web-effect-playback.d.ts +30 -0
  350. package/dist/web-effect-playback.js +71 -0
  351. package/dist/web-effect-playback.js.map +1 -0
  352. package/dist/web-effect-poster.d.ts +8 -0
  353. package/dist/web-effect-poster.js +52 -0
  354. package/dist/web-effect-poster.js.map +1 -0
  355. package/dist/web-effect-profiles.d.ts +118 -0
  356. package/dist/web-effect-profiles.js +180 -0
  357. package/dist/web-effect-profiles.js.map +1 -0
  358. package/dist/web-effect-service.d.ts +93 -0
  359. package/dist/web-effect-service.js +335 -0
  360. package/dist/web-effect-service.js.map +1 -0
  361. package/dist/web-effect-tools.d.ts +48 -0
  362. package/dist/web-effect-tools.js +129 -0
  363. package/dist/web-effect-tools.js.map +1 -0
  364. package/dist/web-effect-trials.d.ts +86 -0
  365. package/dist/web-effect-trials.js +204 -0
  366. package/dist/web-effect-trials.js.map +1 -0
  367. package/dist/web-effect-upload.d.ts +27 -0
  368. package/dist/web-effect-upload.js +185 -0
  369. package/dist/web-effect-upload.js.map +1 -0
  370. package/dist/web-effect-worker.d.ts +8 -0
  371. package/dist/web-effect-worker.js +170 -0
  372. package/dist/web-effect-worker.js.map +1 -0
  373. package/dist/web-effect.d.ts +103 -0
  374. package/dist/web-effect.js +153 -0
  375. package/dist/web-effect.js.map +1 -0
  376. package/dist/web-guest-access.d.ts +125 -0
  377. package/dist/web-guest-access.js +234 -0
  378. package/dist/web-guest-access.js.map +1 -0
  379. package/dist/web-guest-rooms.d.ts +82 -0
  380. package/dist/web-guest-rooms.js +135 -0
  381. package/dist/web-guest-rooms.js.map +1 -0
  382. package/dist/web-identity-color.d.ts +87 -0
  383. package/dist/web-identity-color.js +209 -0
  384. package/dist/web-identity-color.js.map +1 -0
  385. package/dist/web-identity-palette.d.ts +113 -0
  386. package/dist/web-identity-palette.js +84 -0
  387. package/dist/web-identity-palette.js.map +1 -0
  388. package/dist/web-image-meta.d.ts +38 -0
  389. package/dist/web-image-meta.js +262 -0
  390. package/dist/web-image-meta.js.map +1 -0
  391. package/dist/web-login.d.ts +47 -0
  392. package/dist/web-login.js +115 -0
  393. package/dist/web-login.js.map +1 -1
  394. package/dist/web-mod-agent.d.ts +50 -0
  395. package/dist/web-mod-agent.js +207 -0
  396. package/dist/web-mod-agent.js.map +1 -0
  397. package/dist/web-mod-bundle.d.ts +157 -0
  398. package/dist/web-mod-bundle.js +529 -0
  399. package/dist/web-mod-bundle.js.map +1 -0
  400. package/dist/web-mod-export.d.ts +68 -0
  401. package/dist/web-mod-export.js +100 -0
  402. package/dist/web-mod-export.js.map +1 -0
  403. package/dist/web-mod-store.d.ts +289 -0
  404. package/dist/web-mod-store.js +638 -0
  405. package/dist/web-mod-store.js.map +1 -0
  406. package/dist/web-org.d.ts +3 -3
  407. package/dist/web-org.js +11 -17
  408. package/dist/web-org.js.map +1 -1
  409. package/dist/web-owner-css-store.d.ts +222 -0
  410. package/dist/web-owner-css-store.js +440 -0
  411. package/dist/web-owner-css-store.js.map +1 -0
  412. package/dist/web-owner-css.d.ts +58 -0
  413. package/dist/web-owner-css.js +552 -0
  414. package/dist/web-owner-css.js.map +1 -0
  415. package/dist/web-prefs.d.ts +62 -0
  416. package/dist/web-prefs.js +232 -36
  417. package/dist/web-prefs.js.map +1 -1
  418. package/dist/web-preview.d.ts +15 -3
  419. package/dist/web-preview.js +28 -9
  420. package/dist/web-preview.js.map +1 -1
  421. package/dist/web-returning-device.d.ts +41 -0
  422. package/dist/web-returning-device.js +57 -5
  423. package/dist/web-returning-device.js.map +1 -1
  424. package/dist/web-session-authorization.d.ts +132 -0
  425. package/dist/web-session-authorization.js +193 -0
  426. package/dist/web-session-authorization.js.map +1 -0
  427. package/dist/web-session.d.ts +70 -10
  428. package/dist/web-session.js +137 -22
  429. package/dist/web-session.js.map +1 -1
  430. package/dist/web-settings-patch.d.ts +67 -2
  431. package/dist/web-settings-patch.js +196 -1
  432. package/dist/web-settings-patch.js.map +1 -1
  433. package/dist/web-settings-tools.d.ts +89 -0
  434. package/dist/web-settings-tools.js +273 -0
  435. package/dist/web-settings-tools.js.map +1 -0
  436. package/dist/web-shell-routes.d.ts +2 -2
  437. package/dist/web-shell-routes.js +2 -2
  438. package/dist/web-shell-routes.js.map +1 -1
  439. package/dist/web-terminal-http.js +15 -2
  440. package/dist/web-terminal-http.js.map +1 -1
  441. package/dist/web-theme-store.d.ts +257 -0
  442. package/dist/web-theme-store.js +627 -0
  443. package/dist/web-theme-store.js.map +1 -0
  444. package/dist/web-theme.d.ts +123 -0
  445. package/dist/web-theme.js +416 -0
  446. package/dist/web-theme.js.map +1 -0
  447. package/dist/web-upload.d.ts +13 -0
  448. package/dist/web-upload.js +39 -3
  449. package/dist/web-upload.js.map +1 -1
  450. package/dist/web-widget.d.ts +72 -0
  451. package/dist/web-widget.js +245 -0
  452. package/dist/web-widget.js.map +1 -0
  453. package/dist/workflow-templates/default.md +1 -1
  454. package/package.json +1 -1
  455. package/templates/systemd.service.ejs +8 -0
@@ -6,16 +6,17 @@
6
6
  // right, the agent's at full width, and one rounded composer pinned below with attach, Stop reply and Send inside it.
7
7
  // The instance's own actions (details, start, stop, restart, delete) are in the header's ⋯ menu, and only the ones
8
8
  // that apply to its state are offered (#1408 rough edges 4 and 7).
9
- import { html, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
10
- import { t } from "/assets/app-i18n.js";
11
- import { appStore, useStore } from "/assets/app-store.js";
9
+ import { html, useCallback, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
10
+ import { lang, t } from "/assets/app-i18n.js";
11
+ import { appStore, guestPage, useStore } from "/assets/app-store.js";
12
+ import { AuthorizeChip } from "/assets/web-authorize.js";
12
13
  import { useLease } from "/assets/app-ctx.js";
13
- import { namesOf, namesStore } from "/assets/instance-nav.js";
14
+ import { modelText, namesOf, namesStore, navStore as instanceNavStore } from "/assets/instance-nav.js";
14
15
  import { PanelHeader, onPanelKey, setTitle, statusClass, statusLabel, openDrawer, requestNewInstance } from "/assets/app-shell.js";
15
- import { detailsPath } from "/assets/app-route.js";
16
+ import { detailsPath, textLinkSegments } from "/assets/app-route.js";
16
17
  import { InstanceSwitch } from "/assets/instance-switch.js";
17
18
  import { HeaderTools } from "/assets/header-tools.js";
18
- import { navigate } from "/assets/app-nav.js";
19
+ import { navigate, navStore } from "/assets/app-nav.js";
19
20
  import { Menu } from "/assets/ui-menu.js";
20
21
  import { Dialog } from "/assets/ui-dialog.js";
21
22
  import { Empty, Skeleton } from "/assets/ui-states.js";
@@ -23,6 +24,26 @@ import { Icon } from "/assets/ui-icons.js";
23
24
  import { toast } from "/assets/ui-toast.js";
24
25
  import { confirmDialog } from "/assets/ui-confirm.js";
25
26
  import { savePref } from "/assets/app-prefs.js";
27
+ import { applyTheme, clearAmbient, resolvedMode, themeFor, themeImageUrl, themeMenuItems, themesStore } from "/assets/app-themes.js";
28
+ import { colorMenuItem } from "/assets/identity-color.js";
29
+ import { IdentityCard, useCard } from "/assets/identity-card.js";
30
+ import { AgentWidget, instanceBinds, widgetFor } from "/assets/agent-widget.js";
31
+
32
+ // #1629 r4 §1: the identity card folded into the chat's header — folded by default; open or not is this device's choice.
33
+ const CHAT_CARD_KEY = "agend_chat_card";
34
+ const chatCardOpen = () => { try { return localStorage.getItem(CHAT_CARD_KEY) === "open"; } catch { return false; } };
35
+ function ChatCard({ name, card }) {
36
+ const [open, setOpen] = useState(chatCardOpen);
37
+ if (!card) return null;
38
+ const toggle = () => { const next = !open; setOpen(next); try { if (next) localStorage.setItem(CHAT_CARD_KEY, "open"); else localStorage.removeItem(CHAT_CARD_KEY); } catch { /* this page only */ } };
39
+ return html`<div data-trust="card" class=${`chat-card${open ? " open" : ""}`}>
40
+ <button type="button" class="chat-card-toggle" aria-expanded=${open ? "true" : "false"} onClick=${toggle}>
41
+ <${Icon} name="chevron" size=${14} cls=${open ? "caret" : "caret flipped"} /><span>${t(open ? "card.hide" : "card.show")}</span>
42
+ ${!open && card.tagline ? html`<span class="chat-card-peek">${card.tagline.text}</span>` : null}
43
+ </button>
44
+ ${open ? html`<${IdentityCard} name=${name} card=${card} compact=${true} />` : null}
45
+ </div>`;
46
+ }
26
47
  import "./chat-strings.js";
27
48
  import { confirmedWrite } from "./settings-confirm.js";
28
49
  import { createChatStore } from "./chat-store.js";
@@ -34,6 +55,11 @@ import { PreviewPanel, claimPanel, openPanel, panelClaims, panelStore, panelVers
34
55
  import { needsArgument, paletteFor, parseCommandLine } from "./chat-commands.js";
35
56
  import { installTour, refreshTourSpot } from "./chat-tour.js";
36
57
  import { LightboxHost, openLightbox } from "./image-lightbox.js";
58
+ import { ChatEffectPosters } from "/ui/js/chat-effect-posters.js";
59
+ import { ChatEffects, chatEffectsMenuAvailable } from "./chat-effects.js";
60
+ import { setFollowsReducedMotion } from "/assets/fx-motion.js";
61
+ import { AmbientMotionNote, ChatAmbient, ambientMenuItems } from "./chat-ambient.js";
62
+ import { startAppEffects, getAppEffects } from "./app-effects.js";
37
63
 
38
64
  const R = () => globalThis.AgendChatRender;
39
65
  const P = () => globalThis.AgendPreview;
@@ -93,6 +119,8 @@ export function boot({ stream, boot: bootData, deps = {} }) {
93
119
  }
94
120
  // #1604: the preview opt-in and Tour live in Settings → This device now (and on each preview card's menu).
95
121
  installTour();
122
+ // #2118: viewing current in-scope Chat effects needs no management or personal-slot authority.
123
+ startAppEffects({ store });
96
124
  return store;
97
125
  }
98
126
 
@@ -171,14 +199,51 @@ function NotFound({ name }) {
171
199
  action=${html`<button type="button" class="btn only-narrow" onClick=${openDrawer}>${t("chat.backToList")}</button>`} /></div></div>`;
172
200
  }
173
201
 
202
+ /**
203
+ * #1629: the mode the page is drawn in, followed live (the chosen theme, or the system's for "system"), so an agent's
204
+ * light and dark palettes swap with it.
205
+ */
206
+ function useResolvedMode(lease) {
207
+ const [mode, setMode] = useState(resolvedMode);
208
+ useEffect(() => {
209
+ const T = globalThis.AgendTheme;
210
+ const update = () => setMode(resolvedMode());
211
+ const off = T && typeof T.onChange === "function" ? T.onChange(update) : null;
212
+ let mq = null;
213
+ try { mq = globalThis.matchMedia ? globalThis.matchMedia("(prefers-color-scheme: light)") : null; } catch { mq = null; }
214
+ if (mq && typeof mq.addEventListener === "function") lease.on(mq, "change", update);
215
+ return () => { if (typeof off === "function") off(); };
216
+ }, [lease]);
217
+ return mode;
218
+ }
219
+
174
220
  function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
175
221
  const names = useStore(namesStore); // #1722: which name leads (the title)
222
+ // #1629: this agent's theme, on this surface only; redrawn when the themes, the viewer's switch or the mode move.
223
+ useStore(themesStore);
224
+ const mode = useResolvedMode(lease);
225
+ const theme = themeFor(name);
226
+ const themeKey = JSON.stringify(theme);
227
+ const card = useCard(name, inst.card_v);
176
228
  const [dialog, setDialog] = useState(null); // "delete" | null
229
+ const [canvasPlayback, setCanvasPlayback] = useState(null);
230
+ const publishCanvasPlayback = useCallback(value => setCanvasPlayback(previous =>
231
+ !previous && !value.playing || previous?.name === value.name && previous.playing === value.playing ? previous : value), []);
177
232
  const [wrap, setWrap] = useState(codeWrap);
178
- const view = useRef(null), split = useRef(null), thread = useRef(null);
233
+ const view = useRef(null), split = useRef(null), thread = useRef(null), bg = useRef(null), effectSurface = useRef(null);
234
+ useLayoutEffect(() => { applyTheme(view.current, theme, mode); }, [themeKey, mode]);
235
+ useLayoutEffect(() => {
236
+ const img = bg.current;
237
+ if (img && img.style && theme && theme.background) img.style.setProperty("--th-bg-opacity", String(theme.background.opacity));
238
+ }, [themeKey]);
179
239
  useEffect(() => { wrapListeners.add(setWrap); return () => wrapListeners.delete(setWrap); }, []);
180
240
  // First visit to this instance's chat on this page: its history (a navigation — it counts as use). Back again: nothing.
181
- useEffect(() => { store.openHistory(name, lease); }, [name]);
241
+ // #2188: left before it answered (passing through on the way to another chat) — the read is aborted.
242
+ useEffect(() => {
243
+ const ac = typeof AbortController === "function" ? new AbortController() : null;
244
+ store.openHistory(name, lease, ac?.signal);
245
+ return () => ac?.abort();
246
+ }, [name]);
182
247
  useEffect(() => { try { localStorage.setItem("agend_last_instance", name); } catch { /* none */ } }, [name]);
183
248
  // Files dragged over the chat show where they go; a drop attaches them (counted, so moving over messages does not flicker).
184
249
  useEffect(() => {
@@ -217,97 +282,219 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
217
282
  running ? null : { key: "start", label: t("chat.start"), icon: "play", onSelect: () => action("start") },
218
283
  running ? { key: "restart", label: t("chat.restart"), icon: "restart", onSelect: () => action("restart") } : null,
219
284
  running ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", onSelect: () => action("stop") } : null,
285
+ // #1958: its identity colour and where it shows (the public link too, as profile edits).
286
+ colorMenuItem(name, inst),
287
+ // #1629: a person's control over this agent's theme — not over the public link (it only views themes).
288
+ ...themeMenuItems(name),
289
+ // #2117: its ambient — why it is not showing here (with the way to change that), and a person's Clear.
290
+ ...ambientMenuItems(name, { publicLink: document.body.dataset.publicLink === "1", clear: clearAmbient }),
291
+ chatEffectsMenuAvailable() ? { key: "effects", label: t("chatFx.title"), onSelect: () => setDialog("effects") } : null,
220
292
  { key: "delete", label: t("chat.delete"), icon: "trash", danger: true, onSelect: () => setDialog("delete") },
221
293
  ];
222
294
  const cls = statusClass(inst, exec, awaiting);
295
+ // #1920 phase 3: a guest has this chat only — no Details, View or Log to switch to, no lifecycle, colour, theme,
296
+ // effect management or delete (a guest's status frame carries no model or effort either, so those chips do not show).
297
+ const guest = guestPage();
223
298
  // #1269 quick actions: the model and effort it runs, each a way to /model or /effort (the same pick list).
224
299
  const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
225
300
  const sub = html`<span class="status"><span class=${`dot ${cls}`} aria-hidden="true"></span>${statusLabel(inst, exec, awaiting)}</span>
226
- ${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", inst.model)} aria-label=${t("chat.chipModel", inst.model)} onClick=${() => pick("model")}>${inst.model}</button>` : null}
301
+ ${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => pick("model")}>${modelText(inst)}</button>` : null}
227
302
  ${inst.effort ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
228
303
  return html`<div class=${`panel p-chat${wrap ? " wrap-code" : ""}`} ref=${view}>
229
- <${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="chat" />`}><${HeaderTools} navKey=${navKey} /><${Menu} items=${items} label=${t("app.more")} /></${PanelHeader}>
304
+ <${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? null : html`<${InstanceSwitch} name=${name} current="chat" />`} notice=${html`<${ChatEffects} name=${name} lease=${lease} element=${effectSurface} store=${store} open=${dialog === "effects"} onPlayback=${publishCanvasPlayback} onClose=${() => setDialog(null)} />`}><${ChatEffectPosters} name=${name} lease=${lease} element=${effectSurface} /><${HeaderTools} navKey=${navKey} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
305
+ <${ChatCard} key=${name} name=${name} card=${card} />
306
+ <${AgentWidget} slot="chat_header" widget=${widgetFor(name, "chat_header")} binds=${instanceBinds(inst, exec, awaiting)} />
230
307
  <div class="chat-split" ref=${split}>
231
- <div class="chat-main">
308
+ <div ref=${effectSurface} class=${`chat-main${theme && theme.background ? " has-th-bg" : ""}`}>
309
+ ${theme && theme.background ? html`<img ref=${bg} class=${`th-bg th-bg-${theme.background.fit}`} src=${themeImageUrl(name, theme.background.image)} alt="" aria-hidden="true" decoding="async" />` : null}
310
+ <${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
311
+ <${AmbientMotionNote} name=${name} canvasPlaying=${canvasPlayback?.name === name && canvasPlayback.playing} />
232
312
  <${Thread} name=${name} th=${thread} />
233
- <${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} />
313
+ <${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${theme ? theme.workingLabel : undefined} />
314
+ <${ChatAmbient} name=${name} surface=${effectSurface} part="front" />
234
315
  </div>
235
316
  <${PreviewPanel} name=${name} split=${split} msgs=${() => store.state.msgs[name] || []} subscribe=${(fn) => store.subscribe(fn)}
236
317
  keyOf=${msgKey} reveal=${(k) => thread.current && thread.current.reveal(k)} download=${downloadHtml} allow=${(commit) => setPreviewOptIn(true, commit)} />
237
318
  </div>
238
319
  <${LightboxHost} />
239
- <div class="drop-overlay" aria-hidden="true"><div class="drop-card"><${Icon} name="attach" size=${32} /><span>${t("chat.dropHere")}</span></div></div>
320
+ <div data-trust="drop" class="drop-overlay" aria-hidden="true"><div class="drop-card"><${Icon} name="attach" size=${32} /><span>${t("chat.dropHere")}</span></div></div>
240
321
  ${dialog === "delete" ? html`<${DeleteDialog} name=${name} onClose=${() => setDialog(null)} />` : null}
241
322
  </div>`;
242
323
  }
243
324
 
244
- /** The thread: Preact owns the elements, the keyed renderer owns what is inside the list (chat-thread.js). */
325
+ /**
326
+ * #2053: the last THREAD_KEEP chats' threads are kept when their view leaves (the chat view is keyed by instance, so
327
+ * each switch built the thread again: a revisit was no faster than a first visit). A kept thread is its list element
328
+ * and its renderer, and nothing more: it leaves as a chat always did (previews stop), with no subscription or timer
329
+ * left; shown again, one render draws only what changed meanwhile, where the reader was. A new language, another
330
+ * document, or more than the budget lets one go.
331
+ *
332
+ * #2184: the budget (raised for people who switch among ~10 favourites): up to THREAD_KEEP threads on a wide screen and
333
+ * THREAD_KEEP_NARROW on a narrow one (a phone), and THREAD_KEEP_NODES drawn messages in all, so memory stays bounded
334
+ * however long the chats are. Over it, the least recently shown goes first — one that is not a favourite before a
335
+ * favourite; the chat on screen never goes.
336
+ */
337
+ export const THREAD_KEEP = 12;
338
+ export const THREAD_KEEP_NARROW = 6;
339
+ export const THREAD_KEEP_NODES = 2400;
340
+ /**
341
+ * #2188: a chat shown for less than this, on the way to another (clicking down the sidebar), is not kept: it is let go
342
+ * as it leaves, so passing through chats never evicts the ones in use. One that was already kept stays kept.
343
+ */
344
+ export const THREAD_PASS_MS = 1000;
345
+ const monotonic = () => (typeof performance === "object" && typeof performance.now === "function" ? performance.now() : Date.now());
346
+ let now = monotonic;
347
+ /** Tests only: the clock THREAD_PASS_MS is measured on (null: the page's monotonic clock). */
348
+ export function setThreadClock(fn) { now = typeof fn === "function" ? fn : monotonic; }
349
+ /**
350
+ * The kept threads to let go, in order. `kept`: [name, drawn messages], least recently shown first; `current`: the
351
+ * one on screen (never let go).
352
+ */
353
+ export function threadsToLetGo(kept, current, { keep, maxNodes, favorites }) {
354
+ let count = kept.length, nodes = kept.reduce((n, [, d]) => n + d, 0);
355
+ const out = [];
356
+ const order = [...kept.filter(([n]) => n !== current && !favorites.has(n)), ...kept.filter(([n]) => n !== current && favorites.has(n))];
357
+ for (const [n, d] of order) {
358
+ if (count <= keep && nodes <= maxNodes) break;
359
+ out.push(n); count--; nodes -= d;
360
+ }
361
+ return out;
362
+ }
363
+ const narrowScreen = () => typeof matchMedia === "function" && matchMedia("(max-width: 899px)").matches;
364
+ /** Let go what the budget does not hold (after `current` was shown, or drew more). */
365
+ function trimKept(current) {
366
+ const kept = [...keptThreads].map(([n, e]) => [n, e.thread.nodes.size]);
367
+ const favorites = new Set(instanceNavStore.get().favorites);
368
+ for (const n of threadsToLetGo(kept, current, { keep: narrowScreen() ? THREAD_KEEP_NARROW : THREAD_KEEP, maxNodes: THREAD_KEEP_NODES, favorites })) {
369
+ letGo(keptThreads.get(n), true); keptThreads.delete(n);
370
+ }
371
+ }
372
+ const keptThreads = new Map(); // name → { thread, list, sink, lang }
373
+ /** Tests only: let every kept thread go. */
374
+ export function forgetKeptThreads() { for (const e of keptThreads.values()) letGo(e, true); keptThreads.clear(); }
375
+ function letGo(e, own) { e.thread.dispose(own); e.list.remove(); }
376
+
377
+ /** A chat's thread: its own list element and renderer, and a sink for the view that shows it. */
378
+ function threadEntry(name, doc) {
379
+ const list = doc.createElement("div");
380
+ list.className = "thread";
381
+ list.setAttribute("role", "log");
382
+ list.setAttribute("aria-live", "off");
383
+ list.setAttribute("aria-label", t("chat.conversationWith", name));
384
+ const e = { list, sink: null, lang: lang(), thread: null };
385
+ const thread = createThread(list, doc.createElement("div"), {
386
+ t, tf: t, isUser: (x) => store.isUser(x, name), onJump: (j) => e.sink?.setJump(j), onEmpty: (v) => e.sink?.setEmpty(v),
387
+ setPreviewOptIn, copyText, download: downloadHtml, toggleWrap,
388
+ effectTrialStatus: x => getAppEffects()?.trialStatus(x) ?? "localAdmin",
389
+ effectTrialFailure: x => getAppEffects()?.trialFailure(x),
390
+ fixEffectTrial: (x, reason) => {
391
+ if (reason === "reducedMotion") setFollowsReducedMotion(false);
392
+ else if (reason === "busy") getAppEffects()?.stopTrial();
393
+ else if (reason === "transport") location.reload();
394
+ else if (reason === "localAdmin") location.href = "/signin?next=" + encodeURIComponent(location.pathname + location.search);
395
+ else navigate("/settings/appearance");
396
+ },
397
+ canTryEffect: x => getAppEffects()?.canTry(x) === true,
398
+ startEffectTrial: x => { const seq = navStore.get().seq; void getAppEffects()?.startTrial(x).then(ok => { if (!ok && e.sink && navStore.get().seq === seq && document.hasFocus?.() === true && document.visibilityState === "visible") toast(t("chat.effectTrialUnavailable")); }); },
399
+ clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
400
+ replyButtonBusy: (id) => store.state.rbBusy.has(id),
401
+ pollMine: (id) => store.state.pollMine[id], // #1823: this device's own vote
402
+ serverNow: () => Date.now() + store.state.clockSkew, // #1823: the deadline is the server's
403
+ panel: { shown: shownKey, open: (spec, o) => openPanel(spec, o), version: panelVersion, claim: claimPanel, claims: panelClaims }, // #1481, #1704
404
+ openImage: openLightbox, // an image, full size, in the page
405
+ emojis: (id) => !!emojiIndex && emojiIndex.known(id), // #1630: the fleet's server emoji
406
+ mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
407
+ });
408
+ e.thread = thread;
409
+ return e;
410
+ }
411
+ /** While a thread is shown: what it follows. Returns what lets it all go. */
412
+ function follow(name, thread) {
413
+ // #1630: the server-emoji list is read once a message shows a tag; when it arrives, those messages are drawn again.
414
+ // #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
415
+ const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
416
+ const offs = [];
417
+ if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
418
+ if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
419
+ wantEmojis(store.state.msgs[name]);
420
+ offs.push(store.subscribe((instance, kind) => {
421
+ if (instance !== name) return;
422
+ if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
423
+ else if (kind === "sent") thread.jumpLatest();
424
+ }));
425
+ offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
426
+ offs.push(getAppEffects()?.subscribeTrial(() => thread.render(store.state.msgs[name] || [])) ?? (() => {}));
427
+ offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
428
+ return () => { for (const off of offs) off(); };
429
+ }
430
+
431
+ /** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
245
432
  function Thread({ name, th }) {
246
- const scroller = useRef(null), list = useRef(null);
433
+ const scroller = useRef(null), host = useRef(null);
247
434
  const [jump, setJump] = useState({ show: false, unseen: 0 });
248
435
  const [empty, setEmpty] = useState(false);
249
436
  useLayoutEffect(() => {
250
- const thread = createThread(list.current, scroller.current, {
251
- t, tf: t, isUser: (x) => store.isUser(x, name), onJump: setJump, onEmpty: setEmpty,
252
- setPreviewOptIn, copyText, download: downloadHtml, toggleWrap,
253
- clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
254
- replyButtonBusy: (id) => store.state.rbBusy.has(id),
255
- pollMine: (id) => store.state.pollMine[id], // #1823: this device's own vote
256
- serverNow: () => Date.now() + store.state.clockSkew, // #1823: the deadline is the server's
257
- panel: { shown: shownKey, open: (spec, o) => openPanel(spec, o), version: panelVersion, claim: claimPanel, claims: panelClaims }, // #1481, #1704
258
- openImage: openLightbox, // an image, full size, in the page
259
- emojis: (id) => !!emojiIndex && emojiIndex.known(id), // #1630: the fleet's server emoji
260
- mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
261
- });
437
+ const doc = host.current.ownerDocument;
438
+ let e = keptThreads.get(name);
439
+ // Kept only while it can be shown as it is: the same language, and this page's own document.
440
+ if (e && (e.lang !== lang() || e.list.ownerDocument !== doc)) { letGo(e, true); keptThreads.delete(name); e = undefined; }
441
+ const kept = !!e;
442
+ if (e) keptThreads.delete(name);
443
+ else e = threadEntry(name, doc);
444
+ keptThreads.set(name, e); // the most recent last
445
+ // A thread already kept is trimmed against at once; a new one first has to stay (see THREAD_PASS_MS).
446
+ if (kept) trimKept(name);
447
+ const shownAt = now();
448
+ const thread = e.thread, sc = scroller.current, ls = e.list;
449
+ e.sink = { setJump, setEmpty };
450
+ host.current.append(ls);
451
+ thread.attach(sc);
262
452
  th.current = thread;
263
- // #1630: the server-emoji list is read once a message shows a tag; when it arrives, those messages are drawn again.
264
- // #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
265
- const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
266
- const offEmojis = emojiIndex ? emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])) : () => {};
267
- const offMentions = mentionIndex ? mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])) : () => {};
453
+ // Up to date, at the reader's place (the bottom unless they had scrolled up); a kept one's cards say what they can do.
268
454
  thread.render(store.state.msgs[name] || [], { restore: store.state.scrollMemo[name] ?? null });
269
- wantEmojis(store.state.msgs[name]);
270
- const off = store.subscribe((instance, kind) => {
271
- if (instance !== name) return;
272
- if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
273
- else if (kind === "sent") thread.jumpLatest();
274
- });
275
- const offPv = P() ? P().onChange(() => thread.refreshCards()) : () => {};
276
- const offPanel = panelStore.subscribe(() => thread.refreshCards()); // a card shows whether the panel has its block
455
+ if (kept) thread.refreshCards();
456
+ const unfollow = follow(name, thread);
277
457
  const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
278
- const sc = scroller.current, ls = list.current;
279
458
  sc.addEventListener("scroll", onScroll, { passive: true });
280
459
  ls.addEventListener("click", thread.onClick);
281
460
  return () => {
282
- off(); offPv(); offPanel(); offEmojis(); offMentions();
461
+ unfollow();
283
462
  sc.removeEventListener("scroll", onScroll);
284
463
  ls.removeEventListener("click", thread.onClick);
285
- thread.dispose(); // the whole thread goes: every preview in it stops first
464
+ e.sink = null;
286
465
  th.current = null;
466
+ // Passed through (new, and shown under THREAD_PASS_MS): let go. Otherwise kept for a later visit, within the budget.
467
+ if (keptThreads.get(name) === e && !kept && now() - shownAt < THREAD_PASS_MS) { keptThreads.delete(name); letGo(e, false); }
468
+ else if (keptThreads.get(name) === e) { thread.park(); ls.remove(); trimKept(null); } // kept for a later visit: the chat left
469
+ else letGo(e, false); // let go meanwhile (a new language)
287
470
  };
288
471
  }, [name]);
289
472
  return html`<div class="scroller" ref=${scroller}>
290
- <div class="thread" ref=${list} role="log" aria-live="off" aria-label=${t("chat.conversationWith", name)}></div>
473
+ <div class="thread-host" ref=${host}></div>
291
474
  ${empty ? html`<${Empty} icon="chat" title=${t("chat.noMessages")} />` : null}
292
- ${jump.show ? html`<button type="button" class="jump-latest" onClick=${() => th.current && th.current.jumpLatest()}>
293
- <${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}
475
+ <div class="jump-dock">${jump.show ? html`<button data-trust="thread-control" type="button" class="jump-latest" onClick=${() => th.current && th.current.jumpLatest()}>
476
+ <${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}</div>
294
477
  </div>`;
295
478
  }
296
479
 
297
480
  /** Everything under the thread: prompts, the working line, a failed send, files waiting, and the composer. */
298
- function Dock({ name, inst, lease, exec, awaiting }) {
481
+ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
299
482
  useChatTick(name, ["state"]);
300
483
  const s = store.state;
301
- return html`<div class="dock"><div class="dock-col">
484
+ return html`<div data-trust="composer" class="dock"><div class="dock-col">
302
485
  <${Prompts} name=${name} />
303
- <${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} />
486
+ <${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${workingLabel} />
304
487
  <${CommandCard} name=${name} lease=${lease} />
305
488
  <${QuickActions} name=${name} inst=${inst} lease=${lease} />
306
489
  ${s.failedSends[name] ? html`<div class="failed-send" role="alert"><span>${t("chat.notSent")}</span><span class="txt">${s.failedSends[name]}</span>
307
490
  <button type="button" class="btn btn-primary btn-sm" onClick=${() => store.putBack(name)}>${t("chat.putBack")}</button>
308
491
  <button type="button" class="btn btn-sm" onClick=${() => store.discardFailed(name)}>${t("chat.discard")}</button></div>` : null}
492
+ ${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
493
+ ${s.sendFailures[name] ? html`<div class="failed-send send-failure" role="alert"><span>${s.sendFailures[name].error}</span>
494
+ <button type="button" class="btn btn-sm" disabled=${!store.canRetrySend(name)} onClick=${() => store.retrySend(name)}>${t("chat.retrySend")}</button></div>` : null}
309
495
  <${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
310
- <p class="hint" title=${t("chat.composerMore")}>${t("chat.composerHint")}</p>
496
+ <p class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</p>
497
+ ${guestPage() ? html`<p class="guest-note">${t("chat.guestNote")}</p>` : html`<${AuthorizeChip} />`}
311
498
  </div></div>`;
312
499
  }
313
500
 
@@ -327,7 +514,8 @@ function CommandCard({ name, lease }) {
327
514
  : c.compact === "lost" ? t("chat.compactLost") : c.compact === "not_compacted" ? t("chat.compactNotCompacted") : t("chat.compactUnknown")}</p>` : null}
328
515
  ${c.choices && c.choices.prompt ? html`<pre class="cmd-text">${c.choices.prompt}</pre>` : null}
329
516
  ${c.choices ? html`<div class="cmd-choices" role="group" aria-label=${t("chat.cmdChoose")}>${c.choices.options.map((o) => html`<button key=${o.id} type="button"
330
- class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined} onClick=${() => run(o.id)}>${o.label}</button>`)}</div>` : null}
517
+ class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined}
518
+ disabled=${!!o.disabled} title=${o.disabled || undefined} onClick=${() => { if (!o.disabled) run(o.id); }}>${o.label}</button>`)}</div>` : null}
331
519
  </div>`;
332
520
  }
333
521
 
@@ -347,14 +535,14 @@ function QuickActions({ name, inst, lease }) {
347
535
  function Prompts({ name }) {
348
536
  const list = store.promptsFor(name);
349
537
  if (!list.length) return null;
350
- return html`<div class="prompts" aria-live="polite">${list.map(p => html`<div key=${p.nonce} class=${`prompt${p.resolved ? " done" : ""}`} role="group" aria-label=${p.text}>
351
- <div class="txt">${p.resolved ? (p.outcome || t("chat.promptGone")) : p.text}</div>
538
+ return html`<div data-trust="permission" class="prompts" aria-live="polite">${list.map(p => html`<div key=${p.nonce} class=${`prompt${p.resolved ? " done" : ""}`} role="group" aria-label=${p.text}>
539
+ <div class="txt">${p.resolved ? (p.outcome || t("chat.promptGone")) : textLinkSegments(p.text, p.links).map(sg => sg.href ? html`<a data-trust="link" class="st-link" href=${sg.href}>${sg.text}</a>` : sg.text)}</div>
352
540
  ${p.resolved ? null : html`<div class="acts">${p.actions.map(a => html`<button key=${a.id} type="button" class="btn" disabled=${!!p.busy} onClick=${() => store.answerPrompt(p, a.id)}>${a.label}</button>`)}
353
541
  ${p.busy ? html`<span class="wait">${t("chat.promptAnswering")}</span>` : null}</div>`}</div>`)}</div>`;
354
542
  }
355
543
 
356
544
  /** "<name> is working… 0:42", "looks stuck", "Stopping…", or what it waits on at its terminal. Text only. */
357
- function WorkBar({ name, lease, exec, awaiting }) {
545
+ function WorkBar({ name, lease, exec, awaiting, workingLabel }) {
358
546
  const s = store.state;
359
547
  const busy = R().isBusy(exec) ? exec : "";
360
548
  const kind = awaiting != null ? "awaiting" : s.stopping[name] && busy ? "stopping" : busy;
@@ -365,7 +553,9 @@ function WorkBar({ name, lease, exec, awaiting }) {
365
553
  return () => lease.clear(h);
366
554
  }, [kind, lease]);
367
555
  if (!kind) return null;
368
- const label = t(kind === "stuck" ? "chat.stuck" : kind === "stopping" ? "chat.stopping" : kind === "awaiting" ? "chat.awaiting" : "chat.working", name);
556
+ // #1629: the agent's own working label (its theme), shown as text — only for "working", never for the other states.
557
+ const label = kind === "working" && workingLabel ? workingLabel
558
+ : t(kind === "stuck" ? "chat.stuck" : kind === "stopping" ? "chat.stopping" : kind === "awaiting" ? "chat.awaiting" : "chat.working", name);
369
559
  const since = s.workingSince[name];
370
560
  return html`<div class=${`work-bar ${kind}`}><span class="pulse" aria-hidden="true"></span><span class="lbl">${label}</span>
371
561
  ${kind === "awaiting" ? html`<span class="note" title=${t("chat.approxNote")}>${awaiting || t("chat.approxNote")}</span>`
@@ -381,8 +571,10 @@ function Composer({ name, lease, busy }) {
381
571
  const value = s.drafts[name] || "";
382
572
  const files = s.pendingFiles[name] || [];
383
573
  const content = !!value.trim() || files.length > 0;
384
- // A file waiting: what goes is a message with its file, never a command (no palette, no command line).
385
- const palette = files.length || closedFor === value ? null : paletteFor(value);
574
+ // A file waiting: what goes is a message with its file, never a command (no palette, no command line). A guest never
575
+ // runs one (#1920: the server refuses its slash text), so it has no palette either.
576
+ const guest = guestPage();
577
+ const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
386
578
  const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
387
579
  // Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
388
580
  useLayoutEffect(() => { const el = input.current; if (!el) return; el.style.height = "auto"; el.style.height = `${Math.min(el.scrollHeight, 240)}px`; }, [value]);
@@ -403,6 +595,8 @@ function Composer({ name, lease, busy }) {
403
595
  else complete(pick);
404
596
  return;
405
597
  }
598
+ // #1920: a guest's "/…" would be refused as a command — said here, the draft kept to edit.
599
+ if (guest && /^\s*\//.test(value)) { toast(t("chat.guestNoCommands"), false); return; }
406
600
  const cmd = files.length ? null : parseCommandLine(value);
407
601
  if (cmd && needsArgument(cmd.command) && !cmd.args) { setValue(`/${cmd.command} `); return; }
408
602
  if (cmd) { runLine(cmd); return; }
@@ -428,7 +622,7 @@ function Composer({ name, lease, busy }) {
428
622
  };
429
623
  const optId = (c) => `cmd-${c.name}`;
430
624
  return html`<div class="composer-box">
431
- ${palette ? html`<ul class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
625
+ ${palette ? html`<ul data-trust="palette" class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
432
626
  ${palette.length ? palette.map((c) => html`<li key=${c.name} id=${optId(c)} role="option" aria-selected=${c === pick ? "true" : "false"}
433
627
  class=${c === pick ? "on" : ""} onMouseDown=${(e) => { e.preventDefault(); complete(c); }}>
434
628
  <span class="cmd-name mono">/${c.name}${c.arg ? html` <span class="cmd-arg">${c.arg}</span>` : null}</span><span class="cmd-desc">${t(`chat.cmd_${c.name}`)}</span></li>`)
@@ -14,8 +14,13 @@ import { PanelHeader, setTitle, statusClass, statusLabel, openDrawer } from "/as
14
14
  import { navigate } from "/assets/app-nav.js";
15
15
  import { settingsPath } from "/assets/app-route.js";
16
16
  import { InstanceSwitch } from "/assets/instance-switch.js";
17
+ import { modelText } from "/assets/instance-nav.js";
17
18
  import { HeaderTools } from "/assets/header-tools.js";
18
19
  import { Menu } from "/assets/ui-menu.js";
20
+ import { themeMenuItems, themesStore } from "/assets/app-themes.js";
21
+ import { colorMenuItem } from "/assets/identity-color.js";
22
+ import { IdentityCard, useCard } from "/assets/identity-card.js";
23
+ import { AgentWidget, instanceBinds, widgetFor } from "/assets/agent-widget.js";
19
24
  import { Empty, ErrorState, Skeleton } from "/assets/ui-states.js";
20
25
  import { toast } from "/assets/ui-toast.js";
21
26
  import "./chat-strings.js";
@@ -27,10 +32,10 @@ export function DetailsPanel({ route, navKey }) {
27
32
  const name = route.instance;
28
33
  const inst = app.instances.find((i) => i.name === name) || null;
29
34
  useEffect(() => { setTitle(`${name} · ${t("app.details")}`); }, [name, navKey]);
30
- if (!app.ready) return html`<div class="panel p-details"><${PanelHeader} title=${name} /><div class="panel-body"><${Skeleton} lines=${6} /></div></div>`;
35
+ if (!app.ready) return html`<div data-effect-panel="details" class="panel p-details"><${PanelHeader} title=${name} /><div data-trust="panel-content" class="panel-body"><${Skeleton} lines=${6} /></div></div>`;
31
36
  if (!inst) {
32
- return html`<div class="panel p-details"><${PanelHeader} title=${name} />
33
- <div class="panel-body center"><${Empty} icon="alert" title=${t("chat.notFound", name)} hint=${t("chat.notFoundHint")}
37
+ return html`<div data-effect-panel="details" class="panel p-details"><${PanelHeader} title=${name} />
38
+ <div data-trust="panel-content" class="panel-body center"><${Empty} icon="alert" title=${t("chat.notFound", name)} hint=${t("chat.notFoundHint")}
34
39
  action=${html`<button type="button" class="btn only-narrow" onClick=${openDrawer}>${t("chat.backToList")}</button>`} /></div></div>`;
35
40
  }
36
41
  return html`<${DetailsView} key=${name} name=${name} inst=${inst} navKey=${navKey} exec=${app.exec[name]}
@@ -38,7 +43,10 @@ export function DetailsPanel({ route, navKey }) {
38
43
  }
39
44
 
40
45
  function DetailsView({ name, inst, navKey, exec, awaiting }) {
46
+ useStore(themesStore); // #1629: Lock / Unlock follows the themes as read
41
47
  const lease = useLease(`details:${name}`);
48
+ // #1629 r4 §1: the identity card at the top, read when the status frame's card_v moves.
49
+ const card = useCard(name, inst.card_v);
42
50
  const [d, setD] = useState(null);
43
51
  const [failed, setFailed] = useState(false);
44
52
  const [busy, setBusy] = useState("");
@@ -92,6 +100,8 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
92
100
  { key: "edit", label: t("chat.dEditInSettings"), icon: "settings", onSelect: editInSettings },
93
101
  st === "running" ? { key: "pause", label: t("chat.dPause"), icon: "pause", disabled: !!busy, onSelect: () => pauseWake("pause") } : null,
94
102
  st === "paused" ? { key: "wake", label: t("chat.dWake"), icon: "play", disabled: !!busy, onSelect: () => pauseWake("wake") } : null,
103
+ colorMenuItem(name, inst), // #1958
104
+ ...themeMenuItems(name), // #1629
95
105
  ] : [
96
106
  { key: "edit", label: t("chat.dEditInSettings"), icon: "settings", onSelect: editInSettings },
97
107
  st === "running" ? null : st === "paused" ? null : { key: "start", label: t("chat.start"), icon: "play", disabled: !!busy, onSelect: () => lifecycle("start") },
@@ -99,6 +109,9 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
99
109
  st === "running" ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", disabled: !!busy, onSelect: () => lifecycle("stop") } : null,
100
110
  st === "running" ? { key: "pause", label: t("chat.dPause"), icon: "pause", disabled: !!busy, onSelect: () => pauseWake("pause") } : null,
101
111
  st === "paused" ? { key: "wake", label: t("chat.dWake"), icon: "play", disabled: !!busy, onSelect: () => pauseWake("wake") } : null,
112
+ colorMenuItem(name, inst), // #1958: its identity colour (the public link too)
113
+ // #1629: a person's Lock and Reset of this agent's own web theme (not over the public link).
114
+ ...themeMenuItems(name),
102
115
  ];
103
116
 
104
117
  const cls = statusClass(inst, exec, awaiting);
@@ -131,7 +144,7 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
131
144
  <button type="button" class="btn btn-sm d-edit-settings" onClick=${editInSettings}>${t("chat.dEditInSettings")}</button></div></section>
132
145
  <section class="card"><h3>${t("chat.dRuntime")}</h3>
133
146
  ${row(t("chat.dBackend"), d.backend || "--")}
134
- ${row(t("chat.dModel"), `${d.model || sl.model?.display_name || "--"}${d.model_source === "live" ? "" : src(d.model_source)}`)}
147
+ ${row(t("chat.dModel"), `${(d.model && modelText(d)) || sl.model?.display_name || "--"}${d.model_source === "live" ? "" : src(d.model_source)}`)}
135
148
  ${d.effort ? row(t("chat.dEffort"), `${d.effort}${src(d.effort_source)}`) : null}
136
149
  ${row(t("chat.dCost"), `$${cost.toFixed(2)} ${t("chat.dCostNote")}`)}
137
150
  ${row(t("chat.dContext"), html`<span class="pct">${ctx != null ? `${Math.round(ctx)}%` : "--"}</span>${ctx != null ? bar(ctx, ctx > 80 ? "warn" : "") : null}`)}
@@ -141,8 +154,9 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
141
154
  <span class="mono muted">${a.timestamp ? new Date(`${a.timestamp.replace(" ", "T")}Z`).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : ""}</span>
142
155
  <span class="ev">${a.event}</span> ${a.summary || ""}</div>`) : html`<p class="note">${t("chat.dNoActivity")}</p>`}</section>`;
143
156
  }
144
- return html`<div class="panel p-details">
157
+ return html`<div data-effect-panel="details" class="panel p-details">
145
158
  <${PanelHeader} title=${name} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="details" />`}><${HeaderTools} navKey=${navKey} /><${Menu} items=${items} label=${t("app.more")} /></${PanelHeader}>
146
- <div class="panel-body"><div class="col" ref=${body}>${busy ? html`<p class="note" role="status">${t("chat.dWorking")}</p>` : null}${content}</div></div>
159
+ <div data-trust="panel-content" class="panel-body"><div class="col" ref=${body}>${busy ? html`<p class="note" role="status">${t("chat.dWorking")}</p>` : null}<${IdentityCard} name=${name} card=${card} />
160
+ <${AgentWidget} slot="details" widget=${widgetFor(name, "details")} binds=${instanceBinds(inst, exec, awaiting)} />${content}</div></div>
147
161
  </div>`;
148
162
  }