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