@songsid/agend 2.3.0-beta.25 → 2.3.0-beta.27

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 (440) hide show
  1. package/README.md +7 -5
  2. package/README.zh-TW.md +7 -5
  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.js.map +1 -1
  8. package/dist/agent-endpoint.js +8 -0
  9. package/dist/agent-endpoint.js.map +1 -1
  10. package/dist/agent-file-read.js +1 -1
  11. package/dist/agent-file-read.js.map +1 -1
  12. package/dist/agent-whats-new.js +12 -2
  13. package/dist/agent-whats-new.js.map +1 -1
  14. package/dist/apply-job.js.map +1 -1
  15. package/dist/atomic-write.js.map +1 -1
  16. package/dist/auth-api.d.ts +5 -0
  17. package/dist/auth-api.js +21 -0
  18. package/dist/auth-api.js.map +1 -1
  19. package/dist/authority/authority-state.d.ts +4 -0
  20. package/dist/authority/authority-state.js +17 -8
  21. package/dist/authority/authority-state.js.map +1 -1
  22. package/dist/authority/session-authorizations.js.map +1 -1
  23. package/dist/authority/web-authority.d.ts +14 -6
  24. package/dist/authority/web-authority.js +19 -5
  25. package/dist/authority/web-authority.js.map +1 -1
  26. package/dist/backend/antigravity.d.ts +2 -2
  27. package/dist/backend/antigravity.js +2 -1
  28. package/dist/backend/antigravity.js.map +1 -1
  29. package/dist/backend/claude-code.js.map +1 -1
  30. package/dist/backend/codex-package.js.map +1 -1
  31. package/dist/backend/codex-session-lookup.js.map +1 -1
  32. package/dist/backend/codex.js +1 -1
  33. package/dist/backend/codex.js.map +1 -1
  34. package/dist/backend/effort-metadata.js.map +1 -1
  35. package/dist/backend/grok.js.map +1 -1
  36. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  37. package/dist/backend/kiro-kas-store.js.map +1 -1
  38. package/dist/backend/kiro-v2-store.js.map +1 -1
  39. package/dist/backend/kiro-v3-identity.js.map +1 -1
  40. package/dist/backend/kiro.js.map +1 -1
  41. package/dist/backend/launch-effort.js.map +1 -1
  42. package/dist/backend/mock.js.map +1 -1
  43. package/dist/backend/muse.js.map +1 -1
  44. package/dist/backend/opencode-permission.js.map +1 -1
  45. package/dist/backend/opencode.js.map +1 -1
  46. package/dist/backend/session-signals.js.map +1 -1
  47. package/dist/backend-availability.js.map +1 -1
  48. package/dist/backend-command.js.map +1 -1
  49. package/dist/backend-outage.js.map +1 -1
  50. package/dist/binary-probe.js.map +1 -1
  51. package/dist/bug-report.js.map +1 -1
  52. package/dist/bundled-skills.js.map +1 -1
  53. package/dist/cache-service.js +1 -1
  54. package/dist/cache-service.js.map +1 -1
  55. package/dist/channel/adapters/discord.d.ts +2 -0
  56. package/dist/channel/adapters/discord.js +40 -5
  57. package/dist/channel/adapters/discord.js.map +1 -1
  58. package/dist/channel/adapters/telegram.d.ts +6 -0
  59. package/dist/channel/adapters/telegram.js +54 -14
  60. package/dist/channel/adapters/telegram.js.map +1 -1
  61. package/dist/channel/mcp-server.js +10 -1
  62. package/dist/channel/mcp-server.js.map +1 -1
  63. package/dist/channel/mcp-tools.js +2 -2
  64. package/dist/channel/mcp-tools.js.map +1 -1
  65. package/dist/channel/message-queue.js.map +1 -1
  66. package/dist/channel/sticker-download.js.map +1 -1
  67. package/dist/channel/tool-router.js.map +1 -1
  68. package/dist/classic-channel-manager.js +3 -2
  69. package/dist/classic-channel-manager.js.map +1 -1
  70. package/dist/cli-entry.js.map +1 -1
  71. package/dist/cli-flags.js.map +1 -1
  72. package/dist/cli.d.ts +1 -0
  73. package/dist/cli.js +44 -9
  74. package/dist/cli.js.map +1 -1
  75. package/dist/command-table.js.map +1 -1
  76. package/dist/compact-watch.js.map +1 -1
  77. package/dist/completion-install.js.map +1 -1
  78. package/dist/completion.js.map +1 -1
  79. package/dist/config-validator.js +22 -6
  80. package/dist/config-validator.js.map +1 -1
  81. package/dist/config.js +8 -2
  82. package/dist/config.js.map +1 -1
  83. package/dist/connection-secrets.js.map +1 -1
  84. package/dist/context-guardian.js.map +1 -1
  85. package/dist/coredump-filter.js.map +1 -1
  86. package/dist/cpu-profile.js.map +1 -1
  87. package/dist/cross-instance-envelope.js.map +1 -1
  88. package/dist/daemon.d.ts +3 -0
  89. package/dist/daemon.js +76 -4
  90. package/dist/daemon.js.map +1 -1
  91. package/dist/daily-summary.js.map +1 -1
  92. package/dist/darwin-memory.js.map +1 -1
  93. package/dist/dashboard-live.d.ts +8 -0
  94. package/dist/dashboard-live.js +35 -0
  95. package/dist/dashboard-live.js.map +1 -0
  96. package/dist/dashboard-permissions.d.ts +69 -0
  97. package/dist/dashboard-permissions.js +138 -0
  98. package/dist/dashboard-permissions.js.map +1 -0
  99. package/dist/decision-list.js.map +1 -1
  100. package/dist/delivery-consumed-watch.js.map +1 -1
  101. package/dist/delivery-outbox.js.map +1 -1
  102. package/dist/delivery-outbox.test.js.map +1 -1
  103. package/dist/delivery-reconciliation.test.js.map +1 -1
  104. package/dist/diagnostic-redaction.js.map +1 -1
  105. package/dist/doctor.d.ts +3 -0
  106. package/dist/doctor.js +10 -0
  107. package/dist/doctor.js.map +1 -1
  108. package/dist/e2e-isolation.js.map +1 -1
  109. package/dist/event-log.js.map +1 -1
  110. package/dist/export-import.js +3 -2
  111. package/dist/export-import.js.map +1 -1
  112. package/dist/fleet-control-audit.js.map +1 -1
  113. package/dist/fleet-label.js.map +1 -1
  114. package/dist/fleet-lock.js +3 -2
  115. package/dist/fleet-lock.js.map +1 -1
  116. package/dist/fleet-manager.d.ts +84 -7
  117. package/dist/fleet-manager.js +562 -158
  118. package/dist/fleet-manager.js.map +1 -1
  119. package/dist/fleet-process-handlers.js +3 -2
  120. package/dist/fleet-process-handlers.js.map +1 -1
  121. package/dist/fleet-startup.d.ts +14 -0
  122. package/dist/fleet-startup.js +30 -0
  123. package/dist/fleet-startup.js.map +1 -0
  124. package/dist/fleet-traffic.js.map +1 -1
  125. package/dist/forged-envelope-scan.d.ts +0 -20
  126. package/dist/forged-envelope-scan.js +3 -2
  127. package/dist/forged-envelope-scan.js.map +1 -1
  128. package/dist/full-restart.js.map +1 -1
  129. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +2 -0
  130. package/dist/general-knowledge/skills/web-appearance/SKILL.md +2 -0
  131. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +43 -21
  132. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +52 -23
  133. package/dist/host-memory.js.map +1 -1
  134. package/dist/idle-gate-log.d.ts +19 -0
  135. package/dist/idle-gate-log.js +15 -0
  136. package/dist/idle-gate-log.js.map +1 -0
  137. package/dist/instance-config-impact.js +6 -0
  138. package/dist/instance-config-impact.js.map +1 -1
  139. package/dist/instance-lifecycle.js +1 -1
  140. package/dist/instance-lifecycle.js.map +1 -1
  141. package/dist/instructions.d.ts +2 -0
  142. package/dist/instructions.js +6 -2
  143. package/dist/instructions.js.map +1 -1
  144. package/dist/kiro-compact-pane.js.map +1 -1
  145. package/dist/kiro-db-reader.js.map +1 -1
  146. package/dist/kiro-engine-status.js.map +1 -1
  147. package/dist/kiro-log-gate.js.map +1 -1
  148. package/dist/kiro-transcript-lane.js.map +1 -1
  149. package/dist/locale.js +40 -6
  150. package/dist/locale.js.map +1 -1
  151. package/dist/log-service.js.map +1 -1
  152. package/dist/log-steps.js.map +1 -1
  153. package/dist/logger.js +3 -2
  154. package/dist/logger.js.map +1 -1
  155. package/dist/login-controller.js.map +1 -1
  156. package/dist/login-manager.js.map +1 -1
  157. package/dist/login-shell.js.map +1 -1
  158. package/dist/memory-pressure.js.map +1 -1
  159. package/dist/muse-usage-relay.js.map +1 -1
  160. package/dist/needs-you-hub.js.map +1 -1
  161. package/dist/needs-you-live.js.map +1 -1
  162. package/dist/network-family.js.map +1 -1
  163. package/dist/operator-control-paths.js +3 -4
  164. package/dist/operator-control-paths.js.map +1 -1
  165. package/dist/outbound-handlers.js.map +1 -1
  166. package/dist/outbound-schemas.d.ts +68 -68
  167. package/dist/outbound-schemas.js +1 -1
  168. package/dist/outbound-schemas.js.map +1 -1
  169. package/dist/outbox-open-error.js.map +1 -1
  170. package/dist/pane-context-cache.js.map +1 -1
  171. package/dist/paths.js.map +1 -1
  172. package/dist/private-dir.js.map +1 -1
  173. package/dist/probe-worker-pool.js.map +1 -1
  174. package/dist/profile-control.js.map +1 -1
  175. package/dist/prompt-file-ref.js.map +1 -1
  176. package/dist/provider-probe.js.map +1 -1
  177. package/dist/provider-secret-registry.js.map +1 -1
  178. package/dist/public-link-auto-open.d.ts +32 -0
  179. package/dist/public-link-auto-open.js +86 -0
  180. package/dist/public-link-auto-open.js.map +1 -0
  181. package/dist/public-link-owner.d.ts +10 -0
  182. package/dist/public-link-owner.js +36 -0
  183. package/dist/public-link-owner.js.map +1 -0
  184. package/dist/public-link-progress.js.map +1 -1
  185. package/dist/public-preview.js.map +1 -1
  186. package/dist/public-web-gateway.js +7 -2
  187. package/dist/public-web-gateway.js.map +1 -1
  188. package/dist/public-web-link.d.ts +35 -1
  189. package/dist/public-web-link.js +142 -43
  190. package/dist/public-web-link.js.map +1 -1
  191. package/dist/quickstart-api.js.map +1 -1
  192. package/dist/quickstart.d.ts +5 -0
  193. package/dist/quickstart.js +25 -16
  194. package/dist/quickstart.js.map +1 -1
  195. package/dist/reply-buttons-controller.js.map +1 -1
  196. package/dist/reply-dedup.js.map +1 -1
  197. package/dist/resource-report.js.map +1 -1
  198. package/dist/restart-guard.js.map +1 -1
  199. package/dist/restart-progress.js.map +1 -1
  200. package/dist/rollout-index.js.map +1 -1
  201. package/dist/runtime-cpu-profile.js.map +1 -1
  202. package/dist/scheduler/db.js.map +1 -1
  203. package/dist/scheduler/scheduler.js.map +1 -1
  204. package/dist/scheduler/task-board-text.js +16 -11
  205. package/dist/scheduler/task-board-text.js.map +1 -1
  206. package/dist/scheduler/task-board.d.ts +39 -11
  207. package/dist/scheduler/task-board.js +123 -45
  208. package/dist/scheduler/task-board.js.map +1 -1
  209. package/dist/scheduler/types.d.ts +1 -1
  210. package/dist/sd-notify.js.map +1 -1
  211. package/dist/search/search-index.js.map +1 -1
  212. package/dist/secret-file.js.map +1 -1
  213. package/dist/secret-store.js.map +1 -1
  214. package/dist/service-installer.d.ts +7 -1
  215. package/dist/service-installer.js +13 -1
  216. package/dist/service-installer.js.map +1 -1
  217. package/dist/service-plan.d.ts +33 -4
  218. package/dist/service-plan.js +177 -8
  219. package/dist/service-plan.js.map +1 -1
  220. package/dist/session-oom.d.ts +21 -0
  221. package/dist/session-oom.js +139 -0
  222. package/dist/session-oom.js.map +1 -0
  223. package/dist/settings-api.js +3 -1
  224. package/dist/settings-api.js.map +1 -1
  225. package/dist/settings-baseline.js.map +1 -1
  226. package/dist/settings-change.d.ts +5 -0
  227. package/dist/settings-change.js +71 -1
  228. package/dist/settings-change.js.map +1 -1
  229. package/dist/settings-confirmation.d.ts +4 -0
  230. package/dist/settings-confirmation.js +2 -0
  231. package/dist/settings-confirmation.js.map +1 -1
  232. package/dist/settings-control.js.map +1 -1
  233. package/dist/settings-effect.js +4 -1
  234. package/dist/settings-effect.js.map +1 -1
  235. package/dist/settings-http-confirmation.js +1 -0
  236. package/dist/settings-http-confirmation.js.map +1 -1
  237. package/dist/settings-instance-patch.js +13 -1
  238. package/dist/settings-instance-patch.js.map +1 -1
  239. package/dist/settings-transaction.js.map +1 -1
  240. package/dist/setup-auth.js.map +1 -1
  241. package/dist/setup-host.d.ts +5 -1
  242. package/dist/setup-host.js +8 -1
  243. package/dist/setup-host.js.map +1 -1
  244. package/dist/setup-wizard.js +5 -2
  245. package/dist/setup-wizard.js.map +1 -1
  246. package/dist/spawn-gate.d.ts +4 -0
  247. package/dist/spawn-gate.js +15 -5
  248. package/dist/spawn-gate.js.map +1 -1
  249. package/dist/start-attempt.d.ts +1 -0
  250. package/dist/start-attempt.js +18 -0
  251. package/dist/start-attempt.js.map +1 -0
  252. package/dist/start-failure-notice.d.ts +4 -0
  253. package/dist/start-failure-notice.js +20 -0
  254. package/dist/start-failure-notice.js.map +1 -0
  255. package/dist/start-failure.d.ts +147 -0
  256. package/dist/start-failure.js +281 -0
  257. package/dist/start-failure.js.map +1 -0
  258. package/dist/startup-memory-deadline.d.ts +23 -0
  259. package/dist/startup-memory-deadline.js +55 -0
  260. package/dist/startup-memory-deadline.js.map +1 -0
  261. package/dist/statusline-watcher.js.map +1 -1
  262. package/dist/storm-window.js.map +1 -1
  263. package/dist/systemd-peer-bus.js.map +1 -1
  264. package/dist/target-queue-worker.js.map +1 -1
  265. package/dist/task-board-summary.js.map +1 -1
  266. package/dist/task-board-tool.js +3 -2
  267. package/dist/task-board-tool.js.map +1 -1
  268. package/dist/task-board-waker.js +8 -4
  269. package/dist/task-board-waker.js.map +1 -1
  270. package/dist/tips.js.map +1 -1
  271. package/dist/tmux-control.js.map +1 -1
  272. package/dist/tmux-exec-helper.js.map +1 -1
  273. package/dist/tmux-manager.d.ts +1 -0
  274. package/dist/tmux-manager.js +2 -1
  275. package/dist/tmux-manager.js.map +1 -1
  276. package/dist/tmux-read.js +1 -1
  277. package/dist/tmux-read.js.map +1 -1
  278. package/dist/tool-permissions.js.map +1 -1
  279. package/dist/tool-progress.js.map +1 -1
  280. package/dist/topic-archiver.js.map +1 -1
  281. package/dist/topic-commands.js.map +1 -1
  282. package/dist/topic-link.js.map +1 -1
  283. package/dist/transcript-delta-reader.js.map +1 -1
  284. package/dist/transcript-jsonl.js.map +1 -1
  285. package/dist/transcript-monitor.js.map +1 -1
  286. package/dist/transcript-sources.js.map +1 -1
  287. package/dist/tunnel/cloudflared-install.js.map +1 -1
  288. package/dist/tunnel/cloudflared.js.map +1 -1
  289. package/dist/tunnel/lease.js +3 -2
  290. package/dist/tunnel/lease.js.map +1 -1
  291. package/dist/tunnel/manager.js.map +1 -1
  292. package/dist/tunnel/public-fetch.js.map +1 -1
  293. package/dist/tunnel/purpose-lane.js.map +1 -1
  294. package/dist/tunnel/types.js.map +1 -1
  295. package/dist/types.d.ts +22 -1
  296. package/dist/ui/chat-render.js +7 -17
  297. package/dist/ui/chat-store.js +171 -61
  298. package/dist/ui/chat-strings.js +9 -4
  299. package/dist/ui/chat-thread.js +48 -1
  300. package/dist/ui/fleet-canvas-layout.js +3 -0
  301. package/dist/ui/fleet-canvas.js +23 -20
  302. package/dist/ui/fleet-office-editor.js +149 -0
  303. package/dist/ui/fleet-office-model.js +358 -0
  304. package/dist/ui/fleet-office-pack.js +199 -0
  305. package/dist/ui/fleet-office-skin.js +474 -0
  306. package/dist/ui/fleet-office-store.js +49 -0
  307. package/dist/ui/fleet-office-zones.js +177 -0
  308. package/dist/ui/fleet-office.js +434 -0
  309. package/dist/ui/fleet-org.js +18 -5
  310. package/dist/ui/guest-refused.html +41 -0
  311. package/dist/ui/image-lightbox.js +9 -3
  312. package/dist/ui/panel-chat.js +35 -22
  313. package/dist/ui/panel-settings.js +24 -7
  314. package/dist/ui/settings-dialogs.js +11 -2
  315. package/dist/ui/settings-model.js +20 -0
  316. package/dist/ui/settings-strings.js +36 -0
  317. package/dist/ui/settings-web-authority.js +7 -2
  318. package/dist/ui/shared/agend-auth.js +2 -0
  319. package/dist/ui/shared/app-i18n.js +2 -2
  320. package/dist/ui/shared/app-prefs.js +2 -0
  321. package/dist/ui/shared/app-shell.js +62 -9
  322. package/dist/ui/shared/app-store.js +1 -0
  323. package/dist/ui/shared/app-stream.js +2 -1
  324. package/dist/ui/shared/app.css +164 -15
  325. package/dist/ui/shared/guest-refused.js +35 -0
  326. package/dist/ui/shared/header-tools.js +2 -2
  327. package/dist/ui/shared/instance-nav.js +25 -8
  328. package/dist/ui/shared/tokens.css +4 -0
  329. package/dist/ui/shared/ui-dialog.js +2 -2
  330. package/dist/ui/shared/usage-dialog.js +152 -6
  331. package/dist/ui/shared/view-strings.js +20 -0
  332. package/dist/ui/shared/web-authorize.js +3 -1
  333. package/dist/update-check.js.map +1 -1
  334. package/dist/update-dispatch.js.map +1 -1
  335. package/dist/update-install.js.map +1 -1
  336. package/dist/update-launch.js.map +1 -1
  337. package/dist/update-marker.js.map +1 -1
  338. package/dist/update-preflight.js.map +1 -1
  339. package/dist/update-progress.d.ts +3 -1
  340. package/dist/update-progress.js +3 -1
  341. package/dist/update-progress.js.map +1 -1
  342. package/dist/usage/format-rich.js.map +1 -1
  343. package/dist/usage/providers.d.ts +8 -0
  344. package/dist/usage/providers.js +6 -2
  345. package/dist/usage/providers.js.map +1 -1
  346. package/dist/usage/statusline-usage.js.map +1 -1
  347. package/dist/usage/usage-api.js +2 -1
  348. package/dist/usage/usage-api.js.map +1 -1
  349. package/dist/usage-report.js.map +1 -1
  350. package/dist/usage-rollup.js +1 -1
  351. package/dist/usage-rollup.js.map +1 -1
  352. package/dist/user-systemd.js.map +1 -1
  353. package/dist/view-api.js +1 -1
  354. package/dist/view-api.js.map +1 -1
  355. package/dist/wake-coordinator.js.map +1 -1
  356. package/dist/web-ambient.js.map +1 -1
  357. package/dist/web-api.d.ts +19 -2
  358. package/dist/web-api.js +180 -28
  359. package/dist/web-api.js.map +1 -1
  360. package/dist/web-app-icon.js.map +1 -1
  361. package/dist/web-appearance-policy.js.map +1 -1
  362. package/dist/web-appearance-tools.js.map +1 -1
  363. package/dist/web-appearance-view.js.map +1 -1
  364. package/dist/web-appearance.js.map +1 -1
  365. package/dist/web-auth.js +5 -4
  366. package/dist/web-auth.js.map +1 -1
  367. package/dist/web-backend-login.js.map +1 -1
  368. package/dist/web-card.js.map +1 -1
  369. package/dist/web-channel-echo.js.map +1 -1
  370. package/dist/web-chat-history.d.ts +41 -11
  371. package/dist/web-chat-history.js +93 -22
  372. package/dist/web-chat-history.js.map +1 -1
  373. package/dist/web-commands.js.map +1 -1
  374. package/dist/web-effect-bundle.d.ts +2 -2
  375. package/dist/web-effect-library.js.map +1 -1
  376. package/dist/web-effect-playback.js.map +1 -1
  377. package/dist/web-effect-profiles.d.ts +16 -16
  378. package/dist/web-effect-profiles.js +3 -2
  379. package/dist/web-effect-profiles.js.map +1 -1
  380. package/dist/web-effect-service.js.map +1 -1
  381. package/dist/web-effect-tools.js.map +1 -1
  382. package/dist/web-effect-trials.js.map +1 -1
  383. package/dist/web-effect-upload.js.map +1 -1
  384. package/dist/web-effect.d.ts +7 -7
  385. package/dist/web-effect.js.map +1 -1
  386. package/dist/web-experimental.js.map +1 -1
  387. package/dist/web-guest-access.js +1 -1
  388. package/dist/web-guest-access.js.map +1 -1
  389. package/dist/web-guest-rooms.js.map +1 -1
  390. package/dist/web-host-guard.js.map +1 -1
  391. package/dist/web-icons.js.map +1 -1
  392. package/dist/web-identity-color.js.map +1 -1
  393. package/dist/web-images.js.map +1 -1
  394. package/dist/web-libavoid.js.map +1 -1
  395. package/dist/web-login.js.map +1 -1
  396. package/dist/web-mentions.js.map +1 -1
  397. package/dist/web-mod-agent.js.map +1 -1
  398. package/dist/web-mod-store.js.map +1 -1
  399. package/dist/web-office-store.d.ts +104 -0
  400. package/dist/web-office-store.js +236 -0
  401. package/dist/web-office-store.js.map +1 -0
  402. package/dist/web-owner-css-store.js.map +1 -1
  403. package/dist/web-owner-css.js.map +1 -1
  404. package/dist/web-png.js.map +1 -1
  405. package/dist/web-prefs.js +2 -0
  406. package/dist/web-prefs.js.map +1 -1
  407. package/dist/web-profile.js.map +1 -1
  408. package/dist/web-reactions.d.ts +2 -1
  409. package/dist/web-request-context.d.ts +5 -0
  410. package/dist/web-request-context.js.map +1 -1
  411. package/dist/web-returning-device.js +1 -1
  412. package/dist/web-returning-device.js.map +1 -1
  413. package/dist/web-schedules.js.map +1 -1
  414. package/dist/web-send-dedupe.d.ts +36 -0
  415. package/dist/web-send-dedupe.js +69 -0
  416. package/dist/web-send-dedupe.js.map +1 -0
  417. package/dist/web-session-authorization.js.map +1 -1
  418. package/dist/web-session.js +1 -1
  419. package/dist/web-session.js.map +1 -1
  420. package/dist/web-settings-patch.d.ts +1 -0
  421. package/dist/web-settings-patch.js +6 -4
  422. package/dist/web-settings-patch.js.map +1 -1
  423. package/dist/web-settings-tools.js.map +1 -1
  424. package/dist/web-terminal-http.js.map +1 -1
  425. package/dist/web-terminal.js.map +1 -1
  426. package/dist/web-theme-store.js.map +1 -1
  427. package/dist/web-upload.d.ts +5 -1
  428. package/dist/web-upload.js +6 -4
  429. package/dist/web-upload.js.map +1 -1
  430. package/dist/web-widget.js.map +1 -1
  431. package/dist/webhook-emitter.js.map +1 -1
  432. package/dist/windows-shortcuts.js +1 -1
  433. package/dist/windows-shortcuts.js.map +1 -1
  434. package/dist/workflow-templates/default.md +1 -1
  435. package/dist/ws-server.js.map +1 -1
  436. package/dist/yaml-shared-nodes.js.map +1 -1
  437. package/launcher/agend-install.cjs +281 -6
  438. package/launcher/private-home.cjs +43 -0
  439. package/package.json +3 -2
  440. package/templates/windows-shortcuts.ps1 +1 -1
