@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
@@ -3,8 +3,14 @@
3
3
  // stream, every POLL_MS while the page is shown — the user's decision 1). Ids are opaque; there is never any text.
4
4
  //
5
5
  // - The store keeps each message's latest state: queued (a dotted line), delivering (a dash and a pulse), delivered
6
- // (the line stays FADE_MS while it fades, then goes); consumed, failed, uncertain or cancelled ends it at once (a
7
- // failed or uncertain delivery is Needs you's to show, #1386). A finished message never comes back.
6
+ // or consumed (the line stays FADE_MS while it fades, then goes — long enough to see: an MCP send is delivered and
7
+ // read almost at once); failed, uncertain or cancelled ends it at once (a failed or uncertain delivery is Needs you's
8
+ // to show, #1386). A finished message never comes back.
9
+ // - And the work (the server's Delegations, stream event `delegation`, the snapshot's `delegations`): a task's
10
+ // delegator → worker line stays, "in progress" (a moving dash), until its report comes back; then a short return
11
+ // pulse worker → delegator (RETURN_MS), and it goes. One that waits past DELEGATION_TTL_MS fades as "no report yet".
12
+ // - Every line ends on something drawn: a card, or a folded team's box (a name not on the canvas draws nothing), and
13
+ // runs through lanes that touch the boxes it joins — two cards in one box are joined inside it.
8
14
  // - Edges are routed metro-style through the lanes the layout keeps empty (beside the columns and between card rows
9
15
  // inside a box; the bus above each row of teams; the trunk at the left), so a line never crosses a card.
10
16
  // - Near: one edge per (from, to, kind, phase), ×N when several share it. Far (zoomed out): one edge per team pair.
@@ -12,14 +18,20 @@
12
18
  // recipients, or far, it is one edge per recipient team, ×N (the user's decision 3). At most MAX_EDGES are drawn.
13
19
  import { OC } from "./fleet-canvas-layout.js";
14
20
 
15
- export const FADE_MS = 3000;
21
+ export const FADE_MS = 12000;
22
+ export const RETURN_MS = 2400;
23
+ /** As the server's (src/fleet-traffic.ts): how long a delegation waits for its report. */
24
+ export const DELEGATION_TTL_MS = 4 * 3_600_000;
16
25
  export const POLL_MS = 5000;
17
26
  export const MAX_EDGES = 60;
18
27
  export const BUNDLE_AT = 8;
19
28
  export const FAR_K = 0.55;
20
- const MAX_ITEMS = 400, MAX_DONE = 2000;
29
+ const MAX_ITEMS = 400, MAX_DONE = 2000, MAX_WORK = 200;
30
+ const DSTATES = new Set(["open", "done", "expired", "cancelled"]);
21
31
  const LIVE = new Set(["queued", "delivering"]);
22
- const ENDS = new Set(["consumed", "failed", "uncertain", "cancelled"]);
32
+ const ENDS = new Set(["failed", "uncertain", "cancelled"]);
33
+ /** Delivered, or read: the line fades out (FADE_MS). */
34
+ const SETTLED = new Set(["delivered", "consumed"]);
23
35
  const KINDS = new Set(["task", "report", "query", "update", "send", "broadcast"]);
24
36
  const mono = () => (typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now());
25
37
 
@@ -28,55 +40,118 @@ export function itemOk(x) {
28
40
  if (!x || typeof x !== "object") return null;
29
41
  const { id, from, to, kind, state, op } = x;
30
42
  if (typeof id !== "string" || !/^[0-9a-f]{16}$/.test(id) || typeof from !== "string" || !from || typeof to !== "string" || !to) return null;
31
- if (!KINDS.has(kind) || !(LIVE.has(state) || state === "delivered" || ENDS.has(state))) return null;
43
+ if (!KINDS.has(kind) || !(LIVE.has(state) || SETTLED.has(state) || ENDS.has(state))) return null;
32
44
  return { id, from, to, kind, state, ...(typeof op === "string" && /^[0-9a-f]{16}$/.test(op) ? { op } : {}) };
33
45
  }
34
46
 
47
+ /** A delegation as the server sends it, checked; null for anything else. */
48
+ export function delegationOk(x) {
49
+ if (!x || typeof x !== "object") return null;
50
+ const { id, from, to, state, age_ms } = x;
51
+ if (typeof id !== "string" || !/^[0-9a-f]{16}$/.test(id) || typeof from !== "string" || !from || typeof to !== "string" || !to || from === to) return null;
52
+ if (!DSTATES.has(state) || typeof age_ms !== "number" || !Number.isFinite(age_ms) || age_ms < 0) return null;
53
+ return { id, from, to, state, age: age_ms };
54
+ }
55
+
35
56
  /**
36
- * The page's traffic: `apply(item, nowMs)` one stream item, `load(snapshot, nowMs)` a snapshot, `sweep(nowMs)` drops
37
- * faded lines; each returns whether anything shown changed. `items()` what to draw: { …item, fading }.
57
+ * The page's traffic: `apply(item, nowMs)` one stream item, `delegation(d, nowMs)` one delegation event,
58
+ * `load(snapshot, nowMs, since)` a snapshot, `sweep(nowMs)` drops faded lines; each returns whether anything shown
59
+ * changed. `items()` what to draw: { …item, fading }. `streamRev()` counts the stream's events: a snapshot asked for
60
+ * before the latest one (`since` older than it) may be older than what the stream already said, so it adds only what
61
+ * is new and ends or rewinds nothing (#2135 review).
62
+ * Ended work is remembered (bounded): a late snapshot never draws a delegation that is done, expired or taken back.
38
63
  */
