blun-king-cli 9.1.453 → 9.1.454

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.
@@ -31,6 +31,7 @@ const REQUIRED_EVIDENCE_INPUT_KEYS = new Set([
31
31
  'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
32
32
  ]);
33
33
  const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
34
+ const EVIDENCE_PRODUCER_REF_RE = /^[a-f0-9]{16}$/u;
34
35
  const DECISION_BASIS_RE = /^[a-f0-9]{16}$/u;
35
36
  const COMPLETION_CRITERION_REF_RE = /^[a-f0-9]{16}$/u;
36
37
  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;
@@ -180,6 +181,20 @@ function successfulToolDigest(toolName) {
180
181
  return crypto.createHash('sha256').update(`tool:${name}`).digest('hex').slice(0, 16);
181
182
  }
182
183
 
184
+ function evidenceProducerRef(identity) {
185
+ const value = bounded(identity, 'evidence producer identity', 1024);
186
+ return crypto.createHash('sha256')
187
+ .update(`evidence-producer:${value}`)
188
+ .digest('hex')
189
+ .slice(0, 16);
190
+ }
191
+
192
+ function normalizedEvidenceProducerRef(value, field = 'evidence producer ref') {
193
+ const ref = String(value ?? '').trim();
194
+ if (!EVIDENCE_PRODUCER_REF_RE.test(ref)) throw new TypeError(`${field} is invalid`);
195
+ return ref;
196
+ }
197
+
183
198
  function successfulToolCallDigest(turnId, toolCallId, toolName) {
184
199
  const turn = normalizedTurnId(turnId);
185
200
  const callId = bounded(toolCallId, 'verificationProof toolCallId', 256);
@@ -475,10 +490,32 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
475
490
  && Object.hasOwn(input, 'criterionRef')
476
491
  && Object.hasOwn(input, 'claim')
477
492
  && keys.every((key) => ['subject', 'toolCallId', 'toolName', 'kind', 'scope', 'criterionRef', 'claim', 'sharpnessProof'].includes(key));
493
+ const replayProvenanceResult = options.allowLegacy === true
494
+ && subject === 'result'
495
+ && keys.length === 8
496
+ && Object.hasOwn(input, 'toolCallId')
497
+ && Object.hasOwn(input, 'toolName')
498
+ && Object.hasOwn(input, 'kind')
499
+ && Object.hasOwn(input, 'scope')
500
+ && Object.hasOwn(input, 'criterionRef')
501
+ && Object.hasOwn(input, 'producerRef')
502
+ && Object.hasOwn(input, 'claim');
503
+ const replayProvenanceVerifier = options.allowLegacy === true
504
+ && subject === 'verifier'
505
+ && (keys.length === 8 || keys.length === 9)
506
+ && Object.hasOwn(input, 'toolCallId')
507
+ && Object.hasOwn(input, 'toolName')
508
+ && Object.hasOwn(input, 'kind')
509
+ && Object.hasOwn(input, 'scope')
510
+ && Object.hasOwn(input, 'criterionRef')
511
+ && Object.hasOwn(input, 'producerRef')
512
+ && Object.hasOwn(input, 'claim')
513
+ && keys.every((key) => ['subject', 'toolCallId', 'toolName', 'kind', 'scope', 'criterionRef', 'producerRef', 'claim', 'sharpnessProof'].includes(key));
478
514
  if (!legacyName && !legacyExact && !legacySubjectResult && !legacySubjectVerifier
479
515
  && !legacyTypedSubjectResult && !legacyTypedSubjectVerifier
480
516
  && !legacyScopedResult && !legacyScopedVerifier
481
- && !freshResult && !freshVerifier && !replayResult && !replayVerifier) {
517
+ && !freshResult && !freshVerifier && !replayResult && !replayVerifier
518
+ && !replayProvenanceResult && !replayProvenanceVerifier) {
482
519
  throw new TypeError('verificationProof fields are invalid');
483
520
  }
484
521
  const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
@@ -500,25 +537,39 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
500
537
  toolName: input.toolName,
501
538
  ...(legacySubject ? {} : { kind: input.kind }),
502
539
  ...(legacyScopedResult || legacyScopedVerifier || freshResult || freshVerifier
503
- || replayResult || replayVerifier ? { scope: input.scope } : {}),
540
+ || replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier
541
+ ? { scope: input.scope } : {}),
504
542
  claim: input.claim,
