@songsid/agend 2.3.3-beta.4 → 2.3.3-beta.6
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.
- package/dist/agent-cli.js +13 -112
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +0 -1
- package/dist/agent-endpoint.js +1 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +3 -6
- package/dist/agent-whats-new.js +7 -18
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +0 -7
- package/dist/auth-api.js +1 -15
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/claude-code.js +4 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +1 -0
- package/dist/backend/codex.js +106 -15
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +57 -11
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/types.d.ts +35 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/bundled-skills.d.ts +4 -6
- package/dist/bundled-skills.js +10 -14
- package/dist/bundled-skills.js.map +1 -1
- package/dist/channel/mcp-server.js +5 -5
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +1 -3
- package/dist/channel/mcp-tools.js +4 -58
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +6 -3
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +17 -3
- package/dist/daemon.js +76 -107
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-live.d.ts +1 -1
- package/dist/dashboard-live.js +3 -3
- package/dist/dashboard-live.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +1 -1
- package/dist/dashboard-permissions.js +1 -3
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/fleet-manager.d.ts +31 -44
- package/dist/fleet-manager.js +94 -227
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +11 -1
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +13 -204
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +26 -63
- package/dist/instance-lifecycle.d.ts +6 -0
- package/dist/instance-lifecycle.js +20 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +14 -4
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.js +19 -7
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +0 -62
- package/dist/outbound-schemas.js +3 -42
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +0 -9
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +2 -0
- package/dist/public-web-link.js +5 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/search/search-docs.d.ts +1 -1
- package/dist/search/search-docs.js +2 -2
- package/dist/search/search-docs.js.map +1 -1
- package/dist/settings-api.d.ts +6 -0
- package/dist/settings-api.js +14 -2
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +3 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +17 -3
- package/dist/settings-confirmation.js +8 -5
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-http-confirmation.js +31 -1
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/tool-permissions.js +3 -10
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +27 -7
- package/dist/topic-commands.js +94 -36
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/ui/chat-ambient.js +4 -4
- package/dist/ui/chat-commands.js +3 -2
- package/dist/ui/chat-store.js +4 -1
- package/dist/ui/chat-strings.js +12 -46
- package/dist/ui/chat-thread.js +51 -59
- package/dist/ui/fleet-office-skin.js +37 -14
- package/dist/ui/fleet-office.js +31 -7
- package/dist/ui/html-attachment.js +22 -8
- package/dist/ui/panel-chat.js +21 -50
- package/dist/ui/preview-panel.js +12 -21
- package/dist/ui/preview.js +10 -3
- package/dist/ui/search-dialog.js +30 -14
- package/dist/ui/settings-appearance.js +1 -3
- package/dist/ui/settings-confirm.js +18 -0
- package/dist/ui/settings-guests.js +13 -4
- package/dist/ui/settings-learn.js +1 -1
- package/dist/ui/settings-mods.js +5 -12
- package/dist/ui/settings-registry.js +0 -1
- package/dist/ui/settings-strings.js +6 -6
- package/dist/ui/shared/app-i18n.js +13 -2
- package/dist/ui/shared/app-prefs.js +17 -17
- package/dist/ui/shared/app-shell.js +47 -9
- package/dist/ui/shared/app-store.js +6 -2
- package/dist/ui/shared/app.css +19 -36
- package/dist/ui/shared/app.js +5 -4
- package/dist/ui/shared/instance-nav.js +9 -9
- package/dist/ui/shared/instance-switch.js +8 -2
- package/dist/ui/shared/panel-view.js +22 -8
- package/dist/ui/shared/view-strings.js +6 -0
- package/dist/usage/usage-api.d.ts +3 -0
- package/dist/usage/usage-api.js +5 -1
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/view-api.d.ts +2 -0
- package/dist/view-api.js +64 -0
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +9 -17
- package/dist/web-api.js +255 -371
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +0 -2
- package/dist/web-appearance-tools.js +2 -5
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.d.ts +0 -6
- package/dist/web-appearance-view.js +0 -1
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +14 -7
- package/dist/web-appearance.js +5 -6
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-asset-parts.d.ts +1 -17
- package/dist/web-asset-parts.js +1 -15
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +1 -7
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +3 -7
- package/dist/web-chat-history.js +10 -17
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +9 -1
- package/dist/web-commands.js +13 -0
- package/dist/web-commands.js.map +1 -1
- package/dist/web-experimental.d.ts +2 -11
- package/dist/web-experimental.js +8 -36
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-guest-access.d.ts +40 -2
- package/dist/web-guest-access.js +50 -14
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +2 -45
- package/dist/web-mod-bundle.js +15 -63
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-store.d.ts +3 -48
- package/dist/web-mod-store.js +30 -97
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +13 -0
- package/dist/web-prefs.js +38 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -13
- package/dist/web-preview.js +12 -23
- package/dist/web-preview.js.map +1 -1
- package/dist/web-session.d.ts +11 -3
- package/dist/web-session.js +22 -11
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +1 -0
- package/dist/web-settings-patch.js +6 -3
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +0 -13
- package/dist/web-settings-tools.js +12 -84
- package/dist/web-settings-tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +0 -376
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +0 -672
- package/dist/ui/app-effects.js +0 -251
- package/dist/ui/chat-effect-posters.js +0 -42
- package/dist/ui/chat-effects.js +0 -167
- package/dist/ui/effect-trial-controller.js +0 -124
- package/dist/ui/effect-trial-offer.js +0 -2
- package/dist/ui/effect-trial-panel.js +0 -40
- package/dist/ui/effects-waker.js +0 -2
- package/dist/ui/settings-effects.js +0 -202
- package/dist/ui/settings-owner-effects.js +0 -82
- package/dist/ui/shared/effect-app-runtime.js +0 -299
- package/dist/ui/shared/effect-baseline.js +0 -64
- package/dist/ui/shared/effect-chat-runtime.js +0 -162
- package/dist/ui/shared/effect-cursor.js +0 -126
- package/dist/ui/shared/effect-display-metadata.js +0 -39
- package/dist/ui/shared/effect-frame.js +0 -146
- package/dist/ui/shared/effect-geometry.js +0 -137
- package/dist/ui/shared/effect-host-style.js +0 -10
- package/dist/ui/shared/effect-owner-regions.js +0 -74
- package/dist/ui/shared/effect-owner-source.js +0 -122
- package/dist/ui/shared/effect-policy.js +0 -151
- package/dist/ui/shared/effect-poster-reader.js +0 -98
- package/dist/ui/shared/effect-posters.js +0 -117
- package/dist/ui/shared/effect-profiler.js +0 -184
- package/dist/ui/shared/effect-profiles.js +0 -65
- package/dist/ui/shared/effect-registry.js +0 -545
- package/dist/ui/shared/effect-runtime-loader.js +0 -236
- package/dist/ui/shared/effect-safety.js +0 -153
- package/dist/ui/shared/effect-stock-snapshot.js +0 -43
- package/dist/ui/shared/effect-stock-trust.js +0 -217
- package/dist/ui/shared/effect-trial-shadow.js +0 -62
- package/dist/ui/shared/effect-trial-source.js +0 -103
- package/dist/ui/shared/effect-trial-stage.js +0 -170
- package/dist/ui/shared/effect-trial.css +0 -40
- package/dist/ui/shared/feature-flags.js +0 -127
- package/dist/ui/shared/owner-effect-prefs.js +0 -117
- package/dist/web-effect-bootstrap.d.ts +0 -12
- package/dist/web-effect-bootstrap.js +0 -49
- package/dist/web-effect-bootstrap.js.map +0 -1
- package/dist/web-effect-bundle.d.ts +0 -28
- package/dist/web-effect-bundle.js +0 -171
- package/dist/web-effect-bundle.js.map +0 -1
- package/dist/web-effect-canonical.d.ts +0 -8
- package/dist/web-effect-canonical.js +0 -27
- package/dist/web-effect-canonical.js.map +0 -1
- package/dist/web-effect-library.d.ts +0 -114
- package/dist/web-effect-library.js +0 -530
- package/dist/web-effect-library.js.map +0 -1
- package/dist/web-effect-playback.d.ts +0 -30
- package/dist/web-effect-playback.js +0 -71
- package/dist/web-effect-playback.js.map +0 -1
- package/dist/web-effect-poster.d.ts +0 -8
- package/dist/web-effect-poster.js +0 -52
- package/dist/web-effect-poster.js.map +0 -1
- package/dist/web-effect-profiles.d.ts +0 -118
- package/dist/web-effect-profiles.js +0 -181
- package/dist/web-effect-profiles.js.map +0 -1
- package/dist/web-effect-service.d.ts +0 -93
- package/dist/web-effect-service.js +0 -335
- package/dist/web-effect-service.js.map +0 -1
- package/dist/web-effect-tools.d.ts +0 -48
- package/dist/web-effect-tools.js +0 -129
- package/dist/web-effect-tools.js.map +0 -1
- package/dist/web-effect-trials.d.ts +0 -86
- package/dist/web-effect-trials.js +0 -204
- package/dist/web-effect-trials.js.map +0 -1
- package/dist/web-effect-upload.d.ts +0 -27
- package/dist/web-effect-upload.js +0 -185
- package/dist/web-effect-upload.js.map +0 -1
- package/dist/web-effect-worker.d.ts +0 -8
- package/dist/web-effect-worker.js +0 -170
- package/dist/web-effect-worker.js.map +0 -1
- package/dist/web-effect.d.ts +0 -103
- package/dist/web-effect.js +0 -153
- package/dist/web-effect.js.map +0 -1
- package/dist/web-experimental-http.d.ts +0 -2
- package/dist/web-experimental-http.js +0 -12
- package/dist/web-experimental-http.js.map +0 -1
- package/dist/web-mod-agent.d.ts +0 -50
- package/dist/web-mod-agent.js +0 -207
- 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
|
-
}
|