blun-king-cli 9.1.364 → 9.1.365
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/bin/cognitive-memory-command.cjs +245 -0
- package/blun.mjs +22 -4
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const { createRuntimeCognitiveTurnLifecycle } = require('./cognitive-turn-lifecycle.cjs');
|
|
5
|
+
const { resolveCognitiveEvidenceGroup } = require('./cognitive-effective-view.cjs');
|
|
6
|
+
|
|
7
|
+
const FOCUS_DOMAINS = new Set(['self', 'team', 'goal', 'open_thread', 'next_trigger', 'expected_evidence']);
|
|
8
|
+
const POSITIVE_ID_RE = /^[1-9]\d*$/u;
|
|
9
|
+
const NUMERIC_CHAT_ID_RE = /^-?\d+$/u;
|
|
10
|
+
|
|
11
|
+
function fail(code) {
|
|
12
|
+
const error = new Error(code);
|
|
13
|
+
error.code = code;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function clean(value, max) {
|
|
18
|
+
const text = String(value ?? '').replace(/[\u0000-\u001f\u007f]+/gu, ' ').replace(/\s+/gu, ' ').trim();
|
|
19
|
+
return text && text.length <= max ? text : '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function digestId(prefix, values) {
|
|
23
|
+
return `${prefix}-${crypto.createHash('sha256').update(values.join('\0')).digest('hex').slice(0, 40)}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isCognitiveMemoryCommand(args) {
|
|
27
|
+
const action = String(args ?? '').trim().split(/\s+/u, 1)[0].toLowerCase();
|
|
28
|
+
return action === 'focus' || action === 'correct' || action === 'delete';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseCognitiveMemoryCommand(args) {
|
|
32
|
+
const text = String(args ?? '').trim();
|
|
33
|
+
if (text.toLowerCase() === 'focus') return { action: 'focus' };
|
|
34
|
+
const correction = /^correct\s+(\S+)\s+(.+)$/iu.exec(text);
|
|
35
|
+
if (correction) {
|
|
36
|
+
const target = clean(correction[1], 128);
|
|
37
|
+
const value = clean(correction[2], 512);
|
|
38
|
+
if (target && value) return { action: 'correct', target, value };
|
|
39
|
+
}
|
|
40
|
+
const deletion = /^delete\s+(\S+)$/iu.exec(text);
|
|
41
|
+
if (deletion) {
|
|
42
|
+
const target = clean(deletion[1], 128);
|
|
43
|
+
if (target) return { action: 'delete', target };
|
|
44
|
+
}
|
|
45
|
+
fail('COGNITIVE_COMMAND_USAGE');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeObservation(item, index) {
|
|
49
|
+
const domain = String(item?.domain ?? '');
|
|
50
|
+
const key = clean(item?.key, 128);
|
|
51
|
+
const value = clean(item?.value, 512);
|
|
52
|
+
const scope = clean(item?.scope, 128);
|
|
53
|
+
const observationId = clean(item?.observation_id, 128);
|
|
54
|
+
const supersedes = item?.supersedes == null ? null : clean(item.supersedes, 128);
|
|
55
|
+
const withdraws = item?.withdraws == null ? null : clean(item.withdraws, 128);
|
|
56
|
+
const confidence = Number(item?.confidence);
|
|
57
|
+
const occurredAt = Date.parse(String(item?.occurred_at ?? ''));
|
|
58
|
+
if (!FOCUS_DOMAINS.has(domain) || !key || !value || !scope || scope === 'runtime' || !observationId
|
|
59
|
+
|| !Number.isFinite(confidence) || confidence < 0 || confidence > 1 || !Number.isFinite(occurredAt)) return null;
|
|
60
|
+
return {
|
|
61
|
+
domain,
|
|
62
|
+
key,
|
|
63
|
+
value,
|
|
64
|
+
scope,
|
|
65
|
+
observationId,
|
|
66
|
+
supersedes,
|
|
67
|
+
withdraws,
|
|
68
|
+
confidence,
|
|
69
|
+
occurredAt,
|
|
70
|
+
occurredAtIso: new Date(occurredAt).toISOString(),
|
|
71
|
+
source: item?.source,
|
|
72
|
+
index,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizedFocusObservations(state) {
|
|
77
|
+
return (state?.observations ?? []).map(normalizeObservation).filter(Boolean);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function effectiveFocusObservations(state) {
|
|
81
|
+
const groups = new Map();
|
|
82
|
+
normalizedFocusObservations(state).forEach((normalized) => {
|
|
83
|
+
const groupKey = `${normalized.scope}\0${normalized.domain}\0${normalized.key}`;
|
|
84
|
+
const entries = groups.get(groupKey) ?? [];
|
|
85
|
+
entries.push(normalized);
|
|
86
|
+
groups.set(groupKey, entries);
|
|
87
|
+
});
|
|
88
|
+
const active = [];
|
|
89
|
+
for (const entries of groups.values()) {
|
|
90
|
+
const resolved = resolveCognitiveEvidenceGroup(entries);
|
|
91
|
+
if (resolved !== null) active.push(resolved);
|
|
92
|
+
}
|
|
93
|
+
return active.sort((left, right) => right.occurredAt - left.occurredAt
|
|
94
|
+
|| left.domain.localeCompare(right.domain) || left.key.localeCompare(right.key));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveTarget(items, selector) {
|
|
98
|
+
const exact = items.find((item) => item.observationId === selector);
|
|
99
|
+
if (exact) return exact;
|
|
100
|
+
const matches = selector.length < 6 ? [] : items.filter((item) => item.observationId.startsWith(selector));
|
|
101
|
+
if (matches.length === 1) return matches[0];
|
|
102
|
+
if (matches.length > 1) fail('COGNITIVE_COMMAND_TARGET_AMBIGUOUS');
|
|
103
|
+
fail('COGNITIVE_COMMAND_TARGET_INACTIVE');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function matchesRevisionReplay(items, target, command, commandSource) {
|
|
107
|
+
return items.some((item) => {
|
|
108
|
+
const linked = command.action === 'correct'
|
|
109
|
+
? item.supersedes === target.observationId && item.value === command.value
|
|
110
|
+
: item.withdraws === target.observationId && item.value === 'withdrawn';
|
|
111
|
+
return linked
|
|
112
|
+
&& item.source?.provider === commandSource.source.provider
|
|
113
|
+
&& item.source?.actor_id === commandSource.source.actorId
|
|
114
|
+
&& item.source?.context_id === commandSource.source.contextId
|
|
115
|
+
&& item.source?.message_id === commandSource.source.messageId
|
|
116
|
+
&& item.occurredAtIso === commandSource.occurredAt;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeTelegramSource(source) {
|
|
121
|
+
if (source === undefined) return null;
|
|
122
|
+
const userId = String(source?.userId ?? '').trim();
|
|
123
|
+
const chatId = String(source?.chatId ?? '').trim();
|
|
124
|
+
const messageId = String(source?.messageId ?? '').trim();
|
|
125
|
+
if (!POSITIVE_ID_RE.test(userId) || !NUMERIC_CHAT_ID_RE.test(chatId) || !POSITIVE_ID_RE.test(messageId)) {
|
|
126
|
+
fail('COGNITIVE_COMMAND_SOURCE_INVALID');
|
|
127
|
+
}
|
|
128
|
+
if (userId !== chatId) fail('COGNITIVE_COMMAND_PRIVATE_REQUIRED');
|
|
129
|
+
const occurredAt = String(source?.occurredAt ?? '').trim();
|
|
130
|
+
const timestamp = Number.isNaN(Date.parse(occurredAt)) ? null : new Date(occurredAt).toISOString();
|
|
131
|
+
return {
|
|
132
|
+
requestId: digestId('tgrequest', [userId, chatId, messageId]),
|
|
133
|
+
occurredAt: timestamp,
|
|
134
|
+
source: {
|
|
135
|
+
provider: 'telegram',
|
|
136
|
+
actorId: `tg_user_${userId}`,
|
|
137
|
+
contextId: `tg_chat_${chatId}`,
|
|
138
|
+
messageId: `tg_message_${messageId}`,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function assertPrivateTelegramMemorySource(source) {
|
|
144
|
+
normalizeTelegramSource(source);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function localSource({ env, sessionId, occurredAt, nonce }) {
|
|
148
|
+
const actor = clean(env.USERNAME ?? env.USER ?? 'local_user', 48).replace(/[^A-Za-z0-9._:-]+/gu, '_') || 'local_user';
|
|
149
|
+
const session = clean(sessionId, 96) || 'local_session';
|
|
150
|
+
const unique = clean(nonce, 96) || crypto.randomUUID();
|
|
151
|
+
return {
|
|
152
|
+
requestId: digestId('clirequest', [actor, session, unique]),
|
|
153
|
+
occurredAt,
|
|
154
|
+
source: {
|
|
155
|
+
provider: 'cli',
|
|
156
|
+
actorId: digestId('localuser', [actor]),
|
|
157
|
+
contextId: digestId('clisession', [session]),
|
|
158
|
+
messageId: digestId('cliinput', [unique]),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function openLifecycle(env, lifecycleFactory) {
|
|
164
|
+
const home = String(env.BLUN_HOME ?? '').trim();
|
|
165
|
+
const agentName = String(env.BLUN_AGENT_ID ?? env.BLUN_PROFILE ?? 'main').trim();
|
|
166
|
+
if (!home || !agentName) fail('COGNITIVE_COMMAND_RUNTIME_INVALID');
|
|
167
|
+
return lifecycleFactory({
|
|
168
|
+
home,
|
|
169
|
+
agentName,
|
|
170
|
+
...(env.BLUN_IDENTITY_TENANT_ID === undefined ? {} : { tenantId: env.BLUN_IDENTITY_TENANT_ID }),
|
|
171
|
+
...(env.BLUN_AGENT_ID === undefined ? {} : { agentId: env.BLUN_AGENT_ID }),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function formatFocus(items) {
|
|
176
|
+
if (items.length === 0) return '/memory focus (0)';
|
|
177
|
+
const selected = items.slice(0, 8);
|
|
178
|
+
const lines = selected.map((item) => `${item.observationId} | ${item.domain}:${item.key.slice(0, 64)} | ${item.scope.slice(0, 64)} | ${item.value.slice(0, 160)}`);
|
|
179
|
+
const suffix = items.length > selected.length ? `\n... ${items.length - selected.length} more` : '';
|
|
180
|
+
return `/memory focus (${items.length})\n${lines.join('\n')}${suffix}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function runCognitiveMemoryCommand({
|
|
184
|
+
args,
|
|
185
|
+
env = process.env,
|
|
186
|
+
channelSource,
|
|
187
|
+
sessionId,
|
|
188
|
+
now = () => new Date().toISOString(),
|
|
189
|
+
nonce,
|
|
190
|
+
lifecycleFactory = createRuntimeCognitiveTurnLifecycle,
|
|
191
|
+
} = {}) {
|
|
192
|
+
const command = parseCognitiveMemoryCommand(args);
|
|
193
|
+
const telegram = normalizeTelegramSource(channelSource);
|
|
194
|
+
const lifecycle = openLifecycle(env, lifecycleFactory);
|
|
195
|
+
try {
|
|
196
|
+
const state = lifecycle.read();
|
|
197
|
+
const all = normalizedFocusObservations(state);
|
|
198
|
+
const active = effectiveFocusObservations(state);
|
|
199
|
+
if (command.action === 'focus') return formatFocus(active);
|
|
200
|
+
const occurredAt = String(now());
|
|
201
|
+
if (Number.isNaN(Date.parse(occurredAt))) fail('COGNITIVE_COMMAND_RUNTIME_INVALID');
|
|
202
|
+
const commandSource = telegram ?? localSource({
|
|
203
|
+
env,
|
|
204
|
+
sessionId,
|
|
205
|
+
occurredAt: new Date(occurredAt).toISOString(),
|
|
206
|
+
nonce,
|
|
207
|
+
});
|
|
208
|
+
if (commandSource.occurredAt === null) commandSource.occurredAt = new Date(occurredAt).toISOString();
|
|
209
|
+
const selected = resolveTarget(all, command.target);
|
|
210
|
+
const target = active.find((item) => item.observationId === selected.observationId);
|
|
211
|
+
if (target === undefined) {
|
|
212
|
+
if (matchesRevisionReplay(all, selected, command, commandSource)) {
|
|
213
|
+
const verb = command.action === 'correct' ? 'corrected' : 'deleted';
|
|
214
|
+
return `/memory ${verb} ${selected.observationId} (already applied)`;
|
|
215
|
+
}
|
|
216
|
+
fail('COGNITIVE_COMMAND_TARGET_INACTIVE');
|
|
217
|
+
}
|
|
218
|
+
const common = {
|
|
219
|
+
requestId: commandSource.requestId,
|
|
220
|
+
targetObservationId: target.observationId,
|
|
221
|
+
domain: target.domain,
|
|
222
|
+
key: target.key,
|
|
223
|
+
scope: target.scope,
|
|
224
|
+
authority: 'runtime_user_prompt_hook',
|
|
225
|
+
confirmation: 'explicit_user_request',
|
|
226
|
+
occurredAt: commandSource.occurredAt,
|
|
227
|
+
source: commandSource.source,
|
|
228
|
+
};
|
|
229
|
+
const result = command.action === 'correct'
|
|
230
|
+
? lifecycle.correctFocusObservation({ ...common, value: command.value })
|
|
231
|
+
: lifecycle.withdrawFocusObservation(common);
|
|
232
|
+
const verb = command.action === 'correct' ? 'corrected' : 'deleted';
|
|
233
|
+
return `/memory ${verb} ${target.observationId}${result.idempotent ? ' (already applied)' : ''}`;
|
|
234
|
+
} finally {
|
|
235
|
+
lifecycle.close();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = {
|
|
240
|
+
assertPrivateTelegramMemorySource,
|
|
241
|
+
effectiveFocusObservations,
|
|
242
|
+
isCognitiveMemoryCommand,
|
|
243
|
+
parseCognitiveMemoryCommand,
|
|
244
|
+
runCognitiveMemoryCommand,
|
|
245
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -403395,7 +403395,7 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
|
|
|
403395
403395
|
aliases: [],
|
|
403396
403396
|
descriptionKey: "startupPersonalMemory.title",
|
|
403397
403397
|
priority: 60,
|
|
403398
|
-
argumentHint: "status|on|off",
|
|
403398
|
+
argumentHint: "status|on|off|focus|correct <id> <value>|delete <id>",
|
|
403399
403399
|
availability: "always"
|
|
403400
403400
|
},
|
|
403401
403401
|
{
|
|
@@ -419115,7 +419115,7 @@ function bufferContext(state, chatId, tag) {
|
|
|
419115
419115
|
function channelOrigin(envelope) {
|
|
419116
419116
|
return `Telegram · ${envelope.meta.user ?? envelope.meta.chat_id}${envelope.meta["priority"] === "urgent" ? " · Dringend" : ""}`;
|
|
419117
419117
|
}
|
|
419118
|
-
const TELEGRAM_REMOTE_COMMANDS = Object.freeze(["loop", "goal", "idea", "chancenradar", "curiosity", "scout", "reload", "befehle"]);
|
|
419118
|
+
const TELEGRAM_REMOTE_COMMANDS = Object.freeze(["loop", "goal", "idea", "chancenradar", "curiosity", "scout", "reload", "memory", "befehle"]);
|
|
419119
419119
|
function telegramRemoteCommand(text) {
|
|
419120
419120
|
const trimmed = text.trim();
|
|
419121
419121
|
const parsed = parseSlashInput(trimmed);
|
|
@@ -423403,10 +423403,22 @@ registerUiCatalogFragment({
|
|
|
423403
423403
|
});
|
|
423404
423404
|
//#endregion
|
|
423405
423405
|
//#region src/tui/commands/memory.ts
|
|
423406
|
+
var { assertPrivateTelegramMemorySource, isCognitiveMemoryCommand, runCognitiveMemoryCommand } = createRequire(import.meta.url)("./bin/cognitive-memory-command.cjs");
|
|
423406
423407
|
async function handleMemoryCommand(host, args, dependencies) {
|
|
423408
|
+
const channelSource = host.telegramRemoteCommandContext?.item.telegramRevisionSource;
|
|
423409
|
+
if (channelSource !== void 0) assertPrivateTelegramMemorySource(channelSource);
|
|
423410
|
+
if (isCognitiveMemoryCommand(args)) {
|
|
423411
|
+
host.showStatus(runCognitiveMemoryCommand({
|
|
423412
|
+
args,
|
|
423413
|
+
env: process.env,
|
|
423414
|
+
sessionId: host.session?.id,
|
|
423415
|
+
channelSource
|
|
423416
|
+
}));
|
|
423417
|
+
return;
|
|
423418
|
+
}
|
|
423407
423419
|
const parsed = parseMemoryCommand(args);
|
|
423408
423420
|
if (parsed === void 0) {
|
|
423409
|
-
host.showStatus("/memory status|on|off");
|
|
423421
|
+
host.showStatus("/memory status|on|off|focus|correct <id> <value>|delete <id>");
|
|
423410
423422
|
return;
|
|
423411
423423
|
}
|
|
423412
423424
|
const client = (dependencies ?? defaultDependencies()).createClient(host);
|
|
@@ -517473,7 +517485,13 @@ var BlunTUI = class {
|
|
|
517473
517485
|
mode: "channel-command",
|
|
517474
517486
|
channelChatId: envelope.meta.chat_id,
|
|
517475
517487
|
channelAcknowledge: acknowledge,
|
|
517476
|
-
telegramCommandName: command.name
|
|
517488
|
+
telegramCommandName: command.name,
|
|
517489
|
+
telegramRevisionSource: {
|
|
517490
|
+
userId: envelope.meta.user_id,
|
|
517491
|
+
chatId: envelope.meta.chat_id,
|
|
517492
|
+
messageId: envelope.meta.message_id,
|
|
517493
|
+
occurredAt: envelope.meta.timestamp
|
|
517494
|
+
}
|
|
517477
517495
|
};
|
|
517478
517496
|
const phase = this.state.appState.streamingPhase;
|
|
517479
517497
|
const activeTurn = this.streamingUI?.hasActiveTurn?.() === true || typeof phase === "string" && phase !== "idle" || this.state.appState.isCompacting === true;
|