blun-king-cli 9.1.453 → 9.1.455

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.
@@ -14,15 +14,20 @@ const TRIGGER_KINDS = new Set([
14
14
  ]);
15
15
  const VERIFICATION_SUBJECTS = new Set(['result', 'verifier']);
16
16
  const VERIFICATION_KINDS = new Set(['inspection', 'integrity', 'syntax', 'test', 'reachability']);
17
+ const EXTERNAL_REPORT_SOURCES = new Set(['telegram']);
17
18
  const MODEL_KEYS = new Set([
18
- 'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'verificationProof', 'nextTrigger', 'problemFrame', 'updatedAt',
19
+ 'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'verificationProof', 'nextTrigger', 'problemFrame', 'externalReportSource', 'updatedAt',
19
20
  ]);
20
- const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt']);
21
+ const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt', 'externalReportOrigin']);
21
22
  const PROBLEM_FRAME_KEYS = new Set([
22
23
  'successCriterion', 'missingKnowledge', 'candidateActions', 'selectedAction',
23
24
  'selectionReason', 'supportChoice', 'risk', 'reversibility', 'decisionBasis',
24
25
  ]);
25
26
  const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
27
+ const EXTERNAL_REPORT_SELECTOR_KEYS = new Set(['source', 'chatId', 'messageId']);
28
+ const EXTERNAL_REPORT_ORIGIN_KEYS = new Set([
29
+ 'source', 'reporter', 'reporterRef', 'eventRef', 'occurredAt',
30
+ ]);
26
31
  const EVIDENCE_INPUT_KEYS = new Set([
27
32
  'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs', 'toolArgs',
28
33
  'resultEvidenceKinds', 'resultEvidenceScopes',
@@ -31,6 +36,8 @@ const REQUIRED_EVIDENCE_INPUT_KEYS = new Set([
31
36
  'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
32
37
  ]);
33
38
  const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
39
+ const EVIDENCE_PRODUCER_REF_RE = /^[a-f0-9]{16}$/u;
40
+ const EXTERNAL_REPORT_REF_RE = /^[a-f0-9]{16}$/u;
34
41
  const DECISION_BASIS_RE = /^[a-f0-9]{16}$/u;
35
42
  const COMPLETION_CRITERION_REF_RE = /^[a-f0-9]{16}$/u;
36
43
  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 +187,145 @@ function successfulToolDigest(toolName) {
180
187
  return crypto.createHash('sha256').update(`tool:${name}`).digest('hex').slice(0, 16);
181
188
  }
182
189
 
190
+ function evidenceProducerRef(identity) {
191
+ const value = bounded(identity, 'evidence producer identity', 1024);
192
+ return crypto.createHash('sha256')
193
+ .update(`evidence-producer:${value}`)
194
+ .digest('hex')
195
+ .slice(0, 16);
196
+ }
197
+
198
+ function normalizedExternalReportSource(value, field = 'external report source') {
199
+ const source = String(value ?? '').trim();
200
+ if (!EXTERNAL_REPORT_SOURCES.has(source)) throw new TypeError(`${field} is invalid`);
201
+ return source;
202
+ }
203
+
204
+ function normalizeExternalReportSelector(input) {
205
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
206
+ throw new TypeError('externalReportSource is required for external_report evidenceBasis');
207
+ }
208
+ for (const key of Object.keys(input)) {
209
+ if (!EXTERNAL_REPORT_SELECTOR_KEYS.has(key)) {
210
+ throw new TypeError(`externalReportSource field is unsupported: ${key}`);
211
+ }
212
+ }
213
+ return Object.freeze({
214
+ source: normalizedExternalReportSource(input.source),
215
+ chatId: bounded(input.chatId, 'externalReportSource chatId', 64),
216
+ messageId: bounded(input.messageId, 'externalReportSource messageId', 64),
217
+ });
218
+ }
219
+
220
+ function externalReportSourceFromChannelMeta(meta) {
221
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta)) {
222
+ throw new TypeError('channel metadata is required for external report provenance');
223
+ }
224
+ const reporter = bounded(meta.user ?? meta.user_id, 'external report reporter', 128);
225
+ return Object.freeze({
226
+ source: 'telegram',
227
+ chatId: bounded(meta.chat_id, 'external report chatId', 64),
228
+ messageId: bounded(meta.message_id, 'external report messageId', 64),
229
+ reporter,
230
+ reporterId: bounded(meta.user_id ?? reporter, 'external report reporterId', 128),
231
+ occurredAt: normalizedTimestamp(meta.ts, 'external report occurredAt', true),
232
+ });
233
+ }
234
+
235
+ function normalizeRuntimeExternalReportSource(input) {
236
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
237
+ throw new TypeError('trusted external report source is invalid');
238
+ }
239
+ const reporter = bounded(input.reporter ?? input.reporterId, 'external report reporter', 128);
240
+ return Object.freeze({
241
+ source: normalizedExternalReportSource(input.source),
242
+ chatId: bounded(input.chatId, 'external report chatId', 64),
243
+ messageId: bounded(input.messageId, 'external report messageId', 64),
244
+ reporter,
245
+ reporterId: bounded(input.reporterId ?? reporter, 'external report reporterId', 128),
246
+ occurredAt: normalizedTimestamp(input.occurredAt, 'external report occurredAt', true),
247
+ });
248
+ }
249
+
250
+ function externalReportSourcesFromOrigin(origin) {
251
+ if (!origin || typeof origin !== 'object' || !Array.isArray(origin.externalReportSources)) {
252
+ return Object.freeze([]);
253
+ }
254
+ const sources = [];
255
+ const seen = new Set();
256
+ for (const value of origin.externalReportSources) {
257
+ let source;
258
+ try {
259
+ source = normalizeRuntimeExternalReportSource(value);
260
+ } catch {
261
+ continue;
262
+ }
263
+ const key = `${source.source}\0${source.chatId}\0${source.messageId}`;
264
+ if (seen.has(key)) continue;
265
+ seen.add(key);
266
+ sources.push(source);
267
+ }
268
+ return Object.freeze(sources.slice(0, 32));
269
+ }
270
+
271
+ function normalizedExternalReportRef(value, field) {
272
+ const ref = String(value ?? '').trim();
273
+ if (!EXTERNAL_REPORT_REF_RE.test(ref)) throw new TypeError(`${field} is invalid`);
274
+ return ref;
275
+ }
276
+
277
+ function normalizeExternalReportOrigin(input) {
278
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
279
+ throw new TypeError('externalReportOrigin is invalid');
280
+ }
281
+ for (const key of Object.keys(input)) {
282
+ if (!EXTERNAL_REPORT_ORIGIN_KEYS.has(key)) {
283
+ throw new TypeError(`externalReportOrigin field is unsupported: ${key}`);
284
+ }
285
+ }
286
+ return Object.freeze({
287
+ source: normalizedExternalReportSource(input.source, 'externalReportOrigin source'),
288
+ reporter: bounded(input.reporter, 'externalReportOrigin reporter', 128),
289
+ reporterRef: normalizedExternalReportRef(input.reporterRef, 'externalReportOrigin reporterRef'),
290
+ eventRef: normalizedExternalReportRef(input.eventRef, 'externalReportOrigin eventRef'),
291
+ occurredAt: normalizedTimestamp(input.occurredAt, 'externalReportOrigin occurredAt', true),
292
+ });
293
+ }
294
+
295
+ function resolveExternalReportOrigin(selectorInput, runtimeExternalReports) {
296
+ const selector = normalizeExternalReportSelector(selectorInput);
297
+ const reports = Array.isArray(runtimeExternalReports)
298
+ ? runtimeExternalReports.map(normalizeRuntimeExternalReportSource)
299
+ : [];
300
+ const report = reports.find((candidate) => candidate.source === selector.source
301
+ && candidate.chatId === selector.chatId
302
+ && candidate.messageId === selector.messageId);
303
+ if (report === undefined) {
304
+ throw new TypeError('externalReportSource does not match a trusted external report in the current turn');
305
+ }
306
+ const reporterRef = crypto.createHash('sha256')
307
+ .update(`external-report-reporter:${report.source}\0${report.reporterId}`)
308
+ .digest('hex')
309
+ .slice(0, 16);
310
+ const eventRef = crypto.createHash('sha256')
311
+ .update(`external-report-event:${report.source}\0${report.chatId}\0${report.messageId}\0${reporterRef}\0${report.occurredAt}`)
312
+ .digest('hex')
313
+ .slice(0, 16);
314
+ return Object.freeze({
315
+ source: report.source,
316
+ reporter: report.reporter,
317
+ reporterRef,
318
+ eventRef,
319
+ occurredAt: report.occurredAt,
320
+ });
321
+ }
322
+
323
+ function normalizedEvidenceProducerRef(value, field = 'evidence producer ref') {
324
+ const ref = String(value ?? '').trim();
325
+ if (!EVIDENCE_PRODUCER_REF_RE.test(ref)) throw new TypeError(`${field} is invalid`);
326
+ return ref;
327
+ }
328
+
183
329
  function successfulToolCallDigest(turnId, toolCallId, toolName) {
184
330
  const turn = normalizedTurnId(turnId);
185
331
  const callId = bounded(toolCallId, 'verificationProof toolCallId', 256);
@@ -475,10 +621,32 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
475
621
  && Object.hasOwn(input, 'criterionRef')
476
622
  && Object.hasOwn(input, 'claim')
477
623
  && keys.every((key) => ['subject', 'toolCallId', 'toolName', 'kind', 'scope', 'criterionRef', 'claim', 'sharpnessProof'].includes(key));
624
+ const replayProvenanceResult = options.allowLegacy === true
625
+ && subject === 'result'
626
+ && keys.length === 8
627
+ && Object.hasOwn(input, 'toolCallId')
628
+ && Object.hasOwn(input, 'toolName')
629
+ && Object.hasOwn(input, 'kind')
630
+ && Object.hasOwn(input, 'scope')
631
+ && Object.hasOwn(input, 'criterionRef')
632
+ && Object.hasOwn(input, 'producerRef')
633
+ && Object.hasOwn(input, 'claim');
634
+ const replayProvenanceVerifier = options.allowLegacy === true
635
+ && subject === 'verifier'
636
+ && (keys.length === 8 || keys.length === 9)
637
+ && Object.hasOwn(input, 'toolCallId')
638
+ && Object.hasOwn(input, 'toolName')
639
+ && Object.hasOwn(input, 'kind')
640
+ && Object.hasOwn(input, 'scope')
641
+ && Object.hasOwn(input, 'criterionRef')
642
+ && Object.hasOwn(input, 'producerRef')
643
+ && Object.hasOwn(input, 'claim')
644
+ && keys.every((key) => ['subject', 'toolCallId', 'toolName', 'kind', 'scope', 'criterionRef', 'producerRef', 'claim', 'sharpnessProof'].includes(key));
478
645
  if (!legacyName && !legacyExact && !legacySubjectResult && !legacySubjectVerifier
479
646
  && !legacyTypedSubjectResult && !legacyTypedSubjectVerifier
480
647
  && !legacyScopedResult && !legacyScopedVerifier
481
- && !freshResult && !freshVerifier && !replayResult && !replayVerifier) {
648
+ && !freshResult && !freshVerifier && !replayResult && !replayVerifier
649
+ && !replayProvenanceResult && !replayProvenanceVerifier) {
482
650
  throw new TypeError('verificationProof fields are invalid');
483
651
  }
484
652
  const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
@@ -500,25 +668,39 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
500
668
  toolName: input.toolName,
501
669
  ...(legacySubject ? {} : { kind: input.kind }),
502
670
  ...(legacyScopedResult || legacyScopedVerifier || freshResult || freshVerifier
503
- || replayResult || replayVerifier ? { scope: input.scope } : {}),
671
+ || replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier
672
+ ? { scope: input.scope } : {}),
504
673
  claim: input.claim,
