@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
@@ -8,8 +8,9 @@
8
8
  // #1665 first: the host's preferences go into this browser's cache before any module below reads its key.
9
9
  import { bootPrefs } from "./app-prefs.js";
10
10
  import { html, render } from "./app-html.js";
11
- import { Shell, handleKey, showDialog, closeDrawer, openDrawer, shellStore, retryable, setNewInstanceOpener } from "./app-shell.js";
11
+ import { Shell, handleKey, handleSearchKey, showDialog, closeDrawer, openDrawer, shellStore, retryable, setNewInstanceOpener, isMacLike } from "./app-shell.js";
12
12
  import { installSwipe } from "./app-swipe.js";
13
+ import { installTextUndo } from "./text-undo.js";
13
14
  import { startRouter, navStore, navigate } from "./app-nav.js";
14
15
  import { createStream } from "./app-stream.js";
15
16
  import { appStore, applyStatus, applyActivity, guestPage } from "./app-store.js";
@@ -93,6 +94,8 @@ if (mode === "full") {
93
94
  panels.set("log", { load: () => loadLog().then((m) => m.LogPanel) });
94
95
  // #1386 §6.3: desktop notifications for new "Needs you" items, in any panel, while this signed-in page is open.
95
96
  startNeedsNotifier({ open: (instance) => navigate(chatPath(instance)) });
97
+ // #2233: a backend sign-in running anywhere is shown on every admin page (the module lives behind the session gate).
98
+ import("/ui/js/backend-signin.js").then((m) => m.installSignIn(), () => {});
96
99
  }
97
100
  // #1665: this device's first upload of its own choices (once), then another device's changes at each status frame.
98
101
  // A guest's page only reads them (prefsWritable is false there).
@@ -121,6 +124,11 @@ if (!guest) installOwnerCss();
121
124
  }
122
125
 
123
126
  document.addEventListener("keydown", handleKey);
127
+ // #2293: Ctrl+K / ⌘K (and Ctrl+F in a chat) — an admin's full app only; the server refuses a guest's search anyway.
128
+ if (mode === "full" && !guest) document.addEventListener("keydown", handleSearchKey);
129
+ // #2342: Ctrl/⌘+Z and Ctrl+Shift+Z / Ctrl+Y / ⌘⇧Z in every text field, one history per field (the browser's is one
130
+ // for the whole page, and any script write wipes it).
131
+ installTextUndo({ mac: isMacLike() });
124
132
  // #1605: on a phone (the sidebar is a drawer), swipe right in the main area to open it, left to close it — the same
125
133
  // open/close as ☰ and the scrim. Nothing that owns a sideways drag (code, tables, the terminal, inputs, dialogs) and
126
134
  // never the left edge (iOS: "back").
