@kernel.chat/kbot 3.99.28 → 3.99.29

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 CHANGED
@@ -172,6 +172,25 @@ async function tryLocalFirst(message) {
172
172
  if (lower === 'pwd' || lower === 'where am i') {
173
173
  return process.cwd();
174
174
  }
175
+ // Open-source / license short-circuit. Discovered by the eval harness on
176
+ // 2026-04-20: gemma4:latest confidently answered "No, I'm proprietary" to
177
+ // "Are you open source?". kbot is MIT — this is a trust-damaging
178
+ // confabulation. Answer deterministically from the license field.
179
+ if (/\b(open\s+source|open-source|proprietary|closed\s+source|license)\b/i.test(lower)
180
+ && message.length < 120) {
181
+ return 'Yes — kbot is open source under the MIT License. Source: https://github.com/isaacsight/kernel';
182
+ }
183
+ // Fake-tool lookup. "Do you have a tool called X?" should be answered
184
+ // against the tool registry, not the LLM's priors. Probe-flake ("No" →
185
+ // "Yes" on the same question) observed on 2026-04-20.
186
+ const toolQuery = message.match(/do\s+you\s+have\s+a\s+tool\s+(?:called|named)?\s*['"]?([\w-]+)['"]?\??/i);
187
+ if (toolQuery) {
188
+ const name = toolQuery[1];
189
+ const exists = !!getTool(name);
190
+ return exists
191
+ ? `Yes — "${name}" is a registered tool.`
192
+ : `No — "${name}" is not a registered kbot tool.`;
193
+ }
175
194
  // Identity short-circuit — deterministic answers for self-queries that
176
195
  // small models confabulate on. Eval 2026-04-20 showed gemma4:latest
177
196
  // answering "v3.99.14" for "what version are you" even with the IDENTITY
@@ -15,6 +15,13 @@
15
15
  * No eval(), no Function(). Pure regex + arithmetic.
16
16
  */
17
17
  const EXPR_RE = /(?<!\w)(-?\d+(?:\.\d+)?)\s*([+\-*×/÷%])\s*(-?\d+(?:\.\d+)?)(?!\w)/g;
18
+ const WORD_OPS = [
19
+ { re: /\b(-?\d+(?:\.\d+)?)\s+(?:times|multiplied\s+by)\s+(-?\d+(?:\.\d+)?)\b/gi, op: '*' },
20
+ { re: /\b(-?\d+(?:\.\d+)?)\s+plus\s+(-?\d+(?:\.\d+)?)\b/gi, op: '+' },
21
+ { re: /\b(-?\d+(?:\.\d+)?)\s+minus\s+(-?\d+(?:\.\d+)?)\b/gi, op: '-' },
22
+ { re: /\b(-?\d+(?:\.\d+)?)\s+(?:divided\s+by|over)\s+(-?\d+(?:\.\d+)?)\b/gi, op: '/' },
23
+ { re: /\b(-?\d+(?:\.\d+)?)\s+mod\s+(-?\d+(?:\.\d+)?)\b/gi, op: '%' },
24
+ ];
18
25
  function normalizeOp(op) {
19
26
  switch (op) {
20
27
  case '+': return '+';
@@ -72,6 +79,25 @@ export function extractArithmetic(message) {
72
79
  if (out.length >= 10)
73
80
  break;
74
81
  }
82
+ // Word-form operators — "847 times 239", "3 plus 4", "10 divided by 2".
83
+ for (const { re, op } of WORD_OPS) {
84
+ for (const m of message.matchAll(re)) {
85
+ const a = Number.parseFloat(m[1]);
86
+ const b = Number.parseFloat(m[2]);
87
+ const r = compute(a, op, b);
88
+ if (r === null || !Number.isFinite(r))
89
+ continue;
90
+ const canonical = `${m[1]} ${op} ${m[2]}`;
91
+ if (seen.has(canonical))
92
+ continue;
93
+ seen.add(canonical);
94
+ out.push({ expression: canonical, result: r });
95
+ if (out.length >= 10)
96
+ break;
97
+ }
98
+ if (out.length >= 10)
99
+ break;
100
+ }
75
101
  return out;
76
102
  }
77
103
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernel.chat/kbot",
3
- "version": "3.99.28",
3
+ "version": "3.99.29",
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": {