kiku-agent 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/server/agent/agentCore.js +5 -8
- package/server/agent/appPaths.js +2 -0
- package/server/agent/cli.js +457 -248
- package/server/agent/commandRegistry.js +367 -40
- package/server/agent/commands.js +145 -41
- package/server/agent/config.js +367 -216
- package/server/agent/doctor.js +1 -1
- package/server/agent/errors.js +205 -0
- package/server/agent/llmClient.js +369 -351
- package/server/agent/queryDetails.js +234 -182
- package/server/agent/session.js +410 -258
- package/server/agent/terminalImage.js +108 -2
- package/server/agent/ui.js +17 -11
- package/src/assets/images/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdxrcipx4j61k422wtpp02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdxrg3cq5j62bw340u0x02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdy9hpg8aj62c0340hdt02.jpg +0 -0
package/server/agent/commands.js
CHANGED
|
@@ -787,17 +787,31 @@ export const handlers = {
|
|
|
787
787
|
const totalDur = ((sess.stats.totalDurationMs || 0) / 1000).toFixed(1);
|
|
788
788
|
const createdAt = sess.createdAt ? sess.createdAt.toLocaleString() : 'unknown';
|
|
789
789
|
|
|
790
|
+
const lines = [
|
|
791
|
+
`${C.gold}Session ID:${C.reset} ${C.white}${sess.id}${C.reset}`,
|
|
792
|
+
`${C.gold}Created At:${C.reset} ${C.white}${createdAt}${C.reset}`,
|
|
793
|
+
`${C.gold}Total Messages:${C.reset} ${C.cyberCyan}${msgCount}${C.reset} total ${C.dim}(${userMsgs} user, ${assistantMsgs} assistant)${C.reset}`,
|
|
794
|
+
`${C.gold}Total Queries:${C.reset} ${C.cyberCyan}${sess.stats.totalQueries}${C.reset}`,
|
|
795
|
+
`${C.gold}Tool Calls:${C.reset} ${C.cyberCyan}${toolCalls}${C.reset}`,
|
|
796
|
+
`${C.gold}Total Duration:${C.reset} ${C.cyberCyan}${totalDur}s${C.reset}`,
|
|
797
|
+
`${C.gold}Thinking Mode:${C.reset} ${C.bold}${C.gold}${sess.thinkingMode.toUpperCase()}${C.reset}`
|
|
798
|
+
];
|
|
799
|
+
|
|
800
|
+
const toolStats = typeof sess.getToolStats === 'function' ? sess.getToolStats() : {};
|
|
801
|
+
const toolEntries = Object.entries(toolStats);
|
|
802
|
+
if (toolEntries.length > 0) {
|
|
803
|
+
lines.push('', `${C.bold}${C.lavender}Tool Executions Breakdown:${C.reset}`);
|
|
804
|
+
for (const [name, s] of toolEntries) {
|
|
805
|
+
const dur = (s.totalDurationMs / 1000).toFixed(2);
|
|
806
|
+
lines.push(` ${C.cyberCyan}• ${name.padEnd(24)}${C.reset} ${C.white}${s.count} call(s)${C.reset} ${C.dim}(${s.successCount} ok, ${s.notFoundCount} empty, ${s.errorCount} err • ${dur}s)${C.reset}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
lines.push('', `${C.dim}Commands: /session summary | /session export [markdown|json|text] | /session copy | /session clear${C.reset}`);
|
|
811
|
+
|
|
790
812
|
printSolidPanel({
|
|
791
|
-
title: `Active Session Details`,
|
|
792
|
-
lines
|
|
793
|
-
`${C.gold}Session ID:${C.reset} ${C.white}${sess.id}${C.reset}`,
|
|
794
|
-
`${C.gold}Created At:${C.reset} ${C.white}${createdAt}${C.reset}`,
|
|
795
|
-
`${C.gold}Total Messages:${C.reset} ${C.cyberCyan}${msgCount}${C.reset} total ${C.dim}(${userMsgs} user, ${assistantMsgs} assistant)${C.reset}`,
|
|
796
|
-
`${C.gold}Total Queries:${C.reset} ${C.cyberCyan}${sess.stats.totalQueries}${C.reset}`,
|
|
797
|
-
`${C.gold}Tool Calls:${C.reset} ${C.cyberCyan}${toolCalls}${C.reset}`,
|
|
798
|
-
`${C.gold}Total Duration:${C.reset} ${C.cyberCyan}${totalDur}s${C.reset}`,
|
|
799
|
-
`${C.gold}Thinking Mode:${C.reset} ${C.bold}${C.gold}${sess.thinkingMode.toUpperCase()}${C.reset}`
|
|
800
|
-
],
|
|
813
|
+
title: `Active Session Details • ${sess.id}`,
|
|
814
|
+
lines,
|
|
801
815
|
headerBg: '\x1b[48;2;48;54;70m',
|
|
802
816
|
bg: '\x1b[48;2;36;40;52m'
|
|
803
817
|
});
|
|
@@ -909,19 +923,43 @@ export const handlers = {
|
|
|
909
923
|
}
|
|
910
924
|
|
|
911
925
|
if (action === 'export') {
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
926
|
+
const format = (extra || '').toLowerCase().trim();
|
|
927
|
+
let exported;
|
|
928
|
+
if (format === 'json') {
|
|
929
|
+
exported = await agent.session.exportToJson();
|
|
930
|
+
} else if (format === 'text' || format === 'txt') {
|
|
931
|
+
exported = await agent.session.exportToText();
|
|
932
|
+
} else {
|
|
933
|
+
exported = await agent.session.exportToMarkdown();
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
printRichCard({
|
|
937
|
+
title: 'Session Exported Successfully',
|
|
938
|
+
status: 'ok',
|
|
939
|
+
lines: [
|
|
940
|
+
`• Format: ${exported.format.toUpperCase()}`,
|
|
941
|
+
`• File: ${exported.filePath}`,
|
|
942
|
+
`• Size: ${(exported.size || 0).toLocaleString()} bytes`,
|
|
943
|
+
],
|
|
944
|
+
});
|
|
915
945
|
return 'HANDLED';
|
|
916
946
|
}
|
|
917
947
|
|
|
948
|
+
if (action === 'copy') {
|
|
949
|
+
if (typeof ctx.copyQueryDetails === 'function') {
|
|
950
|
+
ctx.copyQueryDetails(`session ${extra || ''}`.trim());
|
|
951
|
+
return 'HANDLED';
|
|
952
|
+
}
|
|
953
|
+
return handlers['/copy']({ ...ctx, sub: 'session', extra });
|
|
954
|
+
}
|
|
955
|
+
|
|
918
956
|
if (action === 'clear' || action === 'new' || action === 'reset') {
|
|
919
957
|
await agent.endSession();
|
|
920
958
|
console.log(`\n ${C.emerald}[OK] Previous session archived. Started fresh.${C.reset}\n`);
|
|
921
959
|
return 'HANDLED';
|
|
922
960
|
}
|
|
923
961
|
|
|
924
|
-
console.log(`\n ${C.amber}Unknown session action: "${sub}". Use /session [info|summary|list|load|export|clear]${C.reset}\n`);
|
|
962
|
+
console.log(`\n ${C.amber}Unknown session action: "${sub}". Use /session [info|summary|list|load|export|copy|clear]${C.reset}\n`);
|
|
925
963
|
return 'HANDLED';
|
|
926
964
|
},
|
|
927
965
|
|
|
@@ -988,14 +1026,28 @@ export const handlers = {
|
|
|
988
1026
|
return 'HANDLED';
|
|
989
1027
|
}
|
|
990
1028
|
|
|
991
|
-
|
|
1029
|
+
let requested = action === 'reset' ? 'sakura' : action;
|
|
1030
|
+
if (action === 'next' || action === 'cycle') {
|
|
1031
|
+
const currentIndex = THEME_NAMES.indexOf(current);
|
|
1032
|
+
const nextIndex = (currentIndex + 1) % THEME_NAMES.length;
|
|
1033
|
+
requested = THEME_NAMES[nextIndex];
|
|
1034
|
+
} else if (action === 'prev') {
|
|
1035
|
+
const currentIndex = THEME_NAMES.indexOf(current);
|
|
1036
|
+
const prevIndex = (currentIndex - 1 + THEME_NAMES.length) % THEME_NAMES.length;
|
|
1037
|
+
requested = THEME_NAMES[prevIndex];
|
|
1038
|
+
}
|
|
1039
|
+
|
|
992
1040
|
const theme = setTheme(requested);
|
|
993
1041
|
if (!theme) {
|
|
994
1042
|
console.log(`\n ${C.amber}Unknown theme: "${action}".${C.reset}`);
|
|
995
|
-
console.log(` ${C.dim}Available: ${THEME_NAMES.join(', ')}${C.reset}\n`);
|
|
1043
|
+
console.log(` ${C.dim}Available: ${THEME_NAMES.join(', ')}, next, prev, reset${C.reset}\n`);
|
|
996
1044
|
return 'HANDLED';
|
|
997
1045
|
}
|
|
998
1046
|
|
|
1047
|
+
if (ctx.tuiPrompt) {
|
|
1048
|
+
ctx.tuiPrompt.render();
|
|
1049
|
+
}
|
|
1050
|
+
|
|
999
1051
|
printSolidPanel({
|
|
1000
1052
|
title: 'Theme Updated',
|
|
1001
1053
|
lines: [
|
|
@@ -1369,9 +1421,24 @@ export const handlers = {
|
|
|
1369
1421
|
ctx.modelManager.client = ctx.client;
|
|
1370
1422
|
ctx.modelManager.requestedModel = info.model;
|
|
1371
1423
|
const resolution = await ctx.modelManager.resolve({ requestedModel: info.model });
|
|
1372
|
-
if (resolution.connected && resolution.
|
|
1373
|
-
|
|
1424
|
+
if (resolution.connected && resolution.selectedModel) {
|
|
1425
|
+
ctx.setCatalogOnlyMode?.(false);
|
|
1426
|
+
if (ctx.tuiPrompt) {
|
|
1427
|
+
ctx.tuiPrompt.isConnected = true;
|
|
1428
|
+
ctx.tuiPrompt.modelName = resolution.selectedModel;
|
|
1429
|
+
ctx.tuiPrompt.provider = info.providerName;
|
|
1430
|
+
ctx.tuiPrompt.render();
|
|
1431
|
+
}
|
|
1432
|
+
if (resolution.usedFallback) {
|
|
1433
|
+
console.log(` ${C.amber}• Requested model unavailable; using ${resolution.selectedModel}${C.reset}`);
|
|
1434
|
+
}
|
|
1374
1435
|
} else if (!resolution.connected) {
|
|
1436
|
+
ctx.setCatalogOnlyMode?.(true);
|
|
1437
|
+
if (ctx.tuiPrompt) {
|
|
1438
|
+
ctx.tuiPrompt.isConnected = false;
|
|
1439
|
+
ctx.tuiPrompt.provider = info.providerName;
|
|
1440
|
+
ctx.tuiPrompt.render();
|
|
1441
|
+
}
|
|
1375
1442
|
console.log(` ${C.amber}• Provider is not ready: ${resolution.error}${C.reset}`);
|
|
1376
1443
|
}
|
|
1377
1444
|
}
|
|
@@ -1495,9 +1562,11 @@ export const handlers = {
|
|
|
1495
1562
|
return 'HANDLED';
|
|
1496
1563
|
}
|
|
1497
1564
|
|
|
1498
|
-
if (
|
|
1499
|
-
ctx.client.setApiKey(newKey);
|
|
1500
|
-
|
|
1565
|
+
if (ctx.client?.setApiKey) {
|
|
1566
|
+
ctx.client.setApiKey(newKey, targetProvider);
|
|
1567
|
+
}
|
|
1568
|
+
if (ctx.agent?.client?.setApiKey) {
|
|
1569
|
+
ctx.agent.client.setApiKey(newKey, targetProvider);
|
|
1501
1570
|
}
|
|
1502
1571
|
const pDef = getProvider(targetProvider);
|
|
1503
1572
|
if (pDef) pDef.apiKey = newKey;
|
|
@@ -1505,7 +1574,7 @@ export const handlers = {
|
|
|
1505
1574
|
const masked = newKey.length > 8
|
|
1506
1575
|
? `${newKey.slice(0, 4)}...${newKey.slice(-4)}`
|
|
1507
1576
|
: '****';
|
|
1508
|
-
console.log(`\n ${C.emerald}[OK] API key
|
|
1577
|
+
console.log(`\n ${C.emerald}[OK] API key saved & persisted for ${targetProvider}${C.reset} (${masked})\n`);
|
|
1509
1578
|
return 'HANDLED';
|
|
1510
1579
|
}
|
|
1511
1580
|
|
|
@@ -1535,9 +1604,10 @@ export const handlers = {
|
|
|
1535
1604
|
},
|
|
1536
1605
|
|
|
1537
1606
|
'/model': async (ctx) => {
|
|
1538
|
-
const
|
|
1607
|
+
const rawSub = (ctx.sub || '').trim();
|
|
1608
|
+
const action = (rawSub || 'status').toLowerCase();
|
|
1539
1609
|
const providerName = ctx.client.providerName || 'Model';
|
|
1540
|
-
const { getRecommendedModels } = await import('./config.js');
|
|
1610
|
+
const { getRecommendedModels, CLOUD_PROVIDERS } = await import('./config.js');
|
|
1541
1611
|
|
|
1542
1612
|
if (action === 'reconnect') {
|
|
1543
1613
|
if (!ctx.modelManager) {
|
|
@@ -1547,8 +1617,18 @@ export const handlers = {
|
|
|
1547
1617
|
const resolution = await ctx.modelManager.resolve();
|
|
1548
1618
|
if (resolution.connected && resolution.selectedModel) {
|
|
1549
1619
|
ctx.setCatalogOnlyMode?.(false);
|
|
1620
|
+
if (ctx.tuiPrompt) {
|
|
1621
|
+
ctx.tuiPrompt.isConnected = true;
|
|
1622
|
+
ctx.tuiPrompt.modelName = resolution.selectedModel;
|
|
1623
|
+
ctx.tuiPrompt.render();
|
|
1624
|
+
}
|
|
1550
1625
|
console.log(`\n ${C.emerald}[OK] Connected using ${resolution.selectedModel}.${C.reset}\n`);
|
|
1551
1626
|
} else {
|
|
1627
|
+
ctx.setCatalogOnlyMode?.(true);
|
|
1628
|
+
if (ctx.tuiPrompt) {
|
|
1629
|
+
ctx.tuiPrompt.isConnected = false;
|
|
1630
|
+
ctx.tuiPrompt.render();
|
|
1631
|
+
}
|
|
1552
1632
|
console.log(`\n ${C.red}[MODEL ERROR] ${resolution.error || 'No chat model is ready.'}${C.reset}\n`);
|
|
1553
1633
|
}
|
|
1554
1634
|
return 'HANDLED';
|
|
@@ -1590,19 +1670,42 @@ export const handlers = {
|
|
|
1590
1670
|
`${C.bold}${C.lavender}Reported by Server:${C.reset}`,
|
|
1591
1671
|
...(serverModels.length ? serverModels.map(s => ` ${s}`) : [` ${C.dim}No live model list reported by ${providerName}.${C.reset}`]),
|
|
1592
1672
|
'',
|
|
1593
|
-
`${C.dim}Select model: /model use <model_id>${C.reset}`
|
|
1673
|
+
`${C.dim}Select model: /model use <model_id> or /model <provider_or_model>${C.reset}`
|
|
1594
1674
|
],
|
|
1595
1675
|
});
|
|
1596
1676
|
return 'HANDLED';
|
|
1597
1677
|
}
|
|
1598
1678
|
|
|
1599
|
-
|
|
1679
|
+
const isDirectUse = action === 'use';
|
|
1680
|
+
const targetModel = isDirectUse ? (ctx.extra?.trim() || '') : (!['status', 'diag', 'diagnostics'].includes(action) ? rawSub : '');
|
|
1681
|
+
|
|
1682
|
+
if (targetModel) {
|
|
1600
1683
|
try {
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1684
|
+
const lowerTarget = targetModel.toLowerCase();
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
if (CLOUD_PROVIDERS[lowerTarget]) {
|
|
1688
|
+
return await handlers['/provider']({ ...ctx, sub: 'use', extra: lowerTarget, parts: ['/provider', 'use', lowerTarget] });
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
let matchedProvider = null;
|
|
1693
|
+
if (lowerTarget.includes('gemma') || lowerTarget === 'google/gemma-4-e4b') {
|
|
1694
|
+
matchedProvider = 'lmstudio';
|
|
1695
|
+
} else {
|
|
1696
|
+
for (const [pId, pDef] of Object.entries(CLOUD_PROVIDERS)) {
|
|
1697
|
+
if (pDef.recommendedModels?.some(rm => rm.id.toLowerCase() === lowerTarget || rm.id.toLowerCase().includes(lowerTarget))) {
|
|
1698
|
+
matchedProvider = pId;
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
if (matchedProvider && matchedProvider !== ctx.client.provider) {
|
|
1705
|
+
console.log(` ${C.cyberCyan}• Switching provider to ${CLOUD_PROVIDERS[matchedProvider].name} for model ${targetModel}...${C.reset}`);
|
|
1706
|
+
return await handlers['/provider']({ ...ctx, sub: 'use', extra: `${matchedProvider} ${targetModel}`, parts: ['/provider', 'use', matchedProvider, targetModel] });
|
|
1605
1707
|
}
|
|
1708
|
+
|
|
1606
1709
|
if (ctx.modelManager) {
|
|
1607
1710
|
const selected = ctx.modelManager.select(targetModel, liveCheck.models || []);
|
|
1608
1711
|
console.log(`\n ${C.emerald}[OK] Active model changed to ${selected}.${C.reset}\n`);
|
|
@@ -2349,20 +2452,12 @@ export const handlers = {
|
|
|
2349
2452
|
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2350
2453
|
const request = parseDetailsRequest(reference);
|
|
2351
2454
|
const details = ctx.agent?.lastQueryDetails || null;
|
|
2352
|
-
|
|
2353
|
-
printRichCard({
|
|
2354
|
-
title: 'No Diagnostics Recorded',
|
|
2355
|
-
status: 'warning',
|
|
2356
|
-
lines: ['No completed model queries are available to copy.'],
|
|
2357
|
-
});
|
|
2358
|
-
return 'HANDLED';
|
|
2359
|
-
}
|
|
2360
|
-
const copyable = getCopyableContent(details, request);
|
|
2455
|
+
const copyable = getCopyableContent(details, request, ctx.agent?.session);
|
|
2361
2456
|
if (!copyable?.text) {
|
|
2362
2457
|
printRichCard({
|
|
2363
2458
|
title: 'Nothing to Copy',
|
|
2364
2459
|
status: 'warning',
|
|
2365
|
-
lines: ['The requested query section is empty.'],
|
|
2460
|
+
lines: ['The requested query or session section is empty.'],
|
|
2366
2461
|
});
|
|
2367
2462
|
return 'HANDLED';
|
|
2368
2463
|
}
|
|
@@ -2385,7 +2480,8 @@ export const handlers = {
|
|
|
2385
2480
|
},
|
|
2386
2481
|
|
|
2387
2482
|
'/gallery': async (ctx) => {
|
|
2388
|
-
const
|
|
2483
|
+
const count = Number(ctx.sub) || 2;
|
|
2484
|
+
const displayed = typeof ctx.showTerminalImage === 'function' && ctx.showTerminalImage(count);
|
|
2389
2485
|
if (!displayed) {
|
|
2390
2486
|
printRichCard({
|
|
2391
2487
|
title: 'Terminal Gallery Unavailable',
|
|
@@ -2638,6 +2734,11 @@ export const handlers = {
|
|
|
2638
2734
|
}
|
|
2639
2735
|
return 'HANDLED';
|
|
2640
2736
|
},
|
|
2737
|
+
|
|
2738
|
+
'/exit': async (ctx) => {
|
|
2739
|
+
try { await ctx.agent?.endSession(); } catch {}
|
|
2740
|
+
return 'EXIT';
|
|
2741
|
+
},
|
|
2641
2742
|
};
|
|
2642
2743
|
|
|
2643
2744
|
const ALIASES = {
|
|
@@ -2676,6 +2777,9 @@ const ALIASES = {
|
|
|
2676
2777
|
'/export': '/save',
|
|
2677
2778
|
'/r': '/retry',
|
|
2678
2779
|
'/kvip': '/key',
|
|
2780
|
+
'/switch': '/provider',
|
|
2781
|
+
'/providers': '/provider',
|
|
2782
|
+
'/fallback': '/provider',
|
|
2679
2783
|
};
|
|
2680
2784
|
|
|
2681
2785
|
export async function routeCommand(rawInput, ctx) {
|