@mandujs/core 0.53.0 → 0.53.2

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 (112) hide show
  1. package/package.json +38 -2
  2. package/src/a11y/run-audit.ts +6 -6
  3. package/src/brain/adapters/oauth-flow.ts +1 -1
  4. package/src/brain/architecture/analyzer.ts +0 -2
  5. package/src/brain/credentials.ts +2 -2
  6. package/src/bundler/__tests__/build-runner.ts +13 -1
  7. package/src/bundler/__tests__/extended-watch.test.ts +3 -2
  8. package/src/bundler/__tests__/fast-refresh.test.ts +10 -12
  9. package/src/bundler/build.test.ts +30 -33
  10. package/src/bundler/build.ts +56 -27
  11. package/src/bundler/dev.ts +44 -39
  12. package/src/bundler/prerender.ts +63 -0
  13. package/src/bundler/safe-build.test.ts +2 -1
  14. package/src/bundler/types.ts +13 -6
  15. package/src/change/history.ts +1 -1
  16. package/src/change/snapshot.ts +18 -9
  17. package/src/client/Link.tsx +9 -9
  18. package/src/client/globals.ts +3 -2
  19. package/src/client/hooks.ts +0 -4
  20. package/src/client/hydrate.ts +1 -1
  21. package/src/client/router.ts +12 -12
  22. package/src/client/rpc.ts +1 -1
  23. package/src/client/use-fetch.ts +6 -6
  24. package/src/client/use-sse.ts +5 -5
  25. package/src/config/mcp-status.ts +0 -1
  26. package/src/config/validate.ts +4 -4
  27. package/src/config/watcher.ts +1 -1
  28. package/src/content/collection.ts +8 -12
  29. package/src/content/content-layer.ts +5 -5
  30. package/src/content/data-store.ts +2 -2
  31. package/src/content/loader-context.ts +0 -1
  32. package/src/content/loaders/file.ts +1 -1
  33. package/src/content/loaders/glob.ts +4 -4
  34. package/src/content/loaders/types.ts +1 -1
  35. package/src/content/meta-store.ts +1 -1
  36. package/src/content/types.ts +1 -1
  37. package/src/contract/define.ts +11 -12
  38. package/src/contract/index.ts +3 -3
  39. package/src/contract/protection.ts +2 -2
  40. package/src/contract/types.ts +1 -1
  41. package/src/contract/validator.ts +0 -1
  42. package/src/db/__tests__/db.test.ts +4 -7
  43. package/src/db/index.ts +138 -51
  44. package/src/db/migrations/index.ts +3 -0
  45. package/src/db/migrations/lock.ts +78 -23
  46. package/src/db/migrations/runner.ts +118 -101
  47. package/src/deploy/cache.ts +1 -0
  48. package/src/design/extract.ts +2 -1
  49. package/src/design/patch.ts +0 -1
  50. package/src/devtools/ai/mcp-connector.ts +24 -24
  51. package/src/devtools/client/components/kitchen-root.tsx +1 -1
  52. package/src/devtools/client/components/panel/errors-panel.tsx +1 -1
  53. package/src/devtools/client/state-manager.ts +1 -1
  54. package/src/devtools/init.ts +2 -3
  55. package/src/devtools/server/source-context.ts +1 -1
  56. package/src/devtools/worker/redaction-worker.ts +8 -4
  57. package/src/devtools/worker/worker-manager.ts +4 -3
  58. package/src/error/classifier.ts +1 -1
  59. package/src/error/domains.ts +1 -1
  60. package/src/filling/sse.ts +5 -5
  61. package/src/generator/generate.ts +2 -2
  62. package/src/guard/analyzer.ts +1 -1
  63. package/src/guard/ast-analyzer.ts +7 -7
  64. package/src/guard/auto-correct.ts +2 -2
  65. package/src/guard/config-guard.ts +0 -2
  66. package/src/guard/contract-guard.ts +1 -1
  67. package/src/guard/decision-memory.ts +2 -2
  68. package/src/guard/design-inline-class.ts +2 -1
  69. package/src/guard/graph.ts +1 -1
  70. package/src/guard/healing.ts +36 -41
  71. package/src/guard/negotiation.ts +3 -3
  72. package/src/guard/reporter.ts +1 -4
  73. package/src/guard/semantic-slots.ts +2 -2
  74. package/src/guard/suggestions.ts +2 -2
  75. package/src/guard/watcher.ts +5 -4
  76. package/src/index.ts +5 -5
  77. package/src/intent/index.ts +1 -1
  78. package/src/kitchen/api/agent-devtools-api.ts +544 -0
  79. package/src/kitchen/api/contract-api.ts +1 -1
  80. package/src/kitchen/api/file-api.ts +3 -3
  81. package/src/kitchen/kitchen-handler.ts +63 -38
  82. package/src/kitchen/kitchen-ui.ts +346 -62
  83. package/src/lockfile/validate.ts +0 -1
  84. package/src/openapi/generator.ts +2 -1
  85. package/src/openapi/openapi.test.ts +43 -14
  86. package/src/perf/user-marks.ts +1 -1
  87. package/src/plugins/registry.ts +1 -2
  88. package/src/plugins/types.ts +1 -1
  89. package/src/resource/__tests__/generator.test.ts +32 -15
  90. package/src/resource/ddl/__tests__/emit.test.ts +24 -0
  91. package/src/resource/ddl/diff.ts +0 -1
  92. package/src/resource/ddl/emit.ts +12 -1
  93. package/src/resource/generator-repo.ts +40 -20
  94. package/src/resource/generator.ts +1 -1
  95. package/src/resource/generators/contract.ts +1 -1
  96. package/src/resource/schema.ts +2 -1
  97. package/src/router/fs-routes.ts +6 -6
  98. package/src/router/fs-scanner.ts +1 -1
  99. package/src/runtime/env.ts +1 -1
  100. package/src/runtime/image-handler.ts +2 -1
  101. package/src/runtime/middleware.ts +2 -1
  102. package/src/runtime/server.ts +67 -60
  103. package/src/runtime/ssr.ts +3 -3
  104. package/src/runtime/streaming-ssr.ts +25 -27
  105. package/src/scheduler/validate.ts +1 -1
  106. package/src/seo/integration/ssr.ts +2 -3
  107. package/src/seo/render/opengraph.ts +1 -1
  108. package/src/seo/resolve/index.ts +0 -4
  109. package/src/slot/corrector.ts +1 -1
  110. package/src/slot/validator.ts +4 -4
  111. package/src/watcher/reporter.ts +1 -1
  112. package/src/watcher/watcher.ts +19 -19
