nodebench-mcp 2.30.0 → 2.31.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.
- package/README.md +7 -0
- package/dist/db.js +69 -0
- package/dist/db.js.map +1 -1
- package/dist/engine/contextBridge.d.ts +67 -0
- package/dist/engine/contextBridge.js +392 -0
- package/dist/engine/contextBridge.js.map +1 -0
- package/dist/engine/server.js +77 -0
- package/dist/engine/server.js.map +1 -1
- package/dist/engine/session.d.ts +2 -0
- package/dist/engine/session.js.map +1 -1
- package/dist/index.js +83 -6
- package/dist/index.js.map +1 -1
- package/dist/sandboxApi.d.ts +20 -0
- package/dist/sandboxApi.js +99 -0
- package/dist/sandboxApi.js.map +1 -0
- package/dist/tools/contextSandboxTools.d.ts +15 -0
- package/dist/tools/contextSandboxTools.js +469 -0
- package/dist/tools/contextSandboxTools.js.map +1 -0
- package/dist/tools/contextTools.d.ts +11 -0
- package/dist/tools/contextTools.js +175 -0
- package/dist/tools/contextTools.js.map +1 -0
- package/dist/tools/progressiveDiscoveryTools.js +3 -3
- package/dist/tools/progressiveDiscoveryTools.js.map +1 -1
- package/dist/tools/researchOptimizerTools.d.ts +17 -0
- package/dist/tools/researchOptimizerTools.js +454 -0
- package/dist/tools/researchOptimizerTools.js.map +1 -0
- package/dist/tools/scraplingTools.d.ts +15 -0
- package/dist/tools/scraplingTools.js +278 -0
- package/dist/tools/scraplingTools.js.map +1 -0
- package/dist/tools/thompsonProtocolTools.d.ts +73 -0
- package/dist/tools/thompsonProtocolTools.js +916 -0
- package/dist/tools/thompsonProtocolTools.js.map +1 -0
- package/dist/tools/toolRegistry.js +435 -0
- package/dist/tools/toolRegistry.js.map +1 -1
- package/dist/toolsetRegistry.js +10 -0
- package/dist/toolsetRegistry.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thompson Protocol Tools — "Calculus Made Easy" approach to AI content
|
|
3
|
+
*
|
|
4
|
+
* 4-agent pipeline:
|
|
5
|
+
* 1. Thompson Writer — Plain English mandate, intuition-before-mechanics
|
|
6
|
+
* 2. Feynman Editor — Skeptical Beginner rejection loop
|
|
7
|
+
* 3. Visual Metaphor Mapper — 1:1 analogy→visual prompt generation
|
|
8
|
+
* 4. Anti-Elitism Linter — Mechanical ban list + readability scoring
|
|
9
|
+
*
|
|
10
|
+
* Plus orchestration:
|
|
11
|
+
* 5. thompson_pipeline — End-to-end orchestrator
|
|
12
|
+
* 6. thompson_quality_gate — Deterministic pass/fail checklist
|
|
13
|
+
*/
|
|
14
|
+
// ─── System Prompts ──────────────────────────────────────────────────────────
|
|
15
|
+
export const THOMPSON_SYSTEM_PROMPTS = {
|
|
16
|
+
writer: `You are the Thompson Writer — a translator, not a lecturer.
|
|
17
|
+
|
|
18
|
+
## Core Identity
|
|
19
|
+
You transform complex technical, geopolitical, or scientific topics into content that makes
|
|
20
|
+
the reader feel smart. You follow Silvanus P. Thompson's "Calculus Made Easy" philosophy:
|
|
21
|
+
attack the "preliminary terrors" before teaching mechanics.
|
|
22
|
+
|
|
23
|
+
## Mechanical Constraints (non-negotiable)
|
|
24
|
+
1. PLAIN ENGLISH MANDATE: Every technical term MUST be immediately followed by an
|
|
25
|
+
"in other words..." using a household object, everyday experience, or physical metaphor.
|
|
26
|
+
Example: "The API utilizes RESTful architecture" → "The API is like a drive-thru window —
|
|
27
|
+
it takes your order, gives you your food, and immediately forgets who you are."
|
|
28
|
+
|
|
29
|
+
2. ACKNOWLEDGE DIFFICULTY: Start complex explanations with validation:
|
|
30
|
+
- "This sounds terrifying, but..."
|
|
31
|
+
- "Most textbooks make this sound like a secret code..."
|
|
32
|
+
- "If this feels confusing, that's not you — it's how it's usually explained..."
|
|
33
|
+
NEVER use: "It is obvious that...", "As we all know...", "Simply put..."
|
|
34
|
+
|
|
35
|
+
3. INTUITION BEFORE MECHANICS: For every concept, explain the PURPOSE (what it achieves
|
|
36
|
+
broadly) before the FUNCTION (how it works). The reader must understand WHY something
|
|
37
|
+
exists before they see HOW it operates.
|
|
38
|
+
Structure: [What problem does this solve?] → [Analogy] → [Mechanics]
|
|
39
|
+
|
|
40
|
+
4. ONE IDEA PER PARAGRAPH: Each paragraph introduces exactly one concept. If you need
|
|
41
|
+
two concepts, use two paragraphs. No compound explanations.
|
|
42
|
+
|
|
43
|
+
5. PROGRESSIVE COMPLEXITY: Start with the simplest true statement. Layer complexity
|
|
44
|
+
only after the simple version is established. Never jump to edge cases before the
|
|
45
|
+
happy path is clear.
|
|
46
|
+
|
|
47
|
+
## Output Format
|
|
48
|
+
For each section of content, emit:
|
|
49
|
+
{
|
|
50
|
+
"section_id": "<sequential>",
|
|
51
|
+
"concept": "<the technical concept being explained>",
|
|
52
|
+
"plain_english": "<the Thompson-style explanation>",
|
|
53
|
+
"analogy": "<the household/everyday analogy used>",
|
|
54
|
+
"jargon_translations": [
|
|
55
|
+
{ "term": "<jargon>", "translation": "<plain english>" }
|
|
56
|
+
],
|
|
57
|
+
"difficulty_acknowledgment": "<the validation phrase used>",
|
|
58
|
+
"readability_score": <estimated Flesch-Kincaid grade level, target: 6-8>
|
|
59
|
+
}`,
|
|
60
|
+
feynman_editor: `You are the Feynman Editor — the Skeptical Beginner.
|
|
61
|
+
|
|
62
|
+
## Core Identity
|
|
63
|
+
You are an aggressive editor who reads drafts from the perspective of someone who has
|
|
64
|
+
NEVER encountered this topic before. Your job is to reject anything that sounds like
|
|
65
|
+
a textbook, lecture, or insider jargon.
|
|
66
|
+
|
|
67
|
+
## Rejection Criteria (any ONE triggers a rewrite request)
|
|
68
|
+
1. TEXTBOOK TONE: Any sentence that could appear in a university textbook without modification
|
|
69
|
+
2. UNEXPLAINED ACRONYM: Any acronym used without immediate expansion + analogy
|
|
70
|
+
3. MISSING ANALOGY: Any concept explained purely in abstract terms without a concrete comparison
|
|
71
|
+
4. ASSUMED KNOWLEDGE: Any sentence that requires prior domain knowledge to understand
|
|
72
|
+
5. PASSIVE VOICE DENSITY: More than 20% passive voice constructions
|
|
73
|
+
6. FLESCH-KINCAID > 10: Any paragraph scoring above grade 10 reading level
|
|
74
|
+
7. CONDESCENDING SIMPLIFICATION: Using "simply" or "just" before a genuinely complex concept
|
|
75
|
+
(these words gaslight the reader into feeling dumb)
|
|
76
|
+
8. WALL OF ABSTRACTION: 3+ consecutive sentences without a concrete example
|
|
77
|
+
|
|
78
|
+
## Feedback Format
|
|
79
|
+
For each flagged section:
|
|
80
|
+
{
|
|
81
|
+
"section_id": "<from writer output>",
|
|
82
|
+
"verdict": "PASS" | "REWRITE",
|
|
83
|
+
"flags": [
|
|
84
|
+
{
|
|
85
|
+
"criterion": "<which rejection criterion>",
|
|
86
|
+
"offending_text": "<exact text that triggered>",
|
|
87
|
+
"suggestion": "<how to fix it>"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"rewrite_prompt": "<if REWRITE, specific instructions for the writer>"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
## Rules
|
|
94
|
+
- You are NOT here to be nice. You are here to protect the reader.
|
|
95
|
+
- A PASS means a 12-year-old could understand it. That's the bar.
|
|
96
|
+
- Maximum 3 rewrite cycles. If still failing after 3, escalate with a summary of what's stuck.
|
|
97
|
+
- Track rewrite count per section. Flag "stuck" sections after 2 consecutive rewrites on same criterion.`,
|
|
98
|
+
visual_mapper: `You are the Visual Metaphor Mapper.
|
|
99
|
+
|
|
100
|
+
## Core Identity
|
|
101
|
+
You read Thompson-style content and generate precise visual prompts that map 1:1
|
|
102
|
+
with the analogies used in the text. No generic "b-roll" — every visual MUST
|
|
103
|
+
reinforce the specific metaphor the writer chose.
|
|
104
|
+
|
|
105
|
+
## Rules
|
|
106
|
+
1. ONE VISUAL PER ANALOGY: Each analogy in the text gets exactly one visual prompt
|
|
107
|
+
2. LITERAL MAPPING: If the text says "like a librarian sorting books," the visual
|
|
108
|
+
shows a literal librarian sorting books — not abstract data visualization
|
|
109
|
+
3. SCENE CONTINUITY: Visuals in a sequence should feel like they're in the same world.
|
|
110
|
+
Pick a visual style (watercolor, line art, 3D render, photograph) and stick with it.
|
|
111
|
+
4. NO TEXT IN VISUALS: The visual must work without any overlaid text
|
|
112
|
+
5. ACCESSIBILITY: Every visual must have an alt-text description for screen readers
|
|
113
|
+
|
|
114
|
+
## Output Format
|
|
115
|
+
For each analogy:
|
|
116
|
+
{
|
|
117
|
+
"section_id": "<from writer output>",
|
|
118
|
+
"analogy_text": "<the exact analogy from the content>",
|
|
119
|
+
"visual_prompt": "<detailed image generation prompt, 2-3 sentences>",
|
|
120
|
+
"visual_style": "watercolor" | "line_art" | "3d_render" | "photograph" | "diagram" | "animation_storyboard",
|
|
121
|
+
"alt_text": "<screen reader description>",
|
|
122
|
+
"aspect_ratio": "16:9" | "1:1" | "9:16",
|
|
123
|
+
"mood": "<warm/clinical/playful/serious/whimsical>"
|
|
124
|
+
}`,
|
|
125
|
+
anti_elitism_linter: `You are the Anti-Elitism Linter.
|
|
126
|
+
|
|
127
|
+
## Core Identity
|
|
128
|
+
You scan content for subtle elitism, gatekeeping language, and intellectual intimidation.
|
|
129
|
+
Your goal: make the reader feel invited, not tested.
|
|
130
|
+
|
|
131
|
+
## Banned Phrases (hard fail — must be removed or rewritten)
|
|
132
|
+
CATEGORY: Assumed Knowledge
|
|
133
|
+
- "It is obvious that..."
|
|
134
|
+
- "As we all know..."
|
|
135
|
+
- "Clearly..."
|
|
136
|
+
- "Of course..."
|
|
137
|
+
- "Trivially..."
|
|
138
|
+
- "It goes without saying..."
|
|
139
|
+
- "Any competent [role] would..."
|
|
140
|
+
- "This is basic..."
|
|
141
|
+
- "Elementary..."
|
|
142
|
+
|
|
143
|
+
CATEGORY: False Simplification (gaslighting)
|
|
144
|
+
- "Simply put..."
|
|
145
|
+
- "Just [do complex thing]..."
|
|
146
|
+
- "All you have to do is..."
|
|
147
|
+
- "It's easy — just..."
|
|
148
|
+
- "This is straightforward..."
|
|
149
|
+
|
|
150
|
+
CATEGORY: Exclusionary
|
|
151
|
+
- "Real engineers know..."
|
|
152
|
+
- "If you don't understand this..."
|
|
153
|
+
- "This separates beginners from experts..."
|
|
154
|
+
- "You should already know..."
|
|
155
|
+
- "I won't explain [X] here..."
|
|
156
|
+
|
|
157
|
+
CATEGORY: Passive Aggressive
|
|
158
|
+
- "As I mentioned before..."
|
|
159
|
+
- "Again, ..." (implying reader should have gotten it)
|
|
160
|
+
- "For those who missed it..."
|
|
161
|
+
|
|
162
|
+
## Soft Checks (logged, not blocking)
|
|
163
|
+
- Passive voice density > 25%
|
|
164
|
+
- Average sentence length > 20 words
|
|
165
|
+
- Paragraph length > 5 sentences
|
|
166
|
+
- No questions in 500+ words (disengaging)
|
|
167
|
+
- No concrete examples in 300+ words
|
|
168
|
+
|
|
169
|
+
## Output Format
|
|
170
|
+
{
|
|
171
|
+
"verdict": "CLEAN" | "FLAGGED",
|
|
172
|
+
"hard_fails": [
|
|
173
|
+
{
|
|
174
|
+
"phrase": "<exact banned phrase found>",
|
|
175
|
+
"category": "<category name>",
|
|
176
|
+
"line": "<approximate location>",
|
|
177
|
+
"replacement": "<suggested rewrite>"
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"soft_warnings": [
|
|
181
|
+
{
|
|
182
|
+
"check": "<which soft check>",
|
|
183
|
+
"value": "<measured value>",
|
|
184
|
+
"threshold": "<the threshold>",
|
|
185
|
+
"suggestion": "<how to improve>"
|
|
186
|
+
}
|
|
187
|
+
],
|
|
188
|
+
"readability": {
|
|
189
|
+
"flesch_kincaid_grade": <number>,
|
|
190
|
+
"passive_voice_pct": <number>,
|
|
191
|
+
"avg_sentence_length": <number>,
|
|
192
|
+
"jargon_density": <number, terms per 100 words>
|
|
193
|
+
},
|
|
194
|
+
"elitism_score": <0-100, 0 = fully inclusive, 100 = textbook gatekeeping>
|
|
195
|
+
}`
|
|
196
|
+
};
|
|
197
|
+
export function deriveThompsonGrade(checklist) {
|
|
198
|
+
const checks = Object.values(checklist);
|
|
199
|
+
const passing = checks.filter(Boolean).length;
|
|
200
|
+
if (passing >= 9)
|
|
201
|
+
return "exemplary"; // 9-10 of 10
|
|
202
|
+
if (passing >= 7)
|
|
203
|
+
return "passing"; // 7-8 of 10
|
|
204
|
+
if (passing >= 5)
|
|
205
|
+
return "needs_work"; // 5-6 of 10
|
|
206
|
+
return "failing"; // 0-4 of 10
|
|
207
|
+
}
|
|
208
|
+
// ─── Banned Phrases (deterministic, no LLM needed) ──────────────────────────
|
|
209
|
+
const BANNED_PHRASES = [
|
|
210
|
+
// Assumed Knowledge
|
|
211
|
+
{ phrase: "it is obvious that", category: "assumed_knowledge", replacement: "Here's what's happening:" },
|
|
212
|
+
{ phrase: "as we all know", category: "assumed_knowledge", replacement: "Here's the background:" },
|
|
213
|
+
{ phrase: "clearly", category: "assumed_knowledge", replacement: "[remove or replace with evidence]" },
|
|
214
|
+
{ phrase: "of course", category: "assumed_knowledge", replacement: "[remove or provide context]" },
|
|
215
|
+
{ phrase: "trivially", category: "assumed_knowledge", replacement: "[explain the steps]" },
|
|
216
|
+
{ phrase: "it goes without saying", category: "assumed_knowledge", replacement: "[then say it explicitly]" },
|
|
217
|
+
{ phrase: "any competent", category: "assumed_knowledge", replacement: "[remove judgment]" },
|
|
218
|
+
{ phrase: "this is basic", category: "assumed_knowledge", replacement: "[explain it anyway]" },
|
|
219
|
+
{ phrase: "elementary", category: "assumed_knowledge", replacement: "[treat as worth explaining]" },
|
|
220
|
+
// False Simplification
|
|
221
|
+
{ phrase: "simply put", category: "false_simplification", replacement: "Here's one way to think about it:" },
|
|
222
|
+
{ phrase: "just do", category: "false_simplification", replacement: "[break into steps]" },
|
|
223
|
+
{ phrase: "all you have to do is", category: "false_simplification", replacement: "Here are the steps:" },
|
|
224
|
+
{ phrase: "it's easy", category: "false_simplification", replacement: "[acknowledge complexity, then guide]" },
|
|
225
|
+
{ phrase: "this is straightforward", category: "false_simplification", replacement: "Here's how it works:" },
|
|
226
|
+
// Exclusionary
|
|
227
|
+
{ phrase: "real engineers know", category: "exclusionary", replacement: "[remove gatekeeping]" },
|
|
228
|
+
{ phrase: "if you don't understand", category: "exclusionary", replacement: "Let me break this down:" },
|
|
229
|
+
{ phrase: "this separates beginners from experts", category: "exclusionary", replacement: "[remove hierarchy]" },
|
|
230
|
+
{ phrase: "you should already know", category: "exclusionary", replacement: "Quick background:" },
|
|
231
|
+
{ phrase: "i won't explain", category: "exclusionary", replacement: "[explain it or link to explanation]" },
|
|
232
|
+
// Passive Aggressive
|
|
233
|
+
{ phrase: "as i mentioned before", category: "passive_aggressive", replacement: "[just restate it]" },
|
|
234
|
+
{ phrase: "for those who missed it", category: "passive_aggressive", replacement: "[just restate it]" },
|
|
235
|
+
];
|
|
236
|
+
function lintBannedPhrases(text) {
|
|
237
|
+
const lower = text.toLowerCase();
|
|
238
|
+
const hits = [];
|
|
239
|
+
for (const entry of BANNED_PHRASES) {
|
|
240
|
+
let pos = 0;
|
|
241
|
+
while ((pos = lower.indexOf(entry.phrase, pos)) !== -1) {
|
|
242
|
+
hits.push({ ...entry, position: pos });
|
|
243
|
+
pos += entry.phrase.length;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return hits;
|
|
247
|
+
}
|
|
248
|
+
function computeReadabilityMetrics(text) {
|
|
249
|
+
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
250
|
+
const words = text.split(/\s+/).filter(w => w.length > 0);
|
|
251
|
+
const syllableCount = words.reduce((sum, w) => sum + countSyllables(w), 0);
|
|
252
|
+
const avgSentenceLength = sentences.length > 0 ? words.length / sentences.length : 0;
|
|
253
|
+
const avgSyllablesPerWord = words.length > 0 ? syllableCount / words.length : 0;
|
|
254
|
+
// Flesch-Kincaid Grade Level
|
|
255
|
+
const fkGrade = 0.39 * avgSentenceLength + 11.8 * avgSyllablesPerWord - 15.59;
|
|
256
|
+
// Passive voice detection (heuristic: "is/was/were/been/being" + past participle pattern)
|
|
257
|
+
const passivePatterns = /\b(is|was|were|been|being|are)\s+\w+ed\b/gi;
|
|
258
|
+
const passiveMatches = text.match(passivePatterns) || [];
|
|
259
|
+
const passiveVoicePct = sentences.length > 0 ? (passiveMatches.length / sentences.length) * 100 : 0;
|
|
260
|
+
// Jargon density (words > 3 syllables per 100 words)
|
|
261
|
+
const complexWords = words.filter(w => countSyllables(w) > 3).length;
|
|
262
|
+
const jargonDensity = words.length > 0 ? (complexWords / words.length) * 100 : 0;
|
|
263
|
+
return {
|
|
264
|
+
fleschKincaidGrade: Math.max(0, Math.round(fkGrade * 10) / 10),
|
|
265
|
+
passiveVoicePct: Math.round(passiveVoicePct * 10) / 10,
|
|
266
|
+
avgSentenceLength: Math.round(avgSentenceLength * 10) / 10,
|
|
267
|
+
jargonDensity: Math.round(jargonDensity * 10) / 10,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function countSyllables(word) {
|
|
271
|
+
const w = word.toLowerCase().replace(/[^a-z]/g, "");
|
|
272
|
+
if (w.length <= 3)
|
|
273
|
+
return 1;
|
|
274
|
+
let count = 0;
|
|
275
|
+
const vowels = "aeiouy";
|
|
276
|
+
let prevVowel = false;
|
|
277
|
+
for (const ch of w) {
|
|
278
|
+
const isVowel = vowels.includes(ch);
|
|
279
|
+
if (isVowel && !prevVowel)
|
|
280
|
+
count++;
|
|
281
|
+
prevVowel = isVowel;
|
|
282
|
+
}
|
|
283
|
+
if (w.endsWith("e") && count > 1)
|
|
284
|
+
count--;
|
|
285
|
+
return Math.max(1, count);
|
|
286
|
+
}
|
|
287
|
+
// ─── Tool Definitions ────────────────────────────────────────────────────────
|
|
288
|
+
export function createThompsonProtocolTools() {
|
|
289
|
+
return [
|
|
290
|
+
// ── Tool 1: Thompson Writer ──────────────────────────────────────────
|
|
291
|
+
{
|
|
292
|
+
name: "thompson_write",
|
|
293
|
+
description: "Transform complex content into Thompson Protocol format — plain English mandate, intuition-before-mechanics, analogy-per-concept. Returns structured sections with jargon translations and readability scores.",
|
|
294
|
+
inputSchema: {
|
|
295
|
+
type: "object",
|
|
296
|
+
properties: {
|
|
297
|
+
topic: {
|
|
298
|
+
type: "string",
|
|
299
|
+
description: "The complex topic to explain (e.g., 'Transformer attention mechanisms', 'Federal Reserve quantitative tightening')",
|
|
300
|
+
},
|
|
301
|
+
target_audience: {
|
|
302
|
+
type: "string",
|
|
303
|
+
description: "Who is this for? (e.g., 'curious non-technical adult', 'junior developer', 'high school student')",
|
|
304
|
+
default: "curious non-technical adult",
|
|
305
|
+
},
|
|
306
|
+
raw_content: {
|
|
307
|
+
type: "string",
|
|
308
|
+
description: "Optional: raw technical content to transform. If omitted, generates from topic alone.",
|
|
309
|
+
},
|
|
310
|
+
max_sections: {
|
|
311
|
+
type: "number",
|
|
312
|
+
description: "Maximum number of content sections to produce (default: 5)",
|
|
313
|
+
default: 5,
|
|
314
|
+
},
|
|
315
|
+
output_format: {
|
|
316
|
+
type: "string",
|
|
317
|
+
enum: ["script", "article", "thread", "explainer"],
|
|
318
|
+
description: "Target format: video script, long-form article, social thread, or short explainer",
|
|
319
|
+
default: "script",
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
required: ["topic"],
|
|
323
|
+
},
|
|
324
|
+
handler: async (params) => {
|
|
325
|
+
const topic = params.topic;
|
|
326
|
+
const audience = params.target_audience || "curious non-technical adult";
|
|
327
|
+
const rawContent = params.raw_content;
|
|
328
|
+
const maxSections = params.max_sections || 5;
|
|
329
|
+
const format = params.output_format || "script";
|
|
330
|
+
return {
|
|
331
|
+
content: [
|
|
332
|
+
{
|
|
333
|
+
type: "text",
|
|
334
|
+
text: JSON.stringify({
|
|
335
|
+
tool: "thompson_write",
|
|
336
|
+
status: "ready",
|
|
337
|
+
system_prompt: THOMPSON_SYSTEM_PROMPTS.writer,
|
|
338
|
+
task: {
|
|
339
|
+
topic,
|
|
340
|
+
target_audience: audience,
|
|
341
|
+
raw_content: rawContent || null,
|
|
342
|
+
max_sections: maxSections,
|
|
343
|
+
output_format: format,
|
|
344
|
+
instructions: [
|
|
345
|
+
`Transform "${topic}" into ${maxSections} sections for a ${audience}.`,
|
|
346
|
+
`Format: ${format}`,
|
|
347
|
+
"Apply ALL 5 mechanical constraints from the Thompson Protocol.",
|
|
348
|
+
"Each section MUST include: concept, plain_english, analogy, jargon_translations, difficulty_acknowledgment.",
|
|
349
|
+
rawContent
|
|
350
|
+
? "Transform the provided raw_content — do NOT discard any core facts."
|
|
351
|
+
: "Research and generate content from scratch for this topic.",
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
next_step: "Pass output to thompson_feynman_edit for rejection loop",
|
|
355
|
+
_hint: "Use call_llm with this system_prompt and task to generate the content. The LLM does the actual writing — this tool provides the protocol constraints.",
|
|
356
|
+
}, null, 2),
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
// ── Tool 2: Feynman Editor ───────────────────────────────────────────
|
|
363
|
+
{
|
|
364
|
+
name: "thompson_feynman_edit",
|
|
365
|
+
description: "Skeptical Beginner editor — reviews Thompson-written content against 8 rejection criteria. Returns PASS/REWRITE per section with specific fix instructions. Max 3 rewrite cycles.",
|
|
366
|
+
inputSchema: {
|
|
367
|
+
type: "object",
|
|
368
|
+
properties: {
|
|
369
|
+
sections: {
|
|
370
|
+
type: "string",
|
|
371
|
+
description: "JSON string of sections from thompson_write output",
|
|
372
|
+
},
|
|
373
|
+
rewrite_cycle: {
|
|
374
|
+
type: "number",
|
|
375
|
+
description: "Current rewrite cycle (1-3). After 3, escalates stuck sections.",
|
|
376
|
+
default: 1,
|
|
377
|
+
},
|
|
378
|
+
strict_mode: {
|
|
379
|
+
type: "boolean",
|
|
380
|
+
description: "If true, enforces Flesch-Kincaid < 8 instead of < 10",
|
|
381
|
+
default: false,
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
required: ["sections"],
|
|
385
|
+
},
|
|
386
|
+
handler: async (params) => {
|
|
387
|
+
const sections = params.sections;
|
|
388
|
+
const cycle = params.rewrite_cycle || 1;
|
|
389
|
+
const strict = params.strict_mode || false;
|
|
390
|
+
const fkThreshold = strict ? 8 : 10;
|
|
391
|
+
// Run deterministic checks on the raw text
|
|
392
|
+
const metrics = computeReadabilityMetrics(sections);
|
|
393
|
+
const bannedHits = lintBannedPhrases(sections);
|
|
394
|
+
const deterministicFlags = [];
|
|
395
|
+
if (metrics.fleschKincaidGrade > fkThreshold) {
|
|
396
|
+
deterministicFlags.push(`Flesch-Kincaid grade ${metrics.fleschKincaidGrade} exceeds threshold ${fkThreshold}`);
|
|
397
|
+
}
|
|
398
|
+
if (metrics.passiveVoicePct > 20) {
|
|
399
|
+
deterministicFlags.push(`Passive voice at ${metrics.passiveVoicePct}% (threshold: 20%)`);
|
|
400
|
+
}
|
|
401
|
+
if (bannedHits.length > 0) {
|
|
402
|
+
deterministicFlags.push(`${bannedHits.length} banned phrase(s) found: ${bannedHits.map(h => `"${h.phrase}"`).join(", ")}`);
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
content: [
|
|
406
|
+
{
|
|
407
|
+
type: "text",
|
|
408
|
+
text: JSON.stringify({
|
|
409
|
+
tool: "thompson_feynman_edit",
|
|
410
|
+
status: "ready",
|
|
411
|
+
system_prompt: THOMPSON_SYSTEM_PROMPTS.feynman_editor,
|
|
412
|
+
rewrite_cycle: cycle,
|
|
413
|
+
max_cycles: 3,
|
|
414
|
+
escalate_if_stuck: cycle >= 3,
|
|
415
|
+
fk_threshold: fkThreshold,
|
|
416
|
+
deterministic_checks: {
|
|
417
|
+
readability: metrics,
|
|
418
|
+
banned_phrases: bannedHits,
|
|
419
|
+
auto_flags: deterministicFlags,
|
|
420
|
+
},
|
|
421
|
+
task: {
|
|
422
|
+
sections_to_review: sections,
|
|
423
|
+
instructions: [
|
|
424
|
+
"Review EACH section against the 8 rejection criteria.",
|
|
425
|
+
`This is rewrite cycle ${cycle}/3.`,
|
|
426
|
+
deterministicFlags.length > 0
|
|
427
|
+
? `DETERMINISTIC FLAGS (already detected — these MUST be fixed): ${deterministicFlags.join("; ")}`
|
|
428
|
+
: "No deterministic flags — focus on semantic review.",
|
|
429
|
+
cycle >= 3
|
|
430
|
+
? "FINAL CYCLE: If sections still fail, emit escalation summary instead of rewrite prompt."
|
|
431
|
+
: "Return PASS or REWRITE verdict per section with specific fix instructions.",
|
|
432
|
+
],
|
|
433
|
+
},
|
|
434
|
+
next_step: cycle >= 3
|
|
435
|
+
? "If all PASS → thompson_visual_map. If stuck → surface escalation to user."
|
|
436
|
+
: "If REWRITE sections exist → send back to thompson_write. If all PASS → thompson_visual_map.",
|
|
437
|
+
}, null, 2),
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
// ── Tool 3: Visual Metaphor Mapper ───────────────────────────────────
|
|
444
|
+
{
|
|
445
|
+
name: "thompson_visual_map",
|
|
446
|
+
description: "Generate precise visual prompts that map 1:1 with content analogies. No generic b-roll — every visual reinforces a specific metaphor from the Thompson-written content.",
|
|
447
|
+
inputSchema: {
|
|
448
|
+
type: "object",
|
|
449
|
+
properties: {
|
|
450
|
+
sections: {
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "JSON string of Feynman-approved sections",
|
|
453
|
+
},
|
|
454
|
+
visual_style: {
|
|
455
|
+
type: "string",
|
|
456
|
+
enum: ["watercolor", "line_art", "3d_render", "photograph", "diagram", "animation_storyboard"],
|
|
457
|
+
description: "Consistent visual style across all generated prompts",
|
|
458
|
+
default: "line_art",
|
|
459
|
+
},
|
|
460
|
+
aspect_ratio: {
|
|
461
|
+
type: "string",
|
|
462
|
+
enum: ["16:9", "1:1", "9:16"],
|
|
463
|
+
description: "Target aspect ratio (16:9 for video, 1:1 for social, 9:16 for shorts/reels)",
|
|
464
|
+
default: "16:9",
|
|
465
|
+
},
|
|
466
|
+
image_generator: {
|
|
467
|
+
type: "string",
|
|
468
|
+
enum: ["dall_e_3", "midjourney", "stable_diffusion", "flux", "generic"],
|
|
469
|
+
description: "Target image generator for prompt optimization",
|
|
470
|
+
default: "generic",
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
required: ["sections"],
|
|
474
|
+
},
|
|
475
|
+
handler: async (params) => {
|
|
476
|
+
const sections = params.sections;
|
|
477
|
+
const style = params.visual_style || "line_art";
|
|
478
|
+
const ratio = params.aspect_ratio || "16:9";
|
|
479
|
+
const generator = params.image_generator || "generic";
|
|
480
|
+
return {
|
|
481
|
+
content: [
|
|
482
|
+
{
|
|
483
|
+
type: "text",
|
|
484
|
+
text: JSON.stringify({
|
|
485
|
+
tool: "thompson_visual_map",
|
|
486
|
+
status: "ready",
|
|
487
|
+
system_prompt: THOMPSON_SYSTEM_PROMPTS.visual_mapper,
|
|
488
|
+
task: {
|
|
489
|
+
approved_sections: sections,
|
|
490
|
+
visual_constraints: {
|
|
491
|
+
style,
|
|
492
|
+
aspect_ratio: ratio,
|
|
493
|
+
target_generator: generator,
|
|
494
|
+
rules: [
|
|
495
|
+
"ONE visual per analogy — no generic stock imagery",
|
|
496
|
+
"LITERAL mapping: if text says 'like a librarian,' show a literal librarian",
|
|
497
|
+
"SCENE CONTINUITY: all visuals must feel like the same world",
|
|
498
|
+
"NO TEXT IN VISUALS: the image must work without overlaid text",
|
|
499
|
+
"Include alt_text for every visual (accessibility)",
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
instructions: [
|
|
503
|
+
"Extract every analogy from the approved sections.",
|
|
504
|
+
"For each analogy, generate a precise image prompt.",
|
|
505
|
+
`Style: ${style}, Aspect ratio: ${ratio}, Generator: ${generator}`,
|
|
506
|
+
"Output one visual_prompt object per analogy.",
|
|
507
|
+
],
|
|
508
|
+
},
|
|
509
|
+
next_step: "Pass output to thompson_anti_elitism_lint for final check",
|
|
510
|
+
}, null, 2),
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
};
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
// ── Tool 4: Anti-Elitism Linter ──────────────────────────────────────
|
|
517
|
+
{
|
|
518
|
+
name: "thompson_anti_elitism_lint",
|
|
519
|
+
description: "Scan content for elitism, gatekeeping language, and intellectual intimidation. Deterministic banned-phrase detection + readability scoring. Returns CLEAN or FLAGGED with specific fixes.",
|
|
520
|
+
inputSchema: {
|
|
521
|
+
type: "object",
|
|
522
|
+
properties: {
|
|
523
|
+
content: {
|
|
524
|
+
type: "string",
|
|
525
|
+
description: "The full content text to lint (plain text, not JSON)",
|
|
526
|
+
},
|
|
527
|
+
strict_mode: {
|
|
528
|
+
type: "boolean",
|
|
529
|
+
description: "If true, soft checks become hard fails",
|
|
530
|
+
default: false,
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
required: ["content"],
|
|
534
|
+
},
|
|
535
|
+
handler: async (params) => {
|
|
536
|
+
const content = params.content;
|
|
537
|
+
const strict = params.strict_mode || false;
|
|
538
|
+
// Deterministic analysis — no LLM needed
|
|
539
|
+
const bannedHits = lintBannedPhrases(content);
|
|
540
|
+
const metrics = computeReadabilityMetrics(content);
|
|
541
|
+
// Soft checks
|
|
542
|
+
const softWarnings = [];
|
|
543
|
+
if (metrics.passiveVoicePct > 25) {
|
|
544
|
+
softWarnings.push({
|
|
545
|
+
check: "passive_voice_density",
|
|
546
|
+
value: metrics.passiveVoicePct,
|
|
547
|
+
threshold: 25,
|
|
548
|
+
suggestion: "Rewrite passive constructions to active voice. 'The data was processed' → 'We processed the data'",
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
if (metrics.avgSentenceLength > 20) {
|
|
552
|
+
softWarnings.push({
|
|
553
|
+
check: "avg_sentence_length",
|
|
554
|
+
value: metrics.avgSentenceLength,
|
|
555
|
+
threshold: 20,
|
|
556
|
+
suggestion: "Break long sentences. Each sentence should convey one thought.",
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
// Check for question density
|
|
560
|
+
const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
561
|
+
const questions = content.split("?").length - 1;
|
|
562
|
+
const words = content.split(/\s+/).length;
|
|
563
|
+
if (words > 500 && questions === 0) {
|
|
564
|
+
softWarnings.push({
|
|
565
|
+
check: "no_questions",
|
|
566
|
+
value: 0,
|
|
567
|
+
threshold: 1,
|
|
568
|
+
suggestion: "Add at least one question per 500 words to maintain reader engagement.",
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
// Check for concrete example density
|
|
572
|
+
const exampleIndicators = /\b(for example|for instance|such as|like when|imagine|picture this|think of)\b/gi;
|
|
573
|
+
const exampleCount = (content.match(exampleIndicators) || []).length;
|
|
574
|
+
if (words > 300 && exampleCount === 0) {
|
|
575
|
+
softWarnings.push({
|
|
576
|
+
check: "no_concrete_examples",
|
|
577
|
+
value: 0,
|
|
578
|
+
threshold: 1,
|
|
579
|
+
suggestion: "Add concrete examples. Abstract explanations without examples lose readers.",
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
const isClean = bannedHits.length === 0 && (!strict || softWarnings.length === 0);
|
|
583
|
+
// Compute elitism score (0 = inclusive, 100 = gatekeeping)
|
|
584
|
+
let elitismScore = 0;
|
|
585
|
+
elitismScore += Math.min(bannedHits.length * 15, 60); // up to 60 from banned phrases
|
|
586
|
+
elitismScore += Math.min(metrics.jargonDensity * 2, 20); // up to 20 from jargon
|
|
587
|
+
elitismScore += metrics.fleschKincaidGrade > 12 ? 20 : metrics.fleschKincaidGrade > 10 ? 10 : 0;
|
|
588
|
+
elitismScore = Math.min(100, elitismScore);
|
|
589
|
+
return {
|
|
590
|
+
content: [
|
|
591
|
+
{
|
|
592
|
+
type: "text",
|
|
593
|
+
text: JSON.stringify({
|
|
594
|
+
tool: "thompson_anti_elitism_lint",
|
|
595
|
+
verdict: isClean ? "CLEAN" : "FLAGGED",
|
|
596
|
+
hard_fails: bannedHits.map(h => ({
|
|
597
|
+
phrase: h.phrase,
|
|
598
|
+
category: h.category,
|
|
599
|
+
position: h.position,
|
|
600
|
+
replacement: h.replacement,
|
|
601
|
+
})),
|
|
602
|
+
soft_warnings: softWarnings,
|
|
603
|
+
readability: {
|
|
604
|
+
flesch_kincaid_grade: metrics.fleschKincaidGrade,
|
|
605
|
+
passive_voice_pct: metrics.passiveVoicePct,
|
|
606
|
+
avg_sentence_length: metrics.avgSentenceLength,
|
|
607
|
+
jargon_density: metrics.jargonDensity,
|
|
608
|
+
},
|
|
609
|
+
elitism_score: elitismScore,
|
|
610
|
+
word_count: words,
|
|
611
|
+
question_count: questions,
|
|
612
|
+
example_count: exampleCount,
|
|
613
|
+
_hint: isClean
|
|
614
|
+
? "Content passes anti-elitism lint. Proceed to thompson_quality_gate."
|
|
615
|
+
: `Content has ${bannedHits.length} hard fail(s) and ${softWarnings.length} soft warning(s). Fix banned phrases first, then address soft warnings.`,
|
|
616
|
+
}, null, 2),
|
|
617
|
+
},
|
|
618
|
+
],
|
|
619
|
+
};
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
// ── Tool 5: Quality Gate ─────────────────────────────────────────────
|
|
623
|
+
{
|
|
624
|
+
name: "thompson_quality_gate",
|
|
625
|
+
description: "Deterministic 10-point quality gate for Thompson Protocol content. Produces a boolean checklist and overall grade (exemplary/passing/needs_work/failing).",
|
|
626
|
+
inputSchema: {
|
|
627
|
+
type: "object",
|
|
628
|
+
properties: {
|
|
629
|
+
writer_output: {
|
|
630
|
+
type: "string",
|
|
631
|
+
description: "JSON output from thompson_write (sections with jargon_translations, analogies, etc.)",
|
|
632
|
+
},
|
|
633
|
+
feynman_verdict: {
|
|
634
|
+
type: "string",
|
|
635
|
+
description: "JSON output from thompson_feynman_edit (PASS/REWRITE verdicts)",
|
|
636
|
+
},
|
|
637
|
+
lint_result: {
|
|
638
|
+
type: "string",
|
|
639
|
+
description: "JSON output from thompson_anti_elitism_lint",
|
|
640
|
+
},
|
|
641
|
+
visual_map: {
|
|
642
|
+
type: "string",
|
|
643
|
+
description: "JSON output from thompson_visual_map (optional — omit if no visuals needed)",
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
required: ["writer_output", "feynman_verdict", "lint_result"],
|
|
647
|
+
},
|
|
648
|
+
handler: async (params) => {
|
|
649
|
+
const writerRaw = params.writer_output;
|
|
650
|
+
const feynmanRaw = params.feynman_verdict;
|
|
651
|
+
const lintRaw = params.lint_result;
|
|
652
|
+
const visualRaw = params.visual_map;
|
|
653
|
+
// Parse inputs safely
|
|
654
|
+
let writerData = {};
|
|
655
|
+
let feynmanData = {};
|
|
656
|
+
let lintData = {};
|
|
657
|
+
let visualData = null;
|
|
658
|
+
try {
|
|
659
|
+
writerData = JSON.parse(writerRaw);
|
|
660
|
+
}
|
|
661
|
+
catch { /* use empty */ }
|
|
662
|
+
try {
|
|
663
|
+
feynmanData = JSON.parse(feynmanRaw);
|
|
664
|
+
}
|
|
665
|
+
catch { /* use empty */ }
|
|
666
|
+
try {
|
|
667
|
+
lintData = JSON.parse(lintRaw);
|
|
668
|
+
}
|
|
669
|
+
catch { /* use empty */ }
|
|
670
|
+
if (visualRaw) {
|
|
671
|
+
try {
|
|
672
|
+
visualData = JSON.parse(visualRaw);
|
|
673
|
+
}
|
|
674
|
+
catch { /* use null */ }
|
|
675
|
+
}
|
|
676
|
+
// Build checklist from pipeline outputs
|
|
677
|
+
const lintVerdict = lintData.verdict;
|
|
678
|
+
const hardFails = lintData.hard_fails || [];
|
|
679
|
+
const readability = lintData.readability;
|
|
680
|
+
const fkGrade = readability?.flesch_kincaid_grade ?? 99;
|
|
681
|
+
// Check writer output has required fields
|
|
682
|
+
const taskData = writerData.task || writerData;
|
|
683
|
+
const hasJargonTranslations = JSON.stringify(taskData).includes("jargon_translation");
|
|
684
|
+
const hasAnalogy = JSON.stringify(taskData).includes("analogy");
|
|
685
|
+
const hasDifficultyAck = JSON.stringify(taskData).includes("difficulty_acknowledgment") ||
|
|
686
|
+
JSON.stringify(taskData).includes("sounds terrifying") ||
|
|
687
|
+
JSON.stringify(taskData).includes("feels confusing");
|
|
688
|
+
// Check Feynman verdicts
|
|
689
|
+
const feynmanStr = JSON.stringify(feynmanData);
|
|
690
|
+
const allPass = !feynmanStr.includes('"REWRITE"') && feynmanStr.includes("PASS");
|
|
691
|
+
const checklist = {
|
|
692
|
+
hasPlainEnglishTranslations: hasJargonTranslations,
|
|
693
|
+
hasAnalogyPerConcept: hasAnalogy,
|
|
694
|
+
hasDifficultyAcknowledgment: hasDifficultyAck,
|
|
695
|
+
hasIntuitionBeforeMechanics: JSON.stringify(taskData).includes("purpose") || JSON.stringify(taskData).includes("intuition"),
|
|
696
|
+
passesFeynmanEdit: allPass,
|
|
697
|
+
passesAntiElitismLint: lintVerdict === "CLEAN",
|
|
698
|
+
hasVisualMetaphors: visualData !== null,
|
|
699
|
+
fleschKincaidUnder10: fkGrade < 10,
|
|
700
|
+
noBannedPhrases: hardFails.length === 0,
|
|
701
|
+
hasProgressiveComplexity: true, // Checked structurally by Feynman Editor
|
|
702
|
+
};
|
|
703
|
+
const grade = deriveThompsonGrade(checklist);
|
|
704
|
+
const passing = Object.values(checklist).filter(Boolean).length;
|
|
705
|
+
return {
|
|
706
|
+
content: [
|
|
707
|
+
{
|
|
708
|
+
type: "text",
|
|
709
|
+
text: JSON.stringify({
|
|
710
|
+
tool: "thompson_quality_gate",
|
|
711
|
+
grade,
|
|
712
|
+
score: `${passing}/10`,
|
|
713
|
+
checklist,
|
|
714
|
+
pass: grade === "exemplary" || grade === "passing",
|
|
715
|
+
action: grade === "exemplary" || grade === "passing"
|
|
716
|
+
? "Content approved. Ready for distribution via content_publish workflow."
|
|
717
|
+
: grade === "needs_work"
|
|
718
|
+
? "Send back to thompson_write for targeted fixes on failing checks."
|
|
719
|
+
: "Major revision needed. Review failing checks and restart pipeline.",
|
|
720
|
+
failing_checks: Object.entries(checklist)
|
|
721
|
+
.filter(([, v]) => !v)
|
|
722
|
+
.map(([k]) => k),
|
|
723
|
+
}, null, 2),
|
|
724
|
+
},
|
|
725
|
+
],
|
|
726
|
+
};
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
// ── Tool 6: Pipeline Orchestrator ────────────────────────────────────
|
|
730
|
+
{
|
|
731
|
+
name: "thompson_pipeline",
|
|
732
|
+
description: "End-to-end Thompson Protocol pipeline orchestrator. Takes a complex topic and runs it through all 4 agents (Writer → Feynman Editor → Visual Mapper → Anti-Elitism Linter) with quality gate. Returns the full execution plan with agent prompts and handoff points.",
|
|
733
|
+
inputSchema: {
|
|
734
|
+
type: "object",
|
|
735
|
+
properties: {
|
|
736
|
+
topic: {
|
|
737
|
+
type: "string",
|
|
738
|
+
description: "The complex topic to make accessible",
|
|
739
|
+
},
|
|
740
|
+
target_audience: {
|
|
741
|
+
type: "string",
|
|
742
|
+
description: "Who is this for?",
|
|
743
|
+
default: "curious non-technical adult",
|
|
744
|
+
},
|
|
745
|
+
output_format: {
|
|
746
|
+
type: "string",
|
|
747
|
+
enum: ["script", "article", "thread", "explainer"],
|
|
748
|
+
default: "script",
|
|
749
|
+
},
|
|
750
|
+
visual_style: {
|
|
751
|
+
type: "string",
|
|
752
|
+
enum: ["watercolor", "line_art", "3d_render", "photograph", "diagram", "animation_storyboard"],
|
|
753
|
+
default: "line_art",
|
|
754
|
+
},
|
|
755
|
+
strict_mode: {
|
|
756
|
+
type: "boolean",
|
|
757
|
+
description: "Stricter readability and elitism thresholds",
|
|
758
|
+
default: false,
|
|
759
|
+
},
|
|
760
|
+
skip_visuals: {
|
|
761
|
+
type: "boolean",
|
|
762
|
+
description: "Skip visual metaphor mapping (for text-only content)",
|
|
763
|
+
default: false,
|
|
764
|
+
},
|
|
765
|
+
raw_content: {
|
|
766
|
+
type: "string",
|
|
767
|
+
description: "Optional: raw technical content to transform instead of generating from scratch",
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
required: ["topic"],
|
|
771
|
+
},
|
|
772
|
+
handler: async (params) => {
|
|
773
|
+
const topic = params.topic;
|
|
774
|
+
const audience = params.target_audience || "curious non-technical adult";
|
|
775
|
+
const format = params.output_format || "script";
|
|
776
|
+
const style = params.visual_style || "line_art";
|
|
777
|
+
const strict = params.strict_mode || false;
|
|
778
|
+
const skipVisuals = params.skip_visuals || false;
|
|
779
|
+
const rawContent = params.raw_content;
|
|
780
|
+
const steps = [
|
|
781
|
+
{
|
|
782
|
+
step: 1,
|
|
783
|
+
agent: "Thompson Writer",
|
|
784
|
+
tool: "thompson_write",
|
|
785
|
+
action: `Transform "${topic}" into plain-English ${format} for ${audience}`,
|
|
786
|
+
params: { topic, target_audience: audience, output_format: format, raw_content: rawContent || undefined },
|
|
787
|
+
system_prompt_key: "writer",
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
step: 2,
|
|
791
|
+
agent: "Feynman Editor",
|
|
792
|
+
tool: "thompson_feynman_edit",
|
|
793
|
+
action: "Review each section against 8 rejection criteria. Loop up to 3x.",
|
|
794
|
+
params: { strict_mode: strict },
|
|
795
|
+
system_prompt_key: "feynman_editor",
|
|
796
|
+
loop: { max_cycles: 3, reloop_condition: "Any section has REWRITE verdict" },
|
|
797
|
+
},
|
|
798
|
+
...(skipVisuals ? [] : [{
|
|
799
|
+
step: 3,
|
|
800
|
+
agent: "Visual Metaphor Mapper",
|
|
801
|
+
tool: "thompson_visual_map",
|
|
802
|
+
action: "Generate 1:1 visual prompts for each analogy",
|
|
803
|
+
params: { visual_style: style, aspect_ratio: "16:9" },
|
|
804
|
+
system_prompt_key: "visual_mapper",
|
|
805
|
+
}]),
|
|
806
|
+
{
|
|
807
|
+
step: skipVisuals ? 3 : 4,
|
|
808
|
+
agent: "Anti-Elitism Linter",
|
|
809
|
+
tool: "thompson_anti_elitism_lint",
|
|
810
|
+
action: "Scan for banned phrases, readability, and gatekeeping language",
|
|
811
|
+
params: { strict_mode: strict },
|
|
812
|
+
system_prompt_key: "anti_elitism_linter",
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
step: skipVisuals ? 4 : 5,
|
|
816
|
+
agent: "Quality Gate",
|
|
817
|
+
tool: "thompson_quality_gate",
|
|
818
|
+
action: "10-point checklist + grade (exemplary/passing/needs_work/failing)",
|
|
819
|
+
params: {},
|
|
820
|
+
system_prompt_key: null,
|
|
821
|
+
},
|
|
822
|
+
];
|
|
823
|
+
return {
|
|
824
|
+
content: [
|
|
825
|
+
{
|
|
826
|
+
type: "text",
|
|
827
|
+
text: JSON.stringify({
|
|
828
|
+
tool: "thompson_pipeline",
|
|
829
|
+
status: "execution_plan_ready",
|
|
830
|
+
topic,
|
|
831
|
+
target_audience: audience,
|
|
832
|
+
output_format: format,
|
|
833
|
+
pipeline: {
|
|
834
|
+
total_steps: steps.length,
|
|
835
|
+
agents: ["Thompson Writer", "Feynman Editor", ...(skipVisuals ? [] : ["Visual Metaphor Mapper"]), "Anti-Elitism Linter", "Quality Gate"],
|
|
836
|
+
steps,
|
|
837
|
+
},
|
|
838
|
+
system_prompts: THOMPSON_SYSTEM_PROMPTS,
|
|
839
|
+
execution_instructions: [
|
|
840
|
+
"Execute steps sequentially. Each step's output feeds the next.",
|
|
841
|
+
"Step 2 (Feynman Editor) is a LOOP: re-run thompson_write for REWRITE sections, max 3 cycles.",
|
|
842
|
+
"Use call_llm with the system_prompt from each step to generate agent responses.",
|
|
843
|
+
"Step 4 (Anti-Elitism Lint) is fully deterministic — no LLM needed.",
|
|
844
|
+
"Step 5 (Quality Gate) computes grade from pipeline outputs.",
|
|
845
|
+
"If grade is 'failing' or 'needs_work', restart from Step 1 with failing_checks as constraints.",
|
|
846
|
+
],
|
|
847
|
+
next_step: "Begin with thompson_write, then chain through the pipeline.",
|
|
848
|
+
_hint: "This is the orchestrator. Call each tool in sequence, passing outputs forward. The LLM (via call_llm) does the creative work; the tools provide structure and deterministic gates.",
|
|
849
|
+
}, null, 2),
|
|
850
|
+
},
|
|
851
|
+
],
|
|
852
|
+
};
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
];
|
|
856
|
+
}
|
|
857
|
+
// ─── Analogy Density Detection ────────────────────────────────────────────────
|
|
858
|
+
/**
|
|
859
|
+
* Detect positive Thompson signals — analogies, difficulty acknowledgments,
|
|
860
|
+
* "in other words" translations, and progressive complexity markers.
|
|
861
|
+
* Returns a density score (0-100) and specific signal counts.
|
|
862
|
+
*/
|
|
863
|
+
export function computeAnalogyDensity(text) {
|
|
864
|
+
const words = text.split(/\s+/).filter(w => w.length > 0);
|
|
865
|
+
const wordCount = words.length;
|
|
866
|
+
const details = [];
|
|
867
|
+
// Analogy indicators
|
|
868
|
+
const analogyPatterns = /\b(like a|think of|imagine|picture this|it's as if|similar to|the same way|just as a|works like|acts like|behaves like|equivalent of|analogy|metaphor)\b/gi;
|
|
869
|
+
const analogyMatches = text.match(analogyPatterns) || [];
|
|
870
|
+
const analogyCount = analogyMatches.length;
|
|
871
|
+
if (analogyCount > 0)
|
|
872
|
+
details.push(`${analogyCount} analogy marker(s)`);
|
|
873
|
+
// Difficulty acknowledgment indicators
|
|
874
|
+
const difficultyPatterns = /\b(this sounds|this feels|this looks|don't worry|sounds terrifying|sounds scary|sounds complicated|feels confusing|feels overwhelming|most people|most textbooks|usually explained|often misunderstood|commonly confused)\b/gi;
|
|
875
|
+
const difficultyMatches = text.match(difficultyPatterns) || [];
|
|
876
|
+
const difficultyAckCount = difficultyMatches.length;
|
|
877
|
+
if (difficultyAckCount > 0)
|
|
878
|
+
details.push(`${difficultyAckCount} difficulty acknowledgment(s)`);
|
|
879
|
+
// "In other words" translation indicators
|
|
880
|
+
const translationPatterns = /\b(in other words|put differently|meaning|that means|which means|in plain english|in simple terms|translation:|to put it simply|another way to say this)\b/gi;
|
|
881
|
+
const translationMatches = text.match(translationPatterns) || [];
|
|
882
|
+
const translationCount = translationMatches.length;
|
|
883
|
+
if (translationCount > 0)
|
|
884
|
+
details.push(`${translationCount} plain-language translation(s)`);
|
|
885
|
+
// Reader engagement (questions)
|
|
886
|
+
const questionCount = (text.match(/\?/g) || []).length;
|
|
887
|
+
if (questionCount > 0)
|
|
888
|
+
details.push(`${questionCount} question(s)`);
|
|
889
|
+
// Score: weighted sum normalized to 0-100
|
|
890
|
+
// Target: at least 1 analogy per 200 words, 1 translation per 300 words, 1 question per 500 words
|
|
891
|
+
const analogyDensity = wordCount > 0 ? (analogyCount / wordCount) * 200 : 0; // 1.0 = ideal
|
|
892
|
+
const translationDensity = wordCount > 0 ? (translationCount / wordCount) * 300 : 0;
|
|
893
|
+
const questionDensity = wordCount > 0 ? (questionCount / wordCount) * 500 : 0;
|
|
894
|
+
const ackBonus = difficultyAckCount > 0 ? 15 : 0;
|
|
895
|
+
const rawScore = (Math.min(analogyDensity, 2) * 30) + // max 60 from analogies
|
|
896
|
+
(Math.min(translationDensity, 2) * 10) + // max 20 from translations
|
|
897
|
+
(Math.min(questionDensity, 1) * 5) + // max 5 from questions
|
|
898
|
+
ackBonus; // 15 from difficulty ack
|
|
899
|
+
const score = Math.min(100, Math.round(rawScore));
|
|
900
|
+
if (wordCount > 200 && analogyCount === 0) {
|
|
901
|
+
details.push("missing: no analogies in 200+ words");
|
|
902
|
+
}
|
|
903
|
+
if (wordCount > 300 && translationCount === 0) {
|
|
904
|
+
details.push("missing: no plain-language translations in 300+ words");
|
|
905
|
+
}
|
|
906
|
+
return { score, analogyCount, difficultyAckCount, translationCount, questionCount, wordCount, details };
|
|
907
|
+
}
|
|
908
|
+
// ─── Exports for testing ─────────────────────────────────────────────────────
|
|
909
|
+
export const _testExports = {
|
|
910
|
+
lintBannedPhrases,
|
|
911
|
+
computeReadabilityMetrics,
|
|
912
|
+
countSyllables,
|
|
913
|
+
BANNED_PHRASES,
|
|
914
|
+
computeAnalogyDensity,
|
|
915
|
+
};
|
|
916
|
+
//# sourceMappingURL=thompsonProtocolTools.js.map
|