@songsid/agend 2.3.3-beta.3 → 2.3.3-beta.5

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 (238) hide show
  1. package/dist/agent-cli.js +13 -112
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.d.ts +0 -1
  4. package/dist/agent-endpoint.js +1 -9
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/agent-whats-new.d.ts +3 -6
  7. package/dist/agent-whats-new.js +7 -18
  8. package/dist/agent-whats-new.js.map +1 -1
  9. package/dist/auth-api.d.ts +0 -7
  10. package/dist/auth-api.js +1 -15
  11. package/dist/auth-api.js.map +1 -1
  12. package/dist/backend/codex.d.ts +1 -0
  13. package/dist/backend/codex.js +106 -15
  14. package/dist/backend/codex.js.map +1 -1
  15. package/dist/backend/types.d.ts +17 -0
  16. package/dist/backend/types.js.map +1 -1
  17. package/dist/bundled-skills.d.ts +4 -6
  18. package/dist/bundled-skills.js +10 -14
  19. package/dist/bundled-skills.js.map +1 -1
  20. package/dist/channel/mcp-server.js +5 -5
  21. package/dist/channel/mcp-server.js.map +1 -1
  22. package/dist/channel/mcp-tools.d.ts +1 -3
  23. package/dist/channel/mcp-tools.js +4 -58
  24. package/dist/channel/mcp-tools.js.map +1 -1
  25. package/dist/cli.js +3 -1
  26. package/dist/cli.js.map +1 -1
  27. package/dist/config-validator.js +6 -3
  28. package/dist/config-validator.js.map +1 -1
  29. package/dist/daemon.d.ts +11 -3
  30. package/dist/daemon.js +59 -107
  31. package/dist/daemon.js.map +1 -1
  32. package/dist/dashboard-live.d.ts +1 -1
  33. package/dist/dashboard-live.js +3 -3
  34. package/dist/dashboard-live.js.map +1 -1
  35. package/dist/dashboard-permissions.d.ts +1 -1
  36. package/dist/dashboard-permissions.js +1 -3
  37. package/dist/dashboard-permissions.js.map +1 -1
  38. package/dist/fleet-manager.d.ts +26 -44
  39. package/dist/fleet-manager.js +70 -226
  40. package/dist/fleet-manager.js.map +1 -1
  41. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +11 -1
  42. package/dist/general-knowledge/skills/session-management/SKILL.md +6 -0
  43. package/dist/general-knowledge/skills/web-appearance/SKILL.md +13 -204
  44. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +26 -63
  45. package/dist/instance-lifecycle.js +1 -1
  46. package/dist/instance-lifecycle.js.map +1 -1
  47. package/dist/locale.js +2 -0
  48. package/dist/locale.js.map +1 -1
  49. package/dist/outbound-handlers.js +19 -7
  50. package/dist/outbound-handlers.js.map +1 -1
  51. package/dist/outbound-schemas.d.ts +0 -62
  52. package/dist/outbound-schemas.js +2 -41
  53. package/dist/outbound-schemas.js.map +1 -1
  54. package/dist/public-web-gateway.js +0 -9
  55. package/dist/public-web-gateway.js.map +1 -1
  56. package/dist/public-web-link.d.ts +2 -0
  57. package/dist/public-web-link.js +5 -0
  58. package/dist/public-web-link.js.map +1 -1
  59. package/dist/search/search-docs.d.ts +1 -1
  60. package/dist/search/search-docs.js +2 -2
  61. package/dist/search/search-docs.js.map +1 -1
  62. package/dist/settings-api.d.ts +6 -0
  63. package/dist/settings-api.js +14 -2
  64. package/dist/settings-api.js.map +1 -1
  65. package/dist/settings-change.js +3 -1
  66. package/dist/settings-change.js.map +1 -1
  67. package/dist/task-board-waker.d.ts +4 -1
  68. package/dist/task-board-waker.js +13 -2
  69. package/dist/task-board-waker.js.map +1 -1
  70. package/dist/tool-permissions.js +3 -10
  71. package/dist/tool-permissions.js.map +1 -1
  72. package/dist/topic-commands.d.ts +6 -2
  73. package/dist/topic-commands.js +2 -0
  74. package/dist/topic-commands.js.map +1 -1
  75. package/dist/types.d.ts +2 -1
  76. package/dist/ui/chat-ambient.js +4 -4
  77. package/dist/ui/chat-commands.js +3 -2
  78. package/dist/ui/chat-store.js +4 -1
  79. package/dist/ui/chat-strings.js +6 -42
  80. package/dist/ui/chat-thread.js +6 -44
  81. package/dist/ui/panel-chat.js +21 -50
  82. package/dist/ui/preview-panel.js +3 -15
  83. package/dist/ui/search-dialog.js +30 -14
  84. package/dist/ui/settings-appearance.js +1 -3
  85. package/dist/ui/settings-guests.js +13 -4
  86. package/dist/ui/settings-learn.js +1 -1
  87. package/dist/ui/settings-mods.js +5 -12
  88. package/dist/ui/settings-registry.js +0 -1
  89. package/dist/ui/settings-strings.js +6 -6
  90. package/dist/ui/shared/app-prefs.js +17 -17
  91. package/dist/ui/shared/app-shell.js +12 -8
  92. package/dist/ui/shared/app-store.js +6 -2
  93. package/dist/ui/shared/app.css +4 -36
  94. package/dist/ui/shared/app.js +5 -4
  95. package/dist/ui/shared/instance-nav.js +9 -9
  96. package/dist/ui/shared/instance-switch.js +8 -2
  97. package/dist/ui/shared/panel-view.js +22 -8
  98. package/dist/ui/shared/view-strings.js +6 -0
  99. package/dist/usage/usage-api.d.ts +3 -0
  100. package/dist/usage/usage-api.js +5 -1
  101. package/dist/usage/usage-api.js.map +1 -1
  102. package/dist/view-api.d.ts +2 -0
  103. package/dist/view-api.js +64 -0
  104. package/dist/view-api.js.map +1 -1
  105. package/dist/web-api.d.ts +9 -17
  106. package/dist/web-api.js +255 -371
  107. package/dist/web-api.js.map +1 -1
  108. package/dist/web-appearance-tools.d.ts +0 -2
  109. package/dist/web-appearance-tools.js +2 -5
  110. package/dist/web-appearance-tools.js.map +1 -1
  111. package/dist/web-appearance-view.d.ts +0 -6
  112. package/dist/web-appearance-view.js +0 -1
  113. package/dist/web-appearance-view.js.map +1 -1
  114. package/dist/web-appearance.d.ts +14 -7
  115. package/dist/web-appearance.js +5 -6
  116. package/dist/web-appearance.js.map +1 -1
  117. package/dist/web-asset-parts.d.ts +1 -17
  118. package/dist/web-asset-parts.js +1 -15
  119. package/dist/web-asset-parts.js.map +1 -1
  120. package/dist/web-auth.js +1 -7
  121. package/dist/web-auth.js.map +1 -1
  122. package/dist/web-chat-history.d.ts +3 -7
  123. package/dist/web-chat-history.js +10 -17
  124. package/dist/web-chat-history.js.map +1 -1
  125. package/dist/web-commands.d.ts +9 -1
  126. package/dist/web-commands.js +13 -0
  127. package/dist/web-commands.js.map +1 -1
  128. package/dist/web-experimental.d.ts +2 -11
  129. package/dist/web-experimental.js +8 -36
  130. package/dist/web-experimental.js.map +1 -1
  131. package/dist/web-guest-access.d.ts +40 -2
  132. package/dist/web-guest-access.js +50 -14
  133. package/dist/web-guest-access.js.map +1 -1
  134. package/dist/web-mod-bundle.d.ts +2 -45
  135. package/dist/web-mod-bundle.js +15 -63
  136. package/dist/web-mod-bundle.js.map +1 -1
  137. package/dist/web-mod-store.d.ts +3 -48
  138. package/dist/web-mod-store.js +30 -97
  139. package/dist/web-mod-store.js.map +1 -1
  140. package/dist/web-prefs.d.ts +13 -0
  141. package/dist/web-prefs.js +38 -0
  142. package/dist/web-prefs.js.map +1 -1
  143. package/dist/web-preview.d.ts +1 -11
  144. package/dist/web-preview.js +6 -20
  145. package/dist/web-preview.js.map +1 -1
  146. package/dist/web-session.d.ts +11 -3
  147. package/dist/web-session.js +22 -11
  148. package/dist/web-session.js.map +1 -1
  149. package/dist/web-settings-patch.d.ts +1 -0
  150. package/dist/web-settings-patch.js +6 -3
  151. package/dist/web-settings-patch.js.map +1 -1
  152. package/dist/web-settings-tools.d.ts +0 -13
  153. package/dist/web-settings-tools.js +12 -84
  154. package/dist/web-settings-tools.js.map +1 -1
  155. package/launcher/fleet-service-owner.cjs +4 -1
  156. package/package.json +1 -1
  157. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +0 -376
  158. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +0 -672
  159. package/dist/ui/app-effects.js +0 -251
  160. package/dist/ui/chat-effect-posters.js +0 -42
  161. package/dist/ui/chat-effects.js +0 -167
  162. package/dist/ui/effect-trial-controller.js +0 -124
  163. package/dist/ui/effect-trial-offer.js +0 -2
  164. package/dist/ui/effect-trial-panel.js +0 -40
  165. package/dist/ui/effects-waker.js +0 -2
  166. package/dist/ui/settings-effects.js +0 -202
  167. package/dist/ui/settings-owner-effects.js +0 -82
  168. package/dist/ui/shared/effect-app-runtime.js +0 -299
  169. package/dist/ui/shared/effect-baseline.js +0 -64
  170. package/dist/ui/shared/effect-chat-runtime.js +0 -162
  171. package/dist/ui/shared/effect-cursor.js +0 -126
  172. package/dist/ui/shared/effect-display-metadata.js +0 -39
  173. package/dist/ui/shared/effect-frame.js +0 -146
  174. package/dist/ui/shared/effect-geometry.js +0 -137
  175. package/dist/ui/shared/effect-host-style.js +0 -10
  176. package/dist/ui/shared/effect-owner-regions.js +0 -74
  177. package/dist/ui/shared/effect-owner-source.js +0 -122
  178. package/dist/ui/shared/effect-policy.js +0 -151
  179. package/dist/ui/shared/effect-poster-reader.js +0 -98
  180. package/dist/ui/shared/effect-posters.js +0 -117
  181. package/dist/ui/shared/effect-profiler.js +0 -184
  182. package/dist/ui/shared/effect-profiles.js +0 -65
  183. package/dist/ui/shared/effect-registry.js +0 -545
  184. package/dist/ui/shared/effect-runtime-loader.js +0 -236
  185. package/dist/ui/shared/effect-safety.js +0 -153
  186. package/dist/ui/shared/effect-stock-snapshot.js +0 -43
  187. package/dist/ui/shared/effect-stock-trust.js +0 -217
  188. package/dist/ui/shared/effect-trial-shadow.js +0 -62
  189. package/dist/ui/shared/effect-trial-source.js +0 -103
  190. package/dist/ui/shared/effect-trial-stage.js +0 -170
  191. package/dist/ui/shared/effect-trial.css +0 -40
  192. package/dist/ui/shared/feature-flags.js +0 -127
  193. package/dist/ui/shared/owner-effect-prefs.js +0 -117
  194. package/dist/web-effect-bootstrap.d.ts +0 -12
  195. package/dist/web-effect-bootstrap.js +0 -49
  196. package/dist/web-effect-bootstrap.js.map +0 -1
  197. package/dist/web-effect-bundle.d.ts +0 -28
  198. package/dist/web-effect-bundle.js +0 -171
  199. package/dist/web-effect-bundle.js.map +0 -1
  200. package/dist/web-effect-canonical.d.ts +0 -8
  201. package/dist/web-effect-canonical.js +0 -27
  202. package/dist/web-effect-canonical.js.map +0 -1
  203. package/dist/web-effect-library.d.ts +0 -114
  204. package/dist/web-effect-library.js +0 -530
  205. package/dist/web-effect-library.js.map +0 -1
  206. package/dist/web-effect-playback.d.ts +0 -30
  207. package/dist/web-effect-playback.js +0 -71
  208. package/dist/web-effect-playback.js.map +0 -1
  209. package/dist/web-effect-poster.d.ts +0 -8
  210. package/dist/web-effect-poster.js +0 -52
  211. package/dist/web-effect-poster.js.map +0 -1
  212. package/dist/web-effect-profiles.d.ts +0 -118
  213. package/dist/web-effect-profiles.js +0 -181
  214. package/dist/web-effect-profiles.js.map +0 -1
  215. package/dist/web-effect-service.d.ts +0 -93
  216. package/dist/web-effect-service.js +0 -335
  217. package/dist/web-effect-service.js.map +0 -1
  218. package/dist/web-effect-tools.d.ts +0 -48
  219. package/dist/web-effect-tools.js +0 -129
  220. package/dist/web-effect-tools.js.map +0 -1
  221. package/dist/web-effect-trials.d.ts +0 -86
  222. package/dist/web-effect-trials.js +0 -204
  223. package/dist/web-effect-trials.js.map +0 -1
  224. package/dist/web-effect-upload.d.ts +0 -27
  225. package/dist/web-effect-upload.js +0 -185
  226. package/dist/web-effect-upload.js.map +0 -1
  227. package/dist/web-effect-worker.d.ts +0 -8
  228. package/dist/web-effect-worker.js +0 -170
  229. package/dist/web-effect-worker.js.map +0 -1
  230. package/dist/web-effect.d.ts +0 -103
  231. package/dist/web-effect.js +0 -153
  232. package/dist/web-effect.js.map +0 -1
  233. package/dist/web-experimental-http.d.ts +0 -2
  234. package/dist/web-experimental-http.js +0 -12
  235. package/dist/web-experimental-http.js.map +0 -1
  236. package/dist/web-mod-agent.d.ts +0 -50
  237. package/dist/web-mod-agent.js +0 -207
  238. package/dist/web-mod-agent.js.map +0 -1
