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.
- package/README.md +32 -0
- package/dist/analysis/chaos-testing.d.ts.map +1 -1
- package/dist/analysis/chaos-testing.js +8 -1
- package/dist/analysis/chaos-testing.js.map +1 -1
- package/dist/browser.d.ts +8 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +144 -1
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +468 -33
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +513 -33
- package/dist/mcp-server.js.map +1 -1
- package/dist/performance/metrics.d.ts +2 -2
- package/dist/performance/metrics.d.ts.map +1 -1
- package/dist/performance/metrics.js +37 -1
- package/dist/performance/metrics.js.map +1 -1
- package/dist/persona-questionnaire.d.ts +103 -1
- package/dist/persona-questionnaire.d.ts.map +1 -1
- package/dist/persona-questionnaire.js +396 -35
- package/dist/persona-questionnaire.js.map +1 -1
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +4 -0
- package/dist/personas.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/values/persona-values.d.ts +5 -0
- package/dist/values/persona-values.d.ts.map +1 -1
- package/dist/values/persona-values.js +79 -38
- package/dist/values/persona-values.js.map +1 -1
- package/dist/visual/cross-browser.d.ts.map +1 -1
- package/dist/visual/cross-browser.js +10 -1
- package/dist/visual/cross-browser.js.map +1 -1
- package/docs/personas/Persona-Index.md +126 -0
- package/docs/research/Values-Research.md +375 -0
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
1306
|
-
const
|
|
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({
|
|
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
|
-
|
|
1337
|
-
|
|
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:
|
|
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,
|
|
1633
|
+
rawQuestions: questions,
|
|
1352
1634
|
}, null, 2),
|
|
1353
1635
|
},
|
|
1354
1636
|
],
|
|
1355
1637
|
};
|
|
1356
1638
|
});
|
|
1357
|
-
server.tool("
|
|
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
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
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 (
|
|
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 (
|
|
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, {
|