cbrowser 16.9.4 → 16.13.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.
Files changed (39) hide show
  1. package/README.md +32 -0
  2. package/dist/analysis/chaos-testing.d.ts.map +1 -1
  3. package/dist/analysis/chaos-testing.js +8 -1
  4. package/dist/analysis/chaos-testing.js.map +1 -1
  5. package/dist/browser.d.ts +8 -0
  6. package/dist/browser.d.ts.map +1 -1
  7. package/dist/browser.js +144 -1
  8. package/dist/browser.js.map +1 -1
  9. package/dist/cli.js +3 -2
  10. package/dist/cli.js.map +1 -1
  11. package/dist/mcp-server-remote.d.ts.map +1 -1
  12. package/dist/mcp-server-remote.js +468 -33
  13. package/dist/mcp-server-remote.js.map +1 -1
  14. package/dist/mcp-server.d.ts.map +1 -1
  15. package/dist/mcp-server.js +513 -33
  16. package/dist/mcp-server.js.map +1 -1
  17. package/dist/performance/metrics.d.ts +2 -2
  18. package/dist/performance/metrics.d.ts.map +1 -1
  19. package/dist/performance/metrics.js +37 -1
  20. package/dist/performance/metrics.js.map +1 -1
  21. package/dist/persona-questionnaire.d.ts +103 -1
  22. package/dist/persona-questionnaire.d.ts.map +1 -1
  23. package/dist/persona-questionnaire.js +396 -35
  24. package/dist/persona-questionnaire.js.map +1 -1
  25. package/dist/personas.d.ts.map +1 -1
  26. package/dist/personas.js +4 -0
  27. package/dist/personas.js.map +1 -1
  28. package/dist/types.d.ts +2 -2
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/values/persona-values.d.ts +5 -0
  31. package/dist/values/persona-values.d.ts.map +1 -1
  32. package/dist/values/persona-values.js +79 -38
  33. package/dist/values/persona-values.js.map +1 -1
  34. package/dist/visual/cross-browser.d.ts.map +1 -1
  35. package/dist/visual/cross-browser.js +10 -1
  36. package/dist/visual/cross-browser.js.map +1 -1
  37. package/docs/personas/Persona-Index.md +126 -0
  38. package/docs/research/Values-Research.md +375 -0
  39. package/package.json +1 -1
