kiku-agent 1.3.1 → 1.3.3

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 (232) hide show
  1. package/README.md +69 -12
  2. package/package.json +5 -12
  3. package/server/agent/agentCore.js +319 -46
  4. package/server/agent/appPaths.js +31 -0
  5. package/server/agent/audioPlayer.js +339 -12
  6. package/server/agent/autoContext.js +193 -0
  7. package/server/agent/catalogEntityResolver.js +170 -28
  8. package/server/agent/clarifyingQuestions.js +137 -35
  9. package/server/agent/cli.js +177 -48
  10. package/server/agent/cloudAdapters.js +116 -0
  11. package/server/agent/commandRegistry.js +409 -80
  12. package/server/agent/commands.js +1056 -125
  13. package/server/agent/config.js +1088 -690
  14. package/server/agent/contextCompactor.js +213 -21
  15. package/server/agent/cpuMetrics.js +199 -0
  16. package/server/agent/engineService.js +591 -0
  17. package/server/agent/eval/AGENTS.md +228 -0
  18. package/server/agent/eval/analyze.py +684 -0
  19. package/server/agent/eval/gates.json +15 -0
  20. package/server/agent/eval/modules/analysis/compare.js +198 -0
  21. package/server/agent/eval/modules/analysis/gates.js +72 -0
  22. package/server/agent/eval/modules/analysis/judge.js +70 -0
  23. package/server/agent/eval/modules/core/cli.js +453 -0
  24. package/server/agent/eval/modules/core/config.js +60 -0
  25. package/server/agent/eval/modules/core/store.js +236 -0
  26. package/server/agent/eval/modules/evaluation/cases.js +689 -0
  27. package/server/agent/eval/modules/evaluation/runner.js +288 -0
  28. package/server/agent/eval/modules/evaluation/scoring.js +363 -0
  29. package/server/agent/eval/modules/evaluation/worker.js +122 -0
  30. package/server/agent/eval/modules/reporting/benchmark.js +265 -0
  31. package/server/agent/eval/modules/reporting/benchmarkPng.js +487 -0
  32. package/server/agent/eval/modules/reporting/dashboard.js +1406 -0
  33. package/server/agent/eval/modules/server/evalApi.js +1099 -0
  34. package/server/agent/eval/modules/ui/evalUi.js +4799 -0
  35. package/server/agent/eval/modules/utils/smokes.js +119 -0
  36. package/server/agent/eventBus.js +1 -0
  37. package/server/agent/ink/main.jsx +196 -191
  38. package/server/agent/jobManager.js +11 -1
  39. package/server/agent/lanDiscovery.js +721 -23
  40. package/server/agent/llmClient.js +317 -71
  41. package/server/agent/lmStudioWakeup.js +517 -65
  42. package/server/agent/locationService.js +797 -0
  43. package/server/agent/mcpQuizData.js +509 -0
  44. package/server/agent/memory/contextAllocator.js +19 -7
  45. package/server/agent/memory.js +3 -1
  46. package/server/agent/modelCompatibility.js +352 -0
  47. package/server/agent/modelDefaults.js +1 -0
  48. package/server/agent/modelManager.js +46 -2
  49. package/server/agent/newsWatch.js +9 -3
  50. package/server/agent/processManager.js +229 -0
  51. package/server/agent/prompts.js +174 -8
  52. package/server/agent/routingInterceptor.js +9 -1
  53. package/server/agent/sanitizer.js +41 -6
  54. package/server/agent/searchQueryPlanner.js +202 -0
  55. package/server/agent/session.js +59 -4
  56. package/server/agent/tests/agentBubbleSystem.test.js +65 -0
  57. package/server/agent/tests/agentFollowUps.test.js +253 -0
  58. package/server/agent/tests/agentMusicControl.test.js +245 -0
  59. package/server/agent/tests/apiRouter.test.js +108 -0
  60. package/server/agent/tests/architectureFixes.test.js +81 -0
  61. package/server/agent/tests/architectureRefactoring.test.js +194 -0
  62. package/server/agent/tests/autoContext.test.js +176 -0
  63. package/server/agent/tests/autoResearch.test.js +64 -0
  64. package/server/agent/tests/bilibiliEmbed.test.js +296 -0
  65. package/server/agent/tests/catalogSync.test.js +130 -0
  66. package/server/agent/tests/chatHardening.test.js +121 -0
  67. package/server/agent/tests/chineseWebSearchEscalation.test.js +62 -0
  68. package/server/agent/tests/citations.test.js +180 -0
  69. package/server/agent/tests/clarificationAnalytics.test.js +219 -0
  70. package/server/agent/tests/clarifyingQuestions.test.js +627 -0
  71. package/server/agent/tests/cliAutomation.test.js +158 -0
  72. package/server/agent/tests/cloudProviders.test.js +322 -0
  73. package/server/agent/tests/commandInput.test.js +24 -0
  74. package/server/agent/tests/commands.test.js +506 -0
  75. package/server/agent/tests/contextIntelligence.test.js +209 -0
  76. package/server/agent/tests/conversationSafety.test.js +53 -0
  77. package/server/agent/tests/cpuMetrics.test.js +101 -0
  78. package/server/agent/tests/decisionAndSearchEnhancements.test.js +167 -0
  79. package/server/agent/tests/dramaAnswerQuality.test.js +73 -0
  80. package/server/agent/tests/dynamicCatalogIngestion.test.js +285 -0
  81. package/server/agent/tests/dynamicRoutingAndSearch.test.js +92 -0
  82. package/server/agent/tests/edgeCasesScenarios.test.js +131 -0
  83. package/server/agent/tests/engineService.test.js +436 -0
  84. package/server/agent/tests/engineUnload.test.js +94 -0
  85. package/server/agent/tests/entertainmentMetrics.test.js +126 -0
  86. package/server/agent/tests/envBootstrap.test.js +87 -0
  87. package/server/agent/tests/envCommand.test.js +284 -0
  88. package/server/agent/tests/errorHandling.test.js +80 -0
  89. package/server/agent/tests/errors.test.js +13 -0
  90. package/server/agent/tests/evalAnalyze.test.js +54 -0
  91. package/server/agent/tests/evalCommand.test.js +54 -0
  92. package/server/agent/tests/evalCorpus.test.js +126 -0
  93. package/server/agent/tests/evalHarness.test.js +62 -0
  94. package/server/agent/tests/evalJudgeContext.test.js +38 -0
  95. package/server/agent/tests/evalMetrics.test.js +320 -0
  96. package/server/agent/tests/evalStore.test.js +190 -0
  97. package/server/agent/tests/evalStudio.test.js +179 -0
  98. package/server/agent/tests/feedbackLearning.test.js +186 -0
  99. package/server/agent/tests/fetchApi.test.js +99 -0
  100. package/server/agent/tests/firstRun.test.js +44 -0
  101. package/server/agent/tests/gemma4eOptimization.test.js +95 -0
  102. package/server/agent/tests/hardwareTiers.test.js +206 -0
  103. package/server/agent/tests/hybridWebSearch.test.js +194 -0
  104. package/server/agent/tests/judgeService.test.js +251 -0
  105. package/server/agent/tests/lanDiscovery.test.js +324 -0
  106. package/server/agent/tests/lanInterfaces.test.js +510 -0
  107. package/server/agent/tests/lmStudioWakeup.test.js +278 -0
  108. package/server/agent/tests/locationTool.test.js +278 -0
  109. package/server/agent/tests/mcpClient.test.js +201 -0
  110. package/server/agent/tests/memoryRoute.test.js +173 -0
  111. package/server/agent/tests/memoryUiNormalization.test.js +75 -0
  112. package/server/agent/tests/modelCompatibility.test.js +148 -0
  113. package/server/agent/tests/modelManager.test.js +141 -0
  114. package/server/agent/tests/multilingualScenarios.test.js +69 -0
  115. package/server/agent/tests/musicControlUiBridge.test.js +113 -0
  116. package/server/agent/tests/newCommands.test.js +347 -0
  117. package/server/agent/tests/newCommandsTui.test.js +141 -0
  118. package/server/agent/tests/newTools.test.js +113 -0
  119. package/server/agent/tests/newsIntelligence.test.js +178 -0
  120. package/server/agent/tests/nextGenArchitecture.test.js +101 -0
  121. package/server/agent/tests/offlineInput.test.js +217 -0
  122. package/server/agent/tests/openDomainPrompt.test.js +197 -0
  123. package/server/agent/tests/openDomainSearch.test.js +121 -0
  124. package/server/agent/tests/packagingGuard.test.js +57 -0
  125. package/server/agent/tests/presentation.test.js +61 -0
  126. package/server/agent/tests/processManager.test.js +144 -0
  127. package/server/agent/tests/providerPersistence.test.js +44 -0
  128. package/server/agent/tests/queryBenchmarks.test.js +83 -0
  129. package/server/agent/tests/queryDetails.test.js +126 -0
  130. package/server/agent/tests/quickActionsAndModals.test.js +180 -0
  131. package/server/agent/tests/realWorldValidation.test.js +391 -0
  132. package/server/agent/tests/resilienceAndConcurrency.test.js +117 -0
  133. package/server/agent/tests/runtimeWatchdog.test.js +32 -0
  134. package/server/agent/tests/sanitizer.test.js +37 -0
  135. package/server/agent/tests/scenariosData.js +378 -0
  136. package/server/agent/tests/schemaRouting.test.js +54 -0
  137. package/server/agent/tests/searchQueryPlanner.test.js +108 -0
  138. package/server/agent/tests/securityAuditRemediation.test.js +221 -0
  139. package/server/agent/tests/securityAuditRemediations2.test.js +154 -0
  140. package/server/agent/tests/securityHardening.test.js +146 -0
  141. package/server/agent/tests/securitySanitizer.test.js +38 -0
  142. package/server/agent/tests/sessionAndCopy.test.js +267 -0
  143. package/server/agent/tests/sidebarControls.test.js +135 -0
  144. package/server/agent/tests/statefulMemory.test.js +277 -0
  145. package/server/agent/tests/subagent.test.js +216 -0
  146. package/server/agent/tests/subagentProgress.test.js +109 -0
  147. package/server/agent/tests/subagentTextFallback.test.js +115 -0
  148. package/server/agent/tests/suggestions.test.js +199 -0
  149. package/server/agent/tests/suggestionsTui.test.js +251 -0
  150. package/server/agent/tests/telemetryAndProfiles.test.js +179 -0
  151. package/server/agent/tests/terminalImage.test.js +80 -0
  152. package/server/agent/tests/terminalMarkdown.test.js +166 -0
  153. package/server/agent/tests/terminalText.test.js +133 -0
  154. package/server/agent/tests/textProtocolLoop.test.js +197 -0
  155. package/server/agent/tests/textToolParsing.test.js +187 -0
  156. package/server/agent/tests/theme.test.js +26 -0
  157. package/server/agent/tests/toolRegistryParity.test.js +53 -0
  158. package/server/agent/tests/toolRoutingRegressions.test.js +160 -0
  159. package/server/agent/tests/tools.test.js +259 -0
  160. package/server/agent/tests/unifiedEndpoints.test.js +159 -0
  161. package/server/agent/tests/v110Features.test.js +262 -0
  162. package/server/agent/tests/versionBump.test.js +69 -0
  163. package/server/agent/tests/vramEstimator.test.js +595 -0
  164. package/server/agent/tests/webChatDocs.test.js +199 -0
  165. package/server/agent/tests/webChatInferenceControl.test.js +298 -0
  166. package/server/agent/tests/webChatInspection.test.js +551 -0
  167. package/server/agent/tests/webChatMcp.test.js +94 -0
  168. package/server/agent/tests/webChatMemory.test.js +112 -0
  169. package/server/agent/tests/webChatSecurity.test.js +169 -0
  170. package/server/agent/tests/webExtract.test.js +284 -0
  171. package/server/agent/tests/webSources.test.js +281 -0
  172. package/server/agent/textToolProtocol.js +173 -46
  173. package/server/agent/toolRouter.js +318 -44
  174. package/server/agent/tools.js +877 -96
  175. package/server/agent/ui.js +39 -34
  176. package/server/agent/vramService.js +35 -54
  177. package/server/agent/webSearch.js +441 -162
  178. package/server/agent/websearch/orchestrator.js +15 -5
  179. package/server/agent/websearch/resilience/cache.js +9 -2
  180. package/server/agent/websearch/security/ssrf.js +41 -10
  181. package/server/agent/youtubeAuth.js +5 -20
  182. package/server/apiRouter.js +5558 -4422
  183. package/server/eval/judgeRubrics.js +110 -0
  184. package/server/eval/judgeService.js +290 -0
  185. package/server/eval/runBenchmark.js +182 -0
  186. package/server/mcp/data/discography.js +2 -2
  187. package/server/mcp/examples/antigravity.example.json +17 -0
  188. package/server/mcp/examples/librechat.example.yaml +16 -0
  189. package/server/mcp/handler.js +8 -4
  190. package/server/mcp/security.js +220 -21
  191. package/server/mcp/server.js +2 -1
  192. package/server/mcp/tests/connection.test.js +162 -0
  193. package/server/mcp/tests/mcpKeyAuth.test.js +156 -0
  194. package/server/mcp/tools.js +7 -1
  195. package/server/supabase_migration_prefix.sql +102 -0
  196. package/server/supabase_schema.sql +63 -0
  197. package/server/web-chat/core/connection.js +179 -218
  198. package/server/web-chat/core/conversationContext.js +9 -2
  199. package/server/web-chat/core/directCatalogEngine.js +52 -28
  200. package/server/web-chat/core/dramaKnowledge.js +33 -22
  201. package/server/web-chat/core/followUpEngine.js +735 -81
  202. package/server/web-chat/core/futureEventGuard.js +5 -1
  203. package/server/web-chat/core/musicControlAction.js +70 -0
  204. package/server/web-chat/core/security.js +32 -21
  205. package/server/web-chat/core/toolInspector.js +6 -6
  206. package/server/web-chat/core/urlGuard.js +45 -0
  207. package/server/web-chat/router.js +204 -157
  208. package/server/web-chat/routes/chatRoute.js +885 -488
  209. package/server/web-chat/routes/engineRoute.js +270 -0
  210. package/server/web-chat/routes/exportRoute.js +1 -1
  211. package/server/web-chat/routes/lanDiscoveryRoute.js +17 -2
  212. package/server/web-chat/routes/lmStudioLoadRoute.js +172 -0
  213. package/server/web-chat/routes/mcpRoute.js +386 -0
  214. package/server/web-chat/routes/memoryRoute.js +253 -0
  215. package/server/web-chat/routes/providerRoute.js +49 -16
  216. package/server/web-chat/routes/staticRoute.js +306 -17
  217. package/server/web-chat/routes/statusRoute.js +194 -73
  218. package/server/web-chat/routes/suggestRoute.js +11 -3
  219. package/server/web-chat/routes/verifyRoute.js +26 -4
  220. package/server/web-chat/server.js +7 -6
  221. package/server/web-chat/ui/docsBuilder.js +3558 -0
  222. package/server/web-chat/ui/htmlBuilder.js +12 -0
  223. package/server/web-chat/ui/scripts/client.js +3401 -1302
  224. package/server/web-chat/ui/scripts/webchatI18n.js +155 -153
  225. package/server/web-chat/ui/settingsBuilder.js +7870 -0
  226. package/server/web-chat/ui/styles/lan-card.css +165 -0
  227. package/server/web-chat/ui/styles/main.css +4289 -447
  228. package/server/web-chat/ui/sw.js +64 -0
  229. package/server/web-chat/ui/templates/body.html +407 -281
  230. package/src/data/awardsData.js +146 -146
  231. package/src/data/musicVideoData.js +6 -4
  232. package/src/lib/vram/estimator.js +195 -19
