verifiable-thinking-mcp 0.4.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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/package.json +75 -0
  4. package/src/index.ts +38 -0
  5. package/src/lib/cache.ts +246 -0
  6. package/src/lib/compression.ts +804 -0
  7. package/src/lib/compute/cache.ts +86 -0
  8. package/src/lib/compute/classifier.ts +555 -0
  9. package/src/lib/compute/confidence.ts +79 -0
  10. package/src/lib/compute/context.ts +154 -0
  11. package/src/lib/compute/extract.ts +200 -0
  12. package/src/lib/compute/filter.ts +224 -0
  13. package/src/lib/compute/index.ts +171 -0
  14. package/src/lib/compute/math.ts +247 -0
  15. package/src/lib/compute/patterns.ts +564 -0
  16. package/src/lib/compute/registry.ts +145 -0
  17. package/src/lib/compute/solvers/arithmetic.ts +65 -0
  18. package/src/lib/compute/solvers/calculus.ts +249 -0
  19. package/src/lib/compute/solvers/derivation-core.ts +371 -0
  20. package/src/lib/compute/solvers/derivation-latex.ts +160 -0
  21. package/src/lib/compute/solvers/derivation-mistakes.ts +1046 -0
  22. package/src/lib/compute/solvers/derivation-simplify.ts +451 -0
  23. package/src/lib/compute/solvers/derivation-transform.ts +620 -0
  24. package/src/lib/compute/solvers/derivation.ts +67 -0
  25. package/src/lib/compute/solvers/facts.ts +120 -0
  26. package/src/lib/compute/solvers/formula.ts +728 -0
  27. package/src/lib/compute/solvers/index.ts +36 -0
  28. package/src/lib/compute/solvers/logic.ts +422 -0
  29. package/src/lib/compute/solvers/probability.ts +307 -0
  30. package/src/lib/compute/solvers/statistics.ts +262 -0
  31. package/src/lib/compute/solvers/word-problems.ts +408 -0
  32. package/src/lib/compute/types.ts +107 -0
  33. package/src/lib/concepts.ts +111 -0
  34. package/src/lib/domain.ts +731 -0
  35. package/src/lib/extraction.ts +912 -0
  36. package/src/lib/index.ts +122 -0
  37. package/src/lib/judge.ts +260 -0
  38. package/src/lib/math/ast.ts +842 -0
  39. package/src/lib/math/index.ts +8 -0
  40. package/src/lib/math/operators.ts +171 -0
  41. package/src/lib/math/tokenizer.ts +477 -0
  42. package/src/lib/patterns.ts +200 -0
  43. package/src/lib/session.ts +825 -0
  44. package/src/lib/think/challenge.ts +323 -0
  45. package/src/lib/think/complexity.ts +504 -0
  46. package/src/lib/think/confidence-drift.ts +507 -0
  47. package/src/lib/think/consistency.ts +347 -0
  48. package/src/lib/think/guidance.ts +188 -0
  49. package/src/lib/think/helpers.ts +568 -0
  50. package/src/lib/think/hypothesis.ts +216 -0
  51. package/src/lib/think/index.ts +127 -0
  52. package/src/lib/think/prompts.ts +262 -0
  53. package/src/lib/think/route.ts +358 -0
  54. package/src/lib/think/schema.ts +98 -0
  55. package/src/lib/think/scratchpad-schema.ts +662 -0
  56. package/src/lib/think/spot-check.ts +961 -0
  57. package/src/lib/think/types.ts +93 -0
  58. package/src/lib/think/verification.ts +260 -0
  59. package/src/lib/tokens.ts +177 -0
  60. package/src/lib/verification.ts +620 -0
  61. package/src/prompts/index.ts +10 -0
  62. package/src/prompts/templates.ts +336 -0
  63. package/src/resources/index.ts +8 -0
  64. package/src/resources/sessions.ts +196 -0
  65. package/src/tools/compress.ts +138 -0
  66. package/src/tools/index.ts +5 -0
  67. package/src/tools/scratchpad.ts +2659 -0
  68. package/src/tools/sessions.ts +144 -0
