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 CHANGED
@@ -87,7 +87,7 @@ Automatic installation is available for global npm installs. Source checkouts, l
87
87
 
88
88
  After an update or rollback, restart `kiku-agent` and any application hosting `kiku-mcp`. Update settings and rollback history are stored in the normal Kiku user data directory as `update-state.json`; sessions, memory, `.env`, and YouTube cookies are not modified.
89
89
 
90
- For a local checkout before the package is published, use `npm pack` followed by `npm install -g ./kiku-agent-1.0.9.tgz`.
90
+ For a local checkout before the package is published, use `npm pack` followed by `npm install -g ./kiku-agent-1.0.11.tgz`.
91
91
 
92
92
  ### ✨ Features
93
93
  - **LM Studio Integration**: Connects seamlessly to LM Studio's OpenAI-compatible server at `http://127.0.0.1:1234/v1` (default model `google/gemma-4-e4b`, with auto-detection of loaded models).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiku-agent",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Kiku AI terminal agent and MCP server for Ju Jingyi knowledge, media, and career data.",
5
5
  "license": "GPL-3.0",
6
6
  "type": "module",
@@ -29,10 +29,10 @@
29
29
  "kikuUpdate": {
30
30
  "channel": "stable",
31
31
  "releaseSummary": [
32
- "Added graceful 404 error handling for unpublished channels and non-existent version lookups",
33
- "Enhanced CLI update diagnostics with active release channel switching guidance",
34
- "Updated production model defaults and live fallback resolution across cloud providers",
35
- "Hardened REST API key validation and isolated rate-limit probes"
32
+ "Added session restore and resume support",
33
+ "Fixed CJK fullwidth table wrapping in TUI",
34
+ "Enhanced React 19 rendering safety",
35
+ "Added comprehensive version bump tool"
36
36
  ],
37
37
  "releaseUrl": "https://www.npmjs.com/package/kiku-agent"
38
38
  },
@@ -63,7 +63,8 @@
63
63
  "kiku-agent": "node server/agent/cli.js",
64
64
  "kiku-mcp": "node server/mcp/stdio.js",
65
65
  "kiku-ink": "npx tsx server/agent/ink/main.jsx",
66
- "test": "node --test server/agent/tests/*.test.js server/mcp/tests/*.test.js"
66
+ "test": "node --test server/agent/tests/*.test.js server/mcp/tests/*.test.js",
67
+ "bump": "node bump_version.js"
67
68
  },
68
69
  "dependencies": {
69
70
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -12,8 +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';
16
- import { AgentJobManager } from './jobManager.js';
15
+ import { contextCompactor } from './contextCompactor.js';
16
+ import { AgentJobManager } from './jobManager.js';
17
17
 
18
18
  export function detectDominantLanguage(text) {
19
19
  if (!text || typeof text !== 'string') return 'th';
@@ -64,8 +64,8 @@ export class KikuAgent {
64
64
  this.mcpManager = options.mcpManager || new McpClientManager();
65
65
  this.router = new DynamicToolRouter(this.getAllToolsSchema());
66
66
  this.research = new ResearchOrchestrator({ client: this.client });
67
- this.suggestions = new SuggestionEngine({ client: this.client, memory: this.memory });
68
- this.jobs = new AgentJobManager();
67
+ this.suggestions = new SuggestionEngine({ client: this.client, memory: this.memory });
68
+ this.jobs = new AgentJobManager();
69
69
  this.systemPrompt = this.buildSystemPrompt();
70
70
  this.initHistory();
71
71
  this.restoredMessageCount = 0;
@@ -103,39 +103,39 @@ export class KikuAgent {
103
103
  return this._ready;
104
104
  }
105
105
 
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);
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);
129
129
  }
130
130
 
131
131
  getResearchStatus() {
132
132
  return this.research.getStatus();
133
133
  }
134
134
 
