blun-king-cli 5.4.0 → 6.0.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 (55) hide show
  1. package/bin/blun.js +100 -0
  2. package/lib/auth.js +162 -0
  3. package/lib/chat.js +82 -0
  4. package/lib/client.js +65 -0
  5. package/lib/ui.js +215 -0
  6. package/package.json +14 -18
  7. package/api.js +0 -965
  8. package/blun-cli.js +0 -3726
  9. package/blun-cli.js.claude +0 -3578
  10. package/blunking-api.js +0 -7
  11. package/bot.js +0 -188
  12. package/browser-controller.js +0 -116
  13. package/chat-memory.js +0 -103
  14. package/file-helper.js +0 -63
  15. package/fuzzy-match.js +0 -78
  16. package/identities.js +0 -106
  17. package/installer.js +0 -160
  18. package/job-manager.js +0 -146
  19. package/local-data.js +0 -71
  20. package/message-builder.js +0 -28
  21. package/noisy-evals.js +0 -38
  22. package/palace-memory.js +0 -246
  23. package/reference-inspector.js +0 -256
  24. package/runtime.js +0 -570
  25. package/task-executor.js +0 -104
  26. package/tests/browser-controller.test.js +0 -47
  27. package/tests/cli.test.js +0 -93
  28. package/tests/file-helper.test.js +0 -18
  29. package/tests/installer.test.js +0 -39
  30. package/tests/job-manager.test.js +0 -99
  31. package/tests/merge-compat.test.js +0 -77
  32. package/tests/messages.test.js +0 -23
  33. package/tests/noisy-evals.test.js +0 -12
  34. package/tests/noisy-intent-corpus.test.js +0 -45
  35. package/tests/reference-inspector.test.js +0 -42
  36. package/tests/runtime.test.js +0 -119
  37. package/tests/task-executor.test.js +0 -40
  38. package/tests/tools.test.js +0 -23
  39. package/tests/user-profile.test.js +0 -66
  40. package/tests/website-builder.test.js +0 -66
  41. package/tmp-build-smoke/nicrazy-landing/index.html +0 -53
  42. package/tmp-build-smoke/nicrazy-landing/style.css +0 -110
  43. package/tmp-shot-smoke/website-shot-1776006760424.png +0 -0
  44. package/tmp-shot-smoke/website-shot-1776007850007.png +0 -0
  45. package/tmp-shot-smoke/website-shot-1776007886209.png +0 -0
  46. package/tmp-shot-smoke/website-shot-1776007903766.png +0 -0
  47. package/tmp-shot-smoke/website-shot-1776008737117.png +0 -0
  48. package/tmp-shot-smoke/website-shot-1776008988859.png +0 -0
  49. package/tmp-smoke/nicrazy-landing/index.html +0 -66
  50. package/tmp-smoke/nicrazy-landing/style.css +0 -104
  51. package/tools.js +0 -177
  52. package/user-profile.js +0 -395
  53. package/website-builder.js +0 -394
  54. package/website-shot-1776010648230.png +0 -0
  55. package/website_builder.txt +0 -38
