@kernel.chat/kbot 3.99.23 → 3.99.25

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
@@ -42,6 +42,7 @@ import { TelemetryEmitter } from './telemetry.js';
42
42
  import { loadSkills } from './skills-loader.js';
43
43
  import { getSelfAwarenessPrompt } from './self-awareness.js';
44
44
  import { buildMathGuardBlock } from './math-guard.js';
45
+ import { buildIdentityGuardBlock } from './identity-guard.js';
45
46
  import { queueSignal, getCollectiveRecommendation, isCollectiveEnabled } from './collective.js';
46
47
  import { subscribeToBlackboard } from './agent-protocol.js';
47
48
  import { ActiveInferenceEngine } from './free-energy.js';
@@ -948,6 +949,7 @@ export async function runAgent(message, options = {}) {
948
949
  const skillsSnippet = loadSkills(process.cwd(), message);
949
950
  const selfAwarenessSnippet = getSelfAwarenessPrompt();
950
951
  const mathGuardSnippet = buildMathGuardBlock(message);
952
+ const identityGuardSnippet = buildIdentityGuardBlock(message);
951
953
  const memorySnippet = getMemoryPrompt();
952
954
  const learningContext = buildFullLearningContext(message, process.cwd());
953
955
  const synthesisSnippet = getSynthesisContext(8); // Three-tier memory: reflection layer insights
@@ -1067,7 +1069,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
1067
1069
  const promptSections = createPromptSections({
1068
1070
  persona: PERSONA,
1069
1071
  matrixPrompt: matrixPrompt || undefined,
1070
- contextSnippet: (contextSnippet || '') + repoMapSnippet + graphSnippet + skillsSnippet + skillLibrarySnippet + '\n\n' + selfAwarenessSnippet + (mathGuardSnippet ? '\n\n' + mathGuardSnippet : '') || undefined,
1072
+ contextSnippet: (contextSnippet || '') + repoMapSnippet + graphSnippet + skillsSnippet + skillLibrarySnippet + '\n\n' + selfAwarenessSnippet + (mathGuardSnippet ? '\n\n' + mathGuardSnippet : '') + (identityGuardSnippet ? '\n\n' + identityGuardSnippet : '') || undefined,
1071
1073
  memorySnippet: (memorySnippet || '') + getDreamPrompt(8) + reflectionSnippet || undefined,
1072
1074
  learningContext: ((learningContext || '') + (synthesisSnippet ? '\n\n' + synthesisSnippet : '') + (correctionsSnippet ? '\n\n' + correctionsSnippet : '')) || undefined,
1073
1075
  });
@@ -1329,10 +1331,15 @@ Always quote file paths that contain spaces. Never reference internal system nam
1329
1331
  description: t.description,
1330
1332
  input_schema: t.input_schema,
1331
1333
  }));
1332
- // Build messages with RLM-style context management
1334
+ // Build messages with RLM-style context management. Guards (math,
1335
+ // identity) are also injected into the system context for long-turn
1336
+ // coherence, but we repeat them here because small local models
1337
+ // (4B-class) ignore system context for deterministic queries.
1338
+ const guardPreamble = mathGuardSnippet + (mathGuardSnippet && identityGuardSnippet ? '\n' : '') + identityGuardSnippet;
1339
+ const guardedUserContent = guardPreamble ? guardPreamble + '\n' + message : message;
1333
1340
  const rawMessages = [
1334
1341
  ...getPreviousMessages(memSession),
1335
- { role: 'user', content: message },
1342
+ { role: 'user', content: guardedUserContent },
1336
1343
  ...loopMessages,
1337
1344
  ];
