blun-king-cli 9.1.449 → 9.1.451
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.
- package/bin/cognitive-action-checkpoint.cjs +189 -24
- package/bin/goal-completion-evidence-policy.cjs +12 -0
- package/blun.mjs +36 -4
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ 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']);
|
|
16
|
+
const VERIFICATION_KINDS = new Set(['inspection', 'integrity', 'syntax', 'test', 'reachability']);
|
|
15
17
|
const MODEL_KEYS = new Set([
|
|
16
18
|
'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'verificationProof', 'nextTrigger', 'problemFrame', 'updatedAt',
|
|
17
19
|
]);
|
|
@@ -23,6 +25,7 @@ const PROBLEM_FRAME_KEYS = new Set([
|
|
|
23
25
|
const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
|
|
24
26
|
const EVIDENCE_INPUT_KEYS = new Set([
|
|
25
27
|
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs', 'toolArgs',
|
|
28
|
+
'resultEvidenceKinds',
|
|
26
29
|
]);
|
|
27
30
|
const REQUIRED_EVIDENCE_INPUT_KEYS = new Set([
|
|
28
31
|
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
|
|
@@ -35,14 +38,19 @@ const ACTION_ONLY_TOOL_NAMES = new Set([
|
|
|
35
38
|
'ExitPlanMode', 'GenerateImage', 'GenerateSpeech', 'GenerateVideo', 'LipSyncMedia',
|
|
36
39
|
'MistakeRecord', 'SetGoalBudget', 'TaskStop', 'TaskUpdate', 'UpdateGoal', 'Write',
|
|
37
40
|
]);
|
|
38
|
-
const
|
|
41
|
+
const INSPECTION_TOOL_NAMES = new Set([
|
|
39
42
|
'codebasesearch', 'cronlist', 'fetchurl', 'getgoal', 'getmedia', 'glob', 'grep',
|
|
40
|
-
'read', 'readmediafile', 'taskoutput', '
|
|
43
|
+
'read', 'readmediafile', 'taskoutput', 'understandimage', 'understandvideo',
|
|
41
44
|
'websearch',
|
|
42
45
|
]);
|
|
46
|
+
const TEST_TOOL_NAMES = new Set(['test']);
|
|
43
47
|
const COMMAND_TOOL_NAMES = new Set(['bash', 'command', 'exec_command', 'shell']);
|
|
44
48
|
const MUTATING_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:cp|mv|rm|mkdir|touch|tee|install|scp|sftp|ssh)\b|(?:sed\s+-i\b)|(?:git\s+(?:add|commit|push|checkout|switch|reset|clean|merge|rebase)\b)|(?:Set-Content|Add-Content|Copy-Item|Move-Item|Remove-Item|New-Item|Start-Process)\b)/iu;
|
|
45
|
-
const
|
|
49
|
+
const INSPECTION_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:git\s+(?:status|rev-parse|show))|(?:wc|rg|grep|cat|ls|stat)\b|(?:Get-Content|Get-Item|Test-Path|Measure-Object)\b)/iu;
|
|
50
|
+
const INTEGRITY_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:git\s+(?:diff(?:\s+--check)?|fsck))|(?:sha(?:1|256|512)sum|shasum|cmp|diff)\b|(?:certutil\s+-hashfile)|(?:Get-FileHash|Compare-Object)\b)/iu;
|
|
51
|
+
const SYNTAX_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:node\s+--check\b)|(?:(?:npx\s+)?tsc\b)|(?:(?:npm|pnpm|yarn)\s+(?:(?:run\s+)?typecheck)\b))/iu;
|
|
52
|
+
const TEST_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:node\s+--test\b)|(?:node\s+(?!--)[^\s;&|]*(?:check|test|verify|validate|lint|gate|probe)[^\s;&|]*\b)|(?:(?:npm|pnpm|yarn)\s+(?:test|(?:run\s+)?(?:test|lint|check|build))\b)|(?:python(?:3)?\s+-m\s+pytest\b)|(?:pytest\b)|(?:go\s+test\b)|(?:cargo\s+test\b)|(?:dotnet\s+test\b)|(?:(?:npx\s+)?(?:eslint|biome\s+check)\b))/iu;
|
|
53
|
+
const RUNTIME_EVIDENCE_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:node\s+(?!--check\b)(?:-e\b|[^\s;&|]+)|(?:python(?:3)?\s+(?!-m\s+pytest\b)[^\s;&|]+)|(?:npm|pnpm|yarn)\s+(?:test|run\b)|pytest\b|go\s+test\b|cargo\s+test\b|dotnet\s+test\b)/iu;
|
|
46
54
|
const MAX_SUCCESSFUL_TOOL_DIGESTS = 32;
|
|
47
55
|
|
|
48
56
|
function bounded(value, field, max = 512) {
|
|
@@ -180,6 +188,17 @@ function successfulToolCallDigest(turnId, toolCallId, toolName) {
|
|
|
180
188
|
.slice(0, 16);
|
|
181
189
|
}
|
|
182
190
|
|
|
191
|
+
function successfulVerificationCallDigest(turnId, toolCallId, toolName, kind) {
|
|
192
|
+
const turn = normalizedTurnId(turnId);
|
|
193
|
+
const callId = bounded(toolCallId, 'verificationProof toolCallId', 256);
|
|
194
|
+
const name = bounded(toolName, 'verificationProof toolName', 128);
|
|
195
|
+
const normalizedKind = normalizedVerificationKind(kind);
|
|
196
|
+
return crypto.createHash('sha256')
|
|
197
|
+
.update(`turn:${turn}\0call:${callId}\0tool:${name}\0kind:${normalizedKind}`)
|
|
198
|
+
.digest('hex')
|
|
199
|
+
.slice(0, 16);
|
|
200
|
+
}
|
|
201
|
+
|
|
183
202
|
function isActionOnlyTool(toolName) {
|
|
184
203
|
return ACTION_ONLY_TOOL_NAMES.has(String(toolName ?? '').trim());
|
|
185
204
|
}
|
|
@@ -190,14 +209,45 @@ function verificationCommand(toolArgs) {
|
|
|
190
209
|
return typeof command === 'string' ? command.trim().replace(/\s+/gu, ' ') : '';
|
|
191
210
|
}
|
|
192
211
|
|
|
193
|
-
function
|
|
212
|
+
function verificationKindsForToolCall(toolName, toolArgs) {
|
|
213
|
+
const normalizedName = String(toolName ?? '').trim().toLowerCase();
|
|
214
|
+
if (INSPECTION_TOOL_NAMES.has(normalizedName)) return Object.freeze(['inspection']);
|
|
215
|
+
if (TEST_TOOL_NAMES.has(normalizedName)) return Object.freeze(['test']);
|
|
216
|
+
if (!COMMAND_TOOL_NAMES.has(normalizedName)) return Object.freeze([]);
|
|
217
|
+
const command = verificationCommand(toolArgs);
|
|
218
|
+
if (command.length < 1 || MUTATING_COMMAND.test(command)) return Object.freeze([]);
|
|
219
|
+
const kinds = [];
|
|
220
|
+
if (INSPECTION_COMMAND.test(command)) kinds.push('inspection');
|
|
221
|
+
if (INTEGRITY_COMMAND.test(command)) kinds.push('integrity');
|
|
222
|
+
if (SYNTAX_COMMAND.test(command)) kinds.push('syntax');
|
|
223
|
+
if (TEST_COMMAND.test(command)) kinds.push('test');
|
|
224
|
+
return Object.freeze(kinds);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function canCarryExplicitResultEvidence(toolName, toolArgs) {
|
|
194
228
|
const normalizedName = String(toolName ?? '').trim().toLowerCase();
|
|
195
|
-
if (VERIFICATION_TOOL_NAMES.has(normalizedName)) return true;
|
|
196
229
|
if (!COMMAND_TOOL_NAMES.has(normalizedName)) return false;
|
|
197
230
|
const command = verificationCommand(toolArgs);
|
|
198
231
|
return command.length > 0
|
|
199
232
|
&& !MUTATING_COMMAND.test(command)
|
|
200
|
-
&&
|
|
233
|
+
&& RUNTIME_EVIDENCE_COMMAND.test(command);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function normalizedVerificationKind(value, field = 'verificationProof kind') {
|
|
237
|
+
const kind = String(value ?? '').trim();
|
|
238
|
+
if (!VERIFICATION_KINDS.has(kind)) throw new TypeError(`${field} is invalid`);
|
|
239
|
+
return kind;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function normalizedResultEvidenceKinds(value) {
|
|
243
|
+
if (value === undefined) return Object.freeze([]);
|
|
244
|
+
const kinds = Array.isArray(value) ? value.map((item) => String(item ?? '').trim()) : [];
|
|
245
|
+
if (!Array.isArray(value) || value.length > VERIFICATION_KINDS.size
|
|
246
|
+
|| kinds.some((item) => !VERIFICATION_KINDS.has(item))
|
|
247
|
+
|| new Set(kinds).size !== kinds.length) {
|
|
248
|
+
throw new TypeError('result evidence kinds are invalid');
|
|
249
|
+
}
|
|
250
|
+
return Object.freeze(kinds);
|
|
201
251
|
}
|
|
202
252
|
|
|
203
253
|
function normalizeSuccessfulToolDigests(value) {
|
|
@@ -210,39 +260,116 @@ function normalizeSuccessfulToolDigests(value) {
|
|
|
210
260
|
return Object.freeze([...value]);
|
|
211
261
|
}
|
|
212
262
|
|
|
263
|
+
function normalizeExactVerificationCall(input, receipt, label, options = {}) {
|
|
264
|
+
const requireKind = options.requireKind === true;
|
|
265
|
+
const expectedKeys = requireKind ? 4 : 3;
|
|
266
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)
|
|
267
|
+
|| Object.keys(input).length !== expectedKeys
|
|
268
|
+
|| !Object.hasOwn(input, 'toolCallId')
|
|
269
|
+
|| !Object.hasOwn(input, 'toolName')
|
|
270
|
+
|| !Object.hasOwn(input, 'claim')
|
|
271
|
+
|| requireKind !== Object.hasOwn(input, 'kind')) {
|
|
272
|
+
throw new TypeError(`${label} fields are invalid`);
|
|
273
|
+
}
|
|
274
|
+
const toolCallId = bounded(input.toolCallId, `${label} toolCallId`, 256);
|
|
275
|
+
const toolName = bounded(input.toolName, `${label} toolName`, 128);
|
|
276
|
+
const claim = bounded(input.claim, `${label} claim`);
|
|
277
|
+
if (isActionOnlyTool(toolName)) {
|
|
278
|
+
const purpose = label === 'verificationProof' ? 'verification proof' : label;
|
|
279
|
+
throw new TypeError(`action-only tool cannot serve as ${purpose}`);
|
|
280
|
+
}
|
|
281
|
+
const callDigest = successfulToolCallDigest(receipt.turnId, toolCallId, toolName);
|
|
282
|
+
if (!receipt.successfulToolCallDigests.includes(callDigest)) {
|
|
283
|
+
throw new TypeError(`${label} must match a successful current-turn verification call`);
|
|
284
|
+
}
|
|
285
|
+
if (requireKind) {
|
|
286
|
+
const kind = normalizedVerificationKind(input.kind, `${label} kind`);
|
|
287
|
+
const typedDigest = successfulVerificationCallDigest(receipt.turnId, toolCallId, toolName, kind);
|
|
288
|
+
if (!receipt.successfulVerificationCallDigests.includes(typedDigest)) {
|
|
289
|
+
throw new TypeError(`${label} kind must match the successful current-turn verification call`);
|
|
290
|
+
}
|
|
291
|
+
return Object.freeze({ toolCallId, toolName, kind, claim });
|
|
292
|
+
}
|
|
293
|
+
return Object.freeze({ toolCallId, toolName, claim });
|
|
294
|
+
}
|
|
295
|
+
|
|
213
296
|
function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
|
|
214
297
|
const keys = input && typeof input === 'object' && !Array.isArray(input)
|
|
215
298
|
? Object.keys(input)
|
|
216
299
|
: [];
|
|
217
|
-
const
|
|
300
|
+
const legacyName = options.allowLegacy === true
|
|
218
301
|
&& keys.length === 2
|
|
219
302
|
&& Object.hasOwn(input, 'toolName')
|
|
220
303
|
&& Object.hasOwn(input, 'claim');
|
|
221
|
-
const
|
|
304
|
+
const legacyExact = options.allowLegacy === true
|
|
305
|
+
&& keys.length === 3
|
|
306
|
+
&& Object.hasOwn(input, 'toolCallId')
|
|
307
|
+
&& Object.hasOwn(input, 'toolName')
|
|
308
|
+
&& Object.hasOwn(input, 'claim');
|
|
309
|
+
const subject = String(input?.subject ?? '').trim();
|
|
310
|
+
const legacySubjectResult = options.allowLegacy === true
|
|
311
|
+
&& subject === 'result'
|
|
312
|
+
&& keys.length === 4
|
|
222
313
|
&& Object.hasOwn(input, 'toolCallId')
|
|
223
314
|
&& Object.hasOwn(input, 'toolName')
|
|
224
315
|
&& Object.hasOwn(input, 'claim');
|
|
225
|
-
|
|
316
|
+
const legacySubjectVerifier = options.allowLegacy === true
|
|
317
|
+
&& subject === 'verifier'
|
|
318
|
+
&& (keys.length === 4 || keys.length === 5)
|
|
319
|
+
&& Object.hasOwn(input, 'toolCallId')
|
|
320
|
+
&& Object.hasOwn(input, 'toolName')
|
|
321
|
+
&& Object.hasOwn(input, 'claim')
|
|
322
|
+
&& keys.every((key) => ['subject', 'toolCallId', 'toolName', 'claim', 'sharpnessProof'].includes(key));
|
|
323
|
+
const currentResult = subject === 'result'
|
|
324
|
+
&& keys.length === 5
|
|
325
|
+
&& Object.hasOwn(input, 'toolCallId')
|
|
326
|
+
&& Object.hasOwn(input, 'toolName')
|
|
327
|
+
&& Object.hasOwn(input, 'kind')
|
|
328
|
+
&& Object.hasOwn(input, 'claim');
|
|
329
|
+
const currentVerifier = subject === 'verifier'
|
|
330
|
+
&& (keys.length === 5 || keys.length === 6)
|
|
331
|
+
&& Object.hasOwn(input, 'toolCallId')
|
|
332
|
+
&& Object.hasOwn(input, 'toolName')
|
|
333
|
+
&& Object.hasOwn(input, 'kind')
|
|
334
|
+
&& Object.hasOwn(input, 'claim')
|
|
335
|
+
&& keys.every((key) => ['subject', 'toolCallId', 'toolName', 'kind', 'claim', 'sharpnessProof'].includes(key));
|
|
336
|
+
if (!legacyName && !legacyExact && !legacySubjectResult && !legacySubjectVerifier
|
|
337
|
+
&& !currentResult && !currentVerifier) {
|
|
226
338
|
throw new TypeError('verificationProof fields are invalid');
|
|
227
339
|
}
|
|
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
340
|
const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
|
|
234
|
-
if (
|
|
341
|
+
if (legacyName) {
|
|
342
|
+
const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
|
|
343
|
+
const claim = bounded(input.claim, 'verificationProof claim');
|
|
344
|
+
if (isActionOnlyTool(toolName)) {
|
|
345
|
+
throw new TypeError('action-only tool cannot serve as verification proof');
|
|
346
|
+
}
|
|
235
347
|
if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
|
|
236
348
|
throw new TypeError('verificationProof must name a successful current-turn tool');
|
|
237
349
|
}
|
|
238
350
|
return Object.freeze({ toolName, claim });
|
|
239
351
|
}
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
352
|
+
const legacySubject = legacySubjectResult || legacySubjectVerifier;
|
|
353
|
+
const primary = normalizeExactVerificationCall(legacyExact ? input : {
|
|
354
|
+
toolCallId: input.toolCallId,
|
|
355
|
+
toolName: input.toolName,
|
|
356
|
+
...(legacySubject ? {} : { kind: input.kind }),
|
|
357
|
+
claim: input.claim,
|
|
358
|
+
}, receipt, 'verificationProof', { requireKind: !legacyExact && !legacySubject });
|
|
359
|
+
if (legacyExact) return primary;
|
|
360
|
+
if (!VERIFICATION_SUBJECTS.has(subject)) throw new TypeError('verificationProof subject is invalid');
|
|
361
|
+
if (subject === 'result') return Object.freeze({ subject, ...primary });
|
|
362
|
+
if (input.sharpnessProof === undefined) {
|
|
363
|
+
throw new TypeError('verifier proof requires a sharpnessProof');
|
|
364
|
+
}
|
|
365
|
+
const sharpnessProof = normalizeExactVerificationCall(input.sharpnessProof, receipt, 'sharpnessProof', {
|
|
366
|
+
requireKind: !legacySubject,
|
|
367
|
+
});
|
|
368
|
+
if (sharpnessProof.toolCallId === primary.toolCallId
|
|
369
|
+
&& sharpnessProof.toolName === primary.toolName) {
|
|
370
|
+
throw new TypeError('sharpnessProof must name a distinct verification call');
|
|
244
371
|
}
|
|
245
|
-
return Object.freeze({
|
|
372
|
+
return Object.freeze({ subject, ...primary, sharpnessProof });
|
|
246
373
|
}
|
|
247
374
|
|
|
248
375
|
function emptyActionEvidenceReceipt(turnId) {
|
|
@@ -254,6 +381,7 @@ function emptyActionEvidenceReceipt(turnId) {
|
|
|
254
381
|
failedTools: 0,
|
|
255
382
|
successfulToolDigests: Object.freeze([]),
|
|
256
383
|
successfulToolCallDigests: Object.freeze([]),
|
|
384
|
+
successfulVerificationCallDigests: Object.freeze([]),
|
|
257
385
|
digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
|
|
258
386
|
});
|
|
259
387
|
}
|
|
@@ -261,7 +389,10 @@ function emptyActionEvidenceReceipt(turnId) {
|
|
|
261
389
|
function normalizeActionEvidenceReceipt(input) {
|
|
262
390
|
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
|
|
263
391
|
const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
|
|
264
|
-
const allowedKeys = new Set([
|
|
392
|
+
const allowedKeys = new Set([
|
|
393
|
+
...requiredKeys, 'successfulToolDigests', 'successfulToolCallDigests',
|
|
394
|
+
'successfulVerificationCallDigests',
|
|
395
|
+
]);
|
|
265
396
|
if (!Object.keys(input).every((key) => allowedKeys.has(key))
|
|
266
397
|
|| ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
|
|
267
398
|
throw new TypeError('evidence receipt fields are invalid');
|
|
@@ -273,6 +404,9 @@ function normalizeActionEvidenceReceipt(input) {
|
|
|
273
404
|
failedTools: Number(input.failedTools),
|
|
274
405
|
successfulToolDigests: normalizeSuccessfulToolDigests(input.successfulToolDigests),
|
|
275
406
|
successfulToolCallDigests: normalizeSuccessfulToolDigests(input.successfulToolCallDigests),
|
|
407
|
+
successfulVerificationCallDigests: normalizeSuccessfulToolDigests(
|
|
408
|
+
input.successfulVerificationCallDigests,
|
|
409
|
+
),
|
|
276
410
|
digest: String(input.digest ?? ''),
|
|
277
411
|
};
|
|
278
412
|
if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
|
|
@@ -305,20 +439,37 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
305
439
|
const successful = decision === 'passed' && outcome === 'success';
|
|
306
440
|
const successfulToolDigests = [...prior.successfulToolDigests];
|
|
307
441
|
const successfulToolCallDigests = [...prior.successfulToolCallDigests];
|
|
442
|
+
const successfulVerificationCallDigests = [...prior.successfulVerificationCallDigests];
|
|
308
443
|
const toolDigest = successfulToolDigest(toolName);
|
|
309
444
|
if (successful && !successfulToolDigests.includes(toolDigest)) {
|
|
310
445
|
successfulToolDigests.push(toolDigest);
|
|
311
446
|
if (successfulToolDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolDigests.shift();
|
|
312
447
|
}
|
|
313
|
-
const
|
|
448
|
+
const providedResultKinds = normalizedResultEvidenceKinds(input.resultEvidenceKinds);
|
|
449
|
+
const explicitResultKinds = successful && canCarryExplicitResultEvidence(toolName, input.toolArgs)
|
|
450
|
+
? providedResultKinds
|
|
451
|
+
: [];
|
|
452
|
+
const verificationKinds = successful
|
|
453
|
+
? [...new Set([...verificationKindsForToolCall(toolName, input.toolArgs), ...explicitResultKinds])]
|
|
454
|
+
: [];
|
|
455
|
+
const verificationCall = verificationKinds.length > 0;
|
|
314
456
|
if (verificationCall) {
|
|
315
457
|
const callDigest = successfulToolCallDigest(turnId, toolCallId, toolName);
|
|
316
458
|
if (!successfulToolCallDigests.includes(callDigest)) successfulToolCallDigests.push(callDigest);
|
|
317
459
|
if (successfulToolCallDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolCallDigests.shift();
|
|
460
|
+
for (const kind of verificationKinds) {
|
|
461
|
+
const typedDigest = successfulVerificationCallDigest(turnId, toolCallId, toolName, kind);
|
|
462
|
+
if (!successfulVerificationCallDigests.includes(typedDigest)) {
|
|
463
|
+
successfulVerificationCallDigests.push(typedDigest);
|
|
464
|
+
}
|
|
465
|
+
if (successfulVerificationCallDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) {
|
|
466
|
+
successfulVerificationCallDigests.shift();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
318
469
|
}
|
|
319
470
|
const digest = crypto.createHash('sha256').update([
|
|
320
471
|
prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
|
|
321
|
-
|
|
472
|
+
verificationKinds.join(','),
|
|
322
473
|
].join('\0')).digest('hex').slice(0, 16);
|
|
323
474
|
return Object.freeze({
|
|
324
475
|
turnId,
|
|
@@ -327,6 +478,7 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
327
478
|
failedTools: prior.failedTools + (successful ? 0 : 1),
|
|
328
479
|
successfulToolDigests: Object.freeze(successfulToolDigests),
|
|
329
480
|
successfulToolCallDigests: Object.freeze(successfulToolCallDigests),
|
|
481
|
+
successfulVerificationCallDigests: Object.freeze(successfulVerificationCallDigests),
|
|
330
482
|
digest,
|
|
331
483
|
});
|
|
332
484
|
}
|
|
@@ -440,7 +592,18 @@ function projectActionCheckpoint(checkpoint) {
|
|
|
440
592
|
const call = value.verificationProof.toolCallId === undefined
|
|
441
593
|
? value.verificationProof.toolName
|
|
442
594
|
: `${value.verificationProof.toolName} call ${value.verificationProof.toolCallId}`;
|
|
443
|
-
|
|
595
|
+
const subject = value.verificationProof.subject === undefined
|
|
596
|
+
? ''
|
|
597
|
+
: ` [${value.verificationProof.subject}]`;
|
|
598
|
+
const kind = value.verificationProof.kind === undefined
|
|
599
|
+
? ''
|
|
600
|
+
: ` [${value.verificationProof.kind}]`;
|
|
601
|
+
lines.push(`Verification proof${subject}${kind}: ${call} - ${value.verificationProof.claim}`);
|
|
602
|
+
if (value.verificationProof.sharpnessProof !== undefined) {
|
|
603
|
+
const sharpness = value.verificationProof.sharpnessProof;
|
|
604
|
+
const sharpnessKind = sharpness.kind === undefined ? '' : ` [${sharpness.kind}]`;
|
|
605
|
+
lines.push(`Sharpness proof${sharpnessKind}: ${sharpness.toolName} call ${sharpness.toolCallId} - ${sharpness.claim}`);
|
|
606
|
+
}
|
|
444
607
|
}
|
|
445
608
|
if (value.nextTrigger !== undefined) {
|
|
446
609
|
lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
|
|
@@ -480,5 +643,7 @@ module.exports = {
|
|
|
480
643
|
normalizeVerificationProof,
|
|
481
644
|
projectActionCheckpoint,
|
|
482
645
|
successfulToolCallDigest,
|
|
646
|
+
successfulVerificationCallDigest,
|
|
483
647
|
successfulToolDigest,
|
|
648
|
+
verificationKindsForToolCall,
|
|
484
649
|
};
|
|
@@ -22,6 +22,18 @@ 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
|
+
}
|
|
33
|
+
if (/(?:verificationProof|sharpnessProof) kind must match the successful current-turn verification call/u
|
|
34
|
+
.test(String(error?.message ?? ''))) {
|
|
35
|
+
return ['The completion proof kind exceeds what its exact verification call measured.'];
|
|
36
|
+
}
|
|
25
37
|
if (/action-only tool/u.test(String(error?.message ?? ''))) {
|
|
26
38
|
return ['The completion proof names an action-only tool, not a verification tool.'];
|
|
27
39
|
}
|
package/blun.mjs
CHANGED
|
@@ -260349,10 +260349,36 @@ 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
|
-
|
|
260355
|
-
|
|
260355
|
+
kind: _enum(["inspection", "integrity", "syntax", "test", "reachability"]),
|
|
260356
|
+
claim: string().min(1).max(512),
|
|
260357
|
+
sharpnessProof: object({
|
|
260358
|
+
toolCallId: string().min(1).max(256),
|
|
260359
|
+
toolName: string().min(1).max(128),
|
|
260360
|
+
kind: _enum(["inspection", "integrity", "syntax", "test", "reachability"]),
|
|
260361
|
+
claim: string().min(1).max(512)
|
|
260362
|
+
}).strict().optional()
|
|
260363
|
+
}).strict().superRefine((value, ctx) => {
|
|
260364
|
+
if (value.subject === "verifier" && value.sharpnessProof === void 0) ctx.addIssue({
|
|
260365
|
+
code: "custom",
|
|
260366
|
+
path: ["sharpnessProof"],
|
|
260367
|
+
message: "verifier subject requires sharpnessProof"
|
|
260368
|
+
});
|
|
260369
|
+
if (value.subject === "result" && value.sharpnessProof !== void 0) ctx.addIssue({
|
|
260370
|
+
code: "custom",
|
|
260371
|
+
path: ["sharpnessProof"],
|
|
260372
|
+
message: "result subject cannot carry sharpnessProof"
|
|
260373
|
+
});
|
|
260374
|
+
if (value.sharpnessProof !== void 0
|
|
260375
|
+
&& value.sharpnessProof.toolCallId === value.toolCallId
|
|
260376
|
+
&& value.sharpnessProof.toolName === value.toolName) ctx.addIssue({
|
|
260377
|
+
code: "custom",
|
|
260378
|
+
path: ["sharpnessProof"],
|
|
260379
|
+
message: "sharpnessProof must name a distinct verification call"
|
|
260380
|
+
});
|
|
260381
|
+
}).optional(),
|
|
260356
260382
|
nextTrigger: object({
|
|
260357
260383
|
kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
|
|
260358
260384
|
condition: string().min(1).max(512),
|
|
@@ -261668,6 +261694,10 @@ function telemetryToolErrorType(result) {
|
|
|
261668
261694
|
function toolResultText(result) {
|
|
261669
261695
|
return toolOutputText(result.output);
|
|
261670
261696
|
}
|
|
261697
|
+
function explicitToolResultEvidenceKinds(result) {
|
|
261698
|
+
const text = toolResultText(result);
|
|
261699
|
+
return /(?:^|\s)BLUN_EVIDENCE_KIND=reachability(?:\s|$)/u.test(text) ? ["reachability"] : [];
|
|
261700
|
+
}
|
|
261671
261701
|
function abandonedToolResultOutput(ended) {
|
|
261672
261702
|
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.`;
|
|
261673
261703
|
}
|
|
@@ -262770,7 +262800,8 @@ var init_turn = __esmMin((() => {
|
|
|
262770
262800
|
toolName: started.name,
|
|
262771
262801
|
outcome,
|
|
262772
262802
|
durationMs: Date.now() - started.startedAt,
|
|
262773
|
-
toolArgs: started.args
|
|
262803
|
+
toolArgs: started.args,
|
|
262804
|
+
resultEvidenceKinds: explicitToolResultEvidenceKinds(event.result)
|
|
262774
262805
|
});
|
|
262775
262806
|
this.agent.telemetry.track("tool_call", properties);
|
|
262776
262807
|
this.agent.feedRootMissionContract("result", {
|
|
@@ -262875,7 +262906,8 @@ var init_outcome_prompts = __esmMin((() => {}));
|
|
|
262875
262906
|
var update_goal_default;
|
|
262876
262907
|
var init_update_goal$1 = __esmMin((() => {
|
|
262877
262908
|
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.
|
|
262909
|
+
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";
|
|
262910
|
+
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";
|
|
262879
262911
|
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
262912
|
}));
|
|
262881
262913
|
//#endregion
|