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/doctor.js
CHANGED
|
@@ -57,7 +57,7 @@ export async function runDoctor(options = {}) {
|
|
|
57
57
|
{ id: 'node', required: true, ok: nodeMajor >= 20, detail: `Node ${process.versions.node} (requires >=20)` },
|
|
58
58
|
{ id: 'data', required: true, ok: dataWritable, detail: dataDir },
|
|
59
59
|
{ id: 'gpu', required: false, ok: Boolean(gpu.available), detail: gpu.available && gpu.primary ? `${gpu.primary.name} (${gpu.primary.totalGb} GB VRAM${gpu.primary.driverVersion !== 'N/A' ? `, Driver ${gpu.primary.driverVersion}` : ''})` : 'CPU fallback / no dedicated acceleration detected' },
|
|
60
|
-
{ id: 'lmstudio', required: false, ok: Boolean(lm.connected), detail: lm.connected ? `${lm.models?.length || 0} loaded model(s)` : 'offline;
|
|
60
|
+
{ id: 'lmstudio', required: false, ok: Boolean(lm.connected), detail: lm.connected ? `${lm.models?.length || 0} loaded model(s)` : 'offline; LM Studio server required for AI chat (/model reconnect)' },
|
|
61
61
|
{ id: 'ffmpeg', required: false, ok: ffmpegStatic && fluentFfmpeg, detail: 'audio add-on only; intentionally not installed with the core CLI' },
|
|
62
62
|
{ id: 'speaker', required: false, ok: speaker, detail: 'native audio add-on only; intentionally not installed with the core CLI' },
|
|
63
63
|
{ id: 'yt-dlp', required: false, ok: ytDlp, detail: 'recommended for YouTube search and playback' },
|
package/server/agent/errors.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { C } from './ui.js';
|
|
2
|
+
|
|
1
3
|
export function createAbortError(message = 'Query cancelled by user.') {
|
|
2
4
|
const error = new Error(message);
|
|
3
5
|
error.name = 'AbortError';
|
|
@@ -7,3 +9,206 @@ export function createAbortError(message = 'Query cancelled by user.') {
|
|
|
7
9
|
export function isAbortError(error) {
|
|
8
10
|
return error?.name === 'AbortError';
|
|
9
11
|
}
|
|
12
|
+
|
|
13
|
+
export function parseApiErrorText(errorText) {
|
|
14
|
+
if (!errorText) return 'Unknown error';
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(errorText);
|
|
17
|
+
if (parsed.error) {
|
|
18
|
+
if (typeof parsed.error === 'string') return parsed.error;
|
|
19
|
+
if (parsed.error.message) return parsed.error.message;
|
|
20
|
+
if (parsed.error.description) return parsed.error.description;
|
|
21
|
+
}
|
|
22
|
+
if (parsed.message) return parsed.message;
|
|
23
|
+
if (parsed.description) return parsed.description;
|
|
24
|
+
} catch { }
|
|
25
|
+
return String(errorText).replace(/\s+/g, ' ').trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseExecutionError(err, context = {}) {
|
|
29
|
+
const rawMsg = err?.message || String(err || '');
|
|
30
|
+
const provider = (context.provider || context.client?.provider || 'cloud').toLowerCase();
|
|
31
|
+
const providerName = context.providerName || context.client?.providerName || (provider === 'lmstudio' ? 'LM Studio' : 'AI Provider');
|
|
32
|
+
const model = context.model || context.client?.model || 'active model';
|
|
33
|
+
const isLocal = provider === 'lmstudio';
|
|
34
|
+
|
|
35
|
+
let cleanMsg = rawMsg;
|
|
36
|
+
const jsonMatch = rawMsg.match(/(\{[\s\S]*\})/);
|
|
37
|
+
if (jsonMatch) {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(jsonMatch[1]);
|
|
40
|
+
if (parsed.error) {
|
|
41
|
+
if (typeof parsed.error === 'string') cleanMsg = parsed.error;
|
|
42
|
+
else if (parsed.error.message) cleanMsg = parsed.error.message;
|
|
43
|
+
else if (parsed.error.description) cleanMsg = parsed.error.description;
|
|
44
|
+
} else if (parsed.message) {
|
|
45
|
+
cleanMsg = parsed.message;
|
|
46
|
+
}
|
|
47
|
+
} catch { }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
cleanMsg = cleanMsg.replace(/^[A-Za-z0-9_\s]+error\s*\(\d+\):\s*/i, '').trim();
|
|
51
|
+
|
|
52
|
+
let type = 'general';
|
|
53
|
+
let title = 'Execution Error';
|
|
54
|
+
let headline = cleanMsg;
|
|
55
|
+
let details = [];
|
|
56
|
+
let recoveryOptions = [];
|
|
57
|
+
|
|
58
|
+
const lowerRaw = rawMsg.toLowerCase();
|
|
59
|
+
const status = err?.status || (rawMsg.match(/\((\d{3})\)/)?.[1] ? parseInt(rawMsg.match(/\((\d{3})\)/)[1], 10) : null);
|
|
60
|
+
|
|
61
|
+
if (status === 402 || lowerRaw.includes('insufficient credits') || lowerRaw.includes('credit balance') || lowerRaw.includes('purchased credits') || lowerRaw.includes('payment required')) {
|
|
62
|
+
type = 'insufficient_credits';
|
|
63
|
+
title = `${providerName} • Insufficient Credits (402)`;
|
|
64
|
+
headline = `Your ${providerName} account has run out of credits or has zero balance.`;
|
|
65
|
+
details = [
|
|
66
|
+
'The provider rejected the request with HTTP 402 (Payment Required).',
|
|
67
|
+
cleanMsg && cleanMsg !== headline ? `Provider note: ${cleanMsg}` : '',
|
|
68
|
+
].filter(Boolean);
|
|
69
|
+
recoveryOptions = [
|
|
70
|
+
{ action: 'Switch to local LM Studio (Free/Offline)', cmd: '/provider use lmstudio' },
|
|
71
|
+
{ action: 'Switch to free Cloud tier (Groq)', cmd: '/provider use groq' },
|
|
72
|
+
{ action: 'Switch to another configured provider', cmd: '/provider list' },
|
|
73
|
+
];
|
|
74
|
+
} else if (status === 429 || lowerRaw.includes('rate limit') || lowerRaw.includes('quota') || lowerRaw.includes('tokens per minute') || lowerRaw.includes('tpm') || lowerRaw.includes('rpm')) {
|
|
75
|
+
type = 'rate_limit';
|
|
76
|
+
title = `${providerName} • Rate Limit Exceeded (429)`;
|
|
77
|
+
headline = `Usage limit reached on ${providerName} (${model}).`;
|
|
78
|
+
details = [
|
|
79
|
+
'Request exceeded tokens-per-minute (TPM) or requests-per-minute (RPM) quota.',
|
|
80
|
+
cleanMsg && cleanMsg !== headline ? `Details: ${cleanMsg}` : '',
|
|
81
|
+
].filter(Boolean);
|
|
82
|
+
recoveryOptions = [
|
|
83
|
+
{ action: 'Wait a few seconds and retry', cmd: '/retry' },
|
|
84
|
+
{ action: 'Switch to local LM Studio (No rate limits)', cmd: '/provider use lmstudio' },
|
|
85
|
+
{ action: 'Switch to another cloud provider', cmd: '/provider use groq' },
|
|
86
|
+
];
|
|
87
|
+
} else if (status === 401 || status === 403 || lowerRaw.includes('invalid api key') || lowerRaw.includes('unauthorized') || lowerRaw.includes('authentication') || lowerRaw.includes('forbidden') || lowerRaw.includes('api key not found')) {
|
|
88
|
+
type = 'auth';
|
|
89
|
+
title = `${providerName} • Authentication Failed (${status || 401})`;
|
|
90
|
+
headline = `The API key for ${providerName} was rejected or is missing.`;
|
|
91
|
+
details = [
|
|
92
|
+
`Check your API key configuration for ${providerName}.`,
|
|
93
|
+
cleanMsg && cleanMsg !== headline ? `Details: ${cleanMsg}` : '',
|
|
94
|
+
].filter(Boolean);
|
|
95
|
+
recoveryOptions = [
|
|
96
|
+
{ action: `Set API key for ${provider}`, cmd: `/provider setkey ${provider} <your_key>` },
|
|
97
|
+
{ action: 'Switch to local LM Studio (No key required)', cmd: '/provider use lmstudio' },
|
|
98
|
+
{ action: 'Switch provider', cmd: '/provider list' },
|
|
99
|
+
];
|
|
100
|
+
} else if (status === 404 || lowerRaw.includes('model not found') || lowerRaw.includes('does not exist') || lowerRaw.includes('model_not_found')) {
|
|
101
|
+
type = 'model_not_found';
|
|
102
|
+
title = `${providerName} • Model Not Found (404)`;
|
|
103
|
+
headline = `Model "${model}" is not available on ${providerName}.`;
|
|
104
|
+
details = [
|
|
105
|
+
'The model ID might be deprecated, typoed, or not enabled for your account.',
|
|
106
|
+
cleanMsg && cleanMsg !== headline ? `Details: ${cleanMsg}` : '',
|
|
107
|
+
].filter(Boolean);
|
|
108
|
+
recoveryOptions = [
|
|
109
|
+
{ action: 'List available models', cmd: `/provider models ${provider}` },
|
|
110
|
+
{ action: 'Switch to recommended model', cmd: '/model recommend' },
|
|
111
|
+
{ action: 'Switch to local LM Studio', cmd: '/provider use lmstudio' },
|
|
112
|
+
];
|
|
113
|
+
} else if (lowerRaw.includes('econnrefused') || lowerRaw.includes('fetch failed') || lowerRaw.includes('networkerror') || lowerRaw.includes('enotfound') || lowerRaw.includes('connect error') || lowerRaw.includes('failed to fetch')) {
|
|
114
|
+
type = 'network';
|
|
115
|
+
if (isLocal) {
|
|
116
|
+
title = 'LM Studio • Local Server Offline';
|
|
117
|
+
headline = 'Cannot reach LM Studio local server at http://127.0.0.1:1234/v1';
|
|
118
|
+
details = [
|
|
119
|
+
'LM Studio application is not open or local inference server is stopped.',
|
|
120
|
+
];
|
|
121
|
+
recoveryOptions = [
|
|
122
|
+
{ action: 'Start LM Studio and click "Start Server", then run', cmd: '/model reconnect' },
|
|
123
|
+
{ action: 'Switch to a Cloud AI provider', cmd: '/provider use groq' },
|
|
124
|
+
];
|
|
125
|
+
} else {
|
|
126
|
+
title = `${providerName} • Network Connection Error`;
|
|
127
|
+
headline = `Could not reach ${providerName} cloud servers.`;
|
|
128
|
+
details = [
|
|
129
|
+
'Connection dropped, timed out, or internet connectivity is unavailable.',
|
|
130
|
+
];
|
|
131
|
+
recoveryOptions = [
|
|
132
|
+
{ action: 'Switch to local LM Studio (offline)', cmd: '/provider use lmstudio' },
|
|
133
|
+
{ action: 'Retry previous query', cmd: '/retry' },
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
} else if ((status >= 500 && status < 600) || lowerRaw.includes('internal server error') || lowerRaw.includes('bad gateway') || lowerRaw.includes('service unavailable')) {
|
|
137
|
+
type = 'server_error';
|
|
138
|
+
title = `${providerName} • Provider Server Error (${status || 500})`;
|
|
139
|
+
headline = `${providerName} server is experiencing downtime or internal errors.`;
|
|
140
|
+
details = [
|
|
141
|
+
cleanMsg || 'Temporary upstream error from cloud provider.',
|
|
142
|
+
];
|
|
143
|
+
recoveryOptions = [
|
|
144
|
+
{ action: 'Switch to local LM Studio (offline)', cmd: '/provider use lmstudio' },
|
|
145
|
+
{ action: 'Switch to another cloud provider', cmd: '/provider use groq' },
|
|
146
|
+
];
|
|
147
|
+
} else if (lowerRaw.includes('context_length') || lowerRaw.includes('context length') || lowerRaw.includes('maximum context')) {
|
|
148
|
+
type = 'context_length';
|
|
149
|
+
title = `${providerName} • Context Length Exceeded`;
|
|
150
|
+
headline = `Conversation history exceeds maximum context window of ${model}.`;
|
|
151
|
+
details = [
|
|
152
|
+
'The current session has accumulated more tokens than the model can accept in one turn.',
|
|
153
|
+
];
|
|
154
|
+
recoveryOptions = [
|
|
155
|
+
{ action: 'Archive & reset session history', cmd: '/clear' },
|
|
156
|
+
{ action: 'Clear message history only', cmd: '/history clear' },
|
|
157
|
+
];
|
|
158
|
+
} else {
|
|
159
|
+
type = 'general';
|
|
160
|
+
title = `${providerName} • Query Failed`;
|
|
161
|
+
headline = cleanMsg || 'An error occurred while processing the query.';
|
|
162
|
+
if (cleanMsg.length > 120) {
|
|
163
|
+
headline = `${cleanMsg.slice(0, 120)}...`;
|
|
164
|
+
details = [cleanMsg];
|
|
165
|
+
}
|
|
166
|
+
recoveryOptions = [
|
|
167
|
+
{ action: 'Switch to local LM Studio', cmd: '/provider use lmstudio' },
|
|
168
|
+
{ action: 'Switch provider or model', cmd: '/provider list' },
|
|
169
|
+
{ action: 'Retry query', cmd: '/retry' },
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
type,
|
|
175
|
+
title,
|
|
176
|
+
headline,
|
|
177
|
+
details,
|
|
178
|
+
recoveryOptions,
|
|
179
|
+
status,
|
|
180
|
+
provider,
|
|
181
|
+
providerName,
|
|
182
|
+
model,
|
|
183
|
+
isLocal,
|
|
184
|
+
rawError: rawMsg,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function formatExecutionErrorCard(err, context = {}) {
|
|
189
|
+
const diag = parseExecutionError(err, context);
|
|
190
|
+
const lines = [
|
|
191
|
+
`${C.bold}${C.white}${diag.headline}${C.reset}`,
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
if (diag.details && diag.details.length > 0) {
|
|
195
|
+
lines.push('');
|
|
196
|
+
for (const d of diag.details) {
|
|
197
|
+
lines.push(`${C.slate}${d}${C.reset}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (diag.recoveryOptions && diag.recoveryOptions.length > 0) {
|
|
202
|
+
lines.push('', `${C.bold}${C.lavender}Recommended Recovery Actions:${C.reset}`);
|
|
203
|
+
for (const opt of diag.recoveryOptions) {
|
|
204
|
+
lines.push(` ${C.gold}• ${opt.action}:${C.reset} ${C.bold}${C.cyberCyan}${opt.cmd}${C.reset}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
title: diag.title,
|
|
210
|
+
status: 'error',
|
|
211
|
+
lines,
|
|
212
|
+
footer: `Switch immediately: ${C.cyberCyan}/provider use lmstudio${C.reset}${C.dim} | ${C.cyberCyan}/provider list${C.reset}${C.dim} | ${C.cyberCyan}/model${C.reset}`,
|
|
213
|
+
};
|
|
214
|
+
}
|