package/runtime.js DELETED
@@ -1,570 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const {
4
- getIdentity,
5
- findIdentityInText,
6
- recommendIdentity,
7
- listIdentities
8
- } = require("./identities");
9
- const { hasApproxWord, hasApproxPhrase } = require("./fuzzy-match");
10
-
11
- const REGISTRY = {
12
- active: {
13
- core: "v500",
14
- roles: {
15
- default: "v3",
16
- dev: "v3",
17
- operator: "v3",
18
- critic: "v3",
19
- support: "v3",
20
- web_ui: "v3",
21
- website_builder: "v3",
22
- artifact: "v3",
23
- research: "v3",
24
- installer: "v3"
25
- }
26
- }
27
- };
28
-
29
- function normalize(text) {
30
- return String(text || "")
31
- .normalize("NFD")
32
- .replace(/[\u0300-\u036f]/g, "")
33
- .toLowerCase()
34
- .replace(/ß/g, "ss")
35
- .replace(/\s+/g, " ")
36
- .trim();
37
- }
38
-
39
- function hasAny(text, phrases) {
40
- return phrases.some((phrase) => text.includes(phrase));
41
- }
42
-
43
- function scoreExact(text, phrases, weight = 1) {
44
- return phrases.reduce((score, phrase) => score + (text.includes(phrase) ? weight : 0), 0);
45
- }
46
-
47
- function scoreApproxWords(text, words, weight = 1) {
48
- return words.reduce((score, word) => score + (hasApproxWord(text, [word]) ? weight : 0), 0);
49
- }
50
-
51
- function scoreApproxPhrases(text, phrases, weight = 1) {
52
- return phrases.reduce((score, phrase) => score + (hasApproxPhrase(text, phrase) ? weight : 0), 0);
53
- }
54
-
55
- function clampConfidence(score) {
56
- return Math.max(0, Math.min(1, score / 8));
57
- }
58
-
59
- function hasResolvableUrl(text) {
60
- return /https?:\/\/\S+/i.test(String(text || "")) ||
61
- /\b(?:www\.)?[a-z0-9.-]+\.[a-z]{2,}(?:\/\S*)?\b/i.test(String(text || ""));
62
- }
63
-
64
- function detectIntent(raw, session) {
65
- const text = normalize(raw);
66
- const activeTask = session?.activeTask || null;
67
-
68
- const continuationWords = [
69
- "mach weiter",
70
- "weiter",
71
- "mach fertig",
72
- "arbeite autonom",
73
- "zieh durch",
74
- "weiter machen",
75
- "go on",
76
- "continue",
77
- "finish it",
78
- "mach einfach",
79
- "und jetzt",
80
- "weitermachen",
81
- "findest du auch selbst",
82
- "kannst du auch selbst",
83
- "such selbst",
84
- "such dir selbst"
85
- ];
86
- const analysisTerms = [
87
- "analysier",
88
- "analyse",
89
- "anschauen",
90
- "anschau",
91
- "review",
92
- "bewerte",
93
- "bewerten",
94
- "audit",
95
- "check",
96
- "kritisiere",
97
- "verbessern",
98
- "umbauen"
99
- ];
100
- const websiteTerms = [
101
- "landingpage",
102
- "landing page",
103
- "website",
104
- "webseite",
105
- "homepage",
106
- "onepager",
107
- "hero",
108
- "cta",
109
- "index.html",
110
- "style.css",
111
- "nicrazy.com",
112
- "url"
113
- ];
114
- const screenshotTerms = [
115
- "screenshot",
116
- "bild",
117
- "foto",
118
- "aufnahme",
119
- "screen",
120
- "abbild"
121
- ];
122
- const buildTerms = [
123
- "baue",
124
- "bau",
125
- "erstelle",
126
- "erstell",
127
- "mach",
128
- "machen",
129
- "ueberarbeite",
130
- "uberarbeite",
131
- "redesign"
132
- ];
133
- const systemTerms = [
134
- "ki",
135
- "ai",
136
- "system",
137
- "modell",
138
- "agent",
139
- "prompt",
140
- "routing",
141
- "antwort",
142
- "antworten",
143
- "identitat",
144
- "identitaten",
145
- "persona",
146
- "speicher",
147
- "daten"
148
- ];
149
- const qualityTerms = [
150
- "falsch",
151
- "schlecht",
152
- "unsauber",
153
- "kaputt",
154
- "verliert",
155
- "halluziniert",
156
- "inkonsistent",
157
- "problem"
158
- ];
159
- const fileTerms = [
160
- "als datei",
161
- "als txt",
162
- "als json",
163
- "als zip",
164
- "zum download",
165
- "download",
166
- "datei",
167
- "file",
168
- "artefakt",
169
- "artefakte"
170
- ];
171
- const debugTerms = [
172
- "error",
173
- "typeerror",
174
- "referenceerror",
175
- "syntaxerror",
176
- "fehler",
177
- "bug",
178
- "geht nicht",
179
- "funktioniert nicht",
180
- "timeout"
181
- ];
182
- const operatorTerms = [
183
- "prioritat",
184
- "priorisieren",
185
- "task",
186
- "operator",
187
- "blocker",
188
- "naechste schritte",
189
- "nachste schritte",
190
- "plan"
191
- ];
192
- const codingTerms = [
193
- "api",
194
- "route",
195
- "backend",
196
- "frontend",
197
- "node",
198
- "express",
199
- "javascript",
200
- "html",
201
- "css",
202
- "sql",
203
- "python",
204
- "code"
205
- ];
206
- const installTerms = [
207
- "installier",
208
- "installiere",
209
- "install",
210
- "installen",
211
- "plugin",
212
- "plugins",
213
- "skill",
214
- "skills",
215
- "telegram bot",
216
- "playwright",
217
- "npm paket",
218
- "paket installieren",
219
- "github installieren",
220
- "extension"
221
- ];
222
-
223
- const wantsFollowup = hasAny(text, continuationWords) || hasApproxWord(text, ["weiter", "weitermachen", "selbst", "continue", "findest"]);
224
- const wantsAnalysis = hasAny(text, analysisTerms) || hasApproxWord(text, ["analysier", "analyse", "review"]);
225
- const wantsWebsite = hasAny(text, websiteTerms) || hasApproxWord(text, ["website", "webseite", "landingpage", "homepage"]);
226
- const wantsBuild = hasAny(text, buildTerms) || hasApproxWord(text, ["baue", "erstelle", "redesign"]);
227
- const wantsSystem = hasAny(text, systemTerms);
228
- const wantsQuality = hasAny(text, qualityTerms);
229
- const wantsFiles = hasAny(text, fileTerms);
230
- const wantsScreenshot = hasAny(text, screenshotTerms) || hasApproxWord(text, ["screenshot", "screen", "bild"]);
231
- const wantsDebug = hasAny(text, debugTerms);
232
- const wantsOperator = hasAny(text, operatorTerms);
233
- const wantsCoding = hasAny(text, codingTerms);
234
-
235
- const screenshotStorageFollowup =
236
- activeTask?.type === "browser_capture" &&
237
- wantsScreenshot &&
238
- !hasResolvableUrl(raw) &&
239
- /(ordner|datei|file|gespeichert|download|liegt|finde|fehlt|nicht im ordner|nicht da|wo ist)/i.test(text);
240
-
241
- const scores = {
242
- installation:
243
- scoreExact(text, installTerms, 1) +
244
- scoreApproxWords(text, ["installier", "installieren", "telegram", "telegramm", "plugin", "skill", "playwright"], 1.5) +
245
- scoreApproxPhrases(text, ["telegram bot", "playwright"], 2),
246
- browser_capture:
247
- (wantsWebsite ? 2 : scoreApproxWords(text, ["website", "webseite"], 1.5)) +
248
- (wantsScreenshot ? 3 : scoreApproxWords(text, ["screenshot", "screen"], 1.5)),
249
- website_builder:
250
- (wantsWebsite ? 2 : scoreApproxWords(text, ["website", "webseite", "landingpage", "homepage"], 1.5)) +
251
- (wantsBuild ? 2 : scoreApproxWords(text, ["baue", "erstelle", "redesign"], 1.5)),
252
- website_analysis:
253
- (wantsWebsite ? 2 : scoreApproxWords(text, ["website", "webseite"], 1.5)) +
254
- (wantsAnalysis ? 2 : 0),
255
- system_analysis:
256
- (wantsSystem ? 1.5 : 0) +
257
- (wantsQuality ? 1 : 0) +
258
- (wantsAnalysis ? 2 : 0),
259
- debugging: wantsDebug ? 3 : 0,
260
- file_generation: wantsFiles ? 2 : 0,
261
- operator: wantsOperator ? 2 : 0,
262
- coding: wantsCoding ? 2 : 0,
263
- chat: 0
264
- };
265
-
266
- if (activeTask && wantsFollowup && !wantsAnalysis && !wantsDebug) {
267
- return { kind: "followup", activeTask, scores, confidence: 1 };
268
- }
269
-
270
- if (screenshotStorageFollowup) {
271
- return { kind: "followup", activeTask, scores, confidence: 1 };
272
- }
273
-
274
- if (/^(hi|hey|hallo|moin|servus|yo|na|guten morgen|guten abend)\b/.test(text) && text.split(" ").length <= 5) {
275
- return { kind: "chat", scores, confidence: 1 };
276
- }
277
-
278
- const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
279
- const [topKind, topScore] = ranked[0];
280
- const confidence = clampConfidence(topScore);
281
-
282
- if (topScore < 2) {
283
- return { kind: "chat", scores, confidence };
284
- }
285
-
286
- switch (topKind) {
287
- case "installation":
288
- return { kind: "install", scores, confidence };
289
- case "browser_capture":
290
- return { kind: "browser_capture", scores, confidence };
291
- case "website_builder":
292
- return { kind: "build", domain: "website", scores, confidence };
293
- case "website_analysis":
294
- return { kind: "analyze", domain: "website", scores, confidence };
295
- case "system_analysis":
296
- return { kind: "analyze", domain: "system", scores, confidence };
297
- case "debugging":
298
- return { kind: "debug", scores, confidence };
299
- case "file_generation":
300
- return { kind: "file", scores, confidence };
301
- case "operator":
302
- return { kind: "operator", scores, confidence };
303
- case "coding":
304
- return { kind: "coding", scores, confidence };
305
- default:
306
- return { kind: "chat", scores, confidence };
307
- }
308
- }
309
-
310
- function classifyTask(raw, session) {
311
- const intent = detectIntent(raw, session);
312
-
313
- if (intent.kind === "followup") {
314
- return {
315
- type: intent.activeTask.type,
316
- role: intent.activeTask.role,
317
- output: intent.activeTask.output,
318
- reason: "active_task_carryover",
319
- followup: true
320
- };
321
- }
322
-
323
- switch (intent.kind) {
324
- case "build":
325
- return { type: "website_builder", role: "website_builder", output: "file", reason: "build_intent" };
326
- case "browser_capture":
327
- return { type: "browser_capture", role: "web_ui", output: "file", reason: "browser_capture" };
328
- case "file":
329
- return { type: "file_generation", role: "artifact", output: "file", reason: "file_request" };
330
- case "analyze":
331
- return intent.domain === "website"
332
- ? { type: "analysis", role: "web_ui", output: "analysis", reason: "website_analysis" }
333
- : { type: "analysis", role: "research", output: "analysis", reason: "system_analysis" };
334
- case "debug":
335
- return { type: "debugging", role: "dev", output: "analysis", reason: "debug" };
336
- case "operator":
337
- return { type: "operator", role: "operator", output: "plan", reason: "operator" };
338
- case "coding":
339
- return { type: "coding", role: "dev", output: "code", reason: "coding" };
340
- case "install":
341
- return { type: "installation", role: "installer", output: "action", reason: "install" };
342
- default:
343
- return { type: "chat", role: "default", output: "text", reason: "default" };
344
- }
345
- }
346
-
347
- function classifyTaskDetailed(raw, session) {
348
- const task = classifyTask(raw, session);
349
- const intent = detectIntent(raw, session);
350
- return {
351
- task,
352
- scores: intent.scores || {},
353
- confidence: intent.confidence || 0
354
- };
355
- }
356
-
357
- function buildCorePrompt() {
358
- return [
359
- "Du bist BLUN KING, entwickelt von Mayk Biletti und dem Team von BLUN.",
360
- "Antworte klar, direkt, menschlich und ohne Bot-Floskeln.",
361
- "Sag zuerst die eigentliche Antwort und bleibe technisch belastbar.",
362
- "Einfache Fragen kurz beantworten. Komplexe Themen strukturiert, aber ohne Leerlauf.",
363
- "Erfinde nichts. Markiere Unsicherheit sauber.",
364
- "Wenn Dateien erzeugt werden sollen, liefere echte Dateien statt Platzhaltertext.",
365
- "Du arbeitest in einer lokalen Runtime mit Shell-Zugriff, lokalem Dateisystem, lokalem Speicher, lokalen Installationen und optionalen Referenz-Inspektionen uber Fetch, Screenshots und Browser-Automation.",
366
- "Behaupte niemals, du hattest keinen Zugriff auf lokale Tools, Dateien, Installationen oder Shell-Befehle.",
367
- "User schreiben oft mit Tippfehlern, Umgangssprache und kaputten Saetzen. Erkenne den beabsichtigten Intent trotzdem korrekt und robust.",
368
- "Wenn der Intent klar und lokal ausfuehrbar ist, handle zuerst statt lange zu diskutieren.",
369
- "Stelle Rueckfragen nur bei echten externen Blockern wie fehlenden Tokens, Logins, Zielsystemen oder Rechten."
370
- ].join("\n");
371
- }
372
-
373
- function rolePrompt(role) {
374
- const map = {
375
- default: "Rolle: Premium-Chat. Kurz, direkt, hilfreich.",
376
- dev: "Rolle: Senior Developer. Technisch, konkret, ohne Marketing-Sprache.",
377
- operator: "Rolle: Operator. Priorisieren, Abhangigkeiten erkennen, klare nachste Schritte.",
378
- critic: "Rolle: Critic. Nur echte Schwachen und klare Verbesserungen.",
379
- support: "Rolle: Support. Ruhig, klar, losungsorientiert.",
380
- web_ui: "Rolle: Web/UI Analytiker. Ziel der Seite verstehen, Hierarchie, Conversion, Schwachen.",
381
- website_builder: "Rolle: Website Builder. Sofort bauen. Referenzen nur als Stilquelle. Immer echte HTML/CSS-Dateien liefern.",
382
- artifact: "Rolle: Artifact Builder. Erzeuge Dateien oder andere nutzbare Artefakte statt Chat-Text.",
383
- research: "Rolle: Research. Finde das Relevante, ordne Beobachtungen und leite klare Schlusse ab.",
384
- installer: "Rolle: Installer. Fuehre lokale Installationen, Setup-Schritte und Verifikationen direkt aus statt darueber zu reden."
385
- };
386
- return map[role] || map.default;
387
- }
388
-
389
- function outputContract(output) {
390
- const map = {
391
- text: "Output: normaler Text. Direkt, kopierbar, kein Meta-Gelaber.",
392
- code: "Output: nur der notige Code oder klare Dateianderungen. Keine unnotige Erklarung.",
393
- file: "Output: echte Dateien oder Artefakte erzeugen und verifizieren.",
394
- analysis: "Output: klare Analyse mit Starken, Schwachen, Risiken und bester Verbesserung.",
395
- plan: "Output: Prioritaten, Abhangigkeiten, nachster sinnvoller Schritt.",
396
- action: "Output: fuehre die lokale Aktion direkt aus und bestaetige knapp das Ergebnis."
397
- };
398
- return map[output] || map.text;
399
- }
400
-
401
- function resolveIdentity(raw, task, session, preferredIdentity) {
402
- if (preferredIdentity) {
403
- return getIdentity(preferredIdentity);
404
- }
405
-
406
- const explicit = findIdentityInText(raw);
407
- if (explicit) return explicit;
408
-
409
- if (session?.activeTask?.identityId) {
410
- return getIdentity(session.activeTask.identityId);
411
- }
412
-
413
- return recommendIdentity(task);
414
- }
415
-
416
- function composeSystemPrompt(task, session, rawMessage = "", preferredIdentity) {
417
- const identity = resolveIdentity(rawMessage, task, session, preferredIdentity);
418
- const parts = [
419
- buildCorePrompt(),
420
- rolePrompt(task.role),
421
- outputContract(task.output),
422
- `Identitaet: ${identity.name}. ${identity.prompt}`
423
- ];
424
- const websiteBuilderRules = path.join(__dirname, "website_builder.txt");
425
-
426
- if (task.type === "website_builder" && fs.existsSync(websiteBuilderRules)) {
427
- parts.push(fs.readFileSync(websiteBuilderRules, "utf8"));
428
- }
429
-
430
- if (session?.activeTask && task.followup) {
431
- parts.push(`Aktiver Task-Kontext: ${JSON.stringify(session.activeTask)}`);
432
- }
433
-
434
- return parts.join("\n\n");
435
- }
436
-
437
- function estimateTokens(input = "") {
438
- return Math.max(1, Math.ceil(String(input).length / 4));
439
- }
440
-
441
- function scoreResponse(answer, task, userMessage = "") {
442
- const reasons = [];
443
- let score = 100;
444
- const text = String(answer || "");
445
- const user = normalize(userMessage);
446
-
447
- if (task.type === "chat" && /^(was ist|was bedeutet|ja oder nein|brauch ich|kennst du)\b/.test(user) && text.length > 500) {
448
- score -= 30;
449
- reasons.push("too_long_for_simple_question");
450
- }
451
-
452
- if (/(certainly|absolutely|gerne helfe ich dir|basierend auf deiner anfrage|als ki)/i.test(text)) {
453
- score -= 25;
454
- reasons.push("bot_phrasing");
455
- }
456
-
457
- if (task.type === "debugging") {
458
- const match = String(userMessage).match(/\b(TypeError|ReferenceError|SyntaxError|Error)\b/i);
459
- if (match && !text.includes(match[1])) {
460
- score -= 10;
461
- reasons.push("error_not_repeated");
462
- }
463
- }
464
-
465
- if (task.type === "website_builder") {
466
- const hasFiles = /(index\.html|style\.css|write_file|create_project|<html|<!doctype)/i.test(text);
467
- if (!hasFiles) {
468
- score -= 40;
469
- reasons.push("no_real_build_output");
470
- }
471
- }
472
-
473
- if (task.type === "installation") {
474
- if (/(<!doctype|<html|<body|landingpage|tailwindcss|hero section)/i.test(text) || /(python-telegram-bot|bot\.py|dotenv|applicationbuilder)/i.test(text)) {
475
- score -= 60;
476
- reasons.push("action_mismatch");
477
- }
478
- if (/(hast du bereits ein repository|schick mir den code|gib mir den link|welche sprache nutzt du)/i.test(text)) {
479
- score -= 35;
480
- reasons.push("refusal_or_detour");
481
- }
482
- }
483
-
484
- if (task.type === "browser_capture") {
485
- if (!/(screenshot|bild|png|erstellt|gespeichert)/i.test(text)) {
486
- score -= 45;
487
- reasons.push("action_mismatch");
488
- }
489
- }
490
-
491
- return { score: Math.max(0, score), reasons };
492
- }
493
-
494
- function selfCheckResponse(answer, task, userMessage = "") {
495
- const quality = scoreResponse(answer, task, userMessage);
496
- const criticalReasons = ["action_mismatch", "refusal_or_detour", "no_real_build_output"];
497
- const critical = quality.reasons.some((reason) => criticalReasons.includes(reason));
498
- const blocked = task.type !== "chat" && (critical || quality.score < 70);
499
-
500
- return {
501
- ...quality,
502
- blocked,
503
- critical,
504
- summary: blocked
505
- ? "Antwort vor Ausgabe blockiert, weil sie nicht sauber zum Intent oder Output-Vertrag passt."
506
- : "Antwort ist konsistent zum Task."
507
- };
508
- }
509
-
510
- function buildBlockedActionReply(task, reasons = []) {
511
- if (task.type === "installation") {
512
- return "Die Modellantwort wurde blockiert, weil sie nicht zur Installationsaufgabe passte. Fuehre stattdessen die Installation direkt oder ueber den Install-Task aus.";
513
- }
514
- if (task.type === "browser_capture") {
515
- return "Die Modellantwort wurde blockiert, weil statt eines Screenshots nur Text erzeugt wurde. Nutze den Browser-Capture-Pfad erneut.";
516
- }
517
- if (task.type === "website_builder") {
518
- return "Die Modellantwort wurde blockiert, weil kein sauberer Website-Build erzeugt wurde. Erzeuge echte Dateien mit gueltigem HTML/CSS und Referenztreue.";
519
- }
520
- return `Die Modellantwort wurde blockiert. Gruende: ${reasons.join(", ") || "output_contract"}.`;
521
- }
522
-
523
- function composePrompt(task, extraContext = "", toolsPrompt = "") {
524
- const prompt = composeSystemPrompt(task, {});
525
- return [prompt, String(extraContext || "").trim(), String(toolsPrompt || "").trim()]
526
- .filter(Boolean)
527
- .join("\n\n");
528
- }
529
-
530
- function buildRetryPrompt(reasons = []) {
531
- const hints = reasons.map((reason) => {
532
- switch (reason) {
533
- case "too_long_for_simple_question":
534
- return "Antworte kuerzer. Maximal 2 bis 3 Saetze fuer einfache Fragen.";
535
- case "bot_phrasing":
536
- return "Keine Bot-Floskeln. Direkt und menschlich formulieren.";
537
- case "error_not_repeated":
538
- return "Wiederhole den konkreten Fehlernamen einmal exakt.";
539
- case "no_real_build_output":
540
- return "Erzeuge echte Dateien oder ein Projekt statt nur darueber zu reden.";
541
- case "irrelevant_fallback_or_refusal":
542
- return "Kein Ausweichen und keine Verweigerung. Liefere die geforderte Ausgabe direkt.";
543
- case "action_mismatch":
544
- return "Deine Antwort passt nicht zur angeforderten Aktion. Fokussiere dich strikt auf den Intent und fuehre ihn direkt aus.";
545
- case "refusal_or_detour":
546
- return "Keine unnoetigen Rueckfragen oder Umwege. Frage nur bei echtem externen Blocker nach.";
547
- default:
548
- return `Verbessere die Antwort bezueglich: ${reason}`;
549
- }
550
- });
551
-
552
- return `[SYSTEM] Deine letzte Antwort war nicht gut genug. Korrigiere sie jetzt. ${hints.join(" ")}`.trim();
553
- }
554
-
555
- module.exports = {
556
- REGISTRY,
557
- normalize,
558
- detectIntent,
559
- classifyTask,
560
- classifyTaskDetailed,
561
- resolveIdentity,
562
- composeSystemPrompt,
563
- composePrompt,
564
- buildRetryPrompt,
565
- scoreResponse,
566
- selfCheckResponse,
567
- buildBlockedActionReply,
568
- estimateTokens,
569
- listIdentities
570
- };
package/task-executor.js DELETED
@@ -1,104 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const browser = require("./browser-controller");
4
- const fileHelper = require("./file-helper");
5
- const { installSource, resolveInstallRequest } = require("./installer");
6
- const { extractUrls } = require("./reference-inspector");
7
- const userProfile = require("./user-profile");
8
-
9
- function isDirectActionTask(task) {
10
- return ["installation", "browser_capture"].includes(task?.type);
11
- }
12
-
13
- function resolveInstallRequestWithSession(message, session, userId) {
14
- const learned = userProfile.resolveInstallAlias(userId, message);
15
- if (learned) {
16
- return {
17
- source: learned,
18
- kind: "npm",
19
- label: learned
20
- };
21
- }
22
-
23
- return resolveInstallRequest(message) || resolveInstallRequest(session?.activeTask?.lastUserIntent || "");
24
- }
25
-
26
- function resolveCaptureUrl(message, session) {
27
- return extractUrls(message)[0] || extractUrls(session?.activeTask?.lastUserIntent || "")[0] || null;
28
- }
29
-
30
- async function executeInstallationTask(message, session, userId) {
31
- const request = resolveInstallRequestWithSession(message, session, userId);
32
- if (!request) {
33
- throw new Error("No install source could be resolved from the message.");
34
- }
35
-
36
- const installed = await installSource(request.source);
37
- userProfile.learnInstallAlias(userId, message, request.source);
38
- const answer = `${installed.alreadyInstalled ? "Bereits installiert" : "Installiert"}: ${request.label || request.source}`;
39
-
40
- return {
41
- status: "completed",
42
- answer,
43
- install: installed,
44
- steps: [
45
- { step: 1, action: "intent_detected", detail: "installation" },
46
- { step: 2, action: "install_target_resolved", detail: request.source },
47
- { step: 3, action: installed.alreadyInstalled ? "already_installed" : "installed", detail: installed.target }
48
- ]
49
- };
50
- }
51
-
52
- async function executeBrowserCaptureTask(message, session, workdir) {
53
- const url = resolveCaptureUrl(message, session);
54
- if (!url) {
55
- throw new Error("No website URL found for screenshot capture.");
56
- }
57
-
58
- fs.mkdirSync(workdir, { recursive: true });
59
- await browser.open(url);
60
- try {
61
- const shotName = `website-shot-${Date.now()}.png`;
62
- const shotPath = path.join(workdir, shotName);
63
- await browser.screenshot(shotPath);
64
- const snapshot = await browser.snapshot();
65
- const artifact = fileHelper.writeArtifact(shotName, fs.readFileSync(shotPath));
66
-
67
- return {
68
- status: "completed",
69
- answer: `Screenshot der Webseite erstellt: ${shotName}`,
70
- steps: [
71
- { step: 1, action: "intent_detected", detail: "browser_capture" },
72
- { step: 2, action: "browser_opened", detail: url },
73
- { step: 3, action: "screenshot_written", files: [shotName] }
74
- ],
75
- files: [{ path: shotName, content: "" }],
76
- references: [{ url, title: snapshot.title, screenshotPath: shotPath }],
77
- artifact: {
78
- artifact_id: artifact.artifact_id,
79
- filename: artifact.filename,
80
- download: `/download/${artifact.artifact_id}`
81
- },
82
- snapshot
83
- };
84
- } finally {
85
- await browser.close().catch(() => {});
86
- }
87
- }
88
-
89
- async function executeDirectTask({ task, message, session, workdir }) {
90
- if (task?.type === "installation") {
91
- return executeInstallationTask(message, session, session?.id);
92
- }
93
- if (task?.type === "browser_capture") {
94
- return executeBrowserCaptureTask(message, session, workdir);
95
- }
96
- throw new Error(`Task ${task?.type || "unknown"} is not a direct action task.`);
97
- }
98
-
99
- module.exports = {
100
- isDirectActionTask,
101
- resolveInstallRequestWithSession,
102
- resolveCaptureUrl,
103
- executeDirectTask
104
- };