@songsid/agend 2.3.2 → 2.3.3-beta.10
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-instructions.md +2 -2
- 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 +23 -5
- package/dist/agent-whats-new.js +58 -40
- 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/authority/authority-state.js +1 -1
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/web-authority.d.ts +9 -4
- package/dist/authority/web-authority.js +10 -5
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/antigravity.js +6 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +20 -1
- package/dist/backend/claude-code.js +54 -29
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/claude-transcript-errors.d.ts +18 -0
- package/dist/backend/claude-transcript-errors.js +19 -0
- package/dist/backend/claude-transcript-errors.js.map +1 -0
- package/dist/backend/codex-session-lookup.d.ts +16 -3
- package/dist/backend/codex-session-lookup.js +45 -11
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.d.ts +16 -0
- package/dist/backend/codex.js +184 -35
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.js +58 -12
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-reconcile-store.js +8 -1
- package/dist/backend/kiro-reconcile-store.js.map +1 -1
- package/dist/backend/kiro.d.ts +0 -5
- package/dist/backend/kiro.js +2 -10
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.js +2 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +53 -4
- 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/adapters/discord.js +60 -17
- package/dist/channel/adapters/discord.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/classic-channel-manager.d.ts +4 -1
- package/dist/classic-channel-manager.js +21 -7
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +23 -6
- package/dist/cli.js.map +1 -1
- package/dist/config-history.d.ts +50 -0
- package/dist/config-history.js +263 -0
- package/dist/config-history.js.map +1 -0
- package/dist/config-validator.js +10 -8
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +11 -4
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +10 -5
- package/dist/cross-instance-envelope.js +20 -17
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +130 -64
- package/dist/daemon.js +603 -428
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.d.ts +8 -1
- package/dist/daily-summary.js +8 -5
- package/dist/daily-summary.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/delivery-outbox.d.ts +31 -22
- package/dist/delivery-outbox.js +119 -161
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js +10 -25
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.js +16 -1
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-level-config.js +1 -1
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +74 -66
- package/dist/fleet-manager.js +285 -414
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +5 -0
- package/dist/fleet-process-handlers.js +21 -0
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/fleet-service-owner.d.ts +11 -0
- package/dist/fleet-service-owner.js +3 -0
- package/dist/fleet-service-owner.js.map +1 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +11 -1
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +1 -1
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -1
- package/dist/general-knowledge/skills/session-management/SKILL.md +7 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +13 -204
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +30 -68
- package/dist/instance-lifecycle.d.ts +19 -0
- package/dist/instance-lifecycle.js +73 -15
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.js +6 -7
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-reconcile.js +3 -3
- package/dist/kiro-reconcile.js.map +1 -1
- package/dist/locale.js +96 -58
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/needs-you-hub.d.ts +1 -0
- package/dist/needs-you-hub.js +6 -3
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +3 -1
- package/dist/needs-you-live.js +24 -2
- package/dist/needs-you-live.js.map +1 -1
- package/dist/needs-you.d.ts +13 -2
- package/dist/needs-you.js +11 -1
- package/dist/needs-you.js.map +1 -1
- package/dist/notify-socket.d.ts +10 -0
- package/dist/notify-socket.js +45 -0
- package/dist/notify-socket.js.map +1 -0
- package/dist/outbound-handlers.js +20 -8
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +0 -62
- package/dist/outbound-schemas.js +4 -43
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/parked-notices.d.ts +87 -0
- package/dist/parked-notices.js +194 -0
- package/dist/parked-notices.js.map +1 -0
- package/dist/public-link-progress.d.ts +5 -2
- package/dist/public-link-progress.js +4 -3
- package/dist/public-link-progress.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 +12 -0
- package/dist/public-web-link.js +29 -5
- package/dist/public-web-link.js.map +1 -1
- package/dist/quota-notice.d.ts +23 -0
- package/dist/quota-notice.js +22 -0
- package/dist/quota-notice.js.map +1 -0
- package/dist/scheduler/task-board.d.ts +17 -25
- package/dist/scheduler/task-board.js +29 -39
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/sd-notify.d.ts +23 -15
- package/dist/sd-notify.js +88 -29
- package/dist/sd-notify.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 +16 -1
- package/dist/settings-api.js +56 -16
- 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-effect.js +0 -2
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +31 -1
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-wizard.d.ts +3 -0
- package/dist/setup-wizard.js +4 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/task-board-waker.d.ts +43 -1
- package/dist/task-board-waker.js +121 -9
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tmux-control.d.ts +1 -1
- package/dist/tmux-control.js +3 -2
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.js +18 -16
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +2 -0
- package/dist/tmux-read.js +7 -1
- package/dist/tmux-read.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 +29 -7
- package/dist/topic-commands.js +97 -37
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.d.ts +7 -0
- package/dist/transcript-monitor.js +48 -3
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +5 -0
- package/dist/tunnel/cloudflared.js +32 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/named-diagnostics.d.ts +2 -1
- package/dist/tunnel/named-diagnostics.js +4 -1
- package/dist/tunnel/named-diagnostics.js.map +1 -1
- package/dist/tunnel/named-link-test.d.ts +90 -0
- package/dist/tunnel/named-link-test.js +270 -0
- package/dist/tunnel/named-link-test.js.map +1 -0
- package/dist/tunnel/purpose-lane.d.ts +3 -1
- package/dist/tunnel/purpose-lane.js +2 -0
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/types.d.ts +9 -11
- 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/panel-log.js +2 -2
- package/dist/ui/panel-needs.js +11 -9
- package/dist/ui/panel-settings.js +5 -3
- 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 +3 -37
- 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-named-test.js +144 -0
- package/dist/ui/settings-registry.js +0 -2
- package/dist/ui/settings-strings.js +16 -16
- package/dist/ui/settings-web-authority.js +4 -3
- package/dist/ui/shared/app-i18n.js +15 -4
- package/dist/ui/shared/app-needs.js +5 -1
- 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 +33 -39
- 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/ui/shared/web-authorize.js +35 -13
- package/dist/update-check.d.ts +13 -4
- package/dist/update-check.js +17 -4
- package/dist/update-check.js.map +1 -1
- 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 +257 -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 +3 -8
- package/dist/web-experimental.js +9 -33
- 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 -1
- package/dist/web-settings-patch.js +9 -7
- 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/dist/workflow-templates/default.md +2 -2
- package/dist/working-recovery.d.ts +0 -2
- package/dist/working-recovery.js +2 -23
- package/dist/working-recovery.js.map +1 -1
- package/launcher/agend-install.cjs +7 -1
- package/launcher/fleet-service-owner.cjs +150 -0
- 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 -673
- package/dist/task-board-summary.d.ts +0 -101
- package/dist/task-board-summary.js +0 -177
- package/dist/task-board-summary.js.map +0 -1
- 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/upgrade-notices.d.ts +0 -17
- package/dist/upgrade-notices.js +0 -93
- package/dist/upgrade-notices.js.map +0 -1
- 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,122 +0,0 @@
|
|
|
1
|
-
/** #1629: host-only personal contribution model. No fetch, timer, DOM creation, registry or playback. */
|
|
2
|
-
import { checkEffectOwnerSlots } from "./effect-runtime-loader.js";
|
|
3
|
-
import { effectStockSnapshot } from "./effect-stock-snapshot.js";
|
|
4
|
-
const SLOTS = ["fx.page", "fx.header", "fx.sidebar", "fx.box"], ALL_SLOTS = [...SLOTS, "fx.cursor"];
|
|
5
|
-
|
|
6
|
-
/** The app supplies its one loader/metadata scheduler, lifetime and fixed stock target resolver. */
|
|
7
|
-
export function createOwnerEffectSource({ current, readMetadata, readResource, resolveTarget, stock, onChange = () => {}, onUnavailable = () => {} } = {}) {
|
|
8
|
-
let dead = false, generation = 0, serial = 0, metadata = null, error = null, active = null, rev = -1, checking = false, cursor = null;
|
|
9
|
-
const bySlot = new Map(), byKey = new Map(), paused = new Map(), failed = new Map();
|
|
10
|
-
const safe = fn => { try { return fn(); } catch { return null; } };
|
|
11
|
-
function alive() {
|
|
12
|
-
if (dead || checking) return false;
|
|
13
|
-
const before = generation; checking = true;
|
|
14
|
-
let value; try { value = current() === true; } catch { value = false; } finally { checking = false; }
|
|
15
|
-
return !dead && before === generation && value;
|
|
16
|
-
}
|
|
17
|
-
const fresh = (body, gen) => alive() && generation === gen && metadata === body;
|
|
18
|
-
function drop(slot) { const b = bySlot.get(slot); if (b) byKey.delete(b.key); bySlot.delete(slot); }
|
|
19
|
-
function clear() { bySlot.clear(); byKey.clear(); }
|
|
20
|
-
const notify = () => { if (alive()) safe(onChange); };
|
|
21
|
-
function dispose() { if (dead) return; dead = true; generation++; metadata = null; cursor = null; clear(); paused.clear(); failed.clear(); }
|
|
22
|
-
const unavailable = { ok: false, dispose };
|
|
23
|
-
if (![current, readMetadata, readResource, resolveTarget, stock, onChange, onUnavailable].every(x => typeof x === "function")) return unavailable;
|
|
24
|
-
function row(slot) { return alive() && SLOTS.includes(slot) ? metadata?.slots[slot] ?? null : null; }
|
|
25
|
-
function bindingCurrent(b) {
|
|
26
|
-
if (!b || !alive() || generation !== b.generation || bySlot.get(b.slot) !== b || byKey.get(b.key) !== b || failed.get(b.slot) === b.id) return false;
|
|
27
|
-
const selected = metadata?.slots[b.slot];
|
|
28
|
-
return selected?.status === "available" && selected.id === b.id && b.element.isConnected === true && !dead && generation === b.generation;
|
|
29
|
-
}
|
|
30
|
-
function cursorSelection() {
|
|
31
|
-
const body = metadata, gen = generation;
|
|
32
|
-
if (!alive() || !body || error) return null;
|
|
33
|
-
const selected = body.slots["fx.cursor"];
|
|
34
|
-
if (selected.status !== "available" || failed.get("fx.cursor") === selected.id || !selected.manifest.cursor) return null;
|
|
35
|
-
if (!cursor || cursor.id !== selected.id || cursor.generation !== gen) cursor = Object.freeze({ id: selected.id, generation: gen, hotspot: Object.freeze([...selected.manifest.cursor.hotspot]) });
|
|
36
|
-
return fresh(body, gen) && failed.get("fx.cursor") !== selected.id ? cursor : null;
|
|
37
|
-
}
|
|
38
|
-
function cursorCurrent(value) {
|
|
39
|
-
try {
|
|
40
|
-
const expected = cursorSelection(); if (!expected || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return false;
|
|
41
|
-
const d = Object.getOwnPropertyDescriptors(value);
|
|
42
|
-
if (Reflect.ownKeys(d).length !== 3 || !["id", "generation", "hotspot"].every(k => d[k] && Object.hasOwn(d[k], "value")) || d.id.value !== expected.id || d.generation.value !== expected.generation) return false;
|
|
43
|
-
const a = d.hotspot.value; if (!Array.isArray(a) || Object.getPrototypeOf(a) !== Array.prototype) return false;
|
|
44
|
-
const h = Object.getOwnPropertyDescriptors(a);
|
|
45
|
-
return Reflect.ownKeys(h).length === 3 && h.length.value === 2 && [0, 1].every(k => h[k] && Object.hasOwn(h[k], "value") && h[k].value === expected.hotspot[k]) && cursorSelection() === expected;
|
|
46
|
-
} catch { return false; }
|
|
47
|
-
}
|
|
48
|
-
function refuse(b) {
|
|
49
|
-
if (!bindingCurrent(b)) return;
|
|
50
|
-
failed.set(b.slot, b.id); drop(b.slot); notify();
|
|
51
|
-
if (!dead && generation === b.generation && failed.get(b.slot) === b.id && metadata?.slots[b.slot]?.id === b.id) safe(() => onUnavailable(b.slot, b.id));
|
|
52
|
-
}
|
|
53
|
-
return { ok: true, cursorSelection, cursorCurrent,
|
|
54
|
-
refuseCursor(value) {
|
|
55
|
-
const own = cursorSelection(); if (!own || !cursorCurrent(value)) return;
|
|
56
|
-
failed.set("fx.cursor", own.id); notify();
|
|
57
|
-
if (!dead && generation === own.generation && failed.get("fx.cursor") === own.id && metadata?.slots["fx.cursor"]?.id === own.id) safe(() => onUnavailable("fx.cursor", own.id));
|
|
58
|
-
},
|
|
59
|
-
needsMetadata() { return alive() && metadata === null && error === null; },
|
|
60
|
-
invalidate() { if (dead) return; generation++; metadata = null; cursor = null; error = null; clear(); failed.clear(); notify(); },
|
|
61
|
-
async read(signal) {
|
|
62
|
-
if (!alive() || metadata !== null || error !== null || active || signal?.aborted) return false;
|
|
63
|
-
const op = { generation }; active = op;
|
|
64
|
-
const valid = () => alive() && generation === op.generation && active === op && !signal?.aborted;
|
|
65
|
-
try {
|
|
66
|
-
const answer = await readMetadata(signal); if (!valid()) return false;
|
|
67
|
-
const next = checkEffectOwnerSlots(answer);
|
|
68
|
-
if (!next || next.rev < rev) { error = next ? "stale" : "unreadable"; clear(); notify(); return false; }
|
|
69
|
-
rev = next.rev; metadata = next;
|
|
70
|
-
for (const [slot, id] of paused) if (next.slots[slot]?.id !== id) paused.delete(slot);
|
|
71
|
-
notify(); return valid() && metadata === next;
|
|
72
|
-
} catch { if (valid()) { error = "unreadable"; clear(); notify(); } return false; }
|
|
73
|
-
finally { if (active === op) active = null; }
|
|
74
|
-
},
|
|
75
|
-
descriptors() {
|
|
76
|
-
if (!alive() || !metadata || error) return [];
|
|
77
|
-
const body = metadata, gen = generation;
|
|
78
|
-
// #2188: the stock snapshot walks the whole page (thousands of computed styles). With no slot holding an effect
|
|
79
|
-
// that could play, there is nothing for it to admit: skipped — every page change (a status, a chat switch) ran it.
|
|
80
|
-
if (!SLOTS.some(slot => body.slots[slot]?.status === "available" && failed.get(slot) !== body.slots[slot].id)) {
|
|
81
|
-
for (const slot of SLOTS) drop(slot);
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
const proof = safe(() => effectStockSnapshot(stock()));
|
|
85
|
-
if (!fresh(body, gen)) return [];
|
|
86
|
-
if (!proof) { clear(); return []; }
|
|
87
|
-
const result = [];
|
|
88
|
-
for (const slot of SLOTS) {
|
|
89
|
-
const selected = body.slots[slot];
|
|
90
|
-
if (selected.status !== "available" || failed.get(slot) === selected.id) { drop(slot); continue; }
|
|
91
|
-
const element = safe(() => resolveTarget(slot, selected.manifest, proof));
|
|
92
|
-
if (!fresh(body, gen)) return [];
|
|
93
|
-
if (!element || element.isConnected !== true) { drop(slot); continue; }
|
|
94
|
-
let b = bySlot.get(slot);
|
|
95
|
-
if (!b || b.id !== selected.id || b.element !== element || b.generation !== gen) {
|
|
96
|
-
drop(slot); b = Object.freeze({ kind: "owner", slot, id: selected.id, key: `owner:${++serial}:${slot}:${selected.id}`, generation: gen, element });
|
|
97
|
-
bySlot.set(slot, b); byKey.set(b.key, b);
|
|
98
|
-
}
|
|
99
|
-
result.push({ key: b.key, id: b.id, by: "human", region: slot.slice(3), layer: selected.manifest.layer, order: 0, paused: paused.get(slot) === b.id, visible: true, instance: null, element });
|
|
100
|
-
}
|
|
101
|
-
return fresh(body, gen) ? result : [];
|
|
102
|
-
},
|
|
103
|
-
binding(key, id) { const b = byKey.get(key); return b?.id === id && bindingCurrent(b) ? b : null; },
|
|
104
|
-
slotBinding(slot) { const b = bySlot.get(slot); return bindingCurrent(b) ? b : null; },
|
|
105
|
-
current: bindingCurrent,
|
|
106
|
-
async load(b, signal) {
|
|
107
|
-
if (!bindingCurrent(b) || signal?.aborted) return null;
|
|
108
|
-
let value; try { value = await readResource({ id: b.id, slot: b.slot }, signal); } catch { value = null; }
|
|
109
|
-
if (!bindingCurrent(b) || signal?.aborted) return null;
|
|
110
|
-
if (!value) { refuse(b); return null; }
|
|
111
|
-
return value;
|
|
112
|
-
},
|
|
113
|
-
pause(slot) { const selected = row(slot); if (selected?.status !== "available") return false; paused.set(slot, selected.id); notify(); return true; },
|
|
114
|
-
unpause(slot) { const selected = row(slot); if (selected?.status !== "available") return false; paused.delete(slot); notify(); return true; },
|
|
115
|
-
snapshot() {
|
|
116
|
-
const live = alive();
|
|
117
|
-
return { error: live ? error : null, rev, loading: live && metadata === null && error === null,
|
|
118
|
-
slots: Object.fromEntries(ALL_SLOTS.map(slot => { const selected = live ? metadata?.slots[slot] : null; return [slot, selected ? failed.get(slot) === selected.id ? { status: "unavailable", id: selected.id } : selected : { status: "unknown", id: null }]; })) };
|
|
119
|
-
},
|
|
120
|
-
dispose,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/** #1629 r3: pure host policy. No frame creation, DOM reads, content reads or network. */
|
|
2
|
-
import { effectTrialStageIdentity } from "./effect-trial-stage.js";
|
|
3
|
-
const OWN = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
4
|
-
const plain = o => !!o && (Object.getPrototypeOf(o) === Object.prototype || Object.getPrototypeOf(o) === null);
|
|
5
|
-
const finite = v => typeof v === "number" && Number.isFinite(v);
|
|
6
|
-
const instanceName = v => typeof v === "string" && v.length >= 1 && v.length <= 128;
|
|
7
|
-
const TYPES = Object.freeze({ state: ["state"], reply: [], mention: [], theme: ["mode"], env: ["width", "height", "dpr", "quality"], pointer: ["kind", "x", "y", "buttons"] });
|
|
8
|
-
/** Reserved for host-drawn widgets, never exposed to a Canvas Worker. */
|
|
9
|
-
export const WIDGET_SIGNAL_KINDS = Object.freeze(["status", "usage", "fleet"]);
|
|
10
|
-
|
|
11
|
-
/** Build a fresh content-free signal. Unknown fields are never copied or read. */
|
|
12
|
-
export function effectEvent(type, fields, instance, ts) {
|
|
13
|
-
if (!OWN(TYPES, type) || !plain(fields) || !finite(ts) || ts < 0 || instance !== null && !instanceName(instance)) return null;
|
|
14
|
-
const out = { v: 1, type, ts };
|
|
15
|
-
if (["state", "reply", "mention"].includes(type)) {
|
|
16
|
-
if (OWN(fields, "instance") && fields.instance !== instance) return null;
|
|
17
|
-
if (instance !== null) out.instance = instance;
|
|
18
|
-
}
|
|
19
|
-
for (const key of TYPES[type]) out[key] = fields[key];
|
|
20
|
-
if (type === "pointer") for (const key of ["dx", "dy"]) if (OWN(fields, key)) out[key] = fields[key];
|
|
21
|
-
return validEffectEvent(out, instance) ? out : null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Independent send fence: exact keys, enums, numeric bounds and the current region's own instance. */
|
|
25
|
-
export function validEffectEvent(d, instance) {
|
|
26
|
-
if (instance !== null && !instanceName(instance)) return false;
|
|
27
|
-
if (!plain(d) || d.v !== 1 || !OWN(TYPES, d.type) || !finite(d.ts) || d.ts < 0) return false;
|
|
28
|
-
const keys = ["v", "type", "ts", ...TYPES[d.type]];
|
|
29
|
-
if (["state", "reply", "mention"].includes(d.type) && OWN(d, "instance")) {
|
|
30
|
-
if (!instanceName(instance) || d.instance !== instance) return false;
|
|
31
|
-
keys.push("instance");
|
|
32
|
-
}
|
|
33
|
-
if (d.type === "pointer") for (const key of ["dx", "dy"]) if (OWN(d, key)) {
|
|
34
|
-
if (!finite(d[key]) || Math.abs(d[key]) > 10000) return false;
|
|
35
|
-
keys.push(key);
|
|
36
|
-
}
|
|
37
|
-
if (Object.keys(d).sort().join(",") !== keys.sort().join(",")) return false;
|
|
38
|
-
switch (d.type) {
|
|
39
|
-
case "state": return ["idle", "working", "needs_you", "stopped"].includes(d.state);
|
|
40
|
-
case "reply": case "mention": return true;
|
|
41
|
-
case "theme": return ["light", "dark"].includes(d.mode);
|
|
42
|
-
case "env": return Number.isInteger(d.width) && d.width >= 1 && d.width <= 4096 && Number.isInteger(d.height) && d.height >= 1 && d.height <= 4096 && d.width * d.height <= 8388608 && finite(d.dpr) && d.dpr >= 0.5 && d.dpr <= 3 && ["normal", "low"].includes(d.quality);
|
|
43
|
-
case "pointer": return ["move", "down", "up", "click", "wheel"].includes(d.kind) && finite(d.x) && d.x >= 0 && d.x <= 4096 && finite(d.y) && d.y >= 0 && d.y <= 4096 && Number.isInteger(d.buttons) && d.buttons >= 0 && d.buttons <= 31;
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Least privilege at the final send; the instance is the host's binding, never taken from event data. */
|
|
49
|
-
export function maySendEffectEvent(event, binding) {
|
|
50
|
-
if (!plain(binding) || !validEffectEvent(event, binding.instance) || !Array.isArray(binding.wants) || event.type === "mention") return false;
|
|
51
|
-
if (["state", "reply", "mention"].includes(event.type)) {
|
|
52
|
-
if (binding.instance === null ? OWN(event, "instance") : !OWN(event, "instance") || event.instance !== binding.instance) return false;
|
|
53
|
-
}
|
|
54
|
-
return event.type === "env" || binding.wants.includes(event.type);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function constrainedDevice(signals) {
|
|
58
|
-
return !!signals && (finite(signals.memory) && signals.memory > 0 && signals.memory <= 4 || finite(signals.cores) && signals.cores > 0 && signals.cores <= 4 || signals.coarse === true && finite(signals.width) && signals.width > 0 && signals.width <= 900);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const EFFECT_BLOCK_REASONS = Object.freeze([
|
|
62
|
-
"transport_unavailable", "preview_unavailable", "preview_not_consented", "unsupported_host", "safe_mode", "reduced_motion", "save_data", "hidden", "disabled", "public_link",
|
|
63
|
-
"baseline_unsupported", "baseline_unavailable", "baseline_timeout", "calibrating", "clock_unavailable", "gates_unknown", "owner_css", "stock_overlay", "modal",
|
|
64
|
-
"safety_off", "safety_unknown", "interrupted", "paused", "host_budget", "frame_limit", "front_limit", "offscreen", "region_unavailable", "resource_unavailable", "resource_timeout", "runtime_unavailable", "unknown",
|
|
65
|
-
]);
|
|
66
|
-
|
|
67
|
-
/** Diagnostic names only; the admission policy below still validates every required flag. */
|
|
68
|
-
export function effectPlaybackReason(c) {
|
|
69
|
-
if (!c || ["safe", "publicLink", "reducedMotion", "hidden", "enabled", "show", "transportReady"].some(k => typeof c[k] !== "boolean")) return "gates_unknown";
|
|
70
|
-
return c.safe ? "safe_mode" : c.reducedMotion ? "reduced_motion"
|
|
71
|
-
: c.hidden ? "hidden" : !c.enabled ? "unsupported_host" : !c.show ? "disabled" : !c.transportReady ? "transport_unavailable" : null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const OWNER_ORDER = Object.freeze({ page: 0, header: 1, sidebar: 2, box: 3 });
|
|
75
|
-
/** Recomputed from host descriptors; queued/off/unknown descriptors never get a frame reservation. */
|
|
76
|
-
export function allocateEffectFrames(descriptors, context) {
|
|
77
|
-
if (!Array.isArray(descriptors) || descriptors.length > 128 || !plain(context)) return [];
|
|
78
|
-
const keys = new Set(), duplicate = new Set();
|
|
79
|
-
for (const d of descriptors) if (d && typeof d.key === "string") { if (keys.has(d.key)) duplicate.add(d.key); keys.add(d.key); }
|
|
80
|
-
const result = descriptors.map(d => ({ key: d?.key, verdict: "off", reason: "descriptor_invalid", queuePos: null }));
|
|
81
|
-
const gates = ["safe", "publicLink", "reducedMotion", "hidden", "enabled", "show", "transportReady", "constrained", "modal"];
|
|
82
|
-
if (gates.some(k => typeof context[k] !== "boolean") || context.openInstance !== null && !instanceName(context.openInstance)) return result.map(r => ({ ...r, reason: "gates_unknown" }));
|
|
83
|
-
let global = null;
|
|
84
|
-
if (context.safe === true) global = "safe_mode";
|
|
85
|
-
else if (context.reducedMotion === true) global = "reduced_motion";
|
|
86
|
-
else if (context.hidden === true) global = "hidden";
|
|
87
|
-
else if (context.enabled !== true) global = "unsupported_host";
|
|
88
|
-
else if (context.show !== true) global = "disabled";
|
|
89
|
-
|
|
90
|
-
const candidates = [];
|
|
91
|
-
for (let i = 0; i < descriptors.length; i++) {
|
|
92
|
-
const d = descriptors[i], r = result[i];
|
|
93
|
-
if (!plain(d) || typeof d.key !== "string" || d.key.length < 1 || d.key.length > 256 || duplicate.has(d.key) || !/^[0-9a-f]{32}$/.test(d.id) || !["chat", "page", "header", "sidebar", "box"].includes(d.region) || !["front", "behind"].includes(d.layer) || !Number.isSafeInteger(d.order) || d.order < 0 || typeof d.paused !== "boolean") continue;
|
|
94
|
-
// Missing/unknown provenance always receives the agent restrictions.
|
|
95
|
-
const trialIdentity = d.trialStage === undefined || d.trialStage === null ? null : effectTrialStageIdentity(d.trialStage);
|
|
96
|
-
const trial = !!trialIdentity && d.region === "chat" && trialIdentity.key === d.key && trialIdentity.id === d.id && trialIdentity.instance === d.instance && trialIdentity.generation === d.generation;
|
|
97
|
-
if (d.trialStage != null && !trial) { r.reason = "trial_unknown"; continue; }
|
|
98
|
-
const owner = !trial && d.by === "human";
|
|
99
|
-
if (!owner && d.region !== "chat") { r.reason = "scope"; continue; }
|
|
100
|
-
if (d.region === "chat" && (!instanceName(d.instance) || d.instance !== context.openInstance)) { r.reason = "closed_chat"; continue; }
|
|
101
|
-
if (global) { r.reason = global; continue; }
|
|
102
|
-
if (context.publicLink && (d.region !== "chat" || trial)) { r.reason = "public_link"; continue; }
|
|
103
|
-
if (!context.transportReady) { r.reason = "transport_unavailable"; continue; }
|
|
104
|
-
if (d.paused === true) { r.reason = "paused"; continue; }
|
|
105
|
-
if (d.visible !== true) { r.reason = "offscreen"; continue; }
|
|
106
|
-
if (d.layer === "front" && context.modal === true && !trial) { r.reason = "modal"; continue; }
|
|
107
|
-
candidates.push({ i, d, priority: owner && OWN(OWNER_ORDER, d.region) ? OWNER_ORDER[d.region] : 4 });
|
|
108
|
-
}
|
|
109
|
-
candidates.sort((a, b) => a.priority - b.priority || a.d.order - b.d.order || a.i - b.i);
|
|
110
|
-
const max = context.constrained === true ? 1 : 3, frontMax = context.constrained === true ? 1 : 2;
|
|
111
|
-
let active = 0, front = 0, queued = 0;
|
|
112
|
-
for (const { i, d } of candidates) {
|
|
113
|
-
const r = result[i];
|
|
114
|
-
if (active >= max || d.layer === "front" && front >= frontMax) {
|
|
115
|
-
r.verdict = "queued"; r.reason = active >= max ? "frame_limit" : "front_limit"; r.queuePos = ++queued;
|
|
116
|
-
} else {
|
|
117
|
-
r.verdict = "active"; r.reason = null; active++; if (d.layer === "front") front++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return result;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const finiteRect = r => plain(r) && ["x", "y", "width", "height"].every(k => finite(r[k]) && Math.abs(r[k]) <= 1e7) && r.width >= 0 && r.height >= 0;
|
|
124
|
-
/**
|
|
125
|
-
* Integer padded holes are unioned before evenodd: overlapping holes must never repaint trusted pixels.
|
|
126
|
-
* Input is the host's viewport rects, not mod geometry. Unknown/overflow means hide, never an unclipped fallback.
|
|
127
|
-
*/
|
|
128
|
-
export function effectTrustClip(frame, trusted) {
|
|
129
|
-
const unknown = { ok: false, reason: "trust_geometry_unknown" };
|
|
130
|
-
if (!finiteRect(frame) || frame.width < 1 || frame.height < 1 || frame.width > 4096 || frame.height > 4096 || !Number.isInteger(frame.width) || !Number.isInteger(frame.height) || !Array.isArray(trusted) || trusted.length > 128) return unknown;
|
|
131
|
-
const rects = [];
|
|
132
|
-
for (const r of trusted) {
|
|
133
|
-
if (!finiteRect(r)) return unknown;
|
|
134
|
-
const x1 = Math.max(0, Math.floor(r.x - frame.x - 8)), y1 = Math.max(0, Math.floor(r.y - frame.y - 8));
|
|
135
|
-
const x2 = Math.min(frame.width, Math.ceil(r.x + r.width - frame.x + 8)), y2 = Math.min(frame.height, Math.ceil(r.y + r.height - frame.y + 8));
|
|
136
|
-
if (x2 > x1 && y2 > y1) rects.push({ x1, y1, x2, y2 });
|
|
137
|
-
}
|
|
138
|
-
const ys = [...new Set(rects.flatMap(r => [r.y1, r.y2]))].sort((a, b) => a - b), holes = [];
|
|
139
|
-
for (let i = 0; i + 1 < ys.length; i++) {
|
|
140
|
-
const y1 = ys[i], y2 = ys[i + 1], spans = rects.filter(r => r.y1 <= y1 && r.y2 >= y2).map(r => [r.x1, r.x2]).sort((a, b) => a[0] - b[0]);
|
|
141
|
-
let span = null;
|
|
142
|
-
for (const next of spans) {
|
|
143
|
-
if (span && next[0] <= span[1]) span[1] = Math.max(span[1], next[1]);
|
|
144
|
-
else { if (span) holes.push({ x: span[0], y: y1, width: span[1] - span[0], height: y2 - y1 }); span = [...next]; }
|
|
145
|
-
}
|
|
146
|
-
if (span) holes.push({ x: span[0], y: y1, width: span[1] - span[0], height: y2 - y1 });
|
|
147
|
-
if (holes.length > 512) return { ok: false, reason: "trust_geometry_limit" };
|
|
148
|
-
}
|
|
149
|
-
const rectPath = r => `M${r.x} ${r.y}H${r.x + r.width}V${r.y + r.height}H${r.x}Z`;
|
|
150
|
-
return { ok: true, holes, path: `path(evenodd, "${rectPath({ x: 0, y: 0, width: frame.width, height: frame.height })}${holes.map(rectPath).join("")}")` };
|
|
151
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/** #1629: closed static-poster reads only. No epoch, library/runtime route, code, or response-derived URL. */
|
|
2
|
-
const pools = new WeakMap(), ID = /^[0-9a-f]{32}$/;
|
|
3
|
-
const plain = x => !!x && (Object.getPrototypeOf(x) === Object.prototype || Object.getPrototypeOf(x) === null);
|
|
4
|
-
const exact = (x, keys) => plain(x) && Reflect.ownKeys(x).length === keys.length && keys.every(k => Object.hasOwn(x, k) && Object.hasOwn(Object.getOwnPropertyDescriptor(x, k), "value"));
|
|
5
|
-
const instanceOK = x => typeof x === "string" && x.length >= 1 && x.length <= 128;
|
|
6
|
-
export function checkEffectPosters(value) {
|
|
7
|
-
if (!exact(value, ["rev", "posters"]) || !Number.isSafeInteger(value.rev) || value.rev < 0 || !Array.isArray(value.posters) || value.posters.length > 2) return null;
|
|
8
|
-
const out = [], ids = new Set(), descriptors = Object.getOwnPropertyDescriptors(value.posters);
|
|
9
|
-
if (Reflect.ownKeys(descriptors).length !== value.posters.length + 1) return null;
|
|
10
|
-
for (let n = 0; n < value.posters.length; n++) {
|
|
11
|
-
const descriptor = descriptors[n]; if (!descriptor || !Object.hasOwn(descriptor, "value")) return null;
|
|
12
|
-
const p = descriptor.value;
|
|
13
|
-
// Titles are display-only, never selectors/URLs/CSS. The server's normalized UTF-16 input bound is 400.
|
|
14
|
-
if (!exact(p, ["id", "title"]) || typeof p.id !== "string" || !ID.test(p.id) || ids.has(p.id) || typeof p.title !== "string" || p.title.length < 1 || p.title.length > 400) return null;
|
|
15
|
-
ids.add(p.id); out.push(Object.freeze({ id: p.id, title: p.title }));
|
|
16
|
-
}
|
|
17
|
-
return Object.freeze({ rev: value.rev, posters: Object.freeze(out) });
|
|
18
|
-
}
|
|
19
|
-
/** Refuse animation/non-PNG/oversized images before DOM decode. This is not a CRC or compressed-data decoder. */
|
|
20
|
-
export function checkStaticEffectPoster(bytes) {
|
|
21
|
-
if (!(bytes instanceof Uint8Array) || bytes.length < 45 || bytes.length > 1048576 || [137, 80, 78, 71, 13, 10, 26, 10].some((b, n) => bytes[n] !== b)) return null;
|
|
22
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), width = view.getUint32(16), height = view.getUint32(20);
|
|
23
|
-
if (!width || !height || width > 4096 || height > 4096 || width * height > 8388608) return null;
|
|
24
|
-
let offset = 8, count = 0, data = false;
|
|
25
|
-
while (offset + 12 <= bytes.length && ++count <= 4096) {
|
|
26
|
-
const length = view.getUint32(offset), type = String.fromCharCode(...bytes.subarray(offset + 4, offset + 8));
|
|
27
|
-
if (offset + length + 12 > bytes.length || ["acTL", "fcTL", "fdAT"].includes(type) || (count === 1 ? type !== "IHDR" || length !== 13 : type === "IHDR")) return null;
|
|
28
|
-
if (type === "IDAT" && length) data = true;
|
|
29
|
-
offset += length + 12;
|
|
30
|
-
if (type === "IEND") return !length && data && offset === bytes.length ? new Uint8Array(bytes) : null;
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
/** Two physical requests per Window, across canceled/replaced readers; cancellation does not release early. */
|
|
35
|
-
export function createEffectPosterReader({ window: host, current } = {}) {
|
|
36
|
-
const unavailable = { ok: false, dispose() {} };
|
|
37
|
-
if (!host || typeof current !== "function" || ![host.fetch, host.AbortController, host.setTimeout, host.clearTimeout, host.performance?.now].every(x => typeof x === "function")) return unavailable;
|
|
38
|
-
if (!pools.has(host)) pools.set(host, new Set());
|
|
39
|
-
const all = pools.get(host), own = new Set(); let dead = false, revoked = false, previous = null;
|
|
40
|
-
const alive = () => !dead && !revoked && current() === true;
|
|
41
|
-
function stop() { if (dead) return; dead = true; for (const op of own) op.abort(); }
|
|
42
|
-
function now() {
|
|
43
|
-
let n; try { n = host.performance.now(); } catch { stop(); return null; }
|
|
44
|
-
if (!Number.isFinite(n) || n < 0 || previous !== null && n < previous) { stop(); return null; }
|
|
45
|
-
previous = n; return n;
|
|
46
|
-
}
|
|
47
|
-
function read(path, mime, limit, project, signal) {
|
|
48
|
-
if (!alive() || signal?.aborted || all.size >= 2) return Promise.resolve(null);
|
|
49
|
-
const began = now(); if (began === null) return Promise.resolve(null);
|
|
50
|
-
const controller = new host.AbortController(), op = {}; all.add(op); own.add(op);
|
|
51
|
-
let reader = null, body = null, cancellation = null, timer, release;
|
|
52
|
-
const cancelled = new Promise(resolve => { release = resolve; });
|
|
53
|
-
const cancelBody = () => {
|
|
54
|
-
if (cancellation) return cancellation;
|
|
55
|
-
try { cancellation = Promise.resolve(reader ? reader.cancel() : body?.cancel?.()).catch(() => {}); }
|
|
56
|
-
catch { cancellation = Promise.resolve(); }
|
|
57
|
-
return cancellation;
|
|
58
|
-
};
|
|
59
|
-
op.abort = () => { controller.abort(); if (body) void cancelBody(); release(null); };
|
|
60
|
-
const valid = () => {
|
|
61
|
-
const t = now();
|
|
62
|
-
if (!alive() || controller.signal.aborted || t === null || t < began || t - began >= 10000) { op.abort(); return false; }
|
|
63
|
-
return true;
|
|
64
|
-
};
|
|
65
|
-
const work = Promise.resolve().then(async () => {
|
|
66
|
-
if (!valid()) return null;
|
|
67
|
-
const response = await host.fetch(path, { credentials: "same-origin", cache: "no-store", redirect: "error", signal: controller.signal }); body = response.body;
|
|
68
|
-
if (!valid()) return null;
|
|
69
|
-
if ([401, 403, 409].includes(response.status)) { revoked = true; for (const pending of own) pending.abort(); return null; }
|
|
70
|
-
if (!response.ok || response.redirected || (response.headers.get("content-type") || "").split(";")[0].trim().toLowerCase() !== mime) return null;
|
|
71
|
-
const length = response.headers.get("content-length");
|
|
72
|
-
if (length !== null && (!/^(0|[1-9][0-9]*)$/.test(length) || !Number.isSafeInteger(Number(length)) || Number(length) > limit) || typeof body?.getReader !== "function") return null;
|
|
73
|
-
reader = body.getReader(); const chunks = []; let size = 0;
|
|
74
|
-
for (;;) {
|
|
75
|
-
const part = await reader.read(); if (!valid()) return null;
|
|
76
|
-
if (part.done) break;
|
|
77
|
-
if (!(part.value instanceof Uint8Array) || (size += part.value.length) > limit) return null;
|
|
78
|
-
chunks.push(new Uint8Array(part.value));
|
|
79
|
-
}
|
|
80
|
-
const bytes = new Uint8Array(size); let offset = 0; for (const part of chunks) { bytes.set(part, offset); offset += part.length; }
|
|
81
|
-
if (!valid()) return null; const answer = project(bytes); return valid() ? answer : null;
|
|
82
|
-
}).catch(() => null).finally(async () => {
|
|
83
|
-
controller.abort(); await cancelBody(); all.delete(op); own.delete(op); host.clearTimeout(timer); signal?.removeEventListener("abort", op.abort);
|
|
84
|
-
});
|
|
85
|
-
timer = host.setTimeout(op.abort, 10000); signal?.addEventListener("abort", op.abort, { once: true });
|
|
86
|
-
return Promise.race([work, cancelled]);
|
|
87
|
-
}
|
|
88
|
-
return { ok: true,
|
|
89
|
-
summary(instance, signal) {
|
|
90
|
-
return instanceOK(instance) ? read(`/ui/effect-posters?instance=${encodeURIComponent(instance)}`, "application/json", 8192, bytes => {
|
|
91
|
-
try { return checkEffectPosters(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes))); } catch { return null; }
|
|
92
|
-
}, signal) : Promise.resolve(null);
|
|
93
|
-
},
|
|
94
|
-
image(instance, id, signal) {
|
|
95
|
-
return instanceOK(instance) && typeof id === "string" && ID.test(id) ? read(`/ui/effect-posters/${id}.png?instance=${encodeURIComponent(instance)}`, "image/png", 1048576, checkStaticEffectPoster, signal) : Promise.resolve(null);
|
|
96
|
-
}, dispose: stop,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/** #1629: static current-chat posters. No Worker, preview frame, events, profile write or personal-slot authority. */
|
|
2
|
-
import { createEffectPosterReader, checkEffectPosters } from "./effect-poster-reader.js";
|
|
3
|
-
import { createEffectGeometry, hostEffectRect } from "./effect-geometry.js";
|
|
4
|
-
import { stockEffectTrust } from "./effect-stock-trust.js";
|
|
5
|
-
import { effectStockSnapshot } from "./effect-stock-snapshot.js";
|
|
6
|
-
const decoding = new WeakMap();
|
|
7
|
-
export function createChatPosters({ window: host, name, element, current, observe, factories = {} } = {}) {
|
|
8
|
-
let dead = false, failed = false, generation = 0, allowed = false, metadata = null, request = null, queued = false, raf = null;
|
|
9
|
-
let root = null, geometry = null, reader = null, mutation = null, resize = null, offAuth = null;
|
|
10
|
-
const rows = new Map(), offs = [];
|
|
11
|
-
const alive = () => !dead && current() === true && element?.isConnected === true && element.ownerDocument === host.document && host.document.body.dataset.mode === "full" && !host.AgendAuth.sessionEnded();
|
|
12
|
-
const make = (key, fallback) => factories[key] ?? fallback;
|
|
13
|
-
const gate = () => {
|
|
14
|
-
try {
|
|
15
|
-
const x = observe();
|
|
16
|
-
if (!alive() || x.shown !== true || x.fallback !== true || host.document.visibilityState !== "visible") return null;
|
|
17
|
-
const rect = hostEffectRect(element), proof = effectStockSnapshot(make("stock", stockEffectTrust)(host, { frameRoot: root, ownerCss: x.ownerCss }));
|
|
18
|
-
if (!rect || !proof || rect.x + rect.width <= 0 || rect.y + rect.height <= 0 || rect.x >= host.innerWidth || rect.y >= host.innerHeight) return null;
|
|
19
|
-
return { rect, proof };
|
|
20
|
-
} catch { return null; }
|
|
21
|
-
};
|
|
22
|
-
function clear() {
|
|
23
|
-
for (const row of rows.values()) {
|
|
24
|
-
row.controller.abort(); row.binding?.remove?.(); row.image.remove();
|
|
25
|
-
if (row.url) host.URL.revokeObjectURL(row.url);
|
|
26
|
-
}
|
|
27
|
-
rows.clear(); geometry?.hide?.();
|
|
28
|
-
}
|
|
29
|
-
function cancel() { generation++; request?.controller.abort(); clear(); metadata = null; }
|
|
30
|
-
function fail() { if (dead) return; failed = true; cancel(); }
|
|
31
|
-
function dispose() {
|
|
32
|
-
if (dead) return; dead = true; cancel(); reader?.dispose?.();
|
|
33
|
-
if (raf !== null) host.cancelAnimationFrame(raf); raf = null;
|
|
34
|
-
for (const off of offs) { try { off(); } catch { /* release all */ } }
|
|
35
|
-
offAuth?.(); mutation?.disconnect(); resize?.disconnect(); geometry?.dispose?.(); root?.remove();
|
|
36
|
-
}
|
|
37
|
-
const unavailable = () => ({ ok: false, dispose });
|
|
38
|
-
try {
|
|
39
|
-
if (!host?.document || typeof current !== "function" || typeof observe !== "function" || typeof name !== "string" || !name.length || name.length > 128 || host.document.body.dataset.mode !== "full" ||
|
|
40
|
-
![host.MutationObserver, host.ResizeObserver, host.requestAnimationFrame, host.cancelAnimationFrame, host.URL?.createObjectURL, host.URL?.revokeObjectURL, host.Blob, host.AbortController, host.setTimeout, host.clearTimeout, host.performance?.now, host.AgendAuth?.onSessionEnd, host.AgendAuth?.sessionEnded].every(x => typeof x === "function")) return unavailable();
|
|
41
|
-
offAuth = host.AgendAuth.onSessionEnd(dispose);
|
|
42
|
-
if (!alive()) { dispose(); return unavailable(); }
|
|
43
|
-
root = host.document.createElement("div"); root.className = "agend-effect-layer"; host.document.body.append(root);
|
|
44
|
-
geometry = make("geometry", createEffectGeometry)({ window: host }); if (!geometry.ok) throw Error("geometry unavailable");
|
|
45
|
-
reader = make("reader", createEffectPosterReader)({ window: host, current: alive }); if (!reader.ok) throw Error("reader unavailable");
|
|
46
|
-
if (!decoding.has(host)) decoding.set(host, new Set());
|
|
47
|
-
mutation = new host.MutationObserver(invalidate); mutation.observe(host.document.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });
|
|
48
|
-
resize = new host.ResizeObserver(invalidate); resize.observe(host.document.documentElement); resize.observe(element);
|
|
49
|
-
for (const type of ["scroll", "resize", "orientationchange"]) { host.addEventListener(type, invalidate, true); offs.push(() => host.removeEventListener(type, invalidate, true)); }
|
|
50
|
-
const visibility = () => { if (host.document.visibilityState !== "visible") { cancel(); allowed = false; } invalidate(); };
|
|
51
|
-
host.document.addEventListener("visibilitychange", visibility); offs.push(() => host.document.removeEventListener("visibilitychange", visibility));
|
|
52
|
-
host.addEventListener("pagehide", dispose); offs.push(() => host.removeEventListener("pagehide", dispose));
|
|
53
|
-
} catch { dispose(); return unavailable(); }
|
|
54
|
-
function schedule() { if (alive() && raf === null) raf = host.requestAnimationFrame(() => { raf = null; sync(); }); }
|
|
55
|
-
function invalidate() { if (dead) return; geometry.hide(); schedule(); }
|
|
56
|
-
function valid(mine) { return alive() && generation === mine && !failed && !!gate(); }
|
|
57
|
-
async function loadImage(row, mine) {
|
|
58
|
-
try {
|
|
59
|
-
const bytes = await reader.image(name, row.id, row.controller.signal);
|
|
60
|
-
if (!valid(mine) || rows.get(row.id) !== row) return;
|
|
61
|
-
if (!bytes) { fail(); return; }
|
|
62
|
-
const pending = decoding.get(host); if (pending.size >= 2) { fail(); return; }
|
|
63
|
-
const token = {}; pending.add(token);
|
|
64
|
-
let timer = null, aborted = false;
|
|
65
|
-
const abort = () => { aborted = true; row.controller.abort(); if (rows.get(row.id) === row) fail(); };
|
|
66
|
-
try {
|
|
67
|
-
const began = host.performance.now();
|
|
68
|
-
if (!Number.isFinite(began) || began < 0) { fail(); return; }
|
|
69
|
-
timer = host.setTimeout(abort, 10000);
|
|
70
|
-
row.url = host.URL.createObjectURL(new host.Blob([bytes], { type: "image/png" }));
|
|
71
|
-
row.image.src = row.url;
|
|
72
|
-
await row.image.decode();
|
|
73
|
-
if (aborted || !valid(mine) || rows.get(row.id) !== row) return;
|
|
74
|
-
const ended = host.performance.now();
|
|
75
|
-
if (!Number.isFinite(ended) || ended < began || ended - began >= 10000) { fail(); return; }
|
|
76
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
77
|
-
if (row.image.naturalWidth !== view.getUint32(16) || row.image.naturalHeight !== view.getUint32(20)) { fail(); return; }
|
|
78
|
-
row.ready = true; sync();
|
|
79
|
-
} finally { host.clearTimeout(timer); pending.delete(token); }
|
|
80
|
-
} catch { if (alive() && generation === mine) fail(); }
|
|
81
|
-
}
|
|
82
|
-
async function read() {
|
|
83
|
-
const op = { controller: new host.AbortController(), generation }; request = op;
|
|
84
|
-
try {
|
|
85
|
-
const result = await reader.summary(name, op.controller.signal);
|
|
86
|
-
if (!valid(op.generation) || request !== op) return;
|
|
87
|
-
const checked = checkEffectPosters(result);
|
|
88
|
-
if (!checked) { fail(); return; }
|
|
89
|
-
metadata = checked;
|
|
90
|
-
for (const p of checked.posters) {
|
|
91
|
-
const image = host.document.createElement("img"); image.className = "agend-effect-poster"; image.setAttribute("alt", ""); image.setAttribute("aria-hidden", "true"); image.decoding = "async";
|
|
92
|
-
root.append(image);
|
|
93
|
-
const binding = geometry.bind(image, { layer: "behind", by: "agent" });
|
|
94
|
-
if (!binding.ok || typeof image.decode !== "function") { binding.remove?.(); image.remove(); fail(); return; }
|
|
95
|
-
const row = { id: p.id, image, binding, controller: new host.AbortController(), url: null, ready: false }; rows.set(p.id, row);
|
|
96
|
-
// The IMG is proved hidden synchronously before any source is assigned or control yields.
|
|
97
|
-
void loadImage(row, op.generation);
|
|
98
|
-
}
|
|
99
|
-
} catch { if (alive() && generation === op.generation) fail(); }
|
|
100
|
-
finally { if (request === op) request = null; if (queued && alive()) { queued = false; sync(); } }
|
|
101
|
-
}
|
|
102
|
-
function sync() {
|
|
103
|
-
if (!alive()) { dispose(); return; }
|
|
104
|
-
geometry.hide();
|
|
105
|
-
const evidence = gate();
|
|
106
|
-
if (!evidence) { if (allowed) cancel(); allowed = false; return; }
|
|
107
|
-
allowed = true;
|
|
108
|
-
if (failed) return;
|
|
109
|
-
for (const row of rows.values()) if (row.ready && !row.binding.sync(evidence.rect, evidence.proof)) { fail(); return; }
|
|
110
|
-
if (!metadata && !request) void read();
|
|
111
|
-
if (rows.size) schedule();
|
|
112
|
-
}
|
|
113
|
-
return { ok: true, sync,
|
|
114
|
-
changed() { if (!alive()) return; cancel(); failed = false; if (request) queued = true; else sync(); },
|
|
115
|
-
dispose, snapshot: () => ({ failed, count: rows.size, ready: [...rows.values()].filter(r => r.ready).length }),
|
|
116
|
-
};
|
|
117
|
-
}
|