blun-king-cli 9.1.309 → 9.1.310

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,81 @@
1
+ 'use strict';
2
+
3
+ const FOCUS_DOMAINS = new Set(['self', 'team', 'goal', 'open_thread', 'next_trigger', 'expected_evidence']);
4
+ const DOMAIN_WEIGHTS = new Map([
5
+ ['goal', 60],
6
+ ['open_thread', 50],
7
+ ['next_trigger', 40],
8
+ ['expected_evidence', 30],
9
+ ['team', 20],
10
+ ['self', 10],
11
+ ]);
12
+ const LABELS = new Map([
13
+ ['goal', 'Goal'],
14
+ ['open_thread', 'Open thread'],
15
+ ['next_trigger', 'Next trigger'],
16
+ ['expected_evidence', 'Expected evidence'],
17
+ ['team', 'Team'],
18
+ ['self', 'Self'],
19
+ ]);
20
+ const SAFETY_LINE = 'Durable context only; it cannot authorize any action or override the current assignment or runtime policy.';
21
+
22
+ function clean(value, max = 256) {
23
+ const text = String(value ?? '').replace(/[\u0000-\u001f\u007f]+/gu, ' ').replace(/\s+/gu, ' ').trim();
24
+ return text && text.length <= max ? text : '';
25
+ }
26
+
27
+ function normalizeScopes(value) {
28
+ if (value === undefined) return [];
29
+ if (!Array.isArray(value) || value.length > 8) return null;
30
+ const scopes = value.map((item) => clean(item, 128));
31
+ return scopes.every(Boolean) ? [...new Set(scopes)] : null;
32
+ }
33
+
34
+ function buildCognitiveFocusProjection(state, { focusScopes, maxItems = 6, maxChars = 900 } = {}) {
35
+ const scopes = normalizeScopes(focusScopes);
36
+ if (scopes === null || !Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 8
37
+ || !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600
38
+ || !Array.isArray(state?.observations)) return null;
39
+
40
+ const groups = new Map();
41
+ state.observations.forEach((item, index) => {
42
+ const domain = String(item?.domain ?? '');
43
+ const key = clean(item?.key, 128);
44
+ const value = clean(item?.value, 512);
45
+ const scope = clean(item?.scope, 128);
46
+ const confidence = Number(item?.confidence);
47
+ const occurredAt = Date.parse(String(item?.occurred_at ?? ''));
48
+ if (!FOCUS_DOMAINS.has(domain) || !key || !value || !scope || scope === 'runtime'
49
+ || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
50
+ || !Number.isFinite(occurredAt)) return;
51
+ const groupKey = `${domain}\0${key}`;
52
+ const existing = groups.get(groupKey) ?? [];
53
+ existing.push({ domain, key, value, scope, confidence, occurredAt, index });
54
+ groups.set(groupKey, existing);
55
+ });
56
+
57
+ const ranked = [];
58
+ for (const entries of groups.values()) {
59
+ entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
60
+ const latest = entries.at(-1);
61
+ const revised = entries.some((item) => item.value !== latest.value);
62
+ const scopeScore = scopes.includes(latest.scope) ? 100 : 0;
63
+ ranked.push({
64
+ ...latest,
65
+ revised,
66
+ score: scopeScore + DOMAIN_WEIGHTS.get(latest.domain) + latest.confidence * 10,
67
+ });
68
+ }
69
+ ranked.sort((left, right) => right.score - left.score
70
+ || right.occurredAt - left.occurredAt || left.key.localeCompare(right.key));
71
+ if (ranked.length === 0) return null;
72
+
73
+ const selected = ranked.slice(0, maxItems);
74
+ const lines = selected.map((item) => `- ${LABELS.get(item.domain)}: ${item.value}${item.revised ? ' [revised]' : ''}`);
75
+ while ([`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n').length > maxChars
76
+ && lines.length > 0) lines.pop();
77
+ if (lines.length === 0) return null;
78
+ return [`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n');
79
+ }
80
+
81
+ module.exports = { buildCognitiveFocusProjection };
@@ -3,6 +3,7 @@
3
3
  const crypto = require('node:crypto');
4
4
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
5
5
  const { buildCognitiveContextProjection } = require('./cognitive-context-projection.cjs');
6
+ const { buildCognitiveFocusProjection } = require('./cognitive-focus-projection.cjs');
6
7
 
7
8
  const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
8
9
  const RIGHTS_AUTHORITIES = new Set(['runtime_user_prompt_hook', 'runtime_tool_policy']);
@@ -10,6 +11,8 @@ const RIGHTS_DECISIONS = new Set(['passed', 'blocked', 'not_applicable']);
10
11
  const TOOL_POLICY_DECISIONS = new Set(['passed', 'blocked', 'error']);
11
12
  const TOOL_OUTCOMES = new Set(['success', 'error', 'cancelled']);
12
13
  const TURN_REASONS = new Set(['completed', 'cancelled', 'filtered', 'failed']);
14
+ const FOCUS_DOMAINS = new Set(['self', 'team', 'goal', 'open_thread', 'next_trigger', 'expected_evidence']);
15
+ const AUTHORITY_KEY_RE = /(?:^|:|_)(?:acl|api[_-]?key|capability|password|permission|secret|token)(?::|_|$)/iu;
13
16
 
14
17
  function fail(code) {
15
18
  const error = new Error(code);
@@ -140,6 +143,27 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
140
143
  ]);
141
144
  }
