kiku-agent 1.0.7 → 1.0.9
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/LICENSE +621 -0
- package/README.md +296 -14
- package/package.json +95 -92
- package/server/agent/agentCore.js +58 -31
- package/server/agent/cli.js +79 -43
- package/server/agent/commandFeatures.js +517 -0
- package/server/agent/commandInput.js +113 -0
- package/server/agent/commandRegistry.js +122 -8
- package/server/agent/commands.js +403 -28
- package/server/agent/jobManager.js +96 -0
- package/server/agent/mcpClient.js +68 -11
- package/server/agent/memory.js +87 -14
- package/server/agent/quizData.js +384 -0
- package/server/agent/terminalImage.js +248 -0
- package/server/agent/tools.js +153 -43
- package/server/agent/ui.js +67 -21
- package/server/agent/updateManager.js +21 -3
- package/src/assets/images/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
- package/src/assets/images/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
|
@@ -12,7 +12,8 @@ import { ResearchOrchestrator } from './research.js';
|
|
|
12
12
|
import { SuggestionEngine } from './suggestions.js';
|
|
13
13
|
import { McpClientManager } from './mcpClient.js';
|
|
14
14
|
import { DynamicToolRouter } from './toolRouter.js';
|
|
15
|
-
import { contextCompactor } from './contextCompactor.js';
|
|
15
|
+
import { contextCompactor } from './contextCompactor.js';
|
|
16
|
+
import { AgentJobManager } from './jobManager.js';
|
|
16
17
|
|
|
17
18
|
export function detectDominantLanguage(text) {
|
|
18
19
|
if (!text || typeof text !== 'string') return 'th';
|
|
@@ -63,7 +64,8 @@ export class KikuAgent {
|
|
|
63
64
|
this.mcpManager = options.mcpManager || new McpClientManager();
|
|
64
65
|
this.router = new DynamicToolRouter(this.getAllToolsSchema());
|
|
65
66
|
this.research = new ResearchOrchestrator({ client: this.client });
|
|
66
|
-
this.suggestions = new SuggestionEngine({ client: this.client, memory: this.memory });
|
|
67
|
+
this.suggestions = new SuggestionEngine({ client: this.client, memory: this.memory });
|
|
68
|
+
this.jobs = new AgentJobManager();
|
|
67
69
|
this.systemPrompt = this.buildSystemPrompt();
|
|
68
70
|
this.initHistory();
|
|
69
71
|
this.restoredMessageCount = 0;
|
|
@@ -101,33 +103,39 @@ export class KikuAgent {
|
|
|
101
103
|
return this._ready;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
async runSubagent(task, options = {}) {
|
|
105
|
-
await this._ready;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
})
|
|
106
|
+
async runSubagent(task, options = {}) {
|
|
107
|
+
await this._ready;
|
|
108
|
+
return this.jobs.run('subagent', task, async ({ signal }) => {
|
|
109
|
+
const subagent = new ResearchSubagent({
|
|
110
|
+
client: this.client,
|
|
111
|
+
maxIterations: options.maxIterations || 3,
|
|
112
|
+
});
|
|
113
|
+
return subagent.run(task, {
|
|
114
|
+
signal,
|
|
115
|
+
onProgress: options.onProgress,
|
|
116
|
+
skillContext: this.memory.buildUserSkillContext(),
|
|
117
|
+
});
|
|
118
|
+
}, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async runResearch(question, options = {}) {
|
|
122
|
+
await this._ready;
|
|
123
|
+
const type = options.mode === 'verify' ? 'verify' : 'research';
|
|
124
|
+
return this.jobs.run(type, question, ({ signal }) => this.research.run(question, {
|
|
125
|
+
...options,
|
|
126
|
+
signal,
|
|
127
|
+
skillContext: this.memory.buildUserSkillContext(),
|
|
128
|
+
}), options);
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
getResearchStatus() {
|
|
126
132
|
return this.research.getStatus();
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
cancelResearch() {
|
|
130
|
-
|
|
135
|
+
cancelResearch() {
|
|
136
|
+
const direct = this.research.cancel();
|
|
137
|
+
const tracked = this.jobs.cancelType('research') || this.jobs.cancelType('verify');
|
|
138
|
+
return direct || tracked;
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
async generateSuggestions(options = {}) {
|
|
@@ -135,14 +143,33 @@ export class KikuAgent {
|
|
|
135
143
|
return this.suggestions.generateSuggestions(options);
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
async runSuggestionBatch(targets, options = {}) {
|
|
139
|
-
await this._ready;
|
|
140
|
-
return this.suggestions.runBatchSubagents(targets,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
async runSuggestionBatch(targets, options = {}) {
|
|
147
|
+
await this._ready;
|
|
148
|
+
return this.jobs.run('suggestion', String(targets || 'all'), ({ signal }) => this.suggestions.runBatchSubagents(targets, {
|
|
149
|
+
...options,
|
|
150
|
+
signal,
|
|
151
|
+
}), options);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getLastSuggestions() {
|
|
155
|
+
return this.suggestions.getLastSuggestions();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getJobs() {
|
|
159
|
+
return this.jobs.list();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getJob(id) {
|
|
163
|
+
return this.jobs.get(id);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
cancelJob(id) {
|
|
167
|
+
return this.jobs.cancel(id);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
clearJobs() {
|
|
171
|
+
return this.jobs.clear();
|
|
172
|
+
}
|
|
146
173
|
|
|
147
174
|
initHistory() {
|
|
148
175
|
this.conversationHistory = [
|
package/server/agent/cli.js
CHANGED
|
@@ -17,8 +17,9 @@ import { sanitizeQuery } from './sanitizer.js';
|
|
|
17
17
|
import { isAbortError } from './errors.js';
|
|
18
18
|
import { COMMAND_DEFINITIONS, SUB_COMMANDS, findCommandDefinition, getCommandDetails } from './commandRegistry.js';
|
|
19
19
|
import { ModelManager } from './modelManager.js';
|
|
20
|
-
import { PACKAGE_VERSION } from './version.js';
|
|
21
|
-
import { getTheme, getThemeName } from './theme.js';
|
|
20
|
+
import { PACKAGE_VERSION } from './version.js';
|
|
21
|
+
import { getTheme, getThemeName } from './theme.js';
|
|
22
|
+
import { createTerminalImageGallery } from './terminalImage.js';
|
|
22
23
|
import {
|
|
23
24
|
classifyToolResult,
|
|
24
25
|
formatDetailDuration,
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
printWelcomeHero, getWelcomeHeroLines, randomFanName, setupOutputInterception,
|
|
39
40
|
rawWrite, INPUT_PLACEHOLDER, UI_ICONS,
|
|
40
41
|
spinnerFrames, thinkingFrames, createProgressRunner,
|
|
41
|
-
enterAlternateScreen, exitAlternateScreen, getTuiViewportLayout, VirtualScreenBuffer,
|
|
42
|
+
enterAlternateScreen, exitAlternateScreen, getTuiViewportLayout, getNewOutputScrollOffset, VirtualScreenBuffer,
|
|
42
43
|
} from './ui.js';
|
|
43
44
|
|
|
44
45
|
let activeAbortController = null;
|
|
@@ -490,7 +491,11 @@ function printAd() {
|
|
|
490
491
|
|
|
491
492
|
class InteractiveTuiPrompt {
|
|
492
493
|
constructor(options = {}) {
|
|
493
|
-
this.
|
|
494
|
+
this.defaultPromptText = options.prompt || 'Kiku ❯ ';
|
|
495
|
+
this.promptText = this.defaultPromptText;
|
|
496
|
+
this.inputPlaceholder = INPUT_PLACEHOLDER;
|
|
497
|
+
this.recordInputHistory = true;
|
|
498
|
+
this.cancelValue = null;
|
|
494
499
|
this.agent = options.agent || null;
|
|
495
500
|
this.modelName = options.modelName || 'gemma-4-e4b';
|
|
496
501
|
this.provider = options.provider || 'lmstudio';
|
|
@@ -521,27 +526,28 @@ class InteractiveTuiPrompt {
|
|
|
521
526
|
);
|
|
522
527
|
}
|
|
523
528
|
|
|
524
|
-
addViewportLines(lines) {
|
|
525
|
-
if (lines === null || lines === undefined) return;
|
|
526
|
-
if (!Array.isArray(lines)) lines = [lines];
|
|
527
|
-
const wasLive = this.scrollOffset === 0;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
529
|
+
addViewportLines(lines) {
|
|
530
|
+
if (lines === null || lines === undefined) return;
|
|
531
|
+
if (!Array.isArray(lines)) lines = [lines];
|
|
532
|
+
const wasLive = this.scrollOffset === 0;
|
|
533
|
+
let addedLineCount = 0;
|
|
534
|
+
for (const l of lines) {
|
|
535
|
+
if (typeof l === 'string') {
|
|
536
|
+
const sublines = l.split('\n');
|
|
537
|
+
for (const sl of sublines) {
|
|
538
|
+
this.viewportLines.push(sl);
|
|
539
|
+
addedLineCount += 1;
|
|
540
|
+
}
|
|
541
|
+
} else if (l != null) {
|
|
542
|
+
this.viewportLines.push(String(l));
|
|
543
|
+
addedLineCount += 1;
|
|
544
|
+
}
|
|
537
545
|
}
|
|
538
546
|
if (this.viewportLines.length > 5000) {
|
|
539
547
|
const removedCount = this.viewportLines.length - 5000;
|
|
540
548
|
this.viewportLines.splice(0, removedCount);
|
|
541
549
|
}
|
|
542
|
-
|
|
543
|
-
this.scrollOffset = 0;
|
|
544
|
-
}
|
|
550
|
+
this.scrollOffset = getNewOutputScrollOffset(addedLineCount, process.stdout.rows || 24, wasLive ? 0 : this.scrollOffset);
|
|
545
551
|
this.render();
|
|
546
552
|
}
|
|
547
553
|
|
|
@@ -578,10 +584,15 @@ class InteractiveTuiPrompt {
|
|
|
578
584
|
try { if (input.isTTY) input.setRawMode(false); } catch { }
|
|
579
585
|
}
|
|
580
586
|
|
|
581
|
-
async readLine() {
|
|
582
|
-
return new Promise((resolve, reject) => {
|
|
583
|
-
this._cleanupInput();
|
|
584
|
-
|
|
587
|
+
async readLine(options = {}) {
|
|
588
|
+
return new Promise((resolve, reject) => {
|
|
589
|
+
this._cleanupInput();
|
|
590
|
+
const inputOptions = typeof options === 'string' ? { prompt: options } : options;
|
|
591
|
+
this.promptText = inputOptions.prompt || this.defaultPromptText;
|
|
592
|
+
this.inputPlaceholder = inputOptions.placeholder || INPUT_PLACEHOLDER;
|
|
593
|
+
this.recordInputHistory = inputOptions.history !== false;
|
|
594
|
+
this.cancelValue = inputOptions.cancelValue ?? null;
|
|
595
|
+
this.line = '';
|
|
585
596
|
this.cursor = 0;
|
|
586
597
|
this.historyIndex = -1;
|
|
587
598
|
this.popoverIndex = 0;
|
|
@@ -615,7 +626,7 @@ class InteractiveTuiPrompt {
|
|
|
615
626
|
this.cursor = 0;
|
|
616
627
|
this.popoverActive = false;
|
|
617
628
|
this.popoverItems = [];
|
|
618
|
-
if (result && result.trim()) this.history.push(result);
|
|
629
|
+
if (this.recordInputHistory && result && result.trim()) this.history.push(result);
|
|
619
630
|
const resolve = this.activeResolve;
|
|
620
631
|
this.activeResolve = null;
|
|
621
632
|
this.activeReject = null;
|
|
@@ -784,7 +795,7 @@ class InteractiveTuiPrompt {
|
|
|
784
795
|
trailingStyled = `${C.dim}${C.slate}${ghostDisplay}${C.reset}`;
|
|
785
796
|
}
|
|
786
797
|
} else if (!this.line) {
|
|
787
|
-
const placeholder = sliceLineForDisplay(
|
|
798
|
+
const placeholder = sliceLineForDisplay(this.inputPlaceholder, 0, maxLineWidth).displayLine;
|
|
788
799
|
trailingStyled = `${C.dim}${C.slate}${placeholder}${C.reset}`;
|
|
789
800
|
}
|
|
790
801
|
|
|
@@ -801,8 +812,12 @@ class InteractiveTuiPrompt {
|
|
|
801
812
|
handleKeypress(str, key) {
|
|
802
813
|
if (!key && !str) return;
|
|
803
814
|
|
|
804
|
-
if ((key?.ctrl && key?.name === 'c') || str === '\x03') {
|
|
805
|
-
if (
|
|
815
|
+
if ((key?.ctrl && key?.name === 'c') || str === '\x03') {
|
|
816
|
+
if (this.cancelValue !== null) {
|
|
817
|
+
this.finish(this.cancelValue);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (isProcessing) {
|
|
806
821
|
if (this.onCancel) this.onCancel();
|
|
807
822
|
else if (this.onSigInt) this.onSigInt();
|
|
808
823
|
this.addViewportLines(` ${C.amber}[✓ Cancelled by user]${C.reset}`);
|
|
@@ -827,8 +842,12 @@ class InteractiveTuiPrompt {
|
|
|
827
842
|
return;
|
|
828
843
|
}
|
|
829
844
|
|
|
830
|
-
if (key?.name === 'escape' || str === '\x1b') {
|
|
831
|
-
if (this.
|
|
845
|
+
if (key?.name === 'escape' || str === '\x1b') {
|
|
846
|
+
if (this.cancelValue !== null) {
|
|
847
|
+
this.finish(this.cancelValue);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (this.popoverActive) {
|
|
832
851
|
this.popoverActive = false;
|
|
833
852
|
this.popoverItems = [];
|
|
834
853
|
this.ghostText = '';
|
|
@@ -1601,7 +1620,7 @@ async function main() {
|
|
|
1601
1620
|
return false;
|
|
1602
1621
|
}
|
|
1603
1622
|
|
|
1604
|
-
const tuiPrompt = new InteractiveTuiPrompt({
|
|
1623
|
+
const tuiPrompt = new InteractiveTuiPrompt({
|
|
1605
1624
|
prompt: 'Kiku ❯ ',
|
|
1606
1625
|
agent,
|
|
1607
1626
|
modelName: opts.model || client.model || 'gemma-4-e4b',
|
|
@@ -1621,7 +1640,8 @@ async function main() {
|
|
|
1621
1640
|
process.exit(0);
|
|
1622
1641
|
}
|
|
1623
1642
|
}
|
|
1624
|
-
});
|
|
1643
|
+
});
|
|
1644
|
+
const terminalGallery = createTerminalImageGallery();
|
|
1625
1645
|
|
|
1626
1646
|
globalTuiPrompt = tuiPrompt;
|
|
1627
1647
|
setupOutputInterception(tuiPrompt, () => isProcessing);
|
|
@@ -1743,9 +1763,17 @@ async function main() {
|
|
|
1743
1763
|
|
|
1744
1764
|
const mcpStatus = agent.mcpManager ? agent.mcpManager.getStatus() : null;
|
|
1745
1765
|
if (mcpStatus && mcpStatus.activeServersCount > 0 && !opts.mcp) {
|
|
1746
|
-
const serverSummaries = mcpStatus.servers
|
|
1747
|
-
.filter(s => s.status === 'connected')
|
|
1748
|
-
.map(s =>
|
|
1766
|
+
const serverSummaries = mcpStatus.servers
|
|
1767
|
+
.filter(s => s.status === 'connected')
|
|
1768
|
+
.map(s => {
|
|
1769
|
+
const sources = (s.sources || [])
|
|
1770
|
+
.map(source => String(source).split(/[\\/]/).pop())
|
|
1771
|
+
.filter(Boolean);
|
|
1772
|
+
const sourceLabel = sources.length > 0
|
|
1773
|
+
? ` • source: ${[...new Set(sources)].join(', ')}`
|
|
1774
|
+
: '';
|
|
1775
|
+
return `• ${s.name} (${s.type}) — ${s.toolsCount} tools${sourceLabel}`;
|
|
1776
|
+
});
|
|
1749
1777
|
printRichCard({
|
|
1750
1778
|
title: 'Persisted MCP Connected',
|
|
1751
1779
|
status: 'mcp',
|
|
@@ -1804,10 +1832,16 @@ async function main() {
|
|
|
1804
1832
|
printHelp,
|
|
1805
1833
|
printQueryDetails,
|
|
1806
1834
|
copyQueryDetails,
|
|
1807
|
-
printWelcome: printWelcomeHero,
|
|
1808
|
-
printAd,
|
|
1809
|
-
|
|
1810
|
-
|
|
1835
|
+
printWelcome: printWelcomeHero,
|
|
1836
|
+
printAd,
|
|
1837
|
+
showTerminalImage: () => {
|
|
1838
|
+
const imageLines = terminalGallery.maybeRender(queryCount, { force: true });
|
|
1839
|
+
if (imageLines.length > 0) tuiPrompt.addViewportLines(imageLines);
|
|
1840
|
+
return imageLines.length > 0;
|
|
1841
|
+
},
|
|
1842
|
+
cancelActiveQuery,
|
|
1843
|
+
readInput: options => tuiPrompt.readLine(options),
|
|
1844
|
+
enqueueQuery: async (prompt, isRetry) => {
|
|
1811
1845
|
queryQueue.push({ id: 'q_' + Date.now(), number: nextQueryNumber++, prompt, addedAt: Date.now() });
|
|
1812
1846
|
triggerQueueProcessing();
|
|
1813
1847
|
},
|
|
@@ -1879,10 +1913,12 @@ async function main() {
|
|
|
1879
1913
|
});
|
|
1880
1914
|
}
|
|
1881
1915
|
} finally {
|
|
1882
|
-
activeAbortController = null;
|
|
1883
|
-
queryCount++;
|
|
1884
|
-
printAd();
|
|
1885
|
-
|
|
1916
|
+
activeAbortController = null;
|
|
1917
|
+
queryCount++;
|
|
1918
|
+
printAd();
|
|
1919
|
+
const imageLines = terminalGallery.maybeRender(queryCount);
|
|
1920
|
+
if (imageLines.length > 0) tuiPrompt.addViewportLines(imageLines);
|
|
1921
|
+
activeQueryNumber = null;
|
|
1886
1922
|
if (globalTuiPrompt) {
|
|
1887
1923
|
globalTuiPrompt.agentState = '';
|
|
1888
1924
|
globalTuiPrompt.render();
|