@winspan/claude-forge 8.35.0 → 8.37.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 (115) hide show
  1. package/README.md +5 -0
  2. package/dist/agents/registry.d.ts +2 -1
  3. package/dist/agents/registry.d.ts.map +1 -1
  4. package/dist/agents/registry.js +17 -4
  5. package/dist/agents/registry.js.map +1 -1
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +5 -1
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/mcp.d.ts +14 -0
  10. package/dist/cli/commands/mcp.d.ts.map +1 -0
  11. package/dist/cli/commands/mcp.js +164 -0
  12. package/dist/cli/commands/mcp.js.map +1 -0
  13. package/dist/cli/commands/menu.js +33 -0
  14. package/dist/cli/commands/menu.js.map +1 -1
  15. package/dist/cli/index.js +2 -0
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/core/ai/provider.d.ts +23 -1
  18. package/dist/core/ai/provider.d.ts.map +1 -1
  19. package/dist/core/ai/provider.js +67 -1
  20. package/dist/core/ai/provider.js.map +1 -1
  21. package/dist/core/ai/types.d.ts +28 -0
  22. package/dist/core/ai/types.d.ts.map +1 -1
  23. package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
  24. package/dist/daemon/handlers/user-prompt.js +25 -3
  25. package/dist/daemon/handlers/user-prompt.js.map +1 -1
  26. package/dist/daemon/index.d.ts.map +1 -1
  27. package/dist/daemon/index.js +7 -2
  28. package/dist/daemon/index.js.map +1 -1
  29. package/dist/daemon/routing-state.d.ts +14 -0
  30. package/dist/daemon/routing-state.d.ts.map +1 -1
  31. package/dist/daemon/routing-state.js +34 -0
  32. package/dist/daemon/routing-state.js.map +1 -1
  33. package/dist/engine/agent-router.d.ts +37 -0
  34. package/dist/engine/agent-router.d.ts.map +1 -1
  35. package/dist/engine/agent-router.js +58 -0
  36. package/dist/engine/agent-router.js.map +1 -1
  37. package/dist/engine/conventions/routing.yaml +31 -2
  38. package/dist/intelligence/classifier.d.ts +63 -43
  39. package/dist/intelligence/classifier.d.ts.map +1 -1
  40. package/dist/intelligence/classifier.js +256 -191
  41. package/dist/intelligence/classifier.js.map +1 -1
  42. package/dist/intelligence/context-gatherer.d.ts +101 -0
  43. package/dist/intelligence/context-gatherer.d.ts.map +1 -0
  44. package/dist/intelligence/context-gatherer.js +417 -0
  45. package/dist/intelligence/context-gatherer.js.map +1 -0
  46. package/dist/intelligence/cot-classifier.d.ts +95 -0
  47. package/dist/intelligence/cot-classifier.d.ts.map +1 -0
  48. package/dist/intelligence/cot-classifier.js +391 -0
  49. package/dist/intelligence/cot-classifier.js.map +1 -0
  50. package/dist/intelligence/execution-doc-builder.d.ts +90 -0
  51. package/dist/intelligence/execution-doc-builder.d.ts.map +1 -1
  52. package/dist/intelligence/execution-doc-builder.js +459 -42
  53. package/dist/intelligence/execution-doc-builder.js.map +1 -1
  54. package/dist/intelligence/intent-types.d.ts +13 -0
  55. package/dist/intelligence/intent-types.d.ts.map +1 -0
  56. package/dist/intelligence/intent-types.js +19 -0
  57. package/dist/intelligence/intent-types.js.map +1 -0
  58. package/dist/intelligence/multimodal-parser.d.ts +105 -0
  59. package/dist/intelligence/multimodal-parser.d.ts.map +1 -0
  60. package/dist/intelligence/multimodal-parser.js +425 -0
  61. package/dist/intelligence/multimodal-parser.js.map +1 -0
  62. package/dist/mcp/server.d.ts +58 -0
  63. package/dist/mcp/server.d.ts.map +1 -0
  64. package/dist/mcp/server.js +201 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/skills/registry.d.ts.map +1 -1
  67. package/dist/skills/registry.js +2 -1
  68. package/dist/skills/registry.js.map +1 -1
  69. package/dist/skills/tools/skill-invoke.d.ts +5 -4
  70. package/dist/skills/tools/skill-invoke.d.ts.map +1 -1
  71. package/dist/skills/tools/skill-invoke.js +6 -5
  72. package/dist/skills/tools/skill-invoke.js.map +1 -1
  73. package/dist/web/server.d.ts +2 -0
  74. package/dist/web/server.d.ts.map +1 -1
  75. package/dist/web/server.js +13 -0
  76. package/dist/web/server.js.map +1 -1
  77. package/dist/web/static/assets/{AIConfig-DiUFET_Q.js → AIConfig-D4VglzCl.js} +2 -2
  78. package/dist/web/static/assets/{AIConfig-DiUFET_Q.js.map → AIConfig-D4VglzCl.js.map} +1 -1
  79. package/dist/web/static/assets/{Agents-bNNGbQnL.js → Agents-ne5lXc7V.js} +2 -2
  80. package/dist/web/static/assets/{Agents-bNNGbQnL.js.map → Agents-ne5lXc7V.js.map} +1 -1
  81. package/dist/web/static/assets/Dashboard-D4j0Zmek.js +2 -0
  82. package/dist/web/static/assets/Dashboard-D4j0Zmek.js.map +1 -0
  83. package/dist/web/static/assets/{Drawer-DOUcx6m1.js → Drawer-Lo5ihVP-.js} +2 -2
  84. package/dist/web/static/assets/{Drawer-DOUcx6m1.js.map → Drawer-Lo5ihVP-.js.map} +1 -1
  85. package/dist/web/static/assets/{Events-DQHP6Uaq.js → Events-DBJ1B7OW.js} +2 -2
  86. package/dist/web/static/assets/{Events-DQHP6Uaq.js.map → Events-DBJ1B7OW.js.map} +1 -1
  87. package/dist/web/static/assets/{ExecutionTrace-Co8ARdg-.js → ExecutionTrace-Du9XADc1.js} +2 -2
  88. package/dist/web/static/assets/{ExecutionTrace-Co8ARdg-.js.map → ExecutionTrace-Du9XADc1.js.map} +1 -1
  89. package/dist/web/static/assets/{Routing-BW3eGD-8.js → Routing-BNQ09OlH.js} +2 -2
  90. package/dist/web/static/assets/{Routing-BW3eGD-8.js.map → Routing-BNQ09OlH.js.map} +1 -1
  91. package/dist/web/static/assets/{SessionDetail-Cbd7Jwox.js → SessionDetail-BPrPyMNa.js} +2 -2
  92. package/dist/web/static/assets/{SessionDetail-Cbd7Jwox.js.map → SessionDetail-BPrPyMNa.js.map} +1 -1
  93. package/dist/web/static/assets/{Sessions-ZQSCgXyy.js → Sessions-o3EXsXz9.js} +2 -2
  94. package/dist/web/static/assets/{Sessions-ZQSCgXyy.js.map → Sessions-o3EXsXz9.js.map} +1 -1
  95. package/dist/web/static/assets/{Skills-C5-5zOSH.js → Skills-Czt5mkyc.js} +2 -2
  96. package/dist/web/static/assets/{Skills-C5-5zOSH.js.map → Skills-Czt5mkyc.js.map} +1 -1
  97. package/dist/web/static/assets/{export-CbQTOt71.js → export-C0mlC4AT.js} +2 -2
  98. package/dist/web/static/assets/{export-CbQTOt71.js.map → export-C0mlC4AT.js.map} +1 -1
  99. package/dist/web/static/assets/index-B1J7nBu0.js +3 -0
  100. package/dist/web/static/assets/index-B1J7nBu0.js.map +1 -0
  101. package/dist/web/static/assets/index-BVqk4bSO.css +1 -0
  102. package/dist/web/static/assets/{lucide-BanPULT1.js → lucide-Bu44HVAM.js} +33 -73
  103. package/dist/web/static/assets/lucide-Bu44HVAM.js.map +1 -0
  104. package/dist/web/static/index.html +3 -3
  105. package/package.json +2 -1
  106. package/dist/web/static/assets/Dashboard-Ciyyw6ph.js +0 -2
  107. package/dist/web/static/assets/Dashboard-Ciyyw6ph.js.map +0 -1
  108. package/dist/web/static/assets/Methodologies-CXNrDXwG.js +0 -5
  109. package/dist/web/static/assets/Methodologies-CXNrDXwG.js.map +0 -1
  110. package/dist/web/static/assets/MethodologyDetail-rV3W1utf.js +0 -2
  111. package/dist/web/static/assets/MethodologyDetail-rV3W1utf.js.map +0 -1
  112. package/dist/web/static/assets/index-DJK5beK6.js +0 -3
  113. package/dist/web/static/assets/index-DJK5beK6.js.map +0 -1
  114. package/dist/web/static/assets/index-phpuytMI.css +0 -1
  115. package/dist/web/static/assets/lucide-BanPULT1.js.map +0 -1
