@songsid/agend 2.3.0-beta.9 → 2.3.1

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 (743) hide show
  1. package/README.md +16 -8
  2. package/README.zh-TW.md +16 -8
  3. package/dist/adapter-world.js.map +1 -1
  4. package/dist/agend-home.d.ts +5 -0
  5. package/dist/agend-home.js +7 -0
  6. package/dist/agend-home.js.map +1 -0
  7. package/dist/agent-cli-instructions.md +7 -1
  8. package/dist/agent-cli.js +41 -0
  9. package/dist/agent-cli.js.map +1 -1
  10. package/dist/agent-endpoint.d.ts +9 -0
  11. package/dist/agent-endpoint.js +23 -0
  12. package/dist/agent-endpoint.js.map +1 -1
  13. package/dist/agent-file-read.js +1 -1
  14. package/dist/agent-file-read.js.map +1 -1
  15. package/dist/agent-whats-new.d.ts +16 -0
  16. package/dist/agent-whats-new.js +53 -12
  17. package/dist/agent-whats-new.js.map +1 -1
  18. package/dist/apply-job.js.map +1 -1
  19. package/dist/atomic-write.js.map +1 -1
  20. package/dist/auth-api.d.ts +12 -0
  21. package/dist/auth-api.js +77 -22
  22. package/dist/auth-api.js.map +1 -1
  23. package/dist/authority/authority-state.d.ts +4 -0
  24. package/dist/authority/authority-state.js +17 -8
  25. package/dist/authority/authority-state.js.map +1 -1
  26. package/dist/authority/session-authorizations.js.map +1 -1
  27. package/dist/authority/web-authority.d.ts +16 -8
  28. package/dist/authority/web-authority.js +19 -5
  29. package/dist/authority/web-authority.js.map +1 -1
  30. package/dist/backend/antigravity.d.ts +2 -2
  31. package/dist/backend/antigravity.js +2 -1
  32. package/dist/backend/antigravity.js.map +1 -1
  33. package/dist/backend/claude-code.js +13 -4
  34. package/dist/backend/claude-code.js.map +1 -1
  35. package/dist/backend/codex-metadata.d.ts +8 -0
  36. package/dist/backend/codex-metadata.js +74 -17
  37. package/dist/backend/codex-metadata.js.map +1 -1
  38. package/dist/backend/codex-package.d.ts +30 -0
  39. package/dist/backend/codex-package.js +133 -0
  40. package/dist/backend/codex-package.js.map +1 -0
  41. package/dist/backend/codex-session-lookup.js.map +1 -1
  42. package/dist/backend/codex.d.ts +2 -0
  43. package/dist/backend/codex.js +25 -5
  44. package/dist/backend/codex.js.map +1 -1
  45. package/dist/backend/credential-profile.d.ts +2 -0
  46. package/dist/backend/credential-profile.js +2 -0
  47. package/dist/backend/credential-profile.js.map +1 -1
  48. package/dist/backend/effort-metadata.js.map +1 -1
  49. package/dist/backend/grok.js +58 -3
  50. package/dist/backend/grok.js.map +1 -1
  51. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  52. package/dist/backend/kiro-kas-store.js.map +1 -1
  53. package/dist/backend/kiro-v2-store.js.map +1 -1
  54. package/dist/backend/kiro-v3-identity.js.map +1 -1
  55. package/dist/backend/kiro.d.ts +9 -1
  56. package/dist/backend/kiro.js +102 -1
  57. package/dist/backend/kiro.js.map +1 -1
  58. package/dist/backend/launch-effort.js.map +1 -1
  59. package/dist/backend/mock.js.map +1 -1
  60. package/dist/backend/muse.d.ts +20 -2
  61. package/dist/backend/muse.js +154 -16
  62. package/dist/backend/muse.js.map +1 -1
  63. package/dist/backend/opencode-permission.js +1 -1
  64. package/dist/backend/opencode-permission.js.map +1 -1
  65. package/dist/backend/opencode.js +2 -1
  66. package/dist/backend/opencode.js.map +1 -1
  67. package/dist/backend/session-signals.js.map +1 -1
  68. package/dist/backend-availability.js.map +1 -1
  69. package/dist/backend-command.d.ts +2 -0
  70. package/dist/backend-command.js +3 -1
  71. package/dist/backend-command.js.map +1 -1
  72. package/dist/backend-outage.js.map +1 -1
  73. package/dist/binary-probe.js +1 -1
  74. package/dist/binary-probe.js.map +1 -1
  75. package/dist/bug-report-cli.d.ts +30 -0
  76. package/dist/bug-report-cli.js +139 -0
  77. package/dist/bug-report-cli.js.map +1 -0
  78. package/dist/bug-report.d.ts +47 -0
  79. package/dist/bug-report.js +137 -0
  80. package/dist/bug-report.js.map +1 -0
  81. package/dist/bundled-skills.d.ts +18 -0
  82. package/dist/bundled-skills.js +38 -0
  83. package/dist/bundled-skills.js.map +1 -0
  84. package/dist/cache-service.js +1 -1
  85. package/dist/cache-service.js.map +1 -1
  86. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  87. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  88. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  89. package/dist/channel/adapters/discord.d.ts +4 -0
  90. package/dist/channel/adapters/discord.js +57 -12
  91. package/dist/channel/adapters/discord.js.map +1 -1
  92. package/dist/channel/adapters/telegram.d.ts +7 -1
  93. package/dist/channel/adapters/telegram.js +57 -49
  94. package/dist/channel/adapters/telegram.js.map +1 -1
  95. package/dist/channel/mcp-server.js +21 -4
  96. package/dist/channel/mcp-server.js.map +1 -1
  97. package/dist/channel/mcp-tools.d.ts +3 -1
  98. package/dist/channel/mcp-tools.js +41 -4
  99. package/dist/channel/mcp-tools.js.map +1 -1
  100. package/dist/channel/message-queue.js.map +1 -1
  101. package/dist/channel/sticker-download.js.map +1 -1
  102. package/dist/channel/tool-router.js.map +1 -1
  103. package/dist/child-process.d.ts +21 -0
  104. package/dist/child-process.js +54 -0
  105. package/dist/child-process.js.map +1 -0
  106. package/dist/classic-channel-manager.js +3 -2
  107. package/dist/classic-channel-manager.js.map +1 -1
  108. package/dist/cli-entry.js.map +1 -1
  109. package/dist/cli-flags.js.map +1 -1
  110. package/dist/cli.d.ts +1 -0
  111. package/dist/cli.js +448 -313
  112. package/dist/cli.js.map +1 -1
  113. package/dist/command-table.d.ts +1 -1
  114. package/dist/command-table.js +1 -1
  115. package/dist/command-table.js.map +1 -1
  116. package/dist/compact-watch.js.map +1 -1
  117. package/dist/completion-install.js.map +1 -1
  118. package/dist/completion.js.map +1 -1
  119. package/dist/config-validator.d.ts +6 -0
  120. package/dist/config-validator.js +81 -8
  121. package/dist/config-validator.js.map +1 -1
  122. package/dist/config.d.ts +15 -3
  123. package/dist/config.js +41 -10
  124. package/dist/config.js.map +1 -1
  125. package/dist/connection-secrets.js.map +1 -1
  126. package/dist/context-guardian.js.map +1 -1
  127. package/dist/coredump-filter.js.map +1 -1
  128. package/dist/cpu-profile.js.map +1 -1
  129. package/dist/cross-instance-envelope.js.map +1 -1
  130. package/dist/cross-instance-notice.d.ts +7 -0
  131. package/dist/cross-instance-notice.js +10 -0
  132. package/dist/cross-instance-notice.js.map +1 -1
  133. package/dist/daemon.d.ts +52 -1
  134. package/dist/daemon.js +385 -27
  135. package/dist/daemon.js.map +1 -1
  136. package/dist/daily-summary.js.map +1 -1
  137. package/dist/darwin-memory.js +1 -1
  138. package/dist/darwin-memory.js.map +1 -1
  139. package/dist/dashboard-live.d.ts +8 -0
  140. package/dist/dashboard-live.js +35 -0
  141. package/dist/dashboard-live.js.map +1 -0
  142. package/dist/dashboard-permissions.d.ts +69 -0
  143. package/dist/dashboard-permissions.js +138 -0
  144. package/dist/dashboard-permissions.js.map +1 -0
  145. package/dist/decision-list.js.map +1 -1
  146. package/dist/delivery-consumed-watch.js.map +1 -1
  147. package/dist/delivery-outbox.d.ts +11 -0
  148. package/dist/delivery-outbox.js +52 -2
  149. package/dist/delivery-outbox.js.map +1 -1
  150. package/dist/delivery-outbox.test.js.map +1 -1
  151. package/dist/delivery-reconciliation.test.js.map +1 -1
  152. package/dist/diagnostic-redaction.d.ts +10 -0
  153. package/dist/diagnostic-redaction.js +167 -0
  154. package/dist/diagnostic-redaction.js.map +1 -0
  155. package/dist/doctor.d.ts +8 -0
  156. package/dist/doctor.js +52 -0
  157. package/dist/doctor.js.map +1 -1
  158. package/dist/e2e-isolation.js.map +1 -1
  159. package/dist/event-log.js.map +1 -1
  160. package/dist/export-import.js +7 -5
  161. package/dist/export-import.js.map +1 -1
  162. package/dist/fleet-context.d.ts +1 -1
  163. package/dist/fleet-control-audit.js.map +1 -1
  164. package/dist/fleet-label.js.map +1 -1
  165. package/dist/fleet-level-config.d.ts +1 -1
  166. package/dist/fleet-level-config.js +2 -0
  167. package/dist/fleet-level-config.js.map +1 -1
  168. package/dist/fleet-lock.d.ts +6 -0
  169. package/dist/fleet-lock.js +22 -2
  170. package/dist/fleet-lock.js.map +1 -1
  171. package/dist/fleet-manager.d.ts +283 -15
  172. package/dist/fleet-manager.js +1825 -331
  173. package/dist/fleet-manager.js.map +1 -1
  174. package/dist/fleet-process-handlers.d.ts +18 -0
  175. package/dist/fleet-process-handlers.js +70 -0
  176. package/dist/fleet-process-handlers.js.map +1 -0
  177. package/dist/fleet-startup.d.ts +14 -0
  178. package/dist/fleet-startup.js +30 -0
  179. package/dist/fleet-startup.js.map +1 -0
  180. package/dist/fleet-traffic.js.map +1 -1
  181. package/dist/forged-envelope-scan.d.ts +0 -20
  182. package/dist/forged-envelope-scan.js +3 -2
  183. package/dist/forged-envelope-scan.js.map +1 -1
  184. package/dist/full-restart.d.ts +5 -5
  185. package/dist/full-restart.js +5 -5
  186. package/dist/full-restart.js.map +1 -1
  187. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  188. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  189. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  190. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  191. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  192. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +11 -0
  193. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  194. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  195. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  196. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  197. package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -4
  198. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +554 -0
  199. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +156 -32
  200. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  201. package/dist/general-knowledge/steering/core-rules.md +1 -1
  202. package/dist/host-memory.js.map +1 -1
  203. package/dist/idle-gate-log.d.ts +19 -0
  204. package/dist/idle-gate-log.js +15 -0
  205. package/dist/idle-gate-log.js.map +1 -0
  206. package/dist/instance-config-impact.js +25 -4
  207. package/dist/instance-config-impact.js.map +1 -1
  208. package/dist/instance-lifecycle.d.ts +11 -1
  209. package/dist/instance-lifecycle.js +97 -25
  210. package/dist/instance-lifecycle.js.map +1 -1
  211. package/dist/instructions.d.ts +12 -6
  212. package/dist/instructions.js +19 -10
  213. package/dist/instructions.js.map +1 -1
  214. package/dist/kiro-compact-pane.js.map +1 -1
  215. package/dist/kiro-db-reader.js.map +1 -1
  216. package/dist/kiro-engine-status.js.map +1 -1
  217. package/dist/kiro-log-gate.js.map +1 -1
  218. package/dist/kiro-transcript-lane.js.map +1 -1
  219. package/dist/locale.js +450 -20
  220. package/dist/locale.js.map +1 -1
  221. package/dist/log-service.js +1 -0
  222. package/dist/log-service.js.map +1 -1
  223. package/dist/log-steps.d.ts +1 -0
  224. package/dist/log-steps.js +15 -0
  225. package/dist/log-steps.js.map +1 -1
  226. package/dist/logger.js +3 -2
  227. package/dist/logger.js.map +1 -1
  228. package/dist/login-controller.d.ts +45 -0
  229. package/dist/login-controller.js +101 -5
  230. package/dist/login-controller.js.map +1 -1
  231. package/dist/login-flows.js +1 -1
  232. package/dist/login-manager.js.map +1 -1
  233. package/dist/login-shell.js +3 -1
  234. package/dist/login-shell.js.map +1 -1
  235. package/dist/ls-attach-resolve.d.ts +96 -0
  236. package/dist/ls-attach-resolve.js +180 -0
  237. package/dist/ls-attach-resolve.js.map +1 -0
  238. package/dist/ls-rows.d.ts +12 -0
  239. package/dist/ls-rows.js +83 -4
  240. package/dist/ls-rows.js.map +1 -1
  241. package/dist/memory-pressure.js.map +1 -1
  242. package/dist/muse-usage-relay.js.map +1 -1
  243. package/dist/needs-you-hub.d.ts +98 -1
  244. package/dist/needs-you-hub.js +313 -44
  245. package/dist/needs-you-hub.js.map +1 -1
  246. package/dist/needs-you-live.d.ts +5 -2
  247. package/dist/needs-you-live.js +6 -3
  248. package/dist/needs-you-live.js.map +1 -1
  249. package/dist/needs-you.d.ts +68 -3
  250. package/dist/needs-you.js +39 -4
  251. package/dist/needs-you.js.map +1 -1
  252. package/dist/network-family.js.map +1 -1
  253. package/dist/operator-control-paths.js +3 -4
  254. package/dist/operator-control-paths.js.map +1 -1
  255. package/dist/outbound-handlers.d.ts +5 -2
  256. package/dist/outbound-handlers.js +81 -24
  257. package/dist/outbound-handlers.js.map +1 -1
  258. package/dist/outbound-schemas.d.ts +106 -60
  259. package/dist/outbound-schemas.js +33 -10
  260. package/dist/outbound-schemas.js.map +1 -1
  261. package/dist/outbox-open-error.js.map +1 -1
  262. package/dist/pane-context-cache.js.map +1 -1
  263. package/dist/paths.js.map +1 -1
  264. package/dist/private-dir.js.map +1 -1
  265. package/dist/probe-worker-pool.js.map +1 -1
  266. package/dist/profile-control.js.map +1 -1
  267. package/dist/prompt-file-ref.js.map +1 -1
  268. package/dist/provider-probe.js.map +1 -1
  269. package/dist/provider-secret-registry.d.ts +5 -3
  270. package/dist/provider-secret-registry.js +8 -2
  271. package/dist/provider-secret-registry.js.map +1 -1
  272. package/dist/public-link-auto-open.d.ts +32 -0
  273. package/dist/public-link-auto-open.js +86 -0
  274. package/dist/public-link-auto-open.js.map +1 -0
  275. package/dist/public-link-owner.d.ts +10 -0
  276. package/dist/public-link-owner.js +36 -0
  277. package/dist/public-link-owner.js.map +1 -0
  278. package/dist/public-link-progress.d.ts +12 -1
  279. package/dist/public-link-progress.js +5 -10
  280. package/dist/public-link-progress.js.map +1 -1
  281. package/dist/public-preview.js.map +1 -1
  282. package/dist/public-tunnel-config.d.ts +11 -0
  283. package/dist/public-tunnel-config.js +68 -0
  284. package/dist/public-tunnel-config.js.map +1 -0
  285. package/dist/public-web-gateway.d.ts +2 -0
  286. package/dist/public-web-gateway.js +19 -6
  287. package/dist/public-web-gateway.js.map +1 -1
  288. package/dist/public-web-link.d.ts +48 -3
  289. package/dist/public-web-link.js +177 -51
  290. package/dist/public-web-link.js.map +1 -1
  291. package/dist/quickstart-api.d.ts +9 -0
  292. package/dist/quickstart-api.js +82 -0
  293. package/dist/quickstart-api.js.map +1 -1
  294. package/dist/quickstart.d.ts +5 -0
  295. package/dist/quickstart.js +40 -25
  296. package/dist/quickstart.js.map +1 -1
  297. package/dist/reply-buttons-controller.js.map +1 -1
  298. package/dist/reply-dedup.js.map +1 -1
  299. package/dist/resource-report.js +1 -1
  300. package/dist/resource-report.js.map +1 -1
  301. package/dist/restart-guard.js.map +1 -1
  302. package/dist/restart-progress.d.ts +80 -1
  303. package/dist/restart-progress.js +306 -21
  304. package/dist/restart-progress.js.map +1 -1
  305. package/dist/rollout-index.js.map +1 -1
  306. package/dist/runtime-cpu-profile.js.map +1 -1
  307. package/dist/scheduler/db.d.ts +40 -13
  308. package/dist/scheduler/db.js +123 -89
  309. package/dist/scheduler/db.js.map +1 -1
  310. package/dist/scheduler/scheduler.js.map +1 -1
  311. package/dist/scheduler/task-board-text.d.ts +26 -0
  312. package/dist/scheduler/task-board-text.js +140 -0
  313. package/dist/scheduler/task-board-text.js.map +1 -0
  314. package/dist/scheduler/task-board.d.ts +730 -0
  315. package/dist/scheduler/task-board.js +2179 -0
  316. package/dist/scheduler/task-board.js.map +1 -0
  317. package/dist/scheduler/types.d.ts +6 -1
  318. package/dist/sd-notify.js +2 -1
  319. package/dist/sd-notify.js.map +1 -1
  320. package/dist/search/search-docs.d.ts +58 -0
  321. package/dist/search/search-docs.js +63 -0
  322. package/dist/search/search-docs.js.map +1 -0
  323. package/dist/search/search-index.d.ts +149 -0
  324. package/dist/search/search-index.js +453 -0
  325. package/dist/search/search-index.js.map +1 -0
  326. package/dist/search/search-store.d.ts +122 -0
  327. package/dist/search/search-store.js +236 -0
  328. package/dist/search/search-store.js.map +1 -0
  329. package/dist/search/search-worker.d.ts +1 -0
  330. package/dist/search/search-worker.js +163 -0
  331. package/dist/search/search-worker.js.map +1 -0
  332. package/dist/secret-file.js.map +1 -1
  333. package/dist/secret-store.d.ts +2 -0
  334. package/dist/secret-store.js +21 -0
  335. package/dist/secret-store.js.map +1 -1
  336. package/dist/service-installer.d.ts +98 -1
  337. package/dist/service-installer.js +246 -11
  338. package/dist/service-installer.js.map +1 -1
  339. package/dist/service-owner.d.ts +44 -0
  340. package/dist/service-owner.js +48 -0
  341. package/dist/service-owner.js.map +1 -0
  342. package/dist/service-plan.d.ts +39 -2
  343. package/dist/service-plan.js +188 -6
  344. package/dist/service-plan.js.map +1 -1
  345. package/dist/session-oom-stop.d.ts +45 -0
  346. package/dist/session-oom-stop.js +259 -0
  347. package/dist/session-oom-stop.js.map +1 -0
  348. package/dist/session-oom.d.ts +34 -0
  349. package/dist/session-oom.js +251 -0
  350. package/dist/session-oom.js.map +1 -0
  351. package/dist/settings-api.d.ts +14 -1
  352. package/dist/settings-api.js +74 -13
  353. package/dist/settings-api.js.map +1 -1
  354. package/dist/settings-baseline.js.map +1 -1
  355. package/dist/settings-change.d.ts +5 -0
  356. package/dist/settings-change.js +167 -6
  357. package/dist/settings-change.js.map +1 -1
  358. package/dist/settings-confirmation.d.ts +9 -1
  359. package/dist/settings-confirmation.js +18 -8
  360. package/dist/settings-confirmation.js.map +1 -1
  361. package/dist/settings-control.js +2 -1
  362. package/dist/settings-control.js.map +1 -1
  363. package/dist/settings-effect.js +29 -4
  364. package/dist/settings-effect.js.map +1 -1
  365. package/dist/settings-http-confirmation.d.ts +13 -0
  366. package/dist/settings-http-confirmation.js +15 -3
  367. package/dist/settings-http-confirmation.js.map +1 -1
  368. package/dist/settings-instance-patch.js +13 -1
  369. package/dist/settings-instance-patch.js.map +1 -1
  370. package/dist/settings-request-capability.d.ts +4 -1
  371. package/dist/settings-request-capability.js +11 -3
  372. package/dist/settings-request-capability.js.map +1 -1
  373. package/dist/settings-transaction.js.map +1 -1
  374. package/dist/setup-auth.d.ts +1 -0
  375. package/dist/setup-auth.js +2 -1
  376. package/dist/setup-auth.js.map +1 -1
  377. package/dist/setup-form.d.ts +1 -1
  378. package/dist/setup-form.js +23 -7
  379. package/dist/setup-form.js.map +1 -1
  380. package/dist/setup-host.d.ts +6 -1
  381. package/dist/setup-host.js +15 -4
  382. package/dist/setup-host.js.map +1 -1
  383. package/dist/setup-wizard.js +6 -3
  384. package/dist/setup-wizard.js.map +1 -1
  385. package/dist/spawn-gate.d.ts +7 -0
  386. package/dist/spawn-gate.js +45 -12
  387. package/dist/spawn-gate.js.map +1 -1
  388. package/dist/start-attempt.d.ts +11 -0
  389. package/dist/start-attempt.js +29 -0
  390. package/dist/start-attempt.js.map +1 -0
  391. package/dist/start-failure-chat.d.ts +37 -0
  392. package/dist/start-failure-chat.js +142 -0
  393. package/dist/start-failure-chat.js.map +1 -0
  394. package/dist/start-failure-notice.d.ts +4 -0
  395. package/dist/start-failure-notice.js +20 -0
  396. package/dist/start-failure-notice.js.map +1 -0
  397. package/dist/start-failure.d.ts +171 -0
  398. package/dist/start-failure.js +348 -0
  399. package/dist/start-failure.js.map +1 -0
  400. package/dist/startup-memory-deadline.d.ts +23 -0
  401. package/dist/startup-memory-deadline.js +55 -0
  402. package/dist/startup-memory-deadline.js.map +1 -0
  403. package/dist/statusline-watcher.js.map +1 -1
  404. package/dist/storm-window.js.map +1 -1
  405. package/dist/systemd-peer-bus.js.map +1 -1
  406. package/dist/target-queue-worker.js.map +1 -1
  407. package/dist/task-board-summary.d.ts +101 -0
  408. package/dist/task-board-summary.js +177 -0
  409. package/dist/task-board-summary.js.map +1 -0
  410. package/dist/task-board-tool.d.ts +25 -0
  411. package/dist/task-board-tool.js +155 -0
  412. package/dist/task-board-tool.js.map +1 -0
  413. package/dist/task-board-waker.d.ts +51 -0
  414. package/dist/task-board-waker.js +141 -0
  415. package/dist/task-board-waker.js.map +1 -0
  416. package/dist/telegram-api-root.d.ts +1 -0
  417. package/dist/telegram-api-root.js +40 -0
  418. package/dist/telegram-api-root.js.map +1 -0
  419. package/dist/tips.js.map +1 -1
  420. package/dist/tmux-control.js +1 -1
  421. package/dist/tmux-control.js.map +1 -1
  422. package/dist/tmux-exec-helper.d.ts +104 -0
  423. package/dist/tmux-exec-helper.js +412 -0
  424. package/dist/tmux-exec-helper.js.map +1 -0
  425. package/dist/tmux-manager.d.ts +1 -0
  426. package/dist/tmux-manager.js +29 -13
  427. package/dist/tmux-manager.js.map +1 -1
  428. package/dist/tmux-read.js +2 -2
  429. package/dist/tmux-read.js.map +1 -1
  430. package/dist/tool-permissions.js.map +1 -1
  431. package/dist/tool-progress.js.map +1 -1
  432. package/dist/topic-archiver.js.map +1 -1
  433. package/dist/topic-commands.d.ts +13 -0
  434. package/dist/topic-commands.js +56 -33
  435. package/dist/topic-commands.js.map +1 -1
  436. package/dist/topic-link.js.map +1 -1
  437. package/dist/transcript-delta-reader.js.map +1 -1
  438. package/dist/transcript-jsonl.js.map +1 -1
  439. package/dist/transcript-monitor.js +5 -0
  440. package/dist/transcript-monitor.js.map +1 -1
  441. package/dist/transcript-sources.d.ts +2 -0
  442. package/dist/transcript-sources.js +4 -0
  443. package/dist/transcript-sources.js.map +1 -1
  444. package/dist/tunnel/cloudflared-install.js +3 -1
  445. package/dist/tunnel/cloudflared-install.js.map +1 -1
  446. package/dist/tunnel/cloudflared.d.ts +7 -16
  447. package/dist/tunnel/cloudflared.js +100 -13
  448. package/dist/tunnel/cloudflared.js.map +1 -1
  449. package/dist/tunnel/lease.js +3 -2
  450. package/dist/tunnel/lease.js.map +1 -1
  451. package/dist/tunnel/manager.js.map +1 -1
  452. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  453. package/dist/tunnel/named-diagnostics.js +52 -0
  454. package/dist/tunnel/named-diagnostics.js.map +1 -0
  455. package/dist/tunnel/public-fetch.js.map +1 -1
  456. package/dist/tunnel/purpose-lane.js.map +1 -1
  457. package/dist/tunnel/types.d.ts +4 -1
  458. package/dist/tunnel/types.js.map +1 -1
  459. package/dist/types.d.ts +56 -2
  460. package/dist/ui/app-effects.js +23 -5
  461. package/dist/ui/app.html +2 -0
  462. package/dist/ui/backend-signin.js +297 -0
  463. package/dist/ui/chat-effects.js +10 -3
  464. package/dist/ui/chat-msg-actions.js +176 -0
  465. package/dist/ui/chat-render.js +62 -18
  466. package/dist/ui/chat-store.js +218 -48
  467. package/dist/ui/chat-strings.js +40 -8
  468. package/dist/ui/chat-thread.js +119 -10
  469. package/dist/ui/effect-trial-offer.js +2 -31
  470. package/dist/ui/effects-waker.js +2 -0
  471. package/dist/ui/fleet-canvas-layout.js +13 -4
  472. package/dist/ui/fleet-canvas.js +69 -28
  473. package/dist/ui/fleet-office-editor.js +149 -0
  474. package/dist/ui/fleet-office-model.js +358 -0
  475. package/dist/ui/fleet-office-pack.js +199 -0
  476. package/dist/ui/fleet-office-skin.js +474 -0
  477. package/dist/ui/fleet-office-store.js +49 -0
  478. package/dist/ui/fleet-office-zones.js +177 -0
  479. package/dist/ui/fleet-office.js +434 -0
  480. package/dist/ui/fleet-org.js +18 -5
  481. package/dist/ui/fleet-route-scene.js +272 -0
  482. package/dist/ui/fleet-route-worker.js +21 -0
  483. package/dist/ui/fleet-route.js +88 -0
  484. package/dist/ui/fleet-task-card.js +436 -0
  485. package/dist/ui/fleet-task-common.js +482 -0
  486. package/dist/ui/fleet-task-work.js +107 -0
  487. package/dist/ui/fleet-tasks.js +569 -0
  488. package/dist/ui/fleet-traffic.js +81 -18
  489. package/dist/ui/fleet-usage.js +36 -2
  490. package/dist/ui/guest-refused.html +41 -0
  491. package/dist/ui/icons/icon-192.png +0 -0
  492. package/dist/ui/icons/icon-512.png +0 -0
  493. package/dist/ui/image-lightbox.js +45 -1
  494. package/dist/ui/image-zoom.js +242 -0
  495. package/dist/ui/panel-chat.js +214 -57
  496. package/dist/ui/panel-details.js +2 -0
  497. package/dist/ui/panel-fleet.js +32 -71
  498. package/dist/ui/panel-log.js +3 -3
  499. package/dist/ui/panel-needs.js +23 -6
  500. package/dist/ui/panel-settings.js +166 -33
  501. package/dist/ui/preview-panel.js +2 -1
  502. package/dist/ui/search-dialog.js +350 -0
  503. package/dist/ui/settings-appearance.js +170 -10
  504. package/dist/ui/settings-apply.js +3 -3
  505. package/dist/ui/settings-confirm.js +7 -2
  506. package/dist/ui/settings-dialogs.js +46 -21
  507. package/dist/ui/settings-discord.js +6 -2
  508. package/dist/ui/settings-guests.js +2 -1
  509. package/dist/ui/settings-learn.js +1 -0
  510. package/dist/ui/settings-model.js +51 -18
  511. package/dist/ui/settings-mods.js +8 -3
  512. package/dist/ui/settings-owner-effects.js +6 -4
  513. package/dist/ui/settings-strings.js +186 -6
  514. package/dist/ui/settings-token.js +3 -1
  515. package/dist/ui/settings-web-authority.js +13 -5
  516. package/dist/ui/settings-wizard.js +13 -7
  517. package/dist/ui/shared/agend-auth.js +2 -0
  518. package/dist/ui/shared/app-i18n.js +12 -5
  519. package/dist/ui/shared/app-needs.js +2 -2
  520. package/dist/ui/shared/app-prefs.js +15 -0
  521. package/dist/ui/shared/app-route.js +18 -0
  522. package/dist/ui/shared/app-shell.js +189 -28
  523. package/dist/ui/shared/app-store.js +3 -2
  524. package/dist/ui/shared/app-stream.js +5 -1
  525. package/dist/ui/shared/app-themes.js +116 -1
  526. package/dist/ui/shared/app.css +668 -48
  527. package/dist/ui/shared/app.js +9 -1
  528. package/dist/ui/shared/card-strings.js +4 -0
  529. package/dist/ui/shared/effect-app-runtime.js +7 -5
  530. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  531. package/dist/ui/shared/effect-cursor.js +3 -2
  532. package/dist/ui/shared/effect-display-metadata.js +39 -0
  533. package/dist/ui/shared/effect-owner-source.js +8 -1
  534. package/dist/ui/shared/effect-registry.js +14 -4
  535. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  536. package/dist/ui/shared/effect-stock-trust.js +5 -5
  537. package/dist/ui/shared/feature-flags.js +127 -0
  538. package/dist/ui/shared/guest-refused.js +35 -0
  539. package/dist/ui/shared/header-tools.js +2 -2
  540. package/dist/ui/shared/identity-card.js +26 -8
  541. package/dist/ui/shared/identity-color.js +17 -10
  542. package/dist/ui/shared/instance-nav.js +183 -28
  543. package/dist/ui/shared/owner-css.js +30 -1
  544. package/dist/ui/shared/panel-view.js +14 -29
  545. package/dist/ui/shared/text-undo.js +210 -0
  546. package/dist/ui/shared/tokens.css +4 -0
  547. package/dist/ui/shared/ui-dialog.js +5 -4
  548. package/dist/ui/shared/ui-icons.js +4 -0
  549. package/dist/ui/shared/ui-menu.js +20 -7
  550. package/dist/ui/shared/ui-toast.js +16 -3
  551. package/dist/ui/shared/usage-dialog.js +216 -16
  552. package/dist/ui/shared/view-strings.js +70 -4
  553. package/dist/ui/shared/web-authorize.js +50 -1
  554. package/dist/ui/signin.html +2 -0
  555. package/dist/ui/web-terminal/terminal.html +10 -10
  556. package/dist/ui/web-terminal/terminal.js +58 -12
  557. package/dist/update-check.js.map +1 -1
  558. package/dist/update-dispatch.js +1 -1
  559. package/dist/update-dispatch.js.map +1 -1
  560. package/dist/update-install.js.map +1 -1
  561. package/dist/update-launch.d.ts +10 -1
  562. package/dist/update-launch.js +39 -14
  563. package/dist/update-launch.js.map +1 -1
  564. package/dist/update-marker.js.map +1 -1
  565. package/dist/update-preflight.js +1 -1
  566. package/dist/update-preflight.js.map +1 -1
  567. package/dist/update-progress.d.ts +3 -1
  568. package/dist/update-progress.js +3 -1
  569. package/dist/update-progress.js.map +1 -1
  570. package/dist/usage/format-rich.js +6 -3
  571. package/dist/usage/format-rich.js.map +1 -1
  572. package/dist/usage/i18n-keys.d.ts +1 -1
  573. package/dist/usage/i18n-keys.js +2 -1
  574. package/dist/usage/i18n-keys.js.map +1 -1
  575. package/dist/usage/i18n.d.ts +13 -1
  576. package/dist/usage/i18n.js +29 -0
  577. package/dist/usage/i18n.js.map +1 -1
  578. package/dist/usage/providers.d.ts +89 -2
  579. package/dist/usage/providers.js +247 -41
  580. package/dist/usage/providers.js.map +1 -1
  581. package/dist/usage/statusline-usage.js.map +1 -1
  582. package/dist/usage/usage-api.d.ts +5 -1
  583. package/dist/usage/usage-api.js +185 -58
  584. package/dist/usage/usage-api.js.map +1 -1
  585. package/dist/usage-report.js.map +1 -1
  586. package/dist/usage-rollup.js +1 -1
  587. package/dist/usage-rollup.js.map +1 -1
  588. package/dist/user-systemd.d.ts +24 -0
  589. package/dist/user-systemd.js +75 -0
  590. package/dist/user-systemd.js.map +1 -0
  591. package/dist/view-api.d.ts +7 -0
  592. package/dist/view-api.js +16 -4
  593. package/dist/view-api.js.map +1 -1
  594. package/dist/wake-coordinator.js.map +1 -1
  595. package/dist/web-ambient.js.map +1 -1
  596. package/dist/web-api.d.ts +58 -11
  597. package/dist/web-api.js +1104 -71
  598. package/dist/web-api.js.map +1 -1
  599. package/dist/web-app-icon-worker.d.ts +1 -0
  600. package/dist/web-app-icon-worker.js +16 -0
  601. package/dist/web-app-icon-worker.js.map +1 -0
  602. package/dist/web-app-icon.d.ts +109 -0
  603. package/dist/web-app-icon.js +257 -0
  604. package/dist/web-app-icon.js.map +1 -0
  605. package/dist/web-appearance-policy.js.map +1 -1
  606. package/dist/web-appearance-tools.d.ts +1 -0
  607. package/dist/web-appearance-tools.js +7 -3
  608. package/dist/web-appearance-tools.js.map +1 -1
  609. package/dist/web-appearance-view.js.map +1 -1
  610. package/dist/web-appearance.d.ts +4 -10
  611. package/dist/web-appearance.js +7 -4
  612. package/dist/web-appearance.js.map +1 -1
  613. package/dist/web-auth.d.ts +8 -4
  614. package/dist/web-auth.js +33 -21
  615. package/dist/web-auth.js.map +1 -1
  616. package/dist/web-backend-login.d.ts +130 -0
  617. package/dist/web-backend-login.js +225 -0
  618. package/dist/web-backend-login.js.map +1 -0
  619. package/dist/web-card.js.map +1 -1
  620. package/dist/web-channel-echo.js.map +1 -1
  621. package/dist/web-chat-history.d.ts +118 -8
  622. package/dist/web-chat-history.js +220 -25
  623. package/dist/web-chat-history.js.map +1 -1
  624. package/dist/web-commands.d.ts +5 -1
  625. package/dist/web-commands.js +1 -1
  626. package/dist/web-commands.js.map +1 -1
  627. package/dist/web-cookie-names.d.ts +5 -0
  628. package/dist/web-cookie-names.js +12 -0
  629. package/dist/web-cookie-names.js.map +1 -0
  630. package/dist/web-effect-bundle.d.ts +2 -2
  631. package/dist/web-effect-library.js.map +1 -1
  632. package/dist/web-effect-playback.js.map +1 -1
  633. package/dist/web-effect-profiles.d.ts +16 -16
  634. package/dist/web-effect-profiles.js +3 -2
  635. package/dist/web-effect-profiles.js.map +1 -1
  636. package/dist/web-effect-service.js.map +1 -1
  637. package/dist/web-effect-tools.js.map +1 -1
  638. package/dist/web-effect-trials.js.map +1 -1
  639. package/dist/web-effect-upload.js.map +1 -1
  640. package/dist/web-effect.d.ts +6 -6
  641. package/dist/web-effect.js.map +1 -1
  642. package/dist/web-experimental-http.d.ts +2 -0
  643. package/dist/web-experimental-http.js +12 -0
  644. package/dist/web-experimental-http.js.map +1 -0
  645. package/dist/web-experimental.d.ts +11 -0
  646. package/dist/web-experimental.js +49 -0
  647. package/dist/web-experimental.js.map +1 -0
  648. package/dist/web-guest-access.d.ts +4 -2
  649. package/dist/web-guest-access.js +22 -10
  650. package/dist/web-guest-access.js.map +1 -1
  651. package/dist/web-guest-rooms.js.map +1 -1
  652. package/dist/web-host-guard.d.ts +11 -0
  653. package/dist/web-host-guard.js +11 -0
  654. package/dist/web-host-guard.js.map +1 -1
  655. package/dist/web-icons.d.ts +33 -1
  656. package/dist/web-icons.js +83 -11
  657. package/dist/web-icons.js.map +1 -1
  658. package/dist/web-identity-color.js.map +1 -1
  659. package/dist/web-image-meta.d.ts +13 -0
  660. package/dist/web-image-meta.js +62 -0
  661. package/dist/web-image-meta.js.map +1 -1
  662. package/dist/web-images.js.map +1 -1
  663. package/dist/web-libavoid.d.ts +14 -0
  664. package/dist/web-libavoid.js +46 -0
  665. package/dist/web-libavoid.js.map +1 -0
  666. package/dist/web-login.js.map +1 -1
  667. package/dist/web-mentions.js.map +1 -1
  668. package/dist/web-mod-agent.js.map +1 -1
  669. package/dist/web-mod-store.d.ts +2 -0
  670. package/dist/web-mod-store.js +10 -0
  671. package/dist/web-mod-store.js.map +1 -1
  672. package/dist/web-office-store.d.ts +104 -0
  673. package/dist/web-office-store.js +236 -0
  674. package/dist/web-office-store.js.map +1 -0
  675. package/dist/web-owner-css-store.d.ts +44 -1
  676. package/dist/web-owner-css-store.js +70 -9
  677. package/dist/web-owner-css-store.js.map +1 -1
  678. package/dist/web-owner-css.js.map +1 -1
  679. package/dist/web-png.d.ts +33 -0
  680. package/dist/web-png.js +329 -0
  681. package/dist/web-png.js.map +1 -0
  682. package/dist/web-prefs.d.ts +6 -0
  683. package/dist/web-prefs.js +29 -3
  684. package/dist/web-prefs.js.map +1 -1
  685. package/dist/web-preview.d.ts +2 -0
  686. package/dist/web-preview.js +2 -0
  687. package/dist/web-preview.js.map +1 -1
  688. package/dist/web-profile.js.map +1 -1
  689. package/dist/web-reactions.d.ts +35 -0
  690. package/dist/web-reactions.js +47 -0
  691. package/dist/web-reactions.js.map +1 -0
  692. package/dist/web-request-context.d.ts +5 -0
  693. package/dist/web-request-context.js.map +1 -1
  694. package/dist/web-returning-device.d.ts +4 -2
  695. package/dist/web-returning-device.js +13 -6
  696. package/dist/web-returning-device.js.map +1 -1
  697. package/dist/web-schedules.js.map +1 -1
  698. package/dist/web-send-dedupe.d.ts +36 -0
  699. package/dist/web-send-dedupe.js +69 -0
  700. package/dist/web-send-dedupe.js.map +1 -0
  701. package/dist/web-session-authorization.js.map +1 -1
  702. package/dist/web-session.js +1 -1
  703. package/dist/web-session.js.map +1 -1
  704. package/dist/web-settings-patch.d.ts +3 -1
  705. package/dist/web-settings-patch.js +11 -5
  706. package/dist/web-settings-patch.js.map +1 -1
  707. package/dist/web-settings-tools.d.ts +6 -0
  708. package/dist/web-settings-tools.js +52 -17
  709. package/dist/web-settings-tools.js.map +1 -1
  710. package/dist/web-terminal-http.js +15 -2
  711. package/dist/web-terminal-http.js.map +1 -1
  712. package/dist/web-terminal.js +1 -1
  713. package/dist/web-terminal.js.map +1 -1
  714. package/dist/web-theme-store.d.ts +2 -2
  715. package/dist/web-theme-store.js +6 -3
  716. package/dist/web-theme-store.js.map +1 -1
  717. package/dist/web-theme.d.ts +29 -0
  718. package/dist/web-theme.js +89 -1
  719. package/dist/web-theme.js.map +1 -1
  720. package/dist/web-upload.d.ts +18 -1
  721. package/dist/web-upload.js +45 -7
  722. package/dist/web-upload.js.map +1 -1
  723. package/dist/web-widget.js.map +1 -1
  724. package/dist/webhook-emitter.js.map +1 -1
  725. package/dist/windows-shortcuts.d.ts +78 -0
  726. package/dist/windows-shortcuts.js +203 -0
  727. package/dist/windows-shortcuts.js.map +1 -0
  728. package/dist/workflow-templates/default.md +3 -0
  729. package/dist/working-state-journal.d.ts +68 -0
  730. package/dist/working-state-journal.js +93 -0
  731. package/dist/working-state-journal.js.map +1 -0
  732. package/dist/ws-server.js.map +1 -1
  733. package/dist/yaml-shared-nodes.d.ts +25 -0
  734. package/dist/yaml-shared-nodes.js +185 -0
  735. package/dist/yaml-shared-nodes.js.map +1 -0
  736. package/launcher/agend-install.cjs +828 -0
  737. package/launcher/install-admission.cjs +4 -39
  738. package/launcher/postinstall.cjs +14 -12
  739. package/launcher/private-home.cjs +43 -0
  740. package/package.json +16 -13
  741. package/scripts/preinstall-guard.cjs +2 -5
  742. package/templates/windows-shortcuts.ps1 +181 -0
  743. package/launcher/old-updater-note.cjs +0 -48
