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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. package/README.md +7 -1
  2. package/README.zh-TW.md +7 -1
  3. package/dist/agent-cli-instructions.md +9 -2
  4. package/dist/agent-cli.js +63 -2
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +4 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.d.ts +4 -1
  9. package/dist/agent-file-read.js +189 -16
  10. package/dist/agent-file-read.js.map +1 -1
  11. package/dist/agent-whats-new.d.ts +11 -0
  12. package/dist/agent-whats-new.js +29 -5
  13. package/dist/agent-whats-new.js.map +1 -1
  14. package/dist/auth-api.d.ts +30 -1
  15. package/dist/auth-api.js +45 -13
  16. package/dist/auth-api.js.map +1 -1
  17. package/dist/authority/authority-state.d.ts +38 -0
  18. package/dist/authority/authority-state.js +114 -0
  19. package/dist/authority/authority-state.js.map +1 -0
  20. package/dist/authority/session-authorizations.d.ts +43 -0
  21. package/dist/authority/session-authorizations.js +102 -0
  22. package/dist/authority/session-authorizations.js.map +1 -0
  23. package/dist/authority/web-authority.d.ts +120 -0
  24. package/dist/authority/web-authority.js +157 -0
  25. package/dist/authority/web-authority.js.map +1 -0
  26. package/dist/backend/claude-code.d.ts +2 -2
  27. package/dist/backend/claude-code.js +18 -8
  28. package/dist/backend/claude-code.js.map +1 -1
  29. package/dist/backend/codex-metadata.d.ts +8 -0
  30. package/dist/backend/codex-metadata.js +74 -17
  31. package/dist/backend/codex-metadata.js.map +1 -1
  32. package/dist/backend/codex.js +15 -2
  33. package/dist/backend/codex.js.map +1 -1
  34. package/dist/backend/credential-profile.d.ts +2 -0
  35. package/dist/backend/credential-profile.js +2 -0
  36. package/dist/backend/credential-profile.js.map +1 -1
  37. package/dist/backend/types.d.ts +6 -0
  38. package/dist/backend/types.js +15 -0
  39. package/dist/backend/types.js.map +1 -1
  40. package/dist/backend-availability.d.ts +120 -0
  41. package/dist/backend-availability.js +396 -0
  42. package/dist/backend-availability.js.map +1 -0
  43. package/dist/backend-command.d.ts +23 -3
  44. package/dist/backend-command.js +25 -4
  45. package/dist/backend-command.js.map +1 -1
  46. package/dist/bug-report-cli.d.ts +30 -0
  47. package/dist/bug-report-cli.js +138 -0
  48. package/dist/bug-report-cli.js.map +1 -0
  49. package/dist/bug-report.d.ts +47 -0
  50. package/dist/bug-report.js +136 -0
  51. package/dist/bug-report.js.map +1 -0
  52. package/dist/bundled-skills.d.ts +14 -0
  53. package/dist/bundled-skills.js +29 -0
  54. package/dist/bundled-skills.js.map +1 -0
  55. package/dist/cache-ledger.d.ts +16 -3
  56. package/dist/cache-ledger.js +33 -13
  57. package/dist/cache-ledger.js.map +1 -1
  58. package/dist/cache-service.d.ts +55 -3
  59. package/dist/cache-service.js +152 -16
  60. package/dist/cache-service.js.map +1 -1
  61. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  62. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  63. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  64. package/dist/channel/adapters/discord.d.ts +9 -0
  65. package/dist/channel/adapters/discord.js +49 -4
  66. package/dist/channel/adapters/discord.js.map +1 -1
  67. package/dist/channel/mcp-tools.js +22 -11
  68. package/dist/channel/mcp-tools.js.map +1 -1
  69. package/dist/channel/types.d.ts +6 -0
  70. package/dist/classic-channel-manager.d.ts +10 -5
  71. package/dist/classic-channel-manager.js +12 -9
  72. package/dist/classic-channel-manager.js.map +1 -1
  73. package/dist/cli.js +224 -111
  74. package/dist/cli.js.map +1 -1
  75. package/dist/config-validator.d.ts +6 -0
  76. package/dist/config-validator.js +129 -3
  77. package/dist/config-validator.js.map +1 -1
  78. package/dist/config.d.ts +13 -1
  79. package/dist/config.js +14 -5
  80. package/dist/config.js.map +1 -1
  81. package/dist/cross-instance-notice.d.ts +7 -0
  82. package/dist/cross-instance-notice.js +10 -0
  83. package/dist/cross-instance-notice.js.map +1 -1
  84. package/dist/daemon.d.ts +24 -1
  85. package/dist/daemon.js +151 -19
  86. package/dist/daemon.js.map +1 -1
  87. package/dist/decision-list.d.ts +25 -0
  88. package/dist/decision-list.js +30 -0
  89. package/dist/decision-list.js.map +1 -0
  90. package/dist/delivery-outbox.d.ts +10 -0
  91. package/dist/delivery-outbox.js +17 -1
  92. package/dist/delivery-outbox.js.map +1 -1
  93. package/dist/diagnostic-redaction.d.ts +10 -0
  94. package/dist/diagnostic-redaction.js +167 -0
  95. package/dist/diagnostic-redaction.js.map +1 -0
  96. package/dist/fleet-context.d.ts +10 -0
  97. package/dist/fleet-manager.d.ts +505 -15
  98. package/dist/fleet-manager.js +1870 -204
  99. package/dist/fleet-manager.js.map +1 -1
  100. package/dist/fleet-process-handlers.d.ts +18 -0
  101. package/dist/fleet-process-handlers.js +69 -0
  102. package/dist/fleet-process-handlers.js.map +1 -0
  103. package/dist/fleet-traffic.d.ts +74 -0
  104. package/dist/fleet-traffic.js +196 -0
  105. package/dist/fleet-traffic.js.map +1 -0
  106. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  107. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +17 -6
  108. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
  109. package/dist/general-knowledge/skills/fleet-config/SKILL.md +2 -0
  110. package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
  111. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  112. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  113. package/dist/general-knowledge/skills/web-appearance/SKILL.md +190 -47
  114. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +545 -0
  115. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  116. package/dist/human-delivery-receipts.d.ts +24 -1
  117. package/dist/human-delivery-receipts.js +64 -11
  118. package/dist/human-delivery-receipts.js.map +1 -1
  119. package/dist/instance-config-impact.js +7 -1
  120. package/dist/instance-config-impact.js.map +1 -1
  121. package/dist/instance-lifecycle.d.ts +10 -1
  122. package/dist/instance-lifecycle.js +79 -18
  123. package/dist/instance-lifecycle.js.map +1 -1
  124. package/dist/instance-names.d.ts +4 -0
  125. package/dist/instance-names.js +12 -0
  126. package/dist/instance-names.js.map +1 -0
  127. package/dist/instructions.d.ts +12 -1
  128. package/dist/instructions.js +13 -2
  129. package/dist/instructions.js.map +1 -1
  130. package/dist/locale.js +173 -12
  131. package/dist/locale.js.map +1 -1
  132. package/dist/log-steps.d.ts +1 -0
  133. package/dist/log-steps.js +15 -0
  134. package/dist/log-steps.js.map +1 -1
  135. package/dist/login-controller.d.ts +45 -0
  136. package/dist/login-controller.js +101 -5
  137. package/dist/login-controller.js.map +1 -1
  138. package/dist/ls-attach-resolve.d.ts +96 -0
  139. package/dist/ls-attach-resolve.js +180 -0
  140. package/dist/ls-attach-resolve.js.map +1 -0
  141. package/dist/ls-rows.d.ts +12 -0
  142. package/dist/ls-rows.js +82 -3
  143. package/dist/ls-rows.js.map +1 -1
  144. package/dist/memory-hogs.d.ts +27 -0
  145. package/dist/memory-hogs.js +124 -0
  146. package/dist/memory-hogs.js.map +1 -0
  147. package/dist/needs-you.d.ts +10 -0
  148. package/dist/needs-you.js +2 -1
  149. package/dist/needs-you.js.map +1 -1
  150. package/dist/outbound-handlers.d.ts +11 -0
  151. package/dist/outbound-handlers.js +139 -11
  152. package/dist/outbound-handlers.js.map +1 -1
  153. package/dist/outbound-schemas.d.ts +200 -4
  154. package/dist/outbound-schemas.js +124 -11
  155. package/dist/outbound-schemas.js.map +1 -1
  156. package/dist/public-web-gateway.js +17 -4
  157. package/dist/public-web-gateway.js.map +1 -1
  158. package/dist/public-web-link.d.ts +9 -0
  159. package/dist/public-web-link.js +8 -0
  160. package/dist/public-web-link.js.map +1 -1
  161. package/dist/quickstart-api.d.ts +19 -1
  162. package/dist/quickstart-api.js +94 -4
  163. package/dist/quickstart-api.js.map +1 -1
  164. package/dist/reply-buttons-controller.d.ts +4 -0
  165. package/dist/reply-buttons-controller.js +2 -2
  166. package/dist/reply-buttons-controller.js.map +1 -1
  167. package/dist/restart-progress.d.ts +80 -1
  168. package/dist/restart-progress.js +306 -21
  169. package/dist/restart-progress.js.map +1 -1
  170. package/dist/rollout-index.d.ts +5 -0
  171. package/dist/rollout-index.js +26 -5
  172. package/dist/rollout-index.js.map +1 -1
  173. package/dist/scheduler/db.d.ts +40 -13
  174. package/dist/scheduler/db.js +123 -89
  175. package/dist/scheduler/db.js.map +1 -1
  176. package/dist/scheduler/task-board-text.d.ts +26 -0
  177. package/dist/scheduler/task-board-text.js +135 -0
  178. package/dist/scheduler/task-board-text.js.map +1 -0
  179. package/dist/scheduler/task-board.d.ts +650 -0
  180. package/dist/scheduler/task-board.js +1938 -0
  181. package/dist/scheduler/task-board.js.map +1 -0
  182. package/dist/scheduler/types.d.ts +6 -1
  183. package/dist/search/search-docs.d.ts +58 -0
  184. package/dist/search/search-docs.js +63 -0
  185. package/dist/search/search-docs.js.map +1 -0
  186. package/dist/search/search-index.d.ts +141 -0
  187. package/dist/search/search-index.js +440 -0
  188. package/dist/search/search-index.js.map +1 -0
  189. package/dist/search/search-store.d.ts +122 -0
  190. package/dist/search/search-store.js +236 -0
  191. package/dist/search/search-store.js.map +1 -0
  192. package/dist/search/search-worker.d.ts +1 -0
  193. package/dist/search/search-worker.js +163 -0
  194. package/dist/search/search-worker.js.map +1 -0
  195. package/dist/service-installer.js +12 -0
  196. package/dist/service-installer.js.map +1 -1
  197. package/dist/settings-api.d.ts +23 -0
  198. package/dist/settings-api.js +164 -3
  199. package/dist/settings-api.js.map +1 -1
  200. package/dist/settings-change.js +104 -0
  201. package/dist/settings-change.js.map +1 -1
  202. package/dist/settings-confirmation.d.ts +3 -1
  203. package/dist/settings-confirmation.js +2 -0
  204. package/dist/settings-confirmation.js.map +1 -1
  205. package/dist/settings-effect.js +34 -3
  206. package/dist/settings-effect.js.map +1 -1
  207. package/dist/settings-http-confirmation.d.ts +11 -0
  208. package/dist/settings-http-confirmation.js +10 -0
  209. package/dist/settings-http-confirmation.js.map +1 -1
  210. package/dist/setup-form.d.ts +1 -1
  211. package/dist/setup-form.js +23 -7
  212. package/dist/setup-form.js.map +1 -1
  213. package/dist/spawn-gate.d.ts +1 -0
  214. package/dist/spawn-gate.js +13 -3
  215. package/dist/spawn-gate.js.map +1 -1
  216. package/dist/task-board-tool.d.ts +25 -0
  217. package/dist/task-board-tool.js +154 -0
  218. package/dist/task-board-tool.js.map +1 -0
  219. package/dist/task-board-waker.d.ts +44 -0
  220. package/dist/task-board-waker.js +128 -0
  221. package/dist/task-board-waker.js.map +1 -0
  222. package/dist/tmux-exec-helper.d.ts +94 -0
  223. package/dist/tmux-exec-helper.js +407 -0
  224. package/dist/tmux-exec-helper.js.map +1 -0
  225. package/dist/tmux-manager.d.ts +5 -0
  226. package/dist/tmux-manager.js +42 -11
  227. package/dist/tmux-manager.js.map +1 -1
  228. package/dist/tool-permissions.d.ts +0 -7
  229. package/dist/tool-permissions.js +44 -5
  230. package/dist/tool-permissions.js.map +1 -1
  231. package/dist/topic-commands.d.ts +17 -1
  232. package/dist/topic-commands.js +45 -19
  233. package/dist/topic-commands.js.map +1 -1
  234. package/dist/tunnel/cloudflared.js +37 -2
  235. package/dist/tunnel/cloudflared.js.map +1 -1
  236. package/dist/tunnel/types.d.ts +3 -1
  237. package/dist/tunnel/types.js.map +1 -1
  238. package/dist/types.d.ts +63 -1
  239. package/dist/ui/app-effects.js +90 -32
  240. package/dist/ui/backend-signin.js +297 -0
  241. package/dist/ui/chat-ambient.js +307 -0
  242. package/dist/ui/chat-effect-posters.js +8 -8
  243. package/dist/ui/chat-effects.js +110 -15
  244. package/dist/ui/chat-msg-actions.js +176 -0
  245. package/dist/ui/chat-render.js +21 -3
  246. package/dist/ui/chat-store.js +64 -12
  247. package/dist/ui/chat-strings.js +85 -8
  248. package/dist/ui/chat-thread.js +204 -21
  249. package/dist/ui/effect-trial-controller.js +13 -8
  250. package/dist/ui/effect-trial-panel.js +19 -16
  251. package/dist/ui/effects-waker.js +70 -0
  252. package/dist/ui/fleet-canvas-layout.js +117 -0
  253. package/dist/ui/fleet-canvas.js +555 -0
  254. package/dist/ui/fleet-org.js +27 -12
  255. package/dist/ui/fleet-route-scene.js +207 -0
  256. package/dist/ui/fleet-route-worker.js +21 -0
  257. package/dist/ui/fleet-route.js +88 -0
  258. package/dist/ui/fleet-task-card.js +436 -0
  259. package/dist/ui/fleet-task-common.js +448 -0
  260. package/dist/ui/fleet-tasks.js +535 -0
  261. package/dist/ui/fleet-traffic.js +359 -0
  262. package/dist/ui/fleet-usage.js +435 -0
  263. package/dist/ui/panel-chat.js +264 -66
  264. package/dist/ui/panel-details.js +2 -1
  265. package/dist/ui/panel-fleet.js +65 -83
  266. package/dist/ui/panel-needs.js +4 -2
  267. package/dist/ui/panel-settings.js +56 -11
  268. package/dist/ui/preview.js +3 -2
  269. package/dist/ui/search-dialog.js +349 -0
  270. package/dist/ui/settings-appearance.js +44 -19
  271. package/dist/ui/settings-apply.js +3 -3
  272. package/dist/ui/settings-catalog.js +92 -0
  273. package/dist/ui/settings-confirm.js +7 -2
  274. package/dist/ui/settings-dialogs.js +40 -12
  275. package/dist/ui/settings-effects.js +4 -3
  276. package/dist/ui/settings-guests.js +117 -0
  277. package/dist/ui/settings-learn.js +25 -0
  278. package/dist/ui/settings-model.js +15 -6
  279. package/dist/ui/settings-mods.js +23 -9
  280. package/dist/ui/settings-owner-effects.js +15 -13
  281. package/dist/ui/settings-strings.js +122 -42
  282. package/dist/ui/settings-web-authority.js +91 -0
  283. package/dist/ui/shared/agend-auth.js +1 -1
  284. package/dist/ui/shared/app-i18n.js +11 -2
  285. package/dist/ui/shared/app-prefs.js +17 -4
  286. package/dist/ui/shared/app-route.js +19 -1
  287. package/dist/ui/shared/app-session.js +2 -0
  288. package/dist/ui/shared/app-shell.js +174 -30
  289. package/dist/ui/shared/app-store.js +26 -1
  290. package/dist/ui/shared/app-stream.js +5 -1
  291. package/dist/ui/shared/app-themes.js +140 -7
  292. package/dist/ui/shared/app.css +704 -20
  293. package/dist/ui/shared/app.js +26 -15
  294. package/dist/ui/shared/card-strings.js +8 -0
  295. package/dist/ui/shared/effect-app-runtime.js +68 -19
  296. package/dist/ui/shared/effect-chat-runtime.js +16 -3
  297. package/dist/ui/shared/effect-frame.js +26 -7
  298. package/dist/ui/shared/effect-geometry.js +24 -9
  299. package/dist/ui/shared/effect-owner-source.js +8 -1
  300. package/dist/ui/shared/effect-policy.js +21 -7
  301. package/dist/ui/shared/effect-profiler.js +31 -9
  302. package/dist/ui/shared/effect-registry.js +97 -50
  303. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  304. package/dist/ui/shared/effect-stock-trust.js +62 -10
  305. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  306. package/dist/ui/shared/effect-trial-source.js +1 -1
  307. package/dist/ui/shared/effect-trial-stage.js +54 -31
  308. package/dist/ui/shared/effect-trial.css +40 -0
  309. package/dist/ui/shared/fx-motion.js +67 -0
  310. package/dist/ui/shared/identity-card.js +100 -11
  311. package/dist/ui/shared/identity-color.js +14 -10
  312. package/dist/ui/shared/instance-nav.js +179 -23
  313. package/dist/ui/shared/owner-css.js +30 -1
  314. package/dist/ui/shared/panel-view.js +24 -4
  315. package/dist/ui/shared/tokens.css +9 -3
  316. package/dist/ui/shared/ui-icons.js +2 -0
  317. package/dist/ui/shared/ui-menu.js +20 -7
  318. package/dist/ui/shared/ui-toast.js +16 -3
  319. package/dist/ui/shared/usage-dialog.js +34 -3
  320. package/dist/ui/shared/view-strings.js +72 -0
  321. package/dist/ui/shared/web-authorize.js +198 -0
  322. package/dist/ui/web-terminal/terminal.css +4 -4
  323. package/dist/ui/web-terminal/terminal.html +1 -1
  324. package/dist/update-launch.d.ts +9 -0
  325. package/dist/update-launch.js +38 -12
  326. package/dist/update-launch.js.map +1 -1
  327. package/dist/usage/format-rich.js +6 -3
  328. package/dist/usage/format-rich.js.map +1 -1
  329. package/dist/usage/i18n-keys.d.ts +1 -1
  330. package/dist/usage/i18n-keys.js +2 -1
  331. package/dist/usage/i18n-keys.js.map +1 -1
  332. package/dist/usage/i18n.d.ts +13 -1
  333. package/dist/usage/i18n.js +29 -0
  334. package/dist/usage/i18n.js.map +1 -1
  335. package/dist/usage/providers.d.ts +81 -2
  336. package/dist/usage/providers.js +240 -38
  337. package/dist/usage/providers.js.map +1 -1
  338. package/dist/usage/usage-api.d.ts +5 -1
  339. package/dist/usage/usage-api.js +184 -58
  340. package/dist/usage/usage-api.js.map +1 -1
  341. package/dist/usage-report.d.ts +158 -0
  342. package/dist/usage-report.js +226 -0
  343. package/dist/usage-report.js.map +1 -0
  344. package/dist/usage-rollup.d.ts +51 -0
  345. package/dist/usage-rollup.js +168 -0
  346. package/dist/usage-rollup.js.map +1 -0
  347. package/dist/user-systemd.d.ts +24 -0
  348. package/dist/user-systemd.js +75 -0
  349. package/dist/user-systemd.js.map +1 -0
  350. package/dist/view-api.d.ts +39 -0
  351. package/dist/view-api.js +69 -6
  352. package/dist/view-api.js.map +1 -1
  353. package/dist/web-ambient.d.ts +72 -0
  354. package/dist/web-ambient.js +161 -0
  355. package/dist/web-ambient.js.map +1 -0
  356. package/dist/web-api.d.ts +98 -14
  357. package/dist/web-api.js +1027 -83
  358. package/dist/web-api.js.map +1 -1
  359. package/dist/web-appearance-policy.d.ts +4 -6
  360. package/dist/web-appearance-policy.js +5 -12
  361. package/dist/web-appearance-policy.js.map +1 -1
  362. package/dist/web-appearance-tools.d.ts +3 -0
  363. package/dist/web-appearance-tools.js +22 -3
  364. package/dist/web-appearance-tools.js.map +1 -1
  365. package/dist/web-appearance-view.d.ts +27 -2
  366. package/dist/web-appearance-view.js +129 -14
  367. package/dist/web-appearance-view.js.map +1 -1
  368. package/dist/web-appearance.d.ts +17 -3
  369. package/dist/web-appearance.js +85 -8
  370. package/dist/web-appearance.js.map +1 -1
  371. package/dist/web-asset-parts.d.ts +2 -2
  372. package/dist/web-asset-parts.js +3 -3
  373. package/dist/web-asset-parts.js.map +1 -1
  374. package/dist/web-auth.js +3 -1
  375. package/dist/web-auth.js.map +1 -1
  376. package/dist/web-backend-login.d.ts +130 -0
  377. package/dist/web-backend-login.js +225 -0
  378. package/dist/web-backend-login.js.map +1 -0
  379. package/dist/web-card.d.ts +14 -0
  380. package/dist/web-card.js +40 -6
  381. package/dist/web-card.js.map +1 -1
  382. package/dist/web-channel-echo.d.ts +7 -1
  383. package/dist/web-channel-echo.js +6 -2
  384. package/dist/web-channel-echo.js.map +1 -1
  385. package/dist/web-chat-history.d.ts +83 -0
  386. package/dist/web-chat-history.js +137 -5
  387. package/dist/web-chat-history.js.map +1 -1
  388. package/dist/web-commands.d.ts +6 -1
  389. package/dist/web-commands.js +1 -1
  390. package/dist/web-commands.js.map +1 -1
  391. package/dist/web-effect-bootstrap.d.ts +12 -0
  392. package/dist/web-effect-bootstrap.js +49 -0
  393. package/dist/web-effect-bootstrap.js.map +1 -0
  394. package/dist/web-effect-bundle.js +46 -1
  395. package/dist/web-effect-bundle.js.map +1 -1
  396. package/dist/web-effect-playback.d.ts +30 -0
  397. package/dist/web-effect-playback.js +71 -0
  398. package/dist/web-effect-playback.js.map +1 -0
  399. package/dist/web-effect-profiles.d.ts +4 -4
  400. package/dist/web-effect-service.d.ts +11 -4
  401. package/dist/web-effect-service.js +25 -4
  402. package/dist/web-effect-service.js.map +1 -1
  403. package/dist/web-effect-tools.d.ts +1 -0
  404. package/dist/web-effect-tools.js +4 -4
  405. package/dist/web-effect-tools.js.map +1 -1
  406. package/dist/web-effect-trials.d.ts +2 -2
  407. package/dist/web-effect-trials.js +3 -3
  408. package/dist/web-effect-trials.js.map +1 -1
  409. package/dist/web-effect-upload.js +1 -1
  410. package/dist/web-effect-upload.js.map +1 -1
  411. package/dist/web-effect-worker.d.ts +1 -1
  412. package/dist/web-effect-worker.js +2 -2
  413. package/dist/web-effect-worker.js.map +1 -1
  414. package/dist/web-effect.d.ts +1 -1
  415. package/dist/web-guest-access.d.ts +126 -0
  416. package/dist/web-guest-access.js +238 -0
  417. package/dist/web-guest-access.js.map +1 -0
  418. package/dist/web-guest-rooms.d.ts +82 -0
  419. package/dist/web-guest-rooms.js +135 -0
  420. package/dist/web-guest-rooms.js.map +1 -0
  421. package/dist/web-host-guard.d.ts +11 -0
  422. package/dist/web-host-guard.js +11 -0
  423. package/dist/web-host-guard.js.map +1 -1
  424. package/dist/web-identity-color.d.ts +1 -1
  425. package/dist/web-identity-color.js +2 -2
  426. package/dist/web-identity-color.js.map +1 -1
  427. package/dist/web-identity-palette.d.ts +1 -1
  428. package/dist/web-identity-palette.js +2 -2
  429. package/dist/web-identity-palette.js.map +1 -1
  430. package/dist/web-image-meta.d.ts +13 -0
  431. package/dist/web-image-meta.js +62 -0
  432. package/dist/web-image-meta.js.map +1 -1
  433. package/dist/web-libavoid.d.ts +14 -0
  434. package/dist/web-libavoid.js +46 -0
  435. package/dist/web-libavoid.js.map +1 -0
  436. package/dist/web-login.d.ts +47 -0
  437. package/dist/web-login.js +115 -0
  438. package/dist/web-login.js.map +1 -1
  439. package/dist/web-mod-agent.d.ts +1 -1
  440. package/dist/web-mod-agent.js +7 -5
  441. package/dist/web-mod-agent.js.map +1 -1
  442. package/dist/web-mod-bundle.d.ts +9 -1
  443. package/dist/web-mod-bundle.js +62 -3
  444. package/dist/web-mod-bundle.js.map +1 -1
  445. package/dist/web-mod-export.d.ts +9 -0
  446. package/dist/web-mod-export.js +21 -0
  447. package/dist/web-mod-export.js.map +1 -1
  448. package/dist/web-mod-store.d.ts +7 -1
  449. package/dist/web-mod-store.js +19 -6
  450. package/dist/web-mod-store.js.map +1 -1
  451. package/dist/web-owner-css-store.d.ts +44 -1
  452. package/dist/web-owner-css-store.js +70 -9
  453. package/dist/web-owner-css-store.js.map +1 -1
  454. package/dist/web-prefs.d.ts +14 -0
  455. package/dist/web-prefs.js +37 -4
  456. package/dist/web-prefs.js.map +1 -1
  457. package/dist/web-preview.d.ts +6 -2
  458. package/dist/web-preview.js +4 -2
  459. package/dist/web-preview.js.map +1 -1
  460. package/dist/web-returning-device.d.ts +41 -0
  461. package/dist/web-returning-device.js +57 -5
  462. package/dist/web-returning-device.js.map +1 -1
  463. package/dist/web-session-authorization.d.ts +132 -0
  464. package/dist/web-session-authorization.js +193 -0
  465. package/dist/web-session-authorization.js.map +1 -0
  466. package/dist/web-session.d.ts +70 -10
  467. package/dist/web-session.js +137 -22
  468. package/dist/web-session.js.map +1 -1
  469. package/dist/web-settings-patch.d.ts +49 -2
  470. package/dist/web-settings-patch.js +145 -1
  471. package/dist/web-settings-patch.js.map +1 -1
  472. package/dist/web-settings-tools.d.ts +94 -0
  473. package/dist/web-settings-tools.js +301 -0
  474. package/dist/web-settings-tools.js.map +1 -0
  475. package/dist/web-shell-routes.d.ts +1 -1
  476. package/dist/web-shell-routes.js +1 -1
  477. package/dist/web-shell-routes.js.map +1 -1
  478. package/dist/web-terminal-http.js +15 -2
  479. package/dist/web-terminal-http.js.map +1 -1
  480. package/dist/web-theme-store.d.ts +39 -0
  481. package/dist/web-theme-store.js +122 -11
  482. package/dist/web-theme-store.js.map +1 -1
  483. package/dist/web-theme.d.ts +31 -0
  484. package/dist/web-theme.js +99 -2
  485. package/dist/web-theme.js.map +1 -1
  486. package/dist/web-upload.d.ts +13 -0
  487. package/dist/web-upload.js +39 -3
  488. package/dist/web-upload.js.map +1 -1
  489. package/dist/windows-shortcuts.d.ts +78 -0
  490. package/dist/windows-shortcuts.js +203 -0
  491. package/dist/windows-shortcuts.js.map +1 -0
  492. package/dist/workflow-templates/default.md +4 -1
  493. package/dist/yaml-shared-nodes.d.ts +25 -0
  494. package/dist/yaml-shared-nodes.js +185 -0
  495. package/dist/yaml-shared-nodes.js.map +1 -0
  496. package/launcher/agend-install.cjs +553 -0
  497. package/launcher/install-admission.cjs +4 -39
  498. package/launcher/postinstall.cjs +14 -12
  499. package/package.json +4 -2
  500. package/scripts/preinstall-guard.cjs +2 -5
  501. package/templates/windows-shortcuts.ps1 +181 -0
  502. package/launcher/old-updater-note.cjs +0 -48