@@ -0,0 +1,487 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { parseArgs } from 'node:util';
5
+ import { deflateSync } from 'node:zlib';
6
+ import { BENCHMARK_ROWS, ROADMAP, computeBenchmarkSummary } from './benchmark.js';
7
+
8
+ const REPO_ROOT = fileURLToPath(new URL('../../../../', import.meta.url));
9
+
10
+ const FONT = {
11
+ 32: [0, 0, 0, 0, 0, 0, 0],
12
+ 33: [4, 4, 4, 4, 4, 0, 4],
13
+ 34: [10, 10, 10, 0, 0, 0, 0],
14
+ 35: [10, 10, 31, 10, 31, 10, 10],
15
+ 36: [4, 15, 20, 14, 5, 30, 4],
16
+ 37: [24, 25, 2, 4, 8, 13, 11],
17
+ 38: [12, 18, 20, 8, 21, 18, 13],
18
+ 39: [4, 4, 8, 0, 0, 0, 0],
19
+ 40: [2, 4, 8, 8, 8, 4, 2],
20
+ 41: [8, 4, 2, 2, 2, 4, 8],
21
+ 42: [0, 4, 21, 14, 21, 4, 0],
22
+ 43: [0, 4, 4, 31, 4, 4, 0],
23
+ 44: [0, 0, 0, 0, 12, 4, 8],
24
+ 45: [0, 0, 0, 31, 0, 0, 0],
25
+ 46: [0, 0, 0, 0, 12, 12, 0],
26
+ 47: [1, 1, 2, 4, 8, 16, 16],
27
+ 48: [14, 17, 19, 21, 25, 17, 14],
28
+ 49: [4, 12, 4, 4, 4, 4, 14],
29
+ 50: [14, 17, 1, 2, 4, 8, 31],
30
+ 51: [31, 2, 4, 2, 1, 17, 14],
31
+ 52: [2, 6, 10, 18, 31, 2, 2],
32
+ 53: [31, 16, 30, 1, 1, 17, 14],
33
+ 54: [6, 8, 16, 30, 17, 17, 14],
34
+ 55: [31, 1, 2, 4, 8, 8, 8],
35
+ 56: [14, 17, 17, 14, 17, 17, 14],
36
+ 57: [14, 17, 17, 15, 1, 2, 12],
37
+ 58: [0, 12, 12, 0, 12, 12, 0],
38
+ 59: [0, 12, 12, 0, 12, 4, 8],
39
+ 60: [2, 4, 8, 16, 8, 4, 2],
40
+ 61: [0, 0, 31, 0, 31, 0, 0],
41
+ 62: [8, 4, 2, 1, 2, 4, 8],
42
+ 63: [14, 17, 1, 2, 4, 0, 4],
43
+ 64: [14, 17, 27, 21, 23, 16, 14],
44
+ 65: [14, 17, 17, 31, 17, 17, 17],
45
+ 66: [30, 17, 17, 30, 17, 17, 30],
46
+ 67: [14, 17, 16, 16, 16, 17, 14],
47
+ 68: [28, 18, 17, 17, 17, 18, 28],
48
+ 69: [31, 16, 16, 30, 16, 16, 31],
49
+ 70: [31, 16, 16, 30, 16, 16, 16],
50
+ 71: [14, 17, 16, 23, 17, 17, 14],
51
+ 72: [17, 17, 17, 31, 17, 17, 17],
52
+ 73: [14, 4, 4, 4, 4, 4, 14],
53
+ 74: [7, 2, 2, 2, 2, 18, 12],
54
+ 75: [17, 18, 20, 24, 20, 18, 17],
55
+ 76: [16, 16, 16, 16, 16, 16, 31],
56
+ 77: [17, 27, 21, 21, 17, 17, 17],
57
+ 78: [17, 25, 25, 21, 19, 19, 17],
58
+ 79: [14, 17, 17, 17, 17, 17, 14],
59
+ 80: [30, 17, 17, 30, 16, 16, 16],
60
+ 81: [14, 17, 17, 17, 21, 18, 13],
61
+ 82: [30, 17, 17, 30, 20, 18, 17],
62
+ 83: [14, 17, 16, 14, 1, 17, 14],
63
+ 84: [31, 4, 4, 4, 4, 4, 4],
64
+ 85: [17, 17, 17, 17, 17, 17, 14],
65
+ 86: [17, 17, 17, 17, 17, 10, 4],
66
+ 87: [17, 17, 17, 21, 21, 27, 17],
67
+ 88: [17, 17, 10, 4, 10, 17, 17],
68
+ 89: [17, 17, 10, 4, 4, 4, 4],
69
+ 90: [31, 1, 2, 4, 8, 16, 31],
70
+ 91: [14, 8, 8, 8, 8, 8, 14],
71
+ 92: [16, 16, 8, 4, 2, 1, 1],
72
+ 93: [14, 2, 2, 2, 2, 2, 14],
73
+ 94: [4, 10, 17, 0, 0, 0, 0],
74
+ 95: [0, 0, 0, 0, 0, 0, 31],
75
+ 96: [8, 4, 2, 0, 0, 0, 0],
76
+ 97: [0, 0, 14, 1, 15, 17, 15],
77
+ 98: [16, 16, 30, 17, 17, 17, 30],
78
+ 99: [0, 0, 14, 16, 16, 17, 14],
79
+ 100: [1, 1, 15, 17, 17, 17, 15],
80
+ 101: [0, 0, 14, 17, 30, 16, 14],
81
+ 102: [6, 8, 8, 30, 8, 8, 8],
82
+ 103: [0, 14, 17, 17, 15, 16, 14],
83
+ 104: [16, 16, 30, 17, 17, 17, 17],
84
+ 105: [4, 0, 12, 4, 4, 4, 14],
85
+ 106: [2, 0, 6, 2, 2, 18, 12],
86
+ 107: [16, 16, 18, 20, 24, 20, 18],
87
+ 108: [12, 4, 4, 4, 4, 4, 14],
88
+ 109: [0, 0, 26, 21, 21, 21, 21],
89
+ 110: [0, 0, 30, 17, 17, 17, 17],
90
+ 111: [0, 0, 14, 17, 17, 17, 14],
91
+ 112: [0, 0, 30, 17, 17, 30, 16],
92
+ 113: [0, 0, 15, 17, 17, 15, 1],
93
+ 114: [0, 0, 13, 18, 16, 16, 16],
94
+ 115: [0, 0, 15, 16, 14, 1, 30],
95
+ 116: [8, 8, 30, 8, 8, 9, 6],
96
+ 117: [0, 0, 17, 17, 17, 17, 15],
97
+ 118: [0, 0, 17, 17, 17, 10, 4],
98
+ 119: [0, 0, 17, 17, 21, 21, 10],
99
+ 120: [0, 0, 17, 10, 4, 10, 17],
100
+ 121: [0, 0, 17, 17, 15, 1, 14],
101
+ 122: [0, 0, 31, 2, 4, 8, 31],
102
+ 123: [2, 4, 4, 8, 4, 4, 2],
103
+ 124: [4, 4, 4, 4, 4, 4, 4],
104
+ 125: [8, 4, 4, 2, 4, 4, 8],
105
+ 126: [0, 0, 0, 9, 21, 0, 0]
106
+ };
107
+
108
+ const C_BG = [11, 15, 25];
109
+ const C_CARD = [22, 27, 46];
110
+ const C_LINE = [45, 55, 85];
111
+ const C_TEXT = [241, 245, 249];
112
+ const C_MUTED = [148, 163, 184];
113
+ const C_ROSE = [244, 63, 94];
114
+ const C_GREEN = [52, 211, 153];
115
+ const C_AMBER = [251, 191, 36];
116
+ const C_RED = [248, 113, 113];
117
+ const C_PURPLE = [165, 180, 252];
118
+ const C_GRAY = [148, 163, 184];
119
+
120
+ const PARETO_COLORS = { candidate: C_GREEN, baseline: C_AMBER, legacy: C_GRAY, live: C_ROSE };
121
+
122
+ function asciiOnly(s) {
123
+ return String(s ?? '').split('').map(ch => {
124
+ const code = ch.charCodeAt(0);
125
+ if (code >= 32 && code <= 126) return ch;
126
+ if (code === 8594) return '>';
127
+ return ' ';
128
+ }).join('');
129
+ }
130
+
131
+ function charStep(scale) {
132
+ return 6 * scale;
133
+ }
134
+
135
+ function textWidth(s, scale) {
136
+ return asciiOnly(s).length * charStep(scale);
137
+ }
138
+
139
+ function wrapText(s, maxChars) {
140
+ const words = asciiOnly(s).split(' ').filter(w => w.length > 0);
141
+ const lines = [];
142
+ let cur = '';
143
+ for (const w of words) {
144
+ if ((cur + (cur ? ' ' : '') + w).length <= maxChars) {
145
+ cur = cur ? cur + ' ' + w : w;
146
+ } else {
147
+ if (cur) lines.push(cur);
148
+ if (w.length > maxChars) {
149
+ for (let i = 0; i < w.length; i += maxChars) lines.push(w.slice(i, i + maxChars));
150
+ cur = '';
151
+ } else {
152
+ cur = w;
153
+ }
154
+ }
155
+ }
156
+ if (cur) lines.push(cur);
157
+ if (!lines.length) lines.push('');
158
+ return lines;
159
+ }
160
+
161
+ function fitText(s, maxChars) {
162
+ const t = asciiOnly(s);
163
+ if (t.length <= maxChars) return t;
164
+ return t.slice(0, Math.max(0, maxChars - 1)) + '>';
165
+ }
166
+
167
+ function makeCanvas(w, h) {
168
+ return { w, h, buf: Buffer.alloc(w * h * 3, 0) };
169
+ }
170
+
171
+ function fillRect(cv, x, y, w, h, c) {
172
+ const x0 = Math.max(0, Math.round(x));
173
+ const y0 = Math.max(0, Math.round(y));
174
+ const x1 = Math.min(cv.w, Math.round(x + w));
175
+ const y1 = Math.min(cv.h, Math.round(y + h));
176
+ for (let yy = y0; yy < y1; yy++) {
177
+ for (let xx = x0; xx < x1; xx++) {
178
+ const o = (yy * cv.w + xx) * 3;
179
+ cv.buf[o] = c[0];
180
+ cv.buf[o + 1] = c[1];
181
+ cv.buf[o + 2] = c[2];
182
+ }
183
+ }
184
+ }
185
+
186
+ function drawChar(cv, ch, x, y, scale, c) {
187
+ const code = asciiOnly(ch).charCodeAt(0);
188
+ const glyph = FONT[code] || FONT[32];
189
+ for (let r = 0; r < 7; r++) {
190
+ const bits = glyph[r];
191
+ for (let col = 0; col < 5; col++) {
192
+ if (bits & (1 << (4 - col))) {
193
+ fillRect(cv, x + col * scale, y + r * scale, scale, scale, c);
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ function drawText(cv, s, x, y, scale, c) {
200
+ const t = asciiOnly(s);
201
+ for (let i = 0; i < t.length; i++) {
202
+ drawChar(cv, t[i], x + i * charStep(scale), y, scale, c);
203
+ }
204
+ }
205
+
206
+ function drawCircle(cv, cx, cy, r, c) {
207
+ const rr = Math.round(r);
208
+ for (let yy = -rr; yy <= rr; yy++) {
209
+ for (let xx = -rr; xx <= rr; xx++) {
210
+ if (xx * xx + yy * yy <= rr * rr) {
211
+ const px = Math.round(cx) + xx;
212
+ const py = Math.round(cy) + yy;
213
+ if (px >= 0 && py >= 0 && px < cv.w && py < cv.h) {
214
+ const o = (py * cv.w + px) * 3;
215
+ cv.buf[o] = c[0];
216
+ cv.buf[o + 1] = c[1];
217
+ cv.buf[o + 2] = c[2];
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ const CRC_TABLE = (() => {
225
+ const t = new Int32Array(256);
226
+ for (let n = 0; n < 256; n++) {
227
+ let c = n;
228
+ for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
229
+ t[n] = c;
230
+ }
231
+ return t;
232
+ })();
233
+
234
+ function crc32(buf) {
235
+ let c = 0xffffffff;
236
+ for (let i = 0; i < buf.length; i++) c = CRC_TABLE[(c ^ buf[i]) & 255] ^ (c >>> 8);
237
+ return (c ^ 0xffffffff) >>> 0;
238
+ }
239
+
240
+ function chunk(type, data) {
241
+ const len = Buffer.alloc(4);
242
+ len.writeUInt32BE(data.length, 0);
243
+ const td = Buffer.from(type, 'ascii');
244
+ const crc = Buffer.alloc(4);
245
+ crc.writeUInt32BE(crc32(Buffer.concat([td, data])), 0);
246
+ return Buffer.concat([len, td, data, crc]);
247
+ }
248
+
249
+ function encodePng(cv) {
250
+ const stride = cv.w * 3 + 1;
251
+ const raw = Buffer.alloc(stride * cv.h, 0);
252
+ for (let y = 0; y < cv.h; y++) {
253
+ raw[y * stride] = 0;
254
+ cv.buf.copy(raw, y * stride + 1, y * cv.w * 3, (y + 1) * cv.w * 3);
255
+ }
256
+ const ihdr = Buffer.alloc(13);
257
+ ihdr.writeUInt32BE(cv.w, 0);
258
+ ihdr.writeUInt32BE(cv.h, 4);
259
+ ihdr[8] = 8;
260
+ ihdr[9] = 2;
261
+ ihdr[10] = 0;
262
+ ihdr[11] = 0;
263
+ ihdr[12] = 0;
264
+ const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
265
+ return Buffer.concat([sig, chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0))]);
266
+ }
267
+
268
+ function formatObserved(row, values) {
269
+ const v = values[row.key];
270
+ if (v === null || v === undefined) return 'N-A';
271
+ if (row.format === 'percent') return (v * 100).toFixed(1) + '%';
272
+ if (row.format === 'cost') return '$' + Number(v).toFixed(3);
273
+ return Number(v).toFixed(2);
274
+ }
275
+
276
+ function statusOf(statusMap, key) {
277
+ const st = statusMap[key];
278
+ if (st === true) return { label: 'PASS', color: C_GREEN };
279
+ if (st === false) return { label: 'FAIL', color: C_RED };
280
+ return { label: 'N-A', color: C_MUTED };
281
+ }
282
+
283
+ export function renderBenchmarkPng(rows = [], summary = {}, opts = {}) {
284
+ const width = Math.max(900, Math.min(2400, Number(opts.width) || 1440));
285
+ const S = 2;
286
+ const ST = 3;
287
+ const margin = 48;
288
+ const W = width - margin * 2;
289
+ const lineH = 7 * S + 8;
290
+ const mathOnly = (opts.mode || 'full') === 'math';
291
+ const bench = summary && summary.benchmark && summary.benchmark.values ? summary.benchmark : computeBenchmarkSummary(rows, summary);
292
+ const values = bench.values;
293
+ const model = asciiOnly((summary && summary.model) || (rows && rows.model) || 'agent-model');
294
+ const stamp = new Date().toISOString().slice(0, 19).replace('T', ' ');
295
+
296
+ const colDim = Math.round(W * 0.24);
297
+ const colInd = Math.round(W * 0.35);
298
+ const colTgt = Math.round(W * 0.15);
299
+ const colObs = Math.round(W * 0.13);
300
+ const colSta = W - colDim - colInd - colTgt - colObs;
301
+ const maxDim = Math.floor(colDim / charStep(S)) - 1;
302
+ const maxInd = Math.floor(colInd / charStep(S)) - 1;
303
+ const maxTgt = Math.floor(colTgt / charStep(S)) - 1;
304
+ const maxObs = Math.floor(colObs / charStep(S)) - 1;
305
+
306
+ const tableRows = BENCHMARK_ROWS.map(r => {
307
+ const dim = wrapText(r.dimension, maxDim);
308
+ const ind = wrapText(r.indicator + ' [' + r.benchmark + ']', maxInd);
309
+ const lines = Math.max(dim.length, ind.length, 1);
310
+ return { def: r, observed: formatObserved(r, values), state: statusOf(bench.status, r.key), dim, ind, lines };
311
+ });
312
+
313
+ const failKeys = Object.keys(bench.failureDistribution || {});
314
+ const formulas = [
315
+ 'Pass@k = E[ 1 - C(n-c,k) / C(n,k) ] (n runs, c passes)',
316
+ 'eta_traj = L* / max(L, L*) (L* shortest path, L actual)',
317
+ 'S_faith = sum I(Claim in K) / m (K tool output, m claims)'
318
+ ];
319
+ const road = ROADMAP.map(p => ({
320
+ head: p.phase + ' ' + p.weeks + ': ' + p.title,
321
+ lines: p.actions.reduce((acc, a) => acc.concat(wrapText('- ' + a, Math.floor(W / charStep(S)) - 4)), [])
322
+ }));
323
+
324
+ const rowH = tableRows.map(r => r.lines * lineH + 18);
325
+ const tableH = 52 + rowH.reduce((a, b) => a + b, 0);
326
+ const paretoH = 430;
327
+ const failH = 60 + failKeys.length * 52;
328
+ const formulaH = 60 + formulas.length * 34;
329
+ const roadH = road.reduce((a, p) => a + 44 + p.lines.length * 30, 56);
330
+ const totalH = mathOnly
331
+ ? 330 + tableH + formulaH
332
+ : 390 + tableH + 40 + paretoH + 40 + failH + 40 + formulaH + 40 + roadH + 60;
333
+
334
+ const cv = makeCanvas(width, totalH);
335
+ fillRect(cv, 0, 0, width, totalH, C_BG);
336
+ let y = 0;
337
+ fillRect(cv, 0, y, width, 150, C_CARD);
338
+ fillRect(cv, 0, 150, width, 4, C_ROSE);
339
+ drawText(cv, 'Kiku Agent Benchmark', margin, y + 30, ST, C_TEXT);
340
+ drawText(cv, 'ISO-IEC 42001 + NIST AI RMF | model: ' + model, margin, y + 92, S, C_MUTED);
341
+ drawText(cv, stamp + ' UTC', width - margin - textWidth(stamp + ' UTC', S), y + 92, S, C_MUTED);
342
+ y = 190;
343
+
344
+ const drawSection = (title) => {
345
+ drawText(cv, title, margin, y, S, C_PURPLE);
346
+ y += 40;
347
+ return y;
348
+ };
349
+
350
+ drawSection('Benchmark Dashboard');
351
+ const tx = margin;
352
+ fillRect(cv, tx, y, W, tableH, C_CARD);
353
+ drawText(cv, 'Metric Dimension', tx + 12, y + 14, S, C_MUTED);
354
+ drawText(cv, 'Indicator [Benchmark]', tx + colDim + 12, y + 14, S, C_MUTED);
355
+ drawText(cv, 'Target', tx + colDim + colInd + 12, y + 14, S, C_MUTED);
356
+ drawText(cv, 'Observed', tx + colDim + colInd + colTgt + 12, y + 14, S, C_MUTED);
357
+ drawText(cv, 'Status', tx + colDim + colInd + colTgt + colObs + 12, y + 14, S, C_MUTED);
358
+ let ry = y + 52;
359
+ tableRows.forEach((r, idx) => {
360
+ if (idx > 0) fillRect(cv, tx, ry - 9, W, 2, C_LINE);
361
+ r.dim.forEach((ln, i) => drawText(cv, ln, tx + 12, ry + i * lineH, S, C_TEXT));
362
+ r.ind.forEach((ln, i) => drawText(cv, ln, tx + colDim + 12, ry + i * lineH, S, i === 0 ? C_TEXT : C_MUTED));
363
+ drawText(cv, fitText(r.def.target, maxTgt), tx + colDim + colInd + 12, ry, S, C_MUTED);
364
+ drawText(cv, fitText(r.observed, maxObs), tx + colDim + colInd + colTgt + 12, ry, S, C_TEXT);
365
+ const bx = tx + colDim + colInd + colTgt + colObs + 12;
366
+ fillRect(cv, bx, ry - 2, 78, 26, r.state.color);
367
+ drawText(cv, r.state.label, bx + 8, ry + 2, S, C_BG);
368
+ ry += rowH[idx];
369
+ });
370
+ y += tableH + 40;
371
+
372
+ if (!mathOnly) {
373
+ drawSection('Trajectory vs Cost Pareto Frontier');
374
+ const px = margin;
375
+ const py = y;
376
+ const pw = W;
377
+ const ph = paretoH - 60;
378
+ fillRect(cv, px, py, pw, ph + 40, C_CARD);
379
+ const ox = px + 90;
380
+ const oy = py + ph - 30;
381
+ const axW = pw - 150;
382
+ const axH = ph - 80;
383
+ fillRect(cv, ox, oy, axW, 2, C_LINE);
384
+ fillRect(cv, ox, oy - axH, 2, axH, C_LINE);
385
+ for (let g = 0; g <= 4; g++) {
386
+ const gx = ox + Math.round(axW * g / 4);
387
+ fillRect(cv, gx, oy - axH, 2, axH, [28, 34, 56]);
388
+ drawText(cv, '$' + (0.08 * g / 4).toFixed(2), gx - 24, oy + 10, S, C_MUTED);
389
+ }
390
+ for (let g = 0; g <= 3; g++) {
391
+ const gy = oy - Math.round(axH * g / 3);
392
+ fillRect(cv, ox, gy, axW, 2, [28, 34, 56]);
393
+ drawText(cv, String(40 + Math.round(60 * g / 3)), ox - 48, gy - 6, S, C_MUTED);
394
+ }
395
+ const capX = ox + Math.round(axW / 2) - Math.round(textWidth('Cost per task (USD) 0.00 - 0.08', S) / 2);
396
+ drawText(cv, 'Cost per task (USD) 0.00 - 0.08', capX, oy + 32, S, C_MUTED);
397
+ drawText(cv, 'Success %', ox - 48, py + 8, S, C_MUTED);
398
+ const dotX = c => ox + Math.round(Math.min(Math.max(Number(c) || 0, 0), 0.08) * (axW / 0.08));
399
+ const dotY = s => oy - Math.round(((Math.min(Math.max(Number(s) || 0, 40), 100) - 40) / 60) * axH);
400
+ (bench.pareto || []).forEach((p, i) => {
401
+ const cx = dotX(p.cost);
402
+ const cy = dotY(p.successRate);
403
+ drawCircle(cv, cx, cy, p.kind === 'live' ? 9 : 7, PARETO_COLORS[p.kind] || C_TEXT);
404
+ const label = p.label + ' (' + p.successRate + '%, $' + Number(p.cost).toFixed(3) + ')';
405
+ const lx = Math.min(cx + 14, px + pw - textWidth(label, S) - 8);
406
+ const ly = Math.max(py + 8, cy - 24 - (i % 2) * 20);
407
+ drawText(cv, label, lx, ly, S, PARETO_COLORS[p.kind] || C_TEXT);
408
+ });
409
+ y += paretoH + 40;
410
+ }
411
+
412
+ if (!mathOnly) {
413
+ drawSection('Failure Mode Distribution');
414
+ fillRect(cv, margin, y, W, failH - 20, C_CARD);
415
+ let fy = y + 16;
416
+ for (const key of failKeys) {
417
+ const f = bench.failureDistribution[key];
418
+ const pct = Math.round((f.rate || 0) * 1000) / 10;
419
+ drawText(cv, fitText(f.label, 30), margin + 12, fy, S, C_TEXT);
420
+ const barX = margin + 380;
421
+ const barW = W - 380 - 200;
422
+ fillRect(cv, barX, fy, barW, 20, C_LINE);
423
+ fillRect(cv, barX, fy, Math.round(barW * (f.rate || 0)), 20, C_ROSE);
424
+ drawText(cv, pct.toFixed(1) + '% (' + f.count + ')', barX + barW + 16, fy, S, C_MUTED);
425
+ fy += 52;
426
+ }
427
+ y += failH + 40;
428
+ }
429
+
430
+ drawSection('Formulas');
431
+ fillRect(cv, margin, y, W, formulaH - 20, C_CARD);
432
+ let my = y + 16;
433
+ for (const f of formulas) {
434
+ drawText(cv, fitText(f, Math.floor(W / charStep(S)) - 4), margin + 12, my, S, C_TEXT);
435
+ my += 34;
436
+ }
437
+ y += formulaH + 40;
438
+
439
+ if (!mathOnly) {
440
+ drawSection('Enhancement Roadmap');
441
+ fillRect(cv, margin, y, W, roadH - 16, C_CARD);
442
+ let dy = y + 16;
443
+ for (const p of road) {
444
+ drawText(cv, fitText(p.head, Math.floor(W / charStep(S)) - 4), margin + 12, dy, S, C_PURPLE);
445
+ dy += 34;
446
+ for (const ln of p.lines) {
447
+ drawText(cv, ln, margin + 28, dy, S, C_TEXT);
448
+ dy += 30;
449
+ }
450
+ dy += 10;
451
+ }
452
+ y += roadH + 40;
453
+ }
454
+
455
+ drawText(cv, 'kiku-agent eval | offline snapshot', margin, totalH - 36, S, C_MUTED);
456
+ return encodePng(cv);
457
+ }
458
+
459
+ export async function writeBenchmarkPng(report, outputPath, opts = {}) {
460
+ const meta = report.metadata || {};
461
+ const base = report.summary || {};
462
+ const summary = Object.assign({}, base, { model: base.model || meta.model });
463
+ const png = renderBenchmarkPng(report.rows || [], summary, opts);
464
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
465
+ await fs.writeFile(outputPath, png);
466
+ return outputPath;
467
+ }
468
+
469
+ if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
470
+ const { values } = parseArgs({
471
+ options: {
472
+ in: { type: 'string', default: 'artifacts/eval/live.json' },
473
+ out: { type: 'string', default: 'artifacts/eval/benchmark.png' },
474
+ width: { type: 'string', default: '1440' },
475
+ mode: { type: 'string', default: 'full' }
476
+ }
477
+ });
478
+ const inPath = path.resolve(REPO_ROOT, values.in);
479
+ const outPath = path.resolve(REPO_ROOT, values.out);
480
+ try {
481
+ const report = JSON.parse(await fs.readFile(inPath, 'utf8'));
482
+ await writeBenchmarkPng(report, outPath, { width: Number(values.width), mode: values.mode });
483
+ } catch (err) {
484
+ console.error(String((err && err.message) || err));
485
+ process.exit(1);
486
+ }
487
+ }