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.
- package/bin/cognitive-context-projection.cjs +73 -0
- package/bin/cognitive-turn-lifecycle.cjs +12 -0
- package/blun.mjs +13 -0
- package/package.json +1 -1
|
@@ -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);
|