@@ -0,0 +1,176 @@
1
+ // #2272: a chat message's own actions, beyond the hover strip — the message menu (right-click, long-press on a phone),
2
+ // swipe left to reply (as in Telegram), and R on a focused message. One install per thread element.
3
+ //
4
+ // - Right-click on a message opens Reply / Copy text / Copy link. Shift+right-click, a right-click with text selected,
5
+ // or one on a link, an image or a field keeps the browser's own menu.
6
+ // - A long press (500 ms without moving) opens the same menu; the click and the browser menu it would also cause are
7
+ // swallowed.
8
+ // - Swipe left on a message: it follows the finger (up to 64 px); let go past 56 px and it is a reply. A mostly
9
+ // vertical move is scrolling, never a reply. A swipe right is the phone drawer's (app-swipe.js), on a message too: the
10
+ // two never fight.
11
+ // - R (no modifier) on a focused message replies; ↑/↓ move to the message before or after (one message is in the Tab
12
+ // order); Escape closes the menu.
13
+
14
+ const LONG_PRESS_MS = 500, MOVE_TOLERANCE = 10, SWIPE_REPLY_PX = 56, SWIPE_MAX_PX = 64;
15
+
16
+ /** The message element an event is on (a person's or an agent's message of this thread), or null. */
17
+ function messageEl(list, target) {
18
+ const el = target && target.closest ? target.closest(".msg[data-key]") : null;
19
+ return el && list.contains(el) ? el : null;
20
+ }
21
+ /** The browser's own menu stays for: Shift, selected text, a link, an image, a field, code. */
22
+ function keepsNativeMenu(e, doc) {
23
+ if (e.shiftKey) return true;
24
+ const sel = doc.getSelection ? doc.getSelection() : null;
25
+ if (sel && !sel.isCollapsed && String(sel).trim()) return true;
26
+ return !!(e.target && e.target.closest && e.target.closest("a[href], img, video, input, textarea, select, pre, code, [contenteditable='true']"));
27
+ }
28
+
29
+ /**
30
+ * Install the gestures on `list` (the thread element). `deps`:
31
+ * messageOf(key) → the row; reply(row); copyText(text) → Promise<boolean>; linkOf(row) → string; notice(text);
32
+ * canReply() → boolean (false: no Reply — a page that cannot send); t(key).
33
+ * Returns a function that removes everything (and closes an open menu).
34
+ */
35
+ export function installMessageActions(list, deps) {
36
+ const doc = list.ownerDocument, win = doc.defaultView;
37
+ let menu = null, press = null, swipe = null, swallowClick = false;
38
+
39
+ function closeMenu() {
40
+ if (!menu) return;
41
+ menu.remove(); menu = null;
42
+ doc.removeEventListener("pointerdown", outside, true);
43
+ doc.removeEventListener("keydown", onMenuKey, true);
44
+ win?.removeEventListener("scroll", closeMenu, true);
45
+ win?.removeEventListener("blur", closeMenu);
46
+ }
47
+ function outside(e) { if (menu && !menu.contains(e.target)) closeMenu(); }
48
+ function onMenuKey(e) {
49
+ if (!menu) return;
50
+ if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); closeMenu(); return; }
51
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
52
+ e.preventDefault();
53
+ const items = [...menu.querySelectorAll("button")], i = items.indexOf(doc.activeElement);
54
+ items[(i + (e.key === "ArrowDown" ? 1 : items.length - 1)) % items.length]?.focus();
55
+ }
56
+ }
57
+ /** The message menu at (x, y), within the window; focus on its first item. */
58
+ function openMenu(row, x, y) {
59
+ closeMenu();
60
+ const items = [];
61
+ if (deps.canReply?.() !== false) items.push(["reply", deps.t("chat.reply"), () => deps.reply(row)]);
62
+ items.push(["copyText", deps.t("chat.copyText"), async () => { const ok = await deps.copyText(String(row.click?.label ?? row.text ?? "")); deps.notice(deps.t(ok ? "chat.copied" : "chat.copyFailed"), ok); }]);
63
+ items.push(["copyLink", deps.t("chat.copyLink"), async () => { const ok = await deps.copyText(deps.linkOf(row)); deps.notice(deps.t(ok ? "chat.copied" : "chat.copyFailed"), ok); }]);
64
+ menu = doc.createElement("div");
65
+ menu.className = "msg-menu";
66
+ menu.setAttribute("role", "menu");
67
+ menu.setAttribute("aria-label", deps.t("chat.msgMenu"));
68
+ menu.setAttribute("data-trust", "menu");
69
+ for (const [id, label, run] of items) {
70
+ const b = doc.createElement("button");
71
+ b.type = "button"; b.className = "menu-item"; b.setAttribute("role", "menuitem"); b.dataset.item = id; b.textContent = label;
72
+ b.addEventListener("click", () => { closeMenu(); void run(); });
73
+ menu.append(b);
74
+ }
75
+ doc.body.append(menu);
76
+ const w = win?.innerWidth ?? 1024, h = win?.innerHeight ?? 768, r = menu.getBoundingClientRect();
77
+ menu.style.left = `${Math.max(4, Math.min(x, w - r.width - 4))}px`;
78
+ menu.style.top = `${Math.max(4, Math.min(y, h - r.height - 4))}px`;
79
+ doc.addEventListener("pointerdown", outside, true);
80
+ doc.addEventListener("keydown", onMenuKey, true);
81
+ win?.addEventListener("scroll", closeMenu, true);
82
+ win?.addEventListener("blur", closeMenu);
83
+ menu.querySelector("button")?.focus();
84
+ }
85
+
86
+ function onContextMenu(e) {
87
+ if (swallowClick) { e.preventDefault(); return; } // the long press already opened ours
88
+ cancelPress(); // Android's long press fires this itself: one menu
89
+ const el = messageEl(list, e.target);
90
+ if (!el || keepsNativeMenu(e, doc)) return;
91
+ const row = deps.messageOf(el.dataset.key);
92
+ if (!row) return;
93
+ e.preventDefault();
94
+ openMenu(row, e.clientX, e.clientY);
95
+ }
96
+ function onKeyDown(e) {
97
+ // ↑/↓ on a focused message: the one before or after it (one message is in the Tab order: the thread's rove()).
98
+ if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey
99
+ && e.target && e.target.matches && e.target.matches(".msg[data-key]") && list.contains(e.target)) {
100
+ const all = [...list.querySelectorAll(".msg[data-key]")], i = all.indexOf(e.target);
101
+ const to = all[i + (e.key === "ArrowDown" ? 1 : -1)];
102
+ if (to) { e.preventDefault(); deps.rove?.(to.dataset.key); to.focus(); to.scrollIntoView?.({ block: "nearest" }); }
103
+ return;
104
+ }
105
+ if (e.key !== "r" && e.key !== "R") return;
106
+ if (e.ctrlKey || e.metaKey || e.altKey || deps.canReply?.() === false) return;
107
+ const el = e.target && e.target.matches && e.target.matches(".msg[data-key]") && list.contains(e.target) ? e.target : null;
108
+ const row = el ? deps.messageOf(el.dataset.key) : null;
109
+ if (!row) return;
110
+ e.preventDefault();
111
+ deps.reply(row);
112
+ }
113
+
114
+ function onTouchStart(e) {
115
+ if (e.touches.length !== 1) { cancelPress(); cancelSwipe(); return; }
116
+ const el = messageEl(list, e.target);
117
+ if (!el) return;
118
+ const t0 = e.touches[0];
119
+ const native = keepsNativeMenu({ target: e.target, shiftKey: false }, doc); // a link, an image, code: the browser's
120
+ press = native ? null : { el, x: t0.clientX, y: t0.clientY, timer: setTimeout(() => {
121
+ const row = deps.messageOf(el.dataset.key);
122
+ press = null; cancelSwipe();
123
+ if (!row) return;
124
+ swallowClick = true; setTimeout(() => { swallowClick = false; }, 700);
125
+ openMenu(row, t0.clientX, t0.clientY);
126
+ }, LONG_PRESS_MS) };
127
+ swipe = deps.canReply?.() === false || native ? null : { el, x: t0.clientX, y: t0.clientY, dx: 0, locked: null };
128
+ }
129
+ function onTouchMove(e) {
130
+ const p = e.touches[0];
131
+ if (press && (Math.abs(p.clientX - press.x) > MOVE_TOLERANCE || Math.abs(p.clientY - press.y) > MOVE_TOLERANCE)) cancelPress();
132
+ if (!swipe) return;
133
+ const dx = p.clientX - swipe.x, dy = p.clientY - swipe.y;
134
+ if (swipe.locked === null && (Math.abs(dx) > MOVE_TOLERANCE || Math.abs(dy) > MOVE_TOLERANCE)) swipe.locked = dx < 0 && Math.abs(dy) < -dx / 2 ? "x" : "no";
135
+ if (swipe.locked !== "x") return;
136
+ swipe.dx = Math.max(0, Math.min(-dx, SWIPE_MAX_PX)); // how far left, as a positive distance
137
+ swipe.el.style.transform = `translateX(${-swipe.dx}px)`;
138
+ swipe.el.classList.toggle("swipe-ready", swipe.dx >= SWIPE_REPLY_PX);
139
+ }
140
+ function onTouchEnd() {
141
+ cancelPress();
142
+ if (!swipe) return;
143
+ const { el, dx, locked } = swipe;
144
+ cancelSwipe();
145
+ if (locked === "x" && dx >= SWIPE_REPLY_PX) { const row = deps.messageOf(el.dataset.key); if (row) deps.reply(row); }
146
+ }
147
+ function cancelPress() { if (press) { clearTimeout(press.timer); press = null; } }
148
+ function cancelSwipe() {
149
+ if (!swipe) return;
150
+ swipe.el.style.transform = ""; swipe.el.classList.remove("swipe-ready");
151
+ swipe = null;
152
+ }
153
+ function onClickCapture(e) { if (swallowClick) { e.preventDefault(); e.stopPropagation(); } }
154
+
155
+ // A message focused by a click or a tap becomes the one Tab comes back to.
156
+ function onFocusIn(e) { const el = e.target && e.target.matches && e.target.matches(".msg[data-key]") ? e.target : null; if (el && list.contains(el)) deps.rove?.(el.dataset.key); }
157
+ list.addEventListener("focusin", onFocusIn);
158
+ list.addEventListener("contextmenu", onContextMenu);
159
+ list.addEventListener("keydown", onKeyDown);
160
+ list.addEventListener("touchstart", onTouchStart, { passive: true });
161
+ list.addEventListener("touchmove", onTouchMove, { passive: true });
162
+ list.addEventListener("touchend", onTouchEnd, { passive: true });
163
+ list.addEventListener("touchcancel", onTouchEnd, { passive: true });
164
+ list.addEventListener("click", onClickCapture, true);
165
+ return () => {
166
+ closeMenu(); cancelPress(); cancelSwipe();
167
+ list.removeEventListener("focusin", onFocusIn);
168
+ list.removeEventListener("contextmenu", onContextMenu);
169
+ list.removeEventListener("keydown", onKeyDown);
170
+ list.removeEventListener("touchstart", onTouchStart);
171
+ list.removeEventListener("touchmove", onTouchMove);
172
+ list.removeEventListener("touchend", onTouchEnd);
173
+ list.removeEventListener("touchcancel", onTouchEnd);
174
+ list.removeEventListener("click", onClickCapture, true);
175
+ };
176
+ }
@@ -53,11 +53,14 @@
53
53
 
