@goodandready/dsh-moa 0.2.10 → 0.2.12

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.
@@ -0,0 +1,270 @@
1
+ // lib/moa-prompts.js
2
+ // Prompts, evaluation rubrics, and message sanitizer for @goodandready/dsh-moa.
3
+
4
+ import { stripOrSummarizeCode } from './moa-parser.js'
5
+
6
+ export const SYSTEM_ROLE_PROPOSER = `You are a specialized expert developer agent participating as an independent advisor in a Mixture of Agents (MoA) ensemble.
7
+ Your task is to provide the highest-quality, robust, complete, production-ready solution to the user request.
8
+ Write clean, modern, fully functional code without placeholders or shortcuts.
9
+ When generating files for a project, explicitly specify file paths using fenced code blocks with file annotations, e.g.:
10
+ \`\`\`html file="index.html"
11
+ \`\`\`
12
+ \`\`\`javascript file="script.js"
13
+ \`\`\`
14
+ \`\`\`css file="style.css"
15
+ \`\`\``
16
+
17
+ export const ANTIPATTERNS_RUBRIC = `### 🚫 Strict Antipatterns Evaluation Checklist
18
+ Penalize and strictly downgrade candidates exhibiting any of the following flaws:
19
+ 1. 🚫 Lazy Code & Placeholders:
20
+ - Phrases like "// ... rest of code unchanged", "/* TODO: implement */", incomplete functions or stubs returning null/mock without notice.
21
+ 2. 🚫 Blind Mocking:
22
+ - Hardcoded dummy arrays instead of real dynamic logic, user input handling, or real API integration.
23
+ 3. 🚫 Silent Failures & Missing Error Handling:
24
+ - Missing try/catch around async calls, fetch, JSON.parse; lack of user-facing fallback or retry states.
25
+ 4. 🚫 AI Slop UI & Poor Ergonomics:
26
+ - Generic purple/cyan gradients on pure black backgrounds, blurry drop-shadows without borders, lack of typographic hierarchy, low contrast (e.g. light gray text on white).
27
+ 5. 🚫 Missing UI States:
28
+ - Lack of loading state (spinner/skeleton), error display with retry action, or empty state when no data exists.
29
+ 6. 🚫 Broken Layout & Mobile Incompatibility:
30
+ - Fixed pixel widths (e.g. width: 800px) overflowing small viewports; unscrollable modal dialogs.
31
+ 7. 🚫 Monolithic God Objects & Overengineering:
32
+ - Dumping 1000+ lines into a single unmaintainable file, or building 10+ abstraction layers for a 2-function task.
33
+ 8. 🚫 Context Amnesia & Regressions:
34
+ - Dropping or breaking previously functioning project features while adding new code.`
35
+
36
+ export const LANGUAGE_MIRRORING_DIRECTIVE = `### 🌐 Language Mirroring Requirement
37
+ CRITICAL: You MUST write your entire analysis, reasoning, verdict, explanations and instructions in the EXACT SAME LANGUAGE as the user's prompt (e.g. Russian if the prompt is written in Russian, English if in English). Do NOT switch or translate to English unless explicitly requested by the user.`
38
+
39
+ /**
40
+ * Formats a provider + model slot into a readable string key.
41
+ */
42
+ export function slotLabel(slot) {
43
+ if (!slot) return 'unknown'
44
+ if (typeof slot === 'string') return slot
45
+ if (slot.provider && slot.model) return `${slot.provider}:${slot.model}`
46
+ return slot.model || slot.provider || 'unknown'
47
+ }
48
+
49
+ /**
50
+ * Extracts and sanitizes conversation history for candidate models.
51
+ * Robust against complex DSH message content types (strings, part arrays, objects, tool calls).
52
+ */
53
+ export function cleanAdvisoryMessages(messages = [], maxCharBudget = 24000) {
54
+ if (!Array.isArray(messages)) return []
55
+
56
+ const trimmed = []
57
+ for (const msg of messages) {
58
+ if (!msg || typeof msg !== 'object') continue
59
+ const role = msg.role
60
+ if (role !== 'user' && role !== 'assistant') continue
61
+
62
+ let text = ''
63
+ if (typeof msg.content === 'string') {
64
+ text = msg.content
65
+ } else if (Array.isArray(msg.content)) {
66
+ text = msg.content
67
+ .filter((part) => part && typeof part === 'object' && part.type === 'text' && typeof part.text === 'string')
68
+ .map((part) => part.text)
69
+ .join('\n')
70
+ } else if (msg.content && typeof msg.content === 'object') {
71
+ if (typeof msg.content.text === 'string') {
72
+ text = msg.content.text
73
+ } else if (typeof msg.content.content === 'string') {
74
+ text = msg.content.content
75
+ }
76
+ } else if (typeof msg.text === 'string') {
77
+ text = msg.text
78
+ }
79
+
80
+ text = text.trim()
81
+ if (!text) continue
82
+
83
+ if (text.length > maxCharBudget) {
84
+ const head = text.slice(0, Math.floor(maxCharBudget * 0.7))
85
+ const tail = text.slice(-Math.floor(maxCharBudget * 0.3))
86
+ text = `${head}\n... [trimmed ${text.length - maxCharBudget} characters] ...\n${tail}`
87
+ }
88
+
89
+ trimmed.push({ role, content: text })
90
+ }
91
+ return trimmed
92
+ }
93
+
94
+ /**
95
+ * Detects whether the user's prompt is a short/vague creative task requiring clarification.
96
+ */
97
+ export function isBroadPromptRequiringQuestions(userPrompt = '', messages = []) {
98
+ if (!userPrompt || typeof userPrompt !== 'string') return false
99
+ const p = userPrompt.trim()
100
+ const wordCount = p.split(/\s+/).length
101
+
102
+ if (/^(да|нет|1|2|3|4|ок|погнали|давай|yes|no)\b/i.test(p) && wordCount <= 5) {
103
+ return false
104
+ }
105
+
106
+ const hasRecentQuestion = messages.some((m) => {
107
+ const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '')
108
+ return text.includes('Уточнение требований') || text.includes('опросник') || text.includes('Вариант 1')
109
+ })
110
+ if (hasRecentQuestion) {
111
+ return false
112
+ }
113
+
114
+ const creationTriggers = [
115
+ 'сделай', 'создай', 'напиши', 'разработай', 'придумай', 'реализуй',
116
+ 'make', 'build', 'create', 'generate', 'develop',
117
+ ]
118
+ const startsWithCreation = creationTriggers.some((t) => p.toLowerCase().startsWith(t))
119
+
120
+ if (startsWithCreation && wordCount <= 18) {
121
+ return true
122
+ }
123
+
124
+ const vagueNouns = ['приложение', 'игру', 'сервис', 'сайт', 'лендинг', 'калькулятор', 'виджет', 'дашборд', 'app', 'game', 'tool', 'website']
125
+ if (vagueNouns.some((n) => p.toLowerCase().includes(n))) {
126
+ if (wordCount <= 12) return true
127
+ }
128
+
129
+ return false
130
+ }
131
+
132
+ /**
133
+ * Builds the questionnaire prompt for broad/unclear tasks.
134
+ */
135
+ export function buildQuestionSynthesisPrompt(userPrompt, referenceOutputs = []) {
136
+ const joined = referenceOutputs
137
+ .map((r, i) => `Advisor ${i + 1} (${r.label}):\n${r.text}`)
138
+ .join('\n\n')
139
+
140
+ return `You are the lead architect and judge in a Mixture of Agents (MoA) ensemble.
141
+ The user gave the task:
142
+ "${userPrompt}"
143
+
144
+ The advisors proposed the following decision points and clarifications:
145
+ ${joined}
146
+
147
+ ${LANGUAGE_MIRRORING_DIRECTIVE}
148
+
149
+ Your task is to synthesize a single, compact, friendly and structured questionnaire (2-4 questions) in the same language as the user's prompt.
150
+ Each question must offer 2-3 concrete recommended answer options (e.g.: 1. Format: single-file HTML/JS or React? 2. Style: minimalism, iOS or neubrutalism?).
151
+ At the end, add a note that the user can answer briefly (e.g.: "1, 2, dark theme") or trust the defaults.`
152
+ }
153
+
154
+ /**
155
+ * Builds the curator synthesis prompt with component analysis, model recommendation, and optional blind review.
156
+ */
157
+ export function buildCuratorSynthesisPrompt(userPrompt, referenceOutputs = [], judgeCriteria = '', options = {}) {
158
+ const isBlind = Boolean(options.blindEvaluation)
159
+
160
+ const joined = referenceOutputs
161
+ .map((r, i) => {
162
+ const fileSummary = (r.files && r.files.length > 0)
163
+ ? ` [Files created: ${r.files.map((f) => f.relativePath).join(', ')}]`
164
+ : ''
165
+ let textContent = r.text
166
+ if (referenceOutputs.length >= 3 && textContent.length > 3000) {
167
+ textContent = stripOrSummarizeCode(textContent)
168
+ }
169
+ const header = isBlind
170
+ ? `Candidate ${i + 1}:${fileSummary}`
171
+ : `Candidate ${i + 1} — ${r.label}:${fileSummary}`
172
+ return `${header}\n${textContent}`
173
+ })
174
+ .join('\n\n')
175
+
176
+ const criteriaBlock = judgeCriteria && judgeCriteria.trim()
177
+ ? `\n### 🎯 Additional evaluation criteria:\n${judgeCriteria.trim()}\n`
178
+ : ''
179
+
180
+ return `You are the expert Lead Technical Curator and Solution Architect in a Mixture of Agents (MoA) ensemble.
181
+ Your mission is not merely to select one candidate, but to synthesize the optimal solution by extracting the finest components from each candidate's response, identifying potential flaws using the strict antipatterns rubric, and selecting/advising which single agent model is best suited to assemble the final unified deliverable.
182
+
183
+ User Request:
184
+ ${userPrompt}
185
+ ${criteriaBlock}
186
+ Candidate proposals:
187
+ ${joined}
188
+
189
+ ${ANTIPATTERNS_RUBRIC}
190
+
191
+ ${LANGUAGE_MIRRORING_DIRECTIVE}
192
+
193
+ Instructions:
194
+ Your response MUST be structured into three clear parts:
195
+
196
+ ### 1. 🔍 Curator Analysis & Component Breakdown
197
+ - For EACH candidate, provide:
198
+ - ⭐ **Strongest aspects** (e.g. robust architecture, superior UI/CSS design, clean data validation).
199
+ - ⚠️ **Defects or Antipatterns** found from the checklist above.
200
+ - Highlight which candidate provides the best foundation for each component (e.g. Candidate 1 for core logic, Candidate 2 for visual UI).
201
+
202
+ ### 2. 🧩 Assembly Recipe & Recommended Master Assembler
203
+ - Recommend the best single agent model to assemble and finalize the solution:
204
+ RECOMMENDED_ASSEMBLER: <number from 1 to N> ${isBlind ? '' : '(<provider:model>)'}
205
+ - State the machine winner index marker for file promotion:
206
+ WINNER_CANDIDATE_INDEX: <number from 1 to N>
207
+ - Provide the exact blueprint / instructions for combining the best pieces into a unified deliverable.
208
+
209
+ ### 3. 🚀 Unified Solution & Execution Guide
210
+ - Present the final synthesized code or complete instructions combining the best candidate features.
211
+ - How to run, verify, and use the deliverable.`
212
+ }
213
+
214
+ /**
215
+ * Builds the judge synthesis prompt for standard or curator mode with optional blind evaluation.
216
+ */
217
+ export function buildSynthesisPrompt(userPrompt, referenceOutputs = [], judgeCriteria = '', options = {}) {
218
+ if (options.curatorSynthesis) {
219
+ return buildCuratorSynthesisPrompt(userPrompt, referenceOutputs, judgeCriteria, options)
220
+ }
221
+
222
+ const isBlind = Boolean(options.blindEvaluation)
223
+
224
+ const joined = referenceOutputs
225
+ .map((r, i) => {
226
+ const fileSummary = (r.files && r.files.length > 0)
227
+ ? ` [Files created: ${r.files.map((f) => f.relativePath).join(', ')}]`
228
+ : ''
229
+ let textContent = r.text
230
+ if (referenceOutputs.length >= 3 && textContent.length > 3000) {
231
+ textContent = stripOrSummarizeCode(textContent)
232
+ }
233
+ const header = isBlind
234
+ ? `Reference ${i + 1}:${fileSummary}`
235
+ : `Reference ${i + 1} — ${r.label}:${fileSummary}`
236
+ return `${header}\n${textContent}`
237
+ })
238
+ .join('\n\n')
239
+
240
+ const criteriaBlock = judgeCriteria && judgeCriteria.trim()
241
+ ? `\n### 🎯 Additional evaluation criteria from the user:\n${judgeCriteria.trim()}\n`
242
+ : ''
243
+
244
+ return `You are the expert aggregator/judge in a Mixture of Agents (MoA) process. You evaluate solutions from multiple candidate models, judge which one is best (or how to combine their best parts), and deliver the final authoritative verdict and solution.
245
+
246
+ Original user prompt:
247
+ ${userPrompt}
248
+ ${criteriaBlock}
249
+ Reference responses from candidate models:
250
+ ${joined}
251
+
252
+ ${ANTIPATTERNS_RUBRIC}
253
+
254
+ ${LANGUAGE_MIRRORING_DIRECTIVE}
255
+
256
+ Instructions:
257
+ Your response MUST be structured into three clear parts:
258
+
259
+ ### 1. ⚖️ Judge verdict and comparative analysis
260
+ - **Winner**: clearly name the chosen candidate (e.g. "Winner: Candidate 1" or "Reference 1 is chosen").
261
+ - Always add the machine winner-selection marker:
262
+ WINNER_CANDIDATE_INDEX: <number from 1 to N>
263
+ - **Why this choice**: compare code, architecture, strengths, weaknesses and reliability of all candidates in detail.
264
+
265
+ ### 2. 📁 Project files created
266
+ - List the winner files promoted to the project root and their purpose.
267
+
268
+ ### 3. 🚀 How to run and use
269
+ - Describe how to open and run the created project.`
270
+ }