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.
Files changed (70) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/accessibility-empathy.d.ts +6 -0
  3. package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
  4. package/dist/analysis/accessibility-empathy.js +28 -77
  5. package/dist/analysis/accessibility-empathy.js.map +1 -1
  6. package/dist/analysis/competitive-benchmark.js +2 -2
  7. package/dist/analysis/competitive-benchmark.js.map +1 -1
  8. package/dist/analysis/persona-comparison.d.ts.map +1 -1
  9. package/dist/analysis/persona-comparison.js +5 -1
  10. package/dist/analysis/persona-comparison.js.map +1 -1
  11. package/dist/browser.js +2 -2
  12. package/dist/browser.js.map +1 -1
  13. package/dist/cli.js +44 -7
  14. package/dist/cli.js.map +1 -1
  15. package/dist/daemon.d.ts +2 -0
  16. package/dist/daemon.d.ts.map +1 -1
  17. package/dist/daemon.js +8 -1
  18. package/dist/daemon.js.map +1 -1
  19. package/dist/mcp-server-remote.d.ts +3 -0
  20. package/dist/mcp-server-remote.d.ts.map +1 -1
  21. package/dist/mcp-server-remote.js +12 -1
  22. package/dist/mcp-server-remote.js.map +1 -1
  23. package/dist/mcp-tools/account-personas.d.ts +3 -2
  24. package/dist/mcp-tools/account-personas.d.ts.map +1 -1
  25. package/dist/mcp-tools/account-personas.js +13 -4
  26. package/dist/mcp-tools/account-personas.js.map +1 -1
  27. package/dist/mcp-tools/base/advanced-interaction-tools.js +1 -1
  28. package/dist/mcp-tools/base/advanced-interaction-tools.js.map +1 -1
  29. package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
  30. package/dist/mcp-tools/base/audit-tools.js +49 -15
  31. package/dist/mcp-tools/base/audit-tools.js.map +1 -1
  32. package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
  33. package/dist/mcp-tools/base/browser-state-tools.js +25 -4
  34. package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
  35. package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
  36. package/dist/mcp-tools/base/cognitive-tools.js +14 -2
  37. package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
  38. package/dist/mcp-tools/base/gif-tools.d.ts +19 -0
  39. package/dist/mcp-tools/base/gif-tools.d.ts.map +1 -1
  40. package/dist/mcp-tools/base/gif-tools.js +53 -24
  41. package/dist/mcp-tools/base/gif-tools.js.map +1 -1
  42. package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
  43. package/dist/mcp-tools/base/persona-comparison-tools.js +44 -28
  44. package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
  45. package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
  46. package/dist/mcp-tools/base/values-tools.js +12 -0
  47. package/dist/mcp-tools/base/values-tools.js.map +1 -1
  48. package/dist/mcp-tools/base/visual-testing-tools.d.ts +17 -0
  49. package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
  50. package/dist/mcp-tools/base/visual-testing-tools.js +184 -148
  51. package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
  52. package/dist/mcp-tools/inline-image.d.ts +82 -0
  53. package/dist/mcp-tools/inline-image.d.ts.map +1 -0
  54. package/dist/mcp-tools/inline-image.js +163 -0
  55. package/dist/mcp-tools/inline-image.js.map +1 -0
  56. package/dist/personas.d.ts +37 -0
  57. package/dist/personas.d.ts.map +1 -1
  58. package/dist/personas.js +137 -3
  59. package/dist/personas.js.map +1 -1
  60. package/dist/security/audit-wrapper.d.ts.map +1 -1
  61. package/dist/security/audit-wrapper.js +1 -0
  62. package/dist/security/audit-wrapper.js.map +1 -1
  63. package/dist/security/script-gate.d.ts +33 -0
  64. package/dist/security/script-gate.d.ts.map +1 -0
  65. package/dist/security/script-gate.js +37 -0
  66. package/dist/security/script-gate.js.map +1 -0
  67. package/dist/security/tool-permissions.d.ts.map +1 -1
  68. package/dist/security/tool-permissions.js +4 -0
  69. package/dist/security/tool-permissions.js.map +1 -1
  70. package/package.json +1 -1