@@ -509,27 +509,17 @@
509
509
  /** Whether the chat shows "working" and its Stop button for an instance in this execution state. */
510
510
  function isBusy(state) { return state === "working" || state === "stuck"; }
511
511
 
512
- /** What a key in the composer does: Enter sends, Shift+Enter is a new line, nothing happens mid-IME-composition. */
513
- 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) {
514
517
  if (!ev || ev.key !== "Enter" || ev.isComposing || ev.keyCode === 229) return "none";
518
+ if (opts && opts.touch) return "newline";
515
519
  if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.metaKey) return "newline";
516
520
  return "send";
517
521
  }
518
522
 
519
- /**
520
- * A send that failed: where does its text go? Back into the composer only if that composer is the same
521
- * target's, still on screen and empty; otherwise it is kept aside for that target (never put into another
522
- * target's composer, never over a draft someone is typing) and offered back when that chat is open.
523
- */
524
- function settleFailedSend(target, current, composerPresent, composerValue) {
525
- return target === current && composerPresent && !composerValue ? "restore" : "keep";
526
- }
527
-
528
- /** "Put back" for a kept message: in front of whatever is being typed now, never replacing it. */
529
- function putBack(failedText, draft) {
530
- return draft ? failedText + "\n" + draft : failedText;
531
- }
532
-
533
523
  /** The defaults of the upload limits the server enforces (web-upload.ts UPLOAD_LIMITS), for a page that was given none. */