135
- cancelResearch() {
136
- const direct = this.research.cancel();
137
- const tracked = this.jobs.cancelType('research') || this.jobs.cancelType('verify');
138
- return direct || tracked;
135
+ cancelResearch() {
136
+ const direct = this.research.cancel();
137
+ const tracked = this.jobs.cancelType('research') || this.jobs.cancelType('verify');
138
+ return direct || tracked;
139
139
  }
140
140
 
141
141
  async generateSuggestions(options = {}) {
@@ -143,33 +143,33 @@ export class KikuAgent {
143
143
  return this.suggestions.generateSuggestions(options);
144
144
  }
145
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
+ 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
+ }
173
173
 
174
174
  initHistory() {
175
175
  this.conversationHistory = [
@@ -181,13 +181,7 @@ export class KikuAgent {
181
181
  }
182
182
 
183
183
  clearMemory() {
184
- if (this.session.messages.length > 0) {
185
- this.memory.archiveSession(this.session.toSnapshot()).catch(() => { });
186
- this.memory.consolidateSession(this.session.toSnapshot()).catch(() => { });
187
- }
188
- this.initHistory();
189
- this.session.reset();
190
- this.session.clearDisk().catch(() => { });
184
+ return this.endSession();
191
185
  }
192
186
 
193
187
  async loadSession() {
@@ -223,6 +217,59 @@ export class KikuAgent {
223
217
  }
224
218
  }
225
219
 
220
+ async restoreSession(sessionId) {
221
+ await this._ready;
222
+ if (!sessionId) return null;
223
+
224
+ let targetSessionId = String(sessionId).trim();
225
+ const archives = this.memory.getArchivedSessions(50);
226
+ if (/^\d+$/.test(targetSessionId)) {
227
+ const idx = parseInt(targetSessionId, 10) - 1;
228
+ if (archives[idx]) {
229
+ targetSessionId = archives[idx].id;
230
+ }
231
+ } else {
232
+ const match = archives.find(s => s.id === targetSessionId || s.id.toLowerCase().includes(targetSessionId.toLowerCase()));
233
+ if (match) {
234
+ targetSessionId = match.id;
235
+ }
236
+ }
237
+
238
+ const archived = await this.memory.loadArchivedSession(targetSessionId);
239
+ if (!archived) return null;
240
+
241
+ if (this.session.messages.length > 0 && this.session.id !== targetSessionId) {
242
+ await this.memory.archiveSession(this.session.toSnapshot()).catch(() => {});
243
+ }
244
+
245
+ this.session.restoreFromSnapshot(archived);
246
+ await this.session.saveToDisk().catch(() => {});
247
+
248
+ this.initHistory();
249
+ for (const msg of this.session.messages) {
250
+ if (msg.role === 'user') {
251
+ const textPart = msg.parts?.find(p => p.type === 'text');
252
+ if (textPart?.content) {
253
+ this.conversationHistory.push({
254
+ role: 'user',
255
+ content: textPart.content
256
+ });
257
+ }
258
+ } else if (msg.role === 'assistant') {
259
+ const textPart = msg.parts?.find(p => p.type === 'text');
260
+ if (textPart?.content) {
261
+ this.conversationHistory.push({
262
+ role: 'assistant',
263
+ content: textPart.content
264
+ });
265
+ }
266
+ }
267
+ }
268
+ this.restoredMessageCount = this.session.messages.length;
269
+ eventBus.emit(Events.SESSION_SAVED, { sessionId: this.session.id });
270
+ return archived;
271
+ }
272
+
226
273
  getEnrichedSystemPrompt() {
227
274
  const memoryContext = this.memory.buildMemoryContext();
228
275
  return this.systemPrompt + memoryContext;
@@ -507,7 +554,10 @@ ${toolsSummary}
507
554
 
508
555
  this.applySlidingWindow();
509
556
 
510
- const currentAssistantSessionMsg = this.session.createAssistantMessage();
557
+ const currentAssistantSessionMsg = this.session.createAssistantMessage({
558
+ model: this.client?.model || null,
559
+ provider: this.client?.providerName || this.client?.provider || null,
560
+ });
511
561
 
512
562
  let currentIteration = 0;
513
563
  const toolResultCache = new Map();
@@ -19,3 +19,5 @@ export function getKikuDataDir(env = process.env, platform = process.platform) {
19
19
  export const KIKU_DATA_DIR = getKikuDataDir();
20
20
  export const SESSION_DIR = path.join(KIKU_DATA_DIR, 'sessions');
21
21
  export const EXPORT_DIR = path.join(KIKU_DATA_DIR, 'exports');
22
+ export const PROVIDER_KEYS_FILE = path.join(KIKU_DATA_DIR, 'provider_keys.json');
23
+ export const SETTINGS_FILE = path.join(KIKU_DATA_DIR, 'settings.json');