54
54
  // #1630: a Discord server emoji, `<:name:id>` / `<a:name:id>` — escaped by now — drawn as its picture. Only an id the
55
55
  // page knows (the fleet's servers: /ui/emojis) and only from the dashboard's own route; the src is built from the
56
- // matched digits, the name (word characters only) goes into alt/title. An unknown id stays the literal text.
56
+ // matched digits, the name (word characters only) goes into alt/title. Any other — an id the page does not know, or
57
+ // before the list has come — reads `:name:` (never the raw markup); so does a picture that does not load
58
+ // (chat-thread.js swaps it: an <img> sized to an emoji showed an empty box, the user's "tofu").
57
59
  var EMOJI_RE = /&lt;(a?):([A-Za-z0-9_]{2,32}):(\d{15,25})&gt;/g;
58
60
  function emojiImg(id, name) {
59
61
  return '<img class="emoji" src="/ui/img/emoji/' + id + '" alt=":' + name + ':" title=":' + name + ':" decoding="async">';
60
62
  }
63
+ function emojiText(name) { return '<span class="emoji-text" title=":' + name + ':">:' + name + ':</span>'; }
61
64
 
62
65
  // #1682: Discord mentions — `<@id>` / `<@!id>` (a user), `<@&id>` (a role), `<#id>` (a channel) — escaped by now (the
