@songsid/agend 2.2.0-beta.9 → 2.3.0-beta.1

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 (422) hide show
  1. package/README.md +40 -24
  2. package/README.zh-TW.md +40 -24
  3. package/dist/agent-cli.js +248 -2
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.d.ts +2 -0
  6. package/dist/agent-endpoint.js +28 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.d.ts +30 -0
  9. package/dist/agent-file-read.js +129 -0
  10. package/dist/agent-file-read.js.map +1 -0
  11. package/dist/agent-whats-new.js +20 -1
  12. package/dist/agent-whats-new.js.map +1 -1
  13. package/dist/atomic-write.js +9 -1
  14. package/dist/atomic-write.js.map +1 -1
  15. package/dist/auth-api.d.ts +3 -1
  16. package/dist/auth-api.js +32 -1
  17. package/dist/auth-api.js.map +1 -1
  18. package/dist/backend/antigravity.d.ts +33 -0
  19. package/dist/backend/antigravity.js +129 -21
  20. package/dist/backend/antigravity.js.map +1 -1
  21. package/dist/backend/claude-code.d.ts +3 -0
  22. package/dist/backend/claude-code.js +9 -0
  23. package/dist/backend/claude-code.js.map +1 -1
  24. package/dist/backend/codex-metadata.d.ts +33 -0
  25. package/dist/backend/codex-metadata.js +122 -18
  26. package/dist/backend/codex-metadata.js.map +1 -1
  27. package/dist/backend/codex.d.ts +3 -0
  28. package/dist/backend/codex.js +17 -1
  29. package/dist/backend/codex.js.map +1 -1
  30. package/dist/backend/effort-metadata.d.ts +37 -1
  31. package/dist/backend/effort-metadata.js +86 -15
  32. package/dist/backend/effort-metadata.js.map +1 -1
  33. package/dist/backend/grok.d.ts +3 -0
  34. package/dist/backend/grok.js +9 -0
  35. package/dist/backend/grok.js.map +1 -1
  36. package/dist/backend/kiro.d.ts +3 -1
  37. package/dist/backend/kiro.js +7 -3
  38. package/dist/backend/kiro.js.map +1 -1
  39. package/dist/backend/launch-effort.d.ts +61 -0
  40. package/dist/backend/launch-effort.js +170 -0
  41. package/dist/backend/launch-effort.js.map +1 -0
  42. package/dist/backend/muse.js +10 -2
  43. package/dist/backend/muse.js.map +1 -1
  44. package/dist/backend/types.d.ts +24 -0
  45. package/dist/backend/types.js +12 -1
  46. package/dist/backend/types.js.map +1 -1
  47. package/dist/backend-command.d.ts +91 -0
  48. package/dist/backend-command.js +122 -0
  49. package/dist/backend-command.js.map +1 -0
  50. package/dist/channel/adapters/discord.d.ts +14 -0
  51. package/dist/channel/adapters/discord.js +66 -4
  52. package/dist/channel/adapters/discord.js.map +1 -1
  53. package/dist/channel/adapters/telegram.d.ts +26 -1
  54. package/dist/channel/adapters/telegram.js +98 -7
  55. package/dist/channel/adapters/telegram.js.map +1 -1
  56. package/dist/channel/mcp-tools.js +36 -0
  57. package/dist/channel/mcp-tools.js.map +1 -1
  58. package/dist/channel/tool-router.d.ts +2 -0
  59. package/dist/channel/tool-router.js +1 -1
  60. package/dist/channel/tool-router.js.map +1 -1
  61. package/dist/channel/types.d.ts +31 -0
  62. package/dist/cli.js +124 -24
  63. package/dist/cli.js.map +1 -1
  64. package/dist/command-table.d.ts +2 -2
  65. package/dist/command-table.js +5 -2
  66. package/dist/command-table.js.map +1 -1
  67. package/dist/compact-watch.d.ts +86 -0
  68. package/dist/compact-watch.js +288 -0
  69. package/dist/compact-watch.js.map +1 -0
  70. package/dist/config-validator.js +55 -0
  71. package/dist/config-validator.js.map +1 -1
  72. package/dist/config.d.ts +1 -0
  73. package/dist/config.js +2 -1
  74. package/dist/config.js.map +1 -1
  75. package/dist/daemon.d.ts +9 -0
  76. package/dist/daemon.js +394 -29
  77. package/dist/daemon.js.map +1 -1
  78. package/dist/delivery-outbox.d.ts +8 -1
  79. package/dist/delivery-outbox.js +6 -2
  80. package/dist/delivery-outbox.js.map +1 -1
  81. package/dist/doctor.js +7 -0
  82. package/dist/doctor.js.map +1 -1
  83. package/dist/fleet-context.d.ts +12 -0
  84. package/dist/fleet-manager.d.ts +247 -9
  85. package/dist/fleet-manager.js +1227 -142
  86. package/dist/fleet-manager.js.map +1 -1
  87. package/dist/forged-envelope-scan.d.ts +4 -3
  88. package/dist/forged-envelope-scan.js +4 -3
  89. package/dist/forged-envelope-scan.js.map +1 -1
  90. package/dist/general-knowledge/skills/model-discovery/SKILL.md +4 -1
  91. package/dist/general-knowledge/skills/web-appearance/SKILL.md +258 -0
  92. package/dist/install-lock.d.ts +18 -1
  93. package/dist/install-lock.js +30 -3
  94. package/dist/install-lock.js.map +1 -1
  95. package/dist/instance-avatar.d.ts +5 -4
  96. package/dist/instance-avatar.js +15 -8
  97. package/dist/instance-avatar.js.map +1 -1
  98. package/dist/instance-config-impact.js +3 -0
  99. package/dist/instance-config-impact.js.map +1 -1
  100. package/dist/instance-lifecycle.js +9 -4
  101. package/dist/instance-lifecycle.js.map +1 -1
  102. package/dist/instructions.js +2 -2
  103. package/dist/instructions.js.map +1 -1
  104. package/dist/kiro-compact-pane.d.ts +38 -0
  105. package/dist/kiro-compact-pane.js +57 -0
  106. package/dist/kiro-compact-pane.js.map +1 -0
  107. package/dist/launchd-handoff.d.ts +43 -0
  108. package/dist/launchd-handoff.js +56 -0
  109. package/dist/launchd-handoff.js.map +1 -0
  110. package/dist/locale.js +202 -30
  111. package/dist/locale.js.map +1 -1
  112. package/dist/log-service.js +10 -2
  113. package/dist/log-service.js.map +1 -1
  114. package/dist/login-shell.d.ts +21 -0
  115. package/dist/login-shell.js +98 -0
  116. package/dist/login-shell.js.map +1 -0
  117. package/dist/needs-you-hub.d.ts +4 -0
  118. package/dist/needs-you-hub.js +31 -6
  119. package/dist/needs-you-hub.js.map +1 -1
  120. package/dist/needs-you.d.ts +1 -13
  121. package/dist/needs-you.js +3 -37
  122. package/dist/needs-you.js.map +1 -1
  123. package/dist/outbound-handlers.d.ts +32 -0
  124. package/dist/outbound-handlers.js +150 -45
  125. package/dist/outbound-handlers.js.map +1 -1
  126. package/dist/outbound-schemas.d.ts +76 -0
  127. package/dist/outbound-schemas.js +65 -4
  128. package/dist/outbound-schemas.js.map +1 -1
  129. package/dist/provider-probe.d.ts +29 -0
  130. package/dist/provider-probe.js +198 -2
  131. package/dist/provider-probe.js.map +1 -1
  132. package/dist/public-web-gateway.d.ts +2 -0
  133. package/dist/public-web-gateway.js +25 -1
  134. package/dist/public-web-gateway.js.map +1 -1
  135. package/dist/quickstart-api.d.ts +1 -1
  136. package/dist/quickstart-api.js +8 -6
  137. package/dist/quickstart-api.js.map +1 -1
  138. package/dist/quickstart.d.ts +12 -0
  139. package/dist/quickstart.js +36 -6
  140. package/dist/quickstart.js.map +1 -1
  141. package/dist/reply-buttons-controller.d.ts +86 -5
  142. package/dist/reply-buttons-controller.js +271 -7
  143. package/dist/reply-buttons-controller.js.map +1 -1
  144. package/dist/reply-buttons.d.ts +48 -3
  145. package/dist/reply-buttons.js +105 -10
  146. package/dist/reply-buttons.js.map +1 -1
  147. package/dist/reply-polls.d.ts +85 -0
  148. package/dist/reply-polls.js +154 -0
  149. package/dist/reply-polls.js.map +1 -0
  150. package/dist/repo-worktree-guard.d.ts +36 -0
  151. package/dist/repo-worktree-guard.js +143 -0
  152. package/dist/repo-worktree-guard.js.map +1 -0
  153. package/dist/restart-guard.d.ts +6 -3
  154. package/dist/restart-guard.js +6 -3
  155. package/dist/restart-guard.js.map +1 -1
  156. package/dist/restart-progress.d.ts +0 -2
  157. package/dist/restart-progress.js +0 -2
  158. package/dist/restart-progress.js.map +1 -1
  159. package/dist/scheduler/scheduler.d.ts +58 -1
  160. package/dist/scheduler/scheduler.js +176 -10
  161. package/dist/scheduler/scheduler.js.map +1 -1
  162. package/dist/sd-notify.d.ts +22 -1
  163. package/dist/sd-notify.js +63 -10
  164. package/dist/sd-notify.js.map +1 -1
  165. package/dist/service-activation.d.ts +15 -7
  166. package/dist/service-activation.js +42 -26
  167. package/dist/service-activation.js.map +1 -1
  168. package/dist/service-installer.d.ts +24 -0
  169. package/dist/service-installer.js +49 -1
  170. package/dist/service-installer.js.map +1 -1
  171. package/dist/service-plan.d.ts +11 -0
  172. package/dist/service-plan.js +23 -0
  173. package/dist/service-plan.js.map +1 -1
  174. package/dist/settings-api.js +74 -1
  175. package/dist/settings-api.js.map +1 -1
  176. package/dist/settings-change.js +8 -0
  177. package/dist/settings-change.js.map +1 -1
  178. package/dist/settings-effect.js +16 -2
  179. package/dist/settings-effect.js.map +1 -1
  180. package/dist/settings-http-confirmation.js +5 -0
  181. package/dist/settings-http-confirmation.js.map +1 -1
  182. package/dist/systemd-peer-bus.d.ts +95 -0
  183. package/dist/systemd-peer-bus.js +430 -0
  184. package/dist/systemd-peer-bus.js.map +1 -0
  185. package/dist/systemd-runtime.d.ts +27 -1
  186. package/dist/systemd-runtime.js +69 -7
  187. package/dist/systemd-runtime.js.map +1 -1
  188. package/dist/tool-permissions.js +53 -3
  189. package/dist/tool-permissions.js.map +1 -1
  190. package/dist/topic-commands.d.ts +72 -2
  191. package/dist/topic-commands.js +156 -38
  192. package/dist/topic-commands.js.map +1 -1
  193. package/dist/topic-link.d.ts +128 -0
  194. package/dist/topic-link.js +269 -0
  195. package/dist/topic-link.js.map +1 -0
  196. package/dist/transcript-jsonl.d.ts +7 -2
  197. package/dist/transcript-jsonl.js +13 -7
  198. package/dist/transcript-jsonl.js.map +1 -1
  199. package/dist/transcript-sources.js +6 -1
  200. package/dist/transcript-sources.js.map +1 -1
  201. package/dist/types.d.ts +21 -0
  202. package/dist/ui/app-effects.js +187 -0
  203. package/dist/ui/chat-commands.js +1 -0
  204. package/dist/ui/chat-effect-posters.js +42 -0
  205. package/dist/ui/chat-effects.js +72 -0
  206. package/dist/ui/chat-live-replies.js +58 -0
  207. package/dist/ui/chat-render.js +59 -14
  208. package/dist/ui/chat-store.js +78 -8
  209. package/dist/ui/chat-strings.js +43 -16
  210. package/dist/ui/chat-thread.js +175 -31
  211. package/dist/ui/chat-tour.js +18 -15
  212. package/dist/ui/effect-trial-controller.js +119 -0
  213. package/dist/ui/effect-trial-offer.js +31 -0
  214. package/dist/ui/effect-trial-panel.js +37 -0
  215. package/dist/ui/image-lightbox.js +1 -1
  216. package/dist/ui/panel-chat.js +101 -27
  217. package/dist/ui/panel-details.js +18 -5
  218. package/dist/ui/panel-fleet.js +18 -5
  219. package/dist/ui/panel-log.js +10 -10
  220. package/dist/ui/panel-needs.js +13 -10
  221. package/dist/ui/panel-settings.js +66 -12
  222. package/dist/ui/preview-panel.js +25 -9
  223. package/dist/ui/settings-appearance.js +325 -0
  224. package/dist/ui/settings-dialogs.js +11 -6
  225. package/dist/ui/settings-effects.js +201 -0
  226. package/dist/ui/settings-model.js +3 -1
  227. package/dist/ui/settings-mods.js +257 -0
  228. package/dist/ui/settings-owner-effects.js +80 -0
  229. package/dist/ui/settings-strings.js +269 -10
  230. package/dist/ui/settings-token.js +7 -0
  231. package/dist/ui/settings-wizard.js +5 -3
  232. package/dist/ui/shared/agend-auth.js +27 -3
  233. package/dist/ui/shared/agent-widget.js +248 -0
  234. package/dist/ui/shared/app-i18n.js +3 -1
  235. package/dist/ui/shared/app-prefs.js +30 -10
  236. package/dist/ui/shared/app-route.js +23 -1
  237. package/dist/ui/shared/app-session.js +1 -1
  238. package/dist/ui/shared/app-shell.js +29 -16
  239. package/dist/ui/shared/app-store.js +2 -0
  240. package/dist/ui/shared/app-stream.js +15 -7
  241. package/dist/ui/shared/app-themes.js +188 -0
  242. package/dist/ui/shared/app.css +266 -11
  243. package/dist/ui/shared/app.js +6 -0
  244. package/dist/ui/shared/card-strings.js +28 -0
  245. package/dist/ui/shared/effect-app-runtime.js +248 -0
  246. package/dist/ui/shared/effect-baseline.js +64 -0
  247. package/dist/ui/shared/effect-chat-runtime.js +154 -0
  248. package/dist/ui/shared/effect-cursor.js +125 -0
  249. package/dist/ui/shared/effect-frame.js +127 -0
  250. package/dist/ui/shared/effect-geometry.js +122 -0
  251. package/dist/ui/shared/effect-host-style.js +10 -0
  252. package/dist/ui/shared/effect-owner-regions.js +74 -0
  253. package/dist/ui/shared/effect-owner-source.js +115 -0
  254. package/dist/ui/shared/effect-policy.js +137 -0
  255. package/dist/ui/shared/effect-poster-reader.js +98 -0
  256. package/dist/ui/shared/effect-posters.js +117 -0
  257. package/dist/ui/shared/effect-profiler.js +162 -0
  258. package/dist/ui/shared/effect-profiles.js +65 -0
  259. package/dist/ui/shared/effect-registry.js +496 -0
  260. package/dist/ui/shared/effect-runtime-loader.js +234 -0
  261. package/dist/ui/shared/effect-safety.js +153 -0
  262. package/dist/ui/shared/effect-stock-snapshot.js +34 -0
  263. package/dist/ui/shared/effect-stock-trust.js +165 -0
  264. package/dist/ui/shared/effect-trial-source.js +103 -0
  265. package/dist/ui/shared/effect-trial-stage.js +147 -0
  266. package/dist/ui/shared/header-tools.js +35 -15
  267. package/dist/ui/shared/identity-card.js +202 -0
  268. package/dist/ui/shared/identity-color.js +261 -0
  269. package/dist/ui/shared/instance-nav.js +22 -14
  270. package/dist/ui/shared/instance-switch.js +5 -2
  271. package/dist/ui/shared/owner-css.js +221 -0
  272. package/dist/ui/shared/owner-effect-prefs.js +117 -0
  273. package/dist/ui/shared/panel-view.js +96 -12
  274. package/dist/ui/shared/signin.js +17 -9
  275. package/dist/ui/shared/ui-dialog.js +1 -1
  276. package/dist/ui/shared/ui-icons.js +3 -0
  277. package/dist/ui/shared/ui-menu.js +1 -1
  278. package/dist/ui/shared/ui-toast.js +1 -1
  279. package/dist/ui/shared/view-strings.js +40 -2
  280. package/dist/ui/signin.html +9 -3
  281. package/dist/update-check.d.ts +31 -0
  282. package/dist/update-check.js +47 -0
  283. package/dist/update-check.js.map +1 -1
  284. package/dist/update-launch.d.ts +48 -1
  285. package/dist/update-launch.js +159 -5
  286. package/dist/update-launch.js.map +1 -1
  287. package/dist/usage/providers.d.ts +28 -8
  288. package/dist/usage/providers.js +96 -41
  289. package/dist/usage/providers.js.map +1 -1
  290. package/dist/view-api.d.ts +29 -0
  291. package/dist/view-api.js +214 -17
  292. package/dist/view-api.js.map +1 -1
  293. package/dist/web-api.d.ts +86 -2
  294. package/dist/web-api.js +1160 -92
  295. package/dist/web-api.js.map +1 -1
  296. package/dist/web-appearance-limits.d.ts +82 -0
  297. package/dist/web-appearance-limits.js +83 -0
  298. package/dist/web-appearance-limits.js.map +1 -0
  299. package/dist/web-appearance-policy.d.ts +66 -0
  300. package/dist/web-appearance-policy.js +94 -0
  301. package/dist/web-appearance-policy.js.map +1 -0
  302. package/dist/web-appearance-tools.d.ts +50 -0
  303. package/dist/web-appearance-tools.js +105 -0
  304. package/dist/web-appearance-tools.js.map +1 -0
  305. package/dist/web-appearance-view.d.ts +99 -0
  306. package/dist/web-appearance-view.js +163 -0
  307. package/dist/web-appearance-view.js.map +1 -0
  308. package/dist/web-appearance.d.ts +79 -0
  309. package/dist/web-appearance.js +298 -0
  310. package/dist/web-appearance.js.map +1 -0
  311. package/dist/web-asset-parts.d.ts +98 -0
  312. package/dist/web-asset-parts.js +133 -0
  313. package/dist/web-asset-parts.js.map +1 -0
  314. package/dist/web-auth.js +17 -1
  315. package/dist/web-auth.js.map +1 -1
  316. package/dist/web-card.d.ts +96 -0
  317. package/dist/web-card.js +232 -0
  318. package/dist/web-card.js.map +1 -0
  319. package/dist/web-chat-history.d.ts +39 -1
  320. package/dist/web-chat-history.js +69 -9
  321. package/dist/web-chat-history.js.map +1 -1
  322. package/dist/web-commands.d.ts +39 -3
  323. package/dist/web-commands.js +22 -5
  324. package/dist/web-commands.js.map +1 -1
  325. package/dist/web-effect-bundle.d.ts +28 -0
  326. package/dist/web-effect-bundle.js +126 -0
  327. package/dist/web-effect-bundle.js.map +1 -0
  328. package/dist/web-effect-canonical.d.ts +8 -0
  329. package/dist/web-effect-canonical.js +27 -0
  330. package/dist/web-effect-canonical.js.map +1 -0
  331. package/dist/web-effect-library.d.ts +114 -0
  332. package/dist/web-effect-library.js +530 -0
  333. package/dist/web-effect-library.js.map +1 -0
  334. package/dist/web-effect-poster.d.ts +8 -0
  335. package/dist/web-effect-poster.js +52 -0
  336. package/dist/web-effect-poster.js.map +1 -0
  337. package/dist/web-effect-profiles.d.ts +118 -0
  338. package/dist/web-effect-profiles.js +180 -0
  339. package/dist/web-effect-profiles.js.map +1 -0
  340. package/dist/web-effect-service.d.ts +86 -0
  341. package/dist/web-effect-service.js +312 -0
  342. package/dist/web-effect-service.js.map +1 -0
  343. package/dist/web-effect-tools.d.ts +47 -0
  344. package/dist/web-effect-tools.js +126 -0
  345. package/dist/web-effect-tools.js.map +1 -0
  346. package/dist/web-effect-trials.d.ts +86 -0
  347. package/dist/web-effect-trials.js +204 -0
  348. package/dist/web-effect-trials.js.map +1 -0
  349. package/dist/web-effect-upload.d.ts +29 -0
  350. package/dist/web-effect-upload.js +190 -0
  351. package/dist/web-effect-upload.js.map +1 -0
  352. package/dist/web-effect-worker.d.ts +8 -0
  353. package/dist/web-effect-worker.js +170 -0
  354. package/dist/web-effect-worker.js.map +1 -0
  355. package/dist/web-effect.d.ts +89 -0
  356. package/dist/web-effect.js +132 -0
  357. package/dist/web-effect.js.map +1 -0
  358. package/dist/web-identity-color.d.ts +87 -0
  359. package/dist/web-identity-color.js +209 -0
  360. package/dist/web-identity-color.js.map +1 -0
  361. package/dist/web-identity-palette.d.ts +113 -0
  362. package/dist/web-identity-palette.js +84 -0
  363. package/dist/web-identity-palette.js.map +1 -0
  364. package/dist/web-image-meta.d.ts +25 -0
  365. package/dist/web-image-meta.js +200 -0
  366. package/dist/web-image-meta.js.map +1 -0
  367. package/dist/web-login.d.ts +14 -0
  368. package/dist/web-login.js +4 -2
  369. package/dist/web-login.js.map +1 -1
  370. package/dist/web-mod-agent.d.ts +50 -0
  371. package/dist/web-mod-agent.js +203 -0
  372. package/dist/web-mod-agent.js.map +1 -0
  373. package/dist/web-mod-bundle.d.ts +144 -0
  374. package/dist/web-mod-bundle.js +464 -0
  375. package/dist/web-mod-bundle.js.map +1 -0
  376. package/dist/web-mod-export.d.ts +59 -0
  377. package/dist/web-mod-export.js +79 -0
  378. package/dist/web-mod-export.js.map +1 -0
  379. package/dist/web-mod-store.d.ts +283 -0
  380. package/dist/web-mod-store.js +624 -0
  381. package/dist/web-mod-store.js.map +1 -0
  382. package/dist/web-org.d.ts +3 -3
  383. package/dist/web-org.js +11 -17
  384. package/dist/web-org.js.map +1 -1
  385. package/dist/web-owner-css-store.d.ts +180 -0
  386. package/dist/web-owner-css-store.js +384 -0
  387. package/dist/web-owner-css-store.js.map +1 -0
  388. package/dist/web-owner-css.d.ts +58 -0
  389. package/dist/web-owner-css.js +552 -0
  390. package/dist/web-owner-css.js.map +1 -0
  391. package/dist/web-prefs.d.ts +66 -0
  392. package/dist/web-prefs.js +243 -35
  393. package/dist/web-prefs.js.map +1 -1
  394. package/dist/web-preview.d.ts +9 -1
  395. package/dist/web-preview.js +24 -7
  396. package/dist/web-preview.js.map +1 -1
  397. package/dist/web-profile.d.ts +32 -0
  398. package/dist/web-profile.js +132 -0
  399. package/dist/web-profile.js.map +1 -0
  400. package/dist/web-session.d.ts +14 -0
  401. package/dist/web-session.js +10 -0
  402. package/dist/web-session.js.map +1 -1
  403. package/dist/web-settings-patch.d.ts +18 -0
  404. package/dist/web-settings-patch.js +51 -0
  405. package/dist/web-settings-patch.js.map +1 -1
  406. package/dist/web-shell-routes.d.ts +1 -1
  407. package/dist/web-shell-routes.js +1 -1
  408. package/dist/web-shell-routes.js.map +1 -1
  409. package/dist/web-theme-store.d.ts +218 -0
  410. package/dist/web-theme-store.js +516 -0
  411. package/dist/web-theme-store.js.map +1 -0
  412. package/dist/web-theme.d.ts +121 -0
  413. package/dist/web-theme.js +407 -0
  414. package/dist/web-theme.js.map +1 -0
  415. package/dist/web-upload.d.ts +119 -4
  416. package/dist/web-upload.js +252 -39
  417. package/dist/web-upload.js.map +1 -1
  418. package/dist/web-widget.d.ts +72 -0
  419. package/dist/web-widget.js +245 -0
  420. package/dist/web-widget.js.map +1 -0
  421. package/launcher/install-admission.cjs +61 -6
  422. package/package.json +3 -3
