blun-king-cli 9.1.390 → 9.1.392

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,14 +2,25 @@
2
2
 
3
3
  const { openCognitiveStateStore } = require('./cognitive-state-store.cjs');
4
4
 
5
- const COGNITIVE_MEMORY_ADAPTER_VERSION = 5;
5
+ const COGNITIVE_MEMORY_ADAPTER_VERSION = 6;
6
6
  const KIND_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
7
7
  const RETENTION_CLASSES = new Set(['operational', 'durable', 'relationship', 'audit']);
8
- const ACCESS_KEYS = Object.freeze([
8
+ const ACCESS_REQUIRED_KEYS = Object.freeze([
9
9
  'actorId', 'agentId', 'channelId', 'conversationId', 'permissionContext',
10
10
  'portalId', 'requestedScope', 'tenantId', 'userId',
11
11
  ]);
12
- const PERMISSION_KEYS = Object.freeze(['authority', 'decision', 'receiptId']);
12
+ const ACCESS_OPTIONAL_KEYS = Object.freeze(['identityLink']);
13
+ const PERMISSION_REQUIRED_KEYS = Object.freeze(['authority', 'decision', 'receiptId']);
14
+ const PERMISSION_OPTIONAL_KEYS = Object.freeze(['identityReceiptId']);
15
+ const IDENTITY_LINK_KEYS = Object.freeze([
16
+ 'authority', 'canonicalUserId', 'confirmedAt', 'method', 'receiptId',
17
+ 'sourcePortal', 'sourceSubjectId', 'status', 'tenantId', 'version',
18
+ ]);
19
+ const IDENTITY_LINK_AUTHORITIES = new Map([
20
+ ['verified_oauth', 'oauth_verifier'],
21
+ ['secure_account_link', 'account_link_service'],
22
+ ['explicit_confirmed', 'identity_confirmation_service'],
23
+ ]);
13
24
  const REQUIRED_METHODS = Object.freeze([
14
25
  'commit',
15
26
  'read',
@@ -31,13 +42,70 @@ function exactKeys(value, expected) {
31
42
  && Object.keys(value).sort().join('\0') === [...expected].sort().join('\0');
32
43
  }
33
44
 
45
+ function requiredAndOptionalKeys(value, required, optional) {
46
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
47
+ const allowed = new Set([...required, ...optional]);
48
+ return required.every((key) => Object.prototype.hasOwnProperty.call(value, key))
49
+ && Object.keys(value).every((key) => allowed.has(key));
50
+ }
51
+
34
52
  function safeId(value) {
35
53
  const text = String(value ?? '').trim();
36
54
  return KIND_RE.test(text) ? text : '';
37
55
  }
38
56
 
57
+ function normalizeIdentityLink(access, normalized) {
58
+ const link = access.identityLink;
59
+ const identityReceiptId = normalized.permissionContext.identityReceiptId;
60
+ if (normalized.userId === normalized.actorId) {
61
+ if (link !== undefined || identityReceiptId !== undefined) fail('COGNITIVE_IDENTITY_LINK_INVALID');
62
+ return undefined;
63
+ }
64
+ if (link === undefined || link === null) fail('COGNITIVE_IDENTITY_LINK_REQUIRED');
65
+ if (!exactKeys(link, IDENTITY_LINK_KEYS)) fail('COGNITIVE_IDENTITY_LINK_INVALID');
66
+ if (String(link.status ?? '') === 'revoked') fail('COGNITIVE_IDENTITY_LINK_REVOKED');
67
+ const method = String(link.method ?? '');
68
+ const normalizedLink = {
69
+ version: Number(link.version),
70
+ tenantId: safeId(link.tenantId),
71
+ canonicalUserId: safeId(link.canonicalUserId),
72
+ sourcePortal: safeId(link.sourcePortal),
73
+ sourceSubjectId: safeId(link.sourceSubjectId),
74
+ method,
75
+ status: String(link.status ?? ''),
76
+ confirmedAt: String(link.confirmedAt ?? '').trim(),
77
+ authority: safeId(link.authority),
78
+ receiptId: safeId(link.receiptId),
79
+ };
80
+ if (normalizedLink.version !== 1 || normalizedLink.status !== 'active'
81
+ || Number.isNaN(Date.parse(normalizedLink.confirmedAt))
82
+ || IDENTITY_LINK_AUTHORITIES.get(method) !== normalizedLink.authority
83
+ || normalizedLink.tenantId !== normalized.tenantId
84
+ || normalizedLink.canonicalUserId !== normalized.userId
85
+ || normalizedLink.sourcePortal !== normalized.portalId
86
+ || normalizedLink.sourceSubjectId !== normalized.actorId
87
+ || !identityReceiptId || normalizedLink.receiptId !== identityReceiptId) {
88
+ fail('COGNITIVE_IDENTITY_LINK_INVALID');
89
+ }
90
+ const canonicalScope = `relationship:${normalized.userId}:private`;
91
+ const personalScope = `user:${normalized.userId}:private`;
92
+ if ((normalized.requestedScope.startsWith('relationship:') && normalized.requestedScope !== canonicalScope)
93
+ || (normalized.requestedScope.startsWith('user:') && normalized.requestedScope !== personalScope)) {
94
+ fail('COGNITIVE_IDENTITY_SCOPE_MISMATCH');
95
+ }
96
+ return Object.freeze({
97
+ ...normalizedLink,
98
+ confirmedAt: new Date(normalizedLink.confirmedAt).toISOString(),
99
+ });
100
+ }
101
+
39
102
  function normalizeMemoryAccess(access, request) {
40
- if (!exactKeys(access, ACCESS_KEYS) || !exactKeys(access.permissionContext, PERMISSION_KEYS)) {
103
+ if (!requiredAndOptionalKeys(access, ACCESS_REQUIRED_KEYS, ACCESS_OPTIONAL_KEYS)
104
+ || !requiredAndOptionalKeys(
105
+ access.permissionContext,
106
+ PERMISSION_REQUIRED_KEYS,
107
+ PERMISSION_OPTIONAL_KEYS,
108
+ )) {
41
109
  fail('COGNITIVE_MEMORY_ACCESS_INVALID');
42
110
  }
43
111
  const normalized = {
@@ -53,6 +121,9 @@ function normalizeMemoryAccess(access, request) {
53
121
  authority: safeId(access.permissionContext.authority),
54
122
  decision: String(access.permissionContext.decision ?? ''),
55
123
  receiptId: safeId(access.permissionContext.receiptId),
124
+ ...(Object.prototype.hasOwnProperty.call(access.permissionContext, 'identityReceiptId')
125
+ ? { identityReceiptId: safeId(access.permissionContext.identityReceiptId) }
126
+ : {}),
56
127
  },
57
128
  };
58
129
  if (!normalized.tenantId || !normalized.userId || !normalized.actorId || !normalized.agentId
@@ -66,6 +137,8 @@ function normalizeMemoryAccess(access, request) {
66
137
  || requestTenant !== normalized.tenantId || requestAgent !== normalized.agentId) {
67
138
  fail('COGNITIVE_MEMORY_ACCESS_SCOPE_MISMATCH');
68
139
  }
140
+ const identityLink = normalizeIdentityLink(access, normalized);
141
+ if (identityLink) normalized.identityLink = identityLink;
69
142
  normalized.permissionContext = Object.freeze(normalized.permissionContext);
70
143
  return Object.freeze(normalized);
71
144
  }
@@ -473,7 +473,7 @@ function createCognitiveTurnLifecycle({
473
473
  const authorization = input?.authorization;
474
474
  const memoryAccess = {
475
475
  tenantId: tenant,
476
- userId: String(candidate?.subject_id ?? ''),
476
+ userId: agent,
477
477
  actorId: agent,
478
478
  agentId: agent,
479
479
  portalId: 'cli',
@@ -6,6 +6,10 @@ const TRAILING_WORK_PERMISSION_QUESTION = /(?:^|\r?\n\s*\r?\n)(?:kann|darf|soll)
6
6
  const INTERNAL_CONTROL_MARKER = /\b(?:cron|loop|checkpoint|handoff|resume|session|sha|tool|werkzeug|hook|queue|lane|inbound|outbound|arbeitsstand|arbeitsauftrag|bau freigabe|freigabe dieser lane|turn|zug|status|private nachricht|telegram nachricht|reply|verlauf|basis|qa gate|wartezustand|f\d+|m\d+|w\d+)\b/gu;
7
7
  const NO_USER_VALUE = /\b(?:keine neue aktion|keine neue information|keine neue nachricht|kein neuer auftrag|kein neuer zweck|kein neuer inbound bedarf|kein offener handlungsbedarf|nichts neues zu melden|nichts zu melden|nichts weiteres|bleibt pausiert|warte auf (?:dein|sein|ihr|papas) (?:zeichen|go|freigabe|antwort)|ich warte|ich sende nichts|wiederholen wäre spam|keine weitere nachricht|bereits geantwortet|bereits gesendet|kein werkzeugaufruf nötig|stand unverändert|zug läuft seit|kein bau ohne|qa gate steht noch aus|wartezustand)\b/u;
8
8
 
9
+ const MEDIA_PROGRESS_MARKER = /\b(?:bildgenerierung|bildjob|generateimage|getmedia|media job|medienjob)\b/u;
10
+ const MEDIA_PENDING_STATE = /\b(?:auftrag wurde angenommen|job wurde angenommen|job angenommen|aufruf wurde abgesetzt|angestossen|angestoßen|processing|verarbeitung)\b/u;
11
+ const MEDIA_NO_RESULT = /\b(?:kein neues bild|noch kein bild|kein bild liefern|nichts angekommen|nichts neues gelandet|nicht verfuegbar|nicht verfügbar|getmedia fehlt)\b/u;
12
+
9
13
  function normalize(text) {
10
14
  return String(text ?? '')
11
15
  .normalize('NFKC')
@@ -27,6 +31,7 @@ function isPrivateInternalStatusReply(chatId, text) {
27
31
  const value = normalize(sanitized.length === 0 ? text : sanitized);
28
32
  if (value.length === 0) return false;
29
33
  if (sanitized.length === 0 && WORK_PERMISSION_QUESTION.test(value)) return true;
34
+ if (MEDIA_PROGRESS_MARKER.test(value) && MEDIA_PENDING_STATE.test(value) && MEDIA_NO_RESULT.test(value)) return true;
30
35
 
31
36
  const internalMarkers = value.match(INTERNAL_CONTROL_MARKER) ?? [];
32
37
  return internalMarkers.length > 0 && NO_USER_VALUE.test(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.390",
3
+ "version": "9.1.392",
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": {
@@ -6,6 +6,10 @@ const TRAILING_WORK_PERMISSION_QUESTION = /(?:^|\r?\n\s*\r?\n)(?:kann|darf|soll)
6
6
  const INTERNAL_CONTROL_MARKER = /\b(?:cron|loop|checkpoint|handoff|resume|session|sha|tool|werkzeug|hook|queue|lane|inbound|outbound|arbeitsstand|arbeitsauftrag|bau freigabe|freigabe dieser lane|turn|zug|status|private nachricht|telegram nachricht|reply|verlauf|basis|qa gate|wartezustand|f\d+|m\d+|w\d+)\b/gu;
7
7
  const NO_USER_VALUE = /\b(?:keine neue aktion|keine neue information|keine neue nachricht|kein neuer auftrag|kein neuer zweck|kein neuer inbound bedarf|kein offener handlungsbedarf|nichts neues zu melden|nichts zu melden|nichts weiteres|bleibt pausiert|warte auf (?:dein|sein|ihr|papas) (?:zeichen|go|freigabe|antwort)|ich warte|ich sende nichts|wiederholen wäre spam|keine weitere nachricht|bereits geantwortet|bereits gesendet|kein werkzeugaufruf nötig|stand unverändert|zug läuft seit|kein bau ohne|qa gate steht noch aus|wartezustand)\b/u;
8
8
 
9
+ const MEDIA_PROGRESS_MARKER = /\b(?:bildgenerierung|bildjob|generateimage|getmedia|media job|medienjob)\b/u;
10
+ const MEDIA_PENDING_STATE = /\b(?:auftrag wurde angenommen|job wurde angenommen|job angenommen|aufruf wurde abgesetzt|angestossen|angestoßen|processing|verarbeitung)\b/u;
11
+ const MEDIA_NO_RESULT = /\b(?:kein neues bild|noch kein bild|kein bild liefern|nichts angekommen|nichts neues gelandet|nicht verfuegbar|nicht verfügbar|getmedia fehlt)\b/u;
12
+
9
13
  function normalize(text) {
10
14
  return String(text ?? '')
11
15
  .normalize('NFKC')
@@ -27,6 +31,7 @@ function isPrivateInternalStatusReply(chatId, text) {
27
31
  const value = normalize(sanitized.length === 0 ? text : sanitized);
28
32
  if (value.length === 0) return false;
29
33
  if (sanitized.length === 0 && WORK_PERMISSION_QUESTION.test(value)) return true;
34
+ if (MEDIA_PROGRESS_MARKER.test(value) && MEDIA_PENDING_STATE.test(value) && MEDIA_NO_RESULT.test(value)) return true;
30
35
 
31
36
  const internalMarkers = value.match(INTERNAL_CONTROL_MARKER) ?? [];
32
37
  return internalMarkers.length > 0 && NO_USER_VALUE.test(value);