kiku-agent 1.2.0 → 1.3.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 (100) hide show
  1. package/README.md +459 -407
  2. package/package.json +27 -3
  3. package/server/agent/agentCore.js +635 -267
  4. package/server/agent/appPaths.js +2 -0
  5. package/server/agent/catalogEntityResolver.js +629 -0
  6. package/server/agent/citations.js +0 -21
  7. package/server/agent/clarificationAnalytics.js +1 -1
  8. package/server/agent/clarifyingQuestions.js +286 -216
  9. package/server/agent/cli.js +528 -73
  10. package/server/agent/commandFeatures.js +123 -24
  11. package/server/agent/commandRegistry.js +128 -62
  12. package/server/agent/commands.js +1410 -95
  13. package/server/agent/config.js +253 -163
  14. package/server/agent/contextCompactor.js +239 -25
  15. package/server/agent/dynamicCatalog.js +192 -0
  16. package/server/agent/envBootstrap.js +46 -0
  17. package/server/agent/envManager.js +317 -0
  18. package/server/agent/eventBus.js +3 -3
  19. package/server/agent/feedbackLearning.js +0 -8
  20. package/server/agent/gpuMetrics.js +188 -55
  21. package/server/agent/ingestionWorker.js +339 -0
  22. package/server/agent/lanDiscovery.js +369 -0
  23. package/server/agent/llmClient.js +111 -39
  24. package/server/agent/lmStudioWakeup.js +144 -0
  25. package/server/agent/mcpClient.js +2 -2
  26. package/server/agent/memory.js +635 -494
  27. package/server/agent/modelConfig.js +487 -0
  28. package/server/agent/newsIntelligence.js +219 -0
  29. package/server/agent/newsWatch.js +85 -0
  30. package/server/agent/prompts.js +146 -0
  31. package/server/agent/publicTools.js +7 -0
  32. package/server/agent/queryDetails.js +62 -1
  33. package/server/agent/research.js +314 -35
  34. package/server/agent/routingInterceptor.js +77 -0
  35. package/server/agent/rumoredProjects.js +15 -0
  36. package/server/agent/sanitizer.js +117 -17
  37. package/server/agent/subagent.js +52 -9
  38. package/server/agent/suggestionTui.js +33 -13
  39. package/server/agent/suggestions.js +29 -13
  40. package/server/agent/telemetry.js +0 -11
  41. package/server/agent/terminalImage.js +0 -1
  42. package/server/agent/textToolProtocol.js +190 -0
  43. package/server/agent/tokenTracker.js +220 -0
  44. package/server/agent/toolRouter.js +362 -216
  45. package/server/agent/tools.js +1155 -194
  46. package/server/agent/ui.js +144 -41
  47. package/server/agent/vramService.js +572 -0
  48. package/server/agent/webSearch.js +2279 -273
  49. package/server/agent/youtubeAuth.js +82 -73
  50. package/server/agent/youtubeTranscript.js +209 -0
  51. package/server/apiRouter.js +383 -179
  52. package/server/mcp/data/brands.js +5 -5
  53. package/server/mcp/data/discography.js +56 -45
  54. package/server/mcp/data/gallery.js +9 -0
  55. package/server/mcp/data/index.js +1 -0
  56. package/server/mcp/data/industryIndex.js +251 -0
  57. package/server/mcp/data/profile.js +5 -5
  58. package/server/mcp/data/timeline.js +2 -2
  59. package/server/mcp/data/works.js +2 -2
  60. package/server/mcp/handler.js +0 -2
  61. package/server/mcp/prompts.js +42 -2
  62. package/server/mcp/resources.js +5 -1
  63. package/server/mcp/security.js +25 -14
  64. package/server/mcp/server.js +92 -3
  65. package/server/mcp/tools.js +20 -11
  66. package/server/web-chat/core/connection.js +218 -194
  67. package/server/web-chat/core/conversationContext.js +1 -1
  68. package/server/web-chat/core/directCatalogEngine.js +190 -184
  69. package/server/web-chat/core/followUpEngine.js +88 -11
  70. package/server/web-chat/core/security.js +7 -4
  71. package/server/web-chat/router.js +54 -17
  72. package/server/web-chat/routes/bilibiliRoute.js +34 -0
  73. package/server/web-chat/routes/chatRoute.js +488 -397
  74. package/server/web-chat/routes/clarifyConfigRoute.js +1 -2
  75. package/server/web-chat/routes/exportRoute.js +2 -1
  76. package/server/web-chat/routes/lanDiscoveryRoute.js +90 -0
  77. package/server/web-chat/routes/newsRoute.js +36 -6
  78. package/server/web-chat/routes/providerRoute.js +87 -0
  79. package/server/web-chat/routes/staticRoute.js +61 -13
  80. package/server/web-chat/routes/statusRoute.js +23 -2
  81. package/server/web-chat/routes/vramRoute.js +22 -0
  82. package/server/web-chat/server.js +135 -106
  83. package/server/web-chat/ui/htmlBuilder.js +8 -8
  84. package/server/web-chat/ui/previews/modelSettings.preview.html +41 -0
  85. package/server/web-chat/ui/scripts/agentBubbleSystem.js +0 -5
  86. package/server/web-chat/ui/scripts/client.js +10526 -8397
  87. package/server/web-chat/ui/scripts/webchatI18n.js +190 -138
  88. package/server/web-chat/ui/styles/main.css +7471 -4246
  89. package/server/web-chat/ui/templates/body.html +305 -179
  90. package/src/data/brandsData.js +29 -6
  91. package/src/data/discographyData.js +918 -900
  92. package/src/data/galleryData.js +66 -0
  93. package/src/data/industryIndexData.js +292 -0
  94. package/src/data/musicVideoData.js +48 -5
  95. package/src/data/profileData.js +15 -1
  96. package/src/data/timelineData.js +4 -3
  97. package/src/data/worksData.js +536 -535
  98. package/src/lib/vram/estimator.js +464 -0
  99. package/src/lib/vram/modelRegistry.js +387 -0
  100. package/tokens.css +33 -2
