@kage-core/kage-graph-mcp 1.1.36 → 1.1.38

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/dist/daemon.js CHANGED
@@ -1,5 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.viewerStaticHeaders = viewerStaticHeaders;
4
+ exports.viewerRedirectLocation = viewerRedirectLocation;
5
+ exports.viewerBenchmarkReport = viewerBenchmarkReport;
6
+ exports.daemonContextReport = daemonContextReport;
3
7
  exports.readDaemonStatus = readDaemonStatus;
4
8
  exports.daemonDoctor = daemonDoctor;
5
9
  exports.stopDaemon = stopDaemon;
@@ -34,6 +38,107 @@ function contentType(filePath) {
34
38
  return "text/markdown; charset=utf-8";
35
39
  return "application/octet-stream";
36
40
  }
41
+ function viewerStaticHeaders(filePath) {
42
+ return {
43
+ "content-type": contentType(filePath),
44
+ "x-content-type-options": "nosniff",
45
+ "referrer-policy": "no-referrer",
46
+ "cross-origin-opener-policy": "same-origin",
47
+ "content-security-policy": [
48
+ "default-src 'self'",
49
+ "script-src 'self'",
50
+ "script-src-attr 'none'",
51
+ "style-src 'self' 'unsafe-inline'",
52
+ "img-src 'self' data:",
53
+ "connect-src 'self'",
54
+ "font-src 'none'",
55
+ "object-src 'none'",
56
+ "base-uri 'none'",
57
+ "frame-ancestors 'none'",
58
+ ].join("; "),
59
+ };
60
+ }
61
+ function viewerRedirectLocation(pathname, search, fallbackSearch) {
62
+ if (pathname !== "/" && pathname !== "/viewer" && pathname !== "/viewer/")
63
+ return null;
64
+ return `/viewer/index.html${search || fallbackSearch}`;
65
+ }
66
+ function viewerBenchmarkReport(projectDir) {
67
+ const gates = (0, kernel_js_1.benchmarkProject)(projectDir);
68
+ const memoryQuality = (0, kernel_js_1.benchmarkCodingMemoryQuality)();
69
+ const memoryScale = (0, kernel_js_1.benchmarkMemoryScale)({ sizes: [240], topK: 10 });
70
+ const requiredGates = gates.gates.filter((gate) => gate.required);
71
+ const requiredGatePasses = requiredGates.filter((gate) => gate.pass).length;
72
+ const requiredGatePercent = requiredGates.length ? Math.round((requiredGatePasses / requiredGates.length) * 100) : (gates.ok ? 100 : 0);
73
+ const recallAt10 = Number(memoryQuality.summary.recall_at_10_percent ?? memoryQuality.summary.recall_at_k_percent ?? 0);
74
+ const sourceDiversity = memoryQuality.source_diversity;
75
+ const scaleHitRate = Number(memoryScale.summary.largest_hit_rate_percent ?? 0);
76
+ const scaleContextCut = Number(memoryScale.summary.largest_context_reduction_percent ?? 0);
77
+ return {
78
+ ...gates,
79
+ summary: memoryQuality.summary,
80
+ memory_quality: {
81
+ dataset: memoryQuality.dataset,
82
+ summary: memoryQuality.summary,
83
+ source_diversity: memoryQuality.source_diversity,
84
+ by_category: memoryQuality.by_category,
85
+ baselines: memoryQuality.baselines,
86
+ caveats: memoryQuality.caveats,
87
+ },
88
+ memory_scale: {
89
+ sizes: memoryScale.sizes,
90
+ summary: memoryScale.summary,
91
+ results: memoryScale.results,
92
+ caveats: memoryScale.caveats,
93
+ },
94
+ proof_ledger: [
95
+ {
96
+ id: "memory-quality",
97
+ label: "Coding-memory retrieval",
98
+ metric: `${recallAt10.toFixed(2)}% R@10, ${memoryQuality.summary.ndcg_at_10.toFixed(4)} NDCG@10`,
99
+ target: ">=95% R@10 and >=0.85 NDCG@10",
100
+ actual: recallAt10,
101
+ unit: "percent",
102
+ pass: recallAt10 >= 95 && memoryQuality.summary.ndcg_at_10 >= 0.85,
103
+ command: "kage benchmark --memory-quality --json",
104
+ next_action: "Use this as the fast coding-memory retrieval proof before publishing benchmark claims.",
105
+ },
106
+ {
107
+ id: "source-diversity",
108
+ label: "Source diversity",
109
+ metric: `${sourceDiversity.unique_sources} sources in top ${sourceDiversity.top_k}; max ${sourceDiversity.max_results_from_one_source} from one source`,
110
+ target: ">=2 sources and <=3 results from one observed session",
111
+ actual: sourceDiversity.unique_sources,
112
+ unit: "score",
113
+ pass: sourceDiversity.pass,
114
+ command: "kage benchmark --memory-quality --json",
115
+ next_action: sourceDiversity.pass ? "Use this to prove noisy sessions cannot crowd out independent repo memory." : "Inspect recall source diversity before publishing memory-quality claims.",
116
+ },
117
+ {
118
+ id: "memory-scale",
119
+ label: "Memory scale sanity",
120
+ metric: `${memoryScale.summary.largest_packets} packets, ${scaleHitRate.toFixed(2)}% hit rate, ${scaleContextCut.toFixed(2)}% context cut`,
121
+ target: ">=95% hit rate and >=80% context reduction",
122
+ actual: scaleHitRate,
123
+ unit: "percent",
124
+ pass: scaleHitRate >= 95 && scaleContextCut >= 80,
125
+ command: "kage benchmark --scale --sizes 240 --json",
126
+ next_action: "Run kage benchmark --scale --sizes 240,1000,5000 --json for a larger release proof.",
127
+ },
128
+ {
129
+ id: "local-gates",
130
+ label: "Repo trust gates",
131
+ metric: `${requiredGatePasses}/${requiredGates.length || gates.gates.length} required gates passing`,
132
+ target: "100% required gates passing",
133
+ actual: requiredGatePercent,
134
+ unit: "percent",
135
+ pass: gates.ok,
136
+ command: "kage benchmark --project . --json",
137
+ next_action: gates.ok ? "Keep this green before handoff, README updates, or release." : "Fix failing repo benchmark gates before sharing this memory state.",
138
+ },
139
+ ],
140
+ };
141
+ }
37
142
  function statusPath(projectDir) {
38
143
  return (0, node_path_1.join)(daemonDir(projectDir), "status.json");
39
144
  }
