blun-king-cli 9.1.328 → 9.1.329
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.
- package/bin/identity-context-policy.cjs +2 -2
- package/bin/personality-choice-policy.cjs +2 -1
- package/bin/personality-mode.cjs +16 -12
- package/bin/personality-setup-policy.cjs +20 -9
- package/bin/profile-identity-resolution.cjs +33 -1
- package/bin/relationship-learning-policy.cjs +4 -1
- package/package.json +1 -1
|
@@ -455,7 +455,7 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
455
455
|
const manifestTenantId = safeId(manifest?.tenant_id);
|
|
456
456
|
const tenantId = configuredTenantId || manifestTenantId;
|
|
457
457
|
const configuredAgentId = safeId(env.BLUN_AGENT_ID);
|
|
458
|
-
const agentId = configuredAgentId || safeId(
|
|
458
|
+
const agentId = configuredAgentId || safeId(setup.agent_id);
|
|
459
459
|
if (!tenantId || tenantId !== manifestTenantId || !agentId || manifest?.version !== 1) return undefined;
|
|
460
460
|
|
|
461
461
|
const actorId = `telegram-${subjectId}`;
|
|
@@ -575,7 +575,7 @@ function identitySystemBlock(env = process.env) {
|
|
|
575
575
|
|
|
576
576
|
const configuredAgentId = safeId(env.BLUN_AGENT_ID);
|
|
577
577
|
if (env.BLUN_AGENT_ID && !configuredAgentId) return '';
|
|
578
|
-
const agentId = configuredAgentId || safeId(
|
|
578
|
+
const agentId = configuredAgentId || safeId(setup.agent_id);
|
|
579
579
|
if (!agentId) return '';
|
|
580
580
|
const actorId = safeId(env.BLUN_IDENTITY_ACTOR_ID);
|
|
581
581
|
const groupId = safeId(env.BLUN_IDENTITY_GROUP_ID);
|
|
@@ -6,6 +6,7 @@ const os = require('node:os');
|
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
|
|
8
8
|
const { ensurePersonalityWorkspace } = require('./personality-setup-policy.cjs');
|
|
9
|
+
const { readProfilePersona } = require('./profile-identity-resolution.cjs');
|
|
9
10
|
|
|
10
11
|
const MAX_PERSONA_BYTES = 64 * 1024;
|
|
11
12
|
|
|
@@ -14,7 +15,7 @@ function fail(code, detail = '') {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function resolvedHome(env) {
|
|
17
|
-
const configured = String(env.
|
|
18
|
+
const configured = String(env.BLUN_HOME ?? env.BLUN_SHARED_HOME ?? '').trim();
|
|
18
19
|
return path.resolve(configured || path.join(os.homedir(), '.blun'));
|
|
19
20
|
}
|
|
20
21
|
|
package/bin/personality-mode.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
|
+
const { identityFileCandidates } = require('./profile-identity-resolution.cjs');
|
|
6
7
|
|
|
7
8
|
const MAX_PERSONA_BYTES = 64 * 1024;
|
|
8
9
|
const INVALID_PERSONALITY_CHOICE = Symbol('invalid-personality-choice');
|
|
@@ -13,19 +14,22 @@ function resolvedHome(env) {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function persistedPersonalityChoice(env) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const candidates = identityFileCandidates('persona.json', env);
|
|
18
|
+
for (const personaPath of candidates) {
|
|
19
|
+
try {
|
|
20
|
+
const stat = fs.lstatSync(personaPath);
|
|
21
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PERSONA_BYTES) return INVALID_PERSONALITY_CHOICE;
|
|
22
|
+
const persona = JSON.parse(fs.readFileSync(personaPath, 'utf8'));
|
|
23
|
+
if (!persona || typeof persona !== 'object' || Array.isArray(persona)) return INVALID_PERSONALITY_CHOICE;
|
|
24
|
+
if (!Object.prototype.hasOwnProperty.call(persona, 'personalityEnabled')) return undefined;
|
|
25
|
+
return typeof persona.personalityEnabled === 'boolean'
|
|
26
|
+
? persona.personalityEnabled
|
|
27
|
+
: INVALID_PERSONALITY_CHOICE;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error?.code !== 'ENOENT') return INVALID_PERSONALITY_CHOICE;
|
|
30
|
+
}
|
|
28
31
|
}
|
|
32
|
+
return undefined;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
function personalityContextEnabled(env = process.env) {
|
|
@@ -96,20 +96,27 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
96
96
|
const realRoot = fs.realpathSync(root);
|
|
97
97
|
if (!isInside(realHome, realRoot)) return { ready: false, reason: 'identity_root_outside_home' };
|
|
98
98
|
|
|
99
|
-
const
|
|
99
|
+
const personaResult = readProfilePersona(env);
|
|
100
|
+
const persona = personaResult.persona || {};
|
|
100
101
|
const displayName = String(persona.name ?? env.BLUN_AGENT_ID ?? 'King').trim().slice(0, 120) || 'King';
|
|
101
|
-
const
|
|
102
|
-
const
|
|
102
|
+
const configuredAgentId = safeId(env.BLUN_AGENT_ID);
|
|
103
|
+
const configuredTenantId = safeId(env.BLUN_IDENTITY_TENANT_ID);
|
|
104
|
+
const initialAgentId = configuredAgentId || (personaResult.persona ? agentIdFromName(displayName) : 'king');
|
|
105
|
+
const initialTenantId = configuredTenantId || 'local';
|
|
103
106
|
const manifestPath = path.join(root, 'manifest.json');
|
|
104
107
|
createJsonOnce(root, manifestPath, {
|
|
105
108
|
version: 1,
|
|
106
|
-
tenant_id:
|
|
107
|
-
active_agent_id:
|
|
109
|
+
tenant_id: initialTenantId,
|
|
110
|
+
active_agent_id: initialAgentId,
|
|
108
111
|
});
|
|
109
112
|
const manifest = readJsonFile(manifestPath);
|
|
110
113
|
const tenantId = safeId(manifest?.tenant_id);
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
if (manifest?.version !== 1 || !tenantId || (configuredTenantId && tenantId !== configuredTenantId)) {
|
|
115
|
+
return { ready: false, reason: 'identity_manifest_invalid' };
|
|
116
|
+
}
|
|
117
|
+
const agentId = configuredAgentId
|
|
118
|
+
|| (personaResult.persona ? agentIdFromName(displayName) : safeId(manifest.active_agent_id))
|
|
119
|
+
|| 'king';
|
|
113
120
|
|
|
114
121
|
const profilePath = path.join(root, 'agents', agentId, 'profile.json');
|
|
115
122
|
createJsonOnce(root, profilePath, {
|
|
@@ -125,10 +132,14 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
125
132
|
path.join(root, 'agents', agentId, 'JOURNAL.md'),
|
|
126
133
|
'# Journal\n\nLong-term development notes, shared milestones, and confirmed lessons belong here. Current tasks, secrets, permissions, and incident logs do not.\n',
|
|
127
134
|
);
|
|
128
|
-
|
|
135
|
+
const resolvedSoul = resolveSoulFile(env);
|
|
136
|
+
const profileSoulPath = path.join(profileHome, 'SOUL.md');
|
|
137
|
+
if (resolvedSoul.text && path.resolve(resolvedSoul.path) !== path.resolve(profileSoulPath)) {
|
|
138
|
+
createTextOnce(profileHome, profileSoulPath, `${resolvedSoul.text}\n`);
|
|
139
|
+
} else if (!resolvedSoul.text) {
|
|
129
140
|
createTextOnce(
|
|
130
141
|
profileHome,
|
|
131
|
-
|
|
142
|
+
profileSoulPath,
|
|
132
143
|
`# ${displayName}\n\nIch bin ${displayName}, der persoenliche BLUN-Agent dieses Profils. Meine eigene Stimme, Vorlieben und gewachsene gemeinsame Geschichte werden hier behutsam weiterentwickelt. Bestaetigte Beziehungen gehoeren in den Beziehungsgraphen; Auftraege, Rechte, Secrets und Incident-Logs gehoeren nicht in meine Seele.\n`,
|
|
133
144
|
);
|
|
134
145
|
}
|
|
@@ -62,6 +62,21 @@ function readProfilePersona(env = process.env, options = {}) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function legacySoulNames(env, persona) {
|
|
66
|
+
const names = [];
|
|
67
|
+
for (const value of [env.BLUN_AGENT_ID, persona?.name, env.BLUN_PROFILE]) {
|
|
68
|
+
const slug = String(value || '')
|
|
69
|
+
.normalize('NFKD')
|
|
70
|
+
.replace(/[\u0300-\u036f]/gu, '')
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[^a-z0-9._-]+/gu, '-')
|
|
73
|
+
.replace(/^[^a-z0-9]+|[^a-z0-9]+$/gu, '')
|
|
74
|
+
.slice(0, 64);
|
|
75
|
+
if (slug && slug !== 'default' && !names.includes(`_${slug}_soul.md`)) names.push(`_${slug}_soul.md`);
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
|
|
65
80
|
function resolveSoulFile(env = process.env, options = {}) {
|
|
66
81
|
const fsImpl = options.fsImpl || fs;
|
|
67
82
|
const explicit = String(env.BLUN_SOUL_PATH || '').trim();
|
|
@@ -69,7 +84,24 @@ function resolveSoulFile(env = process.env, options = {}) {
|
|
|
69
84
|
const target = path.resolve(explicit);
|
|
70
85
|
return { path: target, text: readSafeText(target, fsImpl), explicit: true };
|
|
71
86
|
}
|
|
72
|
-
const
|
|
87
|
+
const homeDir = options.homeDir || os.homedir();
|
|
88
|
+
const profileHome = String(env.BLUN_HOME || '').trim();
|
|
89
|
+
const sharedHome = String(env.BLUN_SHARED_HOME || '').trim();
|
|
90
|
+
const profileName = String(env.BLUN_PROFILE || '').trim().toLowerCase();
|
|
91
|
+
const isNamedProfile = profileName && profileName !== 'default';
|
|
92
|
+
const persona = readProfilePersona(env, options).persona;
|
|
93
|
+
const legacyRoots = uniquePaths([
|
|
94
|
+
sharedHome,
|
|
95
|
+
homeDir,
|
|
96
|
+
profileHome && path.dirname(profileHome),
|
|
97
|
+
]);
|
|
98
|
+
const legacyCandidates = legacySoulNames(env, persona)
|
|
99
|
+
.flatMap((filename) => legacyRoots.map((root) => path.join(root, filename)));
|
|
100
|
+
const candidates = uniquePaths([
|
|
101
|
+
profileHome && path.join(profileHome, 'SOUL.md'),
|
|
102
|
+
...legacyCandidates,
|
|
103
|
+
...(!isNamedProfile ? identityFileCandidates('SOUL.md', env, homeDir).slice(1) : []),
|
|
104
|
+
]);
|
|
73
105
|
for (const target of candidates) {
|
|
74
106
|
const text = readSafeText(target, fsImpl);
|
|
75
107
|
if (text) return { path: target, text, explicit: false };
|
|
@@ -101,7 +101,10 @@ function recordExplicitRelationshipCandidate(input = {}) {
|
|
|
101
101
|
if (!fs.lstatSync(root).isDirectory() || fs.lstatSync(root).isSymbolicLink()) return undefined;
|
|
102
102
|
const realRoot = fs.realpathSync(root);
|
|
103
103
|
const manifest = readManifest(realRoot);
|
|
104
|
-
|
|
104
|
+
const agentRoot = path.join(realRoot, 'agents', agentId);
|
|
105
|
+
if (manifest?.version !== 1 || manifest.tenant_id !== tenantId) return undefined;
|
|
106
|
+
const agentStat = fs.lstatSync(agentRoot);
|
|
107
|
+
if (!agentStat.isDirectory() || agentStat.isSymbolicLink()) return undefined;
|
|
105
108
|
// The timestamp keeps later confirmations or contradictions as separate history,
|
|
106
109
|
// while an identical channel event remains idempotent on retry.
|
|
107
110
|
const identity = [tenantId, agentId, actorId, occurredAt, explicit.kind, explicit.value].join('\0');
|