505
543
  }, receipt, 'verificationProof', {
506
544
  requireKind: !legacyExact && !legacySubject,
507
545
  requireScope: legacyScopedResult || legacyScopedVerifier || freshResult || freshVerifier
508
- || replayResult || replayVerifier,
546
+ || replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier,
509
547
  });
510
548
  if (legacyExact) return primary;
511
549
  if (!VERIFICATION_SUBJECTS.has(subject)) throw new TypeError('verificationProof subject is invalid');
512
550
  const criterionRef = freshResult || freshVerifier
513
551
  ? completionCriterionRef(input.criterion)
514
- : replayResult || replayVerifier
552
+ : replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier
515
553
  ? String(input.criterionRef ?? '')
516
554
  : undefined;
517
555
  if (criterionRef !== undefined && !COMPLETION_CRITERION_REF_RE.test(criterionRef)) {
518
556
  throw new TypeError('verificationProof criterionRef is invalid');
519
557
  }
558
+ const producerRef = freshResult || freshVerifier
559
+ ? receipt.producerRef
560
+ : replayProvenanceResult || replayProvenanceVerifier
561
+ ? normalizedEvidenceProducerRef(input.producerRef, 'verificationProof producerRef')
562
+ : undefined;
563
+ if (producerRef !== undefined && receipt.producerRef !== producerRef) {
564
+ throw new TypeError('verificationProof producerRef must match the runtime evidence producer');
565
+ }
520
566
  if (subject === 'result') {
521
- return Object.freeze({ subject, ...primary, ...(criterionRef === undefined ? {} : { criterionRef }) });
567
+ return Object.freeze({
568
+ subject,
569
+ ...primary,
570
+ ...(criterionRef === undefined ? {} : { criterionRef }),
571
+ ...(producerRef === undefined ? {} : { producerRef }),
572
+ });
522
573
  }
523
574
  if (input.sharpnessProof === undefined) {
524
575
  throw new TypeError('verifier proof requires a sharpnessProof');
@@ -535,14 +586,19 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
535
586
  subject,
536
587
  ...primary,
537
588
  ...(criterionRef === undefined ? {} : { criterionRef }),
589
+ ...(producerRef === undefined ? {} : { producerRef }),
538
590
  sharpnessProof,
539
591
  });
540
592
  }
541
593
 
