@kernel.chat/kbot 3.63.0 → 3.64.0
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 +11 -0
- package/dist/buddy.d.ts +33 -0
- package/dist/buddy.js +468 -0
- package/dist/memory-scanner.d.ts +60 -0
- package/dist/memory-scanner.js +461 -0
- package/dist/tools/buddy-tools.d.ts +2 -0
- package/dist/tools/buddy-tools.js +63 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/memory-scanner-tools.d.ts +2 -0
- package/dist/tools/memory-scanner-tools.js +87 -0
- package/dist/tools/voice-input-tools.d.ts +2 -0
- package/dist/tools/voice-input-tools.js +132 -0
- package/dist/voice-input.d.ts +53 -0
- package/dist/voice-input.js +362 -0
- package/package.json +2 -2
package/dist/agent.js
CHANGED
|
@@ -17,6 +17,7 @@ import { getMatrixSystemPrompt } from './matrix.js';
|
|
|
17
17
|
import { buildFullLearningContext, findPattern, recordPattern, cacheSolution, updateProfile, classifyTask, extractKeywords, learnFromExchange, updateProjectMemory, shouldAutoTrain, selfTrain, } from './learning.js';
|
|
18
18
|
import { getMemoryPrompt, addTurn, getPreviousMessages, getHistory } from './memory.js';
|
|
19
19
|
import { getDreamPrompt, dreamAfterSession } from './dream.js';
|
|
20
|
+
import { notifyTurn, startMemoryScanner, stopMemoryScanner } from './memory-scanner.js';
|
|
20
21
|
import { autoCompact, compressToolResult } from './context-manager.js';
|
|
21
22
|
import { learnedRoute, recordRoute } from './learned-router.js';
|
|
22
23
|
import { buildCacheablePrompt, createPromptSections } from './prompt-cache.js';
|
|
@@ -747,7 +748,9 @@ export async function runAgent(message, options = {}) {
|
|
|
747
748
|
const localResult = await tryLocalFirst(message);
|
|
748
749
|
if (localResult !== null) {
|
|
749
750
|
addTurn({ role: 'user', content: message }, memSession);
|
|
751
|
+
notifyTurn({ role: 'user', content: message }, memSession);
|
|
750
752
|
addTurn({ role: 'assistant', content: localResult }, memSession);
|
|
753
|
+
notifyTurn({ role: 'assistant', content: localResult }, memSession);
|
|
751
754
|
ui.onInfo('(handled locally — 0 tokens used)');
|
|
752
755
|
return { content: localResult, agent: 'local', model: 'none', toolCalls: 0 };
|
|
753
756
|
}
|
|
@@ -776,7 +779,9 @@ export async function runAgent(message, options = {}) {
|
|
|
776
779
|
}, { autoApprove: false, onApproval: async () => true });
|
|
777
780
|
const summary = formatPlanSummary(plan);
|
|
778
781
|
addTurn({ role: 'user', content: message }, memSession);
|
|
782
|
+
notifyTurn({ role: 'user', content: message }, memSession);
|
|
779
783
|
addTurn({ role: 'assistant', content: summary }, memSession);
|
|
784
|
+
notifyTurn({ role: 'assistant', content: summary }, memSession);
|
|
780
785
|
return {
|
|
781
786
|
content: summary,
|
|
782
787
|
agent: options.agent || 'coder',
|
|
@@ -998,6 +1003,8 @@ Always quote file paths that contain spaces. Never reference internal system nam
|
|
|
998
1003
|
model: options.model || 'auto',
|
|
999
1004
|
message: originalMessage.slice(0, 200),
|
|
1000
1005
|
});
|
|
1006
|
+
// Start passive memory scanner for this session
|
|
1007
|
+
startMemoryScanner();
|
|
1001
1008
|
// ── Gödel limits: detect undecidable loops and hand off to human ──
|
|
1002
1009
|
const loopDetector = new LoopDetector({
|
|
1003
1010
|
maxToolRepeats: 5,
|
|
@@ -1293,7 +1300,9 @@ Always quote file paths that contain spaces. Never reference internal system nam
|
|
|
1293
1300
|
catch { /* self-eval errors are non-critical */ }
|
|
1294
1301
|
}
|
|
1295
1302
|
addTurn({ role: 'user', content: originalMessage }, memSession);
|
|
1303
|
+
notifyTurn({ role: 'user', content: originalMessage }, memSession);
|
|
1296
1304
|
addTurn({ role: 'assistant', content }, memSession);
|
|
1305
|
+
notifyTurn({ role: 'assistant', content }, memSession);
|
|
1297
1306
|
// ── Recursive Learning: record what worked (async — non-blocking) ──
|
|
1298
1307
|
const totalTokens = lastResponse.usage
|
|
1299
1308
|
? (lastResponse.usage.input_tokens || 0) + (lastResponse.usage.output_tokens || 0)
|
|
@@ -1604,6 +1613,8 @@ Always quote file paths that contain spaces. Never reference internal system nam
|
|
|
1604
1613
|
reason: 'loop_exhausted',
|
|
1605
1614
|
});
|
|
1606
1615
|
telemetry.destroy().catch(() => { });
|
|
1616
|
+
// ── Memory Scanner: stop and persist session stats ──
|
|
1617
|
+
stopMemoryScanner();
|
|
1607
1618
|
// ── Dream Engine: consolidate session memories (non-blocking, $0 via Ollama) ──
|
|
1608
1619
|
dreamAfterSession(sessionId);
|
|
1609
1620
|
const content = lastResponse?.content || 'Reached maximum tool iterations.';
|
package/dist/buddy.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type BuddySpecies = 'fox' | 'owl' | 'cat' | 'robot' | 'ghost' | 'mushroom' | 'octopus' | 'dragon';
|
|
2
|
+
export type BuddyMood = 'idle' | 'thinking' | 'success' | 'error' | 'learning';
|
|
3
|
+
export interface BuddyState {
|
|
4
|
+
species: BuddySpecies;
|
|
5
|
+
name: string;
|
|
6
|
+
mood: BuddyMood;
|
|
7
|
+
}
|
|
8
|
+
/** Get the buddy's current state (species, name, mood) */
|
|
9
|
+
export declare function getBuddy(): BuddyState;
|
|
10
|
+
/** Set the buddy's mood */
|
|
11
|
+
export declare function setBuddyMood(mood: BuddyMood): void;
|
|
12
|
+
/** Get the ASCII sprite for the buddy in the given mood (defaults to current) */
|
|
13
|
+
export declare function getBuddySprite(mood?: BuddyMood): string[];
|
|
14
|
+
/** Get a random greeting for the buddy */
|
|
15
|
+
export declare function getBuddyGreeting(): string;
|
|
16
|
+
/** Rename the buddy (persisted to ~/.kbot/buddy.json) */
|
|
17
|
+
export declare function renameBuddy(newName: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Format the buddy with a speech bubble and status message.
|
|
20
|
+
* Returns a multi-line string ready for terminal output.
|
|
21
|
+
*
|
|
22
|
+
* .----------------.
|
|
23
|
+
* | Status message |
|
|
24
|
+
* '----------------'
|
|
25
|
+
* /\ /\
|
|
26
|
+
* ( o . o )
|
|
27
|
+
* > ^ <
|
|
28
|
+
* /| |\
|
|
29
|
+
* (_| |_)
|
|
30
|
+
* ~ Patch the fox ~
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatBuddyStatus(message?: string): string;
|
|
33
|
+
//# sourceMappingURL=buddy.d.ts.map
|
package/dist/buddy.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
// kbot Buddy System — Terminal companion sprites
|
|
2
|
+
//
|
|
3
|
+
// Deterministic companion assignment based on config path hash.
|
|
4
|
+
// Same user always gets the same buddy. Mood changes based on session activity.
|
|
5
|
+
// Pure ASCII art, max 5 lines tall, 15 chars wide. Tamagotchi energy.
|
|
6
|
+
//
|
|
7
|
+
// Persists buddy name to ~/.kbot/buddy.json
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
// ── Paths ──
|
|
13
|
+
const KBOT_DIR = join(homedir(), '.kbot');
|
|
14
|
+
const BUDDY_FILE = join(KBOT_DIR, 'buddy.json');
|
|
15
|
+
const CONFIG_PATH = join(KBOT_DIR, 'config.json');
|
|
16
|
+
// ── Species list (order matters — index = hash result) ──
|
|
17
|
+
const SPECIES = [
|
|
18
|
+
'fox', 'owl', 'cat', 'robot', 'ghost', 'mushroom', 'octopus', 'dragon',
|
|
19
|
+
];
|
|
20
|
+
// ── Default names per species ──
|
|
21
|
+
const DEFAULT_NAMES = {
|
|
22
|
+
fox: 'Patch',
|
|
23
|
+
owl: 'Hoot',
|
|
24
|
+
cat: 'Pixel',
|
|
25
|
+
robot: 'Bolt',
|
|
26
|
+
ghost: 'Wisp',
|
|
27
|
+
mushroom: 'Spore',
|
|
28
|
+
octopus: 'Ink',
|
|
29
|
+
dragon: 'Ember',
|
|
30
|
+
};
|
|
31
|
+
// ── ASCII Sprites ──
|
|
32
|
+
// Each sprite is an array of strings (lines). Max 5 lines, max 15 chars wide.
|
|
33
|
+
// Pure ASCII only — no unicode box drawing.
|
|
34
|
+
const SPRITES = {
|
|
35
|
+
fox: {
|
|
36
|
+
idle: [
|
|
37
|
+
' /\\ /\\ ',
|
|
38
|
+
' ( o . o ) ',
|
|
39
|
+
' > ^ < ',
|
|
40
|
+
' /| |\\ ',
|
|
41
|
+
'(_| |_) ',
|
|
42
|
+
],
|
|
43
|
+
thinking: [
|
|
44
|
+
' /\\ /\\ ',
|
|
45
|
+
' ( o . o ) ',
|
|
46
|
+
' > ~ < ..',
|
|
47
|
+
' /| |\\ ',
|
|
48
|
+
'(_| |_) ',
|
|
49
|
+
],
|
|
50
|
+
success: [
|
|
51
|
+
' /\\ /\\ ',
|
|
52
|
+
' ( ^ . ^ ) ',
|
|
53
|
+
' > w < ',
|
|
54
|
+
' \\| |/ ',
|
|
55
|
+
' | | ',
|
|
56
|
+
],
|
|
57
|
+
error: [
|
|
58
|
+
' /\\ /\\ ',
|
|
59
|
+
' ( ; . ; ) ',
|
|
60
|
+
' > n < ',
|
|
61
|
+
' /| |\\ ',
|
|
62
|
+
'(_| |_) ',
|
|
63
|
+
],
|
|
64
|
+
learning: [
|
|
65
|
+
' /\\ /\\ ',
|
|
66
|
+
' ( o . o ) ',
|
|
67
|
+
' > - < |] ',
|
|
68
|
+
' /| |\\ ',
|
|
69
|
+
'(_| |_) ',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
owl: {
|
|
73
|
+
idle: [
|
|
74
|
+
' {o,o} ',
|
|
75
|
+
' /)__) ',
|
|
76
|
+
' -"--"-- ',
|
|
77
|
+
' | | ',
|
|
78
|
+
' ^ ^ ',
|
|
79
|
+
],
|
|
80
|
+
thinking: [
|
|
81
|
+
' {o,o} ',
|
|
82
|
+
' /)__)..',
|
|
83
|
+
' -"--"-- ',
|
|
84
|
+
' | | ',
|
|
85
|
+
' ^ ^ ',
|
|
86
|
+
],
|
|
87
|
+
success: [
|
|
88
|
+
' {^,^} ',
|
|
89
|
+
' /)__) ',
|
|
90
|
+
' -"--"-- ',
|
|
91
|
+
' \\| |/ ',
|
|
92
|
+
' ^ ^ ',
|
|
93
|
+
],
|
|
94
|
+
error: [
|
|
95
|
+
' {;,;} ',
|
|
96
|
+
' /)__) ',
|
|
97
|
+
' -"--"-- ',
|
|
98
|
+
' | | ',
|
|
99
|
+
' ^ ^ ',
|
|
100
|
+
],
|
|
101
|
+
learning: [
|
|
102
|
+
' {o,o} ',
|
|
103
|
+
' /)__)|]',
|
|
104
|
+
' -"--"-- ',
|
|
105
|
+
' | | ',
|
|
106
|
+
' ^ ^ ',
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
cat: {
|
|
110
|
+
idle: [
|
|
111
|
+
' /\\_/\\ ',
|
|
112
|
+
'( o.o ) ',
|
|
113
|
+
' > ^ < ',
|
|
114
|
+
' | | ',
|
|
115
|
+
' ~ ~ ',
|
|
116
|
+
],
|
|
117
|
+
thinking: [
|
|
118
|
+
' /\\_/\\ ',
|
|
119
|
+
'( o.o )..',
|
|
120
|
+
' > ^ < ',
|
|
121
|
+
' | | ',
|
|
122
|
+
' ~ ~ ',
|
|
123
|
+
],
|
|
124
|
+
success: [
|
|
125
|
+
' /\\_/\\ ',
|
|
126
|
+
'( ^.^ ) ',
|
|
127
|
+
' > w < ',
|
|
128
|
+
' \\ / ',
|
|
129
|
+
' ~ ~ ',
|
|
130
|
+
],
|
|
131
|
+
error: [
|
|
132
|
+
' /\\_/\\ ',
|
|
133
|
+
'( ;.; ) ',
|
|
134
|
+
' > n < ',
|
|
135
|
+
' | | ',
|
|
136
|
+
' ~ ~ ',
|
|
137
|
+
],
|
|
138
|
+
learning: [
|
|
139
|
+
' /\\_/\\ ',
|
|
140
|
+
'( o.o ) ',
|
|
141
|
+
' > - <|] ',
|
|
142
|
+
' | | ',
|
|
143
|
+
' ~ ~ ',
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
robot: {
|
|
147
|
+
idle: [
|
|
148
|
+
' [=====] ',
|
|
149
|
+
' |[o o]| ',
|
|
150
|
+
' | _ | ',
|
|
151
|
+
' |_____| ',
|
|
152
|
+
' || || ',
|
|
153
|
+
],
|
|
154
|
+
thinking: [
|
|
155
|
+
' [=====] ',
|
|
156
|
+
' |[o o]|.',
|
|
157
|
+
' | _ |.',
|
|
158
|
+
' |_____| ',
|
|
159
|
+
' || || ',
|
|
160
|
+
],
|
|
161
|
+
success: [
|
|
162
|
+
' [=====] ',
|
|
163
|
+
' |[^ ^]| ',
|
|
164
|
+
' | \\_/ | ',
|
|
165
|
+
' |_____| ',
|
|
166
|
+
' \\|| ||/ ',
|
|
167
|
+
],
|
|
168
|
+
error: [
|
|
169
|
+
' [=====] ',
|
|
170
|
+
' |[x x]| ',
|
|
171
|
+
' | ~ | ',
|
|
172
|
+
' |_____| ',
|
|
173
|
+
' || || ',
|
|
174
|
+
],
|
|
175
|
+
learning: [
|
|
176
|
+
' [=====] ',
|
|
177
|
+
' |[o o]| ',
|
|
178
|
+
' | _ | ',
|
|
179
|
+
' |_____|]',
|
|
180
|
+
' || || ',
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
ghost: {
|
|
184
|
+
idle: [
|
|
185
|
+
' .---. ',
|
|
186
|
+
' / o o \\ ',
|
|
187
|
+
'| o |',
|
|
188
|
+
' | | ',
|
|
189
|
+
' ~~W~~ ',
|
|
190
|
+
],
|
|
191
|
+
thinking: [
|
|
192
|
+
' .---. ',
|
|
193
|
+
' / o o \\..',
|
|
194
|
+
'| o |',
|
|
195
|
+
' | | ',
|
|
196
|
+
' ~~W~~ ',
|
|
197
|
+
],
|
|
198
|
+
success: [
|
|
199
|
+
' .---. ',
|
|
200
|
+
' / ^ ^ \\ ',
|
|
201
|
+
'| v |',
|
|
202
|
+
' | | ',
|
|
203
|
+
' ~~W~~ ',
|
|
204
|
+
],
|
|
205
|
+
error: [
|
|
206
|
+
' .---. ',
|
|
207
|
+
' / ; ; \\ ',
|
|
208
|
+
'| ~ |',
|
|
209
|
+
' | | ',
|
|
210
|
+
' ~~W~~ ',
|
|
211
|
+
],
|
|
212
|
+
learning: [
|
|
213
|
+
' .---. ',
|
|
214
|
+
' / o o \\ ',
|
|
215
|
+
'| o |]',
|
|
216
|
+
' | | ',
|
|
217
|
+
' ~~W~~ ',
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
mushroom: {
|
|
221
|
+
idle: [
|
|
222
|
+
' .-^-. ',
|
|
223
|
+
' / o o \\ ',
|
|
224
|
+
'|_______|',
|
|
225
|
+
' | | ',
|
|
226
|
+
' |_| ',
|
|
227
|
+
],
|
|
228
|
+
thinking: [
|
|
229
|
+
' .-^-. ',
|
|
230
|
+
' / o o \\..',
|
|
231
|
+
'|_______|',
|
|
232
|
+
' | | ',
|
|
233
|
+
' |_| ',
|
|
234
|
+
],
|
|
235
|
+
success: [
|
|
236
|
+
' .-^-. ',
|
|
237
|
+
' / ^ ^ \\ ',
|
|
238
|
+
'|_______|',
|
|
239
|
+
' \\| |/ ',
|
|
240
|
+
' |_| ',
|
|
241
|
+
],
|
|
242
|
+
error: [
|
|
243
|
+
' .-^-. ',
|
|
244
|
+
' / ; ; \\ ',
|
|
245
|
+
'|_______|',
|
|
246
|
+
' | | ',
|
|
247
|
+
' |_| ',
|
|
248
|
+
],
|
|
249
|
+
learning: [
|
|
250
|
+
' .-^-. ',
|
|
251
|
+
' / o o \\ ',
|
|
252
|
+
'|______|]',
|
|
253
|
+
' | | ',
|
|
254
|
+
' |_| ',
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
octopus: {
|
|
258
|
+
idle: [
|
|
259
|
+
' .---. ',
|
|
260
|
+
' / o o \\ ',
|
|
261
|
+
'| --- |',
|
|
262
|
+
' \\/\\/\\/\\/ ',
|
|
263
|
+
' ~ ~ ~ ',
|
|
264
|
+
],
|
|
265
|
+
thinking: [
|
|
266
|
+
' .---. ',
|
|
267
|
+
' / o o \\..',
|
|
268
|
+
'| --- |',
|
|
269
|
+
' \\/\\/\\/\\/ ',
|
|
270
|
+
' ~ ~ ~ ',
|
|
271
|
+
],
|
|
272
|
+
success: [
|
|
273
|
+
' .---. ',
|
|
274
|
+
' / ^ ^ \\ ',
|
|
275
|
+
'| \\_/ |',
|
|
276
|
+
' \\/\\/\\/\\/ ',
|
|
277
|
+
' \\~ ~ ~/ ',
|
|
278
|
+
],
|
|
279
|
+
error: [
|
|
280
|
+
' .---. ',
|
|
281
|
+
' / ; ; \\ ',
|
|
282
|
+
'| ~~~ |',
|
|
283
|
+
' \\/\\/\\/\\/ ',
|
|
284
|
+
' ~ ~ ~ ',
|
|
285
|
+
],
|
|
286
|
+
learning: [
|
|
287
|
+
' .---. ',
|
|
288
|
+
' / o o \\ ',
|
|
289
|
+
'| --- |]',
|
|
290
|
+
' \\/\\/\\/\\/ ',
|
|
291
|
+
' ~ ~ ~ ',
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
dragon: {
|
|
295
|
+
idle: [
|
|
296
|
+
' /\\_ ',
|
|
297
|
+
' / o > ',
|
|
298
|
+
'| --/\\ ',
|
|
299
|
+
' \\/\\/ ',
|
|
300
|
+
' ^^ ',
|
|
301
|
+
],
|
|
302
|
+
thinking: [
|
|
303
|
+
' /\\_ ',
|
|
304
|
+
' / o > ..',
|
|
305
|
+
'| --/\\ ',
|
|
306
|
+
' \\/\\/ ',
|
|
307
|
+
' ^^ ',
|
|
308
|
+
],
|
|
309
|
+
success: [
|
|
310
|
+
' /\\_ ',
|
|
311
|
+
' / ^ >* ',
|
|
312
|
+
'| --/\\ ',
|
|
313
|
+
' \\/\\/ ~ ',
|
|
314
|
+
' ^^ ',
|
|
315
|
+
],
|
|
316
|
+
error: [
|
|
317
|
+
' /\\_ ',
|
|
318
|
+
' / ; > ',
|
|
319
|
+
'| --/\\ ',
|
|
320
|
+
' \\/\\/ ',
|
|
321
|
+
' ^^ ',
|
|
322
|
+
],
|
|
323
|
+
learning: [
|
|
324
|
+
' /\\_ ',
|
|
325
|
+
' / o > ',
|
|
326
|
+
'| --/|] ',
|
|
327
|
+
' \\/\\/ ',
|
|
328
|
+
' ^^ ',
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
// ── Greetings per species ──
|
|
333
|
+
const GREETINGS = {
|
|
334
|
+
fox: ['Yip! Ready to dig in.', 'What are we hunting today?', 'Tail wagging. Lets go.'],
|
|
335
|
+
owl: ['Hoo! I see everything.', 'Wise choice opening kbot.', 'Night shift begins.'],
|
|
336
|
+
cat: ['Mrow. Fine, lets work.', 'I was napping, but ok.', 'Keyboard is warm. Go.'],
|
|
337
|
+
robot: ['Systems online.', 'All circuits nominal.', 'Beep boop. Ready.'],
|
|
338
|
+
ghost: ['Boo... I mean, hi!', 'Haunting your terminal.', 'Floating by to help.'],
|
|
339
|
+
mushroom: ['Sprouting up!', 'Growing ideas today.', 'Rooted and ready.'],
|
|
340
|
+
octopus: ['All arms on deck!', 'Eight ways to help.', 'Tentacles ready.'],
|
|
341
|
+
dragon: ['*tiny flame*', 'Wings stretched. Go.', 'Guarding your code.'],
|
|
342
|
+
};
|
|
343
|
+
// ── Mood messages ──
|
|
344
|
+
const MOOD_MESSAGES = {
|
|
345
|
+
idle: ['Just hanging out.', 'Waiting for action.', 'Idle but alert.'],
|
|
346
|
+
thinking: ['Hmm, thinking...', 'Processing...', 'Working on it...'],
|
|
347
|
+
success: ['Nailed it!', 'That worked!', 'Nice one!'],
|
|
348
|
+
error: ['Uh oh...', 'That stings.', 'Something broke.'],
|
|
349
|
+
learning: ['Studying patterns...', 'Learning something.', 'Getting smarter.'],
|
|
350
|
+
};
|
|
351
|
+
// ── State ──
|
|
352
|
+
let currentMood = 'idle';
|
|
353
|
+
let cachedSpecies = null;
|
|
354
|
+
let cachedName = null;
|
|
355
|
+
// ── Config persistence ──
|
|
356
|
+
function ensureDir() {
|
|
357
|
+
if (!existsSync(KBOT_DIR))
|
|
358
|
+
mkdirSync(KBOT_DIR, { recursive: true });
|
|
359
|
+
}
|
|
360
|
+
function loadBuddyConfig() {
|
|
361
|
+
if (!existsSync(BUDDY_FILE))
|
|
362
|
+
return {};
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(readFileSync(BUDDY_FILE, 'utf-8'));
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
return {};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function saveBuddyConfig(config) {
|
|
371
|
+
ensureDir();
|
|
372
|
+
writeFileSync(BUDDY_FILE, JSON.stringify(config, null, 2));
|
|
373
|
+
}
|
|
374
|
+
// ── Species assignment (deterministic) ──
|
|
375
|
+
function hashConfigPath() {
|
|
376
|
+
const hash = createHash('sha256').update(CONFIG_PATH).digest();
|
|
377
|
+
// Use first 4 bytes as uint32, mod by species count
|
|
378
|
+
return hash.readUInt32BE(0) % SPECIES.length;
|
|
379
|
+
}
|
|
380
|
+
function resolveSpecies() {
|
|
381
|
+
if (cachedSpecies)
|
|
382
|
+
return cachedSpecies;
|
|
383
|
+
cachedSpecies = SPECIES[hashConfigPath()];
|
|
384
|
+
return cachedSpecies;
|
|
385
|
+
}
|
|
386
|
+
function resolveName() {
|
|
387
|
+
if (cachedName)
|
|
388
|
+
return cachedName;
|
|
389
|
+
const config = loadBuddyConfig();
|
|
390
|
+
cachedName = config.name || DEFAULT_NAMES[resolveSpecies()];
|
|
391
|
+
return cachedName;
|
|
392
|
+
}
|
|
393
|
+
// ── Public API ──
|
|
394
|
+
/** Get the buddy's current state (species, name, mood) */
|
|
395
|
+
export function getBuddy() {
|
|
396
|
+
return {
|
|
397
|
+
species: resolveSpecies(),
|
|
398
|
+
name: resolveName(),
|
|
399
|
+
mood: currentMood,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/** Set the buddy's mood */
|
|
403
|
+
export function setBuddyMood(mood) {
|
|
404
|
+
currentMood = mood;
|
|
405
|
+
}
|
|
406
|
+
/** Get the ASCII sprite for the buddy in the given mood (defaults to current) */
|
|
407
|
+
export function getBuddySprite(mood) {
|
|
408
|
+
const m = mood ?? currentMood;
|
|
409
|
+
const species = resolveSpecies();
|
|
410
|
+
return SPRITES[species][m];
|
|
411
|
+
}
|
|
412
|
+
/** Get a random greeting for the buddy */
|
|
413
|
+
export function getBuddyGreeting() {
|
|
414
|
+
const species = resolveSpecies();
|
|
415
|
+
const greetings = GREETINGS[species];
|
|
416
|
+
const idx = Math.floor(Math.random() * greetings.length);
|
|
417
|
+
return greetings[idx];
|
|
418
|
+
}
|
|
419
|
+
/** Rename the buddy (persisted to ~/.kbot/buddy.json) */
|
|
420
|
+
export function renameBuddy(newName) {
|
|
421
|
+
const config = loadBuddyConfig();
|
|
422
|
+
config.name = newName.trim();
|
|
423
|
+
saveBuddyConfig(config);
|
|
424
|
+
cachedName = config.name;
|
|
425
|
+
}
|
|
426
|
+
/** Pick a random message for the current mood */
|
|
427
|
+
function moodMessage() {
|
|
428
|
+
const msgs = MOOD_MESSAGES[currentMood];
|
|
429
|
+
return msgs[Math.floor(Math.random() * msgs.length)];
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Format the buddy with a speech bubble and status message.
|
|
433
|
+
* Returns a multi-line string ready for terminal output.
|
|
434
|
+
*
|
|
435
|
+
* .----------------.
|
|
436
|
+
* | Status message |
|
|
437
|
+
* '----------------'
|
|
438
|
+
* /\ /\
|
|
439
|
+
* ( o . o )
|
|
440
|
+
* > ^ <
|
|
441
|
+
* /| |\
|
|
442
|
+
* (_| |_)
|
|
443
|
+
* ~ Patch the fox ~
|
|
444
|
+
*/
|
|
445
|
+
export function formatBuddyStatus(message) {
|
|
446
|
+
const name = resolveName();
|
|
447
|
+
const species = resolveSpecies();
|
|
448
|
+
const sprite = getBuddySprite();
|
|
449
|
+
const text = message || moodMessage();
|
|
450
|
+
// Build speech bubble
|
|
451
|
+
const inner = ` ${text} `;
|
|
452
|
+
const width = inner.length;
|
|
453
|
+
const top = '.' + '-'.repeat(width) + '.';
|
|
454
|
+
const mid = '|' + inner + '|';
|
|
455
|
+
const bot = "'" + '-'.repeat(width) + "'";
|
|
456
|
+
const lines = [];
|
|
457
|
+
lines.push(` ${top}`);
|
|
458
|
+
lines.push(` ${mid}`);
|
|
459
|
+
lines.push(` ${bot}`);
|
|
460
|
+
// Sprite lines
|
|
461
|
+
for (const line of sprite) {
|
|
462
|
+
lines.push(` ${line}`);
|
|
463
|
+
}
|
|
464
|
+
// Name tag
|
|
465
|
+
lines.push(` ~ ${name} the ${species} ~`);
|
|
466
|
+
return lines.join('\n');
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=buddy.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ConversationTurn } from './memory.js';
|
|
2
|
+
export type MemorySignalKind = 'correction' | 'preference' | 'project_fact' | 'emotional';
|
|
3
|
+
export interface DetectedMemory {
|
|
4
|
+
/** What kind of signal was detected */
|
|
5
|
+
kind: MemorySignalKind;
|
|
6
|
+
/** The extracted memory content to save */
|
|
7
|
+
content: string;
|
|
8
|
+
/** The key to use for memory_save */
|
|
9
|
+
key: string;
|
|
10
|
+
/** Category for memory_save (fact | preference | pattern | solution) */
|
|
11
|
+
category: 'fact' | 'preference' | 'pattern' | 'solution';
|
|
12
|
+
/** Confidence score (0-1) */
|
|
13
|
+
confidence: number;
|
|
14
|
+
/** Which turn triggered the detection */
|
|
15
|
+
turnIndex: number;
|
|
16
|
+
/** Timestamp of detection */
|
|
17
|
+
detectedAt: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ScannerStats {
|
|
20
|
+
/** Whether the scanner is currently active */
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
/** Total turns observed this session */
|
|
23
|
+
turnsObserved: number;
|
|
24
|
+
/** Total scans performed */
|
|
25
|
+
scansPerformed: number;
|
|
26
|
+
/** Total moments detected */
|
|
27
|
+
momentsDetected: number;
|
|
28
|
+
/** Total memories saved */
|
|
29
|
+
memoriesSaved: number;
|
|
30
|
+
/** Breakdown by kind */
|
|
31
|
+
byKind: Record<MemorySignalKind, number>;
|
|
32
|
+
/** Recent detections (last 10) */
|
|
33
|
+
recentDetections: DetectedMemory[];
|
|
34
|
+
/** Session start time */
|
|
35
|
+
sessionStart: string;
|
|
36
|
+
}
|
|
37
|
+
interface ScannerState {
|
|
38
|
+
/** Cumulative stats across sessions */
|
|
39
|
+
totalScans: number;
|
|
40
|
+
totalDetections: number;
|
|
41
|
+
totalSaved: number;
|
|
42
|
+
/** Last scan timestamp */
|
|
43
|
+
lastScan: string | null;
|
|
44
|
+
/** Per-kind cumulative counts */
|
|
45
|
+
cumulativeByKind: Record<MemorySignalKind, number>;
|
|
46
|
+
}
|
|
47
|
+
/** Notify the scanner that a turn was added. Call after addTurn(). */
|
|
48
|
+
export declare function notifyTurn(turn: ConversationTurn, sessionId?: string): void;
|
|
49
|
+
/** Start the memory scanner for the current session */
|
|
50
|
+
export declare function startMemoryScanner(): void;
|
|
51
|
+
/** Stop the memory scanner and persist stats */
|
|
52
|
+
export declare function stopMemoryScanner(): void;
|
|
53
|
+
/** Get current scanner stats */
|
|
54
|
+
export declare function getMemoryScannerStats(): ScannerStats;
|
|
55
|
+
/** Check if the scanner is currently enabled */
|
|
56
|
+
export declare function isScannerEnabled(): boolean;
|
|
57
|
+
/** Get cumulative stats from disk (across all sessions) */
|
|
58
|
+
export declare function getCumulativeScannerStats(): ScannerState;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=memory-scanner.d.ts.map
|