blun-king-cli 9.1.386 → 9.1.387

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.
@@ -20,7 +20,7 @@ function safeId(value) {
20
20
  return SAFE_ID_RE.test(text) ? text : '';
21
21
  }
22
22
 
23
- function authorizeAttentionCandidate({ store, tenantId, currentAgent, now, input } = {}) {
23
+ function authorizeAttentionCandidate({ store, tenantId, currentAgent, now, input, memoryAccess } = {}) {
24
24
  if (!store || typeof store.authorizeAttention !== 'function' || typeof now !== 'function'
25
25
  || !exactKeys(input, new Set(['candidate', 'channel', 'authorization']))) fail();
26
26
  const tenant = safeId(tenantId);
@@ -61,7 +61,7 @@ function authorizeAttentionCandidate({ store, tenantId, currentAgent, now, input
61
61
  receiptId,
62
62
  authority,
63
63
  authorizedAt: new Date(issuedAtMs).toISOString(),
64
- });
64
+ }, memoryAccess);
65
65
  if (!admitted.claimed) {
66
66
  return {
67
67
  accepted: false,
@@ -2,8 +2,13 @@
2
2
 
3
3
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
4
4
 
5
- const COGNITIVE_MEMORY_ADAPTER_VERSION = 1;
5
+ const COGNITIVE_MEMORY_ADAPTER_VERSION = 2;
6
6
  const KIND_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
7
+ const ACCESS_KEYS = Object.freeze([
8
+ 'actorId', 'agentId', 'channelId', 'conversationId', 'permissionContext',
9
+ 'portalId', 'requestedScope', 'tenantId', 'userId',
10
+ ]);
11
+ const PERMISSION_KEYS = Object.freeze(['authority', 'decision', 'receiptId']);
7
12
  const REQUIRED_METHODS = Object.freeze([
8
13
  'commit',
9
14
  'read',
@@ -20,6 +25,50 @@ function fail(code) {
20
25
  throw error;
21
26
  }
22
27
 
28
+ function exactKeys(value, expected) {
29
+ return value && typeof value === 'object' && !Array.isArray(value)
30
+ && Object.keys(value).sort().join('\0') === [...expected].sort().join('\0');
31
+ }
32
+
33
+ function safeId(value) {
34
+ const text = String(value ?? '').trim();
35
+ return KIND_RE.test(text) ? text : '';
36
+ }
37
+
38
+ function normalizeMemoryAccess(access, request) {
39
+ if (!exactKeys(access, ACCESS_KEYS) || !exactKeys(access.permissionContext, PERMISSION_KEYS)) {
40
+ fail('COGNITIVE_MEMORY_ACCESS_INVALID');
41
+ }
42
+ const normalized = {
43
+ tenantId: safeId(access.tenantId),
44
+ userId: safeId(access.userId),
45
+ actorId: safeId(access.actorId),
46
+ agentId: safeId(access.agentId),
47
+ portalId: safeId(access.portalId),
48
+ channelId: safeId(access.channelId),
49
+ conversationId: safeId(access.conversationId),
50
+ requestedScope: safeId(access.requestedScope),
51
+ permissionContext: {
52
+ authority: safeId(access.permissionContext.authority),
53
+ decision: String(access.permissionContext.decision ?? ''),
54
+ receiptId: safeId(access.permissionContext.receiptId),
55
+ },
56
+ };
57
+ if (!normalized.tenantId || !normalized.userId || !normalized.actorId || !normalized.agentId
58
+ || !normalized.portalId || !normalized.channelId || !normalized.conversationId
59
+ || !normalized.requestedScope || !normalized.permissionContext.authority
60
+ || !normalized.permissionContext.receiptId) fail('COGNITIVE_MEMORY_ACCESS_INVALID');
61
+ if (normalized.permissionContext.decision !== 'passed') fail('COGNITIVE_MEMORY_ACCESS_DENIED');
62
+ const requestTenant = safeId(request?.tenantId);
63
+ const requestAgent = safeId(request?.agentId ?? request?.candidate?.responsible_agent ?? request?.claimedBy);
64
+ if (!requestTenant || !requestAgent
65
+ || requestTenant !== normalized.tenantId || requestAgent !== normalized.agentId) {
66
+ fail('COGNITIVE_MEMORY_ACCESS_SCOPE_MISMATCH');
67
+ }
68
+ normalized.permissionContext = Object.freeze(normalized.permissionContext);
69
+ return Object.freeze(normalized);
70
+ }
71
+
23
72
  function assertCognitiveMemoryAdapter(adapter) {
24
73
  if (!adapter || typeof adapter !== 'object' || Array.isArray(adapter)) {
25
74
  fail('COGNITIVE_MEMORY_ADAPTER_INVALID');
@@ -34,14 +83,35 @@ function assertCognitiveMemoryAdapter(adapter) {
34
83
  return adapter;
35
84
  }
36
85
 
86
+ function guardCognitiveMemoryAdapter(adapter) {
87
+ const raw = assertCognitiveMemoryAdapter(adapter);
88
+ const guarded = {
89
+ contractVersion: raw.contractVersion,
90
+ kind: raw.kind,
91
+ commit: (input, access) => raw.commit(input, normalizeMemoryAccess(access, input)),
92
+ read: (input, access) => raw.read(input, normalizeMemoryAccess(access, input)),
93
+ hasEvent: (input, access) => {
94
+ if (!exactKeys(input, ['agentId', 'eventId', 'tenantId']) || !safeId(input.eventId)) {
95
+ fail('COGNITIVE_MEMORY_ACCESS_INVALID');
96
+ }
97
+ return raw.hasEvent(input, normalizeMemoryAccess(access, input));
98
+ },
99
+ verify: (input, access) => raw.verify(input, normalizeMemoryAccess(access, input)),
100
+ claimAttention: (input, access) => raw.claimAttention(input, normalizeMemoryAccess(access, input)),
101
+ authorizeAttention: (input, access) => raw.authorizeAttention(input, normalizeMemoryAccess(access, input)),
102
+ close: () => raw.close(),
103
+ };
104
+ return Object.freeze(guarded);
105
+ }
106
+
37
107
  function createLocalCognitiveMemoryAdapter({ home } = {}) {
38
108
  const store = openCognitiveStateStore({ home });
39
- return assertCognitiveMemoryAdapter({
109
+ return guardCognitiveMemoryAdapter({
40
110
  contractVersion: COGNITIVE_MEMORY_ADAPTER_VERSION,
41
111
  kind: 'local-cognitive-event-store',
42
112
  commit: (input) => store.commit(input),
43
113
  read: (input) => store.read(input),
44
- hasEvent: (eventId) => store.hasEvent(eventId),
114
+ hasEvent: (input) => store.hasEvent(input),
45
115
  verify: (input) => store.verify(input),
46
116
  claimAttention: (input) => store.claimAttention(input),
47
117
  authorizeAttention: (input) => store.authorizeAttention(input),
@@ -52,7 +122,7 @@ function createLocalCognitiveMemoryAdapter({ home } = {}) {
52
122
  function openCognitiveMemoryAdapter({ home, adapter } = {}) {
53
123
  return adapter === undefined
54
124
  ? createLocalCognitiveMemoryAdapter({ home })
55
- : assertCognitiveMemoryAdapter(adapter);
125
+ : guardCognitiveMemoryAdapter(adapter);
56
126
  }
57
127
 
58
128
  module.exports = {
@@ -60,5 +130,6 @@ module.exports = {
60
130
  REQUIRED_METHODS,
61
131
  assertCognitiveMemoryAdapter,
62
132
  createLocalCognitiveMemoryAdapter,
133
+ normalizeMemoryAccess,
63
134
  openCognitiveMemoryAdapter,
64
135
  };
@@ -289,8 +289,18 @@ function openCognitiveStateStore({ home } = {}) {
289
289
  };
290
290
  }
291
291
 
292
- function hasEvent(eventId) {
293
- const event = safeId(eventId);
292
+ function hasEvent(input) {
293
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
294
+ const keys = Object.keys(input);
295
+ const event = safeId(input.eventId);
296
+ const tenant = safeId(input.tenantId);
297
+ const agent = safeId(input.agentId);
298
+ if (keys.length !== 3 || !keys.every((key) => ['agentId', 'eventId', 'tenantId'].includes(key))
299
+ || !event || !tenant || !agent) fail('COGNITIVE_INVALID_EVENT');
300
+ return Boolean(db.prepare(`SELECT 1 AS found FROM cognitive_events
301
+ WHERE event_id = ? AND tenant_id = ? AND agent_id = ?`).get(event, tenant, agent));
302
+ }
303
+ const event = safeId(input);
294
304
  if (!event) fail('COGNITIVE_INVALID_EVENT');
295
305
  return Boolean(db.prepare('SELECT 1 AS found FROM cognitive_events WHERE event_id = ?').get(event));
296
306
  }
@@ -82,6 +82,21 @@ function createCognitiveTurnLifecycle({
82
82
  const runtime = safeId(runtimeId ?? `runtime-${process.pid}-${Date.now()}`);
83
83
  if (!tenant || !agent || !runtime || typeof now !== 'function') fail('COGNITIVE_LIFECYCLE_INVALID');
84
84
  const store = openCognitiveMemoryAdapter({ home, adapter: memoryAdapter });
85
+ const internalMemoryAccess = Object.freeze({
86
+ tenantId: tenant,
87
+ userId: agent,
88
+ actorId: agent,
89
+ agentId: agent,
90
+ portalId: 'cli',
91
+ channelId: 'runtime',
92
+ conversationId: runtime,
93
+ requestedScope: 'agent:memory',
94
+ permissionContext: Object.freeze({
95
+ authority: 'runtime_internal',
96
+ decision: 'passed',
97
+ receiptId: runtime,
98
+ }),
99
+ });
85
100
  const stageTimes = new Map();
86
101
  const stageResults = new Map();
87
102
 
@@ -104,7 +119,7 @@ function createCognitiveTurnLifecycle({
104
119
  ...item,
105
120
  }));
106
121
  for (let attempt = 0; attempt < 4; attempt += 1) {
107
- const expectedVersion = store.read({ tenantId: tenant, agentId: agent }).version;
122
+ const expectedVersion = store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess).version;
108
123
  try {
109
124
  const result = store.commit({
110
125
  tenantId: tenant,
@@ -119,7 +134,7 @@ function createCognitiveTurnLifecycle({
119
134
  message_id: stageKey,
120
135
  },
121
136
  observations: normalized,
122
- });
137
+ }, internalMemoryAccess);
123
138
  stageResults.set(stageKey, result);
124
139
  return result;
125
140
  } catch (error) {
@@ -132,7 +147,7 @@ function createCognitiveTurnLifecycle({
132
147
  function commitDurableStage(stageKey, observations) {
133
148
  if (stageResults.has(stageKey)) return { ...stageResults.get(stageKey), idempotent: true };
134
149
  const eventId = digestId('durable', [tenant, agent, stageKey]);
135
- if (store.hasEvent(eventId)) {
150
+ if (store.hasEvent({ eventId, tenantId: tenant, agentId: agent }, internalMemoryAccess)) {
136
151
  const result = { idempotent: true };
137
152
  stageResults.set(stageKey, result);
138
153
  return result;
@@ -143,12 +158,12 @@ function createCognitiveTurnLifecycle({
143
158
  ...item,
144
159
  }));
145
160
  for (let attempt = 0; attempt < 4; attempt += 1) {
146
- if (store.hasEvent(eventId)) {
161
+ if (store.hasEvent({ eventId, tenantId: tenant, agentId: agent }, internalMemoryAccess)) {
147
162
  const result = { idempotent: true };
148
163
  stageResults.set(stageKey, result);
149
164
  return result;
150
165
  }
151
- const expectedVersion = store.read({ tenantId: tenant, agentId: agent }).version;
166
+ const expectedVersion = store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess).version;
152
167
  try {
153
168
  const result = store.commit({
154
169
  tenantId: tenant,
@@ -163,7 +178,7 @@ function createCognitiveTurnLifecycle({
163
178
  message_id: stageKey,
164
179
  },
165
180
  observations: normalized,
166
- });
181
+ }, internalMemoryAccess);
167
182
  stageResults.set(stageKey, result);
168
183
  return result;
169
184
  } catch (error) {
@@ -288,8 +303,23 @@ function createCognitiveTurnLifecycle({
288
303
  ...(correction ? { supersedes: targetObservationId } : { withdraws: targetObservationId }),
289
304
  };
290
305
  const normalizedOccurredAt = new Date(occurredAt).toISOString();
306
+ const revisionMemoryAccess = {
307
+ tenantId: tenant,
308
+ userId: normalizedSource.actor_id,
309
+ actorId: normalizedSource.actor_id,
310
+ agentId: agent,
311
+ portalId: normalizedSource.provider,
312
+ channelId: normalizedSource.provider,
313
+ conversationId: normalizedSource.context_id,
314
+ requestedScope: scope,
315
+ permissionContext: {
316
+ authority: input.authority,
317
+ decision: 'passed',
318
+ receiptId: requestId,
319
+ },
320
+ };
291
321
  for (let attempt = 0; attempt < 4; attempt += 1) {
292
- const current = store.read({ tenantId: tenant, agentId: agent });
322
+ const current = store.read({ tenantId: tenant, agentId: agent }, revisionMemoryAccess);
293
323
  const existing = current.observations.find((item) => item.observation_id === observationId);
294
324
  if (existing) {
295
325
  const matches = existing.domain === observation.domain && existing.key === observation.key
@@ -317,7 +347,7 @@ function createCognitiveTurnLifecycle({
317
347
  occurredAt: normalizedOccurredAt,
318
348
  source: normalizedSource,
319
349
  observations: [observation],
320
- });
350
+ }, revisionMemoryAccess);
321
351
  } catch (error) {
322
352
  if (error?.code !== 'COGNITIVE_VERSION_CONFLICT' || attempt === 3) throw error;
323
353
  }
@@ -414,7 +444,7 @@ function createCognitiveTurnLifecycle({
414
444
  if (!exactKeys(input, new Set(['turnId', 'focusScopes']))) fail('COGNITIVE_LIFECYCLE_INVALID');
415
445
  const turnId = safeTurnId(input.turnId);
416
446
  if (turnId === null) fail('COGNITIVE_LIFECYCLE_INVALID');
417
- const state = store.read({ tenantId: tenant, agentId: agent });
447
+ const state = store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess);
418
448
  const continuity = buildCognitiveContextProjection(state, {
419
449
  currentTurnId: turnId,
420
450
  currentRuntimeId: runtime,
@@ -424,12 +454,30 @@ function createCognitiveTurnLifecycle({
424
454
  }
425
455
 
426
456
  function authorizeAttention(input) {
457
+ const candidate = input?.candidate;
458
+ const authorization = input?.authorization;
459
+ const memoryAccess = {
460
+ tenantId: tenant,
461
+ userId: String(candidate?.subject_id ?? ''),
462
+ actorId: agent,
463
+ agentId: agent,
464
+ portalId: 'cli',
465
+ channelId: String(input?.channel ?? ''),
466
+ conversationId: runtime,
467
+ requestedScope: String(candidate?.evidence_scope ?? ''),
468
+ permissionContext: {
469
+ authority: String(authorization?.authority ?? ''),
470
+ decision: authorization?.decision === 'passed' ? 'passed' : 'blocked',
471
+ receiptId: String(authorization?.receiptId ?? ''),
472
+ },
473
+ };
427
474
  return authorizeAttentionCandidate({
428
475
  store,
429
476
  tenantId: tenant,
430
477
  currentAgent: agent,
431
478
  now,
432
479
  input,
480
+ memoryAccess,
433
481
  });
434
482
  }
435
483
 
@@ -445,8 +493,8 @@ function createCognitiveTurnLifecycle({
445
493
  authorizeAttention,
446
494
  projectForTurn,
447
495
  endTurn,
448
- read: () => store.read({ tenantId: tenant, agentId: agent }),
449
- verify: () => store.verify({ tenantId: tenant, agentId: agent }),
496
+ read: () => store.read({ tenantId: tenant, agentId: agent }, internalMemoryAccess),
497
+ verify: () => store.verify({ tenantId: tenant, agentId: agent }, internalMemoryAccess),
450
498
  close: () => store.close(),
451
499
  };
452
500
  }
@@ -87,7 +87,24 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
87
87
  value.tenantId, value.agentId, value.actorId, value.sourcePortal, value.sourceChannel,
88
88
  value.conversationId, value.messageId, value.occurredAt, value.topic,
89
89
  ]);
90
- if (store.hasEvent(eventId)) return { claimed: true, idempotent: true };
90
+ const memoryAccess = {
91
+ tenantId: value.tenantId,
92
+ userId: value.actorId,
93
+ actorId: value.actorId,
94
+ agentId: value.agentId,
95
+ portalId: value.sourcePortal,
96
+ channelId: value.sourceChannel,
97
+ conversationId: value.conversationId,
98
+ requestedScope: relationshipScope(value.actorId),
99
+ permissionContext: {
100
+ authority: 'runtime_personality_policy',
101
+ decision: 'passed',
102
+ receiptId: eventId,
103
+ },
104
+ };
105
+ if (store.hasEvent({ eventId, tenantId: value.tenantId, agentId: value.agentId }, memoryAccess)) {
106
+ return { claimed: true, idempotent: true };
107
+ }
91
108
  const claim = store.claimAttention({
92
109
  tenantId: value.tenantId,
93
110
  candidate: {
@@ -107,10 +124,10 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
107
124
  },
108
125
  claimedBy: value.agentId,
109
126
  claimedAt: value.occurredAt,
110
- });
127
+ }, memoryAccess);
111
128
  if (!claim.claimed) return { claimed: false, next_allowed_at: claim.next_allowed_at };
112
129
  for (let attempt = 0; attempt < 3; attempt += 1) {
113
- const current = store.read({ tenantId: value.tenantId, agentId: value.agentId });
130
+ const current = store.read({ tenantId: value.tenantId, agentId: value.agentId }, memoryAccess);
114
131
  try {
115
132
  store.commit({
116
133
  tenantId: value.tenantId,
@@ -133,7 +150,7 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
133
150
  epistemic_state: 'verified',
134
151
  scope: relationshipScope(value.actorId),
135
152
  }],
136
- });
153
+ }, memoryAccess);
137
154
  return { claimed: true, idempotent: false };
138
155
  } catch (error) {
139
156
  if (error?.code !== 'COGNITIVE_VERSION_CONFLICT' || attempt === 2) throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.386",
3
+ "version": "9.1.387",
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": {