@@ -821,45 +821,131 @@ export function printRichStatusCard({ status, title, lines = [], footer = '', wi
821
821
  }
822
822
 
823
823
  export function cleanTableCell(raw) {
824
- if (!raw) return '';
824
+ if (!raw && raw !== 0) return '';
825
825
  return String(raw)
826
826
  .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (m, text, url) => {
827
827
  const t = text.trim();
828
828
  const u = url.trim();
829
829
  return t === u ? u : `${t} (${u})`;
830
830
  })
831
+ .replace(/<br\s*\/?>/gi, '\n')
832
+ .replace(/<[^>]+>/g, '')
831
833
  .replace(/\*\*/g, '')
832
834
  .replace(/`/g, '')
833
- .replace(/\s+/g, ' ')
834
- .trim();
835
+ .replace(/\r/g, '')
836
+ .split('\n')
837
+ .map(line => line.replace(/[^\S\n]+/g, ' ').trim())
838
+ .filter(Boolean)
839
+ .join('\n');
840
+ }
841
+
842
+ export function wrapTableCell(text, colWidth) {
843
+ if (!text && text !== 0) return [''];
844
+ const safeWidth = Math.max(2, colWidth);
845
+ const rawLines = String(text).split('\n');
846
+ const wrappedLines = [];
847
+
848
+ for (const rawLine of rawLines) {
849
+ const trimmed = rawLine.trim();
850
+ if (!trimmed) {
851
+ wrappedLines.push('');
852
+ continue;
853
+ }
854
+
855
+ const words = trimmed.split(/\s+/).filter(Boolean);
856
+ let currentLine = '';
857
+
858
+ for (const word of words) {
859
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
860
+ if (getStringDisplayWidth(testLine) <= safeWidth) {
861
+ currentLine = testLine;
862
+ } else {
863
+ if (currentLine) {
864
+ wrappedLines.push(currentLine);
865
+ currentLine = '';
866
+ }
867
+ if (getStringDisplayWidth(word) > safeWidth) {
868
+ let chunk = '';
869
+ for (const item of getGraphemeSegments(word)) {
870
+ const itemWidth = getGraphemeDisplayWidth(item.segment);
871
+ if (getStringDisplayWidth(chunk) + itemWidth > safeWidth) {
872
+ if (chunk) wrappedLines.push(chunk);
873
+ chunk = item.segment;
874
+ } else {
875
+ chunk += item.segment;
876
+ }
877
+ }
878
+ if (chunk) {
879
+ currentLine = chunk;
880
+ }
881
+ } else {
882
+ currentLine = word;
883
+ }
884
+ }
885
+ }
886
+ if (currentLine) {
887
+ wrappedLines.push(currentLine);
888
+ }
889
+ }
890
+
891
+ return wrappedLines.length > 0 ? wrappedLines : [''];
835
892
  }
836
893
 
837
894
  export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth() - 4) {
838
895
  if (!headers.length && !rows.length) return [];
839
896
  const colCount = Math.max(headers.length, ...rows.map(r => r.length));
840
- const colWidths = new Array(colCount).fill(4);
841
897
 
842
898
  const cleanHeaders = headers.map(cleanTableCell);
843
899
  const cleanRows = rows.map(r => r.map(cleanTableCell));
844
900
 
901
+ const headerWidths = new Array(colCount).fill(0);
902
+ const naturalWidths = new Array(colCount).fill(4);
903
+
845
904
  for (let c = 0; c < colCount; c++) {
846
- const headerLen = getStringDisplayWidth(cleanHeaders[c] || '');
847
- colWidths[c] = Math.max(colWidths[c], headerLen);
905
+ const hLines = (cleanHeaders[c] || '').split('\n');
906
+ const hMax = Math.max(...hLines.map(l => getStringDisplayWidth(l)), 0);
907
+ headerWidths[c] = Math.max(hMax, 4);
908
+ naturalWidths[c] = headerWidths[c];
909
+
848
910
  for (const row of cleanRows) {
849
- const cellLen = getStringDisplayWidth(row[c] || '');
850
- colWidths[c] = Math.max(colWidths[c], cellLen);
911
+ const cellLines = (row[c] || '').split('\n');
912
+ for (const line of cellLines) {
913
+ naturalWidths[c] = Math.max(naturalWidths[c], getStringDisplayWidth(line));
914
+ }
851
915
  }
852
916
  }
853
917
 
854
- const totalVisualWidth = colWidths.reduce((sum, w) => sum + w + 3, 1);
855
- if (totalVisualWidth > maxWidth) {
856
- const available = Math.max(20, maxWidth - colCount * 3 - 1);
857
- const sumWidths = colWidths.reduce((a, b) => a + b, 0);
858
- for (let c = 0; c < colCount; c++) {
859
- colWidths[c] = Math.max(6, Math.floor((colWidths[c] / sumWidths) * available));
918
+ const borderOverhead = 3 * colCount + 3;
919
+ const availableWidth = Math.max(colCount * 6, maxWidth - borderOverhead);
920
+
921
+ const colWidths = [...naturalWidths];
922
+ const totalNatural = colWidths.reduce((a, b) => a + b, 0);
923
+
924
+ if (totalNatural > availableWidth) {
925
+ const isCompact = colWidths.map(w => w <= 14);
926
+ const compactSum = colWidths.reduce((sum, w, i) => isCompact[i] ? sum + w : sum, 0);
927
+ const wideCount = isCompact.filter(c => !c).length;
928
+
929
+ if (wideCount > 0 && availableWidth > compactSum + wideCount * 8) {
930
+ const wideAvailable = availableWidth - compactSum;
931
+ const wideNaturalSum = colWidths.reduce((sum, w, i) => !isCompact[i] ? sum + w : sum, 0);
932
+ for (let c = 0; c < colCount; c++) {
933
+ if (!isCompact[c]) {
934
+ colWidths[c] = Math.max(headerWidths[c], Math.max(8, Math.floor((colWidths[c] / wideNaturalSum) * wideAvailable)));
935
+ }
936
+ }
937
+ } else {
938
+ for (let c = 0; c < colCount; c++) {
939
+ colWidths[c] = Math.max(4, Math.floor((colWidths[c] / totalNatural) * availableWidth));
940
+ }
860
941
  }
861
942
  }
862
943
 
944
+ const cleanRowsWrapped = cleanRows.map(row => {
945
+ return Array.from({ length: colCount }, (_, c) => wrapTableCell(row[c] || '—', colWidths[c]));
946
+ });
947
+ const hasMultiLine = cleanRowsWrapped.some(rowLines => rowLines.some(lines => lines.length > 1));
948
+
863
949
  const output = [];
864
950
  const topBorder = ` ┌─${colWidths.map(w => '─'.repeat(w)).join('─┬─')}─┐`;
865
951
  const midBorder = ` ├─${colWidths.map(w => '─'.repeat(w)).join('─┼─')}─┤`;
@@ -868,40 +954,47 @@ export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth
868
954
  output.push(`${C.slate}${topBorder}${C.reset}`);
869
955
 
870
956
  if (cleanHeaders.length > 0) {
871
- const headerCells = [];
872
- for (let c = 0; c < colCount; c++) {
873
- const rawText = cleanHeaders[c] || '';
874
- const textLen = getStringDisplayWidth(rawText);
875
- const text = textLen > colWidths[c] && colWidths[c] > 3
876
- ? fitStyledText(rawText, colWidths[c] - 1) + '…'
877
- : fitStyledText(rawText, colWidths[c]);
878
- const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
879
- headerCells.push(`${C.bold}${C.gold}${text}${C.reset}${' '.repeat(pad)}`);
957
+ const headerLinesPerCol = Array.from({ length: colCount }, (_, c) => wrapTableCell(cleanHeaders[c] || '', colWidths[c]));
958
+ const maxHeaderLines = Math.max(...headerLinesPerCol.map(l => l.length), 1);
959
+
960
+ for (let lineIdx = 0; lineIdx < maxHeaderLines; lineIdx++) {
961
+ const headerCells = [];
962
+ for (let c = 0; c < colCount; c++) {
963
+ const text = headerLinesPerCol[c]?.[lineIdx] || '';
964
+ const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
965
+ headerCells.push(`${C.bold}${C.gold}${text}${C.reset}${' '.repeat(pad)}`);
966
+ }
967
+ output.push(` ${C.slate}│${C.reset} ${headerCells.join(` ${C.slate}│${C.reset} `)} ${C.slate}│${C.reset}`);
880
968
  }
881
- output.push(` ${C.slate}│${C.reset} ${headerCells.join(` ${C.slate}│${C.reset} `)} ${C.slate}│${C.reset}`);
882
969
  output.push(`${C.slate}${midBorder}${C.reset}`);
883
970
  }
884
971
 
885
- for (const row of cleanRows) {
886
- const rowCells = [];
887
- for (let c = 0; c < colCount; c++) {
888
- const cellRaw = row[c] || '—';
889
- const cellLen = getStringDisplayWidth(cellRaw);
890
- const text = cellLen > colWidths[c] && colWidths[c] > 3
891
- ? fitStyledText(cellRaw, colWidths[c] - 1) + '…'
892
- : fitStyledText(cellRaw, colWidths[c]);
893
- const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
894
- rowCells.push(`${C.white}${text}${C.reset}${' '.repeat(pad)}`);
972
+ for (let rIdx = 0; rIdx < cleanRowsWrapped.length; rIdx++) {
973
+ const rowLinesPerCol = cleanRowsWrapped[rIdx];
974
+ const maxRowLines = Math.max(...rowLinesPerCol.map(l => l.length), 1);
975
+
976
+ for (let lineIdx = 0; lineIdx < maxRowLines; lineIdx++) {
977
+ const rowCells = [];
978
+ for (let c = 0; c < colCount; c++) {
979
+ const text = rowLinesPerCol[c]?.[lineIdx] || '';
980
+ const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
981
+ rowCells.push(`${C.white}${text}${C.reset}${' '.repeat(pad)}`);
982
+ }
983
+ output.push(` ${C.slate}│${C.reset} ${rowCells.join(` ${C.slate}│${C.reset} `)} ${C.slate}│${C.reset}`);
984
+ }
985
+
986
+ if (hasMultiLine && rIdx < cleanRowsWrapped.length - 1) {
987
+ output.push(`${C.slate}${midBorder}${C.reset}`);
895
988
  }
896
- output.push(` ${C.slate}│${C.reset} ${rowCells.join(` ${C.slate}│${C.reset} `)} ${C.slate}│${C.reset}`);
897
989
  }
898
990
 
899
991
  output.push(`${C.slate}${botBorder}${C.reset}`);
900
992
  return output;
901
993
  }
902
994
 
903
- export function formatTerminalMarkdown(text) {
995
+ export function formatTerminalMarkdown(text, maxWidth = getFullWidth() - 8) {
904
996
  if (!text) return '';
997
+ const dividerWidth = Math.max(16, Math.min(Number(maxWidth) || (getFullWidth() - 12), getFullWidth() - 12));
905
998
  return text
906
999
  .replace(/^#####?\s*(.+)$/gm, ` ${C.bold}${C.lavender}• $1${C.reset}`)
907
1000
  .replace(/^####\s*(.+)$/gm, `${C.bold}${C.cyberCyan}▸ $1${C.reset}`)
@@ -916,13 +1009,17 @@ export function formatTerminalMarkdown(text) {
916
1009
  .replace(/`([^`]+)`/g, `${C.cyberCyan}$1${C.reset}`)
917
1010
  .replace(/^(\s*)(\d+)\.\s+/gm, `$1 ${C.gold}$2.${C.reset} `)
918
1011
  .replace(/^(\s*)[*•+-]\s+/gm, `$1 ${C.sakura}•${C.reset} `)
919
- .replace(/^\s*[-*_]{3,}\s*$/gm, ` ${C.slate}${'─'.repeat(Math.max(20, getFullWidth() - 6))}${C.reset}`);
1012
+ .replace(/^\s*[-*_]{3,}\s*$/gm, ` ${C.slate}${'─'.repeat(dividerWidth)}${C.reset}`);
920
1013
  }
921
1014
 
922
1015
  export function parseMarkdownTableRow(text) {
923
1016
  const trimmed = String(text || '').trim();
924
1017
  if (!trimmed.includes('|')) return null;
925
1018
 
1019
+ if (/^(\s*)[*•+->#]/.test(trimmed) || /^(\s*)\d+\.\s/.test(trimmed)) {
1020
+ return null;
1021
+ }
1022
+
926
1023
  const content = trimmed
927
1024
  .replace(/^\|/, '')
928
1025
  .replace(/\|$/, '');
@@ -959,7 +1056,7 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
959
1056
  const rendered = formatRichTable(tableHeaders, tableRows, maxWidth);
960
1057
  result.push(...rendered);
961
1058
  } else {
962
- result.push(formatTerminalMarkdown(`| ${tableHeaders.join(' | ')} |`));
1059
+ result.push(formatTerminalMarkdown(expandMarkdownLinks(pendingHeaderRawLine || `| ${tableHeaders.join(' | ')} |`), maxWidth));
963
1060
  }
964
1061
  tableHeaders = null;
965
1062
  tableRows = [];
@@ -967,6 +1064,7 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
967
1064
  };
968
1065
 
969
1066
  let pendingHeader = null;
1067
+ let pendingHeaderRawLine = null;
970
1068
 
971
1069
  for (let i = 0; i < lines.length; i++) {
972
1070
  const rawLine = lines[i];
@@ -976,11 +1074,13 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
976
1074
  if (cells && isMarkdownTableSeparator(cells)) {
977
1075
  tableHeaders = pendingHeader.map(cleanTableCell);
978
1076
  pendingHeader = null;
1077
+ pendingHeaderRawLine = null;
979
1078
  tableRows = [];
980
1079
  continue;
981
1080
  } else {
982
- result.push(formatTerminalMarkdown(expandMarkdownLinks(`| ${pendingHeader.join(' | ')} |`)));
1081
+ result.push(formatTerminalMarkdown(expandMarkdownLinks(pendingHeaderRawLine), maxWidth));
983
1082
  pendingHeader = null;
1083
+ pendingHeaderRawLine = null;
984
1084
  }
985
1085
  }
986
1086
 
@@ -999,15 +1099,18 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
999
1099
 
1000
1100
  if (cells && !isMarkdownTableSeparator(cells)) {
1001
1101
  pendingHeader = cells;
1102
+ pendingHeaderRawLine = rawLine;
1002
1103
  continue;
1003
1104
  }
1004
1105
 
1005
- const formatted = formatTerminalMarkdown(expandMarkdownLinks(rawLine));
1106
+ const formatted = formatTerminalMarkdown(expandMarkdownLinks(rawLine), maxWidth);
1006
1107
  result.push(formatted);
1007
1108
  }
1008
1109
 
1009
1110
  if (pendingHeader) {
1010
- result.push(formatTerminalMarkdown(expandMarkdownLinks(`| ${pendingHeader.join(' | ')} |`)));
1111
+ result.push(formatTerminalMarkdown(expandMarkdownLinks(pendingHeaderRawLine), maxWidth));
1112
+ pendingHeader = null;
1113
+ pendingHeaderRawLine = null;
1011
1114
  }
1012
1115
  flushTable();
1013
1116