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