cbrowser 17.2.0 → 17.3.1

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.
@@ -40,6 +40,14 @@ import { capturePerformanceBaseline, detectPerformanceRegression, listPerformanc
40
40
  import { getPersonaValues, PERSONA_VALUE_PROFILES, rankInfluencePatternsForProfile, INFLUENCE_PATTERNS, } from "./values/index.js";
41
41
  // Version from package.json - single source of truth
42
42
  import { VERSION } from "./version.js";
43
+ // Persona questionnaire imports
44
+ import { generatePersonaQuestionnaire, buildTraitsFromAnswers, TRAIT_REFERENCE_MATRIX, deriveValuesFromTraits, } from "./persona-questionnaire.js";
45
+ // ============================================================================
46
+ // CRITICAL: Redirect console.log to stderr for MCP stdio transport
47
+ // MCP uses stdout for JSON-RPC messages. Any console.log output corrupts
48
+ // the protocol and causes "Unexpected token" JSON parsing errors in clients.
49
+ // ============================================================================
50
+ console.log = (...args) => console.error(...args);
43
51
  // Shared browser instance
44
52
  let browser = null;
45
53
  async function getBrowser() {
@@ -127,6 +135,142 @@ async function withRetry(operation, options = {}) {
127
135
  }
128
136
  throw lastError;
129
137
  }
138
+ const VALUES_QUESTIONS = [
139
+ {
140
+ id: "security", value: "security",
141
+ question: "How important is safety and stability to this persona?",
142
+ options: [
143
+ { value: 0.0, label: "Not Important", description: "Takes risks freely, ignores safety warnings" },
144
+ { value: 0.33, label: "Somewhat Important", description: "Considers safety but willing to take chances" },
145
+ { value: 0.67, label: "Important", description: "Prefers established, secure options" },
146
+ { value: 1.0, label: "Very Important", description: "Prioritizes safety above almost everything" },
147
+ ],
148
+ },
149
+ {
150
+ id: "stimulation", value: "stimulation",
151
+ question: "How much does this persona seek excitement and novelty?",
152
+ options: [
153
+ { value: 0.0, label: "Avoids", description: "Prefers predictable, calm experiences" },
154
+ { value: 0.33, label: "Occasionally", description: "Open to new things but not seeking them" },
155
+ { value: 0.67, label: "Seeks", description: "Actively looks for new and exciting experiences" },
156
+ { value: 1.0, label: "Craves", description: "Constantly seeking stimulation and novelty" },
157
+ ],
158
+ },
159
+ {
160
+ id: "achievement", value: "achievement",
161
+ question: "How driven is this persona by personal success and competence?",
162
+ options: [
163
+ { value: 0.0, label: "Not Driven", description: "Success is not a priority" },
164
+ { value: 0.33, label: "Moderately", description: "Likes to succeed but not at all costs" },
165
+ { value: 0.67, label: "Driven", description: "Works hard to demonstrate competence" },
166
+ { value: 1.0, label: "Highly Driven", description: "Success and achievement are paramount" },
167
+ ],
168
+ },
169
+ {
170
+ id: "conformity", value: "conformity",
171
+ question: "How much does this persona follow social expectations and norms?",
172
+ options: [
173
+ { value: 0.0, label: "Independent", description: "Makes own rules, ignores conventions" },
174
+ { value: 0.33, label: "Flexible", description: "Follows norms when convenient" },
175
+ { value: 0.67, label: "Compliant", description: "Generally follows social expectations" },
176
+ { value: 1.0, label: "Traditional", description: "Strongly adheres to social norms" },
177
+ ],
178
+ },
179
+ {
180
+ id: "hedonism", value: "hedonism",
181
+ question: "How much does this persona prioritize pleasure and enjoyment?",
182
+ options: [
183
+ { value: 0.0, label: "Practical", description: "Prioritizes function over pleasure" },
184
+ { value: 0.33, label: "Balanced", description: "Enjoys pleasure but not a priority" },
185
+ { value: 0.67, label: "Pleasure-Seeking", description: "Actively seeks enjoyable experiences" },
186
+ { value: 1.0, label: "Hedonistic", description: "Pleasure and enjoyment are top priorities" },
187
+ ],
188
+ },
189
+ {
190
+ id: "power", value: "power",
191
+ question: "How important is social status and influence to this persona?",
192
+ options: [
193
+ { value: 0.0, label: "Not Important", description: "Doesn't care about status or control" },
194
+ { value: 0.33, label: "Minor Concern", description: "Aware of status but not driven by it" },
195
+ { value: 0.67, label: "Important", description: "Values influence and recognition" },
196
+ { value: 1.0, label: "Critical", description: "Status and control are major motivators" },
197
+ ],
198
+ },
199
+ {
200
+ id: "tradition", value: "tradition",
201
+ question: "How much does this persona value cultural and family traditions?",
202
+ options: [
203
+ { value: 0.0, label: "Progressive", description: "Embraces change, questions traditions" },
204
+ { value: 0.33, label: "Moderate", description: "Respects traditions but open to change" },
205
+ { value: 0.67, label: "Traditional", description: "Values and maintains traditions" },
206
+ { value: 1.0, label: "Strongly Traditional", description: "Traditions are core to identity" },
207
+ ],
208
+ },
209
+ {
210
+ id: "benevolence", value: "benevolence",
211
+ question: "How much does this persona prioritize caring for close others?",
212
+ options: [
213
+ { value: 0.0, label: "Self-Focused", description: "Prioritizes own needs over others" },
214
+ { value: 0.33, label: "Balanced", description: "Cares for others when convenient" },
215
+ { value: 0.67, label: "Caring", description: "Actively helps and supports close others" },
216
+ { value: 1.0, label: "Devoted", description: "Others' welfare is a top priority" },
217
+ ],
218
+ },
219
+ {
220
+ id: "universalism", value: "universalism",
221
+ question: "How much does this persona care about broader social and environmental issues?",
222
+ options: [
223
+ { value: 0.0, label: "Narrow Focus", description: "Focuses on immediate concerns only" },
224
+ { value: 0.33, label: "Aware", description: "Somewhat concerned about broader issues" },
225
+ { value: 0.67, label: "Engaged", description: "Actively considers social/environmental impact" },
226
+ { value: 1.0, label: "Activist", description: "Deeply committed to social justice and environment" },
227
+ ],
228
+ },
229
+ {
230
+ id: "selfDirection", value: "selfDirection",
231
+ question: "How much does this persona value independence and autonomy?",
232
+ options: [
233
+ { value: 0.0, label: "Guided", description: "Prefers clear direction from others" },
234
+ { value: 0.33, label: "Moderate", description: "Likes some guidance but can be independent" },
235
+ { value: 0.67, label: "Independent", description: "Prefers making own choices" },
236
+ { value: 1.0, label: "Autonomous", description: "Strongly values independence and self-reliance" },
237
+ ],
238
+ },
239
+ ];
240
+ const questionnaireSessionsMap = new Map();
241
+ function getQuestionnaireSession(sessionId) {
242
+ return questionnaireSessionsMap.get(sessionId);
243
+ }
244
+ function setQuestionnaireSession(sessionId, session) {
245
+ questionnaireSessionsMap.set(sessionId, session);
246
+ }
247
+ function clearQuestionnaireSession(sessionId) {
248
+ questionnaireSessionsMap.delete(sessionId);
249
+ }
250
+ function getTraitHeader(trait) {
251
+ const headers = {
252
+ patience: "Patience", riskTolerance: "Risk", comprehension: "Comprehension",
253
+ persistence: "Persistence", curiosity: "Curiosity", workingMemory: "Memory",
254
+ readingTendency: "Reading", resilience: "Resilience", selfEfficacy: "Confidence",
255
+ satisficing: "Decisions", trustCalibration: "Trust", interruptRecovery: "Focus",
256
+ informationForaging: "Search Style", changeBlindness: "Awareness", anchoringBias: "Anchoring",
257
+ timeHorizon: "Time Focus", attributionStyle: "Attribution", metacognitivePlanning: "Planning",
258
+ proceduralFluency: "Procedures", transferLearning: "Transfer", authoritySensitivity: "Authority",
259
+ emotionalContagion: "Emotional", fearOfMissingOut: "FOMO", socialProofSensitivity: "Social Proof",
260
+ mentalModelRigidity: "Flexibility",
261
+ };
262
+ return headers[trait] || trait;
263
+ }
264
+ function convertToThirdPerson(question) {
265
+ return question
266
+ .replace(/\bdo you\b/gi, "does this persona")
267
+ .replace(/\bare you\b/gi, "is this persona")
268
+ .replace(/\byou're\b/gi, "this persona is")
269
+ .replace(/\byou've\b/gi, "they have")
270
+ .replace(/\byou'd\b/gi, "they would")
271
+ .replace(/\byour\b/gi, "their")
272
+ .replace(/\byou\b/gi, "they");
273
+ }
130
274
  // Session storage (in-memory, cleared when server restarts)
131
275
  const comparisonSessions = new Map();
132
276
  // WCAG criteria reference for barrier mapping
@@ -435,6 +579,62 @@ async function registerCBrowserTools() {
435
579
  };
436
580
  });
437
581
  });
