blun-king-cli 9.1.358 → 9.1.360

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.
@@ -36,6 +36,7 @@ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxCh
36
36
  if (scopes === null || !Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 8
37
37
  || !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600
38
38
  || !Array.isArray(state?.observations)) return null;
39
+ if (scopes.length === 0) return null;
39
40
 
40
41
  const groups = new Map();
41
42
  state.observations.forEach((item, index) => {
@@ -48,7 +49,7 @@ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxCh
48
49
  if (!FOCUS_DOMAINS.has(domain) || !key || !value || !scope || scope === 'runtime'
49
50
  || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
50
51
  || !Number.isFinite(occurredAt)) return;
51
- const groupKey = `${domain}\0${key}`;
52
+ const groupKey = `${scope}\0${domain}\0${key}`;
52
53
  const existing = groups.get(groupKey) ?? [];
53
54
  existing.push({ domain, key, value, scope, confidence, occurredAt, index });
54
55
  groups.set(groupKey, existing);
@@ -58,12 +59,12 @@ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxCh
58
59
  for (const entries of groups.values()) {
59
60
  entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
60
61
  const latest = entries.at(-1);
62
+ if (!scopes.includes(latest.scope)) continue;
61
63
  const revised = entries.some((item) => item.value !== latest.value);
62
- const scopeScore = scopes.includes(latest.scope) ? 100 : 0;
63
64
  ranked.push({
64
65
  ...latest,
65
66
  revised,
66
- score: scopeScore + DOMAIN_WEIGHTS.get(latest.domain) + latest.confidence * 10,
67
+ score: DOMAIN_WEIGHTS.get(latest.domain) + latest.confidence * 10,
67
68
  });
68
69
  }
69
70
  ranked.sort((left, right) => right.score - left.score
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ const CHANNEL_RE = /<channel\b([^>]*)>[\s\S]*?<\/channel>/giu;
4
+ const TELEGRAM_SOURCE_RE = /\bsource\s*=\s*["']telegram["']/iu;
5
+ const CHAT_ID_RE = /\bchat_id\s*=\s*["'](-?\d+)["']/iu;
6
+ const USER_ID_RE = /\buser_id\s*=\s*["'](\d+)["']/iu;
7
+ const DM_MARKER_RE = /^#?\s*\[tg\s+dm\b[^\]]*\bchat_id\s*=\s*(\d+)[^\]]*\]/gimu;
8
+
9
+ function inputText(input) {
10
+ if (!Array.isArray(input)) return '';
11
+ return input
12
+ .filter((part) => part?.type === 'text' && typeof part.text === 'string')
13
+ .map((part) => part.text)
14
+ .join('\n');
15
+ }
16
+
17
+ function relationshipScope(subjectId) {
18
+ return `relationship:telegram-${subjectId}:private`;
19
+ }
20
+
21
+ function cognitiveFocusScopesForTurn(input) {
22
+ const text = inputText(input);
23
+ if (!text) return [];
24
+ const scopes = [];
25
+ for (const match of text.matchAll(CHANNEL_RE)) {
26
+ const attributes = match[1] ?? '';
27
+ if (!TELEGRAM_SOURCE_RE.test(attributes)) continue;
28
+ const chatId = CHAT_ID_RE.exec(attributes)?.[1];
29
+ if (!chatId || chatId.startsWith('-')) continue;
30
+ const subjectId = USER_ID_RE.exec(attributes)?.[1] ?? chatId;
31
+ scopes.push(relationshipScope(subjectId));
32
+ }
33
+ for (const match of text.matchAll(DM_MARKER_RE)) scopes.push(relationshipScope(match[1]));
34
+ return [...new Set(scopes)].slice(0, 8);
35
+ }
36
+
37
+ module.exports = { cognitiveFocusScopesForTurn };
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
4
6
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
5
7
  const { buildCognitiveContextProjection } = require('./cognitive-context-projection.cjs');
6
8
  const { buildCognitiveFocusProjection } = require('./cognitive-focus-projection.cjs');
@@ -45,6 +47,25 @@ function digestId(prefix, values) {
45
47
  return `${prefix}-${digest}`;
46
48
  }
47
49
 
50
+ function identityFromManifest(home) {
51
+ const root = fs.realpathSync(path.resolve(String(home ?? '')));
52
+ const identityRoot = path.join(root, 'identity');
53
+ const manifestPath = path.join(identityRoot, 'manifest.json');
54
+ try {
55
+ const identityStat = fs.lstatSync(identityRoot);
56
+ const manifestStat = fs.lstatSync(manifestPath);
57
+ if (!identityStat.isDirectory() || identityStat.isSymbolicLink()
58
+ || !manifestStat.isFile() || manifestStat.isSymbolicLink() || manifestStat.size > 64 * 1024) return null;
59
+ if (fs.realpathSync(identityRoot) !== identityRoot || fs.realpathSync(manifestPath) !== manifestPath) return null;
60
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
61
+ const tenantId = safeId(manifest?.tenant_id);
62
+ const agentId = safeId(manifest?.active_agent_id);
63
+ return manifest?.version === 1 && tenantId && agentId ? { tenantId, agentId } : null;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
48
69
  function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now = () => new Date().toISOString() } = {}) {
49
70
  const tenant = safeId(tenantId);
50
71
  const agent = safeId(agentId);
@@ -97,6 +118,50 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
97
118
  fail('COGNITIVE_VERSION_CONFLICT');
98
119
  }
99
120
 
121
+ function commitDurableStage(stageKey, observations) {
122
+ if (stageResults.has(stageKey)) return { ...stageResults.get(stageKey), idempotent: true };
123
+ const eventId = digestId('durable', [tenant, agent, stageKey]);
124
+ if (store.hasEvent(eventId)) {
125
+ const result = { idempotent: true };
126
+ stageResults.set(stageKey, result);
127
+ return result;
128
+ }
129
+ const occurredAt = stageTime(`durable:${stageKey}`);
130
+ const normalized = observations.map((item, index) => ({
131
+ observation_id: digestId('durableobs', [eventId, String(index)]),
132
+ ...item,
133
+ }));
134
+ for (let attempt = 0; attempt < 4; attempt += 1) {
135
+ if (store.hasEvent(eventId)) {
136
+ const result = { idempotent: true };
137
+ stageResults.set(stageKey, result);
138
+ return result;
139
+ }
140
+ const expectedVersion = store.read({ tenantId: tenant, agentId: agent }).version;
141
+ try {
142
+ const result = store.commit({
143
+ tenantId: tenant,
144
+ agentId: agent,
145
+ eventId,
146
+ expectedVersion,
147
+ occurredAt,
148
+ source: {
149
+ provider: 'runtime',
150
+ actor_id: agent,
151
+ context_id: 'durable-focus',
152
+ message_id: stageKey,
153
+ },
154
+ observations: normalized,
155
+ });
156
+ stageResults.set(stageKey, result);
157
+ return result;
158
+ } catch (error) {
159
+ if (error?.code !== 'COGNITIVE_VERSION_CONFLICT' || attempt === 3) throw error;
160
+ }
161
+ }
162
+ fail('COGNITIVE_VERSION_CONFLICT');
163
+ }
164
+
100
165
  function startTurn(input) {
101
166
  if (!exactKeys(input, new Set(['turnId', 'originKind']))) fail('COGNITIVE_LIFECYCLE_INVALID');
102
167
  const turnId = safeTurnId(input.turnId);
@@ -162,7 +227,7 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
162
227
  || !scope || scope === 'runtime') fail('COGNITIVE_FOCUS_INVALID');
163
228
  return { domain, key, value, confidence, scope };
164
229
  });
165
- return commitStage(`focus-${input.snapshotId}`, observations);
230
+ return commitDurableStage(`focus-${input.snapshotId}`, observations);
166
231
  }
167
232
 
168
233
  function toolFields(input, allowedKeys) {
@@ -281,14 +346,25 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
281
346
  };
282
347
  }
283
348
 
284
- function createRuntimeCognitiveTurnLifecycle({ home, agentName, runtimeId, now } = {}) {
349
+ function createRuntimeCognitiveTurnLifecycle({ home, agentName, tenantId, agentId, runtimeId, now } = {}) {
285
350
  const resolvedHome = String(home ?? '').trim();
286
351
  const resolvedAgent = String(agentName ?? '').trim();
287
352
  if (!resolvedHome || !resolvedAgent) fail('COGNITIVE_LIFECYCLE_INVALID');
353
+ const explicitTenant = tenantId === undefined ? '' : safeId(tenantId);
354
+ const explicitAgent = agentId === undefined ? '' : safeId(agentId);
355
+ if ((tenantId !== undefined && !explicitTenant) || (agentId !== undefined && !explicitAgent)) {
356
+ fail('COGNITIVE_LIFECYCLE_INVALID');
357
+ }
358
+ const manifestIdentity = identityFromManifest(resolvedHome);
359
+ const identity = {
360
+ tenantId: explicitTenant || manifestIdentity?.tenantId || '',
361
+ agentId: explicitAgent || manifestIdentity?.agentId || '',
362
+ };
363
+ const hasSharedIdentity = Boolean(identity.tenantId && identity.agentId);
288
364
  return createCognitiveTurnLifecycle({
289
365
  home: resolvedHome,
290
- tenantId: digestId('home', [resolvedHome.toLowerCase()]),
291
- agentId: digestId('agent', [resolvedAgent.toLowerCase()]),
366
+ tenantId: hasSharedIdentity ? identity.tenantId : digestId('home', [resolvedHome.toLowerCase()]),
367
+ agentId: hasSharedIdentity ? identity.agentId : digestId('agent', [resolvedAgent.toLowerCase()]),
292
368
  runtimeId,
293
369
  now,
294
370
  });
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+
5
+ const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
6
+ const STATUSES = new Set(['active', 'paused', 'blocked']);
7
+ const NEXT_TRIGGER = new Map([
8
+ ['active', 'Continue the active goal from its last verified state.'],
9
+ ['paused', 'Wait until the goal is explicitly resumed.'],
10
+ ['blocked', 'Resolve the current blocker before continuing.'],
11
+ ]);
12
+
13
+ function bounded(value, max = 512) {
14
+ const text = String(value ?? '').replace(/[\u0000-\u001f\u007f]+/gu, ' ').replace(/\s+/gu, ' ').trim();
15
+ if (!text) return '';
16
+ return text.length <= max ? text : `${text.slice(0, max - 3).trimEnd()}...`;
17
+ }
18
+
19
+ function buildCognitiveWorkFocus(goal) {
20
+ if (!goal || typeof goal !== 'object' || Array.isArray(goal)) return null;
21
+ const goalId = String(goal.goalId ?? '').trim();
22
+ const objective = bounded(goal.objective);
23
+ const completionCriterion = bounded(goal.completionCriterion)
24
+ || 'Goal completion must be supported by verified evidence.';
25
+ const status = String(goal.status ?? '');
26
+ const turnsUsed = Number(goal.turnsUsed);
27
+ if (!SAFE_ID_RE.test(goalId) || !objective || !STATUSES.has(status)
28
+ || !Number.isSafeInteger(turnsUsed) || turnsUsed < 0) return null;
29
+
30
+ const focusScope = `goal:${goalId}`;
31
+ if (!SAFE_ID_RE.test(focusScope)) return null;
32
+ const digest = crypto.createHash('sha256')
33
+ .update([goalId, objective, completionCriterion, status, String(turnsUsed)].join('\0'))
34
+ .digest('hex').slice(0, 40);
35
+ const keyRoot = `goal:${goalId}`;
36
+ return {
37
+ snapshotId: `goalfocus-${digest}`,
38
+ focusScope,
39
+ observations: [
40
+ { domain: 'goal', key: `${keyRoot}:objective`, value: objective, confidence: 1, scope: focusScope },
41
+ { domain: 'open_thread', key: `${keyRoot}:status`, value: `Goal is ${status}.`, confidence: 1, scope: focusScope },
42
+ { domain: 'next_trigger', key: `${keyRoot}:next`, value: NEXT_TRIGGER.get(status), confidence: 1, scope: focusScope },
43
+ { domain: 'expected_evidence', key: `${keyRoot}:evidence`, value: completionCriterion, confidence: 1, scope: focusScope },
44
+ ],
45
+ };
46
+ }
47
+
48
+ module.exports = { buildCognitiveWorkFocus };
package/blun.mjs CHANGED
@@ -261464,7 +261464,7 @@ function toolResultText(result) {
261464
261464
  function abandonedToolResultOutput(ended) {
261465
261465
  return `Tool call did not complete: ${ended.reason === "cancelled" ? "the turn was cancelled" : ended.reason === "failed" ? `the turn failed${ended.error !== void 0 ? ` (${ended.error.message})` : ""}` : "the turn ended"} before its result was recorded. Do not assume the tool completed successfully.`;
261466
261466
  }
261467
- var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaGenerationToolNamesForText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261467
+ var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaGenerationToolNamesForText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261468
261468
  var init_turn = __esmMin((() => {
261469
261469
  init_dist$4();
261470
261470
  init_src$4();
@@ -261485,6 +261485,8 @@ var init_turn = __esmMin((() => {
261485
261485
  ({ projectRecurringCronHistory } = createRequire(import.meta.url)("./bin/recurring-cron-history-policy.cjs"));
261486
261486
  ({ TelegramDeliveryLedger } = createRequire(import.meta.url)("./bin/repeated-user-message-projection.cjs"));
261487
261487
  ({ createRuntimeCognitiveTurnLifecycle } = createRequire(import.meta.url)("./bin/cognitive-turn-lifecycle.cjs"));
261488
+ ({ cognitiveFocusScopesForTurn } = createRequire(import.meta.url)("./bin/cognitive-focus-scope.cjs"));
261489
+ ({ buildCognitiveWorkFocus } = createRequire(import.meta.url)("./bin/cognitive-work-focus.cjs"));
261488
261490
  ({ buildAttentionQueueItem } = createRequire(import.meta.url)("./bin/cognitive-attention-delivery.cjs"));
261489
261491
  BLUN_LEAN_TOOL_NAMES = new Set(BLUN_CORE_TOOL_NAMES);
261490
261492
  BLUN_ATTACHMENT_MARKER_RE = /\b(?:attachment_file_id|telegram-anhang|telegram attachment)\b/i;
@@ -261568,7 +261570,9 @@ var init_turn = __esmMin((() => {
261568
261570
  try {
261569
261571
  this.cognitiveLifecycle = createRuntimeCognitiveTurnLifecycle({
261570
261572
  home,
261571
- agentName: this.agent.activeProfile?.name ?? this.agent.type
261573
+ agentName: this.agent.activeProfile?.name ?? this.agent.type,
261574
+ tenantId: process.env.BLUN_IDENTITY_TENANT_ID,
261575
+ agentId: process.env.BLUN_AGENT_ID
261572
261576
  });
261573
261577
  return this.cognitiveLifecycle;
261574
261578
  } catch (error) {
@@ -261618,9 +261622,19 @@ var init_turn = __esmMin((() => {
261618
261622
  this.recordCognitiveStage("recordToolPolicy", policy);
261619
261623
  }
261620
261624
  }
261621
- projectCognitiveState(turnId) {
261625
+ projectCognitiveState(turnId, input) {
261622
261626
  try {
261623
- return this.getCognitiveLifecycle()?.projectForTurn({ turnId }) ?? null;
261627
+ const focusScopes = cognitiveFocusScopesForTurn(input);
261628
+ const lifecycle = this.getCognitiveLifecycle();
261629
+ const workFocus = buildCognitiveWorkFocus(this.agent.goal.getGoal().goal);
261630
+ if (workFocus !== null) {
261631
+ lifecycle?.recordFocusSnapshot({
261632
+ snapshotId: workFocus.snapshotId,
261633
+ observations: workFocus.observations
261634
+ });
261635
+ focusScopes.unshift(workFocus.focusScope);
261636
+ }
261637
+ return lifecycle?.projectForTurn({ turnId, focusScopes }) ?? null;
261624
261638
  } catch (error) {
261625
261639
  this.agent.telemetry.track("cognitive_lifecycle_error", { stage: "projectForTurn", error_type: error?.code ?? error?.name ?? "Error" });
261626
261640
  return null;
@@ -262178,7 +262192,7 @@ var init_turn = __esmMin((() => {
262178
262192
  if (blunTurnNeedsInitialMcp(input, origin)) await this.agent.mcp?.waitForInitialLoad(signal);
262179
262193
  const personalMemoryRecall = await this.agent.injection.injectPersonalMemoryForTurn(turnId, input, origin, signal);
262180
262194
  await this.agent.injection.injectGoal();
262181
- const cognitiveProjection = this.projectCognitiveState(turnId);
262195
+ const cognitiveProjection = this.projectCognitiveState(turnId, input);
262182
262196
  if (cognitiveProjection !== null) this.agent.context.appendSystemReminder(cognitiveProjection, {
262183
262197
  kind: "injection",
262184
262198
  variant: "cognitive_continuity"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.358",
3
+ "version": "9.1.360",
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": {