@songsid/agend 2.2.1 → 2.3.0-beta.10

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 (437) 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 +29 -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.d.ts +9 -0
  61. package/dist/channel/adapters/discord.js +52 -2
  62. package/dist/channel/adapters/discord.js.map +1 -1
  63. package/dist/channel/adapters/telegram.d.ts +8 -1
  64. package/dist/channel/adapters/telegram.js +74 -4
  65. package/dist/channel/adapters/telegram.js.map +1 -1
  66. package/dist/channel/mcp-tools.js +48 -2
  67. package/dist/channel/mcp-tools.js.map +1 -1
  68. package/dist/channel/types.d.ts +21 -0
  69. package/dist/classic-channel-manager.d.ts +10 -5
  70. package/dist/classic-channel-manager.js +12 -9
  71. package/dist/classic-channel-manager.js.map +1 -1
  72. package/dist/cli.js +130 -31
  73. package/dist/cli.js.map +1 -1
  74. package/dist/config-validator.js +119 -4
  75. package/dist/config-validator.js.map +1 -1
  76. package/dist/daemon.d.ts +5 -0
  77. package/dist/daemon.js +273 -22
  78. package/dist/daemon.js.map +1 -1
  79. package/dist/decision-list.d.ts +25 -0
  80. package/dist/decision-list.js +30 -0
  81. package/dist/decision-list.js.map +1 -0
  82. package/dist/delivery-outbox.d.ts +10 -0
  83. package/dist/delivery-outbox.js +12 -1
  84. package/dist/delivery-outbox.js.map +1 -1
  85. package/dist/fleet-context.d.ts +15 -0
  86. package/dist/fleet-manager.d.ts +506 -11
  87. package/dist/fleet-manager.js +1784 -130
  88. package/dist/fleet-manager.js.map +1 -1
  89. package/dist/fleet-traffic.d.ts +74 -0
  90. package/dist/fleet-traffic.js +196 -0
  91. package/dist/fleet-traffic.js.map +1 -0
  92. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
  93. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
  94. package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
  95. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  96. package/dist/general-knowledge/skills/web-appearance/SKILL.md +492 -0
  97. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +476 -0
  98. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  99. package/dist/human-delivery-receipts.d.ts +24 -1
  100. package/dist/human-delivery-receipts.js +64 -11
  101. package/dist/human-delivery-receipts.js.map +1 -1
  102. package/dist/instance-avatar.js +3 -0
  103. package/dist/instance-avatar.js.map +1 -1
  104. package/dist/instance-config-impact.js +6 -1
  105. package/dist/instance-config-impact.js.map +1 -1
  106. package/dist/instance-lifecycle.js +9 -4
  107. package/dist/instance-lifecycle.js.map +1 -1
  108. package/dist/instance-names.d.ts +4 -0
  109. package/dist/instance-names.js +12 -0
  110. package/dist/instance-names.js.map +1 -0
  111. package/dist/instructions.d.ts +12 -1
  112. package/dist/instructions.js +13 -2
  113. package/dist/instructions.js.map +1 -1
  114. package/dist/locale.js +100 -8
  115. package/dist/locale.js.map +1 -1
  116. package/dist/ls-attach-resolve.d.ts +95 -0
  117. package/dist/ls-attach-resolve.js +174 -0
  118. package/dist/ls-attach-resolve.js.map +1 -0
  119. package/dist/memory-hogs.d.ts +27 -0
  120. package/dist/memory-hogs.js +124 -0
  121. package/dist/memory-hogs.js.map +1 -0
  122. package/dist/needs-you-hub.d.ts +4 -0
  123. package/dist/needs-you-hub.js +31 -6
  124. package/dist/needs-you-hub.js.map +1 -1
  125. package/dist/needs-you.d.ts +1 -13
  126. package/dist/needs-you.js +3 -37
  127. package/dist/needs-you.js.map +1 -1
  128. package/dist/outbound-handlers.d.ts +15 -0
  129. package/dist/outbound-handlers.js +152 -11
  130. package/dist/outbound-handlers.js.map +1 -1
  131. package/dist/outbound-schemas.d.ts +228 -4
  132. package/dist/outbound-schemas.js +147 -1
  133. package/dist/outbound-schemas.js.map +1 -1
  134. package/dist/public-web-gateway.js +21 -1
  135. package/dist/public-web-gateway.js.map +1 -1
  136. package/dist/public-web-link.d.ts +9 -0
  137. package/dist/public-web-link.js +8 -0
  138. package/dist/public-web-link.js.map +1 -1
  139. package/dist/quickstart-api.d.ts +10 -1
  140. package/dist/quickstart-api.js +12 -4
  141. package/dist/quickstart-api.js.map +1 -1
  142. package/dist/reply-buttons-controller.d.ts +4 -0
  143. package/dist/reply-buttons-controller.js +2 -2
  144. package/dist/reply-buttons-controller.js.map +1 -1
  145. package/dist/repo-worktree-guard.d.ts +36 -0
  146. package/dist/repo-worktree-guard.js +143 -0
  147. package/dist/repo-worktree-guard.js.map +1 -0
  148. package/dist/restart-progress.d.ts +65 -1
  149. package/dist/restart-progress.js +236 -10
  150. package/dist/restart-progress.js.map +1 -1
  151. package/dist/rollout-index.d.ts +5 -0
  152. package/dist/rollout-index.js +26 -5
  153. package/dist/rollout-index.js.map +1 -1
  154. package/dist/scheduler/scheduler.d.ts +70 -1
  155. package/dist/scheduler/scheduler.js +256 -17
  156. package/dist/scheduler/scheduler.js.map +1 -1
  157. package/dist/service-installer.d.ts +2 -1
  158. package/dist/service-installer.js +18 -3
  159. package/dist/service-installer.js.map +1 -1
  160. package/dist/settings-api.d.ts +19 -0
  161. package/dist/settings-api.js +210 -2
  162. package/dist/settings-api.js.map +1 -1
  163. package/dist/settings-change.js +112 -0
  164. package/dist/settings-change.js.map +1 -1
  165. package/dist/settings-effect.js +32 -2
  166. package/dist/settings-effect.js.map +1 -1
  167. package/dist/settings-http-confirmation.js +5 -0
  168. package/dist/settings-http-confirmation.js.map +1 -1
  169. package/dist/tmux-manager.d.ts +5 -0
  170. package/dist/tmux-manager.js +16 -0
  171. package/dist/tmux-manager.js.map +1 -1
  172. package/dist/tool-permissions.d.ts +4 -6
  173. package/dist/tool-permissions.js +100 -2
  174. package/dist/tool-permissions.js.map +1 -1
  175. package/dist/topic-commands.d.ts +17 -7
  176. package/dist/topic-commands.js +39 -21
  177. package/dist/topic-commands.js.map +1 -1
  178. package/dist/topic-link.d.ts +128 -0
  179. package/dist/topic-link.js +269 -0
  180. package/dist/topic-link.js.map +1 -0
  181. package/dist/types.d.ts +61 -1
  182. package/dist/ui/app-effects.js +233 -0
  183. package/dist/ui/chat-ambient.js +307 -0
  184. package/dist/ui/chat-effect-posters.js +42 -0
  185. package/dist/ui/chat-effects.js +160 -0
  186. package/dist/ui/chat-live-replies.js +58 -0
  187. package/dist/ui/chat-render.js +9 -6
  188. package/dist/ui/chat-store.js +9 -3
  189. package/dist/ui/chat-strings.js +70 -2
  190. package/dist/ui/chat-thread.js +191 -37
  191. package/dist/ui/chat-tour.js +1 -1
  192. package/dist/ui/effect-trial-controller.js +124 -0
  193. package/dist/ui/effect-trial-offer.js +31 -0
  194. package/dist/ui/effect-trial-panel.js +40 -0
  195. package/dist/ui/fleet-cache.js +23 -1
  196. package/dist/ui/fleet-canvas-layout.js +111 -0
  197. package/dist/ui/fleet-canvas.js +525 -0
  198. package/dist/ui/fleet-org.js +27 -12
  199. package/dist/ui/fleet-traffic.js +343 -0
  200. package/dist/ui/fleet-usage.js +401 -0
  201. package/dist/ui/image-lightbox.js +1 -1
  202. package/dist/ui/panel-chat.js +203 -57
  203. package/dist/ui/panel-details.js +20 -6
  204. package/dist/ui/panel-fleet.js +43 -18
  205. package/dist/ui/panel-log.js +6 -6
  206. package/dist/ui/panel-needs.js +3 -3
  207. package/dist/ui/panel-settings.js +34 -13
  208. package/dist/ui/preview-panel.js +25 -9
  209. package/dist/ui/preview.js +3 -2
  210. package/dist/ui/settings-appearance.js +349 -0
  211. package/dist/ui/settings-catalog.js +92 -0
  212. package/dist/ui/settings-dialogs.js +40 -12
  213. package/dist/ui/settings-effects.js +202 -0
  214. package/dist/ui/settings-guests.js +117 -0
  215. package/dist/ui/settings-learn.js +25 -0
  216. package/dist/ui/settings-model.js +15 -6
  217. package/dist/ui/settings-mods.js +271 -0
  218. package/dist/ui/settings-owner-effects.js +80 -0
  219. package/dist/ui/settings-strings.js +333 -10
  220. package/dist/ui/settings-web-authority.js +91 -0
  221. package/dist/ui/shared/agend-auth.js +28 -4
  222. package/dist/ui/shared/agent-widget.js +248 -0
  223. package/dist/ui/shared/app-i18n.js +6 -2
  224. package/dist/ui/shared/app-prefs.js +34 -13
  225. package/dist/ui/shared/app-route.js +24 -2
  226. package/dist/ui/shared/app-session.js +3 -1
  227. package/dist/ui/shared/app-shell.js +102 -23
  228. package/dist/ui/shared/app-store.js +28 -1
  229. package/dist/ui/shared/app-stream.js +14 -7
  230. package/dist/ui/shared/app-themes.js +206 -0
  231. package/dist/ui/shared/app.css +565 -4
  232. package/dist/ui/shared/app.js +23 -10
  233. package/dist/ui/shared/card-strings.js +32 -0
  234. package/dist/ui/shared/effect-app-runtime.js +297 -0
  235. package/dist/ui/shared/effect-baseline.js +64 -0
  236. package/dist/ui/shared/effect-chat-runtime.js +167 -0
  237. package/dist/ui/shared/effect-cursor.js +125 -0
  238. package/dist/ui/shared/effect-frame.js +146 -0
  239. package/dist/ui/shared/effect-geometry.js +137 -0
  240. package/dist/ui/shared/effect-host-style.js +10 -0
  241. package/dist/ui/shared/effect-owner-regions.js +74 -0
  242. package/dist/ui/shared/effect-owner-source.js +115 -0
  243. package/dist/ui/shared/effect-policy.js +151 -0
  244. package/dist/ui/shared/effect-poster-reader.js +98 -0
  245. package/dist/ui/shared/effect-posters.js +117 -0
  246. package/dist/ui/shared/effect-profiler.js +184 -0
  247. package/dist/ui/shared/effect-profiles.js +65 -0
  248. package/dist/ui/shared/effect-registry.js +535 -0
  249. package/dist/ui/shared/effect-runtime-loader.js +234 -0
  250. package/dist/ui/shared/effect-safety.js +153 -0
  251. package/dist/ui/shared/effect-stock-snapshot.js +43 -0
  252. package/dist/ui/shared/effect-stock-trust.js +217 -0
  253. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  254. package/dist/ui/shared/effect-trial-source.js +103 -0
  255. package/dist/ui/shared/effect-trial-stage.js +170 -0
  256. package/dist/ui/shared/effect-trial.css +40 -0
  257. package/dist/ui/shared/fx-motion.js +67 -0
  258. package/dist/ui/shared/header-tools.js +1 -1
  259. package/dist/ui/shared/identity-card.js +273 -0
  260. package/dist/ui/shared/identity-color.js +261 -0
  261. package/dist/ui/shared/instance-nav.js +215 -41
  262. package/dist/ui/shared/owner-css.js +221 -0
  263. package/dist/ui/shared/owner-effect-prefs.js +117 -0
  264. package/dist/ui/shared/panel-view.js +103 -10
  265. package/dist/ui/shared/signin.js +2 -0
  266. package/dist/ui/shared/tokens.css +9 -3
  267. package/dist/ui/shared/ui-dialog.js +1 -1
  268. package/dist/ui/shared/ui-icons.js +3 -0
  269. package/dist/ui/shared/ui-menu.js +1 -1
  270. package/dist/ui/shared/ui-toast.js +1 -1
  271. package/dist/ui/shared/usage-dialog.js +22 -2
  272. package/dist/ui/shared/view-strings.js +98 -2
  273. package/dist/ui/shared/web-authorize.js +151 -0
  274. package/dist/ui/signin.html +1 -0
  275. package/dist/ui/web-terminal/terminal.css +4 -4
  276. package/dist/usage/providers.d.ts +28 -0
  277. package/dist/usage/providers.js +126 -36
  278. package/dist/usage/providers.js.map +1 -1
  279. package/dist/usage-report.d.ts +158 -0
  280. package/dist/usage-report.js +226 -0
  281. package/dist/usage-report.js.map +1 -0
  282. package/dist/usage-rollup.d.ts +51 -0
  283. package/dist/usage-rollup.js +168 -0
  284. package/dist/usage-rollup.js.map +1 -0
  285. package/dist/view-api.d.ts +68 -0
  286. package/dist/view-api.js +249 -8
  287. package/dist/view-api.js.map +1 -1
  288. package/dist/web-ambient.d.ts +72 -0
  289. package/dist/web-ambient.js +161 -0
  290. package/dist/web-ambient.js.map +1 -0
  291. package/dist/web-api.d.ts +111 -5
  292. package/dist/web-api.js +1235 -58
  293. package/dist/web-api.js.map +1 -1
  294. package/dist/web-appearance-limits.d.ts +82 -0
  295. package/dist/web-appearance-limits.js +83 -0
  296. package/dist/web-appearance-limits.js.map +1 -0
  297. package/dist/web-appearance-policy.d.ts +64 -0
  298. package/dist/web-appearance-policy.js +87 -0
  299. package/dist/web-appearance-policy.js.map +1 -0
  300. package/dist/web-appearance-tools.d.ts +53 -0
  301. package/dist/web-appearance-tools.js +124 -0
  302. package/dist/web-appearance-tools.js.map +1 -0
  303. package/dist/web-appearance-view.d.ts +124 -0
  304. package/dist/web-appearance-view.js +278 -0
  305. package/dist/web-appearance-view.js.map +1 -0
  306. package/dist/web-appearance.d.ts +93 -0
  307. package/dist/web-appearance.js +373 -0
  308. package/dist/web-appearance.js.map +1 -0
  309. package/dist/web-asset-parts.d.ts +93 -0
  310. package/dist/web-asset-parts.js +133 -0
  311. package/dist/web-asset-parts.js.map +1 -0
  312. package/dist/web-auth.js +20 -2
  313. package/dist/web-auth.js.map +1 -1
  314. package/dist/web-card.d.ts +110 -0
  315. package/dist/web-card.js +266 -0
  316. package/dist/web-card.js.map +1 -0
  317. package/dist/web-channel-echo.d.ts +7 -1
  318. package/dist/web-channel-echo.js +6 -2
  319. package/dist/web-channel-echo.js.map +1 -1
  320. package/dist/web-chat-history.d.ts +59 -0
  321. package/dist/web-chat-history.js +102 -4
  322. package/dist/web-chat-history.js.map +1 -1
  323. package/dist/web-commands.d.ts +1 -0
  324. package/dist/web-effect-bootstrap.d.ts +12 -0
  325. package/dist/web-effect-bootstrap.js +49 -0
  326. package/dist/web-effect-bootstrap.js.map +1 -0
  327. package/dist/web-effect-bundle.d.ts +28 -0
  328. package/dist/web-effect-bundle.js +171 -0
  329. package/dist/web-effect-bundle.js.map +1 -0
  330. package/dist/web-effect-canonical.d.ts +8 -0
  331. package/dist/web-effect-canonical.js +27 -0
  332. package/dist/web-effect-canonical.js.map +1 -0
  333. package/dist/web-effect-library.d.ts +114 -0
  334. package/dist/web-effect-library.js +530 -0
  335. package/dist/web-effect-library.js.map +1 -0
  336. package/dist/web-effect-playback.d.ts +30 -0
  337. package/dist/web-effect-playback.js +71 -0
  338. package/dist/web-effect-playback.js.map +1 -0
  339. package/dist/web-effect-poster.d.ts +8 -0
  340. package/dist/web-effect-poster.js +52 -0
  341. package/dist/web-effect-poster.js.map +1 -0
  342. package/dist/web-effect-profiles.d.ts +118 -0
  343. package/dist/web-effect-profiles.js +180 -0
  344. package/dist/web-effect-profiles.js.map +1 -0
  345. package/dist/web-effect-service.d.ts +93 -0
  346. package/dist/web-effect-service.js +335 -0
  347. package/dist/web-effect-service.js.map +1 -0
  348. package/dist/web-effect-tools.d.ts +48 -0
  349. package/dist/web-effect-tools.js +129 -0
  350. package/dist/web-effect-tools.js.map +1 -0
  351. package/dist/web-effect-trials.d.ts +86 -0
  352. package/dist/web-effect-trials.js +204 -0
  353. package/dist/web-effect-trials.js.map +1 -0
  354. package/dist/web-effect-upload.d.ts +27 -0
  355. package/dist/web-effect-upload.js +185 -0
  356. package/dist/web-effect-upload.js.map +1 -0
  357. package/dist/web-effect-worker.d.ts +8 -0
  358. package/dist/web-effect-worker.js +170 -0
  359. package/dist/web-effect-worker.js.map +1 -0
  360. package/dist/web-effect.d.ts +103 -0
  361. package/dist/web-effect.js +153 -0
  362. package/dist/web-effect.js.map +1 -0
  363. package/dist/web-guest-access.d.ts +125 -0
  364. package/dist/web-guest-access.js +234 -0
  365. package/dist/web-guest-access.js.map +1 -0
  366. package/dist/web-guest-rooms.d.ts +82 -0
  367. package/dist/web-guest-rooms.js +135 -0
  368. package/dist/web-guest-rooms.js.map +1 -0
  369. package/dist/web-identity-color.d.ts +87 -0
  370. package/dist/web-identity-color.js +209 -0
  371. package/dist/web-identity-color.js.map +1 -0
  372. package/dist/web-identity-palette.d.ts +113 -0
  373. package/dist/web-identity-palette.js +84 -0
  374. package/dist/web-identity-palette.js.map +1 -0
  375. package/dist/web-image-meta.d.ts +25 -0
  376. package/dist/web-image-meta.js +200 -0
  377. package/dist/web-image-meta.js.map +1 -0
  378. package/dist/web-login.d.ts +47 -0
  379. package/dist/web-login.js +115 -0
  380. package/dist/web-login.js.map +1 -1
  381. package/dist/web-mod-agent.d.ts +50 -0
  382. package/dist/web-mod-agent.js +207 -0
  383. package/dist/web-mod-agent.js.map +1 -0
  384. package/dist/web-mod-bundle.d.ts +157 -0
  385. package/dist/web-mod-bundle.js +529 -0
  386. package/dist/web-mod-bundle.js.map +1 -0
  387. package/dist/web-mod-export.d.ts +68 -0
  388. package/dist/web-mod-export.js +100 -0
  389. package/dist/web-mod-export.js.map +1 -0
  390. package/dist/web-mod-store.d.ts +289 -0
  391. package/dist/web-mod-store.js +638 -0
  392. package/dist/web-mod-store.js.map +1 -0
  393. package/dist/web-org.d.ts +3 -3
  394. package/dist/web-org.js +11 -17
  395. package/dist/web-org.js.map +1 -1
  396. package/dist/web-owner-css-store.d.ts +180 -0
  397. package/dist/web-owner-css-store.js +384 -0
  398. package/dist/web-owner-css-store.js.map +1 -0
  399. package/dist/web-owner-css.d.ts +58 -0
  400. package/dist/web-owner-css.js +552 -0
  401. package/dist/web-owner-css.js.map +1 -0
  402. package/dist/web-prefs.d.ts +62 -0
  403. package/dist/web-prefs.js +232 -36
  404. package/dist/web-prefs.js.map +1 -1
  405. package/dist/web-preview.d.ts +15 -3
  406. package/dist/web-preview.js +28 -9
  407. package/dist/web-preview.js.map +1 -1
  408. package/dist/web-returning-device.d.ts +41 -0
  409. package/dist/web-returning-device.js +57 -5
  410. package/dist/web-returning-device.js.map +1 -1
  411. package/dist/web-session-authorization.d.ts +132 -0
  412. package/dist/web-session-authorization.js +193 -0
  413. package/dist/web-session-authorization.js.map +1 -0
  414. package/dist/web-session.d.ts +70 -10
  415. package/dist/web-session.js +137 -22
  416. package/dist/web-session.js.map +1 -1
  417. package/dist/web-settings-patch.d.ts +67 -2
  418. package/dist/web-settings-patch.js +196 -1
  419. package/dist/web-settings-patch.js.map +1 -1
  420. package/dist/web-settings-tools.d.ts +89 -0
  421. package/dist/web-settings-tools.js +270 -0
  422. package/dist/web-settings-tools.js.map +1 -0
  423. package/dist/web-shell-routes.d.ts +2 -2
  424. package/dist/web-shell-routes.js +2 -2
  425. package/dist/web-shell-routes.js.map +1 -1
  426. package/dist/web-theme-store.d.ts +257 -0
  427. package/dist/web-theme-store.js +627 -0
  428. package/dist/web-theme-store.js.map +1 -0
  429. package/dist/web-theme.d.ts +123 -0
  430. package/dist/web-theme.js +416 -0
  431. package/dist/web-theme.js.map +1 -0
  432. package/dist/web-widget.d.ts +72 -0
  433. package/dist/web-widget.js +245 -0
  434. package/dist/web-widget.js.map +1 -0
  435. package/dist/workflow-templates/default.md +1 -1
  436. package/package.json +1 -1
  437. 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 } 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,11 +199,43 @@ 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