@@ -0,0 +1,425 @@
1
+ /**
2
+ * MultimodalParser — turns a raw user prompt into a structured multimodal input.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Detect `[Image #N]` markers and markdown image references in the prompt.
6
+ * 2. Resolve those references to absolute file paths (defaulting to the
7
+ * Claude Code image cache at `~/.claude/image-cache/<session>/<n>.<ext>`).
8
+ * 3. Sniff the file header to determine a supported media type
9
+ * (PNG / JPEG / GIF / WebP).
10
+ * 4. Call `AIProvider.completeWithImage` in parallel for every resolved image
11
+ * and parse the JSON response into a typed `ImageAnalysis`.
12
+ * 5. Extract fenced code blocks (```lang...```), preserving language and
13
+ * their 1-based line position within the original prompt.
14
+ * 6. Produce a plain-text view of the prompt with markers and code fences
15
+ * stripped, suitable for downstream classification.
16
+ *
17
+ * Failure model: this module never throws on missing files, unsupported media
18
+ * types, network errors, or malformed Vision JSON. Each failure is recorded in
19
+ * `meta.errors` and the analysis falls back to a neutral default
20
+ * (`screenshotType: 'unknown'`, empty fields) so the classifier can still run
21
+ * on text alone.
22
+ */
23
+ import { promises as fs } from 'node:fs';
24
+ import * as os from 'node:os';
25
+ import * as path from 'node:path';
26
+ import { logger } from '../core/utils/logger.js';
27
+ // ---------------------------------------------------------------------------
28
+ // Constants & regexes
29
+ // ---------------------------------------------------------------------------
30
+ const DEFAULT_VISION_TIMEOUT_MS = 10_000;
31
+ const DEFAULT_VISION_MAX_TOKENS = 1024;
32
+ /** Order matters in the cache lookup: most likely first to short-circuit. */
33
+ const CACHE_IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp'];
34
+ /** Magic-number prefix table used by `detectMediaType`. */
35
+ const MAGIC_PREFIXES = [
36
+ { media: 'image/png', bytes: [0x89, 0x50, 0x4e, 0x47] },
37
+ { media: 'image/jpeg', bytes: [0xff, 0xd8, 0xff] },
38
+ { media: 'image/gif', bytes: [0x47, 0x49, 0x46, 0x38] },
39
+ ];
40
+ const SCREENSHOT_TYPES = new Set([
41
+ 'ui', 'terminal', 'code', 'error', 'diagram', 'unknown',
42
+ ]);
43
+ const IMAGE_MARKER_RE = /\[Image\s+#(\d+)\]/gi;
44
+ const MARKDOWN_IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
45
+ /**
46
+ * Fenced code block: opening ``` plus optional language + newline, body
47
+ * (non-greedy), then closing ```. Tolerates CRLF and missing trailing newline.
48
+ */
49
+ const FENCED_BLOCK_RE = /```([a-zA-Z0-9_+\-#.]*)[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
50
+ // ---------------------------------------------------------------------------
51
+ // Vision prompt — kept module-level so it's easy to inspect / snapshot in tests.
52
+ // ---------------------------------------------------------------------------
53
+ export const VISION_ANALYSIS_PROMPT = `Analyze this screenshot and respond with ONLY a JSON object:
54
+
55
+ {
56
+ "screenshotType": "ui|terminal|code|error|diagram|unknown",
57
+ "ocrText": "all visible text",
58
+ "visibleElements": ["element1", "element2"],
59
+ "errorMessages": ["error1"],
60
+ "semanticDescription": "one sentence describing what this screenshot shows"
61
+ }
62
+
63
+ Guidelines:
64
+ - screenshotType: "ui" = web/desktop UI; "terminal" = CLI output; "code" = code editor; "error" = error dialog/page; "diagram" = chart/diagram
65
+ - ocrText: extract ALL visible text, line by line
66
+ - visibleElements: identify UI components (buttons, menus, tabs, etc.), for UI screenshots
67
+ - errorMessages: extract error messages, stack traces, warnings if visible
68
+ - semanticDescription: explain WHAT this screenshot tells us (e.g., "shows methodology execution page that should have been removed")
69
+
70
+ Respond with the JSON only — no prose, no markdown fences.`;
71
+ /** Per-call clone of the magic-prefix table so we can also handle WebP separately. */
72
+ function matchesPrefix(buf, prefix) {
73
+ if (buf.length < prefix.length)
74
+ return false;
75
+ for (let i = 0; i < prefix.length; i++) {
76
+ if (buf[i] !== prefix[i])
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ /** Read the first 12 bytes of a file and map them to a supported media type. */
82
+ export async function detectMediaType(filePath) {
83
+ let handle;
84
+ try {
85
+ handle = await fs.open(filePath, 'r');
86
+ const buffer = Buffer.alloc(12);
87
+ const { bytesRead } = await handle.read(buffer, 0, 12, 0);
88
+ if (bytesRead < 3)
89
+ return undefined;
90
+ const head = buffer.subarray(0, bytesRead);
91
+ for (const { media, bytes } of MAGIC_PREFIXES) {
92
+ if (matchesPrefix(head, bytes))
93
+ return media;
94
+ }
95
+ // WebP: RIFF....WEBP — needs the 12-byte window.
96
+ if (bytesRead >= 12
97
+ && matchesPrefix(head, [0x52, 0x49, 0x46, 0x46])
98
+ && head[8] === 0x57 && head[9] === 0x45 && head[10] === 0x42 && head[11] === 0x50) {
99
+ return 'image/webp';
100
+ }
101
+ return undefined;
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ finally {
107
+ await handle?.close().catch(() => {
108
+ /* best-effort close */
109
+ });
110
+ }
111
+ }
112
+ /** Look in `<cacheRoot>/<session>/<n>.<ext>` for the most recent match. */
113
+ export async function defaultResolveCachedImage(marker, cacheRoot) {
114
+ const numMatch = /#(\d+)/.exec(marker);
115
+ if (!numMatch)
116
+ return undefined;
117
+ const n = numMatch[1];
118
+ const root = cacheRoot ?? path.join(os.homedir(), '.claude', 'image-cache');
119
+ let sessionDirs;
120
+ try {
121
+ sessionDirs = await fs.readdir(root);
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ const candidates = [];
127
+ for (const entry of sessionDirs) {
128
+ const sessionPath = path.join(root, entry);
129
+ try {
130
+ const sessionStat = await fs.stat(sessionPath);
131
+ if (!sessionStat.isDirectory())
132
+ continue;
133
+ }
134
+ catch {
135
+ continue;
136
+ }
137
+ for (const ext of CACHE_IMAGE_EXTENSIONS) {
138
+ const candidate = path.join(sessionPath, `${n}.${ext}`);
139
+ try {
140
+ const fileStat = await fs.stat(candidate);
141
+ if (fileStat.isFile()) {
142
+ candidates.push({ filePath: candidate, mtimeMs: fileStat.mtimeMs });
143
+ }
144
+ }
145
+ catch {
146
+ // file absent — keep searching
147
+ }
148
+ }
149
+ }
150
+ if (candidates.length === 0)
151
+ return undefined;
152
+ candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
153
+ return candidates[0].filePath;
154
+ }
155
+ /** Compute 1-based line number for a byte offset within `text`. */
156
+ function lineNumberAt(text, offset) {
157
+ if (offset <= 0)
158
+ return 1;
159
+ let line = 1;
160
+ const end = Math.min(offset, text.length);
161
+ for (let i = 0; i < end; i++) {
162
+ if (text.charCodeAt(i) === 0x0a /* \n */)
163
+ line++;
164
+ }
165
+ return line;
166
+ }
167
+ /** Extract fenced code blocks. Indented-style blocks are intentionally ignored. */
168
+ export function extractCodeBlocks(prompt) {
169
+ const blocks = [];
170
+ const re = new RegExp(FENCED_BLOCK_RE.source, FENCED_BLOCK_RE.flags);
171
+ let match;
172
+ while ((match = re.exec(prompt)) !== null) {
173
+ const rawLang = match[1] ?? '';
174
+ const content = (match[2] ?? '').replace(/\r?\n$/, '');
175
+ blocks.push({
176
+ language: rawLang.length > 0 ? rawLang.toLowerCase() : undefined,
177
+ content,
178
+ startLine: lineNumberAt(prompt, match.index),
179
+ });
180
+ }
181
+ return blocks;
182
+ }
183
+ /** Extract `[Image #N]` markers preserving prompt order and duplicates. */
184
+ function extractImageMarkers(prompt) {
185
+ const found = [];
186
+ const re = new RegExp(IMAGE_MARKER_RE.source, IMAGE_MARKER_RE.flags);
187
+ let match;
188
+ while ((match = re.exec(prompt)) !== null) {
189
+ found.push(match[0]);
190
+ }
191
+ return found;
192
+ }
193
+ /** Extract markdown image references (`![alt](path)`), preserving order. */
194
+ function extractMarkdownImages(prompt) {
195
+ const found = [];
196
+ const re = new RegExp(MARKDOWN_IMAGE_RE.source, MARKDOWN_IMAGE_RE.flags);
197
+ let match;
198
+ while ((match = re.exec(prompt)) !== null) {
199
+ found.push({ alt: match[1] ?? '', path: match[2] });
200
+ }
201
+ return found;
202
+ }
203
+ /** Build the prompt with image references and fenced code blocks removed. */
204
+ function stripMarkersAndBlocks(prompt) {
205
+ return prompt
206
+ .replace(new RegExp(FENCED_BLOCK_RE.source, FENCED_BLOCK_RE.flags), '')
207
+ .replace(new RegExp(MARKDOWN_IMAGE_RE.source, MARKDOWN_IMAGE_RE.flags), '')
208
+ .replace(new RegExp(IMAGE_MARKER_RE.source, IMAGE_MARKER_RE.flags), '')
209
+ .replace(/[ \t]+\n/g, '\n')
210
+ .replace(/\n{3,}/g, '\n\n')
211
+ .trim();
212
+ }
213
+ function isScreenshotType(val) {
214
+ return typeof val === 'string' && SCREENSHOT_TYPES.has(val);
215
+ }
216
+ function asStringArray(val) {
217
+ if (!Array.isArray(val))
218
+ return [];
219
+ return val.filter((x) => typeof x === 'string');
220
+ }
221
+ /**
222
+ * Parse the JSON object returned by the Vision call. Tolerates surrounding
223
+ * prose / markdown fences. Returns `null` if no JSON object can be recovered.
224
+ */
225
+ export function parseVisionResponse(text) {
226
+ if (!text)
227
+ return null;
228
+ let candidate = text.trim();
229
+ // Strip ```json ... ``` fences if present.
230
+ const fenceMatch = /```(?:json)?\s*\r?\n?([\s\S]*?)\r?\n?```/i.exec(candidate);
231
+ if (fenceMatch)
232
+ candidate = fenceMatch[1].trim();
233
+ // If still not pure JSON, try to recover the first {...} block.
234
+ if (!candidate.startsWith('{')) {
235
+ const firstBrace = candidate.indexOf('{');
236
+ const lastBrace = candidate.lastIndexOf('}');
237
+ if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace)
238
+ return null;
239
+ candidate = candidate.slice(firstBrace, lastBrace + 1);
240
+ }
241
+ let parsed;
242
+ try {
243
+ parsed = JSON.parse(candidate);
244
+ }
245
+ catch {
246
+ return null;
247
+ }
248
+ if (typeof parsed !== 'object' || parsed === null)
249
+ return null;
250
+ const obj = parsed;
251
+ return {
252
+ screenshotType: isScreenshotType(obj.screenshotType) ? obj.screenshotType : 'unknown',
253
+ ocrText: typeof obj.ocrText === 'string' ? obj.ocrText : '',
254
+ visibleElements: asStringArray(obj.visibleElements),
255
+ errorMessages: asStringArray(obj.errorMessages),
256
+ semanticDescription: typeof obj.semanticDescription === 'string' ? obj.semanticDescription : '',
257
+ };
258
+ }
259
+ function defaultAnalysis(imagePath, analysisMs) {
260
+ return {
261
+ imagePath,
262
+ screenshotType: 'unknown',
263
+ ocrText: '',
264
+ visibleElements: [],
265
+ errorMessages: [],
266
+ semanticDescription: '',
267
+ analysisMs,
268
+ };
269
+ }
270
+ // ---------------------------------------------------------------------------
271
+ // MultimodalParser
272
+ // ---------------------------------------------------------------------------
273
+ export class MultimodalParser {
274
+ ai;
275
+ visionTimeoutMs;
276
+ visionModel;
277
+ customResolver;
278
+ imageCacheDir;
279
+ constructor(ai, options) {
280
+ this.ai = ai;
281
+ this.visionTimeoutMs = options?.visionTimeoutMs ?? DEFAULT_VISION_TIMEOUT_MS;
282
+ this.visionModel = options?.visionModel;
283
+ this.customResolver = options?.imagePathResolver;
284
+ this.imageCacheDir = options?.imageCacheDir;
285
+ }
286
+ /** Main entry point. See module header for the failure model. */
287
+ async parse(prompt) {
288
+ const start = Date.now();
289
+ const errors = [];
290
+ // --- 1. Pure-text artifacts (code blocks + stripped text) -------------
291
+ const codeBlocks = extractCodeBlocks(prompt);
292
+ const textWithoutMarkers = stripMarkersAndBlocks(prompt);
293
+ // --- 2. Collect image references --------------------------------------
294
+ const markerTokens = extractImageMarkers(prompt);
295
+ const markdownImages = extractMarkdownImages(prompt);
296
+ const imagesFound = markerTokens.length + markdownImages.length;
297
+ if (imagesFound === 0) {
298
+ return {
299
+ originalText: prompt,
300
+ textWithoutMarkers,
301
+ images: [],
302
+ codeBlocks,
303
+ meta: {
304
+ parseMs: Date.now() - start,
305
+ imagesFound: 0,
306
+ imagesAnalyzed: 0,
307
+ codeBlocksFound: codeBlocks.length,
308
+ errors,
309
+ },
310
+ };
311
+ }
312
+ // --- 3. Resolve marker -> path (custom resolver, then default cache) --
313
+ const references = [];
314
+ for (const marker of markerTokens) {
315
+ const custom = this.customResolver?.(marker);
316
+ if (custom) {
317
+ references.push({ marker, resolvedPath: custom });
318
+ continue;
319
+ }
320
+ const fromCache = await defaultResolveCachedImage(marker, this.imageCacheDir);
321
+ if (fromCache) {
322
+ references.push({ marker, resolvedPath: fromCache });
323
+ }
324
+ else {
325
+ const reason = `Could not resolve ${marker} to a file path`;
326
+ references.push({ marker, resolutionError: reason });
327
+ errors.push(reason);
328
+ }
329
+ }
330
+ for (const md of markdownImages) {
331
+ // Markdown image paths are already explicit; expand `~` for convenience.
332
+ const expanded = md.path.startsWith('~/')
333
+ ? path.join(os.homedir(), md.path.slice(2))
334
+ : md.path;
335
+ references.push({ marker: `![${md.alt}](${md.path})`, resolvedPath: expanded });
336
+ }
337
+ // --- 4. Vision analysis (parallel) ------------------------------------
338
+ let images = [];
339
+ let imagesAnalyzed = 0;
340
+ const visionFn = this.ai?.completeWithImage?.bind(this.ai);
341
+ if (!visionFn) {
342
+ if (references.some((r) => r.resolvedPath)) {
343
+ errors.push('AI provider not configured for vision; skipped image analysis');
344
+ }
345
+ }
346
+ else {
347
+ const tasks = references.map((ref) => this.analyzeImage(ref, visionFn, errors));
348
+ const settled = await Promise.all(tasks);
349
+ const successes = [];
350
+ for (const result of settled) {
351
+ if (!result)
352
+ continue;
353
+ successes.push(result.analysis);
354
+ if (result.success)
355
+ imagesAnalyzed++;
356
+ }
357
+ images = successes;
358
+ }
359
+ return {
360
+ originalText: prompt,
361
+ textWithoutMarkers,
362
+ images,
363
+ codeBlocks,
364
+ meta: {
365
+ parseMs: Date.now() - start,
366
+ imagesFound,
367
+ imagesAnalyzed,
368
+ codeBlocksFound: codeBlocks.length,
369
+ errors,
370
+ },
371
+ };
372
+ }
373
+ /**
374
+ * Analyze a single image. Returns `null` only when path resolution failed
375
+ * upstream (we already pushed an error for that case). Otherwise returns
376
+ * an analysis plus a `success` flag indicating whether Vision actually
377
+ * returned a usable JSON payload.
378
+ */
379
+ async analyzeImage(ref, completeWithImage, errors) {
380
+ if (!ref.resolvedPath)
381
+ return null;
382
+ const imagePath = ref.resolvedPath;
383
+ const started = Date.now();
384
+ // Make sure the file actually exists & is a supported format before we
385
+ // burn an API call on it.
386
+ const mediaType = await detectMediaType(imagePath);
387
+ if (!mediaType) {
388
+ errors.push(`Skipped ${ref.marker}: file missing or unsupported media type (${imagePath})`);
389
+ return { analysis: defaultAnalysis(imagePath, Date.now() - started), success: false };
390
+ }
391
+ try {
392
+ const response = await completeWithImage(VISION_ANALYSIS_PROMPT, {
393
+ images: [{ path: imagePath, mediaType }],
394
+ timeoutMs: this.visionTimeoutMs,
395
+ maxTokens: DEFAULT_VISION_MAX_TOKENS,
396
+ ...(this.visionModel ? { model: this.visionModel } : {}),
397
+ });
398
+ const parsed = parseVisionResponse(response);
399
+ const analysisMs = Date.now() - started;
400
+ if (!parsed) {
401
+ errors.push(`Vision response for ${ref.marker} was not valid JSON; using defaults`);
402
+ return { analysis: defaultAnalysis(imagePath, analysisMs), success: false };
403
+ }
404
+ return {
405
+ analysis: {
406
+ imagePath,
407
+ screenshotType: parsed.screenshotType,
408
+ ocrText: parsed.ocrText,
409
+ visibleElements: parsed.visibleElements,
410
+ errorMessages: parsed.errorMessages,
411
+ semanticDescription: parsed.semanticDescription,
412
+ analysisMs,
413
+ },
414
+ success: true,
415
+ };
416
+ }
417
+ catch (err) {
418
+ const reason = err instanceof Error ? err.message : String(err);
419
+ logger.warn(`[MultimodalParser] vision call failed for ${imagePath}: ${reason}`);
420
+ errors.push(`Vision call failed for ${ref.marker}: ${reason}`);
421
+ return { analysis: defaultAnalysis(imagePath, Date.now() - started), success: false };
422
+ }
423
+ }
424
+ }
425
+ //# sourceMappingURL=multimodal-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multimodal-parser.js","sourceRoot":"","sources":["../../src/intelligence/multimodal-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP Server for claude-forge
3
+ *
4
+ * 嵌入在 daemon 进程内,通过 HTTP(Streamable HTTP transport)向 Claude Code
5
+ * 暴露以下工具:
6
+ * - skill_invoke:Agent 按需获取某个 Skill 的方法论内容
7
+ * - skill_list:列出所有可用 Skill(id / name / description / keywords)
8
+ *
9
+ * 调用链路:
10
+ * Claude Code → HTTP POST /mcp → MCP server → skillInvoke()/skillList()
11
+ * → writeSkillInvocation() 记录
12
+ *
13
+ * 设计要点:
14
+ * - 复用现有 Express app(不新增端口),路径前缀固定为 /mcp
15
+ * - 沿用 daemon auth token(Bearer),与 /api 写操作同一鉴权链路
16
+ * - Stateless 模式:每个请求开一对 server/transport,避免长连接污染
17
+ * daemon 主循环;Skill 调用本身是短平快、无需流式推送的场景
18
+ * - Session 追踪:优先 header `X-Forge-Session-Id`,缺失时回落到
19
+ * routingState 中最近一次活跃路由(heuristic,第一阶段足够)
20
+ */
21
+ import type { Application, Request } from 'express';
22
+ import type { SkillRegistry } from '../skills/registry.js';
23
+ import type { SQLiteStorage } from '../core/storage/sqlite.js';
24
+ import type { InvocationGuard } from '../skills/invocation-guard.js';
25
+ /** Resolved session context for a single MCP tool call. */
26
+ export interface SessionContext {
27
+ sessionId: string | null;
28
+ routeRequestId: string | null;
29
+ agentId: string | null;
30
+ }
31
+ export interface McpServerOptions {
32
+ skillRegistry: SkillRegistry;
33
+ storage: SQLiteStorage;
34
+ guard?: InvocationGuard;
35
+ /**
36
+ * Override the session-resolution strategy. Default behavior:
37
+ * 1. Read header `X-Forge-Session-Id` (passed by the harness if available)
38
+ * 2. Fallback: pick the freshest live routing entry from routingState
39
+ * 3. If neither yields a sessionId → return all-null context (invocation
40
+ * will be recorded with session_id derived from the synthetic
41
+ * "mcp-anon-<ts>" so the writeSkillInvocation NOT NULL constraint still
42
+ * passes; see resolveSessionContext below).
43
+ */
44
+ resolveSession?: (req: Request) => SessionContext;
45
+ }
46
+ /**
47
+ * Mount the MCP server endpoints on an existing Express application.
48
+ *
49
+ * Routes:
50
+ * POST /mcp — JSON-RPC over Streamable HTTP (initialize, tools/list, tools/call)
51
+ * GET /mcp — Standalone SSE channel (server→client notifications)
52
+ * DELETE /mcp — Session termination (stateful mode only; no-op in stateless)
53
+ *
54
+ * Authentication: every method requires the daemon Bearer token. Token-file
55
+ * absence still falls through (early-startup safety, identical to /api).
56
+ */
57
+ export declare function mountMcpServer(app: Application, options: McpServerOptions): void;
58
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAY,MAAM,SAAS,CAAC;AAK9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAOrE,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,cAAc,CAAC;CACnD;AAkJD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CA4ChF"}