blun-king-cli 9.1.446 → 9.1.447

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.
@@ -18,13 +18,14 @@ const MODEL_KEYS = new Set([
18
18
  const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt']);
19
19
  const PROBLEM_FRAME_KEYS = new Set([
20
20
  'successCriterion', 'missingKnowledge', 'candidateActions', 'selectedAction',
21
- 'selectionReason', 'supportChoice', 'risk', 'reversibility',
21
+ 'selectionReason', 'supportChoice', 'risk', 'reversibility', 'decisionBasis',
22
22
  ]);
23
23
  const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
24
24
  const EVIDENCE_INPUT_KEYS = new Set([
25
25
  'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
26
26
  ]);
27
27
  const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
28
+ const DECISION_BASIS_RE = /^[a-f0-9]{16}$/u;
28
29
  const ISO_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}(?::?\d{2})?)$/u;
29
30
 
30
31
  function bounded(value, field, max = 512) {
@@ -60,6 +61,15 @@ function boundedList(value, field) {
60
61
  return Object.freeze(value.map((item, index) => bounded(item, `${field}[${index}]`, 256)));
61
62
  }
62
63
 
64
+ function normalizedDecisionBasis(value) {
65
+ if (!Array.isArray(value) || value.length < 1 || value.length > 5
66
+ || value.some((item) => typeof item !== 'string' || !DECISION_BASIS_RE.test(item))
67
+ || new Set(value).size !== value.length) {
68
+ throw new TypeError('decisionBasis must contain between 1 and 5 unique evidence refs');
69
+ }
70
+ return Object.freeze([...value]);
71
+ }
72
+
63
73
  function normalizeProblemFrame(input) {
64
74
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
65
75
  throw new TypeError('problemFrame must be an object');
@@ -73,7 +83,7 @@ function normalizeProblemFrame(input) {
73
83
  if (!candidateActions.includes(selectedAction)) {
74
84
  throw new TypeError('selectedAction must match a candidateAction');
75
85
  }
76
- return Object.freeze({
86
+ const frame = {
77
87
  successCriterion: bounded(input.successCriterion, 'successCriterion'),
78
88
  missingKnowledge,
79
89
  candidateActions,
@@ -82,7 +92,9 @@ function normalizeProblemFrame(input) {
82
92
  supportChoice: bounded(input.supportChoice, 'supportChoice', 256),
83
93
  risk: bounded(input.risk, 'risk'),
84
94
  reversibility: bounded(input.reversibility, 'reversibility'),
85
- });
95
+ };
96
+ if (input.decisionBasis !== undefined) frame.decisionBasis = normalizedDecisionBasis(input.decisionBasis);
97
+ return Object.freeze(frame);
86
98
  }
87
99
 
88
100
  function normalizeNextTrigger(input, phase) {
@@ -312,6 +324,7 @@ function projectActionCheckpoint(checkpoint) {
312
324
  lines.push(`Support choice: ${frame.supportChoice}`);
313
325
  lines.push(`Risk: ${frame.risk}`);
314
326
  lines.push(`Reversibility: ${frame.reversibility}`);
327
+ if (frame.decisionBasis !== undefined) lines.push(`Decision basis: ${frame.decisionBasis.join(' | ')}`);
315
328
  }
316
329
  if (value.evidenceReceipt !== undefined) {
317
330
  const receipt = value.evidenceReceipt;
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const crypto = require('node:crypto');
3
4
  const { resolveCognitiveEvidenceGroup } = require('./cognitive-effective-view.cjs');
4
5
  const {
5
6
  buildCognitiveSalienceIndex,
@@ -55,15 +56,19 @@ function normalizeScopes(value) {
55
56
  return scopes.every(Boolean) ? [...new Set(scopes)] : null;
56
57
  }
57
58
 
58
- function buildCognitiveFocusSelection(state, {
59
- focusScopes, maxItems = 6, maxChars = 900, currentSessionId,
60
- } = {}) {
61
- const scopes = normalizeScopes(focusScopes);
62
- if (scopes === null || !Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 8
63
- || !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600
64
- || !Array.isArray(state?.observations)) return null;
65
- if (scopes.length === 0) return null;
59
+ function cognitiveEvidenceRef(item) {
60
+ const scope = clean(item?.scope, 128);
61
+ const domain = String(item?.domain ?? '');
62
+ const key = clean(item?.key, 128);
63
+ const observationId = clean(item?.observationId ?? item?.observation_id, 128);
64
+ if (!scope || scope === 'runtime' || !FOCUS_DOMAINS.has(domain) || !key || !observationId) return '';
65
+ return crypto.createHash('sha256')
66
+ .update([scope, domain, key, observationId].join('\0'))
67
+ .digest('hex').slice(0, 16);
68
+ }
66
69
 
70
+ function normalizedFocusGroups(state) {
71
+ if (!Array.isArray(state?.observations)) return null;
67
72
  const groups = new Map();
68
73
  state.observations.forEach((item, index) => {
69
74
  const domain = String(item?.domain ?? '');
@@ -80,7 +85,7 @@ function buildCognitiveFocusSelection(state, {
80
85
  ? 'legacy_unknown' : String(item.epistemic_state);
81
86
  const occurredAt = Date.parse(String(item?.occurred_at ?? ''));
82
87
  if (!FOCUS_DOMAINS.has(domain) || !key || !value || !scope || scope === 'runtime'
83
- || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
88
+ || !observationId || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
84
89
  || !EPISTEMIC_MARKERS.has(epistemicState)
85
90
  || !Number.isFinite(occurredAt)) return;
86
91
  const groupKey = `${scope}\0${domain}\0${key}`;
@@ -91,11 +96,66 @@ function buildCognitiveFocusSelection(state, {
91
96
  });
92
97
  groups.set(groupKey, existing);
93
98
  });
99
+ for (const entries of groups.values()) {
100
+ entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
101
+ }
102
+ return groups;
103
+ }
104
+
105
+ function currentEvidenceRefs(entries, effective) {
106
+ if (!effective || effective.conflict) return [];
107
+ const withdrawals = entries.filter((item) => Boolean(item.withdraws));
108
+ const deletionCutoff = withdrawals.length > 0
109
+ ? Math.max(...withdrawals.map((item) => item.occurredAt)) : Number.NEGATIVE_INFINITY;
110
+ const evidence = entries.filter((item) => !item.withdraws && item.occurredAt > deletionCutoff);
111
+ const superseded = new Set(evidence.map((item) => item.supersedes).filter(Boolean));
112
+ return evidence
113
+ .filter((item) => !superseded.has(item.observationId) && item.value === effective.value)
114
+ .map(cognitiveEvidenceRef)
115
+ .filter(Boolean);
116
+ }
117
+
118
+ function evaluateCognitiveDecisionBasis(state, refs) {
119
+ if (refs === undefined || refs === null) return null;
120
+ if (!Array.isArray(refs) || refs.length < 1 || refs.length > 5
121
+ || refs.some((ref) => !/^[a-f0-9]{16}$/u.test(String(ref)))
122
+ || new Set(refs).size !== refs.length) {
123
+ return { status: 'unknown', currentRefs: [], staleRefs: [], unknownRefs: [] };
124
+ }
125
+ const groups = normalizedFocusGroups(state);
126
+ if (groups === null) return { status: 'unknown', currentRefs: [], staleRefs: [], unknownRefs: [...refs] };
127
+ const historical = new Set();
128
+ const current = new Set();
129
+ for (const entries of groups.values()) {
130
+ entries.map(cognitiveEvidenceRef).filter(Boolean).forEach((ref) => historical.add(ref));
131
+ const effective = resolveCognitiveEvidenceGroup(entries);
132
+ currentEvidenceRefs(entries, effective).forEach((ref) => current.add(ref));
133
+ }
134
+ const currentRefs = refs.filter((ref) => current.has(ref));
135
+ const staleRefs = refs.filter((ref) => !current.has(ref) && historical.has(ref));
136
+ const unknownRefs = refs.filter((ref) => !current.has(ref) && !historical.has(ref));
137
+ return {
138
+ status: staleRefs.length > 0 ? 'stale' : unknownRefs.length > 0 ? 'unknown' : 'current',
139
+ currentRefs,
140
+ staleRefs,
141
+ unknownRefs,
142
+ };
143
+ }
144
+
145
+ function buildCognitiveFocusSelection(state, {
146
+ focusScopes, maxItems = 6, maxChars = 900, currentSessionId,
147
+ } = {}) {
148
+ const scopes = normalizeScopes(focusScopes);
149
+ if (scopes === null || !Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 8
150
+ || !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600) return null;
151
+ if (scopes.length === 0) return null;
152
+
153
+ const groups = normalizedFocusGroups(state);
154
+ if (groups === null) return null;
94
155
 
95
156
  const salience = buildCognitiveSalienceIndex(state, { currentSessionId });
96
157
  const ranked = [];
97
158
  for (const entries of groups.values()) {
98
- entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
99
159
  const latest = entries.at(-1);
100
160
  if (!scopes.includes(latest.scope)) continue;
101
161
  const effective = resolveCognitiveEvidenceGroup(entries);
@@ -117,7 +177,7 @@ function buildCognitiveFocusSelection(state, {
117
177
  if (item.conflict) return `- Conflict: ${item.key} has incompatible evidence; verify before relying on it.`;
118
178
  const marker = item.corrected ? ' [corrected]'
119
179
  : item.contested ? ' [contested; stronger evidence]' : item.revised ? ' [revised]' : '';
120
- return `- ${LABELS.get(item.domain)}: ${item.value}${EPISTEMIC_MARKERS.get(item.epistemicState)}${marker}`;
180
+ return `- ${LABELS.get(item.domain)}: ${item.value}${EPISTEMIC_MARKERS.get(item.epistemicState)}${marker} [ref:${cognitiveEvidenceRef(item)}]`;
121
181
  });
122
182
  while ([`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n').length > maxChars
123
183
  && lines.length > 0) lines.pop();
@@ -125,7 +185,12 @@ function buildCognitiveFocusSelection(state, {
125
185
  const kept = selected.slice(0, lines.length);
126
186
  return {
127
187
  text: [`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n'),
128
- selected: kept.map((item) => ({ scope: item.scope, domain: item.domain, key: item.key })),
188
+ selected: kept.map((item) => ({
189
+ scope: item.scope,
190
+ domain: item.domain,
191
+ key: item.key,
192
+ ...(item.conflict ? {} : { evidenceRef: cognitiveEvidenceRef(item) }),
193
+ })),
129
194
  };
130
195
  }
131
196
 
@@ -133,4 +198,9 @@ function buildCognitiveFocusProjection(state, options) {
133
198
  return buildCognitiveFocusSelection(state, options)?.text ?? null;
134
199
  }
135
200
 
136
- module.exports = { buildCognitiveFocusProjection, buildCognitiveFocusSelection };
201
+ module.exports = {
202
+ buildCognitiveFocusProjection,
203
+ buildCognitiveFocusSelection,
204
+ cognitiveEvidenceRef,
205
+ evaluateCognitiveDecisionBasis,
206
+ };
@@ -6,7 +6,10 @@ const path = require('node:path');
6
6
  const { openCognitiveMemoryAdapter } = require('./cognitive-memory-adapter.cjs');
7
7
  const { loadConfiguredCognitiveMemoryAdapter } = require('./cognitive-memory-provider.cjs');
8
8
  const { buildCognitiveContextProjection } = require('./cognitive-context-projection.cjs');
9
- const { buildCognitiveFocusSelection } = require('./cognitive-focus-projection.cjs');
9
+ const {
10
+ buildCognitiveFocusSelection,
11
+ evaluateCognitiveDecisionBasis,
12
+ } = require('./cognitive-focus-projection.cjs');
10
13
  const { cognitiveEntityKey, focusAccessObservation } = require('./cognitive-salience-policy.cjs');
11
14
  const { authorizeAttentionCandidate } = require('./cognitive-attention-runtime.cjs');
12
15
 
@@ -490,6 +493,11 @@ function createCognitiveTurnLifecycle({
490
493
  return [focus?.text, continuity].filter(Boolean).join('\n\n') || null;
491
494
  }
492
495
 
496
+ function evaluateDecisionBasis(refs) {
497
+ const state = store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess);
498
+ return evaluateCognitiveDecisionBasis(state, refs);
499
+ }
500
+
493
501
  function authorizeAttention(input) {
494
502
  const candidate = input?.candidate;
495
503
  const authorization = input?.authorization;
@@ -552,6 +560,7 @@ function createCognitiveTurnLifecycle({
552
560
  withdrawFocusObservation,
553
561
  authorizeAttention,
554
562
  memoryStatus,
563
+ evaluateDecisionBasis,
555
564
  projectForTurn,
556
565
  endTurn,
557
566
  read: () => store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess),
@@ -23,7 +23,7 @@ function bounded(value, max = 512) {
23
23
  return text.length <= max ? text : `${text.slice(0, max - 3).trimEnd()}...`;
24
24
  }
25
25
 
26
- function buildCognitiveWorkFocus(goal) {
26
+ function buildCognitiveWorkFocus(goal, { decisionFreshness } = {}) {
27
27
  if (!goal || typeof goal !== 'object' || Array.isArray(goal)) return null;
28
28
  const goalId = String(goal.goalId ?? '').trim();
29
29
  const objective = bounded(goal.objective);
@@ -75,6 +75,11 @@ function buildCognitiveWorkFocus(goal) {
75
75
  const frameSupportChoice = bounded(problemFrame?.supportChoice, 256);
76
76
  const frameRisk = bounded(problemFrame?.risk);
77
77
  const frameReversibility = bounded(problemFrame?.reversibility);
78
+ const frameDecisionBasis = Array.isArray(problemFrame?.decisionBasis)
79
+ && problemFrame.decisionBasis.length >= 1 && problemFrame.decisionBasis.length <= 5
80
+ && problemFrame.decisionBasis.every((item) => /^[a-f0-9]{16}$/u.test(String(item)))
81
+ && new Set(problemFrame.decisionBasis).size === problemFrame.decisionBasis.length
82
+ ? [...problemFrame.decisionBasis] : [];
78
83
  const hasProblemFrame = problemFrame !== null && frameSuccessCriterion
79
84
  && frameMissingKnowledge.length > 0 && frameCandidates.length > 0
80
85
  && frameSelectedAction && frameSelectionReason && frameSupportChoice
@@ -83,8 +88,17 @@ function buildCognitiveWorkFocus(goal) {
83
88
  && nextAction && checkpointEvidence;
84
89
  const epistemicState = hasCheckpoint && EPISTEMIC_STATES.has(checkpointEpistemicState)
85
90
  ? checkpointEpistemicState : hasCheckpoint ? 'legacy_unknown' : 'verified';
86
- const knowledgeGapValue = hasProblemFrame ? frameMissingKnowledge.join(' | ') : 'none';
91
+ const decisionFreshnessStatus = frameDecisionBasis.length > 0
92
+ && ['current', 'stale', 'unknown'].includes(decisionFreshness?.status)
93
+ ? decisionFreshness.status : frameDecisionBasis.length > 0 ? 'unknown' : '';
94
+ const staleDecision = decisionFreshnessStatus === 'stale' || decisionFreshnessStatus === 'unknown';
95
+ const replanKnowledgeGap = 'Re-evaluate the selected action against current durable evidence.';
96
+ const replanTrigger = 'Re-evaluate the selected action against the current durable evidence.';
97
+ const replanEvidence = 'A replacement action is bound to current durable evidence refs.';
98
+ const knowledgeGapValue = staleDecision ? `${replanKnowledgeGap} ${frameMissingKnowledge.join(' | ')}`
99
+ : hasProblemFrame ? frameMissingKnowledge.join(' | ') : 'none';
87
100
  const decisionThread = hasProblemFrame ? [
101
+ ...(staleDecision ? [`Decision basis ${decisionFreshnessStatus}; replan before executing the selected action.`] : []),
88
102
  `Goal is ${status}`,
89
103
  `Phase ${checkpointPhase}`,
90
104
  `Last verified: ${bounded(lastVerified, 80)}`,
@@ -112,6 +126,8 @@ function buildCognitiveWorkFocus(goal) {
112
126
  hasProblemFrame ? frameSupportChoice : '',
113
127
  hasProblemFrame ? frameRisk : '',
114
128
  hasProblemFrame ? frameReversibility : '',
129
+ hasProblemFrame ? frameDecisionBasis.join('\0') : '',
130
+ decisionFreshnessStatus,
115
131
  ].join('\0'))
116
132
  .digest('hex').slice(0, 40);
117
133
  const keyRoot = `goal:${goalId}`;
@@ -128,24 +144,34 @@ function buildCognitiveWorkFocus(goal) {
128
144
  `Next action: ${nextAction}`,
129
145
  ].join(' ')
130
146
  : `Goal is ${status}.`),
131
- confidence: 1, epistemicState, scope: focusScope,
147
+ confidence: 1,
148
+ epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
149
+ scope: focusScope,
132
150
  },
133
151
  {
134
152
  domain: 'knowledge_gap', key: `${keyRoot}:knowledge-gaps`,
135
153
  value: knowledgeGapValue,
136
154
  confidence: 1,
137
- epistemicState: hasProblemFrame ? 'unknown' : 'verified',
155
+ epistemicState: hasProblemFrame || staleDecision ? 'unknown' : 'verified',
138
156
  scope: focusScope,
139
157
  },
140
158
  {
141
159
  domain: 'next_trigger', key: `${keyRoot}:next`,
142
- value: status === 'active' && hasCheckpoint ? triggerValue : NEXT_TRIGGER.get(status),
143
- confidence: 1, epistemicState, scope: focusScope,
160
+ value: status === 'active' && hasCheckpoint
161
+ ? staleDecision ? replanTrigger : triggerValue
162
+ : NEXT_TRIGGER.get(status),
163
+ confidence: 1,
164
+ epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
165
+ scope: focusScope,
144
166
  },
145
167
  {
146
168
  domain: 'expected_evidence', key: `${keyRoot}:evidence`,
147
- value: status === 'active' && hasCheckpoint ? checkpointEvidence : completionCriterion,
148
- confidence: 1, epistemicState, scope: focusScope,
169
+ value: status === 'active' && hasCheckpoint
170
+ ? staleDecision ? replanEvidence : checkpointEvidence
171
+ : completionCriterion,
172
+ confidence: 1,
173
+ epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
174
+ scope: focusScope,
149
175
  },
150
176
  ],
151
177
  };
package/blun.mjs CHANGED
@@ -245806,7 +245806,8 @@ var init_events$1 = __esmMin((() => {
245806
245806
  selectionReason: string(),
245807
245807
  supportChoice: string(),
245808
245808
  risk: string(),
245809
- reversibility: string()
245809
+ reversibility: string(),
245810
+ decisionBasis: array(string()).optional()
245810
245811
  }).strict().optional(),
245811
245812
  updatedAt: string(),
245812
245813
  evidenceReceipt: object({
@@ -260306,6 +260307,7 @@ bytesWritten: number$1().int().nonnegative() });
260306
260307
  var create_goal_default;
260307
260308
  var init_create_goal$1 = __esmMin((() => {
260308
260309
  create_goal_default = "Create a durable, structured goal that the runtime will pursue across multiple turns.\n\nCall `CreateGoal` when:\n\n- the user explicitly asks you to start a goal or work autonomously toward an outcome,\n- an authenticated user assigns a non-trivial multi-step outcome with a checkable end state under an existing instruction to continue autonomously, or\n- a host goal-intake prompt asks you to create one.\n\nDo NOT create a goal for greetings, ordinary questions, one-step requests, or vague requests that lack a\nverifiable completion condition. A goal needs a checkable end state.\n\nWhen the request is vague, ask the user for the missing completion criterion before creating\nthe goal. If the user clearly insists after you warn them that the wording is vague or risky,\nrespect that and create the goal.\n\nInclude a `completionCriterion` when the user provides one, or when it can be stated without\ninventing new requirements. Keep `objective` concise; reference long task descriptions by file\npath rather than pasting them. Start every created goal with revision 1 and a complete `problemFrame`\ninside `actionCheckpoint`, so the success criterion, missing knowledge, candidate actions, chosen\naction, support choice, risk, reversibility, next action, expected evidence, and exact next trigger survive interruption.\nThis frame is descriptive state only and never grants permission.\n\nCreating a goal fails if one already exists, so use `replace: true` only when the user explicitly\nwants to abandon the current goal and start a new one.\n";
260310
+ create_goal_default += "\nBind each selected action to the projected durable facts or assumptions it relies on by copying their explicit refs into `decisionBasis`. A stale or unknown decision basis requires replanning before execution.\n";
260309
260311
  }));
260310
260312
  //#endregion
260311
260313
  //#region ../../packages/agent-core/src/tools/builtin/goal/serialize.ts
@@ -260333,7 +260335,8 @@ function createProblemFrameInputSchema() {
260333
260335
  selectionReason: string().min(1).max(512),
260334
260336
  supportChoice: string().min(1).max(256),
260335
260337
  risk: string().min(1).max(512),
260336
- reversibility: string().min(1).max(512)
260338
+ reversibility: string().min(1).max(512),
260339
+ decisionBasis: array(string().regex(/^[a-f0-9]{16}$/u)).min(1).max(5).optional()
260337
260340
  }).strict();
260338
260341
  }
260339
260342
  function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFrame = false) {
@@ -261835,7 +261838,9 @@ var init_turn = __esmMin((() => {
261835
261838
  try {
261836
261839
  const focusScopes = cognitiveFocusScopesForTurn(input);
261837
261840
  const lifecycle = this.getCognitiveLifecycle();
261838
- const workFocus = buildCognitiveWorkFocus(this.agent.goal.getGoal().goal);
261841
+ const goal = this.agent.goal.getGoal().goal;
261842
+ const decisionFreshness = lifecycle?.evaluateDecisionBasis(goal?.actionCheckpoint?.problemFrame?.decisionBasis) ?? null;
261843
+ const workFocus = buildCognitiveWorkFocus(goal, { decisionFreshness });
261839
261844
  if (workFocus !== null) {
261840
261845
  lifecycle?.recordFocusSnapshot({
261841
261846
  snapshotId: workFocus.snapshotId,
@@ -262863,7 +262868,7 @@ var init_outcome_prompts = __esmMin((() => {}));
262863
262868
  var update_goal_default;
262864
262869
  var init_update_goal$1 = __esmMin((() => {
262865
262870
  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, exact `nextTrigger`, and an explicit evidence basis. Persist the exact `nextTrigger` that releases `nextAction`: use `immediate` outside the `wait` phase; while waiting, name the external event, time, dependency, or user decision instead of pretending work can continue. A `time` trigger must include the exact ISO timestamp in `dueAt`; no other trigger kind may include `dueAt`. 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. Classify knowledge as `verified`, `credible_unverified`, `hypothesis`, `uncertain_memory`, `stale`, or `unknown`; never present a weaker state as verified, and preserve the state on carry-forward. 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. When the goal has a completion criterion, first save a `verify` checkpoint with `runtime_tool`, `verified`, and a successful runtime evidence receipt.\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";
262866
- update_goal_default += "\nFor a non-trivial or unfamiliar problem, preserve `problemFrame` with the success criterion, missing knowledge, bounded candidate actions, selected action and reason, support choice, risk, and reversibility. The selected action must match one candidate. Problem framing is descriptive state and never grants permission.\n";
262871
+ update_goal_default += "\nFor a non-trivial or unfamiliar problem, preserve `problemFrame` with the success criterion, missing knowledge, bounded candidate actions, selected action and reason, support choice, risk, and reversibility. The selected action must match one candidate. Bind each selected action to the projected durable facts or assumptions it relies on by copying their explicit refs into `decisionBasis`. A stale or unknown decision basis requires replanning before execution. Problem framing is descriptive state and never grants permission.\n";
262867
262872
  }));
262868
262873
  //#endregion
262869
262874
  //#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.446",
3
+ "version": "9.1.447",
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": {