blun-king-cli 9.1.448 → 9.1.449
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.
|
@@ -22,6 +22,9 @@ const PROBLEM_FRAME_KEYS = new Set([
|
|
|
22
22
|
]);
|
|
23
23
|
const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
|
|
24
24
|
const EVIDENCE_INPUT_KEYS = new Set([
|
|
25
|
+
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs', 'toolArgs',
|
|
26
|
+
]);
|
|
27
|
+
const REQUIRED_EVIDENCE_INPUT_KEYS = new Set([
|
|
25
28
|
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
|
|
26
29
|
]);
|
|
27
30
|
const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
|
|
@@ -32,6 +35,14 @@ const ACTION_ONLY_TOOL_NAMES = new Set([
|
|
|
32
35
|
'ExitPlanMode', 'GenerateImage', 'GenerateSpeech', 'GenerateVideo', 'LipSyncMedia',
|
|
33
36
|
'MistakeRecord', 'SetGoalBudget', 'TaskStop', 'TaskUpdate', 'UpdateGoal', 'Write',
|
|
34
37
|
]);
|
|
38
|
+
const VERIFICATION_TOOL_NAMES = new Set([
|
|
39
|
+
'codebasesearch', 'cronlist', 'fetchurl', 'getgoal', 'getmedia', 'glob', 'grep',
|
|
40
|
+
'read', 'readmediafile', 'taskoutput', 'test', 'understandimage', 'understandvideo',
|
|
41
|
+
'websearch',
|
|
42
|
+
]);
|
|
43
|
+
const COMMAND_TOOL_NAMES = new Set(['bash', 'command', 'exec_command', 'shell']);
|
|
44
|
+
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 VERIFICATION_COMMAND = /(?:^|(?:&&|\|\||;|\s))(?:(?:node\s+--(?:test|check))|(?:node\s+[^\s;&|]*(?:check|test|verify|validate|lint|gate|probe)[^\s;&|]*\b)|(?:(?:npm|pnpm|yarn)\s+(?:test|(?:run\s+)?(?:test|lint|check|typecheck|build)))|(?:python(?:3)?\s+-m\s+pytest)|(?:pytest)|(?:go\s+test)|(?:cargo\s+test)|(?:dotnet\s+test)|(?:npx\s+)?(?:tsc|eslint|biome\s+check)|(?:git\s+(?:diff(?:\s+--check)?|fsck|status|rev-parse|show))|(?:sha(?:1|256|512)sum|shasum|cmp|diff|wc|rg|grep|cat|ls|stat)\b|(?:certutil\s+-hashfile)|(?:Get-FileHash|Get-Content|Get-Item|Test-Path|Compare-Object|Measure-Object)\b)/iu;
|
|
35
46
|
const MAX_SUCCESSFUL_TOOL_DIGESTS = 32;
|
|
36
47
|
|
|
37
48
|
function bounded(value, field, max = 512) {
|
|
@@ -159,10 +170,36 @@ function successfulToolDigest(toolName) {
|
|
|
159
170
|
return crypto.createHash('sha256').update(`tool:${name}`).digest('hex').slice(0, 16);
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
function successfulToolCallDigest(turnId, toolCallId, toolName) {
|
|
174
|
+
const turn = normalizedTurnId(turnId);
|
|
175
|
+
const callId = bounded(toolCallId, 'verificationProof toolCallId', 256);
|
|
176
|
+
const name = bounded(toolName, 'verificationProof toolName', 128);
|
|
177
|
+
return crypto.createHash('sha256')
|
|
178
|
+
.update(`turn:${turn}\0call:${callId}\0tool:${name}`)
|
|
179
|
+
.digest('hex')
|
|
180
|
+
.slice(0, 16);
|
|
181
|
+
}
|
|
182
|
+
|
|
162
183
|
function isActionOnlyTool(toolName) {
|
|
163
184
|
return ACTION_ONLY_TOOL_NAMES.has(String(toolName ?? '').trim());
|
|
164
185
|
}
|
|
165
186
|
|
|
187
|
+
function verificationCommand(toolArgs) {
|
|
188
|
+
if (!toolArgs || typeof toolArgs !== 'object' || Array.isArray(toolArgs)) return '';
|
|
189
|
+
const command = toolArgs.command ?? toolArgs.cmd;
|
|
190
|
+
return typeof command === 'string' ? command.trim().replace(/\s+/gu, ' ') : '';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function isVerificationToolCall(toolName, toolArgs) {
|
|
194
|
+
const normalizedName = String(toolName ?? '').trim().toLowerCase();
|
|
195
|
+
if (VERIFICATION_TOOL_NAMES.has(normalizedName)) return true;
|
|
196
|
+
if (!COMMAND_TOOL_NAMES.has(normalizedName)) return false;
|
|
197
|
+
const command = verificationCommand(toolArgs);
|
|
198
|
+
return command.length > 0
|
|
199
|
+
&& !MUTATING_COMMAND.test(command)
|
|
200
|
+
&& VERIFICATION_COMMAND.test(command);
|
|
201
|
+
}
|
|
202
|
+
|
|
166
203
|
function normalizeSuccessfulToolDigests(value) {
|
|
167
204
|
if (value === undefined) return Object.freeze([]);
|
|
168
205
|
if (!Array.isArray(value) || value.length > MAX_SUCCESSFUL_TOOL_DIGESTS
|
|
@@ -173,11 +210,19 @@ function normalizeSuccessfulToolDigests(value) {
|
|
|
173
210
|
return Object.freeze([...value]);
|
|
174
211
|
}
|
|
175
212
|
|
|
176
|
-
function normalizeVerificationProof(input, evidenceReceipt) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
213
|
+
function normalizeVerificationProof(input, evidenceReceipt, options = {}) {
|
|
214
|
+
const keys = input && typeof input === 'object' && !Array.isArray(input)
|
|
215
|
+
? Object.keys(input)
|
|
216
|
+
: [];
|
|
217
|
+
const legacy = options.allowLegacy === true
|
|
218
|
+
&& keys.length === 2
|
|
219
|
+
&& Object.hasOwn(input, 'toolName')
|
|
220
|
+
&& Object.hasOwn(input, 'claim');
|
|
221
|
+
const current = keys.length === 3
|
|
222
|
+
&& Object.hasOwn(input, 'toolCallId')
|
|
223
|
+
&& Object.hasOwn(input, 'toolName')
|
|
224
|
+
&& Object.hasOwn(input, 'claim');
|
|
225
|
+
if (!legacy && !current) {
|
|
181
226
|
throw new TypeError('verificationProof fields are invalid');
|
|
182
227
|
}
|
|
183
228
|
const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
|
|
@@ -186,10 +231,18 @@ function normalizeVerificationProof(input, evidenceReceipt) {
|
|
|
186
231
|
throw new TypeError('action-only tool cannot serve as verification proof');
|
|
187
232
|
}
|
|
188
233
|
const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
|
|
189
|
-
if (
|
|
190
|
-
|
|
234
|
+
if (legacy) {
|
|
235
|
+
if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
|
|
236
|
+
throw new TypeError('verificationProof must name a successful current-turn tool');
|
|
237
|
+
}
|
|
238
|
+
return Object.freeze({ toolName, claim });
|
|
191
239
|
}
|
|
192
|
-
|
|
240
|
+
const toolCallId = bounded(input.toolCallId, 'verificationProof toolCallId', 256);
|
|
241
|
+
const callDigest = successfulToolCallDigest(receipt.turnId, toolCallId, toolName);
|
|
242
|
+
if (!receipt.successfulToolCallDigests.includes(callDigest)) {
|
|
243
|
+
throw new TypeError('verificationProof must match a successful current-turn verification call');
|
|
244
|
+
}
|
|
245
|
+
return Object.freeze({ toolCallId, toolName, claim });
|
|
193
246
|
}
|
|
194
247
|
|
|
195
248
|
function emptyActionEvidenceReceipt(turnId) {
|
|
@@ -200,6 +253,7 @@ function emptyActionEvidenceReceipt(turnId) {
|
|
|
200
253
|
successfulTools: 0,
|
|
201
254
|
failedTools: 0,
|
|
202
255
|
successfulToolDigests: Object.freeze([]),
|
|
256
|
+
successfulToolCallDigests: Object.freeze([]),
|
|
203
257
|
digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
|
|
204
258
|
});
|
|
205
259
|
}
|
|
@@ -207,7 +261,7 @@ function emptyActionEvidenceReceipt(turnId) {
|
|
|
207
261
|
function normalizeActionEvidenceReceipt(input) {
|
|
208
262
|
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
|
|
209
263
|
const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
|
|
210
|
-
const allowedKeys = new Set([...requiredKeys, 'successfulToolDigests']);
|
|
264
|
+
const allowedKeys = new Set([...requiredKeys, 'successfulToolDigests', 'successfulToolCallDigests']);
|
|
211
265
|
if (!Object.keys(input).every((key) => allowedKeys.has(key))
|
|
212
266
|
|| ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
|
|
213
267
|
throw new TypeError('evidence receipt fields are invalid');
|
|
@@ -218,6 +272,7 @@ function normalizeActionEvidenceReceipt(input) {
|
|
|
218
272
|
successfulTools: Number(input.successfulTools),
|
|
219
273
|
failedTools: Number(input.failedTools),
|
|
220
274
|
successfulToolDigests: normalizeSuccessfulToolDigests(input.successfulToolDigests),
|
|
275
|
+
successfulToolCallDigests: normalizeSuccessfulToolDigests(input.successfulToolCallDigests),
|
|
221
276
|
digest: String(input.digest ?? ''),
|
|
222
277
|
};
|
|
223
278
|
if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
|
|
@@ -232,8 +287,8 @@ function normalizeActionEvidenceReceipt(input) {
|
|
|
232
287
|
function advanceActionEvidenceReceipt(current, input) {
|
|
233
288
|
const prior = normalizeActionEvidenceReceipt(current);
|
|
234
289
|
if (!input || typeof input !== 'object' || Array.isArray(input)
|
|
235
|
-
|| Object.keys(input).
|
|
236
|
-
|| !
|
|
290
|
+
|| !Object.keys(input).every((key) => EVIDENCE_INPUT_KEYS.has(key))
|
|
291
|
+
|| ![...REQUIRED_EVIDENCE_INPUT_KEYS].every((key) => Object.hasOwn(input, key))) {
|
|
237
292
|
throw new TypeError('evidence input fields are invalid');
|
|
238
293
|
}
|
|
239
294
|
const turnId = normalizedTurnId(input.turnId);
|
|
@@ -249,13 +304,21 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
249
304
|
}
|
|
250
305
|
const successful = decision === 'passed' && outcome === 'success';
|
|
251
306
|
const successfulToolDigests = [...prior.successfulToolDigests];
|
|
307
|
+
const successfulToolCallDigests = [...prior.successfulToolCallDigests];
|
|
252
308
|
const toolDigest = successfulToolDigest(toolName);
|
|
253
309
|
if (successful && !successfulToolDigests.includes(toolDigest)) {
|
|
254
310
|
successfulToolDigests.push(toolDigest);
|
|
255
311
|
if (successfulToolDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolDigests.shift();
|
|
256
312
|
}
|
|
313
|
+
const verificationCall = successful && isVerificationToolCall(toolName, input.toolArgs);
|
|
314
|
+
if (verificationCall) {
|
|
315
|
+
const callDigest = successfulToolCallDigest(turnId, toolCallId, toolName);
|
|
316
|
+
if (!successfulToolCallDigests.includes(callDigest)) successfulToolCallDigests.push(callDigest);
|
|
317
|
+
if (successfulToolCallDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolCallDigests.shift();
|
|
318
|
+
}
|
|
257
319
|
const digest = crypto.createHash('sha256').update([
|
|
258
320
|
prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
|
|
321
|
+
String(verificationCall),
|
|
259
322
|
].join('\0')).digest('hex').slice(0, 16);
|
|
260
323
|
return Object.freeze({
|
|
261
324
|
turnId,
|
|
@@ -263,6 +326,7 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
263
326
|
successfulTools: prior.successfulTools + (successful ? 1 : 0),
|
|
264
327
|
failedTools: prior.failedTools + (successful ? 0 : 1),
|
|
265
328
|
successfulToolDigests: Object.freeze(successfulToolDigests),
|
|
329
|
+
successfulToolCallDigests: Object.freeze(successfulToolCallDigests),
|
|
266
330
|
digest,
|
|
267
331
|
});
|
|
268
332
|
}
|
|
@@ -352,7 +416,9 @@ function normalizeActionCheckpoint(input, options = {}) {
|
|
|
352
416
|
if (phase !== 'verify' || evidenceBasis !== 'runtime_tool') {
|
|
353
417
|
throw new TypeError('verificationProof requires a runtime_tool verify checkpoint');
|
|
354
418
|
}
|
|
355
|
-
checkpoint.verificationProof = normalizeVerificationProof(input.verificationProof, evidenceReceipt
|
|
419
|
+
checkpoint.verificationProof = normalizeVerificationProof(input.verificationProof, evidenceReceipt, {
|
|
420
|
+
allowLegacy: replay,
|
|
421
|
+
});
|
|
356
422
|
}
|
|
357
423
|
return Object.freeze(checkpoint);
|
|
358
424
|
}
|
|
@@ -371,7 +437,10 @@ function projectActionCheckpoint(checkpoint) {
|
|
|
371
437
|
lines.push(`Next action: ${value.nextAction}`);
|
|
372
438
|
lines.push(`Expected evidence: ${value.expectedEvidence}`);
|
|
373
439
|
if (value.verificationProof !== undefined) {
|
|
374
|
-
|
|
440
|
+
const call = value.verificationProof.toolCallId === undefined
|
|
441
|
+
? value.verificationProof.toolName
|
|
442
|
+
: `${value.verificationProof.toolName} call ${value.verificationProof.toolCallId}`;
|
|
443
|
+
lines.push(`Verification proof: ${call} - ${value.verificationProof.claim}`);
|
|
375
444
|
}
|
|
376
445
|
if (value.nextTrigger !== undefined) {
|
|
377
446
|
lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
|
|
@@ -410,5 +479,6 @@ module.exports = {
|
|
|
410
479
|
normalizeActionCheckpoint,
|
|
411
480
|
normalizeVerificationProof,
|
|
412
481
|
projectActionCheckpoint,
|
|
482
|
+
successfulToolCallDigest,
|
|
413
483
|
successfulToolDigest,
|
|
414
484
|
};
|
|
@@ -19,14 +19,14 @@ function verificationProofGaps(checkpoint) {
|
|
|
19
19
|
return ['Bind the completion claim to a successful verification tool from the checkpoint turn.'];
|
|
20
20
|
}
|
|
21
21
|
try {
|
|
22
|
-
normalizeVerificationProof(checkpoint.verificationProof, checkpoint.evidenceReceipt);
|
|
22
|
+
normalizeVerificationProof(checkpoint.verificationProof, checkpoint.evidenceReceipt, { allowLegacy: true });
|
|
23
23
|
return [];
|
|
24
24
|
} catch (error) {
|
|
25
25
|
if (/action-only tool/u.test(String(error?.message ?? ''))) {
|
|
26
26
|
return ['The completion proof names an action-only tool, not a verification tool.'];
|
|
27
27
|
}
|
|
28
|
-
if (/successful current-turn tool/u.test(String(error?.message ?? ''))) {
|
|
29
|
-
return ['The completion proof does not match a successful verification
|
|
28
|
+
if (/successful current-turn (?:tool|verification call)/u.test(String(error?.message ?? ''))) {
|
|
29
|
+
return ['The completion proof does not match a successful verification call from the checkpoint turn.'];
|
|
30
30
|
}
|
|
31
31
|
return ['The completion verification proof is malformed.'];
|
|
32
32
|
}
|
package/blun.mjs
CHANGED
|
@@ -260349,6 +260349,7 @@ 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
|
+
toolCallId: string().min(1).max(256),
|
|
260352
260353
|
toolName: string().min(1).max(128),
|
|
260353
260354
|
claim: string().min(1).max(512)
|
|
260354
260355
|
}).strict().optional(),
|
|
@@ -262737,6 +262738,7 @@ var init_turn = __esmMin((() => {
|
|
|
262737
262738
|
this.toolCallDupType.set(event.toolCallId, dupType === "cross_step" ? "cross_step" : "normal");
|
|
262738
262739
|
this.toolCallStartedAt.set(event.toolCallId, {
|
|
262739
262740
|
name: event.name,
|
|
262741
|
+
args: event.args,
|
|
262740
262742
|
startedAt: Date.now()
|
|
262741
262743
|
});
|
|
262742
262744
|
this.agent.feedRootMissionContract("start", {
|
|
@@ -262767,7 +262769,8 @@ var init_turn = __esmMin((() => {
|
|
|
262767
262769
|
toolCallId: event.toolCallId,
|
|
262768
262770
|
toolName: started.name,
|
|
262769
262771
|
outcome,
|
|
262770
|
-
durationMs: Date.now() - started.startedAt
|
|
262772
|
+
durationMs: Date.now() - started.startedAt,
|
|
262773
|
+
toolArgs: started.args
|
|
262771
262774
|
});
|
|
262772
262775
|
this.agent.telemetry.track("tool_call", properties);
|
|
262773
262776
|
this.agent.feedRootMissionContract("result", {
|
|
@@ -262872,7 +262875,7 @@ var init_outcome_prompts = __esmMin((() => {}));
|
|
|
262872
262875
|
var update_goal_default;
|
|
262873
262876
|
var init_update_goal$1 = __esmMin((() => {
|
|
262874
262877
|
update_goal_default = "Update the current autonomous goal. Set `status` only for a lifecycle change. After a coherent work slice, save `actionCheckpoint` with a monotone revision, the last verified result, exact next action, expected evidence, exact `nextTrigger`, and an explicit evidence basis. Persist the exact `nextTrigger` that releases `nextAction`: use `immediate` outside the `wait` phase; while waiting, name the external event, time, dependency, or user decision instead of pretending work can continue. A `time` trigger must include the exact ISO timestamp in `dueAt`; no other trigger kind may include `dueAt`. Use `runtime_tool` only when a successful tool in this turn measured the result; use `user_statement` for a direct user assertion, `external_report` for a report not independently measured here, and `carried_forward` only when the last verified text is unchanged. Classify knowledge as `verified`, `credible_unverified`, `hypothesis`, `uncertain_memory`, `stale`, or `unknown`; never present a weaker state as verified, and preserve the state on carry-forward. Start at revision 1 and increment the currently projected revision by exactly one; stale writers fail closed. This is durable progress state, not permission, and should change only when the facts change. A checkpoint-only call keeps the goal active.\n\n- `active` — resume a paused or blocked goal when the user explicitly asks you to work on that goal.\n- `complete` — the objective is fully satisfied, all files are written, all tests pass, and any stated validation has passed. When the goal has a completion criterion, first save a `verify` checkpoint with `runtime_tool`, `verified`, and a successful runtime evidence receipt.\n- `blocked` — a genuine external condition or required user decision prevents progress.\n- `paused` — set the goal aside for now.\n\nDo not mark complete after a plan or partial result. If useful work remains, checkpoint it and continue. Do not ask for permission merely to execute an already authorized checkpoint; ask only at a real rights boundary or missing user decision.\n";
|
|
262875
|
-
update_goal_default += "\nBefore completing a goal with a criterion, bind the verified claim to the successful current-turn verification
|
|
262878
|
+
update_goal_default += "\nBefore completing a goal with a criterion, bind the verified claim to the exact successful current-turn verification call in `verificationProof`, including its `toolCallId`. A write, edit, copy, deploy, or other action is not proof that the changed behavior works, even when it shares a mixed-use tool such as `Bash` with tests. Run a separate observation, test, or counterexample probe and cite that exact successful call plus the claim it supports.\n";
|
|
262876
262879
|
update_goal_default += "\nFor a non-trivial or unfamiliar problem, preserve `problemFrame` with the success criterion, missing knowledge, bounded candidate actions, selected action and reason, support choice, risk, and reversibility. The selected action must match one candidate. Bind each selected action to the projected durable facts or assumptions it relies on by copying their explicit refs into `decisionBasis`. A stale or unknown decision basis requires replanning before execution. Problem framing is descriptive state and never grants permission.\n";
|
|
262877
262880
|
}));
|
|
262878
262881
|
//#endregion
|