534
524
  var FILE_LIMITS = { maxFileBytes: 50 * 1024 * 1024, maxFiles: 5, maxTotalBytes: 100 * 1024 * 1024 };
535
525
  /**
@@ -666,7 +656,7 @@
666
656
  return {
667
657
  renderMarkdown: renderMarkdown, escapeHtml: escapeHtml, mergeMessages: mergeMessages, applyReplyButtons: applyReplyButtons, composerKey: composerKey,
668
658
  pollRemaining: pollRemaining,
669
- settleFailedSend: settleFailedSend, putBack: putBack,
659
+
670
660
  FILE_LIMITS: FILE_LIMITS, fileLimits: fileLimits, fileRejection: fileRejection, formatSize: formatSize, checkFiles: checkFiles, attachmentsHtml: attachmentsHtml, isHtmlAttachment: isHtmlAttachment,
671
661
  nextDeliveryState: nextDeliveryState, applyDelivery: applyDelivery, deliveryHtml: deliveryHtml, isBusy: isBusy,
672
662
  applyReactions: applyReactions, reactionsHtml: reactionsHtml,
@@ -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: {}, replies: {}, failedSends: {}, pendingFiles: {}, sending: {}, inFlightFiles: {}, sendFailures: {}, instanceStatus: {}, waking: {},
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
@@ -108,6 +108,8 @@ export function createChatStore(deps) {
108
108
  liveReplies.message(m, extra);
109
109
  const before = s.msgs[m.instance];
110
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");
111
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));
112
114
  if (s.msgs[m.instance] !== before) changed(m.instance, "msgs");
113
115
  }
@@ -166,6 +168,7 @@ export function createChatStore(deps) {
166
168
  // Kept even when the person has moved on: the store is page-lived, and the answer belongs to `name`.
167
169
  for (const m of r.messages) liveReplies.message(m);
168
170
  s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500);
171
+ settleOutgoing(name);
169
172
  changed(name, "msgs");
170
173
  } catch { s.historyRead.delete(name); /* the live stream still works; the next visit tries again */ }
