@songsid/agend 2.3.0-beta.8 → 2.3.0

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 (736) 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 +9 -2
  8. package/dist/agent-cli.js +44 -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 +54 -11
  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.js.map +1 -1
  61. package/dist/backend/opencode-permission.js +1 -1
  62. package/dist/backend/opencode-permission.js.map +1 -1
  63. package/dist/backend/opencode.js +2 -1
  64. package/dist/backend/opencode.js.map +1 -1
  65. package/dist/backend/session-signals.js.map +1 -1
  66. package/dist/backend-availability.js.map +1 -1
  67. package/dist/backend-command.d.ts +2 -0
  68. package/dist/backend-command.js +3 -1
  69. package/dist/backend-command.js.map +1 -1
  70. package/dist/backend-outage.js.map +1 -1
  71. package/dist/binary-probe.js +1 -1
  72. package/dist/binary-probe.js.map +1 -1
  73. package/dist/bug-report-cli.d.ts +30 -0
  74. package/dist/bug-report-cli.js +139 -0
  75. package/dist/bug-report-cli.js.map +1 -0
  76. package/dist/bug-report.d.ts +47 -0
  77. package/dist/bug-report.js +137 -0
  78. package/dist/bug-report.js.map +1 -0
  79. package/dist/bundled-skills.d.ts +18 -0
  80. package/dist/bundled-skills.js +38 -0
  81. package/dist/bundled-skills.js.map +1 -0
  82. package/dist/cache-service.js +1 -1
  83. package/dist/cache-service.js.map +1 -1
  84. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  85. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  86. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  87. package/dist/channel/adapters/discord.d.ts +4 -0
  88. package/dist/channel/adapters/discord.js +57 -12
  89. package/dist/channel/adapters/discord.js.map +1 -1
  90. package/dist/channel/adapters/telegram.d.ts +6 -0
  91. package/dist/channel/adapters/telegram.js +54 -14
  92. package/dist/channel/adapters/telegram.js.map +1 -1
  93. package/dist/channel/mcp-server.js +21 -4
  94. package/dist/channel/mcp-server.js.map +1 -1
  95. package/dist/channel/mcp-tools.d.ts +3 -1
  96. package/dist/channel/mcp-tools.js +43 -6
  97. package/dist/channel/mcp-tools.js.map +1 -1
  98. package/dist/channel/message-queue.js.map +1 -1
  99. package/dist/channel/sticker-download.js.map +1 -1
  100. package/dist/channel/tool-router.js.map +1 -1
  101. package/dist/child-process.d.ts +21 -0
  102. package/dist/child-process.js +54 -0
  103. package/dist/child-process.js.map +1 -0
  104. package/dist/classic-channel-manager.js +3 -2
  105. package/dist/classic-channel-manager.js.map +1 -1
  106. package/dist/cli-entry.js.map +1 -1
  107. package/dist/cli-flags.js.map +1 -1
  108. package/dist/cli.d.ts +1 -0
  109. package/dist/cli.js +443 -311
  110. package/dist/cli.js.map +1 -1
  111. package/dist/command-table.d.ts +1 -1
  112. package/dist/command-table.js +1 -1
  113. package/dist/command-table.js.map +1 -1
  114. package/dist/compact-watch.js.map +1 -1
  115. package/dist/completion-install.js.map +1 -1
  116. package/dist/completion.js.map +1 -1
  117. package/dist/config-validator.d.ts +6 -0
  118. package/dist/config-validator.js +81 -8
  119. package/dist/config-validator.js.map +1 -1
  120. package/dist/config.d.ts +15 -3
  121. package/dist/config.js +41 -10
  122. package/dist/config.js.map +1 -1
  123. package/dist/connection-secrets.js.map +1 -1
  124. package/dist/context-guardian.js.map +1 -1
  125. package/dist/coredump-filter.js.map +1 -1
  126. package/dist/cpu-profile.js.map +1 -1
  127. package/dist/cross-instance-envelope.js.map +1 -1
  128. package/dist/cross-instance-notice.d.ts +7 -0
  129. package/dist/cross-instance-notice.js +10 -0
  130. package/dist/cross-instance-notice.js.map +1 -1
  131. package/dist/daemon.d.ts +39 -1
  132. package/dist/daemon.js +234 -15
  133. package/dist/daemon.js.map +1 -1
  134. package/dist/daily-summary.js.map +1 -1
  135. package/dist/darwin-memory.js +1 -1
  136. package/dist/darwin-memory.js.map +1 -1
  137. package/dist/dashboard-live.d.ts +8 -0
  138. package/dist/dashboard-live.js +35 -0
  139. package/dist/dashboard-live.js.map +1 -0
  140. package/dist/dashboard-permissions.d.ts +69 -0
  141. package/dist/dashboard-permissions.js +138 -0
  142. package/dist/dashboard-permissions.js.map +1 -0
  143. package/dist/decision-list.d.ts +25 -0
  144. package/dist/decision-list.js +30 -0
  145. package/dist/decision-list.js.map +1 -0
  146. package/dist/delivery-consumed-watch.js.map +1 -1
  147. package/dist/delivery-outbox.js +5 -0
  148. package/dist/delivery-outbox.js.map +1 -1
  149. package/dist/delivery-outbox.test.js.map +1 -1
  150. package/dist/delivery-reconciliation.test.js.map +1 -1
  151. package/dist/diagnostic-redaction.d.ts +10 -0
  152. package/dist/diagnostic-redaction.js +167 -0
  153. package/dist/diagnostic-redaction.js.map +1 -0
  154. package/dist/doctor.d.ts +8 -0
  155. package/dist/doctor.js +52 -0
  156. package/dist/doctor.js.map +1 -1
  157. package/dist/e2e-isolation.js.map +1 -1
  158. package/dist/event-log.js.map +1 -1
  159. package/dist/export-import.js +7 -5
  160. package/dist/export-import.js.map +1 -1
  161. package/dist/fleet-control-audit.js.map +1 -1
  162. package/dist/fleet-label.js.map +1 -1
  163. package/dist/fleet-level-config.d.ts +1 -1
  164. package/dist/fleet-level-config.js +2 -0
  165. package/dist/fleet-level-config.js.map +1 -1
  166. package/dist/fleet-lock.js +3 -2
  167. package/dist/fleet-lock.js.map +1 -1
  168. package/dist/fleet-manager.d.ts +258 -15
  169. package/dist/fleet-manager.js +1605 -304
  170. package/dist/fleet-manager.js.map +1 -1
  171. package/dist/fleet-process-handlers.d.ts +18 -0
  172. package/dist/fleet-process-handlers.js +70 -0
  173. package/dist/fleet-process-handlers.js.map +1 -0
  174. package/dist/fleet-startup.d.ts +14 -0
  175. package/dist/fleet-startup.js +30 -0
  176. package/dist/fleet-startup.js.map +1 -0
  177. package/dist/fleet-traffic.d.ts +36 -0
  178. package/dist/fleet-traffic.js +100 -0
  179. package/dist/fleet-traffic.js.map +1 -1
  180. package/dist/forged-envelope-scan.d.ts +0 -20
  181. package/dist/forged-envelope-scan.js +3 -2
  182. package/dist/forged-envelope-scan.js.map +1 -1
  183. package/dist/full-restart.d.ts +5 -5
  184. package/dist/full-restart.js +5 -5
  185. package/dist/full-restart.js.map +1 -1
  186. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  187. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  188. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  189. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  190. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  191. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  192. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  193. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  194. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  195. package/dist/general-knowledge/skills/web-appearance/SKILL.md +28 -4
  196. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +553 -0
  197. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +170 -35
  198. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  199. package/dist/general-knowledge/steering/core-rules.md +1 -1
  200. package/dist/host-memory.js.map +1 -1
  201. package/dist/idle-gate-log.d.ts +19 -0
  202. package/dist/idle-gate-log.js +15 -0
  203. package/dist/idle-gate-log.js.map +1 -0
  204. package/dist/instance-config-impact.js +25 -4
  205. package/dist/instance-config-impact.js.map +1 -1
  206. package/dist/instance-lifecycle.d.ts +10 -1
  207. package/dist/instance-lifecycle.js +86 -25
  208. package/dist/instance-lifecycle.js.map +1 -1
  209. package/dist/instructions.d.ts +12 -6
  210. package/dist/instructions.js +19 -10
  211. package/dist/instructions.js.map +1 -1
  212. package/dist/kiro-compact-pane.js.map +1 -1
  213. package/dist/kiro-db-reader.js.map +1 -1
  214. package/dist/kiro-engine-status.js.map +1 -1
  215. package/dist/kiro-log-gate.js.map +1 -1
  216. package/dist/kiro-transcript-lane.js.map +1 -1
  217. package/dist/locale.js +428 -20
  218. package/dist/locale.js.map +1 -1
  219. package/dist/log-service.js.map +1 -1
  220. package/dist/log-steps.d.ts +1 -0
  221. package/dist/log-steps.js +15 -0
  222. package/dist/log-steps.js.map +1 -1
  223. package/dist/logger.js +3 -2
  224. package/dist/logger.js.map +1 -1
  225. package/dist/login-controller.d.ts +45 -0
  226. package/dist/login-controller.js +101 -5
  227. package/dist/login-controller.js.map +1 -1
  228. package/dist/login-flows.js +1 -1
  229. package/dist/login-manager.js.map +1 -1
  230. package/dist/login-shell.js +3 -1
  231. package/dist/login-shell.js.map +1 -1
  232. package/dist/ls-attach-resolve.d.ts +96 -0
  233. package/dist/ls-attach-resolve.js +180 -0
  234. package/dist/ls-attach-resolve.js.map +1 -0
  235. package/dist/ls-rows.d.ts +12 -0
  236. package/dist/ls-rows.js +83 -4
  237. package/dist/ls-rows.js.map +1 -1
  238. package/dist/memory-pressure.js.map +1 -1
  239. package/dist/muse-usage-relay.js.map +1 -1
  240. package/dist/needs-you-hub.d.ts +98 -1
  241. package/dist/needs-you-hub.js +313 -44
  242. package/dist/needs-you-hub.js.map +1 -1
  243. package/dist/needs-you-live.d.ts +5 -2
  244. package/dist/needs-you-live.js +6 -3
  245. package/dist/needs-you-live.js.map +1 -1
  246. package/dist/needs-you.d.ts +68 -3
  247. package/dist/needs-you.js +38 -4
  248. package/dist/needs-you.js.map +1 -1
  249. package/dist/network-family.js.map +1 -1
  250. package/dist/operator-control-paths.js +3 -4
  251. package/dist/operator-control-paths.js.map +1 -1
  252. package/dist/outbound-handlers.d.ts +3 -0
  253. package/dist/outbound-handlers.js +76 -19
  254. package/dist/outbound-handlers.js.map +1 -1
  255. package/dist/outbound-schemas.d.ts +112 -64
  256. package/dist/outbound-schemas.js +39 -12
  257. package/dist/outbound-schemas.js.map +1 -1
  258. package/dist/outbox-open-error.js.map +1 -1
  259. package/dist/pane-context-cache.js.map +1 -1
  260. package/dist/paths.js.map +1 -1
  261. package/dist/private-dir.js.map +1 -1
  262. package/dist/probe-worker-pool.js.map +1 -1
  263. package/dist/profile-control.js.map +1 -1
  264. package/dist/prompt-file-ref.js.map +1 -1
  265. package/dist/provider-probe.js.map +1 -1
  266. package/dist/provider-secret-registry.d.ts +5 -3
  267. package/dist/provider-secret-registry.js +8 -2
  268. package/dist/provider-secret-registry.js.map +1 -1
  269. package/dist/public-link-auto-open.d.ts +32 -0
  270. package/dist/public-link-auto-open.js +86 -0
  271. package/dist/public-link-auto-open.js.map +1 -0
  272. package/dist/public-link-owner.d.ts +10 -0
  273. package/dist/public-link-owner.js +36 -0
  274. package/dist/public-link-owner.js.map +1 -0
  275. package/dist/public-link-progress.d.ts +12 -1
  276. package/dist/public-link-progress.js +5 -10
  277. package/dist/public-link-progress.js.map +1 -1
  278. package/dist/public-preview.js.map +1 -1
  279. package/dist/public-tunnel-config.d.ts +11 -0
  280. package/dist/public-tunnel-config.js +68 -0
  281. package/dist/public-tunnel-config.js.map +1 -0
  282. package/dist/public-web-gateway.d.ts +2 -0
  283. package/dist/public-web-gateway.js +19 -6
  284. package/dist/public-web-gateway.js.map +1 -1
  285. package/dist/public-web-link.d.ts +48 -3
  286. package/dist/public-web-link.js +177 -51
  287. package/dist/public-web-link.js.map +1 -1
  288. package/dist/quickstart-api.d.ts +9 -0
  289. package/dist/quickstart-api.js +82 -0
  290. package/dist/quickstart-api.js.map +1 -1
  291. package/dist/quickstart.d.ts +5 -0
  292. package/dist/quickstart.js +40 -25
  293. package/dist/quickstart.js.map +1 -1
  294. package/dist/reply-buttons-controller.js.map +1 -1
  295. package/dist/reply-dedup.js.map +1 -1
  296. package/dist/resource-report.js +1 -1
  297. package/dist/resource-report.js.map +1 -1
  298. package/dist/restart-guard.js.map +1 -1
  299. package/dist/restart-progress.d.ts +80 -1
  300. package/dist/restart-progress.js +306 -21
  301. package/dist/restart-progress.js.map +1 -1
  302. package/dist/rollout-index.js.map +1 -1
  303. package/dist/runtime-cpu-profile.js.map +1 -1
  304. package/dist/scheduler/db.d.ts +40 -13
  305. package/dist/scheduler/db.js +123 -89
  306. package/dist/scheduler/db.js.map +1 -1
  307. package/dist/scheduler/scheduler.js.map +1 -1
  308. package/dist/scheduler/task-board-text.d.ts +26 -0
  309. package/dist/scheduler/task-board-text.js +140 -0
  310. package/dist/scheduler/task-board-text.js.map +1 -0
  311. package/dist/scheduler/task-board.d.ts +730 -0
  312. package/dist/scheduler/task-board.js +2179 -0
  313. package/dist/scheduler/task-board.js.map +1 -0
  314. package/dist/scheduler/types.d.ts +6 -1
  315. package/dist/sd-notify.js +2 -1
  316. package/dist/sd-notify.js.map +1 -1
  317. package/dist/search/search-docs.d.ts +58 -0
  318. package/dist/search/search-docs.js +63 -0
  319. package/dist/search/search-docs.js.map +1 -0
  320. package/dist/search/search-index.d.ts +141 -0
  321. package/dist/search/search-index.js +440 -0
  322. package/dist/search/search-index.js.map +1 -0
  323. package/dist/search/search-store.d.ts +122 -0
  324. package/dist/search/search-store.js +236 -0
  325. package/dist/search/search-store.js.map +1 -0
  326. package/dist/search/search-worker.d.ts +1 -0
  327. package/dist/search/search-worker.js +163 -0
  328. package/dist/search/search-worker.js.map +1 -0
  329. package/dist/secret-file.js.map +1 -1
  330. package/dist/secret-store.d.ts +2 -0
  331. package/dist/secret-store.js +21 -0
  332. package/dist/secret-store.js.map +1 -1
  333. package/dist/service-installer.d.ts +75 -0
  334. package/dist/service-installer.js +206 -0
  335. package/dist/service-installer.js.map +1 -1
  336. package/dist/service-owner.d.ts +44 -0
  337. package/dist/service-owner.js +48 -0
  338. package/dist/service-owner.js.map +1 -0
  339. package/dist/service-plan.d.ts +39 -2
  340. package/dist/service-plan.js +188 -6
  341. package/dist/service-plan.js.map +1 -1
  342. package/dist/session-oom.d.ts +21 -0
  343. package/dist/session-oom.js +139 -0
  344. package/dist/session-oom.js.map +1 -0
  345. package/dist/settings-api.d.ts +13 -0
  346. package/dist/settings-api.js +73 -12
  347. package/dist/settings-api.js.map +1 -1
  348. package/dist/settings-baseline.js.map +1 -1
  349. package/dist/settings-change.d.ts +5 -0
  350. package/dist/settings-change.js +167 -6
  351. package/dist/settings-change.js.map +1 -1
  352. package/dist/settings-confirmation.d.ts +9 -1
  353. package/dist/settings-confirmation.js +18 -8
  354. package/dist/settings-confirmation.js.map +1 -1
  355. package/dist/settings-control.js +2 -1
  356. package/dist/settings-control.js.map +1 -1
  357. package/dist/settings-effect.js +29 -4
  358. package/dist/settings-effect.js.map +1 -1
  359. package/dist/settings-http-confirmation.d.ts +13 -0
  360. package/dist/settings-http-confirmation.js +15 -3
  361. package/dist/settings-http-confirmation.js.map +1 -1
  362. package/dist/settings-instance-patch.js +13 -1
  363. package/dist/settings-instance-patch.js.map +1 -1
  364. package/dist/settings-request-capability.d.ts +4 -1
  365. package/dist/settings-request-capability.js +11 -3
  366. package/dist/settings-request-capability.js.map +1 -1
  367. package/dist/settings-transaction.js.map +1 -1
  368. package/dist/setup-auth.d.ts +1 -0
  369. package/dist/setup-auth.js +2 -1
  370. package/dist/setup-auth.js.map +1 -1
  371. package/dist/setup-form.d.ts +1 -1
  372. package/dist/setup-form.js +23 -7
  373. package/dist/setup-form.js.map +1 -1
  374. package/dist/setup-host.d.ts +6 -1
  375. package/dist/setup-host.js +15 -4
  376. package/dist/setup-host.js.map +1 -1
  377. package/dist/setup-wizard.js +6 -3
  378. package/dist/setup-wizard.js.map +1 -1
  379. package/dist/spawn-gate.d.ts +5 -0
  380. package/dist/spawn-gate.js +28 -8
  381. package/dist/spawn-gate.js.map +1 -1
  382. package/dist/start-attempt.d.ts +1 -0
  383. package/dist/start-attempt.js +18 -0
  384. package/dist/start-attempt.js.map +1 -0
  385. package/dist/start-failure-notice.d.ts +4 -0
  386. package/dist/start-failure-notice.js +20 -0
  387. package/dist/start-failure-notice.js.map +1 -0
  388. package/dist/start-failure.d.ts +147 -0
  389. package/dist/start-failure.js +281 -0
  390. package/dist/start-failure.js.map +1 -0
  391. package/dist/startup-memory-deadline.d.ts +23 -0
  392. package/dist/startup-memory-deadline.js +55 -0
  393. package/dist/startup-memory-deadline.js.map +1 -0
  394. package/dist/statusline-watcher.js.map +1 -1
  395. package/dist/storm-window.js.map +1 -1
  396. package/dist/systemd-peer-bus.js.map +1 -1
  397. package/dist/target-queue-worker.js.map +1 -1
  398. package/dist/task-board-summary.d.ts +101 -0
  399. package/dist/task-board-summary.js +177 -0
  400. package/dist/task-board-summary.js.map +1 -0
  401. package/dist/task-board-tool.d.ts +25 -0
  402. package/dist/task-board-tool.js +155 -0
  403. package/dist/task-board-tool.js.map +1 -0
  404. package/dist/task-board-waker.d.ts +51 -0
  405. package/dist/task-board-waker.js +141 -0
  406. package/dist/task-board-waker.js.map +1 -0
  407. package/dist/tips.js.map +1 -1
  408. package/dist/tmux-control.js +1 -1
  409. package/dist/tmux-control.js.map +1 -1
  410. package/dist/tmux-exec-helper.d.ts +104 -0
  411. package/dist/tmux-exec-helper.js +412 -0
  412. package/dist/tmux-exec-helper.js.map +1 -0
  413. package/dist/tmux-manager.d.ts +1 -0
  414. package/dist/tmux-manager.js +29 -13
  415. package/dist/tmux-manager.js.map +1 -1
  416. package/dist/tmux-read.js +2 -2
  417. package/dist/tmux-read.js.map +1 -1
  418. package/dist/tool-permissions.js +9 -42
  419. package/dist/tool-permissions.js.map +1 -1
  420. package/dist/tool-progress.js.map +1 -1
  421. package/dist/topic-archiver.js.map +1 -1
  422. package/dist/topic-commands.d.ts +13 -0
  423. package/dist/topic-commands.js +56 -33
  424. package/dist/topic-commands.js.map +1 -1
  425. package/dist/topic-link.js.map +1 -1
  426. package/dist/transcript-delta-reader.js.map +1 -1
  427. package/dist/transcript-jsonl.js.map +1 -1
  428. package/dist/transcript-monitor.js +5 -0
  429. package/dist/transcript-monitor.js.map +1 -1
  430. package/dist/transcript-sources.d.ts +2 -0
  431. package/dist/transcript-sources.js +4 -0
  432. package/dist/transcript-sources.js.map +1 -1
  433. package/dist/tunnel/cloudflared-install.js +3 -1
  434. package/dist/tunnel/cloudflared-install.js.map +1 -1
  435. package/dist/tunnel/cloudflared.d.ts +7 -16
  436. package/dist/tunnel/cloudflared.js +100 -13
  437. package/dist/tunnel/cloudflared.js.map +1 -1
  438. package/dist/tunnel/lease.js +3 -2
  439. package/dist/tunnel/lease.js.map +1 -1
  440. package/dist/tunnel/manager.js.map +1 -1
  441. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  442. package/dist/tunnel/named-diagnostics.js +52 -0
  443. package/dist/tunnel/named-diagnostics.js.map +1 -0
  444. package/dist/tunnel/public-fetch.js.map +1 -1
  445. package/dist/tunnel/purpose-lane.js.map +1 -1
  446. package/dist/tunnel/types.d.ts +4 -1
  447. package/dist/tunnel/types.js.map +1 -1
  448. package/dist/types.d.ts +55 -2
  449. package/dist/ui/app-effects.js +27 -5
  450. package/dist/ui/app.html +2 -0
  451. package/dist/ui/backend-signin.js +297 -0
  452. package/dist/ui/chat-effects.js +15 -7
  453. package/dist/ui/chat-msg-actions.js +176 -0
  454. package/dist/ui/chat-render.js +67 -20
  455. package/dist/ui/chat-store.js +218 -48
  456. package/dist/ui/chat-strings.js +42 -10
  457. package/dist/ui/chat-thread.js +249 -23
  458. package/dist/ui/effect-trial-controller.js +13 -8
  459. package/dist/ui/effect-trial-offer.js +2 -31
  460. package/dist/ui/effects-waker.js +2 -0
  461. package/dist/ui/fleet-canvas-layout.js +13 -4
  462. package/dist/ui/fleet-canvas.js +120 -43
  463. package/dist/ui/fleet-office-editor.js +149 -0
  464. package/dist/ui/fleet-office-model.js +358 -0
  465. package/dist/ui/fleet-office-pack.js +199 -0
  466. package/dist/ui/fleet-office-skin.js +474 -0
  467. package/dist/ui/fleet-office-store.js +49 -0
  468. package/dist/ui/fleet-office-zones.js +177 -0
  469. package/dist/ui/fleet-office.js +434 -0
  470. package/dist/ui/fleet-org.js +21 -8
  471. package/dist/ui/fleet-route-scene.js +272 -0
  472. package/dist/ui/fleet-route-worker.js +21 -0
  473. package/dist/ui/fleet-route.js +88 -0
  474. package/dist/ui/fleet-task-card.js +436 -0
  475. package/dist/ui/fleet-task-common.js +482 -0
  476. package/dist/ui/fleet-task-work.js +107 -0
  477. package/dist/ui/fleet-tasks.js +569 -0
  478. package/dist/ui/fleet-traffic.js +227 -45
  479. package/dist/ui/fleet-usage.js +36 -2
  480. package/dist/ui/guest-refused.html +41 -0
  481. package/dist/ui/icons/icon-192.png +0 -0
  482. package/dist/ui/icons/icon-512.png +0 -0
  483. package/dist/ui/image-lightbox.js +45 -1
  484. package/dist/ui/image-zoom.js +242 -0
  485. package/dist/ui/panel-chat.js +282 -89
  486. package/dist/ui/panel-details.js +2 -0
  487. package/dist/ui/panel-fleet.js +32 -71
  488. package/dist/ui/panel-needs.js +23 -6
  489. package/dist/ui/panel-settings.js +158 -32
  490. package/dist/ui/preview-panel.js +2 -1
  491. package/dist/ui/search-dialog.js +350 -0
  492. package/dist/ui/settings-appearance.js +170 -10
  493. package/dist/ui/settings-apply.js +3 -3
  494. package/dist/ui/settings-confirm.js +7 -2
  495. package/dist/ui/settings-dialogs.js +46 -21
  496. package/dist/ui/settings-discord.js +6 -2
  497. package/dist/ui/settings-guests.js +2 -1
  498. package/dist/ui/settings-learn.js +1 -0
  499. package/dist/ui/settings-model.js +51 -18
  500. package/dist/ui/settings-mods.js +8 -3
  501. package/dist/ui/settings-owner-effects.js +6 -4
  502. package/dist/ui/settings-strings.js +180 -6
  503. package/dist/ui/settings-token.js +3 -1
  504. package/dist/ui/settings-web-authority.js +13 -5
  505. package/dist/ui/settings-wizard.js +13 -7
  506. package/dist/ui/shared/agend-auth.js +3 -1
  507. package/dist/ui/shared/app-i18n.js +12 -5
  508. package/dist/ui/shared/app-needs.js +2 -2
  509. package/dist/ui/shared/app-prefs.js +15 -0
  510. package/dist/ui/shared/app-route.js +18 -0
  511. package/dist/ui/shared/app-shell.js +172 -28
  512. package/dist/ui/shared/app-store.js +3 -2
  513. package/dist/ui/shared/app-stream.js +7 -2
  514. package/dist/ui/shared/app-themes.js +116 -1
  515. package/dist/ui/shared/app.css +681 -54
  516. package/dist/ui/shared/app.js +9 -1
  517. package/dist/ui/shared/card-strings.js +4 -0
  518. package/dist/ui/shared/effect-app-runtime.js +7 -5
  519. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  520. package/dist/ui/shared/effect-cursor.js +3 -2
  521. package/dist/ui/shared/effect-display-metadata.js +39 -0
  522. package/dist/ui/shared/effect-owner-source.js +8 -1
  523. package/dist/ui/shared/effect-policy.js +2 -2
  524. package/dist/ui/shared/effect-registry.js +30 -11
  525. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  526. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  527. package/dist/ui/shared/effect-stock-trust.js +40 -9
  528. package/dist/ui/shared/effect-trial-source.js +1 -1
  529. package/dist/ui/shared/effect-trial-stage.js +27 -21
  530. package/dist/ui/shared/effect-trial.css +1 -1
  531. package/dist/ui/shared/feature-flags.js +127 -0
  532. package/dist/ui/shared/guest-refused.js +35 -0
  533. package/dist/ui/shared/header-tools.js +2 -2
  534. package/dist/ui/shared/identity-card.js +26 -8
  535. package/dist/ui/shared/identity-color.js +17 -10
  536. package/dist/ui/shared/instance-nav.js +183 -28
  537. package/dist/ui/shared/owner-css.js +30 -1
  538. package/dist/ui/shared/panel-view.js +14 -29
  539. package/dist/ui/shared/text-undo.js +210 -0
  540. package/dist/ui/shared/tokens.css +13 -3
  541. package/dist/ui/shared/ui-dialog.js +5 -4
  542. package/dist/ui/shared/ui-icons.js +4 -0
  543. package/dist/ui/shared/ui-menu.js +20 -7
  544. package/dist/ui/shared/ui-toast.js +16 -3
  545. package/dist/ui/shared/usage-dialog.js +206 -16
  546. package/dist/ui/shared/view-strings.js +68 -4
  547. package/dist/ui/shared/web-authorize.js +50 -1
  548. package/dist/ui/signin.html +2 -0
  549. package/dist/ui/web-terminal/terminal.css +4 -4
  550. package/dist/ui/web-terminal/terminal.html +10 -10
  551. package/dist/ui/web-terminal/terminal.js +58 -12
  552. package/dist/update-check.js.map +1 -1
  553. package/dist/update-dispatch.js +1 -1
  554. package/dist/update-dispatch.js.map +1 -1
  555. package/dist/update-install.js.map +1 -1
  556. package/dist/update-launch.d.ts +10 -1
  557. package/dist/update-launch.js +39 -14
  558. package/dist/update-launch.js.map +1 -1
  559. package/dist/update-marker.js.map +1 -1
  560. package/dist/update-preflight.js +1 -1
  561. package/dist/update-preflight.js.map +1 -1
  562. package/dist/update-progress.d.ts +3 -1
  563. package/dist/update-progress.js +3 -1
  564. package/dist/update-progress.js.map +1 -1
  565. package/dist/usage/format-rich.js +6 -3
  566. package/dist/usage/format-rich.js.map +1 -1
  567. package/dist/usage/i18n-keys.d.ts +1 -1
  568. package/dist/usage/i18n-keys.js +2 -1
  569. package/dist/usage/i18n-keys.js.map +1 -1
  570. package/dist/usage/i18n.d.ts +13 -1
  571. package/dist/usage/i18n.js +29 -0
  572. package/dist/usage/i18n.js.map +1 -1
  573. package/dist/usage/providers.d.ts +89 -2
  574. package/dist/usage/providers.js +247 -41
  575. package/dist/usage/providers.js.map +1 -1
  576. package/dist/usage/statusline-usage.js.map +1 -1
  577. package/dist/usage/usage-api.d.ts +5 -1
  578. package/dist/usage/usage-api.js +185 -58
  579. package/dist/usage/usage-api.js.map +1 -1
  580. package/dist/usage-report.js.map +1 -1
  581. package/dist/usage-rollup.js +1 -1
  582. package/dist/usage-rollup.js.map +1 -1
  583. package/dist/user-systemd.d.ts +24 -0
  584. package/dist/user-systemd.js +75 -0
  585. package/dist/user-systemd.js.map +1 -0
  586. package/dist/view-api.d.ts +7 -0
  587. package/dist/view-api.js +16 -4
  588. package/dist/view-api.js.map +1 -1
  589. package/dist/wake-coordinator.js.map +1 -1
  590. package/dist/web-ambient.js +9 -2
  591. package/dist/web-ambient.js.map +1 -1
  592. package/dist/web-api.d.ts +58 -10
  593. package/dist/web-api.js +1104 -71
  594. package/dist/web-api.js.map +1 -1
  595. package/dist/web-app-icon-worker.d.ts +1 -0
  596. package/dist/web-app-icon-worker.js +16 -0
  597. package/dist/web-app-icon-worker.js.map +1 -0
  598. package/dist/web-app-icon.d.ts +109 -0
  599. package/dist/web-app-icon.js +257 -0
  600. package/dist/web-app-icon.js.map +1 -0
  601. package/dist/web-appearance-policy.js.map +1 -1
  602. package/dist/web-appearance-tools.d.ts +1 -0
  603. package/dist/web-appearance-tools.js +7 -3
  604. package/dist/web-appearance-tools.js.map +1 -1
  605. package/dist/web-appearance-view.js.map +1 -1
  606. package/dist/web-appearance.d.ts +4 -10
  607. package/dist/web-appearance.js +7 -4
  608. package/dist/web-appearance.js.map +1 -1
  609. package/dist/web-auth.d.ts +8 -4
  610. package/dist/web-auth.js +33 -21
  611. package/dist/web-auth.js.map +1 -1
  612. package/dist/web-backend-login.d.ts +130 -0
  613. package/dist/web-backend-login.js +225 -0
  614. package/dist/web-backend-login.js.map +1 -0
  615. package/dist/web-card.js.map +1 -1
  616. package/dist/web-channel-echo.js.map +1 -1
  617. package/dist/web-chat-history.d.ts +118 -8
  618. package/dist/web-chat-history.js +220 -25
  619. package/dist/web-chat-history.js.map +1 -1
  620. package/dist/web-commands.d.ts +5 -1
  621. package/dist/web-commands.js +1 -1
  622. package/dist/web-commands.js.map +1 -1
  623. package/dist/web-cookie-names.d.ts +5 -0
  624. package/dist/web-cookie-names.js +12 -0
  625. package/dist/web-cookie-names.js.map +1 -0
  626. package/dist/web-effect-bundle.d.ts +2 -2
  627. package/dist/web-effect-library.js.map +1 -1
  628. package/dist/web-effect-playback.js.map +1 -1
  629. package/dist/web-effect-profiles.d.ts +16 -16
  630. package/dist/web-effect-profiles.js +3 -2
  631. package/dist/web-effect-profiles.js.map +1 -1
  632. package/dist/web-effect-service.js.map +1 -1
  633. package/dist/web-effect-tools.js.map +1 -1
  634. package/dist/web-effect-trials.js.map +1 -1
  635. package/dist/web-effect-upload.js.map +1 -1
  636. package/dist/web-effect.d.ts +6 -6
  637. package/dist/web-effect.js.map +1 -1
  638. package/dist/web-experimental-http.d.ts +2 -0
  639. package/dist/web-experimental-http.js +12 -0
  640. package/dist/web-experimental-http.js.map +1 -0
  641. package/dist/web-experimental.d.ts +11 -0
  642. package/dist/web-experimental.js +49 -0
  643. package/dist/web-experimental.js.map +1 -0
  644. package/dist/web-guest-access.d.ts +4 -2
  645. package/dist/web-guest-access.js +22 -10
  646. package/dist/web-guest-access.js.map +1 -1
  647. package/dist/web-guest-rooms.js.map +1 -1
  648. package/dist/web-host-guard.d.ts +11 -0
  649. package/dist/web-host-guard.js +11 -0
  650. package/dist/web-host-guard.js.map +1 -1
  651. package/dist/web-icons.d.ts +33 -1
  652. package/dist/web-icons.js +83 -11
  653. package/dist/web-icons.js.map +1 -1
  654. package/dist/web-identity-color.js.map +1 -1
  655. package/dist/web-image-meta.d.ts +13 -0
  656. package/dist/web-image-meta.js +62 -0
  657. package/dist/web-image-meta.js.map +1 -1
  658. package/dist/web-images.js.map +1 -1
  659. package/dist/web-libavoid.d.ts +14 -0
  660. package/dist/web-libavoid.js +46 -0
  661. package/dist/web-libavoid.js.map +1 -0
  662. package/dist/web-login.js.map +1 -1
  663. package/dist/web-mentions.js.map +1 -1
  664. package/dist/web-mod-agent.js.map +1 -1
  665. package/dist/web-mod-store.d.ts +2 -0
  666. package/dist/web-mod-store.js +10 -0
  667. package/dist/web-mod-store.js.map +1 -1
  668. package/dist/web-office-store.d.ts +104 -0
  669. package/dist/web-office-store.js +236 -0
  670. package/dist/web-office-store.js.map +1 -0
  671. package/dist/web-owner-css-store.d.ts +44 -1
  672. package/dist/web-owner-css-store.js +70 -9
  673. package/dist/web-owner-css-store.js.map +1 -1
  674. package/dist/web-owner-css.js.map +1 -1
  675. package/dist/web-png.d.ts +33 -0
  676. package/dist/web-png.js +329 -0
  677. package/dist/web-png.js.map +1 -0
  678. package/dist/web-prefs.d.ts +6 -0
  679. package/dist/web-prefs.js +29 -3
  680. package/dist/web-prefs.js.map +1 -1
  681. package/dist/web-preview.d.ts +2 -0
  682. package/dist/web-preview.js +2 -0
  683. package/dist/web-preview.js.map +1 -1
  684. package/dist/web-profile.js.map +1 -1
  685. package/dist/web-reactions.d.ts +35 -0
  686. package/dist/web-reactions.js +47 -0
  687. package/dist/web-reactions.js.map +1 -0
  688. package/dist/web-request-context.d.ts +5 -0
  689. package/dist/web-request-context.js.map +1 -1
  690. package/dist/web-returning-device.d.ts +4 -2
  691. package/dist/web-returning-device.js +13 -6
  692. package/dist/web-returning-device.js.map +1 -1
  693. package/dist/web-schedules.js.map +1 -1
  694. package/dist/web-send-dedupe.d.ts +36 -0
  695. package/dist/web-send-dedupe.js +69 -0
  696. package/dist/web-send-dedupe.js.map +1 -0
  697. package/dist/web-session-authorization.js.map +1 -1
  698. package/dist/web-session.js +1 -1
  699. package/dist/web-session.js.map +1 -1
  700. package/dist/web-settings-patch.d.ts +3 -1
  701. package/dist/web-settings-patch.js +11 -5
  702. package/dist/web-settings-patch.js.map +1 -1
  703. package/dist/web-settings-tools.d.ts +6 -0
  704. package/dist/web-settings-tools.js +52 -17
  705. package/dist/web-settings-tools.js.map +1 -1
  706. package/dist/web-terminal-http.js +15 -2
  707. package/dist/web-terminal-http.js.map +1 -1
  708. package/dist/web-terminal.js +1 -1
  709. package/dist/web-terminal.js.map +1 -1
  710. package/dist/web-theme-store.d.ts +2 -2
  711. package/dist/web-theme-store.js +6 -3
  712. package/dist/web-theme-store.js.map +1 -1
  713. package/dist/web-theme.d.ts +29 -0
  714. package/dist/web-theme.js +89 -1
  715. package/dist/web-theme.js.map +1 -1
  716. package/dist/web-upload.d.ts +18 -1
  717. package/dist/web-upload.js +45 -7
  718. package/dist/web-upload.js.map +1 -1
  719. package/dist/web-widget.js.map +1 -1
  720. package/dist/webhook-emitter.js.map +1 -1
  721. package/dist/windows-shortcuts.d.ts +78 -0
  722. package/dist/windows-shortcuts.js +203 -0
  723. package/dist/windows-shortcuts.js.map +1 -0
  724. package/dist/workflow-templates/default.md +4 -1
  725. package/dist/ws-server.js.map +1 -1
  726. package/dist/yaml-shared-nodes.d.ts +25 -0
  727. package/dist/yaml-shared-nodes.js +185 -0
  728. package/dist/yaml-shared-nodes.js.map +1 -0
  729. package/launcher/agend-install.cjs +828 -0
  730. package/launcher/install-admission.cjs +4 -39
  731. package/launcher/postinstall.cjs +14 -12
  732. package/launcher/private-home.cjs +43 -0
  733. package/package.json +16 -13
  734. package/scripts/preinstall-guard.cjs +2 -5
  735. package/templates/windows-shortcuts.ps1 +181 -0
  736. package/launcher/old-updater-note.cjs +0 -48