505
674
  }, receipt, 'verificationProof', {
506
675
  requireKind: !legacyExact && !legacySubject,
507
676
  requireScope: legacyScopedResult || legacyScopedVerifier || freshResult || freshVerifier
508
- || replayResult || replayVerifier,
677
+ || replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier,
509
678
  });
510
679
  if (legacyExact) return primary;
511
680
  if (!VERIFICATION_SUBJECTS.has(subject)) throw new TypeError('verificationProof subject is invalid');
512
681
  const criterionRef = freshResult || freshVerifier
513
682
  ? completionCriterionRef(input.criterion)
514
- : replayResult || replayVerifier
683
+ : replayResult || replayVerifier || replayProvenanceResult || replayProvenanceVerifier
515
684
  ? String(input.criterionRef ?? '')
516
685
  : undefined;
517
686
  if (criterionRef !== undefined && !COMPLETION_CRITERION_REF_RE.test(criterionRef)) {
518
687
  throw new TypeError('verificationProof criterionRef is invalid');
519
688
  }
689
+ const producerRef = freshResult || freshVerifier
690
+ ? receipt.producerRef
691
+ : replayProvenanceResult || replayProvenanceVerifier
692
+ ? normalizedEvidenceProducerRef(input.producerRef, 'verificationProof producerRef')
693
+ : undefined;
694
+ if (producerRef !== undefined && receipt.producerRef !== producerRef) {
695
+ throw new TypeError('verificationProof producerRef must match the runtime evidence producer');
696
+ }
520
697
  if (subject === 'result') {
521
- return Object.freeze({ subject, ...primary, ...(criterionRef === undefined ? {} : { criterionRef }) });
698
+ return Object.freeze({
699
+ subject,
700
+ ...primary,
701
+ ...(criterionRef === undefined ? {} : { criterionRef }),
702
+ ...(producerRef === undefined ? {} : { producerRef }),
703
+ });
522
704
  }