@@ -0,0 +1,504 @@
1
+ /**
2
+ * SEMANTIC COMPLEXITY ROUTER v3
3
+ *
4
+ * Architecture: Linguistic feature extraction + semantic composition
5
+ * No hardcoded term weights; principles-driven scoring formula:
6
+ *
7
+ * complexity = verb_base × domain_weight × intensity_modifier
8
+ *
9
+ * Key improvements over v2:
10
+ * - "explain why" handled via semantic boosters (counterintuitive, meta-cognitive)
11
+ * - Probability/statistics domain properly weighted (0.80)
12
+ * - Intensity modifiers for quantifiers, impossibility, comparative language
13
+ * - Negation correction ("not difficult" → lower score)
14
+ * - Compositional semantics: no magic weights, principled formula
15
+ * - Domain detection now uses unified src/lib/domain.ts
16
+ */
17
+
18
+ import { getDomainWeight } from "../domain.ts";
19
+
20
+ export interface ComplexityResult {
21
+ score: number;
22
+ tier: "Low" | "Moderate" | "High" | "Very Hard" | "Almost Impossible";
23
+ /** Confidence bounds for calibration feedback */
24
+ confidence: {
25
+ /** Router confidence in this classification (0-1) */
26
+ level: number;
27
+ /** Is this score in the gray zone (0.28-0.72)? */
28
+ inGrayZone: boolean;
29
+ /** Distance from nearest tier boundary */
30
+ boundaryDistance: number;
31
+ };
32
+ explanation: {
33
+ verb_type: string;
34
+ verb_score: number;
35
+ domain_detected: string;
36
+ domain_weight: number;
37
+ intensity_signals: string[];
38
+ intensity_modifier: number;
39
+ };
40
+ reasoning: string;
41
+ }
42
+
43
+ export function assessPromptComplexity(text: string): ComplexityResult {
44
+ const lower = text.toLowerCase();
45
+
46
+ // ===== PHASE 1: VERB EXTRACTION =====
47
+ // Linguistic primary: the main reasoning action requested
48
+ const verbHierarchy: [string, number][] = [
49
+ ["prove or refute", 0.95],
50
+ ["prove that", 0.95],
51
+ ["prove", 0.95],
52
+ ["refute", 0.95],
53
+ ["derive the", 0.85],
54
+ ["derive", 0.8],
55
+ ["construct a", 0.78],
56
+ ["construct", 0.75],
57
+ ["design", 0.73],
58
+ ["implement", 0.7],
59
+ ["explain why", 0.7], // Elevated - causal reasoning requires deep understanding
60
+ ["explain how", 0.65],
61
+ ["explain", 0.55],
62
+ // Bare "why" questions - causal reasoning without "explain"
63
+ ["why can't", 0.7],
64
+ ["why cannot", 0.7],
65
+ ["why doesn't", 0.65],
66
+ ["why does", 0.62],
67
+ ["why do", 0.62],
68
+ ["why is", 0.6],
69
+ ["why are", 0.6],
70
+ ["describe", 0.45],
71
+ ["compare", 0.48],
72
+ ["analyze", 0.5],
73
+ ];
74
+
75
+ let verb_type = "generic";
76
+ let verb_base = 0.4; // default if no verb detected
77
+
78
+ // Match longest verb first (sorted by specificity in the array)
79
+ for (const [verb, weight] of verbHierarchy) {
80
+ if (lower.includes(verb)) {
81
+ verb_type = verb;
82
+ verb_base = weight;
83
+ break;
84
+ }
85
+ }
86
+
87
+ // ===== PHASE 2: SEMANTIC BOOSTERS =====
88
+ // Patterns that elevate the base verb complexity
89
+ let verb_boosted = verb_base;
90
+
91
+ // Booster 1: Counterintuitive signals (indicate cognitive rigor needed)
92
+ const counterintuitive_keywords = [
93
+ "counterintuitive",
94
+ "paradox",
95
+ "paradoxical",
96
+ "surprising",
97
+ "unintuitive",
98
+ "confusing",
99
+ "why is this",
100
+ "why does this seem",
101
+ "seems wrong",
102
+ "trick question",
103
+ ];
104
+
105
+ const has_counterintuitive = counterintuitive_keywords.some((kw) => lower.includes(kw));
106
+
107
+ if (has_counterintuitive && verb_base < 0.75) {
108
+ verb_boosted += 0.15;
109
+ verb_type += " [counterintuitive]";
110
+ }
111
+
112
+ // Booster 2: Meta-cognitive reasoning (reasoning about reasoning/human cognition)
113
+ const metacognitive_patterns = [
114
+ /why do (people|we|humans)/,
115
+ /why don't (people|we|humans)/,
116
+ /initially guess/,
117
+ /systematic(ally)? (fail|error|mistake)/,
118
+ /cognitive bias/,
119
+ /intuition (vs|versus) reality/,
120
+ /what makes (people|us|humans)/,
121
+ /common mistake/,
122
+ /why (most|many) people/,
123
+ ];
124
+
125
+ const has_metacognitive = metacognitive_patterns.some((pattern) => pattern.test(lower));
126
+
127
+ if (has_metacognitive) {
128
+ verb_boosted += 0.1;
129
+ verb_type += " [meta-cognitive]";
130
+ }
131
+
132
+ // Booster 3: Step-by-step / rigorous requirement
133
+ const rigor_patterns = [
134
+ /step[- ]by[- ]step/,
135
+ /rigorously/,
136
+ /formally prove/,
137
+ /show (your|the) work/,
138
+ /detailed explanation/,
139
+ ];
140
+
141
+ const has_rigor = rigor_patterns.some((p) => p.test(lower));
142
+ if (has_rigor) {
143
+ verb_boosted += 0.08;
144
+ verb_type += " [rigorous]";
145
+ }
146
+
147
+ // Booster 4: Trap detection (questions that look simple but aren't)
148
+ // Short questions with numbers and keywords that indicate deceptive simplicity
149
+ const trap_patterns = [
150
+ /\b(cost|price|pay|spend|total|together)\b.*\$?\d/i, // Bat & Ball style
151
+ /\$?\d+.*\b(cost|price|pay|spend|total|together)\b/i, // Reversed order
152
+ /how many.*\d+!/i, // Factorial problems (100!)
153
+ /trailing zero/i, // Number theory trap
154
+ /\d+\s*(ball|bat|item|object)s?\b/i, // Word problem with objects
155
+ /\d+\s*machines?.*\d+\s*(minutes?|widgets?)/i, // Rate problems (5 machines, 5 minutes)
156
+ /\d+\s*(black|white|red|blue).*socks?/i, // Pigeonhole principle
157
+ /minimum.*guarantee/i, // Pigeonhole phrasing
158
+ /clock.*hands?.*overlap/i, // Clock problems
159
+ /hands?.*clock.*overlap/i, // Clock problems alt
160
+ /average speed.*round trip/i, // Harmonic mean trap
161
+ /round trip.*average speed/i, // Harmonic mean trap alt
162
+ /\d+\s*mph.*returns?.*\d+\s*mph/i, // Speed trap
163
+ /doubles?.*every day.*\d+\s*days/i, // Exponential growth trap (lily pad)
164
+ /\d+\s*days.*cover.*lake/i, // Lily pad variant
165
+ /overlap.*\d+\s*hours/i, // Clock overlap
166
+ /\d+\s*hours.*overlap/i, // Clock overlap alt
167
+ /times.*hands.*overlap/i, // Clock overlap natural phrasing
168
+ /times.*overlap/i, // Generic overlap question
169
+ /wason|selection task/i, // Wason card task
170
+ /vowel.*even|even.*vowel/i, // Wason card rule pattern
171
+ /how many ways.*arrange/i, // Combinatorics
172
+ /arrange.*letters/i, // Anagram problems
173
+ /letters.*arrange/i, // Anagram variant
174
+ /how many ways.*letters/i, // Anagram direct question
175
+ /\^\d{2,}/i, // Large exponents (7^100)
176
+ /mod\s+\d+/i, // Modular arithmetic
177
+ /last digit/i, // Modular arithmetic variant
178
+ /\d+-chamber.*bullet/i, // Russian roulette style
179
+ /revolver.*bullet/i, // Russian roulette variant
180
+ /adjacent bullet/i, // Conditional probability setup
181
+ // Self-reference paradoxes
182
+ /this statement.*(true|false|itself)/i, // Liar paradox
183
+ /statement.*refer.*itself/i, // Self-reference
184
+ // Sample size / statistical reasoning
185
+ /which.*(more|less) reliable/i, // Sample size comparison
186
+ /survey of \d+/i, // Sample size context
187
+ /sample.*(size|of \d+)/i, // Sample size explicit
188
+ // Simpson's paradox
189
+ /(overall|aggregate|total).*compar/i, // Aggregation paradox
190
+ /hospital.*surviv/i, // Simpson's classic example
191
+ /(mild|severe).*(surviv|rate)/i, // Subgroup analysis
192
+ /better overall/i, // Simpson's phrasing
193
+ // Expected value / geometric distribution
194
+ /expected (number|value).*(flip|roll|toss|draw|trial)/i, // EV questions
195
+ /until you get (heads|tails|a \w+)/i, // Geometric distribution
196
+ /flip.*until/i, // Stopping time problems
197
+ // Logic meta-questions (validity judgments)
198
+ /\b(VALID|INVALID|UNSOUND|SOUND)\b/i, // Logic validity options
199
+ /valid.*argument|argument.*valid/i, // Validity question
200
+ // Sorites / vagueness paradoxes
201
+ /heap.*grain|grain.*heap/i, // Sorites paradox
202
+ /\d+ grain/i, // Sorites setup
203
+ // Counterintuitive probability problems (100 prisoners, Monty Hall style)
204
+ /\d+\s*prisoners?.*\d+\s*boxes/i, // 100 prisoners problem
205
+ /prisoners?.*boxes.*strategy/i, // Prisoners/boxes with strategy
206
+ /loop[\s-]*follow|cycle[\s-]*strategy/i, // Loop-following strategy
207
+ /survival.*probability.*strategy/i, // Strategy + survival
208
+ /strategy.*survival.*probability/i, // Strategy + survival alt
209
+ /monty hall/i, // Monty Hall problem
210
+ /switch.*doors?|doors?.*switch/i, // Monty Hall phrasing
211
+ /\d+\s*doors?.*goat/i, // Monty Hall variant
212
+ ];
213
+ const has_trap_pattern = trap_patterns.some((p) => p.test(text)); // Use original text for case
214
+ const is_short_with_numbers = text.length < 350 && /\d/.test(text); // Increased threshold
215
+
216
+ // Some traps don't have numbers but are still traps (anagrams, combinatorics)
217
+ const is_numberless_trap =
218
+ text.length < 200 &&
219
+ (/arrange.*letters/i.test(text) ||
220
+ /permutation.*letters/i.test(text) ||
221
+ /how many ways/i.test(text));
222
+
223
+ // Track trap detection - will add to intensity_signals after it's declared
224
+ const trap_detected = (has_trap_pattern && is_short_with_numbers) || is_numberless_trap;
225
+ if (trap_detected && verb_base < 0.7) {
226
+ verb_boosted += 0.25; // Strong boost for trap questions
227
+ verb_type += " [trap-detected]";
228
+ }
229
+
230
+ verb_boosted = Math.min(0.99, verb_boosted); // cap at 0.99
231
+
232
+ // ===== PHASE 3: DOMAIN DETECTION =====
233
+ // Use unified domain detector from src/lib/domain.ts
234
+ const { domain: domain_name, weight: domain_weight } = getDomainWeight(text);
235
+
236
+ // ===== PHASE 4: INTENSITY MODIFIERS =====
237
+ // Signals that increase reasoning depth without changing domain
238
+ let intensity_mod = 1.0;
239
+ const intensity_signals: string[] = [];
240
+
241
+ // Add trap pattern to intensity signals (detected in Phase 2)
242
+ if (trap_detected) {
243
+ intensity_signals.push("trap_pattern");
244
+ intensity_mod *= 1.15; // Additional multiplier for traps
245
+ }
246
+
247
+ // Quantifiers (logical depth)
248
+ const quantifier_patterns = [
249
+ /\ball\b/,
250
+ /\bany\b/,
251
+ /\bnon-?trivial/,
252
+ /\bevery\b/,
253
+ /\bno\s+\w+\s+can\b/,
254
+ ];
255
+ if (quantifier_patterns.some((p) => p.test(lower))) {
256
+ intensity_mod *= 1.1;
257
+ intensity_signals.push("quantifier");
258
+ }
259
+
260
+ // Impossibility/negative results (open problem signals)
261
+ const impossibility_patterns = [
262
+ /cannot (achieve|scale|exceed|be solved)/,
263
+ /\bno\b.*\balgorithm\b/,
264
+ /\bimpossible to\b/,
265
+ /why (no|not|can't|cannot)\b/,
266
+ /prove.*impossible/,
267
+ ];
268
+ if (impossibility_patterns.some((p) => p.test(lower))) {
269
+ intensity_mod *= 1.08;
270
+ intensity_signals.push("impossibility");
271
+ }
272
+
273
+ // Comparative language (optimality, bounds)
274
+ const comparative_patterns = [
275
+ /faster than/,
276
+ /better than/,
277
+ /worse than/,
278
+ /optimal/,
279
+ /upper bound/,
280
+ /lower bound/,
281
+ /at least/,
282
+ /at most/,
283
+ ];
284
+ if (comparative_patterns.some((p) => p.test(lower))) {
285
+ intensity_mod *= 1.08;
286
+ intensity_signals.push("comparative");
287
+ }
288
+
289
+ // Proof requirement (even without "prove" verb)
290
+ const proof_patterns = [
291
+ /\bproof\b/,
292
+ /\btheorem\b/,
293
+ /\blemma\b/,
294
+ /\bcorollary\b/,
295
+ /show that/,
296
+ /demonstrate that/,
297
+ ];
298
+ if (proof_patterns.some((p) => p.test(lower))) {
299
+ intensity_mod *= 1.05;
300
+ intensity_signals.push("proof_structure");
301
+ }
302
+
303
+ // Multi-part questions (higher cognitive load)
304
+ const multi_part_patterns = [
305
+ /give (two|three|four|\d+) (examples?|reasons?|explanations?)/i,
306
+ /what are (two|three|\d+) possible/i,
307
+ /list (two|three|\d+)/i,
308
+ /compare.*contrast/i,
309
+ /similarities.*differences/i,
310
+ ];
311
+ if (multi_part_patterns.some((p) => p.test(lower))) {
312
+ intensity_mod *= 1.12;
313
+ intensity_signals.push("multi_part");
314
+ }
315
+
316
+ // Rule-based reasoning (Wason selection task style)
317
+ const rule_patterns = [
318
+ /rule:.*which.*flip/i,
319
+ /which cards? must you flip/i,
320
+ /test the rule/i,
321
+ /if.*on one side.*on (the )?other/i,
322
+ /must.*flip.*test/i,
323
+ ];
324
+ if (rule_patterns.some((p) => p.test(lower))) {
325
+ intensity_mod *= 1.35; // Strong boost for logical rule verification
326
+ intensity_signals.push("rule_based");
327
+ }
328
+
329
+ // ===== PHASE 5: NEGATION CORRECTION =====
330
+ // Handle "not difficult", "not complex" → lower score
331
+ const negation_patterns = [
332
+ /not (difficult|complex|hard)/,
333
+ /not requiring/,
334
+ /simple (explanation|answer)/,
335
+ /just (explain|describe)/,
336
+ /briefly/,
337
+ ];
338
+ if (negation_patterns.some((p) => p.test(lower))) {
339
+ verb_boosted *= 0.7; // 30% penalty
340
+ verb_type += " [simplified]";
341
+ }
342
+
343
+ // ===== PHASE 5b: META-QUESTION DETECTION =====
344
+ // Questions ABOUT psychological research, biases, or experiments should use direct path.
345
+ // Extended reasoning on meta-cognitive questions leads to overthinking.
346
+ // Example: "Which group estimates higher?" is about anchoring research, not a math problem.
347
+ const meta_question_patterns = [
348
+ /which (group|sample|population) (estimates?|guesses?|predicts?|reports?)/i, // Anchoring research
349
+ /research (shows?|found|demonstrates?|indicates?)/i, // Citing research findings
350
+ /study (found|shows?|demonstrates?)/i, // Study citations
351
+ /experiment (shows?|found|demonstrates?)/i, // Experiment findings
352
+ /psychology (research|studies?|experiments?)/i, // Psychology context
353
+ /cognitive (bias|heuristic|psychology)/i, // Cognitive science terms
354
+ /asked .*(longer|shorter|higher|lower|more|less).*asked/i, // Classic anchoring setup
355
+ /one group.*(another|different|second) group/i, // Between-subjects comparison
356
+ /participants? (were|are) (asked|shown|given)/i, // Experimental procedure
357
+ /what (does|do) (the|this) (research|study|experiment)/i, // Meta-question about research
358
+ ];
359
+
360
+ const is_meta_question = meta_question_patterns.some((p) => p.test(text));
361
+ if (is_meta_question) {
362
+ // Strong penalty: meta-questions benefit from direct intuition, not extended reasoning
363
+ verb_boosted *= 0.6; // 40% penalty - biases toward "Low" tier (direct path)
364
+ verb_type += " [meta-question]";
365
+ intensity_signals.push("meta_question");
366
+ }
367
+
368
+ // ===== PHASE 6: COMPOSITE SCORING =====
369
+ // If we detected a high-weight domain but no explicit verb, boost the base
370
+ // This handles "What is X?" questions in difficult domains
371
+ if (verb_type === "generic" && domain_weight >= 0.7) {
372
+ verb_boosted = Math.max(verb_boosted, 0.55);
373
+ }
374
+
375
+ const composite_score = verb_boosted * domain_weight * intensity_mod;
376
+ const final_score = Math.min(1.0, composite_score);
377
+
378
+ // ===== PHASE 7: TIER CLASSIFICATION =====
379
+ let tier: ComplexityResult["tier"];
380
+
381
+ if (final_score < 0.3) {
382
+ tier = "Low";
383
+ } else if (final_score < 0.5) {
384
+ tier = "Moderate";
385
+ } else if (final_score < 0.72) {
386
+ tier = "High";
387
+ } else if (final_score < 0.85) {
388
+ tier = "Very Hard";
389
+ } else {
390
+ tier = "Almost Impossible";
391
+ }
392
+
393
+ // ASYMMETRIC DEFAULT: When in doubt, verify
394
+ // Any intensity signal or trap detection → minimum Moderate
395
+ // Rationale: under-routing causes wrong answers, over-routing only wastes tokens
396
+ // EXCEPTION: meta_question signals should stay Low (direct path preferred for these)
397
+ const signals_excluding_meta = intensity_signals.filter((s) => s !== "meta_question");
398
+ if (tier === "Low" && signals_excluding_meta.length > 0) {
399
+ tier = "Moderate";
400
+ }
401
+
402
+ // ===== CONFIDENCE CALCULATION =====
403
+ // Calculate confidence based on distance from tier boundaries
404
+ const TIER_BOUNDARIES = [0.3, 0.5, 0.72, 0.85];
405
+ const GRAY_ZONE_LOWER = 0.28;
406
+ const GRAY_ZONE_UPPER = 0.72;
407
+
408
+ // Find minimum distance to any tier boundary
409
+ let minBoundaryDistance = 1.0;
410
+ for (const boundary of TIER_BOUNDARIES) {
411
+ const distance = Math.abs(final_score - boundary);
412
+ if (distance < minBoundaryDistance) {
413
+ minBoundaryDistance = distance;
414
+ }
415
+ }
416
+
417
+ // Confidence is higher when far from boundaries
418
+ // Scale: 0.5 at boundary, 0.95 when far from all boundaries
419
+ const confidenceLevel = Math.min(0.95, 0.5 + minBoundaryDistance * 2.5);
420
+ const inGrayZone = final_score >= GRAY_ZONE_LOWER && final_score <= GRAY_ZONE_UPPER;
421
+
422
+ // ===== REASONING EXPLANATION =====
423
+ const reasoning = `Score: ${final_score.toFixed(3)} | Tier: ${tier}
424
+ Verb: "${verb_type}" (base: ${verb_base.toFixed(2)} → boosted: ${verb_boosted.toFixed(2)})
425
+ Domain: ${domain_name} (weight: ${domain_weight.toFixed(2)})
426
+ Intensity: ${intensity_signals.join(", ") || "none"} (modifier: ${intensity_mod.toFixed(2)}x)
427
+
428
+ Calculation: ${verb_boosted.toFixed(2)} × ${domain_weight.toFixed(
429
+ 2,
430
+ )} × ${intensity_mod.toFixed(2)} = ${final_score.toFixed(3)}`;
431
+
432
+ return {
433
+ score: final_score,
434
+ tier,
435
+ confidence: {
436
+ level: confidenceLevel,
437
+ inGrayZone,
438
+ boundaryDistance: minBoundaryDistance,
439
+ },
440
+ explanation: {
441
+ verb_type,
442
+ verb_score: verb_boosted,
443
+ domain_detected: domain_name,
444
+ domain_weight,
445
+ intensity_signals,
446
+ intensity_modifier: intensity_mod,
447
+ },
448
+ reasoning: reasoning.trim(),
449
+ };
450
+ }
451
+ /**
452
+ * Detect if a question is trivially simple (can skip all reasoning phases)
453
+ * These questions should use direct LLM answer with minimal prompting
454
+ */
455
+ export function isTrivialQuestion(question: string): boolean {
456
+ // Must be very short
457
+ if (question.length > 80) return false; // Reduced from 150
458
+
459
+ // Must have very low complexity score
460
+ const complexity = assessPromptComplexity(question);
461
+ if (complexity.score >= 0.25) return false; // Stricter than 0.3
462
+
463
+ // Exclude questions with reasoning indicators
464
+ const reasoningIndicators = [
465
+ /minimum|maximum|optimal/i,
466
+ /guarantee|ensure|worst case/i,
467
+ /how many (ways|times|steps)/i,
468
+ /strategy|approach|method/i,
469
+ /arrange|permutation|combination/i,
470
+ /probability|chance|likely/i,
471
+ /\d+.*machines?.*\d+/i, // Rate problems
472
+ /overlap|intersect/i,
473
+ ];
474
+ if (reasoningIndicators.some((p) => p.test(question))) return false;
475
+
476
+ // Check for simple answer patterns
477
+ const expectsSimpleAnswer =
478
+ /\b(yes|no|true|false)\s*(\?|$|\.)/i.test(question) ||
479
+ /\b(is|are|does|do|can|will|has|have)\s+\w+.*\?/i.test(question) ||
480
+ /\banswer\s+(yes|no|true|false)\b/i.test(question) ||
481
+ /\b(valid|invalid)\s*(\?|$)/i.test(question);
482
+
483
+ // Check for simple logical patterns
484
+ const simpleLogic =
485
+ /\bif\s+.+,\s*then\b/i.test(question) ||
486
+ /\ball\s+\w+\s+are\s+\w+/i.test(question) ||
487
+ /\b(therefore|thus|so)\b/i.test(question);
488
+
489
+ return expectsSimpleAnswer || simpleLogic;
490
+ }
491
+
492
+ /**
493
+ * Get a minimal prompt for trivial questions
494
+ * Designed for direct LLM answer with no reasoning overhead
495
+ */
496
+ export function getTrivialPrompt(question: string): {
497
+ system: string;
498
+ user: string;
499
+ } {
500
+ return {
501
+ system: "Answer directly. Just YES, NO, or the answer. Nothing else.",
502
+ user: question,
503
+ };
504
+ }