171
174
  void lease;
@@ -242,71 +245,186 @@ export function createChatStore(deps) {
242
245
  }
243
246
  const promptsFor = (instance) => Object.values(s.prompts).filter(p => p.instance === instance);
244
247
 
245
- // ── Sending. Never lose anything: a failed send's files go back to that chat's list and its text back into its own
246
- // composer when that is empty, otherwise it is kept for its chat and offered back (Put back).
247
- async function uploadFile(target, file) {
248
- const r = await deps.fetch(`/ui/upload?instance=${encodeURIComponent(target)}`, {
249
- method: "POST",
250
- headers: { "Content-Type": file.type || "application/octet-stream", "X-Agend-Filename": encodeURIComponent(file.name || "file") },
251
- 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);
252
308
  });
253
- let body = {};
254
- try { body = await r.json(); } catch { /* not JSON */ }
255
- if (!r.ok || !body || typeof body.id !== "string") throw new Error(body && body.error ? `${file.name}: ${body.error}` : `${file.name}: upload failed (${r.status})`);
256
- return body;
257
309
  }
258
- 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) {
259
312
  const txt = (s.drafts[target] || "").trim();
260
- const quote = s.replies[target]; // #2272: the message this one replies to (its row; the server reads the rest)
261
313
  const files = (s.pendingFiles[target] || []).slice();
262
- if ((!txt && !files.length) || !target || s.sending[target]) return;
263
- s.sending[target] = true;
264
- s.waking[target] = s.instanceStatus[target] === "paused";
265
- delete s.sendFailures[target];
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);
266
324
  s.drafts[target] = "";