63
66
  // role's & too), drawn as a pill: the name the page knows (store.mentions(kind, id), inserted escaped), else @user /
@@ -82,7 +85,7 @@
82
85
  // path held its whole column wide.
83
86
  s = s.replace(/`([^`\n]+)`/g, function (_m, code) { return stash(store, "<code>" + (store.inCell ? code.replace(/\//g, "/<wbr>") : code) + "</code>"); });
84
87
  var known = store.emojis;
85
- if (known) s = s.replace(EMOJI_RE, function (m, _a, name, id) { return known(id) ? stash(store, emojiImg(id, name)) : m; });
88
+ s = s.replace(EMOJI_RE, function (_m, _a, name, id) { return stash(store, known && known(id) ? emojiImg(id, name) : emojiText(name)); });
86
89
  var names = store.mentions;
87
90
  s = s.replace(MENTION_RE, function (_m, sigil, id) {
88
91
  var kind = MENTION_KIND[sigil];
@@ -577,6 +580,21 @@
577
580
  * With { htmlCards: true } (a server-marked agent reply, as for renderMarkdown) each .html/.htm file also gets an
578
581
  * empty card placeholder <div class="html-card" data-att="<id>"> after the list — the id is 32 hex, nothing else.
579
582
  */
583
+ // The box a photo is drawn in (app.css: .msg .att-img img, max 260 × 220): its own size, scaled down to fit.
584
+ var PHOTO_MAX_W = 260, PHOTO_MAX_H = 220;
585
+ /**
586
+ * #2184: a photo's drawn size as width/height attributes, from the size the server read from its headers: the box is there
587
+ * before the picture loads, so the text around it never moves when it does (a chat opened cold jittered, as each
588
+ * photo grew from nothing). Nothing when the size is not known (drawn as before).
589
+ */
590
+ function photoBox(a) {
591
+ var w = a.width, h = a.height;
592
+ var ok = function (v) { return typeof v === "number" && v >= 1 && v <= 65535 && Math.floor(v) === v; };
593
+ if (!ok(w) || !ok(h)) return "";
594
+ var k = Math.min(1, PHOTO_MAX_W / w, PHOTO_MAX_H / h);
595
+ return ' width="' + Math.max(1, Math.round(w * k)) + '" height="' + Math.max(1, Math.round(h * k)) + '"';
596
+ }
597
+
580
598
  function attachmentsHtml(attachments, opts) {
581
599
  if (!Array.isArray(attachments)) return "";
582
600
  var cards = !!(opts && opts.htmlCards);
@@ -599,7 +617,7 @@
599
617
  else if (kind) after.push('<div class="text-card" data-att="' + a.id + '" data-type="' + kind + '"></div>');
600
618
  var name = escapeHtml(a.name || "file");
601
619
  if (a.kind === "photo") {
602
- out.push('<a class="att-img" href="' + url + '" target="_blank" rel="noopener noreferrer"><img src="' + url + '" alt="' + name + '" loading="lazy"></a>');
620
+ out.push('<a class="att-img" href="' + url + '" target="_blank" rel="noopener noreferrer"><img src="' + url + '" alt="' + name + '"' + photoBox(a) + ' loading="lazy"></a>');
603
621
  } else {
604
622
  out.push('<a class="att-file" href="' + url + '" download="' + name + '">📎 ' + name + ' <span class="att-size">' + escapeHtml(formatSize(a.size)) + "</span></a>");
605
623
  }
@@ -42,7 +42,7 @@ const COMPACT_PAGE_LIMIT_MS = 11 * 60_000;
42
42
  /** Build a store. `deps` (fetch, toast, announce, t, now) are injected so tests can drive every path. */
43
43
  export function createChatStore(deps) {
44
44
  const s = {
45
- msgs: {}, drafts: {}, failedSends: {}, pendingFiles: {}, sending: {}, inFlightFiles: {},
45
+ msgs: {}, drafts: {}, replies: {}, failedSends: {}, pendingFiles: {}, sending: {}, inFlightFiles: {}, sendFailures: {}, instanceStatus: {}, waking: {},
46
46
  prompts: {}, workingSince: {}, stopping: {}, cancelling: {}, scrollMemo: {}, historyRead: new Set(), rbBusy: new Set(),
47
47
  pollMine: readPollMine(),
48
48
  // #1823 (#1828 review): the server's clock minus this device's. A poll's deadline is the server's; a page whose clock
@@ -61,9 +61,10 @@ export function createChatStore(deps) {
61
61
 
62
62
  /** Tell subscribers what changed: `kind` "msgs" (the thread redraws) or "state" (everything else). */
63
63
  function changed(instance, kind = "state") { for (const fn of [...subs]) fn(instance, kind); }
64
- async function api(method, path, body) {
64
+ async function api(method, path, body, signal) {
65
65
  const o = { method, headers: { "Content-Type": "application/json" } };
66
66
  if (body) o.body = JSON.stringify(body);
67
+ if (signal) o.signal = signal;
67
68
  const r = await deps.fetch(path, o);
68
69
  return r.json();
69
70
  }
@@ -82,6 +83,7 @@ export function createChatStore(deps) {
82
83
  function applyStatus(d) {
83
84
  if (!d || !Array.isArray(d.instances)) return;
84
85
  for (const i of d.instances) {
86
+ s.instanceStatus[i.name] = i.status;
85
87
  noteExec(i.name, (i.execution_state !== undefined ? i.execution_state : i.state) ?? null);
86
88
  const waits = i.state === "awaiting_input" ? (typeof i.interaction_summary === "string" ? i.interaction_summary : "") : null;
87
89
  if (i.name === s.current && waits != null && s.awaiting[i.name] == null) deps.announce(tf("chat.awaiting", i.name));
@@ -142,12 +144,17 @@ export function createChatStore(deps) {
142
144
  if (!r || !r.answered) deps.toast(r && r.error ? r.error : t("chat.disconnected"), false);
143
145
  }
144
146
 
145
- /** Read an instance's history once per page (the first time a person opens its chat). */
146
- async function openHistory(name, lease) {
147
- if (s.historyRead.has(name)) return false;
147
+ /**
148
+ * Read an instance's history once per page (the first time a person opens its chat). #2188: `signal` — the chat was
149
+ * left before the answer came (clicking down the sidebar): the read is aborted, nothing is merged, and the next visit
150
+ * reads again.
151
+ */
152
+ async function openHistory(name, lease, signal) {
153
+ if (s.historyRead.has(name) || signal?.aborted) return false;
148
154
  s.historyRead.add(name);
149
155
  try {
150
- const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=200`);
156
+ const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=200`, undefined, signal);
157
+ if (signal?.aborted) { s.historyRead.delete(name); return false; }
151
158
  if (!r || !Array.isArray(r.messages)) { s.historyRead.delete(name); return true; }
152
159
  // Kept even when the person has moved on: the store is page-lived, and the answer belongs to `name`.
153
160
  for (const m of r.messages) liveReplies.message(m);
@@ -243,9 +250,12 @@ export function createChatStore(deps) {
243
250
  }
244
251
  async function send(target) {
245
252
  const txt = (s.drafts[target] || "").trim();
253
+ const quote = s.replies[target]; // #2272: the message this one replies to (its row; the server reads the rest)
246
254
  const files = (s.pendingFiles[target] || []).slice();
247
255
  if ((!txt && !files.length) || !target || s.sending[target]) return;
248
256
  s.sending[target] = true;
257
+ s.waking[target] = s.instanceStatus[target] === "paused";
258
+ delete s.sendFailures[target];
249
259
  s.drafts[target] = "";
250
260
  s.pendingFiles[target] = []; s.inFlightFiles[target] = files;
251
261
  changed(target);
@@ -254,20 +264,37 @@ export function createChatStore(deps) {
254
264
  try {
255
265
  const ids = [];
256
266
  for (const f of files) ids.push((await uploadFile(target, f)).id);
257
- r = await api("POST", "/ui/send", ids.length ? { instance: target, message: txt, attachments: ids } : { instance: target, message: txt });
267
+ const body = ids.length ? { instance: target, message: txt, attachments: ids } : { instance: target, message: txt };
268
+ if (quote) body.replyTo = { boot: quote.boot, id: quote.id };
269
+ r = await api("POST", "/ui/send", body);
258
270
  } catch (err) { r = { error: err && err.message ? err.message : t("chat.disconnected") }; }
259
271
  s.sending[target] = false;
272
+ delete s.waking[target];
260
273
  delete s.inFlightFiles[target];
261
- if (r && !r.error) { changed(target); return; }
274
+ if (r && !r.error) { if (s.replies[target] === quote) delete s.replies[target]; changed(target); return; }
275
+ // #2272: the quoted message is gone: the reply goes, the text and files stay (sent again without it, or re-aimed).
276
+ if (r && r.reason === "reply_gone" && s.replies[target] === quote) delete s.replies[target];
262
277
  if (files.length) s.pendingFiles[target] = R().checkFiles(files, s.pendingFiles[target] || []).kept;
263
278
  if (txt) {
264
279
  const shown = s.current === target;
265
280
  if (R().settleFailedSend(target, s.current, shown, shown ? (s.drafts[target] || "") : "") === "restore") s.drafts[target] = txt;
266
281
  else s.failedSends[target] = s.failedSends[target] ? `${s.failedSends[target]}\n${txt}` : txt;
267
282
  }
283
+ const why = r && r.reason;
284
+ const key = typeof why === "string" ? `chat.sendFailure_${why}` : "";
285
+ const localized = key ? t(key) : "";
286
+ const error = localized && localized !== key ? localized : (r && r.error ? r.error : t("chat.disconnected"));
287
+ s.sendFailures[target] = { error, text: txt, files };
268
288
  changed(target);
269
- deps.toast(r && r.error ? r.error : t("chat.disconnected"), false);
289
+ deps.toast(error, false);
270
290
  }
291
+ function canRetrySend(target) {
292
+ const failed = s.sendFailures[target], files = s.pendingFiles[target] || [];
293
+ return !!failed && !s.sending[target] && !s.failedSends[target]
294
+ && (s.drafts[target] || "").trim() === failed.text
295
+ && files.length === failed.files.length && files.every((f, i) => f === failed.files[i]);
296
+ }
297
+ function retrySend(target) { if (canRetrySend(target)) return send(target); }
271
298
  function addFiles(target, list) {
272
299
  if (!target) return;
273
300
  const inFlight = s.inFlightFiles[target] || [];
@@ -307,8 +334,32 @@ export function createChatStore(deps) {
307
334
  changed(target);
308
335
  }
309
336
  function discardFailed(target) { delete s.failedSends[target]; changed(target); }
310
- /** Typing: kept for its chat, no redraw (the composer already shows it). */
311
- function setDraft(target, value) { s.drafts[target] = value; }
337
+ /** Typing stays local, except a failed send's Retry button must follow edits too. */
338
+ function setDraft(target, value) { s.drafts[target] = value; if (s.sendFailures[target]) changed(target); }
339
+ /**
340
+ * #2272: reply to a message of this chat — what the bar above the composer shows (its sender and first words); only
341
+ * its row is sent. One at a time per chat: a new one replaces it.
342
+ */
343
+ function startReply(target, m) {
344
+ if (!target || !m || typeof m.boot !== "string" || typeof m.id !== "number") return;
345
+ s.replies[target] = { boot: m.boot, id: m.id, sender: String(m.sender || ""), text: String(m.click?.label ?? m.text ?? "").replace(/\s+/g, " ").trim().slice(0, 160),
346
+ media: (m.attachments || []).length ? ((m.attachments || []).some(a => a.kind === "photo") ? "photo" : "document") : undefined };
347
+ changed(target);
348
+ }
349
+ function clearReply(target) { if (s.replies[target]) { delete s.replies[target]; changed(target); } }
350
+ /**
351
+ * #2272: the quoted message, drawn for a jump: in what this chat holds, or after reading the longest history the
352
+ * server keeps for it (the newest 500). false: no longer kept.
353
+ */
354
+ async function findQuoted(name, key) {
355
+ const has = () => (s.msgs[name] || []).some(m => `${m.boot}-${m.id}` === key);
356
+ if (has()) return true;
357
+ try {
358
+ const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=500`);
359
+ if (r && Array.isArray(r.messages)) { s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500); changed(name, "msgs"); }
360
+ } catch { /* the jump says it is not there */ }
361
+ return has();
362
+ }
312
363
 