523
705
  if (input.sharpnessProof === undefined) {
524
706
  throw new TypeError('verifier proof requires a sharpnessProof');
@@ -535,14 +717,19 @@ function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
535
717
  subject,
536
718
  ...primary,
537
719
  ...(criterionRef === undefined ? {} : { criterionRef }),
720
+ ...(producerRef === undefined ? {} : { producerRef }),
538
721
  sharpnessProof,
539
722
  });
540
723
  }
541
724
 
542
- function emptyActionEvidenceReceipt(turnId) {
725
+ function emptyActionEvidenceReceipt(turnId, producerRef) {
543
726
  const normalized = normalizedTurnId(turnId);
727
+ const producer = producerRef === undefined
728
+ ? undefined
729
+ : normalizedEvidenceProducerRef(producerRef);
544
730
  return Object.freeze({
545
731
  turnId: normalized,
732
+ ...(producer === undefined ? {} : { producerRef: producer }),
546
733
  completedTools: 0,
547
734
  successfulTools: 0,
548
735
  failedTools: 0,
@@ -550,7 +737,12 @@ function emptyActionEvidenceReceipt(turnId) {
550
737
  successfulToolCallDigests: Object.freeze([]),
551
738
  successfulVerificationCallDigests: Object.freeze([]),
552
739
  successfulVerificationScopeDigests: Object.freeze([]),
553
- digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
740
+ digest: crypto.createHash('sha256')
741
+ .update(producer === undefined
742
+ ? `turn:${normalized}:empty`
743
+ : `producer:${producer}\0turn:${normalized}:empty`)
744
+ .digest('hex')
745
+ .slice(0, 16),
554
746
  });
555
747
  }
556
748
 
@@ -559,7 +751,7 @@ function normalizeActionEvidenceReceipt(input) {
559
751
  const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
560
752
  const allowedKeys = new Set([
561
753
  ...requiredKeys, 'successfulToolDigests', 'successfulToolCallDigests',
562
- 'successfulVerificationCallDigests', 'successfulVerificationScopeDigests',
754
+ 'successfulVerificationCallDigests', 'successfulVerificationScopeDigests', 'producerRef',
563
755
  ]);
564
756
  if (!Object.keys(input).every((key) => allowedKeys.has(key))
565
757
  || ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
@@ -567,6 +759,9 @@ function normalizeActionEvidenceReceipt(input) {
567
759
  }
568
760
  const receipt = {
569
761
  turnId: normalizedTurnId(input.turnId),
762
+ ...(input.producerRef === undefined
763
+ ? {}
764
+ : { producerRef: normalizedEvidenceProducerRef(input.producerRef) }),
570
765
  completedTools: Number(input.completedTools),
571
766
  successfulTools: Number(input.successfulTools),
572
767
  failedTools: Number(input.failedTools),
@@ -660,11 +855,13 @@ function advanceActionEvidenceReceipt(current, input) {
660
855
  }
661
856
  }
662
857
  const digest = crypto.createHash('sha256').update([
663
- prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
858
+ prior.digest, prior.producerRef ?? 'legacy', String(turnId), toolCallId, toolName,
859
+ decision, outcome, String(durationMs),
664
860
  verificationKinds.join(','), verificationScopes.join(','),
665
861
  ].join('\0')).digest('hex').slice(0, 16);
666
862
  return Object.freeze({
667
863
  turnId,
864
+ ...(prior.producerRef === undefined ? {} : { producerRef: prior.producerRef }),
668
865
  completedTools: prior.completedTools + 1,
669
866
  successfulTools: prior.successfulTools + (successful ? 1 : 0),
670
867
  failedTools: prior.failedTools + (successful ? 0 : 1),
@@ -687,7 +884,7 @@ function assertActionCheckpointRevision(current, input) {
687
884
  return inputRevision;
688
885
  }
689
886
 
690
- function assertActionCheckpointEvidenceBasis(current, input, runtimeEvidence) {
887
+ function assertActionCheckpointEvidenceBasis(current, input, runtimeEvidence, runtimeExternalReports) {
691
888
  const basis = normalizedEvidenceBasis(input?.evidenceBasis);
692
889
  const epistemicState = normalizedEpistemicState(input?.epistemicState);
693
890
  if (basis === 'runtime_tool') {
@@ -699,8 +896,15 @@ function assertActionCheckpointEvidenceBasis(current, input, runtimeEvidence) {
699
896
  throw new TypeError('runtime_tool evidenceBasis requires verified epistemicState');
700
897
  }
701
898
  }
702
- if (basis === 'external_report' && epistemicState === 'verified') {
703
- throw new TypeError('external_report evidenceBasis cannot claim verified epistemicState');
899
+ if (basis === 'external_report') {
900
+ if (epistemicState === 'verified') {
901
+ throw new TypeError('external_report evidenceBasis cannot claim verified epistemicState');
902
+ }
903
+ if (runtimeExternalReports !== undefined) {
904
+ resolveExternalReportOrigin(input?.externalReportSource, runtimeExternalReports);
905
+ }
906
+ } else if (input?.externalReportSource !== undefined) {
907
+ throw new TypeError('externalReportSource requires external_report evidenceBasis');
704
908
  }
705
909
  if (basis === 'carried_forward') {
706
910
  if (!current) throw new TypeError('carried_forward evidenceBasis requires a current checkpoint');
@@ -751,6 +955,22 @@ function normalizeActionCheckpoint(input, options = {}) {
751
955
  if (input.nextTrigger !== undefined) checkpoint.nextTrigger = normalizeNextTrigger(input.nextTrigger, phase);
752
956
  else if (!replay) throw new TypeError('nextTrigger is required');
753
957
  if (input.problemFrame !== undefined) checkpoint.problemFrame = normalizeProblemFrame(input.problemFrame);
958
+ if (input.externalReportSource !== undefined && evidenceBasis !== 'external_report') {
959
+ throw new TypeError('externalReportSource requires external_report evidenceBasis');
960
+ }
961
+ if (evidenceBasis === 'external_report' && options.runtimeExternalReports !== undefined) {
962
+ checkpoint.externalReportOrigin = resolveExternalReportOrigin(
963
+ input.externalReportSource,
964
+ options.runtimeExternalReports,
965
+ );
966
+ } else if (replay && input.externalReportOrigin !== undefined) {
967
+ checkpoint.externalReportOrigin = normalizeExternalReportOrigin(input.externalReportOrigin);
968
+ } else if (evidenceBasis === 'carried_forward'
969
+ && options.previousCheckpoint?.externalReportOrigin !== undefined) {
970
+ checkpoint.externalReportOrigin = normalizeExternalReportOrigin(
971
+ options.previousCheckpoint.externalReportOrigin,
972
+ );
973
+ }
754
974
  const evidenceReceipt = options.runtimeEvidence !== undefined
755
975
  ? normalizeActionEvidenceReceipt(options.runtimeEvidence)
756
976
  : options.preserveRuntimeEvidence === true && input.evidenceReceipt !== undefined
@@ -797,7 +1017,10 @@ function projectActionCheckpoint(checkpoint) {
797
1017
  const criterion = value.verificationProof.criterionRef === undefined
798
1018
  ? ''
799
1019
  : ` [criterion: ${value.verificationProof.criterionRef}]`;
800
- lines.push(`Verification proof${subject}${kind}${scope}${criterion}: ${call} - ${value.verificationProof.claim}`);
1020
+ const producer = value.verificationProof.producerRef === undefined
1021
+ ? ''
1022
+ : ` [producer: ${value.verificationProof.producerRef}]`;
1023
+ lines.push(`Verification proof${subject}${kind}${scope}${criterion}${producer}: ${call} - ${value.verificationProof.claim}`);
801
1024
  if (value.verificationProof.sharpnessProof !== undefined) {
802
1025
  const sharpness = value.verificationProof.sharpnessProof;
803
1026
  const sharpnessKind = sharpness.kind === undefined ? '' : ` [${sharpness.kind}]`;
@@ -822,6 +1045,10 @@ function projectActionCheckpoint(checkpoint) {
822
1045
  lines.push(`Reversibility: ${frame.reversibility}`);
823
1046
  if (frame.decisionBasis !== undefined) lines.push(`Decision basis: ${frame.decisionBasis.join(' | ')}`);
824
1047
  }
1048
+ if (value.externalReportOrigin !== undefined) {
1049
+ const report = value.externalReportOrigin;
1050
+ lines.push(`External report origin: ${report.reporter} via ${report.source}; event ${report.eventRef}; occurred ${report.occurredAt}`);
1051
+ }
825
1052
  if (value.evidenceReceipt !== undefined) {
826
1053
  const receipt = value.evidenceReceipt;
827
1054
  lines.push(`Runtime evidence: turn ${receipt.turnId}; ${receipt.completedTools} completed, ${receipt.successfulTools} successful, ${receipt.failedTools} failed; digest ${receipt.digest}`);
@@ -839,7 +1066,10 @@ module.exports = {
839
1066
  assertActionCheckpointEvidenceBasis,
840
1067
  assertActionCheckpointRevision,
841
1068
  completionCriterionRef,
1069
+ evidenceProducerRef,
842
1070
  emptyActionEvidenceReceipt,
1071
+ externalReportSourceFromChannelMeta,
1072
+ externalReportSourcesFromOrigin,
843
1073
  normalizeActionCheckpoint,
844
1074
  normalizeVerificationProof,
845
1075
  projectActionCheckpoint,
@@ -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, externalReportSourcesFromOrigin, 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 ?? []);
@@ -230281,9 +230281,11 @@ var init_goal$1 = __esmMin((() => {
230281
230281
  if (input.actionCheckpoint !== void 0) {
230282
230282
  assertActionCheckpointRevision(undefined, input.actionCheckpoint);
230283
230283
  const runtimeEvidence = this.agent.turn.actionEvidenceReceiptForCurrentTurn();
230284
- assertActionCheckpointEvidenceBasis(undefined, input.actionCheckpoint, runtimeEvidence);
230284
+ const runtimeExternalReports = this.agent.turn.externalReportSourcesForCurrentTurn();
230285
+ assertActionCheckpointEvidenceBasis(undefined, input.actionCheckpoint, runtimeEvidence, runtimeExternalReports);
230285
230286
  state.actionCheckpoint = normalizeActionCheckpoint(input.actionCheckpoint, {
230286
- runtimeEvidence
230287
+ runtimeEvidence,
230288
+ runtimeExternalReports
230287
230289
  });
230288
230290
  }
230289
230291
  this.persistState(state);
@@ -230365,9 +230367,12 @@ var init_goal$1 = __esmMin((() => {
230365
230367
  if (state.status !== "active") throw new BlunError(ErrorCodes.GOAL_STATUS_INVALID, `Cannot checkpoint a goal in status "${state.status}"`);
230366
230368
  assertActionCheckpointRevision(state.actionCheckpoint, input);
230367
230369
  const runtimeEvidence = this.agent.turn.actionEvidenceReceiptForCurrentTurn();
230368
- assertActionCheckpointEvidenceBasis(state.actionCheckpoint, input, runtimeEvidence);
230370
+ const runtimeExternalReports = this.agent.turn.externalReportSourcesForCurrentTurn();
230371
+ assertActionCheckpointEvidenceBasis(state.actionCheckpoint, input, runtimeEvidence, runtimeExternalReports);
230369
230372
  state.actionCheckpoint = normalizeActionCheckpoint(input, {
230370
- runtimeEvidence
230373
+ runtimeEvidence,
230374
+ runtimeExternalReports,
230375
+ previousCheckpoint: state.actionCheckpoint
230371
230376
  });
230372
230377
  this.persistState(state, { change: { kind: "progress", actor } });
230373
230378
  this.appendGoalUpdate({ actionCheckpoint: state.actionCheckpoint, actor });
@@ -230425,7 +230430,9 @@ var init_goal$1 = __esmMin((() => {
230425
230430
  async markComplete(input = {}, actor = "model") {
230426
230431
  const state = this.state;
230427
230432
  if (state === void 0 || state.status !== "active") return null;
230428
- const completionEvidence = evaluateGoalCompletionEvidence(state);
230433
+ const completionEvidence = evaluateGoalCompletionEvidence(state, {
230434
+ expectedProducerRef: this.agent.evidenceProducerRef
230435
+ });
230429
230436
  if (!completionEvidence.allPassed) {
230430
230437
  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
230438
  }
@@ -245790,6 +245797,13 @@ var init_events$1 = __esmMin((() => {
245790
245797
  phase: _enum(["orient", "plan", "act", "verify", "learn", "wait"]),
245791
245798
  evidenceBasis: _enum(["runtime_tool", "user_statement", "external_report", "carried_forward", "legacy_unknown"]),
245792
245799
  epistemicState: _enum(["verified", "credible_unverified", "hypothesis", "uncertain_memory", "stale", "unknown", "legacy_unknown"]),
245800
+ externalReportOrigin: object({
245801
+ source: _enum(["telegram"]),
245802
+ reporter: string(),
245803
+ reporterRef: string().regex(/^[a-f0-9]{16}$/u),
245804
+ eventRef: string().regex(/^[a-f0-9]{16}$/u),
245805
+ occurredAt: string()
245806
+ }).strict().optional(),
245793
245807
  lastVerified: string(),
245794
245808
  nextAction: string(),
245795
245809
  expectedEvidence: string(),
@@ -245812,6 +245826,7 @@ var init_events$1 = __esmMin((() => {
245812
245826
  updatedAt: string(),
245813
245827
  evidenceReceipt: object({
245814
245828
  turnId: number$1().int().min(0),
245829
+ producerRef: string().regex(/^[a-f0-9]{16}$/u).optional(),
245815
245830
  completedTools: number$1().int().min(0),
245816
245831
  successfulTools: number$1().int().min(0),
245817
245832
  failedTools: number$1().int().min(0),
@@ -260308,6 +260323,7 @@ var create_goal_default;
260308
260323
  var init_create_goal$1 = __esmMin((() => {
260309
260324
  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
260325
  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";
260326
+ create_goal_default += "\nWhen the checkpoint rests on a forwarded Telegram report, use `external_report` with `credible_unverified` and select the exact visible channel event in `externalReportSource` with its `source`, `chat_id` as `chatId`, and `message_id` as `messageId`. The runtime binds the reporter and event reference; never invent them.\n";
260311
260327
  }));
260312
260328
  //#endregion
260313
260329
  //#region ../../packages/agent-core/src/tools/builtin/goal/serialize.ts
@@ -260345,6 +260361,11 @@ function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFra
260345
260361
  phase: _enum(["orient", "plan", "act", "verify", "learn", "wait"]),
260346
260362
  evidenceBasis: _enum(["runtime_tool", "user_statement", "external_report", "carried_forward"]),
260347
260363
  epistemicState: _enum(["verified", "credible_unverified", "hypothesis", "uncertain_memory", "stale", "unknown"]),
260364
+ externalReportSource: object({
260365
+ source: _enum(["telegram"]),
260366
+ chatId: string().min(1).max(64),
260367
+ messageId: string().min(1).max(64)
260368
+ }).strict().optional(),
260348
260369
  lastVerified: string().min(1).max(512),
260349
260370
  nextAction: string().min(1).max(512),
260350
260371
  expectedEvidence: string().min(1).max(512),
@@ -261711,6 +261732,12 @@ function explicitToolResultEvidenceScopes(result) {
261711
261732
  function abandonedToolResultOutput(ended) {
261712
261733
  return `Tool call did not complete: ${ended.reason === "cancelled" ? "the turn was cancelled" : ended.reason === "failed" ? `the turn failed${ended.error !== void 0 ? ` (${ended.error.message})` : ""}` : "the turn ended"} before its result was recorded. Do not assume the tool completed successfully.`;
261713
261734
  }
261735
+ function durablePromptOrigin(origin) {
261736
+ if (!origin || typeof origin !== "object" || !Object.hasOwn(origin, "externalReportSources")) return origin;
261737
+ const { externalReportSources, ...durableOrigin } = origin;
261738
+ void externalReportSources;
261739
+ return durableOrigin;
261740
+ }
261714
261741
  var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaToolNamesForTurnText, rankedSupportToolNamesForGoal, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261715
261742
  var init_turn = __esmMin((() => {
261716
261743
  init_dist$4();
@@ -261800,6 +261827,7 @@ var init_turn = __esmMin((() => {
261800
261827
  cognitiveToolPolicyByCall = /* @__PURE__ */ new Map();
261801
261828
  cognitiveToolBatchesByTurn = /* @__PURE__ */ new Map();
261802
261829
  cognitiveActionEvidenceByTurn = /* @__PURE__ */ new Map();
261830
+ cognitiveExternalReportsByTurn = /* @__PURE__ */ new Map();
261803
261831
  constructor(agent) {
261804
261832
  this.agent = agent;
261805
261833
  }
@@ -261846,7 +261874,7 @@ var init_turn = __esmMin((() => {
261846
261874
  return;
261847
261875
  }
261848
261876
  this.cognitiveToolPolicyByCall.delete(input.toolCallId);
261849
- const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId);
261877
+ const currentEvidence = this.cognitiveActionEvidenceByTurn.get(input.turnId) ?? emptyActionEvidenceReceipt(input.turnId, this.agent.evidenceProducerRef);
261850
261878
  this.cognitiveActionEvidenceByTurn.set(input.turnId, advanceActionEvidenceReceipt(currentEvidence, {
261851
261879
  ...input,
261852
261880
  decision: policy.decision
@@ -261877,7 +261905,24 @@ var init_turn = __esmMin((() => {
261877
261905
  }
261878
261906
  actionEvidenceReceiptForCurrentTurn() {
261879
261907
  const turnId = this.currentId;
261880
- return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId);
261908
+ return this.cognitiveActionEvidenceByTurn.get(turnId) ?? emptyActionEvidenceReceipt(turnId, this.agent.evidenceProducerRef);
261909
+ }
261910
+ recordExternalReportSources(turnId, origin) {
261911
+ const incoming = externalReportSourcesFromOrigin(origin);
261912
+ if (incoming.length === 0) return;
261913
+ const current = this.cognitiveExternalReportsByTurn.get(turnId) ?? [];
261914
+ const merged = [...current];
261915
+ const seen = new Set(current.map((source) => `${source.source}\0${source.chatId}\0${source.messageId}`));
261916
+ for (const source of incoming) {
261917
+ const key = `${source.source}\0${source.chatId}\0${source.messageId}`;
261918
+ if (seen.has(key)) continue;
261919
+ seen.add(key);
261920
+ merged.push(source);
261921
+ }
261922
+ this.cognitiveExternalReportsByTurn.set(turnId, Object.freeze(merged.slice(-32)));
261923
+ }
261924
+ externalReportSourcesForCurrentTurn() {
261925
+ return this.cognitiveExternalReportsByTurn.get(this.currentId) ?? Object.freeze([]);
261881
261926
  }
261882
261927
  projectCognitiveState(turnId, input) {
261883
261928
  try {
@@ -261933,7 +261978,7 @@ var init_turn = __esmMin((() => {
261933
261978
  this.agent.records.logRecord({
261934
261979
  type: "turn.prompt",
261935
261980
  input,
261936
- origin
261981
+ origin: durablePromptOrigin(origin)
261937
261982
  });
261938
261983
  const buffered = this.agent.fullCompaction.isCompacting;
261939
261984
  const turnId = this.launch(input, origin);
@@ -261952,7 +261997,7 @@ var init_turn = __esmMin((() => {
261952
261997
  this.agent.records.logRecord({
261953
261998
  type: "turn.steer",
261954
261999
  input,
261955
- origin
262000
+ origin: durablePromptOrigin(origin)
261956
262001
  });
261957
262002
  if (this.activeTurn || this.agent.fullCompaction.isCompacting) {
261958
262003
  this.bufferSteer(input, origin);
@@ -261975,7 +262020,7 @@ var init_turn = __esmMin((() => {
261975
262020
  this.agent.records.logRecord({
261976
262021
  type: "turn.steer",
261977
262022
  input,
261978
- origin
262023
+ origin: durablePromptOrigin(origin)
261979
262024
  });
261980
262025
  this.bufferSteer(input, origin, this.currentId);
261981
262026
  return {
@@ -262107,6 +262152,9 @@ var init_turn = __esmMin((() => {
262107
262152
  return this.flushSteerBuffer(turnId, snapshot.throughSequence);
262108
262153
  }
262109
262154
  bufferSteer(input, origin, targetTurnId) {
262155
+ const active = this.activeTurn;
262156
+ const reportTurnId = targetTurnId ?? (active !== null && active !== "resuming" ? active.turnId : void 0);
262157
+ if (reportTurnId !== void 0) this.recordExternalReportSources(reportTurnId, origin);
262110
262158
  this.steerBuffer.push({
262111
262159
  sequence: this.nextSteerSequence,
262112
262160
  input,
@@ -262124,7 +262172,7 @@ var init_turn = __esmMin((() => {
262124
262172
  remaining.push(steer);
262125
262173
  continue;
262126
262174
  }
262127
- this.agent.context.appendUserMessage(steer.input, steer.origin);
262175
+ this.agent.context.appendUserMessage(steer.input, durablePromptOrigin(steer.origin));
262128
262176
  flushed = true;
262129
262177
  }
262130
262178
  this.steerBuffer = remaining;
@@ -262277,6 +262325,8 @@ var init_turn = __esmMin((() => {
262277
262325
  const telemetryMode = this.telemetryMode();
262278
262326
  this.telemetryModeByTurn.set(turnId, telemetryMode);
262279
262327
  this.currentStepByTurn.set(turnId, 0);
262328
+ this.recordExternalReportSources(turnId, origin);
262329
+ const persistedOrigin = durablePromptOrigin(origin);
262280
262330
  this.agent.telemetry.track("turn_started", {
262281
262331
  mode: telemetryMode,
262282
262332
  ...this.requestProviderProps()
@@ -262287,9 +262337,9 @@ var init_turn = __esmMin((() => {
262287
262337
  this.agent.emitEvent({
262288
262338
  type: "turn.started",
262289
262339
  turnId,
262290
- origin
262340
+ origin: persistedOrigin
262291
262341
  });
262292
- this.agent.context.appendUserMessage(input, origin);
262342
+ this.agent.context.appendUserMessage(input, persistedOrigin);
262293
262343
  this.recordCognitiveStage("startTurn", { turnId, originKind: origin.kind });
262294
262344
  const startedAt = Date.now();
262295
262345
  let ended;
@@ -262297,7 +262347,7 @@ var init_turn = __esmMin((() => {
262297
262347
  let completedStopReason;
262298
262348
  let errorEvent;
262299
262349
  try {
262300
- const promptHookEnded = await this.applyUserPromptHook(turnId, input, origin, signal, startedAt);
262350
+ const promptHookEnded = await this.applyUserPromptHook(turnId, input, persistedOrigin, signal, startedAt);
262301
262351
  this.recordCognitiveStage("recordRightsCheck", {
262302
262352
  turnId,
262303
262353
  decision: origin.kind !== "user" ? "not_applicable" : promptHookEnded?.blocked === true ? "blocked" : "passed",
@@ -262307,7 +262357,7 @@ var init_turn = __esmMin((() => {
262307
262357
  ended = promptHookEnded.event;
262308
262358
  blockedByUserPromptHook = promptHookEnded.blocked;
262309
262359
  } else {
262310
- const stopReason = await this.runStepLoop(turnId, signal, input, origin);
262360
+ const stopReason = await this.runStepLoop(turnId, signal, input, persistedOrigin);
262311
262361
  completedStopReason = stopReason;
262312
262362
  ended = {
262313
262363
  type: "turn.ended",
@@ -262386,6 +262436,7 @@ var init_turn = __esmMin((() => {
262386
262436
  this.currentStepByTurn.delete(turnId);
262387
262437
  this.interruptedTelemetryTurnIds.delete(turnId);
262388
262438
  this.cognitiveActionEvidenceByTurn.delete(turnId);
262439
+ this.cognitiveExternalReportsByTurn.delete(turnId);
262389
262440
  this.stepFailureByTurn.delete(turnId);
262390
262441
  await this.agent.records.flush();
262391
262442
  return {
@@ -262919,6 +262970,7 @@ var init_update_goal$1 = __esmMin((() => {
262919
262970
  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
262971
  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
262972
  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";
262973
+ 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. For a fresh Telegram report, copy the exact visible channel `source`, `chat_id` as `chatId`, and `message_id` as `messageId` into `externalReportSource`. The runtime, not the model, binds the reporter and event reference. A `carried_forward` checkpoint retains that origin automatically.\n";
262922
262974
  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
262975
  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
262976
  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 +265415,7 @@ var init_agent = __esmMin((() => {
265363
265415
  blunConfig;
265364
265416
  blunHomeDir;
265365
265417
  homedir;
265418
+ evidenceProducerRef;
265366
265419
  mediaOriginalsDir;
265367
265420
  rpc;
265368
265421
  toolServices;
@@ -265418,6 +265471,7 @@ var init_agent = __esmMin((() => {
265418
265471
  this.blunConfig = options.config;
265419
265472
  this.blunHomeDir = options.blunHomeDir;
265420
265473
  this.homedir = options.homedir;
265474
+ this.evidenceProducerRef = options.evidenceProducerRef;
265421
265475
  this.mediaOriginalsDir = options.mediaOriginalsDir;
265422
265476
  this.rpc = options.rpc;
265423
265477
  this.toolServices = options.toolServices;
@@ -265668,9 +265722,9 @@ var init_agent = __esmMin((() => {
265668
265722
  get rpcMethods() {
265669
265723
  return {
265670
265724
  prompt: (payload) => {
265671
- this.turn.promptWithAcceptance(payload.input);
265725
+ this.turn.promptWithAcceptance(payload.input, payload.origin ?? USER_PROMPT_ORIGIN);
265672
265726
  },
265673
- promptAccepted: (payload) => this.turn.promptWithAcceptance(payload.input),
265727
+ promptAccepted: (payload) => this.turn.promptWithAcceptance(payload.input, payload.origin ?? USER_PROMPT_ORIGIN),
265674
265728
  runShellCommand: (payload) => this.tools.runShellCommand(payload.command, payload.commandId),
265675
265729
  cancelShellCommand: (payload) => this.tools.cancelShellCommand(payload.commandId),
265676
265730
  steer: (payload) => {
@@ -298011,6 +298065,7 @@ var init_session$1 = __esmMin((() => {
298011
298065
  config: this.options.config,
298012
298066
  blunHomeDir: this.options.blunHomeDir,
298013
298067
  homedir,
298068
+ evidenceProducerRef: evidenceProducerRef(`${this.options.id ?? this.options.homedir}\0${id}`),
298014
298069
  mediaOriginalsDir: sessionMediaOriginalsDir(this.options.homedir),
298015
298070
  onMediaDropped: (dropped) => {
298016
298071
  for (const part of dropped) this.rpc.emitEvent({
@@ -326623,17 +326678,20 @@ var Session = class {
326623
326678
  async prompt(input) {
326624
326679
  await this.promptAccepted(input);
326625
326680
  }
326626
- async promptAccepted(input) {
326681
+ async promptAccepted(input, origin = USER_PROMPT_ORIGIN) {
326627
326682
  this.ensureOpen();
326628
326683
  const normalized = normalizePromptInput(input);
326684
+ const normalizedOrigin = origin && typeof origin === "object" ? origin : USER_PROMPT_ORIGIN;
326629
326685
  const hasPromptAccepted = this.rpc.promptAccepted !== void 0;
326630
326686
  return this.trackUserInputAcceptance(normalized, hasPromptAccepted ? () => this.rpc.promptAccepted({
326631
326687
  sessionId: this.id,
326632
- input: normalized
326688
+ input: normalized,
326689
+ origin: normalizedOrigin
326633
326690
  }) : async () => {
326634
326691
  await this.rpc.prompt({
326635
326692
  sessionId: this.id,
326636
- input: normalized
326693
+ input: normalized,
326694
+ origin: normalizedOrigin
326637
326695
  });
326638
326696
  return {
326639
326697
  accepted: true,
@@ -327902,7 +327960,8 @@ var SDKRpcClientBase = class {
327902
327960
  await rpc.prompt({
327903
327961
  sessionId: input.sessionId,
327904
327962
  agentId,
327905
- input: input.input
327963
+ input: input.input,
327964
+ origin: input.origin
327906
327965
  });
327907
327966
  return {
327908
327967
  accepted: true,
@@ -327913,7 +327972,8 @@ var SDKRpcClientBase = class {
327913
327972
  return rpc.promptAccepted({
327914
327973
  sessionId: input.sessionId,
327915
327974
  agentId,
327916
- input: input.input
327975
+ input: input.input,
327976
+ origin: input.origin
327917
327977
  });
327918
327978
  }
327919
327979
  async runShellCommand(input) {
@@ -419155,12 +419215,14 @@ const CONTEXT_TRUNCATED_MARKER = "\n[Telegram-Kontext gekuerzt]";
419155
419215
  function createChannelPreambleState() {
419156
419216
  return {
419157
419217
  sent: false,
419158
- bufferedByChat: /* @__PURE__ */ new Map()
419218
+ bufferedByChat: /* @__PURE__ */ new Map(),
419219
+ reportSourcesByChat: /* @__PURE__ */ new Map()
419159
419220
  };
419160
419221
  }
419161
419222
  function resetChannelPreambleState(state) {
419162
419223
  state.sent = false;
419163
419224
  state.bufferedByChat?.clear();
419225
+ state.reportSourcesByChat?.clear();
419164
419226
  }
419165
419227
  /** Structured metadata is authoritative; old queue envelopes use the tag note. */
419166
419228
  function channelMessageAddressed(envelope) {
@@ -419170,21 +419232,35 @@ function channelMessageAddressed(envelope) {
419170
419232
  if (addressed === "semantic") return true;
419171
419233
  return !envelope.tag.includes("NUR MITLESEN");
419172
419234
  }
419173
- function bufferContext(state, chatId, tag) {
419235
+ function bufferContext(state, chatId, tag, reportSource) {
419174
419236
  const buffers = state.bufferedByChat ?? /* @__PURE__ */ new Map();
419175
419237
  state.bufferedByChat = buffers;
419238
+ const reportBuffers = state.reportSourcesByChat ?? /* @__PURE__ */ new Map();
419239
+ state.reportSourcesByChat = reportBuffers;
419176
419240
  const messages = buffers.get(chatId) ?? [];
419241
+ const reports = reportBuffers.get(chatId) ?? [];
419177
419242
  const projectedTag = projectUnaddressedTelegramContext(tag);
419178
419243
  const boundedTag = projectedTag.length <= MAX_BUFFERED_CONTEXT_CHARS ? projectedTag : `${projectedTag.slice(0, MAX_BUFFERED_CONTEXT_CHARS - 28)}${CONTEXT_TRUNCATED_MARKER}`;
419179
419244
  messages.push(boundedTag);
419245
+ reports.push(reportSource);
419180
419246
  let chars = messages.reduce((sum, message) => sum + message.length, 0);
419181
- while (messages.length > MAX_BUFFERED_CONTEXT_MESSAGES || chars > MAX_BUFFERED_CONTEXT_CHARS) chars -= messages.shift()?.length ?? 0;
419247
+ while (messages.length > MAX_BUFFERED_CONTEXT_MESSAGES || chars > MAX_BUFFERED_CONTEXT_CHARS) {
419248
+ chars -= messages.shift()?.length ?? 0;
419249
+ reports.shift();
419250
+ }
419182
419251
  buffers.set(chatId, messages);
419252
+ reportBuffers.set(chatId, reports);
419183
419253
  }
419184
419254
  /** Muted origin prefix for the transcript line, e.g. "Telegram · User". */
419185
419255
  function channelOrigin(envelope) {
419186
419256
  return `Telegram · ${envelope.meta.user ?? envelope.meta.chat_id}${envelope.meta["priority"] === "urgent" ? " · Dringend" : ""}`;
419187
419257
  }
419258
+ function channelPromptOrigin(channelReportSources) {
419259
+ return {
419260
+ kind: "user",
419261
+ externalReportSources: channelReportSources
419262
+ };
419263
+ }
419188
419264
  const TELEGRAM_REMOTE_COMMANDS = Object.freeze(["loop", "goal", "idea", "chancenradar", "curiosity", "scout", "reload", "memory", "befehle"]);
419189
419265
  function telegramRemoteCommand(text) {
419190
419266
  const trimmed = text.trim();
@@ -419206,24 +419282,35 @@ function telegramRemoteCommand(text) {
419206
419282
  function injectChannelEnvelope(host, envelope, preamble) {
419207
419283
  const origin = channelOrigin(envelope);
419208
419284
  const contextOnly = !channelMessageAddressed(envelope);
419285
+ const reportSource = {
419286
+ source: "telegram",
419287
+ chatId: envelope.meta.chat_id,
419288
+ messageId: envelope.meta.message_id,
419289
+ reporter: envelope.meta.user,
419290
+ reporterId: envelope.meta.user_id,
419291
+ occurredAt: envelope.meta.ts
419292
+ };
419209
419293
  if (contextOnly) {
419210
- bufferContext(preamble, envelope.meta.chat_id, envelope.tag);
419294
+ bufferContext(preamble, envelope.meta.chat_id, envelope.tag, reportSource);
419211
419295
  host.displayContext?.(envelope.text, origin);
419212
419296
  return "buffered";
419213
419297
  }
419214
419298
  const modelParts = [...preamble.bufferedByChat?.get(envelope.meta.chat_id) ?? [], envelope.tag];
419299
+ const channelReportSources = [...preamble.reportSourcesByChat?.get(envelope.meta.chat_id) ?? [], reportSource];
419215
419300
  if (!preamble.sent && envelope.preamble !== void 0 && envelope.preamble.length > 0) {
419216
419301
  modelParts.unshift(envelope.preamble);
419217
419302
  preamble.sent = true;
419218
419303
  }
419219
419304
  const modelInput = modelParts.join("\n\n");
419220
419305
  if (!host.canDeliver() || host.isBusy()) {
419221
- host.enqueue(modelInput, envelope.text, origin, contextOnly);
419306
+ host.enqueue(modelInput, envelope.text, origin, contextOnly, channelReportSources);
419222
419307
  preamble.bufferedByChat?.delete(envelope.meta.chat_id);
419308
+ preamble.reportSourcesByChat?.delete(envelope.meta.chat_id);
419223
419309
  return "queued";
419224
419310
  }
419225
- host.deliverNow(modelInput, envelope.text, origin, contextOnly);
419311
+ host.deliverNow(modelInput, envelope.text, origin, contextOnly, channelReportSources);
419226
419312
  preamble.bufferedByChat?.delete(envelope.meta.chat_id);
419313
+ preamble.reportSourcesByChat?.delete(envelope.meta.chat_id);
419227
419314
  return "delivered";
419228
419315
  }
419229
419316
  /** Parse one queue line defensively — a malformed line must never crash the TUI. */
@@ -517649,10 +517736,10 @@ var BlunTUI = class {
517649
517736
  injectChannelEnvelope({
517650
517737
  canDeliver: () => this.session !== void 0 && this.state.appState.model.trim().length > 0,
517651
517738
  isBusy: () => this.state.queuedMessages.length > 0 || this.queueSteerInFlight !== void 0 || this.deferUserMessages || this.streamingUI.hasActiveTurn() || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting,
517652
- deliverNow: (modelInput, displayText, origin, contextOnly) => {
517653
- this.sendChannelMessageInternal(this.requireSession(), modelInput, displayText, origin, routedEnvelope.meta.chat_id, routedEnvelope.meta["image_path"], contextOnly, false, acknowledge, false, void 0, directEnvelope !== void 0, directFocus.resumeGranted);
517739
+ deliverNow: (modelInput, displayText, origin, contextOnly, channelReportSources) => {
517740
+ this.sendChannelMessageInternal(this.requireSession(), modelInput, displayText, origin, routedEnvelope.meta.chat_id, routedEnvelope.meta["image_path"], contextOnly, false, acknowledge, false, void 0, directEnvelope !== void 0, directFocus.resumeGranted, channelReportSources);
517654
517741
  },
517655
- enqueue: (modelInput, displayText, origin, contextOnly) => {
517742
+ enqueue: (modelInput, displayText, origin, contextOnly, channelReportSources) => {
517656
517743
  const item = {
517657
517744
  text: modelInput,
517658
517745
  displayText,
@@ -517660,6 +517747,7 @@ var BlunTUI = class {
517660
517747
  agentId: this.harness.interactiveAgentId,
517661
517748
  mode: "channel",
517662
517749
  channelChatId: routedEnvelope.meta.chat_id,
517750
+ channelReportSources: channelReportSources,
517663
517751
  channelContextOnly: contextOnly,
517664
517752
  channelAcknowledge: acknowledge,
517665
517753
  ...urgentEnvelope !== void 0 ? { channelUrgent: true } : {},
@@ -517819,7 +517907,7 @@ var BlunTUI = class {
517819
517907
  this.failSessionRequest(uiText("blunTui.session.sendFailed", { error: formatErrorMessage$2(error) }));
517820
517908
  });
517821
517909
  }
517822
- sendChannelMessageInternal(session, modelInput, displayText, origin, channelChatId, channelImagePath, contextOnly = false, transcriptRendered = false, acknowledge, channelAttention = false, queueKey, channelDirect = false, channelDirectResume = false) {
517910
+ sendChannelMessageInternal(session, modelInput, displayText, origin, channelChatId, channelImagePath, contextOnly = false, transcriptRendered = false, acknowledge, channelAttention = false, queueKey, channelDirect = false, channelDirectResume = false, channelReportSources = []) {
517823
517911
  armPersonalMemoryRememberIntent(session.id, displayText, {
517824
517912
  permissionMode: this.state.appState.permissionMode,
517825
517913
  channel: true
@@ -517853,7 +517941,7 @@ var BlunTUI = class {
517853
517941
  type: "text",
517854
517942
  text: focusedModelInput
517855
517943
  }, imagePart] : focusedModelInput;
517856
- session.promptAccepted(promptInput).then((result) => {
517944
+ session.promptAccepted(promptInput, channelPromptOrigin(channelReportSources)).then((result) => {
517857
517945
  if (result.accepted) {
517858
517946
  acknowledge?.();
517859
517947
  return;
@@ -517866,6 +517954,7 @@ var BlunTUI = class {
517866
517954
  agentId: this.harness.interactiveAgentId,
517867
517955
  mode: "channel",
517868
517956
  channelChatId,
517957
+ channelReportSources: channelReportSources,
517869
517958
  channelContextOnly: contextOnly,
517870
517959
  channelTranscriptRendered: true,
517871
517960
  channelAcknowledge: acknowledge,
@@ -518229,7 +518318,7 @@ var BlunTUI = class {
518229
518318
  const activeSession = this.session ?? session;
518230
518319
  if (item.mode === "channel") {
518231
518320
  this.harness.withInteractiveAgent(item.agentId ?? "main", () => {
518232
- this.sendChannelMessageInternal(activeSession, item.text, item.displayText ?? "", item.origin, item.channelChatId, item.channelImagePath, item.channelContextOnly, item.channelTranscriptRendered, item.channelAcknowledge, item.channelAttention, item.queueKey, item.channelDirect, item.channelDirectResume);
518321
+ this.sendChannelMessageInternal(activeSession, item.text, item.displayText ?? "", item.origin, item.channelChatId, item.channelImagePath, item.channelContextOnly, item.channelTranscriptRendered, item.channelAcknowledge, item.channelAttention, item.queueKey, item.channelDirect, item.channelDirectResume, item.channelReportSources);
518233
518322
  });
518234
518323
  return;
518235
518324
  }
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.455",
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": {