@songsid/agend 2.3.0-beta.2 → 2.3.0-beta.20

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 (502) hide show
  1. package/README.md +7 -1
  2. package/README.zh-TW.md +7 -1
  3. package/dist/agent-cli-instructions.md +9 -2
  4. package/dist/agent-cli.js +63 -2
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +4 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.d.ts +4 -1
  9. package/dist/agent-file-read.js +189 -16
  10. package/dist/agent-file-read.js.map +1 -1
  11. package/dist/agent-whats-new.d.ts +11 -0
  12. package/dist/agent-whats-new.js +29 -5
  13. package/dist/agent-whats-new.js.map +1 -1
  14. package/dist/auth-api.d.ts +30 -1
  15. package/dist/auth-api.js +45 -13
  16. package/dist/auth-api.js.map +1 -1
  17. package/dist/authority/authority-state.d.ts +38 -0
  18. package/dist/authority/authority-state.js +114 -0
  19. package/dist/authority/authority-state.js.map +1 -0
  20. package/dist/authority/session-authorizations.d.ts +43 -0
  21. package/dist/authority/session-authorizations.js +102 -0
  22. package/dist/authority/session-authorizations.js.map +1 -0
  23. package/dist/authority/web-authority.d.ts +120 -0
  24. package/dist/authority/web-authority.js +157 -0
  25. package/dist/authority/web-authority.js.map +1 -0
  26. package/dist/backend/claude-code.d.ts +2 -2
  27. package/dist/backend/claude-code.js +18 -8
  28. package/dist/backend/claude-code.js.map +1 -1
  29. package/dist/backend/codex-metadata.d.ts +8 -0
  30. package/dist/backend/codex-metadata.js +74 -17
  31. package/dist/backend/codex-metadata.js.map +1 -1
  32. package/dist/backend/codex.js +15 -2
  33. package/dist/backend/codex.js.map +1 -1
  34. package/dist/backend/credential-profile.d.ts +2 -0
  35. package/dist/backend/credential-profile.js +2 -0
  36. package/dist/backend/credential-profile.js.map +1 -1
  37. package/dist/backend/types.d.ts +6 -0
  38. package/dist/backend/types.js +15 -0
  39. package/dist/backend/types.js.map +1 -1
  40. package/dist/backend-availability.d.ts +120 -0
  41. package/dist/backend-availability.js +396 -0
  42. package/dist/backend-availability.js.map +1 -0
  43. package/dist/backend-command.d.ts +23 -3
  44. package/dist/backend-command.js +25 -4
  45. package/dist/backend-command.js.map +1 -1
  46. package/dist/bug-report-cli.d.ts +30 -0
  47. package/dist/bug-report-cli.js +138 -0
  48. package/dist/bug-report-cli.js.map +1 -0
  49. package/dist/bug-report.d.ts +47 -0
  50. package/dist/bug-report.js +136 -0
  51. package/dist/bug-report.js.map +1 -0
  52. package/dist/bundled-skills.d.ts +14 -0
  53. package/dist/bundled-skills.js +29 -0
  54. package/dist/bundled-skills.js.map +1 -0
  55. package/dist/cache-ledger.d.ts +16 -3
  56. package/dist/cache-ledger.js +33 -13
  57. package/dist/cache-ledger.js.map +1 -1
  58. package/dist/cache-service.d.ts +55 -3
  59. package/dist/cache-service.js +152 -16
  60. package/dist/cache-service.js.map +1 -1
  61. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  62. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  63. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  64. package/dist/channel/adapters/discord.d.ts +9 -0
  65. package/dist/channel/adapters/discord.js +49 -4
  66. package/dist/channel/adapters/discord.js.map +1 -1
  67. package/dist/channel/mcp-tools.js +22 -11
  68. package/dist/channel/mcp-tools.js.map +1 -1
  69. package/dist/channel/types.d.ts +6 -0
  70. package/dist/classic-channel-manager.d.ts +10 -5
  71. package/dist/classic-channel-manager.js +12 -9
  72. package/dist/classic-channel-manager.js.map +1 -1
  73. package/dist/cli.js +224 -111
  74. package/dist/cli.js.map +1 -1
  75. package/dist/config-validator.d.ts +6 -0
  76. package/dist/config-validator.js +129 -3
  77. package/dist/config-validator.js.map +1 -1
  78. package/dist/config.d.ts +13 -1
  79. package/dist/config.js +14 -5
  80. package/dist/config.js.map +1 -1
  81. package/dist/cross-instance-notice.d.ts +7 -0
  82. package/dist/cross-instance-notice.js +10 -0
  83. package/dist/cross-instance-notice.js.map +1 -1
  84. package/dist/daemon.d.ts +24 -1
  85. package/dist/daemon.js +151 -19
  86. package/dist/daemon.js.map +1 -1
  87. package/dist/decision-list.d.ts +25 -0
  88. package/dist/decision-list.js +30 -0
  89. package/dist/decision-list.js.map +1 -0
  90. package/dist/delivery-outbox.d.ts +10 -0
  91. package/dist/delivery-outbox.js +17 -1
  92. package/dist/delivery-outbox.js.map +1 -1
  93. package/dist/diagnostic-redaction.d.ts +10 -0
  94. package/dist/diagnostic-redaction.js +167 -0
  95. package/dist/diagnostic-redaction.js.map +1 -0
  96. package/dist/fleet-context.d.ts +10 -0
  97. package/dist/fleet-manager.d.ts +505 -15
  98. package/dist/fleet-manager.js +1870 -204
  99. package/dist/fleet-manager.js.map +1 -1
  100. package/dist/fleet-process-handlers.d.ts +18 -0
  101. package/dist/fleet-process-handlers.js +69 -0
  102. package/dist/fleet-process-handlers.js.map +1 -0
  103. package/dist/fleet-traffic.d.ts +74 -0
  104. package/dist/fleet-traffic.js +196 -0
  105. package/dist/fleet-traffic.js.map +1 -0
  106. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  107. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +17 -6
  108. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
  109. package/dist/general-knowledge/skills/fleet-config/SKILL.md +2 -0
  110. package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
  111. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  112. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  113. package/dist/general-knowledge/skills/web-appearance/SKILL.md +190 -47
  114. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +545 -0
  115. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  116. package/dist/human-delivery-receipts.d.ts +24 -1
  117. package/dist/human-delivery-receipts.js +64 -11
  118. package/dist/human-delivery-receipts.js.map +1 -1
  119. package/dist/instance-config-impact.js +7 -1
  120. package/dist/instance-config-impact.js.map +1 -1
  121. package/dist/instance-lifecycle.d.ts +10 -1
  122. package/dist/instance-lifecycle.js +79 -18
  123. package/dist/instance-lifecycle.js.map +1 -1
  124. package/dist/instance-names.d.ts +4 -0
  125. package/dist/instance-names.js +12 -0
  126. package/dist/instance-names.js.map +1 -0
  127. package/dist/instructions.d.ts +12 -1
  128. package/dist/instructions.js +13 -2
  129. package/dist/instructions.js.map +1 -1
  130. package/dist/locale.js +173 -12
  131. package/dist/locale.js.map +1 -1
  132. package/dist/log-steps.d.ts +1 -0
  133. package/dist/log-steps.js +15 -0
  134. package/dist/log-steps.js.map +1 -1
  135. package/dist/login-controller.d.ts +45 -0
  136. package/dist/login-controller.js +101 -5
  137. package/dist/login-controller.js.map +1 -1
  138. package/dist/ls-attach-resolve.d.ts +96 -0
  139. package/dist/ls-attach-resolve.js +180 -0
  140. package/dist/ls-attach-resolve.js.map +1 -0
  141. package/dist/ls-rows.d.ts +12 -0
  142. package/dist/ls-rows.js +82 -3
  143. package/dist/ls-rows.js.map +1 -1
  144. package/dist/memory-hogs.d.ts +27 -0
  145. package/dist/memory-hogs.js +124 -0
  146. package/dist/memory-hogs.js.map +1 -0
  147. package/dist/needs-you.d.ts +10 -0
  148. package/dist/needs-you.js +2 -1
  149. package/dist/needs-you.js.map +1 -1
  150. package/dist/outbound-handlers.d.ts +11 -0
  151. package/dist/outbound-handlers.js +139 -11
  152. package/dist/outbound-handlers.js.map +1 -1
  153. package/dist/outbound-schemas.d.ts +200 -4
  154. package/dist/outbound-schemas.js +124 -11
  155. package/dist/outbound-schemas.js.map +1 -1
  156. package/dist/public-web-gateway.js +17 -4
  157. package/dist/public-web-gateway.js.map +1 -1
  158. package/dist/public-web-link.d.ts +9 -0
  159. package/dist/public-web-link.js +8 -0
  160. package/dist/public-web-link.js.map +1 -1
  161. package/dist/quickstart-api.d.ts +19 -1
  162. package/dist/quickstart-api.js +94 -4
  163. package/dist/quickstart-api.js.map +1 -1
  164. package/dist/reply-buttons-controller.d.ts +4 -0
  165. package/dist/reply-buttons-controller.js +2 -2
  166. package/dist/reply-buttons-controller.js.map +1 -1
  167. package/dist/restart-progress.d.ts +80 -1
  168. package/dist/restart-progress.js +306 -21
  169. package/dist/restart-progress.js.map +1 -1
  170. package/dist/rollout-index.d.ts +5 -0
  171. package/dist/rollout-index.js +26 -5
  172. package/dist/rollout-index.js.map +1 -1
  173. package/dist/scheduler/db.d.ts +40 -13
  174. package/dist/scheduler/db.js +123 -89
  175. package/dist/scheduler/db.js.map +1 -1
  176. package/dist/scheduler/task-board-text.d.ts +26 -0
  177. package/dist/scheduler/task-board-text.js +135 -0
  178. package/dist/scheduler/task-board-text.js.map +1 -0
  179. package/dist/scheduler/task-board.d.ts +650 -0
  180. package/dist/scheduler/task-board.js +1938 -0
  181. package/dist/scheduler/task-board.js.map +1 -0
  182. package/dist/scheduler/types.d.ts +6 -1
  183. package/dist/search/search-docs.d.ts +58 -0
  184. package/dist/search/search-docs.js +63 -0
  185. package/dist/search/search-docs.js.map +1 -0
  186. package/dist/search/search-index.d.ts +141 -0
  187. package/dist/search/search-index.js +440 -0
  188. package/dist/search/search-index.js.map +1 -0
  189. package/dist/search/search-store.d.ts +122 -0
  190. package/dist/search/search-store.js +236 -0
  191. package/dist/search/search-store.js.map +1 -0
  192. package/dist/search/search-worker.d.ts +1 -0
  193. package/dist/search/search-worker.js +163 -0
  194. package/dist/search/search-worker.js.map +1 -0
  195. package/dist/service-installer.js +12 -0
  196. package/dist/service-installer.js.map +1 -1
  197. package/dist/settings-api.d.ts +23 -0
  198. package/dist/settings-api.js +164 -3
  199. package/dist/settings-api.js.map +1 -1
  200. package/dist/settings-change.js +104 -0
  201. package/dist/settings-change.js.map +1 -1
  202. package/dist/settings-confirmation.d.ts +3 -1
  203. package/dist/settings-confirmation.js +2 -0
  204. package/dist/settings-confirmation.js.map +1 -1
  205. package/dist/settings-effect.js +34 -3
  206. package/dist/settings-effect.js.map +1 -1
  207. package/dist/settings-http-confirmation.d.ts +11 -0
  208. package/dist/settings-http-confirmation.js +10 -0
  209. package/dist/settings-http-confirmation.js.map +1 -1
  210. package/dist/setup-form.d.ts +1 -1
  211. package/dist/setup-form.js +23 -7
  212. package/dist/setup-form.js.map +1 -1
  213. package/dist/spawn-gate.d.ts +1 -0
  214. package/dist/spawn-gate.js +13 -3
  215. package/dist/spawn-gate.js.map +1 -1
  216. package/dist/task-board-tool.d.ts +25 -0
  217. package/dist/task-board-tool.js +154 -0
  218. package/dist/task-board-tool.js.map +1 -0
  219. package/dist/task-board-waker.d.ts +44 -0
  220. package/dist/task-board-waker.js +128 -0
  221. package/dist/task-board-waker.js.map +1 -0
  222. package/dist/tmux-exec-helper.d.ts +94 -0
  223. package/dist/tmux-exec-helper.js +407 -0
  224. package/dist/tmux-exec-helper.js.map +1 -0
  225. package/dist/tmux-manager.d.ts +5 -0
  226. package/dist/tmux-manager.js +42 -11
  227. package/dist/tmux-manager.js.map +1 -1
  228. package/dist/tool-permissions.d.ts +0 -7
  229. package/dist/tool-permissions.js +44 -5
  230. package/dist/tool-permissions.js.map +1 -1
  231. package/dist/topic-commands.d.ts +17 -1
  232. package/dist/topic-commands.js +45 -19
  233. package/dist/topic-commands.js.map +1 -1
  234. package/dist/tunnel/cloudflared.js +37 -2
  235. package/dist/tunnel/cloudflared.js.map +1 -1
  236. package/dist/tunnel/types.d.ts +3 -1
  237. package/dist/tunnel/types.js.map +1 -1
  238. package/dist/types.d.ts +63 -1
  239. package/dist/ui/app-effects.js +90 -32
  240. package/dist/ui/backend-signin.js +297 -0
  241. package/dist/ui/chat-ambient.js +307 -0
  242. package/dist/ui/chat-effect-posters.js +8 -8
  243. package/dist/ui/chat-effects.js +110 -15
  244. package/dist/ui/chat-msg-actions.js +176 -0
  245. package/dist/ui/chat-render.js +21 -3
  246. package/dist/ui/chat-store.js +64 -12
  247. package/dist/ui/chat-strings.js +85 -8
  248. package/dist/ui/chat-thread.js +204 -21
  249. package/dist/ui/effect-trial-controller.js +13 -8
  250. package/dist/ui/effect-trial-panel.js +19 -16
  251. package/dist/ui/effects-waker.js +70 -0
  252. package/dist/ui/fleet-canvas-layout.js +117 -0
  253. package/dist/ui/fleet-canvas.js +555 -0
  254. package/dist/ui/fleet-org.js +27 -12
  255. package/dist/ui/fleet-route-scene.js +207 -0
  256. package/dist/ui/fleet-route-worker.js +21 -0
  257. package/dist/ui/fleet-route.js +88 -0
  258. package/dist/ui/fleet-task-card.js +436 -0
  259. package/dist/ui/fleet-task-common.js +448 -0
  260. package/dist/ui/fleet-tasks.js +535 -0
  261. package/dist/ui/fleet-traffic.js +359 -0
  262. package/dist/ui/fleet-usage.js +435 -0
  263. package/dist/ui/panel-chat.js +264 -66
  264. package/dist/ui/panel-details.js +2 -1
  265. package/dist/ui/panel-fleet.js +65 -83
  266. package/dist/ui/panel-needs.js +4 -2
  267. package/dist/ui/panel-settings.js +56 -11
  268. package/dist/ui/preview.js +3 -2
  269. package/dist/ui/search-dialog.js +349 -0
  270. package/dist/ui/settings-appearance.js +44 -19
  271. package/dist/ui/settings-apply.js +3 -3
  272. package/dist/ui/settings-catalog.js +92 -0
  273. package/dist/ui/settings-confirm.js +7 -2
  274. package/dist/ui/settings-dialogs.js +40 -12
  275. package/dist/ui/settings-effects.js +4 -3
  276. package/dist/ui/settings-guests.js +117 -0
  277. package/dist/ui/settings-learn.js +25 -0
  278. package/dist/ui/settings-model.js +15 -6
  279. package/dist/ui/settings-mods.js +23 -9
  280. package/dist/ui/settings-owner-effects.js +15 -13
  281. package/dist/ui/settings-strings.js +122 -42
  282. package/dist/ui/settings-web-authority.js +91 -0
  283. package/dist/ui/shared/agend-auth.js +1 -1
  284. package/dist/ui/shared/app-i18n.js +11 -2
  285. package/dist/ui/shared/app-prefs.js +17 -4
  286. package/dist/ui/shared/app-route.js +19 -1
  287. package/dist/ui/shared/app-session.js +2 -0
  288. package/dist/ui/shared/app-shell.js +174 -30
  289. package/dist/ui/shared/app-store.js +26 -1
  290. package/dist/ui/shared/app-stream.js +5 -1
  291. package/dist/ui/shared/app-themes.js +140 -7
  292. package/dist/ui/shared/app.css +704 -20
  293. package/dist/ui/shared/app.js +26 -15
  294. package/dist/ui/shared/card-strings.js +8 -0
  295. package/dist/ui/shared/effect-app-runtime.js +68 -19
  296. package/dist/ui/shared/effect-chat-runtime.js +16 -3
  297. package/dist/ui/shared/effect-frame.js +26 -7
  298. package/dist/ui/shared/effect-geometry.js +24 -9
  299. package/dist/ui/shared/effect-owner-source.js +8 -1
  300. package/dist/ui/shared/effect-policy.js +21 -7
  301. package/dist/ui/shared/effect-profiler.js +31 -9
  302. package/dist/ui/shared/effect-registry.js +97 -50
  303. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  304. package/dist/ui/shared/effect-stock-trust.js +62 -10
  305. package/dist/ui/shared/effect-trial-shadow.js +62 -0
  306. package/dist/ui/shared/effect-trial-source.js +1 -1
  307. package/dist/ui/shared/effect-trial-stage.js +54 -31
  308. package/dist/ui/shared/effect-trial.css +40 -0
  309. package/dist/ui/shared/fx-motion.js +67 -0
  310. package/dist/ui/shared/identity-card.js +100 -11
  311. package/dist/ui/shared/identity-color.js +14 -10
  312. package/dist/ui/shared/instance-nav.js +179 -23
  313. package/dist/ui/shared/owner-css.js +30 -1
  314. package/dist/ui/shared/panel-view.js +24 -4
  315. package/dist/ui/shared/tokens.css +9 -3
  316. package/dist/ui/shared/ui-icons.js +2 -0
  317. package/dist/ui/shared/ui-menu.js +20 -7
  318. package/dist/ui/shared/ui-toast.js +16 -3
  319. package/dist/ui/shared/usage-dialog.js +34 -3
  320. package/dist/ui/shared/view-strings.js +72 -0
  321. package/dist/ui/shared/web-authorize.js +198 -0
  322. package/dist/ui/web-terminal/terminal.css +4 -4
  323. package/dist/ui/web-terminal/terminal.html +1 -1
  324. package/dist/update-launch.d.ts +9 -0
  325. package/dist/update-launch.js +38 -12
  326. package/dist/update-launch.js.map +1 -1
  327. package/dist/usage/format-rich.js +6 -3
  328. package/dist/usage/format-rich.js.map +1 -1
  329. package/dist/usage/i18n-keys.d.ts +1 -1
  330. package/dist/usage/i18n-keys.js +2 -1
  331. package/dist/usage/i18n-keys.js.map +1 -1
  332. package/dist/usage/i18n.d.ts +13 -1
  333. package/dist/usage/i18n.js +29 -0
  334. package/dist/usage/i18n.js.map +1 -1
  335. package/dist/usage/providers.d.ts +81 -2
  336. package/dist/usage/providers.js +240 -38
  337. package/dist/usage/providers.js.map +1 -1
  338. package/dist/usage/usage-api.d.ts +5 -1
  339. package/dist/usage/usage-api.js +184 -58
  340. package/dist/usage/usage-api.js.map +1 -1
  341. package/dist/usage-report.d.ts +158 -0
  342. package/dist/usage-report.js +226 -0
  343. package/dist/usage-report.js.map +1 -0
  344. package/dist/usage-rollup.d.ts +51 -0
  345. package/dist/usage-rollup.js +168 -0
  346. package/dist/usage-rollup.js.map +1 -0
  347. package/dist/user-systemd.d.ts +24 -0
  348. package/dist/user-systemd.js +75 -0
  349. package/dist/user-systemd.js.map +1 -0
  350. package/dist/view-api.d.ts +39 -0
  351. package/dist/view-api.js +69 -6
  352. package/dist/view-api.js.map +1 -1
  353. package/dist/web-ambient.d.ts +72 -0
  354. package/dist/web-ambient.js +161 -0
  355. package/dist/web-ambient.js.map +1 -0
  356. package/dist/web-api.d.ts +98 -14
  357. package/dist/web-api.js +1027 -83
  358. package/dist/web-api.js.map +1 -1
  359. package/dist/web-appearance-policy.d.ts +4 -6
  360. package/dist/web-appearance-policy.js +5 -12
  361. package/dist/web-appearance-policy.js.map +1 -1
  362. package/dist/web-appearance-tools.d.ts +3 -0
  363. package/dist/web-appearance-tools.js +22 -3
  364. package/dist/web-appearance-tools.js.map +1 -1
  365. package/dist/web-appearance-view.d.ts +27 -2
  366. package/dist/web-appearance-view.js +129 -14
  367. package/dist/web-appearance-view.js.map +1 -1
  368. package/dist/web-appearance.d.ts +17 -3
  369. package/dist/web-appearance.js +85 -8
  370. package/dist/web-appearance.js.map +1 -1
  371. package/dist/web-asset-parts.d.ts +2 -2
  372. package/dist/web-asset-parts.js +3 -3
  373. package/dist/web-asset-parts.js.map +1 -1
  374. package/dist/web-auth.js +3 -1
  375. package/dist/web-auth.js.map +1 -1
  376. package/dist/web-backend-login.d.ts +130 -0
  377. package/dist/web-backend-login.js +225 -0
  378. package/dist/web-backend-login.js.map +1 -0
  379. package/dist/web-card.d.ts +14 -0
  380. package/dist/web-card.js +40 -6
  381. package/dist/web-card.js.map +1 -1
  382. package/dist/web-channel-echo.d.ts +7 -1
  383. package/dist/web-channel-echo.js +6 -2
  384. package/dist/web-channel-echo.js.map +1 -1
  385. package/dist/web-chat-history.d.ts +83 -0
  386. package/dist/web-chat-history.js +137 -5
  387. package/dist/web-chat-history.js.map +1 -1
  388. package/dist/web-commands.d.ts +6 -1
  389. package/dist/web-commands.js +1 -1
  390. package/dist/web-commands.js.map +1 -1
  391. package/dist/web-effect-bootstrap.d.ts +12 -0
  392. package/dist/web-effect-bootstrap.js +49 -0
  393. package/dist/web-effect-bootstrap.js.map +1 -0
  394. package/dist/web-effect-bundle.js +46 -1
  395. package/dist/web-effect-bundle.js.map +1 -1
  396. package/dist/web-effect-playback.d.ts +30 -0
  397. package/dist/web-effect-playback.js +71 -0
  398. package/dist/web-effect-playback.js.map +1 -0
  399. package/dist/web-effect-profiles.d.ts +4 -4
  400. package/dist/web-effect-service.d.ts +11 -4
  401. package/dist/web-effect-service.js +25 -4
  402. package/dist/web-effect-service.js.map +1 -1
  403. package/dist/web-effect-tools.d.ts +1 -0
  404. package/dist/web-effect-tools.js +4 -4
  405. package/dist/web-effect-tools.js.map +1 -1
  406. package/dist/web-effect-trials.d.ts +2 -2
  407. package/dist/web-effect-trials.js +3 -3
  408. package/dist/web-effect-trials.js.map +1 -1
  409. package/dist/web-effect-upload.js +1 -1
  410. package/dist/web-effect-upload.js.map +1 -1
  411. package/dist/web-effect-worker.d.ts +1 -1
  412. package/dist/web-effect-worker.js +2 -2
  413. package/dist/web-effect-worker.js.map +1 -1
  414. package/dist/web-effect.d.ts +1 -1
  415. package/dist/web-guest-access.d.ts +126 -0
  416. package/dist/web-guest-access.js +238 -0
  417. package/dist/web-guest-access.js.map +1 -0
  418. package/dist/web-guest-rooms.d.ts +82 -0
  419. package/dist/web-guest-rooms.js +135 -0
  420. package/dist/web-guest-rooms.js.map +1 -0
  421. package/dist/web-host-guard.d.ts +11 -0
  422. package/dist/web-host-guard.js +11 -0
  423. package/dist/web-host-guard.js.map +1 -1
  424. package/dist/web-identity-color.d.ts +1 -1
  425. package/dist/web-identity-color.js +2 -2
  426. package/dist/web-identity-color.js.map +1 -1
  427. package/dist/web-identity-palette.d.ts +1 -1
  428. package/dist/web-identity-palette.js +2 -2
  429. package/dist/web-identity-palette.js.map +1 -1
  430. package/dist/web-image-meta.d.ts +13 -0
  431. package/dist/web-image-meta.js +62 -0
  432. package/dist/web-image-meta.js.map +1 -1
  433. package/dist/web-libavoid.d.ts +14 -0
  434. package/dist/web-libavoid.js +46 -0
  435. package/dist/web-libavoid.js.map +1 -0
  436. package/dist/web-login.d.ts +47 -0
  437. package/dist/web-login.js +115 -0
  438. package/dist/web-login.js.map +1 -1
  439. package/dist/web-mod-agent.d.ts +1 -1
  440. package/dist/web-mod-agent.js +7 -5
  441. package/dist/web-mod-agent.js.map +1 -1
  442. package/dist/web-mod-bundle.d.ts +9 -1
  443. package/dist/web-mod-bundle.js +62 -3
  444. package/dist/web-mod-bundle.js.map +1 -1
  445. package/dist/web-mod-export.d.ts +9 -0
  446. package/dist/web-mod-export.js +21 -0
  447. package/dist/web-mod-export.js.map +1 -1
  448. package/dist/web-mod-store.d.ts +7 -1
  449. package/dist/web-mod-store.js +19 -6
  450. package/dist/web-mod-store.js.map +1 -1
  451. package/dist/web-owner-css-store.d.ts +44 -1
  452. package/dist/web-owner-css-store.js +70 -9
  453. package/dist/web-owner-css-store.js.map +1 -1
  454. package/dist/web-prefs.d.ts +14 -0
  455. package/dist/web-prefs.js +37 -4
  456. package/dist/web-prefs.js.map +1 -1
  457. package/dist/web-preview.d.ts +6 -2
  458. package/dist/web-preview.js +4 -2
  459. package/dist/web-preview.js.map +1 -1
  460. package/dist/web-returning-device.d.ts +41 -0
  461. package/dist/web-returning-device.js +57 -5
  462. package/dist/web-returning-device.js.map +1 -1
  463. package/dist/web-session-authorization.d.ts +132 -0
  464. package/dist/web-session-authorization.js +193 -0
  465. package/dist/web-session-authorization.js.map +1 -0
  466. package/dist/web-session.d.ts +70 -10
  467. package/dist/web-session.js +137 -22
  468. package/dist/web-session.js.map +1 -1
  469. package/dist/web-settings-patch.d.ts +49 -2
  470. package/dist/web-settings-patch.js +145 -1
  471. package/dist/web-settings-patch.js.map +1 -1
  472. package/dist/web-settings-tools.d.ts +94 -0
  473. package/dist/web-settings-tools.js +301 -0
  474. package/dist/web-settings-tools.js.map +1 -0
  475. package/dist/web-shell-routes.d.ts +1 -1
  476. package/dist/web-shell-routes.js +1 -1
  477. package/dist/web-shell-routes.js.map +1 -1
  478. package/dist/web-terminal-http.js +15 -2
  479. package/dist/web-terminal-http.js.map +1 -1
  480. package/dist/web-theme-store.d.ts +39 -0
  481. package/dist/web-theme-store.js +122 -11
  482. package/dist/web-theme-store.js.map +1 -1
  483. package/dist/web-theme.d.ts +31 -0
  484. package/dist/web-theme.js +99 -2
  485. package/dist/web-theme.js.map +1 -1
  486. package/dist/web-upload.d.ts +13 -0
  487. package/dist/web-upload.js +39 -3
  488. package/dist/web-upload.js.map +1 -1
  489. package/dist/windows-shortcuts.d.ts +78 -0
  490. package/dist/windows-shortcuts.js +203 -0
  491. package/dist/windows-shortcuts.js.map +1 -0
  492. package/dist/workflow-templates/default.md +4 -1
  493. package/dist/yaml-shared-nodes.d.ts +25 -0
  494. package/dist/yaml-shared-nodes.js +185 -0
  495. package/dist/yaml-shared-nodes.js.map +1 -0
  496. package/launcher/agend-install.cjs +553 -0
  497. package/launcher/install-admission.cjs +4 -39
  498. package/launcher/postinstall.cjs +14 -12
  499. package/package.json +4 -2
  500. package/scripts/preinstall-guard.cjs +2 -5
  501. package/templates/windows-shortcuts.ps1 +181 -0
  502. package/launcher/old-updater-note.cjs +0 -48