@@ -0,0 +1,297 @@
1
+ // #2233: signing a backend in from the dashboard — the dialog, the line every admin page shows while a sign-in runs,
2
+ // and the list Settings → Agents shows. Behind the session gate (/ui/js/); a guest's page never loads it.
3
+ //
4
+ // What the page is told comes from GET /ui/backend-login, as this session may see it (web-backend-login.ts): the
5
+ // session that started a sign-in gets its terminal link, access code and device code; every other admin page only that
6
+ // one runs, on which device, and may cancel it. The stream's `backend_login` event carries nothing — it only says to
7
+ // read again. Starting is two steps, as in chat: check (the server keeps what it found) and start (with its id).
8
+ import { html, useEffect, useState } from "/assets/app-html.js";
9
+ import { t, register } from "/assets/app-i18n.js";
10
+ import { appStore, createStore, useStore } from "/assets/app-store.js";
11
+ import { closeDialog, shellStore, showDialog } from "/assets/app-shell.js";
12
+ import { Dialog } from "/assets/ui-dialog.js";
13
+ import { toast } from "/assets/ui-toast.js";
14
+
15
+ register("signin", {
16
+ en: {
17
+ title: "Sign in to {0}",
18
+ checking: "Checking whether {0} is signed in…",
19
+ confirmDevice: "AgEnD runs {0}'s own sign-in on this machine. You get a link and a one-time code here, to finish in your browser.",
20
+ confirmTerminal: "AgEnD runs {0}'s own sign-in on this machine and opens it as a terminal in your browser (only that sign-in, no shell). You get the link and a one-time access code here.",
21
+ stillSignedIn: "It is still signed in: signing in again replaces that login.",
22
+ logoutFirst: "It signs out first ({0}), then in.",
23
+ publicNote: "Not at this machine? A temporary public link opens the terminal from anywhere, for this sign-in only.",
24
+ go: "Sign in",
25
+ goPublic: "Use a temporary public link",
26
+ cancel: "Cancel",
27
+ close: "Close",
28
+ starting: "Starting…",
29
+ deviceStep: "Open this page and enter the code:",
30
+ linkStep: "Open this page to sign in; if it gives you a code, paste it into the sign-in terminal:",
31
+ terminalStep: "Open the sign-in terminal and enter this access code there. It works once, for {0} minutes.",
32
+ openTerminal: "Open the sign-in terminal",
33
+ accessCode: "Access code",
34
+ code: "Code",
35
+ copy: "Copy",
36
+ copied: "Copied",
37
+ publicLink: "Public link: anyone with the link and the code could use it — keep both to yourself.",
38
+ loopback: "This terminal listens on the machine's own address only. Open it on that machine, or set web_terminal.bind for your network.",
39
+ waitingHint: "Waiting for the sign-in to show its link…",
40
+ signedIn: "{0} is signed in.",
41
+ ended: "The sign-in ended.",
42
+ cancelSignIn: "Cancel sign-in",
43
+ cancelled: "Sign-in cancelled.",
44
+ running: "Signing in to {0} (started on {1}).",
45
+ open: "Open",
46
+ expired: "That took too long — start again.",
47
+ notHere: "Signing in is not available over the public link. Open the dashboard on the machine running AgEnD, or use /login in your chat.",
48
+ notAdmin: "Only a fleet admin signed in on this machine or its network can sign a backend in.",
49
+ notOffered: "A public link is not offered for this sign-in.",
50
+ failed: "That did not work.",
51
+ listTitle: "Sign-ins",
52
+ listHint: "Each backend's login on this machine, and each credential profile an agent uses.",
53
+ stUsable: "signed in",
54
+ stNotSignedIn: "not signed in",
55
+ stNotInstalled: "not installed",
56
+ stTooOld: "too old",
57
+ stUnknown: "not checked",
58
+ signIn: "Sign in",
59
+ signInAgain: "Sign in again",
60
+ onHost: "Sign in on the machine: {0}",
61
+ install: "Install on the machine: {0}",
62
+ update: "Update on the machine: {0}",
63
+ profile: "{0} · profile {1}",
64
+ },
65
+ "zh-TW": {
66
+ title: "登入 {0}",
67
+ checking: "正在確認 {0} 是否已登入…",
68
+ confirmDevice: "AgEnD 會在這台機器上執行 {0} 自己的登入。這裡會給你一個連結和一次性代碼,在瀏覽器完成登入。",
69
+ confirmTerminal: "AgEnD 會在這台機器上執行 {0} 自己的登入,並在瀏覽器以終端機開啟(只有這個登入,沒有 shell)。這裡會給你連結和一次性存取碼。",
70
+ stillSignedIn: "它目前仍是登入狀態:重新登入會取代原本的登入。",
71
+ logoutFirst: "會先登出({0}),再登入。",
72
+ publicNote: "不在這台機器前?臨時公開連結可以從任何地方開啟終端機,只用於這次登入。",
73
+ go: "登入",
74
+ goPublic: "使用臨時公開連結",
75
+ cancel: "取消",
76
+ close: "關閉",
77
+ starting: "啟動中…",
78
+ deviceStep: "開啟這個頁面並輸入代碼:",
79
+ linkStep: "開啟這個頁面登入;如果它給你一組代碼,請貼到登入終端機:",
80
+ terminalStep: "開啟登入終端機,在那裡輸入這個存取碼。只能用一次,{0} 分鐘內有效。",
81
+ openTerminal: "開啟登入終端機",
82
+ accessCode: "存取碼",
83
+ code: "代碼",
84
+ copy: "複製",
85
+ copied: "已複製",
86
+ publicLink: "公開連結:拿到連結和代碼的人都能使用——兩者都只留給自己。",
87
+ loopback: "這個終端機只在本機位址上接聽。請在那台機器上開啟,或為你的網路設定 web_terminal.bind。",
88
+ waitingHint: "等待登入顯示連結…",
89
+ signedIn: "{0} 已登入。",
90
+ ended: "登入已結束。",
91
+ cancelSignIn: "取消登入",
92
+ cancelled: "已取消登入。",
93
+ running: "正在登入 {0}(從 {1} 啟動)。",
94
+ open: "開啟",
95
+ expired: "等太久了——請重新開始。",
96
+ notHere: "公開連結上不能登入。請在執行 AgEnD 的機器上開啟儀表板,或在聊天裡用 /login。",
97
+ notAdmin: "只有在這台機器或它的網路上登入的 fleet 管理員,才能讓後端登入。",
98
+ notOffered: "這次登入不提供公開連結。",
99
+ failed: "沒有成功。",
100
+ listTitle: "登入",
101
+ listHint: "這台機器上每個 backend 的登入,以及代理使用的每個憑證設定檔。",
102
+ stUsable: "已登入",
103
+ stNotSignedIn: "未登入",
104
+ stNotInstalled: "未安裝",
105
+ stTooOld: "版本太舊",
106
+ stUnknown: "未檢查",
107
+ signIn: "登入",
108
+ signInAgain: "重新登入",
109
+ onHost: "在機器上登入:{0}",
110
+ install: "在機器上安裝:{0}",
111
+ update: "在機器上更新:{0}",
112
+ profile: "{0} · 設定檔 {1}",
113
+ },
114
+ });
115
+ const tn = (k, ...v) => t(`signin.${k}`, ...v);
116
+ const shownOf = (backend, profile) => (profile ? tn("profile", backend, profile) : backend);
117
+
118
+ /** What this page knows: the current sign-in as this session may see it, and Settings' list. */
119
+ export const signInStore = createStore({ loaded: false, login: null, targets: [] });
120
+
121
+ let reading = null, again = false;
122
+ /**
123
+ * Read GET /ui/backend-login (one read at a time). Asked again while one is on its way (an event that came after it was
124
+ * sent): one more read follows it, so the last change is never missed (#2262 review).
125
+ */
126
+ export function refreshSignIn() {
127
+ if (reading) { again = true; return reading; }
128
+ reading = fetch("/ui/backend-login", { cache: "no-store" })
129
+ .then((r) => (r.ok ? r.json() : null))
130
+ .then((b) => { if (b) signInStore.set({ loaded: true, login: b.login || null, targets: Array.isArray(b.targets) ? b.targets : [] }); return b; })
131
+ .catch(() => null)
132
+ .finally(() => { reading = null; if (again) { again = false; void refreshSignIn(); } });
133
+ return reading;
134
+ }
135
+
136
+ let installed = false;
137
+ /** Once per admin page: listen for `backend_login`, read once, and show the line while a sign-in runs. */
138
+ export function installSignIn() {
139
+ if (installed || !signInOffered()) return;
140
+ installed = true;
141
+ const on = appStore.get().streamOn;
142
+ if (typeof on === "function") on("backend_login", () => { void refreshSignIn(); });
143
+ void refreshSignIn();
144
+ shellStore.set({ banner: SignInLine });
145
+ }
146
+
147
+ /** Open the dialog for one login (a backend's default one, or a credential profile). */
148
+ export function openSignIn({ backend, profile }) {
149
+ showDialog(SignInDialog, { backend, ...(profile ? { profile } : {}) });
150
+ }
151
+
152
+ /** Open the dialog on the running sign-in (its starter's view, or the cancel-only one). */
153
+ function openRunning(login) { showDialog(SignInDialog, { backend: login.backend, ...(login.profile ? { profile: login.profile } : {}), resume: true }); }
154
+
155
+ async function post(path, body) {
156
+ try {
157
+ const res = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
158
+ let json = null; try { json = await res.json(); } catch { /* none */ }
159
+ return { ok: res.ok, status: res.status, body: json };
160
+ } catch { return { ok: false, status: 0, body: null }; }
161
+ }
162
+
163
+ const errorText = (code) => (code === "confirm_expired" ? tn("expired") : code === "tunnel_not_offered" ? tn("notOffered")
164
+ : code === "sign_in_not_over_public_link" ? tn("notHere") : code === "sign_in_needs_a_local_admin" ? tn("notAdmin") : tn("failed"));
165
+ /** The public link's page (#2233: Sign in is refused there — never offered). */
166
+ export const signInOffered = () => !(typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1");
167
+ const loopbackHost = (h) => h === "localhost" || h === "::1" || h === "[::1]" || /^127\./.test(h);
168
+
169
+ function CopyField({ label, value }) {
170
+ const [copied, setCopied] = useState(false);
171
+ const copy = async () => { try { await navigator.clipboard.writeText(value); setCopied(true); } catch { toast(value); } };
172
+ return html`<div class="signin-field"><span class="signin-label">${label}</span><code class="signin-value">${value}</code>
173
+ <button type="button" class="btn btn-sm" onClick=${copy}>${copied ? tn("copied") : tn("copy")}</button></div>`;
174
+ }
175
+
176
+ /** The starter's view of a running sign-in: the device link and code, or the terminal and its access code. */
177
+ function Running({ login }) {
178
+ const { hint, terminal, accessCode } = login;
179
+ const away = terminal && terminal.loopback && typeof location !== "undefined" && !loopbackHost(location.hostname);
180
+ return html`<div class="signin-run">
181
+ ${hint ? html`<p>${hint.code ? tn("deviceStep") : tn("linkStep")}</p>
182
+ ${hint.linkable ? html`<p><a data-trust="navigation" class="btn btn-primary" href=${hint.url} target="_blank" rel="noopener noreferrer">${tn("open")}</a> <span class="signin-url">${hint.url}</span></p>`
183
+ : html`<${CopyField} label="URL" value=${hint.url} />`}
184
+ ${hint.code ? html`<${CopyField} label=${tn("code")} value=${hint.code} />` : null}` : null}
185
+ ${terminal ? html`<p>${tn("terminalStep", terminal.ttlMinutes)}</p>
186
+ <p><a data-trust="navigation" class="btn btn-primary" href=${terminal.url} target="_blank" rel="noopener noreferrer">${tn("openTerminal")}</a></p>
187
+ ${accessCode ? html`<${CopyField} label=${tn("accessCode")} value=${accessCode} />` : null}
188
+ ${terminal.public ? html`<p class="signin-note">${tn("publicLink")}</p>` : null}
189
+ ${away ? html`<p class="signin-note">${tn("loopback")}</p>` : null}` : null}
190
+ ${!hint && !terminal ? html`<p class="signin-note">${tn("waitingHint")}</p>` : null}
191
+ </div>`;
192
+ }
193
+
194
+ export function SignInDialog({ backend, profile, resume = false, onClose }) {
195
+ const s = useStore(signInStore);
196
+ const [step, setStep] = useState(resume ? { kind: "login" } : { kind: "checking" });
197
+ const [busy, setBusy] = useState(false);
198
+ const shown = shownOf(backend, profile);
199
+ const close = onClose || closeDialog;
200
+ useEffect(() => {
201
+ if (resume) return undefined;
202
+ let gone = false;
203
+ post("/ui/backend-login/check", { backend, ...(profile ? { profile } : {}) }).then((r) => {
204
+ if (gone) return;
205
+ if (r.body && r.body.confirm) setStep({ kind: "confirm", confirm: r.body.confirm });
206
+ else setStep({ kind: "message", text: (r.body && (r.body.message || (r.body.error && errorText(r.body.error)))) || tn("failed") });
207
+ });
208
+ return () => { gone = true; };
209
+ }, []);
210
+ const login = s.login;
211
+ const live = step.kind === "login" && login && login.phase === "running";
212
+ // While a running sign-in is on screen, read it every 2 s too (a page without the stream still sees it move).
213
+ useEffect(() => {
214
+ if (!live) return undefined;
215
+ const h = setInterval(() => { void refreshSignIn(); }, 2000);
216
+ return () => clearInterval(h);
217
+ }, [live]);
218
+ const start = async (tunnel) => {
219
+ setBusy(true); setStep({ kind: "starting" });
220
+ const r = await post("/ui/backend-login/start", { confirm: step.confirm.id, ...(tunnel ? { tunnel: true } : {}) });
221
+ setBusy(false);
222
+ if (r.body && r.body.login) { signInStore.set({ login: r.body.login }); setStep({ kind: "login" }); return; }
223
+ setStep({ kind: "message", text: (r.body && (r.body.message || (r.body.error && errorText(r.body.error)))) || tn("failed") });
224
+ };
225
+ const cancelRun = async () => {
226
+ setBusy(true);
227
+ const r = await post("/ui/backend-login/cancel", {});
228
+ setBusy(false);
229
+ toast((r.body && r.body.message) || tn("cancelled"));
230
+ void refreshSignIn();
231
+ };
232
+ let body, actions;
233
+ if (step.kind === "checking" || step.kind === "starting") {
234
+ body = html`<p role="status">${step.kind === "checking" ? tn("checking", shown) : tn("starting")}</p>`;
235
+ actions = html`<button type="button" class="btn" disabled=${busy} onClick=${close}>${tn("cancel")}</button>`;
236
+ } else if (step.kind === "confirm") {
237
+ const c = step.confirm;
238
+ body = html`<p>${c.deviceAuth ? tn("confirmDevice", shown) : tn("confirmTerminal", shown)}</p>
239
+ ${c.tokenPresent ? html`<p>${tn("stillSignedIn")}</p>` : null}
240
+ ${c.logoutFirst ? html`<p>${tn("logoutFirst", c.logoutFirst)}</p>` : null}
241
+ ${c.publicLink ? html`<p class="signin-note">${tn("publicNote")}</p>` : null}`;
242
+ actions = html`<button type="button" class="btn" onClick=${close}>${tn("cancel")}</button>
243
+ ${c.publicLink ? html`<button type="button" class="btn" onClick=${() => start(true)}>${tn("goPublic")}</button>` : null}
244
+ <button type="button" class="btn btn-primary" onClick=${() => start(false)}>${tn("go")}</button>`;
245
+ } else if (step.kind === "message") {
246
+ body = html`<p class="signin-message">${step.text}</p>`;
247
+ actions = html`<button type="button" class="btn btn-primary" onClick=${close}>${tn("close")}</button>`;
248
+ } else if (!login) {
249
+ body = html`<p class="signin-note">${tn("ended")}</p>`;
250
+ actions = html`<button type="button" class="btn btn-primary" onClick=${close}>${tn("close")}</button>`;
251
+ } else if (!login.mine) {
252
+ body = html`<p>${login.phase === "running" ? tn("running", shownOf(login.backend, login.profile), login.startedOn) : tn("ended")}</p>`;
253
+ actions = html`<button type="button" class="btn" onClick=${close}>${tn("close")}</button>
254
+ ${login.phase === "running" ? html`<button type="button" class="btn btn-danger" disabled=${busy} onClick=${cancelRun}>${tn("cancelSignIn")}</button>` : null}`;
255
+ } else {
256
+ const lines = Array.isArray(login.lines) ? login.lines : [];
257
+ body = html`${login.phase === "running" ? html`<${Running} login=${login} />`
258
+ : html`<p role="status" class=${login.ok ? "signin-ok" : "signin-message"}>${login.ok ? tn("signedIn", shownOf(login.backend, login.profile)) : tn("ended")}</p>`}
259
+ ${lines.length ? html`<ul class="signin-lines">${lines.map((l, i) => html`<li key=${i}>${l}</li>`)}</ul>` : null}`;
260
+ actions = html`<button type="button" class="btn" onClick=${close}>${tn("close")}</button>
261
+ ${login.phase === "running" ? html`<button type="button" class="btn btn-danger" disabled=${busy} onClick=${cancelRun}>${tn("cancelSignIn")}</button>` : null}`;
262
+ }
263
+ return html`<${Dialog} title=${tn("title", shown)} onClose=${close} busy=${busy} actions=${actions}>${body}</${Dialog}>`;
264
+ }
265
+
266
+ /** The line every admin page shows while a sign-in runs (the shell's banner slot). */
267
+ export function SignInLine() {
268
+ const { login } = useStore(signInStore);
269
+ if (!login || login.phase !== "running") return null;
270
+ return html`<div data-trust="status" class="conn op-line signin-line" role="status">
271
+ <span>${tn("running", shownOf(login.backend, login.profile), login.startedOn)}</span>
272
+ <button type="button" class="btn btn-sm" onClick=${() => openRunning(login)}>${tn("open")}</button></div>`;
273
+ }
274
+
275
+ const STATE_KEY = { usable: "stUsable", not_signed_in: "stNotSignedIn", not_installed: "stNotInstalled", too_old: "stTooOld", unknown: "stUnknown" };
276
+
277
+ /** Settings → Agents: each login on this machine, with Sign in where the dashboard can do it. */
278
+ export function SignInList() {
279
+ const { targets, loaded, login } = useStore(signInStore);
280
+ useEffect(() => { void refreshSignIn(); }, []);
281
+ if (!loaded || !targets.length) return null;
282
+ const running = login && login.phase === "running";
283
+ return html`<section class="s-group signin-list" aria-label=${tn("listTitle")}>
284
+ <div class="list-head"><span>${tn("listTitle")}</span></div>
285
+ <p class="sub">${tn("listHint")}</p>
286
+ ${targets.map((x) => html`<div key=${`${x.backend}:${x.profile || ""}`} class="s-row">
287
+ <span class="s-name">${shownOf(x.backend, x.profile)}</span>
288
+ <span class="tag">${tn(STATE_KEY[x.state] || "stUnknown")}</span>
289
+ <span class="s-br" aria-hidden="true"></span>
290
+ ${x.install ? html`<span class="s-meta"><code>${tn("install", x.install)}</code></span>` : null}
291
+ ${x.update ? html`<span class="s-meta"><code>${tn("update", x.update)}</code></span>` : null}
292
+ ${x.hostCommand ? html`<span class="s-meta"><code>${tn("onHost", x.hostCommand)}</code></span>` : null}
293
+ <span class="s-actions">${x.signIn ? html`<button type="button" class=${x.state === "not_signed_in" ? "btn btn-sm btn-primary" : "btn btn-sm"} disabled=${!!running}
294
+ onClick=${() => openSignIn(x)}>${x.state === "usable" ? tn("signInAgain") : tn("signIn")}</button>` : null}</span>
295
+ </div>`)}
296
+ </section>`;
297
+ }
@@ -9,7 +9,7 @@ import { navigate } from "/assets/app-nav.js";
9
9
  import { toast } from "/assets/ui-toast.js";
10
10
  import { setThemesShown, themesShown, themesStore } from "/assets/app-themes.js";
11
11
  import { setFollowsReducedMotion } from "/assets/fx-motion.js";
12
- import { getAppEffects } from "./app-effects.js";
12
+ import { getAppEffects, wakeEffects, effectsLoaded, onEffectsAwake } from "./effects-waker.js";
13
13
 
14
14
  register("chatFx", {
15
15
  en: { stock: "Off: the page layout could not be verified ({0}). Reload and report this code if it persists.", transport: "Off: the isolated effect frame could not start. Reload to try again.", enableHere: "Turn effects on here", playMotion: "Play effects anyway", leaveSafe: "Leave safe mode", viewerShow: "Show themes, effects and widgets here", clock: "Off: the page timing clock is unavailable. Reload to try again.", gatesUnknown: "Off: the playback conditions could not be verified.", resource: "Off: this effect could not be loaded or verified. Reload to try again.", frontQueue: "Waiting for a foreground slot.", consent: "This page has older playback information. Reload to use the isolated effect player.", previewMissing: "Off: the isolated effect frame is unavailable. Reload to try again.", unsupported: "Off: this browser lacks required Canvas Worker or layout support.", calibration: "Starting the effect.", baseline: "This page has older playback information. Reload to try again.", safetyUnknown: "Stopped: this device's safety history could not be verified. Reload to check it.", rawReason: "Playback blocked: {0}", missingRow: "Playback state is unavailable (runtime_unavailable).", region: "Off: this effect does not declare the chat region.", blocked: "Placed effects are blocked for this viewer:", deviceSetting: "Open device preferences", webSetting: "Open Web settings", reloadHint: "Reload page",
@@ -78,8 +78,13 @@ function ReasonAction({ reason, onClose = () => {} }) {
78
78
  }
79
79
 
80
80
  const NO_PLAYBACK = () => {};
81
- export function ChatEffects({ name, lease, element, store, open, onClose, onPlayback = NO_PLAYBACK }) {
81
+ export function ChatEffects({ name, lease, element, store, open, onClose, onPlayback = NO_PLAYBACK, active = true }) {
82
82
  const cssLife = useStore(ownerCssLifecycle);
83
+ // #2193: `active` — this chat has placements (its status row says so). While the runtime sleeps, a chat with none
84
+ // attaches and reads nothing (the dialog says "No effects in this chat"); one with some wakes it and attaches once it
85
+ // runs. Once it runs (woken by anything — a Try card needs its chat attached), every chat attaches as before.
86
+ const [awake, setAwake] = useState(() => getAppEffects() !== null || effectsLoaded());
87
+ useEffect(() => (awake ? undefined : onEffectsAwake(() => setAwake(true))), [awake]);
83
88
  useStore(themesStore); // Viewer-local off/on is visible immediately, independent of runtime callbacks.
84
89
  // Which effects were running just before this dialog opened ("was running" is said in the past, never shown as
85
90
  // current). The ⋯ menu that opens it is a barrier too, so "just before" is within RECENT_MS of the observed stop.
@@ -100,6 +105,8 @@ export function ChatEffects({ name, lease, element, store, open, onClose, onPlay
100
105
  lease.hold(cleanup);
101
106
  try {
102
107
  setBusy(false); onClose(); setCanWrite(false); setState(null);
108
+ if (!active && !awake) { setState({ error: null, rows: [], effects: [], loading: false }); return cleanup; }
109
+ if (!awake) { void wakeEffects(); return cleanup; } // "Checking effects…" until it runs
103
110
  const host = window, owner = getAppEffects(host);
104
111
  const current = () => live && lease.current();
105
112
  if (!owner) { setState({ error: null, rows: [], effects: [], unavailable: true, reason: typeof host.OffscreenCanvas !== "function" || typeof host.Worker !== "function" ? "unsupported_host" : "runtime_unavailable" }); return cleanup; }
@@ -132,7 +139,7 @@ export function ChatEffects({ name, lease, element, store, open, onClose, onPlay
132
139
  if (lease.current()) setState({ error: "runtime_unavailable", rows: [], effects: [] });
133
140
  return cleanup;
134
141
  }
135
- }, [name, lease]);
142
+ }, [name, lease, active, awake]);
136
143
  const rows = state?.rows ?? [], effects = state?.effects ?? [];
137
144
  if (open && !wasOpen.current) {
138
145
  const now = performance.now();
@@ -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
+ }
@@ -432,6 +432,44 @@
432
432
  return msgs;
433
433
  }
434
434
 
435
+ /**
436
+ * #2305: the list after a reactions update for the row `boot`/`id` (the instance's reactions — its minimal reply): the
437
+ * same list when nothing changed, else a copy. At most 8 distinct short strings are taken; the server sent them.
438
+ */
439
+ function applyReactions(list, boot, id, reactions) {
440
+ var msgs = list || [];
441
+ if (!Array.isArray(reactions)) return msgs;
442
+ var clean = [];
443
+ for (var j = 0; j < reactions.length && clean.length < 8; j++) {
444
+ var e = reactions[j];
445
+ if (typeof e === "string" && e.length > 0 && e.length <= 16 && clean.indexOf(e) < 0) clean.push(e);
446
+ }
447
+ for (var i = msgs.length - 1; i >= 0; i--) {
448
+ if (!msgs[i] || msgs[i].boot !== boot || msgs[i].id !== id) continue;
449
+ var had = msgs[i].reactions || [];
450
+ var same = had.length === clean.length;
451
+ for (var k = 0; same && k < had.length; k++) if (had[k] !== clean[k]) same = false;
452
+ if (same) return msgs;
453
+ var out = msgs.slice();
454
+ out[i] = Object.assign({}, msgs[i], { reactions: clean });
455
+ return out;
456
+ }
457
+ return msgs;
458
+ }
459
+ /** #2305: an emoji drawn as one — a heart, ☃, ✍ and the like have a text form unless U+FE0F follows. */
460
+ function emojiForm(e) { return /\p{Emoji_Presentation}/u.test(e) ? e : e + "\uFE0F"; }
461
+ /** #2305: a message's reactions under its bubble, labelled for a screen reader (`label`: who reacted); "" when none. */
462
+ function reactionsHtml(reactions, label) {
463
+ if (!Array.isArray(reactions) || !reactions.length) return "";
464
+ var items = "";
465
+ for (var i = 0; i < reactions.length; i++) {
466
+ if (typeof reactions[i] !== "string") continue;
467
+ items += '<span class="reaction" role="img" aria-label="' + escapeHtml(reactions[i]) + '">' + escapeHtml(emojiForm(reactions[i])) + "</span>";
468
+ }
469
+ var l = escapeHtml(label || "");
470
+ return items ? '<div class="msg-reactions" role="group" aria-label="' + l + '" title="' + l + '">' + items + "</div>" : "";
471
+ }
472
+
435
473
  var TICK_GLYPH = { queued: "◷", processing: "✓", delivered: "✓✓", failed: "!", cancelled: "⊘" };
436
474
  /** The ticks for one state, labelled for a screen reader as well as a pointer; "" when there is none. */
437
475
  function deliveryHtml(state, labels) {
@@ -471,27 +509,17 @@
471
509
  /** Whether the chat shows "working" and its Stop button for an instance in this execution state. */
472
510
  function isBusy(state) { return state === "working" || state === "stuck"; }
473
511
 
474
- /** What a key in the composer does: Enter sends, Shift+Enter is a new line, nothing happens mid-IME-composition. */
475
- function composerKey(ev) {
512
+ /**
513
+ * What a key in the composer does: Enter sends, Shift+Enter is a new line, nothing happens mid-IME-composition.
514
+ * #2478 (the user's request): on a touch screen (a coarse pointer) Return is a new line — the Send button sends.
515
+ */
516
+ function composerKey(ev, opts) {
476
517
  if (!ev || ev.key !== "Enter" || ev.isComposing || ev.keyCode === 229) return "none";
518
+ if (opts && opts.touch) return "newline";
477
519
  if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey) return "newline";
478
520
  return "send";
479
521
  }
480
522
 
481
- /**
482
- * A send that failed: where does its text go? Back into the composer only if that composer is the same
483
- * target's, still on screen and empty; otherwise it is kept aside for that target (never put into another
484
- * target's composer, never over a draft someone is typing) and offered back when that chat is open.
485
- */
486
- function settleFailedSend(target, current, composerPresent, composerValue) {
487
- return target === current && composerPresent && !composerValue ? "restore" : "keep";
488
- }
489
-
490
- /** "Put back" for a kept message: in front of whatever is being typed now, never replacing it. */
491
- function putBack(failedText, draft) {
492
- return draft ? failedText + "\n" + draft : failedText;
493
- }
494
-
495
523
  /** The defaults of the upload limits the server enforces (web-upload.ts UPLOAD_LIMITS), for a page that was given none. */
496
524
  var FILE_LIMITS = { maxFileBytes: 50 * 1024 * 1024, maxFiles: 5, maxTotalBytes: 100 * 1024 * 1024 };
497
525
  /**
@@ -580,6 +608,21 @@
580
608
  * With { htmlCards: true } (a server-marked agent reply, as for renderMarkdown) each .html/.htm file also gets an
581
609
  * empty card placeholder <div class="html-card" data-att="<id>"> after the list — the id is 32 hex, nothing else.
582
610
  */
611
+ // The box a photo is drawn in (app.css: .msg .att-img img, max 260 × 220): its own size, scaled down to fit.
612
+ var PHOTO_MAX_W = 260, PHOTO_MAX_H = 220;
613
+ /**
614
+ * #2184: a photo's drawn size as width/height attributes, from the size the server read from its headers: the box is there
615
+ * before the picture loads, so the text around it never moves when it does (a chat opened cold jittered, as each
616
+ * photo grew from nothing). Nothing when the size is not known (drawn as before).
617
+ */
618
+ function photoBox(a) {
619
+ var w = a.width, h = a.height;
620
+ var ok = function (v) { return typeof v === "number" && v >= 1 && v <= 65535 && Math.floor(v) === v; };
621
+ if (!ok(w) || !ok(h)) return "";
622
+ var k = Math.min(1, PHOTO_MAX_W / w, PHOTO_MAX_H / h);
623
+ return ' width="' + Math.max(1, Math.round(w * k)) + '" height="' + Math.max(1, Math.round(h * k)) + '"';
624
+ }
625
+
583
626
  function attachmentsHtml(attachments, opts) {
584
627
  if (!Array.isArray(attachments)) return "";
585
628
  var cards = !!(opts && opts.htmlCards);
@@ -602,7 +645,7 @@
602
645
  else if (kind) after.push('<div class="text-card" data-att="' + a.id + '" data-type="' + kind + '"></div>');
603
646
  var name = escapeHtml(a.name || "file");
604
647
  if (a.kind === "photo") {
605
- out.push('<a class="att-img" href="' + url + '" target="_blank" rel="noopener noreferrer"><img src="' + url + '" alt="' + name + '" loading="lazy"></a>');
648
+ out.push('<a class="att-img" href="' + url + '" target="_blank" rel="noopener noreferrer"><img src="' + url + '" alt="' + name + '"' + photoBox(a) + ' loading="lazy"></a>');
606
649
  } else {
607
650
  out.push('<a class="att-file" href="' + url + '" download="' + name + '">📎 ' + name + ' <span class="att-size">' + escapeHtml(formatSize(a.size)) + "</span></a>");
608
651
  }
@@ -613,9 +656,10 @@
613
656
  return {
614
657
  renderMarkdown: renderMarkdown, escapeHtml: escapeHtml, mergeMessages: mergeMessages, applyReplyButtons: applyReplyButtons, composerKey: composerKey,
615
658
  pollRemaining: pollRemaining,
616
- settleFailedSend: settleFailedSend, putBack: putBack,
659
+
617
660
  FILE_LIMITS: FILE_LIMITS, fileLimits: fileLimits, fileRejection: fileRejection, formatSize: formatSize, checkFiles: checkFiles, attachmentsHtml: attachmentsHtml, isHtmlAttachment: isHtmlAttachment,
618
661
  nextDeliveryState: nextDeliveryState, applyDelivery: applyDelivery, deliveryHtml: deliveryHtml, isBusy: isBusy,
662
+ applyReactions: applyReactions, reactionsHtml: reactionsHtml,
619
663
  isNearBottom: isNearBottom, CODE_FOLD_LINES: CODE_FOLD_LINES, lineCount: lineCount,
620
664
  LONG_PASTE_CHARS: LONG_PASTE_CHARS, isLongPaste: isLongPaste, formatElapsed: formatElapsed,
621
665
  htmlFences: htmlFences, attachmentPreviewType: attachmentPreviewType, highlight: highlight,