@@ -64,6 +169,83 @@ function readBody(req) {
64
169
  req.on("error", reject);
65
170
  });
66
171
  }
172
+ function stringArray(value) {
173
+ if (Array.isArray(value))
174
+ return value.map(String).filter(Boolean);
175
+ if (typeof value === "string")
176
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
177
+ return [];
178
+ }
179
+ function filePathHints(query) {
180
+ const matches = query.match(/[A-Za-z0-9_./@-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|kts|rb|php|cs|c|h|cc|cpp|hpp|swift|json|md)\b/g) ?? [];
181
+ return [...new Set(matches.map((match) => match.replace(/^\.\//, "")).filter((match) => !/^https?:\/\//.test(match)))];
182
+ }
183
+ function wantsDependencyPath(query) {
184
+ return /\b(connect|connected|dependency|depend|depends|path|impact|flow|trace)\b/i.test(query);
185
+ }
186
+ function riskContextBlock(result) {
187
+ const targets = Object.values(result.targets);
188
+ if (!targets.length)
189
+ return "";
190
+ const lines = targets.slice(0, 5).map((item) => {
191
+ const coChange = item.git.co_change_partners.length
192
+ ? ` Co-change: ${item.git.co_change_partners.slice(0, 3).map((partner) => `${partner.file_path} (${partner.count})`).join(", ")}.`
193
+ : "";
194
+ return `- ${item.risk_summary}${coChange}`;
195
+ });
196
+ return `\n## Risk Signals\n${lines.join("\n")}`;
197
+ }
198
+ function daemonContextReport(projectDir, body) {
199
+ const query = String(body.query ?? "");
200
+ const limit = Number(body.limit ?? 5);
201
+ const validation = (0, kernel_js_1.validateProject)(projectDir);
202
+ const recallResult = (0, kernel_js_1.recall)(projectDir, query, limit, Boolean(body.explain));
203
+ const graphResult = (0, kernel_js_1.queryGraph)(projectDir, query, 5);
204
+ const explicitTargets = [...stringArray(body.targets), ...filePathHints(query)];
205
+ const changedFiles = stringArray(body.changed_files);
206
+ const riskResult = explicitTargets.length || changedFiles.length ? (0, kernel_js_1.kageRisk)(projectDir, explicitTargets, changedFiles) : null;
207
+ const pathHints = filePathHints(query);
208
+ const dependencyResult = wantsDependencyPath(query) && pathHints.length >= 2
209
+ ? (0, kernel_js_1.kageDependencyPath)(projectDir, pathHints[0], pathHints[1])
210
+ : null;
211
+ const reconciliation = (0, kernel_js_1.kageMemoryReconciliation)(projectDir, {
212
+ sessionId: typeof body.session_id === "string" ? body.session_id : undefined,
213
+ limit: 5,
214
+ });
215
+ const teammateBrief = (0, kernel_js_1.kageTeammateBrief)(projectDir, {
216
+ query,
217
+ targets: explicitTargets,
218
+ changedFiles,
219
+ recallResult,
220
+ riskResult,
221
+ reconciliation,
222
+ });
223
+ const learningLedger = typeof body.session_id === "string" && body.session_id.trim()
224
+ ? (0, kernel_js_1.kageSessionLearningLedger)(projectDir, { sessionId: body.session_id, limit: 20 })
225
+ : null;
226
+ const validationText = validation.ok ? "Memory healthy." : `Warnings: ${validation.warnings.join("; ")}`;
227
+ const contextBlock = [
228
+ recallResult.context_block,
229
+ teammateBrief.context_block,
230
+ learningLedger ? learningLedger.context_block : "",
231
+ graphResult.context_block ? `\n## Graph Facts\n${graphResult.context_block}` : "",
232
+ riskResult ? riskContextBlock(riskResult) : "",
233
+ dependencyResult ? `\n## Dependency Path\n${dependencyResult.summary}${dependencyResult.path.length ? `\nPath: ${dependencyResult.path.join(" -> ")}` : ""}` : "",
234
+ reconciliation.unresolved_count ? `\n## Memory Reconciliation\n${reconciliation.agent_instruction}` : "",
235
+ `\n_${validationText}_`,
236
+ ].filter(Boolean).join("");
237
+ return {
238
+ context_block: contextBlock,
239
+ recall: recallResult,
240
+ graph: graphResult,
241
+ teammate_brief: teammateBrief,
242
+ learning_ledger: learningLedger,
243
+ risk: riskResult,
244
+ dependency_path: dependencyResult,
245
+ validation,
246
+ memory_reconciliation: reconciliation,
247
+ };
248
+ }
67
249
  function readDaemonStatus(projectDir) {
68
250
  const path = statusPath(projectDir);
69
251
  if (!(0, node_fs_1.existsSync)(path))
@@ -93,9 +275,22 @@ function daemonDoctor(projectDir) {
93
275
  status,
94
276
  endpoints: [
95
277
  `GET http://${DEFAULT_HOST}:${restPort}/health`,
278
+ `POST http://${DEFAULT_HOST}:${restPort}/kage/context`,
96
279
  `POST http://${DEFAULT_HOST}:${restPort}/kage/recall`,
280
+ `POST http://${DEFAULT_HOST}:${restPort}/kage/capture`,
281
+ `POST http://${DEFAULT_HOST}:${restPort}/kage/learn`,
282
+ `POST http://${DEFAULT_HOST}:${restPort}/kage/feedback`,
97
283
  `POST http://${DEFAULT_HOST}:${restPort}/kage/observe`,
98
284
  `POST http://${DEFAULT_HOST}:${restPort}/kage/distill`,
285
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/learning-ledger`,
286
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/replay`,
287
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/setup-doctor`,
288
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/profile`,
289
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/xray`,
290
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/capabilities`,
291
+ `GET http://${DEFAULT_HOST}:${restPort}/kage/context-slots`,
292
+ `POST http://${DEFAULT_HOST}:${restPort}/kage/context-slots`,
293
+ `DELETE http://${DEFAULT_HOST}:${restPort}/kage/context-slots/:label`,
99
294
  `GET http://${DEFAULT_HOST}:${restPort}/kage/metrics`,
100
295
  `GET http://${DEFAULT_HOST}:${restPort}/kage/quality`,
101
296
  `GET http://${DEFAULT_HOST}:${restPort}/kage/inbox`,
@@ -187,12 +382,71 @@ async function startDaemon(projectDir, options = {}) {
187
382
  json(res, 200, (0, kernel_js_1.qualityReport)(projectDir));
188
383
  return;
189
384
  }
385
+ if (req.method === "GET" && url.pathname === "/kage/profile") {
386
+ json(res, 200, (0, kernel_js_1.kageProjectProfile)(projectDir));
387
+ return;
388
+ }
389
+ if (req.method === "GET" && url.pathname === "/kage/xray") {
390
+ json(res, 200, (0, kernel_js_1.kageRepoXray)(projectDir));
391
+ return;
392
+ }
393
+ if (req.method === "GET" && url.pathname === "/kage/capabilities") {
394
+ json(res, 200, (0, kernel_js_1.kageCapabilityAudit)(projectDir));
395
+ return;
396
+ }
397
+ if (req.method === "GET" && url.pathname === "/kage/replay") {
398
+ json(res, 200, (0, kernel_js_1.kageSessionReplay)(projectDir, {
399
+ sessionId: url.searchParams.get("session") ?? undefined,
400
+ limit: Number(url.searchParams.get("limit") ?? 200),
401
+ }));
402
+ return;
403
+ }
404
+ if (req.method === "GET" && url.pathname === "/kage/learning-ledger") {
405
+ json(res, 200, (0, kernel_js_1.kageSessionLearningLedger)(projectDir, {
406
+ sessionId: url.searchParams.get("session") ?? undefined,
407
+ limit: Number(url.searchParams.get("limit") ?? 50),
408
+ }));
409
+ return;
410
+ }
411
+ if (req.method === "GET" && url.pathname === "/kage/context-slots") {
412
+ json(res, 200, (0, kernel_js_1.kageContextSlots)(projectDir));
413
+ return;
414
+ }
415
+ if (req.method === "GET" && url.pathname === "/kage/lifecycle") {
416
+ json(res, 200, (0, kernel_js_1.kageMemoryLifecycle)(projectDir));
417
+ return;
418
+ }
419
+ if (req.method === "GET" && url.pathname === "/kage/memory-audit") {
420
+ json(res, 200, (0, kernel_js_1.kageMemoryAudit)(projectDir, Number(url.searchParams.get("limit") ?? 100)));
421
+ return;
422
+ }
423
+ if (req.method === "GET" && url.pathname === "/kage/handoff") {
424
+ json(res, 200, (0, kernel_js_1.kageMemoryHandoff)(projectDir));
425
+ return;
426
+ }
427
+ if (req.method === "GET" && url.pathname === "/kage/timeline") {
428
+ json(res, 200, (0, kernel_js_1.kageMemoryTimeline)(projectDir, Number(url.searchParams.get("days") ?? 14)));
429
+ return;
430
+ }
431
+ if (req.method === "GET" && url.pathname === "/kage/lineage") {
432
+ json(res, 200, (0, kernel_js_1.kageMemoryLineage)(projectDir));
433
+ return;
434
+ }
190
435
  if (req.method === "GET" && url.pathname === "/kage/inbox") {
191
436
  json(res, 200, (0, kernel_js_1.memoryInbox)(projectDir));
192
437
  return;
193
438
  }
194
439
  if (req.method === "GET" && url.pathname === "/kage/benchmark") {
195
- json(res, 200, (0, kernel_js_1.benchmarkProject)(projectDir));
440
+ json(res, 200, url.searchParams.get("mode") === "memory_quality" ? (0, kernel_js_1.benchmarkCodingMemoryQuality)() : (0, kernel_js_1.benchmarkProject)(projectDir));
441
+ return;
442
+ }
443
+ if (req.method === "GET" && url.pathname === "/kage/setup-doctor") {
444
+ json(res, 200, (0, kernel_js_1.setupDoctor)(projectDir));
445
+ return;
446
+ }
447
+ if (req.method === "POST" && url.pathname === "/kage/context") {
448
+ const body = await readBody(req);
449
+ json(res, 200, daemonContextReport(projectDir, body));
196
450
  return;
197
451
  }
198
452
  if (req.method === "POST" && url.pathname === "/kage/recall") {
@@ -200,6 +454,63 @@ async function startDaemon(projectDir, options = {}) {
200
454
  json(res, 200, (0, kernel_js_1.recall)(projectDir, String(body.query ?? ""), Number(body.limit ?? 5), Boolean(body.explain)));
201
455
  return;
202
456
  }
457
+ if (req.method === "POST" && url.pathname === "/kage/capture") {
458
+ const body = await readBody(req);
459
+ const result = (0, kernel_js_1.capture)({
460
+ projectDir,
461
+ title: String(body.title ?? ""),
462
+ summary: body.summary == null ? undefined : String(body.summary),
463
+ body: String(body.body ?? body.learning ?? ""),
464
+ type: body.type == null ? undefined : String(body.type),
465
+ tags: stringArray(body.tags),
466
+ paths: stringArray(body.paths),
467
+ stack: stringArray(body.stack),
468
+ });
469
+ json(res, result.ok ? 200 : 400, result);
470
+ return;
471
+ }
472
+ if (req.method === "POST" && url.pathname === "/kage/learn") {
473
+ const body = await readBody(req);
474
+ const result = (0, kernel_js_1.learn)({
475
+ projectDir,
476
+ learning: String(body.learning ?? body.body ?? ""),
477
+ title: body.title == null ? undefined : String(body.title),
478
+ type: body.type == null ? undefined : String(body.type),
479
+ evidence: body.evidence == null ? undefined : String(body.evidence),
480
+ verifiedBy: body.verified_by == null ? body.verifiedBy == null ? undefined : String(body.verifiedBy) : String(body.verified_by),
481
+ tags: stringArray(body.tags),
482
+ paths: stringArray(body.paths),
483
+ stack: stringArray(body.stack),
484
+ });
485
+ json(res, result.ok ? 200 : 400, result);
486
+ return;
487
+ }
488
+ if (req.method === "POST" && url.pathname === "/kage/feedback") {
489
+ const body = await readBody(req);
490
+ const result = (0, kernel_js_1.recordFeedback)(projectDir, String(body.packet_id ?? body.packet ?? ""), String(body.kind ?? ""));
491
+ json(res, result.ok ? 200 : 400, result);
492
+ return;
493
+ }
494
+ if (req.method === "POST" && url.pathname === "/kage/context-slots") {
495
+ const body = await readBody(req);
496
+ const result = (0, kernel_js_1.setContextSlot)(projectDir, {
497
+ label: String(body.label ?? ""),
498
+ content: String(body.content ?? ""),
499
+ description: body.description == null ? undefined : String(body.description),
500
+ pinned: body.pinned == null ? undefined : Boolean(body.pinned),
501
+ size_limit: body.size_limit == null ? undefined : Number(body.size_limit),
502
+ paths: stringArray(body.paths),
503
+ tags: stringArray(body.tags),
504
+ });
505
+ json(res, result.ok ? 200 : 400, result);
506
+ return;
507
+ }
508
+ if (req.method === "DELETE" && url.pathname.startsWith("/kage/context-slots/")) {
509
+ const label = decodeURIComponent(url.pathname.slice("/kage/context-slots/".length));
510
+ const result = (0, kernel_js_1.deleteContextSlot)(projectDir, label);
511
+ json(res, result.ok ? 200 : 404, result);
512
+ return;
513
+ }
203
514
  if (req.method === "POST" && url.pathname === "/kage/observe") {
204
515
  const body = await readBody(req);
205
516
  json(res, 200, (0, kernel_js_1.observe)(projectDir, body));
@@ -244,11 +555,24 @@ async function startViewer(projectDir, options = {}) {
244
555
  const qualityPath = (0, node_path_1.join)(reportsDir, "quality.json");
245
556
  const benchmarkPath = (0, node_path_1.join)(reportsDir, "benchmark.json");
246
557
  const contributorsPath = (0, node_path_1.join)(reportsDir, "contributors.json");
558
+ const profilePath = (0, node_path_1.join)(reportsDir, "profile.json");
559
+ const xrayPath = (0, node_path_1.join)(reportsDir, "xray.json");
560
+ const capabilitiesPath = (0, node_path_1.join)(reportsDir, "capabilities.json");
561
+ const slotsPath = (0, node_path_1.join)(reportsDir, "context-slots.json");
247
562
  const decisionsPath = (0, node_path_1.join)(reportsDir, "decisions.json");
248
563
  const riskPath = (0, node_path_1.join)(reportsDir, "risk.json");
249
564
  const moduleHealthPath = (0, node_path_1.join)(reportsDir, "module-health.json");
250
565
  const graphInsightsPath = (0, node_path_1.join)(reportsDir, "graph-insights.json");
251
566
  const workspacePath = (0, node_path_1.join)(reportsDir, "workspace.json");
567
+ const sessionsPath = (0, node_path_1.join)(reportsDir, "sessions.json");
568
+ const replayPath = (0, node_path_1.join)(reportsDir, "replay.json");
569
+ const memoryAccessPath = (0, node_path_1.join)(reportsDir, "memory-access.json");
570
+ const memoryAuditPath = (0, node_path_1.join)(reportsDir, "memory-audit.json");
571
+ const handoffPath = (0, node_path_1.join)(reportsDir, "handoff.json");
572
+ const lifecyclePath = (0, node_path_1.join)(reportsDir, "lifecycle.json");
573
+ const timelinePath = (0, node_path_1.join)(reportsDir, "timeline.json");
574
+ const lineagePath = (0, node_path_1.join)(reportsDir, "lineage.json");
575
+ const setupPath = (0, node_path_1.join)(reportsDir, "setup.json");
252
576
  // Pre-generate lightweight JSON reports so the viewer can load them directly.
253
577
  try {
254
578
  (0, node_fs_1.mkdirSync)(reportsDir, { recursive: true });
@@ -257,24 +581,37 @@ async function startViewer(projectDir, options = {}) {
257
581
  const inbox = (0, kernel_js_1.memoryInbox)(projectDir);
258
582
  (0, node_fs_1.writeFileSync)(inboxPath, JSON.stringify(inbox, null, 2));
259
583
  (0, node_fs_1.writeFileSync)(qualityPath, JSON.stringify((0, kernel_js_1.qualityReport)(projectDir), null, 2));
260
- (0, node_fs_1.writeFileSync)(benchmarkPath, JSON.stringify((0, kernel_js_1.benchmarkProject)(projectDir), null, 2));
584
+ (0, node_fs_1.writeFileSync)(benchmarkPath, JSON.stringify(viewerBenchmarkReport(projectDir), null, 2));
261
585
  (0, node_fs_1.writeFileSync)(contributorsPath, JSON.stringify((0, kernel_js_1.kageContributors)(projectDir), null, 2));
586
+ (0, node_fs_1.writeFileSync)(profilePath, JSON.stringify((0, kernel_js_1.kageProjectProfile)(projectDir), null, 2));
587
+ (0, node_fs_1.writeFileSync)(xrayPath, JSON.stringify((0, kernel_js_1.kageRepoXray)(projectDir), null, 2));
588
+ (0, node_fs_1.writeFileSync)(capabilitiesPath, JSON.stringify((0, kernel_js_1.kageCapabilityAudit)(projectDir), null, 2));
589
+ (0, node_fs_1.writeFileSync)(slotsPath, JSON.stringify((0, kernel_js_1.kageContextSlots)(projectDir), null, 2));
262
590
  (0, node_fs_1.writeFileSync)(decisionsPath, JSON.stringify((0, kernel_js_1.kageDecisionIntelligence)(projectDir), null, 2));
263
591
  (0, node_fs_1.writeFileSync)(riskPath, JSON.stringify((0, kernel_js_1.kageRisk)(projectDir), null, 2));
264
592
  (0, node_fs_1.writeFileSync)(moduleHealthPath, JSON.stringify((0, kernel_js_1.kageModuleHealth)(projectDir), null, 2));
265
593
  (0, node_fs_1.writeFileSync)(graphInsightsPath, JSON.stringify((0, kernel_js_1.kageGraphInsights)(projectDir), null, 2));
266
594
  (0, node_fs_1.writeFileSync)(workspacePath, JSON.stringify((0, kernel_js_1.kageWorkspace)(projectDir), null, 2));
595
+ (0, node_fs_1.writeFileSync)(sessionsPath, JSON.stringify((0, kernel_js_1.kageSessionCaptureReport)(projectDir), null, 2));
596
+ (0, node_fs_1.writeFileSync)(replayPath, JSON.stringify((0, kernel_js_1.kageSessionReplay)(projectDir), null, 2));
597
+ (0, node_fs_1.writeFileSync)(memoryAccessPath, JSON.stringify((0, kernel_js_1.kageMemoryAccess)(projectDir), null, 2));
598
+ (0, node_fs_1.writeFileSync)(memoryAuditPath, JSON.stringify((0, kernel_js_1.kageMemoryAudit)(projectDir), null, 2));
599
+ (0, node_fs_1.writeFileSync)(handoffPath, JSON.stringify((0, kernel_js_1.kageMemoryHandoff)(projectDir), null, 2));
600
+ (0, node_fs_1.writeFileSync)(lifecyclePath, JSON.stringify((0, kernel_js_1.kageMemoryLifecycle)(projectDir), null, 2));
601
+ (0, node_fs_1.writeFileSync)(timelinePath, JSON.stringify((0, kernel_js_1.kageMemoryTimeline)(projectDir), null, 2));
602
+ (0, node_fs_1.writeFileSync)(lineagePath, JSON.stringify((0, kernel_js_1.kageMemoryLineage)(projectDir), null, 2));
603
+ (0, node_fs_1.writeFileSync)(setupPath, JSON.stringify((0, kernel_js_1.setupDoctor)(projectDir), null, 2));
267
604
  }
268
605
  catch {
269
606
  // non-fatal: viewer will show 404 for reports if generation fails
270
607
  }
271
- const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&view=code`;
608
+ const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&profile=${encodeURIComponent(profilePath)}&xray=${encodeURIComponent(xrayPath)}&capabilities=${encodeURIComponent(capabilitiesPath)}&slots=${encodeURIComponent(slotsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&sessions=${encodeURIComponent(sessionsPath)}&replay=${encodeURIComponent(replayPath)}&memoryAccess=${encodeURIComponent(memoryAccessPath)}&memoryAudit=${encodeURIComponent(memoryAuditPath)}&handoff=${encodeURIComponent(handoffPath)}&lifecycle=${encodeURIComponent(lifecyclePath)}&timeline=${encodeURIComponent(timelinePath)}&lineage=${encodeURIComponent(lineagePath)}&setup=${encodeURIComponent(setupPath)}&view=code`;
272
609
  const server = (0, node_http_1.createServer)((req, res) => {
273
610
  const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
274
611
  let filePath = null;
275
- if (requestUrl.pathname === "/" || requestUrl.pathname === "/viewer") {
276
- const viewerSearch = requestUrl.search || new URL(url).search;
277
- res.writeHead(302, { location: `/viewer/index.html${viewerSearch}` });
612
+ const redirectLocation = viewerRedirectLocation(requestUrl.pathname, requestUrl.search, new URL(url).search);
613
+ if (redirectLocation) {
614
+ res.writeHead(302, { location: redirectLocation });
278
615
  res.end();
279
616
  return;
280
617
  }
@@ -303,7 +640,7 @@ async function startViewer(projectDir, options = {}) {
303
640
  json(res, 200, { ok: true, files });
304
641
  return;
305
642
  }
306
- res.writeHead(200, { "content-type": contentType(filePath) });
643
+ res.writeHead(200, viewerStaticHeaders(filePath));
307
644
  res.end((0, node_fs_1.readFileSync)(filePath));
308
645
  });
309
646
  await new Promise((resolveListen) => server.listen(port, host, resolveListen));