542
- function emptyActionEvidenceReceipt(turnId) {
594
+ function emptyActionEvidenceReceipt(turnId, producerRef) {
543
595
  const normalized = normalizedTurnId(turnId);
596
+ const producer = producerRef === undefined
597
+ ? undefined
598
+ : normalizedEvidenceProducerRef(producerRef);
544
599
  return Object.freeze({
545
600
  turnId: normalized,
601
+ ...(producer === undefined ? {} : { producerRef: producer }),
546
602
  completedTools: 0,
547
603
  successfulTools: 0,
548
604
  failedTools: 0,
@@ -550,7 +606,12 @@ function emptyActionEvidenceReceipt(turnId) {
550
606
  successfulToolCallDigests: Object.freeze([]),
551
607
  successfulVerificationCallDigests: Object.freeze([]),
552
608
  successfulVerificationScopeDigests: Object.freeze([]),
553
- digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
609
+ digest: crypto.createHash('sha256')
610
+ .update(producer === undefined
611
+ ? `turn:${normalized}:empty`
612
+ : `producer:${producer}\0turn:${normalized}:empty`)
613
+ .digest('hex')
614
+ .slice(0, 16),
554
615
  });
555
616
  }
556
617
 
@@ -559,7 +620,7 @@ function normalizeActionEvidenceReceipt(input) {
559
620
  const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
560
621
  const allowedKeys = new Set([
561
622
  ...requiredKeys, 'successfulToolDigests', 'successfulToolCallDigests',
562
- 'successfulVerificationCallDigests', 'successfulVerificationScopeDigests',
623
+ 'successfulVerificationCallDigests', 'successfulVerificationScopeDigests', 'producerRef',
563
624
  ]);
564
625
  if (!Object.keys(input).every((key) => allowedKeys.has(key))
565
626
  || ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
@@ -567,6 +628,9 @@ function normalizeActionEvidenceReceipt(input) {
567
628
  }
568
629
  const receipt = {
569
630
  turnId: normalizedTurnId(input.turnId),
631
+ ...(input.producerRef === undefined
632
+ ? {}
633
+ : { producerRef: normalizedEvidenceProducerRef(input.producerRef) }),
570
634
  completedTools: Number(input.completedTools),
571
635
  successfulTools: Number(input.successfulTools),
572
636
  failedTools: Number(input.failedTools),
@@ -660,11 +724,13 @@ function advanceActionEvidenceReceipt(current, input) {
660
724
  }
661
725
  }
662
726
  const digest = crypto.createHash('sha256').update([
663
- prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
727
+ prior.digest, prior.producerRef ?? 'legacy', String(turnId), toolCallId, toolName,
728
+ decision, outcome, String(durationMs),
664
729
  verificationKinds.join(','), verificationScopes.join(','),
665
730
  ].join('\0')).digest('hex').slice(0, 16);
666
731
  return Object.freeze({
667
732
  turnId,
733
+ ...(prior.producerRef === undefined ? {} : { producerRef: prior.producerRef }),
668
734
  completedTools: prior.completedTools + 1,
669
735
  successfulTools: prior.successfulTools + (successful ? 1 : 0),
670
736
  failedTools: prior.failedTools + (successful ? 0 : 1),
@@ -797,7 +863,10 @@ function projectActionCheckpoint(checkpoint) {
797
863
  const criterion = value.verificationProof.criterionRef === undefined
798
864
  ? ''
799
865
  : ` [criterion: ${value.verificationProof.criterionRef}]`;
800
- lines.push(`Verification proof${subject}${kind}${scope}${criterion}: ${call} - ${value.verificationProof.claim}`);
866
+ const producer = value.verificationProof.producerRef === undefined
867
+ ? ''
868
+ : ` [producer: ${value.verificationProof.producerRef}]`;
869
+ lines.push(`Verification proof${subject}${kind}${scope}${criterion}${producer}: ${call} - ${value.verificationProof.claim}`);
801
870
  if (value.verificationProof.sharpnessProof !== undefined) {
802
871
  const sharpness = value.verificationProof.sharpnessProof;
803
872
  const sharpnessKind = sharpness.kind === undefined ? '' : ` [${sharpness.kind}]`;
@@ -839,6 +908,7 @@ module.exports = {
839
908
  assertActionCheckpointEvidenceBasis,
840
909
  assertActionCheckpointRevision,
841
910
  completionCriterionRef,
911
+ evidenceProducerRef,
842
912
  emptyActionEvidenceReceipt,
843
913
  normalizeActionCheckpoint,
844
914
  normalizeVerificationProof,
@@ -15,7 +15,7 @@ function successfulRuntimeEvidence(checkpoint) {
15
15
  return Number.isSafeInteger(successfulTools) && successfulTools > 0;
16
16
  }
17
17
 
18
- function verificationProofGaps(checkpoint, criterion) {
18
+ function verificationProofGaps(checkpoint, criterion, expectedProducerRef) {
19
19
  if (!checkpoint?.verificationProof) {
20
20
  return ['Bind the completion claim to a successful verification tool from the checkpoint turn.'];
21
21
  }
@@ -28,6 +28,12 @@ function verificationProofGaps(checkpoint, criterion) {
28
28
  if (proof.criterionRef !== completionCriterionRef(criterion)) {
29
29
  return ['Bind the completion proof to the active completion criterion.'];
30
30
  }
31
+ if (expectedProducerRef !== undefined && proof.producerRef === undefined) {
32
+ return ['Bind the completion proof to the runtime that produced its measurement.'];
33
+ }
34
+ if (expectedProducerRef !== undefined && proof.producerRef !== expectedProducerRef) {
35
+ return ['The completion proof was produced by a different agent session; preserve it as an external report instead of relabelling it as local verification.'];
36
+ }
31
37
  return [];
32
38
  } catch (error) {
33
39
  if (/verifier proof requires a sharpnessProof|sharpnessProof must name a distinct verification call/u
@@ -56,7 +62,7 @@ function verificationProofGaps(checkpoint, criterion) {
56
62
  }
57
63
  }
58
64
 
59
- function evaluateGoalCompletionEvidence(goal) {
65
+ function evaluateGoalCompletionEvidence(goal, options = {}) {
60
66
  if (!hasCompletionCriterion(goal)) {
61
67
  return {
62
68
  required: false,
@@ -94,7 +100,11 @@ function evaluateGoalCompletionEvidence(goal) {
94
100
  && checkpoint.evidenceBasis === 'runtime_tool'
95
101
  && checkpoint.epistemicState === 'verified'
96
102
  && hasRuntimeEvidence) {
97
- gaps.push(...verificationProofGaps(checkpoint, goal.completionCriterion));
103
+ gaps.push(...verificationProofGaps(
104
+ checkpoint,
105
+ goal.completionCriterion,
106
+ options.expectedProducerRef,
107
+ ));
98
108
  }
99
109
 
100
110
  return {
package/blun.mjs CHANGED
@@ -21463,7 +21463,7 @@ var { rollbackPersonalityChoice, setPersonalityEnabledAtomic } = createRequire(i
21463
21463
  var { projectSoulText } = createRequire(import.meta.url)("./bin/soul-organization-policy.cjs");
21464
21464
  var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
21465
21465
  var { readProfilePersona, resolveSoulFile } = createRequire(import.meta.url)("./bin/profile-identity-resolution.cjs");
21466
- var { advanceActionEvidenceReceipt, assertActionCheckpointEvidenceBasis, assertActionCheckpointRevision, emptyActionEvidenceReceipt, normalizeActionCheckpoint, projectActionCheckpoint } = createRequire(import.meta.url)("./bin/cognitive-action-checkpoint.cjs");
21466
+ var { advanceActionEvidenceReceipt, assertActionCheckpointEvidenceBasis, assertActionCheckpointRevision, emptyActionEvidenceReceipt, evidenceProducerRef, normalizeActionCheckpoint, projectActionCheckpoint } = createRequire(import.meta.url)("./bin/cognitive-action-checkpoint.cjs");
21467
21467
  var { evaluateGoalCompletionEvidence } = createRequire(import.meta.url)("./bin/goal-completion-evidence-policy.cjs");
21468
21468
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21469
21469
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
@@ -230425,7 +230425,9 @@ var init_goal$1 = __esmMin((() => {
230425
230425
  async markComplete(input = {}, actor = "model") {
230426
230426
  const state = this.state;
230427
230427
  if (state === void 0 || state.status !== "active") return null;
230428
- const completionEvidence = evaluateGoalCompletionEvidence(state);
230428
+ const completionEvidence = evaluateGoalCompletionEvidence(state, {
230429
+ expectedProducerRef: this.agent.evidenceProducerRef
230430
+ });
230429
230431
  if (!completionEvidence.allPassed) {
230430
230432
  throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Completion criterion needs revision: ${completionEvidence.gaps.join(" ")} Run the required verification, save a runtime-backed verify checkpoint, then mark the goal complete.`);
230431
230433
  }
@@ -245812,6 +245814,7 @@ var init_events$1 = __esmMin((() => {
245812
245814
  updatedAt: string(),
245813
245815
  evidenceReceipt: object({
245814
245816
  turnId: number$1().int().min(0),
245817
+ producerRef: string().regex(/^[a-f0-9]{16}$/u).optional(),
245815
245818
  completedTools: number$1().int().min(0),
245816
245819
  successfulTools: number$1().int().min(0),
245817
245820
  failedTools: number$1().int().min(0),
@@ -261846,7 +261849,7 @@ var init_turn = __esmMin((() => {
261846
261849
  return;
261847
261850
  }
261848
261851
  this.cognitiveToolPolicyByCall.delete(input.toolCallId);
261849
- const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId);
261852
+ const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId, this.agent.evidenceProducerRef);
261850
261853
  this.cognitiveActionEvidenceByTurn.set(input.turnId, advanceActionEvidenceReceipt(currentEvidence, {
261851
261854
  ...input,
261852
261855
  decision: policy.decision
@@ -261877,7 +261880,7 @@ var init_turn = __esmMin((() => {
261877
261880
  }
261878
261881
  actionEvidenceReceiptForCurrentTurn() {
261879
261882
  const turnId = this.currentId;
261880
- return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId);
261883
+ return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId, this.agent.evidenceProducerRef);
261881
261884
  }
261882
261885
  projectCognitiveState(turnId, input) {
261883
261886
  try {
@@ -262919,6 +262922,7 @@ var init_update_goal$1 = __esmMin((() => {
262919
262922
  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";
262920
262923
  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";
262921
262924
  update_goal_default += "\nCopy the active goal's exact `completionCriterion` into `verificationProof.criterion`. The runtime stores only its bounded reference and refuses completion if the proof belongs to a different or superseded completion criterion.\n";
262925
+ update_goal_default += "\nThe runtime binds fresh verification automatically to the session and agent that produced the successful tool result. Do not invent or copy a producer reference. Preserve forwarded measurements as `external_report` with `credible_unverified` until this agent measures them independently; forwarding is allowed, relabelling them as local verification is not.\n";
262922
262926
  update_goal_default += "\nSet the proof `kind` to the exact capability of that call: `inspection` reads or searches, `integrity` compares bytes or hashes, `syntax` parses or type-checks, and `test` runs assertions. None of these alone proves a stronger kind. Use `reachability` only for a successful runtime probe that actually invokes the changed path and emits the exact marker `BLUN_EVIDENCE_KIND=reachability` after its assertions; loading a module without reaching the changed path is not reachability.\n";
262923
262927
  update_goal_default += "\nBind each proof `scope` to the exact target measured by that successful call, never to a free-text claim or intended file. Read and search tools derive scope from their target arguments. For shell or command tools, include the same safe token `BLUN_EVIDENCE_SCOPE=<scope>` in the launched non-mutating verification command and emit it only after that exact target succeeds; the runtime requires both sides.\n";
262924
262928
  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";
@@ -265363,6 +265367,7 @@ var init_agent = __esmMin((() => {
265363
265367
  blunConfig;
265364
265368
  blunHomeDir;
265365
265369
  homedir;
265370
+ evidenceProducerRef;
265366
265371
  mediaOriginalsDir;
265367
265372
  rpc;
265368
265373
  toolServices;
@@ -265418,6 +265423,7 @@ var init_agent = __esmMin((() => {
265418
265423
  this.blunConfig = options.config;
265419
265424
  this.blunHomeDir = options.blunHomeDir;
265420
265425
  this.homedir = options.homedir;
265426
+ this.evidenceProducerRef = options.evidenceProducerRef;
265421
265427
  this.mediaOriginalsDir = options.mediaOriginalsDir;
265422
265428
  this.rpc = options.rpc;
265423
265429
  this.toolServices = options.toolServices;
@@ -298011,6 +298017,7 @@ var init_session$1 = __esmMin((() => {
298011
298017
  config: this.options.config,
298012
298018
  blunHomeDir: this.options.blunHomeDir,
298013
298019
  homedir,
298020
+ evidenceProducerRef: evidenceProducerRef(`${this.options.id ?? this.options.homedir}\0${id}`),
298014
298021
  mediaOriginalsDir: sessionMediaOriginalsDir(this.options.homedir),
298015
298022
  onMediaDropped: (dropped) => {
298016
298023
  for (const part of dropped) this.rpc.emitEvent({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.453",
3
+ "version": "9.1.454",
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": {