blun-king-cli 9.1.363 → 9.1.364

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.
@@ -230,6 +230,94 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
230
230
  return commitDurableStage(`focus-${input.snapshotId}`, observations);
231
231
  }
232
232
 
233
+ function commitExplicitFocusRevision(kind, input) {
234
+ const correction = kind === 'correction';
235
+ const allowed = correction
236
+ ? new Set(['requestId', 'targetObservationId', 'domain', 'key', 'value', 'scope', 'authority', 'confirmation', 'occurredAt', 'source'])
237
+ : new Set(['requestId', 'targetObservationId', 'domain', 'key', 'scope', 'authority', 'confirmation', 'occurredAt', 'source']);
238
+ if (!exactKeys(input, allowed)) fail('COGNITIVE_REVISION_INVALID');
239
+ const requestId = safeId(input.requestId);
240
+ const targetObservationId = safeId(input.targetObservationId);
241
+ const domain = String(input.domain ?? '');
242
+ const key = cleanLabel(input.key, 128);
243
+ const scope = cleanLabel(input.scope, 128);
244
+ const occurredAt = String(input.occurredAt ?? '').trim();
245
+ const source = input.source;
246
+ if (input.authority !== 'runtime_user_prompt_hook' || input.confirmation !== 'explicit_user_request') {
247
+ fail('COGNITIVE_REVISION_EXPLICIT_USER_REQUIRED');
248
+ }
249
+ if (!requestId || !targetObservationId || !FOCUS_DOMAINS.has(domain) || !key
250
+ || AUTHORITY_KEY_RE.test(key) || !scope || scope === 'runtime'
251
+ || Number.isNaN(Date.parse(occurredAt))
252
+ || !exactKeys(source, new Set(['provider', 'actorId', 'contextId', 'messageId']))) {
253
+ fail('COGNITIVE_REVISION_INVALID');
254
+ }
255
+ const normalizedSource = {
256
+ provider: safeId(source.provider),
257
+ actor_id: safeId(source.actorId),
258
+ context_id: safeId(source.contextId),
259
+ message_id: safeId(source.messageId),
260
+ };
261
+ if (Object.values(normalizedSource).some((value) => !value)) fail('COGNITIVE_REVISION_INVALID');
262
+ const value = correction ? cleanLabel(input.value, 512) : 'withdrawn';
263
+ if (!value) fail('COGNITIVE_REVISION_INVALID');
264
+ const eventId = digestId('revision', [tenant, agent, kind, requestId]);
265
+ const observationId = digestId('revisionobs', [eventId]);
266
+ const observation = {
267
+ observation_id: observationId,
268
+ domain,
269
+ key,
270
+ value,
271
+ confidence: 1,
272
+ scope,
273
+ ...(correction ? { supersedes: targetObservationId } : { withdraws: targetObservationId }),
274
+ };
275
+ const normalizedOccurredAt = new Date(occurredAt).toISOString();
276
+ for (let attempt = 0; attempt < 4; attempt += 1) {
277
+ const current = store.read({ tenantId: tenant, agentId: agent });
278
+ const existing = current.observations.find((item) => item.observation_id === observationId);
279
+ if (existing) {
280
+ const matches = existing.domain === observation.domain && existing.key === observation.key
281
+ && existing.value === observation.value && existing.confidence === 1
282
+ && existing.scope === observation.scope && existing.occurred_at === normalizedOccurredAt
283
+ && existing.supersedes === (observation.supersedes ?? null)
284
+ && existing.withdraws === (observation.withdraws ?? null)
285
+ && existing.source.provider === normalizedSource.provider
286
+ && existing.source.actor_id === normalizedSource.actor_id
287
+ && existing.source.context_id === normalizedSource.context_id
288
+ && existing.source.message_id === normalizedSource.message_id;
289
+ if (!matches) fail('COGNITIVE_REVISION_REQUEST_REUSE');
290
+ return { version: current.version, idempotent: true };
291
+ }
292
+ const target = current.observations.find((item) => item.observation_id === targetObservationId);
293
+ if (target && Date.parse(normalizedOccurredAt) < Date.parse(target.occurred_at)) {
294
+ fail('COGNITIVE_REVISION_TIME_ORDER');
295
+ }
296
+ try {
297
+ return store.commit({
298
+ tenantId: tenant,
299
+ agentId: agent,
300
+ eventId,
301
+ expectedVersion: current.version,
302
+ occurredAt: normalizedOccurredAt,
303
+ source: normalizedSource,
304
+ observations: [observation],
305
+ });
306
+ } catch (error) {
307
+ if (error?.code !== 'COGNITIVE_VERSION_CONFLICT' || attempt === 3) throw error;
308
+ }
309
+ }
310
+ fail('COGNITIVE_VERSION_CONFLICT');
311
+ }
312
+
313
+ function correctFocusObservation(input) {
314
+ return commitExplicitFocusRevision('correction', input);
315
+ }
316
+
317
+ function withdrawFocusObservation(input) {
318
+ return commitExplicitFocusRevision('withdrawal', input);
319
+ }
320
+
233
321
  function toolFields(input, allowedKeys) {
234
322
  if (!exactKeys(input, allowedKeys)) fail('COGNITIVE_LIFECYCLE_INVALID');
235
323
  const turnId = safeTurnId(input.turnId);
@@ -337,6 +425,8 @@ function createCognitiveTurnLifecycle({ home, tenantId, agentId, runtimeId, now
337
425
  recordToolResult,
338
426
  recordToolBatch,
339
427
  recordFocusSnapshot,
428
+ correctFocusObservation,
429
+ withdrawFocusObservation,
340
430
  authorizeAttention,
341
431
  projectForTurn,
342
432
  endTurn,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.363",
3
+ "version": "9.1.364",
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": {