blun-king-cli 9.1.448 → 9.1.450

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.
@@ -12,6 +12,7 @@ const EPISTEMIC_STATES = new Set([
12
12
  const TRIGGER_KINDS = new Set([
13
13
  'immediate', 'external_event', 'time', 'dependency', 'user_decision',
14
14
  ]);
15
+ const VERIFICATION_SUBJECTS = new Set(['result', 'verifier']);
15
16
  const MODEL_KEYS = new Set([
16
17
  'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'verificationProof', 'nextTrigger', 'problemFrame', 'updatedAt',
17
18
  ]);
@@ -22,6 +23,9 @@ const PROBLEM_FRAME_KEYS = new Set([
22
23
  ]);
23
24
  const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
24
25
  const EVIDENCE_INPUT_KEYS = new Set([
26
+ 'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs', 'toolArgs',
27
+ ]);
28
+ const REQUIRED_EVIDENCE_INPUT_KEYS = new Set([
25
29
  'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
26
30
  ]);
27
31
  const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
@@ -32,6 +36,14 @@ const ACTION_ONLY_TOOL_NAMES = new Set([
32
36
  'ExitPlanMode', 'GenerateImage', 'GenerateSpeech', 'GenerateVideo', 'LipSyncMedia',
33
37
  'MistakeRecord', 'SetGoalBudget', 'TaskStop', 'TaskUpdate', 'UpdateGoal', 'Write',
34
38
  ]);
39
+ const VERIFICATION_TOOL_NAMES = new Set([
40
+ 'codebasesearch', 'cronlist', 'fetchurl', 'getgoal', 'getmedia', 'glob', 'grep',
41
+ 'read', 'readmediafile', 'taskoutput', 'test', 'understandimage', 'understandvideo',
42
+ 'websearch',
43
+ ]);
44
+ const COMMAND_TOOL_NAMES = new Set(['bash', 'command', 'exec_command', 'shell']);
45
+ const MUTATING_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:cp|mv|rm|mkdir|touch|tee|install|scp|sftp|ssh)\b|(?:sed\s+-i\b)|(?:git\s+(?:add|commit|push|checkout|switch|reset|clean|merge|rebase)\b)|(?:Set-Content|Add-Content|Copy-Item|Move-Item|Remove-Item|New-Item|Start-Process)\b)/iu;
46
+ const VERIFICATION_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:node\s+--(?:test|check))|(?:node\s+[^\s;&|]*(?:check|test|verify|validate|lint|gate|probe)[^\s;&|]*\b)|(?:(?:npm|pnpm|yarn)\s+(?:test|(?:run\s+)?(?:test|lint|check|typecheck|build)))|(?:python(?:3)?\s+-m\s+pytest)|(?:pytest)|(?:go\s+test)|(?:cargo\s+test)|(?:dotnet\s+test)|(?:npx\s+)?(?:tsc|eslint|biome\s+check)|(?:git\s+(?:diff(?:\s+--check)?|fsck|status|rev-parse|show))|(?:sha(?:1|256|512)sum|shasum|cmp|diff|wc|rg|grep|cat|ls|stat)\b|(?:certutil\s+-hashfile)|(?:Get-FileHash|Get-Content|Get-Item|Test-Path|Compare-Object|Measure-Object)\b)/iu;
35
47
  const MAX_SUCCESSFUL_TOOL_DIGESTS = 32;
36
48
 