241
  useEffect(() => { store.openHistory(name, lease); }, [name]);
@@ -217,97 +277,176 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
217
277
  running ? null : { key: "start", label: t("chat.start"), icon: "play", onSelect: () => action("start") },
218
278
  running ? { key: "restart", label: t("chat.restart"), icon: "restart", onSelect: () => action("restart") } : null,
219
279
  running ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", onSelect: () => action("stop") } : null,
280
+ // #1958: its identity colour and where it shows (the public link too, as profile edits).
281
+ colorMenuItem(name, inst),
282
+ // #1629: a person's control over this agent's theme — not over the public link (it only views themes).
283
+ ...themeMenuItems(name),
284
+ // #2117: its ambient — why it is not showing here (with the way to change that), and a person's Clear.
285
+ ...ambientMenuItems(name, { publicLink: document.body.dataset.publicLink === "1", clear: clearAmbient }),
286
+ chatEffectsMenuAvailable() ? { key: "effects", label: t("chatFx.title"), onSelect: () => setDialog("effects") } : null,
220
287
  { key: "delete", label: t("chat.delete"), icon: "trash", danger: true, onSelect: () => setDialog("delete") },
221
288
  ];
222
289
  const cls = statusClass(inst, exec, awaiting);
290
+ // #1920 phase 3: a guest has this chat only — no Details, View or Log to switch to, no lifecycle, colour, theme,
291
+ // effect management or delete (a guest's status frame carries no model or effort either, so those chips do not show).
292
+ const guest = guestPage();
223
293
  // #1269 quick actions: the model and effort it runs, each a way to /model or /effort (the same pick list).