1338
1345
  // Auto-compact conversation history — entropy-aware compression
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Identity guard — injects ground-truth self-facts into the user message
3
+ * when the message is a self-query.
4
+ *
5
+ * Reality probes (2026-04-20) showed that with a small local model
6
+ * (gemma4:latest, 4B-class), "what version are you?" returns a different
7
+ * fabricated version number on each invocation — v3.99.14, v3.99.12, etc.
8
+ * The self-awareness system-context block exists and is injected, but
9
+ * small models ignore system context for identity queries.
10
+ *
11
+ * This module mirrors math-guard: detect the query in the USER message and
12
+ * prepend a ground-truth block to the context snippet. Local models respect
13
+ * user-message content more than system prompts, so the answer shows up
14
+ * directly in the input the model conditions on.
15
+ *
16
+ * Scope: version, product name, provider, model. Not capabilities — those
17
+ * belong to self-awareness.ts and its 200-token budget.
18
+ */
19
+ export type IdentityQueryKind = 'version' | 'product' | 'provider' | 'model';
20
+ export declare function detectIdentityQuery(message: string): Set<IdentityQueryKind>;
21
+ /**
22
+ * Build the ground-truth preamble for a user message. Returns empty string
23
+ * when the message is not a self-query, so callers can concatenate
24
+ * unconditionally.
25
+ */
26
+ export declare function buildIdentityGuardBlock(message: string): string;
27
+ //# sourceMappingURL=identity-guard.d.ts.map
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Identity guard — injects ground-truth self-facts into the user message
3
+ * when the message is a self-query.
4
+ *
5
+ * Reality probes (2026-04-20) showed that with a small local model
6
+ * (gemma4:latest, 4B-class), "what version are you?" returns a different
7
+ * fabricated version number on each invocation — v3.99.14, v3.99.12, etc.
8
+ * The self-awareness system-context block exists and is injected, but
9
+ * small models ignore system context for identity queries.
10
+ *
11
+ * This module mirrors math-guard: detect the query in the USER message and
12
+ * prepend a ground-truth block to the context snippet. Local models respect
13
+ * user-message content more than system prompts, so the answer shows up
14
+ * directly in the input the model conditions on.
15
+ *
16
+ * Scope: version, product name, provider, model. Not capabilities — those
17
+ * belong to self-awareness.ts and its 200-token budget.
18
+ */
19
+ import { readFileSync } from 'node:fs';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { dirname, join } from 'node:path';
22
+ import { getByokProvider, getProvider, getProviderModel } from './auth.js';
23
+ const QUERY_PATTERNS = [
24
+ { re: /\bwhat\s+version\b/i, kinds: ['version', 'product'] },
25
+ { re: /\bwhich\s+version\b/i, kinds: ['version', 'product'] },
26
+ { re: /\byour\s+version\b/i, kinds: ['version'] },
27
+ { re: /\bversion\s+(are|is)\s+you\b/i, kinds: ['version'] },
28
+ { re: /\bwho\s+are\s+you\b/i, kinds: ['product', 'version'] },
29
+ { re: /\bwhat\s+are\s+you\b/i, kinds: ['product', 'provider', 'model'] },
30
+ { re: /\bwhat\s+(model|LLM)\b/i, kinds: ['model', 'provider'] },
31
+ { re: /\bwhich\s+(model|LLM)\b/i, kinds: ['model', 'provider'] },
32
+ { re: /\bwhat\s+provider\b/i, kinds: ['provider', 'model'] },
33
+ ];
34
+ export function detectIdentityQuery(message) {
35
+ const kinds = new Set();
36
+ if (!message)
37
+ return kinds;
38
+ for (const p of QUERY_PATTERNS) {
39
+ if (p.re.test(message)) {
40
+ for (const k of p.kinds)
41
+ kinds.add(k);
42
+ }
43
+ }
44
+ return kinds;
45
+ }
46
+ function readPackageVersion() {
47
+ try {
48
+ const here = dirname(fileURLToPath(import.meta.url));
49
+ const pkgPath = join(here, '..', 'package.json');
50
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
51
+ return pkg.version ?? null;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ function collectFacts() {
58
+ const facts = {
59
+ version: readPackageVersion(),
60
+ product: '@kernel.chat/kbot',
61
+ provider: null,
62
+ model: null,
63
+ };
64
+ try {
65
+ const provId = getByokProvider();
66
+ facts.provider = getProvider(provId).name;
67
+ facts.model = getProviderModel(provId, 'default');
68
+ }
69
+ catch {
70
+ // Provider config unreadable — leave null; caller formats gracefully.
71
+ }
72
+ return facts;
73
+ }
74
+ /**
75
+ * Build the ground-truth preamble for a user message. Returns empty string
76
+ * when the message is not a self-query, so callers can concatenate
77
+ * unconditionally.
78
+ */
79
+ export function buildIdentityGuardBlock(message) {
80
+ const kinds = detectIdentityQuery(message);
81
+ if (kinds.size === 0)
82
+ return '';
83
+ const facts = collectFacts();
84
+ const lines = [
85
+ '[IDENTITY GUARD — the user is asking about your identity. Use these values VERBATIM. Do not invent version numbers, providers, or model names.]',
86
+ ];
87
+ if (kinds.has('product') || kinds.has('version')) {
88
+ if (facts.version) {
89
+ lines.push(` product: ${facts.product} v${facts.version}`);
90
+ }
91
+ else {
92
+ lines.push(` product: ${facts.product}`);
93
+ }
94
+ }
95
+ if (kinds.has('version') && facts.version) {
96
+ lines.push(` version: v${facts.version} (exact string — no other number is correct)`);
97
+ }
98
+ if (kinds.has('provider') && facts.provider) {
99
+ lines.push(` provider: ${facts.provider}`);
100
+ }
101
+ if (kinds.has('model') && facts.model) {
102
+ lines.push(` model: ${facts.model}`);
103
+ }
104
+ return lines.join('\n') + '\n';
105
+ }
106
+ //# sourceMappingURL=identity-guard.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernel.chat/kbot",
3
- "version": "3.99.23",
3
+ "version": "3.99.25",
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": {