37
49
  function bounded(value, field, max = 512) {
@@ -159,10 +171,36 @@ function successfulToolDigest(toolName) {
159
171
  return crypto.createHash('sha256').update(`tool:${name}`).digest('hex').slice(0, 16);
160
172
  }
161
173
 
174
+ function successfulToolCallDigest(turnId, toolCallId, toolName) {
175
+ const turn = normalizedTurnId(turnId);
176
+ const callId = bounded(toolCallId, 'verificationProof toolCallId', 256);
177
+ const name = bounded(toolName, 'verificationProof toolName', 128);
178
+ return crypto.createHash('sha256')
179
+ .update(`turn:${turn}\0call:${callId}\0tool:${name}`)
180
+ .digest('hex')
181
+ .slice(0, 16);
182
+ }
183
+
162
184
  function isActionOnlyTool(toolName) {
163
185
  return ACTION_ONLY_TOOL_NAMES.has(String(toolName ?? '').trim());
164
186
  }
165
187
 
188
+ function verificationCommand(toolArgs) {
189
+ if (!toolArgs || typeof toolArgs !== 'object' || Array.isArray(toolArgs)) return '';
190
+ const command = toolArgs.command ?? toolArgs.cmd;
191
+ return typeof command === 'string' ? command.trim().replace(/\s+/gu, ' ') : '';
192
+ }
193
+
194
+ function isVerificationToolCall(toolName, toolArgs) {
195
+ const normalizedName = String(toolName ?? '').trim().toLowerCase();
196
+ if (VERIFICATION_TOOL_NAMES.has(normalizedName)) return true;
197
+ if (!COMMAND_TOOL_NAMES.has(normalizedName)) return false;
198
+ const command = verificationCommand(toolArgs);
199
+ return command.length > 0
200
+ && !MUTATING_COMMAND.test(command)
201
+ && VERIFICATION_COMMAND.test(command);
202
+ }
203
+
166
204
  function normalizeSuccessfulToolDigests(value) {
167
205
  if (value === undefined) return Object.freeze([]);
168
206
  if (!Array.isArray(value) || value.length > MAX_SUCCESSFUL_TOOL_DIGESTS
@@ -173,23 +211,85 @@ function normalizeSuccessfulToolDigests(value) {
173
211
  return Object.freeze([...value]);
174
212
  }
175
213
 
176
- function normalizeVerificationProof(input, evidenceReceipt) {
214
+ function normalizeExactVerificationCall(input, receipt, label) {
177
215
  if (!input || typeof input !== 'object' || Array.isArray(input)
178
- || Object.keys(input).length !== 2
216
+ || Object.keys(input).length !== 3
217
+ || !Object.hasOwn(input, 'toolCallId')
179
218
  || !Object.hasOwn(input, 'toolName')
180
219
  || !Object.hasOwn(input, 'claim')) {
181
- throw new TypeError('verificationProof fields are invalid');
220
+ throw new TypeError(`${label} fields are invalid`);
182
221
  }
183
- const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
184
- const claim = bounded(input.claim, 'verificationProof claim');
222
+ const toolCallId = bounded(input.toolCallId, `${label} toolCallId`, 256);
223
+ const toolName = bounded(input.toolName, `${label} toolName`, 128);
224
+ const claim = bounded(input.claim, `${label} claim`);
185
225
  if (isActionOnlyTool(toolName)) {
186
- throw new TypeError('action-only tool cannot serve as verification proof');
226
+ const purpose = label === 'verificationProof' ? 'verification proof' : label;
227
+ throw new TypeError(`action-only tool cannot serve as ${purpose}`);
187
228
  }
188
- const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
189
- if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
190
- throw new TypeError('verificationProof must name a successful current-turn tool');
229
+ const callDigest = successfulToolCallDigest(receipt.turnId, toolCallId, toolName);
230
+ if (!receipt.successfulToolCallDigests.includes(callDigest)) {
231
+ throw new TypeError(`${label} must match a successful current-turn verification call`);
191
232
  }
192
- return Object.freeze({ toolName, claim });
233
+ return Object.freeze({ toolCallId, toolName, claim });
234
+ }
235
+
236
+ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
237
+ const keys = input && typeof input === 'object' && !Array.isArray(input)
238
+ ? Object.keys(input)
239
+ : [];
240
+ const legacyName = options.allowLegacy === true
241
+ && keys.length === 2
242
+ && Object.hasOwn(input, 'toolName')
243
+ && Object.hasOwn(input, 'claim');
244
+ const legacyExact = options.allowLegacy === true
245
+ && keys.length === 3
246
+ && Object.hasOwn(input, 'toolCallId')
247
+ && Object.hasOwn(input, 'toolName')
248
+ && Object.hasOwn(input, 'claim');
249
+ const subject = String(input?.subject ?? '').trim();
250
+ const currentResult = subject === 'result'
251
+ && keys.length === 4
252
+ && Object.hasOwn(input, 'toolCallId')
253
+ && Object.hasOwn(input, 'toolName')
254
+ && Object.hasOwn(input, 'claim');
255
+ const currentVerifier = subject === 'verifier'
256
+ && (keys.length === 4 || keys.length === 5)
257
+ && Object.hasOwn(input, 'toolCallId')
258
+ && Object.hasOwn(input, 'toolName')
259
+ && Object.hasOwn(input, 'claim')
260
+ && keys.every((key) => ['subject', 'toolCallId', 'toolName', 'claim', 'sharpnessProof'].includes(key));
261
+ if (!legacyName && !legacyExact && !currentResult && !currentVerifier) {
262
+ throw new TypeError('verificationProof fields are invalid');
263
+ }
264
+ const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
265
+ if (legacyName) {
266
+ const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
267
+ const claim = bounded(input.claim, 'verificationProof claim');
268
+ if (isActionOnlyTool(toolName)) {
269
+ throw new TypeError('action-only tool cannot serve as verification proof');
270
+ }
271
+ if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
272
+ throw new TypeError('verificationProof must name a successful current-turn tool');
273
+ }
274
+ return Object.freeze({ toolName, claim });
275
+ }
276
+ const primary = normalizeExactVerificationCall(legacyExact ? input : {
277
+ toolCallId: input.toolCallId,
278
+ toolName: input.toolName,
279
+ claim: input.claim,
280
+ }, receipt, 'verificationProof');
281
+ if (legacyExact) return primary;
282
+ if (!VERIFICATION_SUBJECTS.has(subject)) throw new TypeError('verificationProof subject is invalid');
283
+ if (subject === 'result') return Object.freeze({ subject, ...primary });
284
+ if (input.sharpnessProof === undefined) {
285
+ throw new TypeError('verifier proof requires a sharpnessProof');
286
+ }
287
+ const sharpnessProof = normalizeExactVerificationCall(input.sharpnessProof, receipt, 'sharpnessProof');
288
+ if (sharpnessProof.toolCallId === primary.toolCallId
289
+ && sharpnessProof.toolName === primary.toolName) {
290
+ throw new TypeError('sharpnessProof must name a distinct verification call');
291
+ }
292
+ return Object.freeze({ subject, ...primary, sharpnessProof });
193
293
  }
194
294
 
195
295
  function emptyActionEvidenceReceipt(turnId) {
@@ -200,6 +300,7 @@ function emptyActionEvidenceReceipt(turnId) {
200
300
  successfulTools: 0,
201
301
  failedTools: 0,
202
302
  successfulToolDigests: Object.freeze([]),
303
+ successfulToolCallDigests: Object.freeze([]),
203
304
  digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
204
305
  });
205
306
  }
@@ -207,7 +308,7 @@ function emptyActionEvidenceReceipt(turnId) {
207
308
  function normalizeActionEvidenceReceipt(input) {
208
309
  if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
209
310
  const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
210
- const allowedKeys = new Set([...requiredKeys, 'successfulToolDigests']);
311
+ const allowedKeys = new Set([...requiredKeys, 'successfulToolDigests', 'successfulToolCallDigests']);
211
312
  if (!Object.keys(input).every((key) => allowedKeys.has(key))
212
313
  || ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
213
314
  throw new TypeError('evidence receipt fields are invalid');
@@ -218,6 +319,7 @@ function normalizeActionEvidenceReceipt(input) {
218
319
  successfulTools: Number(input.successfulTools),
219
320
  failedTools: Number(input.failedTools),
220
321
  successfulToolDigests: normalizeSuccessfulToolDigests(input.successfulToolDigests),
322
+ successfulToolCallDigests: normalizeSuccessfulToolDigests(input.successfulToolCallDigests),
221
323
  digest: String(input.digest ?? ''),
222
324
  };
223
325
  if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
@@ -232,8 +334,8 @@ function normalizeActionEvidenceReceipt(input) {
232
334
  function advanceActionEvidenceReceipt(current, input) {
233
335
  const prior = normalizeActionEvidenceReceipt(current);
234
336
  if (!input || typeof input !== 'object' || Array.isArray(input)
235
- || Object.keys(input).length !== EVIDENCE_INPUT_KEYS.size
236
- || !Object.keys(input).every((key) => EVIDENCE_INPUT_KEYS.has(key))) {
337
+ || !Object.keys(input).every((key) => EVIDENCE_INPUT_KEYS.has(key))
338
+ || ![...REQUIRED_EVIDENCE_INPUT_KEYS].every((key) => Object.hasOwn(input, key))) {
237
339
  throw new TypeError('evidence input fields are invalid');
238
340
  }
239
341
  const turnId = normalizedTurnId(input.turnId);
@@ -249,13 +351,21 @@ function advanceActionEvidenceReceipt(current, input) {
249
351
  }
250
352
  const successful = decision === 'passed' && outcome === 'success';
251
353
  const successfulToolDigests = [...prior.successfulToolDigests];
354
+ const successfulToolCallDigests = [...prior.successfulToolCallDigests];
252
355
  const toolDigest = successfulToolDigest(toolName);
253
356
  if (successful && !successfulToolDigests.includes(toolDigest)) {
254
357
  successfulToolDigests.push(toolDigest);
255
358
  if (successfulToolDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolDigests.shift();
256
359
  }
360
+ const verificationCall = successful && isVerificationToolCall(toolName, input.toolArgs);
361
+ if (verificationCall) {
362
+ const callDigest = successfulToolCallDigest(turnId, toolCallId, toolName);
363
+ if (!successfulToolCallDigests.includes(callDigest)) successfulToolCallDigests.push(callDigest);
364
+ if (successfulToolCallDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolCallDigests.shift();
365
+ }
257
366
  const digest = crypto.createHash('sha256').update([
258
367
  prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
368
+ String(verificationCall),
259
369
  ].join('\0')).digest('hex').slice(0, 16);
260
370
  return Object.freeze({
261
371
  turnId,
@@ -263,6 +373,7 @@ function advanceActionEvidenceReceipt(current, input) {
263
373
  successfulTools: prior.successfulTools + (successful ? 1 : 0),
264
374
  failedTools: prior.failedTools + (successful ? 0 : 1),
265
375
  successfulToolDigests: Object.freeze(successfulToolDigests),
376
+ successfulToolCallDigests: Object.freeze(successfulToolCallDigests),
266
377
  digest,
267
378
  });
268
379
  }
@@ -352,7 +463,9 @@ function normalizeActionCheckpoint(input, options = {}) {
352
463
  if (phase !== 'verify' || evidenceBasis !== 'runtime_tool') {
353
464
  throw new TypeError('verificationProof requires a runtime_tool verify checkpoint');
354
465
  }
355
- checkpoint.verificationProof = normalizeVerificationProof(input.verificationProof, evidenceReceipt);
466
+ checkpoint.verificationProof = normalizeVerificationProof(input.verificationProof, evidenceReceipt, {
467
+ allowLegacy: replay,
468
+ });
356
469
  }
357
470
  return Object.freeze(checkpoint);
358
471
  }
@@ -371,7 +484,17 @@ function projectActionCheckpoint(checkpoint) {
371
484
  lines.push(`Next action: ${value.nextAction}`);
372
485
  lines.push(`Expected evidence: ${value.expectedEvidence}`);
373
486
  if (value.verificationProof !== undefined) {
374
- lines.push(`Verification proof: ${value.verificationProof.toolName} - ${value.verificationProof.claim}`);
487
+ const call = value.verificationProof.toolCallId === undefined
488
+ ? value.verificationProof.toolName
489
+ : `${value.verificationProof.toolName} call ${value.verificationProof.toolCallId}`;
490
+ const subject = value.verificationProof.subject === undefined
491
+ ? ''
492
+ : ` [${value.verificationProof.subject}]`;
493
+ lines.push(`Verification proof${subject}: ${call} - ${value.verificationProof.claim}`);
494
+ if (value.verificationProof.sharpnessProof !== undefined) {
495
+ const sharpness = value.verificationProof.sharpnessProof;
496
+ lines.push(`Sharpness proof: ${sharpness.toolName} call ${sharpness.toolCallId} - ${sharpness.claim}`);
497
+ }
375
498
  }
376
499
  if (value.nextTrigger !== undefined) {
377
500
  lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
@@ -410,5 +533,6 @@ module.exports = {
410
533
  normalizeActionCheckpoint,
411
534
  normalizeVerificationProof,
412
535
  projectActionCheckpoint,
536
+ successfulToolCallDigest,
413
537
  successfulToolDigest,
414
538
  };
@@ -19,14 +19,22 @@ function verificationProofGaps(checkpoint) {
19
19
  return ['Bind the completion claim to a successful verification tool from the checkpoint turn.'];
20
20
  }
21
21
  try {
22
- normalizeVerificationProof(checkpoint.verificationProof, checkpoint.evidenceReceipt);
22
+ normalizeVerificationProof(checkpoint.verificationProof, checkpoint.evidenceReceipt, { allowLegacy: true });
23
23
  return [];
24
24
  } catch (error) {
25
+ if (/verifier proof requires a sharpnessProof|sharpnessProof must name a distinct verification call/u
26
+ .test(String(error?.message ?? ''))) {
27
+ return ['A verifier completion needs a distinct current-turn counterexample or mutation call.'];
28
+ }
29
+ if (/sharpnessProof must match a successful current-turn verification call/u
30
+ .test(String(error?.message ?? ''))) {
31
+ return ['The verifier sharpness proof does not match a successful current-turn verification call.'];
32
+ }
25
33
  if (/action-only tool/u.test(String(error?.message ?? ''))) {
26
34
  return ['The completion proof names an action-only tool, not a verification tool.'];
27
35
  }
28
- if (/successful current-turn tool/u.test(String(error?.message ?? ''))) {
29
- return ['The completion proof does not match a successful verification tool from the checkpoint turn.'];
36
+ if (/successful current-turn (?:tool|verification call)/u.test(String(error?.message ?? ''))) {
37
+ return ['The completion proof does not match a successful verification call from the checkpoint turn.'];
30
38
  }
31
39
  return ['The completion verification proof is malformed.'];
32
40
  }
package/blun.mjs CHANGED
@@ -260349,9 +260349,34 @@ function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFra
260349
260349
  nextAction: string().min(1).max(512),
260350
260350
  expectedEvidence: string().min(1).max(512),
260351
260351
  verificationProof: object({
260352
+ subject: _enum(["result", "verifier"]),
260353
+ toolCallId: string().min(1).max(256),
260352
260354
  toolName: string().min(1).max(128),
260353
- claim: string().min(1).max(512)
260354
- }).strict().optional(),
260355
+ claim: string().min(1).max(512),
260356
+ sharpnessProof: object({
260357
+ toolCallId: string().min(1).max(256),
260358
+ toolName: string().min(1).max(128),
260359
+ claim: string().min(1).max(512)
260360
+ }).strict().optional()
260361
+ }).strict().superRefine((value, ctx) => {
260362
+ if (value.subject === "verifier" && value.sharpnessProof === void 0) ctx.addIssue({
260363
+ code: "custom",
260364
+ path: ["sharpnessProof"],
260365
+ message: "verifier subject requires sharpnessProof"
260366
+ });
260367
+ if (value.subject === "result" && value.sharpnessProof !== void 0) ctx.addIssue({
260368
+ code: "custom",
260369
+ path: ["sharpnessProof"],
260370
+ message: "result subject cannot carry sharpnessProof"
260371
+ });
260372
+ if (value.sharpnessProof !== void 0
260373
+ && value.sharpnessProof.toolCallId === value.toolCallId
260374
+ && value.sharpnessProof.toolName === value.toolName) ctx.addIssue({
260375
+ code: "custom",
260376
+ path: ["sharpnessProof"],
260377
+ message: "sharpnessProof must name a distinct verification call"
260378
+ });
260379
+ }).optional(),
260355
260380
  nextTrigger: object({
260356
260381
  kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
260357
260382
  condition: string().min(1).max(512),
@@ -262737,6 +262762,7 @@ var init_turn = __esmMin((() => {
262737
262762
  this.toolCallDupType.set(event.toolCallId, dupType === "cross_step" ? "cross_step" : "normal");
262738
262763
  this.toolCallStartedAt.set(event.toolCallId, {
262739
262764
  name: event.name,
262765
+ args: event.args,
262740
262766
  startedAt: Date.now()
262741
262767
  });
262742
262768
  this.agent.feedRootMissionContract("start", {
@@ -262767,7 +262793,8 @@ var init_turn = __esmMin((() => {
262767
262793
  toolCallId: event.toolCallId,
262768
262794
  toolName: started.name,
262769
262795
  outcome,
262770
- durationMs: Date.now() - started.startedAt
262796
+ durationMs: Date.now() - started.startedAt,
262797
+ toolArgs: started.args
262771
262798
  });
262772
262799
  this.agent.telemetry.track("tool_call", properties);
262773
262800
  this.agent.feedRootMissionContract("result", {
@@ -262872,7 +262899,7 @@ var init_outcome_prompts = __esmMin((() => {}));
262872
262899
  var update_goal_default;
262873
262900
  var init_update_goal$1 = __esmMin((() => {
262874
262901
  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";
262875
- update_goal_default += "\nBefore completing a goal with a criterion, bind the verified claim to the successful current-turn verification tool in `verificationProof`. A write or edit is an action, not proof that the changed behavior works. Run a separate observation, test, or counterexample probe and name that successful tool plus the exact claim it supports.\n";
262902
+ update_goal_default += "\nBefore completing a goal with a criterion, bind the verified claim to the exact successful current-turn verification call in `verificationProof`, including its `toolCallId`. A write, edit, copy, deploy, or other action is not proof that the changed behavior works, even when it shares a mixed-use tool such as `Bash` with tests. Use `subject: result` for a result, report, measurement, or download. Use `subject: verifier` only when the new or changed test, gate, harness, or detector itself is the completion subject; then bind `sharpnessProof` to a separate successful current-turn counterexample or mutation call. Do not require a red probe for a normal report or measurement.\n";
262876
262903
  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";
262877
262904
  }));
262878
262905
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.448",
3
+ "version": "9.1.450",
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": {