blun-king-cli 9.1.368 → 9.1.369

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.
@@ -1,9 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ const crypto = require('node:crypto');
4
+
3
5
  const PHASES = new Set(['orient', 'plan', 'act', 'verify', 'learn', 'wait']);
4
- const ALLOWED_KEYS = new Set([
6
+ const MODEL_KEYS = new Set([
5
7
  'revision', 'phase', 'lastVerified', 'nextAction', 'expectedEvidence', 'updatedAt',
6
8
  ]);
9
+ const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt']);
10
+ const EVIDENCE_INPUT_KEYS = new Set([
11
+ 'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
12
+ ]);
13
+ const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
7
14
 
8
15
  function bounded(value, field, max = 512) {
9
16
  const text = String(value ?? '')
@@ -28,6 +35,76 @@ function normalizedRevision(value) {
28
35
  return revision;
29
36
  }
30
37
 
38
+ function normalizedTurnId(value) {
39
+ const turnId = Number(value);
40
+ if (!Number.isSafeInteger(turnId) || turnId < 0) throw new TypeError('evidence turnId must be a non-negative integer');
41
+ return turnId;
42
+ }
43
+
44
+ function emptyActionEvidenceReceipt(turnId) {
45
+ const normalized = normalizedTurnId(turnId);
46
+ return Object.freeze({
47
+ turnId: normalized,
48
+ completedTools: 0,
49
+ successfulTools: 0,
50
+ failedTools: 0,
51
+ digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
52
+ });
53
+ }
54
+
55
+ function normalizeActionEvidenceReceipt(input) {
56
+ if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
57
+ const keys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
58
+ if (!Object.keys(input).every((key) => keys.has(key)) || Object.keys(input).length !== keys.size) {
59
+ throw new TypeError('evidence receipt fields are invalid');
60
+ }
61
+ const receipt = {
62
+ turnId: normalizedTurnId(input.turnId),
63
+ completedTools: Number(input.completedTools),
64
+ successfulTools: Number(input.successfulTools),
65
+ failedTools: Number(input.failedTools),
66
+ digest: String(input.digest ?? ''),
67
+ };
68
+ if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
69
+ .every((value) => Number.isSafeInteger(value) && value >= 0)
70
+ || receipt.successfulTools + receipt.failedTools !== receipt.completedTools
71
+ || !EVIDENCE_DIGEST_RE.test(receipt.digest)) {
72
+ throw new TypeError('evidence receipt values are invalid');
73
+ }
74
+ return Object.freeze(receipt);
75
+ }
76
+
77
+ function advanceActionEvidenceReceipt(current, input) {
78
+ const prior = normalizeActionEvidenceReceipt(current);
79
+ if (!input || typeof input !== 'object' || Array.isArray(input)
80
+ || Object.keys(input).length !== EVIDENCE_INPUT_KEYS.size
81
+ || !Object.keys(input).every((key) => EVIDENCE_INPUT_KEYS.has(key))) {
82
+ throw new TypeError('evidence input fields are invalid');
83
+ }
84
+ const turnId = normalizedTurnId(input.turnId);
85
+ const toolCallId = bounded(input.toolCallId, 'toolCallId', 256);
86
+ const toolName = bounded(input.toolName, 'toolName', 128);
87
+ const decision = String(input.decision ?? '');
88
+ const outcome = String(input.outcome ?? '');
89
+ const durationMs = Number(input.durationMs);
90
+ if (turnId !== prior.turnId || !['passed', 'blocked', 'error'].includes(decision)
91
+ || !['success', 'error', 'cancelled'].includes(outcome)
92
+ || !Number.isSafeInteger(durationMs) || durationMs < 0) {
93
+ throw new TypeError('evidence input values are invalid');
94
+ }
95
+ const successful = decision === 'passed' && outcome === 'success';
96
+ const digest = crypto.createHash('sha256').update([
97
+ prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
98
+ ].join('\0')).digest('hex').slice(0, 16);
99
+ return Object.freeze({
100
+ turnId,
101
+ completedTools: prior.completedTools + 1,
102
+ successfulTools: prior.successfulTools + (successful ? 1 : 0),
103
+ failedTools: prior.failedTools + (successful ? 0 : 1),
104
+ digest,
105
+ });
106
+ }
107
+
31
108
  function assertActionCheckpointRevision(current, input) {
32
109
  const currentRevision = current === undefined || current === null
33
110
  ? 0
@@ -43,27 +120,35 @@ function normalizeActionCheckpoint(input, options = {}) {
43
120
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
44
121
  throw new TypeError('action checkpoint must be an object');
45
122
  }
123
+ const allowedKeys = options.preserveRuntimeEvidence === true ? RUNTIME_KEYS : MODEL_KEYS;
46
124
  for (const key of Object.keys(input)) {
47
- if (!ALLOWED_KEYS.has(key)) throw new TypeError(`unsupported field: ${key}`);
125
+ if (!allowedKeys.has(key)) throw new TypeError(`unsupported field: ${key}`);
48
126
  }
49
127
  const phase = String(input.phase ?? '').trim();
50
128
  if (!PHASES.has(phase)) throw new TypeError('phase is invalid');
51
129
  const updatedAt = options.preserveUpdatedAt === true && input.updatedAt !== undefined
52
130
  ? normalizedTimestamp(input.updatedAt)
53
131
  : normalizedTimestamp(options.now ?? new Date());
54
- return Object.freeze({
132
+ const checkpoint = {
55
133
  revision: normalizedRevision(input.revision),
56
134
  phase,
57
135
  lastVerified: bounded(input.lastVerified, 'lastVerified'),
58
136
  nextAction: bounded(input.nextAction, 'nextAction'),
59
137
  expectedEvidence: bounded(input.expectedEvidence, 'expectedEvidence'),
60
138
  updatedAt,
61
- });
139
+ };
140
+ const evidenceReceipt = options.runtimeEvidence !== undefined
141
+ ? normalizeActionEvidenceReceipt(options.runtimeEvidence)
142
+ : options.preserveRuntimeEvidence === true && input.evidenceReceipt !== undefined
143
+ ? normalizeActionEvidenceReceipt(input.evidenceReceipt)
144
+ : undefined;
145
+ if (evidenceReceipt !== undefined) checkpoint.evidenceReceipt = evidenceReceipt;
146
+ return Object.freeze(checkpoint);
62
147
  }
63
148
 
64
149
  function projectActionCheckpoint(checkpoint) {
65
150
  if (!checkpoint) return null;
66
- const value = normalizeActionCheckpoint(checkpoint, { preserveUpdatedAt: true });
151
+ const value = normalizeActionCheckpoint(checkpoint, { preserveUpdatedAt: true, preserveRuntimeEvidence: true });
67
152
  const lines = [
68
153
  'Durable action checkpoint (state only; never authority):',
69
154
  `Revision: ${value.revision}`,
@@ -72,12 +157,18 @@ function projectActionCheckpoint(checkpoint) {
72
157
  ];
73
158
  lines.push(`Next action: ${value.nextAction}`);
74
159
  lines.push(`Expected evidence: ${value.expectedEvidence}`);
160
+ if (value.evidenceReceipt !== undefined) {
161
+ const receipt = value.evidenceReceipt;
162
+ lines.push(`Runtime evidence: turn ${receipt.turnId}; ${receipt.completedTools} completed, ${receipt.successfulTools} successful, ${receipt.failedTools} failed; digest ${receipt.digest}`);
163
+ }
75
164
  lines.push('Resume from this exact next action. Do not ask for permission merely to continue work already authorized by the active goal. Ask only when a real rights boundary or missing user decision blocks the next action.');
76
165
  return lines.join('\n');
77
166
  }
78
167
 
79
168
  module.exports = {
169
+ advanceActionEvidenceReceipt,
80
170
  assertActionCheckpointRevision,
171
+ emptyActionEvidenceReceipt,
81
172
  normalizeActionCheckpoint,
82
173
  projectActionCheckpoint,
83
174
  };
package/blun.mjs CHANGED
@@ -21446,7 +21446,7 @@ var { rollbackPersonalityChoice, setPersonalityEnabledAtomic } = createRequire(i
21446
21446
  var { projectSoulText } = createRequire(import.meta.url)("./bin/soul-organization-policy.cjs");
21447
21447
  var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
21448
21448
  var { readProfilePersona, resolveSoulFile } = createRequire(import.meta.url)("./bin/profile-identity-resolution.cjs");
21449
- var { assertActionCheckpointRevision, normalizeActionCheckpoint, projectActionCheckpoint } = createRequire(import.meta.url)("./bin/cognitive-action-checkpoint.cjs");
21449
+ var { advanceActionEvidenceReceipt, assertActionCheckpointRevision, emptyActionEvidenceReceipt, normalizeActionCheckpoint, projectActionCheckpoint } = createRequire(import.meta.url)("./bin/cognitive-action-checkpoint.cjs");
21450
21450
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21451
21451
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
21452
21452
  const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
@@ -230188,7 +230188,7 @@ var init_goal$1 = __esmMin((() => {
230188
230188
  state.wallClockResumedAt = void 0;
230189
230189
  }
230190
230190
  if (record.budgetLimits !== void 0) state.budgetLimits = record.budgetLimits;
230191
- if (record.actionCheckpoint !== void 0) state.actionCheckpoint = normalizeActionCheckpoint(record.actionCheckpoint, { preserveUpdatedAt: true });
230191
+ if (record.actionCheckpoint !== void 0) state.actionCheckpoint = normalizeActionCheckpoint(record.actionCheckpoint, { preserveUpdatedAt: true, preserveRuntimeEvidence: true });
230192
230192
  if (status === void 0) {
230193
230193
  if (record.actionCheckpoint === void 0) return;
230194
230194
  this.agent.replayBuilder.push({
@@ -230333,7 +230333,9 @@ var init_goal$1 = __esmMin((() => {
230333
230333
  const state = this.requireState();
230334
230334
  if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot checkpoint a goal in status "${state.status}"`);
230335
230335
  assertActionCheckpointRevision(state.actionCheckpoint, input);
230336
- state.actionCheckpoint = normalizeActionCheckpoint(input);
230336
+ state.actionCheckpoint = normalizeActionCheckpoint(input, {
230337
+ runtimeEvidence: this.agent.turn.actionEvidenceReceiptForCurrentTurn()
230338
+ });
230337
230339
  this.persistState(state, { change: { kind: "progress", actor } });
230338
230340
  this.appendGoalUpdate({ actionCheckpoint: state.actionCheckpoint, actor });
230339
230341
  this.track("goal_checkpoint_updated", { actor, phase: state.actionCheckpoint.phase, revision: state.actionCheckpoint.revision });
@@ -245731,7 +245733,14 @@ var init_events$1 = __esmMin((() => {
245731
245733
  lastVerified: string(),
245732
245734
  nextAction: string(),
245733
245735
  expectedEvidence: string(),
245734
- updatedAt: string()
245736
+ updatedAt: string(),
245737
+ evidenceReceipt: object({
245738
+ turnId: number$1().int().min(0),
245739
+ completedTools: number$1().int().min(0),
245740
+ successfulTools: number$1().int().min(0),
245741
+ failedTools: number$1().int().min(0),
245742
+ digest: string()
245743
+ }).strict().optional()
245735
245744
  }).strict().optional()
245736
245745
  });
245737
245746
  object({ goal: goalSnapshotSchema.nullable() });
@@ -261592,6 +261601,7 @@ var init_turn = __esmMin((() => {
261592
261601
  cognitiveLifecycleUnavailable = false;
261593
261602
  cognitiveToolPolicyByCall = /* @__PURE__ */ new Map();
261594
261603
  cognitiveToolBatchesByTurn = /* @__PURE__ */ new Map();
261604
+ cognitiveActionEvidenceByTurn = /* @__PURE__ */ new Map();
261595
261605
  constructor(agent) {
261596
261606
  this.agent = agent;
261597
261607
  }
@@ -261638,6 +261648,11 @@ var init_turn = __esmMin((() => {
261638
261648
  return;
261639
261649
  }
261640
261650
  this.cognitiveToolPolicyByCall.delete(input.toolCallId);
261651
+ const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId);
261652
+ this.cognitiveActionEvidenceByTurn.set(input.turnId, advanceActionEvidenceReceipt(currentEvidence, {
261653
+ ...input,
261654
+ decision: policy.decision
261655
+ }));
261641
261656
  const entries = this.cognitiveToolBatchesByTurn.get(input.turnId) ?? [];
261642
261657
  entries.push({
261643
261658
  toolCallId: input.toolCallId,
@@ -261662,6 +261677,10 @@ var init_turn = __esmMin((() => {
261662
261677
  this.recordCognitiveStage("recordToolPolicy", policy);
261663
261678
  }
261664
261679
  }
261680
+ actionEvidenceReceiptForCurrentTurn() {
261681
+ const turnId = this.currentId;
261682
+ return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId);
261683
+ }
261665
261684
  projectCognitiveState(turnId, input) {
261666
261685
  try {
261667
261686
  const focusScopes = cognitiveFocusScopesForTurn(input);
@@ -262159,6 +262178,7 @@ var init_turn = __esmMin((() => {
262159
262178
  this.telemetryModeByTurn.delete(turnId);
262160
262179
  this.currentStepByTurn.delete(turnId);
262161
262180
  this.interruptedTelemetryTurnIds.delete(turnId);
262181
+ this.cognitiveActionEvidenceByTurn.delete(turnId);
262162
262182
  this.stepFailureByTurn.delete(turnId);
262163
262183
  await this.agent.records.flush();
262164
262184
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.368",
3
+ "version": "9.1.369",
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": {