kiku-agent 1.0.9 → 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 +7 -6
- package/server/agent/agentCore.js +116 -66
- package/server/agent/appPaths.js +2 -0
- package/server/agent/cli.js +457 -248
- package/server/agent/commandRegistry.js +370 -41
- package/server/agent/commands.js +217 -45
- 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 -244
- package/server/agent/terminalImage.js +108 -2
- package/server/agent/ui.js +54 -44
- 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
|
@@ -771,10 +771,14 @@ export const handlers = {
|
|
|
771
771
|
},
|
|
772
772
|
|
|
773
773
|
'/session': async (ctx) => {
|
|
774
|
-
const { agent, sub } = ctx;
|
|
774
|
+
const { agent, sub, extra } = ctx;
|
|
775
775
|
const sess = agent.session;
|
|
776
776
|
const action = (sub || 'info').toLowerCase();
|
|
777
777
|
|
|
778
|
+
if (/^\d+$/.test(action) || action.startsWith('sess_')) {
|
|
779
|
+
return handlers['/session']({ ...ctx, sub: 'load', extra: sub });
|
|
780
|
+
}
|
|
781
|
+
|
|
778
782
|
if (action === 'info' || !action) {
|
|
779
783
|
const msgCount = sess.messages.length;
|
|
780
784
|
const userMsgs = sess.messages.filter(m => m.role === 'user').length;
|
|
@@ -783,17 +787,31 @@ export const handlers = {
|
|
|
783
787
|
const totalDur = ((sess.stats.totalDurationMs || 0) / 1000).toFixed(1);
|
|
784
788
|
const createdAt = sess.createdAt ? sess.createdAt.toLocaleString() : 'unknown';
|
|
785
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
|
+
|
|
786
812
|
printSolidPanel({
|
|
787
|
-
title: `Active Session Details`,
|
|
788
|
-
lines
|
|
789
|
-
`${C.gold}Session ID:${C.reset} ${C.white}${sess.id}${C.reset}`,
|
|
790
|
-
`${C.gold}Created At:${C.reset} ${C.white}${createdAt}${C.reset}`,
|
|
791
|
-
`${C.gold}Total Messages:${C.reset} ${C.cyberCyan}${msgCount}${C.reset} total ${C.dim}(${userMsgs} user, ${assistantMsgs} assistant)${C.reset}`,
|
|
792
|
-
`${C.gold}Total Queries:${C.reset} ${C.cyberCyan}${sess.stats.totalQueries}${C.reset}`,
|
|
793
|
-
`${C.gold}Tool Calls:${C.reset} ${C.cyberCyan}${toolCalls}${C.reset}`,
|
|
794
|
-
`${C.gold}Total Duration:${C.reset} ${C.cyberCyan}${totalDur}s${C.reset}`,
|
|
795
|
-
`${C.gold}Thinking Mode:${C.reset} ${C.bold}${C.gold}${sess.thinkingMode.toUpperCase()}${C.reset}`
|
|
796
|
-
],
|
|
813
|
+
title: `Active Session Details • ${sess.id}`,
|
|
814
|
+
lines,
|
|
797
815
|
headerBg: '\x1b[48;2;48;54;70m',
|
|
798
816
|
bg: '\x1b[48;2;36;40;52m'
|
|
799
817
|
});
|
|
@@ -839,10 +857,10 @@ export const handlers = {
|
|
|
839
857
|
title: `Archived Sessions (Total: ${sessions.length})`,
|
|
840
858
|
lines: sessions.length === 0
|
|
841
859
|
? [`${C.dim}No archived sessions found.${C.reset}`]
|
|
842
|
-
: sessions.map(s => {
|
|
860
|
+
: sessions.map((s, i) => {
|
|
843
861
|
const date = new Date(s.createdAt).toLocaleString();
|
|
844
862
|
const dur = ((s.stats?.totalDurationMs || 0) / 1000).toFixed(1);
|
|
845
|
-
return `${C.gold}${s.id}${C.reset} ${C.dim}(${date})${C.reset} — ${s.messageCount} msgs, ${dur}s`;
|
|
863
|
+
return `${C.gold}[${i + 1}] ${s.id}${C.reset} ${C.dim}(${date})${C.reset} — ${s.messageCount} msgs, ${dur}s`;
|
|
846
864
|
}),
|
|
847
865
|
headerBg: '\x1b[48;2;48;54;70m',
|
|
848
866
|
bg: '\x1b[48;2;36;40;52m'
|
|
@@ -850,20 +868,98 @@ export const handlers = {
|
|
|
850
868
|
return 'HANDLED';
|
|
851
869
|
}
|
|
852
870
|
|
|
871
|
+
if (action === 'load' || action === 'resume' || action === 'restore') {
|
|
872
|
+
const target = (extra || '').trim();
|
|
873
|
+
if (!target) {
|
|
874
|
+
const sessions = agent.memory.getArchivedSessions(10);
|
|
875
|
+
if (sessions.length === 0) {
|
|
876
|
+
console.log(`\n ${C.amber}No archived sessions available to load.${C.reset}\n`);
|
|
877
|
+
return 'HANDLED';
|
|
878
|
+
}
|
|
879
|
+
printSolidPanel({
|
|
880
|
+
title: 'Load Session Usage',
|
|
881
|
+
lines: [
|
|
882
|
+
`${C.dim}Specify a session ID or list number to resume:${C.reset}`,
|
|
883
|
+
'',
|
|
884
|
+
...sessions.map((s, i) => {
|
|
885
|
+
const date = new Date(s.createdAt).toLocaleString();
|
|
886
|
+
return ` ${C.gold}[${i + 1}]${C.reset} ${C.bold}${C.white}${s.id}${C.reset} ${C.dim}(${date} — ${s.messageCount} msgs)${C.reset}`;
|
|
887
|
+
}),
|
|
888
|
+
'',
|
|
889
|
+
`${C.dim}Example: /session load 1 OR /session load ${sessions[0].id}${C.reset}`,
|
|
890
|
+
],
|
|
891
|
+
headerBg: '\x1b[48;2;48;54;70m',
|
|
892
|
+
bg: '\x1b[48;2;36;40;52m',
|
|
893
|
+
});
|
|
894
|
+
return 'HANDLED';
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const restored = await agent.restoreSession(target);
|
|
898
|
+
if (!restored) {
|
|
899
|
+
console.log(`\n ${C.amber}Session "${target}" not found. Use /session list to view available archives.${C.reset}\n`);
|
|
900
|
+
return 'HANDLED';
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const date = restored.createdAt ? new Date(restored.createdAt).toLocaleString() : 'unknown';
|
|
904
|
+
const msgCount = restored.messages?.length || 0;
|
|
905
|
+
const totalDur = (((restored.stats?.totalDurationMs) || 0) / 1000).toFixed(1);
|
|
906
|
+
|
|
907
|
+
printSolidPanel({
|
|
908
|
+
title: 'Session Restored Successfully',
|
|
909
|
+
lines: [
|
|
910
|
+
`${C.gold}Session ID:${C.reset} ${C.bold}${C.white}${restored.id}${C.reset}`,
|
|
911
|
+
`${C.gold}Created At:${C.reset} ${C.white}${date}${C.reset}`,
|
|
912
|
+
`${C.gold}Restored Messages:${C.reset} ${C.cyberCyan}${msgCount}${C.reset} exchanges loaded into memory`,
|
|
913
|
+
`${C.gold}Total Queries:${C.reset} ${C.cyberCyan}${restored.stats?.totalQueries || 0}${C.reset}`,
|
|
914
|
+
`${C.gold}Tool Invocations:${C.reset} ${C.cyberCyan}${restored.stats?.totalToolCalls || 0}${C.reset}`,
|
|
915
|
+
`${C.gold}Total Duration:${C.reset} ${C.cyberCyan}${totalDur}s${C.reset}`,
|
|
916
|
+
'',
|
|
917
|
+
`${C.emerald}✓ Session conversation context reloaded. You can now continue chatting!${C.reset}`,
|
|
918
|
+
],
|
|
919
|
+
headerBg: '\x1b[48;2;48;54;70m',
|
|
920
|
+
bg: '\x1b[48;2;36;40;52m',
|
|
921
|
+
});
|
|
922
|
+
return 'HANDLED';
|
|
923
|
+
}
|
|
924
|
+
|
|
853
925
|
if (action === 'export') {
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
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
|
+
});
|
|
857
945
|
return 'HANDLED';
|
|
858
946
|
}
|
|
859
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
|
+
|
|
860
956
|
if (action === 'clear' || action === 'new' || action === 'reset') {
|
|
861
957
|
await agent.endSession();
|
|
862
958
|
console.log(`\n ${C.emerald}[OK] Previous session archived. Started fresh.${C.reset}\n`);
|
|
863
959
|
return 'HANDLED';
|
|
864
960
|
}
|
|
865
961
|
|
|
866
|
-
console.log(`\n ${C.amber}Unknown session action: "${sub}". Use /session [info|list|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`);
|
|
867
963
|
return 'HANDLED';
|
|
868
964
|
},
|
|
869
965
|
|
|
@@ -930,14 +1026,28 @@ export const handlers = {
|
|
|
930
1026
|
return 'HANDLED';
|
|
931
1027
|
}
|
|
932
1028
|
|
|
933
|
-
|
|
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
|
+
|
|
934
1040
|
const theme = setTheme(requested);
|
|
935
1041
|
if (!theme) {
|
|
936
1042
|
console.log(`\n ${C.amber}Unknown theme: "${action}".${C.reset}`);
|
|
937
|
-
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`);
|
|
938
1044
|
return 'HANDLED';
|
|
939
1045
|
}
|
|
940
1046
|
|
|
1047
|
+
if (ctx.tuiPrompt) {
|
|
1048
|
+
ctx.tuiPrompt.render();
|
|
1049
|
+
}
|
|
1050
|
+
|
|
941
1051
|
printSolidPanel({
|
|
942
1052
|
title: 'Theme Updated',
|
|
943
1053
|
lines: [
|
|
@@ -1168,7 +1278,16 @@ export const handlers = {
|
|
|
1168
1278
|
|
|
1169
1279
|
'/stop': async () => { await audioPlayer.stop(); console.log(`\n ${C.emerald}[OK] Stopped${C.reset}\n`); return 'HANDLED'; },
|
|
1170
1280
|
'/pause': async () => { const r = await audioPlayer.pause(); console.log(`\n ${C.emerald}${r}${C.reset}\n`); return 'HANDLED'; },
|
|
1171
|
-
'/resume': async () => {
|
|
1281
|
+
'/resume': async (ctx) => {
|
|
1282
|
+
const { sub, extra } = ctx || {};
|
|
1283
|
+
if (sub) {
|
|
1284
|
+
const target = extra ? `${sub} ${extra}`.trim() : sub;
|
|
1285
|
+
return handlers['/session']({ ...ctx, sub: 'load', extra: target });
|
|
1286
|
+
}
|
|
1287
|
+
const r = await audioPlayer.resume();
|
|
1288
|
+
console.log(`\n ${C.emerald}${r}${C.reset}\n`);
|
|
1289
|
+
return 'HANDLED';
|
|
1290
|
+
},
|
|
1172
1291
|
'/next': async () => { const r = await audioPlayer.next(); console.log(`\n ${C.emerald}${r}${C.reset}\n`); return 'HANDLED'; },
|
|
1173
1292
|
'/prev': async () => { const r = await audioPlayer.prev(); console.log(`\n ${C.emerald}${r}${C.reset}\n`); return 'HANDLED'; },
|
|
1174
1293
|
'/mute': async () => { const r = await audioPlayer.mute(); console.log(`\n ${C.emerald}${r}${C.reset}\n`); return 'HANDLED'; },
|
|
@@ -1302,9 +1421,24 @@ export const handlers = {
|
|
|
1302
1421
|
ctx.modelManager.client = ctx.client;
|
|
1303
1422
|
ctx.modelManager.requestedModel = info.model;
|
|
1304
1423
|
const resolution = await ctx.modelManager.resolve({ requestedModel: info.model });
|
|
1305
|
-
if (resolution.connected && resolution.
|
|
1306
|
-
|
|
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
|
+
}
|
|
1307
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
|
+
}
|
|
1308
1442
|
console.log(` ${C.amber}• Provider is not ready: ${resolution.error}${C.reset}`);
|
|
1309
1443
|
}
|
|
1310
1444
|
}
|
|
@@ -1428,9 +1562,11 @@ export const handlers = {
|
|
|
1428
1562
|
return 'HANDLED';
|
|
1429
1563
|
}
|
|
1430
1564
|
|
|
1431
|
-
if (
|
|
1432
|
-
ctx.client.setApiKey(newKey);
|
|
1433
|
-
|
|
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);
|
|
1434
1570
|
}
|
|
1435
1571
|
const pDef = getProvider(targetProvider);
|
|
1436
1572
|
if (pDef) pDef.apiKey = newKey;
|
|
@@ -1438,7 +1574,7 @@ export const handlers = {
|
|
|
1438
1574
|
const masked = newKey.length > 8
|
|
1439
1575
|
? `${newKey.slice(0, 4)}...${newKey.slice(-4)}`
|
|
1440
1576
|
: '****';
|
|
1441
|
-
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`);
|
|
1442
1578
|
return 'HANDLED';
|
|
1443
1579
|
}
|
|
1444
1580
|
|
|
@@ -1468,9 +1604,10 @@ export const handlers = {
|
|
|
1468
1604
|
},
|
|
1469
1605
|
|
|
1470
1606
|
'/model': async (ctx) => {
|
|
1471
|
-
const
|
|
1607
|
+
const rawSub = (ctx.sub || '').trim();
|
|
1608
|
+
const action = (rawSub || 'status').toLowerCase();
|
|
1472
1609
|
const providerName = ctx.client.providerName || 'Model';
|
|
1473
|
-
const { getRecommendedModels } = await import('./config.js');
|
|
1610
|
+
const { getRecommendedModels, CLOUD_PROVIDERS } = await import('./config.js');
|
|
1474
1611
|
|
|
1475
1612
|
if (action === 'reconnect') {
|
|
1476
1613
|
if (!ctx.modelManager) {
|
|
@@ -1480,8 +1617,18 @@ export const handlers = {
|
|
|
1480
1617
|
const resolution = await ctx.modelManager.resolve();
|
|
1481
1618
|
if (resolution.connected && resolution.selectedModel) {
|
|
1482
1619
|
ctx.setCatalogOnlyMode?.(false);
|
|
1620
|
+
if (ctx.tuiPrompt) {
|
|
1621
|
+
ctx.tuiPrompt.isConnected = true;
|
|
1622
|
+
ctx.tuiPrompt.modelName = resolution.selectedModel;
|
|
1623
|
+
ctx.tuiPrompt.render();
|
|
1624
|
+
}
|
|
1483
1625
|
console.log(`\n ${C.emerald}[OK] Connected using ${resolution.selectedModel}.${C.reset}\n`);
|
|
1484
1626
|
} else {
|
|
1627
|
+
ctx.setCatalogOnlyMode?.(true);
|
|
1628
|
+
if (ctx.tuiPrompt) {
|
|
1629
|
+
ctx.tuiPrompt.isConnected = false;
|
|
1630
|
+
ctx.tuiPrompt.render();
|
|
1631
|
+
}
|
|
1485
1632
|
console.log(`\n ${C.red}[MODEL ERROR] ${resolution.error || 'No chat model is ready.'}${C.reset}\n`);
|
|
1486
1633
|
}
|
|
1487
1634
|
return 'HANDLED';
|
|
@@ -1523,19 +1670,42 @@ export const handlers = {
|
|
|
1523
1670
|
`${C.bold}${C.lavender}Reported by Server:${C.reset}`,
|
|
1524
1671
|
...(serverModels.length ? serverModels.map(s => ` ${s}`) : [` ${C.dim}No live model list reported by ${providerName}.${C.reset}`]),
|
|
1525
1672
|
'',
|
|
1526
|
-
`${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}`
|
|
1527
1674
|
],
|
|
1528
1675
|
});
|
|
1529
1676
|
return 'HANDLED';
|
|
1530
1677
|
}
|
|
1531
1678
|
|
|
1532
|
-
|
|
1679
|
+
const isDirectUse = action === 'use';
|
|
1680
|
+
const targetModel = isDirectUse ? (ctx.extra?.trim() || '') : (!['status', 'diag', 'diagnostics'].includes(action) ? rawSub : '');
|
|
1681
|
+
|
|
1682
|
+
if (targetModel) {
|
|
1533
1683
|
try {
|
|
1534
|
-
const
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
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] });
|
|
1538
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] });
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1539
1709
|
if (ctx.modelManager) {
|
|
1540
1710
|
const selected = ctx.modelManager.select(targetModel, liveCheck.models || []);
|
|
1541
1711
|
console.log(`\n ${C.emerald}[OK] Active model changed to ${selected}.${C.reset}\n`);
|
|
@@ -2282,20 +2452,12 @@ export const handlers = {
|
|
|
2282
2452
|
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2283
2453
|
const request = parseDetailsRequest(reference);
|
|
2284
2454
|
const details = ctx.agent?.lastQueryDetails || null;
|
|
2285
|
-
|
|
2286
|
-
printRichCard({
|
|
2287
|
-
title: 'No Diagnostics Recorded',
|
|
2288
|
-
status: 'warning',
|
|
2289
|
-
lines: ['No completed model queries are available to copy.'],
|
|
2290
|
-
});
|
|
2291
|
-
return 'HANDLED';
|
|
2292
|
-
}
|
|
2293
|
-
const copyable = getCopyableContent(details, request);
|
|
2455
|
+
const copyable = getCopyableContent(details, request, ctx.agent?.session);
|
|
2294
2456
|
if (!copyable?.text) {
|
|
2295
2457
|
printRichCard({
|
|
2296
2458
|
title: 'Nothing to Copy',
|
|
2297
2459
|
status: 'warning',
|
|
2298
|
-
lines: ['The requested query section is empty.'],
|
|
2460
|
+
lines: ['The requested query or session section is empty.'],
|
|
2299
2461
|
});
|
|
2300
2462
|
return 'HANDLED';
|
|
2301
2463
|
}
|
|
@@ -2318,7 +2480,8 @@ export const handlers = {
|
|
|
2318
2480
|
},
|
|
2319
2481
|
|
|
2320
2482
|
'/gallery': async (ctx) => {
|
|
2321
|
-
const
|
|
2483
|
+
const count = Number(ctx.sub) || 2;
|
|
2484
|
+
const displayed = typeof ctx.showTerminalImage === 'function' && ctx.showTerminalImage(count);
|
|
2322
2485
|
if (!displayed) {
|
|
2323
2486
|
printRichCard({
|
|
2324
2487
|
title: 'Terminal Gallery Unavailable',
|
|
@@ -2571,6 +2734,11 @@ export const handlers = {
|
|
|
2571
2734
|
}
|
|
2572
2735
|
return 'HANDLED';
|
|
2573
2736
|
},
|
|
2737
|
+
|
|
2738
|
+
'/exit': async (ctx) => {
|
|
2739
|
+
try { await ctx.agent?.endSession(); } catch {}
|
|
2740
|
+
return 'EXIT';
|
|
2741
|
+
},
|
|
2574
2742
|
};
|
|
2575
2743
|
|
|
2576
2744
|
const ALIASES = {
|
|
@@ -2580,6 +2748,7 @@ const ALIASES = {
|
|
|
2580
2748
|
'/?': '/help', '/commands': '/help', '/menu': '/help',
|
|
2581
2749
|
'/hist': '/history', '/h': '/history',
|
|
2582
2750
|
'/sess': '/session', '/sessions': '/session',
|
|
2751
|
+
'/load-session': '/session', '/load': '/session', '/restore': '/session',
|
|
2583
2752
|
'/think': '/thinking', '/th': '/thinking',
|
|
2584
2753
|
'/detail': '/details', '/d': '/details',
|
|
2585
2754
|
'/language': '/lang',
|
|
@@ -2608,6 +2777,9 @@ const ALIASES = {
|
|
|
2608
2777
|
'/export': '/save',
|
|
2609
2778
|
'/r': '/retry',
|
|
2610
2779
|
'/kvip': '/key',
|
|
2780
|
+
'/switch': '/provider',
|
|
2781
|
+
'/providers': '/provider',
|
|
2782
|
+
'/fallback': '/provider',
|
|
2611
2783
|
};
|
|
2612
2784
|
|
|
2613
2785
|
export async function routeCommand(rawInput, ctx) {
|