224
294
  const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
225
295
  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}
296
+ ${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
297
  ${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
298
  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}>
299
+ <${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}>
300
+ <${ChatCard} key=${name} name=${name} card=${card} />
301
+ <${AgentWidget} slot="chat_header" widget=${widgetFor(name, "chat_header")} binds=${instanceBinds(inst, exec, awaiting)} />
230
302
  <div class="chat-split" ref=${split}>
231
- <div class="chat-main">
303
+ <div ref=${effectSurface} class=${`chat-main${theme && theme.background ? " has-th-bg" : ""}`}>
304
+ ${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}
305
+ <${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
306
+ <${AmbientMotionNote} name=${name} canvasPlaying=${canvasPlayback?.name === name && canvasPlayback.playing} />
232
307
  <${Thread} name=${name} th=${thread} />
233
- <${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} />
308
+ <${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${theme ? theme.workingLabel : undefined} />
309
+ <${ChatAmbient} name=${name} surface=${effectSurface} part="front" />
234
310
  </div>
235
311
  <${PreviewPanel} name=${name} split=${split} msgs=${() => store.state.msgs[name] || []} subscribe=${(fn) => store.subscribe(fn)}
236
312
  keyOf=${msgKey} reveal=${(k) => thread.current && thread.current.reveal(k)} download=${downloadHtml} allow=${(commit) => setPreviewOptIn(true, commit)} />
237
313
  </div>
238
314
  <${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>
315
+ <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
316
  ${dialog === "delete" ? html`<${DeleteDialog} name=${name} onClose=${() => setDialog(null)} />` : null}
241
317
  </div>`;
242
318
  }
243
319
 
244
- /** The thread: Preact owns the elements, the keyed renderer owns what is inside the list (chat-thread.js). */
320
+ /**
321
+ * #2053: the last THREAD_KEEP chats' threads are kept when their view leaves (the chat view is keyed by instance, so
322
+ * each switch built the thread again: a revisit was no faster than a first visit). A kept thread is its list element
323
+ * and its renderer, and nothing more: it leaves as a chat always did (previews stop), with no subscription or timer
324
+ * left; shown again, one render draws only what changed meanwhile, where the reader was. A new language, another
325
+ * document, or more than THREAD_KEEP lets one go.
326
+ */
327
+ export const THREAD_KEEP = 4;
328
+ const keptThreads = new Map(); // name → { thread, list, sink, lang }
329
+ /** Tests only: let every kept thread go. */
330
+ export function forgetKeptThreads() { for (const e of keptThreads.values()) letGo(e, true); keptThreads.clear(); }
331
+ function letGo(e, own) { e.thread.dispose(own); e.list.remove(); }
332
+
333
+ /** A chat's thread: its own list element and renderer, and a sink for the view that shows it. */
334
+ function threadEntry(name, doc) {
335
+ const list = doc.createElement("div");
336
+ list.className = "thread";
337
+ list.setAttribute("role", "log");
338
+ list.setAttribute("aria-live", "off");
339
+ list.setAttribute("aria-label", t("chat.conversationWith", name));
340
+ const e = { list, sink: null, lang: lang(), thread: null };
341
+ const thread = createThread(list, doc.createElement("div"), {
342
+ t, tf: t, isUser: (x) => store.isUser(x, name), onJump: (j) => e.sink?.setJump(j), onEmpty: (v) => e.sink?.setEmpty(v),
343
+ setPreviewOptIn, copyText, download: downloadHtml, toggleWrap,
344
+ effectTrialStatus: x => getAppEffects()?.trialStatus(x) ?? "localAdmin",
345
+ effectTrialFailure: x => getAppEffects()?.trialFailure(x),
346
+ fixEffectTrial: (x, reason) => {
347
+ if (reason === "reducedMotion") setFollowsReducedMotion(false);
348
+ else if (reason === "busy") getAppEffects()?.stopTrial();
349
+ else if (reason === "transport") location.reload();
350
+ else if (reason === "localAdmin") location.href = "/signin?next=" + encodeURIComponent(location.pathname + location.search);
351
+ else navigate("/settings/appearance");
352
+ },
353
+ canTryEffect: x => getAppEffects()?.canTry(x) === true,
354
+ 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")); }); },
355
+ clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
356
+ replyButtonBusy: (id) => store.state.rbBusy.has(id),
357
+ pollMine: (id) => store.state.pollMine[id], // #1823: this device's own vote
358
+ serverNow: () => Date.now() + store.state.clockSkew, // #1823: the deadline is the server's
359
+ panel: { shown: shownKey, open: (spec, o) => openPanel(spec, o), version: panelVersion, claim: claimPanel, claims: panelClaims }, // #1481, #1704
360
+ openImage: openLightbox, // an image, full size, in the page
361
+ emojis: (id) => !!emojiIndex && emojiIndex.known(id), // #1630: the fleet's server emoji
362
+ mentions: (kind, id) => (mentionIndex ? mentionIndex.name(kind, id) : null), // #1682: mention names
363
+ });
364
+ e.thread = thread;
365
+ return e;
366
+ }
367
+ /** While a thread is shown: what it follows. Returns what lets it all go. */
368
+ function follow(name, thread) {
369
+ // #1630: the server-emoji list is read once a message shows a tag; when it arrives, those messages are drawn again.
370
+ // #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
371
+ const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
372
+ const offs = [];
373
+ if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
374
+ if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.state.msgs[name] || [])));
375
+ wantEmojis(store.state.msgs[name]);
376
+ offs.push(store.subscribe((instance, kind) => {
377
+ if (instance !== name) return;
378
+ if (kind === "msgs") { thread.render(store.state.msgs[name] || []); wantEmojis(store.state.msgs[name]); }
379
+ else if (kind === "sent") thread.jumpLatest();
380
+ }));
381
+ offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
382
+ offs.push(getAppEffects()?.subscribeTrial(() => thread.render(store.state.msgs[name] || [])) ?? (() => {}));
383
+ offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
384
+ return () => { for (const off of offs) off(); };
385
+ }
386
+
387
+ /** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
245
388
  function Thread({ name, th }) {
246
- const scroller = useRef(null), list = useRef(null);
389
+ const scroller = useRef(null), host = useRef(null);
247
390
  const [jump, setJump] = useState({ show: false, unseen: 0 });
248
391
  const [empty, setEmpty] = useState(false);
249
392
  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
- });
393
+ const doc = host.current.ownerDocument;
394
+ let e = keptThreads.get(name);
395
+ // Kept only while it can be shown as it is: the same language, and this page's own document.
396
+ if (e && (e.lang !== lang() || e.list.ownerDocument !== doc)) { letGo(e, true); keptThreads.delete(name); e = undefined; }
397
+ const kept = !!e;
398
+ if (e) keptThreads.delete(name);
399
+ else e = threadEntry(name, doc);
400
+ keptThreads.set(name, e); // the most recent last
401
+ for (const [k, old] of keptThreads) {
402
+ if (keptThreads.size <= THREAD_KEEP) break;
403
+ if (k !== name) { letGo(old, true); keptThreads.delete(k); }
404
+ }
405
+ const thread = e.thread, sc = scroller.current, ls = e.list;
406
+ e.sink = { setJump, setEmpty };
407
+ host.current.append(ls);
408
+ thread.attach(sc);
262
409
  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] || [])) : () => {};
410
+ // 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
411
  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
412
+ if (kept) thread.refreshCards();
413
+ const unfollow = follow(name, thread);
277
414
  const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
278
- const sc = scroller.current, ls = list.current;
279
415
  sc.addEventListener("scroll", onScroll, { passive: true });
280
416
  ls.addEventListener("click", thread.onClick);
281
417
  return () => {
282
- off(); offPv(); offPanel(); offEmojis(); offMentions();
418
+ unfollow();
283
419
  sc.removeEventListener("scroll", onScroll);
284
420
  ls.removeEventListener("click", thread.onClick);
285
- thread.dispose(); // the whole thread goes: every preview in it stops first
421
+ e.sink = null;
286
422
  th.current = null;
423
+ if (keptThreads.get(name) === e) { thread.park(); ls.remove(); } // kept for a later visit: the chat left
424
+ else letGo(e, false); // let go meanwhile (a new language)
287
425
  };
288
426
  }, [name]);
289
427
  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>
428
+ <div class="thread-host" ref=${host}></div>
291
429
  ${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}
430
+ <div class="jump-dock">${jump.show ? html`<button data-trust="thread-control" type="button" class="jump-latest" onClick=${() => th.current && th.current.jumpLatest()}>
431
+ <${Icon} name="down" size=${16} />${jump.unseen > 0 ? t("chat.newBelow", jump.unseen) : t("chat.jumpLatest")}</button>` : null}</div>
294
432
  </div>`;
295
433
  }
296
434
 
297
435
  /** Everything under the thread: prompts, the working line, a failed send, files waiting, and the composer. */
298
- function Dock({ name, inst, lease, exec, awaiting }) {
436
+ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
299
437
  useChatTick(name, ["state"]);
300
438
  const s = store.state;
301
- return html`<div class="dock"><div class="dock-col">
439
+ return html`<div data-trust="composer" class="dock"><div class="dock-col">
302
440
  <${Prompts} name=${name} />
303
- <${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} />
441
+ <${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${workingLabel} />
304
442
  <${CommandCard} name=${name} lease=${lease} />
305
443
  <${QuickActions} name=${name} inst=${inst} lease=${lease} />
306
444
  ${s.failedSends[name] ? html`<div class="failed-send" role="alert"><span>${t("chat.notSent")}</span><span class="txt">${s.failedSends[name]}</span>
307
445
  <button type="button" class="btn btn-primary btn-sm" onClick=${() => store.putBack(name)}>${t("chat.putBack")}</button>
308
446
  <button type="button" class="btn btn-sm" onClick=${() => store.discardFailed(name)}>${t("chat.discard")}</button></div>` : null}
309
447
  <${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
310
- <p class="hint" title=${t("chat.composerMore")}>${t("chat.composerHint")}</p>
448
+ <p class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</p>
449
+ ${guestPage() ? html`<p class="guest-note">${t("chat.guestNote")}</p>` : html`<${AuthorizeChip} />`}
311
450
  </div></div>`;
312
451
  }
313
452
 
@@ -327,7 +466,8 @@ function CommandCard({ name, lease }) {
327
466
  : c.compact === "lost" ? t("chat.compactLost") : c.compact === "not_compacted" ? t("chat.compactNotCompacted") : t("chat.compactUnknown")}</p>` : null}
328
467
  ${c.choices && c.choices.prompt ? html`<pre class="cmd-text">${c.choices.prompt}</pre>` : null}
329
468
  ${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}
469
+ class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined}
470
+ disabled=${!!o.disabled} title=${o.disabled || undefined} onClick=${() => { if (!o.disabled) run(o.id); }}>${o.label}</button>`)}</div>` : null}
331
471
  </div>`;
332
472
  }
333
473
 
@@ -347,14 +487,14 @@ function QuickActions({ name, inst, lease }) {
347
487
  function Prompts({ name }) {
348
488
  const list = store.promptsFor(name);
349
489
  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>
490
+ 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}>
491
+ <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
492
  ${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
493
  ${p.busy ? html`<span class="wait">${t("chat.promptAnswering")}</span>` : null}</div>`}</div>`)}</div>`;
354
494
  }
355
495
 
356
496
  /** "<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 }) {
497
+ function WorkBar({ name, lease, exec, awaiting, workingLabel }) {
358
498
  const s = store.state;
359
499
  const busy = R().isBusy(exec) ? exec : "";
360
500
  const kind = awaiting != null ? "awaiting" : s.stopping[name] && busy ? "stopping" : busy;
@@ -365,7 +505,9 @@ function WorkBar({ name, lease, exec, awaiting }) {
365
505
  return () => lease.clear(h);
366
506
  }, [kind, lease]);
367
507
  if (!kind) return null;
368
- const label = t(kind === "stuck" ? "chat.stuck" : kind === "stopping" ? "chat.stopping" : kind === "awaiting" ? "chat.awaiting" : "chat.working", name);
508
+ // #1629: the agent's own working label (its theme), shown as text — only for "working", never for the other states.
509
+ const label = kind === "working" && workingLabel ? workingLabel
510
+ : t(kind === "stuck" ? "chat.stuck" : kind === "stopping" ? "chat.stopping" : kind === "awaiting" ? "chat.awaiting" : "chat.working", name);
369
511
  const since = s.workingSince[name];
370
512
  return html`<div class=${`work-bar ${kind}`}><span class="pulse" aria-hidden="true"></span><span class="lbl">${label}</span>
371
513
  ${kind === "awaiting" ? html`<span class="note" title=${t("chat.approxNote")}>${awaiting || t("chat.approxNote")}</span>`
@@ -381,8 +523,10 @@ function Composer({ name, lease, busy }) {
381
523
  const value = s.drafts[name] || "";
382
524
  const files = s.pendingFiles[name] || [];
383
525
  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);
526
+ // A file waiting: what goes is a message with its file, never a command (no palette, no command line). A guest never
527
+ // runs one (#1920: the server refuses its slash text), so it has no palette either.
528
+ const guest = guestPage();
529
+ const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
386
530
  const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
387
531
  // Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
388
532
  useLayoutEffect(() => { const el = input.current; if (!el) return; el.style.height = "auto"; el.style.height = `${Math.min(el.scrollHeight, 240)}px`; }, [value]);
@@ -403,6 +547,8 @@ function Composer({ name, lease, busy }) {
403
547
  else complete(pick);
404
548
  return;
405
549
  }
550
+ // #1920: a guest's "/…" would be refused as a command — said here, the draft kept to edit.
551
+ if (guest && /^\s*\//.test(value)) { toast(t("chat.guestNoCommands"), false); return; }
406
552
  const cmd = files.length ? null : parseCommandLine(value);
407
553
  if (cmd && needsArgument(cmd.command) && !cmd.args) { setValue(`/${cmd.command} `); return; }
408
554
  if (cmd) { runLine(cmd); return; }
@@ -428,7 +574,7 @@ function Composer({ name, lease, busy }) {
428
574
  };
429
575
  const optId = (c) => `cmd-${c.name}`;
430
576
  return html`<div class="composer-box">
431
- ${palette ? html`<ul class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
577
+ ${palette ? html`<ul data-trust="palette" class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
432
578
  ${palette.length ? palette.map((c) => html`<li key=${c.name} id=${optId(c)} role="option" aria-selected=${c === pick ? "true" : "false"}
433
579
  class=${c === pick ? "on" : ""} onMouseDown=${(e) => { e.preventDefault(); complete(c); }}>
434
580
  <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
  }