267
- s.pendingFiles[target] = []; s.inFlightFiles[target] = files;
325
+ s.pendingFiles[target] = [];
326
+ if (quote) delete s.replies[target];
327
+ changed(target, "msgs");
268
328
  changed(target);
269
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);
270
346
  let r;
271
347
  try {
272
- const ids = [];
273
- for (const f of files) ids.push((await uploadFile(target, f)).id);
274
- const body = ids.length ? { instance: target, message: txt, attachments: ids } : { instance: target, message: txt };
275
- if (quote) body.replyTo = { boot: quote.boot, id: quote.id };
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 };
276
363
  r = await api("POST", "/ui/send", body);
277
- } catch (err) { r = { error: err && err.message ? err.message : t("chat.disconnected") }; }
278
- s.sending[target] = false;
279
- delete s.waking[target];
280
- delete s.inFlightFiles[target];
281
- if (r && !r.error) { if (s.replies[target] === quote) delete s.replies[target]; changed(target); return; }
282
- // #2272: the quoted message is gone: the reply goes, the text and files stay (sent again without it, or re-aimed).
283
- if (r && r.reason === "reply_gone" && s.replies[target] === quote) delete s.replies[target];
284
- if (files.length) s.pendingFiles[target] = R().checkFiles(files, s.pendingFiles[target] || []).kept;
285
- if (txt) {
286
- const shown = s.current === target;
287
- if (R().settleFailedSend(target, s.current, shown, shown ? (s.drafts[target] || "") : "") === "restore") s.drafts[target] = txt;
288
- else s.failedSends[target] = s.failedSends[target] ? `${s.failedSends[target]}\n${txt}` : txt;
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") };
289
370
  }
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; }
290
383
  const why = r && r.reason;
291
384
  const key = typeof why === "string" ? `chat.sendFailure_${why}` : "";
292
385
  const localized = key ? t(key) : "";
293
- const error = localized && localized !== key ? localized : (r && r.error ? r.error : t("chat.disconnected"));
294
- s.sendFailures[target] = { error, text: txt, files };
295
- changed(target);
296
- deps.toast(error, false);
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);
297
407
  }
298
- function canRetrySend(target) {
299
- const failed = s.sendFailures[target], files = s.pendingFiles[target] || [];
300
- return !!failed && !s.sending[target] && !s.failedSends[target]
301
- && (s.drafts[target] || "").trim() === failed.text
302
- && files.length === failed.files.length && files.every((f, i) => f === failed.files[i]);
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);
303
423
  }
304
- function retrySend(target) { if (canRetrySend(target)) return send(target); }
305
424
  function addFiles(target, list) {
306
425
  if (!target) return;
307
- const inFlight = s.inFlightFiles[target] || [];
308
- const res = R().checkFiles(inFlight.concat(s.pendingFiles[target] || []), [...(list || [])]);
309
- s.pendingFiles[target] = res.kept.slice(inFlight.length);
426
+ const res = R().checkFiles(s.pendingFiles[target] || [], [...(list || [])]);
427
+ s.pendingFiles[target] = res.kept;
310
428
  for (const x of res.rejected) { const why = R().fileRejection(x.reason); deps.toast(`${x.name}: ${t(why.key, ...why.values)}`, false); }
311
429
  changed(target);
312
430
  }
