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.
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.364",
3
+ "version": "9.1.365",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {