cbrowser 19.2.0 → 19.2.2
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 +6 -0
- package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +28 -77
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/analysis/competitive-benchmark.js +2 -2
- package/dist/analysis/competitive-benchmark.js.map +1 -1
- package/dist/analysis/persona-comparison.d.ts.map +1 -1
- package/dist/analysis/persona-comparison.js +5 -1
- package/dist/analysis/persona-comparison.js.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +44 -7
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +8 -1
- package/dist/daemon.js.map +1 -1
- package/dist/mcp-server-remote.d.ts +3 -0
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +12 -1
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-tools/account-personas.d.ts +3 -2
- package/dist/mcp-tools/account-personas.d.ts.map +1 -1
- package/dist/mcp-tools/account-personas.js +13 -4
- package/dist/mcp-tools/account-personas.js.map +1 -1
- package/dist/mcp-tools/base/advanced-interaction-tools.js +1 -1
- package/dist/mcp-tools/base/advanced-interaction-tools.js.map +1 -1
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +49 -15
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.js +25 -4
- package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +14 -2
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/gif-tools.d.ts +19 -0
- package/dist/mcp-tools/base/gif-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/gif-tools.js +53 -24
- 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 +44 -28
- package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
- package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/values-tools.js +12 -0
- package/dist/mcp-tools/base/values-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts +17 -0
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +184 -148
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/inline-image.d.ts +82 -0
- package/dist/mcp-tools/inline-image.d.ts.map +1 -0
- package/dist/mcp-tools/inline-image.js +163 -0
- package/dist/mcp-tools/inline-image.js.map +1 -0
- package/dist/personas.d.ts +37 -0
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +137 -3
- package/dist/personas.js.map +1 -1
- package/dist/security/audit-wrapper.d.ts.map +1 -1
- package/dist/security/audit-wrapper.js +1 -0
- package/dist/security/audit-wrapper.js.map +1 -1
- package/dist/security/script-gate.d.ts +33 -0
- package/dist/security/script-gate.d.ts.map +1 -0
- package/dist/security/script-gate.js +37 -0
- package/dist/security/script-gate.js.map +1 -0
- package/dist/security/tool-permissions.d.ts.map +1 -1
- package/dist/security/tool-permissions.js +4 -0
- package/dist/security/tool-permissions.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,9 +231,9 @@ AI agents need boundaries. CBrowser classifies every action by risk level:
|
|
|
231
231
|
| 🔴 **Red** | Submit, delete, purchase | Requires verification |
|
|
232
232
|
| ⬛ **Black** | Bypass auth, inject scripts | Never executes |
|
|
233
233
|
|
|
234
|
-
**How red is decided:** by the element an action would activate as well as by the selector text. The selector string is classified first, as before, so naming a dangerous action in a selector is refused even when the element reads harmless. The element is then classified however it is addressed: the same "Delete account" button is refused by its label, its id, its test id or a structural path, and an element with only a delegated click listener is judged by its own text. A click on a container is judged at the point it lands - beneath fixed bars and overlays, into open shadow roots, slots and same-origin frames, up to the control, pointer-cursor item or text-bearing item there - and the click is sent to that judged point. A refused click is never retried with a "healed" selector. Gated: `click`, `smart_click`, hover-click and the daemon's click; `press_key`, `type_text` and `keyboard` (Enter, including a typed or pressed newline, or Space on the focused control - followed into shadow roots and same-origin frames - and Enter in a field whose form submits one, judged together with a combobox's `aria-activedescendant` item); a `drag` whose press and release share a control; and the NL test runner's `click at X, Y`. Names below a control (icon labels, image alts, slotted and open-shadow text, `<label for>`) and the path a form submits to (a submitter's `formaction`, otherwise the form's `action`) feed the same keyword classification. `force: true` on the MCP `click`, `press_key`, `type_text` and `
|
|
234
|
+
**How red is decided:** by the element an action would activate as well as by the selector text. The selector string is classified first, as before, so naming a dangerous action in a selector is refused even when the element reads harmless. The element is then classified however it is addressed: the same "Delete account" button is refused by its label, its id, its test id or a structural path, and an element with only a delegated click listener is judged by its own text. A click on a container is judged at the point it lands - beneath fixed bars and overlays, into open shadow roots, slots and same-origin frames, up to the control, pointer-cursor item or text-bearing item there - and the click is sent to that judged point. A refused click is never retried with a "healed" selector. Gated: `click`, `smart_click`, hover-click and the daemon's click; `press_key`, `type_text` and `keyboard` (Enter, including a typed or pressed newline, or Space on the focused control - followed into shadow roots and same-origin frames - and Enter in a field whose form submits one, judged together with a combobox's `aria-activedescendant` item); a `drag` whose press and release share a control; and the NL test runner's `click at X, Y`. Caller-supplied page script is red by itself, whatever it does: `evaluate_script` over MCP, `evaluate`/`eval` on the CLI and the daemon's evaluate are refused without force and run nothing, because a script can activate any control without passing the element gate (`document.querySelector('#delete-account').click()` is the click `click` refuses, in one line). Names below a control (icon labels, image alts, slotted and open-shadow text, `<label for>`) and the path a form submits to (a submitter's `formaction`, otherwise the form's `action`) feed the same keyword classification. `force: true` on the MCP `click`, `press_key`, `type_text`, `drag` and `evaluate_script` tools (`smart_click` has none: use `click`), or `--force` on the CLI, proceeds and reports the zone.
|
|
235
235
|
|
|
236
|
-
Not judged, by design: clicking or typing into a text field or a contenteditable editor, and choosing a value with a native checkbox, radio or select or a `role=checkbox`, `radio` or `switch` - the submit that commits them is judged, but a switch or checkbox that saves on its own as it changes commits immediately and is not refused; and a link's address (a link to `/buy` is navigation). Not gated: cognitive journeys and other engines that choose their own clicks
|
|
236
|
+
Not judged, by design: clicking or typing into a text field or a contenteditable editor, and choosing a value with a native checkbox, radio or select or a `role=checkbox`, `radio` or `switch` - the submit that commits them is judged, but a switch or checkbox that saves on its own as it changes commits immediately and is not refused; and a link's address (a link to `/buy` is navigation). Not gated: cognitive journeys and other engines that choose their own clicks. Known limits: labels inside closed shadow roots or drawn by CSS, controls inside cross-origin frames, icon-only controls with no accessible name, a confirmation button with neutral text ("Confirm", "OK") inside a dialog, a form with no `action` or an empty `formaction` (it submits to the page's own address, which is not judged), an `aria-activedescendant` id that only resolves outside the field's own document or shadow root (not followed; ARIA references do not cross roots), a Tab inside typed text that moves focus before a later Enter, one round trip between the check and the click in which a page could swap the element, and keyword false positives: a "Purchase history" tab, a "Buyer protection" link, a `role=option` value picker such as "Pay with PayPal", a harmless "Apply" in a form posting to `/checkout/discount`, and an inert heading such as "Checkout" clicked by id (refusing it costs one forced retry; missing a listener-only "Delete account" cannot be undone). Classification uses English keyword patterns; when the element cannot be read, the selector text alone decides.
|
|
237
237
|
|
|
238
238
|
---
|
|
239
239
|
|
|
@@ -49,6 +49,12 @@ export declare function filterBarriersToViewport<T extends {
|
|
|
49
49
|
};
|
|
50
50
|
export declare function formatEmpathyAuditReport(result: EmpathyAuditResult): string;
|
|
51
51
|
export declare function generateEmpathyAuditHtmlReport(result: EmpathyAuditResult): string;
|
|
52
|
+
/**
|
|
53
|
+
* Disability shorthand -> roster name. One table for every tool now lives in
|
|
54
|
+
* src/personas.ts (PERSONA_ALIASES); kept under this name for existing
|
|
55
|
+
* importers. (2026-10-09)
|
|
56
|
+
*/
|
|
57
|
+
export declare const EMPATHY_PERSONA_ALIASES: Record<string, string>;
|
|
52
58
|
/**
|
|
53
59
|
* The published WCAG list, DERIVED from the barriers rather than accumulated
|
|
54
60
|
* alongside them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EASpB,MAAM,aAAa,CAAC;AAuKrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AAwjED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAmF3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAkTjF;AAqJD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EASpB,MAAM,aAAa,CAAC;AAuKrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AAwjED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAmF3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAkTjF;AAqJD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAmB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAsW7B"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { weightKeyFor, barrierWeightFor, weightedSeverity } from "../visual/perceptual-transport.js";
|
|
7
7
|
import { VERSION } from "../version.js";
|
|
8
8
|
import { CBrowser } from "../browser.js";
|
|
9
|
-
import { getAccessibilityPersona } from "../personas.js";
|
|
9
|
+
import { getAccessibilityPersona, PERSONA_ALIASES } from "../personas.js";
|
|
10
10
|
import { runCognitiveJourney, isApiKeyConfigured, } from "../cognitive/index.js";
|
|
11
11
|
import { calculatePerceptualScore, analyzePerceptualTransport, } from "../visual/perceptual-transport.js";
|
|
12
12
|
import { buildOTCognitiveProfile, estimateCognitiveLoad, extractPageMetrics, } from "../visual/cognitive-transport.js";
|
|
@@ -2521,6 +2521,12 @@ function inferGoalType(goal) {
|
|
|
2521
2521
|
return "navigate_to";
|
|
2522
2522
|
return "complete_action";
|
|
2523
2523
|
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Disability shorthand -> roster name. One table for every tool now lives in
|
|
2526
|
+
* src/personas.ts (PERSONA_ALIASES); kept under this name for existing
|
|
2527
|
+
* importers. (2026-10-09)
|
|
2528
|
+
*/
|
|
2529
|
+
export const EMPATHY_PERSONA_ALIASES = PERSONA_ALIASES;
|
|
2524
2530
|
/**
|
|
2525
2531
|
* The published WCAG list, DERIVED from the barriers rather than accumulated
|
|
2526
2532
|
* alongside them.
|
|
@@ -2569,90 +2575,35 @@ export async function runEmpathyAudit(url, options) {
|
|
|
2569
2575
|
const results = [];
|
|
2570
2576
|
const allWcagViolations = new Set();
|
|
2571
2577
|
const allBarriers = [];
|
|
2572
|
-
//
|
|
2573
|
-
//
|
|
2574
|
-
const personaMap =
|
|
2575
|
-
"motor-tremor": "motor-impairment-tremor",
|
|
2576
|
-
"motor": "motor-impairment-tremor",
|
|
2577
|
-
"tremor": "motor-impairment-tremor",
|
|
2578
|
-
"low-vision": "low-vision-magnified",
|
|
2579
|
-
"vision": "low-vision-magnified",
|
|
2580
|
-
"magnified": "low-vision-magnified",
|
|
2581
|
-
"adhd": "cognitive-adhd",
|
|
2582
|
-
"cognitive": "cognitive-adhd",
|
|
2583
|
-
"attention": "cognitive-adhd",
|
|
2584
|
-
"dyslexia": "dyslexic-user",
|
|
2585
|
-
"dyslexic": "dyslexic-user",
|
|
2586
|
-
"reading": "dyslexic-user",
|
|
2587
|
-
"deaf": "deaf-user",
|
|
2588
|
-
"hearing": "deaf-user",
|
|
2589
|
-
"elderly": "elderly-low-vision",
|
|
2590
|
-
"elderly-user": "elderly-low-vision", // v14.2.5: Added missing mapping
|
|
2591
|
-
"elderly-low-vision": "elderly-low-vision",
|
|
2592
|
-
"senior": "elderly-low-vision",
|
|
2593
|
-
"old": "elderly-low-vision", // v14.2.5: Additional synonym
|
|
2594
|
-
"color-blind": "color-blind-deuteranopia",
|
|
2595
|
-
"colorblind": "color-blind-deuteranopia",
|
|
2596
|
-
"deuteranopia": "color-blind-deuteranopia",
|
|
2597
|
-
// v18.35.0: New research-backed cognitive disability personas
|
|
2598
|
-
"autism": "autism-spectrum",
|
|
2599
|
-
"autistic": "autism-spectrum",
|
|
2600
|
-
"asd": "autism-spectrum",
|
|
2601
|
-
"autism-spectrum": "autism-spectrum",
|
|
2602
|
-
"intellectual-disability": "intellectual-disability",
|
|
2603
|
-
"intellectual": "intellectual-disability",
|
|
2604
|
-
"learning-disability": "intellectual-disability",
|
|
2605
|
-
"aphasia": "aphasia-receptive",
|
|
2606
|
-
"aphasia-receptive": "aphasia-receptive",
|
|
2607
|
-
"wernicke": "aphasia-receptive",
|
|
2608
|
-
"dyscalculia": "dyscalculia",
|
|
2609
|
-
"numeracy": "dyscalculia",
|
|
2610
|
-
};
|
|
2578
|
+
// Disability shorthand -> roster name (module-level so the MCP handler
|
|
2579
|
+
// resolves with the same table).
|
|
2580
|
+
const personaMap = EMPATHY_PERSONA_ALIASES;
|
|
2611
2581
|
// Run audit for each disability type
|
|
2612
2582
|
// v18.35.0: Accept any persona — wrap non-disability personas with default accessibility traits
|
|
2613
|
-
|
|
2614
|
-
|
|
2583
|
+
// Every requested persona resolves through the one resolver BEFORE anything
|
|
2584
|
+
// launches. An unknown name used to fall through to a "persona" with no
|
|
2585
|
+
// traits that scored 0 -- the worst possible score -- with errors: [] and
|
|
2586
|
+
// isDisabilityPersona: true, which a dashboard reads as "the site failed
|
|
2587
|
+
// completely". It now throws UnknownPersonaError instead. (2026-10-09)
|
|
2588
|
+
try {
|
|
2589
|
+
const { loadAccountPersonas } = await import("../mcp-tools/account-personas.js");
|
|
2590
|
+
const { getSessionApiKey } = await import("../mcp-tools/base/cognitive-tools.js");
|
|
2591
|
+
await loadAccountPersonas(getSessionApiKey());
|
|
2592
|
+
}
|
|
2593
|
+
catch { /* falls back to disk and built-ins */ }
|
|
2594
|
+
const { resolvePersonaForTool } = await import("../personas.js");
|
|
2595
|
+
const resolvedNames = disabilities.map((d) => resolvePersonaForTool(d, personaMap, true).name);
|
|
2596
|
+
for (const [index, disability] of disabilities.entries()) {
|
|
2597
|
+
const personaName = resolvedNames[index];
|
|
2615
2598
|
let persona = getAccessibilityPersona(personaName);
|
|
2616
2599
|
let isDisabilityPersona = !!persona;
|
|
2617
2600
|
if (!persona) {
|
|
2618
2601
|
// Try to find as a regular cognitive persona and wrap it
|
|
2619
2602
|
const { getAnyPersona, createCognitivePersona } = await import("../personas.js");
|
|
2620
2603
|
let cognitivePersona = getAnyPersona(personaName);
|
|
2621
|
-
// CMS
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
const { getSessionApiKey } = await import("../mcp-tools/base/cognitive-tools.js");
|
|
2625
|
-
const apiKey = getSessionApiKey();
|
|
2626
|
-
if (apiKey) {
|
|
2627
|
-
const cmsUrl = process.env.CMS_URL || "http://localhost:3200";
|
|
2628
|
-
const res = await fetch(`${cmsUrl}/api/personas`, {
|
|
2629
|
-
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
2630
|
-
});
|
|
2631
|
-
if (res.ok) {
|
|
2632
|
-
const data = await res.json();
|
|
2633
|
-
const cmsMatch = data.personas.find((p) => p.name.toLowerCase() === personaName.toLowerCase() ||
|
|
2634
|
-
p.name.toLowerCase().replace(/\s+/g, '-') === personaName.toLowerCase() ||
|
|
2635
|
-
p.name.toLowerCase().replace(/\s+/g, '_') === personaName.toLowerCase() ||
|
|
2636
|
-
(p.slug && p.slug.toLowerCase() === personaName.toLowerCase()));
|
|
2637
|
-
if (cmsMatch) {
|
|
2638
|
-
const parsedTraits = typeof cmsMatch.traits === "string" ? JSON.parse(cmsMatch.traits) : cmsMatch.traits;
|
|
2639
|
-
cognitivePersona = createCognitivePersona(cmsMatch.name, cmsMatch.name, parsedTraits, {
|
|
2640
|
-
ageRange: cmsMatch.age_range || undefined,
|
|
2641
|
-
});
|
|
2642
|
-
// Attach CMS accessibility traits if stored
|
|
2643
|
-
if (cmsMatch.accessibility_traits) {
|
|
2644
|
-
const at = typeof cmsMatch.accessibility_traits === "string" ? JSON.parse(cmsMatch.accessibility_traits) : cmsMatch.accessibility_traits;
|
|
2645
|
-
cognitivePersona.accessibilityTraits = at;
|
|
2646
|
-
}
|
|
2647
|
-
console.log(`[empathy_audit] Found custom CMS persona: "${cmsMatch.name}" with ${Object.keys(parsedTraits).length} traits${cmsMatch.accessibility_traits ? ' + disability modeling' : ''}`);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
}
|
|
2652
|
-
catch (e) {
|
|
2653
|
-
console.debug(`[empathy_audit] CMS persona lookup failed: ${e.message}`);
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2604
|
+
// No CMS fallback here any more: the account's CMS personas were loaded
|
|
2605
|
+
// into the scoped registry above, so getAnyPersona already sees them, and
|
|
2606
|
+
// a name nothing resolves was refused before the loop. (2026-10-09)
|
|
2656
2607
|
if (cognitivePersona) {
|
|
2657
2608
|
// Wrap cognitive persona as accessibility persona, inferring traits from name + cognitive traits
|
|
2658
2609
|
const ct = cognitivePersona.cognitiveTraits || {};
|