@songsid/agend 2.3.0-beta.9 → 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 (723) hide show
  1. package/README.md +16 -8
  2. package/README.zh-TW.md +16 -8
  3. package/dist/adapter-world.js.map +1 -1
  4. package/dist/agend-home.d.ts +5 -0
  5. package/dist/agend-home.js +7 -0
  6. package/dist/agend-home.js.map +1 -0
  7. package/dist/agent-cli-instructions.md +7 -1
  8. package/dist/agent-cli.js +41 -0
  9. package/dist/agent-cli.js.map +1 -1
  10. package/dist/agent-endpoint.d.ts +9 -0
  11. package/dist/agent-endpoint.js +23 -0
  12. package/dist/agent-endpoint.js.map +1 -1
  13. package/dist/agent-file-read.js +1 -1
  14. package/dist/agent-file-read.js.map +1 -1
  15. package/dist/agent-whats-new.d.ts +16 -0
  16. package/dist/agent-whats-new.js +53 -12
  17. package/dist/agent-whats-new.js.map +1 -1
  18. package/dist/apply-job.js.map +1 -1
  19. package/dist/atomic-write.js.map +1 -1
  20. package/dist/auth-api.d.ts +12 -0
  21. package/dist/auth-api.js +77 -22
  22. package/dist/auth-api.js.map +1 -1
  23. package/dist/authority/authority-state.d.ts +4 -0
  24. package/dist/authority/authority-state.js +17 -8
  25. package/dist/authority/authority-state.js.map +1 -1
  26. package/dist/authority/session-authorizations.js.map +1 -1
  27. package/dist/authority/web-authority.d.ts +16 -8
  28. package/dist/authority/web-authority.js +19 -5
  29. package/dist/authority/web-authority.js.map +1 -1
  30. package/dist/backend/antigravity.d.ts +2 -2
  31. package/dist/backend/antigravity.js +2 -1
  32. package/dist/backend/antigravity.js.map +1 -1
  33. package/dist/backend/claude-code.js +13 -4
  34. package/dist/backend/claude-code.js.map +1 -1
  35. package/dist/backend/codex-metadata.d.ts +8 -0
  36. package/dist/backend/codex-metadata.js +74 -17
  37. package/dist/backend/codex-metadata.js.map +1 -1
  38. package/dist/backend/codex-package.d.ts +30 -0
  39. package/dist/backend/codex-package.js +133 -0
  40. package/dist/backend/codex-package.js.map +1 -0
  41. package/dist/backend/codex-session-lookup.js.map +1 -1
  42. package/dist/backend/codex.d.ts +2 -0
  43. package/dist/backend/codex.js +25 -5
  44. package/dist/backend/codex.js.map +1 -1
  45. package/dist/backend/credential-profile.d.ts +2 -0
  46. package/dist/backend/credential-profile.js +2 -0
  47. package/dist/backend/credential-profile.js.map +1 -1
  48. package/dist/backend/effort-metadata.js.map +1 -1
  49. package/dist/backend/grok.js +58 -3
  50. package/dist/backend/grok.js.map +1 -1
  51. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  52. package/dist/backend/kiro-kas-store.js.map +1 -1
  53. package/dist/backend/kiro-v2-store.js.map +1 -1
  54. package/dist/backend/kiro-v3-identity.js.map +1 -1
  55. package/dist/backend/kiro.d.ts +9 -1
  56. package/dist/backend/kiro.js +102 -1
  57. package/dist/backend/kiro.js.map +1 -1
  58. package/dist/backend/launch-effort.js.map +1 -1
  59. package/dist/backend/mock.js.map +1 -1
  60. package/dist/backend/muse.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 +41 -4
  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.js.map +1 -1
  144. package/dist/delivery-consumed-watch.js.map +1 -1
  145. package/dist/delivery-outbox.js +5 -0
  146. package/dist/delivery-outbox.js.map +1 -1
  147. package/dist/delivery-outbox.test.js.map +1 -1
  148. package/dist/delivery-reconciliation.test.js.map +1 -1
  149. package/dist/diagnostic-redaction.d.ts +10 -0
  150. package/dist/diagnostic-redaction.js +167 -0
  151. package/dist/diagnostic-redaction.js.map +1 -0
  152. package/dist/doctor.d.ts +8 -0
  153. package/dist/doctor.js +52 -0
  154. package/dist/doctor.js.map +1 -1
  155. package/dist/e2e-isolation.js.map +1 -1
  156. package/dist/event-log.js.map +1 -1
  157. package/dist/export-import.js +7 -5
  158. package/dist/export-import.js.map +1 -1
  159. package/dist/fleet-control-audit.js.map +1 -1
  160. package/dist/fleet-label.js.map +1 -1
  161. package/dist/fleet-level-config.d.ts +1 -1
  162. package/dist/fleet-level-config.js +2 -0
  163. package/dist/fleet-level-config.js.map +1 -1
  164. package/dist/fleet-lock.js +3 -2
  165. package/dist/fleet-lock.js.map +1 -1
  166. package/dist/fleet-manager.d.ts +249 -13
  167. package/dist/fleet-manager.js +1561 -292
  168. package/dist/fleet-manager.js.map +1 -1
  169. package/dist/fleet-process-handlers.d.ts +18 -0
  170. package/dist/fleet-process-handlers.js +70 -0
  171. package/dist/fleet-process-handlers.js.map +1 -0
  172. package/dist/fleet-startup.d.ts +14 -0
  173. package/dist/fleet-startup.js +30 -0
  174. package/dist/fleet-startup.js.map +1 -0
  175. package/dist/fleet-traffic.js.map +1 -1
  176. package/dist/forged-envelope-scan.d.ts +0 -20
  177. package/dist/forged-envelope-scan.js +3 -2
  178. package/dist/forged-envelope-scan.js.map +1 -1
  179. package/dist/full-restart.d.ts +5 -5
  180. package/dist/full-restart.js +5 -5
  181. package/dist/full-restart.js.map +1 -1
  182. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  183. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  184. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  185. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  186. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  187. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  188. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  189. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  190. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  191. package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -4
  192. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +553 -0
  193. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +154 -31
  194. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  195. package/dist/general-knowledge/steering/core-rules.md +1 -1
  196. package/dist/host-memory.js.map +1 -1
  197. package/dist/idle-gate-log.d.ts +19 -0
  198. package/dist/idle-gate-log.js +15 -0
  199. package/dist/idle-gate-log.js.map +1 -0
  200. package/dist/instance-config-impact.js +25 -4
  201. package/dist/instance-config-impact.js.map +1 -1
  202. package/dist/instance-lifecycle.d.ts +10 -1
  203. package/dist/instance-lifecycle.js +86 -25
  204. package/dist/instance-lifecycle.js.map +1 -1
  205. package/dist/instructions.d.ts +12 -6
  206. package/dist/instructions.js +19 -10
  207. package/dist/instructions.js.map +1 -1
  208. package/dist/kiro-compact-pane.js.map +1 -1
  209. package/dist/kiro-db-reader.js.map +1 -1
  210. package/dist/kiro-engine-status.js.map +1 -1
  211. package/dist/kiro-log-gate.js.map +1 -1
  212. package/dist/kiro-transcript-lane.js.map +1 -1
  213. package/dist/locale.js +428 -20
  214. package/dist/locale.js.map +1 -1
  215. package/dist/log-service.js.map +1 -1
  216. package/dist/log-steps.d.ts +1 -0
  217. package/dist/log-steps.js +15 -0
  218. package/dist/log-steps.js.map +1 -1
  219. package/dist/logger.js +3 -2
  220. package/dist/logger.js.map +1 -1
  221. package/dist/login-controller.d.ts +45 -0
  222. package/dist/login-controller.js +101 -5
  223. package/dist/login-controller.js.map +1 -1
  224. package/dist/login-flows.js +1 -1
  225. package/dist/login-manager.js.map +1 -1
  226. package/dist/login-shell.js +3 -1
  227. package/dist/login-shell.js.map +1 -1
  228. package/dist/ls-attach-resolve.d.ts +96 -0
  229. package/dist/ls-attach-resolve.js +180 -0
  230. package/dist/ls-attach-resolve.js.map +1 -0
  231. package/dist/ls-rows.d.ts +12 -0
  232. package/dist/ls-rows.js +83 -4
  233. package/dist/ls-rows.js.map +1 -1
  234. package/dist/memory-pressure.js.map +1 -1
  235. package/dist/muse-usage-relay.js.map +1 -1
  236. package/dist/needs-you-hub.d.ts +98 -1
  237. package/dist/needs-you-hub.js +313 -44
  238. package/dist/needs-you-hub.js.map +1 -1
  239. package/dist/needs-you-live.d.ts +5 -2
  240. package/dist/needs-you-live.js +6 -3
  241. package/dist/needs-you-live.js.map +1 -1
  242. package/dist/needs-you.d.ts +68 -3
  243. package/dist/needs-you.js +38 -4
  244. package/dist/needs-you.js.map +1 -1
  245. package/dist/network-family.js.map +1 -1
  246. package/dist/operator-control-paths.js +3 -4
  247. package/dist/operator-control-paths.js.map +1 -1
  248. package/dist/outbound-handlers.d.ts +3 -0
  249. package/dist/outbound-handlers.js +76 -19
  250. package/dist/outbound-handlers.js.map +1 -1
  251. package/dist/outbound-schemas.d.ts +106 -60
  252. package/dist/outbound-schemas.js +33 -10
  253. package/dist/outbound-schemas.js.map +1 -1
  254. package/dist/outbox-open-error.js.map +1 -1
  255. package/dist/pane-context-cache.js.map +1 -1
  256. package/dist/paths.js.map +1 -1
  257. package/dist/private-dir.js.map +1 -1
  258. package/dist/probe-worker-pool.js.map +1 -1
  259. package/dist/profile-control.js.map +1 -1
  260. package/dist/prompt-file-ref.js.map +1 -1
  261. package/dist/provider-probe.js.map +1 -1
  262. package/dist/provider-secret-registry.d.ts +5 -3
  263. package/dist/provider-secret-registry.js +8 -2
  264. package/dist/provider-secret-registry.js.map +1 -1
  265. package/dist/public-link-auto-open.d.ts +32 -0
  266. package/dist/public-link-auto-open.js +86 -0
  267. package/dist/public-link-auto-open.js.map +1 -0
  268. package/dist/public-link-owner.d.ts +10 -0
  269. package/dist/public-link-owner.js +36 -0
  270. package/dist/public-link-owner.js.map +1 -0
  271. package/dist/public-link-progress.d.ts +12 -1
  272. package/dist/public-link-progress.js +5 -10
  273. package/dist/public-link-progress.js.map +1 -1
  274. package/dist/public-preview.js.map +1 -1
  275. package/dist/public-tunnel-config.d.ts +11 -0
  276. package/dist/public-tunnel-config.js +68 -0
  277. package/dist/public-tunnel-config.js.map +1 -0
  278. package/dist/public-web-gateway.d.ts +2 -0
  279. package/dist/public-web-gateway.js +19 -6
  280. package/dist/public-web-gateway.js.map +1 -1
  281. package/dist/public-web-link.d.ts +48 -3
  282. package/dist/public-web-link.js +177 -51
  283. package/dist/public-web-link.js.map +1 -1
  284. package/dist/quickstart-api.d.ts +9 -0
  285. package/dist/quickstart-api.js +82 -0
  286. package/dist/quickstart-api.js.map +1 -1
  287. package/dist/quickstart.d.ts +5 -0
  288. package/dist/quickstart.js +40 -25
  289. package/dist/quickstart.js.map +1 -1
  290. package/dist/reply-buttons-controller.js.map +1 -1
  291. package/dist/reply-dedup.js.map +1 -1
  292. package/dist/resource-report.js +1 -1
  293. package/dist/resource-report.js.map +1 -1
  294. package/dist/restart-guard.js.map +1 -1
  295. package/dist/restart-progress.d.ts +80 -1
  296. package/dist/restart-progress.js +306 -21
  297. package/dist/restart-progress.js.map +1 -1
  298. package/dist/rollout-index.js.map +1 -1
  299. package/dist/runtime-cpu-profile.js.map +1 -1
  300. package/dist/scheduler/db.d.ts +40 -13
  301. package/dist/scheduler/db.js +123 -89
  302. package/dist/scheduler/db.js.map +1 -1
  303. package/dist/scheduler/scheduler.js.map +1 -1
  304. package/dist/scheduler/task-board-text.d.ts +26 -0
  305. package/dist/scheduler/task-board-text.js +140 -0
  306. package/dist/scheduler/task-board-text.js.map +1 -0
  307. package/dist/scheduler/task-board.d.ts +730 -0
  308. package/dist/scheduler/task-board.js +2179 -0
  309. package/dist/scheduler/task-board.js.map +1 -0
  310. package/dist/scheduler/types.d.ts +6 -1
  311. package/dist/sd-notify.js +2 -1
  312. package/dist/sd-notify.js.map +1 -1
  313. package/dist/search/search-docs.d.ts +58 -0
  314. package/dist/search/search-docs.js +63 -0
  315. package/dist/search/search-docs.js.map +1 -0
  316. package/dist/search/search-index.d.ts +141 -0
  317. package/dist/search/search-index.js +440 -0
  318. package/dist/search/search-index.js.map +1 -0
  319. package/dist/search/search-store.d.ts +122 -0
  320. package/dist/search/search-store.js +236 -0
  321. package/dist/search/search-store.js.map +1 -0
  322. package/dist/search/search-worker.d.ts +1 -0
  323. package/dist/search/search-worker.js +163 -0
  324. package/dist/search/search-worker.js.map +1 -0
  325. package/dist/secret-file.js.map +1 -1
  326. package/dist/secret-store.d.ts +2 -0
  327. package/dist/secret-store.js +21 -0
  328. package/dist/secret-store.js.map +1 -1
  329. package/dist/service-installer.d.ts +75 -0
  330. package/dist/service-installer.js +206 -0
  331. package/dist/service-installer.js.map +1 -1
  332. package/dist/service-owner.d.ts +44 -0
  333. package/dist/service-owner.js +48 -0
  334. package/dist/service-owner.js.map +1 -0
  335. package/dist/service-plan.d.ts +39 -2
  336. package/dist/service-plan.js +188 -6
  337. package/dist/service-plan.js.map +1 -1
  338. package/dist/session-oom.d.ts +21 -0
  339. package/dist/session-oom.js +139 -0
  340. package/dist/session-oom.js.map +1 -0
  341. package/dist/settings-api.d.ts +13 -0
  342. package/dist/settings-api.js +73 -12
  343. package/dist/settings-api.js.map +1 -1
  344. package/dist/settings-baseline.js.map +1 -1
  345. package/dist/settings-change.d.ts +5 -0
  346. package/dist/settings-change.js +167 -6
  347. package/dist/settings-change.js.map +1 -1
  348. package/dist/settings-confirmation.d.ts +9 -1
  349. package/dist/settings-confirmation.js +18 -8
  350. package/dist/settings-confirmation.js.map +1 -1
  351. package/dist/settings-control.js +2 -1
  352. package/dist/settings-control.js.map +1 -1
  353. package/dist/settings-effect.js +29 -4
  354. package/dist/settings-effect.js.map +1 -1
  355. package/dist/settings-http-confirmation.d.ts +13 -0
  356. package/dist/settings-http-confirmation.js +15 -3
  357. package/dist/settings-http-confirmation.js.map +1 -1
  358. package/dist/settings-instance-patch.js +13 -1
  359. package/dist/settings-instance-patch.js.map +1 -1
  360. package/dist/settings-request-capability.d.ts +4 -1
  361. package/dist/settings-request-capability.js +11 -3
  362. package/dist/settings-request-capability.js.map +1 -1
  363. package/dist/settings-transaction.js.map +1 -1
  364. package/dist/setup-auth.d.ts +1 -0
  365. package/dist/setup-auth.js +2 -1
  366. package/dist/setup-auth.js.map +1 -1
  367. package/dist/setup-form.d.ts +1 -1
  368. package/dist/setup-form.js +23 -7
  369. package/dist/setup-form.js.map +1 -1
  370. package/dist/setup-host.d.ts +6 -1
  371. package/dist/setup-host.js +15 -4
  372. package/dist/setup-host.js.map +1 -1
  373. package/dist/setup-wizard.js +6 -3
  374. package/dist/setup-wizard.js.map +1 -1
  375. package/dist/spawn-gate.d.ts +5 -0
  376. package/dist/spawn-gate.js +28 -8
  377. package/dist/spawn-gate.js.map +1 -1
  378. package/dist/start-attempt.d.ts +1 -0
  379. package/dist/start-attempt.js +18 -0
  380. package/dist/start-attempt.js.map +1 -0
  381. package/dist/start-failure-notice.d.ts +4 -0
  382. package/dist/start-failure-notice.js +20 -0
  383. package/dist/start-failure-notice.js.map +1 -0
  384. package/dist/start-failure.d.ts +147 -0
  385. package/dist/start-failure.js +281 -0
  386. package/dist/start-failure.js.map +1 -0
  387. package/dist/startup-memory-deadline.d.ts +23 -0
  388. package/dist/startup-memory-deadline.js +55 -0
  389. package/dist/startup-memory-deadline.js.map +1 -0
  390. package/dist/statusline-watcher.js.map +1 -1
  391. package/dist/storm-window.js.map +1 -1
  392. package/dist/systemd-peer-bus.js.map +1 -1
  393. package/dist/target-queue-worker.js.map +1 -1
  394. package/dist/task-board-summary.d.ts +101 -0
  395. package/dist/task-board-summary.js +177 -0
  396. package/dist/task-board-summary.js.map +1 -0
  397. package/dist/task-board-tool.d.ts +25 -0
  398. package/dist/task-board-tool.js +155 -0
  399. package/dist/task-board-tool.js.map +1 -0
  400. package/dist/task-board-waker.d.ts +51 -0
  401. package/dist/task-board-waker.js +141 -0
  402. package/dist/task-board-waker.js.map +1 -0
  403. package/dist/tips.js.map +1 -1
  404. package/dist/tmux-control.js +1 -1
  405. package/dist/tmux-control.js.map +1 -1
  406. package/dist/tmux-exec-helper.d.ts +104 -0
  407. package/dist/tmux-exec-helper.js +412 -0
  408. package/dist/tmux-exec-helper.js.map +1 -0
  409. package/dist/tmux-manager.d.ts +1 -0
  410. package/dist/tmux-manager.js +29 -13
  411. package/dist/tmux-manager.js.map +1 -1
  412. package/dist/tmux-read.js +2 -2
  413. package/dist/tmux-read.js.map +1 -1
  414. package/dist/tool-permissions.js.map +1 -1
  415. package/dist/tool-progress.js.map +1 -1
  416. package/dist/topic-archiver.js.map +1 -1
  417. package/dist/topic-commands.d.ts +13 -0
  418. package/dist/topic-commands.js +56 -33
  419. package/dist/topic-commands.js.map +1 -1
  420. package/dist/topic-link.js.map +1 -1
  421. package/dist/transcript-delta-reader.js.map +1 -1
  422. package/dist/transcript-jsonl.js.map +1 -1
  423. package/dist/transcript-monitor.js +5 -0
  424. package/dist/transcript-monitor.js.map +1 -1
  425. package/dist/transcript-sources.d.ts +2 -0
  426. package/dist/transcript-sources.js +4 -0
  427. package/dist/transcript-sources.js.map +1 -1
  428. package/dist/tunnel/cloudflared-install.js +3 -1
  429. package/dist/tunnel/cloudflared-install.js.map +1 -1
  430. package/dist/tunnel/cloudflared.d.ts +7 -16
  431. package/dist/tunnel/cloudflared.js +100 -13
  432. package/dist/tunnel/cloudflared.js.map +1 -1
  433. package/dist/tunnel/lease.js +3 -2
  434. package/dist/tunnel/lease.js.map +1 -1
  435. package/dist/tunnel/manager.js.map +1 -1
  436. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  437. package/dist/tunnel/named-diagnostics.js +52 -0
  438. package/dist/tunnel/named-diagnostics.js.map +1 -0
  439. package/dist/tunnel/public-fetch.js.map +1 -1
  440. package/dist/tunnel/purpose-lane.js.map +1 -1
  441. package/dist/tunnel/types.d.ts +4 -1
  442. package/dist/tunnel/types.js.map +1 -1
  443. package/dist/types.d.ts +55 -2
  444. package/dist/ui/app-effects.js +23 -5
  445. package/dist/ui/app.html +2 -0
  446. package/dist/ui/backend-signin.js +297 -0
  447. package/dist/ui/chat-effects.js +10 -3
  448. package/dist/ui/chat-msg-actions.js +176 -0
  449. package/dist/ui/chat-render.js +62 -18
  450. package/dist/ui/chat-store.js +218 -48
  451. package/dist/ui/chat-strings.js +40 -8
  452. package/dist/ui/chat-thread.js +119 -10
  453. package/dist/ui/effect-trial-offer.js +2 -31
  454. package/dist/ui/effects-waker.js +2 -0
  455. package/dist/ui/fleet-canvas-layout.js +13 -4
  456. package/dist/ui/fleet-canvas.js +69 -28
  457. package/dist/ui/fleet-office-editor.js +149 -0
  458. package/dist/ui/fleet-office-model.js +358 -0
  459. package/dist/ui/fleet-office-pack.js +199 -0
  460. package/dist/ui/fleet-office-skin.js +474 -0
  461. package/dist/ui/fleet-office-store.js +49 -0
  462. package/dist/ui/fleet-office-zones.js +177 -0
  463. package/dist/ui/fleet-office.js +434 -0
  464. package/dist/ui/fleet-org.js +18 -5
  465. package/dist/ui/fleet-route-scene.js +272 -0
  466. package/dist/ui/fleet-route-worker.js +21 -0
  467. package/dist/ui/fleet-route.js +88 -0
  468. package/dist/ui/fleet-task-card.js +436 -0
  469. package/dist/ui/fleet-task-common.js +482 -0
  470. package/dist/ui/fleet-task-work.js +107 -0
  471. package/dist/ui/fleet-tasks.js +569 -0
  472. package/dist/ui/fleet-traffic.js +81 -18
  473. package/dist/ui/fleet-usage.js +36 -2
  474. package/dist/ui/guest-refused.html +41 -0
  475. package/dist/ui/icons/icon-192.png +0 -0
  476. package/dist/ui/icons/icon-512.png +0 -0
  477. package/dist/ui/image-lightbox.js +45 -1
  478. package/dist/ui/image-zoom.js +242 -0
  479. package/dist/ui/panel-chat.js +200 -56
  480. package/dist/ui/panel-details.js +2 -0
  481. package/dist/ui/panel-fleet.js +32 -71
  482. package/dist/ui/panel-needs.js +23 -6
  483. package/dist/ui/panel-settings.js +158 -32
  484. package/dist/ui/preview-panel.js +2 -1
  485. package/dist/ui/search-dialog.js +350 -0
  486. package/dist/ui/settings-appearance.js +170 -10
  487. package/dist/ui/settings-apply.js +3 -3
  488. package/dist/ui/settings-confirm.js +7 -2
  489. package/dist/ui/settings-dialogs.js +46 -21
  490. package/dist/ui/settings-discord.js +6 -2
  491. package/dist/ui/settings-guests.js +2 -1
  492. package/dist/ui/settings-learn.js +1 -0
  493. package/dist/ui/settings-model.js +51 -18
  494. package/dist/ui/settings-mods.js +8 -3
  495. package/dist/ui/settings-owner-effects.js +6 -4
  496. package/dist/ui/settings-strings.js +180 -6
  497. package/dist/ui/settings-token.js +3 -1
  498. package/dist/ui/settings-web-authority.js +13 -5
  499. package/dist/ui/settings-wizard.js +13 -7
  500. package/dist/ui/shared/agend-auth.js +2 -0
  501. package/dist/ui/shared/app-i18n.js +12 -5
  502. package/dist/ui/shared/app-needs.js +2 -2
  503. package/dist/ui/shared/app-prefs.js +15 -0
  504. package/dist/ui/shared/app-route.js +18 -0
  505. package/dist/ui/shared/app-shell.js +172 -28
  506. package/dist/ui/shared/app-store.js +3 -2
  507. package/dist/ui/shared/app-stream.js +5 -1
  508. package/dist/ui/shared/app-themes.js +116 -1
  509. package/dist/ui/shared/app.css +662 -48
  510. package/dist/ui/shared/app.js +9 -1
  511. package/dist/ui/shared/card-strings.js +4 -0
  512. package/dist/ui/shared/effect-app-runtime.js +7 -5
  513. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  514. package/dist/ui/shared/effect-cursor.js +3 -2
  515. package/dist/ui/shared/effect-display-metadata.js +39 -0
  516. package/dist/ui/shared/effect-owner-source.js +8 -1
  517. package/dist/ui/shared/effect-registry.js +14 -4
  518. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  519. package/dist/ui/shared/effect-stock-trust.js +5 -5
  520. package/dist/ui/shared/feature-flags.js +127 -0
  521. package/dist/ui/shared/guest-refused.js +35 -0
  522. package/dist/ui/shared/header-tools.js +2 -2
  523. package/dist/ui/shared/identity-card.js +26 -8
  524. package/dist/ui/shared/identity-color.js +17 -10
  525. package/dist/ui/shared/instance-nav.js +183 -28
  526. package/dist/ui/shared/owner-css.js +30 -1
  527. package/dist/ui/shared/panel-view.js +14 -29
  528. package/dist/ui/shared/text-undo.js +210 -0
  529. package/dist/ui/shared/tokens.css +4 -0
  530. package/dist/ui/shared/ui-dialog.js +5 -4
  531. package/dist/ui/shared/ui-icons.js +4 -0
  532. package/dist/ui/shared/ui-menu.js +20 -7
  533. package/dist/ui/shared/ui-toast.js +16 -3
  534. package/dist/ui/shared/usage-dialog.js +206 -16
  535. package/dist/ui/shared/view-strings.js +68 -4
  536. package/dist/ui/shared/web-authorize.js +50 -1
  537. package/dist/ui/signin.html +2 -0
  538. package/dist/ui/web-terminal/terminal.html +10 -10
  539. package/dist/ui/web-terminal/terminal.js +58 -12
  540. package/dist/update-check.js.map +1 -1
  541. package/dist/update-dispatch.js +1 -1
  542. package/dist/update-dispatch.js.map +1 -1
  543. package/dist/update-install.js.map +1 -1
  544. package/dist/update-launch.d.ts +10 -1
  545. package/dist/update-launch.js +39 -14
  546. package/dist/update-launch.js.map +1 -1
  547. package/dist/update-marker.js.map +1 -1
  548. package/dist/update-preflight.js +1 -1
  549. package/dist/update-preflight.js.map +1 -1
  550. package/dist/update-progress.d.ts +3 -1
  551. package/dist/update-progress.js +3 -1
  552. package/dist/update-progress.js.map +1 -1
  553. package/dist/usage/format-rich.js +6 -3
  554. package/dist/usage/format-rich.js.map +1 -1
  555. package/dist/usage/i18n-keys.d.ts +1 -1
  556. package/dist/usage/i18n-keys.js +2 -1
  557. package/dist/usage/i18n-keys.js.map +1 -1
  558. package/dist/usage/i18n.d.ts +13 -1
  559. package/dist/usage/i18n.js +29 -0
  560. package/dist/usage/i18n.js.map +1 -1
  561. package/dist/usage/providers.d.ts +89 -2
  562. package/dist/usage/providers.js +247 -41
  563. package/dist/usage/providers.js.map +1 -1
  564. package/dist/usage/statusline-usage.js.map +1 -1
  565. package/dist/usage/usage-api.d.ts +5 -1
  566. package/dist/usage/usage-api.js +185 -58
  567. package/dist/usage/usage-api.js.map +1 -1
  568. package/dist/usage-report.js.map +1 -1
  569. package/dist/usage-rollup.js +1 -1
  570. package/dist/usage-rollup.js.map +1 -1
  571. package/dist/user-systemd.d.ts +24 -0
  572. package/dist/user-systemd.js +75 -0
  573. package/dist/user-systemd.js.map +1 -0
  574. package/dist/view-api.d.ts +7 -0
  575. package/dist/view-api.js +16 -4
  576. package/dist/view-api.js.map +1 -1
  577. package/dist/wake-coordinator.js.map +1 -1
  578. package/dist/web-ambient.js.map +1 -1
  579. package/dist/web-api.d.ts +57 -10
  580. package/dist/web-api.js +1103 -70
  581. package/dist/web-api.js.map +1 -1
  582. package/dist/web-app-icon-worker.d.ts +1 -0
  583. package/dist/web-app-icon-worker.js +16 -0
  584. package/dist/web-app-icon-worker.js.map +1 -0
  585. package/dist/web-app-icon.d.ts +109 -0
  586. package/dist/web-app-icon.js +257 -0
  587. package/dist/web-app-icon.js.map +1 -0
  588. package/dist/web-appearance-policy.js.map +1 -1
  589. package/dist/web-appearance-tools.d.ts +1 -0
  590. package/dist/web-appearance-tools.js +7 -3
  591. package/dist/web-appearance-tools.js.map +1 -1
  592. package/dist/web-appearance-view.js.map +1 -1
  593. package/dist/web-appearance.d.ts +4 -10
  594. package/dist/web-appearance.js +7 -4
  595. package/dist/web-appearance.js.map +1 -1
  596. package/dist/web-auth.d.ts +8 -4
  597. package/dist/web-auth.js +33 -21
  598. package/dist/web-auth.js.map +1 -1
  599. package/dist/web-backend-login.d.ts +130 -0
  600. package/dist/web-backend-login.js +225 -0
  601. package/dist/web-backend-login.js.map +1 -0
  602. package/dist/web-card.js.map +1 -1
  603. package/dist/web-channel-echo.js.map +1 -1
  604. package/dist/web-chat-history.d.ts +118 -8
  605. package/dist/web-chat-history.js +220 -25
  606. package/dist/web-chat-history.js.map +1 -1
  607. package/dist/web-commands.d.ts +5 -1
  608. package/dist/web-commands.js +1 -1
  609. package/dist/web-commands.js.map +1 -1
  610. package/dist/web-cookie-names.d.ts +5 -0
  611. package/dist/web-cookie-names.js +12 -0
  612. package/dist/web-cookie-names.js.map +1 -0
  613. package/dist/web-effect-bundle.d.ts +2 -2
  614. package/dist/web-effect-library.js.map +1 -1
  615. package/dist/web-effect-playback.js.map +1 -1
  616. package/dist/web-effect-profiles.d.ts +16 -16
  617. package/dist/web-effect-profiles.js +3 -2
  618. package/dist/web-effect-profiles.js.map +1 -1
  619. package/dist/web-effect-service.js.map +1 -1
  620. package/dist/web-effect-tools.js.map +1 -1
  621. package/dist/web-effect-trials.js.map +1 -1
  622. package/dist/web-effect-upload.js.map +1 -1
  623. package/dist/web-effect.d.ts +6 -6
  624. package/dist/web-effect.js.map +1 -1
  625. package/dist/web-experimental-http.d.ts +2 -0
  626. package/dist/web-experimental-http.js +12 -0
  627. package/dist/web-experimental-http.js.map +1 -0
  628. package/dist/web-experimental.d.ts +11 -0
  629. package/dist/web-experimental.js +49 -0
  630. package/dist/web-experimental.js.map +1 -0
  631. package/dist/web-guest-access.d.ts +4 -2
  632. package/dist/web-guest-access.js +22 -10
  633. package/dist/web-guest-access.js.map +1 -1
  634. package/dist/web-guest-rooms.js.map +1 -1
  635. package/dist/web-host-guard.d.ts +11 -0
  636. package/dist/web-host-guard.js +11 -0
  637. package/dist/web-host-guard.js.map +1 -1
  638. package/dist/web-icons.d.ts +33 -1
  639. package/dist/web-icons.js +83 -11
  640. package/dist/web-icons.js.map +1 -1
  641. package/dist/web-identity-color.js.map +1 -1
  642. package/dist/web-image-meta.d.ts +13 -0
  643. package/dist/web-image-meta.js +62 -0
  644. package/dist/web-image-meta.js.map +1 -1
  645. package/dist/web-images.js.map +1 -1
  646. package/dist/web-libavoid.d.ts +14 -0
  647. package/dist/web-libavoid.js +46 -0
  648. package/dist/web-libavoid.js.map +1 -0
  649. package/dist/web-login.js.map +1 -1
  650. package/dist/web-mentions.js.map +1 -1
  651. package/dist/web-mod-agent.js.map +1 -1
  652. package/dist/web-mod-store.d.ts +2 -0
  653. package/dist/web-mod-store.js +10 -0
  654. package/dist/web-mod-store.js.map +1 -1
  655. package/dist/web-office-store.d.ts +104 -0
  656. package/dist/web-office-store.js +236 -0
  657. package/dist/web-office-store.js.map +1 -0
  658. package/dist/web-owner-css-store.d.ts +44 -1
  659. package/dist/web-owner-css-store.js +70 -9
  660. package/dist/web-owner-css-store.js.map +1 -1
  661. package/dist/web-owner-css.js.map +1 -1
  662. package/dist/web-png.d.ts +33 -0
  663. package/dist/web-png.js +329 -0
  664. package/dist/web-png.js.map +1 -0
  665. package/dist/web-prefs.d.ts +6 -0
  666. package/dist/web-prefs.js +29 -3
  667. package/dist/web-prefs.js.map +1 -1
  668. package/dist/web-preview.d.ts +2 -0
  669. package/dist/web-preview.js +2 -0
  670. package/dist/web-preview.js.map +1 -1
  671. package/dist/web-profile.js.map +1 -1
  672. package/dist/web-reactions.d.ts +35 -0
  673. package/dist/web-reactions.js +47 -0
  674. package/dist/web-reactions.js.map +1 -0
  675. package/dist/web-request-context.d.ts +5 -0
  676. package/dist/web-request-context.js.map +1 -1
  677. package/dist/web-returning-device.d.ts +4 -2
  678. package/dist/web-returning-device.js +13 -6
  679. package/dist/web-returning-device.js.map +1 -1
  680. package/dist/web-schedules.js.map +1 -1
  681. package/dist/web-send-dedupe.d.ts +36 -0
  682. package/dist/web-send-dedupe.js +69 -0
  683. package/dist/web-send-dedupe.js.map +1 -0
  684. package/dist/web-session-authorization.js.map +1 -1
  685. package/dist/web-session.js +1 -1
  686. package/dist/web-session.js.map +1 -1
  687. package/dist/web-settings-patch.d.ts +3 -1
  688. package/dist/web-settings-patch.js +11 -5
  689. package/dist/web-settings-patch.js.map +1 -1
  690. package/dist/web-settings-tools.d.ts +6 -0
  691. package/dist/web-settings-tools.js +52 -17
  692. package/dist/web-settings-tools.js.map +1 -1
  693. package/dist/web-terminal-http.js +15 -2
  694. package/dist/web-terminal-http.js.map +1 -1
  695. package/dist/web-terminal.js +1 -1
  696. package/dist/web-terminal.js.map +1 -1
  697. package/dist/web-theme-store.d.ts +2 -2
  698. package/dist/web-theme-store.js +6 -3
  699. package/dist/web-theme-store.js.map +1 -1
  700. package/dist/web-theme.d.ts +29 -0
  701. package/dist/web-theme.js +89 -1
  702. package/dist/web-theme.js.map +1 -1
  703. package/dist/web-upload.d.ts +18 -1
  704. package/dist/web-upload.js +45 -7
  705. package/dist/web-upload.js.map +1 -1
  706. package/dist/web-widget.js.map +1 -1
  707. package/dist/webhook-emitter.js.map +1 -1
  708. package/dist/windows-shortcuts.d.ts +78 -0
  709. package/dist/windows-shortcuts.js +203 -0
  710. package/dist/windows-shortcuts.js.map +1 -0
  711. package/dist/workflow-templates/default.md +3 -0
  712. package/dist/ws-server.js.map +1 -1
  713. package/dist/yaml-shared-nodes.d.ts +25 -0
  714. package/dist/yaml-shared-nodes.js +185 -0
  715. package/dist/yaml-shared-nodes.js.map +1 -0
  716. package/launcher/agend-install.cjs +828 -0
  717. package/launcher/install-admission.cjs +4 -39
  718. package/launcher/postinstall.cjs +14 -12
  719. package/launcher/private-home.cjs +43 -0
  720. package/package.json +16 -13
  721. package/scripts/preinstall-guard.cjs +2 -5
  722. package/templates/windows-shortcuts.ps1 +181 -0
  723. package/launcher/old-updater-note.cjs +0 -48