@@ -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];
@@ -429,6 +432,44 @@
429
432
  return msgs;
430
433
  }
431
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
+
432
473
  var TICK_GLYPH = { queued: "◷", processing: "✓", delivered: "✓✓", failed: "!", cancelled: "⊘" };
433
474
  /** The ticks for one state, labelled for a screen reader as well as a pointer; "" when there is none. */
434
475
  function deliveryHtml(state, labels) {
@@ -468,27 +509,17 @@
468
509
  /** Whether the chat shows "working" and its Stop button for an instance in this execution state. */
469
510
  function isBusy(state) { return state === "working" || state === "stuck"; }
470
511
 
471
- /** What a key in the composer does: Enter sends, Shift+Enter is a new line, nothing happens mid-IME-composition. */
472
- 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) {
473
517
  if (!ev || ev.key !== "Enter" || ev.isComposing || ev.keyCode === 229) return "none";
518
+ if (opts && opts.touch) return "newline";
474
519
  if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey) return "newline";
475
520
  return "send";
476
521
  }
477
522
 
478
- /**
479
- * A send that failed: where does its text go? Back into the composer only if that composer is the same
480
- * target's, still on screen and empty; otherwise it is kept aside for that target (never put into another
481
- * target's composer, never over a draft someone is typing) and offered back when that chat is open.
482
- */
483
- function settleFailedSend(target, current, composerPresent, composerValue) {
484
- return target === current && composerPresent && !composerValue ? "restore" : "keep";
485
- }
486
-
487
- /** "Put back" for a kept message: in front of whatever is being typed now, never replacing it. */
488
- function putBack(failedText, draft) {
489
- return draft ? failedText + "\n" + draft : failedText;
490
- }
491
-
492
523
  /** The defaults of the upload limits the server enforces (web-upload.ts UPLOAD_LIMITS), for a page that was given none. */
