@kernel.chat/kbot 3.99.6 → 3.99.8
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/dist/agent.js +19 -3
- package/dist/doctor.js +25 -0
- package/dist/model-capabilities.d.ts +14 -0
- package/dist/model-capabilities.js +71 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -105,12 +105,16 @@ async function safeReadBody(res, maxBytes = MAX_RESPONSE_BODY) {
|
|
|
105
105
|
}
|
|
106
106
|
return decoder.decode(merged);
|
|
107
107
|
}
|
|
108
|
+
// Track whether we've already warned about a weak-tool-calling model this process.
|
|
109
|
+
let weakModelWarningShown = false;
|
|
108
110
|
// ── Local-first execution ──
|
|
109
111
|
async function tryLocalFirst(message) {
|
|
110
112
|
const lower = message.toLowerCase().trim();
|
|
111
|
-
// Only match
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
// Only match simple "read <single-filepath>" — reject anything with trailing
|
|
114
|
+
// conversational text. Prevents "Read package.json and tell me X" from
|
|
115
|
+
// feeding the whole prompt to read_file as a filename.
|
|
116
|
+
const readMatch = lower.match(/^(?:read|cat|view)\s+(\S+)\s*$/i)
|
|
117
|
+
|| lower.match(/^(?:show|open)\s+((?:\.{0,2}\/|~\/|[\w.-]+\.[\w-]+))\s*$/i);
|
|
114
118
|
if (readMatch) {
|
|
115
119
|
const tool = getTool('read_file');
|
|
116
120
|
if (tool)
|
|
@@ -788,6 +792,18 @@ export async function runAgent(message, options = {}) {
|
|
|
788
792
|
if (isLocal && byokProvider === 'ollama') {
|
|
789
793
|
warmOllamaModelCache().catch(() => { }); // non-blocking
|
|
790
794
|
}
|
|
795
|
+
// Step 0b: One-time-per-session warning if configured model lacks tool calling
|
|
796
|
+
if (isLocal && !weakModelWarningShown) {
|
|
797
|
+
weakModelWarningShown = true;
|
|
798
|
+
try {
|
|
799
|
+
const { getWeakModelWarning } = await import('./model-capabilities.js');
|
|
800
|
+
const model = getProviderModel(byokProvider, 'default');
|
|
801
|
+
const warning = await getWeakModelWarning(byokProvider, model);
|
|
802
|
+
if (warning)
|
|
803
|
+
ui.onWarning(warning);
|
|
804
|
+
}
|
|
805
|
+
catch { /* capability check is non-critical */ }
|
|
806
|
+
}
|
|
791
807
|
// Step 0: Parse multimodal content (images in message)
|
|
792
808
|
const parsed = options.multimodal || parseMultimodalMessage(message);
|
|
793
809
|
if (parsed.isMultimodal) {
|
package/dist/doctor.js
CHANGED
|
@@ -404,6 +404,29 @@ async function checkOllamaMLX() {
|
|
|
404
404
|
message: `Ollama ${version} — upgrade to 0.19+ for MLX backend (2x faster on Apple Silicon)`,
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
|
+
async function checkToolCapability() {
|
|
408
|
+
try {
|
|
409
|
+
const { getByokProvider, getProviderModel } = await import('./auth.js');
|
|
410
|
+
const { supportsToolCalls } = await import('./model-capabilities.js');
|
|
411
|
+
const provider = getByokProvider();
|
|
412
|
+
const model = getProviderModel(provider, 'default');
|
|
413
|
+
const ok = await supportsToolCalls(provider, model);
|
|
414
|
+
if (ok === true) {
|
|
415
|
+
return { name: 'Tool calling', status: 'pass', message: `${model} supports tool calls` };
|
|
416
|
+
}
|
|
417
|
+
if (ok === false) {
|
|
418
|
+
return {
|
|
419
|
+
name: 'Tool calling',
|
|
420
|
+
status: 'fail',
|
|
421
|
+
message: `${model} does NOT support tool calls — file reads, bash, git will hallucinate. Switch with \`kbot auth\` to qwen2.5-coder:14b, mistral:7b, or any Ollama model with the "tools" capability.`,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return { name: 'Tool calling', status: 'warn', message: `could not determine tool-call support for ${model}` };
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
return { name: 'Tool calling', status: 'warn', message: 'check failed unexpectedly' };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
407
430
|
// ── Hardware checks (uses machine.ts) ──
|
|
408
431
|
async function checkHardware() {
|
|
409
432
|
const results = [];
|
|
@@ -491,6 +514,8 @@ export async function runDoctor() {
|
|
|
491
514
|
// Ollama MLX check (Apple Silicon only)
|
|
492
515
|
if (ollamaMLXResult)
|
|
493
516
|
checks.push(ollamaMLXResult);
|
|
517
|
+
// Tool-calling capability of configured model
|
|
518
|
+
checks.push(await checkToolCapability());
|
|
494
519
|
// Hardware checks
|
|
495
520
|
checks.push(...hardwareResults);
|
|
496
521
|
// More synchronous checks
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ByokProvider } from './auth.js';
|
|
2
|
+
/**
|
|
3
|
+
* Check whether a specific model can invoke tools.
|
|
4
|
+
* For Ollama/local: queries the model's capabilities endpoint.
|
|
5
|
+
* For cloud providers: returns true for the known-capable list.
|
|
6
|
+
* Returns `null` if the answer can't be determined (non-fatal — caller decides).
|
|
7
|
+
*/
|
|
8
|
+
export declare function supportsToolCalls(provider: ByokProvider, model: string): Promise<boolean | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Human-readable recommendation when a model lacks tool support.
|
|
11
|
+
* Returns null if the model is fine OR if we can't tell.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getWeakModelWarning(provider: ByokProvider, model: string): Promise<string | null>;
|
|
14
|
+
//# sourceMappingURL=model-capabilities.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Model Capability Detection
|
|
2
|
+
//
|
|
3
|
+
// kbot's agent loop depends on tool calling. When the configured model
|
|
4
|
+
// doesn't support tools, users see weird bugs: answers hallucinated instead
|
|
5
|
+
// of reading files, capabilities denied instead of exercised, tool-call
|
|
6
|
+
// syntax printed as markdown instead of invoked.
|
|
7
|
+
//
|
|
8
|
+
// This module answers one question reliably: does the configured model
|
|
9
|
+
// support tool calls?
|
|
10
|
+
//
|
|
11
|
+
// For Ollama, we query `/api/show` which returns `capabilities: ['tools', ...]`
|
|
12
|
+
// for tool-capable models. For cloud providers, tool support is assumed
|
|
13
|
+
// (Anthropic, OpenAI, Google, Groq, Mistral, DeepSeek all support it).
|
|
14
|
+
/** Cloud providers with reliable tool-calling support */
|
|
15
|
+
const CLOUD_TOOL_CAPABLE = new Set([
|
|
16
|
+
'anthropic', 'openai', 'google', 'groq', 'mistral',
|
|
17
|
+
'deepseek', 'cohere', 'xai', 'openrouter', 'together',
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Check whether a specific model can invoke tools.
|
|
21
|
+
* For Ollama/local: queries the model's capabilities endpoint.
|
|
22
|
+
* For cloud providers: returns true for the known-capable list.
|
|
23
|
+
* Returns `null` if the answer can't be determined (non-fatal — caller decides).
|
|
24
|
+
*/
|
|
25
|
+
export async function supportsToolCalls(provider, model) {
|
|
26
|
+
// Cloud providers — trust the allowlist
|
|
27
|
+
if (CLOUD_TOOL_CAPABLE.has(provider))
|
|
28
|
+
return true;
|
|
29
|
+
// Ollama — ask the server directly
|
|
30
|
+
if (provider === 'ollama') {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch('http://localhost:11434/api/show', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({ name: model }),
|
|
36
|
+
signal: AbortSignal.timeout(3000),
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok)
|
|
39
|
+
return null;
|
|
40
|
+
const data = await res.json();
|
|
41
|
+
if (!Array.isArray(data.capabilities))
|
|
42
|
+
return null;
|
|
43
|
+
return data.capabilities.includes('tools');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Unknown — don't speculate
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Human-readable recommendation when a model lacks tool support.
|
|
54
|
+
* Returns null if the model is fine OR if we can't tell.
|
|
55
|
+
*/
|
|
56
|
+
export async function getWeakModelWarning(provider, model) {
|
|
57
|
+
const ok = await supportsToolCalls(provider, model);
|
|
58
|
+
if (ok !== false)
|
|
59
|
+
return null; // true or null → no warning
|
|
60
|
+
const recs = {
|
|
61
|
+
ollama: ['qwen2.5-coder:14b', 'qwen2.5-coder:7b', 'mistral:7b', 'llama3.1:8b', 'kernel-coder:latest'],
|
|
62
|
+
};
|
|
63
|
+
const suggestions = recs[provider]?.filter(s => s !== model).slice(0, 3) ?? [];
|
|
64
|
+
const suggestionStr = suggestions.length > 0
|
|
65
|
+
? ` Suggested tool-capable alternatives: ${suggestions.join(', ')}.`
|
|
66
|
+
: '';
|
|
67
|
+
return `Model "${model}" does not support tool calls. ` +
|
|
68
|
+
`kbot's file reads, shell commands, and git operations will silently fail or be hallucinated.${suggestionStr} ` +
|
|
69
|
+
`Switch with: \`kbot auth\` or set a tool-capable model in ~/.kbot/config.json.`;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=model-capabilities.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernel.chat/kbot",
|
|
3
|
-
"version": "3.99.
|
|
3
|
+
"version": "3.99.8",
|
|
4
4
|
"description": "Open-source terminal AI agent. 787+ tools, 35 agents, 20 providers. Dreams, learns, watches your system. Controls your phone. Fully local, fully sovereign. MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|