@mandujs/core 0.53.3 → 0.54.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 (336) hide show
  1. package/README.md +654 -654
  2. package/package.json +2 -2
  3. package/src/a11y/__tests__/run-audit.test.ts +333 -333
  4. package/src/a11y/fix-hints.ts +76 -76
  5. package/src/a11y/index.ts +18 -18
  6. package/src/a11y/run-audit.ts +394 -394
  7. package/src/a11y/types.ts +125 -125
  8. package/src/auth/__tests__/login.test.ts +1 -1
  9. package/src/auth/__tests__/password.test.ts +122 -122
  10. package/src/auth/__tests__/tokens.test.ts +274 -274
  11. package/src/auth/__tests__/verification.test.ts +274 -274
  12. package/src/auth/index.ts +76 -76
  13. package/src/auth/login.ts +225 -225
  14. package/src/auth/password.ts +120 -120
  15. package/src/auth/reset.ts +243 -243
  16. package/src/auth/tokens.ts +612 -612
  17. package/src/auth/verification.ts +253 -253
  18. package/src/brain/__tests__/redactor.test.ts +94 -94
  19. package/src/brain/adapters/__tests__/_helpers.ts +83 -83
  20. package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
  21. package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
  22. package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
  23. package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
  24. package/src/brain/adapters/anthropic-oauth.ts +1 -1
  25. package/src/brain/adapters/chatgpt-auth.ts +300 -300
  26. package/src/brain/adapters/index.ts +319 -319
  27. package/src/brain/adapters/oauth-flow.ts +439 -439
  28. package/src/brain/consent.ts +240 -240
  29. package/src/brain/credentials.ts +396 -396
  30. package/src/bundler/__tests__/build-runner.ts +113 -113
  31. package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
  32. package/src/bundler/__tests__/extended-watch.test.ts +711 -711
  33. package/src/bundler/__tests__/fast-refresh.test.ts +10 -10
  34. package/src/bundler/__tests__/hdr.test.ts +353 -353
  35. package/src/bundler/__tests__/hmr-client.test.ts +532 -532
  36. package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
  37. package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
  38. package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
  39. package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
  40. package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
  41. package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
  42. package/src/bundler/budget.ts +404 -404
  43. package/src/bundler/build.test.ts +179 -179
  44. package/src/bundler/build.ts +55 -55
  45. package/src/bundler/dev.ts +42 -42
  46. package/src/bundler/manifest-schema.ts +301 -301
  47. package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
  48. package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
  49. package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
  50. package/src/bundler/plugins/block-generated-imports.ts +155 -155
  51. package/src/bundler/plugins/index.ts +83 -83
  52. package/src/bundler/plugins/react-compiler-config.ts +108 -108
  53. package/src/bundler/plugins/react-compiler-lint.ts +253 -253
  54. package/src/bundler/plugins/react-compiler.ts +162 -162
  55. package/src/bundler/reverse-import-graph.ts +339 -339
  56. package/src/bundler/safe-build.test.ts +201 -201
  57. package/src/bundler/safe-build.ts +103 -103
  58. package/src/bundler/scenario-matrix.ts +229 -229
  59. package/src/bundler/types.ts +10 -10
  60. package/src/bundler/vendor-cache-types.ts +130 -130
  61. package/src/bundler/vendor-cache.ts +526 -526
  62. package/src/change/snapshot.ts +18 -18
  63. package/src/client/Form.tsx +105 -105
  64. package/src/client/Link.tsx +9 -9
  65. package/src/client/__tests__/use-sse.test.ts +153 -153
  66. package/src/client/globals.ts +1 -1
  67. package/src/client/hooks.ts +362 -362
  68. package/src/client/hydrate.ts +340 -340
  69. package/src/client/prefetch-helper.ts +55 -55
  70. package/src/client/router.ts +11 -11
  71. package/src/client/runtime.ts +47 -47
  72. package/src/client/serialize.ts +404 -404
  73. package/src/client/use-fetch.ts +6 -6
  74. package/src/client/use-head.ts +197 -197
  75. package/src/client/use-sse.ts +378 -378
  76. package/src/client/window-state.ts +101 -101
  77. package/src/components/Image.tsx +162 -162
  78. package/src/config/validate.ts +3 -3
  79. package/src/config/watcher.ts +311 -311
  80. package/src/constants.ts +40 -40
  81. package/src/content/collection.ts +8 -8
  82. package/src/content/content-layer.ts +7 -7
  83. package/src/content/data-store.ts +245 -245
  84. package/src/content/frontmatter.ts +189 -189
  85. package/src/content/loader-context.ts +171 -171
  86. package/src/content/loaders/api.ts +216 -216
  87. package/src/content/loaders/file.ts +172 -172
  88. package/src/content/loaders/glob.ts +253 -253
  89. package/src/content/loaders/index.ts +34 -34
  90. package/src/content/loaders/types.ts +137 -137
  91. package/src/content/meta-store.ts +209 -209
  92. package/src/content/prebuild.test.ts +571 -571
  93. package/src/content/prebuild.ts +636 -636
  94. package/src/content/schema.ts +20 -20
  95. package/src/content/sidebar.ts +630 -630
  96. package/src/content/slug.ts +110 -110
  97. package/src/content/types.ts +282 -282
  98. package/src/content/watcher.ts +135 -135
  99. package/src/contract/client-safe.test.ts +42 -42
  100. package/src/contract/client-safe.ts +114 -114
  101. package/src/contract/define.ts +11 -11
  102. package/src/contract/index.ts +1 -1
  103. package/src/contract/normalize.test.ts +276 -276
  104. package/src/contract/normalize.ts +410 -410
  105. package/src/contract/registry.test.ts +206 -206
  106. package/src/contract/route-helpers.ts +1 -1
  107. package/src/contract/rpc.ts +443 -443
  108. package/src/contract/schema.ts +48 -48
  109. package/src/contract/types.ts +58 -58
  110. package/src/db/__tests__/db.test.ts +482 -482
  111. package/src/db/index.ts +138 -138
  112. package/src/db/migrations/history-table.ts +345 -345
  113. package/src/db/migrations/index.ts +3 -3
  114. package/src/db/migrations/lock.ts +324 -324
  115. package/src/db/migrations/runner.ts +650 -650
  116. package/src/deploy/cache.ts +140 -140
  117. package/src/deploy/compile/vercel.ts +344 -344
  118. package/src/deploy/index.ts +87 -87
  119. package/src/deploy/inference/brain.ts +268 -268
  120. package/src/deploy/inference/context.ts +182 -182
  121. package/src/deploy/inference/filling-extract.ts +245 -245
  122. package/src/deploy/inference/heuristic.ts +182 -182
  123. package/src/deploy/intent.ts +173 -173
  124. package/src/deploy/plan.ts +178 -178
  125. package/src/design/__tests__/agents-link.test.ts +109 -109
  126. package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
  127. package/src/design/__tests__/lint.test.ts +110 -110
  128. package/src/design/__tests__/parser.test.ts +195 -195
  129. package/src/design/__tests__/tailwind-theme.test.ts +229 -229
  130. package/src/design/agents-link.ts +165 -165
  131. package/src/design/diff.ts +138 -138
  132. package/src/design/extract.ts +285 -285
  133. package/src/design/index.ts +102 -102
  134. package/src/design/lint.ts +209 -209
  135. package/src/design/parser.ts +555 -555
  136. package/src/design/patch.ts +241 -241
  137. package/src/design/scaffold.ts +147 -147
  138. package/src/design/tailwind-theme.ts +441 -441
  139. package/src/design/types.ts +210 -210
  140. package/src/desktop/__tests__/webview-fallback.test.ts +254 -254
  141. package/src/desktop/__tests__/window.test.ts +248 -248
  142. package/src/desktop/__tests__/worker.test.ts +266 -266
  143. package/src/desktop/index.ts +43 -43
  144. package/src/desktop/types.ts +158 -158
  145. package/src/desktop/worker.ts +180 -180
  146. package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
  147. package/src/dev-error-overlay/index.ts +30 -30
  148. package/src/dev-error-overlay/overlay-injector.ts +243 -243
  149. package/src/dev-error-overlay/overlay-styles.ts +52 -52
  150. package/src/dev-error-overlay/types.ts +66 -66
  151. package/src/devtools/ai/context-builder.ts +375 -375
  152. package/src/devtools/ai/index.ts +25 -25
  153. package/src/devtools/ai/mcp-connector.ts +25 -25
  154. package/src/devtools/client/catchers/error-catcher.ts +344 -344
  155. package/src/devtools/client/catchers/index.ts +18 -18
  156. package/src/devtools/client/components/index.ts +39 -39
  157. package/src/devtools/client/components/mandu-character.tsx +331 -331
  158. package/src/devtools/client/components/overlay.tsx +368 -368
  159. package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
  160. package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
  161. package/src/devtools/client/components/panel/islands-panel.tsx +320 -320
  162. package/src/devtools/client/components/panel/network-panel.tsx +291 -291
  163. package/src/devtools/client/components/panel/panel-container.tsx +500 -500
  164. package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
  165. package/src/devtools/client/filters/context-filters.ts +282 -282
  166. package/src/devtools/client/filters/index.ts +16 -16
  167. package/src/devtools/client/index.ts +63 -63
  168. package/src/devtools/client/persistence.ts +335 -335
  169. package/src/devtools/hook/create-hook.ts +207 -207
  170. package/src/devtools/hook/index.ts +13 -13
  171. package/src/devtools/index.ts +439 -439
  172. package/src/devtools/init.ts +265 -265
  173. package/src/devtools/protocol.ts +237 -237
  174. package/src/devtools/server/index.ts +17 -17
  175. package/src/devtools/server/source-context.ts +450 -450
  176. package/src/devtools/types.ts +35 -35
  177. package/src/devtools/worker/index.ts +25 -25
  178. package/src/devtools/worker/redaction-worker.ts +233 -233
  179. package/src/devtools/worker/worker-manager.ts +410 -410
  180. package/src/diagnose/__tests__/checks.test.ts +451 -451
  181. package/src/diagnose/checks.ts +832 -832
  182. package/src/diagnose/index.ts +17 -17
  183. package/src/diagnose/run.ts +93 -93
  184. package/src/diagnose/types.ts +53 -53
  185. package/src/email/__tests__/email.test.ts +355 -355
  186. package/src/email/index.ts +282 -282
  187. package/src/email/smtp.ts +64 -64
  188. package/src/error/domains.ts +265 -265
  189. package/src/error/result.ts +60 -60
  190. package/src/error/types.ts +6 -6
  191. package/src/errors/extractor.ts +409 -409
  192. package/src/errors/index.ts +19 -19
  193. package/src/filling/__tests__/session-sqlite.test.ts +454 -454
  194. package/src/filling/auth.ts +308 -308
  195. package/src/filling/cookie-codec.ts +299 -299
  196. package/src/filling/deps.ts +265 -265
  197. package/src/filling/session-sqlite.ts +617 -617
  198. package/src/filling/sse.ts +5 -5
  199. package/src/filling/ws.ts +78 -78
  200. package/src/generator/index.ts +3 -3
  201. package/src/guard/__tests__/design-inline-class.test.ts +219 -219
  202. package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
  203. package/src/guard/analyzer.ts +360 -360
  204. package/src/guard/auto-correct.ts +1 -1
  205. package/src/guard/contract-guard.ts +9 -9
  206. package/src/guard/define-rule.ts +243 -243
  207. package/src/guard/design-inline-class.ts +393 -393
  208. package/src/guard/file-type.test.ts +24 -24
  209. package/src/guard/graph.ts +1 -1
  210. package/src/guard/healing.ts +36 -36
  211. package/src/guard/presets/atomic.ts +70 -70
  212. package/src/guard/presets/clean.ts +77 -77
  213. package/src/guard/presets/fsd.ts +79 -79
  214. package/src/guard/presets/hexagonal.ts +68 -68
  215. package/src/guard/reporter.ts +442 -442
  216. package/src/guard/rule-presets.ts +379 -379
  217. package/src/guard/semantic-slots.ts +1 -1
  218. package/src/guard/suggestions.ts +358 -358
  219. package/src/guard/tsgolint-bridge.ts +512 -512
  220. package/src/guard/types.ts +348 -348
  221. package/src/guard/watcher.ts +405 -405
  222. package/src/i18n/define.ts +126 -126
  223. package/src/i18n/index.ts +52 -52
  224. package/src/i18n/message-registry.ts +173 -173
  225. package/src/i18n/types.ts +112 -112
  226. package/src/id/index.ts +105 -105
  227. package/src/index.ts +2 -2
  228. package/src/kitchen/api/agent-devtools-api.ts +779 -544
  229. package/src/kitchen/api/errors-grouping.ts +126 -0
  230. package/src/kitchen/kitchen-handler.ts +192 -63
  231. package/src/kitchen/kitchen-ui.ts +842 -464
  232. package/src/logging/index.ts +22 -22
  233. package/src/logging/transports.ts +365 -365
  234. package/src/middleware/bridge.ts +147 -147
  235. package/src/middleware/compose.ts +134 -134
  236. package/src/middleware/compress.ts +62 -62
  237. package/src/middleware/cors.ts +47 -47
  238. package/src/middleware/csrf.ts +328 -328
  239. package/src/middleware/define.ts +132 -132
  240. package/src/middleware/jwt.ts +134 -134
  241. package/src/middleware/logger.ts +58 -58
  242. package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
  243. package/src/middleware/oauth/index.ts +505 -505
  244. package/src/middleware/oauth/providers.ts +115 -115
  245. package/src/middleware/rate-limit/index.ts +522 -522
  246. package/src/middleware/rate-limit/sqlite-store.ts +382 -382
  247. package/src/middleware/scheduler-cron.ts +96 -96
  248. package/src/middleware/secure/__tests__/secure.test.ts +360 -360
  249. package/src/middleware/secure/csp.ts +193 -193
  250. package/src/middleware/session.ts +174 -174
  251. package/src/middleware/timeout.ts +55 -55
  252. package/src/observability/logger-adapter.ts +36 -36
  253. package/src/observability/sqlite-store.ts +254 -254
  254. package/src/openapi/generator.ts +1 -1
  255. package/src/openapi/openapi.test.ts +43 -43
  256. package/src/perf/__tests__/user-marks.test.ts +354 -354
  257. package/src/perf/index.ts +133 -133
  258. package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
  259. package/src/plugins/__tests__/runner.test.ts +409 -409
  260. package/src/plugins/define.ts +124 -124
  261. package/src/plugins/examples/dep-check-plugin.ts +80 -80
  262. package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
  263. package/src/plugins/examples/sitemap-plugin.ts +65 -65
  264. package/src/plugins/runner.ts +361 -361
  265. package/src/plugins/types.ts +368 -368
  266. package/src/report/index.ts +1 -1
  267. package/src/resource/__tests__/generator.test.ts +32 -32
  268. package/src/resource/ddl/__tests__/diff.test.ts +639 -639
  269. package/src/resource/ddl/__tests__/emit.test.ts +823 -823
  270. package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
  271. package/src/resource/ddl/emit.ts +559 -559
  272. package/src/resource/ddl/persistence-types.ts +218 -218
  273. package/src/resource/ddl/type-map.ts +223 -223
  274. package/src/resource/ddl/types.ts +232 -232
  275. package/src/resource/generator-repo.ts +630 -630
  276. package/src/resource/schema.ts +1 -1
  277. package/src/router/fs-patterns.test.ts +96 -96
  278. package/src/router/fs-routes.ts +6 -6
  279. package/src/routes/index.ts +74 -74
  280. package/src/routes/metadata-routes.ts +427 -427
  281. package/src/routes/types.ts +341 -341
  282. package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
  283. package/src/runtime/__tests__/hdr-client.test.ts +223 -223
  284. package/src/runtime/__tests__/http-errors.test.ts +117 -117
  285. package/src/runtime/__tests__/not-found.test.ts +152 -152
  286. package/src/runtime/adapter.ts +47 -47
  287. package/src/runtime/boundary.tsx +252 -252
  288. package/src/runtime/cache.ts +494 -494
  289. package/src/runtime/compose.ts +222 -222
  290. package/src/runtime/fast-refresh-runtime.ts +322 -322
  291. package/src/runtime/handler.ts +65 -65
  292. package/src/runtime/handlers.ts +300 -300
  293. package/src/runtime/http-errors.ts +113 -113
  294. package/src/runtime/image-handler.ts +1 -1
  295. package/src/runtime/lifecycle.ts +381 -381
  296. package/src/runtime/logger.test.ts +345 -345
  297. package/src/runtime/middleware.ts +264 -264
  298. package/src/runtime/not-found.ts +93 -93
  299. package/src/runtime/openapi-endpoint.ts +236 -236
  300. package/src/runtime/ppr.ts +74 -74
  301. package/src/runtime/registry.ts +171 -171
  302. package/src/runtime/router.ts +105 -105
  303. package/src/runtime/server.ts +67 -67
  304. package/src/runtime/shims.ts +48 -48
  305. package/src/runtime/trace.ts +144 -144
  306. package/src/scheduler/validate.ts +169 -169
  307. package/src/seo/index.ts +219 -219
  308. package/src/seo/integration/ssr.ts +306 -306
  309. package/src/seo/render/basic.ts +435 -435
  310. package/src/seo/render/index.ts +143 -143
  311. package/src/seo/render/jsonld.ts +539 -539
  312. package/src/seo/render/opengraph.ts +197 -197
  313. package/src/seo/render/robots.ts +116 -116
  314. package/src/seo/render/sitemap.ts +137 -137
  315. package/src/seo/render/twitter.ts +127 -127
  316. package/src/seo/resolve/opengraph.ts +143 -143
  317. package/src/seo/resolve/robots.ts +73 -73
  318. package/src/seo/resolve/title.ts +94 -94
  319. package/src/seo/resolve/twitter.ts +73 -73
  320. package/src/seo/resolve/url.ts +104 -104
  321. package/src/seo/routes/index.ts +290 -290
  322. package/src/seo/types.ts +588 -588
  323. package/src/slot/validator.ts +39 -39
  324. package/src/storage/s3/__tests__/s3.test.ts +479 -479
  325. package/src/storage/s3/index.ts +412 -412
  326. package/src/testing/__tests__/assertions.test.ts +632 -632
  327. package/src/testing/__tests__/reporter.test.ts +454 -454
  328. package/src/testing/assertions.ts +986 -986
  329. package/src/testing/db.ts +157 -157
  330. package/src/testing/mocks.ts +203 -203
  331. package/src/testing/session.ts +190 -190
  332. package/src/types/branded.ts +56 -56
  333. package/src/types/index.ts +1 -1
  334. package/src/utils/safe-io.ts +188 -188
  335. package/src/utils/string-safe.ts +298 -298
  336. package/src/watcher/watcher.ts +18 -18