@@ -1,16 +1,15 @@
1
1
  /** #1629 B2: one host-owned Canvas Worker pool. Product adapters are deliberately separate. */
2
- import { allocateEffectFrames, effectEvent } from "./effect-policy.js";
3
- import { captureEffectBaseline } from "./effect-baseline.js";
4
- import { createEffectFrame } from "./effect-frame.js";
2
+ import { allocateEffectFrames, effectEvent, effectPlaybackReason } from "./effect-policy.js";
3
+ import { createOpaqueEffectFrame } from "./effect-frame.js";
5
4
  import { createEffectGeometry, hostEffectRect } from "./effect-geometry.js";
6
- import { effectStockSnapshot, effectStockAllows } from "./effect-stock-snapshot.js";
5
+ import { effectStockSnapshot, effectStockAllows, effectStockRefusal } from "./effect-stock-snapshot.js";
7
6
  import { effectTrialStage, effectTrialStageIdentity, watchEffectTrialStage } from "./effect-trial-stage.js";
8
7
  import { EffectBudget } from "./effect-profiler.js";
9
8
 
10
9
  const registries = new WeakMap();
11
10
  const plain = o => !!o && (Object.getPrototypeOf(o) === Object.prototype || Object.getPrototypeOf(o) === null);
12
11
  const finite = v => typeof v === "number" && Number.isFinite(v);
