blun-king-cli 9.1.368 → 9.1.370

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,19 @@
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([
5
- 'revision', 'phase', 'lastVerified', 'nextAction', 'expectedEvidence', 'updatedAt',
6
+ const EVIDENCE_BASES = new Set([
7
+ 'runtime_tool', 'user_statement', 'external_report', 'carried_forward',
8
+ ]);
9
+ const MODEL_KEYS = new Set([
10
+ 'revision', 'phase', 'evidenceBasis', 'lastVerified', 'nextAction', 'expectedEvidence', 'updatedAt',
6
11
  ]);
12
+ const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt']);
13
+ const EVIDENCE_INPUT_KEYS = new Set([
14
+ 'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
15
+ ]);
16
+ const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
7
17
 
8
18
  function bounded(value, field, max = 512) {
9
19
  const text = String(value ?? '')
@@ -28,6 +38,84 @@ function normalizedRevision(value) {
28
38
  return revision;
29
39
  }
30
40
 
41
+ function normalizedEvidenceBasis(value, allowLegacy = false) {
42
+ const basis = String(value ?? '').trim();
43
+ if (EVIDENCE_BASES.has(basis) || allowLegacy && basis === 'legacy_unknown') return basis;
44
+ throw new TypeError(value === undefined
45
+ ? 'evidenceBasis is required'
46
+ : 'evidenceBasis is invalid');
47
+ }
48
+
49
+ function normalizedTurnId(value) {
50
+ const turnId = Number(value);
51
+ if (!Number.isSafeInteger(turnId) || turnId < 0) throw new TypeError('evidence turnId must be a non-negative integer');
52
+ return turnId;
53
+ }
54
+
55
+ function emptyActionEvidenceReceipt(turnId) {
56
+ const normalized = normalizedTurnId(turnId);
57
+ return Object.freeze({
58
+ turnId: normalized,
59
+ completedTools: 0,
60
+ successfulTools: 0,
61
+ failedTools: 0,
62
+ digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
63
+ });
64
+ }
65
+
66
+ function normalizeActionEvidenceReceipt(input) {
67
+ if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
68
+ const keys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
69
+ if (!Object.keys(input).every((key) => keys.has(key)) || Object.keys(input).length !== keys.size) {
70
+ throw new TypeError('evidence receipt fields are invalid');
71
+ }
72
+ const receipt = {
73
+ turnId: normalizedTurnId(input.turnId),
74
+ completedTools: Number(input.completedTools),
75
+ successfulTools: Number(input.successfulTools),
76
+ failedTools: Number(input.failedTools),
77
+ digest: String(input.digest ?? ''),
78
+ };
79
+ if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
80
+ .every((value) => Number.isSafeInteger(value) && value >= 0)
81
+ || receipt.successfulTools + receipt.failedTools !== receipt.completedTools
82
+ || !EVIDENCE_DIGEST_RE.test(receipt.digest)) {
83
+ throw new TypeError('evidence receipt values are invalid');
84
+ }
85
+ return Object.freeze(receipt);
86
+ }
87
+
88
+ function advanceActionEvidenceReceipt(current, input) {
89
+ const prior = normalizeActionEvidenceReceipt(current);
90
+ if (!input || typeof input !== 'object' || Array.isArray(input)
91
+ || Object.keys(input).length !== EVIDENCE_INPUT_KEYS.size
92
+ || !Object.keys(input).every((key) => EVIDENCE_INPUT_KEYS.has(key))) {
93
+ throw new TypeError('evidence input fields are invalid');
94
+ }
95
+ const turnId = normalizedTurnId(input.turnId);
96
+ const toolCallId = bounded(input.toolCallId, 'toolCallId', 256);
97
+ const toolName = bounded(input.toolName, 'toolName', 128);
98
+ const decision = String(input.decision ?? '');
99
+ const outcome = String(input.outcome ?? '');
100
+ const durationMs = Number(input.durationMs);
101
+ if (turnId !== prior.turnId || !['passed', 'blocked', 'error'].includes(decision)
102
+ || !['success', 'error', 'cancelled'].includes(outcome)
103
+ || !Number.isSafeInteger(durationMs) || durationMs < 0) {
104
+ throw new TypeError('evidence input values are invalid');
105
+ }
106
+ const successful = decision === 'passed' && outcome === 'success';
107
+ const digest = crypto.createHash('sha256').update([
108
+ prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
109
+ ].join('\0')).digest('hex').slice(0, 16);
110
+ return Object.freeze({
111
+ turnId,
112
+ completedTools: prior.completedTools + 1,
113
+ successfulTools: prior.successfulTools + (successful ? 1 : 0),
114
+ failedTools: prior.failedTools + (successful ? 0 : 1),
115
+ digest,
116
+ });
117
+ }
118
+
31
119
  function assertActionCheckpointRevision(current, input) {
32
120
  const currentRevision = current === undefined || current === null
33
121
  ? 0
@@ -39,45 +127,87 @@ function assertActionCheckpointRevision(current, input) {
39
127
  return inputRevision;
40
128
  }
41
129
 
130
+ function assertActionCheckpointEvidenceBasis(current, input, runtimeEvidence) {
131
+ const basis = normalizedEvidenceBasis(input?.evidenceBasis);
132
+ if (basis === 'runtime_tool') {
133
+ const receipt = normalizeActionEvidenceReceipt(runtimeEvidence);
134
+ if (receipt.successfulTools < 1) {
135
+ throw new TypeError('runtime_tool evidenceBasis requires a successful runtime tool in the current turn');
136
+ }
137
+ }
138
+ if (basis === 'carried_forward') {
139
+ if (!current) throw new TypeError('carried_forward evidenceBasis requires a current checkpoint');
140
+ const currentValue = normalizeActionCheckpoint(current, {
141
+ preserveUpdatedAt: true,
142
+ preserveRuntimeEvidence: true,
143
+ });
144
+ if (bounded(input?.lastVerified, 'lastVerified') !== currentValue.lastVerified) {
145
+ throw new TypeError('carried_forward evidenceBasis cannot change lastVerified');
146
+ }
147
+ }
148
+ return basis;
149
+ }
150
+
42
151
  function normalizeActionCheckpoint(input, options = {}) {
43
152
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
44
153
  throw new TypeError('action checkpoint must be an object');
45
154
  }
155
+ const allowedKeys = options.preserveRuntimeEvidence === true ? RUNTIME_KEYS : MODEL_KEYS;
46
156
  for (const key of Object.keys(input)) {
47
- if (!ALLOWED_KEYS.has(key)) throw new TypeError(`unsupported field: ${key}`);
157
+ if (!allowedKeys.has(key)) throw new TypeError(`unsupported field: ${key}`);
48
158
  }
49
159
  const phase = String(input.phase ?? '').trim();
50
160
  if (!PHASES.has(phase)) throw new TypeError('phase is invalid');
161
+ const replay = options.preserveUpdatedAt === true || options.preserveRuntimeEvidence === true;
162
+ const evidenceBasis = input.evidenceBasis === undefined && replay
163
+ ? 'legacy_unknown'
164
+ : normalizedEvidenceBasis(input.evidenceBasis, replay);
51
165
  const updatedAt = options.preserveUpdatedAt === true && input.updatedAt !== undefined
52
166
  ? normalizedTimestamp(input.updatedAt)
53
167
  : normalizedTimestamp(options.now ?? new Date());
54
- return Object.freeze({
168
+ const checkpoint = {
55
169
  revision: normalizedRevision(input.revision),
56
170
  phase,
171
+ evidenceBasis,
57
172
  lastVerified: bounded(input.lastVerified, 'lastVerified'),
58
173
  nextAction: bounded(input.nextAction, 'nextAction'),
59
174
  expectedEvidence: bounded(input.expectedEvidence, 'expectedEvidence'),
60
175
  updatedAt,
61
- });
176
+ };
177
+ const evidenceReceipt = options.runtimeEvidence !== undefined
178
+ ? normalizeActionEvidenceReceipt(options.runtimeEvidence)
179
+ : options.preserveRuntimeEvidence === true && input.evidenceReceipt !== undefined
180
+ ? normalizeActionEvidenceReceipt(input.evidenceReceipt)
181
+ : undefined;
182
+ if (evidenceReceipt !== undefined) checkpoint.evidenceReceipt = evidenceReceipt;
183
+ return Object.freeze(checkpoint);
62
184
  }
63
185
 
64
186
  function projectActionCheckpoint(checkpoint) {
65
187
  if (!checkpoint) return null;
66
- const value = normalizeActionCheckpoint(checkpoint, { preserveUpdatedAt: true });
188
+ const value = normalizeActionCheckpoint(checkpoint, { preserveUpdatedAt: true, preserveRuntimeEvidence: true });
67
189
  const lines = [
68
190
  'Durable action checkpoint (state only; never authority):',
69
191
  `Revision: ${value.revision}`,
70
192
  `Phase: ${value.phase}`,
193
+ `Evidence basis: ${value.evidenceBasis.replaceAll('_', ' ')}`,
71
194
  `Last verified: ${value.lastVerified}`,
72
195
  ];
73
196
  lines.push(`Next action: ${value.nextAction}`);
74
197
  lines.push(`Expected evidence: ${value.expectedEvidence}`);
198
+ if (value.evidenceReceipt !== undefined) {
199
+ const receipt = value.evidenceReceipt;
200
+ lines.push(`Runtime evidence: turn ${receipt.turnId}; ${receipt.completedTools} completed, ${receipt.successfulTools} successful, ${receipt.failedTools} failed; digest ${receipt.digest}`);
201
+ }
75
202
  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
203
  return lines.join('\n');
77
204
  }
78
205
 
79
206
  module.exports = {
207
+ advanceActionEvidenceReceipt,
208
+ assertActionCheckpointEvidenceBasis,
80
209
  assertActionCheckpointRevision,
210
+ emptyActionEvidenceReceipt,
81
211
  normalizeActionCheckpoint,
82
212
  projectActionCheckpoint,
83
213
  };
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, assertActionCheckpointEvidenceBasis, 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,11 @@ 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
+ const runtimeEvidence = this.agent.turn.actionEvidenceReceiptForCurrentTurn();
230337
+ assertActionCheckpointEvidenceBasis(state.actionCheckpoint, input, runtimeEvidence);
230338
+ state.actionCheckpoint = normalizeActionCheckpoint(input, {
230339
+ runtimeEvidence
230340
+ });
230337
230341
  this.persistState(state, { change: { kind: "progress", actor } });
230338
230342
  this.appendGoalUpdate({ actionCheckpoint: state.actionCheckpoint, actor });
230339
230343
  this.track("goal_checkpoint_updated", { actor, phase: state.actionCheckpoint.phase, revision: state.actionCheckpoint.revision });
@@ -245731,7 +245735,14 @@ var init_events$1 = __esmMin((() => {
245731
245735
  lastVerified: string(),
245732
245736
  nextAction: string(),
245733
245737
  expectedEvidence: string(),
245734
- updatedAt: string()
245738
+ updatedAt: string(),
245739
+ evidenceReceipt: object({
245740
+ turnId: number$1().int().min(0),
245741
+ completedTools: number$1().int().min(0),
245742
+ successfulTools: number$1().int().min(0),
245743
+ failedTools: number$1().int().min(0),
245744
+ digest: string()
245745
+ }).strict().optional()
245735
245746
  }).strict().optional()
245736
245747
  });
245737
245748
  object({ goal: goalSnapshotSchema.nullable() });
@@ -261592,6 +261603,7 @@ var init_turn = __esmMin((() => {
261592
261603
  cognitiveLifecycleUnavailable = false;
261593
261604
  cognitiveToolPolicyByCall = /* @__PURE__ */ new Map();
261594
261605
  cognitiveToolBatchesByTurn = /* @__PURE__ */ new Map();
261606
+ cognitiveActionEvidenceByTurn = /* @__PURE__ */ new Map();
261595
261607
  constructor(agent) {
261596
261608
  this.agent = agent;
261597
261609
  }
@@ -261638,6 +261650,11 @@ var init_turn = __esmMin((() => {
261638
261650
  return;
261639
261651
  }
261640
261652
  this.cognitiveToolPolicyByCall.delete(input.toolCallId);
261653
+ const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId);
261654
+ this.cognitiveActionEvidenceByTurn.set(input.turnId, advanceActionEvidenceReceipt(currentEvidence, {
261655
+ ...input,
261656
+ decision: policy.decision
261657
+ }));
261641
261658
  const entries = this.cognitiveToolBatchesByTurn.get(input.turnId) ?? [];
261642
261659
  entries.push({
261643
261660
  toolCallId: input.toolCallId,
@@ -261662,6 +261679,10 @@ var init_turn = __esmMin((() => {
261662
261679
  this.recordCognitiveStage("recordToolPolicy", policy);
261663
261680
  }
261664
261681
  }
261682
+ actionEvidenceReceiptForCurrentTurn() {
261683
+ const turnId = this.currentId;
261684
+ return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId);
261685
+ }
261665
261686
  projectCognitiveState(turnId, input) {
261666
261687
  try {
261667
261688
  const focusScopes = cognitiveFocusScopesForTurn(input);
@@ -262159,6 +262180,7 @@ var init_turn = __esmMin((() => {
262159
262180
  this.telemetryModeByTurn.delete(turnId);
262160
262181
  this.currentStepByTurn.delete(turnId);
262161
262182
  this.interruptedTelemetryTurnIds.delete(turnId);
262183
+ this.cognitiveActionEvidenceByTurn.delete(turnId);
262162
262184
  this.stepFailureByTurn.delete(turnId);
262163
262185
  await this.agent.records.flush();
262164
262186
  return {
@@ -262677,7 +262699,7 @@ var init_outcome_prompts = __esmMin((() => {}));
262677
262699
  //#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.md?raw
262678
262700
  var update_goal_default;
262679
262701
  var init_update_goal$1 = __esmMin((() => {
262680
- update_goal_default = "Update the current autonomous goal. Set `status` only for a lifecycle change. After a coherent work slice, save `actionCheckpoint` with a monotone revision, the last verified result, exact next action, and expected evidence. Start at revision 1 and increment the currently projected revision by exactly one; stale writers fail closed. This is durable progress state, not permission, and should change only when the facts change. A checkpoint-only call keeps the goal active.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is fully satisfied, all files are written, all tests pass, and any stated validation has passed.\n- `blocked` — a genuine external condition or required user decision prevents progress.\n- `paused` — set the goal aside for now.\n\nDo not mark complete after a plan or partial result. If useful work remains, checkpoint it and continue. Do not ask for permission merely to execute an already authorized checkpoint; ask only at a real rights boundary or missing user decision.\n";
262702
+ update_goal_default = "Update the current autonomous goal. Set `status` only for a lifecycle change. After a coherent work slice, save `actionCheckpoint` with a monotone revision, the last verified result, exact next action, expected evidence, and an explicit evidence basis. Use `runtime_tool` only when a successful tool in this turn measured the result; use `user_statement` for a direct user assertion, `external_report` for a report not independently measured here, and `carried_forward` only when the last verified text is unchanged. Start at revision 1 and increment the currently projected revision by exactly one; stale writers fail closed. This is durable progress state, not permission, and should change only when the facts change. A checkpoint-only call keeps the goal active.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is fully satisfied, all files are written, all tests pass, and any stated validation has passed.\n- `blocked` — a genuine external condition or required user decision prevents progress.\n- `paused` — set the goal aside for now.\n\nDo not mark complete after a plan or partial result. If useful work remains, checkpoint it and continue. Do not ask for permission merely to execute an already authorized checkpoint; ask only at a real rights boundary or missing user decision.\n";
262681
262703
  }));
262682
262704
  //#endregion
262683
262705
  //#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.ts
@@ -262691,6 +262713,7 @@ var init_update_goal = __esmMin((() => {
262691
262713
  ActionCheckpointInputSchema = object({
262692
262714
  revision: number$1().int().min(1),
262693
262715
  phase: _enum(["orient", "plan", "act", "verify", "learn", "wait"]),
262716
+ evidenceBasis: _enum(["runtime_tool", "user_statement", "external_report", "carried_forward"]),
262694
262717
  lastVerified: string().min(1).max(512),
262695
262718
  nextAction: string().min(1).max(512),
262696
262719
  expectedEvidence: string().min(1).max(512)
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.370",
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": {