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,145 @@
1
+ /**
2
+ * Solver Registry - Registration of compute solvers
3
+ *
4
+ * Each solver declares its type mask and priority.
5
+ *
6
+ * To add a new solver:
7
+ * 1. Create a file in ./solvers/
8
+ * 2. Export `solver` (single) or `solvers` (array) with Solver interface
9
+ * 3. Import and register it below
10
+ */
11
+
12
+ import { type SolverMask, SolverType, shouldTrySolver } from "./classifier.ts";
13
+ // Import all solver modules
14
+ import * as arithmeticModule from "./solvers/arithmetic.ts";
15
+ import * as calculusModule from "./solvers/calculus.ts";
16
+ import * as derivationModule from "./solvers/derivation.ts";
17
+ import * as factsModule from "./solvers/facts.ts";
18
+ import * as formulaModule from "./solvers/formula.ts";
19
+ import * as logicModule from "./solvers/logic.ts";
20
+ import * as probabilityModule from "./solvers/probability.ts";
21
+ import * as statisticsModule from "./solvers/statistics.ts";
22
+ import * as wordProblemsModule from "./solvers/word-problems.ts";
23
+ import type { ComputeResult, Solver } from "./types.ts";
24
+
25
+ // Re-export Solver type for convenience
26
+ export type { Solver } from "./types.ts";
27
+
28
+ // =============================================================================
29
+ // REGISTRY
30
+ // =============================================================================
31
+
32
+ const solvers: Solver[] = [];
33
+ let sortedCache: Solver[] | null = null;
34
+
35
+ /**
36
+ * Register a solver. Call this at module load time.
37
+ */
38
+ export function registerSolver(solver: Solver): void {
39
+ solvers.push(solver);
40
+ sortedCache = null; // Invalidate cache
41
+ }
42
+
43
+ /**
44
+ * Get all registered solvers, sorted by priority
45
+ */
46
+ export function getSolvers(): Solver[] {
47
+ if (!sortedCache) {
48
+ sortedCache = [...solvers].sort((a, b) => a.priority - b.priority);
49
+ }
50
+ return sortedCache;
51
+ }
52
+
53
+ /**
54
+ * Get solvers that match a given mask, sorted by priority
55
+ */
56
+ export function getSolversForMask(mask: SolverMask): Solver[] {
57
+ return getSolvers().filter((s) => shouldTrySolver(mask, s.types));
58
+ }
59
+
60
+ /**
61
+ * Run solvers matching the mask until one succeeds
62
+ */
63
+ export function runSolvers(text: string, lower: string, mask: SolverMask): ComputeResult {
64
+ const matchingSolvers = getSolversForMask(mask);
65
+
66
+ for (const solver of matchingSolvers) {
67
+ const result = solver.solve(text, lower);
68
+ if (result.solved) {
69
+ return result;
70
+ }
71
+ }
72
+
73
+ return { solved: false, confidence: 0 };
74
+ }
75
+
76
+ /**
77
+ * Get registry stats for debugging
78
+ */
79
+ export function getRegistryStats(): { count: number; byType: Record<string, number> } {
80
+ const byType: Record<string, number> = {};
81
+
82
+ for (const solver of solvers) {
83
+ if (solver.types & SolverType.ARITHMETIC) byType.arithmetic = (byType.arithmetic || 0) + 1;
84
+ if (solver.types & SolverType.FORMULA_TIER1)
85
+ byType.formula_tier1 = (byType.formula_tier1 || 0) + 1;
86
+ if (solver.types & SolverType.FORMULA_TIER2)
87
+ byType.formula_tier2 = (byType.formula_tier2 || 0) + 1;
88
+ if (solver.types & SolverType.FORMULA_TIER3)
89
+ byType.formula_tier3 = (byType.formula_tier3 || 0) + 1;
90
+ if (solver.types & SolverType.FORMULA_TIER4)
91
+ byType.formula_tier4 = (byType.formula_tier4 || 0) + 1;
92
+ if (solver.types & SolverType.WORD_PROBLEM)
93
+ byType.word_problem = (byType.word_problem || 0) + 1;
94
+ if (solver.types & SolverType.MULTI_STEP) byType.multi_step = (byType.multi_step || 0) + 1;
95
+ if (solver.types & SolverType.CALCULUS) byType.calculus = (byType.calculus || 0) + 1;
96
+ if (solver.types & SolverType.FACTS) byType.facts = (byType.facts || 0) + 1;
97
+ if (solver.types & SolverType.LOGIC) byType.logic = (byType.logic || 0) + 1;
98
+ if (solver.types & SolverType.PROBABILITY) byType.probability = (byType.probability || 0) + 1;
99
+ if (solver.types & SolverType.DERIVATION) byType.derivation = (byType.derivation || 0) + 1;
100
+ }
101
+
102
+ return { count: solvers.length, byType };
103
+ }
104
+
105
+ /**
106
+ * List all registered solvers with their descriptions (for debugging/docs)
107
+ */
108
+ export function listSolvers(): Array<{ name: string; description: string; priority: number }> {
109
+ return getSolvers().map((s) => ({
110
+ name: s.name,
111
+ description: s.description ?? "(no description)",
112
+ priority: s.priority,
113
+ }));
114
+ }
115
+
116
+ // =============================================================================
117
+ // EXPLICIT SOLVER REGISTRATION
118
+ // Register all solvers from modules (no Bun.Glob needed - works with Node.js too)
119
+ // =============================================================================
120
+
121
+ // Helper to register solvers from a module
122
+ function registerFromModule(mod: Record<string, unknown>): void {
123
+ // Register single solver
124
+ if (mod.solver && typeof mod.solver === "object") {
125
+ registerSolver(mod.solver as Solver);
126
+ }
127
+
128
+ // Register multiple solvers
129
+ if (mod.solvers && Array.isArray(mod.solvers)) {
130
+ for (const s of mod.solvers) {
131
+ registerSolver(s as Solver);
132
+ }
133
+ }
134
+ }
135
+
136
+ // Register all solver modules
137
+ registerFromModule(arithmeticModule);
138
+ registerFromModule(calculusModule);
139
+ registerFromModule(derivationModule);
140
+ registerFromModule(factsModule);
141
+ registerFromModule(formulaModule);
142
+ registerFromModule(logicModule);
143
+ registerFromModule(probabilityModule);
144
+ registerFromModule(statisticsModule);
145
+ registerFromModule(wordProblemsModule);
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Arithmetic solver - safe evaluation of math expressions
3
+ * Uses recursive descent parser instead of eval/Function
4
+ */
5
+
6
+ import { SolverType } from "../classifier.ts";
7
+ import { safeEvaluate } from "../math.ts";
8
+ import { ARITHMETIC } from "../patterns.ts";
9
+ import type { ComputeResult, Solver } from "../types.ts";
10
+
11
+ /**
12
+ * Safe arithmetic evaluation using recursive descent parser
13
+ * Supports: +, -, *, /, ^, (), negative numbers, decimals
14
+ */
15
+ export function tryArithmetic(text: string): ComputeResult {
16
+ const start = performance.now();
17
+
18
+ const patterns = [
19
+ ARITHMETIC.whatIs,
20
+ ARITHMETIC.calculate,
21
+ ARITHMETIC.compute,
22
+ ARITHMETIC.evaluate,
23
+ ARITHMETIC.equalsQuestion,
24
+ ARITHMETIC.bareExpression,
25
+ ];
26
+
27
+ let expr: string | null = null;
28
+ for (const pattern of patterns) {
29
+ const match = text.match(pattern);
30
+ if (match?.[1]) {
31
+ expr = match[1].trim();
32
+ break;
33
+ }
34
+ }
35
+
36
+ if (!expr) return { solved: false, confidence: 0 };
37
+
38
+ // Use safe recursive descent parser instead of new Function()
39
+ const result = safeEvaluate(expr);
40
+
41
+ if (result.success && result.value !== undefined) {
42
+ const time_ms = performance.now() - start;
43
+ return {
44
+ solved: true,
45
+ result: Number.isInteger(result.value) ? result.value : +result.value.toFixed(10),
46
+ method: "arithmetic",
47
+ confidence: 1.0,
48
+ time_ms,
49
+ };
50
+ }
51
+
52
+ return { solved: false, confidence: 0 };
53
+ }
54
+
55
+ // =============================================================================
56
+ // SOLVER REGISTRATION
57
+ // =============================================================================
58
+
59
+ export const solver: Solver = {
60
+ name: "arithmetic",
61
+ description: "Safe evaluation of math expressions (+, -, *, /, ^, parentheses)",
62
+ types: SolverType.ARITHMETIC,
63
+ priority: 10,
64
+ solve: (text, _lower) => tryArithmetic(text),
65
+ };
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Calculus solver - derivatives and definite integrals of polynomials
3
+ */
4
+
5
+ import { SolverType } from "../classifier.ts";
6
+ import { formatResult, normalizeUnicodeSuperscripts } from "../math.ts";
7
+ import { CALCULUS } from "../patterns.ts";
8
+ import type { ComputeResult, PolyTerm, Solver } from "../types.ts";
9
+
10
+ /** Parse a polynomial string like "3x^2 + 2x - 5" into terms @internal */
11
+ function parsePolynomial(text: string): PolyTerm[] {
12
+ // Normalize: whitespace, case, and Unicode superscripts
13
+ const clean = normalizeUnicodeSuperscripts(text).replace(/\s+/g, "").toLowerCase();
14
+
15
+ const terms: PolyTerm[] = [];
16
+
17
+ // Match terms like: "3x^2", "-x", "5", "+2x^-1"
18
+ const termRegex = /([+-]?)(\d*\.?\d*)x(?:\^([+-]?\d+))?|([+-]?)(\d+\.?\d*)/g;
19
+ let match: RegExpExecArray | null;
20
+
21
+ while ((match = termRegex.exec(clean)) !== null) {
22
+ if (match[0] === "") continue;
23
+
24
+ // Variable term (ax^n)
25
+ if (match[2] !== undefined && match[0].includes("x")) {
26
+ const sign = match[1] === "-" ? -1 : 1;
27
+ const coeffStr = match[2];
28
+ const coeff =
29
+ sign * (coeffStr === "" || coeffStr === "+" || coeffStr === "-" ? 1 : parseFloat(coeffStr));
30
+ const exp = match[3] ? parseInt(match[3], 10) : 1;
31
+ terms.push({ coeff, exp });
32
+ }
33
+ // Constant term
34
+ else if (match[5] !== undefined) {
35
+ const sign = match[4] === "-" ? -1 : 1;
36
+ const coeff = sign * parseFloat(match[5]);
37
+ terms.push({ coeff, exp: 0 });
38
+ }
39
+ }
40
+ return terms;
41
+ }
42
+
43
+ /** Differentiate polynomial terms using power rule @internal */
44
+ function differentiateTerms(terms: PolyTerm[]): PolyTerm[] {
45
+ return terms
46
+ .map((t) => ({ coeff: t.coeff * t.exp, exp: t.exp - 1 }))
47
+ .filter((t) => t.coeff !== 0);
48
+ }
49
+
50
+ /** Integrate polynomial terms using power rule @internal */
51
+ function integrateTerms(terms: PolyTerm[]): PolyTerm[] {
52
+ return terms.map((t) => {
53
+ if (t.exp === -1) throw new Error("Cannot integrate 1/x in polynomial mode");
54
+ return { coeff: t.coeff / (t.exp + 1), exp: t.exp + 1 };
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Simpson's Rule for numerical integration
60
+ * Works for any continuous function, not just polynomials
61
+ * Accuracy: O(h^4) where h = (b-a)/n
62
+ */
63
+ export function simpsonIntegrate(fn: (x: number) => number, a: number, b: number, n = 100): number {
64
+ // Handle edge cases
65
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return NaN;
66
+ if (a === b) return 0;
67
+
68
+ // n must be even for Simpson's rule, and capped for safety
69
+ if (n % 2 !== 0) n++;
70
+ n = Math.min(n, 10000);
71
+
72
+ const h = (b - a) / n;
73
+ let sum = fn(a) + fn(b);
74
+
75
+ for (let i = 1; i < n; i++) {
76
+ const x = a + i * h;
77
+ // Odd indices get weight 4, even indices get weight 2
78
+ sum += (i % 2 === 0 ? 2 : 4) * fn(x);
79
+ }
80
+
81
+ return (h / 3) * sum;
82
+ }
83
+
84
+ /**
85
+ * Create a safe evaluator function for simple math expressions
86
+ * Supports: x, numbers, +, -, *, /, ^, sin, cos, tan, exp, ln, sqrt
87
+ * Returns null if expression is not safe to evaluate
88
+ * @internal
89
+ */
90
+ function createSafeFunction(expr: string): ((x: number) => number) | null {
91
+ // Normalize
92
+ const clean = expr.toLowerCase().replace(/\s+/g, "").replace(/\*\*/g, "^");
93
+
94
+ // Whitelist: only allow safe math characters and functions
95
+ const safePattern = /^[x\d+\-*/^().,sincotaexplnqr]+$/;
96
+ if (!safePattern.test(clean)) return null;
97
+
98
+ // Build function by replacing math operations with safe JS equivalents
99
+ // This is still not using eval - we construct the function from parsed components
100
+ try {
101
+ // For now, only support polynomial-like expressions that our parser handles
102
+ // More complex expressions (sin, cos, etc.) would need the full parser
103
+ const terms = parsePolynomial(clean);
104
+ if (terms.length === 0) return null;
105
+
106
+ return (x: number) => evaluatePolynomial(terms, x);
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ /** Evaluate polynomial at a point @internal */
113
+ function evaluatePolynomial(terms: PolyTerm[], x: number): number {
114
+ return terms.reduce((sum, t) => sum + t.coeff * x ** t.exp, 0);
115
+ }
116
+
117
+ /**
118
+ * Try to solve calculus problems (derivatives and integrals)
119
+ */
120
+ export function tryCalculus(text: string): ComputeResult {
121
+ const start = performance.now();
122
+
123
+ // Normalize Unicode superscripts early so regex patterns work
124
+ const normalizedText = normalizeUnicodeSuperscripts(text);
125
+
126
+ // =========================================================================
127
+ // DERIVATIVES: d/dx of f(x) at x=a
128
+ // =========================================================================
129
+ for (const pattern of CALCULUS.derivative) {
130
+ const match = normalizedText.match(pattern);
131
+ if (match?.[1]) {
132
+ try {
133
+ // Extract just the expression part
134
+ let expr = match[1].trim();
135
+ // Remove trailing punctuation, extra phrases, and redundant "d/dx of"
136
+ expr = expr
137
+ .replace(/[.?!]+$/, "")
138
+ .replace(/\s*(at|evaluated|when).*$/i, "")
139
+ .replace(/^d\/dx\s*(of\s*)?/i, "")
140
+ .replace(/^of\s+/i, "")
141
+ .trim();
142
+
143
+ const terms = parsePolynomial(expr);
144
+ if (terms.length === 0) continue;
145
+
146
+ const diffTerms = differentiateTerms(terms);
147
+
148
+ // If evaluating at a point (group 2 present)
149
+ if (match[2]) {
150
+ const xVal = parseFloat(match[2]);
151
+ const result = evaluatePolynomial(diffTerms, xVal);
152
+ const time_ms = performance.now() - start;
153
+ return {
154
+ solved: true,
155
+ result: formatResult(result),
156
+ method: "derivative_eval",
157
+ confidence: 1.0,
158
+ time_ms,
159
+ };
160
+ }
161
+
162
+ // Return symbolic derivative (format as string)
163
+ const resultStr = diffTerms
164
+ .map((t, i) => {
165
+ const sign = t.coeff >= 0 ? (i > 0 ? " + " : "") : " - ";
166
+ const absCoeff = Math.abs(t.coeff);
167
+ const cStr = absCoeff === 1 && t.exp !== 0 ? "" : String(absCoeff);
168
+ const xStr = t.exp === 0 ? "" : t.exp === 1 ? "x" : `x^${t.exp}`;
169
+ return `${sign}${cStr}${xStr}`;
170
+ })
171
+ .join("")
172
+ .trim();
173
+
174
+ const time_ms = performance.now() - start;
175
+ return {
176
+ solved: true,
177
+ result: resultStr || "0",
178
+ method: "derivative_symbolic",
179
+ confidence: 1.0,
180
+ time_ms,
181
+ };
182
+ } catch {
183
+ // Parsing failed, continue
184
+ }
185
+ }
186
+ }
187
+
188
+ // =========================================================================
189
+ // DEFINITE INTEGRALS: integral of f(x) from a to b
190
+ // =========================================================================
191
+ for (const pattern of CALCULUS.integral) {
192
+ const match = text.match(pattern);
193
+ if (match?.[1] && match[2] && match[3]) {
194
+ const a = parseFloat(match[2]);
195
+ const b = parseFloat(match[3]);
196
+
197
+ // First try exact polynomial integration
198
+ try {
199
+ const terms = parsePolynomial(match[1]);
200
+ if (terms.length > 0) {
201
+ const integrated = integrateTerms(terms);
202
+ const result = evaluatePolynomial(integrated, b) - evaluatePolynomial(integrated, a);
203
+ const time_ms = performance.now() - start;
204
+ return {
205
+ solved: true,
206
+ result: formatResult(result),
207
+ method: "definite_integral",
208
+ confidence: 1.0,
209
+ time_ms,
210
+ };
211
+ }
212
+ } catch {
213
+ // Polynomial parsing/integration failed, try numerical
214
+ }
215
+
216
+ // Fallback: Simpson's rule for numerical integration
217
+ const fn = createSafeFunction(match[1]);
218
+ if (fn) {
219
+ try {
220
+ const result = simpsonIntegrate(fn, a, b, 1000);
221
+ const time_ms = performance.now() - start;
222
+ return {
223
+ solved: true,
224
+ result: formatResult(result),
225
+ method: "numerical_integral",
226
+ confidence: 0.95, // Slightly lower confidence for numerical
227
+ time_ms,
228
+ };
229
+ } catch {
230
+ // Numerical integration failed
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ return { solved: false, confidence: 0 };
237
+ }
238
+
239
+ // =============================================================================
240
+ // SOLVER REGISTRATION
241
+ // =============================================================================
242
+
243
+ export const solver: Solver = {
244
+ name: "calculus",
245
+ description: "Derivatives and definite integrals of polynomials, with Simpson's rule fallback",
246
+ types: SolverType.CALCULUS,
247
+ priority: 50,
248
+ solve: (text, _lower) => tryCalculus(text),
249
+ };