@@ -36,6 +36,8 @@ import { getPersona, listPersonas, getCognitiveProfile, createCognitivePersona,
36
36
  import { createInitialEmotionalState, createEmotionalConfig, applyEmotionalTrigger, describeEmotionalState, shouldConsiderAbandonment, calculateAbandonmentModifier, calculateExplorationTendency, calculateDecisionSpeedModifier, } from "./cognitive/emotions.js";
37
37
  // Performance module imports
38
38
  import { capturePerformanceBaseline, detectPerformanceRegression, listPerformanceBaselines, } from "./performance/index.js";
39
+ // Values system (Schwartz's 10 Universal Values)
40
+ import { getPersonaValues, PERSONA_VALUE_PROFILES, rankInfluencePatternsForProfile, INFLUENCE_PATTERNS, } from "./values/index.js";
39
41
  // Version from package.json - single source of truth
40
42
  import { VERSION } from "./version.js";
41
43
  // Shared browser instance
@@ -53,6 +55,23 @@ async function getBrowser() {
53
55
  * v14.2.1: Retry wrapper for transient browser errors.
54
56
  * v14.2.5: Fixed page context desync after error recovery.
55
57
  * Retries operations that fail with common transient error patterns.
58
+ *
59
+ * v16.11.0: CRASH RESILIENCE PATTERN
60
+ * For tools that use context-level operations (setOffline, route interception),
61
+ * use an explicit try-catch + recovery pattern instead of withRetry:
62
+ *
63
+ * try {
64
+ * const result = await dangerousOperation();
65
+ * return { content: [{ type: "text", text: JSON.stringify(result) }] };
66
+ * } catch (error: any) {
67
+ * try { await browser.recoverBrowser(); } catch { }
68
+ * return { content: [{ type: "text", text: JSON.stringify({
69
+ * error: error.message, recovered: true
70
+ * }) }] };
71
+ * }
72
+ *
73
+ * This ensures the MCP server never crashes from unhandled browser errors.
74
+ * See chaos_test for the reference implementation.
56
75
  */
57
76
  async function withRetry(operation, options = {}) {
58
77
  const maxRetries = options.maxRetries ?? 2;
@@ -970,19 +989,56 @@ export async function startMcpServer() {
970
989
  blockUrls: z.array(z.string()).optional().describe("URL patterns to block"),
971
990
  }, async ({ url, networkLatency, offline, blockUrls }) => {
972
991
  const b = await getBrowser();
973
- const result = await runChaosTest(b, url, { networkLatency, offline, blockUrls });
974
- return {
975
- content: [
976
- {
977
- type: "text",
978
- text: JSON.stringify({
979
- passed: result.passed,
980
- errors: result.errors,
981
- duration: result.duration,
982
- }, null, 2),
983
- },
984
- ],
985
- };
992
+ try {
993
+ const result = await runChaosTest(b, url, { networkLatency, offline, blockUrls });
994
+ return {
995
+ content: [
996
+ {
997
+ type: "text",
998
+ text: JSON.stringify({
999
+ passed: result.passed,
1000
+ errors: result.errors,
1001
+ duration: result.duration,
1002
+ // v16.11.0: Include impact analysis in response
1003
+ impact: result.impact,
1004
+ }, null, 2),
1005
+ },
1006
+ ],
1007
+ };
1008
+ }
1009
+ catch (error) {
1010
+ // v16.11.0: Graceful error handling for chaos test crashes
1011
+ // Attempt browser recovery to prevent server crash
1012
+ try {
1013
+ await b.recoverBrowser();
1014
+ }
1015
+ catch {
1016
+ // Browser recovery failed, but continue with error response
1017
+ }
1018
+ return {
1019
+ content: [
1020
+ {
1021
+ type: "text",
1022
+ text: JSON.stringify({
1023
+ passed: false,
1024
+ errors: [`Chaos test crashed: ${error.message}`],
1025
+ duration: 0,
1026
+ impact: {
1027
+ loadTimeMs: 0,
1028
+ blockedResources: [],
1029
+ failedResources: [],
1030
+ delayedResources: [],
1031
+ pageCompleted: false,
1032
+ pageInteractive: false,
1033
+ consoleErrors: 0,
1034
+ degradationSummary: ["Test crashed - browser recovered"],
1035
+ },
1036
+ recovered: true,
1037
+ }, null, 2),
1038
+ },
1039
+ ],
1040
+ };
1041
+ }
986
1042
  });
987
1043
  server.tool("compare_personas", "Compare how different user personas experience a journey. REQUIRES API KEY for internal simulation. For API-free usage over remote MCP, use compare_personas_init + browser tools + compare_personas_record_result + compare_personas_summarize instead.", {
988
1044
  url: z.string().url().describe("Starting URL"),
@@ -1059,6 +1115,7 @@ export async function startMcpServer() {
1059
1115
  goal: z.string().describe("What the simulated user is trying to accomplish"),
1060
1116
  startUrl: z.string().url().describe("Starting URL for the journey"),
1061
1117
  customTraits: z.object({
1118
+ // Core 7 traits
1062
1119
  patience: z.number().min(0).max(1).optional(),
1063
1120
  riskTolerance: z.number().min(0).max(1).optional(),
1064
1121
  comprehension: z.number().min(0).max(1).optional(),
@@ -1066,7 +1123,26 @@ export async function startMcpServer() {
1066
1123
  curiosity: z.number().min(0).max(1).optional(),
1067
1124
  workingMemory: z.number().min(0).max(1).optional(),
1068
1125
  readingTendency: z.number().min(0).max(1).optional(),
1069
- }).optional().describe("Override specific cognitive traits"),
1126
+ // v16.11.0: Extended traits (18 more = 25 total)
1127
+ resilience: z.number().min(0).max(1).optional(),
1128
+ selfEfficacy: z.number().min(0).max(1).optional(),
1129
+ satisficing: z.number().min(0).max(1).optional(),
1130
+ trustCalibration: z.number().min(0).max(1).optional(),
1131
+ interruptRecovery: z.number().min(0).max(1).optional(),
1132
+ informationForaging: z.number().min(0).max(1).optional(),
1133
+ changeBlindness: z.number().min(0).max(1).optional(),
1134
+ anchoringBias: z.number().min(0).max(1).optional(),
1135
+ timeHorizon: z.number().min(0).max(1).optional(),
1136
+ attributionStyle: z.number().min(0).max(1).optional(),
1137
+ metacognitivePlanning: z.number().min(0).max(1).optional(),
1138
+ proceduralFluency: z.number().min(0).max(1).optional(),
1139
+ transferLearning: z.number().min(0).max(1).optional(),
1140
+ authoritySensitivity: z.number().min(0).max(1).optional(),
1141
+ emotionalContagion: z.number().min(0).max(1).optional(),
1142
+ fearOfMissingOut: z.number().min(0).max(1).optional(),
1143
+ socialProofSensitivity: z.number().min(0).max(1).optional(),
1144
+ mentalModelRigidity: z.number().min(0).max(1).optional(),
1145
+ }).optional().describe("Override specific cognitive traits (25 available)"),
1070
1146
  }, async ({ persona: personaName, goal, startUrl, customTraits }) => {
1071
1147
  // Get or create persona
1072
1148
  const existingPersona = getPersona(personaName);
@@ -1076,8 +1152,9 @@ export async function startMcpServer() {
1076
1152
  personaObj = createCognitivePersona(personaName, personaName, customTraits || {});
1077
1153
  }
1078
1154
  else if (customTraits) {
1079
- // Merge custom traits with defaults
1155
+ // v16.11.0: Full 25-trait default set (was only 7, causing trait dropout)
1080
1156
  const defaultTraits = {
1157
+ // Core 7 traits
1081
1158
  patience: 0.5,
1082
1159
  riskTolerance: 0.5,
1083
1160
  comprehension: 0.5,
@@ -1085,6 +1162,26 @@ export async function startMcpServer() {
1085
1162
  curiosity: 0.5,
1086
1163
  workingMemory: 0.5,
1087
1164
  readingTendency: 0.5,
1165
+ // Tier 1: Core (5 more)
1166
+ resilience: 0.5,
1167
+ selfEfficacy: 0.5,
1168
+ satisficing: 0.5,
1169
+ trustCalibration: 0.5,
1170
+ interruptRecovery: 0.5,
1171
+ // Tier 2-6: Extended (13 more)
1172
+ informationForaging: 0.5,
1173
+ changeBlindness: 0.3,
1174
+ anchoringBias: 0.5,
1175
+ timeHorizon: 0.5,
1176
+ attributionStyle: 0.5,
1177
+ metacognitivePlanning: 0.5,
1178
+ proceduralFluency: 0.5,
1179
+ transferLearning: 0.5,
1180
+ authoritySensitivity: 0.5,
1181
+ emotionalContagion: 0.5,
1182
+ fearOfMissingOut: 0.5,
1183
+ socialProofSensitivity: 0.5,
1184
+ mentalModelRigidity: 0.5,
1088
1185
  };
1089
1186
  personaObj = {
1090
1187
  ...existingPersona,
@@ -1130,6 +1227,11 @@ export async function startMcpServer() {
1130
1227
  // Navigate to start URL
1131
1228
  const b = await getBrowser();
1132
1229
  await b.navigate(startUrl);
1230
+ // v16.12.0: Include persona values for influence pattern analysis
1231
+ const personaValues = getPersonaValues(personaObj.name);
1232
+ const influencePatterns = personaValues
1233
+ ? rankInfluencePatternsForProfile(personaValues).slice(0, 5) // Top 5 most effective patterns
1234
+ : undefined;
1133
1235
  return {
1134
1236
  content: [
1135
1237
  {
@@ -1139,6 +1241,36 @@ export async function startMcpServer() {
1139
1241
  name: personaObj.name,
1140
1242
  description: personaObj.description,
1141
1243
  demographics: personaObj.demographics,
1244
+ values: personaValues ? {
1245
+ schwartz: {
1246
+ selfDirection: personaValues.selfDirection,
1247
+ stimulation: personaValues.stimulation,
1248
+ hedonism: personaValues.hedonism,
1249
+ achievement: personaValues.achievement,
1250
+ power: personaValues.power,
1251
+ security: personaValues.security,
1252
+ conformity: personaValues.conformity,
1253
+ tradition: personaValues.tradition,
1254
+ benevolence: personaValues.benevolence,
1255
+ universalism: personaValues.universalism,
1256
+ },
1257
+ higherOrder: {
1258
+ openness: personaValues.openness,
1259
+ selfEnhancement: personaValues.selfEnhancement,
1260
+ conservation: personaValues.conservation,
1261
+ selfTranscendence: personaValues.selfTranscendence,
1262
+ },
1263
+ sdt: {
1264
+ autonomyNeed: personaValues.autonomyNeed,
1265
+ competenceNeed: personaValues.competenceNeed,
1266
+ relatednessNeed: personaValues.relatednessNeed,
1267
+ },
1268
+ maslowLevel: personaValues.maslowLevel,
1269
+ } : undefined,
1270
+ influenceSusceptibility: influencePatterns?.map(ip => ({
1271
+ pattern: ip.pattern.name,
1272
+ susceptibility: ip.susceptibility,
1273
+ })),
1142
1274
  },
1143
1275
  cognitiveProfile: profile,
1144
1276
  initialState,
@@ -1301,27 +1433,147 @@ Begin the simulation now. Narrate your thoughts as this persona.
1301
1433
  ],
1302
1434
  };
1303
1435
  });
1304
- server.tool("list_cognitive_personas", "List all available personas with their cognitive traits", {}, async () => {
1305
- const names = listPersonas();
1306
- const personas = names.map(name => {
1436
+ server.tool("list_cognitive_personas", "List all available personas with their cognitive traits (includes accessibility and emotional personas)", {}, async () => {
1437
+ // v16.11.0: Include all persona types - BUILTIN + ACCESSIBILITY + EMOTIONAL
1438
+ const builtinNames = listPersonas();
1439
+ const accessibilityNames = listAccessibilityPersonas();
1440
+ // Built-in personas (power-user, first-timer, etc.)
1441
+ const builtinPersonas = builtinNames.map(name => {
1307
1442
  const p = getPersona(name);
1308
1443
  if (!p)
1309
1444
  return null;
1310
1445
  const profile = getCognitiveProfile(p);
1446
+ // v16.12.0: Include Schwartz values for each persona
1447
+ const values = getPersonaValues(p.name);
1311
1448
  return {
1312
1449
  name: p.name,
1313
1450
  description: p.description,
1451
+ category: "builtin",
1314
1452
  demographics: p.demographics,
1315
1453
  cognitiveTraits: profile.traits,
1316
1454
  attentionPattern: profile.attentionPattern,
1317
1455
  decisionStyle: profile.decisionStyle,
1456
+ values: values ? {
1457
+ schwartz: {
1458
+ selfDirection: values.selfDirection,
1459
+ stimulation: values.stimulation,
1460
+ hedonism: values.hedonism,
1461
+ achievement: values.achievement,
1462
+ power: values.power,
1463
+ security: values.security,
1464
+ conformity: values.conformity,
1465
+ tradition: values.tradition,
1466
+ benevolence: values.benevolence,
1467
+ universalism: values.universalism,
1468
+ },
1469
+ higherOrder: {
1470
+ openness: values.openness,
1471
+ selfEnhancement: values.selfEnhancement,
1472
+ conservation: values.conservation,
1473
+ selfTranscendence: values.selfTranscendence,
1474
+ },
1475
+ sdt: {
1476
+ autonomyNeed: values.autonomyNeed,
1477
+ competenceNeed: values.competenceNeed,
1478
+ relatednessNeed: values.relatednessNeed,
1479
+ },
1480
+ maslowLevel: values.maslowLevel,
1481
+ } : undefined,
1482
+ };
1483
+ }).filter(Boolean);
1484
+ // Accessibility personas (motor-tremor, low-vision, adhd, etc.)
1485
+ const accessibilityPersonas = accessibilityNames.map(name => {
1486
+ const p = getAccessibilityPersona(name);
1487
+ if (!p)
1488
+ return null;
1489
+ // v16.11.0: Compute disabilityType and barrierTypes from accessibilityTraits
1490
+ const traits = p.accessibilityTraits;
1491
+ let disabilityType = "General accessibility";
1492
+ const barrierTypes = [];
1493
+ if (traits?.tremor) {
1494
+ disabilityType = "Motor impairment (tremor)";
1495
+ barrierTypes.push("motor_precision", "touch_target");
1496
+ }
1497
+ if (traits?.visionLevel !== undefined && traits.visionLevel < 0.5) {
1498
+ disabilityType = "Low vision";
1499
+ barrierTypes.push("visual_clarity", "contrast");
1500
+ }
1501
+ if (traits?.colorBlindness) {
1502
+ disabilityType = `Color blindness (${traits.colorBlindness})`;
1503
+ barrierTypes.push("sensory");
1504
+ }
1505
+ if (traits?.processingSpeed !== undefined && traits.processingSpeed < 0.6) {
1506
+ disabilityType = "Cognitive (Processing)";
1507
+ barrierTypes.push("cognitive_load", "temporal");
1508
+ }
1509
+ if (traits?.attentionSpan !== undefined && traits.attentionSpan < 0.5) {
1510
+ if (!disabilityType.includes("Cognitive")) {
1511
+ disabilityType = "Cognitive (ADHD/Attention)";
1512
+ }
1513
+ barrierTypes.push("cognitive_load");
1514
+ }
1515
+ // Name-based fallback
1516
+ if (disabilityType === "General accessibility") {
1517
+ if (p.name.includes("deaf") || p.name.includes("hearing"))
1518
+ disabilityType = "Hearing impairment";
1519
+ else if (p.name.includes("motor"))
1520
+ disabilityType = "Motor impairment";
1521
+ else if (p.name.includes("vision") || p.name.includes("blind"))
1522
+ disabilityType = "Vision impairment";
1523
+ else if (p.name.includes("cognitive") || p.name.includes("adhd"))
1524
+ disabilityType = "Cognitive";
1525
+ }
1526
+ // v16.12.0: Include Schwartz values for accessibility personas
1527
+ const values = getPersonaValues(p.name);
1528
+ return {
1529
+ name: p.name,
1530
+ description: p.description,
1531
+ category: "accessibility",
1532
+ disabilityType,
1533
+ demographics: p.demographics,
1534
+ cognitiveTraits: p.cognitiveTraits || {},
1535
+ barrierTypes: [...new Set(barrierTypes)], // Deduplicate
1536
+ values: values ? {
1537
+ schwartz: {
1538
+ selfDirection: values.selfDirection,
1539
+ stimulation: values.stimulation,
1540
+ hedonism: values.hedonism,
1541
+ achievement: values.achievement,
1542
+ power: values.power,
1543
+ security: values.security,
1544
+ conformity: values.conformity,
1545
+ tradition: values.tradition,
1546
+ benevolence: values.benevolence,
1547
+ universalism: values.universalism,
1548
+ },
1549
+ higherOrder: {
1550
+ openness: values.openness,
1551
+ selfEnhancement: values.selfEnhancement,
1552
+ conservation: values.conservation,
1553
+ selfTranscendence: values.selfTranscendence,
1554
+ },
1555
+ sdt: {
1556
+ autonomyNeed: values.autonomyNeed,
1557
+ competenceNeed: values.competenceNeed,
1558
+ relatednessNeed: values.relatednessNeed,
1559
+ },
1560
+ maslowLevel: values.maslowLevel,
1561
+ } : undefined,
1318
1562
  };
1319
1563
  }).filter(Boolean);
1564
+ const allPersonas = [...builtinPersonas, ...accessibilityPersonas];
1320
1565
  return {
1321
1566
  content: [
1322
1567
  {
1323
1568
  type: "text",
1324
- text: JSON.stringify({ personas, count: personas.length }, null, 2),
1569
+ text: JSON.stringify({
1570
+ personas: allPersonas,
1571
+ count: allPersonas.length,
1572
+ categories: {
1573
+ builtin: builtinPersonas.length,
1574
+ accessibility: accessibilityPersonas.length,
1575
+ },
1576
+ }, null, 2),
1325
1577
  },
1326
1578
  ],
1327
1579
  };
@@ -1330,39 +1582,116 @@ Begin the simulation now. Narrate your thoughts as this persona.
1330
1582
  // Persona Questionnaire Tools (v16.5.0)
1331
1583
  // Research-based persona generation via questionnaire
1332
1584
  // =========================================================================
1333
- server.tool("persona_questionnaire_get", "Get the persona questionnaire for building a custom persona. Returns research-backed questions that map to cognitive traits. Use comprehensive=true for all 25 traits, or leave false for 8 core traits.", {
1585
+ server.tool("persona_questionnaire_get", "Get the persona questionnaire for building a custom persona. Returns research-backed questions that map to cognitive traits. Use comprehensive=true for all 25 traits, or leave false for 8 core traits. v16.12.0: Now includes category question for value safeguards.", {
1334
1586
  comprehensive: z.boolean().optional().default(false).describe("Include all 25 traits (true) or just 8 core traits (false)"),
1335
1587
  traits: z.array(z.string()).optional().describe("Specific trait names to include (overrides comprehensive)"),
1336
- }, async ({ comprehensive, traits }) => {
1337
- const { generatePersonaQuestionnaire, formatForAskUserQuestion } = await import("./persona-questionnaire.js");
1588
+ includeCategory: z.boolean().optional().default(true).describe("Include category selection question for value safeguards"),
1589
+ }, async ({ comprehensive, traits, includeCategory }) => {
1590
+ const { generatePersonaQuestionnaire, formatForAskUserQuestion, CATEGORY_QUESTION, CATEGORY_VALUE_PRESETS, } = await import("./persona-questionnaire.js");
1338
1591
  const questions = generatePersonaQuestionnaire({
1339
1592
  comprehensive,
1340
1593
  traits: traits,
1341
1594
  });
1342
1595
  const formatted = formatForAskUserQuestion(questions);
1596
+ // v16.12.0: Include category question and value presets
1597
+ const categoryInfo = includeCategory ? {
1598
+ categoryQuestion: CATEGORY_QUESTION,
1599
+ categoryPresets: CATEGORY_VALUE_PRESETS.map(p => ({
1600
+ category: p.category,
1601
+ description: p.description,
1602
+ valueStrategy: p.valueStrategy,
1603
+ guidance: p.guidance,
1604
+ })),
1605
+ } : undefined;
1343
1606
  return {
1344
1607
  content: [
1345
1608
  {
1346
1609
  type: "text",
1347
1610
  text: JSON.stringify({
1348
- instructions: "Present these questions to the user one at a time or all at once. Each answer maps to a trait value. After collecting answers, use persona_questionnaire_build to create the persona.",
1611
+ instructions: `
1612
+ PERSONA CREATION WORKFLOW (v16.12.0):
1613
+
1614
+ 1. FIRST: Ask the category question to determine value assignment strategy:
1615
+ - Cognitive disabilities (ADHD, autism) → specific values from neuroscience
1616
+ - Physical disabilities (motor, mobility) → security/autonomy shifts only
1617
+ - Sensory differences (color-blind, deaf) → neutral values (perception ≠ motivation)
1618
+ - Emotional traits (anxious, confident) → trait-based psychology values
1619
+ - General → population baseline
1620
+
1621
+ 2. THEN: Ask the trait questions to build cognitive profile
1622
+
1623
+ 3. FINALLY: Call persona_questionnaire_build with:
1624
+ - name, description, answers (from trait questions)
1625
+ - category (from category question)
1626
+ - The tool will apply appropriate value safeguards based on category
1627
+
1628
+ This ensures personas are grounded in research, not stereotypes.
1629
+ `.trim(),
1630
+ categoryInfo,
1349
1631
  questionCount: questions.length,
1350
1632
  questions: formatted,
1351
- rawQuestions: questions, // Include raw for programmatic use
1633
+ rawQuestions: questions,
1352
1634
  }, null, 2),
1353
1635
  },
1354
1636
  ],
1355
1637
  };
1356
1638
  });
1357
- server.tool("persona_questionnaire_build", "Build a custom persona from questionnaire answers. Answers should be a map of trait names to values (0-1). Missing traits will use intelligent defaults based on research correlations.", {
1639
+ server.tool("persona_category_guidance", "Get research-based guidance for a specific persona category. Explains what values are appropriate and why, with citations. Use before building a persona to understand category constraints.", {
1640
+ category: z.enum(["cognitive", "physical", "sensory", "emotional", "general"]).describe("Persona category to get guidance for"),
1641
+ }, async ({ category }) => {
1642
+ const { getCategoryValuePreset, COGNITIVE_SUBTYPES, } = await import("./persona-questionnaire.js");
1643
+ const preset = getCategoryValuePreset(category);
1644
+ // Get subtypes if cognitive
1645
+ const subtypes = category === "cognitive"
1646
+ ? Object.entries(COGNITIVE_SUBTYPES).map(([name, info]) => ({
1647
+ name,
1648
+ values: info.values,
1649
+ researchBasis: info.researchBasis,
1650
+ }))
1651
+ : undefined;
1652
+ return {
1653
+ content: [
1654
+ {
1655
+ type: "text",
1656
+ text: JSON.stringify({
1657
+ category,
1658
+ description: preset.description,
1659
+ valueStrategy: preset.valueStrategy,
1660
+ guidance: preset.guidance,
1661
+ defaultValues: preset.defaultValues,
1662
+ researchBasis: preset.researchBasis,
1663
+ subtypes,
1664
+ safeguards: {
1665
+ cognitive: "Use specific values based on the neurobiological profile (e.g., ADHD = high stimulation). NOT stereotyping - grounded in dopamine/reward research.",
1666
+ physical: "Apply security ↑ and autonomyNeed ↑ shifts for predictable interfaces. Don't change core personality values.",
1667
+ sensory: "Use neutral (0.5) values. Sensory perception ≠ motivational psychology. Color-blindness doesn't make someone more/less achievement-oriented.",
1668
+ emotional: "Apply trait-based values from personality psychology (e.g., anxiety = high security-seeking).",
1669
+ general: "Use population baselines. Specific characteristics come from cognitive traits, not disability-based value shifts.",
1670
+ }[category],
1671
+ }, null, 2),
1672
+ },
1673
+ ],
1674
+ };
1675
+ });
1676
+ server.tool("persona_questionnaire_build", "Build a custom persona from questionnaire answers with category-aware value safeguards. Answers map trait names to values (0-1). Category determines value assignment strategy: cognitive disabilities get specific values, physical get security/autonomy shifts, sensory-only get neutral values.", {
1358
1677
  name: z.string().describe("Name for the new persona"),
1359
1678
  description: z.string().describe("Description of the persona"),
1360
1679
  answers: z.record(z.string(), z.number()).describe("Map of trait names to values (0-1), e.g. {patience: 0.25, riskTolerance: 0.75}"),
1680
+ category: z.enum(["cognitive", "physical", "sensory", "emotional", "general"]).optional().describe("Persona category for value safeguards. Auto-detected from name/description if not provided."),
1681
+ valueOverrides: z.record(z.string(), z.number()).optional().describe("Override specific Schwartz values (0-1), e.g. {security: 0.8, stimulation: 0.3}"),
1361
1682
  save: z.boolean().optional().default(true).describe("Save the persona to disk for future use"),
1362
- }, async ({ name, description, answers, save }) => {
1363
- const { buildTraitsFromAnswers, getTraitLabel, getTraitBehaviors } = await import("./persona-questionnaire.js");
1683
+ }, async ({ name, description, answers, category, valueOverrides, save }) => {
1684
+ const { buildTraitsFromAnswers, getTraitLabel, getTraitBehaviors, detectPersonaCategory, buildValuesFromCategory, validateCategoryValues, getCognitiveSubtypeValues, } = await import("./persona-questionnaire.js");
1685
+ // v16.12.0: Detect category if not provided
1686
+ const detectedCategory = category || detectPersonaCategory(name, description);
1364
1687
  // Build traits from answers with research-based correlations
1365
1688
  const traits = buildTraitsFromAnswers(answers);
1689
+ // v16.12.0: Build values based on category with safeguards
1690
+ // Check for cognitive subtype (e.g., adhd-combined, autism-spectrum)
1691
+ const subtypeValues = getCognitiveSubtypeValues(name);
1692
+ const categoryResult = buildValuesFromCategory(detectedCategory, subtypeValues?.values || valueOverrides);
1693
+ // Validate that values match category guidelines
1694
+ const warnings = validateCategoryValues(detectedCategory, categoryResult.values);
1366
1695
  // Create the persona
1367
1696
  const persona = createCognitivePersona(name, description, traits, {});
1368
1697
  // Save if requested
@@ -1394,6 +1723,17 @@ Begin the simulation now. Narrate your thoughts as this persona.
1394
1723
  },
1395
1724
  cognitiveTraits: traits,
1396
1725
  traitSummary,
1726
+ // v16.12.0: Category-aware values
1727
+ category: {
1728
+ detected: detectedCategory,
1729
+ strategy: categoryResult.valueStrategy,
1730
+ guidance: categoryResult.guidance,
1731
+ },
1732
+ values: categoryResult.values,
1733
+ researchBasis: subtypeValues
1734
+ ? [...categoryResult.researchBasis, subtypeValues.researchBasis]
1735
+ : categoryResult.researchBasis,
1736
+ warnings: warnings.length > 0 ? warnings : undefined,
1397
1737
  savedPath,
1398
1738
  usage: `Use persona "${name}" with cognitive-journey or other commands`,
1399
1739
  }, null, 2),
@@ -1445,6 +1785,116 @@ Begin the simulation now. Narrate your thoughts as this persona.
1445
1785
  };
1446
1786
  });
1447
1787
  // =========================================================================
1788
+ // Values System Tools (v16.12.0)
1789
+ // Schwartz's 10 Universal Values, Self-Determination Theory, Maslow
1790
+ // =========================================================================
1791
+ server.tool("persona_values_lookup", "Look up the values profile for a persona (Schwartz's 10 Universal Values, SDT needs, Maslow level). Values describe WHO the persona is at a deeper motivational level, informing influence susceptibility.", {
1792
+ persona: z.string().describe("Persona name (e.g., 'first-timer', 'power-user', 'anxious-user')"),
1793
+ includeInfluencePatterns: z.boolean().optional().default(true).describe("Include ranked influence patterns this persona is susceptible to"),
1794
+ }, async ({ persona, includeInfluencePatterns }) => {
1795
+ const values = getPersonaValues(persona);
1796
+ if (!values) {
1797
+ // List available personas with values
1798
+ const availablePersonas = PERSONA_VALUE_PROFILES.map(p => p.personaName);
1799
+ return {
1800
+ content: [
1801
+ {
1802
+ type: "text",
1803
+ text: JSON.stringify({
1804
+ error: `No values profile found for persona: ${persona}`,
1805
+ availablePersonas,
1806
+ note: "Values are defined for all built-in personas. Custom personas can have values added via the questionnaire.",
1807
+ }, null, 2),
1808
+ },
1809
+ ],
1810
+ };
1811
+ }
1812
+ // Find rationale for this persona
1813
+ const profile = PERSONA_VALUE_PROFILES.find(p => p.personaName.toLowerCase() === persona.toLowerCase());
1814
+ // Calculate influence susceptibility if requested
1815
+ let influencePatterns;
1816
+ if (includeInfluencePatterns) {
1817
+ const ranked = rankInfluencePatternsForProfile(values);
1818
+ influencePatterns = ranked.slice(0, 7).map(r => ({
1819
+ pattern: r.pattern.name,
1820
+ susceptibility: r.susceptibility,
1821
+ description: r.pattern.description,
1822
+ }));
1823
+ }
1824
+ return {
1825
+ content: [
1826
+ {
1827
+ type: "text",
1828
+ text: JSON.stringify({
1829
+ persona,
1830
+ rationale: profile?.rationale,
1831
+ schwartzValues: {
1832
+ selfDirection: { value: values.selfDirection, meaning: "Independent thought, creativity, freedom" },
1833
+ stimulation: { value: values.stimulation, meaning: "Excitement, novelty, challenge" },
1834
+ hedonism: { value: values.hedonism, meaning: "Pleasure, sensuous gratification" },
1835
+ achievement: { value: values.achievement, meaning: "Personal success through competence" },
1836
+ power: { value: values.power, meaning: "Social status, prestige, control" },
1837
+ security: { value: values.security, meaning: "Safety, harmony, stability" },
1838
+ conformity: { value: values.conformity, meaning: "Restraint of actions that harm others" },
1839
+ tradition: { value: values.tradition, meaning: "Respect for customs, heritage" },
1840
+ benevolence: { value: values.benevolence, meaning: "Welfare of close others" },
1841
+ universalism: { value: values.universalism, meaning: "Tolerance, social justice, environment" },
1842
+ },
1843
+ higherOrderValues: {
1844
+ openness: { value: values.openness, meaning: "(selfDirection + stimulation) / 2" },
1845
+ selfEnhancement: { value: values.selfEnhancement, meaning: "(achievement + power) / 2" },
1846
+ conservation: { value: values.conservation, meaning: "(security + conformity + tradition) / 3" },
1847
+ selfTranscendence: { value: values.selfTranscendence, meaning: "(benevolence + universalism) / 2" },
1848
+ },
1849
+ selfDeterminationTheory: {
1850
+ autonomyNeed: { value: values.autonomyNeed, meaning: "Need for choice and control" },
1851
+ competenceNeed: { value: values.competenceNeed, meaning: "Need to feel capable" },
1852
+ relatednessNeed: { value: values.relatednessNeed, meaning: "Need for connection" },
1853
+ },
1854
+ maslowLevel: {
1855
+ level: values.maslowLevel,
1856
+ meaning: values.maslowLevel === "physiological" ? "Basic survival needs"
1857
+ : values.maslowLevel === "safety" ? "Security and stability"
1858
+ : values.maslowLevel === "belonging" ? "Social connection and love"
1859
+ : values.maslowLevel === "esteem" ? "Achievement and recognition"
1860
+ : "Self-fulfillment and growth",
1861
+ },
1862
+ influencePatterns,
1863
+ researchBasis: {
1864
+ schwartz: "Schwartz, S. H. (1992, 2012). Theory of Basic Human Values. DOI: 10.1016/S0065-2601(08)60281-6",
1865
+ sdt: "Deci, E. L., & Ryan, R. M. (1985, 2000). Self-Determination Theory. DOI: 10.1037/0003-066X.55.1.68",
1866
+ maslow: "Maslow, A. H. (1943). A Theory of Human Motivation. DOI: 10.1037/h0054346",
1867
+ },
1868
+ }, null, 2),
1869
+ },
1870
+ ],
1871
+ };
1872
+ });
1873
+ server.tool("list_influence_patterns", "List all research-backed influence/persuasion patterns and which persona values make someone susceptible to each pattern. Based on Cialdini, Kahneman, and behavioral economics research.", {}, async () => {
1874
+ // INFLUENCE_PATTERNS is an array of InfluencePattern objects
1875
+ const patterns = INFLUENCE_PATTERNS.map(pattern => ({
1876
+ name: pattern.name,
1877
+ description: pattern.description,
1878
+ researchBasis: pattern.researchBasis,
1879
+ targetValues: pattern.targetValues,
1880
+ mechanism: pattern.mechanism,
1881
+ examples: pattern.examples,
1882
+ }));
1883
+ return {
1884
+ content: [
1885
+ {
1886
+ type: "text",
1887
+ text: JSON.stringify({
1888
+ count: patterns.length,
1889
+ patterns,
1890
+ usage: "Use persona_values_lookup to see which patterns a specific persona is susceptible to",
1891
+ note: "These patterns describe psychological influence mechanisms. Use ethically for UX optimization, not manipulation.",
1892
+ }, null, 2),
1893
+ },
1894
+ ],
1895
+ };
1896
+ });
1897
+ // =========================================================================
1448
1898
  // Persona Comparison Session Bridge (API-free via Claude orchestration)
1449
1899
  // =========================================================================
1450
1900
  server.tool("compare_personas_init", "Initialize a multi-persona comparison session. Returns all persona profiles and initial states. Claude orchestrates the journeys using browser tools + cognitive_journey_update_state, then records results. NO API KEY NEEDED - Claude is the brain.", {
@@ -1976,6 +2426,8 @@ Begin the simulation now. Narrate your thoughts as this persona.
1976
2426
  runs: z.number().optional().default(3).describe("Number of runs to average"),
1977
2427
  }, async ({ url, name, runs }) => {
1978
2428
  const result = await capturePerformanceBaseline(url, { name, runs });
2429
+ // v16.11.0: Return all available metrics, not just core 4
2430
+ const m = result.metrics;
1979
2431
  return {
1980
2432
  content: [
1981
2433
  {
@@ -1983,19 +2435,47 @@ Begin the simulation now. Narrate your thoughts as this persona.
1983
2435
  text: JSON.stringify({
1984
2436
  name: result.name,
1985
2437
  url: result.url,
1986
- lcp: result.metrics.lcp,
1987
- fcp: result.metrics.fcp,
1988
- ttfb: result.metrics.ttfb,
1989
- cls: result.metrics.cls,
2438
+ // Core Web Vitals
2439
+ coreWebVitals: {
2440
+ lcp: m.lcp,
2441
+ lcpRating: m.lcpRating,
2442
+ fid: m.fid,
2443
+ fidRating: m.fidRating,
2444
+ cls: m.cls,
2445
+ clsRating: m.clsRating,
2446
+ },
2447
+ // Additional timing metrics
2448
+ timingMetrics: {
2449
+ fcp: m.fcp,
2450
+ fcpRating: m.fcpRating,
2451
+ ttfb: m.ttfb,
2452
+ ttfbRating: m.ttfbRating,
2453
+ tti: m.tti,
2454
+ tbt: m.tbt,
2455
+ domContentLoaded: m.domContentLoaded,
2456
+ load: m.load,
2457
+ },
2458
+ // Resource metrics
2459
+ resourceMetrics: {
2460
+ resourceCount: m.resourceCount,
2461
+ transferSize: m.transferSize,
2462
+ },
2463
+ // Flat copy for backward compatibility
2464
+ metrics: {
2465
+ lcp: m.lcp,
2466
+ fcp: m.fcp,
2467
+ ttfb: m.ttfb,
2468
+ cls: m.cls,
2469
+ },
1990
2470
  }, null, 2),
1991
2471
  },
1992
2472
  ],
1993
2473
  };
1994
2474
  });
1995
- server.tool("perf_regression", "Detect performance regression against baseline with configurable sensitivity. Uses dual thresholds: both percentage AND absolute change must be exceeded. Profiles: strict (CI/CD, FCP 10%/50ms), normal (default, FCP 20%/100ms), lenient (dev, FCP 30%/200ms). Sub-50ms FCP variations ignored by default.", {
2475
+ server.tool("perf_regression", "Detect performance regression against baseline with configurable sensitivity. Uses dual thresholds: both percentage AND absolute change must be exceeded. Profiles: strict (perf envs, FCP 10%/50ms), normal (default, FCP 20%/100ms), ci (automated pipelines, FCP 25%/150ms), lenient (dev, FCP 30%/200ms).", {
1996
2476
  url: z.string().url().describe("URL to test"),
1997
2477
  baselineName: z.string().describe("Name of baseline to compare against"),
1998
- sensitivity: z.enum(["strict", "normal", "lenient"]).optional().default("normal").describe("Sensitivity profile: strict (CI/CD), normal (default), lenient (development)"),
2478
+ sensitivity: z.enum(["strict", "normal", "ci", "lenient"]).optional().default("normal").describe("Sensitivity profile: strict (perf testing), normal (local dev), ci (automated pipelines), lenient (development)"),
1999
2479
  thresholdLcp: z.number().optional().describe("Override LCP threshold percentage"),
2000
2480
  }, async ({ url, baselineName, sensitivity, thresholdLcp }) => {
2001
2481
  const result = await detectPerformanceRegression(url, baselineName, {