@@ -11,17 +11,26 @@ import { getGlobalCache, getCacheStoreStats } from "../runtime/cache";
11
11
  import { ActivitySSEBroadcaster } from "./stream/activity-sse";
12
12
  import { GuardAPI } from "./api/guard-api";
13
13
  import { handleRoutesRequest } from "./api/routes-api";
14
- import { FileAPI } from "./api/file-api";
15
- import { GuardDecisionManager } from "./api/guard-decisions";
16
- import { ContractPlaygroundAPI } from "./api/contract-api";
17
- import { renderKitchenHTML } from "./kitchen-ui";
18
- import { eventBus } from "../observability/event-bus";
19
- import fs from "fs/promises";
20
- import path from "path";
21
-
22
- export const KITCHEN_PREFIX = "/__kitchen";
23
-
24
- export interface KitchenOptions {
14
+ import { FileAPI } from "./api/file-api";
15
+ import { GuardDecisionManager } from "./api/guard-decisions";
16
+ import { ContractPlaygroundAPI } from "./api/contract-api";
17
+ import { handleAgentContextRequest } from "./api/agent-devtools-api";
18
+ import { renderKitchenHTML } from "./kitchen-ui";
19
+ import { eventBus } from "../observability/event-bus";
20
+ import fs from "fs/promises";
21
+ import path from "path";
22
+
23
+ export const KITCHEN_PREFIX = "/__kitchen";
24
+
25
+ function reverseCopy<T>(items: readonly T[]): T[] {
26
+ const reversed: T[] = [];
27
+ for (let index = items.length - 1; index >= 0; index -= 1) {
28
+ reversed.push(items[index] as T);
29
+ }
30
+ return reversed;
31
+ }
32
+
33
+ export interface KitchenOptions {
25
34
  rootDir: string;
26
35
  manifest: RoutesManifest;
27
36
  guardConfig: GuardConfig | null;
@@ -74,9 +83,9 @@ export function recordRequest(entry: RequestEntry): void {
74
83
  if (recentRequests.length > MAX_REQUESTS) recentRequests.shift();
75
84
  }
76
85
 
77
- export function getRecentRequests(): RequestEntry[] {
78
- return [...recentRequests].reverse();
79
- }
86
+ export function getRecentRequests(): RequestEntry[] {
87
+ return reverseCopy(recentRequests);
88
+ }
80
89
 
81
90
  /** Parse a window string like "5m", "30s", "1h" into milliseconds. */
82
91
  function parseWindow(input: string): number {
@@ -220,10 +229,11 @@ export class KitchenHandler {
220
229
  this.contractAPI.updateManifest(manifest);
221
230
  }
222
231
 
223
- /** Update guard config when mandu.config.ts changes */
224
- updateGuardConfig(config: GuardConfig | null): void {
225
- this.guardAPI.updateConfig(config);
226
- }
232
+ /** Update guard config when mandu.config.ts changes */
233
+ updateGuardConfig(config: GuardConfig | null): void {
234
+ this.options.guardConfig = config;
235
+ this.guardAPI.updateConfig(config);
236
+ }
227
237
 
228
238
  /** Get the SSE broadcaster for external event injection */
229
239
  get broadcaster(): ActivitySSEBroadcaster {
@@ -322,11 +332,11 @@ export class KitchenHandler {
322
332
  // Requests API — recent HTTP events from eventBus (fallback: ring buffer)
323
333
  if (sub === "/api/requests" && req.method === "GET") {
324
334
  const url = new URL(req.url);
325
- const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
326
- const busEvents = eventBus.getRecent(limit, { type: "http" });
327
- if (busEvents.length > 0) {
328
- return Response.json({ requests: busEvents.slice().reverse() });
329
- }
335
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
336
+ const busEvents = eventBus.getRecent(limit, { type: "http" });
337
+ if (busEvents.length > 0) {
338
+ return Response.json({ requests: reverseCopy(busEvents) });
339
+ }
330
340
  return Response.json({ requests: getRecentRequests().slice(0, limit) });
331
341
  }
332
342
 
@@ -343,21 +353,36 @@ export class KitchenHandler {
343
353
  // Activity API — recent MCP events from eventBus (fallback: activity.jsonl)
344
354
  if (sub === "/api/activity" && req.method === "GET") {
345
355
  const url = new URL(req.url);
346
- const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
347
- const busEvents = eventBus.getRecent(limit, { type: "mcp" });
348
- if (busEvents.length > 0) {
349
- return Response.json({ events: busEvents.slice().reverse() });
350
- }
356
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
357
+ const busEvents = eventBus.getRecent(limit, { type: "mcp" });
358
+ if (busEvents.length > 0) {
359
+ return Response.json({ events: reverseCopy(busEvents) });
360
+ }
351
361
  const events = await this.readRecentActivity();
352
362
  return Response.json({ events });
353
363
  }
354
364
 
355
365
  // Agent Stats API — per-agent (sessionId) aggregation of MCP events
356
- if (sub === "/api/agent-stats" && req.method === "GET") {
357
- return Response.json(computeAgentStats());
358
- }
359
-
360
- // Cache API — cache store stats
366
+ if (sub === "/api/agent-stats" && req.method === "GET") {
367
+ return Response.json(computeAgentStats());
368
+ }
369
+
370
+ // Agent DevTools API — read-only context pack for supervised coding sessions
371
+ if (sub === "/api/agent-context" && req.method === "GET") {
372
+ return handleAgentContextRequest({
373
+ rootDir: this.options.rootDir,
374
+ manifest: this.manifest,
375
+ guardEnabled: !!this.options.guardConfig,
376
+ errors: getKitchenErrors(),
377
+ requests: getRecentRequests().slice(0, 100),
378
+ httpEvents: eventBus.getRecent(100, { type: "http" }),
379
+ mcpEvents: eventBus.getRecent(100, { type: "mcp" }),
380
+ guardEvents: eventBus.getRecent(100, { type: "guard" }),
381
+ agentStats: computeAgentStats(),
382
+ });
383
+ }
384
+
385
+ // Cache API — cache store stats
361
386
  if ((sub === "/api/cache" || sub === "/api/cache-stats") && req.method === "GET") {
362
387
  const store = getGlobalCache();
363
388
  return Response.json({
@@ -488,11 +513,11 @@ export class KitchenHandler {
488
513
  private async readRecentActivity(): Promise<unknown[]> {
489
514
  const logPath = path.join(this.options.rootDir, ".mandu", "activity.jsonl");
490
515
  try {
491
- const content = await fs.readFile(logPath, "utf-8");
492
- const lines = content.trim().split("\n").filter(Boolean);
493
- return lines.slice(-50).reverse().map((line) => {
494
- try { return JSON.parse(line); } catch { return null; }
495
- }).filter(Boolean);
516
+ const content = await fs.readFile(logPath, "utf-8");
517
+ const lines = content.trim().split("\n").filter(Boolean);
518
+ return reverseCopy(lines.slice(-50)).map((line) => {
519
+ try { return JSON.parse(line); } catch { return null; }
520
+ }).filter(Boolean);
496
521
  } catch { return []; }
497
522
  }
498
523
  }
@@ -57,12 +57,13 @@ export function renderKitchenHTML(): string {
57
57
  </div>
58
58
  </section>
59
59
 
60
- <nav class="tabs">
61
- <button class="tab" data-panel="activity">Activity</button>
62
- <button class="tab active" data-panel="routes">Routes</button>
63
- <button class="tab" data-panel="guard">Guard</button>
64
- <button class="tab" data-panel="preview">Preview</button>
65
- <button class="tab" data-panel="contracts">Contracts</button>
60
+ <nav class="tabs">
61
+ <button class="tab" data-panel="activity">Activity</button>
62
+ <button class="tab" data-panel="agent">Agent</button>
63
+ <button class="tab active" data-panel="routes">Routes</button>
64
+ <button class="tab" data-panel="guard">Guard</button>
65
+ <button class="tab" data-panel="preview">Preview</button>
66
+ <button class="tab" data-panel="contracts">Contracts</button>
66
67
  <button class="tab" data-panel="requests">Requests</button>
67
68
  <button class="tab" data-panel="mcp-activity">MCP</button>
68
69
  <button class="tab" data-panel="cache">Cache</button>
@@ -70,24 +71,37 @@ export function renderKitchenHTML(): string {
70
71
  </nav>
71
72
 
72
73
  <main class="panels">
73
- <section id="panel-activity" class="panel">
74
- <div class="panel-header">
75
- <div>
76
- <h2>Activity Stream</h2>
77
- <p class="panel-subtitle">Recent Kitchen events and MCP activity.</p>
74
+ <section id="panel-activity" class="panel">
75
+ <div class="panel-header">
76
+ <div>
77
+ <h2>Activity Stream</h2>
78
+ <p class="panel-subtitle">Recent Kitchen events and MCP activity.</p>
78
79
  </div>
79
80
  <button id="clear-activity" class="btn-sm">Clear</button>
80
81
  </div>
81
82
  <div id="activity-list" class="activity-list">
82
83
  <div class="empty-state">Waiting for MCP activity...</div>
83
- </div>
84
- </section>
85
-
86
- <section id="panel-routes" class="panel active">
87
- <div class="panel-header">
88
- <div>
89
- <h2>Routes</h2>
90
- <p class="panel-subtitle">Current filesystem routes, slots, contracts, and hydration hints.</p>
84
+ </div>
85
+ </section>
86
+
87
+ <section id="panel-agent" class="panel">
88
+ <div class="panel-header">
89
+ <div>
90
+ <h2>Agent Supervisor</h2>
91
+ <p class="panel-subtitle">Situation brief, tool routing, prompt pack, and next safe action for supervised agents.</p>
92
+ </div>
93
+ <button id="refresh-agent" class="btn-sm">Refresh</button>
94
+ </div>
95
+ <div id="agent-content" class="agent-content">
96
+ <div class="empty-state">Open Agent to build a context pack.</div>
97
+ </div>
98
+ </section>
99
+
100
+ <section id="panel-routes" class="panel active">
101
+ <div class="panel-header">
102
+ <div>
103
+ <h2>Routes</h2>
104
+ <p class="panel-subtitle">Current filesystem routes, slots, contracts, and hydration hints.</p>
91
105
  </div>
92
106
  <div id="routes-summary" class="summary"></div>
93
107
  </div>
@@ -1188,11 +1202,141 @@ const CSS = /* css */ `
1188
1202
  box-shadow: 0 12px 28px rgba(30, 42, 58, 0.22);
1189
1203
  }
1190
1204
 
1191
- .requests-list, .mcp-list, .cache-content, .metrics-content {
1192
- display: flex;
1193
- flex-direction: column;
1194
- gap: 6px;
1195
- }
1205
+ .requests-list, .mcp-list, .cache-content, .metrics-content, .agent-content {
1206
+ display: flex;
1207
+ flex-direction: column;
1208
+ gap: 6px;
1209
+ }
1210
+
1211
+ .agent-grid {
1212
+ display: grid;
1213
+ grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr);
1214
+ gap: 14px;
1215
+ }
1216
+
1217
+ .agent-stack {
1218
+ display: flex;
1219
+ flex-direction: column;
1220
+ gap: 12px;
1221
+ }
1222
+
1223
+ .agent-card {
1224
+ padding: 14px;
1225
+ border: 1px solid var(--line);
1226
+ border-radius: 8px;
1227
+ background: rgba(255, 255, 255, 0.72);
1228
+ }
1229
+
1230
+ .agent-card-title {
1231
+ display: flex;
1232
+ align-items: center;
1233
+ justify-content: space-between;
1234
+ gap: 10px;
1235
+ margin-bottom: 8px;
1236
+ color: var(--ink);
1237
+ font-size: 14px;
1238
+ font-weight: 700;
1239
+ }
1240
+
1241
+ .agent-card-body {
1242
+ color: var(--muted);
1243
+ font-size: 13px;
1244
+ line-height: 1.55;
1245
+ }
1246
+
1247
+ .agent-pill-row {
1248
+ display: flex;
1249
+ flex-wrap: wrap;
1250
+ gap: 6px;
1251
+ margin-top: 10px;
1252
+ }
1253
+
1254
+ .agent-pill {
1255
+ padding: 4px 8px;
1256
+ border-radius: 6px;
1257
+ background: rgba(30, 42, 58, 0.08);
1258
+ color: var(--ink);
1259
+ font-family: "IBM Plex Mono", "Cascadia Code", monospace;
1260
+ font-size: 11px;
1261
+ }
1262
+
1263
+ .agent-severity {
1264
+ padding: 3px 7px;
1265
+ border-radius: 6px;
1266
+ font-size: 11px;
1267
+ font-weight: 700;
1268
+ text-transform: uppercase;
1269
+ }
1270
+
1271
+ .agent-severity.info { background: rgba(46, 102, 184, 0.12); color: var(--info); }
1272
+ .agent-severity.warn { background: rgba(173, 122, 18, 0.14); color: var(--warning); }
1273
+ .agent-severity.error { background: rgba(188, 61, 61, 0.12); color: var(--danger); }
1274
+
1275
+ .agent-stat-grid {
1276
+ display: grid;
1277
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1278
+ gap: 8px;
1279
+ margin-top: 12px;
1280
+ }
1281
+
1282
+ .agent-stat {
1283
+ padding: 10px;
1284
+ border-radius: 8px;
1285
+ border: 1px solid rgba(220, 207, 186, 0.7);
1286
+ background: rgba(255, 253, 250, 0.84);
1287
+ }
1288
+
1289
+ .agent-stat-label {
1290
+ color: var(--muted);
1291
+ font-size: 11px;
1292
+ }
1293
+
1294
+ .agent-stat-value {
1295
+ color: var(--ink);
1296
+ font-size: 19px;
1297
+ font-weight: 700;
1298
+ margin-top: 3px;
1299
+ }
1300
+
1301
+ .agent-rec {
1302
+ display: grid;
1303
+ grid-template-columns: 150px 1fr;
1304
+ gap: 10px;
1305
+ padding: 10px 0;
1306
+ border-top: 1px solid rgba(220, 207, 186, 0.65);
1307
+ }
1308
+
1309
+ .agent-rec:first-child {
1310
+ border-top: 0;
1311
+ padding-top: 0;
1312
+ }
1313
+
1314
+ .agent-rec-title {
1315
+ color: var(--ink);
1316
+ font-weight: 700;
1317
+ font-size: 13px;
1318
+ }
1319
+
1320
+ .agent-rec-detail {
1321
+ color: var(--muted);
1322
+ font-size: 12px;
1323
+ line-height: 1.5;
1324
+ }
1325
+
1326
+ .agent-prompt {
1327
+ width: 100%;
1328
+ min-height: 220px;
1329
+ margin-top: 10px;
1330
+ padding: 12px;
1331
+ resize: vertical;
1332
+ border-radius: 8px;
1333
+ border: 1px solid var(--line);
1334
+ background: #fffdfa;
1335
+ color: var(--ink);
1336
+ font-family: "IBM Plex Mono", "Cascadia Code", monospace;
1337
+ font-size: 12px;
1338
+ line-height: 1.45;
1339
+ }
1196
1340
 
1197
1341
  .req-row {
1198
1342
  display: grid;
@@ -1313,13 +1457,17 @@ const CSS = /* css */ `
1313
1457
  grid-template-columns: repeat(2, minmax(0, 1fr));
1314
1458
  }
1315
1459
 
1316
- .contracts-layout {
1317
- grid-template-columns: 1fr;
1318
- }
1319
-
1320
- .guard-summary {
1321
- grid-template-columns: repeat(2, minmax(0, 1fr));
1322
- }
1460
+ .contracts-layout {
1461
+ grid-template-columns: 1fr;
1462
+ }
1463
+
1464
+ .agent-grid {
1465
+ grid-template-columns: 1fr;
1466
+ }
1467
+
1468
+ .guard-summary {
1469
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1470
+ }
1323
1471
  }
1324
1472
 
1325
1473
  @media (max-width: 720px) {
@@ -1345,12 +1493,20 @@ const CSS = /* css */ `
1345
1493
  width: calc(50% - 4px);
1346
1494
  justify-content: center;
1347
1495
  }
1348
-
1349
- .panel-header {
1350
- flex-direction: column;
1351
- }
1352
- }
1353
- `;
1496
+
1497
+ .panel-header {
1498
+ flex-direction: column;
1499
+ }
1500
+
1501
+ .agent-stat-grid {
1502
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1503
+ }
1504
+
1505
+ .agent-rec {
1506
+ grid-template-columns: 1fr;
1507
+ }
1508
+ }
1509
+ `;
1354
1510
 
1355
1511
  // ─── JavaScript ──────────────────────────────────
1356
1512
 
@@ -1367,12 +1523,12 @@ const JS = /* js */ `
1367
1523
  console.log('[Kitchen]', msg);
1368
1524
  }
1369
1525
 
1370
- function escapeHtml(str) {
1371
- if (!str) return '';
1372
- var d = document.createElement('div');
1373
- d.appendChild(document.createTextNode(String(str)));
1374
- return d.innerHTML;
1375
- }
1526
+ function escapeHtml(str) {
1527
+ if (str === null || str === undefined) return '';
1528
+ var d = document.createElement('div');
1529
+ d.appendChild(document.createTextNode(String(str)));
1530
+ return d.innerHTML;
1531
+ }
1376
1532
 
1377
1533
  function setMetric(id, value) {
1378
1534
  var el = document.getElementById(id);
@@ -1893,9 +2049,10 @@ const JS = /* js */ `
1893
2049
 
1894
2050
  loadContracts();
1895
2051
 
1896
- // ─── Requests Tab ─────────────────────────────
1897
- var requestsListEl = document.getElementById('requests-list');
1898
- var requestsDetailEl = document.getElementById('requests-detail');
2052
+ // ─── Requests Tab ─────────────────────────────
2053
+ var requestsListEl = document.getElementById('requests-list');
2054
+ var requestsDetailEl = document.getElementById('requests-detail');
2055
+ var agentContentEl = document.getElementById('agent-content');
1899
2056
 
1900
2057
  function fetchJson(url, cb) {
1901
2058
  var xhr = new XMLHttpRequest();
@@ -1910,16 +2067,142 @@ const JS = /* js */ `
1910
2067
  xhr.send();
1911
2068
  }
1912
2069
 
1913
- function statusClass(s) {
1914
- if (!s) return '';
1915
- if (s >= 500) return 's5';
1916
- if (s >= 400) return 's4';
1917
- if (s >= 300) return 's3';
1918
- return 's2';
1919
- }
1920
-
1921
- function loadRequests() {
1922
- requestsListEl.innerHTML = '<div class="empty-state">Loading requests...</div>';
2070
+ function statusClass(s) {
2071
+ if (!s) return '';
2072
+ if (s >= 500) return 's5';
2073
+ if (s >= 400) return 's4';
2074
+ if (s >= 300) return 's3';
2075
+ return 's2';
2076
+ }
2077
+
2078
+ // ─── Agent Supervisor Tab ─────────────────────
2079
+ function renderAgentPills(items) {
2080
+ if (!items || !items.length) return '';
2081
+ var html = '<div class="agent-pill-row">';
2082
+ for (var i = 0; i < items.length; i++) {
2083
+ html += '<span class="agent-pill">' + escapeHtml(items[i]) + '</span>';
2084
+ }
2085
+ return html + '</div>';
2086
+ }
2087
+
2088
+ function renderAgentDetails(items) {
2089
+ if (!items || !items.length) return '';
2090
+ var html = '<ul>';
2091
+ for (var i = 0; i < items.length; i++) {
2092
+ html += '<li>' + escapeHtml(items[i]) + '</li>';
2093
+ }
2094
+ return html + '</ul>';
2095
+ }
2096
+
2097
+ function renderAgentContext(data) {
2098
+ var situation = data.situation || {};
2099
+ var summary = data.summary || {};
2100
+ var routes = summary.routes || {};
2101
+ var status = data.agentStatus || {};
2102
+ var brain = status.brain || {};
2103
+ var action = data.nextSafeAction || {};
2104
+ var prompt = data.prompt || {};
2105
+ var recs = data.toolRecommendations || [];
2106
+ var cards = data.knowledgeCards || [];
2107
+ var promptText = prompt.copyText || '';
2108
+
2109
+ var html = '<div class="agent-grid">' +
2110
+ '<div class="agent-stack">' +
2111
+ '<div class="agent-card">' +
2112
+ '<div class="agent-card-title">' +
2113
+ '<span>' + escapeHtml(situation.title || 'No situation') + '</span>' +
2114
+ '<span class="agent-severity ' + escapeHtml(situation.severity || 'info') + '">' + escapeHtml(situation.category || 'agent') + '</span>' +
2115
+ '</div>' +
2116
+ '<div class="agent-card-body">' + renderAgentDetails(situation.details || []) + '</div>' +
2117
+ '<div class="agent-stat-grid">' +
2118
+ '<div class="agent-stat"><div class="agent-stat-label">Routes</div><div class="agent-stat-value">' + escapeHtml(routes.total || 0) + '</div></div>' +
2119
+ '<div class="agent-stat"><div class="agent-stat-label">Islands</div><div class="agent-stat-value">' + escapeHtml(routes.islands || 0) + '</div></div>' +
2120
+ '<div class="agent-stat"><div class="agent-stat-label">Contracts</div><div class="agent-stat-value">' + escapeHtml(routes.contracts || 0) + '</div></div>' +
2121
+ '<div class="agent-stat"><div class="agent-stat-label">MCP Calls</div><div class="agent-stat-value">' + escapeHtml(status.observedToolCalls || 0) + '</div></div>' +
2122
+ '</div>' +
2123
+ '</div>' +
2124
+ '<div class="agent-card">' +
2125
+ '<div class="agent-card-title"><span>Next Safe Action</span><span class="agent-severity info">' + escapeHtml(action.mode || 'observe') + '</span></div>' +
2126
+ '<div class="agent-card-body"><strong>' + escapeHtml(action.title || '-') + '</strong><br>' + escapeHtml(action.reason || '') + '</div>' +
2127
+ renderAgentPills([action.tool || '', action.command || ''].filter(Boolean)) +
2128
+ renderAgentPills(action.validation || []) +
2129
+ '</div>' +
2130
+ '<div class="agent-card">' +
2131
+ '<div class="agent-card-title"><span>Prompt Pack</span><button id="copy-agent-prompt" class="btn-sm">Copy</button></div>' +
2132
+ '<textarea id="agent-prompt-text" class="agent-prompt" readonly>' + escapeHtml(promptText) + '</textarea>' +
2133
+ '</div>' +
2134
+ '</div>' +
2135
+ '<div class="agent-stack">' +
2136
+ '<div class="agent-card">' +
2137
+ '<div class="agent-card-title"><span>Brain</span><span class="agent-severity info">' + escapeHtml(brain.oauth || 'unknown') + '</span></div>' +
2138
+ '<div class="agent-card-body">' + escapeHtml(brain.note || '') + '</div>' +
2139
+ renderAgentPills([brain.statusTool || 'mandu.brain.status']) +
2140
+ '</div>' +
2141
+ '<div class="agent-card">' +
2142
+ '<div class="agent-card-title"><span>Tool Router</span></div>';
2143
+
2144
+ for (var i = 0; i < recs.length; i++) {
2145
+ var r = recs[i];
2146
+ html += '<div class="agent-rec">' +
2147
+ '<div class="agent-rec-title">' + escapeHtml(r.skill) + '</div>' +
2148
+ '<div class="agent-rec-detail">' +
2149
+ '<strong>' + escapeHtml(r.task) + '</strong><br>' +
2150
+ escapeHtml(r.useWhen) +
2151
+ renderAgentPills(r.mcpTools || []) +
2152
+ '<div style="margin-top:6px;">Fallback: <code>' + escapeHtml(r.cliFallback) + '</code></div>' +
2153
+ '</div>' +
2154
+ '</div>';
2155
+ }
2156
+
2157
+ html += '</div><div class="agent-card">' +
2158
+ '<div class="agent-card-title"><span>Knowledge</span></div>';
2159
+
2160
+ for (var c = 0; c < cards.length; c++) {
2161
+ var card = cards[c];
2162
+ html += '<div class="agent-rec">' +
2163
+ '<div class="agent-rec-title">' + escapeHtml(card.title) + '</div>' +
2164
+ '<div class="agent-rec-detail">' + escapeHtml(card.body) + renderAgentPills(card.references || []) + '</div>' +
2165
+ '</div>';
2166
+ }
2167
+
2168
+ html += '</div></div></div>';
2169
+ agentContentEl.innerHTML = html;
2170
+
2171
+ var copyBtn = document.getElementById('copy-agent-prompt');
2172
+ if (copyBtn) {
2173
+ copyBtn.addEventListener('click', function() {
2174
+ var textEl = document.getElementById('agent-prompt-text');
2175
+ var text = textEl ? textEl.value : promptText;
2176
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2177
+ navigator.clipboard.writeText(text).then(function() {
2178
+ copyBtn.textContent = 'Copied';
2179
+ setTimeout(function() { copyBtn.textContent = 'Copy'; }, 900);
2180
+ }).catch(function() {
2181
+ textEl && textEl.select();
2182
+ });
2183
+ } else if (textEl) {
2184
+ textEl.select();
2185
+ }
2186
+ });
2187
+ }
2188
+ }
2189
+
2190
+ function loadAgentContext() {
2191
+ if (!agentContentEl) return;
2192
+ agentContentEl.innerHTML = '<div class="empty-state">Building agent context...</div>';
2193
+ fetchJson('/__kitchen/api/agent-context', function(err, data) {
2194
+ if (err) {
2195
+ agentContentEl.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
2196
+ return;
2197
+ }
2198
+ renderAgentContext(data);
2199
+ });
2200
+ }
2201
+
2202
+ document.getElementById('refresh-agent').addEventListener('click', loadAgentContext);
2203
+
2204
+ function loadRequests() {
2205
+ requestsListEl.innerHTML = '<div class="empty-state">Loading requests...</div>';
1923
2206
  fetchJson('/__kitchen/api/requests?limit=100', function(err, data) {
1924
2207
  if (err) {
1925
2208
  requestsListEl.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
@@ -2116,12 +2399,13 @@ const JS = /* js */ `
2116
2399
  document.getElementById('refresh-metrics').addEventListener('click', loadMetrics);
2117
2400
 
2118
2401
  // Lazy-load new tab data when clicked (avoids loading everything up front)
2119
- var tabLoaders = {
2120
- 'requests': loadRequests,
2121
- 'mcp-activity': loadMcpActivity,
2122
- 'cache': loadCacheStats,
2123
- 'metrics': loadMetrics
2124
- };
2402
+ var tabLoaders = {
2403
+ 'agent': loadAgentContext,
2404
+ 'requests': loadRequests,
2405
+ 'mcp-activity': loadMcpActivity,
2406
+ 'cache': loadCacheStats,
2407
+ 'metrics': loadMetrics
2408
+ };
2125
2409
  var loadedTabs = {};
2126
2410
  for (var t = 0; t < tabs.length; t++) {
2127
2411
  tabs[t].addEventListener('click', function() {
@@ -12,7 +12,6 @@ import {
12
12
  type LockfileError,
13
13
  type LockfileWarning,
14
14
  type LockfileMode,
15
- type LockfilePolicyOptions,
16
15
  DEFAULT_POLICIES,
17
16
  LOCKFILE_SCHEMA_VERSION,
18
17
  BYPASS_ENV_VAR,
@@ -1,3 +1,4 @@
1
+ import type * as __ManduNodeCryptoTypes0 from "node:crypto";
1
2
  /**
2
3
  * Mandu OpenAPI Generator
3
4
  * Contract에서 OpenAPI 3.0 스펙 자동 생성
@@ -893,7 +894,7 @@ function sha256Hex(input: string): string {
893
894
  // path (it would turn `hoistSharedSchemas` into an unnecessarily async
894
895
  // cascade on the hot path).
895
896
  // eslint-disable-next-line @typescript-eslint/no-require-imports
896
- const nodeCrypto = require("node:crypto") as typeof import("node:crypto");
897
+ const nodeCrypto = require("node:crypto") as typeof __ManduNodeCryptoTypes0;
897
898
  return nodeCrypto.createHash("sha256").update(input).digest("hex");
898
899
  }
899
900