blun-king-cli 9.1.387 → 9.1.389

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
4
4
 
5
- const COGNITIVE_MEMORY_ADAPTER_VERSION = 2;
5
+ const COGNITIVE_MEMORY_ADAPTER_VERSION = 4;
6
6
  const KIND_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
7
7
  const ACCESS_KEYS = Object.freeze([
8
8
  'actorId', 'agentId', 'channelId', 'conversationId', 'permissionContext',
@@ -69,6 +69,47 @@ function normalizeMemoryAccess(access, request) {
69
69
  return Object.freeze(normalized);
70
70
  }
71
71
 
72
+ function isInternalAgentAccess(access) {
73
+ return access.userId === access.agentId
74
+ && access.actorId === access.agentId
75
+ && access.portalId === 'cli'
76
+ && access.channelId === 'runtime'
77
+ && access.requestedScope === 'agent:memory'
78
+ && access.permissionContext.authority === 'runtime_internal'
79
+ && access.permissionContext.receiptId === access.conversationId;
80
+ }
81
+
82
+ function assertVisibleScope(scope, access) {
83
+ if (!isInternalAgentAccess(access) && safeId(scope) !== access.requestedScope) {
84
+ fail('COGNITIVE_MEMORY_VISIBILITY_DENIED');
85
+ }
86
+ }
87
+
88
+ function assertCommitAccess(input, access) {
89
+ if (isInternalAgentAccess(access)) return;
90
+ if (!Array.isArray(input?.observations) || input.observations.length < 1) {
91
+ fail('COGNITIVE_MEMORY_VISIBILITY_DENIED');
92
+ }
93
+ for (const observation of input.observations) assertVisibleScope(observation?.scope, access);
94
+ if (safeId(input?.source?.provider) !== access.portalId
95
+ || safeId(input?.source?.channel_id) !== access.channelId
96
+ || safeId(input?.source?.actor_id) !== access.actorId
97
+ || safeId(input?.source?.context_id) !== access.conversationId) {
98
+ fail('COGNITIVE_MEMORY_SOURCE_MISMATCH');
99
+ }
100
+ }
101
+
102
+ function projectReadableState(state, access) {
103
+ if (isInternalAgentAccess(access)) return state;
104
+ if (!state || typeof state !== 'object' || !Array.isArray(state.observations)) {
105
+ fail('COGNITIVE_MEMORY_ADAPTER_INVALID');
106
+ }
107
+ return {
108
+ ...state,
109
+ observations: state.observations.filter((item) => safeId(item?.scope) === access.requestedScope),
110
+ };
111
+ }
112
+
72
113
  function assertCognitiveMemoryAdapter(adapter) {
73
114
  if (!adapter || typeof adapter !== 'object' || Array.isArray(adapter)) {
74
115
  fail('COGNITIVE_MEMORY_ADAPTER_INVALID');
@@ -88,17 +129,41 @@ function guardCognitiveMemoryAdapter(adapter) {
88
129
  const guarded = {
89
130
  contractVersion: raw.contractVersion,
90
131
  kind: raw.kind,
91
- commit: (input, access) => raw.commit(input, normalizeMemoryAccess(access, input)),
92
- read: (input, access) => raw.read(input, normalizeMemoryAccess(access, input)),
132
+ commit: (input, access) => {
133
+ const normalizedAccess = normalizeMemoryAccess(access, input);
134
+ assertCommitAccess(input, normalizedAccess);
135
+ return raw.commit(input, normalizedAccess);
136
+ },
137
+ read: (input, access) => {
138
+ const normalizedAccess = normalizeMemoryAccess(access, input);
139
+ return projectReadableState(raw.read(input, normalizedAccess), normalizedAccess);
140
+ },
93
141
  hasEvent: (input, access) => {
94
- if (!exactKeys(input, ['agentId', 'eventId', 'tenantId']) || !safeId(input.eventId)) {
142
+ if (!exactKeys(input, ['agentId', 'eventId', 'tenantId', 'visibilityScope'])
143
+ || !safeId(input.eventId) || !safeId(input.visibilityScope)) {
95
144
  fail('COGNITIVE_MEMORY_ACCESS_INVALID');
96
145
  }
97
- return raw.hasEvent(input, normalizeMemoryAccess(access, input));
146
+ const normalizedAccess = normalizeMemoryAccess(access, input);
147
+ assertVisibleScope(input.visibilityScope, normalizedAccess);
148
+ return raw.hasEvent(input, normalizedAccess);
149
+ },
150
+ verify: (input, access) => {
151
+ const normalizedAccess = normalizeMemoryAccess(access, input);
152
+ const result = raw.verify(input, normalizedAccess);
153
+ if (isInternalAgentAccess(normalizedAccess)) return result;
154
+ if (!result || typeof result.valid !== 'boolean') fail('COGNITIVE_MEMORY_ADAPTER_INVALID');
155
+ return { valid: result.valid };
156
+ },
157
+ claimAttention: (input, access) => {
158
+ const normalizedAccess = normalizeMemoryAccess(access, input);
159
+ assertVisibleScope(input?.candidate?.evidence_scope, normalizedAccess);
160
+ return raw.claimAttention(input, normalizedAccess);
161
+ },
162
+ authorizeAttention: (input, access) => {
163
+ const normalizedAccess = normalizeMemoryAccess(access, input);
164
+ assertVisibleScope(input?.candidate?.evidence_scope, normalizedAccess);
165
+ return raw.authorizeAttention(input, normalizedAccess);
98
166
  },
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
167
  close: () => raw.close(),
103
168
  };
104
169
  return Object.freeze(guarded);
@@ -110,7 +175,10 @@ function createLocalCognitiveMemoryAdapter({ home } = {}) {
110
175
  contractVersion: COGNITIVE_MEMORY_ADAPTER_VERSION,
111
176
  kind: 'local-cognitive-event-store',
112
177
  commit: (input) => store.commit(input),
113
- read: (input) => store.read(input),
178
+ read: (input, access) => store.read({
179
+ ...input,
180
+ visibilityScope: isInternalAgentAccess(access) ? 'agent:memory' : access.requestedScope,
181
+ }),
114
182
  hasEvent: (input) => store.hasEvent(input),
115
183
  verify: (input) => store.verify(input),
116
184
  claimAttention: (input) => store.claimAttention(input),
@@ -110,6 +110,7 @@ function matchesRevisionReplay(items, target, command, commandSource) {
110
110
  : item.withdraws === target.observationId && item.value === 'withdrawn';
111
111
  return linked
112
112
  && item.source?.provider === commandSource.source.provider
113
+ && item.source?.channel_id === commandSource.source.channelId
113
114
  && item.source?.actor_id === commandSource.source.actorId
114
115
  && item.source?.context_id === commandSource.source.contextId
115
116
  && item.source?.message_id === commandSource.source.messageId
@@ -133,6 +134,7 @@ function normalizeTelegramSource(source) {
133
134
  occurredAt: timestamp,
134
135
  source: {
135
136
  provider: 'telegram',
137
+ channelId: 'direct',
136
138
  actorId: `tg_user_${userId}`,
137
139
  contextId: `tg_chat_${chatId}`,
138
140
  messageId: `tg_message_${messageId}`,
@@ -153,6 +155,7 @@ function localSource({ env, sessionId, occurredAt, nonce }) {
153
155
  occurredAt,
154
156
  source: {
155
157
  provider: 'cli',
158
+ channelId: 'terminal',
156
159
  actorId: digestId('localuser', [actor]),
157
160
  contextId: digestId('clisession', [session]),
158
161
  messageId: digestId('cliinput', [unique]),
@@ -42,10 +42,11 @@ function exactKeys(value, keys) {
42
42
  }
43
43
 
44
44
  function normalizeSource(source) {
45
- const allowed = new Set(['provider', 'actor_id', 'context_id', 'message_id']);
45
+ const allowed = new Set(['provider', 'channel_id', 'actor_id', 'context_id', 'message_id']);
46
46
  if (!exactKeys(source, allowed) || hasForbiddenKey(source)) fail('COGNITIVE_FORBIDDEN_FIELD');
47
47
  const normalized = {
48
48
  provider: safeId(source.provider),
49
+ channel_id: safeId(source.channel_id),
49
50
  actor_id: safeId(source.actor_id),
50
51
  context_id: safeId(source.context_id),
51
52
  message_id: safeId(source.message_id),
@@ -167,6 +168,8 @@ function initialize(db) {
167
168
  );
168
169
  CREATE INDEX IF NOT EXISTS cognitive_observations_stream
169
170
  ON cognitive_observations (tenant_id, agent_id, occurred_at, observation_id);
171
+ CREATE INDEX IF NOT EXISTS cognitive_observations_visibility
172
+ ON cognitive_observations (tenant_id, agent_id, scope, occurred_at, observation_id);
170
173
  CREATE TABLE IF NOT EXISTS cognitive_attention_claims (
171
174
  tenant_id TEXT NOT NULL,
172
175
  candidate_id TEXT NOT NULL,
@@ -262,14 +265,20 @@ function openCognitiveStateStore({ home } = {}) {
262
265
  }
263
266
  }
264
267
 
265
- function read({ tenantId, agentId } = {}) {
268
+ function read({ tenantId, agentId, visibilityScope } = {}) {
266
269
  const tenant = safeId(tenantId);
267
270
  const agent = safeId(agentId);
268
- if (!tenant || !agent) fail('COGNITIVE_INVALID_EVENT');
271
+ const requestedVisibility = visibilityScope === undefined ? '' : safeId(visibilityScope);
272
+ if (!tenant || !agent || (visibilityScope !== undefined && !requestedVisibility)) {
273
+ fail('COGNITIVE_INVALID_EVENT');
274
+ }
269
275
  const stream = db.prepare('SELECT version, updated_at FROM cognitive_streams WHERE tenant_id = ? AND agent_id = ?').get(tenant, agent);
270
- const rows = db.prepare(`SELECT domain, fact_key, value_text, confidence, epistemic_state, scope, source_json, occurred_at, observation_id,
271
- supersedes_observation_id, withdraws_observation_id
272
- FROM cognitive_observations WHERE tenant_id = ? AND agent_id = ? ORDER BY occurred_at, rowid`).all(tenant, agent);
276
+ const select = `SELECT domain, fact_key, value_text, confidence, epistemic_state, scope, source_json, occurred_at, observation_id,
277
+ supersedes_observation_id, withdraws_observation_id FROM cognitive_observations`;
278
+ const rows = requestedVisibility && requestedVisibility !== 'agent:memory'
279
+ ? db.prepare(`${select} WHERE tenant_id = ? AND agent_id = ? AND scope = ? ORDER BY occurred_at, rowid`)
280
+ .all(tenant, agent, requestedVisibility)
281
+ : db.prepare(`${select} WHERE tenant_id = ? AND agent_id = ? ORDER BY occurred_at, rowid`).all(tenant, agent);
273
282
  return {
274
283
  version: Number(stream?.version ?? 0),
275
284
  ...(stream?.updated_at ? { updated_at: stream.updated_at } : {}),
@@ -295,8 +304,16 @@ function openCognitiveStateStore({ home } = {}) {
295
304
  const event = safeId(input.eventId);
296
305
  const tenant = safeId(input.tenantId);
297
306
  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');
307
+ const visibilityScope = safeId(input.visibilityScope);
308
+ if (keys.length !== 4
309
+ || !keys.every((key) => ['agentId', 'eventId', 'tenantId', 'visibilityScope'].includes(key))
310
+ || !event || !tenant || !agent || !visibilityScope) fail('COGNITIVE_INVALID_EVENT');
311
+ if (visibilityScope !== 'agent:memory') {
312
+ return Boolean(db.prepare(`SELECT 1 AS found FROM cognitive_events AS event
313
+ INNER JOIN cognitive_observations AS observation ON observation.event_id = event.event_id
314
+ WHERE event.event_id = ? AND event.tenant_id = ? AND event.agent_id = ? AND observation.scope = ?
315
+ LIMIT 1`).get(event, tenant, agent, visibilityScope));
316
+ }
300
317
  return Boolean(db.prepare(`SELECT 1 AS found FROM cognitive_events
301
318
  WHERE event_id = ? AND tenant_id = ? AND agent_id = ?`).get(event, tenant, agent));
302
319
  }
@@ -129,6 +129,7 @@ function createCognitiveTurnLifecycle({
129
129
  occurredAt,
130
130
  source: {
131
131
  provider: 'runtime',
132
+ channel_id: 'runtime',
132
133
  actor_id: agent,
133
134
  context_id: runtime,
134
135
  message_id: stageKey,
@@ -147,7 +148,9 @@ function createCognitiveTurnLifecycle({
147
148
  function commitDurableStage(stageKey, observations) {
148
149
  if (stageResults.has(stageKey)) return { ...stageResults.get(stageKey), idempotent: true };
149
150
  const eventId = digestId('durable', [tenant, agent, stageKey]);
150
- if (store.hasEvent({ eventId, tenantId: tenant, agentId: agent }, internalMemoryAccess)) {
151
+ if (store.hasEvent({
152
+ eventId, tenantId: tenant, agentId: agent, visibilityScope: 'agent:memory',
153
+ }, internalMemoryAccess)) {
151
154
  const result = { idempotent: true };
152
155
  stageResults.set(stageKey, result);
153
156
  return result;
@@ -158,7 +161,9 @@ function createCognitiveTurnLifecycle({
158
161
  ...item,
159
162
  }));
160
163
  for (let attempt = 0; attempt < 4; attempt += 1) {
161
- if (store.hasEvent({ eventId, tenantId: tenant, agentId: agent }, internalMemoryAccess)) {
164
+ if (store.hasEvent({
165
+ eventId, tenantId: tenant, agentId: agent, visibilityScope: 'agent:memory',
166
+ }, internalMemoryAccess)) {
162
167
  const result = { idempotent: true };
163
168
  stageResults.set(stageKey, result);
164
169
  return result;
@@ -173,6 +178,7 @@ function createCognitiveTurnLifecycle({
173
178
  occurredAt,
174
179
  source: {
175
180
  provider: 'runtime',
181
+ channel_id: 'runtime',
176
182
  actor_id: agent,
177
183
  context_id: 'durable-focus',
178
184
  message_id: stageKey,
@@ -278,11 +284,12 @@ function createCognitiveTurnLifecycle({
278
284
  if (!requestId || !targetObservationId || !FOCUS_DOMAINS.has(domain) || !key
279
285
  || AUTHORITY_KEY_RE.test(key) || !scope || scope === 'runtime'
280
286
  || Number.isNaN(Date.parse(occurredAt))
281
- || !exactKeys(source, new Set(['provider', 'actorId', 'contextId', 'messageId']))) {
287
+ || !exactKeys(source, new Set(['provider', 'channelId', 'actorId', 'contextId', 'messageId']))) {
282
288
  fail('COGNITIVE_REVISION_INVALID');
283
289
  }
284
290
  const normalizedSource = {
285
291
  provider: safeId(source.provider),
292
+ channel_id: safeId(source.channelId),
286
293
  actor_id: safeId(source.actorId),
287
294
  context_id: safeId(source.contextId),
288
295
  message_id: safeId(source.messageId),
@@ -309,7 +316,7 @@ function createCognitiveTurnLifecycle({
309
316
  actorId: normalizedSource.actor_id,
310
317
  agentId: agent,
311
318
  portalId: normalizedSource.provider,
312
- channelId: normalizedSource.provider,
319
+ channelId: normalizedSource.channel_id,
313
320
  conversationId: normalizedSource.context_id,
314
321
  requestedScope: scope,
315
322
  permissionContext: {
@@ -328,6 +335,7 @@ function createCognitiveTurnLifecycle({
328
335
  && existing.supersedes === (observation.supersedes ?? null)
329
336
  && existing.withdraws === (observation.withdraws ?? null)
330
337
  && existing.source.provider === normalizedSource.provider
338
+ && existing.source.channel_id === normalizedSource.channel_id
331
339
  && existing.source.actor_id === normalizedSource.actor_id
332
340
  && existing.source.context_id === normalizedSource.context_id
333
341
  && existing.source.message_id === normalizedSource.message_id;
@@ -102,7 +102,10 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
102
102
  receiptId: eventId,
103
103
  },
104
104
  };
105
- if (store.hasEvent({ eventId, tenantId: value.tenantId, agentId: value.agentId }, memoryAccess)) {
105
+ if (store.hasEvent({
106
+ eventId, tenantId: value.tenantId, agentId: value.agentId,
107
+ visibilityScope: relationshipScope(value.actorId),
108
+ }, memoryAccess)) {
106
109
  return { claimed: true, idempotent: true };
107
110
  }
108
111
  const claim = store.claimAttention({
@@ -137,6 +140,7 @@ function createPersonalityMemoryAdapter({ identityRoot, memoryAdapter } = {}) {
137
140
  occurredAt: value.occurredAt,
138
141
  source: {
139
142
  provider: value.sourcePortal,
143
+ channel_id: value.sourceChannel,
140
144
  actor_id: value.actorId,
141
145
  context_id: value.conversationId,
142
146
  message_id: value.messageId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.387",
3
+ "version": "9.1.389",
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": {