blun-king-cli 9.1.446 → 9.1.448
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.
|
@@ -13,19 +13,26 @@ const TRIGGER_KINDS = new Set([
|
|
|
13
13
|
'immediate', 'external_event', 'time', 'dependency', 'user_decision',
|
|
14
14
|
]);
|
|
15
15
|
const MODEL_KEYS = new Set([
|
|
16
|
-
'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'nextTrigger', 'problemFrame', 'updatedAt',
|
|
16
|
+
'revision', 'phase', 'evidenceBasis', 'epistemicState', 'lastVerified', 'nextAction', 'expectedEvidence', 'verificationProof', 'nextTrigger', 'problemFrame', 'updatedAt',
|
|
17
17
|
]);
|
|
18
18
|
const RUNTIME_KEYS = new Set([...MODEL_KEYS, 'evidenceReceipt']);
|
|
19
19
|
const PROBLEM_FRAME_KEYS = new Set([
|
|
20
20
|
'successCriterion', 'missingKnowledge', 'candidateActions', 'selectedAction',
|
|
21
|
-
'selectionReason', 'supportChoice', 'risk', 'reversibility',
|
|
21
|
+
'selectionReason', 'supportChoice', 'risk', 'reversibility', 'decisionBasis',
|
|
22
22
|
]);
|
|
23
23
|
const NEXT_TRIGGER_KEYS = new Set(['kind', 'condition', 'dueAt']);
|
|
24
24
|
const EVIDENCE_INPUT_KEYS = new Set([
|
|
25
25
|
'turnId', 'toolCallId', 'toolName', 'decision', 'outcome', 'durationMs',
|
|
26
26
|
]);
|
|
27
27
|
const EVIDENCE_DIGEST_RE = /^[a-f0-9]{16}$/u;
|
|
28
|
+
const DECISION_BASIS_RE = /^[a-f0-9]{16}$/u;
|
|
28
29
|
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;
|
|
30
|
+
const ACTION_ONLY_TOOL_NAMES = new Set([
|
|
31
|
+
'CreateGoal', 'CronCreate', 'CronDelete', 'DubVideo', 'Edit', 'EnterPlanMode',
|
|
32
|
+
'ExitPlanMode', 'GenerateImage', 'GenerateSpeech', 'GenerateVideo', 'LipSyncMedia',
|
|
33
|
+
'MistakeRecord', 'SetGoalBudget', 'TaskStop', 'TaskUpdate', 'UpdateGoal', 'Write',
|
|
34
|
+
]);
|
|
35
|
+
const MAX_SUCCESSFUL_TOOL_DIGESTS = 32;
|
|
29
36
|
|
|
30
37
|
function bounded(value, field, max = 512) {
|
|
31
38
|
const text = String(value ?? '')
|
|
@@ -60,6 +67,15 @@ function boundedList(value, field) {
|
|
|
60
67
|
return Object.freeze(value.map((item, index) => bounded(item, `${field}[${index}]`, 256)));
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
function normalizedDecisionBasis(value) {
|
|
71
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 5
|
|
72
|
+
|| value.some((item) => typeof item !== 'string' || !DECISION_BASIS_RE.test(item))
|
|
73
|
+
|| new Set(value).size !== value.length) {
|
|
74
|
+
throw new TypeError('decisionBasis must contain between 1 and 5 unique evidence refs');
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze([...value]);
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
function normalizeProblemFrame(input) {
|
|
64
80
|
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
65
81
|
throw new TypeError('problemFrame must be an object');
|
|
@@ -73,7 +89,7 @@ function normalizeProblemFrame(input) {
|
|
|
73
89
|
if (!candidateActions.includes(selectedAction)) {
|
|
74
90
|
throw new TypeError('selectedAction must match a candidateAction');
|
|
75
91
|
}
|
|
76
|
-
|
|
92
|
+
const frame = {
|
|
77
93
|
successCriterion: bounded(input.successCriterion, 'successCriterion'),
|
|
78
94
|
missingKnowledge,
|
|
79
95
|
candidateActions,
|
|
@@ -82,7 +98,9 @@ function normalizeProblemFrame(input) {
|
|
|
82
98
|
supportChoice: bounded(input.supportChoice, 'supportChoice', 256),
|
|
83
99
|
risk: bounded(input.risk, 'risk'),
|
|
84
100
|
reversibility: bounded(input.reversibility, 'reversibility'),
|
|
85
|
-
}
|
|
101
|
+
};
|
|
102
|
+
if (input.decisionBasis !== undefined) frame.decisionBasis = normalizedDecisionBasis(input.decisionBasis);
|
|
103
|
+
return Object.freeze(frame);
|
|
86
104
|
}
|
|
87
105
|
|
|
88
106
|
function normalizeNextTrigger(input, phase) {
|
|
@@ -136,6 +154,44 @@ function normalizedTurnId(value) {
|
|
|
136
154
|
return turnId;
|
|
137
155
|
}
|
|
138
156
|
|
|
157
|
+
function successfulToolDigest(toolName) {
|
|
158
|
+
const name = bounded(toolName, 'verificationProof toolName', 128);
|
|
159
|
+
return crypto.createHash('sha256').update(`tool:${name}`).digest('hex').slice(0, 16);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isActionOnlyTool(toolName) {
|
|
163
|
+
return ACTION_ONLY_TOOL_NAMES.has(String(toolName ?? '').trim());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeSuccessfulToolDigests(value) {
|
|
167
|
+
if (value === undefined) return Object.freeze([]);
|
|
168
|
+
if (!Array.isArray(value) || value.length > MAX_SUCCESSFUL_TOOL_DIGESTS
|
|
169
|
+
|| value.some((item) => typeof item !== 'string' || !EVIDENCE_DIGEST_RE.test(item))
|
|
170
|
+
|| new Set(value).size !== value.length) {
|
|
171
|
+
throw new TypeError('successful tool digests are invalid');
|
|
172
|
+
}
|
|
173
|
+
return Object.freeze([...value]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizeVerificationProof(input, evidenceReceipt) {
|
|
177
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)
|
|
178
|
+
|| Object.keys(input).length !== 2
|
|
179
|
+
|| !Object.hasOwn(input, 'toolName')
|
|
180
|
+
|| !Object.hasOwn(input, 'claim')) {
|
|
181
|
+
throw new TypeError('verificationProof fields are invalid');
|
|
182
|
+
}
|
|
183
|
+
const toolName = bounded(input.toolName, 'verificationProof toolName', 128);
|
|
184
|
+
const claim = bounded(input.claim, 'verificationProof claim');
|
|
185
|
+
if (isActionOnlyTool(toolName)) {
|
|
186
|
+
throw new TypeError('action-only tool cannot serve as verification proof');
|
|
187
|
+
}
|
|
188
|
+
const receipt = normalizeActionEvidenceReceipt(evidenceReceipt);
|
|
189
|
+
if (!receipt.successfulToolDigests.includes(successfulToolDigest(toolName))) {
|
|
190
|
+
throw new TypeError('verificationProof must name a successful current-turn tool');
|
|
191
|
+
}
|
|
192
|
+
return Object.freeze({ toolName, claim });
|
|
193
|
+
}
|
|
194
|
+
|
|
139
195
|
function emptyActionEvidenceReceipt(turnId) {
|
|
140
196
|
const normalized = normalizedTurnId(turnId);
|
|
141
197
|
return Object.freeze({
|
|
@@ -143,14 +199,17 @@ function emptyActionEvidenceReceipt(turnId) {
|
|
|
143
199
|
completedTools: 0,
|
|
144
200
|
successfulTools: 0,
|
|
145
201
|
failedTools: 0,
|
|
202
|
+
successfulToolDigests: Object.freeze([]),
|
|
146
203
|
digest: crypto.createHash('sha256').update(`turn:${normalized}:empty`).digest('hex').slice(0, 16),
|
|
147
204
|
});
|
|
148
205
|
}
|
|
149
206
|
|
|
150
207
|
function normalizeActionEvidenceReceipt(input) {
|
|
151
208
|
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('evidence receipt must be an object');
|
|
152
|
-
const
|
|
153
|
-
|
|
209
|
+
const requiredKeys = new Set(['turnId', 'completedTools', 'successfulTools', 'failedTools', 'digest']);
|
|
210
|
+
const allowedKeys = new Set([...requiredKeys, 'successfulToolDigests']);
|
|
211
|
+
if (!Object.keys(input).every((key) => allowedKeys.has(key))
|
|
212
|
+
|| ![...requiredKeys].every((key) => Object.hasOwn(input, key))) {
|
|
154
213
|
throw new TypeError('evidence receipt fields are invalid');
|
|
155
214
|
}
|
|
156
215
|
const receipt = {
|
|
@@ -158,6 +217,7 @@ function normalizeActionEvidenceReceipt(input) {
|
|
|
158
217
|
completedTools: Number(input.completedTools),
|
|
159
218
|
successfulTools: Number(input.successfulTools),
|
|
160
219
|
failedTools: Number(input.failedTools),
|
|
220
|
+
successfulToolDigests: normalizeSuccessfulToolDigests(input.successfulToolDigests),
|
|
161
221
|
digest: String(input.digest ?? ''),
|
|
162
222
|
};
|
|
163
223
|
if (![receipt.completedTools, receipt.successfulTools, receipt.failedTools]
|
|
@@ -188,6 +248,12 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
188
248
|
throw new TypeError('evidence input values are invalid');
|
|
189
249
|
}
|
|
190
250
|
const successful = decision === 'passed' && outcome === 'success';
|
|
251
|
+
const successfulToolDigests = [...prior.successfulToolDigests];
|
|
252
|
+
const toolDigest = successfulToolDigest(toolName);
|
|
253
|
+
if (successful && !successfulToolDigests.includes(toolDigest)) {
|
|
254
|
+
successfulToolDigests.push(toolDigest);
|
|
255
|
+
if (successfulToolDigests.length > MAX_SUCCESSFUL_TOOL_DIGESTS) successfulToolDigests.shift();
|
|
256
|
+
}
|
|
191
257
|
const digest = crypto.createHash('sha256').update([
|
|
192
258
|
prior.digest, String(turnId), toolCallId, toolName, decision, outcome, String(durationMs),
|
|
193
259
|
].join('\0')).digest('hex').slice(0, 16);
|
|
@@ -196,6 +262,7 @@ function advanceActionEvidenceReceipt(current, input) {
|
|
|
196
262
|
completedTools: prior.completedTools + 1,
|
|
197
263
|
successfulTools: prior.successfulTools + (successful ? 1 : 0),
|
|
198
264
|
failedTools: prior.failedTools + (successful ? 0 : 1),
|
|
265
|
+
successfulToolDigests: Object.freeze(successfulToolDigests),
|
|
199
266
|
digest,
|
|
200
267
|
});
|
|
201
268
|
}
|
|
@@ -281,6 +348,12 @@ function normalizeActionCheckpoint(input, options = {}) {
|
|
|
281
348
|
? normalizeActionEvidenceReceipt(input.evidenceReceipt)
|
|
282
349
|
: undefined;
|
|
283
350
|
if (evidenceReceipt !== undefined) checkpoint.evidenceReceipt = evidenceReceipt;
|
|
351
|
+
if (input.verificationProof !== undefined) {
|
|
352
|
+
if (phase !== 'verify' || evidenceBasis !== 'runtime_tool') {
|
|
353
|
+
throw new TypeError('verificationProof requires a runtime_tool verify checkpoint');
|
|
354
|
+
}
|
|
355
|
+
checkpoint.verificationProof = normalizeVerificationProof(input.verificationProof, evidenceReceipt);
|
|
356
|
+
}
|
|
284
357
|
return Object.freeze(checkpoint);
|
|
285
358
|
}
|
|
286
359
|
|
|
@@ -297,6 +370,9 @@ function projectActionCheckpoint(checkpoint) {
|
|
|
297
370
|
];
|
|
298
371
|
lines.push(`Next action: ${value.nextAction}`);
|
|
299
372
|
lines.push(`Expected evidence: ${value.expectedEvidence}`);
|
|
373
|
+
if (value.verificationProof !== undefined) {
|
|
374
|
+
lines.push(`Verification proof: ${value.verificationProof.toolName} - ${value.verificationProof.claim}`);
|
|
375
|
+
}
|
|
300
376
|
if (value.nextTrigger !== undefined) {
|
|
301
377
|
lines.push(`Next trigger: ${value.nextTrigger.kind.replaceAll('_', ' ')} - ${value.nextTrigger.condition}`);
|
|
302
378
|
if (value.nextTrigger.dueAt !== undefined) lines.push(`Due at: ${value.nextTrigger.dueAt}`);
|
|
@@ -312,6 +388,7 @@ function projectActionCheckpoint(checkpoint) {
|
|
|
312
388
|
lines.push(`Support choice: ${frame.supportChoice}`);
|
|
313
389
|
lines.push(`Risk: ${frame.risk}`);
|
|
314
390
|
lines.push(`Reversibility: ${frame.reversibility}`);
|
|
391
|
+
if (frame.decisionBasis !== undefined) lines.push(`Decision basis: ${frame.decisionBasis.join(' | ')}`);
|
|
315
392
|
}
|
|
316
393
|
if (value.evidenceReceipt !== undefined) {
|
|
317
394
|
const receipt = value.evidenceReceipt;
|
|
@@ -331,5 +408,7 @@ module.exports = {
|
|
|
331
408
|
assertActionCheckpointRevision,
|
|
332
409
|
emptyActionEvidenceReceipt,
|
|
333
410
|
normalizeActionCheckpoint,
|
|
411
|
+
normalizeVerificationProof,
|
|
334
412
|
projectActionCheckpoint,
|
|
413
|
+
successfulToolDigest,
|
|
335
414
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const crypto = require('node:crypto');
|
|
3
4
|
const { resolveCognitiveEvidenceGroup } = require('./cognitive-effective-view.cjs');
|
|
4
5
|
const {
|
|
5
6
|
buildCognitiveSalienceIndex,
|
|
@@ -55,15 +56,19 @@ function normalizeScopes(value) {
|
|
|
55
56
|
return scopes.every(Boolean) ? [...new Set(scopes)] : null;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
function cognitiveEvidenceRef(item) {
|
|
60
|
+
const scope = clean(item?.scope, 128);
|
|
61
|
+
const domain = String(item?.domain ?? '');
|
|
62
|
+
const key = clean(item?.key, 128);
|
|
63
|
+
const observationId = clean(item?.observationId ?? item?.observation_id, 128);
|
|
64
|
+
if (!scope || scope === 'runtime' || !FOCUS_DOMAINS.has(domain) || !key || !observationId) return '';
|
|
65
|
+
return crypto.createHash('sha256')
|
|
66
|
+
.update([scope, domain, key, observationId].join('\0'))
|
|
67
|
+
.digest('hex').slice(0, 16);
|
|
68
|
+
}
|
|
66
69
|
|
|
70
|
+
function normalizedFocusGroups(state) {
|
|
71
|
+
if (!Array.isArray(state?.observations)) return null;
|
|
67
72
|
const groups = new Map();
|
|
68
73
|
state.observations.forEach((item, index) => {
|
|
69
74
|
const domain = String(item?.domain ?? '');
|
|
@@ -80,7 +85,7 @@ function buildCognitiveFocusSelection(state, {
|
|
|
80
85
|
? 'legacy_unknown' : String(item.epistemic_state);
|
|
81
86
|
const occurredAt = Date.parse(String(item?.occurred_at ?? ''));
|
|
82
87
|
if (!FOCUS_DOMAINS.has(domain) || !key || !value || !scope || scope === 'runtime'
|
|
83
|
-
|| !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
|
|
88
|
+
|| !observationId || !Number.isFinite(confidence) || confidence < 0.5 || confidence > 1
|
|
84
89
|
|| !EPISTEMIC_MARKERS.has(epistemicState)
|
|
85
90
|
|| !Number.isFinite(occurredAt)) return;
|
|
86
91
|
const groupKey = `${scope}\0${domain}\0${key}`;
|
|
@@ -91,11 +96,66 @@ function buildCognitiveFocusSelection(state, {
|
|
|
91
96
|
});
|
|
92
97
|
groups.set(groupKey, existing);
|
|
93
98
|
});
|
|
99
|
+
for (const entries of groups.values()) {
|
|
100
|
+
entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
|
|
101
|
+
}
|
|
102
|
+
return groups;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function currentEvidenceRefs(entries, effective) {
|
|
106
|
+
if (!effective || effective.conflict) return [];
|
|
107
|
+
const withdrawals = entries.filter((item) => Boolean(item.withdraws));
|
|
108
|
+
const deletionCutoff = withdrawals.length > 0
|
|
109
|
+
? Math.max(...withdrawals.map((item) => item.occurredAt)) : Number.NEGATIVE_INFINITY;
|
|
110
|
+
const evidence = entries.filter((item) => !item.withdraws && item.occurredAt > deletionCutoff);
|
|
111
|
+
const superseded = new Set(evidence.map((item) => item.supersedes).filter(Boolean));
|
|
112
|
+
return evidence
|
|
113
|
+
.filter((item) => !superseded.has(item.observationId) && item.value === effective.value)
|
|
114
|
+
.map(cognitiveEvidenceRef)
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function evaluateCognitiveDecisionBasis(state, refs) {
|
|
119
|
+
if (refs === undefined || refs === null) return null;
|
|
120
|
+
if (!Array.isArray(refs) || refs.length < 1 || refs.length > 5
|
|
121
|
+
|| refs.some((ref) => !/^[a-f0-9]{16}$/u.test(String(ref)))
|
|
122
|
+
|| new Set(refs).size !== refs.length) {
|
|
123
|
+
return { status: 'unknown', currentRefs: [], staleRefs: [], unknownRefs: [] };
|
|
124
|
+
}
|
|
125
|
+
const groups = normalizedFocusGroups(state);
|
|
126
|
+
if (groups === null) return { status: 'unknown', currentRefs: [], staleRefs: [], unknownRefs: [...refs] };
|
|
127
|
+
const historical = new Set();
|
|
128
|
+
const current = new Set();
|
|
129
|
+
for (const entries of groups.values()) {
|
|
130
|
+
entries.map(cognitiveEvidenceRef).filter(Boolean).forEach((ref) => historical.add(ref));
|
|
131
|
+
const effective = resolveCognitiveEvidenceGroup(entries);
|
|
132
|
+
currentEvidenceRefs(entries, effective).forEach((ref) => current.add(ref));
|
|
133
|
+
}
|
|
134
|
+
const currentRefs = refs.filter((ref) => current.has(ref));
|
|
135
|
+
const staleRefs = refs.filter((ref) => !current.has(ref) && historical.has(ref));
|
|
136
|
+
const unknownRefs = refs.filter((ref) => !current.has(ref) && !historical.has(ref));
|
|
137
|
+
return {
|
|
138
|
+
status: staleRefs.length > 0 ? 'stale' : unknownRefs.length > 0 ? 'unknown' : 'current',
|
|
139
|
+
currentRefs,
|
|
140
|
+
staleRefs,
|
|
141
|
+
unknownRefs,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function buildCognitiveFocusSelection(state, {
|
|
146
|
+
focusScopes, maxItems = 6, maxChars = 900, currentSessionId,
|
|
147
|
+
} = {}) {
|
|
148
|
+
const scopes = normalizeScopes(focusScopes);
|
|
149
|
+
if (scopes === null || !Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 8
|
|
150
|
+
|| !Number.isSafeInteger(maxChars) || maxChars < 320 || maxChars > 1600) return null;
|
|
151
|
+
if (scopes.length === 0) return null;
|
|
152
|
+
|
|
153
|
+
const groups = normalizedFocusGroups(state);
|
|
154
|
+
if (groups === null) return null;
|
|
94
155
|
|
|
95
156
|
const salience = buildCognitiveSalienceIndex(state, { currentSessionId });
|
|
96
157
|
const ranked = [];
|
|
97
158
|
for (const entries of groups.values()) {
|
|
98
|
-
entries.sort((left, right) => left.occurredAt - right.occurredAt || left.index - right.index);
|
|
99
159
|
const latest = entries.at(-1);
|
|
100
160
|
if (!scopes.includes(latest.scope)) continue;
|
|
101
161
|
const effective = resolveCognitiveEvidenceGroup(entries);
|
|
@@ -117,7 +177,7 @@ function buildCognitiveFocusSelection(state, {
|
|
|
117
177
|
if (item.conflict) return `- Conflict: ${item.key} has incompatible evidence; verify before relying on it.`;
|
|
118
178
|
const marker = item.corrected ? ' [corrected]'
|
|
119
179
|
: item.contested ? ' [contested; stronger evidence]' : item.revised ? ' [revised]' : '';
|
|
120
|
-
return `- ${LABELS.get(item.domain)}: ${item.value}${EPISTEMIC_MARKERS.get(item.epistemicState)}${marker}`;
|
|
180
|
+
return `- ${LABELS.get(item.domain)}: ${item.value}${EPISTEMIC_MARKERS.get(item.epistemicState)}${marker} [ref:${cognitiveEvidenceRef(item)}]`;
|
|
121
181
|
});
|
|
122
182
|
while ([`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n').length > maxChars
|
|
123
183
|
&& lines.length > 0) lines.pop();
|
|
@@ -125,7 +185,12 @@ function buildCognitiveFocusSelection(state, {
|
|
|
125
185
|
const kept = selected.slice(0, lines.length);
|
|
126
186
|
return {
|
|
127
187
|
text: [`Current durable focus (${lines.length} items):`, ...lines, SAFETY_LINE].join('\n'),
|
|
128
|
-
selected: kept.map((item) => ({
|
|
188
|
+
selected: kept.map((item) => ({
|
|
189
|
+
scope: item.scope,
|
|
190
|
+
domain: item.domain,
|
|
191
|
+
key: item.key,
|
|
192
|
+
...(item.conflict ? {} : { evidenceRef: cognitiveEvidenceRef(item) }),
|
|
193
|
+
})),
|
|
129
194
|
};
|
|
130
195
|
}
|
|
131
196
|
|
|
@@ -133,4 +198,9 @@ function buildCognitiveFocusProjection(state, options) {
|
|
|
133
198
|
return buildCognitiveFocusSelection(state, options)?.text ?? null;
|
|
134
199
|
}
|
|
135
200
|
|
|
136
|
-
module.exports = {
|
|
201
|
+
module.exports = {
|
|
202
|
+
buildCognitiveFocusProjection,
|
|
203
|
+
buildCognitiveFocusSelection,
|
|
204
|
+
cognitiveEvidenceRef,
|
|
205
|
+
evaluateCognitiveDecisionBasis,
|
|
206
|
+
};
|
|
@@ -6,7 +6,10 @@ const path = require('node:path');
|
|
|
6
6
|
const { openCognitiveMemoryAdapter } = require('./cognitive-memory-adapter.cjs');
|
|
7
7
|
const { loadConfiguredCognitiveMemoryAdapter } = require('./cognitive-memory-provider.cjs');
|
|
8
8
|
const { buildCognitiveContextProjection } = require('./cognitive-context-projection.cjs');
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
buildCognitiveFocusSelection,
|
|
11
|
+
evaluateCognitiveDecisionBasis,
|
|
12
|
+
} = require('./cognitive-focus-projection.cjs');
|
|
10
13
|
const { cognitiveEntityKey, focusAccessObservation } = require('./cognitive-salience-policy.cjs');
|
|
11
14
|
const { authorizeAttentionCandidate } = require('./cognitive-attention-runtime.cjs');
|
|
12
15
|
|
|
@@ -490,6 +493,11 @@ function createCognitiveTurnLifecycle({
|
|
|
490
493
|
return [focus?.text, continuity].filter(Boolean).join('\n\n') || null;
|
|
491
494
|
}
|
|
492
495
|
|
|
496
|
+
function evaluateDecisionBasis(refs) {
|
|
497
|
+
const state = store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess);
|
|
498
|
+
return evaluateCognitiveDecisionBasis(state, refs);
|
|
499
|
+
}
|
|
500
|
+
|
|
493
501
|
function authorizeAttention(input) {
|
|
494
502
|
const candidate = input?.candidate;
|
|
495
503
|
const authorization = input?.authorization;
|
|
@@ -552,6 +560,7 @@ function createCognitiveTurnLifecycle({
|
|
|
552
560
|
withdrawFocusObservation,
|
|
553
561
|
authorizeAttention,
|
|
554
562
|
memoryStatus,
|
|
563
|
+
evaluateDecisionBasis,
|
|
555
564
|
projectForTurn,
|
|
556
565
|
endTurn,
|
|
557
566
|
read: () => store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess),
|
|
@@ -23,7 +23,7 @@ function bounded(value, max = 512) {
|
|
|
23
23
|
return text.length <= max ? text : `${text.slice(0, max - 3).trimEnd()}...`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function buildCognitiveWorkFocus(goal) {
|
|
26
|
+
function buildCognitiveWorkFocus(goal, { decisionFreshness } = {}) {
|
|
27
27
|
if (!goal || typeof goal !== 'object' || Array.isArray(goal)) return null;
|
|
28
28
|
const goalId = String(goal.goalId ?? '').trim();
|
|
29
29
|
const objective = bounded(goal.objective);
|
|
@@ -75,6 +75,11 @@ function buildCognitiveWorkFocus(goal) {
|
|
|
75
75
|
const frameSupportChoice = bounded(problemFrame?.supportChoice, 256);
|
|
76
76
|
const frameRisk = bounded(problemFrame?.risk);
|
|
77
77
|
const frameReversibility = bounded(problemFrame?.reversibility);
|
|
78
|
+
const frameDecisionBasis = Array.isArray(problemFrame?.decisionBasis)
|
|
79
|
+
&& problemFrame.decisionBasis.length >= 1 && problemFrame.decisionBasis.length <= 5
|
|
80
|
+
&& problemFrame.decisionBasis.every((item) => /^[a-f0-9]{16}$/u.test(String(item)))
|
|
81
|
+
&& new Set(problemFrame.decisionBasis).size === problemFrame.decisionBasis.length
|
|
82
|
+
? [...problemFrame.decisionBasis] : [];
|
|
78
83
|
const hasProblemFrame = problemFrame !== null && frameSuccessCriterion
|
|
79
84
|
&& frameMissingKnowledge.length > 0 && frameCandidates.length > 0
|
|
80
85
|
&& frameSelectedAction && frameSelectionReason && frameSupportChoice
|
|
@@ -83,8 +88,17 @@ function buildCognitiveWorkFocus(goal) {
|
|
|
83
88
|
&& nextAction && checkpointEvidence;
|
|
84
89
|
const epistemicState = hasCheckpoint && EPISTEMIC_STATES.has(checkpointEpistemicState)
|
|
85
90
|
? checkpointEpistemicState : hasCheckpoint ? 'legacy_unknown' : 'verified';
|
|
86
|
-
const
|
|
91
|
+
const decisionFreshnessStatus = frameDecisionBasis.length > 0
|
|
92
|
+
&& ['current', 'stale', 'unknown'].includes(decisionFreshness?.status)
|
|
93
|
+
? decisionFreshness.status : frameDecisionBasis.length > 0 ? 'unknown' : '';
|
|
94
|
+
const staleDecision = decisionFreshnessStatus === 'stale' || decisionFreshnessStatus === 'unknown';
|
|
95
|
+
const replanKnowledgeGap = 'Re-evaluate the selected action against current durable evidence.';
|
|
96
|
+
const replanTrigger = 'Re-evaluate the selected action against the current durable evidence.';
|
|
97
|
+
const replanEvidence = 'A replacement action is bound to current durable evidence refs.';
|
|
98
|
+
const knowledgeGapValue = staleDecision ? `${replanKnowledgeGap} ${frameMissingKnowledge.join(' | ')}`
|
|
99
|
+
: hasProblemFrame ? frameMissingKnowledge.join(' | ') : 'none';
|
|
87
100
|
const decisionThread = hasProblemFrame ? [
|
|
101
|
+
...(staleDecision ? [`Decision basis ${decisionFreshnessStatus}; replan before executing the selected action.`] : []),
|
|
88
102
|
`Goal is ${status}`,
|
|
89
103
|
`Phase ${checkpointPhase}`,
|
|
90
104
|
`Last verified: ${bounded(lastVerified, 80)}`,
|
|
@@ -112,6 +126,8 @@ function buildCognitiveWorkFocus(goal) {
|
|
|
112
126
|
hasProblemFrame ? frameSupportChoice : '',
|
|
113
127
|
hasProblemFrame ? frameRisk : '',
|
|
114
128
|
hasProblemFrame ? frameReversibility : '',
|
|
129
|
+
hasProblemFrame ? frameDecisionBasis.join('\0') : '',
|
|
130
|
+
decisionFreshnessStatus,
|
|
115
131
|
].join('\0'))
|
|
116
132
|
.digest('hex').slice(0, 40);
|
|
117
133
|
const keyRoot = `goal:${goalId}`;
|
|
@@ -128,24 +144,34 @@ function buildCognitiveWorkFocus(goal) {
|
|
|
128
144
|
`Next action: ${nextAction}`,
|
|
129
145
|
].join(' ')
|
|
130
146
|
: `Goal is ${status}.`),
|
|
131
|
-
confidence: 1,
|
|
147
|
+
confidence: 1,
|
|
148
|
+
epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
|
|
149
|
+
scope: focusScope,
|
|
132
150
|
},
|
|
133
151
|
{
|
|
134
152
|
domain: 'knowledge_gap', key: `${keyRoot}:knowledge-gaps`,
|
|
135
153
|
value: knowledgeGapValue,
|
|
136
154
|
confidence: 1,
|
|
137
|
-
epistemicState: hasProblemFrame ? 'unknown' : 'verified',
|
|
155
|
+
epistemicState: hasProblemFrame || staleDecision ? 'unknown' : 'verified',
|
|
138
156
|
scope: focusScope,
|
|
139
157
|
},
|
|
140
158
|
{
|
|
141
159
|
domain: 'next_trigger', key: `${keyRoot}:next`,
|
|
142
|
-
value: status === 'active' && hasCheckpoint
|
|
143
|
-
|
|
160
|
+
value: status === 'active' && hasCheckpoint
|
|
161
|
+
? staleDecision ? replanTrigger : triggerValue
|
|
162
|
+
: NEXT_TRIGGER.get(status),
|
|
163
|
+
confidence: 1,
|
|
164
|
+
epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
|
|
165
|
+
scope: focusScope,
|
|
144
166
|
},
|
|
145
167
|
{
|
|
146
168
|
domain: 'expected_evidence', key: `${keyRoot}:evidence`,
|
|
147
|
-
value: status === 'active' && hasCheckpoint
|
|
148
|
-
|
|
169
|
+
value: status === 'active' && hasCheckpoint
|
|
170
|
+
? staleDecision ? replanEvidence : checkpointEvidence
|
|
171
|
+
: completionCriterion,
|
|
172
|
+
confidence: 1,
|
|
173
|
+
epistemicState: staleDecision ? decisionFreshnessStatus : epistemicState,
|
|
174
|
+
scope: focusScope,
|
|
149
175
|
},
|
|
150
176
|
],
|
|
151
177
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const {
|
|
4
|
+
normalizeVerificationProof,
|
|
5
|
+
} = require('./cognitive-action-checkpoint.cjs');
|
|
6
|
+
|
|
3
7
|
function hasCompletionCriterion(goal) {
|
|
4
8
|
return typeof goal?.completionCriterion === 'string'
|
|
5
9
|
&& goal.completionCriterion.trim().length > 0;
|
|
@@ -10,6 +14,24 @@ function successfulRuntimeEvidence(checkpoint) {
|
|
|
10
14
|
return Number.isSafeInteger(successfulTools) && successfulTools > 0;
|
|
11
15
|
}
|
|
12
16
|
|
|
17
|
+
function verificationProofGaps(checkpoint) {
|
|
18
|
+
if (!checkpoint?.verificationProof) {
|
|
19
|
+
return ['Bind the completion claim to a successful verification tool from the checkpoint turn.'];
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
normalizeVerificationProof(checkpoint.verificationProof, checkpoint.evidenceReceipt);
|
|
23
|
+
return [];
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (/action-only tool/u.test(String(error?.message ?? ''))) {
|
|
26
|
+
return ['The completion proof names an action-only tool, not a verification tool.'];
|
|
27
|
+
}
|
|
28
|
+
if (/successful current-turn tool/u.test(String(error?.message ?? ''))) {
|
|
29
|
+
return ['The completion proof does not match a successful verification tool from the checkpoint turn.'];
|
|
30
|
+
}
|
|
31
|
+
return ['The completion verification proof is malformed.'];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
13
35
|
function evaluateGoalCompletionEvidence(goal) {
|
|
14
36
|
if (!hasCompletionCriterion(goal)) {
|
|
15
37
|
return {
|
|
@@ -40,9 +62,16 @@ function evaluateGoalCompletionEvidence(goal) {
|
|
|
40
62
|
if (checkpoint.epistemicState !== 'verified') {
|
|
41
63
|
gaps.push('The completion proof must be classified as verified.');
|
|
42
64
|
}
|
|
43
|
-
|
|
65
|
+
const hasRuntimeEvidence = successfulRuntimeEvidence(checkpoint);
|
|
66
|
+
if (!hasRuntimeEvidence) {
|
|
44
67
|
gaps.push('The runtime evidence receipt must contain at least one successful tool result.');
|
|
45
68
|
}
|
|
69
|
+
if (checkpoint.phase === 'verify'
|
|
70
|
+
&& checkpoint.evidenceBasis === 'runtime_tool'
|
|
71
|
+
&& checkpoint.epistemicState === 'verified'
|
|
72
|
+
&& hasRuntimeEvidence) {
|
|
73
|
+
gaps.push(...verificationProofGaps(checkpoint));
|
|
74
|
+
}
|
|
46
75
|
|
|
47
76
|
return {
|
|
48
77
|
required: true,
|
package/blun.mjs
CHANGED
|
@@ -245806,7 +245806,8 @@ var init_events$1 = __esmMin((() => {
|
|
|
245806
245806
|
selectionReason: string(),
|
|
245807
245807
|
supportChoice: string(),
|
|
245808
245808
|
risk: string(),
|
|
245809
|
-
reversibility: string()
|
|
245809
|
+
reversibility: string(),
|
|
245810
|
+
decisionBasis: array(string()).optional()
|
|
245810
245811
|
}).strict().optional(),
|
|
245811
245812
|
updatedAt: string(),
|
|
245812
245813
|
evidenceReceipt: object({
|
|
@@ -260306,6 +260307,7 @@ bytesWritten: number$1().int().nonnegative() });
|
|
|
260306
260307
|
var create_goal_default;
|
|
260307
260308
|
var init_create_goal$1 = __esmMin((() => {
|
|
260308
260309
|
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
|
+
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";
|
|
260309
260311
|
}));
|
|
260310
260312
|
//#endregion
|
|
260311
260313
|
//#region ../../packages/agent-core/src/tools/builtin/goal/serialize.ts
|
|
@@ -260333,7 +260335,8 @@ function createProblemFrameInputSchema() {
|
|
|
260333
260335
|
selectionReason: string().min(1).max(512),
|
|
260334
260336
|
supportChoice: string().min(1).max(256),
|
|
260335
260337
|
risk: string().min(1).max(512),
|
|
260336
|
-
reversibility: string().min(1).max(512)
|
|
260338
|
+
reversibility: string().min(1).max(512),
|
|
260339
|
+
decisionBasis: array(string().regex(/^[a-f0-9]{16}$/u)).min(1).max(5).optional()
|
|
260337
260340
|
}).strict();
|
|
260338
260341
|
}
|
|
260339
260342
|
function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFrame = false) {
|
|
@@ -260345,6 +260348,10 @@ function createActionCheckpointInputSchema(problemFrameSchema, requireProblemFra
|
|
|
260345
260348
|
lastVerified: string().min(1).max(512),
|
|
260346
260349
|
nextAction: string().min(1).max(512),
|
|
260347
260350
|
expectedEvidence: string().min(1).max(512),
|
|
260351
|
+
verificationProof: object({
|
|
260352
|
+
toolName: string().min(1).max(128),
|
|
260353
|
+
claim: string().min(1).max(512)
|
|
260354
|
+
}).strict().optional(),
|
|
260348
260355
|
nextTrigger: object({
|
|
260349
260356
|
kind: _enum(["immediate", "external_event", "time", "dependency", "user_decision"]),
|
|
260350
260357
|
condition: string().min(1).max(512),
|
|
@@ -261835,7 +261842,9 @@ var init_turn = __esmMin((() => {
|
|
|
261835
261842
|
try {
|
|
261836
261843
|
const focusScopes = cognitiveFocusScopesForTurn(input);
|
|
261837
261844
|
const lifecycle = this.getCognitiveLifecycle();
|
|
261838
|
-
const
|
|
261845
|
+
const goal = this.agent.goal.getGoal().goal;
|
|
261846
|
+
const decisionFreshness = lifecycle?.evaluateDecisionBasis(goal?.actionCheckpoint?.problemFrame?.decisionBasis) ?? null;
|
|
261847
|
+
const workFocus = buildCognitiveWorkFocus(goal, { decisionFreshness });
|
|
261839
261848
|
if (workFocus !== null) {
|
|
261840
261849
|
lifecycle?.recordFocusSnapshot({
|
|
261841
261850
|
snapshotId: workFocus.snapshotId,
|
|
@@ -262863,7 +262872,8 @@ var init_outcome_prompts = __esmMin((() => {}));
|
|
|
262863
262872
|
var update_goal_default;
|
|
262864
262873
|
var init_update_goal$1 = __esmMin((() => {
|
|
262865
262874
|
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";
|
|
262866
|
-
update_goal_default += "\
|
|
262875
|
+
update_goal_default += "\nBefore completing a goal with a criterion, bind the verified claim to the successful current-turn verification tool in `verificationProof`. A write or edit is an action, not proof that the changed behavior works. Run a separate observation, test, or counterexample probe and name that successful tool plus the exact claim it supports.\n";
|
|
262876
|
+
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";
|
|
262867
262877
|
}));
|
|
262868
262878
|
//#endregion
|
|
262869
262879
|
//#region ../../packages/agent-core/src/tools/builtin/goal/update-goal.ts
|