@@ -316,7 +434,7 @@ export function createChatStore(deps) {
316
434
  if (!target) return false;
317
435
  const stamp = new Date().toISOString().slice(0, 19).replace(/[-:T]/g, "");
318
436
  const f = new File([text], `pasted-${stamp}.txt`, { type: "text/plain" });
319
- const taken = (s.inFlightFiles[target] || []).concat(s.pendingFiles[target] || []);
437
+ const taken = s.pendingFiles[target] || [];
320
438
  if (R().checkFiles(taken, [f]).rejected.length) { deps.toast(t("chat.pasteKeptAsText")); return false; }
321
439
  pastedTexts.set(f, text);
322
440
  addFiles(target, [f]);
@@ -333,16 +451,8 @@ export function createChatStore(deps) {
333
451
  changed(target);
334
452
  }
335
453
  const isPasted = (f) => pastedTexts.has(f);
336
- function putBack(target) {
337
- const kept = s.failedSends[target];
338
- if (!kept) return;
339
- s.drafts[target] = R().putBack(kept, s.drafts[target] || "");
340
- delete s.failedSends[target];
341
- changed(target);
342
- }
343
- function discardFailed(target) { delete s.failedSends[target]; changed(target); }
344
- /** Typing stays local, except a failed send's Retry button must follow edits too. */
345
- function setDraft(target, value) { s.drafts[target] = value; if (s.sendFailures[target]) changed(target); }
454
+ /** Typing stays local. */
455
+ function setDraft(target, value) { s.drafts[target] = value; }
346
456
  /**
347
457
  * #2272: reply to a message of this chat — what the bar above the composer shows (its sender and first words); only
348
458
  * its row is sent. One at a time per chat: a new one replaces it.
@@ -363,7 +473,7 @@ export function createChatStore(deps) {
363
473
  if (has()) return true;
364
474
  try {
365
475
  const r = await api("GET", `/ui/history?instance=${encodeURIComponent(name)}&limit=500`);
366
- if (r && Array.isArray(r.messages)) { s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500); changed(name, "msgs"); }
476
+ if (r && Array.isArray(r.messages)) { s.msgs[name] = R().mergeMessages(s.msgs[name], r.messages, 500); settleOutgoing(name); changed(name, "msgs"); }
367
477
  } catch { /* the jump says it is not there */ }
368
478
  return has();
369
479
  }
@@ -461,7 +571,7 @@ export function createChatStore(deps) {
461
571
  attach, applyStatus, applyActivity, ingest, applyDeliveries, openHistory,
462
572
  onPrompt, applyPrompts, resolvePrompt, answerPrompt, answerByNonce, holdPrompt, releasePrompt, promptsFor,
463
573
  applyReplyButtons, clickReplyButton,
464
- uploadFile, send, canRetrySend, retrySend, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, putBack, discardFailed, setDraft,
574
+ uploadFile, send, retryOutgoing, deleteOutgoing, putBackOutgoing, threadRows, addFiles, removeFile, attachPastedText, fileBackAsText, isPasted, setDraft,
465
575
  startReply, clearReply, findQuoted,
466
576
  cancelReply, isUser, runCommand, dismissCommand, commandFree,
467
577
  };