@@ -11,8 +11,10 @@ register("card", {
11
11
  sizeSName: "Small", sizeMName: "Medium", sizeLName: "Large",
12
12
  byHuman: "set by a person",
13
13
  byAgent: "set by the agent",
14
+ menu: "Identity card",
14
15
  show: "Show card",
15
16
  hide: "Hide card",
17
+ showRow: "Show picture and bio",
16
18
  bannerSafe: "Every screen",
17
19
  bannerPhone: "Phone · M card",
18
20
  },
@@ -24,8 +26,10 @@ register("card", {
24
26
  sizeSName: "小", sizeMName: "中", sizeLName: "大",
25
27
  byHuman: "由人設定",
26
28
  byAgent: "由 agent 設定",
29
+ menu: "身分卡",
27
30
  show: "顯示卡片",
28
31
  hide: "隱藏卡片",
32
+ showRow: "顯示小圖和介紹",
29
33
  bannerSafe: "所有螢幕",
30
34
  bannerPhone: "手機 · 中卡片",
31
35
  },
@@ -13,7 +13,7 @@ const episodes = new WeakSet();
13
13
 
14
14
  /** `current` belongs to the page/session episode. Route cleanup only releases its contribution. */
15
15
  export function createAppEffectRuntime({ window: host, current, observe, onChange = () => {}, onDispose = () => {}, resolveOwnerTarget = null, onOwnerUnavailable = () => {}, factories = {} }) {
16
- let dead = false, error = null, loader = null, registry = null, writer = null, source = null, personal = null, trial = null, pending = null, lastMetadata = null, serial = 0, revision = 0, stamp = null, syncing = false, queued = false;
16
+ let dead = false, claimed = false, error = null, loader = null, registry = null, writer = null, source = null, personal = null, trial = null, pending = null, lastMetadata = null, serial = 0, revision = 0, stamp = null, syncing = false, queued = false;
17
17
  const bindings = new Map(), profiles = new Map();
18
18
  let lastContext = null, playbackUnavailable = null;
19
19
  const identity = effectPlaybackIdentity(host), auth = host?.AgendAuth;
@@ -26,7 +26,7 @@ export function createAppEffectRuntime({ window: host, current, observe, onChang
26
26
  };
27
27
  const placementIdentity = p => JSON.stringify([p.by, p.manifest.layer, p.params]);
28
28
  const clock = () => host.performance.now();
29
- let offAuth = null;
29
+ let offAuth = null, disposed = null;
30
30
  const snapshot = s => {
31
31
  const effects = s?.metadata?.effects ?? [], known = registry?.snapshot?.() ?? [];
32
32
  const rows = effects.map(p => {
@@ -76,16 +76,18 @@ export function createAppEffectRuntime({ window: host, current, observe, onChang
76
76
  if (pending?.source === s) safe(() => pending.controller.abort());
77
77
  revision++; stamp = null;
78
78
  }
79
- function dispose() {
80
- if (dead) return; dead = true; discard(source);
79
+ function dispose(restartable = false) {
80
+ if (dead) return disposed; dead = true; discard(source);
81
81
  safe(() => trial?.dispose()); safe(() => pending?.controller.abort()); safe(() => registry?.dispose?.()); safe(() => loader?.dispose?.()); safe(() => personal?.dispose()); safe(() => writer?.dispose()); safe(() => offAuth?.());
82
82
  safe(() => host?.removeEventListener?.("pagehide", dispose));
83
83
  for (const row of profiles.values()) safe(() => row.collector.dispose()); profiles.clear(); bindings.clear(); safe(() => onDispose({ error }));
84
+ disposed = Promise.all([safe(() => loader?.whenDisposed?.()), safe(() => registry?.whenDisposed?.())]).then(() => { if (restartable === true && claimed) episodes.delete(host); });
85
+ return disposed;
84
86
  }
85
87
  function fail(reason) { if (dead) return; error = reason; dispose(); safe(() => onChange({ error, rows: [], effects: [], loading: false })); }
86
88
  const unavailable = { ok: false, sync() {}, changedChat() {}, reply() {}, detachChat() {}, attachChat() { return null; }, attachTrial() { return null; }, stopTrial() {}, trialSnapshot: () => ({ rows: [] }), snapshot: () => snapshot(null), dispose };
87
89
  if (!host || typeof host !== "object" || episodes.has(host) || !identity || typeof current !== "function" || typeof observe !== "function" || typeof auth?.onSessionEnd !== "function" || typeof auth?.sessionEnded !== "function") return unavailable;
88
- episodes.add(host); // Claim before invoking current/auth callbacks: one attempt per Window episode.
90
+ episodes.add(host); claimed = true; // Claim before invoking current/auth callbacks: one attempt per Window episode.
89
91
  if (!alive()) { dispose(); return unavailable; }
90
92
  try {
91
93
  offAuth = auth.onSessionEnd(dispose);
@@ -1,3 +1,4 @@
1
+ import { chatEffectIdentity } from "./effect-display-metadata.js";
1
2
  /** #1629: one committed local Chat lease joins the reviewed host-only effect primitives. */
2
3
  import { createEffectRuntimeLoader } from "./effect-runtime-loader.js";
3
4
  import { createEffectRegistry } from "./effect-registry.js";
@@ -43,13 +44,7 @@ export function createEffectWriter(host, current) {
43
44
  }
44
45
 
45
46
  /** Frozen, server-rendered session identity only. A header-token/public/View load has no identity. */
46
- export function chatEffectIdentity(host) {
47
- try {
48
- const b = host.document.body.dataset, x = JSON.parse(host.document.documentElement.getAttribute("data-prefs") || "null");
49
- if (b.mode !== "full" || b.tier === "guest" || b.publicLink === "1" || b.webTransport === "poll" || !["person", "operator"].includes(x?.ownerScope) || x.unreadable === true || typeof x.ownerEpoch !== "string" || !/^[a-f0-9]{16}$/.test(x.ownerEpoch)) return null;
50
- return Object.freeze({ epoch: x.ownerEpoch, scope: x.ownerScope });
51
- } catch { return null; }
52
- }
47
+ export { chatEffectIdentity } from "./effect-display-metadata.js";
53
48
  /** Chat-only public/guest capability stays separate from personal preferences and management. */
54
49
  export function effectPlaybackIdentity(host) {
55
50
  try {
@@ -25,9 +25,10 @@ export function createEffectCursor({ window: host, current, readImage, onChange
25
25
  const unavailable = { ok: false, update() {}, snapshot: () => ({ status: "unavailable", id: null }), dispose() {} };
26
26
  if (!host?.document || owners.has(host) || ![current, readImage, onChange, host.getComputedStyle, host.Blob, host.URL?.createObjectURL, host.URL?.revokeObjectURL, host.AbortController, host.setTimeout, host.clearTimeout, host.performance?.now].every(x => typeof x === "function")) return unavailable;
27
27
  const token = {}; owners.set(host, token);
28
+ let finishDisposed; const disposed = new Promise(resolve => { finishDisposed = resolve; });
28
29
  const doc = host.document; let dead = false, previous = null, wanted = null, pending = null, active = null, blocked = null, generation = 0, checking = false, notifying = false, status = "off";
29
30
  const safe = fn => { try { return fn(); } catch { return null; } };
30
- const release = () => { if (dead && !pending && owners.get(host) === token) owners.delete(host); };
31
+ const release = () => { if (dead && !pending) { if (owners.get(host) === token) owners.delete(host); finishDisposed(); } };
31
32
  function alive() {
32
33
  if (dead || checking) return false;
33
34
  checking = true; const value = safe(() => current() === true && host.document === doc); checking = false;
@@ -121,5 +122,5 @@ export function createEffectCursor({ window: host, current, readImage, onChange
121
122
  }
122
123
  start(); tell();
123
124
  }
124
- return { ok: true, update, snapshot, dispose };
125
+ return { ok: true, update, snapshot, dispose, whenDisposed: () => disposed };
125
126
  }
@@ -0,0 +1,39 @@
1
+ /** #1629: closed host display metadata. Neither this object nor its token grants a trial read. */
2
+ const KEYS = ["v", "offer", "id", "title", "expiresAt"], ID = /^[0-9a-f]{32}$/;
3
+ const FORBIDDEN = /(?!\u200d)[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
4
+ const VISIBLE = /[^\p{M}\p{C}\p{Z}\u115f\u1160\u3164\uffa0\u2800]/u;
5
+ let graphemes = null;
6
+ export function checkEffectTrialOffer(value) {
7
+ try {
8
+ if (!value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return null;
9
+ const keys = Reflect.ownKeys(value);
10
+ if (keys.length !== KEYS.length || keys.some(k => typeof k !== "string" || !KEYS.includes(k))) return null;
11
+ const x = Object.create(null);
12
+ for (const key of KEYS) {
13
+ const d = Object.getOwnPropertyDescriptor(value, key);
14
+ if (!d || !("value" in d) || !d.enumerable) return null;
15
+ x[key] = d.value;
16
+ }
17
+ if (x.v !== 1 || typeof x.offer !== "string" || !ID.test(x.offer) || typeof x.id !== "string" || !ID.test(x.id)
18
+ || typeof x.title !== "string" || x.title.length > 400 || x.title.normalize("NFC") !== x.title || x.title.trim() !== x.title
19
+ || FORBIDDEN.test(x.title) || !VISIBLE.test(x.title) || !Number.isSafeInteger(x.expiresAt) || x.expiresAt < 0) return null;
20
+ graphemes ??= new Intl.Segmenter(undefined, { granularity: "grapheme" });
21
+ let count = 0; for (const _part of graphemes.segment(x.title)) if (++count > 40) return null;
22
+ if (count < 1) return null;
23
+ return Object.freeze({ v: 1, offer: x.offer, id: x.id, title: x.title, expiresAt: x.expiresAt });
24
+ } catch { return null; }
25
+ }
26
+ /** Role and sender are host fields, never inferred from a message's text. Start must separately authorize. */
27
+ export function effectTrialMessage(message) {
28
+ if (!message || message.role !== "status" || message.sender !== "agend" || typeof message.instance !== "string"
29
+ || message.instance.length < 1 || message.instance.length > 128) return null;
30
+ return checkEffectTrialOffer(message.effectTrial);
31
+ }
32
+
33
+ export function chatEffectIdentity(host) {
34
+ try {
35
+ const b = host.document.body.dataset, x = JSON.parse(host.document.documentElement.getAttribute("data-prefs") || "null");
36
+ if (b.mode !== "full" || b.tier === "guest" || b.publicLink === "1" || b.webTransport === "poll" || !["person", "operator"].includes(x?.ownerScope) || x.unreadable === true || typeof x.ownerEpoch !== "string" || !/^[a-f0-9]{16}$/.test(x.ownerEpoch)) return null;
37
+ return Object.freeze({ epoch: x.ownerEpoch, scope: x.ownerScope });
38
+ } catch { return null; }
39
+ }
@@ -74,7 +74,14 @@ export function createOwnerEffectSource({ current, readMetadata, readResource, r
74
74
  },
75
75
  descriptors() {
76
76
  if (!alive() || !metadata || error) return [];
77
- const body = metadata, gen = generation, proof = safe(() => effectStockSnapshot(stock()));
77
+ const body = metadata, gen = generation;
78
+ // #2188: the stock snapshot walks the whole page (thousands of computed styles). With no slot holding an effect
79
+ // that could play, there is nothing for it to admit: skipped — every page change (a status, a chat switch) ran it.
80
+ if (!SLOTS.some(slot => body.slots[slot]?.status === "available" && failed.get(slot) !== body.slots[slot].id)) {
81
+ for (const slot of SLOTS) drop(slot);
82
+ return [];
83
+ }
84
+ const proof = safe(() => effectStockSnapshot(stock()));
78
85
  if (!fresh(body, gen)) return [];
79
86
  if (!proof) { clear(); return []; }
80
87
  const result = [];
@@ -42,6 +42,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
42
42
  try { if (!host?.document || !host.document.body || registries.has(host) || typeof load !== "function" || typeof stockSnapshot !== "function" || typeof onChange !== "function" || typeof onProfile !== "function" || typeof onMount !== "function" || typeof onRetire !== "function" || typeof clock !== "function" || !safety || ["allowed", "begin", "finish", "pause", "resume"].some(k => typeof safety[k] !== "function") || ["requestAnimationFrame", "cancelAnimationFrame", "setTimeout", "clearTimeout", "MutationObserver", "ResizeObserver", "AbortController"].some(k => typeof host[k] !== "function")) return { ok: false, reason: "registry_unsupported" }; } catch { return { ok: false, reason: "registry_unsupported" }; }
43
43
  let dead = false, blocked = null, revision = 0, last = null, lastView = null, frameSerial = 0, context = null, descriptors = [], raf = null, timer = null;
44
44
  const slots = new Map(), loads = new Set(), paused = new Map(), offs = [], observed = new Set(), failures = new Map(), results = new Map();
45
+ let finishDisposed; const disposed = new Promise(resolve => { finishDisposed = resolve; });
45
46
  let readingStock = false, stockFailure = "stock_evidence_invalid", warned = null;
46
47
  let root, geometry, observer, resize, longTasks = null, longTasksSupported = false;
47
48
  const time = () => {
@@ -189,6 +190,12 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
189
190
  const proposal = revision;
190
191
  if (time() === null) blocked = "clock_unavailable";
191
192
  const hidden = context?.hidden === true || host.document.visibilityState !== "visible";
193
+ // #2188: nothing to place, nothing to prove — the stock walk (thousands of computed styles) is not run for an empty
194
+ // list; a frame still held is retired as superseded, exactly as an allocation of nothing would.
195
+ if (!blocked && descriptors.length === 0) {
196
+ for (const s of [...slots.values()]) { retire(s, "superseded"); if (dead || proposal !== revision) return; }
197
+ results.clear(); tell(); return;
198
+ }
192
199
  const stock = eligible() ? coverage() : null;
193
200
  if (dead || proposal !== revision) return;
194
201
  if (blocked || !eligible() || !stock) {
@@ -336,7 +343,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
336
343
  }).catch(() => { if (current(s)) retire(s, "resource_unavailable"); }).finally(() => {
337
344
  if (request.timer !== null) host.clearTimeout(request.timer);
338
345
  loads.delete(request); if (s.request === request) s.request = null;
339
- if (!dead) reconcile(); else if (loads.size === 0 && registries.get(host) === api) registries.delete(host);
346
+ if (!dead) reconcile(); else if (loads.size === 0) { if (registries.get(host) === api) registries.delete(host); finishDisposed(); }
340
347
  });
341
348
  }
342
349
  schedule(); tell();
@@ -440,7 +447,8 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
440
447
  } catch { return false; }
441
448
  }
442
449
  function pointer(event) {
443
- if (dead || blocked || !pointerGate() || event.isTrusted !== true) return;
450
+ // #2188: no frame to forward to — no gate to read (each read walks the page's computed styles, per pointer event).
451
+ if (dead || blocked || slots.size === 0 || !pointerGate() || event.isTrusted !== true) return;
444
452
  const kinds = { pointermove: "move", pointerdown: "down", pointerup: "up", click: "click", wheel: "wheel" }, kind = kinds[event.type];
445
453
  if (!kind || !finite(event.clientX) || !finite(event.clientY) || Math.abs(event.clientX) > 1e7 || Math.abs(event.clientY) > 1e7 || !Number.isInteger(event.buttons) || event.buttons < 0 || event.buttons > 31) return;
446
454
  try { const target = host.document.elementFromPoint(event.clientX, event.clientY); if (!target || typeof target.closest !== "function" || target.closest("[data-trust]")) return; } catch { return; }
@@ -511,7 +519,8 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
511
519
  },
512
520
  /** Host-only content-free signal API. Raw pointer publishing is refused; native pointer metadata is coalesced here. */
513
521
  publish(type, fields, instance = null) {
514
- if (dead || blocked || !globalAllowed() || context.hidden || !coverage() || !TYPES.includes(type)) return 0;
522
+ // #2188: no frame to send to — nothing to prove (the stock walk ran twice on every page change).
523
+ if (dead || blocked || slots.size === 0 || !globalAllowed() || context.hidden || !coverage() || !TYPES.includes(type)) return 0;
515
524
  const ts = time(); if (ts === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return 0; }
516
525
  let sent = 0; const proposal = revision;
517
526
  for (const s of [...slots.values()]) {
@@ -522,11 +531,12 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
522
531
  }
523
532
  return sent;
524
533
  },
534
+ whenDisposed: () => disposed,
525
535
  dispose() {
526
536
  if (dead) return; dead = true; revision++; stopAll("disposed");
527
537
  if (raf !== null) host.cancelAnimationFrame(raf); if (timer !== null) host.clearTimeout(timer);
528
538
  observer.disconnect(); resize.disconnect(); longTasks?.disconnect(); for (const off of offs) off();
529
- geometry.dispose(); root.remove(); observed.clear(); if (loads.size === 0 && registries.get(host) === api) registries.delete(host);
539
+ geometry.dispose(); root.remove(); observed.clear(); if (loads.size === 0) { if (registries.get(host) === api) registries.delete(host); finishDisposed(); }
530
540
  },
531
541
  };
532
542
  // Read-only host geometry seam; no selector guessing among poster and live roots.
@@ -143,7 +143,8 @@ export function createEffectRuntimeLoader({ window: host, ownerEpoch, current, o
143
143
  if (!host || typeof host !== "object" || loaders.has(host) || typeof host.fetch !== "function" || typeof host.AbortController !== "function" || typeof host.setTimeout !== "function" || typeof host.clearTimeout !== "function" || typeof ownerEpoch !== "string" || !EPOCH.test(ownerEpoch) || typeof current !== "function" || typeof onRevoke !== "function") return { ok: false };
144
144
  const reservation = {}; loaders.set(host, reservation);
145
145
  let dead = false, revoked = false, previous = null; const pending = new Set();
146
- const releaseWindow = () => { if (dead && pending.size === 0 && loaders.get(host) === reservation) loaders.delete(host); };
146
+ let finishDisposed; const disposed = new Promise(resolve => { finishDisposed = resolve; });
147
+ const releaseWindow = () => { if (dead && pending.size === 0) { if (loaders.get(host) === reservation) loaders.delete(host); finishDisposed(); } };
147
148
  // Terminal state is latched before callbacks. Abort releases logical callers, never physical reservations.
148
149
  function revoke(reason) {
149
150
  if (dead || revoked) return;
@@ -215,6 +216,7 @@ export function createEffectRuntimeLoader({ window: host, ownerEpoch, current, o
215
216
  }
216
217
  const ownerRead = (signal, settled = false) => run("placements", `/ui/effects/owner?ownerEpoch=${ownerEpoch}`, checkEffectOwnerSlots, signal, settled);
217
218
  return { ok: true,
219
+ whenDisposed: () => disposed,
218
220
  owner(signal) { return ownerRead(signal); },
219
221
  placements(instance, signal) { return placementRead(instance, signal); },
220
222
  resource(input, signal) { return resourceRead(input, signal); },
@@ -11,10 +11,10 @@ const SPECS = [
11
11
  ["confirmation", ".shell > .pending-stack, .shell .p-settings > .panel-body > .col > .pending-stack.inline"], ["preview", ".shell .chat-split > .pv-panel, .shell .chat-split > .pv-divider"],
12
12
  ["preview-menu", ".shell .thread details.pv-menu"], ["palette", ".shell .dock .cmd-palette"],
13
13
  ["drop", ".shell .p-chat > .drop-overlay"],
14
- ["thread-control", ".shell .chat-main > .amb-note, .shell .p-chat .jump-latest, .shell .thread .msg-tools, .shell .thread .st-more, .shell .thread .cb-head, .shell .thread .cb-more, .shell .thread .tc-head, .shell .thread .tc-more, .shell .thread .pv-head, .shell .thread .pv-note, .shell .thread .pv-banner, .shell .thread .effect-trial-offer, .shell .thread .click-chip"],
14
+ ["thread-control", ".shell .chat-main > .amb-note, .shell .p-chat .jump-latest, .shell .thread .msg-tools, .shell .thread .msg > div.msg-actions, .shell .thread .msg > button.msg-quote, .shell .thread .st-more, .shell .thread .cb-head, .shell .thread .cb-more, .shell .thread .tc-head, .shell .thread .tc-more, .shell .thread .pv-head, .shell .thread .pv-note, .shell .thread .pv-banner, .shell .thread .effect-trial-offer, .shell .thread .click-chip"],
15
15
  ["link", ".shell .thread .md a, .shell .thread .st-text > a.st-link, .shell .dock .prompts .txt > a.st-link"], ["attachment", ".shell .thread .atts"], ["media", ".shell .thread .att-media"],
16
16
  ["panel-navigation", ".shell .p-fleet > .seg, .shell .p-settings > .seg"],
17
- ["panel-content", ".shell .p-fleet > .panel-body, .shell .p-needs > .panel-body, .shell .p-settings > .panel-body, .shell .p-log > .panel-body, .shell .p-details > .panel-body, .shell .p-view > .panel-body, .shell .p-view > .v-term, .shell .p-view > .v-id, .shell .p-view > .v-card"],
17
+ ["panel-content", ".shell .p-fleet > .panel-body, .shell .p-needs > .panel-body, .shell .p-settings > .panel-body, .shell .p-log > .panel-body, .shell .p-details > .panel-body, .shell .p-view > .panel-body, .shell .p-view > .v-term, .shell .p-view > .v-id"],
18
18
  ["panel-controls", ".shell .p-log > .log-bar, .shell .p-settings > .s-pending"],
19
19
  ["session-ended", "body > .agend-session-ended[role=alert]"],
20
20
  ];
@@ -28,7 +28,7 @@ const PANELS = Object.freeze({
28
28
  settings: { required: [".panel-head", ".seg", ".panel-body"], optional: [".s-pending"] },
29
29
  log: { required: [".panel-head", ".panel-body"], optional: [".log-bar"] },
30
30
  details: { required: [".panel-head", ".panel-body"], optional: [] },
31
- view: { required: [".panel-head"], optional: [".panel-body", ".v-term", ".v-id", ".v-card"] },
31
+ view: { required: [".panel-head"], optional: [".panel-body", ".v-term", ".v-id"] },
32
32
  });
33
33
  function panelSurface(doc, chat) {
34
34
  if (chat) return { panel: chat, structural: STRUCTURAL, content: CONTENT, kind: "chat" };
@@ -37,8 +37,8 @@ function panelSurface(doc, chat) {
37
37
  const children = [...panel.children], count = selector => children.filter(el => el.matches(selector)).length;
38
38
  if (spec.required.some(selector => count(selector) !== 1) || spec.optional.some(selector => count(selector) > 1)
39
39
  || children.some(el => !el.matches([...spec.required, ...spec.optional, "dialog.dlg", "dialog.lb"].join(",")))) return null;
40
- if (kind === "view" && !(count(".panel-body") === 1 && !count(".v-term") && !count(".v-id") && !count(".v-card")
41
- || !count(".panel-body") && count(".v-term") === 1 && count(".v-card") === 1)) return null;
40
+ if (kind === "view" && !(count(".panel-body") === 1 && !count(".v-term") && !count(".v-id")
41
+ || !count(".panel-body") && count(".v-term") === 1 && count(".v-id") === 1)) return null;
42
42
  const prefix = `.p-${kind} > `;
43
43
  return { panel, kind, structural: [...STRUCTURAL.slice(0, 5), `.main > .p-${kind}`],
44
44
  content: [...CONTENT.filter(selector => !selector.startsWith(".p-chat") && !selector.startsWith(".chat-main")),
@@ -0,0 +1,127 @@
1
+ // Product feature policy only: this module imports no experimental runtime.
2
+ import { appStore } from "./app-store.js";
3
+ import { navStore } from "./app-nav.js";
4
+ import { ownerEffectsChosen, onOwnerEffectsChanged } from "./app-prefs.js";
5
+ import { register } from "./app-i18n.js";
6
+ import { html, useEffect, useState } from "./app-html.js";
7
+ register("cwControls", { en: { title: "Effects" }, "zh-TW": { title: "效果" } });
8
+ export function canvasWorkerEnabled() { return appStore.get().canvasWorker === true; }
9
+ export function onCanvasWorkerChange(fn) {
10
+ let value = canvasWorkerEnabled();
11
+ return appStore.subscribe(() => { const next = canvasWorkerEnabled(); if (next !== value) { value = next; fn(next); } });
12
+ }
13
+ export function useCanvasWorker() {
14
+ const [value, setValue] = useState(canvasWorkerEnabled);
15
+ useEffect(() => { setValue(canvasWorkerEnabled()); return onCanvasWorkerChange(setValue); }, []);
16
+ return value;
17
+ }
18
+ // Never evaluate the optional module before admission. An obsolete import may finish, but cannot mount.
19
+ export function experimentalComponent(load, select) {
20
+ return function ExperimentalComponent(props) {
21
+ const enabled = useCanvasWorker(), [component, setComponent] = useState(null);
22
+ useEffect(() => {
23
+ let current = true;
24
+ if (enabled) load().then(module => { if (current && canvasWorkerEnabled()) setComponent(() => select(module)); }, () => {});
25
+ return () => { current = false; };
26
+ }, [enabled]);
27
+ return enabled && component ? html`<${component} ...${props} />` : null;
28
+ };
29
+ }
30
+
31
+ /** effect_chat_v of a chat with no placements: sha256(JSON.stringify([])), first 32 hex (fleet-manager's status row). */
32
+ export const EMPTY_CHAT_EFFECTS = "4f53cda18c2baa0c0354bb5f9a3ecbe5";
33
+
34
+ let mod = null, waking = null, chatStore = null, started = false, generation = 0, awakeGeneration = -1, retiring = Promise.resolve();
35
+ const awakeListeners = new Set();
36
+
37
+ /** Whether `name`'s chat has Canvas Worker placements (as the status rows say). */
38
+ export function chatHasEffects(name, app = appStore.get()) {
39
+ const v = name ? app.instances?.find(i => i.name === name)?.effect_chat_v : undefined;
40
+ return canvasWorkerEnabled() && typeof v === "string" && /^[a-f0-9]{32}$/.test(v) && v !== EMPTY_CHAT_EFFECTS;
41
+ }
42
+
43
+ /** The running effects owner, or null while dormant (or when it could not start: as getAppEffects always was). */
44
+ export function getAppEffects(host) { return canvasWorkerEnabled() && mod ? mod.getAppEffects(host) : null; }
45
+
46
+ /** Whether the runtime has been asked to start on this page. */
47
+ export function effectsAwake() { return waking !== null; }
48
+ /** Whether its module has loaded (started, or found it could not: getAppEffects() then says which). */
49
+ export function effectsLoaded() { return mod !== null && awakeGeneration === generation; }
50
+
51
+ /** Follow every completed runtime episode, including a replacement after an experimental off/on. */
52
+ export function onEffectsAwake(fn) {
53
+ if (typeof fn !== "function") return () => {};
54
+ awakeListeners.add(fn);
55
+ if (effectsLoaded()) { try { fn(); } catch { /* a listener's error is its own */ } }
56
+ return () => awakeListeners.delete(fn);
57
+ }
58
+
59
+ /** Load lazily; each explicitly enabled episode owns its startup and cancellation. */
60
+ export function wakeEffects() {
61
+ if (!canvasWorkerEnabled()) return Promise.resolve(null);
62
+ if (waking) return waking;
63
+ const episode = generation;
64
+ waking = (mod ? Promise.resolve(mod) : import("/ui/js/app-effects.js")).then(async m => {
65
+ mod = m;
66
+ await retiring;
67
+ if (episode !== generation || !canvasWorkerEnabled()) return null;
68
+ const owner = chatStore ? m.startAppEffects({ store: chatStore }) : null;
69
+ awakeGeneration = episode;
70
+ for (const fn of [...awakeListeners]) { try { fn(); } catch { /* the rest still hear */ } }
71
+ return owner;
72
+ }, () => null);
73
+ return waking;
74
+ }
75
+
76
+ /** Wake when the open chat has placements or the person has a personal effect. */
77
+ function check() {
78
+ if (!canvasWorkerEnabled()) {
79
+ generation++;
80
+ const owner = mod?.getAppEffects();
81
+ if (owner) retiring = Promise.resolve(owner.dispose?.(true));
82
+ waking = null;
83
+ return;
84
+ }
85
+ if (waking) return;
86
+ const route = navStore.get().route;
87
+ if ((route?.panel === "chat" && chatHasEffects(route.instance)) || ownerEffectsChosen()) void wakeEffects();
88
+ }
89
+
90
+ /** Boot (once per page, with the Chat store the runtime binds to). Starts nothing but the watch. */
91
+ export function startEffectsWaker({ store }) {
92
+ chatStore = store;
93
+ if (started) return;
94
+ started = true;
95
+ appStore.subscribe(check); navStore.subscribe(check); onOwnerEffectsChanged(check);
96
+ check();
97
+ }
98
+
99
+ // Full Canvas Worker metadata stays in the experimental graph, never in ordinary chat boot.
100
+ let metadata = null, metadataPending = null;
101
+ const metadataListeners = new Set();
102
+ export function onCanvasMetadataChange(fn) { metadataListeners.add(fn); return () => metadataListeners.delete(fn); }
103
+ export function loadCanvasWorkerMetadata() {
104
+ if (!canvasWorkerEnabled()) return Promise.resolve(null);
105
+ if (metadata) return Promise.resolve(metadata);
106
+ if (metadataPending?.episode === generation) return metadataPending.promise;
107
+ const episode = generation, op = { episode, promise: null };
108
+ metadataPending = op;
109
+ op.promise = import("./effect-display-metadata.js").then(value => {
110
+ if (episode !== generation || !canvasWorkerEnabled()) return null;
111
+ metadata = value;
112
+ for (const fn of [...metadataListeners]) { try { fn(); } catch { /* independent viewers */ } }
113
+ appStore.set({ canvasMetadataEpoch: episode });
114
+ return metadata;
115
+ }, () => null).finally(() => { if (metadataPending === op) metadataPending = null; });
116
+ return op.promise;
117
+ }
118
+ export function readCanvasTrialMessage(message) {
119
+ if (!canvasWorkerEnabled()) return null;
120
+ if (!metadata) { void loadCanvasWorkerMetadata(); return undefined; }
121
+ return metadata.effectTrialMessage(message);
122
+ }
123
+ export function canvasWorkerMenuAvailable(host) {
124
+ if (!canvasWorkerEnabled()) return false;
125
+ if (!metadata) { void loadCanvasWorkerMetadata(); return false; }
126
+ return metadata.chatEffectIdentity(host) !== null;
127
+ }
@@ -0,0 +1,35 @@
1
+ // What a guest's browser is shown for a page it cannot open. Loaded from /assets/guest-refused.js.
2
+ //
3
+ // The gate answers a guest's navigation with this page instead of its JSON (#2399 smoke): "members" for a page that is
4
+ // not a guest's (Fleet, Settings…), "ended" when no chat is shared with the guest any more. The page's words are in
5
+ // the browser's language, as the sign-in page's are; the HTML carries the English for a browser without scripts.
6
+ (() => {
7
+ "use strict";
8
+ const T = {
9
+ en: {
10
+ members_title: "This page is not open to guests",
11
+ members_lead: "This page is for the fleet's members. As a guest you can use the chats shared with you.",
12
+ members_go: "Open your chats",
13
+ ended_title: "No chats are shared with you",
14
+ ended_lead: "Your guest access on this fleet has no chat now — you may have left the room it came from. Ask whoever invited you to share a chat with you again.",
15
+ },
16
+ "zh-TW": {
17
+ members_title: "訪客無法開啟這個頁面",
18
+ members_lead: "這個頁面只給 fleet 的成員使用。身為訪客,你可以使用分享給你的聊天。",
19
+ members_go: "打開你的聊天",
20
+ ended_title: "目前沒有分享給你的聊天",
21
+ ended_lead: "你在這個 fleet 的訪客權限目前沒有任何聊天——你可能已經離開當初的頻道。請聯絡邀請你的人,再分享聊天給你。",
22
+ },
23
+ };
24
+ const lang = /^zh/i.test(navigator.language || "") ? "zh-TW" : "en";
25
+ const t = (k) => T[lang][k] || T.en[k] || k;
26
+ document.documentElement.lang = lang === "zh-TW" ? "zh-Hant" : "en";
27
+ const main = document.getElementById("refused");
28
+ const why = main && main.dataset.why === "ended" ? "ended" : "members";
29
+ document.getElementById("title").textContent = t(`${why}_title`);
30
+ document.getElementById("lead").textContent = t(`${why}_lead`);
31
+ // Nothing to open when no chat is shared (the chats would answer this page again): no button then.
32
+ const go = document.getElementById("go");
33
+ if (go) { if (why === "ended") go.remove(); else go.textContent = t("members_go"); }
34
+ document.title = `AgEnD — ${t(`${why}_title`)}`;
35
+ })();
@@ -9,7 +9,7 @@ import { html, useEffect, useState } from "./app-html.js";
9
9
  import { t } from "./app-i18n.js";
10
10
  import { createStore, useStore } from "./app-store.js";
11
11
  import { Icon } from "./ui-icons.js";
12
- import { UsageBody, UsageDialog } from "./usage-dialog.js";
12
+ import { UsageBody, UsageCompactToggle, UsageDialog } from "./usage-dialog.js";
13
13
 
14
14
  export const TEXT_SIZE_KEY = "agend_text_size";
15
15
  export const VIEW_FIT_KEY = "agend_view_fit";
@@ -126,7 +126,7 @@ export function UsageRail({ route }) {
126
126
  const { open } = useStore(usageRailStore);
127
127
  if (!wide || !open || !route || !RAIL_PANELS.includes(route.panel) || !usageOffered()) return null;
128
128
  return html`<aside data-trust="usage" class="u-rail" aria-label=${t("view.usage.title_plain")}>
129
- <div class="u-rail-head"><h2>${t("view.usage.title")}</h2>
129
+ <div class="u-rail-head"><h2>${t("view.usage.title")}</h2><${UsageCompactToggle} />
130
130
  <button type="button" class="icon-btn" onClick=${() => setUsageRail(false)} aria-label=${t("app.usageHide")} title=${t("app.usageHide")}><${Icon} name="close" size=${16} /></button></div>
131
131
  <div class="u-rail-body"><${UsageBody} owner="usage-rail" /></div>
132
132
  </aside>`;
@@ -160,35 +160,53 @@ function SizeControl({ name, size }) {
160
160
  </div>`;
161
161
  }
162
162
 
163
+ /**
164
+ * #2217 hotfix (Prism): pictures that failed to load on this page, by src — shared, so the chat's card menu judges a card
165
+ * as drawn (a broken banner is not drawn, and then the card cannot fold), not by its metadata alone.
166
+ */
167
+ export const brokenPictures = createStore({});
168
+ const markBroken = (src) => { if (src && !brokenPictures.get()[src]) brokenPictures.set((s) => ({ ...s, [src]: true })); };
169
+ const drawn = (card, slot) => !!(card && card[slot] && !brokenPictures.get()[card[slot].src]);
170
+ /**
171
+ * #2217: whether a card can fold its row away and keep the banner — a banner drawn, and something on its row (a picture
172
+ * drawn, a tagline or a bio). The chat's card menu enables Show picture and bio only then.
173
+ */
174
+ export const cardFoldable = (card) => !!(card && drawn(card, "banner") && (drawn(card, "portrait") || card.tagline || card.bio));
175
+
163
176
  /**
164
177
  * The card. `compact`: the chat header's own small size (no size control; the banner still follows the size choice,
165
178
  * #2038). Nothing when the card is empty. The
166
179
  * tagline and bio say whose they are (title), since a person's field wins over the agent's.
180
+ * #2217: `rowHidden` — the chat's picture and bio folded away, the banner kept. The user (2026-10-20): no control on the
181
+ * card at all — the chat header's two buttons show / hide the card and fold / unfold its row. Only a card with a banner
182
+ * and something on its row folds (cardFoldable; else it would be the card hidden).
167
183
  */
168
- export function IdentityCard({ name, card, compact = false, sizeControl = !compact }) {
184
+ export function IdentityCard({ name, card, compact = false, sizeControl = !compact, rowHidden = false }) {
169
185
  useStore(cardSizeStore);
170
- const [broken, setBroken] = useState({});
186
+ const broken = useStore(brokenPictures);
171
187
  if (!card) return null;
172
188
  const size = compact ? "c" : cardSizeFor(name);
173
189
  // #2038: the chat header's card is its own small size, but its banner follows the person's size choice too.
174
190
  const bannerSize = compact ? ` bs-${cardSizeFor(name)}` : "";
175
191
  const whose = (f) => t(f.by === "human" ? "card.byHuman" : "card.byAgent");
176
- const pic = (slot, cls, alt) => card[slot] && broken[slot] !== card[slot].src
177
- ? html`<img class=${cls} src=${card[slot].src} alt=${alt} title=${whose(card[slot])} decoding="async" onError=${() => setBroken({ ...broken, [slot]: card[slot].src })} />` : null;
192
+ const pic = (slot, cls, alt) => card[slot] && !broken[card[slot].src]
193
+ ? html`<img class=${cls} src=${card[slot].src} alt=${alt} title=${whose(card[slot])} decoding="async" onError=${() => markBroken(card[slot].src)} />` : null;
178
194
  // #2086: the banner at its focus; on a screen wider than BANNER_MAX_ASPECT it is centred over a blurred copy of itself.
179
195
  const focus = card.banner_focus ? card.banner_focus.value : 50;
180
196
  const atFocus = (el) => { if (el && el.style) el.style.setProperty("--id-focus", `${focus}%`); };
181
197
  const banner = pic("banner", "id-banner", "");
182
- return html`<section class=${`id-card card-${size}${bannerSize}`} aria-label=${t("card.label", name)}>
198
+ const portrait = pic("portrait", "id-portrait", t("card.portraitAlt", name));
199
+ const rowShown = !(rowHidden && banner && cardFoldable(card));
200
+ return html`<section class=${`id-card card-${size}${bannerSize}${rowShown ? "" : " row-hidden"}`} aria-label=${t("card.label", name)}>
183
201
  ${banner ? html`<div class="id-banner-box" ref=${atFocus}><img class="id-banner-fill" src=${card.banner.src} alt="" aria-hidden="true" decoding="async" />${banner}</div>` : null}
184
- <div class="id-row">
185
- ${pic("portrait", "id-portrait", t("card.portraitAlt", name))}
202
+ ${rowShown ? html`<div class="id-row">
203
+ ${portrait}
186
204
  <div class="id-text">
187
205
  ${card.tagline ? html`<p class="id-tagline" title=${whose(card.tagline)}>${card.tagline.text}</p>` : null}
188
206
  ${card.bio ? html`<p class="id-bio" title=${whose(card.bio)}>${card.bio.text}</p>` : null}
189
207
  </div>
190
208
  ${sizeControl ? html`<${SizeControl} name=${name} size=${size} />` : null}
191
- </div>
209
+ </div>` : null}
192
210
  </section>`;
193
211
  }
194
212