@@ -0,0 +1,496 @@
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";
5
+ import { createEffectGeometry, hostEffectRect } from "./effect-geometry.js";
6
+ import { effectStockSnapshot, effectStockAllows } from "./effect-stock-snapshot.js";
7
+ import { effectTrialStage, effectTrialStageIdentity, watchEffectTrialStage } from "./effect-trial-stage.js";
8
+ import { EffectBudget } from "./effect-profiler.js";
9
+
10
+ const registries = new WeakMap();
11
+ const plain = o => !!o && (Object.getPrototypeOf(o) === Object.prototype || Object.getPrototypeOf(o) === null);
12
+ const finite = v => typeof v === "number" && Number.isFinite(v);
13
+ const FLAGS = ["safe", "publicLink", "reducedMotion", "saveData", "hidden", "enabled", "show", "previewReady", "optIn", "constrained", "modal"];
14
+ const TYPES = ["state", "reply", "theme"];
15
+ function unsafeSnapshotDescriptor(d) {
16
+ if (!plain(d) || !plain(d.params ?? {})) return null;
17
+ const params = Object.create(null), keys = Object.keys(d.params ?? {}).sort();
18
+ if (keys.length > 16) return null;
19
+ for (const k of keys) {
20
+ const v = d.params[k];
21
+ if (!/^[a-z][a-z0-9_]{0,31}$/.test(k) || !(typeof v === "boolean" || finite(v) || typeof v === "string" && v.length <= 128)) return null;
22
+ params[k] = v;
23
+ }
24
+ const trialIdentity = d.trialStage == null ? null : effectTrialStageIdentity(d.trialStage);
25
+ if (d.trialStage != null && (!trialIdentity || d.region !== "chat" || trialIdentity.key !== d.key || trialIdentity.id !== d.id || trialIdentity.instance !== d.instance || trialIdentity.generation !== d.generation)) return null;
26
+ const out = { trialStage: d.trialStage ?? null, generation: trialIdentity?.generation ?? null, key: d.key, id: d.id, region: d.region, layer: d.layer, by: !trialIdentity && d.by === "human" ? "human" : "agent", order: d.order,
27
+ paused: d.paused, visible: d.visible, instance: d.region === "chat" ? d.instance : null, params, element: d.element };
28
+ const identity = [out.id, out.region, out.layer, out.by, out.instance, params];
29
+ if (trialIdentity) identity.push([trialIdentity.serial, trialIdentity.generation]);
30
+ out.identity = JSON.stringify(identity);
31
+ return out;
32
+ }
33
+
34
+ function snapshotDescriptor(d) { try { return unsafeSnapshotDescriptor(d); } catch { return null; } }
35
+
36
+ /**
37
+ * `load` is an authenticated, bounded host loader, never a Worker callback. It returns {id,code,params,assets,wants}.
38
+ * Safety is mandatory and synchronous: allowed(id,device), begin({key,id,device}), finish(token,reason), pause(record), resume(id,device). Pause persists a soft latch and history; human resume never clears sticky off.
39
+ * stockSnapshot({frameRoot}) supplies current closed stock paint evidence synchronously; it must not reenter a mutation API.
40
+ * Unknown receipts, roots, trust coverage or gates mount nothing. No storage, network, content or product imports here.
41
+ */
42
+ export function createEffectRegistry({ window: host, origin, boot, load, safety, stockSnapshot, onChange = () => {}, onProfile = () => {}, onMount = () => {}, onRetire = () => {}, clock = () => host.performance.now() } = {}) {
43
+ 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;
45
+ const slots = new Map(), loads = new Set(), paused = new Map(), offs = [], observed = new Set(), failures = new Map(), results = new Map();
46
+ let readingStock = false;
47
+ let root, geometry, observer, resize, longTasks = null, longTasksSupported = false;
48
+ const time = () => {
49
+ let n; try { n = clock(); } catch { return null; }
50
+ if (!finite(n) || n < 0 || last !== null && n < last) return null;
51
+ last = n; return n;
52
+ };
53
+ const nativeClock = () => { const n = time(); if (n === null) throw Error("clock unavailable"); return n; };
54
+ const device = () => context?.constrained === true ? "constrained" : "standard";
55
+ const dpr = device => Math.min(device === "constrained" ? 1.5 : 3, Math.max(0.5, host.devicePixelRatio || 1));
56
+ // The provider is synchronous and observational. Reentrant mutation revokes this pool, rather than
57
+ // allowing an old admission to act on newer descriptors or scheduling unbounded callback retries.
58
+ const readingStages = new Set();
59
+ // A local receipt may synchronously invalidate and notify its owner. Fence its result by revision;
60
+ // recursion on the same receipt is unknown, while an independently issued replacement may proceed.
61
+ const localStage = (d, proof) => {
62
+ if (!d?.trialStage || dead || blocked || readingStages.has(d.trialStage)) return null;
63
+ const before = revision; let stage = null; readingStages.add(d.trialStage);
64
+ try { stage = effectTrialStage(d.trialStage, d, proof); } catch { /* unknown is never permission */ }
65
+ finally { readingStages.delete(d.trialStage); }
66
+ return !dead && !blocked && revision === before && stage?.root === d.element ? stage : null;
67
+ };
68
+ const permits = (d, proof) => d.trialStage ? !!localStage(d, proof) : effectStockAllows(proof, d);
69
+ const coverage = (d = null) => {
70
+ if (dead || blocked || readingStock) return null;
71
+ const before = revision; let proof = null; readingStock = true;
72
+ try { proof = effectStockSnapshot(stockSnapshot(Object.freeze({ frameRoot: root }))); }
73
+ catch { /* unknown is never permission */ }
74
+ finally { readingStock = false; }
75
+ if (dead || blocked || before !== revision) return null;
76
+ return d === null || permits(d, proof) ? proof : null;
77
+ };
78
+ const position = (d, proof) => d.trialStage ? localStage(d, proof)?.rect ?? null : hostEffectRect(d.element);
79
+ const syncGeometry = (s, proof) => {
80
+ const before = revision, rect = position(s.d, proof);
81
+ if (!rect || !current(s) || before !== revision) return null;
82
+ const ok = s.d.trialStage ? s.geometry.sync(proof) : s.geometry.sync(rect, proof);
83
+ return ok && current(s) && before === revision ? rect : null;
84
+ };
85
+ const hideGeometry = () => { geometry?.hide(); for (const s of slots.values()) if (s.d.trialStage) s.geometry?.hide(); };
86
+ function reentrantStock() {
87
+ if (!readingStock) return false;
88
+ if (blocked) return true;
89
+ revision++; blocked = "stock_reentrant"; hideGeometry(); stopAll(blocked); tell(); return true;
90
+ }
91
+ const current = s => !dead && !blocked && slots.get(s.d.key) === s && s.live;
92
+ 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
+ const receipt = (fn, ...args) => { try { return Reflect.apply(fn, safety, args); } catch { return null; } };
94
+ const clearMarker = (s, why) => {
95
+ if (!s.token) return true;
96
+ const token = s.token; s.token = null;
97
+ if (receipt(safety.finish, token, why) === true) return true;
98
+ blocked = "safety_unknown"; return false;
99
+ };
100
+ function retire(s, why, count = false) {
101
+ if (!s.live) return;
102
+ s.live = false;
103
+ if (slots.get(s.d.key) === s) slots.delete(s.d.key);
104
+ 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 });
106
+ s.stageOff?.(); s.stageOff = null;
107
+ s.frame?.remove(why); s.geometry?.remove();
108
+ if (s.loadOff) s.loadOff();
109
+ if (count) {
110
+ paused.set(s.d.key, signature(s.d));
111
+ if (receipt(safety.pause, { id: s.d.id, device: s.device, reason: why, metrics: s.budget?.snapshot().metrics ?? null }) !== true) blocked = "safety_unknown";
112
+ }
113
+ clearMarker(s, why);
114
+ if (s.owner) { try { onRetire(s.owner, why); } catch { /* an observation cannot prevent physical cleanup */ } }
115
+ if (blocked) for (const other of [...slots.values()]) retire(other, blocked);
116
+ }
117
+ function stopAll(why) {
118
+ const proposed = revision, wasDead = dead;
119
+ for (const s of [...slots.values()]) {
120
+ retire(s, why);
121
+ if (revision !== proposed || dead !== wasDead) return false;
122
+ }
123
+ if (calibration) { calibration.cancel(); calibration = null; }
124
+ return true;
125
+ }
126
+ 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";
129
+ const signature = d => d.identity;
130
+ function view() {
131
+ 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) };
135
+ });
136
+ }
137
+ function schedule() {
138
+ if (dead || blocked || !slots.size) return;
139
+ if (raf === null && host.document.visibilityState === "visible" && context?.hidden === false) raf = host.requestAnimationFrame(tick);
140
+ if (timer === null) {
141
+ const waits = [...slots.values()].filter(s => s.hiddenSince !== null).map(s => Math.max(1, 10000 - (last - s.hiddenSince)));
142
+ timer = host.setTimeout(heartbeat, Math.min(1000, ...waits));
143
+ }
144
+ }
145
+ const invalidate = () => {
146
+ if (dead || reentrantStock()) return;
147
+ hideGeometry(); for (const s of slots.values()) s.pointer = null;
148
+ if (time() === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return; }
149
+ reconcile();
150
+ };
151
+ function observe() {
152
+ const nodes = host.document.querySelectorAll("[data-trust]");
153
+ if (nodes.length > 128) throw Error("trust coverage unavailable");
154
+ const wanted = new Set([host.document.documentElement, ...nodes]);
155
+ for (const d of descriptors) if (d.element?.ownerDocument === host.document && d.element.isConnected === true) wanted.add(d.element);
156
+ for (const n of observed) if (!wanted.has(n)) { resize.unobserve(n); observed.delete(n); }
157
+ for (const n of wanted) if (!observed.has(n)) { resize.observe(n); observed.add(n); }
158
+
159
+ }
160
+ function reconcile() {
161
+ if (dead || reentrantStock()) return;
162
+ const proposal = revision;
163
+ if (time() === null) blocked = "clock_unavailable";
164
+ const hidden = context?.hidden === true || host.document.visibilityState !== "visible";
165
+ const stock = eligible() ? coverage() : null;
166
+ if (blocked || !eligible() || !stock) {
167
+ if (!stopAll(blocked ?? "gates_unknown")) return;
168
+ results.clear(); tell(); return;
169
+ }
170
+ // Hidden frames already owned may survive for <=10 s, but no new frame or load is admitted while hidden.
171
+ const ctx = { ...context, hidden: false };
172
+ const ds = [];
173
+ for (const d of descriptors) {
174
+ if (dead || proposal !== revision) return;
175
+ const allowed = permits(d, stock);
176
+ if (dead || proposal !== revision) return;
177
+ ds.push({ ...d, paused: !allowed || d.paused === true || paused.get(d.key) === signature(d) || failures.get(d.key)?.identity === signature(d) });
178
+ }
179
+ const allocation = allocateEffectFrames(ds, ctx); results.clear();
180
+ for (const r of allocation) {
181
+ const d = descriptors.find(d => d.key === r.key);
182
+ if (r.reason === "paused" && d && d.paused !== true && paused.get(d.key) !== signature(d) && failures.get(d.key)?.identity !== signature(d)) {
183
+ const allowed = permits(d, stock);
184
+ if (dead || proposal !== revision) return;
185
+ if (!allowed) r.reason = d.trialStage ? "trial_ended" : stock.front ? "owner_css" : "stock_overlay";
186
+ }
187
+ results.set(r.key, r);
188
+ }
189
+ if (dead || proposal !== revision) return;
190
+ const chosen = new Set(allocation.filter(r => r.verdict === "active").map(r => r.key));
191
+ for (const s of [...slots.values()]) {
192
+ const d = descriptors.find(x => x.key === s.d.key);
193
+ if (!chosen.has(s.d.key) || !d || signature(d) !== signature(s.d) || d.element !== s.d.element || s.device !== device()) retire(s, "superseded");
194
+ if (dead || proposal !== revision) return;
195
+ }
196
+ if (chosen.size === 0) { if (calibration) { calibration.cancel(); calibration = null; } baseline = null; baselineDevice = null; tell(); return; }
197
+ if (hidden) {
198
+ hideGeometry();
199
+ if (calibration) { calibration.cancel(); calibration = null; }
200
+ for (const s of [...slots.values()]) {
201
+ if (!s.frame) retire(s, "hidden");
202
+ 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"); }
203
+ if (dead || proposal !== revision) return;
204
+ }
205
+ schedule(); tell(); return;
206
+ }
207
+ if (!globalAllowed()) { stopAll("gates_unknown"); tell(); return; }
208
+ for (const s of [...slots.values()]) {
209
+ 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
+ if (dead || proposal !== revision) return;
211
+ }
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
+ });
225
+ tell(); return;
226
+ }
227
+ const max = context.constrained ? 1 : 3;
228
+ for (const d of descriptors) {
229
+ if (!chosen.has(d.key) || slots.has(d.key)) continue;
230
+ if (slots.size >= max || loads.size >= max) break;
231
+ const permitted = receipt(safety.allowed, d.id, device());
232
+ 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;
235
+ const rect = position(d, coverage(d));
236
+ if (dead || proposal !== revision) return;
237
+ if (!rect || d.element.ownerDocument !== host.document) { results.set(d.key, { verdict: "off", reason: "geometry_unknown", queuePos: null }); continue; }
238
+ const s = { d, device: device(), owner: null, live: true, stageOff: null, ready: false, readyAt: null, frame: null, geometry: null, budget: null, token: null, request: null, loadOff: null, rect: null, pointer: null, lastPointer: null, hiddenSince: null, healthySince: null };
239
+ slots.set(d.key, s);
240
+ if (d.trialStage) {
241
+ s.stageOff = watchEffectTrialStage(d.trialStage, () => {
242
+ if (!current(s)) return;
243
+ const before = revision; retire(s, "trial_ended");
244
+ if (!dead && before === revision) { reconcile(); tell(); }
245
+ });
246
+ if (!s.stageOff || !current(s)) { retire(s, "trial_ended"); continue; }
247
+ }
248
+ const request = { controller: new host.AbortController(), slot: s, timer: null, began: time() }; s.request = request; loads.add(request);
249
+ const withinDeadline = () => {
250
+ const n = time();
251
+ if (n === null || request.began === null || n - request.began >= 10000) { retire(s, n === null || request.began === null ? "clock_unavailable" : "resource_timeout"); return false; }
252
+ return true;
253
+ };
254
+ const acceptCompletion = () => {
255
+ if (!current(s)) { clearMarker(s, "superseded"); return false; }
256
+ if (!withinDeadline()) return false;
257
+ if (request.controller.signal.aborted || !globalAllowed() || context.hidden || !coverage(d)) {
258
+ if (current(s)) retire(s, "admission_revoked"); else clearMarker(s, "superseded");
259
+ return false;
260
+ }
261
+ return withinDeadline();
262
+ };
263
+ const deadline = () => {
264
+ if (!current(s)) return;
265
+ const n = time();
266
+ if (n === null || n - request.began >= 10000) { retire(s, n === null ? "clock_unavailable" : "resource_timeout"); tell(); return; }
267
+ request.timer = host.setTimeout(deadline, Math.max(1, 10000 - (n - request.began)));
268
+ };
269
+ request.timer = host.setTimeout(deadline, 10000);
270
+ // Keep a physical read reservation until its promise settles, including after abort/revocation.
271
+ Promise.resolve().then(() => {
272
+ if (!current(s) || request.controller.signal.aborted) return null;
273
+ return load({ key: d.key, id: d.id, params: { ...d.params } }, request.controller.signal);
274
+ }).then(resource => {
275
+ if (!acceptCompletion()) return;
276
+ if (!plain(resource) || resource.id !== d.id || !plain(resource.params) || Object.keys(d.params).some(k => resource.params[k] !== d.params[k])) { retire(s, "resource_unknown"); return; }
277
+ let fresh = position(d, coverage(d));
278
+ if (!fresh || receipt(safety.allowed, d.id, s.device) !== true) { retire(s, "safety_unknown"); return; }
279
+ if (!acceptCompletion()) return;
280
+ const token = receipt(safety.begin, { key: d.key, id: d.id, device: s.device });
281
+ if (typeof token !== "string" || token.length < 1 || token.length > 128) { retire(s, "safety_unknown"); return; }
282
+ s.token = token;
283
+ if (!acceptCompletion()) return;
284
+ // Obtain local geometry BEFORE the factory appends its temporarily unknown iframe. bind() is
285
+ // the very next local-stage operation and registers that frame before checking its topology.
286
+ const local = d.trialStage ? localStage(d, coverage(d)) : null;
287
+ if (d.trialStage && !local || !current(s)) { if (current(s)) retire(s, "admission_revoked"); return; }
288
+ if (local) fresh = local.rect;
289
+ 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,
292
+ 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(); },
294
+ onRemove(why) { if (current(s)) { retire(s, why); tell(); } },
295
+ });
296
+ s.frame = frame;
297
+ if (!current(s) || !frame.ok || frame.state().removed) { frame.remove?.("stale_mount"); retire(s, "mount_failed"); return; }
298
+ frame.element.id = `agend-effect-${boot}-${++frameSerial}`;
299
+ s.geometry = local ? local.bind(frame.element, { layer: d.layer }, coverage()) : geometry.bind(frame.element, { layer: d.layer, by: d.by });
300
+ if (!s.geometry.ok) { retire(s, "geometry_unknown"); return; }
301
+ const loaded = () => { if (current(s)) s.budget.loaded(); };
302
+ frame.element.addEventListener("load", loaded); s.loadOff = () => frame.element.removeEventListener("load", loaded);
303
+ const paint = coverage(d);
304
+ if (!current(s) || !paint || !syncGeometry(s, paint)) { retire(s, "geometry_unknown"); return; }
305
+ // One non-serializable host receipt per actual admitted frame. Never append it to a Worker event.
306
+ const owner = Object.freeze({ key: d.key, id: d.id, device: s.device, current: () => current(s) && s.owner === owner });
307
+ s.owner = owner;
308
+ try { onMount(owner); } catch { /* diagnostics/display failure cannot authorize or mutate a mount */ }
309
+ schedule();
310
+ }).catch(() => { if (current(s)) retire(s, "resource_unavailable"); }).finally(() => {
311
+ if (request.timer !== null) host.clearTimeout(request.timer);
312
+ loads.delete(request); if (s.request === request) s.request = null;
313
+ if (!dead) reconcile(); else if (loads.size === 0 && registries.get(host) === api) registries.delete(host);
314
+ });
315
+ }
316
+ schedule(); tell();
317
+ }
318
+ function publishEnv(s, proof = coverage(s.d)) {
319
+ if (!s.ready || !current(s) || context.hidden || !globalAllowed() || !permits(s.d, proof)) return;
320
+ const e = effectEvent("env", { width: s.rect.width, height: s.rect.height, dpr: dpr(s.device), quality: s.budget.snapshot().quality }, null, time());
321
+ if (e) s.frame.send(e);
322
+ }
323
+ function tick() {
324
+ raf = null;
325
+ if (dead) return;
326
+ const paint = coverage();
327
+ if (!globalAllowed() || context.hidden || !paint) { invalidate(); return; }
328
+ const proposal = revision; let policyChanged = false;
329
+ for (const s of [...slots.values()]) {
330
+ if (dead || proposal !== revision) return;
331
+ if (!s.frame) continue;
332
+ if (!permits(s.d, paint)) { retire(s, "superseded"); policyChanged = true; continue; }
333
+ if (dead || proposal !== revision) return;
334
+ const rect = syncGeometry(s, paint);
335
+ if (!current(s)) continue;
336
+ if (!rect) { retire(s, "geometry_unknown"); continue; }
337
+ // The host owns the CSS viewport. A bounded env signal lets Worker code redraw its own bitmap if desired.
338
+ const changed = rect.width !== s.rect.width || rect.height !== s.rect.height; s.rect = rect;
339
+ if (changed) publishEnv(s, paint);
340
+ if (s.ready) s.budget.frame();
341
+ if (s.pointer) {
342
+ const packet = s.pointer; s.pointer = null;
343
+ 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)) {
345
+ const event = effectEvent("pointer", packet.fields, s.d.instance, packet.at);
346
+ if (event && s.frame.send(event)) s.lastPointer = n;
347
+ }
348
+ }
349
+ }
350
+ if (dead || proposal !== revision) return;
351
+ if (policyChanged) reconcile(); else schedule();
352
+ }
353
+ function heartbeat() {
354
+ timer = null;
355
+ if (dead) return;
356
+ if (time() === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return; }
357
+ const proposal = revision, activeBeforeSafety = slots.size;
358
+ if (!checkSafety()) { tell(); return; }
359
+ if (slots.size !== activeBeforeSafety) reconcile();
360
+ if (dead || proposal !== revision) return;
361
+ if (!globalAllowed() || context.hidden || !coverage()) { invalidate(); return; }
362
+ const budgetPauses = new Set();
363
+ for (const s of [...slots.values()]) {
364
+ if (dead || proposal !== revision) return;
365
+ if (!current(s) || !s.frame || !s.ready) continue;
366
+ if (s.d.trialStage && !coverage(s.d)) { if (dead || proposal !== revision) return; if (current(s)) retire(s, "trial_ended"); continue; }
367
+ if (dead || proposal !== revision) return;
368
+ const r = s.budget.gates({ hidden: false, reducedMotion: context.reducedMotion, saveData: context.saveData, show: context.show });
369
+ const state = r.remove ? r : s.readyAt !== null && last - s.readyAt >= 3000 ? s.budget.poll() : r;
370
+ try { onProfile({ key: s.d.key, id: s.d.id, device: s.device, ...state }, s.owner); } catch { /* metrics never authorize a mount */ }
371
+ if (dead || proposal !== revision) return;
372
+ if (!current(s)) continue;
373
+ if (s.d.trialStage && !coverage(s.d)) { if (dead || proposal !== revision) return; if (current(s)) retire(s, "trial_ended"); continue; }
374
+ if (dead || proposal !== revision) return;
375
+ if (state.remove) { const key = s.d.id + ":" + s.device; const count = state.reason === "host_budget" && !budgetPauses.has(key); if (count) budgetPauses.add(key); retire(s, state.reason, count); continue; }
376
+ if (state.badWindows > 0) s.healthySince = null; else if (s.healthySince === null) s.healthySince = last;
377
+ if (s.token && s.healthySince !== null && last - s.healthySince >= 5000 && state.badWindows === 0 && state.metrics !== null && !clearMarker(s, "healthy")) { stopAll("safety_unknown"); break; }
378
+ publishEnv(s);
379
+ }
380
+ if (dead || proposal !== revision) return;
381
+ schedule(); tell();
382
+ }
383
+ function checkSafety() {
384
+ if (dead || blocked) return false;
385
+ const proposed = revision;
386
+ for (const s of [...slots.values()]) {
387
+ if (!current(s)) continue;
388
+ const allowed = receipt(safety.allowed, s.d.id, s.device);
389
+ if (dead || proposed !== revision) return false;
390
+ if (current(s) && allowed !== true) retire(s, "safety_revoked");
391
+ if (dead || blocked || proposed !== revision) return false;
392
+ }
393
+ return !dead && !blocked;
394
+ }
395
+ function revalidateSafety() {
396
+ if (dead || reentrantStock()) return view();
397
+ if (checkSafety()) reconcile(); else tell();
398
+ return view();
399
+ }
400
+ function pointerGate(proof = coverage()) {
401
+ if (!globalAllowed() || context.hidden || context.modal || !proof || proof.front !== true) return false;
402
+ try {
403
+ 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'])");
405
+ } catch { return false; }
406
+ }
407
+ function pointer(event) {
408
+ if (dead || blocked || !pointerGate() || event.isTrusted !== true) return;
409
+ const kinds = { pointermove: "move", pointerdown: "down", pointerup: "up", click: "click", wheel: "wheel" }, kind = kinds[event.type];
410
+ 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
+ try { const target = host.document.elementFromPoint(event.clientX, event.clientY); if (!target || typeof target.closest !== "function" || target.closest("[data-trust]")) return; } catch { return; }
412
+ const at = time(); if (at === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return; }
413
+ for (const s of slots.values()) {
414
+ if (!s.ready || !s.frame || !s.budget.snapshot().forward || !coverage(s.d) || !current(s)) continue;
415
+ const r = hostEffectRect(s.d.element); if (!r) continue;
416
+ const fields = { kind, x: event.clientX - r.x, y: event.clientY - r.y, buttons: event.buttons };
417
+ if (fields.x < 0 || fields.y < 0 || fields.x > r.width || fields.y > r.height) continue;
418
+ if (kind === "wheel") { fields.dx = event.deltaX; fields.dy = event.deltaY; }
419
+ if (effectEvent("pointer", fields, s.d.instance, at)) s.pointer = { fields, at };
420
+ }
421
+ schedule();
422
+ }
423
+ try {
424
+ if (time() === null) throw Error("clock unavailable");
425
+ root = host.document.createElement("div"); root.className = "agend-effect-layer"; host.document.body.append(root);
426
+ geometry = createEffectGeometry({ window: host }); if (!geometry.ok) throw Error("geometry unsupported");
427
+ observer = new host.MutationObserver(invalidate); observer.observe(host.document.documentElement, { childList: true, subtree: true, attributes: true, characterData: true });
428
+ resize = new host.ResizeObserver(invalidate);
429
+ for (const type of ["scroll", "resize", "orientationchange", "focusin", "focusout"]) { host.addEventListener(type, invalidate, true); offs.push(() => host.removeEventListener(type, invalidate, true)); }
430
+ for (const type of ["pointermove", "pointerdown", "pointerup", "click", "wheel"]) { host.addEventListener(type, pointer, { passive: true }); offs.push(() => host.removeEventListener(type, pointer)); }
431
+ host.addEventListener("storage", revalidateSafety); offs.push(() => host.removeEventListener("storage", revalidateSafety));
432
+ const visibility = () => { revalidateSafety(); invalidate(); };
433
+ host.document.addEventListener("visibilitychange", visibility); offs.push(() => host.document.removeEventListener("visibilitychange", visibility));
434
+ if (typeof host.PerformanceObserver === "function" && host.PerformanceObserver.supportedEntryTypes?.includes("longtask")) {
435
+ try {
436
+ longTasks = new host.PerformanceObserver(list => {
437
+ for (const entry of list.getEntries()) for (const s of slots.values()) if (s.ready && s.budget) {
438
+ // No URL, container name, content or private Worker timing is read. Missing attribution stays unknown.
439
+ const attributed = Array.isArray(entry.attribution) && entry.attribution.some(a => a.containerId === s.frame.element.id && typeof a.containerId === "string" && a.containerId.length > 0);
440
+ s.budget.longTask(entry.startTime, entry.duration, attributed);
441
+ }
442
+ }); longTasks.observe({ type: "longtask", buffered: false }); longTasksSupported = true;
443
+ } catch { longTasks?.disconnect(); longTasks = null; }
444
+ }
445
+ } catch {
446
+ dead = true;
447
+ for (const release of [() => observer?.disconnect(), () => resize?.disconnect(), () => longTasks?.disconnect(), ...offs, () => geometry?.dispose(), () => root?.remove()]) { try { release(); } catch { /* continue releasing other host resources */ } }
448
+ return { ok: false, reason: "registry_unsupported" };
449
+ }
450
+ const api = { ok: true,
451
+ update(ds, ctx) {
452
+ if (dead || reentrantStock()) return view();
453
+ hideGeometry();
454
+ revision++;
455
+ if (!Array.isArray(ds) || ds.length > 128 || !plain(ctx)) { descriptors = []; context = null; results.clear(); stopAll("gates_unknown"); return []; }
456
+ descriptors = ds.map(snapshotDescriptor).filter(Boolean);
457
+ for (const [key, identity] of paused) if (!descriptors.some(d => d.key === key && signature(d) === identity)) paused.delete(key);
458
+ for (const [key, f] of failures) if (!descriptors.some(d => d.key === key && signature(d) === f.identity)) failures.delete(key);
459
+ context = Object.fromEntries([...FLAGS, "openInstance"].map(k => [k, ctx[k]]));
460
+ reconcile(); return view();
461
+ },
462
+ snapshot: view,
463
+ /** Host storage/foreground observation only, never called from a frame message. */
464
+ revalidateSafety,
465
+ /** Called only by the host's explicit human Resume UI. A descriptor update never clears this latch. */
466
+ resume(key) {
467
+ if (dead || reentrantStock() || blocked && !blocked.startsWith("baseline_") || typeof key !== "string") return false;
468
+ 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
+ revision++;
470
+ 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;
472
+ },
473
+ /** Host-only content-free signal API. Raw pointer publishing is refused; native pointer metadata is coalesced here. */
474
+ publish(type, fields, instance = null) {
475
+ if (dead || blocked || !globalAllowed() || context.hidden || !coverage() || !TYPES.includes(type)) return 0;
476
+ const ts = time(); if (ts === null) { blocked = "clock_unavailable"; stopAll(blocked); tell(); return 0; }
477
+ let sent = 0; const proposal = revision;
478
+ for (const s of [...slots.values()]) {
479
+ if (dead || proposal !== revision) return sent;
480
+ if (!s.ready || !s.frame || !s.budget.snapshot().forward || !coverage(s.d) || !current(s)) continue;
481
+ if (["state", "reply", "mention"].includes(type) && (s.d.instance !== null ? instance !== s.d.instance : instance !== context.openInstance)) continue;
482
+ const e = effectEvent(type, fields, s.d.instance, ts); if (e && s.frame.send(e)) sent++;
483
+ }
484
+ return sent;
485
+ },
486
+ dispose() {
487
+ if (dead) return; dead = true; revision++; serial++; stopAll("disposed");
488
+ if (raf !== null) host.cancelAnimationFrame(raf); if (timer !== null) host.clearTimeout(timer);
489
+ observer.disconnect(); resize.disconnect(); longTasks?.disconnect(); for (const off of offs) off();
490
+ geometry.dispose(); root.remove(); observed.clear(); if (loads.size === 0 && registries.get(host) === api) registries.delete(host);
491
+ },
492
+ };
493
+ // Read-only host geometry seam; no selector guessing among poster and live roots.
494
+ Object.defineProperty(api, "frameRoot", { get: () => dead ? null : root });
495
+ registries.set(host, api); return api;
496
+ }