582
+ server.tool("scroll", "Scroll the page in a direction. Use when content might be below the fold or to navigate long pages.", {
583
+ direction: z.enum(["down", "up", "top", "bottom"]).default("down").describe("Scroll direction: down (400px), up (400px), top (page start), bottom (page end)"),
584
+ amount: z.number().optional().describe("Custom scroll amount in pixels (only for up/down)"),
585
+ }, async ({ direction, amount }) => {
586
+ const b = await getBrowser();
587
+ const page = await b.getPage();
588
+ try {
589
+ const scrollAmount = amount || 400;
590
+ switch (direction) {
591
+ case "top":
592
+ await page.evaluate(() => window.scrollTo({ top: 0, behavior: "smooth" }));
593
+ break;
594
+ case "bottom":
595
+ await page.evaluate(() => window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }));
596
+ break;
597
+ case "up":
598
+ await page.evaluate((amt) => window.scrollBy({ top: -amt, behavior: "smooth" }), scrollAmount);
599
+ break;
600
+ case "down":
601
+ default:
602
+ await page.evaluate((amt) => window.scrollBy({ top: amt, behavior: "smooth" }), scrollAmount);
603
+ break;
604
+ }
605
+ // Wait for scroll animation
606
+ await new Promise(r => setTimeout(r, 300));
607
+ // Get new scroll position
608
+ const scrollY = await page.evaluate(() => window.scrollY);
609
+ const scrollHeight = await page.evaluate(() => document.body.scrollHeight);
610
+ const viewportHeight = await page.evaluate(() => window.innerHeight);
611
+ return {
612
+ content: [{
613
+ type: "text",
614
+ text: JSON.stringify({
615
+ success: true,
616
+ direction,
617
+ scrollPosition: scrollY,
618
+ scrollHeight,
619
+ viewportHeight,
620
+ atTop: scrollY === 0,
621
+ atBottom: scrollY + viewportHeight >= scrollHeight - 10,
622
+ }, null, 2),
623
+ }],
624
+ };
625
+ }
626
+ catch (error) {
627
+ return {
628
+ content: [{
629
+ type: "text",
630
+ text: JSON.stringify({
631
+ success: false,
632
+ error: error instanceof Error ? error.message : String(error),
633
+ }, null, 2),
634
+ }],
635
+ };
636
+ }
637
+ });
438
638
  // =========================================================================