@@ -4,6 +4,9 @@ import { register } from "/assets/app-i18n.js";
4
4
  register("chat", {
5
5
  en: {
6
6
  waking: "Waking {0}…", retrySend: "Retry send",
7
+ // A message being sent (its own bubble at once) and its files' uploads.
8
+ you: "You", sending: "Sending…", uploadingFile: "Uploading {0}", uploadWaiting: "waiting", uploadFailed: "failed",
9
+ uploadNoConnection: "the upload was cut off — check the connection", sendNoConnection: "it did not reach AgEnD — check the connection", notSentBecause: "Not sent: {0}", deleteUnsent: "Delete",
7
10
  sendFailure_wake_capacity: "No slot is free to wake this instance. Retry when another instance is idle.",
8
11
  sendFailure_wake_credentials: "The instance could not sign in. Check its account on the host, then retry.",
9
12
  sendFailure_wake_timeout: "The instance did not become ready in time. Retry, or check its status on the host.",
@@ -53,7 +56,7 @@ register("chat", {
53
56
  a11yWorking: "{0} started working", a11yIdle: "{0} finished", a11yReplied: "{0} replied", conversationWith: "Conversation with {0}",
54
57
  stopping: "Stopping {0}…", awaiting: "{0} is waiting for your input", approxNote: "Read from the terminal, so approximate", elapsedTitle: "Time since this page saw it start (approximate)",
55
58
  dropHere: "Drop files to attach them", asText: "As text", pasteAttached: "A long paste ({0} characters) was attached as a text file", pasteKeptAsText: "No room for another attachment: the long paste stays as text",
56
- send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", guestMore: "Esc stops the reply",
59
+ send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerHintTouch: "Return adds a line · tap Send to send", composerMore: "/ for commands · Esc stops the reply", guestMore: "Esc stops the reply",
57
60
  guestNote: "You're a guest: the agent sees your messages as a guest's (recognition only).", guestNoCommands: "Guests can't run commands — send it without the leading /.", attachTitle: "Attach files (any type — or paste / drop them)",
58
61
  pvPreview: "Preview", pvStop: "Stop", pvDownload: "Download", pvMenu: "Preview options", pvHtml: "HTML",
59
62
  pvAllowButton: "Allow previews", pvAllow: "Allow HTML previews — they may be able to send data out", pvDisallow: "Stop allowing HTML previews",
@@ -110,7 +113,7 @@ register("chat", {
110
113
  disconnected: "disconnected", tick_queued: "Waiting behind another message", tick_processing: "Handed to the agent", tick_delivered: "The agent has it", tick_failed: "Not delivered", tick_cancelled: "Dropped by Stop", reactedBy: "{0} reacted",
111
114
  promptAnswering: "Answering…", promptGone: "This prompt is no longer open", promptFor: "{0}: {1}",
112
115
  working: "{0} is working…", stuck: "{0} looks stuck", stopReply: "Stop reply", stopReplyTitle: "Stop the current reply (like Esc) and drop the messages still waiting. The instance keeps running.", stopSent: "Stopped",
113
- file_empty: "empty file", file_too_large: "over {0} MB", file_too_large_public: "over {0} MB — the most the public link carries in one upload", file_too_many: "at most {0} files per message", file_too_much: "the files together would be over {0} MB", removeFile: "Remove", notSent: "Not sent:", putBack: "Put back", discard: "Discard",
116
+ file_empty: "empty file", file_too_large: "over {0} MB", file_too_large_public: "over {0} MB — the most the public link carries in one upload", file_too_many: "at most {0} files per message", file_too_much: "the files together would be over {0} MB", removeFile: "Remove", putBack: "Put back",
114
117
  noMessages: "Type a task below.",
115
118
  pick: "Choose an agent to start chatting.", noAgents: "Create an agent to start chatting.", newAgent: "New agent", notFound: "No instance called “{0}”", notFoundHint: "It may have been deleted or renamed.", backToList: "Choose an agent",
116
119
  details: "Instance details", start: "Start instance", stopInstance: "Stop instance", restart: "Restart instance", delete: "Delete instance…",
@@ -143,6 +146,8 @@ register("chat", {
143
146
  },
144
147
  "zh-TW": {
145
148
  waking: "正在喚醒 {0}…", retrySend: "重試傳送",
149
+ you: "你", sending: "傳送中…", uploadingFile: "正在上傳 {0}", uploadWaiting: "等待中", uploadFailed: "失敗",
150
+ uploadNoConnection: "上傳中斷了,請檢查網路連線", sendNoConnection: "沒有送到 AgEnD,請檢查網路連線", notSentBecause: "沒有送出:{0}", deleteUnsent: "刪除",
146
151
  sendFailure_wake_capacity: "目前沒有空位可喚醒這個實例。請等其他實例閒置後重試。",
147
152
  sendFailure_wake_credentials: "實例無法登入。請先在主機檢查它的帳號,再重試。",
148
153
  sendFailure_wake_timeout: "實例未能及時就緒。請重試,或在主機檢查它的狀態。",
@@ -192,7 +197,7 @@ register("chat", {
192
197
  a11yWorking: "{0} 開始工作", a11yIdle: "{0} 已完成", a11yReplied: "{0} 已回覆", conversationWith: "與 {0} 的對話",
193
198
  stopping: "正在中止 {0}…", awaiting: "{0} 正在等你回應", approxNote: "從終端畫面判讀,僅供參考", elapsedTitle: "從這個頁面看到它開始工作起算(約略)",
194
199
  dropHere: "放開即可附加檔案", asText: "改為文字", pasteAttached: "貼上的長文字({0} 個字元)已改為文字檔附加", pasteKeptAsText: "附件已滿,貼上的長文字保留為一般文字",
195
- send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", guestMore: "Esc 中止回覆",
200
+ send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerHintTouch: "Return 換行 · 按「送出」傳送", composerMore: "/ 叫出指令 · Esc 中止回覆", guestMore: "Esc 中止回覆",
196
201
  guestNote: "你是訪客:agent 會把你的訊息視為訪客的訊息(僅供辨識)。", guestNoCommands: "訪客不能執行指令,請去掉開頭的 / 再送出。", attachTitle: "附加檔案(任何類型,也可以貼上或拖曳)",
197
202
  pvPreview: "預覽", pvStop: "停止", pvDownload: "下載", pvMenu: "預覽選項", pvHtml: "HTML",
198
203
  pvAllowButton: "允許預覽", pvAllow: "允許預覽 HTML(預覽可能把資料傳出去)", pvDisallow: "不再允許預覽 HTML",
@@ -247,7 +252,7 @@ register("chat", {
247
252
  disconnected: "已斷線", tick_queued: "排在前一則訊息之後", tick_processing: "已交給 agent", tick_delivered: "agent 已收到", tick_failed: "未送達", tick_cancelled: "已被中止取消", reactedBy: "{0} 的回應",
248
253
  promptAnswering: "回覆中…", promptGone: "這個提示已經結束", promptFor: "{0}:{1}",
249
254
  working: "{0} 處理中…", stuck: "{0} 似乎卡住了", stopReply: "中止回覆", stopReplyTitle: "中止目前的回覆(同 Esc),並取消還在排隊的訊息。instance 會繼續運行。", stopSent: "已中止",
250
- file_empty: "空檔案", file_too_large: "超過 {0} MB", file_too_large_public: "超過 {0} MB,這是公開連結單次上傳的上限", file_too_many: "每則訊息最多 {0} 個檔案", file_too_much: "檔案合計會超過 {0} MB", removeFile: "移除", notSent: "未送出:", putBack: "放回輸入框", discard: "捨棄",
255
+ file_empty: "空檔案", file_too_large: "超過 {0} MB", file_too_large_public: "超過 {0} MB,這是公開連結單次上傳的上限", file_too_many: "每則訊息最多 {0} 個檔案", file_too_much: "檔案合計會超過 {0} MB", removeFile: "移除", putBack: "放回輸入框",
251
256
  noMessages: "直接在下方輸入任務。",
252
257
  pick: "選一個 Agent 開始聊天。", noAgents: "先建立一個 Agent,再開始聊天。", newAgent: "新增 Agent", notFound: "沒有叫「{0}」的 instance", notFoundHint: "它可能已被刪除或改名。", backToList: "選擇 Agent",
253
258
  details: "Instance 詳細資料", start: "啟動 instance", stopInstance: "停止 instance", restart: "重新啟動 instance", delete: "刪除 instance…",
@@ -132,6 +132,7 @@ export function createThread(list, scroller, opts) {
132
132
  // #1770: the label ("17:38", "Yesterday 17:38", "10/11 17:38") depends on today, so it is not part of the html a node
133
133
  // is kept by: it is written into the node (timeLabels) — crossing midnight relabels in place and reloads no preview.
134
134
  const time = x.ts ? `<span class="time" data-ts="${escAttr(x.ts)}" title="${escAttr(chatTimeFull(x.ts))}"></span>` : `<span class="time"></span>`;
135
+ if (x.outgoing) return outgoingHtml(x);
135
136
  const trial = readCanvasTrialMessage(x);
136
137
  if (trial) return canvasWorkerEnabled() ? trialHtml(trial, time, x) : "";
137
138
  if (x.role === "status") return statusHtml(x, time);
@@ -147,6 +148,48 @@ export function createThread(list, scroller, opts) {
147
148
  const act = opts.canReply?.() === false ? "" : `<div data-trust="thread-control" class="msg-actions"><button type="button" class="chip-btn icon-only" data-act="reply" data-arg="${escAttr(key)}" title="${escAttr(tr("chat.reply"))}" aria-label="${escAttr(tr("chat.reply"))}">${REPLY}</button></div>`;
148
149
  return `<div class="msg ${u ? "user" : "agent"}" data-key="${escAttr(key)}" tabindex="-1">${act}<div class="meta"><span class="sender">${escAttr(x.sender)}</span>${time}${ticks}</div>${quoteHtml(x.replyTo)}<div class="body">${clicked ? clickHtml(x.click) : `<div class="md">${renderedMarkdown(x.text, x.role === "agent", opts.emojis, opts.mentions)}</div>`}${R().attachmentsHtml(x.attachments, { htmlCards: x.role === "agent", goneTitle: tr("chat.attGone"), goneLabel: tr("chat.attUnavailable"), inlinePreviews: !PUBLIC_LINK() })}${buttons}</div>${R().reactionsHtml(x.reactions, tr("chat.reactedBy", x.instance || ""))}${tools}</div>`;
149
150
  }
151
+ /**
152
+ * A message this page is sending (chat-store.js): its own bubble at once, dimmed with "Sending…" until the server
153
+ * takes it, then as sent until the server's copy replaces it; failed: marked, with Retry and Delete. Each file shows
154
+ * its upload — a picture as itself (a local preview), anything else by name — with its own bar, moved in place by
155
+ * progress() (the html does not hold the numbers, so a moving bar never redraws the bubble). No data-key: it is no
156
+ * message yet (nothing replies to it, links to it or copies it).
157
+ */
158
+ function outgoingHtml(x) {
159
+ const st = x.outgoing.state, going = st === "queued" || st === "sending";
160
+ const files = (x.files || []).map((f, i) => {
161
+ const pic = f.url ? `<img class="out-pic" src="${escAttr(f.url)}" alt="${escAttr(f.name)}">` : `<span class="out-doc" aria-hidden="true"></span>`;
162
+ return `<div class="out-file" data-i="${escAttr(i)}">${pic}<div class="out-file-info"><span class="out-name">${escAttr(f.name)}</span>`
163
+ + `<span class="out-bar" role="progressbar" aria-label="${escAttr(trf("chat.uploadingFile", f.name))}" aria-valuemin="0" aria-valuemax="100"><span class="out-fill"></span></span>`
164
+ + `<span class="out-pct"></span></div></div>`;
165
+ }).join("");
166
+ const failed = st === "failed" ? `<div data-trust="thread-control" class="out-note lbar" role="alert"><span class="out-error">${escAttr(trf("chat.notSentBecause", x.outgoing.error || ""))}</span>`
167
+ + `<span class="out-actions"><button type="button" class="btn btn-sm btn-primary" data-act="retryOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.retrySend"))}</button>`
168
+ + `<button type="button" class="btn btn-sm" data-act="putBackOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.putBack"))}</button>`
169
+ + `<button type="button" class="btn btn-sm" data-act="deleteOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.deleteUnsent"))}</button></span></div>` : "";
170
+ const state = going ? `<span class="out-state" role="status"><span class="out-spin" aria-hidden="true"></span>${escAttr(tr("chat.sending"))}</span>` : "";
171
+ return `<div class="msg user out out-${st}" data-out="${escAttr(x.clientId)}"${going ? ' aria-busy="true"' : ""} tabindex="-1"><div class="meta"><span class="sender">${escAttr(x.sender)}</span>${state}</div>`
172
+ + `${quoteHtml(x.replyTo)}<div class="body">${x.text ? `<div class="md">${renderedMarkdown(x.text, false, opts.emojis, opts.mentions)}</div>` : ""}`
173
+ + `${files ? `<div class="out-files">${files}</div>` : ""}</div>${failed}</div>`;
174
+ }
175
+ /** The pending bubbles' upload bars and percentages, as their files stand now (in place: CSSOM, no redraw). */
176
+ function progress() {
177
+ for (const v of nodes.values()) {
178
+ const x = v.x;
179
+ if (!x || !x.outgoing || !x.files || !x.files.length) continue;
180
+ for (const el of v.node.querySelectorAll(".out-file[data-i]")) {
181
+ const f = x.files[Number(el.dataset.i)];
182
+ if (!f) continue;
183
+ const pct = f.state === "done" ? 100 : f.total > 0 ? Math.min(100, Math.floor((f.loaded / f.total) * 100)) : 0;
184
+ el.dataset.state = f.state;
185
+ const bar = el.querySelector(".out-bar"), fill = el.querySelector(".out-fill"), label = el.querySelector(".out-pct");
186
+ if (bar) bar.setAttribute("aria-valuenow", String(pct));
187
+ if (fill) fill.style.transform = `scaleX(${pct / 100})`;
188
+ if (label) { const text = f.state === "failed" ? tr("chat.uploadFailed") : f.state === "waiting" ? tr("chat.uploadWaiting") : `${pct}%`; if (label.textContent !== text) label.textContent = text; }
189
+ }
190
+ }
191
+ }
192
+
150
193
  /**
151
194
  * #2272: what a reply quotes, above its body — the quoted sender and first words (or that it had files), as the server
152
195
  * read them when it was sent; a button that shows that message. A guest that may not see it gets only "an earlier
@@ -678,6 +721,7 @@ export function createThread(list, scroller, opts) {
678
721
  armDay();
679
722
  armPolls();
680
723
  rove();
724
+ progress();
681
725
  return added;
682
726
  }
683
727
  /**
@@ -777,6 +821,9 @@ export function createThread(list, scroller, opts) {
777
821
  } else if (act === "showButtons") { const x = msgs.find(y => y.buttons && y.buttons.id === el.dataset.arg); if (x) reveal(msgKey(x)); }
778
822
  else if (act === "reply") { const x = msgs.find(y => msgKey(y) === el.dataset.arg); if (x) opts.startReply?.(x); }
779
823
  else if (act === "jumpQuote") void jumpTo(el.dataset.arg);
824
+ else if (act === "retryOut") opts.retryOutgoing?.(el.dataset.arg); // a failed send, again
825
+ else if (act === "putBackOut") opts.putBackOutgoing?.(el.dataset.arg); // back in the composer, to change it
826
+ else if (act === "deleteOut") opts.deleteOutgoing?.(el.dataset.arg);
780
827
  }
781
828
  /**
782
829
  * #2272: show the message a reply quotes, flashed — drawn now, or after the chat reads back the longest history the
@@ -834,5 +881,5 @@ export function createThread(list, scroller, opts) {
834
881
  armPolls();
835
882
  }
836
883
 
837
- return { render, onScroll, onClick, jumpLatest, reveal, jumpTo, messageOf, rove, refreshCards, park, attach, dispose, nodes, get unseen() { return unseen; } };
884
+ return { render, progress, onScroll, onClick, jumpLatest, reveal, jumpTo, messageOf, rove, refreshCards, park, attach, dispose, nodes, get unseen() { return unseen; } };
838
885
  }
@@ -10,6 +10,9 @@
10
10
  */
11
11
  export const OC = { NW: 184, NH: 56, GAP: 28, PAD: 32, GAP_NARROW: 18, PAD_NARROW: 24, HEAD: 34, TEAM_GAP: 72, ROW_GAP: 96, ROW_MAX: 1400, ROW_MAX_NARROW: 460, LW: 232, LH: 68, TOP_Y: 20, BUS: 44, FOLDED: 64, TRUNK: -30 };
12
12
  const colsFor = (n, narrow) => (n <= 1 ? 1 : n <= 4 || narrow ? 2 : 3);
13
+ /** A search's needle, and whether a node matches it (its name or nickname): the canvas's Find, the Office view's too. */
14
+ export const needle = (q) => String(q || "").trim().toLowerCase();
15
+ export const hits = (node, q) => !!q && (node.name.toLowerCase().includes(q) || (!!node.i && typeof node.i.display_name === "string" && node.i.display_name.toLowerCase().includes(q)));
13
16
  /** The key a team folds by (per device): its name; the instances in no team have one of their own no team name can be. */
14
17
  export const REST_KEY = "\u0000rest";
15
18