cbrowser 20.2.2 → 21.0.0
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/README.md +2 -2
- package/dist/analysis/accessibility-empathy.d.ts +29 -1
- package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +69 -35
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +11 -7
- package/dist/browser.js.map +1 -1
- package/dist/cif-score.d.ts +56 -5
- package/dist/cif-score.d.ts.map +1 -1
- package/dist/cif-score.js +595 -346
- package/dist/cif-score.js.map +1 -1
- package/dist/cli.js +10 -8
- package/dist/cli.js.map +1 -1
- package/dist/cognitive/default-traits.d.ts +14 -0
- package/dist/cognitive/default-traits.d.ts.map +1 -0
- package/dist/cognitive/default-traits.js +36 -0
- package/dist/cognitive/default-traits.js.map +1 -0
- package/dist/cognitive/emotions.d.ts.map +1 -1
- package/dist/cognitive/emotions.js +104 -174
- package/dist/cognitive/emotions.js.map +1 -1
- package/dist/cognitive/index.d.ts +28 -0
- package/dist/cognitive/index.d.ts.map +1 -1
- package/dist/cognitive/index.js +353 -287
- package/dist/cognitive/index.js.map +1 -1
- package/dist/cognitive/temporal.d.ts +0 -24
- package/dist/cognitive/temporal.d.ts.map +1 -1
- package/dist/cognitive/temporal.js +45 -35
- package/dist/cognitive/temporal.js.map +1 -1
- package/dist/data/ctc-reference-v2-2026-10-24.d.ts +16 -0
- package/dist/data/ctc-reference-v2-2026-10-24.d.ts.map +1 -0
- package/dist/data/ctc-reference-v2-2026-10-24.js +1004 -0
- package/dist/data/ctc-reference-v2-2026-10-24.js.map +1 -0
- package/dist/ideal-profiles-gap.d.ts +1 -0
- package/dist/ideal-profiles-gap.d.ts.map +1 -1
- package/dist/ideal-profiles-gap.js +9 -1
- package/dist/ideal-profiles-gap.js.map +1 -1
- package/dist/ideal-profiles.d.ts +8 -0
- package/dist/ideal-profiles.d.ts.map +1 -1
- package/dist/ideal-profiles.js +28 -24
- package/dist/ideal-profiles.js.map +1 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +7 -8
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-tools/account-personas.d.ts.map +1 -1
- package/dist/mcp-tools/account-personas.js +3 -1
- package/dist/mcp-tools/account-personas.js.map +1 -1
- package/dist/mcp-tools/base/answer-provenance.d.ts +28 -0
- package/dist/mcp-tools/base/answer-provenance.d.ts.map +1 -0
- package/dist/mcp-tools/base/answer-provenance.js +32 -0
- package/dist/mcp-tools/base/answer-provenance.js.map +1 -0
- package/dist/mcp-tools/base/audit-tools.d.ts +0 -3
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +152 -84
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/capture-tools.js +1 -1
- package/dist/mcp-tools/base/capture-tools.js.map +1 -1
- package/dist/mcp-tools/base/chain-values.d.ts +38 -0
- package/dist/mcp-tools/base/chain-values.d.ts.map +1 -0
- package/dist/mcp-tools/base/chain-values.js +85 -0
- package/dist/mcp-tools/base/chain-values.js.map +1 -0
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +126 -120
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/gif-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/gif-tools.js +23 -5
- package/dist/mcp-tools/base/gif-tools.js.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.js +229 -172
- package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
- package/dist/mcp-tools/base/site-familiarity.d.ts +31 -0
- package/dist/mcp-tools/base/site-familiarity.d.ts.map +1 -0
- package/dist/mcp-tools/base/site-familiarity.js +30 -0
- package/dist/mcp-tools/base/site-familiarity.js.map +1 -0
- package/dist/mcp-tools/base/site-knowledge-tools.js +1 -1
- package/dist/mcp-tools/base/site-knowledge-tools.js.map +1 -1
- package/dist/mcp-tools/base/story-narrative.d.ts +50 -0
- package/dist/mcp-tools/base/story-narrative.d.ts.map +1 -0
- package/dist/mcp-tools/base/story-narrative.js +88 -0
- package/dist/mcp-tools/base/story-narrative.js.map +1 -0
- package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/values-tools.js +19 -5
- package/dist/mcp-tools/base/values-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts +5 -5
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +38 -11
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/persona-creation-tools.js +6 -6
- package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
- package/dist/mcp-tools/persona-lifecycle-tools.d.ts.map +1 -1
- package/dist/mcp-tools/persona-lifecycle-tools.js +5 -1
- package/dist/mcp-tools/persona-lifecycle-tools.js.map +1 -1
- package/dist/mcp-tools/tier-gate.js +1 -1
- package/dist/mcp-tools/tool-result-saver.d.ts +7 -0
- package/dist/mcp-tools/tool-result-saver.d.ts.map +1 -1
- package/dist/mcp-tools/tool-result-saver.js +11 -0
- package/dist/mcp-tools/tool-result-saver.js.map +1 -1
- package/dist/mcp-tools/trait-provenance.d.ts +109 -0
- package/dist/mcp-tools/trait-provenance.d.ts.map +1 -0
- package/dist/mcp-tools/trait-provenance.js +187 -0
- package/dist/mcp-tools/trait-provenance.js.map +1 -0
- package/dist/mcp-tools/ui-resources.d.ts +6 -7
- package/dist/mcp-tools/ui-resources.d.ts.map +1 -1
- package/dist/mcp-tools/ui-resources.js +162 -12
- package/dist/mcp-tools/ui-resources.js.map +1 -1
- package/dist/mcp-tools/widget-kit.d.ts +129 -3
- package/dist/mcp-tools/widget-kit.d.ts.map +1 -1
- package/dist/mcp-tools/widget-kit.js +507 -28
- package/dist/mcp-tools/widget-kit.js.map +1 -1
- package/dist/performance/metrics.d.ts.map +1 -1
- package/dist/performance/metrics.js +4 -1
- package/dist/performance/metrics.js.map +1 -1
- package/dist/persona-questionnaire.js +1 -1
- package/dist/persona-questionnaire.js.map +1 -1
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +133 -82
- package/dist/personas.js.map +1 -1
- package/dist/recording/engine.d.ts.map +1 -1
- package/dist/recording/engine.js +5 -2
- package/dist/recording/engine.js.map +1 -1
- package/dist/recording/saliency-overlay.d.ts +8 -3
- package/dist/recording/saliency-overlay.d.ts.map +1 -1
- package/dist/recording/saliency-overlay.js +14 -5
- package/dist/recording/saliency-overlay.js.map +1 -1
- package/dist/recording/types.js +1 -1
- package/dist/testing/nl-test-suite.d.ts.map +1 -1
- package/dist/testing/nl-test-suite.js +2 -1
- package/dist/testing/nl-test-suite.js.map +1 -1
- package/dist/trait-reference.d.ts +19 -2
- package/dist/trait-reference.d.ts.map +1 -1
- package/dist/trait-reference.js +19 -2
- package/dist/trait-reference.js.map +1 -1
- package/dist/types.d.ts +32 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +62 -52
- package/dist/types.js.map +1 -1
- package/dist/values/persona-completeness.js +1 -1
- package/dist/values/persona-completeness.js.map +1 -1
- package/dist/visual/ab-comparison.d.ts.map +1 -1
- package/dist/visual/ab-comparison.js +4 -1
- package/dist/visual/ab-comparison.js.map +1 -1
- package/dist/visual/attention-quality.d.ts +1 -1
- package/dist/visual/attention-quality.js +1 -1
- package/dist/visual/attention-transport.d.ts +74 -10
- package/dist/visual/attention-transport.d.ts.map +1 -1
- package/dist/visual/attention-transport.js +353 -193
- package/dist/visual/attention-transport.js.map +1 -1
- package/dist/visual/cognitive-models.d.ts +77 -55
- package/dist/visual/cognitive-models.d.ts.map +1 -1
- package/dist/visual/cognitive-models.js +231 -104
- package/dist/visual/cognitive-models.js.map +1 -1
- package/dist/visual/cognitive-transport-chain.d.ts +328 -30
- package/dist/visual/cognitive-transport-chain.d.ts.map +1 -1
- package/dist/visual/cognitive-transport-chain.js +576 -353
- package/dist/visual/cognitive-transport-chain.js.map +1 -1
- package/dist/visual/cognitive-transport.d.ts +446 -13
- package/dist/visual/cognitive-transport.d.ts.map +1 -1
- package/dist/visual/cognitive-transport.js +1085 -245
- package/dist/visual/cognitive-transport.js.map +1 -1
- package/dist/visual/cross-browser.d.ts.map +1 -1
- package/dist/visual/cross-browser.js +8 -6
- package/dist/visual/cross-browser.js.map +1 -1
- package/dist/visual/ctc-bands.d.ts +91 -0
- package/dist/visual/ctc-bands.d.ts.map +1 -0
- package/dist/visual/ctc-bands.js +117 -0
- package/dist/visual/ctc-bands.js.map +1 -0
- package/dist/visual/ctc-reference.d.ts +287 -0
- package/dist/visual/ctc-reference.d.ts.map +1 -0
- package/dist/visual/ctc-reference.js +376 -0
- package/dist/visual/ctc-reference.js.map +1 -0
- package/dist/visual/demand-laws.d.ts +145 -0
- package/dist/visual/demand-laws.d.ts.map +1 -0
- package/dist/visual/demand-laws.js +220 -0
- package/dist/visual/demand-laws.js.map +1 -0
- package/dist/visual/distance-metrics.d.ts +23 -1
- package/dist/visual/distance-metrics.d.ts.map +1 -1
- package/dist/visual/distance-metrics.js +49 -18
- package/dist/visual/distance-metrics.js.map +1 -1
- package/dist/visual/dro-bound.d.ts +220 -0
- package/dist/visual/dro-bound.d.ts.map +1 -0
- package/dist/visual/dro-bound.js +341 -0
- package/dist/visual/dro-bound.js.map +1 -0
- package/dist/visual/exact-w1.d.ts +60 -0
- package/dist/visual/exact-w1.d.ts.map +1 -0
- package/dist/visual/exact-w1.js +247 -0
- package/dist/visual/exact-w1.js.map +1 -0
- package/dist/visual/human-input.d.ts.map +1 -1
- package/dist/visual/human-input.js +45 -41
- package/dist/visual/human-input.js.map +1 -1
- package/dist/visual/index.d.ts +2 -0
- package/dist/visual/index.d.ts.map +1 -1
- package/dist/visual/index.js +2 -0
- package/dist/visual/index.js.map +1 -1
- package/dist/visual/law-times-report.d.ts +49 -0
- package/dist/visual/law-times-report.d.ts.map +1 -0
- package/dist/visual/law-times-report.js +38 -0
- package/dist/visual/law-times-report.js.map +1 -0
- package/dist/visual/model-provenance.d.ts +151 -0
- package/dist/visual/model-provenance.d.ts.map +1 -0
- package/dist/visual/model-provenance.js +334 -0
- package/dist/visual/model-provenance.js.map +1 -0
- package/dist/visual/normal-cdf.d.ts +14 -0
- package/dist/visual/normal-cdf.d.ts.map +1 -0
- package/dist/visual/normal-cdf.js +26 -0
- package/dist/visual/normal-cdf.js.map +1 -0
- package/dist/visual/perceptual-transport.d.ts +28 -4
- package/dist/visual/perceptual-transport.d.ts.map +1 -1
- package/dist/visual/perceptual-transport.js +66 -32
- package/dist/visual/perceptual-transport.js.map +1 -1
- package/dist/visual/provenance/attention.d.ts +55 -0
- package/dist/visual/provenance/attention.d.ts.map +1 -0
- package/dist/visual/provenance/attention.js +188 -0
- package/dist/visual/provenance/attention.js.map +1 -0
- package/dist/visual/provenance/chain.d.ts +41 -0
- package/dist/visual/provenance/chain.d.ts.map +1 -0
- package/dist/visual/provenance/chain.js +66 -0
- package/dist/visual/provenance/chain.js.map +1 -0
- package/dist/visual/provenance/cif.d.ts +42 -0
- package/dist/visual/provenance/cif.d.ts.map +1 -0
- package/dist/visual/provenance/cif.js +116 -0
- package/dist/visual/provenance/cif.js.map +1 -0
- package/dist/visual/provenance/ctc-reference.d.ts +36 -0
- package/dist/visual/provenance/ctc-reference.d.ts.map +1 -0
- package/dist/visual/provenance/ctc-reference.js +96 -0
- package/dist/visual/provenance/ctc-reference.js.map +1 -0
- package/dist/visual/provenance/distance.d.ts +10 -0
- package/dist/visual/provenance/distance.d.ts.map +1 -0
- package/dist/visual/provenance/distance.js +72 -0
- package/dist/visual/provenance/distance.js.map +1 -0
- package/dist/visual/provenance/dro.d.ts +10 -0
- package/dist/visual/provenance/dro.d.ts.map +1 -0
- package/dist/visual/provenance/dro.js +2 -0
- package/dist/visual/provenance/dro.js.map +1 -0
- package/dist/visual/provenance/emotion.d.ts +26 -0
- package/dist/visual/provenance/emotion.d.ts.map +1 -0
- package/dist/visual/provenance/emotion.js +99 -0
- package/dist/visual/provenance/emotion.js.map +1 -0
- package/dist/visual/provenance/human-input.d.ts +19 -0
- package/dist/visual/provenance/human-input.d.ts.map +1 -0
- package/dist/visual/provenance/human-input.js +84 -0
- package/dist/visual/provenance/human-input.js.map +1 -0
- package/dist/visual/provenance/journey-group.d.ts +15 -0
- package/dist/visual/provenance/journey-group.d.ts.map +1 -0
- package/dist/visual/provenance/journey-group.js +15 -0
- package/dist/visual/provenance/journey-group.js.map +1 -0
- package/dist/visual/provenance/journey-mcp.d.ts +24 -0
- package/dist/visual/provenance/journey-mcp.d.ts.map +1 -0
- package/dist/visual/provenance/journey-mcp.js +54 -0
- package/dist/visual/provenance/journey-mcp.js.map +1 -0
- package/dist/visual/provenance/journey.d.ts +34 -0
- package/dist/visual/provenance/journey.d.ts.map +1 -0
- package/dist/visual/provenance/journey.js +184 -0
- package/dist/visual/provenance/journey.js.map +1 -0
- package/dist/visual/provenance/laws.d.ts +38 -0
- package/dist/visual/provenance/laws.d.ts.map +1 -0
- package/dist/visual/provenance/laws.js +40 -0
- package/dist/visual/provenance/laws.js.map +1 -0
- package/dist/visual/provenance/page-metrics.d.ts +42 -0
- package/dist/visual/provenance/page-metrics.d.ts.map +1 -0
- package/dist/visual/provenance/page-metrics.js +79 -0
- package/dist/visual/provenance/page-metrics.js.map +1 -0
- package/dist/visual/provenance/perceptual.d.ts +63 -0
- package/dist/visual/provenance/perceptual.d.ts.map +1 -0
- package/dist/visual/provenance/perceptual.js +115 -0
- package/dist/visual/provenance/perceptual.js.map +1 -0
- package/dist/visual/provenance/persona-rates.d.ts +84 -0
- package/dist/visual/provenance/persona-rates.d.ts.map +1 -0
- package/dist/visual/provenance/persona-rates.js +112 -0
- package/dist/visual/provenance/persona-rates.js.map +1 -0
- package/dist/visual/provenance/personas.d.ts +122 -0
- package/dist/visual/provenance/personas.d.ts.map +1 -0
- package/dist/visual/provenance/personas.js +298 -0
- package/dist/visual/provenance/personas.js.map +1 -0
- package/dist/visual/provenance/profiles.d.ts +39 -0
- package/dist/visual/provenance/profiles.d.ts.map +1 -0
- package/dist/visual/provenance/profiles.js +37 -0
- package/dist/visual/provenance/profiles.js.map +1 -0
- package/dist/visual/provenance/temporal.d.ts +21 -0
- package/dist/visual/provenance/temporal.d.ts.map +1 -0
- package/dist/visual/provenance/temporal.js +56 -0
- package/dist/visual/provenance/temporal.js.map +1 -0
- package/dist/visual/provenance/visual-distance.d.ts +22 -0
- package/dist/visual/provenance/visual-distance.d.ts.map +1 -0
- package/dist/visual/provenance/visual-distance.js +30 -0
- package/dist/visual/provenance/visual-distance.js.map +1 -0
- package/dist/visual/regression.d.ts.map +1 -1
- package/dist/visual/regression.js +14 -9
- package/dist/visual/regression.js.map +1 -1
- package/dist/visual/responsive.d.ts.map +1 -1
- package/dist/visual/responsive.js +4 -1
- package/dist/visual/responsive.js.map +1 -1
- package/dist/visual/scope-prose.js +2 -2
- package/dist/visual/scope-prose.js.map +1 -1
- package/dist/visual/seeded-random.d.ts +16 -0
- package/dist/visual/seeded-random.d.ts.map +1 -0
- package/dist/visual/seeded-random.js +25 -0
- package/dist/visual/seeded-random.js.map +1 -0
- package/dist/visual/stored-accessibility-traits.d.ts +27 -0
- package/dist/visual/stored-accessibility-traits.d.ts.map +1 -0
- package/dist/visual/stored-accessibility-traits.js +35 -0
- package/dist/visual/stored-accessibility-traits.js.map +1 -0
- package/package.json +1 -1
|
@@ -8,8 +8,15 @@ import { z } from "zod";
|
|
|
8
8
|
import { barrierWeightFor, weightedSeverity, weightKeyFor } from "../../visual/perceptual-transport.js";
|
|
9
9
|
import { writeArtifact } from "../../artifact-store.js";
|
|
10
10
|
import { buildContentWithPreviews } from "../inline-image.js";
|
|
11
|
-
import { bridgeCapacityTraits } from "../../visual/cognitive-models.js";
|
|
11
|
+
import { bridgeCapacityTraits, personaRateContext } from "../../visual/cognitive-models.js";
|
|
12
|
+
import { lawTimesReport } from "../../visual/law-times-report.js";
|
|
13
|
+
import { resolveSiteFamiliarity as resolveRunFamiliarity } from "./site-familiarity.js";
|
|
12
14
|
import { refuseHostedUrl } from "../hosted-inputs.js";
|
|
15
|
+
import { activeProvenance, collectPersonaReads, isolatedProvenanceAsync, personaRateProvenance, ATTENTION_PROVENANCE_TEXT } from "../../visual/model-provenance.js";
|
|
16
|
+
import { ctcRank } from "../../visual/ctc-bands.js";
|
|
17
|
+
import { recordedAnswer } from "./answer-provenance.js";
|
|
18
|
+
import { resolveChainValues, valueModulationReport } from "./chain-values.js";
|
|
19
|
+
import { storyEffortSection, storyVerdict } from "./story-narrative.js";
|
|
13
20
|
import { runAgentReadyAudit, pageAgentReadyFindings, runCompetitiveBenchmark, runEmpathyAudit, runWebMCPReadyAudit, } from "../../analysis/index.js";
|
|
14
21
|
import { listAccessibilityPersonas } from "../../personas.js";
|
|
15
22
|
import { barrierElementCount, countAffectedElements } from "../../analysis/accessibility-empathy.js";
|
|
@@ -17,7 +24,7 @@ import { getDefaultConfig, effectiveDataDir } from "../../config.js";
|
|
|
17
24
|
import { describeRenderTarget } from "../../devices.js";
|
|
18
25
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
19
26
|
import { join } from "path";
|
|
20
|
-
import { makeTempDir } from "../../temp-files.js";
|
|
27
|
+
import { makeTempDir, uniqueStamp } from "../../temp-files.js";
|
|
21
28
|
/**
|
|
22
29
|
* Persist an audit screenshot and return its path.
|
|
23
30
|
*
|
|
@@ -39,7 +46,9 @@ export function writeAuditScreenshot(base64, persona) {
|
|
|
39
46
|
const dir = join(effectiveDataDir(), "audit-screenshots");
|
|
40
47
|
if (!existsSync(dir))
|
|
41
48
|
mkdirSync(dir, { recursive: true });
|
|
42
|
-
|
|
49
|
+
// Time plus random bits: two audits of one persona in the same
|
|
50
|
+
// millisecond wrote one file and both returned its path.
|
|
51
|
+
const path = join(dir, `empathy-${safe}-${uniqueStamp()}.png`);
|
|
43
52
|
writeFileSync(path, Buffer.from(base64, "base64"));
|
|
44
53
|
out.path = path;
|
|
45
54
|
}
|
|
@@ -66,6 +75,10 @@ export function writeAuditScreenshot(base64, persona) {
|
|
|
66
75
|
/**
|
|
67
76
|
* Register audit tools (4 tools + site_cognitive_assessment + visual_cognitive_story)
|
|
68
77
|
*/
|
|
78
|
+
// One shared helper for every attention caller (ISC-48, ISC-48.1): the persona
|
|
79
|
+
// record's own accessibilityTraits, only the fields it sets. It used to be
|
|
80
|
+
// copied here and in the other tool file.
|
|
81
|
+
import { personaStoredAccessibilityTraits as personaAccessibilityTraits } from "../../visual/stored-accessibility-traits.js";
|
|
69
82
|
export function registerAuditTools(server, context) {
|
|
70
83
|
const getBrowserByToken = context?.getBrowserByToken;
|
|
71
84
|
server.registerTool("agent_ready_audit", {
|
|
@@ -204,17 +217,16 @@ export function registerAuditTools(server, context) {
|
|
|
204
217
|
// ── Site Cognitive Assessment (Composite Pipeline) ──
|
|
205
218
|
server.registerTool("site_cognitive_assessment", {
|
|
206
219
|
title: "Site Cognitive Assessment",
|
|
207
|
-
description: "Three-gate pipeline: (1) bot-detection probe, (2) agent-ready qualification, (3) per-persona cognitive effort analysis.
|
|
220
|
+
description: "Three-gate pipeline: (1) bot-detection probe, (2) agent-ready qualification, (3) per-persona cognitive effort analysis. Site familiarity is per run: pass siteFamiliarity (0 first visit, 1 regular user) or every persona is scored as a first visit. No persona carries a familiarity of its own, and site knowledge from crawling is reported but does not change the score.",
|
|
208
221
|
inputSchema: {
|
|
209
222
|
url: z.string().url().describe("URL to assess"),
|
|
210
223
|
personas: z.string().optional().default("first-timer,cognitive-adhd").describe("Comma-separated persona names (default: first-timer,cognitive-adhd)"),
|
|
211
224
|
threshold: z.number().optional().default(60).describe("Minimum agent-ready score to proceed to persona analysis (default: 60)"),
|
|
212
225
|
// Per RUN, not per persona. The same person is familiar with one site and new
|
|
213
|
-
// to another, so this has no value until a site is named
|
|
214
|
-
//
|
|
215
|
-
// the fallback when a caller does not pass one. (2026-08-01)
|
|
226
|
+
// to another, so this has no value until a site is named. Omitted, the run
|
|
227
|
+
// scores a first visit (0.0); see site-familiarity.ts. (2026-08-01, 21.0)
|
|
216
228
|
siteFamiliarity: z.number().min(0).max(1).optional()
|
|
217
|
-
.describe("How familiar these personas are with THIS site: 0 first visit, 1 regular user.
|
|
229
|
+
.describe("How familiar these personas are with THIS site: 0 first visit, 1 regular user. Omitted, every persona is scored as a first visit (0). Familiarity is a persona-site pair, not a disposition, so set it per run; site knowledge does not supply it."),
|
|
218
230
|
userLocation: z.string().optional().describe("User's approximate location (e.g., 'Denver, Colorado, US') — for geo-aware content expectations"),
|
|
219
231
|
userTimezone: z.string().optional().describe("User's timezone (e.g., 'America/Denver') — for time-sensitive content evaluation"),
|
|
220
232
|
userLanguage: z.string().optional().describe("User's expected language (e.g., 'en-US') — for readability calibration"),
|
|
@@ -233,7 +245,9 @@ export function registerAuditTools(server, context) {
|
|
|
233
245
|
idempotentHint: false,
|
|
234
246
|
openWorldHint: true,
|
|
235
247
|
},
|
|
236
|
-
}, async ({ url, personas, threshold, siteFamiliarity, userLocation, userTimezone, userLanguage, proxy, geoRegion, device, waitAfterLoad, waitForSelector, scope, _browserToken }) => {
|
|
248
|
+
}, recordedAnswer(async ({ url, personas, threshold, siteFamiliarity, userLocation, userTimezone, userLanguage, proxy, geoRegion, device, waitAfterLoad, waitForSelector, scope, _browserToken }) => {
|
|
249
|
+
// recordedAnswer: one provenance recording per call, across every await,
|
|
250
|
+
// reported as modelProvenance once gate 3 has scored (answer-provenance.ts).
|
|
237
251
|
// Hosted: http(s) only (sec-paths, 2026-10-10; see hosted-inputs.ts).
|
|
238
252
|
const hostedRefusal = refuseHostedUrl("site_cognitive_assessment", { url });
|
|
239
253
|
if (hostedRefusal)
|
|
@@ -492,6 +506,10 @@ export function registerAuditTools(server, context) {
|
|
|
492
506
|
}
|
|
493
507
|
// ── Gate 3: Per-Persona Cognitive Effort ──
|
|
494
508
|
const personaResults = {};
|
|
509
|
+
// Each scored persona's rank sentence, for the recommendations below.
|
|
510
|
+
const rankSentences = {};
|
|
511
|
+
// Each scored persona's motor and reading rates (personaRateProvenance).
|
|
512
|
+
const personaRates = {};
|
|
495
513
|
const { buildOTCognitiveProfile } = await import("../../visual/cognitive-transport.js");
|
|
496
514
|
const { computeDemandDistribution, computeSequentialCTC } = await import("../../visual/cognitive-transport-chain.js");
|
|
497
515
|
const { extractPageMetrics } = await import("../../visual/cognitive-transport.js");
|
|
@@ -518,7 +536,6 @@ export function registerAuditTools(server, context) {
|
|
|
518
536
|
// extractPageMetrics runs its own lazy-load scroll walk at full_page, so
|
|
519
537
|
// forwarding the scope is the entire fix.
|
|
520
538
|
const pageMetrics = await extractPageMetrics(metricsPage, auditScope);
|
|
521
|
-
const demand = computeDemandDistribution(pageMetrics);
|
|
522
539
|
// Page understanding
|
|
523
540
|
let pageUnderstanding = null;
|
|
524
541
|
try {
|
|
@@ -536,8 +553,8 @@ export function registerAuditTools(server, context) {
|
|
|
536
553
|
};
|
|
537
554
|
}
|
|
538
555
|
catch { }
|
|
539
|
-
//
|
|
540
|
-
//
|
|
556
|
+
// Site knowledge is looked up only to report it. siteFamiliarity is the
|
|
557
|
+
// caller's value or 0.0 (first visit), whatever this install has crawled.
|
|
541
558
|
let hasSiteKnowledge = false;
|
|
542
559
|
try {
|
|
543
560
|
const domain = new URL(url).hostname;
|
|
@@ -554,26 +571,13 @@ export function registerAuditTools(server, context) {
|
|
|
554
571
|
resolvePersonaOrThrow(personaName);
|
|
555
572
|
const personaObj = existingPersona || createCognitivePersona(personaName, personaName, {});
|
|
556
573
|
const traits = { ...(personaObj.cognitiveTraits || {}) };
|
|
557
|
-
// Familiarity is
|
|
558
|
-
//
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
traits.siteFamiliarity = siteFamiliarity;
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
// v18.61.0: without an explicit value it stays a binary gate on
|
|
568
|
-
// whether this install has crawled the site at all.
|
|
569
|
-
if (hasSiteKnowledge) {
|
|
570
|
-
if (reqFam > 0.5)
|
|
571
|
-
traits.siteFamiliarity = 1.0;
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
traits.siteFamiliarity = 0.0;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
574
|
+
// Familiarity is the visit's: the caller's value for THIS run, else
|
|
575
|
+
// 0.0, the same for every persona. Since 21.0 (Stage C) it is
|
|
576
|
+
// Cockburn et al.'s expertise e on the choosing time, handed to the
|
|
577
|
+
// demand, not a persona trait. Through 20.x a site-knowledge branch
|
|
578
|
+
// left the trait unset, which the chain read as 0.5 (site-familiarity.ts).
|
|
579
|
+
const { resolveSiteFamiliarity } = await import("./site-familiarity.js");
|
|
580
|
+
const familiarity = resolveSiteFamiliarity(siteFamiliarity, hasSiteKnowledge);
|
|
577
581
|
// Bridge the derived capacities before building the profile. This was
|
|
578
582
|
// MISSING here and present in cognitive_effort, so the motor layer --
|
|
579
583
|
// whose sole trait is motorCapacity -- scored against traitValue's
|
|
@@ -589,11 +593,22 @@ export function registerAuditTools(server, context) {
|
|
|
589
593
|
// This moves more than motor. readability, readingAttention, totalCTC,
|
|
590
594
|
// bottleneck and abandonmentRisk all shift for every persona here, all
|
|
591
595
|
// converging on what cognitive_effort already reported.
|
|
592
|
-
bridgeCapacityTraits(personaName, traits, personaObj.accessibilityTraits);
|
|
596
|
+
personaRates[personaName] = personaRateProvenance(collectPersonaReads(() => bridgeCapacityTraits(personaName, traits, personaObj.accessibilityTraits, personaRateContext(personaObj))).ids);
|
|
593
597
|
const otProfile = buildOTCognitiveProfile(personaName, traits);
|
|
594
|
-
|
|
598
|
+
// Rebuilt for each persona from the same metrics, so every persona is
|
|
599
|
+
// scored against the same demand; this tool has no values option. The
|
|
600
|
+
// whole handler is one provenance recording, so the demand curves and
|
|
601
|
+
// the chain are counted with everything else the answer read.
|
|
602
|
+
const result = computeSequentialCTC(otProfile, computeDemandDistribution(pageMetrics, { siteFamiliarity: familiarity.value }), { asymmetric: true, interactions: true });
|
|
603
|
+
const rank = ctcRank(result);
|
|
604
|
+
rankSentences[personaName] = rank.sentence;
|
|
595
605
|
personaResults[personaName] = {
|
|
606
|
+
// A percentile of the real reference pages (21.x); `band` names its band.
|
|
596
607
|
totalCTC: Math.round(result.totalCTC * 1000) / 1000,
|
|
608
|
+
// Its 95% interval across bootstrap resamples of the reference pages.
|
|
609
|
+
totalCTCInterval: result.totalCTCInterval.map((x) => Math.round(x * 1000) / 1000),
|
|
610
|
+
band: rank.band,
|
|
611
|
+
referenceScope: result.referenceScope,
|
|
597
612
|
bottleneck: result.bottleneckLayer,
|
|
598
613
|
abandonmentRisk: Math.round(result.abandonmentRisk * 100) + "%",
|
|
599
614
|
deficit: Math.round(result.deficitCost * 1000) / 1000,
|
|
@@ -602,13 +617,9 @@ export function registerAuditTools(server, context) {
|
|
|
602
617
|
name: l.name,
|
|
603
618
|
cost: Math.round(l.transportCost * 1000) / 1000,
|
|
604
619
|
})),
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
...(!familiarityFromCaller && reqFam > 0.5 && !hasSiteKnowledge ? {
|
|
609
|
-
familiarityWarning: `siteFamiliarity downgraded from ${reqFam} to 0.0 — no site knowledge exists for this domain. Pass siteFamiliarity on this call to state it directly, or run page_understand first.`,
|
|
610
|
-
} : {}),
|
|
611
|
-
...(familiarityFromCaller ? { siteFamiliaritySource: "supplied for this run" } : {}),
|
|
620
|
+
siteFamiliarity: familiarity.value,
|
|
621
|
+
siteFamiliaritySource: familiarity.source,
|
|
622
|
+
siteKnowledgeAvailable: familiarity.siteKnowledgeAvailable,
|
|
612
623
|
};
|
|
613
624
|
}
|
|
614
625
|
catch (personaErr) {
|
|
@@ -616,6 +627,9 @@ export function registerAuditTools(server, context) {
|
|
|
616
627
|
}
|
|
617
628
|
}
|
|
618
629
|
response.gate3 = { status: "complete", personas: personaResults };
|
|
630
|
+
// Holds its place in the JSON; filled after the recommendations, which
|
|
631
|
+
// read the band constants, so the block covers the whole answer.
|
|
632
|
+
response.modelProvenance = undefined;
|
|
619
633
|
response.pageMetrics = {
|
|
620
634
|
informationDensity: Math.round(pageMetrics.informationDensity * 1000) / 1000,
|
|
621
635
|
visualComplexity: Math.round(pageMetrics.visualComplexity * 1000) / 1000,
|
|
@@ -628,6 +642,9 @@ export function registerAuditTools(server, context) {
|
|
|
628
642
|
longWordRatio: Math.round(pageMetrics.longWordRatio * 1000) / 1000,
|
|
629
643
|
technicalDensity: Math.round(pageMetrics.technicalDensity * 1000) / 1000,
|
|
630
644
|
scriptFamily: pageMetrics.scriptFamily || "alphabetic",
|
|
645
|
+
// Stage B: the four law times behind the motor, decision, procedure and reading
|
|
646
|
+
// demands; choosing at the run's siteFamiliarity (Stage C).
|
|
647
|
+
lawTimes: lawTimesReport(pageMetrics, resolveRunFamiliarity(siteFamiliarity, false).value),
|
|
631
648
|
// Every ratio above is 0-1 NORMALIZED, not a raw measurement.
|
|
632
649
|
// cognitive_effort's readability penalties report avg word length in
|
|
633
650
|
// raw CHARACTERS (e.g. 11.4), so the same name carried two scales
|
|
@@ -639,14 +656,24 @@ export function registerAuditTools(server, context) {
|
|
|
639
656
|
if (pageUnderstanding)
|
|
640
657
|
response.pageUnderstanding = pageUnderstanding;
|
|
641
658
|
// Generate recommendations
|
|
659
|
+
// The rank, not a bare totalCTC: "CTC=0.41" read as a cost on a scale
|
|
660
|
+
// it is no longer on (a percentile since 21.x), with no reference named.
|
|
642
661
|
const recommendations = [];
|
|
643
662
|
for (const [name, data] of Object.entries(personaResults)) {
|
|
644
663
|
const d = data;
|
|
645
664
|
if (d.bottleneck) {
|
|
646
|
-
recommendations.push(`${name}:
|
|
665
|
+
recommendations.push(`${name}: reduce ${d.bottleneck} complexity first, the costliest layer. ${rankSentences[name]}`);
|
|
647
666
|
}
|
|
648
667
|
}
|
|
649
668
|
response.recommendations = recommendations.slice(0, 5);
|
|
669
|
+
// Reported when at least one persona was scored, as before.
|
|
670
|
+
if (Object.keys(rankSentences).length > 0) {
|
|
671
|
+
response.modelProvenance = activeProvenance();
|
|
672
|
+
// Per scored persona, counted apart from the score's constants.
|
|
673
|
+
response.personaRateProvenance = Object.fromEntries(Object.keys(rankSentences).map((n) => [n, personaRates[n]]));
|
|
674
|
+
}
|
|
675
|
+
else
|
|
676
|
+
delete response.modelProvenance;
|
|
650
677
|
response.result = "COMPLETE";
|
|
651
678
|
response.duration = `${Date.now() - startTime}ms`;
|
|
652
679
|
// Auto-save to site dashboard
|
|
@@ -683,7 +710,7 @@ export function registerAuditTools(server, context) {
|
|
|
683
710
|
response.duration = `${Date.now() - startTime}ms`;
|
|
684
711
|
}
|
|
685
712
|
return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
|
|
686
|
-
});
|
|
713
|
+
}));
|
|
687
714
|
// ── Visual Cognitive Story ──
|
|
688
715
|
server.registerTool("visual_cognitive_story", {
|
|
689
716
|
title: "Visual Cognitive Story",
|
|
@@ -692,7 +719,7 @@ export function registerAuditTools(server, context) {
|
|
|
692
719
|
url: z.string().url().describe("URL to analyze"),
|
|
693
720
|
persona: z.string().optional().default("cognitive-adhd").describe("Persona name"),
|
|
694
721
|
device: z.string().optional().describe("Device to emulate: 'mobile', 'tablet', 'desktop' (1920x1080), or a specific device like 'iPhone 15', 'Pixel 7', 'iPad Pro 11'. Default: no emulation, at the configured viewport (1920x1080 unless CBROWSER_VIEWPORT_WIDTH/HEIGHT or the config file say otherwise); the response reports the device and viewport used. Ignored with _browserToken (the session keeps its own)."),
|
|
695
|
-
useValues: z.boolean().optional().default(false).describe("
|
|
722
|
+
useValues: z.boolean().optional().default(false).describe("Apply the persona's Schwartz values (opt-in). They change two things: the attention quality scoring (which CTAs and distractors the persona weights), and the effort summary, where they modulate the page's demand inside the chain through its registered coefficients (chain.valueModulation.*, hand-set hypotheses, counted in modelProvenance), exactly as cognitive_effort's useValues does. They do not change the attention heatmap. `valueModulation` in the response says whether values were found for the chain and which scores it used. Default: false."),
|
|
696
723
|
// Goal-directed attention is the largest single lever in the semantic
|
|
697
724
|
// layer: computeGoalRelevance boosts any element whose text matches the
|
|
698
725
|
// goal's keywords, which is what separates "where the eye lands on this
|
|
@@ -710,12 +737,15 @@ export function registerAuditTools(server, context) {
|
|
|
710
737
|
idempotentHint: false,
|
|
711
738
|
openWorldHint: true,
|
|
712
739
|
},
|
|
713
|
-
}, async ({ url, persona, device, useValues, goal, aiLayer, _browserToken }) => {
|
|
740
|
+
}, recordedAnswer(async ({ url, persona, device, useValues, goal, aiLayer, _browserToken }) => {
|
|
741
|
+
// recordedAnswer: one provenance recording per call, across every await,
|
|
742
|
+
// reported as modelProvenance (answer-provenance.ts).
|
|
714
743
|
// Hosted: http(s) only (sec-paths, 2026-10-10; see hosted-inputs.ts).
|
|
715
744
|
const hostedRefusal = refuseHostedUrl("visual_cognitive_story", { url });
|
|
716
745
|
if (hostedRefusal)
|
|
717
746
|
return hostedRefusal;
|
|
718
747
|
const startTime = Date.now();
|
|
748
|
+
const { writeFileSync, mkdirSync, existsSync } = await import("fs");
|
|
719
749
|
let ownsVcsBrowser = true;
|
|
720
750
|
// The screenshot's own temp dir, removed in the finally below: a
|
|
721
751
|
// clock-named file in the shared temp dir was read by concurrent calls.
|
|
@@ -772,6 +802,7 @@ export function registerAuditTools(server, context) {
|
|
|
772
802
|
? `the PNG at images.${name}`
|
|
773
803
|
: "not available (the artifact store was unavailable, so the full-resolution PNG was not saved)";
|
|
774
804
|
// ── 1. Attention Heatmap ──
|
|
805
|
+
let attentionProvenance;
|
|
775
806
|
let attentionData = null;
|
|
776
807
|
try {
|
|
777
808
|
const { analyzeAttention } = await import("../../visual/attention-transport.js");
|
|
@@ -782,12 +813,19 @@ export function registerAuditTools(server, context) {
|
|
|
782
813
|
// different model wearing the same name. (2026-07-29)
|
|
783
814
|
const { collectDomAttentionElements } = await import("../../visual/attention-quality.js");
|
|
784
815
|
const domAttentionElements = await collectDomAttentionElements(page).catch(() => []);
|
|
785
|
-
const
|
|
816
|
+
const a11y = await personaAccessibilityTraits(persona);
|
|
817
|
+
// Recorded apart from the score (attentionProvenance): the attention
|
|
818
|
+
// model reads fitted gaze calibration and published colour science,
|
|
819
|
+
// and counted in modelProvenance they made the effort score look
|
|
820
|
+
// partly fitted while the map does not feed it.
|
|
821
|
+
const { result: attnResult, provenance } = await isolatedProvenanceAsync(() => analyzeAttention(ssPath, persona, 4, undefined, domAttentionElements, goal, undefined, undefined, a11y), ATTENTION_PROVENANCE_TEXT);
|
|
822
|
+
attentionProvenance = provenance;
|
|
786
823
|
attentionData = {
|
|
787
824
|
entropy: attnResult.entropy,
|
|
788
825
|
concentration: attnResult.concentration,
|
|
789
826
|
hotspots: attnResult.saliencyMap?.hotspots || [],
|
|
790
827
|
saliencyMap: attnResult.saliencyMap,
|
|
828
|
+
perceptualProfile: attnResult.perceptualProfile,
|
|
791
829
|
};
|
|
792
830
|
if (attnResult.saliencyMap) {
|
|
793
831
|
const { generateHeatmapOverlay } = await import("../../visual/heatmap-overlay.js");
|
|
@@ -943,6 +981,14 @@ export function registerAuditTools(server, context) {
|
|
|
943
981
|
}
|
|
944
982
|
// ── 4. Cognitive Effort Summary ──
|
|
945
983
|
let effortData = null;
|
|
984
|
+
// Unrounded, for the narrative: a band edge must not move with display rounding.
|
|
985
|
+
let storyEffort = null;
|
|
986
|
+
// The ids the bridge read for this persona: personaRateProvenance below.
|
|
987
|
+
let storyPersonaRateIds = new Set();
|
|
988
|
+
// useValues reaches the chain here exactly as in cognitive_effort: the
|
|
989
|
+
// persona's Schwartz scores, through the chain's registered modulation.
|
|
990
|
+
// Through 21.0 the flag never reached this summary at all.
|
|
991
|
+
const chainValues = useValues ? await resolveChainValues(persona) : undefined;
|
|
946
992
|
try {
|
|
947
993
|
const { buildOTCognitiveProfile } = await import("../../visual/cognitive-transport.js");
|
|
948
994
|
const { computeDemandDistribution, computeSequentialCTC } = await import("../../visual/cognitive-transport-chain.js");
|
|
@@ -950,11 +996,24 @@ export function registerAuditTools(server, context) {
|
|
|
950
996
|
const { getAnyPersona, createCognitivePersona, resolvePersonaOrThrow } = await import("../../personas.js");
|
|
951
997
|
// A miss throws instead of yielding an all-midpoint stand-in.
|
|
952
998
|
const personaObj = getAnyPersona(persona) || resolvePersonaOrThrow(persona);
|
|
953
|
-
|
|
999
|
+
// A copy, because the bridge writes into what it is given. This was the
|
|
1000
|
+
// registry's own object, so one story run left the persona carrying
|
|
1001
|
+
// three derived capacities for the rest of the process, which
|
|
1002
|
+
// cognitive_distance then read as stored traits (first-timer to
|
|
1003
|
+
// power-user moved 0.1512 -> 0.1550). (2026-10-21)
|
|
1004
|
+
const traits = { ...(personaObj.cognitiveTraits || {}) };
|
|
1005
|
+
// A first visit, as cognitive_effort and site_cognitive_assessment
|
|
1006
|
+
// score it when no familiarity is supplied (this tool takes none).
|
|
1007
|
+
// Left unset, the chain read the missing-trait capacity (0.5) for
|
|
1008
|
+
// siteFamiliarity, the 20.x defect site-familiarity.ts describes, and
|
|
1009
|
+
// this summary disagreed with cognitive_effort on the same page and
|
|
1010
|
+
// persona (first-timer, prose fixture: 0.669 against 0.700).
|
|
1011
|
+
const { resolveSiteFamiliarity } = await import("./site-familiarity.js");
|
|
1012
|
+
const familiarity = resolveSiteFamiliarity(undefined, false);
|
|
954
1013
|
// Same bridge as Gate 3 and cognitive_effort. Without it this tool has
|
|
955
1014
|
// the identical missing-capacity defect and would reproduce the motor
|
|
956
1015
|
// disagreement against cognitive_effort the moment anyone compared them.
|
|
957
|
-
bridgeCapacityTraits(persona, traits, personaObj.accessibilityTraits);
|
|
1016
|
+
storyPersonaRateIds = collectPersonaReads(() => bridgeCapacityTraits(persona, traits, personaObj.accessibilityTraits, personaRateContext(personaObj))).ids;
|
|
958
1017
|
const otProfile = buildOTCognitiveProfile(persona, traits);
|
|
959
1018
|
// viewport-by-design: visual_cognitive_story declares no `scope` parameter,
|
|
960
1019
|
// so it promises the caller nothing about how much of the page it read, and
|
|
@@ -963,13 +1022,33 @@ export function registerAuditTools(server, context) {
|
|
|
963
1022
|
// a scope WAS offered, echoed, and then not applied. If this tool ever gains
|
|
964
1023
|
// a scope, this marker must go and the scope must be threaded.
|
|
965
1024
|
const pageMetrics = await extractPageMetrics(page);
|
|
966
|
-
const demand = computeDemandDistribution(pageMetrics);
|
|
967
|
-
const ctcResult = computeSequentialCTC(otProfile, demand, {
|
|
1025
|
+
const demand = computeDemandDistribution(pageMetrics, { siteFamiliarity: familiarity.value });
|
|
1026
|
+
const ctcResult = computeSequentialCTC(otProfile, demand, {
|
|
1027
|
+
asymmetric: true, interactions: true, ...(chainValues ? { schwartzValues: chainValues } : {}),
|
|
1028
|
+
});
|
|
1029
|
+
const rank = ctcRank(ctcResult);
|
|
968
1030
|
effortData = {
|
|
1031
|
+
// A percentile of the real reference pages (21.x); `rank` says it in words.
|
|
969
1032
|
totalCTC: Math.round(ctcResult.totalCTC * 1000) / 1000,
|
|
1033
|
+
// Its 95% interval across bootstrap resamples of the reference pages.
|
|
1034
|
+
totalCTCInterval: ctcResult.totalCTCInterval.map((x) => Math.round(x * 1000) / 1000),
|
|
1035
|
+
band: rank.band,
|
|
1036
|
+
referenceScope: ctcResult.referenceScope,
|
|
1037
|
+
rank: rank.sentence,
|
|
1038
|
+
raw: Math.round(ctcResult.rawCTC * 1000) / 1000,
|
|
970
1039
|
bottleneck: ctcResult.bottleneckLayer,
|
|
971
1040
|
abandonmentRisk: Math.round(ctcResult.abandonmentRisk * 100),
|
|
972
1041
|
layers: ctcResult.layers.map(l => ({ name: l.name, cost: Math.round(l.transportCost * 1000) / 1000 })),
|
|
1042
|
+
siteFamiliarity: familiarity.value,
|
|
1043
|
+
siteFamiliaritySource: familiarity.source,
|
|
1044
|
+
};
|
|
1045
|
+
storyEffort = {
|
|
1046
|
+
totalCTC: ctcResult.totalCTC,
|
|
1047
|
+
totalCTCInterval: ctcResult.totalCTCInterval,
|
|
1048
|
+
rawCTC: ctcResult.rawCTC,
|
|
1049
|
+
referenceScope: ctcResult.referenceScope,
|
|
1050
|
+
bottleneck: ctcResult.bottleneckLayer,
|
|
1051
|
+
abandonmentRiskPercent: Math.round(ctcResult.abandonmentRisk * 100),
|
|
973
1052
|
};
|
|
974
1053
|
}
|
|
975
1054
|
catch (e) {
|
|
@@ -1025,9 +1104,6 @@ export function registerAuditTools(server, context) {
|
|
|
1025
1104
|
// ── Build narrative ──
|
|
1026
1105
|
const concentration = attentionData?.concentration ?? 0;
|
|
1027
1106
|
const entropy = attentionData?.entropy ?? 0;
|
|
1028
|
-
const ctc = effortData?.totalCTC ?? 0;
|
|
1029
|
-
const bottleneck = effortData?.bottleneck ?? "unknown";
|
|
1030
|
-
const risk = effortData?.abandonmentRisk ?? 0;
|
|
1031
1107
|
const qualityData = urls.qualityData;
|
|
1032
1108
|
const _nu = new URL(url);
|
|
1033
1109
|
let narrative = `## ${persona} on ${_nu.hostname.replace(/^www\d*\./, '')}${_nu.pathname !== '/' ? _nu.pathname.replace(/\/$/, '') : ''}\n\n`;
|
|
@@ -1054,11 +1130,10 @@ export function registerAuditTools(server, context) {
|
|
|
1054
1130
|
else {
|
|
1055
1131
|
narrative += `Attention is spread evenly (concentration: ${pct}%) without a dominant anchor, though the page is not visually noisy. Nothing is fighting for attention; nothing is claiming it either.\n\n`;
|
|
1056
1132
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
narrative +=
|
|
1061
|
-
narrative += `Abandonment risk: **${risk}%**.\n\n`;
|
|
1133
|
+
// The rank, from the registered bands (story-narrative.ts). This read
|
|
1134
|
+
// totalCTC on the logistic: "comfortable" below 0.5, "Significant
|
|
1135
|
+
// cognitive burden" from 1.0, and 0 (comfortable) when effort failed.
|
|
1136
|
+
narrative += storyEffortSection(persona, storyEffort);
|
|
1062
1137
|
narrative += `### The Question: Do They See What Matters?\n`;
|
|
1063
1138
|
if (qualityData) {
|
|
1064
1139
|
const cta = qualityData.ctaCaptureRate ?? 0;
|
|
@@ -1075,31 +1150,12 @@ export function registerAuditTools(server, context) {
|
|
|
1075
1150
|
narrative += `Attention quality data unavailable.\n`;
|
|
1076
1151
|
}
|
|
1077
1152
|
narrative += `\n### The Verdict\n`;
|
|
1078
|
-
//
|
|
1079
|
-
//
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1082
|
-
// verdict
|
|
1083
|
-
|
|
1084
|
-
// summary, because it is the line people quote. (2026-07-29)
|
|
1085
|
-
const distractRatio = qualityData?.distractorRatio ?? 0;
|
|
1086
|
-
const qScore = qualityData?.qualityScore ?? 0;
|
|
1087
|
-
const leaking = distractRatio > 0.4 || (qualityData !== undefined && qScore < 50);
|
|
1088
|
-
if (ctc < 0.5 && (qualityData?.ctaCaptureRate ?? 0) > 0.2 && !leaking) {
|
|
1089
|
-
narrative += `**Strong.** Easy to use AND attention lands on CTAs. This page works for ${persona}.`;
|
|
1090
|
-
}
|
|
1091
|
-
else if (ctc < 0.5 && (qualityData?.ctaCaptureRate ?? 0) > 0.2 && leaking) {
|
|
1092
|
-
narrative += `**Mixed.** Low cognitive effort and the CTAs do get seen, but attention quality is ${qScore}/100 with ${(distractRatio * 100).toFixed(0)}% going to non-actionable elements. ${persona} can use this page; the design is spending most of their attention on things that do not convert.`;
|
|
1093
|
-
}
|
|
1094
|
-
else if (ctc < 0.5 && (qualityData?.ctaCaptureRate ?? 0) < 0.1) {
|
|
1095
|
-
narrative += `**Easy but unfocused.** Low cognitive effort, but attention misses the CTAs. The page needs stronger visual hierarchy to guide ${persona} toward conversion.`;
|
|
1096
|
-
}
|
|
1097
|
-
else if (ctc > 1.0 && (qualityData?.ctaCaptureRate ?? 0) > 0.2) {
|
|
1098
|
-
narrative += `**Hard but focused.** High cognitive effort, but when ${persona} pushes through, they see the CTAs. Reduce complexity to lower the abandonment risk without losing the visual focus.`;
|
|
1099
|
-
}
|
|
1100
|
-
else {
|
|
1101
|
-
narrative += `**Needs work.** High effort AND low CTA visibility. ${persona} is both struggling to process the page AND not seeing the conversion path. Prioritize: simplify the ${bottleneck} layer, then make CTAs more prominent.`;
|
|
1102
|
-
}
|
|
1153
|
+
// Reads the distractor ratio and quality score as well as the CTA rate:
|
|
1154
|
+
// a verdict that reads less of the data than the section above it is
|
|
1155
|
+
// the line people quote and the one that contradicts it (2026-07-29).
|
|
1156
|
+
// Cost is the registered band; every branch is reachable and tested
|
|
1157
|
+
// (story-narrative.ts, tests/story-verdict.test.ts).
|
|
1158
|
+
narrative += storyVerdict(persona, storyEffort, qualityData).text;
|
|
1103
1159
|
// ── Assemble response ──
|
|
1104
1160
|
// Third layer, opt-in. Runs AFTER the deterministic layers and is
|
|
1105
1161
|
// reported beside them rather than folded in: layers 1 and 2 are
|
|
@@ -1133,9 +1189,13 @@ export function registerAuditTools(server, context) {
|
|
|
1133
1189
|
...(aiAttention ? { aiAttention } : {}),
|
|
1134
1190
|
images: urls,
|
|
1135
1191
|
effort: effortData,
|
|
1192
|
+
// Only when useValues asked for it, as in cognitive_effort.
|
|
1193
|
+
...(useValues ? { valueModulation: valueModulationReport(chainValues) } : {}),
|
|
1136
1194
|
attention: {
|
|
1137
1195
|
entropy: Math.round(entropy * 1000) / 1000,
|
|
1138
1196
|
concentration: Math.round(concentration * 1000) / 1000,
|
|
1197
|
+
// Which perceptual filter drew the map, as attention_analysis reports it.
|
|
1198
|
+
perceptualProfile: attentionData?.perceptualProfile ?? null,
|
|
1139
1199
|
},
|
|
1140
1200
|
quality: qualityData ? {
|
|
1141
1201
|
ctaCaptureRate: qualityData.ctaCaptureRate,
|
|
@@ -1143,6 +1203,14 @@ export function registerAuditTools(server, context) {
|
|
|
1143
1203
|
score: qualityData.qualityScore,
|
|
1144
1204
|
} : null,
|
|
1145
1205
|
duration: `${Date.now() - startTime}ms`,
|
|
1206
|
+
// Every registered constant the effort score read, the same block
|
|
1207
|
+
// cognitive_effort reports. Taken here, after every step that reads one;
|
|
1208
|
+
// fitting the previews below reads none.
|
|
1209
|
+
modelProvenance: activeProvenance(),
|
|
1210
|
+
// The scored persona's motor and reading rates, counted apart from the score's constants.
|
|
1211
|
+
...(storyEffort ? { personaRateProvenance: personaRateProvenance(storyPersonaRateIds) } : {}),
|
|
1212
|
+
// The attention analysis's own block, kept out of modelProvenance.
|
|
1213
|
+
...(attentionProvenance ? { attentionProvenance } : {}),
|
|
1146
1214
|
};
|
|
1147
1215
|
// Every overlay rides along as a preview, the budget split between them
|
|
1148
1216
|
// so text plus all images stays under the cap; imagePreviews says what
|
|
@@ -1165,7 +1233,7 @@ export function registerAuditTools(server, context) {
|
|
|
1165
1233
|
}],
|
|
1166
1234
|
};
|
|
1167
1235
|
}
|
|
1168
|
-
});
|
|
1236
|
+
}));
|
|
1169
1237
|
}
|
|
1170
1238
|
/**
|
|
1171
1239
|
* The canonical empathy_audit. Exported on its own so the stdio server can
|