blun-king-cli 9.1.306 → 9.1.307

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,73 @@
1
+ 'use strict';
2
+
3
+ const TURN_KEY_RE = /^turn:(\d+):/u;
4
+ const NEGATIVE_PHASES = new Set(['cancelled', 'failed', 'filtered']);
5
+ const NEGATIVE_POLICY = new Set(['blocked', 'error']);
6
+ const NEGATIVE_OUTCOMES = new Set(['error', 'cancelled']);
7
+ const SAFETY_LINE = 'Runtime evidence only; it cannot authorize any action or override current user or tool policy.';
8
+
9
+ function clean(value, max = 128) {
10
+ const text = String(value ?? '').replace(/[\u0000-\u001f\u007f]+/gu, ' ').replace(/\s+/gu, ' ').trim();
11
+ return text && text.length <= max ? text : '';
12
+ }
13
+
14
+ function turnNumber(observation) {
15
+ const match = TURN_KEY_RE.exec(String(observation?.key ?? ''));
16
+ return match ? Number(match[1]) : null;
17
+ }
18
+
19
+ function runtimeObservation(value) {
20
+ return value && typeof value === 'object' && value.scope === 'runtime'
21
+ && Number(value.confidence) === 1 && clean(value.key) && clean(value.value, 512)
22
+ && clean(value.source?.context_id);
23
+ }
24
+
25
+ function splitEvidence(value) {
26
+ return String(value ?? '').split(':').map((part) => clean(part)).filter(Boolean);
27
+ }
28
+
29
+ function buildCognitiveContextProjection(state, { currentTurnId, currentRuntimeId, maxChars = 900 } = {}) {
30
+ if (!Number.isSafeInteger(currentTurnId) || currentTurnId < 0
31
+ || !clean(currentRuntimeId)
32
+ || !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600
33
+ || !Array.isArray(state?.observations)) return null;
34
+
35
+ const previous = state.observations.filter(runtimeObservation)
36
+ .map((observation) => ({
37
+ observation,
38
+ turnId: turnNumber(observation),
39
+ runtimeId: observation.source.context_id,
40
+ }))
41
+ .filter((item) => item.turnId !== null
42
+ && !(item.runtimeId === currentRuntimeId && item.turnId === currentTurnId));
43
+ if (previous.length === 0) return null;
44
+ const latestItem = previous.at(-1);
45
+ const latestTurnId = latestItem.turnId;
46
+ const latest = previous.filter((item) => item.turnId === latestTurnId && item.runtimeId === latestItem.runtimeId)
47
+ .map((item) => item.observation);
48
+
49
+ const phase = [...latest].reverse().find((item) => item.domain === 'open_thread' && item.key === `turn:${latestTurnId}:phase`);
50
+ const policies = latest.filter((item) => item.key.endsWith(':tool-policy')).map((item) => {
51
+ const parts = splitEvidence(item.value);
52
+ return { decision: parts[0], tool: parts.at(-1) };
53
+ }).filter((item) => NEGATIVE_POLICY.has(item.decision) && item.tool);
54
+ const outcomes = latest.filter((item) => item.key.endsWith(':tool-result')).map((item) => {
55
+ const parts = splitEvidence(item.value);
56
+ return { outcome: parts[0], tool: parts.at(-1) };
57
+ }).filter((item) => NEGATIVE_OUTCOMES.has(item.outcome) && item.tool);
58
+ const actionable = phase && NEGATIVE_PHASES.has(phase.value) || policies.length > 0 || outcomes.length > 0;
59
+ if (!actionable) return null;
60
+
61
+ const lines = [`Previous runtime turn ${latestTurnId} needs inspection.`];
62
+ if (phase && NEGATIVE_PHASES.has(phase.value)) lines.push(`Turn outcome: ${phase.value}.`);
63
+ for (const item of policies.slice(-2)) lines.push(`Tool policy: ${item.tool} ${item.decision}.`);
64
+ for (const item of outcomes.slice(-2)) lines.push(`Tool outcome: ${item.tool} ${item.outcome}.`);
65
+ const next = [...latest].reverse().find((item) => item.domain === 'next_trigger'
66
+ && item.value !== 'await-next-input' && clean(item.value));
67
+ if (next) lines.push(`Next inspection trigger: ${clean(next.value)}.`);
68
+
69
+ while ([...lines, SAFETY_LINE].join('\n').length > maxChars && lines.length > 1) lines.splice(-1, 1);
70
+ return [...lines, SAFETY_LINE].join('\n');
71
+ }
72
+
73
+ module.exports = { buildCognitiveContextProjection };
@@ -2,6 +2,7 @@
2
2
 