493
524
  var FILE_LIMITS = { maxFileBytes: 50 * 1024 * 1024, maxFiles: 5, maxTotalBytes: 100 * 1024 * 1024 };
494
525
  /**
@@ -577,6 +608,21 @@
577
608
  * With { htmlCards: true } (a server-marked agent reply, as for renderMarkdown) each .html/.htm file also gets an
578
609
  * empty card placeholder <div class="html-card" data-att="<id>"> after the list — the id is 32 hex, nothing else.
579
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
+
580
626
  function attachmentsHtml(attachments, opts) {
581
627
  if (!Array.isArray(attachments)) return "";
582
628
  var cards = !!(opts && opts.htmlCards);
@@ -599,7 +645,7 @@
599
645
  else if (kind) after.push('<div class="text-card" data-att="' + a.id + '" data-type="' + kind + '"></div>');
600
646
  var name = escapeHtml(a.name || "file");
601
647
  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>');
648
+ out.push('<a class="att-img" href="' + url + '" target="_blank" rel="noopener noreferrer"><img src="' + url + '" alt="' + name + '"' + photoBox(a) + ' loading="lazy"></a>');
603
649
  } else {
604
650
  out.push('<a class="att-file" href="' + url + '" download="' + name + '">📎 ' + name + ' <span class="att-size">' + escapeHtml(formatSize(a.size)) + "</span></a>");
605
651
  }
@@ -610,9 +656,10 @@
610
656
  return {
611
657
  renderMarkdown: renderMarkdown, escapeHtml: escapeHtml, mergeMessages: mergeMessages, applyReplyButtons: applyReplyButtons, composerKey: composerKey,
612
658
  pollRemaining: pollRemaining,
613
- settleFailedSend: settleFailedSend, putBack: putBack,
659
+
614
660
  FILE_LIMITS: FILE_LIMITS, fileLimits: fileLimits, fileRejection: fileRejection, formatSize: formatSize, checkFiles: checkFiles, attachmentsHtml: attachmentsHtml, isHtmlAttachment: isHtmlAttachment,
615
661
  nextDeliveryState: nextDeliveryState, applyDelivery: applyDelivery, deliveryHtml: deliveryHtml, isBusy: isBusy,
662
+ applyReactions: applyReactions, reactionsHtml: reactionsHtml,
616
663
  isNearBottom: isNearBottom, CODE_FOLD_LINES: CODE_FOLD_LINES, lineCount: lineCount,
617
664
  LONG_PASTE_CHARS: LONG_PASTE_CHARS, isLongPaste: isLongPaste, formatElapsed: formatElapsed,
618
665
  htmlFences: htmlFences, attachmentPreviewType: attachmentPreviewType, highlight: highlight,
@@ -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: {}, pendingFiles: {}, sending: {}, outgoing: {}, 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));
@@ -106,6 +108,8 @@ export function createChatStore(deps) {
106
108
  liveReplies.message(m, extra);
107
109
  const before = s.msgs[m.instance];
108
110
  s.msgs[m.instance] = R().mergeMessages(before, [m], 500);
111
+ // The live copy of a message this page sent carries its clientId: its pending bubble goes in this same redraw.
112
+ if (settleOutgoing(m.instance, m.role === "user" ? m.clientId : undefined) && s.msgs[m.instance] === before) changed(m.instance, "msgs");
109
113
  if (m.instance === s.current && s.msgs[m.instance] !== before && m.role !== "status" && !isUser(m, m.instance) && (s.msgs[m.instance] || []).length > (before || []).length) deps.announce(tf("chat.a11yReplied", m.sender));
110
114
  if (s.msgs[m.instance] !== before) changed(m.instance, "msgs");
111
115
  }
@@ -127,6 +131,13 @@ export function createChatStore(deps) {
127
131
  s.msgs[u.instance] = R().applyReplyButtons(before, u.buttons);
128
132
  if (s.msgs[u.instance] !== before) changed(u.instance, "msgs");
129
133
  }
134
+ /** #2305: the instance reacted to a message — its bubble shows it. */
135
+ function applyReactions(u) {
136
+ if (!u || typeof u.instance !== "string" || typeof u.boot !== "string" || typeof u.id !== "number") return;
137
+ const before = s.msgs[u.instance];
138
+ s.msgs[u.instance] = R().applyReactions(before, u.boot, u.id, u.reactions);
139
+ if (s.msgs[u.instance] !== before) changed(u.instance, "msgs");
140
+ }
130
141
  /**
131
142
  * #1266: click one of a reply's buttons (the first click anywhere answers for everyone). On a poll (#1823) it is this
132
143
  * session's vote: the answer is its ballot, which only this page marks.
@@ -142,16 +153,22 @@ export function createChatStore(deps) {
142
153
  if (!r || !r.answered) deps.toast(r && r.error ? r.error : t("chat.disconnected"), false);
143
154
  }
144
155
 
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;
156
+ /**
157
+ * Read an instance's history once per page (the first time a person opens its chat). #2188: `signal` — the chat was
158
+ * left before the answer came (clicking down the sidebar): the read is aborted, nothing is merged, and the next visit
159
+ * reads again.
160
+ */
161
+ async function openHistory(name, lease, signal) {
162
+ if (s.historyRead.has(name) || signal?.aborted) return false;
148
163
  s.historyRead.add(name);
149
164
  try {
150
- const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=200`);
165
+ const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=200`, undefined, signal);
166
+ if (signal?.aborted) { s.historyRead.delete(name); return false; }
151
167
  if (!r || !Array.isArray(r.messages)) { s.historyRead.delete(name); return true; }
152
168
  // Kept even when the person has moved on: the store is page-lived, and the answer belongs to `name`.
153
169
  for (const m of r.messages) liveReplies.message(m);
154
170
  s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500);