@@ -1,184 +0,0 @@
1
- /** #1629: host observations only. This pure module mounts nothing and accepts no frame/Worker messages. */
2
- const finite = v => typeof v === "number" && Number.isFinite(v);
3
- const plain = v => !!v && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
4
- const DAY = 86400000;
5
- export const EFFECT_BUDGETS = Object.freeze({
6
- standard: Object.freeze({ fps: 55, longTaskMsPerSecond: 50 }),
7
- constrained: Object.freeze({ fps: 50, longTaskMsPerSecond: 25 }),
8
- });
9
- const deviceClass = v => v === "standard" || v === "constrained";
10
- const id = v => typeof v === "string" && /^[0-9a-f]{32}$/.test(v);
11
-
12
- /** One host-owned mount. The registry must remove the frame when remove is true, never merely hide it. */
13
- export class EffectBudget {
14
- constructor({ device, baselineFps, clock = () => performance.now(), longTasksSupported = false } = {}) {
15
- if (!deviceClass(device) || baselineFps !== undefined && (!finite(baselineFps) || baselineFps <= 0 || baselineFps > 1000) || typeof clock !== "function" || typeof longTasksSupported !== "boolean") throw new TypeError("effect budget options");
16
- this.device = device;
17
- // Legacy host measurements may still be supplied by pure probes. Product playback starts
18
- // immediately and learns cadence concurrently from host rAF, never Worker timing.
19
- // Freeze before the first report: a mount's profile floor must remain coherent.
20
- this.fpsFloor = baselineFps === undefined ? null : Math.min(EFFECT_BUDGETS[device].fps, baselineFps * 0.9);
21
- this.cadence = [];
22
- this.cadenceAt = null;
23
- this.clock = clock;
24
- this.longTasksSupported = longTasksSupported;
25
- this.last = null;
26
- this.epoch = this.time();
27
- this.lastWindow = null;
28
- this.frames = [];
29
- this.tasks = [];
30
- this.overflow = false;
31
- this.hiddenAt = null;
32
- this.verdict = "ok";
33
- this.reason = null;
34
- this.badWindows = 0;
35
- this.metrics = null;
36
- this.loadMs = null;
37
- if (this.epoch === null) this.stop("clock_unavailable");
38
- }
39
- time() {
40
- let now;
41
- try { now = this.clock(); } catch { return null; }
42
- if (!finite(now) || now < 0 || this.last !== null && now < this.last) return null;
43
- this.last = now;
44
- return now;
45
- }
46
- stop(reason = "stopped") {
47
- if (this.verdict === "paused") return this.snapshot();
48
- this.verdict = "paused";
49
- this.reason = reason;
50
- this.frames = [];
51
- this.tasks = [];
52
- return this.snapshot();
53
- }
54
- /** Invoke on host-observed load, not on child-provided timing. First load only. */
55
- loaded() {
56
- const now = this.time();
57
- if (now === null) return this.stop("clock_unavailable");
58
- if (this.verdict !== "paused" && this.loadMs === null) this.loadMs = now - this.epoch;
59
- return this.snapshot();
60
- }
61
- /** Invoke from the host's rAF callback; elapsed time always comes from the host clock. */
62
- frame() {
63
- const now = this.time();
64
- if (now === null) return this.stop("clock_unavailable");
65
- if (this.verdict === "paused" || this.hiddenAt !== null) return this.snapshot();
66
- if (this.fpsFloor === null) {
67
- const interval = this.cadenceAt === null ? null : now - this.cadenceAt;
68
- this.cadenceAt = now;
69
- if (interval !== null && interval >= 1 && interval <= 1000) {
70
- if (this.cadence.length === 256) this.cadence.shift();
71
- this.cadence.push(interval);
72
- }
73
- }
74
- if (now < this.epoch + 1000) return this.snapshot();
75
- this.trim(now);
76
- if (this.frames.at(-1) !== now) {
77
- if (this.frames.length >= 2048) this.overflow = true;
78
- else this.frames.push(now);
79
- }
80
- return this.snapshot();
81
- }
82
- /** Only the host observer calls this. `attributed` means it matched THIS host-owned container. */
83
- longTask(start, duration, attributed = false) {
84
- const now = this.time();
85
- if (now === null) return this.stop("clock_unavailable");
86
- if (this.verdict === "paused" || this.hiddenAt !== null || !this.longTasksSupported) return false;
87
- if (!finite(start) || start < 0 || !finite(duration) || duration < 50 || duration > 60000 || start + duration > now || typeof attributed !== "boolean") return false;
88
- const from = Math.max(start, this.epoch + 1000, now - 2000), to = start + duration;
89
- if (from >= to) return false;
90
- this.trim(now);
91
- if (this.tasks.length >= 256) { this.overflow = true; return false; }
92
- this.tasks.push({ from, to, attributed });
93
- return true;
94
- }
95
- trim(now) {
96
- this.frames = this.frames.filter(t => t > now - 2000);
97
- this.tasks = this.tasks.filter(t => t.to > now - 2000);
98
- }
99
- /** Strict booleans: unknown page gates fail closed. No setting can revive a paused mount. */
100
- gates(raw = {}) {
101
- const now = this.time();
102
- if (now === null) return this.stop("clock_unavailable");
103
- if (!plain(raw)) return this.stop("gates_unknown");
104
- const { hidden, reducedMotion, show } = raw;
105
- if ([hidden, reducedMotion, show].some(v => typeof v !== "boolean")) return this.stop("gates_unknown");
106
- if (this.verdict === "paused") return this.snapshot();
107
- if (reducedMotion || !show) return this.stop(reducedMotion ? "reduced_motion" : "viewer_off");
108
- if (hidden && this.hiddenAt === null) this.hiddenAt = now;
109
- if (hidden && now - this.hiddenAt >= 10000) return this.stop("hidden");
110
- if (!hidden && this.hiddenAt !== null) {
111
- // A delayed visibility callback must not revive a frame whose removal deadline already passed.
112
- if (now - this.hiddenAt >= 10000) return this.stop("hidden");
113
- this.hiddenAt = null;
114
- this.epoch = now;
115
- this.lastWindow = null;
116
- this.frames = [];
117
- this.tasks = [];
118
- this.overflow = false;
119
- this.badWindows = 0;
120
- this.metrics = null;
121
- this.cadence = []; this.cadenceAt = null;
122
- }
123
- return this.snapshot();
124
- }
125
- /** Poll no faster than once per second; a delayed callback never fabricates missed windows. */
126
- poll() {
127
- const now = this.time();
128
- if (now === null) return this.stop("clock_unavailable");
129
- if (this.verdict === "paused") return this.snapshot();
130
- if (this.hiddenAt !== null) return now - this.hiddenAt >= 10000 ? this.stop("hidden") : this.snapshot();
131
- if (now < this.epoch + 3000 || this.lastWindow !== null && now - this.lastWindow < 1000) return this.snapshot();
132
- this.lastWindow = now;
133
- this.trim(now);
134
- const sum = attributed => this.tasks.filter(t => attributed === null || t.attributed === attributed)
135
- .reduce((n, t) => n + Math.max(0, t.to - Math.max(t.from, now - 2000)), 0) / 2;
136
- const fps = this.frames.length / 2;
137
- const hostMs = this.longTasksSupported ? sum(null) : null;
138
- const ownMs = this.longTasksSupported && this.tasks.some(t => t.attributed) ? sum(true) : null;
139
- const limits = EFFECT_BUDGETS[this.device];
140
- if (this.fpsFloor === null && this.cadence.length >= 3) {
141
- // Faster observed intervals distinguish intermittent jank from a genuinely slower
142
- // display. This is a live reference, not an empty-page health certification.
143
- const sorted = [...this.cadence].sort((a, b) => a - b);
144
- const interval = sorted[Math.ceil(sorted.length * (sorted.length < 8 ? 0.5 : 0.2)) - 1];
145
- this.fpsFloor = Math.min(limits.fps, 900 / interval);
146
- this.cadence = []; this.cadenceAt = null;
147
- }
148
- const bad = this.overflow || this.fpsFloor === null || fps < this.fpsFloor || hostMs !== null && hostMs > limits.longTaskMsPerSecond;
149
- this.metrics = this.fpsFloor === null ? null : { at: now, hostFps: fps, hostLongTaskMsPerSecond: hostMs, attributedLongTaskMsPerSecond: ownMs,
150
- attribution: ownMs === null ? "unknown" : "observed", fpsFloor: this.fpsFloor, windowMs: 2000 };
151
- this.overflow = false;
152
- if (bad) {
153
- this.badWindows++;
154
- this.verdict = "degraded";
155
- this.reason = "host_budget";
156
- if (this.badWindows >= 3) return this.stop("host_budget");
157
- } else this.badWindows = 0;
158
- return this.snapshot();
159
- }
160
- snapshot() {
161
- const remove = this.verdict === "paused";
162
- return { verdict: this.verdict, reason: this.reason, remove, forward: !remove && this.hiddenAt === null,
163
- quality: this.verdict === "ok" ? "normal" : "low", pointerHz: this.verdict === "ok" ? 60 : 30,
164
- badWindows: this.badWindows, loadMs: this.loadMs, metrics: this.metrics ? { ...this.metrics } : null };
165
- }
166
- }
167
-
168
- /** Persisted calendar policy is separate from monotonic measurement. No storage is opened by this module. */
169
- export function newEffectPauseHistory(effectId, device, wall) {
170
- if (!id(effectId) || !deviceClass(device) || !finite(wall) || wall < 0) return null;
171
- return { v: 1, id: effectId, device, pauses: [], off: false, lastWall: wall };
172
- }
173
- export function recordEffectPause(history, wall) {
174
- if (!plain(history) || Object.keys(history).sort().join(",") !== "device,id,lastWall,off,pauses,v" || history.v !== 1 || !id(history.id) || !deviceClass(history.device) || typeof history.off !== "boolean" || !finite(history.lastWall) || history.lastWall < 0 || !finite(wall) || wall < 0 || !Array.isArray(history.pauses) || history.pauses.length > 3) return { ok: false, error: "pause_history_unknown" };
175
- let previous = -1;
176
- for (const at of history.pauses) {
177
- if (!finite(at) || at < 0 || at < previous || at > history.lastWall) return { ok: false, error: "pause_history_unknown" };
178
- previous = at;
179
- }
180
- // A wall-clock rollback cannot age out pauses. A forward jump can; off remains sticky for this content id.
181
- const now = Math.max(wall, history.lastWall);
182
- const pauses = [...history.pauses.filter(t => t > now - DAY), now].slice(-3);
183
- return { ok: true, history: { ...history, pauses, lastWall: now, off: history.off || pauses.length >= 3 } };
184
- }
@@ -1,65 +0,0 @@
1
- /** #1629: bounded host-window quantiles; not a frame message handler or admission verdict. */
2
- const plain = o => !!o && (Object.getPrototypeOf(o) === Object.prototype || Object.getPrototypeOf(o) === null);
3
- const finite = x => typeof x === "number" && Number.isFinite(x);
4
- const bounded = (x, max) => finite(x) && x >= 0 && x <= max;
5
- const reasons = [null, "host_budget", "clock_unavailable", "gates_unknown", "hidden", "reduced_motion", "save_data", "viewer_off"];
6
- const keys = ["key", "id", "device", "verdict", "reason", "remove", "forward", "quality", "pointerHz", "badWindows", "loadMs", "metrics"];
7
- const metricKeys = ["at", "hostFps", "hostLongTaskMsPerSecond", "attributedLongTaskMsPerSecond", "attribution", "fpsFloor", "windowMs"];
8
- const exact = (o, allowed) => plain(o) && Object.keys(o).sort().join(",") === [...allowed].sort().join(",");
9
- const quantile = (values, q) => { if (!values.length) return null; const a = [...values].sort((x, y) => x - y); return a[Math.max(0, Math.ceil(q * a.length) - 1)]; };
10
-
11
- /** A new collector belongs to one exact registry key/id/class mount. It never reads message content or child data. */
12
- export function createEffectProfileCollector(options = {}) {
13
- let key, id, device, clock;
14
- try { if (!exact(options, ["key", "id", "device", "clock"])) return { ok: false }; ({ key, id, device, clock } = options); } catch { return { ok: false }; }
15
- if (typeof key !== "string" || key.length < 1 || key.length > 256 || typeof id !== "string" || !/^[a-f0-9]{32}$/.test(id) || !["standard", "constrained"].includes(device) || typeof clock !== "function") return { ok: false };
16
- let dead = false, previous = null, lastSample = null, floor = null, last = null;
17
- const samples = [];
18
- const time = () => {
19
- let n; try { n = clock(); } catch { dead = true; return null; }
20
- if (!finite(n) || n < 0 || previous !== null && n < previous) { dead = true; return null; }
21
- previous = n; return n;
22
- };
23
- if (time() === null) return { ok: false };
24
- return { ok: true,
25
- add(raw) {
26
- if (dead) return false;
27
- try {
28
- // Reject unknown keys without reading their values or accessors.
29
- if (!exact(raw, keys) || raw.key !== key || raw.id !== id || raw.device !== device || !["ok", "degraded", "paused"].includes(raw.verdict) || !reasons.includes(raw.reason)
30
- || typeof raw.remove !== "boolean" || typeof raw.forward !== "boolean" || raw.remove !== (raw.verdict === "paused") || raw.remove && raw.forward
31
- || raw.quality !== (raw.verdict === "ok" ? "normal" : "low") || raw.pointerHz !== (raw.verdict === "ok" ? 60 : 30)
32
- || !Number.isInteger(raw.badWindows) || raw.badWindows < 0 || raw.badWindows > 3 || raw.loadMs !== null && !bounded(raw.loadMs, 600000)) { dead = true; return false; }
33
- const n = time(); if (n === null) return false;
34
- // Warmup is not an observed window and never turns missing measurements into zero cost.
35
- if (raw.metrics === null) return false;
36
- const m = raw.metrics;
37
- if (!exact(m, metricKeys) || !finite(m.at) || m.at < 0 || m.at > n || !bounded(m.hostFps, 1000) || !finite(m.fpsFloor) || m.fpsFloor <= 0 || m.fpsFloor > 1000 || m.windowMs !== 2000
38
- || m.hostLongTaskMsPerSecond !== null && !bounded(m.hostLongTaskMsPerSecond, 256000)
39
- || m.attributedLongTaskMsPerSecond !== null && (!bounded(m.attributedLongTaskMsPerSecond, 256000) || m.hostLongTaskMsPerSecond === null || m.attributedLongTaskMsPerSecond > m.hostLongTaskMsPerSecond)
40
- || m.attribution !== (m.attributedLongTaskMsPerSecond === null ? "unknown" : "observed") || floor !== null && m.fpsFloor !== floor) { dead = true; return false; }
41
- // Only poll() creates a window. rAF/gates/load/stop snapshots retain its immutable host timestamp.
42
- // A paused duplicate may describe the last verdict, but cannot renew the observation or add a sample.
43
- if (lastSample !== null && m.at === lastSample && last) last = { verdict: raw.verdict, reason: raw.reason, loadMs: raw.loadMs };
44
- if (n - m.at > 5000 || lastSample !== null && m.at - lastSample < 1000) return false;
45
- floor = m.fpsFloor; lastSample = m.at;
46
- samples.push({ fps: m.hostFps, task: m.hostLongTaskMsPerSecond, own: m.attributedLongTaskMsPerSecond });
47
- if (samples.length > 120) samples.shift();
48
- last = { verdict: raw.verdict, reason: raw.reason, loadMs: raw.loadMs };
49
- return true;
50
- } catch { dead = true; return false; }
51
- },
52
- report(options = {}) {
53
- let off;
54
- try { if (!plain(options) || Object.keys(options).some(k => k !== "off")) return null; off = options.off ?? false; } catch { dead = true; return null; }
55
- if (dead || typeof off !== "boolean" || lastSample === null || !last) return null;
56
- const n = time(); if (n === null || n - lastSample > 5000) return null;
57
- const host = samples.filter(s => s.task !== null).map(s => s.task), own = samples.filter(s => s.own !== null).map(s => s.own), fps = samples.map(s => s.fps);
58
- return { id, profile: { v: 1, device, verdict: off ? "off" : last.verdict, reason: last.reason, samples: samples.length,
59
- fps_p50: quantile(fps, 0.5), fps_p10: quantile(fps, 0.1), fps_floor: floor,
60
- long_task_p95: quantile(host, 0.95), long_task_samples: host.length,
61
- attributed_long_task_p95: quantile(own, 0.95), attributed_samples: own.length, attribution: own.length ? "observed" : "unknown", load_ms: last.loadMs } };
62
- },
63
- dispose() { dead = true; samples.length = 0; last = null; },
64
- };
65
- }