blun-king-cli 9.1.449 → 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
  ]);
@@ -210,39 +211,85 @@ function normalizeSuccessfulToolDigests(value) {
210
211
  return Object.freeze([...value]);
211
212
  }
212
213
 
214
+ function normalizeExactVerificationCall(input, receipt, label) {
215
+ if (!input || typeof input !== 'object' || Array.isArray(input)
216
+ || Object.keys(input).length !== 3
217
+ || !Object.hasOwn(input, 'toolCallId')
218
+ || !Object.hasOwn(input, 'toolName')
219
+ || !Object.hasOwn(input, 'claim')) {
220
+ throw new TypeError(`${label} fields are invalid`);
221
+ }
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`);
225
+ if (isActionOnlyTool(toolName)) {
226
+ const purpose = label === 'verificationProof' ? 'verification proof' : label;
227
+ throw new TypeError(`action-only tool cannot serve as ${purpose}`);
228
+ }
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`);
232
+ }
233
+ return Object.freeze({ toolCallId, toolName, claim });
234
+ }
235
+
213
236
  function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
214
237
  const keys = input && typeof input === 'object' && !Array.isArray(input)
215
238
  ? Object.keys(input)
216
239
  : [];
217
- const legacy = options.allowLegacy === true
240
+ const legacyName = options.allowLegacy === true
218
241
  && keys.length === 2
219
242
  && Object.hasOwn(input, 'toolName')
220
243
  && Object.hasOwn(input, 'claim');
221
- const current = keys.length === 3
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
222
252
  && Object.hasOwn(input, 'toolCallId')
223
253
  && Object.hasOwn(input, 'toolName')
224
254
  && Object.hasOwn(input, 'claim');
225
- if (!legacy && !current) {
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) {
226
262
  throw new TypeError('verificationProof fields are invalid');
227
263
  }
228
- const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
229
- const claim = bounded(input.claim, 'verificationProof claim');
230
- if (isActionOnlyTool(toolName)) {
231
- throw new TypeError('action-only tool cannot serve as verification proof');
232
- }
233
264
  const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
234
- if (legacy) {
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
+ }
235
271
  if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
236
272
  throw new TypeError('verificationProof must name a successful current-turn tool');
237
273
  }
238
274
  return Object.freeze({ toolName, claim });
239
275
  }
240
- const toolCallId = bounded(input.toolCallId, 'verificationProof toolCallId', 256);
241
- const callDigest = successfulToolCallDigest(receipt.turnId, toolCallId, toolName);
242
- if (!receipt.successfulToolCallDigests.includes(callDigest)) {
243
- throw new TypeError('verificationProof must match a successful current-turn verification call');
244
- }
245
- return Object.freeze({ toolCallId, toolName, claim });
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 });
246
293
  }
247
294
 
248
295
  function emptyActionEvidenceReceipt(turnId) {
@@ -440,7 +487,14 @@ function projectActionCheckpoint(checkpoint) {
440
487
  const call = value.verificationProof.toolCallId === undefined
441
488
  ? value.verificationProof.toolName
442
489
  : `${value.verificationProof.toolName} call ${value.verificationProof.toolCallId}`;
443
- lines.push(`Verification proof: ${call} - ${value.verificationProof.claim}`);
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
+ }
444
498
  }
445
499
  if (value.nextTrigger !== undefined) {
446
500
  lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
@@ -22,6 +22,14 @@ function verificationProofGaps(checkpoint) {
22
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
  }
package/blun.mjs CHANGED
@@ -260349,10 +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"]),
260352
260353
  toolCallId: string().min(1).max(256),
260353
260354
  toolName: string().min(1).max(128),
260354
- claim: string().min(1).max(512)
260355
- }).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(),
260356
260380
  nextTrigger: object({
260357
260381
  kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
260358
260382
  condition: string().min(1).max(512),
@@ -262875,7 +262899,7 @@ var init_outcome_prompts = __esmMin((() => {}));
262875
262899
  var update_goal_default;
262876
262900
  var init_update_goal$1 = __esmMin((() => {
262877
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";
262878
- 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. Run a separate observation, test, or counterexample probe and cite that exact successful call plus the 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";
262879
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";
262880
262904
  }));
262881
262905
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.449",
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": {