313
364
  // ── Stop the reply under way (the composer's Stop, or Esc). Set before the request goes, so a held Esc or a second
314
365
  // click sends nothing more (#1317); "Stopping…" until the agent goes idle, or a minute passes.
@@ -402,7 +453,8 @@ export function createChatStore(deps) {
402
453
  attach, applyStatus, applyActivity, ingest, applyDeliveries, openHistory,
403
454
  onPrompt, applyPrompts, resolvePrompt, answerPrompt, answerByNonce, holdPrompt, releasePrompt, promptsFor,
404
455
  applyReplyButtons, clickReplyButton,
405
- uploadFile, send, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, putBack, discardFailed, setDraft,
456
+ uploadFile, send, canRetrySend, retrySend, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, putBack, discardFailed, setDraft,
457
+ startReply, clearReply, findQuoted,
406
458
  cancelReply, isUser, runCommand, dismissCommand, commandFree,
407
459
  };
408
460
  }
@@ -3,14 +3,42 @@ import { register } from "/assets/app-i18n.js";
3
3
 
4
4
  register("chat", {
5
5
  en: {
6
- effectTrial: "Effect trial", effectTrialLocal: "Try requires a local admin, enabled previews and custom CSS off.",
7
- effectTrialStart: "Try", effectTrialExpired: "This trial offer has expired.", effectTrialUnavailable: "Trial unavailable: access or playback settings may have changed.",
6
+ waking: "Waking {0}…", retrySend: "Retry send",
7
+ sendFailure_wake_capacity: "No slot is free to wake this instance. Retry when another instance is idle.",
8
+ sendFailure_wake_credentials: "The instance could not sign in. Check its account on the host, then retry.",
9
+ sendFailure_wake_timeout: "The instance did not become ready in time. Retry, or check its status on the host.",
10
+ sendFailure_instance_stopped: "This instance was stopped or removed. Start it on the host before retrying.",
11
+ sendFailure_delivery_unavailable: "The instance could not receive this message. Check its status on the host, then retry.",
12
+ sendFailure_delivery_changed: "The session or instance changed before this message was sent. Sign in again if needed, then retry.",
13
+ sendFailure_reply_gone: "The message you replied to is no longer in this chat. Your text is still here: send it without the reply, or reply to another message.",
14
+ reply: "Reply", replyingTo: "Replying to {0}", replyCancel: "Cancel the reply (Esc)", replyHidden: "an earlier message", replyPhoto: "[Photo]", replyFile: "[File]",
15
+ replyGone: "That message is no longer in this chat.", replyJump: "Show the message this replies to", copyText: "Copy text", copyLink: "Copy link", msgMenu: "Message",
16
+
17
+ effectTrialReason_checking: "Checking this session…",
18
+ effectTrialReason_localAdmin: "Sign in as an admin outside the public link to Try.",
19
+ effectTrialReason_chatChanged: "Open the chat that offered this trial.",
20
+ effectTrialReason_busy: "Stop the current trial before starting another.",
21
+ effectTrialReason_safe: "Turn off safe mode to Try.",
22
+ effectTrialReason_effectsOff: "Turn on effects to Try.",
23
+ effectTrialReason_reducedMotion: "Effects follow this device’s reduced-motion setting.",
24
+ effectTrialReason_saveData: "Your device requests reduced data use.",
25
+ effectTrialReason_transport: "The isolated effect player is unavailable. Reload this page.",
26
+ effectTrialReason_unsupported: "Canvas Worker playback is unavailable in this browser.",
27
+ effectTrialReason_inactive: "Return to this page to Try.",
28
+ effectTrialReason_dialog: "Close the other menu or dialog to Try.",
29
+ effectTrialReason_expired: "This trial offer has expired.",
30
+ effectTrialSignIn: "Sign in here",
31
+ effectTrialStopOther: "Stop current trial",
32
+ effectTrialSettings: "Open playback settings",
33
+ effectTrialPlayMotion: "Play effects anyway",
34
+ effectTrial: "Effect trial", effectTrialLocal: "A sample on this page. Nothing is applied or saved.",
35
+ effectTrialStopped: "Try stopped", effectTrialStart: "Try", effectTrialExpired: "This trial offer has expired.", effectTrialUnavailable: "This trial is no longer available.",
8
36
  cmdConfirm: "Clear", cmdConfirmBackend: "Switch and restart", cmdRunning: "Running /{0}…", cmdNotCommand: "Not a command here — Enter sends it as a message.", cmdDismiss: "Dismiss", cmdCommands: "Commands",
9
37
  compactRunning: "Compacting…", compactDone: "✅ Conversation compacted", compactUnknown: "This backend does not report when compaction ends.",
10
38
  compactTimeout: "No sign that compaction finished within {0} minutes — check the agent's own chat.",
11
39
  compactLost: "Can't follow this compaction: the command did not reach the agent, or the agent stopped or changed backend.",
12
40
  compactNotCompacted: "kiro did not compact the conversation: it was too short, or the compaction was stopped.",
13
- cmdChoose: "Choose", cmdCurrent: "current", chipCompact: "Compact", chipClear: "Clear…", chipContext: "Context {0}% used",
41
+ cmdChoose: "Choose", cmdCurrent: "current", cmdSignIn: "Sign in", cmdSignInTo: "Sign in to {0}", chipCompact: "Compact", chipClear: "Clear…", chipContext: "Context {0}% used",
14
42
  chipModel: "Model: {0} — choose another", chipEffort: "Effort: {0} — choose another",
15
43
  cmd_ctx: "Context use, model and effort", cmd_compact: "Compact the conversation", cmd_clear: "Clear the conversation (asks first)",
16
44
  cmd_model: "Show or switch the model", cmd_backend: "Switch the CLI backend, then model and effort (restarts)", cmd_effort: "Show or set the reasoning effort", cmd_cancel: "Stop the current reply",
@@ -25,7 +53,8 @@ register("chat", {
25
53
  a11yWorking: "{0} started working", a11yIdle: "{0} finished", a11yReplied: "{0} replied", conversationWith: "Conversation with {0}",
26
54
  stopping: "Stopping {0}…", awaiting: "{0} is waiting for your input", approxNote: "Read from the terminal, so approximate", elapsedTitle: "Time since this page saw it start (approximate)",
27
55
  dropHere: "Drop files to attach them", asText: "As text", pasteAttached: "A long paste ({0} characters) was attached as a text file", pasteKeptAsText: "No room for another attachment: the long paste stays as text",
28
- send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", attachTitle: "Attach files (any type — or paste / drop them)",
56
+ send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", guestMore: "Esc stops the reply",
57
+ guestNote: "You're a guest: the agent sees your messages as a guest's (recognition only).", guestNoCommands: "Guests can't run commands — send it without the leading /.", attachTitle: "Attach files (any type — or paste / drop them)",
29
58
  pvPreview: "Preview", pvStop: "Stop", pvDownload: "Download", pvMenu: "Preview options", pvHtml: "HTML",
30
59
  pvAllowButton: "Allow previews", pvAllow: "Allow HTML previews — they may be able to send data out", pvDisallow: "Stop allowing HTML previews",
31
60
  pvConfirm: "Allow HTML previews?\n\nThis applies on every device where you are signed in. A preview runs the agent's HTML in an isolated frame. It cannot use your login, but it may be able to send data out, and it can slow or freeze this tab. You still click Preview each time.",
@@ -71,6 +100,8 @@ register("chat", {
71
100
  "pvFail_bad-address": "Cloudflare gave an address that could not be used",
72
101
  pvFail_unknown: "an unexpected error",
73
102
  pvServer_notOffered: "Previews are not available on this page.",
103
+ pvServer_listenFailed: "Previews are unavailable: the fleet could not open its preview port ({0}). Another program may be using it — set web.preview_port, then restart AgEnD.",
104
+ pvServer_notListening: "Previews are not available on this fleet right now.",
74
105
  previews: "Allow HTML previews",
75
106
  clicked: "Clicked:", clickShow: "Show the message these buttons were on",
76
107
  copy: "Copy", copyMessage: "Copy this reply", copied: "Copied", copyFailed: "Copy failed", wrap: "Wrap", showAll: "Show all {0} lines", showMore: "Show more", showLess: "Show less", jumpLatest: "Latest", newBelow: "{0} new",
@@ -94,21 +125,57 @@ register("chat", {
94
125
  dNoActivity: "No activity in the last hour.", dWorking: "Working…",
95
126
  // #1629: a person's control over an agent's own web theme.
96
127
  themeLock: "Lock theme", themeUnlock: "Unlock theme", themeReset: "Reset theme…", themeResetGo: "Reset",
128
+ pageColours: "Page colours", pageAgentOn: "The agent's choice (on)", pageAgentOff: "The agent's choice (off)", pageWhole: "On the whole page", pageChatOnly: "Only its chat",
97
129
  themeResetConfirm: "Reset {0}'s theme to the default? Its colours, picture and working label go; a lock stays.",
98
130
  themeResetDone: "{0}'s theme was reset.", themeLocked: "{0}'s theme is locked: the agent can't change it.", themeUnlocked: "{0}'s theme is unlocked.",
99
131
  themeFailed: "The theme was not changed ({0}).",
100
132
  widgetRemoveHeader: "Remove the chat header widget", widgetRemoveDetails: "Remove the Details widget", widgetRemoved: "{0}'s widget was removed.",
133
+ // #2117: an agent's ambient effect — why it is not showing here, and a person's Clear.
134
+ ambientBlocked_hidden: "Ambient effect hidden: agents' themes are off — turn them on",
135
+ ambientBlocked_safe: "Ambient effect hidden: safe mode (?safe=1) — leave safe mode",
136
+ ambientBlocked_reducedMotion: "Ambient effect hidden: this device follows reduced motion — play effects anyway",
137
+ ambientBlocked_unsupported: "Ambient effect hidden: this browser can't animate it",
138
+ ambientClearLoop: "Clear the ambient effect", ambientClearClick: "Clear the click effect", ambientCleared: "{0}'s ambient effect was cleared.",
139
+ ambientMotionNote: "Your system asks for reduced motion; effects are on.", ambientMotionNoteLink: "Turn off here", ambientMotionNoteClose: "Close",
101
140
  configured: "(configured)", fleetDefault: "(fleet default)",
102
141
  },
103
142
  "zh-TW": {
104
- effectTrial: "效果試播", effectTrialLocal: "需由本機管理員啟用預覽並關閉自訂 CSS,才能試播。",
105
- effectTrialStart: "試播", effectTrialExpired: "試播邀請已過期。", effectTrialUnavailable: "無法試播:存取權或播放設定可能已變更。",
143
+ waking: "正在喚醒 {0}…", retrySend: "重試傳送",
144
+ sendFailure_wake_capacity: "目前沒有空位可喚醒這個實例。請等其他實例閒置後重試。",
145
+ sendFailure_wake_credentials: "實例無法登入。請先在主機檢查它的帳號,再重試。",
146
+ sendFailure_wake_timeout: "實例未能及時就緒。請重試,或在主機檢查它的狀態。",
147
+ sendFailure_instance_stopped: "這個實例已停止或移除。請先在主機啟動它,再重試。",
148
+ sendFailure_delivery_unavailable: "實例目前無法接收這則訊息。請在主機檢查它的狀態,再重試。",
149
+ sendFailure_delivery_changed: "訊息送出前,登入或實例狀態已改變。必要時請重新登入,再重試。",
150
+ sendFailure_reply_gone: "你回覆的訊息已不在這個聊天裡。內容還在:可以不帶回覆直接送出,或改回覆其他訊息。",
151
+ reply: "回覆", replyingTo: "回覆 {0}", replyCancel: "取消回覆(Esc)", replyHidden: "之前的一則訊息", replyPhoto: "[圖片]", replyFile: "[檔案]",
152
+ replyGone: "那則訊息已不在這個聊天裡。", replyJump: "顯示被回覆的訊息", copyText: "複製文字", copyLink: "複製連結", msgMenu: "訊息",
153
+
154
+ effectTrialReason_checking: "正在確認工作階段…",
155
+ effectTrialReason_localAdmin: "請在公開連結以外的儀表板以管理員登入後試播。",
156
+ effectTrialReason_chatChanged: "請開啟發出試播邀請的聊天。",
157
+ effectTrialReason_busy: "請先停止目前的試播。",
158
+ effectTrialReason_safe: "請先關閉安全模式。",
159
+ effectTrialReason_effectsOff: "請先開啟效果。",
160
+ effectTrialReason_reducedMotion: "效果跟隨這台裝置的「減少動態效果」設定。",
161
+ effectTrialReason_saveData: "裝置目前要求節省數據。",
162
+ effectTrialReason_transport: "隔離效果播放器目前無法使用。請重新載入頁面。",
163
+ effectTrialReason_unsupported: "此瀏覽器無法播放 Canvas Worker 效果。",
164
+ effectTrialReason_inactive: "請回到本頁後試播。",
165
+ effectTrialReason_dialog: "請先關閉其他選單或對話框。",
166
+ effectTrialReason_expired: "試播邀請已過期。",
167
+ effectTrialSignIn: "在此登入",
168
+ effectTrialStopOther: "停止目前試播",
169
+ effectTrialSettings: "開啟播放設定",
170
+ effectTrialPlayMotion: "仍然播放效果",
171
+ effectTrial: "效果試播", effectTrialLocal: "僅在本頁試播,不會套用或儲存。",
172
+ effectTrialStopped: "試播已停止", effectTrialStart: "試播", effectTrialExpired: "試播邀請已過期。", effectTrialUnavailable: "此試播已無法使用。",
106
173
  cmdConfirm: "清空", cmdConfirmBackend: "切換並重新啟動", cmdRunning: "執行 /{0} 中…", cmdNotCommand: "這裡沒有這個指令——按 Enter 會當成一般訊息送出。", cmdDismiss: "關閉", cmdCommands: "指令",
107
174
  compactRunning: "壓縮中…", compactDone: "✅ 對話已壓縮", compactUnknown: "這個後端不會回報壓縮何時完成。",
108
175
  compactTimeout: "{0} 分鐘內沒有看到壓縮完成的紀錄,請到代理自己的對話確認。",
109
176
  compactLost: "無法再追蹤這次壓縮:指令沒有送到代理,或代理已停止、換了後端。",
110
177
  compactNotCompacted: "kiro 沒有壓縮對話:對話太短,或壓縮被中止了。",
111
- cmdChoose: "選擇", cmdCurrent: "目前", chipCompact: "壓縮", chipClear: "清空…", chipContext: "已用 {0}% 的 context",
178
+ cmdChoose: "選擇", cmdCurrent: "目前", cmdSignIn: "登入", cmdSignInTo: "登入 {0}", chipCompact: "壓縮", chipClear: "清空…", chipContext: "已用 {0}% 的 context",
112
179
  chipModel: "模型:{0}——選擇其他", chipEffort: "推理強度:{0}——選擇其他",
113
180
  cmd_ctx: "context 用量、模型與推理強度", cmd_compact: "壓縮對話", cmd_clear: "清空對話(會先確認)",
114
181
  cmd_model: "查看或切換模型", cmd_backend: "切換 CLI backend,再選模型和推理強度(會重新啟動)", cmd_effort: "查看或設定推理強度", cmd_cancel: "停止目前的回覆",
@@ -123,7 +190,8 @@ register("chat", {
123
190
  a11yWorking: "{0} 開始工作", a11yIdle: "{0} 已完成", a11yReplied: "{0} 已回覆", conversationWith: "與 {0} 的對話",
124
191
  stopping: "正在中止 {0}…", awaiting: "{0} 正在等你回應", approxNote: "從終端畫面判讀,僅供參考", elapsedTitle: "從這個頁面看到它開始工作起算(約略)",
125
192
  dropHere: "放開即可附加檔案", asText: "改為文字", pasteAttached: "貼上的長文字({0} 個字元)已改為文字檔附加", pasteKeptAsText: "附件已滿,貼上的長文字保留為一般文字",
126
- send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", attachTitle: "附加檔案(任何類型,也可以貼上或拖曳)",
193
+ send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", guestMore: "Esc 中止回覆",
194
+ guestNote: "你是訪客:agent 會把你的訊息視為訪客的訊息(僅供辨識)。", guestNoCommands: "訪客不能執行指令,請去掉開頭的 / 再送出。", attachTitle: "附加檔案(任何類型,也可以貼上或拖曳)",
127
195
  pvPreview: "預覽", pvStop: "停止", pvDownload: "下載", pvMenu: "預覽選項", pvHtml: "HTML",
128
196
  pvAllowButton: "允許預覽", pvAllow: "允許預覽 HTML(預覽可能把資料傳出去)", pvDisallow: "不再允許預覽 HTML",
129
197
  pvConfirm: "要允許預覽 HTML 嗎?\n\n這會套用到你登入的每一台裝置。預覽會在隔離的框架裡執行 agent 的 HTML。它無法使用你的登入,但可能把資料傳出去,也可能讓這個分頁變慢或卡住。每次仍需要你按「預覽」。",
@@ -168,6 +236,8 @@ register("chat", {
168
236
  "pvFail_bad-address": "Cloudflare 給出的網址無法使用",
169
237
  pvFail_unknown: "發生了未預期的錯誤",
170
238
  pvServer_notOffered: "這個頁面不提供預覽。",
239
+ pvServer_listenFailed: "無法預覽:fleet 無法開啟預覽用的連接埠({0})。可能有其他程式正在使用它——請設定 web.preview_port,然後重新啟動 AgEnD。",
240
+ pvServer_notListening: "這個 fleet 目前無法預覽。",
171
241
  previews: "允許預覽 HTML",
172
242
  clicked: "點選了:", clickShow: "顯示這些按鈕所在的訊息",
173
243
  copy: "複製", copyMessage: "複製這則回覆", copied: "已複製", copyFailed: "複製失敗", wrap: "換行", showAll: "展開全部 {0} 行", showMore: "顯示更多", showLess: "收合", jumpLatest: "最新", newBelow: "{0} 則新訊息",
@@ -189,10 +259,17 @@ register("chat", {
189
259
  dEditInSettings: "到設定編輯", dPause: "暫停", dWake: "喚醒", dPaused: "{0} 已暫停", dWoken: "{0} 正在喚醒", dActionFailed: "沒有成功,請再試一次。",
190
260
  dNoActivity: "最近一小時沒有活動。", dWorking: "處理中…",
191
261
  themeLock: "鎖定主題", themeUnlock: "解除主題鎖定", themeReset: "重設主題…", themeResetGo: "重設",
262
+ pageColours: "頁面配色", pageAgentOn: "依 agent 的設定(開)", pageAgentOff: "依 agent 的設定(關)", pageWhole: "整個頁面", pageChatOnly: "只在它的聊天",
192
263
  themeResetConfirm: "要把 {0} 的主題重設為預設嗎?顏色、圖片和處理中文字都會移除;鎖定會保留。",
193
264
  themeResetDone: "已重設 {0} 的主題。", themeLocked: "已鎖定 {0} 的主題:agent 無法變更。", themeUnlocked: "已解除 {0} 的主題鎖定。",
194
265
  themeFailed: "主題沒有變更({0})。",
195
266
  widgetRemoveHeader: "移除聊天標頭的小工具", widgetRemoveDetails: "移除詳細資料頁的小工具", widgetRemoved: "已移除 {0} 的小工具。",
267
+ ambientBlocked_hidden: "環境效果未顯示:已關閉 agent 的主題——開啟",
268
+ ambientBlocked_safe: "環境效果未顯示:安全模式(?safe=1)——離開安全模式",
269
+ ambientBlocked_reducedMotion: "環境效果未顯示:這台裝置跟隨「減少動態效果」——仍然播放",
270
+ ambientBlocked_unsupported: "環境效果未顯示:此瀏覽器無法播放",
271
+ ambientClearLoop: "清除環境效果", ambientClearClick: "清除點擊效果", ambientCleared: "已清除 {0} 的環境效果。",
272
+ ambientMotionNote: "你的系統要求減少動態效果;效果目前開著。", ambientMotionNoteLink: "在這裡關閉", ambientMotionNoteClose: "關閉",
196
273
  configured: "(設定值)", fleetDefault: "(fleet 預設)",
197
274
  },
198
275
  });