package/dist/personas.js CHANGED
@@ -14,7 +14,7 @@ import { join } from "path";
14
14
  import { homedir } from "os";
15
15
  import { applyTraitCorrelations } from "./persona-questionnaire.js";
16
16
  import { AGENT_PERSONAS, isAgentPersona, getAgentPersona, listAgentPersonas, isAgentPersonaObject } from "./agent-personas.js";
17
- import { scopedDataDir } from "./persona-scope.js";
17
+ import { scopedDataDir, scopedAccountId } from "./persona-scope.js";
18
18
  import { TRAIT_DEFINITIONS, canonicalizeTraits } from "./trait-reference.js";
19
19
  // ============================================================================
20
20
  // Custom Personas Storage
@@ -45,6 +45,35 @@ function personasDirFor() {
45
45
  * @since 16.17.0
46
46
  */
47
47
  const RUNTIME_PERSONAS = {};
48
+ /**
49
+ * An account's custom personas, loaded from the CMS, keyed by account.
50
+ *
51
+ * These used to go into RUNTIME_PERSONAS, which is one map for the whole
52
+ * process and is checked FIRST by getAnyPersona. So once any account ran
53
+ * attention_analysis, its personas resolved by name for every other caller in
54
+ * that process - a cross-tenant read - and the trait vector a measurement read
55
+ * for a name depended on whether that had happened yet. list_cognitive_personas
56
+ * never consulted the map, so the roster and persona_lookup returned different
57
+ * records for alexa-eden (WM 0.25 vs 0.40, FOMO 0.25 vs 0.60). (2026-10-09)
58
+ *
59
+ * Stored process-wide because the load is expensive, but READ only through the
60
+ * request's persona scope, so request A can never see account B's personas. And
61
+ * consulted AFTER the on-disk store, which for a scoped request is that
62
+ * account's mirror of the same rows, so every reader agrees on one record.
63
+ */
64
+ const ACCOUNT_PERSONAS = new Map();
65
+ /** Replace an account's loaded personas. Never visible outside that account's scope. */
66
+ export function registerAccountPersonas(accountId, personas) {
67
+ const byName = {};
68
+ for (const p of personas)
69
+ byName[p.name] = p;
70
+ ACCOUNT_PERSONAS.set(accountId, byName);
71
+ }
72
+ /** The current request's account personas; empty when unscoped. */
73
+ function scopedAccountPersonas() {
74
+ const id = scopedAccountId();
75
+ return id === undefined ? {} : ACCOUNT_PERSONAS.get(id) ?? {};
76
+ }
48
77
  function ensurePersonasDir() {
49
78
  if (!existsSync(DATA_DIR)) {
50
79
  mkdirSync(DATA_DIR, { recursive: true });
@@ -1725,6 +1754,12 @@ export function getPersona(name) {
1725
1754
  if (customPersonas[name]) {
1726
1755
  return customPersonas[name];
1727
1756
  }
1757
+ // Then the calling account's CMS personas, in the same order getAnyPersona
1758
+ // uses, so list_cognitive_personas and persona_lookup cannot disagree.
1759
+ const accountPersonas = scopedAccountPersonas();
1760
+ if (accountPersonas[name]) {
1761
+ return accountPersonas[name];
1762
+ }
1728
1763
  // Fall back to built-ins
1729
1764
  return BUILTIN_PERSONAS[name];
1730
1765
  }
@@ -1734,8 +1769,9 @@ export function getPersona(name) {
1734
1769
  export function listPersonas() {
1735
1770
  const builtinNames = Object.keys(BUILTIN_PERSONAS);
1736
1771
  const customNames = Object.keys(loadCustomPersonas());
1772
+ const accountNames = Object.keys(scopedAccountPersonas());
1737
1773
  // Combine and dedupe (custom personas with same name as built-in take precedence in getPersona)
1738
- return [...new Set([...builtinNames, ...customNames])];
1774
+ return [...new Set([...builtinNames, ...customNames, ...accountNames])];
1739
1775
  }
1740
1776
  /**
1741
1777
  * List only custom persona names.
@@ -3330,6 +3366,13 @@ export function getAnyPersona(name) {
3330
3366
  if (customPersonas[name]) {
3331
3367
  return customPersonas[name];
3332
3368
  }
3369
+ // 2b. The calling account's CMS personas (scoped; empty when unscoped).
3370
+ // Same position as in getPersona, so the roster and every measurement
3371
+ // resolve a name to the same record.
3372
+ const accountPersonas = scopedAccountPersonas();
3373
+ if (accountPersonas[name]) {
3374
+ return accountPersonas[name];
3375
+ }
3333
3376
  // 3. Check built-in personas
3334
3377
  if (BUILTIN_PERSONAS[name]) {
3335
3378
  return BUILTIN_PERSONAS[name];
@@ -3442,6 +3485,96 @@ export function resolvePersonaOrThrow(name, syntheticTraits) {
3442
3485
  }
3443
3486
  throw new UnknownPersonaError(name, suggestPersonaNames(name));
3444
3487
  }
3488
+ /**
3489
+ * Shorthand -> roster name, shared by every persona-accepting tool.
3490
+ * v14.2.5: Added elderly-user mapping (issue #190 - persona dropout)
3491
+ */
3492
+ export const PERSONA_ALIASES = {
3493
+ "motor-tremor": "motor-impairment-tremor",
3494
+ "motor": "motor-impairment-tremor",
3495
+ "tremor": "motor-impairment-tremor",
3496
+ "low-vision": "low-vision-magnified",
3497
+ "vision": "low-vision-magnified",
3498
+ "magnified": "low-vision-magnified",
3499
+ "adhd": "cognitive-adhd",
3500
+ "cognitive": "cognitive-adhd",
3501
+ "attention": "cognitive-adhd",
3502
+ "dyslexia": "dyslexic-user",
3503
+ "dyslexic": "dyslexic-user",
3504
+ "reading": "dyslexic-user",
3505
+ "deaf": "deaf-user",
3506
+ "hearing": "deaf-user",
3507
+ "elderly": "elderly-low-vision",
3508
+ "elderly-user": "elderly-low-vision", // v14.2.5: Added missing mapping
3509
+ "elderly-low-vision": "elderly-low-vision",
3510
+ "senior": "elderly-low-vision",
3511
+ "old": "elderly-low-vision", // v14.2.5: Additional synonym
3512
+ "color-blind": "color-blind-deuteranopia",
3513
+ "colorblind": "color-blind-deuteranopia",
3514
+ "deuteranopia": "color-blind-deuteranopia",
3515
+ // v18.35.0: New research-backed cognitive disability personas
3516
+ "autism": "autism-spectrum",
3517
+ "autistic": "autism-spectrum",
3518
+ "asd": "autism-spectrum",
3519
+ "autism-spectrum": "autism-spectrum",
3520
+ "intellectual-disability": "intellectual-disability",
3521
+ "intellectual": "intellectual-disability",
3522
+ "learning-disability": "intellectual-disability",
3523
+ "aphasia": "aphasia-receptive",
3524
+ "aphasia-receptive": "aphasia-receptive",
3525
+ "wernicke": "aphasia-receptive",
3526
+ "dyscalculia": "dyscalculia",
3527
+ "numeracy": "dyscalculia",
3528
+ };
3529
+ /**
3530
+ * Resolve a persona name for a measuring tool, or refuse - the one resolver.
3531
+ *
3532
+ * Every tool that measures "as" a persona must call this BEFORE measuring.
3533
+ * Three tools did their own resolution and each failed open differently:
3534
+ * empathy_audit returned empathyScore 0 with errors: [] for a name that does not
3535
+ * exist, attention_analysis handed the bare string to the relevance LLM, which
3536
+ * role-played it (a one-letter typo of cognitive-adhd scored 81 against the real
3537
+ * persona's 80), and attention_compare reported a nonexistent persona as more
3538
+ * different from power-user than the deliberately extreme real pair. (2026-10-09)
3539
+ *
3540
+ * `aliases` maps shorthand to a roster name (empathy_audit's disability terms).
3541
+ * When the name that measured differs from the one asked for, `resolvedFrom`
3542
+ * says so; a silent substitution reads as the requested persona's result.
3543
+ */
3544
+ export function resolvePersonaForTool(requested, aliases = PERSONA_ALIASES, aliasFirst = false) {
3545
+ // One alias table for every tool: "motor-tremor" resolved in empathy_audit
3546
+ // and was refused by attention_analysis (report v2). Aliases are a FALLBACK by
3547
+ // default, so an exact roster name always wins; empathy_audit passes
3548
+ // aliasFirst because its table deliberately maps elderly-user to the
3549
+ // elderly-low-vision accessibility persona. (2026-10-09)
3550
+ const alias = aliases[requested.toLowerCase()];
3551
+ const aliased = alias && (aliasFirst || !getAnyPersona(requested)) ? alias : requested;
3552
+ const persona = getAnyPersona(aliased);
3553
+ if (!persona)
3554
+ throw new UnknownPersonaError(requested, suggestPersonaNames(requested));
3555
+ const name = persona.name ?? aliased;
3556
+ return name === requested ? { name, persona } : { name, persona, resolvedFrom: requested };
3557
+ }
3558
+ /**
3559
+ * A cognitive persona for a journey engine, refusing names nothing knows.
3560
+ *
3561
+ * Journey and benchmark engines used `getPersona(name) || first-timer`, so a
3562
+ * typo ran as first-timer and the result was reported under the typo. A name no
3563
+ * registry knows now throws UnknownPersonaError. A name that IS known but is not
3564
+ * a cognitive persona (an accessibility or agent persona) keeps the old
3565
+ * first-timer substitution -- a named limit, not fixed here. (2026-10-09)
3566
+ */
3567
+ export function getPersonaOrRefuse(name) {
3568
+ if (!name)
3569
+ return BUILTIN_PERSONAS["first-timer"];
3570
+ const direct = getPersona(name);
3571
+ if (direct)
3572
+ return direct;
3573
+ const any = getAnyPersona(name);
3574
+ if (!any)
3575
+ throw new UnknownPersonaError(name, suggestPersonaNames(name));
3576
+ return getPersona(any.name) ?? BUILTIN_PERSONAS["first-timer"];
3577
+ }
3445
3578
  export function listAllPersonas() {
3446
3579
  const runtimeNames = Object.keys(RUNTIME_PERSONAS);
3447
3580
  const builtinNames = Object.keys(BUILTIN_PERSONAS);
@@ -3449,8 +3582,9 @@ export function listAllPersonas() {
3449
3582
  const emotionalNames = Object.keys(EMOTIONAL_PERSONAS);
3450
3583
  const agentNames = Object.keys(AGENT_PERSONAS);
3451
3584
  const customNames = Object.keys(loadCustomPersonas());
3585
+ const accountNames = Object.keys(scopedAccountPersonas());
3452
3586
  // Combine and dedupe (runtime first for Enterprise priority)
3453
- return [...new Set([...runtimeNames, ...builtinNames, ...accessibilityNames, ...emotionalNames, ...agentNames, ...customNames])];
3587
+ return [...new Set([...runtimeNames, ...builtinNames, ...accessibilityNames, ...emotionalNames, ...agentNames, ...customNames, ...accountNames])];
3454
3588
  }
3455
3589
  /**
3456
3590
  * Register personas at runtime without disk writes.