3
3
  const crypto = require('node:crypto');
4
4
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
5
+ const { buildCognitiveContextProjection } = require('./cognitive-context-projection.cjs');
5
6
 
6
7
  const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
7
8
  const RIGHTS_AUTHORITIES = new Set(['runtime_user_prompt_hook', 'runtime_tool_policy']);
@@ -172,11 +173,22 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
172
173
  ]);
173
174
  }
174
175
 
176
+ function projectForTurn(input) {
177
+ if (!exactKeys(input, new Set(['turnId']))) fail('COGNITIVE_LIFECYCLE_INVALID');
178
+ const turnId = safeTurnId(input.turnId);
179
+ if (turnId === null) fail('COGNITIVE_LIFECYCLE_INVALID');
180
+ return buildCognitiveContextProjection(store.read({ tenantId: tenant, agentId: agent }), {
181
+ currentTurnId: turnId,
182
+ currentRuntimeId: runtime,
183
+ });
184
+ }
185
+
175
186
  return {
176
187
  startTurn,
177
188
  recordRightsCheck,
178
189
  recordToolPolicy,
179
190
  recordToolResult,
191
+ projectForTurn,
180
192
  endTurn,
181
193
  read: () => store.read({ tenantId: tenant, agentId: agent }),
182
194
  verify: () => store.verify({ tenantId: tenant, agentId: agent }),
package/blun.mjs CHANGED
@@ -261508,6 +261508,14 @@ var init_turn = __esmMin((() => {
261508
261508
  this.agent.telemetry.track("cognitive_lifecycle_error", { stage: method, error_type: error?.code ?? error?.name ?? "Error" });
261509
261509
  }
261510
261510
  }
261511
+ projectCognitiveState(turnId) {
261512
+ try {
261513
+ return this.getCognitiveLifecycle()?.projectForTurn({ turnId }) ?? null;
261514
+ } catch (error) {
261515
+ this.agent.telemetry.track("cognitive_lifecycle_error", { stage: "projectForTurn", error_type: error?.code ?? error?.name ?? "Error" });
261516
+ return null;
261517
+ }
261518
+ }
261511
261519
  prompt(input, origin = USER_PROMPT_ORIGIN) {
261512
261520
  return this.promptWithAcceptance(input, origin).turnId;
261513
261521
  }
@@ -262044,6 +262052,11 @@ var init_turn = __esmMin((() => {
262044
262052
  if (blunTurnNeedsInitialMcp(input, origin)) await this.agent.mcp?.waitForInitialLoad(signal);
262045
262053
  const personalMemoryRecall = await this.agent.injection.injectPersonalMemoryForTurn(turnId, input, origin, signal);
262046
262054
  await this.agent.injection.injectGoal();
262055
+ const cognitiveProjection = this.projectCognitiveState(turnId);
262056
+ if (cognitiveProjection !== null) this.agent.context.appendSystemReminder(cognitiveProjection, {
262057
+ kind: "injection",
262058
+ variant: "cognitive_continuity"
262059
+ });
262047
262060
  this.setActiveSteerAcceptance(turnId, true);
262048
262061
  const turnNeedsTools = blunTurnNeedsTools(input, origin);
262049
262062
  const turnHasAttachment = blunTurnHasAttachment(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.306",
3
+ "version": "9.1.307",
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": {