blun-king-cli 9.1.359 → 9.1.361

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,67 @@
1
+ 'use strict';
2
+
3
+ const CANONICAL_LATEST_DOMAINS = new Set([
4
+ 'goal', 'open_thread', 'next_trigger', 'expected_evidence',
5
+ ]);
6
+ const CONFIDENCE_MARGIN = 0.2;
7
+
8
+ function compareVariants(left, right) {
9
+ return right.maxConfidence - left.maxConfidence
10
+ || right.confirmations - left.confirmations
11
+ || right.latestOccurredAt - left.latestOccurredAt
12
+ || left.value.localeCompare(right.value);
13
+ }
14
+
15
+ function resolveCognitiveEvidenceGroup(entries) {
16
+ if (!Array.isArray(entries) || entries.length === 0) return null;
17
+ const ordered = [...entries].sort((left, right) => left.occurredAt - right.occurredAt
18
+ || left.index - right.index);
19
+ const latest = ordered.at(-1);
20
+ const distinctValues = new Set(ordered.map((item) => item.value));
21
+ if (distinctValues.size === 1) return { ...latest, revised: false, contested: false, conflict: false };
22
+ if (CANONICAL_LATEST_DOMAINS.has(latest.domain)) {
23
+ return { ...latest, revised: true, contested: false, conflict: false };
24
+ }
25
+
26
+ const variants = new Map();
27
+ for (const item of ordered) {
28
+ const variant = variants.get(item.value) ?? {
29
+ value: item.value,
30
+ maxConfidence: item.confidence,
31
+ confirmations: 0,
32
+ latestOccurredAt: item.occurredAt,
33
+ representative: item,
34
+ };
35
+ variant.confirmations += 1;
36
+ variant.latestOccurredAt = Math.max(variant.latestOccurredAt, item.occurredAt);
37
+ if (item.confidence > variant.maxConfidence
38
+ || item.confidence === variant.maxConfidence && item.occurredAt >= variant.representative.occurredAt) {
39
+ variant.maxConfidence = item.confidence;
40
+ variant.representative = item;
41
+ }
42
+ variants.set(item.value, variant);
43
+ }
44
+ const ranked = [...variants.values()].sort(compareVariants);
45
+ const strongest = ranked[0];
46
+ const runnerUp = ranked[1];
47
+ if (strongest.maxConfidence - runnerUp.maxConfidence >= CONFIDENCE_MARGIN - Number.EPSILON) {
48
+ return {
49
+ ...strongest.representative,
50
+ confidence: strongest.maxConfidence,
51
+ occurredAt: strongest.latestOccurredAt,
52
+ revised: false,
53
+ contested: true,
54
+ conflict: false,
55
+ };
56
+ }
57
+ return {
58
+ ...latest,
59
+ confidence: strongest.maxConfidence,
60
+ occurredAt: Math.max(...ranked.map((item) => item.latestOccurredAt)),
61
+ revised: false,
62
+ contested: false,
63
+ conflict: true,
64
+ };
65
+ }
66
+
67
+ module.exports = { resolveCognitiveEvidenceGroup };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { resolveCognitiveEvidenceGroup } = require('./cognitive-effective-view.cjs');
4
+
3
5
  const FOCUS_DOMAINS = new Set(['self', 'team', 'goal', 'open_thread', 'next_trigger', 'expected_evidence']);
4
6
  const DOMAIN_WEIGHTS = new Map([
5
7
  ['goal', 60],
@@ -60,11 +62,11 @@ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxCh
60
62
  entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
61
63
  const latest = entries.at(-1);
62
64
  if (!scopes.includes(latest.scope)) continue;
63
- const revised = entries.some((item) => item.value !== latest.value);
65
+ const effective = resolveCognitiveEvidenceGroup(entries);
66
+ if (!effective) continue;
64
67
  ranked.push({
65
- ...latest,
66
- revised,
67
- score: DOMAIN_WEIGHTS.get(latest.domain) + latest.confidence * 10,
68
+ ...effective,
69
+ score: DOMAIN_WEIGHTS.get(effective.domain) + effective.confidence * 10,
68
70
  });
69
71
  }
70
72
  ranked.sort((left, right) => right.score - left.score
@@ -72,7 +74,11 @@ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxCh
72
74
  if (ranked.length === 0) return null;
73
75
 
74
76
  const selected = ranked.slice(0, maxItems);
75
- const lines = selected.map((item) => `- ${LABELS.get(item.domain)}: ${item.value}${item.revised ? ' [revised]' : ''}`);
77
+ const lines = selected.map((item) => {
78
+ if (item.conflict) return `- Conflict: ${item.key} has incompatible evidence; verify before relying on it.`;
79
+ const marker = item.contested ? ' [contested; stronger evidence]' : item.revised ? ' [revised]' : '';
80
+ return `- ${LABELS.get(item.domain)}: ${item.value}${marker}`;
81
+ });
76
82
  while ([`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n').length > maxChars
77
83
  && lines.length > 0) lines.pop();
78
84
  if (lines.length === 0) return null;
@@ -118,6 +118,50 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
118
118
  fail('COGNITIVE_VERSION_CONFLICT');
119
119
  }
120
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
+
121
165
  function startTurn(input) {
122
166
  if (!exactKeys(input, new Set(['turnId', 'originKind']))) fail('COGNITIVE_LIFECYCLE_INVALID');
123
167
  const turnId = safeTurnId(input.turnId);
@@ -183,7 +227,7 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
183
227
  || !scope || scope === 'runtime') fail('COGNITIVE_FOCUS_INVALID');
184
228
  return { domain, key, value, confidence, scope };
185
229
  });
186
- return commitStage(`focus-${input.snapshotId}`, observations);
230
+ return commitDurableStage(`focus-${input.snapshotId}`, observations);
187
231
  }
188
232
 
189
233
  function toolFields(input, allowedKeys) {
@@ -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, cognitiveFocusScopesForTurn, 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();
@@ -261486,6 +261486,7 @@ var init_turn = __esmMin((() => {
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
261488
  ({ cognitiveFocusScopesForTurn } = createRequire(import.meta.url)("./bin/cognitive-focus-scope.cjs"));
261489
+ ({ buildCognitiveWorkFocus } = createRequire(import.meta.url)("./bin/cognitive-work-focus.cjs"));
261489
261490
  ({ buildAttentionQueueItem } = createRequire(import.meta.url)("./bin/cognitive-attention-delivery.cjs"));
261490
261491
  BLUN_LEAN_TOOL_NAMES = new Set(BLUN_CORE_TOOL_NAMES);
261491
261492
  BLUN_ATTACHMENT_MARKER_RE = /\b(?:attachment_file_id|telegram-anhang|telegram attachment)\b/i;
@@ -261624,7 +261625,16 @@ var init_turn = __esmMin((() => {
261624
261625
  projectCognitiveState(turnId, input) {
261625
261626
  try {
261626
261627
  const focusScopes = cognitiveFocusScopesForTurn(input);
261627
- return this.getCognitiveLifecycle()?.projectForTurn({ turnId, focusScopes }) ?? null;
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;
261628
261638
  } catch (error) {
261629
261639
  this.agent.telemetry.track("cognitive_lifecycle_error", { stage: "projectForTurn", error_type: error?.code ?? error?.name ?? "Error" });
261630
261640
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.359",
3
+ "version": "9.1.361",
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": {