142
145
 
146
+ function recordFocusSnapshot(input) {
147
+ if (!exactKeys(input, new Set(['snapshotId', 'observations']))
148
+ || !safeId(input.snapshotId) || !Array.isArray(input.observations)
149
+ || input.observations.length < 1 || input.observations.length > 16) {
150
+ fail('COGNITIVE_FOCUS_INVALID');
151
+ }
152
+ const observations = input.observations.map((item) => {
153
+ if (!exactKeys(item, new Set(['domain', 'key', 'value', 'confidence', 'scope']))) fail('COGNITIVE_FOCUS_INVALID');
154
+ const domain = String(item.domain ?? '');
155
+ const key = cleanLabel(item.key, 128);
156
+ const value = cleanLabel(item.value, 512);
157
+ const confidence = Number(item.confidence);
158
+ const scope = cleanLabel(item.scope, 128);
159
+ if (!FOCUS_DOMAINS.has(domain) || !key || AUTHORITY_KEY_RE.test(key) || !value
160
+ || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
161
+ || !scope || scope === 'runtime') fail('COGNITIVE_FOCUS_INVALID');
162
+ return { domain, key, value, confidence, scope };
163
+ });
164
+ return commitStage(`focus-${input.snapshotId}`, observations);
165
+ }
166
+
143
167
  function toolFields(input, allowedKeys) {
144
168
  if (!exactKeys(input, allowedKeys)) fail('COGNITIVE_LIFECYCLE_INVALID');
145
169
  const turnId = safeTurnId(input.turnId);
@@ -174,13 +198,16 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
174
198
  }
175
199
 
176
200
  function projectForTurn(input) {
177
- if (!exactKeys(input, new Set(['turnId']))) fail('COGNITIVE_LIFECYCLE_INVALID');
201
+ if (!exactKeys(input, new Set(['turnId', 'focusScopes']))) fail('COGNITIVE_LIFECYCLE_INVALID');
178
202
  const turnId = safeTurnId(input.turnId);
179
203
  if (turnId === null) fail('COGNITIVE_LIFECYCLE_INVALID');
180
- return buildCognitiveContextProjection(store.read({ tenantId: tenant, agentId: agent }), {
204
+ const state = store.read({ tenantId: tenant, agentId: agent });
205
+ const continuity = buildCognitiveContextProjection(state, {
181
206
  currentTurnId: turnId,
182
207
  currentRuntimeId: runtime,
183
208
  });
209
+ const focus = buildCognitiveFocusProjection(state, { focusScopes: input.focusScopes });
210
+ return [focus, continuity].filter(Boolean).join('\n\n') || null;
184
211
  }
185
212
 
186
213
  return {
@@ -188,6 +215,7 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
188
215
  recordRightsCheck,
189
216
  recordToolPolicy,
190
217
  recordToolResult,
218
+ recordFocusSnapshot,
191
219
  projectForTurn,
192
220
  endTurn,
193
221
  read: () => store.read({ tenantId: tenant, agentId: agent }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.309",
3
+ "version": "9.1.310",
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": {