@@ -31,6 +31,7 @@ export const SYNCED = {
31
31
  "nav.collapsed": { ls: "agend_instance_collapsed", ...json },
32
32
  "nav.filter": { ls: "agend_instance_filter", ...json },
33
33
  "nav.favorites": { ls: "agend_favorites", ...json }, // #1664
34
+ "nav.sort": { ls: "agend_nav_sort", ...json }, // #2179
34
35
  "preview.optIn": { ls: "agend_html_preview", toLocal: (v) => (v ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
35
36
  "names.primary": { ls: "agend_names_primary", toLocal: (v) => (v === "instance" ? v : null), fromLocal: (s) => (s === "instance" ? s : undefined) }, // #1722
36
37
  // #1629 r2 (d): "Show agents' themes and effects" — on unless turned off (only "off" is cached).
@@ -42,7 +43,10 @@ export const SYNCED = {
42
43
  "widget.sidebar": { ls: "agend_widget_sidebar", ...json },
43
44
  // #1958: where identity colours show by default (#2056: name, bar and ring unless chosen), and the host's switch to turn them off.
44
45
  "color.show": { ls: "agend_color_show", ...json },
46
+ "theme.pageByInstance": { ls: "agend_theme_page", ...json }, // #2196
45
47
  "color.off": { ls: "agend_color_off", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
48
+ // The AI usage view's compact mode: one line per login (usage-dialog.js), in the dialog and the rail alike.
49
+ "usage.compact": { ls: "agend_usage_compact", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
46
50
  };
47
51
  // #1629: fixed personal scope. A separate session-bound owner-effect store synchronizes these, never this host cache.
48
52
  export const OWNER_EFFECT_PREF_KEYS = Object.freeze(["fx.sidebar", "fx.header", "fx.page", "fx.box", "fx.cursor"]);
@@ -77,6 +81,15 @@ const inflight = new Map(); // key → writes sent and not answered yet
77
81
  let flushTimer = null;
78
82
  let writes = Promise.resolve(); // one write at a time, in the order they were made
79
83
  const listeners = new Set();
84
+ // #2193: whether the signed-in person has a personal effect slot chosen (their fx.* keys: in the page's data-prefs and
85
+ // in every host read, never in this host cache). The effects runtime is loaded only when there is something to play.
86
+ const ownerEffectsIn = (values) => !!values && OWNER_EFFECT_PREF_KEYS.some((k) => own(values, k) && values[k] != null && values[k] !== "");
87
+ let ownerFx = initial ? ownerEffectsIn(initial.values) : false;
88
+ const ownerFxListeners = new Set();
89
+ /** Whether the person has any personal effect (fx.page/header/sidebar/box/cursor) chosen, as this page last read. */
90
+ export function ownerEffectsChosen() { return ownerFx; }
91
+ /** Called when that changes. Returns the unsubscribe. */
92
+ export function onOwnerEffectsChanged(fn) { ownerFxListeners.add(fn); return () => ownerFxListeners.delete(fn); }
80
93
 
81
94
  // The host's values into the cache, now — before the sidebar, the tour or the first-run card read their keys. A key
82
95
  // the host does not have: the default, on a device in step with the host; this device's own (to upload) otherwise.
@@ -117,6 +130,8 @@ function take(body) {
117
130
  if (!isView(body) || (rev !== null && body.rev < rev)) return false;
118
131
  rev = body.rev;
119
132
  host = hostValues(body.values);
133
+ const fx = ownerEffectsIn(body.values);
134
+ if (fx !== ownerFx) { ownerFx = fx; for (const fn of [...ownerFxListeners]) { try { fn(fx); } catch { /* the rest still hear */ } } }
120
135
  return true;
121
136
  }
122
137
 
@@ -50,6 +50,24 @@ export function viewPath(instance) { return instance ? `/view/${encodeURICompone
50
50
  export function detailsPath(instance) { return `/ui/fleet/agent/${encodeURIComponent(instance)}`; }
51
51
  export function logPath(instance) { return `/ui/log/${encodeURIComponent(instance)}`; }
52
52
  export function fleetPath(tab) { return tab && tab !== "tasks" ? `/ui/fleet/${tab}` : "/ui/fleet"; }
53
+ /** #2183: this device's last Fleet tab (Fleet remembers it; the Org chart the first time). */
54
+ export const LAST_FLEET_TAB_KEY = "agend_last_fleet_tab";
55
+ export function lastFleetTab() {
56
+ let tab = null;
57
+ try { tab = localStorage.getItem(LAST_FLEET_TAB_KEY); } catch { /* storage blocked: the default */ }
58
+ return FLEET_TABS.includes(tab) ? tab : "org";
59
+ }
60
+ /**
61
+ * #2183: where Fleet (the sidebar's entry, the phone's tab) goes — on a Fleet tab, that tab (the route, not storage: the
62
+ * panel stores it in an effect after this render, and a storage write re-renders nothing); anywhere else, the last one.
63
+ */
64
+ export function fleetEntryPath(route) {
65
+ return fleetPath(route && route.panel === "fleet" ? route.tab : lastFleetTab()); // parseRoute names only FLEET_TABS
66
+ }
67
+ export function rememberFleetTab(tab) {
68
+ if (!FLEET_TABS.includes(tab)) return;
69
+ try { localStorage.setItem(LAST_FLEET_TAB_KEY, tab); } catch { /* this page only */ }
70
+ }
53
71
  export const NEEDS_PATH = "/ui/needs";
54
72
  export function settingsPath(section) { return section && section !== "agents" ? `/settings/${section}` : "/settings"; }
55
73
  export function routePath(route) {
@@ -9,9 +9,11 @@ import { ColorDialogHost } from "./identity-color.js";
9
9
  import { t, lang, setLang, onLang } from "./app-i18n.js";
10
10
  import { appStore, createStore, guestBadge, guestPage, useStore } from "./app-store.js";
11
11
  import { navStore } from "./app-nav.js";
12
- import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH, detailsPath, fleetPath } from "./app-route.js";
12
+ import { toast } from "./ui-toast.js";
13
+ import { chatPath, viewPath, settingsPath, routeKey, NEEDS_PATH, fleetEntryPath } from "./app-route.js";
13
14
  import { SessionMenu } from "./app-session.js";
14
- import { savePref } from "./app-prefs.js";
15
+ import { onPrefsChanged, savePref } from "./app-prefs.js";
16
+ import { applyPageTheme, pageNow, resolvedMode, themesStore } from "./app-themes.js";
15
17
  import { ErrorState, Skeleton } from "./ui-states.js";
16
18
  import { InstanceNav, RosterNav, namesStore, setNamesPrimary } from "./instance-nav.js";
17
19
  import { UsageRail, usageRailStore } from "./header-tools.js";
@@ -19,7 +21,7 @@ import { OwnerCssKeepBar } from "./owner-css.js";
19
21
  import { AgentWidget, fleetBinds, sidebarWidget, sidebarWidgetStore } from "./agent-widget.js";
20
22
 
21
23
  /** Shell state shared with panels: the phone drawer, the collapsed sidebar, extensions a panel module adds. */
22
- export const shellStore = createStore({ drawer: false, collapsed: readCollapsed(), keyboard: false, dialog: null, side: null });
24
+ export const shellStore = createStore({ drawer: false, collapsed: readCollapsed(), keyboard: false, dialog: null, side: null, banner: null });
23
25
 
24
26
  /**
25
27
  * The mounted panel's own sidebar section, in place of the instance list (View's roster: its groups, filter and order,
@@ -62,6 +64,45 @@ export function useCanCreate() {
62
64
  export function showDialog(Component, props = {}) { shellStore.set({ dialog: { Component, props } }); }
63
65
  export function closeDialog() { shellStore.set({ dialog: null }); }
64
66
 
67
+ // ── #2293: global search — an admin's. The dialog lives behind the session gate (/ui/js/search-dialog.js) and loads the
68
+ // first time it opens; the sidebar row, the header's button and the shortcuts only open it. ──
69
+ export const isMacLike = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || "");
70
+ /** What the row and tooltips show: ⌘K on a Mac, Ctrl K elsewhere. */
71
+ export const searchShortcut = () => (isMacLike() ? "⌘K" : "Ctrl K");
72
+ /**
73
+ * Whether this page offers search: an admin's page. A guest's never. #2478 (the user's request): over the public link
74
+ * too, unless the fleet keeps it off there (web.search.public_link: false — the page says data-search-link="0", the
75
+ * server refuses it), so then no entry, no shortcut, and Ctrl+F stays the browser's.
76
+ */
77
+ export function searchOffered(admin) {
78
+ const body = typeof document !== "undefined" ? document.body : null;
79
+ const offLink = !!body && body.dataset.publicLink === "1" && body.dataset.searchLink === "0";
80
+ return !!admin && !guestPage() && !offLink;
81
+ }
82
+ let searchModule = null;
83
+ /** Open search; `preset.instance`: narrowed to that agent (Ctrl+F in its chat). */
84
+ export function openSearch(preset = {}) {
85
+ closeDrawer();
86
+ (searchModule ??= import("/ui/js/search-dialog.js")).then(
87
+ (m) => showDialog(m.SearchDialog, { preset }),
88
+ () => { searchModule = null; toast(t("app.loadFailed"), false); });
89
+ }
90
+ /**
91
+ * Ctrl+K (⌘K) anywhere opens search; Ctrl+F (⌘F) in an agent's chat opens it narrowed to that agent — elsewhere the
92
+ * browser's own find stays. Not while another dialog is open (search, open already, has its input focused).
93
+ */
94
+ export function handleSearchKey(e) {
95
+ if (!searchOffered(true)) return;
96
+ if (e.isComposing || e.altKey || e.shiftKey || !(isMacLike() ? e.metaKey : e.ctrlKey)) return;
97
+ const key = (e.key || "").toLowerCase();
98
+ if (key !== "k" && key !== "f") return;
99
+ if (typeof document !== "undefined" && document.querySelector("dialog[open]")) return;
100
+ const route = navStore.get().route;
101
+ if (key === "f" && !(route && route.panel === "chat" && route.instance)) return;
102
+ e.preventDefault();
103
+ openSearch(key === "f" ? { instance: route.instance } : {});
104
+ }
105
+
65
106
  function readCollapsed() { try { return localStorage.getItem("agend_sidebar") === "hidden"; } catch { return false; } }
66
107
  const narrow = () => typeof matchMedia === "function" && matchMedia("(max-width: 899px)").matches;
67
108
 
@@ -245,6 +286,8 @@ appStore.subscribe((s) => {
245
286
 
246
287
  function statusLabel(i, exec, awaiting) {
247
288
  if (awaiting != null) return t("app.needsYou");
289
+ if (i.startup_phase === "queued") return t("app.statusQueued");
290
+ if (i.startup_phase === "starting") return t("app.statusStarting");
248
291
  if (i.status !== "running") return i.status === "crashed" ? t("app.statusCrashed") : t("app.statusStopped");
249
292
  if (exec === "working") return t("app.statusWorking");
250
293
  if (exec === "stuck") return t("app.statusStuck");
@@ -252,6 +295,8 @@ function statusLabel(i, exec, awaiting) {
252
295
  }
253
296
  export function statusClass(i, exec, awaiting) {
254
297
  if (awaiting != null) return "warn";
298
+ if (i?.startup_phase === "starting") return "busy";
299
+ if (i?.startup_phase === "queued") return "off";
255
300
  if (!i || i.status !== "running") return i && i.status === "crashed" ? "bad" : "off";
256
301
  return exec === "working" ? "busy" : exec === "stuck" ? "warn" : "ok";
257
302
  }
@@ -267,9 +312,7 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
267
312
  const app = useStore(appStore);
268
313
  const shell = useStore(shellStore);
269
314
  const canCreate = useCanCreate();
270
- // #1675: the Fleet panel is "Fleet overview"'s (one current row); the "Fleet" row is current on an instance's Details.
271
- const on = (panel) => !!route && (panel === "fleet" ? route.panel === "details" : route.panel === panel);
272
- const openInstance = openInstanceOf(route);
315
+ const on = (panel) => !!route && route.panel === panel;
273
316
  // A count badge is hidden at 0; its number is also in the link's accessible name.
274
317
  const navLink = (panel, href, icon, label, count = 0) => html`<a class=${`side-row${on(panel) ? " active" : ""}`} href=${href}
275
318
  aria-current=${on(panel) ? "page" : undefined} aria-label=${count ? `${label} (${t("app.needsCount", count)})` : undefined} onClick=${closeDrawer}>
@@ -307,10 +350,11 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
307
350
  <${SidebarResize} />
308
351
  </aside>`;
309
352
  }
310
- // #1675: the whole fleet, on its Org chart (#1389), from anywhere — top left, so at the top of a phone's drawer too. The
311
- // "Fleet" row under it goes to the open instance's Details (#1523 N2); this one is the Fleet panel, any of its tabs.
353
+ // #1675: the whole fleet, from anywhere — top left, so at the top of a phone's drawer too. #2183 (the user's call): the
354
+ // one Fleet entry ("Fleet" / "Fleet 概覽"), on the tab this device used last (the Org chart the first time); an agent's
355
+ // View, Log and Details are its header's tabs, so the sidebar has no "Fleet" (Details) or "View" row of its own.
312
356
  const onFleet = !!route && route.panel === "fleet";
313
- const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${fleetPath("org")}
357
+ const overview = html`<a class=${`side-row side-overview${onFleet ? " active" : ""}`} href=${fleetEntryPath(route)}
314
358
  aria-current=${onFleet ? "page" : undefined} onClick=${closeDrawer}><${Icon} name="org" /><span class="grow">${t("app.fleetOverview")}</span></a>`;
315
359
  return html`<aside id="sidebar" class="sidebar" aria-label=${t("app.menu")}>
316
360
  <div class="side-head" data-trust="navigation">
@@ -320,8 +364,8 @@ function Sidebar({ route, onNewInstance, viewOnly }) {
320
364
  <nav data-trust="navigation" class="side-nav" aria-label=${t("app.menu")}>
321
365
  ${overview}
322
366
  ${navLink("needs", NEEDS_PATH, "inbox", t("app.needsNav"), needsCount)}
323
- ${navLink("fleet", openInstance ? detailsPath(openInstance) : "/ui/fleet", "fleet", t("app.fleet"))}
324
- ${navLink("view", "/view", "view", t("app.view"))}
367
+ ${searchOffered(canCreate) ? html`<button type="button" class="side-row side-search" onClick=${() => openSearch()} title=${t("app.searchTitle", searchShortcut())}
368
+ aria-keyshortcuts=${isMacLike() ? "Meta+K" : "Control+K"}><${Icon} name="search" /><span class="grow">${t("app.search")}</span><kbd class="kbd-hint" aria-hidden="true">${searchShortcut()}</kbd></button>` : null}
325
369
  ${canCreate && onNewInstance ? html`<button type="button" class="side-row side-new" onClick=${onNewInstance}><${Icon} name="plus" /><span class="grow">${t("app.newAgent")}</span></button>` : null}
326
370
  </nav>
327
371
  ${shell.side ? html`<${shell.side.Component} />` : html`<${InstanceNav} route=${route} items=${app.instances} loaded=${app.ready !== false}
@@ -396,11 +440,13 @@ function PrefsPopover() {
396
440
  */
397
441
  export function PanelHeader({ title, sub, children, headingRef, nav, notice }) {
398
442
  const shell = useStore(shellStore);
399
- const canCreate = useCanCreate() && !!newInstanceOpener;
443
+ const admin = useCanCreate();
444
+ const canCreate = admin && !!newInstanceOpener;
400
445
  return html`<header data-trust="header" class=${`panel-head${shell.collapsed ? " collapsed" : ""}`}>
401
446
  <button type="button" id="sbOpen" class="icon-btn sb-open" onClick=${() => (narrow() ? openDrawer() : toggleSidebar())}
402
447
  aria-label=${narrow() ? t("app.openMenu") : t("app.expand")} title=${narrow() ? t("app.openMenu") : t("app.expand")} aria-controls="sidebar" aria-expanded=${shell.drawer ? "true" : "false"}><${Icon} name="menu" /></button>
403
448
  ${canCreate && shell.collapsed ? html`<button type="button" class="icon-btn sb-new" onClick=${requestNewInstance} aria-label=${t("app.newAgent")} title=${t("app.newAgent")}><${Icon} name="plus" /></button>` : null}
449
+ ${searchOffered(admin) ? html`<button type="button" class="icon-btn sb-search" onClick=${() => openSearch()} aria-label=${t("app.search")} title=${t("app.searchTitle", searchShortcut())}><${Icon} name="search" /></button>` : null}
404
450
  <div class="panel-title"><h1 ref=${headingRef} tabindex="-1">${title}</h1>${sub ? html`<div class="panel-sub">${sub}</div>` : null}</div>
405
451
  ${nav || null}
406
452
  <div class="panel-actions">${children}</div>
@@ -409,8 +455,8 @@ export function PanelHeader({ title, sub, children, headingRef, nav, notice }) {
409
455
  }
410
456
 
411
457
  /**
412
- * #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null. With one open, Fleet goes to its
413
- * Details (the user's complaint: Fleet always landed on Tasks), and Chat and View keep it; with none, as before.
458
+ * #1523 N2: the instance a page is about — Chat, View, Log (#1218) or Details of it — or null. #2183: with one open, the
459
+ * phone's Chat tab is current and goes to its chat.
414
460
  */
415
461
  export function openInstanceOf(route) {
416
462
  return route && (route.panel === "chat" || route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? route.instance : null;
@@ -421,14 +467,15 @@ function BottomTabs({ route, viewOnly }) {
421
467
  let last = null;
422
468
  try { last = localStorage.getItem("agend_last_instance"); } catch { /* none */ }
423
469
  const open = openInstanceOf(route);
424
- const chatHref = open && route.panel !== "view" ? chatPath(open) : route && route.panel === "chat" && route.instance ? chatPath(route.instance) : last ? chatPath(last) : "/ui";
470
+ // #2183: Chat stands for the open agent's pages (its View, Log and Details are the tabs under its header), so it goes to that agent's chat.
471
+ const chatHref = open ? chatPath(open) : last ? chatPath(last) : "/ui";
425
472
  const anyAwaiting = Object.keys(app.awaiting).length;
426
473
  const tab = (href, icon, label, active, badge, badgeLabel) => html`<a class=${`tab${active ? " active" : ""}`} href=${href} aria-current=${active ? "page" : undefined}>
427
474
  <${Icon} name=${icon} size=${20} /><span>${label}</span>${badge ? html`<span class="tab-badge" aria-label=${badgeLabel || t("app.needsYou")}>${badge}</span>` : null}</a>`;
428
- let lastView = null;
429
- try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
430
- const viewHref = route && (route.panel === "view" || route.panel === "log" || route.panel === "details") && route.instance ? viewPath(route.instance) : viewPath(lastView);
431
475
  if (viewOnly) {
476
+ let lastView = null;
477
+ try { lastView = localStorage.getItem("agend_last_view"); } catch { /* none */ }
478
+ const viewHref = route && route.panel === "view" && route.instance ? viewPath(route.instance) : viewPath(lastView);
432
479
  return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
433
480
  ${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
434
481
  ${tab(signInHref(), "user", t("app.signIn"), false)}
@@ -436,11 +483,12 @@ function BottomTabs({ route, viewOnly }) {
436
483
  }
437
484
  // #1920: a guest has only its chats (the drawer lists them): no tabs to switch between.
438
485
  if (guestPage()) return null;
486
+ // #2183 (the user's call): four tabs — Chat (current on /ui and on any of an agent's pages), Needs you, Fleet (on its
487
+ // last tab), Settings.
439
488
  return html`<nav data-trust="navigation" class="tabs" aria-label=${t("app.menu")}>
440
- ${tab(chatHref, "chat", t("app.chat"), route && route.panel === "chat", anyAwaiting)}
489
+ ${tab(chatHref, "chat", t("app.chat"), !!open || (!!route && route.panel === "chat"), anyAwaiting)}
441
490
  ${tab(NEEDS_PATH, "inbox", t("app.needsTab"), route && route.panel === "needs", (app.needs || []).length, t("app.needsCount", (app.needs || []).length))}
442
- ${tab(open ? detailsPath(open) : "/ui/fleet", "fleet", t("app.fleet"), route && (route.panel === "fleet" || route.panel === "details"))}
443
- ${tab(viewHref, "view", t("app.view"), route && route.panel === "view")}
491
+ ${tab(fleetEntryPath(route), "fleet", t("app.fleet"), route && route.panel === "fleet")}
444
492
  ${tab(settingsPath(), "settings", t("app.settings"), route && route.panel === "settings")}
445
493
  </nav>`;
446
494
  }
@@ -458,6 +506,13 @@ function ConnectionLine() {
458
506
  // (agend-auth's banner says that) draw nothing either; only reconnecting and a failed catch-up are shown.
459
507
  return null;
460
508
  }
509
+
510
+ /** A connected fleet is still bringing up agents; this is distinct from the reconnect banner. */
511
+ export function StartupLine() {
512
+ const { startup } = useStore(appStore);
513
+ if (!startup) return null;
514
+ return html`<div data-trust="status" class="conn fleet-starting" role="status">${t("app.fleetStarting", startup.ready, startup.total)}</div>`;
515
+ }
461
516
  /** #1580: the fleet is not answering (a restart): said, with when the next try is — never a silent stale page. */
462
517
  function Reconnecting() {
463
518
  const { retryAt } = useStore(appStore);
@@ -487,8 +542,21 @@ export function PendingChanges({ inline = false } = {}) {
487
542
  const { pendingChanges } = useStore(appStore);
488
543
  const list = pendingChanges || [];
489
544
  useTick(list.some(p => p.state === "pending"));
545
+ // #2168: inline (Settings' own column), a request that has just started waiting is brought into view — on a phone the
546
+ // person applied far down the list, and the card that says where to confirm it sat above the screen. Once per request.
547
+ const box = useRef(null), shown = useRef(new Set());
548
+ useEffect(() => {
549
+ if (!inline || !box.current) return;
550
+ for (const p of list) {
551
+ if (p.state !== "pending" || shown.current.has(p.id)) continue;
552
+ shown.current.add(p.id);
553
+ const card = box.current.querySelector(`[data-pending="${p.id}"]`);
554
+ const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
555
+ if (card && typeof card.scrollIntoView === "function") card.scrollIntoView({ block: "nearest", behavior: still ? "auto" : "smooth" });
556
+ }
557
+ });
490
558
  if (!list.length) return null;
491
- return html`<div data-trust="confirmation" class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} class=${`pending-card ${p.state}`} role="status">
559
+ return html`<div data-trust="confirmation" ref=${box} class=${`pending-stack${inline ? " inline" : ""}`} role="region" aria-label=${t("app.pendingTitle")}>${list.map(p => html`<section key=${p.id} data-pending=${p.id} class=${`pending-card ${p.state}`} role="status">
492
560
  <div class="pending-head"><${Icon} name=${p.state === "applied" ? "check" : p.state === "pending" || p.state === "applying" ? "clock" : "alert"} size=${16} />
493
561
  <strong>${t(`app.pending_${p.state}`)}</strong>
494
562
  ${p.state === "pending" ? html`<span class="pending-left">${t("app.pendingLeft", mmss(p.deadline - NOW()))}</span>` : null}
@@ -496,6 +564,7 @@ export function PendingChanges({ inline = false } = {}) {
496
564
  ${p.label ? html`<div class="pending-label">${p.label}</div>` : null}
497
565
  ${Array.isArray(p.summary) && p.summary.length ? html`<ul class="pending-summary">${p.summary.map((line, i) => html`<li key=${i}>${line}</li>`)}</ul>` : null}
498
566
  ${p.state === "pending" ? html`<p class="note">${p.confirmation && p.confirmation.kind === "host_cli" ? t("app.pendingHost", `agend settings confirm ${p.id}`) : t("app.pendingChat")}</p>` : null}
567
+ ${(p.state === "applying" || p.state === "applied") && p.confirmation && p.confirmation.kind === "web" ? html`<p class="note">${t("app.pendingWeb")}</p>` : null}
499
568
  ${p.state !== "pending" && p.state !== "applying" && p.state !== "applied" && p.outcome && p.outcome.message ? html`<p class="note">${p.outcome.message}</p>` : null}
500
569
  ${p.withdraw ? html`<div class="pending-actions"><button type="button" class="btn btn-sm" onClick=${p.withdraw}>${t("app.withdraw")}</button></div>` : null}
501
570
  </section>`)}</div>`;
@@ -545,30 +614,105 @@ function Outlet({ route, seq, panels }) {
545
614
  return html`<${C} route=${route} navKey=${`${routeKey(route)}|${seq}|${lang()}`} />`;
546
615
  }
547
616
 
617
+ /**
618
+ * #2196: the open agent's colours on the whole page (its theme's page section, or a person's choice), drawn on the
619
+ * shell in the same render that opens it — no fetch (the themes are on the page already), no flash, colours only, so
620
+ * no layout moves. Redrawn when the themes, the person's choices, the viewer's switch or the light/dark mode move.
621
+ */
622
+ function usePageTheme(ref, route) {
623
+ useStore(themesStore);
624
+ const [, bump] = useState(0);
625
+ useEffect(() => onPrefsChanged((changed) => { if (changed.has("theme.pageByInstance") || changed.has("fx.show")) bump((n) => n + 1); }), []);
626
+ useEffect(() => {
627
+ const T = globalThis.AgendTheme;
628
+ const off = T && typeof T.onChange === "function" ? T.onChange(() => bump((n) => n + 1)) : null;
629
+ let mq = null;
630
+ try { mq = globalThis.matchMedia ? globalThis.matchMedia("(prefers-color-scheme: light)") : null; } catch { mq = null; }
631
+ const onMq = () => bump((n) => n + 1);
632
+ if (mq && typeof mq.addEventListener === "function") mq.addEventListener("change", onMq);
633
+ return () => { if (typeof off === "function") off(); if (mq && typeof mq.removeEventListener === "function") mq.removeEventListener("change", onMq); };
634
+ }, []);
635
+ const mode = resolvedMode();
636
+ const p = pageNow(mode, route);
637
+ const key = p ? `${mode}|${JSON.stringify(p)}` : "";
638
+ useLayoutEffect(() => { applyPageTheme(ref.current, p, mode); }, [key]);
639
+ // #2199: the agent open now, as body[data-agent] — for a person's own custom CSS to follow (body[data-agent="x"]
640
+ // { --bg-sidebar: … }); set and cleared in the same render as the page colours, whatever the themes switch says.
641
+ const agent = openInstanceOf(route);
642
+ useLayoutEffect(() => {
643
+ const body = typeof document !== "undefined" ? document.body : null;
644
+ if (!body || !body.dataset) return;
645
+ if (agent) body.dataset.agent = agent; else delete body.dataset.agent;
646
+ }, [agent]);
647
+ }
648
+
649
+ /** #2478: how far short of its tallest the view must be, with a text field focused, to be a keyboard (not a browser bar). */
650
+ export const KEYBOARD_MIN_PX = 150;
651
+ /** #2478: the on-screen keyboard is up — something is being typed into, and the view is well short of its tallest. */
652
+ export function keyboardUp(height, tallest, typing) { return !!typing && tallest - height > KEYBOARD_MIN_PX; }
653
+ const NOT_TYPED = /^(button|checkbox|radio|range|color|file|submit|reset|image|hidden)$/i;
654
+ /** #2478: an element the keyboard types into — a textarea, a text-like input, or an editable region. */
655
+ export function isTextEntry(el) {
656
+ if (!el || !el.tagName) return false;
657
+ const tag = String(el.tagName).toUpperCase();
658
+ if (tag === "TEXTAREA") return !el.readOnly && !el.disabled;
659
+ if (tag === "INPUT") return !NOT_TYPED.test(el.type || "text") && !el.readOnly && !el.disabled;
660
+ return el.isContentEditable === true;
661
+ }
662
+
548
663
  export function Shell({ panels, onNewInstance, viewOnly = false }) {
549
664
  const nav = useStore(navStore);
550
665
  const shell = useStore(shellStore);
666
+ const root = useRef(null);
667
+ usePageTheme(root, nav.route);
551
668
  const [, relang] = useState(0);
552
669
  // A language switch re-renders everything and gives the panel a new lease (its key includes the language).
553
670
  useEffect(() => onLang(() => relang(n => n + 1)), []);
554
671
  useEffect(() => { closeDrawer(); }, [nav.seq]);
555
- // The bottom tabs give their room to the keyboard on a phone.
672
+ // The bottom tabs (and the line under the composer) give their room to the keyboard on a phone (#2478).
556
673
  useEffect(() => {
557
674
  const vv = typeof window !== "undefined" ? window.visualViewport : null;
558
- if (!vv) return undefined;
559
- const onResize = () => shellStore.set({ keyboard: window.innerHeight - vv.height > 150 });
560
- vv.addEventListener("resize", onResize);
561
- return () => vv.removeEventListener("resize", onResize);
675
+ if (!vv || typeof document === "undefined") return undefined;
676
+ // The tallest the view has been at this width: what it is with no keyboard. innerHeight is no measure on its own —
677
+ // it shrinks with the keyboard on iOS Safari and, under interactive-widget=resizes-content, on Android — so
678
+ // innerHeight − height stayed near 0 and the strips stayed between the composer and the keyboard.
679
+ // Only a touch screen (a coarse pointer) has an on-screen keyboard: a narrow desk window made shorter is not one.
680
+ // Turned with the keyboard up and the field still focused, the new width has no height without it yet: the keyboard
681
+ // stays up until the field is left, or the view grows from its lowest by more than a keyboard (put away).
682
+ let width = -1, tallest = 0, held = false, lowest = 0;
683
+ const coarse = () => typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
684
+ const check = () => {
685
+ const w = Math.round(vv.width), h = vv.height;
686
+ const typing = coarse() && isTextEntry(document.activeElement);
687
+ if (w !== width) { // turned, or a new width: measured again
688
+ held = width !== -1 && typing && shellStore.get().keyboard;
689
+ width = w; tallest = 0; lowest = h;
690
+ }
691
+ if (held) {
692
+ lowest = Math.min(lowest, h);
693
+ if (!typing || h - lowest > KEYBOARD_MIN_PX) held = false; // left the field, or the keyboard was put away
694
+ }
695
+ if (!held) tallest = Math.max(tallest, window.innerHeight || 0, typing ? 0 : h); // the view itself only when not typing
696
+ const up = held || keyboardUp(h, tallest, typing);
697
+ if (up !== shellStore.get().keyboard) shellStore.set({ keyboard: up });
698
+ };
699
+ check();
700
+ vv.addEventListener("resize", check);
701
+ document.addEventListener("focusin", check);
702
+ document.addEventListener("focusout", check);
703
+ return () => { vv.removeEventListener("resize", check); document.removeEventListener("focusin", check); document.removeEventListener("focusout", check); };
562
704
  }, []);
563
705
  const guest = guestPage(); // #1920: a guest's page — no operations, usage, pending changes or owner CSS
564
706
  const cls = ["shell", shell.collapsed ? "sb-collapsed" : "", shell.drawer ? "sb-open" : "", shell.keyboard ? "kb-open" : "", guest ? "guest" : ""].filter(Boolean).join(" ");
565
- return html`<div class=${cls}>
707
+ return html`<div ref=${root} class=${cls}>
566
708
  <a data-trust="navigation" class="skip" href="#main">${t("app.skip")}</a>
567
709
  <${Sidebar} route=${nav.route} onNewInstance=${onNewInstance} viewOnly=${viewOnly} />
568
710
  <div class="scrim" onClick=${closeDrawer} aria-hidden="true"></div>
569
711
  <main id="main" class="main" tabindex="-1" inert=${shell.drawer && narrow() ? true : undefined}>
570
712
  <${ConnectionLine} />
713
+ <${StartupLine} />
571
714
  ${viewOnly || guest ? null : html`<${OperationLine} route=${nav.route} />`}
715
+ ${viewOnly || guest || !shell.banner ? null : html`<${shell.banner} />`}
572
716
  <${Outlet} route=${nav.route} seq=${nav.seq} panels=${panels} />
573
717
  </main>
574
718
  ${guest ? null : html`<${UsageRail} route=${nav.route} />`}
@@ -24,7 +24,7 @@ export function createStore(initial) {
24
24
  * (app-stream.js catchUp) — "catching" / "retrying" / "failed" / "ok"; `retryHydration` asks for it again.
25
25
  * `connections`: how many chat connections the fleet has (#1519 P7: 0 is web-only — the first-run card); null until known. */
26
26
  export const appStore = createStore({ ready: false, instances: [], uptime: 0, exec: {}, awaiting: {}, connection: "connecting", needs: [], connections: null,
27
- hydration: "none", retryHydration: null });
27
+ hydration: "none", retryHydration: null, canvasWorker: typeof document !== "undefined" && document.body?.dataset.canvasWorker === "1" });
28
28
 
29
29
  /**
30
30
  * #1920 phase 3: this page is a guest's — the server says so on <body> (data-tier="guest"). A guest sees only its own
@@ -49,7 +49,8 @@ export function applyStatus(d) {
49
49
  exec[i.name] = (i.execution_state !== undefined ? i.execution_state : i.state) ?? null;
50
50
  if (i.state === "awaiting_input") awaiting[i.name] = typeof i.interaction_summary === "string" ? i.interaction_summary : "";
51
51
  }
52
- appStore.set({ ready: true, instances: d.instances, uptime: d.uptime ?? 0, exec, awaiting,
52
+ appStore.set({ canvasWorker: d.canvas_worker === true, ready: true, instances: d.instances, uptime: d.uptime ?? 0, exec, awaiting,
53
+ startup: d.startup?.phase === "starting" ? d.startup : null,
53
54
  connections: typeof d.connections === "number" ? d.connections : null,
54
55
  // #1665: the host preferences' revision (app-prefs.js re-reads them when it moves).
55
56
  prefsRev: typeof d.prefs_rev === "number" ? d.prefs_rev : null,
@@ -32,7 +32,10 @@
32
32
  // #1218: steps_tick — an instance's transcript moved while a Log watches it (SSE only: a poll page asks the Log itself).
33
33
  // #1918: traffic — a message between instances moved; delegation — a task opened, or its report closed it (SSE only: the
34
34
  // org canvas reads /ui/traffic when it has no stream).
35
- const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "steps_tick", "clock", "traffic", "delegation"];
35
+ // #2233: backend_login — a dashboard sign-in started, moved or ended (no details: the page reads its own view).
36
+ // tasks — the task board changed: the card numbers a write touched (SSE only: the board reads again when it is shown).
37
+ // offices — the stored offices changed ({ rev }; SSE only: the Office view reads /ui/offices again).
38
+ const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "reactions", "steps_tick", "clock", "traffic", "delegation", "backend_login", "tasks", "offices"];
36
39
  const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
37
40
  const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
38
41
  const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed
@@ -126,6 +129,7 @@ export function createStream(opts) {
126
129
  if (Array.isArray(d.deliveries)) emit("deliveries", d.deliveries);
127
130
  if (Array.isArray(d.prompts)) emit("prompts", d.prompts);
128
131
  if (Array.isArray(d.reply_buttons)) for (const u of d.reply_buttons) emit("reply_buttons", u); // #1266
132
+ if (Array.isArray(d.reactions)) for (const u of d.reactions) emit("reactions", u); // #2305
129
133
  if (Array.isArray(d.needs)) emit("needs", { items: d.needs });
130
134
  return true;
131
135
  } catch { return false; /* the next tick, or the catch-up's retry, tries again */ }
@@ -1,9 +1,12 @@
1
1
  // #1629 (first design §5, r2 (b)(d)): agents' web themes on the page. Each applies to THAT agent's own chat surface
2
- // (and its avatar ring) only — never the shell, the sidebar chrome or another instance. What the host sends is
2
+ // (and its avatar ring) — and, #2196, when the agent (theme.page.on) or a person (theme.pageByInstance) says so, its
3
+ // colours on the whole page while that agent's Chat, View, Log or Details is open: colour tokens only, on the shell
4
+ // (applyPageTheme); AgEnD's status colours, fonts, pictures and layout stay the page's own. What the host sends is
3
5
  // canonical already; this checks it again (a value that fails is dropped, never passed on) and applies it through the
4
6
  // CSSOM (custom properties) and fixed class tokens. No style attribute, no CSS text, no url() built in CSS.
5
7
  // Not shown: with "Show agents' themes, effects and widgets" off (fx.show, synced), and in safe mode (?safe=1).
6
8
  import { appStore, createStore } from "./app-store.js";
9
+ import { navStore } from "./app-nav.js";
7
10
  import { onPrefsChanged, prefInForce, savePref } from "./app-prefs.js";
8
11
  import { safeMode } from "./owner-css.js";
9
12
  import { t } from "./app-i18n.js";
@@ -86,9 +89,110 @@ export function themeFor(name) {
86
89
  out.background = { image: bg.image, fit: bg.fit, opacity: bg.opacity };
87
90
  }
88
91
  if (labelOk(t.workingLabel)) out.workingLabel = t.workingLabel;
92
+ if (t.page && typeof t.page === "object") {
93
+ const sb = {};
94
+ if (t.page.sidebar && typeof t.page.sidebar === "object") for (const m of ["light", "dark"]) if (typeof t.page.sidebar[m] === "string" && COLOR.test(t.page.sidebar[m])) sb[m] = t.page.sidebar[m];
95
+ out.page = { on: t.page.on === true, sidebar: sb };
96
+ }
89
97
  return out;
90
98
  }
91
99
 
100
+ // ── #2196: an agent's colours on the whole page ──
101
+
102
+ /** The token defaults (tokens.css; web-theme.ts THEME_DEFAULTS, pinned by a test) a theme's palette goes over. */
103
+ const PAGE_DEFAULTS = {
104
+ dark: { bg: "#212121", surface: "#2f2f2f", text: "#ececec", text2: "#b4b4b4", accent: "#7ab7ff" },
105
+ light: { bg: "#ffffff", surface: "#f4f4f4", text: "#0d0d0d", text2: "#5d5d5d", accent: "#0a62c2" },
106
+ };
107
+ /** AgEnD's status colours (tokens.css; web-theme.ts STATUS_COLORS, pinned by a test): never themed, always readable. */
108
+ export const STATUS_COLORS = {
109
+ dark: { ok: "#4cc38a", warn: "#e3b341", danger: "#ff8a80", dangerFill: "#c9372c", focus: "#7ab7ff" },
110
+ light: { ok: "#1a7f37", warn: "#8a5a00", danger: "#c4222b", dangerFill: "#c4222b", focus: "#0a62c2" },
111
+ };
112
+ const PAGE_PROPS = { bg: "--bg", sidebar: "--bg-sidebar", surface: "--surface", text: "--text", text2: "--text-2", accent: "--accent" };
113
+ const DERIVED_PROPS = ["--text-3", "--surface-2", "--border", "--border-strong", "--accent-dim", "--on-accent"];
114
+ function lum(hex) {
115
+ const c = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255).map((x) => (x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4));
116
+ return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
117
+ }
118
+ const ratio = (a, b) => { const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); };
119
+ const mix = (a, b, f) => `#${[1, 3, 5].map((i) => Math.round(parseInt(a.slice(i, i + 2), 16) * (1 - f) + parseInt(b.slice(i, i + 2), 16) * f).toString(16).padStart(2, "0")).join("")}`;
120
+
121
+ /** The instance a page is about — Chat, View, Log or Details of it (app-shell.js openInstanceOf) — or null. */
122
+ function openOf(route) {
123
+ return route && ["chat", "view", "log", "details"].includes(route.panel) && typeof route.instance === "string" ? route.instance : null;
124
+ }
125
+ /** A person's choice for `name`: "page" (the whole page), "chat" (only its chat) or null (the agent's own). */
126
+ export function pageChoice(name) {
127
+ const f = prefInForce("theme.pageByInstance");
128
+ const m = f && f.value;
129
+ return m && typeof m === "object" && own(m, name) && (m[name] === "page" || m[name] === "chat") ? m[name] : null;
130
+ }
131
+ /** Set it ("agent" clears it), kept with the host's preferences like the identity colour a person picks. */
132
+ export function setPageChoice(name, choice) {
133
+ const f = prefInForce("theme.pageByInstance");
134
+ const cur = f && f.value && typeof f.value === "object" ? f.value : {};
135
+ const keep = Object.keys(cur).filter((k) => own(cur, k) && k !== name && (cur[k] === "page" || cur[k] === "chat")).map((k) => [k, cur[k]]);
136
+ if (choice === "page" || choice === "chat") keep.push([name, choice]);
137
+ savePref("theme.pageByInstance", keep.length ? Object.fromEntries(keep) : null);
138
+ // In force on this page at once: what draws a theme follows this store (another device's change: onPrefsChanged).
139
+ themesStore.set({ pageRev: (themesStore.get().pageRev || 0) + 1 });
140
+ }
141
+ /** `name`'s theme when its colours go on the whole page (the person's choice, else the agent's), else null. */
142
+ export function pageThemeFor(name) {
143
+ if (!name) return null;
144
+ const theme = themeFor(name);
145
+ if (!theme) return null;
146
+ const choice = pageChoice(name);
147
+ return choice === "page" || (choice !== "chat" && theme.page && theme.page.on) ? theme : null;
148
+ }
149
+ /** The page's colours for `mode`: the theme's palette over the defaults, and its sidebar (else its background). */
150
+ export function pagePalette(theme, mode) {
151
+ const p = { ...PAGE_DEFAULTS[mode], ...(theme.accent ? { accent: theme.accent } : {}), ...((theme.colors && theme.colors[mode]) || {}) };
152
+ const out = {};
153
+ for (const k of Object.keys(PAGE_DEFAULTS[mode])) out[k] = p[k];
154
+ out.sidebar = (theme.page && theme.page.sidebar && theme.page.sidebar[mode]) || out.bg;
155
+ return out;
156
+ }
157
+ /**
158
+ * Whether a page palette reads (web-theme.ts's checks, again here: a person may put an agent's colours on the page
159
+ * though the agent never asked): text and secondary text 4.5:1 and the accent 3:1 on the background, surface and
160
+ * sidebar; every status colour 3:1 on the sidebar and the background.
161
+ */
162
+ export function pageReadable(p, mode) {
163
+ if (!p || !Object.values(p).every((c) => typeof c === "string" && COLOR.test(c))) return false;
164
+ for (const bg of [p.bg, p.surface, p.sidebar]) {
165
+ if (ratio(p.text, bg) < 4.5 - 1e-9 || ratio(p.text2, bg) < 4.5 - 1e-9) return false;
166
+ }
167
+ if (ratio(p.accent, p.bg) < 3 - 1e-9 || ratio(p.accent, p.sidebar) < 3 - 1e-9) return false;
168
+ return Object.values(STATUS_COLORS[mode]).every((c) => ratio(c, p.sidebar) >= 3 - 1e-9 && ratio(c, p.bg) >= 3 - 1e-9);
169
+ }
170
+ /** The page colours in force now for `mode` (the instance the route is about), or null: the page's own. */
171
+ export function pageNow(mode, route = navStore.get().route) {
172
+ const theme = pageThemeFor(openOf(route));
173
+ const p = theme && pagePalette(theme, mode);
174
+ return p && pageReadable(p, mode) ? p : null;
175
+ }
176
+ /**
177
+ * Draw page colours `p` (pageNow) on the shell `el`, or clear them (null): the palette's tokens and the few derived
178
+ * from it (borders, raised rows, the accent's tint and the colour on it), through the CSSOM; class th-page.
179
+ */
180
+ export function applyPageTheme(el, p, mode) {
181
+ if (!el || !el.style) return;
182
+ for (const prop of [...Object.values(PAGE_PROPS), ...DERIVED_PROPS]) el.style.removeProperty(prop);
183
+ el.classList.remove("th-page");
184
+ if (!p) return;
185
+ for (const [k, prop] of Object.entries(PAGE_PROPS)) el.style.setProperty(prop, p[k]);
186
+ el.style.setProperty("--text-3", p.text2);
187
+ el.style.setProperty("--surface-2", mix(p.surface, p.text, 0.08));
188
+ el.style.setProperty("--border", mix(p.bg, p.text, 0.14));
189
+ el.style.setProperty("--border-strong", mix(p.bg, p.text, 0.45));
190
+ const [r, g, b] = [1, 3, 5].map((i) => parseInt(p.accent.slice(i, i + 2), 16));
191
+ el.style.setProperty("--accent-dim", `rgba(${r}, ${g}, ${b}, ${mode === "dark" ? 0.14 : 0.08})`);
192
+ el.style.setProperty("--on-accent", ratio("#ffffff", p.accent) >= ratio("#0d0d0d", p.accent) ? "#ffffff" : "#0d0d0d");
193
+ el.classList.add("th-page");
194
+ }
195
+
92
196
  export function isThemeLocked(name) { return (themesStore.get().locked || []).includes(name); }
93
197
 
94
198
  const TOKENS = ["th", "th-font-inter", "th-font-system", "th-font-mono", "th-bubble-none", "th-bubble-thin", "th-bubble-accent", "th-agent-bubble"];
@@ -193,7 +297,18 @@ export function themeMenuItems(name) {
193
297
  const locked = isThemeLocked(name);
194
298
  const w = themesStore.get().widgets;
195
299
  const placed = w && own(w, name) && w[name] && typeof w[name] === "object" ? w[name] : {};
300
+ // #2196: where this agent's colours go — its own choice, the whole page, or only its chat (the person's wins).
301
+ const choice = pageChoice(name), theme = themeFor(name), mode = resolvedMode();
302
+ const agentsOwn = theme && theme.page && theme.page.on ? "chat.pageAgentOn" : "chat.pageAgentOff";
303
+ const readable = !!theme && pageReadable(pagePalette(theme, mode), mode);
304
+ const page = theme ? [
305
+ { key: "page-heading", heading: true, label: t("chat.pageColours") },
306
+ { key: "page-agent", checked: choice === null, label: t(agentsOwn), onSelect: () => setPageChoice(name, "agent") },
307
+ { key: "page-page", checked: choice === "page", label: t("chat.pageWhole"), disabled: !readable && choice !== "page", onSelect: () => setPageChoice(name, "page") },
308
+ { key: "page-chat", checked: choice === "chat", label: t("chat.pageChatOnly"), onSelect: () => setPageChoice(name, "chat") },
309
+ ] : [];
196
310
  return [
311
+ ...page,
197
312
  ...["chat_header", "details"].filter((slot) => own(placed, slot)).map((slot) => ({
198
313
  key: `widget-remove-${slot}`, label: t(slot === "chat_header" ? "chat.widgetRemoveHeader" : "chat.widgetRemoveDetails"), icon: "trash",
199
314
  onSelect: () => removeWidget(name, slot),