439
639
  // Extraction Tools
440
640
  // =========================================================================
@@ -2816,6 +3016,330 @@ This ensures personas are grounded in research, not stereotypes.
2816
3016
  ],
2817
3017
  };
2818
3018
  });
3019
+ // =========================================================================
3020
+ // Persona Creation Tools (v17.3.0)
3021
+ // =========================================================================
3022
+ server.tool("persona_create_start", "Start creating a custom persona. YOU MUST USE YOUR AskUserQuestion TOOL to present the choice to the user.", {
3023
+ persona_name: z.string().describe("Name for the new persona"),
3024
+ comprehensive: z.boolean().optional().describe("Include all 25 traits (true) or just core 8 traits (false)"),
3025
+ }, async ({ persona_name, comprehensive = false }) => {
3026
+ const sessionId = "local-session";
3027
+ const existingSession = getQuestionnaireSession(sessionId);
3028
+ if (existingSession) {
3029
+ return {
3030
+ content: [{
3031
+ type: "text",
3032
+ text: JSON.stringify({
3033
+ warning: "A questionnaire session is already in progress",
3034
+ existing_persona: existingSession.personaName,
3035
+ progress: `${existingSession.currentIndex}/${existingSession.questions.length} questions answered`,
3036
+ options: ["Use persona_create_questionnaire_answer to continue", "Use persona_create_cancel to discard"],
3037
+ }, null, 2),
3038
+ }],
3039
+ };
3040
+ }
3041
+ const traitCount = comprehensive ? 25 : 8;
3042
+ const totalQuestions = traitCount + VALUES_QUESTIONS.length;
3043
+ return {
3044
+ content: [{
3045
+ type: "text",
3046
+ text: `ACTION REQUIRED: Use your AskUserQuestion tool to ask the user:
3047
+
3048
+ Question: "How would you like to create the '${persona_name}' persona?"
3049
+ Header: "Create Mode"
3050
+
3051
+ Options:
3052
+ 1. "Fill out questionnaire (Recommended)" - Answer ${totalQuestions} questions about this persona's cognitive traits and values.
3053
+ 2. "Describe the persona" - Provide a text description and I'll infer appropriate trait and value settings.
3054
+
3055
+ After the user chooses:
3056
+ - If "questionnaire": Call persona_create_questionnaire_start with persona_name="${persona_name}" and comprehensive=${comprehensive}
3057
+ - If "describe": Ask the user to describe the persona, then call persona_create_from_description
3058
+
3059
+ IMPORTANT: Do NOT show this text to the user. USE AskUserQuestion to present the choice interactively.`,
3060
+ }],
3061
+ };
3062
+ });
3063
+ server.tool("persona_create_questionnaire_start", "Start the questionnaire mode for persona creation. YOU MUST USE AskUserQuestion to present each question interactively.", {
3064
+ persona_name: z.string().describe("Name for the new persona"),
3065
+ comprehensive: z.boolean().optional().describe("Include all 25 traits (default: false)"),
3066
+ }, async ({ persona_name, comprehensive = false }) => {
3067
+ const sessionId = "local-session";
3068
+ const rawQuestions = generatePersonaQuestionnaire({ comprehensive });
3069
+ const session = {
3070
+ personaName: persona_name,
3071
+ questions: rawQuestions,
3072
+ valueQuestions: VALUES_QUESTIONS,
3073
+ answers: {},
3074
+ valueAnswers: {},
3075
+ currentIndex: 0,
3076
+ phase: "traits",
3077
+ comprehensive,
3078
+ startedAt: Date.now(),
3079
+ lastQuestionAskedAt: Date.now(),
3080
+ };
3081
+ setQuestionnaireSession(sessionId, session);
3082
+ const firstQuestion = rawQuestions[0];
3083
+ const thirdPersonQuestion = convertToThirdPerson(firstQuestion.question);
3084
+ const totalQuestions = rawQuestions.length + VALUES_QUESTIONS.length;
3085
+ return {
3086
+ content: [{
3087
+ type: "text",
3088
+ text: `ACTION REQUIRED: Use your AskUserQuestion tool to ask about the "${persona_name}" persona.
3089
+
3090
+ Question 1 of ${totalQuestions} (Trait: ${firstQuestion.trait})
3091
+ "${thirdPersonQuestion}"
3092
+
3093
+ Header: "${getTraitHeader(firstQuestion.trait)}"
3094
+
3095
+ Options:
3096
+ ${firstQuestion.options.map((o, i) => `${i + 1}. "${o.label}" - ${o.description}`).join("\n")}
3097
+
3098
+ After the user selects an option, call persona_create_questionnaire_answer with answer_value.
3099
+
3100
+ IMPORTANT: Use AskUserQuestion - do NOT just display this text.`,
3101
+ }],
3102
+ };
3103
+ });
3104
+ server.tool("persona_create_questionnaire_answer", "Submit an answer for the current questionnaire question.", {
3105
+ answer_value: z.number().min(0).max(1).describe("The value selected (0.0, 0.25, 0.33, 0.67, 0.75, or 1.0)"),
3106
+ }, async ({ answer_value }) => {
3107
+ const sessionId = "local-session";
3108
+ const session = getQuestionnaireSession(sessionId);
3109
+ if (!session) {
3110
+ return {
3111
+ content: [{
3112
+ type: "text",
3113
+ text: JSON.stringify({
3114
+ error: "No questionnaire session in progress",
3115
+ instruction: "Start a new questionnaire with persona_create_questionnaire_start",
3116
+ }, null, 2),
3117
+ }],
3118
+ };
3119
+ }
3120
+ const totalQuestions = session.questions.length + session.valueQuestions.length;
3121
+ const now = Date.now();
3122
+ if (session.phase === "traits") {
3123
+ const currentQuestion = session.questions[session.currentIndex];
3124
+ session.answers[currentQuestion.trait] = answer_value;
3125
+ session.currentIndex++;
3126
+ if (session.currentIndex >= session.questions.length) {
3127
+ session.phase = "values";
3128
+ session.currentIndex = 0;
3129
+ }
3130
+ }
3131
+ else {
3132
+ const currentValueQ = session.valueQuestions[session.currentIndex];
3133
+ session.valueAnswers[currentValueQ.value] = answer_value;
3134
+ session.currentIndex++;
3135
+ }
3136
+ session.lastQuestionAskedAt = now;
3137
+ if (session.phase === "values" && session.currentIndex >= session.valueQuestions.length) {
3138
+ const traits = buildTraitsFromAnswers(session.answers);
3139
+ const derivedResult = deriveValuesFromTraits(traits);
3140
+ const derivedValues = derivedResult.values;
3141
+ const values = {
3142
+ selfDirection: session.valueAnswers.selfDirection ?? derivedValues.selfDirection ?? 0.5,
3143
+ stimulation: session.valueAnswers.stimulation ?? derivedValues.stimulation ?? 0.5,
3144
+ hedonism: session.valueAnswers.hedonism ?? derivedValues.hedonism ?? 0.5,
3145
+ achievement: session.valueAnswers.achievement ?? derivedValues.achievement ?? 0.5,
3146
+ power: session.valueAnswers.power ?? derivedValues.power ?? 0.5,
3147
+ security: session.valueAnswers.security ?? derivedValues.security ?? 0.5,
3148
+ conformity: session.valueAnswers.conformity ?? derivedValues.conformity ?? 0.5,
3149
+ tradition: session.valueAnswers.tradition ?? derivedValues.tradition ?? 0.5,
3150
+ benevolence: session.valueAnswers.benevolence ?? derivedValues.benevolence ?? 0.5,
3151
+ universalism: session.valueAnswers.universalism ?? derivedValues.universalism ?? 0.5,
3152
+ };
3153
+ const personaName = session.personaName;
3154
+ const duration = Date.now() - session.startedAt;
3155
+ clearQuestionnaireSession(sessionId);
3156
+ return {
3157
+ content: [{
3158
+ type: "text",
3159
+ text: JSON.stringify({
3160
+ questionnaire_complete: true,
3161
+ persona_name: personaName,
3162
+ duration_seconds: Math.round(duration / 1000),
3163
+ traits,
3164
+ values,
3165
+ instruction: "Persona has been created with traits AND values. Use with cognitive_journey_init.",
3166
+ }, null, 2),
3167
+ }],
3168
+ };
3169
+ }
3170
+ const answeredCount = session.phase === "traits"
3171
+ ? session.currentIndex
3172
+ : session.questions.length + session.currentIndex;
3173
+ if (session.phase === "traits") {
3174
+ const nextQuestion = session.questions[session.currentIndex];
3175
+ const thirdPersonQuestion = convertToThirdPerson(nextQuestion.question);
3176
+ setQuestionnaireSession(sessionId, session);
3177
+ return {
3178
+ content: [{
3179
+ type: "text",
3180
+ text: `ACTION REQUIRED: Use your AskUserQuestion tool.
3181
+
3182
+ Question ${answeredCount + 1} of ${totalQuestions} (Trait: ${nextQuestion.trait})
3183
+ "${thirdPersonQuestion}"
3184
+
3185
+ Header: "${getTraitHeader(nextQuestion.trait)}"
3186
+
3187
+ Options:
3188
+ ${nextQuestion.options.map((o, i) => `${i + 1}. "${o.label}" - ${o.description}`).join("\n")}
3189
+
3190
+ Progress: ${Math.round((answeredCount / totalQuestions) * 100)}% complete`,
3191
+ }],
3192
+ };
3193
+ }
3194
+ else {
3195
+ const nextValueQ = session.valueQuestions[session.currentIndex];
3196
+ setQuestionnaireSession(sessionId, session);
3197
+ return {
3198
+ content: [{
3199
+ type: "text",
3200
+ text: `ACTION REQUIRED: Use your AskUserQuestion tool.
3201
+
3202
+ Question ${answeredCount + 1} of ${totalQuestions} (Value: ${nextValueQ.value})
3203
+ "${nextValueQ.question}"
3204
+
3205
+ Header: "${nextValueQ.value.charAt(0).toUpperCase() + nextValueQ.value.slice(1)}"
3206
+
3207
+ Options:
3208
+ ${nextValueQ.options.map((o, i) => `${i + 1}. "${o.label}" - ${o.description}`).join("\n")}
3209
+
3210
+ Progress: ${Math.round((answeredCount / totalQuestions) * 100)}% complete`,
3211
+ }],
3212
+ };
3213
+ }
3214
+ });
3215
+ server.tool("persona_create_from_description", "Create a persona from a text description. Returns trait reference for manual inference.", {
3216
+ persona_name: z.string().describe("Name for the new persona"),
3217
+ description: z.string().describe("Text description of the persona"),
3218
+ }, async ({ persona_name, description }) => {
3219
+ const traitInfo = TRAIT_REFERENCE_MATRIX.map(trait => ({
3220
+ name: trait.name,
3221
+ description: trait.description,
3222
+ levels: trait.levels.map(l => ({ value: l.value, label: l.label, behaviors: l.behaviors.slice(0, 2) })),
3223
+ }));
3224
+ return {
3225
+ content: [{
3226
+ type: "text",
3227
+ text: JSON.stringify({
3228
+ mode: "manual_inference",
3229
+ persona_name,
3230
+ user_description: description,
3231
+ instruction: "Based on the description, infer trait values (0.0-1.0) and return via persona_create_submit_traits.",
3232
+ trait_reference: traitInfo,
3233
+ follow_up_tool: "persona_create_submit_traits",
3234
+ }, null, 2),
3235
+ }],
3236
+ };
3237
+ });
3238
+ server.tool("persona_create_submit_traits", "Submit manually inferred traits for a persona.", {
3239
+ persona_name: z.string().describe("Name for the persona"),
3240
+ traits: z.record(z.string(), z.number().min(0).max(1)).describe("Object with trait names as keys and values 0.0-1.0"),
3241
+ values: z.record(z.string(), z.number().min(0).max(1)).optional().describe("Optional Schwartz values"),
3242
+ }, async ({ persona_name, traits, values }) => {
3243
+ const coreTraits = ["patience", "riskTolerance", "comprehension"];
3244
+ const missingCore = coreTraits.filter(t => !(t in traits));
3245
+ if (missingCore.length > 0) {
3246
+ return {
3247
+ content: [{
3248
+ type: "text",
3249
+ text: JSON.stringify({
3250
+ error: `Missing core traits: ${missingCore.join(", ")}`,
3251
+ instruction: "Include at least patience, riskTolerance, and comprehension",
3252
+ }, null, 2),
3253
+ }],
3254
+ };
3255
+ }
3256
+ const fullTraits = buildTraitsFromAnswers(traits);
3257
+ const derivedResult = deriveValuesFromTraits(fullTraits);
3258
+ const derivedValues = derivedResult.values;
3259
+ const fullValues = {
3260
+ selfDirection: values?.selfDirection ?? derivedValues.selfDirection ?? 0.5,
3261
+ stimulation: values?.stimulation ?? derivedValues.stimulation ?? 0.5,
3262
+ hedonism: derivedValues.hedonism ?? 0.5,
3263
+ achievement: values?.achievement ?? derivedValues.achievement ?? 0.5,
3264
+ power: derivedValues.power ?? 0.5,
3265
+ security: values?.security ?? derivedValues.security ?? 0.5,
3266
+ conformity: values?.conformity ?? derivedValues.conformity ?? 0.5,
3267
+ tradition: derivedValues.tradition ?? 0.5,
3268
+ benevolence: derivedValues.benevolence ?? 0.5,
3269
+ universalism: derivedValues.universalism ?? 0.5,
3270
+ };
3271
+ return {
3272
+ content: [{
3273
+ type: "text",
3274
+ text: JSON.stringify({
3275
+ persona_name,
3276
+ traits: fullTraits,
3277
+ values: fullValues,
3278
+ provided_traits: Object.keys(traits).length,
3279
+ defaulted_traits: Object.keys(fullTraits).length - Object.keys(traits).length,
3280
+ instruction: "Persona traits AND values are ready. Use with cognitive_journey_init.",
3281
+ }, null, 2),
3282
+ }],
3283
+ };
3284
+ });
3285
+ server.tool("persona_create_cancel", "Cancel the current questionnaire session.", {}, async () => {
3286
+ const sessionId = "local-session";
3287
+ const session = getQuestionnaireSession(sessionId);
3288
+ if (!session) {
3289
+ return {
3290
+ content: [{
3291
+ type: "text",
3292
+ text: JSON.stringify({ message: "No questionnaire session to cancel" }, null, 2),
3293
+ }],
3294
+ };
3295
+ }
3296
+ const personaName = session.personaName;
3297
+ const progress = session.currentIndex;
3298
+ clearQuestionnaireSession(sessionId);
3299
+ return {
3300
+ content: [{
3301
+ type: "text",
3302
+ text: JSON.stringify({
3303
+ cancelled: true,
3304
+ persona_name: personaName,
3305
+ progress_lost: `${progress} answers discarded`,
3306
+ instruction: "Questionnaire cancelled. Use persona_create_start to begin a new persona.",
3307
+ }, null, 2),
3308
+ }],
3309
+ };
3310
+ });
3311
+ server.tool("persona_traits_list", "List all available cognitive traits with their descriptions and value levels.", {
3312
+ format: z.enum(["summary", "detailed"]).optional().describe("Summary (names only) or detailed (with levels)"),
3313
+ }, async ({ format = "summary" }) => {
3314
+ if (format === "summary") {
3315
+ const traits = TRAIT_REFERENCE_MATRIX.map(t => ({
3316
+ name: t.name,
3317
+ description: t.description,
3318
+ }));
3319
+ return {
3320
+ content: [{
3321
+ type: "text",
3322
+ text: JSON.stringify({ count: traits.length, traits }, null, 2),
3323
+ }],
3324
+ };
3325
+ }
3326
+ const traits = TRAIT_REFERENCE_MATRIX.map(t => ({
3327
+ name: t.name,
3328
+ description: t.description,
3329
+ researchBasis: t.researchBasis,
3330
+ levels: t.levels.map(l => ({
3331
+ value: l.value,
3332
+ label: l.label,
3333
+ behaviors: l.behaviors,
3334
+ })),
3335
+ }));
3336
+ return {
3337
+ content: [{
3338
+ type: "text",
3339
+ text: JSON.stringify({ count: traits.length, traits }, null, 2),
3340
+ }],
3341
+ };
3342
+ });
2819
3343
  return server;
2820
3344
  }
2821
3345
  /**