39
64
  export function createTrafficStore() {
40
65
  const items = new Map(); // id → { item, fadeAt }
41
- const done = new Set(); // ids that ended: a late item for one is ignored
66
+ const work = new Map(); // delegation id → { d, phase: "work" | "return" | "stale", until }
67
+ const done = new Set(); // message ids that ended: a late item for one is ignored
68
+ const ended = new Set(); // delegation ids that ended: never drawn in progress again
69
+ let rev = 0;
42
70
  const finish = (id) => {
43
71
  items.delete(id);
44
72
  done.add(id);
45
73
  if (done.size > MAX_DONE) done.delete(done.values().next().value);
46
74
  };
47
- const store = {
48
- apply(raw, now = mono()) {
49
- const it = itemOk(raw);
50
- if (!it || done.has(it.id)) return false;
51
- const prev = items.get(it.id);
52
- if (ENDS.has(it.state)) { const had = !!prev; finish(it.id); return had; }
53
- if (prev && prev.item.state === "delivered") return false; // delivered never goes back
54
- if (prev && prev.item.state === it.state) return false;
55
- items.delete(it.id);
56
- items.set(it.id, { item: it, fadeAt: it.state === "delivered" ? now + FADE_MS : null });
57
- while (items.size > MAX_ITEMS) items.delete(items.keys().next().value);
75
+ const end = (id) => {
76
+ ended.add(id);
77
+ if (ended.size > MAX_DONE) ended.delete(ended.values().next().value);
78
+ };
79
+ const applyItem = (raw, now) => {
80
+ const it = itemOk(raw);
81
+ if (!it || done.has(it.id)) return false;
82
+ const prev = items.get(it.id);
83
+ if (ENDS.has(it.state)) { const had = !!prev; finish(it.id); return had; }
84
+ if (prev && prev.fadeAt !== null) return false; // fading: never goes back, nor starts again
85
+ if (prev && prev.item.state === it.state) return false;
86
+ if (it.state === "consumed" && !prev) { finish(it.id); return false; } // read before this page drew it
87
+ items.delete(it.id);
88
+ // A message keeps what it was (from, to, kind) through its states; only the state moves.
89
+ items.set(it.id, { item: prev ? { ...prev.item, state: it.state } : it, fadeAt: SETTLED.has(it.state) ? now + FADE_MS : null });
90
+ while (items.size > MAX_ITEMS) items.delete(items.keys().next().value);
91
+ return true;
92
+ };
93
+ const applyDelegation = (raw, now) => {
94
+ const d = delegationOk(raw);
95
+ if (!d || ended.has(d.id)) return false;
96
+ const prev = work.get(d.id);
97
+ if (d.state === "open") {
98
+ if (prev) return false;
99
+ work.set(d.id, { d, phase: "work", until: now + Math.max(0, DELEGATION_TTL_MS - d.age) });
100
+ while (work.size > MAX_WORK) { const oldest = work.keys().next().value; work.delete(oldest); end(oldest); }
58
101
  return true;
59
- },
60
- /** A snapshot is the truth for what is in flight: anything live it no longer lists has ended (missed while away). */
61
- load(snap, now = mono()) {
102
+ }
103
+ end(d.id);
104
+ if (d.state === "cancelled") return work.delete(d.id);
105
+ if (d.state === "done") { work.set(d.id, { d, phase: "return", until: now + RETURN_MS }); return true; }
106
+ if (!prev || prev.phase !== "work") return false; // expired: only one this page shows
107
+ work.set(d.id, { d, phase: "stale", until: now + FADE_MS });
108
+ return true;
109
+ };
110
+ const store = {
111
+ apply(raw, now = mono()) { rev++; return applyItem(raw, now); },
112
+ delegation(raw, now = mono()) { rev++; return applyDelegation(raw, now); },
113
+ streamRev: () => rev,
114
+ /**
115
+ * A snapshot is the truth for what is in flight: anything live it no longer lists has ended (missed while away) —
116
+ * unless the stream has spoken since it was asked for (`since`): then it may be older than what is drawn, and only
117
+ * what is new to this page is taken from it.
118
+ */
119
+ load(snap, now = mono(), since = rev) {
120
+ const fresh = since === rev;
62
121
  const list = snap && Array.isArray(snap.items) ? snap.items.map(itemOk).filter(Boolean) : [];
63
122
  const listed = new Set(list.map((i) => i.id));
64
123
  let changed = false;
65
- for (const [id, e] of items) if (!listed.has(id) && LIVE.has(e.item.state)) { finish(id); changed = true; }
66
- // Delivered, for a line drawn here: it fades now (with no stream, the snapshot is the only news of it). Any other
67
- // finished message is not drawn (it finished up to two minutes ago), and an ended one goes.
124
+ if (fresh) for (const [id, e] of items) if (!listed.has(id) && LIVE.has(e.item.state)) { finish(id); changed = true; }
125
+ // Delivered or read, for a line drawn here: it fades now (with no stream, the snapshot is the only news of it). Any
126
+ // other finished message is not drawn (it finished up to two minutes ago), and an ended one goes.
68
127
  for (const it of list) {
69
- if (LIVE.has(it.state) || (it.state === "delivered" && items.has(it.id))) changed = store.apply(it, now) || changed;
70
- else { const had = items.has(it.id); finish(it.id); changed = changed || had; }
128
+ if (!fresh && items.has(it.id)) continue; // the stream knows it better
129
+ if (LIVE.has(it.state) || (SETTLED.has(it.state) && items.has(it.id))) changed = applyItem(it, now) || changed;
130
+ else if (fresh) { const had = items.has(it.id); finish(it.id); changed = changed || had; }
71
131
  }
132
+ // The open delegations: the snapshot's list is the truth (one it no longer lists ended while this page was away).
133
+ const open = snap && Array.isArray(snap.delegations) ? snap.delegations.filter((d) => delegationOk(d)?.state === "open") : [];
134
+ const ids = new Set(open.map((d) => d.id));
135
+ if (fresh) for (const [id, e] of work) if (e.phase === "work" && !ids.has(id)) { work.delete(id); end(id); changed = true; }
136
+ for (const d of open) changed = applyDelegation(d, now) || changed;
72
137
  return changed;
73
138
  },
74
139
  sweep(now = mono()) {
75
140
  let changed = false;
76
141
  for (const [id, e] of items) if (e.fadeAt !== null && now >= e.fadeAt) { finish(id); changed = true; }
142
+ for (const [id, e] of work) {
143
+ if (now < e.until) continue;
144
+ if (e.phase === "work") { end(id); work.set(id, { ...e, phase: "stale", until: now + FADE_MS }); } // its wait ran out here
145
+ else work.delete(id);
146
+ changed = true;
147
+ }
77
148
  return changed;
78
149
  },
79
- items: () => [...items.values()].map((e) => ({ ...e.item, fading: e.fadeAt !== null })),
150
+ /** What to draw: the messages, then the work (a done one is its return, worker → delegator). */
151
+ items: () => [...[...items.values()].map((e) => ({ ...e.item, fading: e.fadeAt !== null })),
152
+ ...[...work.values()].map(({ d, phase }) => phase === "return"
153
+ ? { id: `r${d.id}`, from: d.to, to: d.from, kind: "report", state: "delivering", phase, fading: false }
154
+ : { id: `w${d.id}`, from: d.from, to: d.to, kind: "task", state: "delivering", phase, fading: false })],
80
155
  fadingCount: () => [...items.values()].filter((e) => e.fadeAt !== null).length,
81
156
  };
82
157
  return store;
@@ -103,6 +178,18 @@ function endOf(L, name) {
103
178
 
104
179
  // ── Routing: lanes the layout keeps empty ──
105
180
 
181
+ /**
182
+ * A side lane's offset from the middle of the box's padding: at least 6 px from the box's border and from its cards.
183
+ * Negative: toward the border, on either side (#2329) — the first lines (o < 0) leave the cards a long last run for a head.
184
+ */
185
+ const laneOffset = (o, PAD = OC.PAD) => Math.max(-PAD / 2 + 6, Math.min(PAD / 2 - 6, o * 0.5));
186
+ const leftLaneOf = (b, io, PAD) => b.x + PAD / 2 + io;
187
+ const rightLaneOf = (b, io, PAD) => b.x + b.w - PAD / 2 - io;
188
+ /** Where a line meets a card's side, from the side's middle (#2329): its full offset, a head's width from its neighbours. */
189
+ const sideOffset = (o) => Math.max(-OC.NH / 2 + 7, Math.min(OC.NH / 2 - 7, o));
190
+ /** The layout's own box padding and gutter (a phone's are narrower). */
191
+ const spacing = (L) => ({ PAD: L.pad ?? OC.PAD, GAP: L.gap ?? OC.GAP });
192
+
106
193
  const firstBusOf = (L) => Math.min(...L.boxes.map((b) => b.busY));
107
194
  /** From an end up to its row's bus lane: the points, and the lane's y. `o` spreads lines sharing a lane. */
108
195
  function outward(L, end, o) {
@@ -116,17 +203,17 @@ function outward(L, end, o) {
116
203
  const x = b.x + b.w / 2 + o * 1.5;
117
204
  return { pts: [{ x, y: b.y }, { x, y: b.busY + o }], bus: b.busY + o };
118
205
  }
119
- const { PAD, GAP, NW, NH } = OC;
206
+ const { NW, NH } = OC, { PAD, GAP } = spacing(L);
120
207
  const card = end.c, col = end.i % b.cols, rowI = Math.floor(end.i / b.cols), lines = Math.ceil(b.cells.length / b.cols);
121
- const io = Math.max(-PAD / 2 + 3, Math.min(PAD / 2 - 3, o * 0.5));
122
- const leftLane = b.x + PAD / 2 + io, rightLane = b.x + b.w - PAD / 2 + io;
208
+ const io = laneOffset(o, PAD);
209
+ const leftLane = leftLaneOf(b, io, PAD), rightLane = rightLaneOf(b, io, PAD);
123
210
  let pts;
124
211
  if (col === 0 || col === b.cols - 1) {
125
- const left = col === 0, lane = left ? leftLane : rightLane, cy = card.y + NH / 2 + io;
212
+ const left = col === 0, lane = left ? leftLane : rightLane, cy = card.y + NH / 2 + sideOffset(o);
126
213
  pts = [{ x: left ? card.x : card.x + NW, y: cy }, { x: lane, y: cy }, { x: lane, y: b.y }];
127
214
  } else {
128
215
  // A middle column: down into the gutter below its row, across to the nearer side lane.
129
- const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2 + io / 2, cx = card.x + NW / 2 + o;
216
+ const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2 - io / 2, cx = card.x + NW / 2 + o;
130
217
  const lane = cx - b.x < b.w / 2 ? leftLane : rightLane;
131
218
  pts = [{ x: cx, y: card.y + NH }, { x: cx, y: gy }, { x: lane, y: gy }, { x: lane, y: b.y }];
132
219
  }
@@ -134,8 +221,43 @@ function outward(L, end, o) {
134
221
  pts.push({ x: top.x, y: b.busY + o });
135
222
  return { pts, bus: b.busY + o };
136
223
  }
137
- /** The points of a line from one end to the other: up to the bus, along it (and the trunk, between rows), down. */
224
+ /**
225
+ * Inside a box, from a card to one of its side lanes (x `lane`): straight out when the card is in that lane's column,
226
+ * else down into the gutter under its row (no card is there) and across. The last point is on the lane.
227
+ */
228
+ function toLane(L, end, lane, o) {
229
+ const { NW, NH } = OC, { PAD, GAP } = spacing(L);
230
+ const b = end.b, card = end.c, col = end.i % b.cols, rowI = Math.floor(end.i / b.cols), lines = Math.ceil(b.cells.length / b.cols);
231
+ const io = laneOffset(o, PAD);
232
+ const left = lane < b.x + b.w / 2;
233
+ if ((left && col === 0) || (!left && col === b.cols - 1)) {
234
+ const cy = card.y + NH / 2 + sideOffset(o);
235
+ return [{ x: left ? card.x : card.x + NW, y: cy }, { x: lane, y: cy }];
236
+ }
237
+ const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2 - io / 2, cx = card.x + NW / 2 + o;
238
+ return [{ x: cx, y: card.y + NH }, { x: cx, y: gy }, { x: lane, y: gy }];
239
+ }
240
+ /** Two cards in one open box: joined inside it, through the side lane nearer to them both (never out to the bus). */
241
+ function inBox(L, a, z, o) {
242
+ const b = a.b, { PAD } = spacing(L);
243
+ const io = laneOffset(o, PAD);
244
+ const mid = ((a.i % b.cols) + (z.i % b.cols)) / 2;
245
+ const lane = mid <= (b.cols - 1) / 2 ? leftLaneOf(b, io, PAD) : rightLaneOf(b, io, PAD);
246
+ return [...toLane(L, a, lane, o), ...toLane(L, z, lane, o).reverse()];
247
+ }
248
+ /** Two of General's cards: under them, through the gap above the first row (or the canvas's empty space below). */
249
+ function betweenTops(a, z, o) {
250
+ const y = a.c.y + a.c.h + 30 + o * 0.6;
251
+ const ax = a.c.x + a.c.w / 2 + o, zx = z.c.x + z.c.w / 2 + o;
252
+ return [{ x: ax, y: a.c.y + a.c.h }, { x: ax, y }, { x: zx, y }, { x: zx, y: z.c.y + z.c.h }];
253
+ }
254
+ /**
255
+ * The points of a line from one end to the other: up to the bus, along it (and the trunk, between rows), down — or,
256
+ * for two cards in one box, inside the box; for two of General's cards, just under them.
257
+ */
138
258
  export function route(L, a, z, o) {
259
+ if (a.kind === "cell" && z.kind === "cell" && a.b === z.b) return inBox(L, a, z, o);
260
+ if (a.kind === "top" && z.kind === "top") return betweenTops(a, z, o);
139
261
  const p = outward(L, a, o), q = outward(L, z, o);
140
262
  const back = [...q.pts].reverse();
141
263
  if (Math.abs(p.bus - q.bus) < 1) return [...p.pts, ...back];
@@ -165,19 +287,63 @@ export function badgeAt(pts) {
165
287
  return best;
166
288
  }
167
289
 
290
+ // ── Arrowheads (#2329) ──
291
+
292
+ /** A line's stroke width (px) by its kind and phase: the work's and its report's are heavier, a team pair's the most. */
293
+ export const strokeOf = (hue, phase) => (phase === "work" || phase === "return" ? 3 : hue === "team" ? 3.5 : 2.5);
294
+ /**
295
+ * The arrowhead and the ends. In stroke widths (the markers' units, fleet-canvas.js): a head LEN long and WIDE across,
296
+ * the line's own stroke ending BACK inside it (its round cap under the head, never out past the tip); the source dot
297
+ * DOT across. In px: the tip GAP short of the card, the dot's centre START off its card; a head only on a last straight
298
+ * run that holds it and ROOM of line before it — else the line ends GAP short with none (its dot and pulse show the way).
299
+ */
300
+ export const HEAD = { LEN: 2.8, WIDE: 2.3, BACK: 1.4, DOT: 2, GAP: 3.5, START: 5, ROOM: 1 };
301
+ /** Whether a line of stroke `w` px whose last straight run is `run` px long gets a head. */
302
+ export const headFits = (run, w) => run >= HEAD.GAP + HEAD.LEN * w + HEAD.ROOM;
303
+ /** An orthogonal polyline without repeated points or points in the middle of a straight run. */
304
+ function corners(pts) {
305
+ const q = pts.filter((pt, i) => !i || pt.x !== pts[i - 1].x || pt.y !== pts[i - 1].y).map((pt) => ({ x: pt.x, y: pt.y }));
306
+ return q.filter((pt, i) => i === 0 || i === q.length - 1 || !((q[i - 1].x === pt.x && pt.x === q[i + 1].x) || (q[i - 1].y === pt.y && pt.y === q[i + 1].y)));
307
+ }
308
+ /** Route `pts` as drawn: its start moved `a` px along its first run, its end `b` px back along its last (each keeps 1 px). */
309
+ export function trimEnds(pts, a, b) {
310
+ const q = corners(pts);
311
+ if (q.length < 2) return q;
312
+ const pull = (i, j, by) => {
313
+ const l = Math.hypot(q[j].x - q[i].x, q[j].y - q[i].y), t = Math.max(0, Math.min(by, l - 1)) / l;
314
+ q[i] = { x: q[i].x + (q[j].x - q[i].x) * t, y: q[i].y + (q[j].y - q[i].y) * t };
315
+ };
316
+ pull(0, 1, a);
317
+ pull(q.length - 1, q.length - 2, b);
318
+ return q;
319
+ }
320
+ /** The length of a route's last straight run. */
321
+ export function lastRun(pts) {
322
+ const q = corners(pts);
323
+ return q.length < 2 ? 0 : Math.hypot(q[q.length - 1].x - q[q.length - 2].x, q[q.length - 1].y - q[q.length - 2].y);
324
+ }
325
+
168
326
  // ── Edges ──
169
327
 
170
328
  /** The colour family of a kind: task, report, broadcast, and the rest (send, query, update). */
171
329
  export const hue = (kind) => (kind === "task" || kind === "report" || kind === "broadcast" ? kind : "send");
172
- const phaseOf = (it) => (it.fading ? "fading" : it.state === "queued" ? "queued" : "live");
173
- const RANK = { live: 0, queued: 1, fading: 2 };
330
+ // A message: queued, live, fading. The work: in progress (work), its report coming back (return), or no report (stale).
331
+ const phaseOf = (it) => it.phase || (it.fading ? "fading" : it.state === "queued" ? "queued" : "live");
332
+ const RANK = { live: 0, return: 0, work: 1, queued: 2, fading: 3, stale: 3 };
333
+ const settled = (phase) => (phase === "fading" || phase === "stale" ? "fading" : phase);
174
334
 
175
335
  /**
176
336
  * The lines to draw from the store's items on layout `L`. `far`: zoomed out (one line per team pair). Each edge:
177
- * { key, hue, phase, n, from, to, d, pts, badge } — `from`/`to` the instance (or team) names it joins, for search.
337
+ * { key, hue, phase, n, from, to, d, pts, w, head, badge, a, z, routed } — `from`/`to` the instance (or team) names it
338
+ * joins, for search; `pts` its route, end to end (on the cards); `d` the path drawn, its ends trimmed off the cards for
339
+ * the dot and the head (#2329); `w` its stroke width; `head` whether it has a head (headFits);
340
+ * `a`/`z` its two ends (fleet-route-scene.js routes the ones on cards). `routed` (#2182): the router's points by
341
+ * line key (fleet-route.js); a line it has none for keeps its lane route — or, while a new answer is on its way, the
342
+ * points the same two ends and kind had in the last one (a line changing phase keeps its place).
178
343
  */
179
- export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
344
+ export function trafficEdges(items, L, { far = false, max = MAX_EDGES, routed = null } = {}) {
180
345
  if (!L.boxes.length && L.tops.length < 2) return [];
346
+ // A top's way out is the first row's bus: with no box there is none, so only top ↔ top lines (betweenTops) are drawn.
181
347
  const groups = new Map();
182
348
  const add = (key, g) => { const cur = groups.get(key); if (cur) { cur.n += g.n; cur.names.push(...g.names); } else groups.set(key, g); };
183
349
  const teamKey = (name) => { const t = teamOf(L, name); return t === null ? "\u0000general" : t ? t.key : undefined; };
@@ -195,7 +361,9 @@ export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
195
361
  const k = far ? "team" : "broadcast";
196
362
  const a = far ? teamEnd(it.from) : endOf(L, it.from), z = teamEnd(it.to);
197
363
  if (!a || !z) continue;
198
- add(`${k}|${a.key}>${z.key}|${phase === "fading" ? "fading" : "live"}`, { a, z, hue: k === "team" ? "team" : "broadcast", phase: phase === "fading" ? "fading" : phase, n: 1, names });
364
+ // Queued and live messages share a line far away (as before); the work keeps its own.
365
+ const ph = settled(phase), lane = ph === "queued" ? "live" : ph;
366
+ add(`${k}|${a.key}>${z.key}|${lane}`, { a, z, hue: k === "team" ? "team" : "broadcast", phase: ph, n: 1, names });
199
367
  continue;
200
368
  }
201
369
  const a = endOf(L, it.from), z = endOf(L, it.to);
@@ -203,10 +371,23 @@ export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
203
371
  add(`${a.key}>${z.key}|${hue(it.kind)}|${phase}`, { a, z, hue: hue(it.kind), phase, n: 1, names });
204
372
  }
205
373
  const list = [...groups.entries()].sort((x, y) => RANK[x[1].phase] - RANK[y[1].phase]).slice(0, max);
374
+ const lent = new Set();
375
+ const routedBy = (key) => {
376
+ if (!routed || far) return null;
377
+ const exact = routed.get(key);
378
+ if (exact) return exact;
379
+ const kin = key.slice(0, key.lastIndexOf("|") + 1);
380
+ for (const [k, pts] of routed) if (k.startsWith(kin) && !groups.has(k) && !lent.has(k)) { lent.add(k); return pts; }
381
+ return null;
382
+ };
206
383
  return list.map(([key, g], i) => {
207
384
  const o = ((i % 7) - 3) * 7; // neighbours 7 px apart (within the ±22 px bus lane)
208
- const pts = route(L, g.a, g.z, o);
209
- return { key, hue: g.hue, phase: g.phase, n: g.n, names: [...new Set(g.names)], d: pathOf(pts), pts, badge: g.n > 1 ? badgeAt(pts) : null };
385
+ const viaRouter = routedBy(key);
386
+ const pts = viaRouter ?? route(L, g.a, g.z, o);
387
+ const w = strokeOf(g.hue, g.phase), head = headFits(lastRun(pts), w);
388
+ const d = pathOf(trimEnds(pts, HEAD.START, HEAD.GAP + (head ? HEAD.BACK * w : 0)));
389
+ return { key, hue: g.hue, phase: g.phase, n: g.n, names: [...new Set(g.names)], d, pts, w, head, badge: g.n > 1 ? badgeAt(pts) : null,
390
+ a: g.a, z: g.z, routed: !!viaRouter };
210
391
  });
211
392
  }
212
393
 
@@ -214,10 +395,11 @@ export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
214
395
  export function sendingBy(items) {
215
396
  const out = new Map();
216
397
  for (const it of items) {
217
- if (it.fading) continue;
398
+ if (it.fading || it.phase === "stale" || it.phase === "return") continue;
218
399
  const l = out.get(it.from) || [];
219
- const key = `${it.kind}>${it.to}>${it.state}`;
220
- if (!l.some((x) => x.key === key)) l.push({ key, kind: it.kind, to: it.to, queued: it.state === "queued" });
400
+ const working = it.phase === "work";
401
+ const key = `${it.kind}>${it.to}>${working ? "work" : it.state}`;
402
+ if (!l.some((x) => x.key === key)) l.push({ key, kind: it.kind, to: it.to, queued: it.state === "queued", working });
221
403
  out.set(it.from, l);
222
404
  }
223
405
  return out;
@@ -183,6 +183,38 @@ export function zonesChanged(report) {
183
183
  return zones.length > 1 || (zones.length === 1 && zones[0] !== report.zone) ? zones : null;
184
184
  }
185
185
 
186
+ /** Estimated pixel width of one character in the 9 px `.us-axis` label. */
187
+ const LABEL_CHAR_W = 5.5;
188
+
189
+ /**
190
+ * The 0-based indices of dates that should receive an x-axis label.
191
+ * Keeps the first (0) and last (n-1) when they are far enough apart not to
192
+ * overlap. When first and last are too close (n-1 < step), only the last
193
+ * (most recent) is shown — overlap-free takes precedence over keeping both
194
+ * ends. Intermediate labels are thinned so that no two consecutive shown
195
+ * labels are closer than `labelWidthPx` pixels apart.
196
+ *
197
+ * @param {number} n Number of data points (dates).
198
+ * @param {number} slotPx Pixels per slot (bar + gap).
199
+ * @param {number} labelWidthPx Estimated rendered width of one label.
200
+ * @returns {Set<number>}
201
+ */
202
+ export function thinXLabels(n, slotPx, labelWidthPx) {
203
+ if (n <= 0) return new Set();
204
+ if (n === 1) return new Set([0]);
205
+ // Minimum slot steps between label centres (+ 2 px breathing room).
206
+ const step = Math.max(1, Math.ceil((labelWidthPx + 2) / slotPx));
207
+ // If first and last are too close to both be shown without overlap, keep
208
+ // only the last (most recent). The caller's "always first+last" contract
209
+ // is relaxed here: overlap-free takes precedence.
210
+ if (n - 1 < step) return new Set([n - 1]);
211
+ const idx = [0];
212
+ // Add intermediates that are at least `step` slots away from n-1.
213
+ for (let i = step; i < n - step; i += step) idx.push(i);
214
+ idx.push(n - 1); // always include last
215
+ return new Set(idx);
216
+ }
217
+
186
218
  /** The trend's series: the top `TOP` agents by the measure over the range, then "others"; one value per date. */
187
219
  export function trendSeries(report, dates) {
188
220
  const priced = report.totals && report.totals.amount !== null && report.totals.amount > 0;
@@ -230,6 +262,8 @@ function Trend({ report, dates }) {
230
262
  const max = Math.max(...sums, 0) || 1;
231
263
  const what = s.priced ? tu("amount") : tu("trendTokens"), others = s.names.length > TOP;
232
264
  const label = others ? tu("trendLabel", what, TOP, dates[0], dates[dates.length - 1]) : tu("trendLabelAll", what, s.names.length, dates[0], dates[dates.length - 1]);
265
+ // Thin x-axis labels: "MM-DD" (5 chars) × LABEL_CHAR_W ≈ 27.5 px at 9 px sans-serif.
266
+ const shownLabels = thinXLabels(dates.length, slot, 5 * LABEL_CHAR_W);
233
267
  return html`<section class="us-trend card" aria-labelledby="us-trend-h">
234
268
  <h3 id="us-trend-h">${tu("trend")} <span class="note">· ${s.priced ? tu("amount") : tu("trendTokens")}</span></h3>
235
269
  <p class="note us-scale">${tu("scale", fmtMeasure(s.priced, max))}</p>
@@ -243,7 +277,7 @@ function Trend({ report, dates }) {
243
277
  const h = v[i] > 0 ? Math.max(1, ((base - top) * v[i]) / max) : 0;
244
278
  y -= h;
245
279
  return h ? html`<rect key=${k} class=${`us-s${k}`} x=${left + i * slot + 2} y=${y} width=${slot - 4} height=${h}><title>${`${date} · ${s.names[k]}: ${fmtMeasure(s.priced, v[i])}`}</title></rect>` : null;
246
- })}${(dates.length - 1 - i) % 7 === 0 || dates.length <= 8 ? html`<text class="us-axis" x=${left + i * slot + slot / 2} y=${H - 6} text-anchor="middle">${date.slice(5)}</text>` : null}</g>`;
280
+ })}${shownLabels.has(i) ? html`<text class="us-axis us-xlabel" x=${left + i * slot + slot / 2} y=${H - 6} text-anchor="middle">${date.slice(5)}</text>` : null}</g>`;
247
281
  })}
248
282
  </svg></div>
249
283
  <${EarlierHint} wide=${wide} />
@@ -287,7 +321,7 @@ function Row({ r, group, max, measure }) {
287
321
  const meta = [r.backends.join(", "), r.models.join(", "), r.profiles.join(", ")].filter(Boolean).join(" · ");
288
322
  return html`<tr class="us-row">
289
323
  <th scope="row" class="us-name"><span class="mono">${keyText(group, r.key)}</span>
290
- <svg class="us-bar" viewBox="0 0 100 6" preserveAspectRatio="none" aria-hidden="true"><rect class="us-bar-bg" width="100" height="6" rx="3" /><rect class="us-bar-fg" width=${Math.round(share * 100)} height="6" rx="3" /></svg>
324
+ <svg class="us-bar" aria-hidden="true"><rect class="us-bar-bg" width="100%" height="6" rx="3" /><rect class="us-bar-fg" width=${`${Math.round(share * 100)}%`} height="6" rx="3" /></svg>
291
325
  ${group !== "instance" || meta ? html`<span class="us-meta">${meta}</span>` : null}
292
326
  ${r.flags.length ? html`<span class="us-flags">${r.flags.map((f) => html`<span key=${f} class="ch-tag">${flagText(f, r)}</span>`)}</span>` : null}</th>
293
327
  <td data-label=${tu("amount")} class=${`num${r.amount === null ? " muted" : ""}`}>${amountText(r)}</td>
@@ -0,0 +1,41 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="referrer" content="no-referrer">
7
+ <meta name="robots" content="noindex">
8
+ <title>AgEnD</title>
9
+ <!--app-icon-->
10
+ <link rel="icon" href="/favicon.ico" sizes="16x16 32x32 64x64">
11
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any">
12
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
13
+ <!--/app-icon-->
14
+ <script src="/assets/theme.js"></script>
15
+ <link rel="stylesheet" href="/assets/tokens.css">
16
+ <style>
17
+ /* What a guest's browser gets for a page it cannot open (#2399 smoke: it was the gate's bare JSON). The sign-in
18
+ page's look: the app's tokens, light and dark (theme.js before paint). */
19
+ * { box-sizing: border-box; }
20
+ body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 16px;
21
+ background: var(--bg); color: var(--text); font: var(--fw-regular) var(--fs-md)/var(--lh-md) var(--font-ui); -webkit-font-smoothing: antialiased; }
22
+ main { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 28px 24px; }
23
+ .brand { margin: 0 0 18px; font-size: var(--fs-lg); font-weight: var(--fw-bold); }
24
+ h1 { margin: 0 0 6px; font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
25
+ p { margin: 0 0 18px; color: var(--text-2); }
26
+ a.go { display: block; width: 100%; padding: 12px; text-align: center; text-decoration: none; font: var(--fw-semibold) var(--fs-md) var(--font-ui);
27
+ color: var(--on-accent); background: var(--accent-fill); border-radius: var(--r-full); }
28
+ a.go:hover { opacity: .88; }
29
+ a.go:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-dim); }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <main id="refused" data-why="__WHY__">
34
+ <div class="brand">AgEnD</div>
35
+ <h1 id="title">This page is not open to guests</h1>
36
+ <p id="lead">This page is for the fleet's members. As a guest you can use the chats shared with you.</p>
37
+ <a id="go" class="go" href="/ui">Open your chats</a>
38
+ </main>
39
+ <script src="/assets/guest-refused.js"></script>
40
+ </body>
41
+ </html>
Binary file
Binary file
@@ -5,10 +5,14 @@
5
5
  //
6
6
  // The native <dialog> opened with showModal(): the browser traps focus and makes the rest inert. No style attribute
7
7
  // (#1300): classes only. Only a file URL the chat itself rendered (/ui/file/<32 hex>) is ever shown.
8
+ //
9
+ // #2333: zoom and pan (image-zoom.js). The toolbar has −, the level (it toggles fit ↔ 100%), + and Fit width; the keys
10
+ // + / − / 0 (fit) / 1 (100%) / W (fit width). Each image opens fitted; stepping to another fits it again.
8
11
  import { html, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
9
12
  import { t } from "/assets/app-i18n.js";
10
13
  import { createStore, useStore } from "/assets/app-store.js";
11
14
  import { Icon } from "/assets/ui-icons.js";
15
+ import { createZoom, STEP } from "./image-zoom.js";
12
16
 
13
17
  const FILE_URL = /^\/ui\/file\/[0-9a-f]{32}$/;
14
18
  /** What is shown: { items: [{ src, name }], index, opener } or null. One per page; the chat that shows it owns it. */
@@ -54,6 +58,10 @@ function ImageLightbox({ items, index, opener, onClose }) {
54
58
  const [at, setAt] = useState(index);
55
59
  const ref = useRef(null);
56
60
  const close = useRef(null);
61
+ const stage = useRef(null);
62
+ const img = useRef(null);
63
+ const zoom = useRef(null);
64
+ const [level, setLevel] = useState(null); // { scale, fit, mode } once the picture has loaded
57
65
  const item = items[at] || items[0];
58
66
  const many = items.length > 1;
59
67
  const step = (d) => setAt((n) => (n + d + items.length) % items.length);
@@ -69,6 +77,25 @@ function ImageLightbox({ items, index, opener, onClose }) {
69
77
  if (opener && typeof opener.focus === "function" && document.contains?.(opener)) opener.focus();
70
78
  };
71
79
  }, []);
80
+ useEffect(() => {
81
+ if (!stage.current || !img.current) return undefined;
82
+ const z = createZoom({ stage: stage.current, img: img.current, onChange: setLevel });
83
+ zoom.current = z;
84
+ return () => { z.destroy(); zoom.current = null; };
85
+ }, []);
86
+ // Another image: hidden until it has loaded and been fitted (its load resets the zoom). A layout effect — in the same
87
+ // commit that set the new src, before any load can arrive: a picture already in the cache (every thumbnail above it
88
+ // has loaded) fires its load before a passive effect would run, and such an effect then hid it for good with the
89
+ // level at "…" (the report on beta.24: Next "flashes once, then the next image is not visible"). A picture that is
90
+ // already complete here (the same file again: no load comes) is fitted at once.
91
+ const first = useRef(true);
92
+ useLayoutEffect(() => {
93
+ if (first.current) { first.current = false; return; }
94
+ const el = img.current;
95
+ if (el) el.classList.remove("lb-ready");
96
+ setLevel(null);
97
+ if (el && el.complete && el.naturalWidth > 0 && zoom.current) zoom.current.reset();
98
+ }, [at]);
72
99
  useEffect(() => {
73
100
  const d = ref.current;
74
101
  if (!d) return undefined;
@@ -80,6 +107,12 @@ function ImageLightbox({ items, index, opener, onClose }) {
80
107
  // The dark area (the dialog itself, or the stage around the image) closes it; the image and the controls do not.
81
108
  const onClick = (e) => { if (e.target === d || (e.target && e.target.classList && e.target.classList.contains("lb-stage"))) onClose(); };
82
109
  const onKey = (e) => {
110
+ const z = zoom.current;
111
+ if (z && !e.ctrlKey && !e.metaKey && !e.altKey) {
112
+ const act = { "+": () => z.zoomBy(STEP), "=": () => z.zoomBy(STEP), "-": () => z.zoomBy(1 / STEP), "_": () => z.zoomBy(1 / STEP),
113
+ "0": () => z.fit(), "1": () => z.actual(), w: () => z.fitWidth(), W: () => z.fitWidth() }[e.key];
114
+ if (act) { e.preventDefault(); act(); return; }
115
+ }
83
116
  if (!many) return;
84
117
  if (e.key === "ArrowRight") { e.preventDefault(); step(1); } else if (e.key === "ArrowLeft") { e.preventDefault(); step(-1); }
85
118
  };
@@ -90,14 +123,25 @@ function ImageLightbox({ items, index, opener, onClose }) {
90
123
  return () => { d.removeEventListener("cancel", onCancel); d.removeEventListener("close", onNativeClose); d.removeEventListener("click", onClick); d.removeEventListener("keydown", onKey); };
91
124
  }, [onClose, many, items.length]);
92
125
  const label = item.name ? t("chat.lbLabel", item.name) : t("chat.lbLabelNoName");
126
+ const pct = level ? Math.round(level.scale * 100) : null;
93
127
  return html`<dialog data-trust="lightbox" ref=${ref} class="lb" role="dialog" aria-modal="true" aria-label=${label}>
94
128
  <div class="lb-bar">
95
129
  <span class="lb-name" title=${item.name}>${item.name}</span>
96
130
  ${many ? html`<span class="lb-count">${t("chat.lbCount", at + 1, items.length)}</span>` : null}
131
+ <span class="lb-zoom" role="group">
132
+ <button type="button" class="icon-btn lb-zoom-out" disabled=${!level} aria-label=${t("chat.lbZoomOut")} title=${t("chat.lbZoomOut")}
133
+ onClick=${() => zoom.current && zoom.current.zoomBy(1 / STEP)}><${Icon} name="minus" /></button>
134
+ <button type="button" class="btn btn-sm lb-level" disabled=${!level} aria-label=${t("chat.lbZoomLevel", pct ?? "")} title=${t("chat.lbZoomLevel", pct ?? "")}
135
+ onClick=${() => zoom.current && zoom.current.toggle()}>${pct === null ? "…" : `${pct}%`}</button>
136
+ <button type="button" class="icon-btn lb-zoom-in" disabled=${!level} aria-label=${t("chat.lbZoomIn")} title=${t("chat.lbZoomIn")}
137
+ onClick=${() => zoom.current && zoom.current.zoomBy(STEP)}><${Icon} name="plus" /></button>
138
+ <button type="button" class=${`icon-btn lb-fit-width${level && level.mode === "width" ? " on" : ""}`} disabled=${!level} aria-pressed=${level && level.mode === "width" ? "true" : "false"}
139
+ aria-label=${t("chat.lbFitWidth")} title=${t("chat.lbFitWidth")} onClick=${() => zoom.current && zoom.current.fitWidth()}><${Icon} name="fitwidth" /></button>
140
+ </span>
97
141
  <a class="btn btn-sm lb-orig" href=${item.src} target="_blank" rel="noopener noreferrer"><${Icon} name="external" size=${14} /><span class="lbl">${t("chat.lbOpenOriginal")}</span></a>
98
142
  <button type="button" class="icon-btn lb-close" ref=${close} aria-label=${t("chat.lbClose")} title=${t("chat.lbClose")} onClick=${onClose}><${Icon} name="close" /></button>
99
143
  </div>
100
- <div class="lb-stage"><img class="lb-img" src=${item.src} alt=${item.name} /></div>
144
+ <div class="lb-stage" ref=${stage}><img class="lb-img" ref=${img} src=${item.src} alt=${item.name} draggable=${false} /></div>
101
145
  ${many ? html`<button type="button" class="icon-btn lb-nav lb-prev" aria-label=${t("chat.lbPrev")} title=${t("chat.lbPrev")} onClick=${() => step(-1)}><${Icon} name="back" /></button>
102
146
  <button type="button" class="icon-btn lb-nav lb-next" aria-label=${t("chat.lbNext")} title=${t("chat.lbNext")} onClick=${() => step(1)}><${Icon} name="forward" /></button>` : null}
103
147
  </dialog>`;