171
+ settleOutgoing(name);
155
172
  changed(name, "msgs");
156
173
  } catch { s.historyRead.delete(name); /* the live stream still works; the next visit tries again */ }
157
174
  void lease;
@@ -228,51 +245,186 @@ export function createChatStore(deps) {
228
245
  }
229
246
  const promptsFor = (instance) => Object.values(s.prompts).filter(p => p.instance === instance);
230
247
 
231
- // ── Sending. Never lose anything: a failed send's files go back to that chat's list and its text back into its own
232
- // composer when that is empty, otherwise it is kept for its chat and offered back (Put back).
233
- async function uploadFile(target, file) {
234
- const r = await deps.fetch(`/ui/upload?instance=${encodeURIComponent(target)}`, {
235
- method: "POST",
236
- headers: { "Content-Type": file.type || "application/octet-stream", "X-Agend-Filename": encodeURIComponent(file.name || "file") },
237
- body: file,
248
+ // ── Sending (Discord-like, the user's request): a message shows at once as its own bubble, dimmed while it goes; its
249
+ // files show their upload progress. The server's copy replaces it when it arrives — matched by the page's clientId
250
+ // (on the live copy) or the messageId the send answered with — so it is never shown twice. A send that fails stays,
251
+ // marked, with Retry and Delete: nothing is lost, and nothing is put back into the composer behind the person's back.
252
+ // Sends of one chat go out in order, one at a time; a later one waits behind an earlier one still going.
253
+ let outSeq = 0;
254
+ const objectUrl = (f) => { try { return globalThis.URL && typeof URL.createObjectURL === "function" && /^image\/(png|jpeg|gif|webp)$/.test(f.type || "") ? URL.createObjectURL(f) : null; } catch { return null; } };
255
+ const freeUrl = (u) => { try { if (u) URL.revokeObjectURL(u); } catch { /* gone */ } };
256
+ const newClientId = () => {
257
+ const bytes = new Uint8Array(12);
258
+ try { globalThis.crypto.getRandomValues(bytes); } catch { for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256); }
259
+ return "c" + [...bytes].map((x) => x.toString(16).padStart(2, "0")).join("");
260
+ };
261
+ /** Pending bubbles the server's copy now stands for go (their previews freed). Returns whether any went. */
262
+ function settleOutgoing(name, clientId) {
263
+ const list = s.outgoing[name];
264
+ if (!list || !list.length) return false;
265
+ const ids = new Set((s.msgs[name] || []).map((m) => m.messageId).filter(Boolean)), clients = new Set(clientId ? [clientId] : []);
266
+ const kept = list.filter((e) => !((e.messageId && ids.has(e.messageId)) || clients.has(e.clientId)));
267
+ if (kept.length === list.length) return false;
268
+ for (const e of list) if (!kept.includes(e)) for (const f of e.files) freeUrl(f.url);
269
+ s.outgoing[name] = kept;
270
+ return true;
271
+ }
272
+ /** The thread as drawn: the server's messages, then this page's pending ones. */
273
+ function threadRows(name) {
274
+ const out = s.outgoing[name];
275
+ return out && out.length ? (s.msgs[name] || []).concat(out) : (s.msgs[name] || []);
276
+ }
277
+ /** Upload one file, its progress told as it goes (XHR: fetch cannot report an upload's progress). */
278
+ async function uploadFile(target, file, onProgress) {
279
+ const url = `/ui/upload?instance=${encodeURIComponent(target)}`;
280
+ const X = deps.XMLHttpRequest;
281
+ if (!X) { // no XHR given (a test): the whole file at once
282
+ const r = await deps.fetch(url, { method: "POST", headers: { "Content-Type": file.type || "application/octet-stream", "X-Agend-Filename": encodeURIComponent(file.name || "file") }, body: file });
283
+ let body = {};
284
+ try { body = await r.json(); } catch { /* not JSON */ }
285
+ if (!r.ok || !body || typeof body.id !== "string") throw new Error(body && body.error ? `${file.name}: ${body.error}` : `${file.name}: upload failed (${r.status})`);
286
+ onProgress?.(file.size || 0, file.size || 0);
287
+ return body;
288
+ }
289
+ // The CSRF header the page's fetch adds (agend-auth.js), added here too; a 401 ends the session as fetch's would.
290
+ const csrf = globalThis.AgendAuth && typeof globalThis.AgendAuth.csrf === "function" ? await globalThis.AgendAuth.csrf().catch(() => null) : null;
291
+ return new Promise((resolve, reject) => {
292
+ const xhr = new X();
293
+ xhr.open("POST", url);
294
+ xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
295
+ xhr.setRequestHeader("X-Agend-Filename", encodeURIComponent(file.name || "file"));
296
+ if (csrf) xhr.setRequestHeader("X-Agend-CSRF", csrf);
297
+ if (xhr.upload && onProgress) xhr.upload.onprogress = (ev) => { if (ev.lengthComputable) onProgress(ev.loaded, ev.total); };
298
+ xhr.onload = () => {
299
+ let body = {};
300
+ try { body = JSON.parse(xhr.responseText || "{}"); } catch { /* not JSON */ }
301
+ if (xhr.status === 401) globalThis.AgendAuth?.unauthorized?.();
302
+ if (xhr.status >= 200 && xhr.status < 300 && body && typeof body.id === "string") resolve(body);
303
+ else reject(new Error(body && body.error ? `${file.name}: ${body.error}` : `${file.name}: upload failed (${xhr.status})`));
304
+ };
305
+ xhr.onerror = () => reject(new Error(`${file.name}: ${t("chat.uploadNoConnection")}`));
306
+ xhr.onabort = () => reject(new Error(`${file.name}: ${t("chat.uploadNoConnection")}`));
307
+ xhr.send(file);
238
308
  });
239
- let body = {};
240
- try { body = await r.json(); } catch { /* not JSON */ }
241
- if (!r.ok || !body || typeof body.id !== "string") throw new Error(body && body.error ? `${file.name}: ${body.error}` : `${file.name}: upload failed (${r.status})`);
242
- return body;
243
309
  }
244
- async function send(target) {
310
+ /** Send what the composer holds: its bubble at once, then out in this chat's order. */
311
+ function send(target) {
245
312
  const txt = (s.drafts[target] || "").trim();
246
313
  const files = (s.pendingFiles[target] || []).slice();
247
- if ((!txt && !files.length) || !target || s.sending[target]) return;
248
- s.sending[target] = true;
314
+ if ((!txt && !files.length) || !target) return;
315
+ const quote = s.replies[target];
316
+ const e = {
317
+ // "You" until the server's copy (with the person's name) replaces it: no request is made for a name.
318
+ boot: "out", id: ++outSeq, instance: target, role: "user", sender: t("chat.you"), text: txt,
319
+ ts: new Date().toISOString(), clientId: newClientId(), replyTo: quote ? { boot: quote.boot, id: quote.id, sender: quote.sender, text: quote.text, media: quote.media } : undefined,
320
+ quote, outgoing: { state: "queued" },
321
+ files: files.map((f) => ({ file: f, name: f.name || "file", size: f.size || 0, type: f.type || "", url: objectUrl(f), loaded: 0, total: f.size || 0, state: "waiting", id: null })),
322
+ };
323
+ (s.outgoing[target] = s.outgoing[target] || []).push(e);
249
324
  s.drafts[target] = "";
250
- s.pendingFiles[target] = []; s.inFlightFiles[target] = files;
325
+ s.pendingFiles[target] = [];
326
+ if (quote) delete s.replies[target];
327
+ changed(target, "msgs");
251
328
  changed(target);
252
329
  changed(target, "sent"); // your own message: the thread goes to the bottom
330
+ return pump(target);
331
+ }
332
+ /** Take this chat's waiting sends, one after another. */
333
+ async function pump(target) {
334
+ if (s.sending[target]) return;
335
+ s.sending[target] = true;
336
+ try {
337
+ for (let e = nextQueued(target); e; e = nextQueued(target)) await deliver(target, e);
338
+ } finally { s.sending[target] = false; delete s.waking[target]; changed(target); }
339
+ }
340
+ const nextQueued = (target) => (s.outgoing[target] || []).find((x) => x.outgoing.state === "queued");
341
+ /** One send: its files (each with its own progress), then the message. */
342
+ async function deliver(target, e) {
343
+ e.outgoing = { state: "sending" };
344
+ s.waking[target] = s.instanceStatus[target] === "paused";
345
+ changed(target, "msgs"); changed(target);
253
346
  let r;
254
347
  try {
255
- const ids = [];
256
- 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 });
258
- } catch (err) { r = { error: err && err.message ? err.message : t("chat.disconnected") }; }
259
- s.sending[target] = false;
260
- delete s.inFlightFiles[target];
261
- if (r && !r.error) { changed(target); return; }
262
- if (files.length) s.pendingFiles[target] = R().checkFiles(files, s.pendingFiles[target] || []).kept;
263
- if (txt) {
264
- const shown = s.current === target;
265
- if (R().settleFailedSend(target, s.current, shown, shown ? (s.drafts[target] || "") : "") === "restore") s.drafts[target] = txt;
266
- else s.failedSends[target] = s.failedSends[target] ? `${s.failedSends[target]}\n${txt}` : txt;
348
+ for (const f of e.files) {
349
+ if (f.id) continue; // uploaded by an earlier try
350
+ f.state = "uploading"; f.loaded = 0; changed(target, "progress");
351
+ let last = 0;
352
+ const body = await uploadFile(target, f.file, (loaded, total) => {
353
+ f.loaded = loaded; f.total = total || f.total;
354
+ const at = deps.now ? deps.now() : Date.now();
355
+ if (at - last > 60 || loaded >= f.total) { last = at; changed(target, "progress"); }
356
+ });
357
+ f.id = body.id; f.state = "done"; f.loaded = f.total; changed(target, "progress");
358
+ }
359
+ const body = { instance: target, message: e.text, clientId: e.clientId };
360
+ const ids = e.files.map((f) => f.id);
361
+ if (ids.length) body.attachments = ids;
362
+ if (e.quote) body.replyTo = { boot: e.quote.boot, id: e.quote.id };
363
+ r = await api("POST", "/ui/send", body);
364
+ } catch (err) {
365
+ const f = e.files.find((x) => x.state === "uploading");
366
+ if (f) f.state = "failed";
367
+ // The browser's own words for a request that never reached the server (Chrome, Firefox, Safari): the page's.
368
+ const offline = err && err.name === "TypeError" && /failed to fetch|networkerror|load failed/i.test(err.message || "");
369
+ r = { error: offline ? t("chat.sendNoConnection") : err && err.message ? err.message : t("chat.disconnected") };
267
370
  }
268
- changed(target);
269
- deps.toast(r && r.error ? r.error : t("chat.disconnected"), false);
371
+ if (!(s.outgoing[target] || []).includes(e)) return; // deleted meanwhile
372
+ if (r && !r.error && r.sent !== false) {
373
+ e.outgoing = { state: "sent" };
374
+ if (typeof r.messageId === "string") e.messageId = r.messageId;
375
+ settleOutgoing(target);
376
+ changed(target, "msgs"); changed(target);
377
+ return;
378
+ }
379
+ // Files the server no longer holds (they are released when a delivery fails) are uploaded again on Retry.
380
+ if (r && typeof r.error === "string" && /files? (are|is) not available|no such file|already sent/i.test(r.error)) for (const f of e.files) { f.id = null; f.state = "waiting"; }
381
+ // #2272: the quoted message is gone: Retry sends it without the quote.
382
+ if (r && r.reason === "reply_gone") { e.quote = undefined; e.replyTo = undefined; }
383
+ const why = r && r.reason;
384
+ const key = typeof why === "string" ? `chat.sendFailure_${why}` : "";
385
+ const localized = key ? t(key) : "";
386
+ e.outgoing = { state: "failed", error: localized && localized !== key ? localized : (r && r.error ? r.error : t("chat.disconnected")) };
387
+ changed(target, "msgs"); changed(target);
388
+ deps.toast(e.outgoing.error, false);
389
+ }
390
+ /** A failed send, sent again (the same bubble; files already uploaded are not uploaded again). */
391
+ function retryOutgoing(target, clientId) {
392
+ const e = (s.outgoing[target] || []).find((x) => x.clientId === clientId);
393
+ if (!e || e.outgoing.state !== "failed") return;
394
+ e.outgoing = { state: "queued" };
395
+ for (const f of e.files) if (f.state === "failed") f.state = "waiting";
396
+ changed(target, "msgs");
397
+ return pump(target);
398
+ }
399
+ /** A failed send, let go (its previews freed). */
400
+ function deleteOutgoing(target, clientId) {
401
+ const list = s.outgoing[target] || [];
402
+ const e = list.find((x) => x.clientId === clientId);
403
+ if (!e || e.outgoing.state !== "failed") return;
404
+ for (const f of e.files) freeUrl(f.url);
405
+ s.outgoing[target] = list.filter((x) => x !== e);
406
+ changed(target, "msgs"); changed(target);
407
+ }
408
+ /**
409
+ * A failed send, back in the composer to change it: its text before what is typed there now, its files (within the
410
+ * limits) and its reply, if none is started; the bubble goes. The page records the composer's undo step first.
411
+ */
412
+ function putBackOutgoing(target, clientId) {
413
+ const list = s.outgoing[target] || [];
414
+ const e = list.find((x) => x.clientId === clientId);
415
+ if (!e || e.outgoing.state !== "failed") return;
416
+ const draft = s.drafts[target] || "";
417
+ s.drafts[target] = e.text ? (draft ? e.text + "\n" + draft : e.text) : draft;
418
+ for (const f of e.files) freeUrl(f.url);
419
+ s.outgoing[target] = list.filter((x) => x !== e);
420
+ if (e.quote && !s.replies[target]) s.replies[target] = e.quote;
421
+ changed(target, "msgs");
422
+ if (e.files.length) addFiles(target, e.files.map((f) => f.file)); else changed(target);
270
423
  }
271
424
  function addFiles(target, list) {
272
425
  if (!target) return;
273
- const inFlight = s.inFlightFiles[target] || [];
274
- const res = R().checkFiles(inFlight.concat(s.pendingFiles[target] || []), [...(list || [])]);
275
- s.pendingFiles[target] = res.kept.slice(inFlight.length);
426
+ const res = R().checkFiles(s.pendingFiles[target] || [], [...(list || [])]);
427
+ s.pendingFiles[target] = res.kept;
276
428
  for (const x of res.rejected) { const why = R().fileRejection(x.reason); deps.toast(`${x.name}: ${t(why.key, ...why.values)}`, false); }
277
429
  changed(target);
278
430
  }
@@ -282,7 +434,7 @@ export function createChatStore(deps) {
282
434
  if (!target) return false;
283
435
  const stamp = new Date().toISOString().slice(0, 19).replace(/[-:T]/g, "");
284
436
  const f = new File([text], `pasted-${stamp}.txt`, { type: "text/plain" });
285
- const taken = (s.inFlightFiles[target] || []).concat(s.pendingFiles[target] || []);
437
+ const taken = s.pendingFiles[target] || [];
286
438
  if (R().checkFiles(taken, [f]).rejected.length) { deps.toast(t("chat.pasteKeptAsText")); return false; }
287
439
  pastedTexts.set(f, text);
288
440
  addFiles(target, [f]);
@@ -299,16 +451,32 @@ export function createChatStore(deps) {
299
451
  changed(target);
300
452
  }
301
453
  const isPasted = (f) => pastedTexts.has(f);
302
- function putBack(target) {
303
- const kept = s.failedSends[target];
304
- if (!kept) return;
305
- s.drafts[target] = R().putBack(kept, s.drafts[target] || "");
306
- delete s.failedSends[target];
454
+ /** Typing stays local. */
455
+ function setDraft(target, value) { s.drafts[target] = value; }
456
+ /**
457
+ * #2272: reply to a message of this chat — what the bar above the composer shows (its sender and first words); only
458
+ * its row is sent. One at a time per chat: a new one replaces it.
459
+ */
460
+ function startReply(target, m) {
461
+ if (!target || !m || typeof m.boot !== "string" || typeof m.id !== "number") return;
462
+ 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),
463
+ media: (m.attachments || []).length ? ((m.attachments || []).some(a => a.kind === "photo") ? "photo" : "document") : undefined };
307
464
  changed(target);
308
465
  }
309
- 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; }
466
+ function clearReply(target) { if (s.replies[target]) { delete s.replies[target]; changed(target); } }
467
+ /**
468
+ * #2272: the quoted message, drawn for a jump: in what this chat holds, or after reading the longest history the
469
+ * server keeps for it (the newest 500). false: no longer kept.
470
+ */
471
+ async function findQuoted(name, key) {
472
+ const has = () => (s.msgs[name] || []).some(m => `${m.boot}-${m.id}` === key);
473
+ if (has()) return true;
474
+ try {
475
+ const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=500`);
476
+ if (r && Array.isArray(r.messages)) { s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500); settleOutgoing(name); changed(name, "msgs"); }
477
+ } catch { /* the jump says it is not there */ }
478
+ return has();
479
+ }
312
480
 
313
481
  // ── Stop the reply under way (the composer's Stop, or Esc). Set before the request goes, so a held Esc or a second
314
482
  // click sends nothing more (#1317); "Stopping…" until the agent goes idle, or a minute passes.
@@ -391,6 +559,7 @@ export function createChatStore(deps) {
391
559
  stream.on("prompts", applyPrompts);
392
560
  stream.on("prompt_resolved", resolvePrompt);
393
561
  stream.on("reply_buttons", applyReplyButtons);
562
+ stream.on("reactions", applyReactions); // #2305
394
563
  stream.on("clock", noteServerClock); // #1823
395
564
  }
396
565
 
@@ -402,7 +571,8 @@ export function createChatStore(deps) {
402
571
  attach, applyStatus, applyActivity, ingest, applyDeliveries, openHistory,
403
572
  onPrompt, applyPrompts, resolvePrompt, answerPrompt, answerByNonce, holdPrompt, releasePrompt, promptsFor,
404
573
  applyReplyButtons, clickReplyButton,
405
- uploadFile, send, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, putBack, discardFailed, setDraft,
574
+ uploadFile, send, retryOutgoing, deleteOutgoing, putBackOutgoing, threadRows, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, setDraft,
575
+ startReply, clearReply, findQuoted,
406
576
  cancelReply, isUser, runCommand, dismissCommand, commandFree,
407
577
  };
408
578
  }