13
- const FLAGS = ["safe", "publicLink", "reducedMotion", "saveData", "hidden", "enabled", "show", "previewReady", "optIn", "constrained", "modal"];
12
+ const FLAGS = ["safe", "publicLink", "reducedMotion", "hidden", "enabled", "show", "transportReady", "constrained", "modal"];
14
13
  const TYPES = ["state", "reply", "theme"];
15
14
  function unsafeSnapshotDescriptor(d) {
16
15
  if (!plain(d) || !plain(d.params ?? {})) return null;
@@ -41,9 +40,9 @@ function snapshotDescriptor(d) { try { return unsafeSnapshotDescriptor(d); } cat
41
40
  */
42
41
  export function createEffectRegistry({ window: host, origin, boot, load, safety, stockSnapshot, onChange = () => {}, onProfile = () => {}, onMount = () => {}, onRetire = () => {}, clock = () => host.performance.now() } = {}) {
43
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" }; }
44
- let dead = false, blocked = null, revision = 0, last = null, lastView = null, frameSerial = 0, context = null, descriptors = [], calibration = null, baseline = null, baselineDevice = null, serial = 0, raf = null, timer = null;
43
+ let dead = false, blocked = null, revision = 0, last = null, lastView = null, frameSerial = 0, context = null, descriptors = [], raf = null, timer = null;
45
44
  const slots = new Map(), loads = new Set(), paused = new Map(), offs = [], observed = new Set(), failures = new Map(), results = new Map();
46
- let readingStock = false;
45
+ let readingStock = false, stockFailure = "stock_evidence_invalid", warned = null;
47
46
  let root, geometry, observer, resize, longTasks = null, longTasksSupported = false;
48
47
  const time = () => {
49
48
  let n; try { n = clock(); } catch { return null; }
@@ -65,14 +64,18 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
65
64
  finally { readingStages.delete(d.trialStage); }
66
65
  return !dead && !blocked && revision === before && stage?.root === d.element ? stage : null;
67
66
  };
68
- const permits = (d, proof) => d.trialStage ? !!localStage(d, proof) : effectStockAllows(proof, d);
67
+ const permits = (d, proof) => eligible(d) && (d.trialStage ? !!localStage(d, proof) : effectStockAllows(proof, d));
69
68
  const coverage = (d = null) => {
70
69
  if (dead || blocked || readingStock) return null;
71
70
  const before = revision; let proof = null; readingStock = true;
72
- try { proof = effectStockSnapshot(stockSnapshot(Object.freeze({ frameRoot: root }))); }
73
- catch { /* unknown is never permission */ }
71
+ let failure = "stock_provider_failed";
72
+ try { const raw = stockSnapshot(Object.freeze({ frameRoot: root })); proof = effectStockSnapshot(raw); failure = proof ? null : effectStockRefusal(raw); }
73
+ catch { /* unavailable stays denied, with a local diagnostic */ }
74
74
  finally { readingStock = false; }
75
75
  if (dead || blocked || before !== revision) return null;
76
+ stockFailure = failure;
77
+ if (failure !== warned) { warned = failure; if (failure) { try { host.console?.warn("[AgEnD effects] Playback blocked:", failure); } catch { /* display only */ } } }
78
+ if (dead || blocked || before !== revision) return null;
76
79
  return d === null || permits(d, proof) ? proof : null;
77
80
  };
78
81
  const position = (d, proof) => d.trialStage ? localStage(d, proof)?.rect ?? null : hostEffectRect(d.element);
@@ -91,6 +94,21 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
91
94
  const current = s => !dead && !blocked && slots.get(s.d.key) === s && s.live;
92
95
  const tell = () => { if (dead) return; const state = view(), stamp = JSON.stringify(state); if (stamp === lastView) return; lastView = stamp; try { onChange(state); } catch { /* a display callback cannot admit code */ } };
93
96
  const receipt = (fn, ...args) => { try { return Reflect.apply(fn, safety, args); } catch { return null; } };
97
+ // #2055: the history's own read names a denial; any other answer stays unknown (and denied).
98
+ const SAFETY_REASON = Object.freeze({ __proto__: null, off: "safety_off", paused: "paused", interrupted: "interrupted" });
99
+ // Display only: the sticky history state per descriptor, which outranks a waiting reason (a queue, a dialog, typing)
100
+ // because it is what Resume must answer. Admission, caps, latches and safety policy never read it.
101
+ const named = new Map();
102
+ // The read can re-enter (an update from a callback): only a pass still current, for the same device, may write.
103
+ const nameSafety = (d, dev = device()) => {
104
+ const proposed = revision;
105
+ const reason = d.trialStage ? null : SAFETY_REASON[receipt(safety.status, d.id, dev)?.state];
106
+ if (dead || proposed !== revision || dev !== device()) return false;
107
+ if (reason) named.set(d.key, { identity: signature(d), reason }); else named.delete(d.key);
108
+ return true;
109
+ };
110
+ // Display only: the closed global gate a row is waiting on, when the host knows it (else the caller's unknown).
111
+ const gateReason = () => effectPlaybackReason(context);
94
112
  const clearMarker = (s, why) => {
95
113
  if (!s.token) return true;
96
114
  const token = s.token; s.token = null;
@@ -102,7 +120,9 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
102
120
  s.live = false;
103
121
  if (slots.get(s.d.key) === s) slots.delete(s.d.key);
104
122
  if (s.request) { s.request.controller.abort(); if (s.request.timer !== null) host.clearTimeout(s.request.timer); }
105
- if (!["disposed", "superseded", "hidden", "human_resume", "calibrating", "geometry_changed"].includes(why)) failures.set(s.d.key, { identity: signature(s.d), reason: why });
123
+ // A closed/unknown viewer gate revokes paint, not the effect identity. Fresh known gates
124
+ // may admit it again; resource failures and actual safety pauses retain their latches.
125
+ if (!["disposed", "superseded", "hidden", "human_resume", "geometry_changed", "gates_unknown"].includes(why)) failures.set(s.d.key, { identity: signature(s.d), reason: why });
106
126
  s.stageOff?.(); s.stageOff = null;
107
127
  s.frame?.remove(why); s.geometry?.remove();
108
128
  if (s.loadOff) s.loadOff();
@@ -110,6 +130,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
110
130
  paused.set(s.d.key, signature(s.d));
111
131
  if (receipt(safety.pause, { id: s.d.id, device: s.device, reason: why, metrics: s.budget?.snapshot().metrics ?? null }) !== true) blocked = "safety_unknown";
112
132
  }
133
+ if (count || why === "safety_revoked") nameSafety(s.d, s.device); // #2055: the third pause, or another tab's, says off now
113
134
  clearMarker(s, why);
114
135
  if (s.owner) { try { onRetire(s.owner, why); } catch { /* an observation cannot prevent physical cleanup */ } }
115
136
  if (blocked) for (const other of [...slots.values()]) retire(other, blocked);
@@ -120,18 +141,24 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
120
141
  retire(s, why);
121
142
  if (revision !== proposed || dead !== wasDead) return false;
122
143
  }
123
- if (calibration) { calibration.cancel(); calibration = null; }
124
144
  return true;
125
145
  }
126
146
  function gatesKnown() { return context && FLAGS.every(k => typeof context[k] === "boolean") && (context.openInstance === null || typeof context.openInstance === "string" && context.openInstance.length >= 1 && context.openInstance.length <= 128); }
127
- function eligible() { return gatesKnown() && !context.safe && !context.publicLink && !context.reducedMotion && !context.saveData && context.enabled && context.show && context.previewReady && context.optIn; }
128
- const globalAllowed = () => eligible() && host.document.visibilityState === "visible";
147
+ function eligible(d = null) {
148
+ return gatesKnown() && !context.safe && !context.reducedMotion && context.enabled && context.show && context.transportReady
149
+ && (!d || !context.publicLink || d.region === "chat" && !d.trialStage);
150
+ }
151
+ const globalAllowed = (d = null) => eligible(d) && host.document.visibilityState === "visible";
129
152
  const signature = d => d.identity;
153
+ // A frame stopped because its gates closed is recorded as gates_unknown; when the host knows which gate, say it.
154
+ const shown = why => (why === "gates_unknown" && !eligible() && gateReason()) || why;
130
155
  function view() {
131
156
  return descriptors.map(d => {
132
- const r = results.get(d.key) ?? { verdict: "off", reason: blocked ?? "unknown", queuePos: null }, s = slots.get(d.key);
133
- return { key: d.key, id: d.id, ...r, phase: s?.frame ? s.ready ? "running" : "starting" : s ? "loading" : null,
134
- quality: s?.budget?.snapshot().quality ?? null, reason: blocked ?? ((paused.get(d.key) === signature(d)) ? "paused" : failures.get(d.key)?.identity === signature(d) ? failures.get(d.key).reason : s && context?.hidden ? "hidden" : r.reason) };
157
+ const r = results.get(d.key) ?? { verdict: "off", reason: blocked ?? (eligible() ? stockFailure : gateReason()) ?? "runtime_unavailable", queuePos: null }, s = slots.get(d.key);
158
+ // #2055 one display precedence: a global latch, then the sticky safety history, then this page's own pause/failure.
159
+ const n = named.get(d.key), sticky = n && n.identity === signature(d) ? n.reason : null;
160
+ return { key: d.key, id: d.id, ...r, ...(sticky && r.verdict === "queued" ? { verdict: "off", queuePos: null } : {}), phase: s?.frame ? s.ready ? "running" : "starting" : s ? "loading" : null,
161
+ quality: s?.budget?.snapshot().quality ?? null, reason: blocked ?? sticky ?? ((paused.get(d.key) === signature(d)) ? "paused" : failures.get(d.key)?.identity === signature(d) ? shown(failures.get(d.key).reason) : s && context?.hidden ? "hidden" : r.reason) };
135
162
  });
136
163
  }
137
164
  function schedule() {
@@ -150,7 +177,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
150
177
  };
151
178
  function observe() {
152
179
  const nodes = host.document.querySelectorAll("[data-trust]");
153
- if (nodes.length > 128) throw Error("trust coverage unavailable");
180
+ if (nodes.length > 4096) throw Error("trust coverage unavailable");
154
181
  const wanted = new Set([host.document.documentElement, ...nodes]);
155
182
  for (const d of descriptors) if (d.element?.ownerDocument === host.document && d.element.isConnected === true) wanted.add(d.element);
156
183
  for (const n of observed) if (!wanted.has(n)) { resize.unobserve(n); observed.delete(n); }
@@ -162,10 +189,19 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
162
189
  const proposal = revision;
163
190
  if (time() === null) blocked = "clock_unavailable";
164
191
  const hidden = context?.hidden === true || host.document.visibilityState !== "visible";
192
+ // #2188: nothing to place, nothing to prove — the stock walk (thousands of computed styles) is not run for an empty
193
+ // list; a frame still held is retired as superseded, exactly as an allocation of nothing would.
194
+ if (!blocked && descriptors.length === 0) {
195
+ for (const s of [...slots.values()]) { retire(s, "superseded"); if (dead || proposal !== revision) return; }
196
+ results.clear(); tell(); return;
197
+ }
165
198
  const stock = eligible() ? coverage() : null;
199
+ if (dead || proposal !== revision) return;
166
200
  if (blocked || !eligible() || !stock) {
167
201
  if (!stopAll(blocked ?? "gates_unknown")) return;
168
- results.clear(); tell(); return;
202
+ results.clear();
203
+ if (!blocked) for (const d of descriptors) if (!nameSafety(d) || dead || proposal !== revision) return;
204
+ tell(); return;
169
205
  }
170
206
  // Hidden frames already owned may survive for <=10 s, but no new frame or load is admitted while hidden.
171
207
  const ctx = { ...context, hidden: false };
@@ -176,6 +212,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
176
212
  if (dead || proposal !== revision) return;
177
213
  ds.push({ ...d, paused: !allowed || d.paused === true || paused.get(d.key) === signature(d) || failures.get(d.key)?.identity === signature(d) });
178
214
  }
215
+ for (const d of descriptors) if (!nameSafety(d) || dead || proposal !== revision) return;
179
216
  const allocation = allocateEffectFrames(ds, ctx); results.clear();
180
217
  for (const r of allocation) {
181
218
  const d = descriptors.find(d => d.key === r.key);
@@ -193,10 +230,9 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
193
230
  if (!chosen.has(s.d.key) || !d || signature(d) !== signature(s.d) || d.element !== s.d.element || s.device !== device()) retire(s, "superseded");
194
231
  if (dead || proposal !== revision) return;
195
232
  }
196
- if (chosen.size === 0) { if (calibration) { calibration.cancel(); calibration = null; } baseline = null; baselineDevice = null; tell(); return; }
233
+ if (chosen.size === 0) { tell(); return; }
197
234
  if (hidden) {
198
235
  hideGeometry();
199
- if (calibration) { calibration.cancel(); calibration = null; }
200
236
  for (const s of [...slots.values()]) {
201
237
  if (!s.frame) retire(s, "hidden");
202
238
  else { if (s.hiddenSince === null) { s.hiddenSince = last; s.healthySince = null; } if (s.budget.gates({ hidden: true, reducedMotion: false, saveData: false, show: true }).remove) retire(s, "hidden"); }
@@ -209,19 +245,9 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
209
245
  if (s.frame && s.hiddenSince !== null) { s.hiddenSince = null; s.healthySince = last; if (s.budget.gates({ hidden: false, reducedMotion: false, saveData: false, show: true }).remove) retire(s, "hidden"); }
210
246
  if (dead || proposal !== revision) return;
211
247
  }
212
- try { observe(); } catch { stopAll("trust_geometry_unknown"); tell(); return; }
213
- if (baseline === null || baselineDevice !== device()) {
214
- if (calibration && baselineDevice === device()) { tell(); return; }
215
- if (!stopAll("calibrating") || dead || proposal !== revision) return;
216
- const epoch = ++serial;
217
- const job = captureEffectBaseline({ window: host, empty: () => !dead && slots.size === 0 && host.document.querySelectorAll("iframe.agend-effect-frame").length === 0 && globalAllowed() && !context.hidden && !!coverage(), clock: nativeClock });
218
- calibration = job; baselineDevice = device();
219
- job.promise.then(r => {
220
- if (dead || calibration !== job || epoch !== serial) return;
221
- calibration = null;
222
- if (!r.ok) { blocked = r.reason; tell(); return; }
223
- baseline = r.baselineFps; reconcile();
224
- });
248
+ try { observe(); } catch {
249
+ if (dead || proposal !== revision || !stopAll("trust_geometry_unknown")) return;
250
+ for (const d of descriptors) results.set(d.key, { verdict: "off", reason: "trust_geometry_unknown", queuePos: null });
225
251
  tell(); return;
226
252
  }
227
253
  const max = context.constrained ? 1 : 3;
@@ -230,8 +256,14 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
230
256
  if (slots.size >= max || loads.size >= max) break;
231
257
  const permitted = receipt(safety.allowed, d.id, device());
232
258
  if (proposal !== revision) return;
233
- if (permitted !== true) { results.set(d.key, { verdict: "off", reason: "safety_unknown", queuePos: null }); continue; }
234
- if (slots.has(d.key) || slots.size >= max || loads.size >= max || !globalAllowed() || context.hidden || !coverage(d) || proposal !== revision || !descriptors.some(x => x.key === d.key && signature(x) === signature(d) && x.element === d.element)) break;
259
+ if (permitted !== true) {
260
+ // #2055: say which denial it is. Only the safety history's own read names it; anything else stays unknown (still denied).
261
+ const st = receipt(safety.status, d.id, device());
262
+ if (proposal !== revision) return;
263
+ const why = SAFETY_REASON[st?.state] ?? "safety_unknown";
264
+ results.set(d.key, { verdict: "off", reason: why, queuePos: null }); continue;
265
+ }
266
+ if (slots.has(d.key) || slots.size >= max || loads.size >= max || !globalAllowed(d) || context.hidden || !coverage(d) || proposal !== revision || !descriptors.some(x => x.key === d.key && signature(x) === signature(d) && x.element === d.element)) break;
235
267
  const rect = position(d, coverage(d));
236
268
  if (dead || proposal !== revision) return;
237
269
  if (!rect || d.element.ownerDocument !== host.document) { results.set(d.key, { verdict: "off", reason: "geometry_unknown", queuePos: null }); continue; }
@@ -254,7 +286,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
254
286
  const acceptCompletion = () => {
255
287
  if (!current(s)) { clearMarker(s, "superseded"); return false; }
256
288
  if (!withinDeadline()) return false;
257
- if (request.controller.signal.aborted || !globalAllowed() || context.hidden || !coverage(d)) {
289
+ if (request.controller.signal.aborted || !globalAllowed(d) || context.hidden || !coverage(d)) {
258
290
  if (current(s)) retire(s, "admission_revoked"); else clearMarker(s, "superseded");
259
291
  return false;
260
292
  }
@@ -287,10 +319,10 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
287
319
  if (d.trialStage && !local || !current(s)) { if (current(s)) retire(s, "admission_revoked"); return; }
288
320
  if (local) fresh = local.rect;
289
321
  s.rect = fresh;
290
- s.budget = new EffectBudget({ device: s.device, baselineFps: baseline, clock: nativeClock, longTasksSupported });
291
- const frame = createEffectFrame({ window: host, root: local?.root ?? root, origin, boot, clock: nativeClock,
322
+ s.budget = new EffectBudget({ device: s.device, clock: nativeClock, longTasksSupported });
323
+ const frame = createOpaqueEffectFrame({ window: host, root: local?.root ?? root, origin, boot, clock: nativeClock,
292
324
  render: { code: resource.code, params: resource.params, assets: resource.assets, wants: resource.wants, instance: d.instance, width: fresh.width, height: fresh.height, dpr: dpr(s.device) },
293
- onReady() { if (!current(s)) return; if (!globalAllowed() || context.hidden || !coverage(d)) { retire(s, "hidden"); tell(); return; } const n = time(); if (n === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return; } s.ready = true; s.readyAt = n; s.healthySince = n; publishEnv(s); schedule(); tell(); },
325
+ onReady() { if (!current(s)) return; if (!globalAllowed(d) || context.hidden || !coverage(d)) { retire(s, "hidden"); tell(); return; } const n = time(); if (n === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return; } s.ready = true; s.readyAt = n; s.healthySince = n; publishEnv(s); schedule(); tell(); },
294
326
  onRemove(why) { if (current(s)) { retire(s, why); tell(); } },
295
327
  });
296
328
  s.frame = frame;
@@ -316,7 +348,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
316
348
  schedule(); tell();
317
349
  }
318
350
  function publishEnv(s, proof = coverage(s.d)) {
319
- if (!s.ready || !current(s) || context.hidden || !globalAllowed() || !permits(s.d, proof)) return;
351
+ if (!s.ready || !current(s) || context.hidden || !globalAllowed(s.d) || !permits(s.d, proof)) return;
320
352
  const e = effectEvent("env", { width: s.rect.width, height: s.rect.height, dpr: dpr(s.device), quality: s.budget.snapshot().quality }, null, time());
321
353
  if (e) s.frame.send(e);
322
354
  }
@@ -339,12 +371,17 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
339
371
  if (changed) publishEnv(s, paint);
340
372
  if (s.ready) s.budget.frame();
341
373
  if (s.pointer) {
342
- const packet = s.pointer; s.pointer = null;
374
+ const packet = s.pointer;
343
375
  const n = time(), b = s.budget.snapshot();
344
- if (n !== null && n - packet.at <= 100 && permits(s.d, paint) && pointerGate(paint) && current(s) && b.forward && (s.lastPointer === null || n - s.lastPointer >= 1000 / b.pointerHz)) {
376
+ if (n === null || n - packet.at > 100 || !permits(s.d, paint) || !pointerGate(paint) || !current(s) || !b.forward) {
377
+ if (s.pointer === packet) s.pointer = null;
378
+ } else if (s.lastPointer === null || n - s.lastPointer >= 1000 / b.pointerHz) {
379
+ if (s.pointer === packet) s.pointer = null;
345
380
  const event = effectEvent("pointer", packet.fields, s.d.instance, packet.at);
346
381
  if (event && s.frame.send(event)) s.lastPointer = n;
347
382
  }
383
+ // Keep the latest fresh sample until the next eligible frame. An early rAF must
384
+ // not consume a click just because the preceding move used this rate interval.
348
385
  }
349
386
  }
350
387
  if (dead || proposal !== revision) return;
@@ -401,11 +438,16 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
401
438
  if (!globalAllowed() || context.hidden || context.modal || !proof || proof.front !== true) return false;
402
439
  try {
403
440
  const focused = host.document.activeElement;
404
- return host.document.designMode !== "on" && !!focused && ["BODY", "BUTTON", "A"].includes(focused.tagName) && focused.isContentEditable === false && typeof focused.matches === "function" && !focused.matches("input,textarea,select,[contenteditable]:not([contenteditable='false'])");
441
+ // Native clicks on ordinary stock content focus its tabindex=-1 main ancestor.
442
+ // Recognize only that unique host root; unknown/editor focus remains a refusal.
443
+ const mains = focused?.tagName === "MAIN" ? host.document.querySelectorAll("#app > .shell > main#main.main[tabindex='-1']") : [];
444
+ const stockMain = focused?.isConnected === true && focused.ownerDocument === host.document && mains.length === 1 && mains[0] === focused;
445
+ return host.document.designMode !== "on" && !!focused && (["BODY", "BUTTON", "A"].includes(focused.tagName) || stockMain) && focused.isContentEditable === false && typeof focused.matches === "function" && !focused.matches("input,textarea,select,[contenteditable]:not([contenteditable='false'])");
405
446
  } catch { return false; }
406
447
  }
407
448
  function pointer(event) {
408
- if (dead || blocked || !pointerGate() || event.isTrusted !== true) return;
449
+ // #2188: no frame to forward to — no gate to read (each read walks the page's computed styles, per pointer event).
450
+ if (dead || blocked || slots.size === 0 || !pointerGate() || event.isTrusted !== true) return;
409
451
  const kinds = { pointermove: "move", pointerdown: "down", pointerup: "up", click: "click", wheel: "wheel" }, kind = kinds[event.type];
410
452
  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;
411
453
  try { const target = host.document.elementFromPoint(event.clientX, event.clientY); if (!target || typeof target.closest !== "function" || target.closest("[data-trust]")) return; } catch { return; }
@@ -416,7 +458,11 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
416
458
  const fields = { kind, x: event.clientX - r.x, y: event.clientY - r.y, buttons: event.buttons };
417
459
  if (fields.x < 0 || fields.y < 0 || fields.x > r.width || fields.y > r.height) continue;
418
460
  if (kind === "wheel") { fields.dx = event.deltaX; fields.dy = event.deltaY; }
419
- if (effectEvent("pointer", fields, s.d.instance, at)) s.pointer = { fields, at };
461
+ if (effectEvent("pointer", fields, s.d.instance, at)) {
462
+ // One slot, with discrete input ahead of motion. A later move must not erase
463
+ // a waiting click; down/up/click may still coalesce to the latest discrete event.
464
+ if (kind !== "move" || !s.pointer || s.pointer.fields.kind === "move" || at - s.pointer.at > 100) s.pointer = { fields, at };
465
+ }
420
466
  }
421
467
  schedule();
422
468
  }
@@ -464,15 +510,16 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
464
510
  revalidateSafety,
465
511
  /** Called only by the host's explicit human Resume UI. A descriptor update never clears this latch. */
466
512
  resume(key) {
467
- if (dead || reentrantStock() || blocked && !blocked.startsWith("baseline_") || typeof key !== "string") return false;
513
+ if (dead || reentrantStock() || blocked || typeof key !== "string") return false;
468
514
  const proposed = revision, d = descriptors.find(d => d.key === key); if (!d || d.trialStage && !coverage(d) || revision !== proposed || receipt(safety.resume, d.id, device()) !== true || revision !== proposed) return false;
469
515
  revision++;
470
516
  if (!stopAll("human_resume")) return false;
471
- if (blocked && !blocked.startsWith("baseline_")) return false; blocked = null; paused.delete(key); failures.delete(key); baseline = null; serial++; reconcile(); return true;
517
+ if (blocked) return false; paused.delete(key); failures.delete(key); reconcile(); return true;
472
518
  },
473
519
  /** Host-only content-free signal API. Raw pointer publishing is refused; native pointer metadata is coalesced here. */
474
520
  publish(type, fields, instance = null) {
475
- if (dead || blocked || !globalAllowed() || context.hidden || !coverage() || !TYPES.includes(type)) return 0;
521
+ // #2188: no frame to send to — nothing to prove (the stock walk ran twice on every page change).
522
+ if (dead || blocked || slots.size === 0 || !globalAllowed() || context.hidden || !coverage() || !TYPES.includes(type)) return 0;
476
523
  const ts = time(); if (ts === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return 0; }
477
524
  let sent = 0; const proposal = revision;
478
525
  for (const s of [...slots.values()]) {
@@ -484,7 +531,7 @@ export function createEffectRegistry({ window: host, origin, boot, load, safety,
484
531
  return sent;
485
532
  },
486
533
  dispose() {
487
- if (dead) return; dead = true; revision++; serial++; stopAll("disposed");
534
+ if (dead) return; dead = true; revision++; stopAll("disposed");
488
535
  if (raf !== null) host.cancelAnimationFrame(raf); if (timer !== null) host.clearTimeout(timer);
489
536
  observer.disconnect(); resize.disconnect(); longTasks?.disconnect(); for (const off of offs) off();
490
537
  geometry.dispose(); root.remove(); observed.clear(); if (loads.size === 0 && registries.get(host) === api) registries.delete(host);
@@ -32,3 +32,12 @@ export function effectStockAllows(snapshot, { layer, by } = {}) {
32
32
  const proof = effectStockSnapshot(snapshot);
33
33
  return !!proof && (layer === "behind" || layer === "front" && proof.front && (by === "human" || proof.agentFront));
34
34
  }
35
+
36
+ /** Display only. Refused provider metadata never becomes paint evidence or authority. */
37
+ export function effectStockRefusal(value) {
38
+ try {
39
+ if (!plain(value)) return "stock_evidence_invalid";
40
+ const d = Object.getOwnPropertyDescriptor(value, "reason");
41
+ return d && Object.hasOwn(d, "value") && typeof d.value === "string" && /^[a-z][a-z0-9_]{0,79}$/.test(d.value) ? d.value : "stock_evidence_invalid";
42
+ } catch { return "stock_evidence_invalid"; }
43
+ }
@@ -11,7 +11,7 @@ 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 .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 .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
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"],
@@ -20,7 +20,7 @@ const SPECS = [
20
20
  ];
21
21
  export const STOCK_TRUST_INVENTORY = Object.freeze(SPECS.map(([kind, selector]) => Object.freeze({ kind, selector })));
22
22
  const STRUCTURAL = ["html", "body", "#app", "#app > .shell", ".shell > .main", ".main > .p-chat", ".p-chat > .chat-split", ".chat-split > .chat-main"];
23
- const CONTENT = [".main > .conn", ".p-chat > .panel-head", ".p-chat > .chat-card", ".p-chat > .aw-chat_header", ".chat-main > .scroller", ".chat-main > .dock", ".sidebar > .side-head", ".sidebar > .side-nav", ".sidebar > .side-section", ".sidebar > .side-widget", ".sidebar > .side-foot", ".u-rail > .u-rail-head", ".u-rail > .u-rail-body"];
23
+ const CONTENT = [".main > .conn", ".p-chat > .panel-head", ".p-chat > .chat-card", ".p-chat > .aw-chat_header", ".chat-main > .amb-note", ".chat-main > .scroller", ".chat-main > .dock", ".sidebar > .side-head", ".sidebar > .side-nav", ".sidebar > .side-section", ".sidebar > .side-widget", ".sidebar > .side-foot", ".u-rail > .u-rail-head", ".u-rail > .u-rail-body"];
24
24
  // Closed producer/topology inventory. Bare Outlet loading/error panels never become an effect surface.
25
25
  const PANELS = Object.freeze({
26
26
  fleet: { required: [".panel-head", ".seg", ".panel-body"], optional: [] },
@@ -95,14 +95,58 @@ function outset(style) {
95
95
  }
96
96
  return pad <= 256 ? Math.ceil(pad) : null;
97
97
  }
98
+ /**
99
+ * #2086: the identity card's banner fill (a blurred copy beside a centred banner) — the one filter stock admits. Only
100
+ * this exact value, on exactly `.id-banner-box > img.id-banner-fill`, absolutely positioned in that box (so the box is
101
+ * its containing block — #2089 review: a fixed fill is the viewport's, and no box clips it), and the box positioned and
102
+ * clipping on both axes; its paint is then that box's rectangle (the blur and its scale never leave it). The box must
103
+ * also be a real block box that the browser reports as the fill's offset parent (#2089 review r3: `display: contents`
104
+ * keeps the computed position and overflow but generates no box, so an outer ancestor would place the fill, and an inline
105
+ * box clips differently). Anything else with a filter refuses.
106
+ */
107
+ export const STOCK_BANNER_FILL_FILTER = "blur(24px) saturate(0.75) brightness(0.7)";
108
+ function stockBannerFill(host, el) {
109
+ const box = el.parentElement;
110
+ if (typeof el.matches !== "function" || !el.matches(".id-banner-box > img.id-banner-fill") || !box) return null;
111
+ const s = host.getComputedStyle(el), b = host.getComputedStyle(box);
112
+ if (s.filter !== STOCK_BANNER_FILL_FILTER || !none(s.backdropFilter) || !none(b.filter) || !none(b.backdropFilter)) return null;
113
+ if (!["hidden", "clip"].includes(b.overflowX) || !["hidden", "clip"].includes(b.overflowY)) return null;
114
+ if (s.position !== "absolute" || !["relative", "absolute"].includes(b.position)) return null;
115
+ if (b.display !== "block" || el.offsetParent !== box) return null;
116
+ return rect(box);
117
+ }
118
+ function zeroClipMargin(host, style) {
119
+ const margin = style.overflowClipMargin ?? style.getPropertyValue?.("overflow-clip-margin");
120
+ // WebKit versions without this property still implement paint containment. An absent
121
+ // computed value is usable only when the engine explicitly reports it unsupported;
122
+ // unknown capability or a supported but unreadable/expanded margin cannot prove a box.
123
+ return margin === "0px" || [undefined, ""].includes(margin)
124
+ && host.CSS?.supports?.("overflow-clip-margin", "1px") === false;
125
+ }
98
126
  function protectedPaint(host, root, budget) {
127
+ // These two stock toolbar boxes physically clip ALL descendants, including fixed-position
128
+ // children. Their entire box is a hole; descendant ink cannot escape it. Inline, fragmented,
129
+ // expanded-clip or CSS-overridden boxes take the ordinary bounded walk instead.
130
+ const s = host.getComputedStyle(root);
131
+ if (root.matches?.(".shell .thread .msg-tools, .shell .thread .codeblock > .cb-head") && s.contain === "paint"
132
+ && s.display === "flex" && s.overflowX === "clip" && s.overflowY === "clip" && zeroClipMargin(host, s)
133
+ && none(s.filter) && none(s.backdropFilter) && s.borderImageSource === "none" && [undefined, "", "visible"].includes(s.contentVisibility) && root.getClientRects().length === 1) {
134
+ if (++budget.n > 4096) { budget.exceeded = true; return null; }
135
+ const r = rect(root), pad = outset(s);
136
+ // Containment clips descendants, not the root's own ink. Do not infer a world-space
137
+ // outset from local CSS lengths under a potentially transformed content ancestor.
138
+ if (!r || pad !== 0) return null;
139
+ return r;
140
+ }
99
141
  const nodes = [root, ...root.querySelectorAll("*")]; budget.n += nodes.length;
100
- if (budget.n > 4096) return null;
142
+ if (budget.n > 4096) { budget.exceeded = true; return null; }
101
143
  let union = null;
102
144
  for (const el of nodes) {
103
145
  if (!visible(host, el)) continue;
104
- const style = host.getComputedStyle(el), r = rect(el), pad = outset(style);
105
- if (!r || pad === null || !none(style.filter) || !none(style.backdropFilter)) return null;
146
+ const style = host.getComputedStyle(el), pad = outset(style);
147
+ if (style.borderImageSource !== undefined && style.borderImageSource !== "none" || ![undefined, "", "visible"].includes(style.contentVisibility)) return null;
148
+ const r = none(style.filter) ? rect(el) : stockBannerFill(host, el);
149
+ if (!r || pad === null || !none(style.backdropFilter)) return null;
106
150
  const a = { x: r.x - pad, y: r.y - pad, width: r.width + pad * 2, height: r.height + pad * 2 };
107
151
  if (!union) union = a;
108
152
  else { const x = Math.min(union.x, a.x), y = Math.min(union.y, a.y); union = { x, y, width: Math.max(union.x + union.width, a.x + a.width) - x, height: Math.max(union.y + union.height, a.y + a.height) - y }; }
@@ -144,22 +188,30 @@ export function stockEffectTrust(host, { frameRoot = null, ownerCss = "unknown"
144
188
  if (s.position !== "relative" || s.zIndex !== "1" || !neutral(s)) return fail("stock_order_unknown");
145
189
  }
146
190
  const marked = [...doc.querySelectorAll("[data-trust]")], expected = new Set(), rects = [], budget = { n: 0 };
147
- if (marked.length > 128) return fail("stock_trust_unknown");
191
+ if (marked.length > 4096) return fail("stock_marker_limit");
148
192
  for (const { kind, selector } of STOCK_TRUST_INVENTORY) for (const el of doc.querySelectorAll(selector)) {
149
193
  if (el.ownerDocument !== doc || el.isConnected !== true || el.getAttribute("data-trust") !== kind) return fail("stock_trust_unknown");
150
194
  expected.add(el);
151
195
  }
152
196
  if (marked.some(el => !expected.has(el)) || surface.kind === "chat" && !chat.querySelector('.dock[data-trust="composer"]') || !surface.panel.querySelector('.panel-head[data-trust="header"]')) return fail("stock_trust_unknown");
153
197
  for (const el of marked) {
154
- const bounds = protectedPaint(host, el, budget); if (!bounds) return fail("stock_paint_unknown");
155
- if (bounds.width && bounds.height) rects.push(bounds);
198
+ const bounds = protectedPaint(host, el, budget); if (!bounds) return fail(budget.exceeded ? "stock_paint_limit" : "stock_paint_unknown");
199
+ // Off-screen history remains inventoried and paint-checked; it need not consume a
200
+ // viewport hole. Include the clip algorithm's extra 8px protection margin.
201
+ if (bounds.width && bounds.height && (!finite(host.innerHeight) || bounds.x + bounds.width + 8 >= 0 && bounds.y + bounds.height + 8 >= 0 && bounds.x - 8 <= host.innerWidth && bounds.y - 8 <= host.innerHeight)) rects.push(bounds);
202
+ }
203
+ // Keep the existing 128-hole transport bound. Conservative union may hide extra
204
+ // effect pixels; it can never reveal protected pixels, even for a dense viewport.
205
+ if (rects.length > 128) {
206
+ const rest = rects.splice(127), x = Math.min(...rest.map(r => r.x)), y = Math.min(...rest.map(r => r.y));
207
+ rects.push({ x, y, width: Math.max(...rest.map(r => r.x + r.width)) - x, height: Math.max(...rest.map(r => r.y + r.height)) - y });
156
208
  }
157
209
  if (doc.querySelector(".agend-session-ended")) return fail("session_ended");
158
210
  let barrier = [...doc.querySelectorAll(BARRIERS)].some(el => visible(host, el));
159
211
  if (host.innerWidth < 900 && [...doc.querySelectorAll(".pv-panel")].some(el => visible(host, el))) barrier = true;
160
- const active = doc.activeElement;
161
212
  if (doc.fullscreenElement || doc.pictureInPictureElement) barrier = true;
162
- if (active && active !== doc.body && active !== doc.documentElement && (active.matches("input,select,textarea,[contenteditable]") || active.isContentEditable)) barrier = true;
213
+ // Focus alone does not change protected paint bounds. Composer/controls stay clipped; editable focus
214
+ // separately suppresses pointer delivery in the registry, and keyboard events never enter a Worker.
163
215
  return { ok: true, rects, front: !barrier, reason: barrier ? "stock_overlay" : "stock", agentFront: !barrier && ownerCss === "off", agentReason: barrier ? "stock_overlay" : ownerCss === "off" ? "stock" : "owner_css", settingsHref: "/settings/appearance" };
164
216
  } catch { return fail("stock_trust_unknown"); }
165
217
  }
@@ -0,0 +1,62 @@
1
+ /** #2039: fixed host-only closed shadow shell. Owner CSS has no exported parts or slots into it. */
2
+ const roots = new WeakMap();
3
+ const clear = root => { while (root.firstChild) root.removeChild(root.firstChild); };
4
+ function sheetOf(host, record) {
5
+ try {
6
+ const link = record.link, sheet = link?.sheet;
7
+ return record.ready && !record.dead && roots.get(record.panel)?.current === record && record.root.mode === "closed" && record.root.host === record.panel
8
+ && link.getRootNode() === record.root && link.isConnected && link.disabled === false && sheet?.ownerNode === link && sheet.disabled === false
9
+ && sheet.href === new URL("/assets/effect-trial.css", host.location.origin).href && typeof sheet.insertRule === "function" && typeof sheet.deleteRule === "function" && sheet.cssRules ? sheet : null;
10
+ } catch { return null; }
11
+ }
12
+ /** Reflect only a shell issued here, after its fixed same-origin stylesheet loaded. Never a serialized capability. */
13
+ export function effectTrialShadow(host, panel) {
14
+ const record = roots.get(panel)?.current;
15
+ return record && sheetOf(host, record) === record.proof?.sheet ? record.proof : null;
16
+ }
17
+ export function createEffectTrialShadow({ window: host, panel, title, labels, onStop, onClose, onReady, onError } = {}) {
18
+ if (!host?.document || panel?.ownerDocument !== host.document || typeof panel.attachShadow !== "function" || typeof onReady !== "function" || typeof onError !== "function") return null;
19
+ let slot = roots.get(panel), cleanup = null;
20
+ const clock = () => (host.performance ?? globalThis.performance).now(), later = host.setTimeout?.bind(host) ?? globalThis.setTimeout, cancel = host.clearTimeout?.bind(host) ?? globalThis.clearTimeout;
21
+ try {
22
+ if (slot?.current && !slot.current.dead) return null;
23
+ if (!slot) { slot = { root: panel.attachShadow({ mode: "closed" }), current: null }; roots.set(panel, slot); }
24
+ const root = slot.root; clear(root);
25
+ const doc = host.document, element = (tag, cls) => { const el = doc.createElement(tag); el.className = cls; return el; };
26
+ const link = element("link", "effect-trial-style"); link.setAttribute("rel", "stylesheet"); link.setAttribute("href", "/assets/effect-trial.css");
27
+ const head = element("header", "effect-trial-head"), label = element("strong", "effect-trial-label"), name = element("span", "effect-trial-title"), close = element("button", "effect-trial-close"), controls = element("div", "effect-trial-controls"), stop = element("button", "effect-trial-stop"), body = element("div", "effect-trial-body"), stage = element("div", "effect-trial-stage"), note = element("p", "effect-trial-note");
28
+ close.setAttribute("type", "button"); stop.setAttribute("type", "button"); stage.setAttribute("aria-hidden", "true");
29
+ head.append(label, name, close); controls.append(stop); body.append(stage);
30
+ const nodes = [head, controls, body, note]; for (const el of nodes) el.setAttribute("hidden", "");
31
+ const began = clock(); if (!Number.isFinite(began) || began < 0) return null;
32
+ let timer = null;
33
+ const record = { panel, root, link, stage, head, controls, body, note, close, stop, ready: false, dead: false, proof: null }; slot.current = record;
34
+ const text = (nextTitle, nextLabels) => {
35
+ if (record.dead || !nextLabels) return;
36
+ label.textContent = nextLabels.label; name.textContent = typeof nextTitle === "string" ? nextTitle : "";
37
+ close.textContent = nextLabels.close; stop.textContent = nextLabels.stop; note.textContent = nextLabels.note;
38
+ panel.setAttribute("data-effect-trial-theme", doc.documentElement.dataset.theme === "dark" ? "dark" : "light");
39
+ };
40
+ const failed = () => { if (!record.dead) { dispose(); try { onError("stage_unavailable"); } catch { /* cleanup already owns failure */ } } };
41
+ const loaded = () => {
42
+ if (record.dead || record.ready || slot.current !== record) return;
43
+ const at = clock(); if (!Number.isFinite(at) || at < began || at - began >= 10000) { failed(); return; }
44
+ record.ready = true; const sheet = sheetOf(host, record);
45
+ if (!sheet) { failed(); return; }
46
+ cancel(timer); timer = null;
47
+ record.proof = Object.freeze({ panel, root, link, sheet, stage, head, controls, body, note, close, stop });
48
+ for (const el of nodes) el.removeAttribute("hidden");
49
+ try { onReady(record.proof); } catch { failed(); }
50
+ };
51
+ const stopClick = () => { if (!record.dead) onStop?.(); }, closeClick = () => { if (!record.dead) onClose?.(); };
52
+ function dispose() {
53
+ if (record.dead) return; record.dead = true; cancel(timer); timer = null;
54
+ link.removeEventListener("load", loaded); link.removeEventListener("error", failed); stop.removeEventListener("click", stopClick); close.removeEventListener("click", closeClick);
55
+ if (slot.current === record) { slot.current = null; clear(root); }
56
+ }
57
+ cleanup = dispose;
58
+ link.addEventListener("load", loaded); link.addEventListener("error", failed); stop.addEventListener("click", stopClick); close.addEventListener("click", closeClick);
59
+ text(title, labels); timer = later(failed, 10000); root.append(link, ...nodes);
60
+ return Object.freeze({ dispose, text });
61
+ } catch { cleanup?.(); return null; }
62
+ }
@@ -64,7 +64,7 @@ export function createEffectTrialSource({ window: host, current, ownerCurrent, s
64
64
  const lease = { identity, reply, stage, owner, current: x.current, onStop: x.onStop, startedAt: x.startedAt, until, dead: false, checking: false, pending: new Set(), timer: null, off: null, binding: null };
65
65
  lease.binding = Object.freeze({ kind: "trial", key: identity.key, id: identity.id, source: lease, generation: identity.generation });
66
66
  active = lease; used.add(stage.receipt);
67
- lease.off = watchEffectTrialStage(stage.receipt, () => stop(lease, "stage_ended"));
67
+ lease.off = watchEffectTrialStage(stage.receipt, reason => stop(lease, typeof reason === "string" ? reason : "stage_ended"));
68
68
  if (typeof lease.off !== "function" || constrainEffectTrialStage(stage.receipt, () => checked(lease)) !== true || !checked(lease)) { stop(lease, "stage_unavailable"); return null; }
69
69
  try { arm(lease); } catch { stop(lease, "trial_unavailable"); return null; }
70
70
  if (!checked(lease)) return null;