@@ -57,13 +57,14 @@ 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" 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>
60
+ <nav class="tabs">
61
+ <button class="tab" data-panel="activity">Activity</button>
62
+ <button class="tab" data-panel="errors">Errors</button>
63
+ <button class="tab" data-panel="agent">Agent</button>
64
+ <button class="tab active" data-panel="routes">Routes</button>
65
+ <button class="tab" data-panel="guard">Guard</button>
66
+ <button class="tab" data-panel="preview">Preview</button>
67
+ <button class="tab" data-panel="contracts">Contracts</button>
67
68
  <button class="tab" data-panel="requests">Requests</button>
68
69
  <button class="tab" data-panel="mcp-activity">MCP</button>
69
70
  <button class="tab" data-panel="cache">Cache</button>
@@ -71,37 +72,67 @@ export function renderKitchenHTML(): string {
71
72
  </nav>
72
73
 
73
74
  <main class="panels">
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>
75
+ <section id="panel-activity" class="panel">
76
+ <div class="panel-header">
77
+ <div>
78
+ <h2>Activity Stream</h2>
79
+ <p class="panel-subtitle">Recent eventBus signals — switch the chips to surface build, cache, ws, and ate categories that are normally invisible.</p>
79
80
  </div>
80
81
  <button id="clear-activity" class="btn-sm">Clear</button>
81
82
  </div>
83
+ <div id="activity-filter" class="activity-filter" role="tablist" aria-label="Activity category">
84
+ <button class="chip active" data-type="all">All</button>
85
+ <button class="chip" data-type="mcp">MCP</button>
86
+ <button class="chip" data-type="http">HTTP</button>
87
+ <button class="chip" data-type="guard">Guard</button>
88
+ <button class="chip" data-type="build">Build</button>
89
+ <button class="chip" data-type="cache">Cache</button>
90
+ <button class="chip" data-type="ws">WS</button>
91
+ <button class="chip" data-type="ate">ATE</button>
92
+ <button class="chip" data-type="error">Error</button>
93
+ </div>
82
94
  <div id="activity-list" class="activity-list">
83
- <div class="empty-state">Waiting for MCP activity...</div>
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>
95
+ <div class="empty-state">Waiting for activity...</div>
96
+ </div>
97
+ </section>
98
+
99
+ <section id="panel-errors" class="panel">
100
+ <div class="panel-header">
101
+ <div>
102
+ <h2>Errors</h2>
103
+ <p class="panel-subtitle">Browser errors grouped by signature repeated occurrences collapse into one row with count, first/last seen, and affected sources.</p>
104
+ </div>
105
+ <div style="display:flex;gap:6px;">
106
+ <button id="refresh-errors" class="btn-sm">Refresh</button>
107
+ <button id="clear-errors" class="btn-sm">Clear</button>
108
+ </div>
109
+ </div>
110
+ <div id="errors-summary" class="errors-summary">
111
+ <span id="errors-summary-text">Open Errors to see grouped browser errors.</span>
112
+ </div>
113
+ <div id="errors-list" class="errors-list">
114
+ <div class="empty-state">No errors captured yet.</div>
115
+ </div>
116
+ </section>
117
+
118
+ <section id="panel-agent" class="panel">
119
+ <div class="panel-header">
120
+ <div>
121
+ <h2>Agent Supervisor</h2>
122
+ <p class="panel-subtitle">Situation brief, tool routing, prompt pack, and next safe action for supervised agents.</p>
123
+ </div>
124
+ <button id="refresh-agent" class="btn-sm">Refresh</button>
125
+ </div>
126
+ <div id="agent-content" class="agent-content">
127
+ <div class="empty-state">Open Agent to build a context pack.</div>
128
+ </div>
129
+ </section>
130
+
131
+ <section id="panel-routes" class="panel active">
132
+ <div class="panel-header">
133
+ <div>
134
+ <h2>Routes</h2>
135
+ <p class="panel-subtitle">Current filesystem routes, slots, contracts, and hydration hints.</p>
105
136
  </div>
106
137
  <div id="routes-summary" class="summary"></div>
107
138
  </div>
@@ -241,10 +272,19 @@ export function renderKitchenHTML(): string {
241
272
  const CSS = /* css */ `
242
273
  * { margin: 0; padding: 0; box-sizing: border-box; }
243
274
 
275
+ /*
276
+ * Plan 18 DA-1 — the legacy dark-tool palette below is intentionally
277
+ * redirected to the Stitch tokens defined at the bottom of this file.
278
+ * Component layout / spacing / interaction is preserved; only the
279
+ * literal colors and the font family swap. The :root block (later in
280
+ * the cascade) wins, so any later override stays effective.
281
+ */
282
+
283
+ body.legacy-dark-tool-shell, /* kept as a hook for future toggle */
244
284
  body {
245
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
246
- background: #0f1117;
247
- color: #e4e4e7;
285
+ font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
286
+ background: var(--bg, #FFFDF5);
287
+ color: var(--ink, #4A3222);
248
288
  min-height: 100vh;
249
289
  }
250
290
 
@@ -253,8 +293,8 @@ const CSS = /* css */ `
253
293
  align-items: center;
254
294
  justify-content: space-between;
255
295
  padding: 12px 20px;
256
- background: #18181b;
257
- border-bottom: 1px solid #27272a;
296
+ background: var(--surface-strong, #FBF6EC);
297
+ border-bottom: 1px solid var(--line, #4A3222);
258
298
  }
259
299
 
260
300
  .logo {
@@ -262,7 +302,8 @@ const CSS = /* css */ `
262
302
  align-items: center;
263
303
  gap: 8px;
264
304
  font-size: 18px;
265
- font-weight: 600;
305
+ font-weight: 700;
306
+ font-family: var(--font-display, var(--font-sans));
266
307
  }
267
308
 
268
309
  .logo-icon { font-size: 24px; }
@@ -272,7 +313,7 @@ const CSS = /* css */ `
272
313
  align-items: center;
273
314
  gap: 6px;
274
315
  font-size: 13px;
275
- color: #a1a1aa;
316
+ color: var(--muted, #7A6B5D);
276
317
  }
277
318
 
278
319
  .status-dot {
@@ -282,15 +323,15 @@ const CSS = /* css */ `
282
323
  transition: background 0.3s;
283
324
  }
284
325
 
285
- .status-dot.connected { background: #22c55e; }
286
- .status-dot.disconnected { background: #ef4444; }
287
- .status-dot.connecting { background: #eab308; }
326
+ .status-dot.connected { background: var(--success, #6B9E47); }
327
+ .status-dot.disconnected { background: var(--danger, #C85450); }
328
+ .status-dot.connecting { background: var(--warning, #E8A93A); }
288
329
 
289
330
  .tabs {
290
331
  display: flex;
291
332
  gap: 0;
292
- background: #18181b;
293
- border-bottom: 1px solid #27272a;
333
+ background: var(--surface-strong, #FBF6EC);
334
+ border-bottom: 1px solid var(--line, #4A3222);
294
335
  padding: 0 20px;
295
336
  }
296
337
 
@@ -298,17 +339,20 @@ const CSS = /* css */ `
298
339
  padding: 10px 20px;
299
340
  background: none;
300
341
  border: none;
301
- color: #71717a;
342
+ color: var(--muted, #7A6B5D);
302
343
  font-size: 14px;
344
+ font-family: var(--font-sans);
345
+ font-weight: 500;
303
346
  cursor: pointer;
304
347
  border-bottom: 2px solid transparent;
305
348
  transition: all 0.2s;
306
349
  }
307
350
 
308
- .tab:hover { color: #e4e4e7; }
351
+ .tab:hover { color: var(--ink, #4A3222); }
309
352
  .tab.active {
310
- color: #a78bfa;
311
- border-bottom-color: #a78bfa;
353
+ color: var(--accent, #FF8C66);
354
+ border-bottom-color: var(--accent, #FF8C66);
355
+ font-weight: 700;
312
356
  }
313
357
 
314
358
  .panels { padding: 16px 20px; }
@@ -328,38 +372,203 @@ const CSS = /* css */ `
328
372
  font-weight: 600;
329
373
  }
330
374
 
375
+ /*
376
+ * Stitch primary button — hard shadow + bold + larger radius.
377
+ * Hover lifts: shadow 0 + translate so the surface "presses" into the
378
+ * shadow slot. Mirrors the .btn-hard pattern in mandujs.com globals.css.
379
+ */
331
380
  .btn-sm {
332
- padding: 4px 12px;
333
- background: #27272a;
334
- border: 1px solid #3f3f46;
335
- border-radius: 6px;
336
- color: #e4e4e7;
381
+ padding: 6px 14px;
382
+ background: var(--surface);
383
+ border: 2px solid var(--ink);
384
+ border-radius: var(--radius-md);
385
+ box-shadow: var(--shadow-sm);
386
+ color: var(--ink);
337
387
  font-size: 12px;
388
+ font-weight: 700;
338
389
  cursor: pointer;
339
- transition: background 0.2s;
390
+ transition: transform 150ms ease, box-shadow 150ms ease, background 0.2s;
340
391
  }
341
392
 
342
- .btn-sm:hover { background: #3f3f46; }
343
- .btn-sm:disabled { opacity: 0.5; cursor: not-allowed; }
393
+ .btn-sm:hover {
394
+ background: var(--accent-soft);
395
+ box-shadow: none;
396
+ transform: translate(2px, 2px);
397
+ }
398
+ .btn-sm:active {
399
+ box-shadow: none;
400
+ transform: translate(2px, 2px);
401
+ }
402
+ .btn-sm:disabled {
403
+ opacity: 0.5;
404
+ cursor: not-allowed;
405
+ box-shadow: var(--shadow-sm);
406
+ transform: none;
407
+ }
344
408
 
345
409
  .empty-state {
346
410
  padding: 40px 20px;
347
411
  text-align: center;
348
- color: #52525b;
412
+ color: var(--muted);
349
413
  font-size: 14px;
350
414
  }
351
415
 
352
416
  /* Activity */
417
+ .activity-filter {
418
+ display: flex;
419
+ flex-wrap: wrap;
420
+ gap: 6px;
421
+ padding: 8px 0 12px;
422
+ margin-bottom: 8px;
423
+ border-bottom: 1px solid var(--line);
424
+ }
425
+
426
+ .chip {
427
+ padding: 4px 10px;
428
+ border: 2px solid var(--line);
429
+ border-radius: var(--radius-sm);
430
+ background: var(--surface);
431
+ color: var(--ink);
432
+ font-family: var(--font-sans);
433
+ font-size: 12px;
434
+ font-weight: 700;
435
+ cursor: pointer;
436
+ transition: transform 100ms ease, box-shadow 100ms ease, background 0.2s;
437
+ }
438
+
439
+ .chip:hover {
440
+ background: var(--accent-soft);
441
+ }
442
+
443
+ .chip.active {
444
+ background: var(--accent);
445
+ color: var(--surface);
446
+ box-shadow: var(--shadow-sm);
447
+ }
448
+
449
+ .chip.active:hover {
450
+ background: var(--accent-strong);
451
+ }
452
+
353
453
  .activity-list {
354
- max-height: calc(100vh - 180px);
454
+ max-height: calc(100vh - 220px);
355
455
  overflow-y: auto;
356
456
  }
357
457
 
458
+ /* Plan 18 P1-4 — Errors panel grouped view */
459
+ .errors-summary {
460
+ display: flex;
461
+ align-items: center;
462
+ gap: 12px;
463
+ padding: 8px 12px;
464
+ background: var(--surface-strong);
465
+ border: 2px solid var(--line);
466
+ border-radius: var(--radius-md);
467
+ margin-bottom: 12px;
468
+ font-size: 13px;
469
+ color: var(--ink);
470
+ font-weight: 500;
471
+ }
472
+
473
+ .errors-list {
474
+ max-height: calc(100vh - 240px);
475
+ overflow-y: auto;
476
+ display: flex;
477
+ flex-direction: column;
478
+ gap: 10px;
479
+ }
480
+
481
+ .error-group {
482
+ background: var(--surface);
483
+ border: 2px solid var(--line);
484
+ border-radius: var(--radius-md);
485
+ box-shadow: var(--shadow-sm);
486
+ padding: 12px 14px;
487
+ cursor: pointer;
488
+ transition: transform 100ms ease, box-shadow 100ms ease;
489
+ }
490
+
491
+ .error-group:hover {
492
+ transform: translate(2px, 2px);
493
+ box-shadow: none;
494
+ }
495
+
496
+ .error-group.expanded {
497
+ transform: none;
498
+ box-shadow: var(--shadow);
499
+ }
500
+
501
+ .error-group-head {
502
+ display: flex;
503
+ align-items: center;
504
+ gap: 10px;
505
+ flex-wrap: wrap;
506
+ }
507
+
508
+ .error-count {
509
+ display: inline-flex;
510
+ align-items: center;
511
+ justify-content: center;
512
+ min-width: 28px;
513
+ height: 22px;
514
+ padding: 0 8px;
515
+ border-radius: var(--radius-sm);
516
+ background: var(--accent);
517
+ color: var(--surface);
518
+ font-size: 12px;
519
+ font-weight: 700;
520
+ }
521
+
522
+ .error-count.dim {
523
+ background: var(--accent-soft);
524
+ color: var(--ink);
525
+ }
526
+
527
+ .error-severity {
528
+ font-size: 10px;
529
+ font-weight: 700;
530
+ text-transform: uppercase;
531
+ padding: 2px 6px;
532
+ border-radius: var(--radius-sm);
533
+ background: rgba(200, 84, 80, 0.18);
534
+ color: var(--danger);
535
+ }
536
+
537
+ .error-message {
538
+ font-family: var(--font-mono);
539
+ font-size: 13px;
540
+ color: var(--ink);
541
+ flex: 1 1 100%;
542
+ word-break: break-word;
543
+ }
544
+
545
+ .error-meta {
546
+ display: flex;
547
+ gap: 12px;
548
+ flex-wrap: wrap;
549
+ margin-top: 6px;
550
+ font-size: 11px;
551
+ color: var(--muted);
552
+ }
553
+
554
+ .error-detail {
555
+ display: none;
556
+ margin-top: 10px;
557
+ padding-top: 10px;
558
+ border-top: 1px dashed var(--line);
559
+ font-family: var(--font-mono);
560
+ font-size: 12px;
561
+ white-space: pre-wrap;
562
+ color: var(--muted);
563
+ }
564
+
565
+ .error-group.expanded .error-detail { display: block; }
566
+
358
567
  .activity-item {
359
568
  padding: 8px 12px;
360
- border-bottom: 1px solid #1e1e22;
569
+ border-bottom: 1px solid var(--line);
361
570
  font-size: 13px;
362
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
571
+ font-family: var(--font-mono);
363
572
  display: flex;
364
573
  gap: 10px;
365
574
  align-items: flex-start;
@@ -372,19 +581,19 @@ const CSS = /* css */ `
372
581
  }
373
582
 
374
583
  .activity-time {
375
- color: #52525b;
584
+ color: var(--muted);
376
585
  white-space: nowrap;
377
586
  flex-shrink: 0;
378
587
  }
379
588
 
380
589
  .activity-tool {
381
- color: #a78bfa;
590
+ color: var(--accent);
382
591
  font-weight: 500;
383
592
  flex-shrink: 0;
384
593
  }
385
594
 
386
595
  .activity-detail {
387
- color: #a1a1aa;
596
+ color: var(--muted);
388
597
  overflow: hidden;
389
598
  text-overflow: ellipsis;
390
599
  white-space: nowrap;
@@ -395,7 +604,7 @@ const CSS = /* css */ `
395
604
  display: flex;
396
605
  gap: 12px;
397
606
  font-size: 12px;
398
- color: #a1a1aa;
607
+ color: var(--muted);
399
608
  }
400
609
 
401
610
  .summary-item {
@@ -406,7 +615,7 @@ const CSS = /* css */ `
406
615
 
407
616
  .summary-count {
408
617
  font-weight: 600;
409
- color: #e4e4e7;
618
+ color: var(--ink);
410
619
  }
411
620
 
412
621
  .route-item {
@@ -414,7 +623,7 @@ const CSS = /* css */ `
414
623
  align-items: center;
415
624
  gap: 12px;
416
625
  padding: 10px 12px;
417
- border-bottom: 1px solid #1e1e22;
626
+ border-bottom: 1px solid var(--line);
418
627
  font-size: 13px;
419
628
  }
420
629
 
@@ -430,12 +639,12 @@ const CSS = /* css */ `
430
639
  text-align: center;
431
640
  }
432
641
 
433
- .route-kind.page { background: #1e3a5f; color: #60a5fa; }
434
- .route-kind.api { background: #1a3c34; color: #4ade80; }
642
+ .route-kind.page { background: rgba(74, 144, 194, 0.15); color: var(--info); }
643
+ .route-kind.api { background: rgba(107, 158, 71, 0.18); color: var(--success); }
435
644
 
436
645
  .route-pattern {
437
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
438
- color: #e4e4e7;
646
+ font-family: var(--font-mono);
647
+ color: var(--ink);
439
648
  flex: 1;
440
649
  }
441
650
 
@@ -449,22 +658,22 @@ const CSS = /* css */ `
449
658
  padding: 1px 6px;
450
659
  border-radius: 3px;
451
660
  font-size: 10px;
452
- background: #27272a;
453
- color: #a1a1aa;
661
+ background: var(--surface-alt);
662
+ color: var(--muted);
454
663
  }
455
664
 
456
665
  /* Guard */
457
666
  .guard-status {
458
667
  margin-bottom: 12px;
459
668
  font-size: 13px;
460
- color: #a1a1aa;
669
+ color: var(--muted);
461
670
  }
462
671
 
463
672
  .guard-summary {
464
673
  display: flex;
465
674
  gap: 16px;
466
675
  padding: 12px;
467
- background: #18181b;
676
+ background: var(--surface-strong);
468
677
  border-radius: 8px;
469
678
  margin-bottom: 12px;
470
679
  }
@@ -480,28 +689,28 @@ const CSS = /* css */ `
480
689
 
481
690
  .guard-stat-label {
482
691
  font-size: 11px;
483
- color: #71717a;
692
+ color: var(--muted);
484
693
  text-transform: uppercase;
485
694
  }
486
695
 
487
- .sev-error { color: #ef4444; }
488
- .sev-warning { color: #eab308; }
489
- .sev-info { color: #3b82f6; }
696
+ .sev-error { color: var(--danger); }
697
+ .sev-warning { color: var(--warning); }
698
+ .sev-info { color: var(--info); }
490
699
 
491
700
  .violation-item {
492
701
  padding: 8px 12px;
493
- border-bottom: 1px solid #1e1e22;
702
+ border-bottom: 1px solid var(--line);
494
703
  font-size: 13px;
495
704
  }
496
705
 
497
706
  .violation-file {
498
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
499
- color: #a78bfa;
707
+ font-family: var(--font-mono);
708
+ color: var(--accent);
500
709
  margin-bottom: 2px;
501
710
  }
502
711
 
503
712
  .violation-msg {
504
- color: #a1a1aa;
713
+ color: var(--muted);
505
714
  font-size: 12px;
506
715
  }
507
716
 
@@ -514,9 +723,9 @@ const CSS = /* css */ `
514
723
  margin-right: 4px;
515
724
  }
516
725
 
517
- .violation-sev.error { background: #3b1111; color: #ef4444; }
518
- .violation-sev.warning { background: #3b2f11; color: #eab308; }
519
- .violation-sev.info { background: #112840; color: #3b82f6; }
726
+ .violation-sev.error { background: rgba(200, 84, 80, 0.18); color: var(--danger); }
727
+ .violation-sev.warning { background: rgba(232, 169, 58, 0.20); color: var(--warning); }
728
+ .violation-sev.info { background: rgba(74, 144, 194, 0.18); color: var(--info); }
520
729
 
521
730
  /* Preview */
522
731
  .preview-list { max-height: 40vh; overflow-y: auto; }
@@ -527,16 +736,16 @@ const CSS = /* css */ `
527
736
  align-items: center;
528
737
  gap: 10px;
529
738
  padding: 8px 12px;
530
- border-bottom: 1px solid #1e1e22;
739
+ border-bottom: 1px solid var(--line);
531
740
  font-size: 13px;
532
741
  cursor: pointer;
533
742
  transition: background 0.15s;
534
743
  }
535
- .change-item:hover { background: #27272a; }
744
+ .change-item:hover { background: var(--surface-alt); }
536
745
  .change-icon { flex-shrink: 0; }
537
746
  .change-path {
538
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
539
- color: #e4e4e7;
747
+ font-family: var(--font-mono);
748
+ color: var(--ink);
540
749
  flex: 1;
541
750
  overflow: hidden;
542
751
  text-overflow: ellipsis;
@@ -550,81 +759,81 @@ const CSS = /* css */ `
550
759
  font-weight: 600;
551
760
  flex-shrink: 0;
552
761
  }
553
- .change-status.added { background: #1a3c34; color: #4ade80; }
554
- .change-status.modified { background: #1e3a5f; color: #60a5fa; }
555
- .change-status.deleted { background: #3b1111; color: #ef4444; }
556
- .change-status.untracked { background: #3b2f11; color: #eab308; }
557
- .change-status.renamed { background: #2a1a3c; color: #a78bfa; }
762
+ .change-status.added { background: rgba(107, 158, 71, 0.18); color: var(--success); }
763
+ .change-status.modified { background: rgba(74, 144, 194, 0.15); color: var(--info); }
764
+ .change-status.deleted { background: rgba(200, 84, 80, 0.18); color: var(--danger); }
765
+ .change-status.untracked { background: rgba(232, 169, 58, 0.20); color: var(--warning); }
766
+ .change-status.renamed { background: var(--accent-soft); color: var(--accent); }
558
767
 
559
768
  .diff-header {
560
769
  display: flex; align-items: center; justify-content: space-between;
561
- padding: 8px 12px; background: #18181b; border-radius: 6px 6px 0 0;
562
- border-bottom: 1px solid #27272a;
770
+ padding: 8px 12px; background: var(--surface-strong); border-radius: 6px 6px 0 0;
771
+ border-bottom: 1px solid var(--surface-alt);
563
772
  }
564
- .diff-file { font-family: monospace; color: #a78bfa; font-size: 13px; }
773
+ .diff-file { font-family: monospace; color: var(--accent); font-size: 13px; }
565
774
  .diff-stats { font-size: 12px; }
566
- .diff-add { color: #4ade80; margin-right: 8px; }
567
- .diff-del { color: #ef4444; }
568
- .diff-hunk-header { padding: 4px 12px; background: #112840; color: #3b82f6; font-size: 12px; font-family: monospace; }
775
+ .diff-add { color: var(--success); margin-right: 8px; }
776
+ .diff-del { color: var(--danger); }
777
+ .diff-hunk-header { padding: 4px 12px; background: rgba(74, 144, 194, 0.18); color: var(--info); font-size: 12px; font-family: monospace; }
569
778
  .diff-line { display: flex; font-family: monospace; font-size: 12px; line-height: 20px; }
570
- .diff-line-num { width: 40px; text-align: right; padding: 0 4px; color: #52525b; user-select: none; flex-shrink: 0; }
779
+ .diff-line-num { width: 40px; text-align: right; padding: 0 4px; color: var(--muted); user-select: none; flex-shrink: 0; }
571
780
  .diff-line-content { flex: 1; padding: 0 8px; white-space: pre; overflow: hidden; text-overflow: ellipsis; }
572
781
  .diff-line.add { background: rgba(74,222,128,0.08); }
573
- .diff-line.add .diff-line-content::before { content: '+'; color: #4ade80; }
782
+ .diff-line.add .diff-line-content::before { content: '+'; color: var(--success); }
574
783
  .diff-line.remove { background: rgba(239,68,68,0.08); }
575
- .diff-line.remove .diff-line-content::before { content: '-'; color: #ef4444; }
784
+ .diff-line.remove .diff-line-content::before { content: '-'; color: var(--danger); }
576
785
  .diff-line.context .diff-line-content::before { content: ' '; }
577
786
 
578
787
  /* Contracts */
579
788
  .contracts-layout { display: flex; gap: 12px; height: calc(100vh - 180px); }
580
- .contracts-list { width: 300px; flex-shrink: 0; overflow-y: auto; border-right: 1px solid #27272a; padding-right: 12px; }
789
+ .contracts-list { width: 300px; flex-shrink: 0; overflow-y: auto; border-right: 1px solid var(--surface-alt); padding-right: 12px; }
581
790
  .contracts-detail { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
582
791
 
583
792
  .contract-item {
584
793
  display: flex; align-items: center; gap: 8px;
585
- padding: 8px 12px; border-bottom: 1px solid #1e1e22;
794
+ padding: 8px 12px; border-bottom: 1px solid var(--line);
586
795
  cursor: pointer; transition: background 0.15s; font-size: 13px;
587
796
  }
588
- .contract-item:hover { background: #27272a; }
589
- .contract-item.selected { background: #27272a; border-left: 2px solid #a78bfa; }
797
+ .contract-item:hover { background: var(--surface-alt); }
798
+ .contract-item.selected { background: var(--surface-alt); border-left: 2px solid var(--accent); }
590
799
 
591
800
  .method-badge {
592
801
  display: inline-block; padding: 1px 6px; border-radius: 3px;
593
802
  font-size: 10px; font-weight: 600; text-transform: uppercase;
594
803
  flex-shrink: 0; min-width: 36px; text-align: center;
595
804
  }
596
- .method-badge.get { background: #1a3c34; color: #4ade80; }
597
- .method-badge.post { background: #1e3a5f; color: #60a5fa; }
598
- .method-badge.put { background: #3b2f11; color: #eab308; }
599
- .method-badge.patch { background: #2a1a3c; color: #a78bfa; }
600
- .method-badge.delete { background: #3b1111; color: #ef4444; }
805
+ .method-badge.get { background: rgba(107, 158, 71, 0.18); color: var(--success); }
806
+ .method-badge.post { background: rgba(74, 144, 194, 0.15); color: var(--info); }
807
+ .method-badge.put { background: rgba(232, 169, 58, 0.20); color: var(--warning); }
808
+ .method-badge.patch { background: var(--accent-soft); color: var(--accent); }
809
+ .method-badge.delete { background: rgba(200, 84, 80, 0.18); color: var(--danger); }
601
810
 
602
- .contract-pattern { font-family: monospace; color: #e4e4e7; }
811
+ .contract-pattern { font-family: monospace; color: var(--ink); }
603
812
 
604
813
  .contract-schema {
605
- background: #18181b; border-radius: 8px; padding: 12px;
814
+ background: var(--surface-strong); border-radius: 8px; padding: 12px;
606
815
  font-family: monospace; font-size: 12px; white-space: pre-wrap;
607
816
  max-height: 40vh; overflow-y: auto;
608
817
  }
609
818
 
610
- .contract-playground { background: #18181b; border-radius: 8px; padding: 12px; }
819
+ .contract-playground { background: var(--surface-strong); border-radius: 8px; padding: 12px; }
611
820
  .contract-playground h3 { font-size: 14px; margin-bottom: 8px; }
612
821
 
613
822
  .playground-controls { display: flex; gap: 8px; margin-bottom: 8px; }
614
823
  .select-sm {
615
- padding: 4px 8px; background: #27272a; border: 1px solid #3f3f46;
616
- border-radius: 6px; color: #e4e4e7; font-size: 12px;
824
+ padding: 4px 8px; background: var(--surface-alt); border: 1px solid var(--line);
825
+ border-radius: 6px; color: var(--ink); font-size: 12px;
617
826
  }
618
827
  .playground-inputs { display: flex; flex-direction: column; gap: 6px; }
619
- .playground-inputs label { font-size: 11px; color: #71717a; display: flex; flex-direction: column; gap: 2px; }
828
+ .playground-inputs label { font-size: 11px; color: var(--muted); display: flex; flex-direction: column; gap: 2px; }
620
829
  .playground-inputs textarea {
621
- background: #27272a; border: 1px solid #3f3f46; border-radius: 4px;
622
- color: #e4e4e7; font-family: monospace; font-size: 12px; padding: 6px;
830
+ background: var(--surface-alt); border: 1px solid var(--line); border-radius: 4px;
831
+ color: var(--ink); font-family: monospace; font-size: 12px; padding: 6px;
623
832
  resize: vertical;
624
833
  }
625
834
  .validate-result { margin-top: 8px; padding: 8px; border-radius: 4px; font-size: 12px; font-family: monospace; }
626
- .validate-result.success { background: #1a3c34; color: #4ade80; }
627
- .validate-result.error { background: #3b1111; color: #ef4444; }
835
+ .validate-result.success { background: rgba(107, 158, 71, 0.18); color: var(--success); }
836
+ .validate-result.error { background: rgba(200, 84, 80, 0.18); color: var(--danger); }
628
837
 
629
838
  .debug-bar {
630
839
  position: fixed;
@@ -632,43 +841,71 @@ const CSS = /* css */ `
632
841
  left: 0;
633
842
  right: 0;
634
843
  padding: 4px 12px;
635
- background: #1a1a2e;
636
- border-top: 1px solid #27272a;
844
+ background: var(--bg);
845
+ border-top: 1px solid var(--surface-alt);
637
846
  font-size: 11px;
638
847
  font-family: monospace;
639
- color: #71717a;
848
+ color: var(--muted);
640
849
  max-height: 60px;
641
850
  overflow-y: auto;
642
851
  }
643
852
 
644
- .debug-bar .err { color: #ef4444; }
645
- .debug-bar .ok { color: #22c55e; }
853
+ .debug-bar .err { color: var(--danger); }
854
+ .debug-bar .ok { color: var(--success); }
646
855
 
856
+ /*
857
+ * Plan 18 DA-1 — token values aligned to mandujs.com Stitch system
858
+ * (app/globals.css + tokens.css). Variable names stay the same so the
859
+ * cascade with the legacy dark-mode block earlier in this file does
860
+ * not break. Stitch identity: Peach #FF8C66 / Dark Brown #4A3222 /
861
+ * Cream #FFFDF5 + hard shadow (blur 0).
862
+ */
647
863
  :root {
648
- --bg: #f4efe6;
649
- --bg-soft: rgba(255, 252, 246, 0.7);
650
- --surface: #fffdfa;
651
- --surface-strong: #f8f1e4;
652
- --surface-alt: #f0e5d2;
653
- --ink: #1e2a3a;
654
- --muted: #677181;
655
- --line: #dccfba;
656
- --accent: #b86a12;
657
- --accent-strong: #8d4f0e;
658
- --accent-soft: rgba(184, 106, 18, 0.14);
659
- --success: #177f56;
660
- --danger: #bc3d3d;
661
- --info: #2e66b8;
662
- --warning: #ad7a12;
663
- --shadow: 0 20px 50px rgba(49, 39, 23, 0.08);
864
+ /* Page surfaces */
865
+ --bg: #FFFDF5; /* mandujs.com --color-background (cream) */
866
+ --bg-soft: rgba(255, 253, 245, 0.7);
867
+ --surface: #FFFFFF; /* --color-surface */
868
+ --surface-strong: #FBF6EC; /* --color-code-bg-light, slightly darker cream */
869
+ --surface-alt: #F5F0E8; /* --color-muted */
870
+
871
+ /* Text */
872
+ --ink: #4A3222; /* --color-foreground (dark brown, not black) */
873
+ --muted: #7A6B5D; /* --color-muted-foreground (warm gray) */
874
+ --line: #4A3222; /* --color-border (2px brown lines are Stitch) */
875
+
876
+ /* Brand */
877
+ --accent: #FF8C66; /* --color-primary (peach) */
878
+ --accent-strong: #FF7A4F; /* --color-primary-hover */
879
+ --accent-soft: rgba(255, 140, 102, 0.14);
880
+
881
+ /* Semantic — docs-grade muted channels (tokens.css) */
882
+ --success: #6B9E47; /* olive green */
883
+ --danger: #C85450; /* muted red-brown */
884
+ --info: #4A90C2; /* calm blue */
885
+ --warning: #E8A93A; /* warm amber */
886
+
887
+ /* Hard shadow (Stitch signature — blur 0, solid color block). The
888
+ * standard variant is exposed as --shadow so existing references
889
+ * inherit the new look without per-selector edits. */
890
+ --shadow: 4px 4px 0 0 var(--ink);
891
+ --shadow-sm: 2px 2px 0 0 var(--ink);
892
+ --shadow-lg: 6px 6px 0 0 var(--ink);
893
+
894
+ /* Radius scale — rounded & playful */
895
+ --radius-sm: 0.5rem;
896
+ --radius-md: 1rem;
897
+ --radius-lg: 1.5rem;
898
+ --radius-xl: 2rem;
899
+
900
+ /* Typography */
901
+ --font-sans: 'Pretendard Variable', 'Pretendard', 'Noto Sans JP', 'Noto Sans SC', ui-sans-serif, system-ui, sans-serif;
902
+ --font-display: 'Nunito', 'Jua', 'Pretendard Variable', 'Pretendard', ui-sans-serif, system-ui, sans-serif;
903
+ --font-mono: 'Consolas', 'Monaco', 'Ubuntu Mono', ui-monospace, monospace;
664
904
  }
665
905
 
666
906
  body {
667
- font-family: "IBM Plex Sans", "Segoe UI Variable", "Segoe UI", sans-serif;
668
- background:
669
- radial-gradient(circle at top left, rgba(226, 186, 124, 0.35), transparent 30%),
670
- radial-gradient(circle at top right, rgba(152, 191, 193, 0.22), transparent 24%),
671
- linear-gradient(180deg, #f8f3ea 0%, #efe7d8 100%);
907
+ font-family: var(--font-sans);
908
+ background: var(--bg);
672
909
  color: var(--ink);
673
910
  padding: 24px;
674
911
  }
@@ -1202,141 +1439,141 @@ const CSS = /* css */ `
1202
1439
  box-shadow: 0 12px 28px rgba(30, 42, 58, 0.22);
1203
1440
  }
1204
1441
 
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
- }
1442
+ .requests-list, .mcp-list, .cache-content, .metrics-content, .agent-content {
1443
+ display: flex;
1444
+ flex-direction: column;
1445
+ gap: 6px;
1446
+ }
1447
+
1448
+ .agent-grid {
1449
+ display: grid;
1450
+ grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr);
1451
+ gap: 14px;
1452
+ }
1453
+
1454
+ .agent-stack {
1455
+ display: flex;
1456
+ flex-direction: column;
1457
+ gap: 12px;
1458
+ }
1459
+
1460
+ .agent-card {
1461
+ padding: 14px;
1462
+ border: 1px solid var(--line);
1463
+ border-radius: 8px;
1464
+ background: rgba(255, 255, 255, 0.72);
1465
+ }
1466
+
1467
+ .agent-card-title {
1468
+ display: flex;
1469
+ align-items: center;
1470
+ justify-content: space-between;
1471
+ gap: 10px;
1472
+ margin-bottom: 8px;
1473
+ color: var(--ink);
1474
+ font-size: 14px;
1475
+ font-weight: 700;
1476
+ }
1477
+
1478
+ .agent-card-body {
1479
+ color: var(--muted);
1480
+ font-size: 13px;
1481
+ line-height: 1.55;
1482
+ }
1483
+
1484
+ .agent-pill-row {
1485
+ display: flex;
1486
+ flex-wrap: wrap;
1487
+ gap: 6px;
1488
+ margin-top: 10px;
1489
+ }
1490
+
1491
+ .agent-pill {
1492
+ padding: 4px 8px;
1493
+ border-radius: 6px;
1494
+ background: rgba(30, 42, 58, 0.08);
1495
+ color: var(--ink);
1496
+ font-family: "IBM Plex Mono", "Cascadia Code", monospace;
1497
+ font-size: 11px;
1498
+ }
1499
+
1500
+ .agent-severity {
1501
+ padding: 3px 7px;
1502
+ border-radius: 6px;
1503
+ font-size: 11px;
1504
+ font-weight: 700;
1505
+ text-transform: uppercase;
1506
+ }
1507
+
1508
+ .agent-severity.info { background: rgba(46, 102, 184, 0.12); color: var(--info); }
1509
+ .agent-severity.warn { background: rgba(173, 122, 18, 0.14); color: var(--warning); }
1510
+ .agent-severity.error { background: rgba(188, 61, 61, 0.12); color: var(--danger); }
1511
+
1512
+ .agent-stat-grid {
1513
+ display: grid;
1514
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1515
+ gap: 8px;
1516
+ margin-top: 12px;
1517
+ }
1518
+
1519
+ .agent-stat {
1520
+ padding: 10px;
1521
+ border-radius: 8px;
1522
+ border: 1px solid rgba(220, 207, 186, 0.7);
1523
+ background: rgba(255, 253, 250, 0.84);
1524
+ }
1525
+
1526
+ .agent-stat-label {
1527
+ color: var(--muted);
1528
+ font-size: 11px;
1529
+ }
1530
+
1531
+ .agent-stat-value {
1532
+ color: var(--ink);
1533
+ font-size: 19px;
1534
+ font-weight: 700;
1535
+ margin-top: 3px;
1536
+ }
1537
+
1538
+ .agent-rec {
1539
+ display: grid;
1540
+ grid-template-columns: 150px 1fr;
1541
+ gap: 10px;
1542
+ padding: 10px 0;
1543
+ border-top: 1px solid rgba(220, 207, 186, 0.65);
1544
+ }
1545
+
1546
+ .agent-rec:first-child {
1547
+ border-top: 0;
1548
+ padding-top: 0;
1549
+ }
1550
+
1551
+ .agent-rec-title {
1552
+ color: var(--ink);
1553
+ font-weight: 700;
1554
+ font-size: 13px;
1555
+ }
1556
+
1557
+ .agent-rec-detail {
1558
+ color: var(--muted);
1559
+ font-size: 12px;
1560
+ line-height: 1.5;
1561
+ }
1562
+
1563
+ .agent-prompt {
1564
+ width: 100%;
1565
+ min-height: 220px;
1566
+ margin-top: 10px;
1567
+ padding: 12px;
1568
+ resize: vertical;
1569
+ border-radius: 8px;
1570
+ border: 1px solid var(--line);
1571
+ background: var(--surface);
1572
+ color: var(--ink);
1573
+ font-family: "IBM Plex Mono", "Cascadia Code", monospace;
1574
+ font-size: 12px;
1575
+ line-height: 1.45;
1576
+ }
1340
1577
 
1341
1578
  .req-row {
1342
1579
  display: grid;
@@ -1457,17 +1694,17 @@ const CSS = /* css */ `
1457
1694
  grid-template-columns: repeat(2, minmax(0, 1fr));
1458
1695
  }
1459
1696
 
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
- }
1697
+ .contracts-layout {
1698
+ grid-template-columns: 1fr;
1699
+ }
1700
+
1701
+ .agent-grid {
1702
+ grid-template-columns: 1fr;
1703
+ }
1704
+
1705
+ .guard-summary {
1706
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1707
+ }
1471
1708
  }
1472
1709
 
1473
1710
  @media (max-width: 720px) {
@@ -1493,20 +1730,20 @@ const CSS = /* css */ `
1493
1730
  width: calc(50% - 4px);
1494
1731
  justify-content: center;
1495
1732
  }
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
- `;
1733
+
1734
+ .panel-header {
1735
+ flex-direction: column;
1736
+ }
1737
+
1738
+ .agent-stat-grid {
1739
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1740
+ }
1741
+
1742
+ .agent-rec {
1743
+ grid-template-columns: 1fr;
1744
+ }
1745
+ }
1746
+ `;
1510
1747
 
1511
1748
  // ─── JavaScript ──────────────────────────────────
1512
1749
 
@@ -1523,12 +1760,12 @@ const JS = /* js */ `
1523
1760
  console.log('[Kitchen]', msg);
1524
1761
  }
1525
1762
 
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
- }
1763
+ function escapeHtml(str) {
1764
+ if (str === null || str === undefined) return '';
1765
+ var d = document.createElement('div');
1766
+ d.appendChild(document.createTextNode(String(str)));
1767
+ return d.innerHTML;
1768
+ }
1532
1769
 
1533
1770
  function setMetric(id, value) {
1534
1771
  var el = document.getElementById(id);
@@ -1559,6 +1796,39 @@ const JS = /* js */ `
1559
1796
  var activityCount = 0;
1560
1797
  var MAX_ITEMS = 200;
1561
1798
  var sseRetryCount = 0;
1799
+ var activityCurrentSSE = null;
1800
+ var activityFilter = 'all'; // plan 18 P1-3
1801
+
1802
+ function disconnectSSE() {
1803
+ if (activityCurrentSSE) {
1804
+ try { activityCurrentSSE.close(); } catch(_) {}
1805
+ activityCurrentSSE = null;
1806
+ }
1807
+ statusDot.className = 'status-dot disconnected';
1808
+ statusLabel.textContent = 'Filter: ' + activityFilter;
1809
+ }
1810
+
1811
+ function loadFilteredEvents(type) {
1812
+ activityList.innerHTML = '<div class="empty-state">Loading ' + escapeHtml(type) + ' events...</div>';
1813
+ fetchJson('/__kitchen/api/events?type=' + encodeURIComponent(type) + '&limit=100', function(err, data) {
1814
+ if (err) {
1815
+ activityList.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
1816
+ return;
1817
+ }
1818
+ var events = data.events || [];
1819
+ if (!events.length) {
1820
+ activityList.innerHTML = '<div class="empty-state">No ' + escapeHtml(type) + ' events yet — try interacting with the app or run a build.</div>';
1821
+ return;
1822
+ }
1823
+ activityList.innerHTML = '';
1824
+ activityCount = 0;
1825
+ for (var i = 0; i < events.length; i++) {
1826
+ // events come newest-first from reverseCopy in the handler; appendActivity inserts at top.
1827
+ // Reverse so newest stays on top after insertBefore.
1828
+ appendActivity(events[events.length - 1 - i]);
1829
+ }
1830
+ });
1831
+ }
1562
1832
 
1563
1833
  function connectSSE() {
1564
1834
  statusDot.className = 'status-dot connecting';
@@ -1568,6 +1838,7 @@ const JS = /* js */ `
1568
1838
  var es;
1569
1839
  try {
1570
1840
  es = new EventSource('/__kitchen/sse/activity');
1841
+ activityCurrentSSE = es;
1571
1842
  } catch(e) {
1572
1843
  log('SSE EventSource failed: ' + e.message, 'err');
1573
1844
  statusDot.className = 'status-dot disconnected';
@@ -1636,11 +1907,37 @@ const JS = /* js */ `
1636
1907
  }
1637
1908
 
1638
1909
  document.getElementById('clear-activity').addEventListener('click', function() {
1639
- activityList.innerHTML = '<div class="empty-state">Waiting for MCP activity...</div>';
1910
+ activityList.innerHTML = '<div class="empty-state">Waiting for activity...</div>';
1640
1911
  activityCount = 0;
1641
1912
  setMetric('metric-activity', 0);
1642
1913
  });
1643
1914
 
1915
+ // Plan 18 P1-3 — Activity filter chips. "All" reconnects SSE for live
1916
+ // mcp tailing; the other chips switch to a one-shot fetch of the bus's
1917
+ // typed slice (build / cache / ws / ate / error / etc) so signals that
1918
+ // would normally stay invisible in the jsonl tail surface here.
1919
+ var filterEl = document.getElementById('activity-filter');
1920
+ if (filterEl) {
1921
+ var chips = filterEl.querySelectorAll('.chip');
1922
+ for (var ci = 0; ci < chips.length; ci++) {
1923
+ chips[ci].addEventListener('click', function() {
1924
+ var all = filterEl.querySelectorAll('.chip');
1925
+ for (var k = 0; k < all.length; k++) all[k].classList.remove('active');
1926
+ this.classList.add('active');
1927
+ var t = this.getAttribute('data-type') || 'all';
1928
+ activityFilter = t;
1929
+ activityList.innerHTML = '';
1930
+ activityCount = 0;
1931
+ if (t === 'all') {
1932
+ connectSSE();
1933
+ } else {
1934
+ disconnectSSE();
1935
+ loadFilteredEvents(t);
1936
+ }
1937
+ });
1938
+ }
1939
+ }
1940
+
1644
1941
  connectSSE();
1645
1942
 
1646
1943
  // ─── Routes ──────────────────────────────────
@@ -2049,10 +2346,10 @@ const JS = /* js */ `
2049
2346
 
2050
2347
  loadContracts();
2051
2348
 
2052
- // ─── Requests Tab ─────────────────────────────
2053
- var requestsListEl = document.getElementById('requests-list');
2054
- var requestsDetailEl = document.getElementById('requests-detail');
2055
- var agentContentEl = document.getElementById('agent-content');
2349
+ // ─── Requests Tab ─────────────────────────────
2350
+ var requestsListEl = document.getElementById('requests-list');
2351
+ var requestsDetailEl = document.getElementById('requests-detail');
2352
+ var agentContentEl = document.getElementById('agent-content');
2056
2353
 
2057
2354
  function fetchJson(url, cb) {
2058
2355
  var xhr = new XMLHttpRequest();
@@ -2067,142 +2364,222 @@ const JS = /* js */ `
2067
2364
  xhr.send();
2068
2365
  }
2069
2366
 
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>';
2367
+ function statusClass(s) {
2368
+ if (!s) return '';
2369
+ if (s >= 500) return 's5';
2370
+ if (s >= 400) return 's4';
2371
+ if (s >= 300) return 's3';
2372
+ return 's2';
2373
+ }
2374
+
2375
+ // ─── Agent Supervisor Tab ─────────────────────
2376
+ function renderAgentPills(items) {
2377
+ if (!items || !items.length) return '';
2378
+ var html = '<div class="agent-pill-row">';
2379
+ for (var i = 0; i < items.length; i++) {
2380
+ html += '<span class="agent-pill">' + escapeHtml(items[i]) + '</span>';
2381
+ }
2382
+ return html + '</div>';
2383
+ }
2384
+
2385
+ function renderAgentDetails(items) {
2386
+ if (!items || !items.length) return '';
2387
+ var html = '<ul>';
2388
+ for (var i = 0; i < items.length; i++) {
2389
+ html += '<li>' + escapeHtml(items[i]) + '</li>';
2390
+ }
2391
+ return html + '</ul>';
2392
+ }
2393
+
2394
+ function renderAgentContext(data) {
2395
+ var situation = data.situation || {};
2396
+ var summary = data.summary || {};
2397
+ var routes = summary.routes || {};
2398
+ var status = data.agentStatus || {};
2399
+ var brain = status.brain || {};
2400
+ var action = data.nextSafeAction || {};
2401
+ var prompt = data.prompt || {};
2402
+ var recs = data.toolRecommendations || [];
2403
+ var cards = data.knowledgeCards || [];
2404
+ var promptText = prompt.copyText || '';
2405
+
2406
+ var html = '<div class="agent-grid">' +
2407
+ '<div class="agent-stack">' +
2408
+ '<div class="agent-card">' +
2409
+ '<div class="agent-card-title">' +
2410
+ '<span>' + escapeHtml(situation.title || 'No situation') + '</span>' +
2411
+ '<span class="agent-severity ' + escapeHtml(situation.severity || 'info') + '">' + escapeHtml(situation.category || 'agent') + '</span>' +
2412
+ '</div>' +
2413
+ '<div class="agent-card-body">' + renderAgentDetails(situation.details || []) + '</div>' +
2414
+ '<div class="agent-stat-grid">' +
2415
+ '<div class="agent-stat"><div class="agent-stat-label">Routes</div><div class="agent-stat-value">' + escapeHtml(routes.total || 0) + '</div></div>' +
2416
+ '<div class="agent-stat"><div class="agent-stat-label">Islands</div><div class="agent-stat-value">' + escapeHtml(routes.islands || 0) + '</div></div>' +
2417
+ '<div class="agent-stat"><div class="agent-stat-label">Contracts</div><div class="agent-stat-value">' + escapeHtml(routes.contracts || 0) + '</div></div>' +
2418
+ '<div class="agent-stat"><div class="agent-stat-label">MCP Calls</div><div class="agent-stat-value">' + escapeHtml(status.observedToolCalls || 0) + '</div></div>' +
2419
+ '</div>' +
2420
+ '</div>' +
2421
+ '<div class="agent-card">' +
2422
+ '<div class="agent-card-title"><span>Next Safe Action</span><span class="agent-severity info">' + escapeHtml(action.mode || 'observe') + '</span></div>' +
2423
+ '<div class="agent-card-body"><strong>' + escapeHtml(action.title || '-') + '</strong><br>' + escapeHtml(action.reason || '') + '</div>' +
2424
+ renderAgentPills([action.tool || '', action.command || ''].filter(Boolean)) +
2425
+ renderAgentPills(action.validation || []) +
2426
+ '</div>' +
2427
+ '<div class="agent-card">' +
2428
+ '<div class="agent-card-title"><span>Prompt Pack</span><button id="copy-agent-prompt" class="btn-sm">Copy</button></div>' +
2429
+ '<textarea id="agent-prompt-text" class="agent-prompt" readonly>' + escapeHtml(promptText) + '</textarea>' +
2430
+ '</div>' +
2431
+ '</div>' +
2432
+ '<div class="agent-stack">' +
2433
+ '<div class="agent-card">' +
2434
+ '<div class="agent-card-title"><span>Brain</span><span class="agent-severity info">' + escapeHtml(brain.oauth || 'unknown') + '</span></div>' +
2435
+ '<div class="agent-card-body">' + escapeHtml(brain.note || '') + '</div>' +
2436
+ renderAgentPills([brain.statusTool || 'mandu.brain.status']) +
2437
+ '</div>' +
2438
+ '<div class="agent-card">' +
2439
+ '<div class="agent-card-title"><span>Tool Router</span></div>';
2440
+
2441
+ for (var i = 0; i < recs.length; i++) {
2442
+ var r = recs[i];
2443
+ html += '<div class="agent-rec">' +
2444
+ '<div class="agent-rec-title">' + escapeHtml(r.skill) + '</div>' +
2445
+ '<div class="agent-rec-detail">' +
2446
+ '<strong>' + escapeHtml(r.task) + '</strong><br>' +
2447
+ escapeHtml(r.useWhen) +
2448
+ renderAgentPills(r.mcpTools || []) +
2449
+ '<div style="margin-top:6px;">Fallback: <code>' + escapeHtml(r.cliFallback) + '</code></div>' +
2450
+ '</div>' +
2451
+ '</div>';
2452
+ }
2453
+
2454
+ html += '</div><div class="agent-card">' +
2455
+ '<div class="agent-card-title"><span>Knowledge</span></div>';
2456
+
2457
+ for (var c = 0; c < cards.length; c++) {
2458
+ var card = cards[c];
2459
+ html += '<div class="agent-rec">' +
2460
+ '<div class="agent-rec-title">' + escapeHtml(card.title) + '</div>' +
2461
+ '<div class="agent-rec-detail">' + escapeHtml(card.body) + renderAgentPills(card.references || []) + '</div>' +
2462
+ '</div>';
2463
+ }
2464
+
2465
+ html += '</div></div></div>';
2466
+ agentContentEl.innerHTML = html;
2467
+
2468
+ var copyBtn = document.getElementById('copy-agent-prompt');
2469
+ if (copyBtn) {
2470
+ copyBtn.addEventListener('click', function() {
2471
+ var textEl = document.getElementById('agent-prompt-text');
2472
+ var text = textEl ? textEl.value : promptText;
2473
+ if (navigator.clipboard && navigator.clipboard.writeText) {
2474
+ navigator.clipboard.writeText(text).then(function() {
2475
+ copyBtn.textContent = 'Copied';
2476
+ setTimeout(function() { copyBtn.textContent = 'Copy'; }, 900);
2477
+ }).catch(function() {
2478
+ textEl && textEl.select();
2479
+ });
2480
+ } else if (textEl) {
2481
+ textEl.select();
2482
+ }
2483
+ });
2484
+ }
2485
+ }
2486
+
2487
+ function loadAgentContext() {
2488
+ if (!agentContentEl) return;
2489
+ agentContentEl.innerHTML = '<div class="empty-state">Building agent context...</div>';
2490
+ fetchJson('/__kitchen/api/agent-context', function(err, data) {
2491
+ if (err) {
2492
+ agentContentEl.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
2493
+ return;
2494
+ }
2495
+ renderAgentContext(data);
2496
+ });
2497
+ }
2498
+
2499
+ document.getElementById('refresh-agent').addEventListener('click', loadAgentContext);
2500
+
2501
+ // ─── Errors (plan 18 P1-4) ──────────────────
2502
+ var errorsSummaryEl = document.getElementById('errors-summary-text');
2503
+ var errorsListEl = document.getElementById('errors-list');
2504
+
2505
+ function fmtRelTime(ts) {
2506
+ if (!ts) return '-';
2507
+ var diff = Date.now() - ts;
2508
+ if (diff < 0) diff = 0;
2509
+ if (diff < 5_000) return 'just now';
2510
+ if (diff < 60_000) return Math.round(diff / 1000) + 's ago';
2511
+ if (diff < 3_600_000) return Math.round(diff / 60_000) + 'm ago';
2512
+ if (diff < 86_400_000) return Math.round(diff / 3_600_000) + 'h ago';
2513
+ return new Date(ts).toLocaleString();
2514
+ }
2515
+
2516
+ function loadErrors() {
2517
+ if (!errorsListEl) return;
2518
+ errorsListEl.innerHTML = '<div class="empty-state">Loading errors...</div>';
2519
+ fetchJson('/__kitchen/api/errors/grouped', function(err, data) {
2520
+ if (err) {
2521
+ errorsListEl.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
2522
+ return;
2523
+ }
2524
+ var groups = data.groups || [];
2525
+ var total = data.totalCount || 0;
2526
+ var groupCount = data.groupCount || 0;
2527
+ if (errorsSummaryEl) {
2528
+ errorsSummaryEl.textContent = total === 0
2529
+ ? 'No errors captured yet.'
2530
+ : total + ' raw error(s) collapsed into ' + groupCount + ' group(s).';
2531
+ }
2532
+ if (!groups.length) {
2533
+ errorsListEl.innerHTML = '<div class="empty-state">No errors captured yet.</div>';
2534
+ return;
2535
+ }
2536
+ var html = '';
2537
+ for (var i = 0; i < groups.length; i++) {
2538
+ var g = groups[i];
2539
+ var sample = g.sample || {};
2540
+ var sources = (g.affectedSources || []).join(', ') || sample.source || 'unknown';
2541
+ var sev = sample.severity || g.severity || 'error';
2542
+ var countCls = g.count >= 5 ? 'error-count' : 'error-count dim';
2543
+ var stackBlock = sample.stack
2544
+ ? '<div class="error-detail">' + escapeHtml(sample.stack) + '</div>'
2545
+ : '<div class="error-detail">No stack frame captured.</div>';
2546
+ html += '<div class="error-group" data-key="' + escapeHtml(g.key) + '">' +
2547
+ '<div class="error-group-head">' +
2548
+ '<span class="' + countCls + '">x' + g.count + '</span>' +
2549
+ '<span class="error-severity">' + escapeHtml(sev) + '</span>' +
2550
+ '<div class="error-message">' + escapeHtml(sample.message || '(no message)') + '</div>' +
2551
+ '</div>' +
2552
+ '<div class="error-meta">' +
2553
+ '<span>first: ' + escapeHtml(fmtRelTime(g.firstSeen)) + '</span>' +
2554
+ '<span>last: ' + escapeHtml(fmtRelTime(g.lastSeen)) + '</span>' +
2555
+ '<span>sources: ' + escapeHtml(sources) + '</span>' +
2556
+ (sample.line ? '<span>line ' + escapeHtml(String(sample.line)) + '</span>' : '') +
2557
+ '</div>' +
2558
+ stackBlock +
2559
+ '</div>';
2560
+ }
2561
+ errorsListEl.innerHTML = html;
2562
+ var rows = errorsListEl.querySelectorAll('.error-group');
2563
+ for (var r = 0; r < rows.length; r++) {
2564
+ rows[r].addEventListener('click', function() {
2565
+ this.classList.toggle('expanded');
2566
+ });
2567
+ }
2568
+ });
2569
+ }
2570
+
2571
+ var refreshErrorsBtn = document.getElementById('refresh-errors');
2572
+ if (refreshErrorsBtn) refreshErrorsBtn.addEventListener('click', loadErrors);
2573
+
2574
+ var clearErrorsBtn = document.getElementById('clear-errors');
2575
+ if (clearErrorsBtn) clearErrorsBtn.addEventListener('click', function() {
2576
+ fetch('/__kitchen/api/errors', { method: 'DELETE' })
2577
+ .then(function() { loadErrors(); })
2578
+ .catch(function() { /* noop */ });
2579
+ });
2580
+
2581
+ function loadRequests() {
2582
+ requestsListEl.innerHTML = '<div class="empty-state">Loading requests...</div>';
2206
2583
  fetchJson('/__kitchen/api/requests?limit=100', function(err, data) {
2207
2584
  if (err) {
2208
2585
  requestsListEl.innerHTML = '<div class="empty-state">Failed: ' + escapeHtml(err.message) + '</div>';
@@ -2399,13 +2776,14 @@ const JS = /* js */ `
2399
2776
  document.getElementById('refresh-metrics').addEventListener('click', loadMetrics);
2400
2777
 
2401
2778
  // Lazy-load new tab data when clicked (avoids loading everything up front)
2402
- var tabLoaders = {
2403
- 'agent': loadAgentContext,
2404
- 'requests': loadRequests,
2405
- 'mcp-activity': loadMcpActivity,
2406
- 'cache': loadCacheStats,
2407
- 'metrics': loadMetrics
2408
- };
2779
+ var tabLoaders = {
2780
+ 'agent': loadAgentContext,
2781
+ 'errors': loadErrors,
2782
+ 'requests': loadRequests,
2783
+ 'mcp-activity': loadMcpActivity,
2784
+ 'cache': loadCacheStats,
2785
+ 'metrics': loadMetrics
2786
+ };
2409
2787
  var loadedTabs = {};
2410
2788
  for (var t = 0; t < tabs.length; t++) {
2411
2789
  tabs[t].addEventListener('click', function() {