blun-king-cli 9.1.256 → 9.1.258
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const os = require('node:os');
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const { recordIdentityJournalCandidate } = require('./identity-journal-policy.cjs');
|
|
4
5
|
|
|
5
6
|
const MAX_FILE_BYTES = 64 * 1024;
|
|
6
7
|
const DEFAULT_MAX_CHARS = 1800;
|
|
@@ -206,7 +207,22 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
206
207
|
purpose: '',
|
|
207
208
|
relevant_roles: [],
|
|
208
209
|
})) created.push('group');
|
|
209
|
-
|
|
210
|
+
const journal = created.includes('relationship') && firstSeenAt
|
|
211
|
+
? recordIdentityJournalCandidate({
|
|
212
|
+
kind: 'first_interaction',
|
|
213
|
+
tenant_id: tenantId,
|
|
214
|
+
agent_id: agentId,
|
|
215
|
+
actor_id: actorId,
|
|
216
|
+
...(groupId ? { group_id: groupId } : {}),
|
|
217
|
+
occurred_at: firstSeenAt,
|
|
218
|
+
}, env)
|
|
219
|
+
: undefined;
|
|
220
|
+
return {
|
|
221
|
+
actor_id: actorId,
|
|
222
|
+
...(groupId ? { group_id: groupId } : {}),
|
|
223
|
+
created,
|
|
224
|
+
...(journal ? { journal } : {}),
|
|
225
|
+
};
|
|
210
226
|
}
|
|
211
227
|
|
|
212
228
|
function identitySystemBlock(env = process.env) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
9
|
+
const ALLOWED_KINDS = new Set(['first_interaction']);
|
|
10
|
+
|
|
11
|
+
function enabled(env) {
|
|
12
|
+
return /^(?:1|true)$/iu.test(String(env.BLUN_IDENTITY_JOURNAL ?? '').trim());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function safeId(value) {
|
|
16
|
+
const normalized = String(value ?? '').trim();
|
|
17
|
+
return SAFE_ID_RE.test(normalized) ? normalized : '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isInside(parent, child) {
|
|
21
|
+
const relative = path.relative(parent, child);
|
|
22
|
+
return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolvedRoot(env) {
|
|
26
|
+
const configuredHome = String(env.BLUN_SHARED_HOME ?? env.BLUN_HOME ?? '').trim();
|
|
27
|
+
const home = path.resolve(configuredHome || path.join(os.homedir(), '.blun'));
|
|
28
|
+
const configuredRoot = String(env.BLUN_IDENTITY_ROOT ?? '').trim();
|
|
29
|
+
const root = path.resolve(configuredRoot || path.join(home, 'identity'));
|
|
30
|
+
if (!isInside(home, root)) return '';
|
|
31
|
+
try {
|
|
32
|
+
if (fs.lstatSync(root).isSymbolicLink()) return '';
|
|
33
|
+
const realHome = fs.realpathSync(home);
|
|
34
|
+
const realRoot = fs.realpathSync(root);
|
|
35
|
+
return isInside(realHome, realRoot) ? realRoot : '';
|
|
36
|
+
} catch {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function trustedTimestamp(value) {
|
|
42
|
+
const text = String(value ?? '').trim();
|
|
43
|
+
if (!text || text.length > 64 || Number.isNaN(Date.parse(text))) return '';
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createJsonOnce(root, target, value) {
|
|
48
|
+
if (!isInside(root, target)) return false;
|
|
49
|
+
let handle;
|
|
50
|
+
try {
|
|
51
|
+
const parent = path.dirname(target);
|
|
52
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
53
|
+
if (!isInside(root, fs.realpathSync(parent))) return false;
|
|
54
|
+
handle = fs.openSync(target, 'wx', 0o600);
|
|
55
|
+
fs.writeFileSync(handle, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
56
|
+
fs.fsyncSync(handle);
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
} finally {
|
|
61
|
+
if (handle !== undefined) fs.closeSync(handle);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function recordIdentityJournalCandidate(signal, env = process.env) {
|
|
66
|
+
if (!enabled(env) || !signal || typeof signal !== 'object') return undefined;
|
|
67
|
+
const kind = String(signal.kind ?? '').trim();
|
|
68
|
+
const tenantId = safeId(signal.tenant_id);
|
|
69
|
+
const configuredTenantId = safeId(env.BLUN_IDENTITY_TENANT_ID);
|
|
70
|
+
const agentId = safeId(signal.agent_id);
|
|
71
|
+
const configuredAgentId = safeId(env.BLUN_AGENT_ID);
|
|
72
|
+
const actorId = safeId(signal.actor_id);
|
|
73
|
+
const groupId = safeId(signal.group_id);
|
|
74
|
+
const occurredAt = trustedTimestamp(signal.occurred_at);
|
|
75
|
+
if (!ALLOWED_KINDS.has(kind)
|
|
76
|
+
|| !tenantId || tenantId !== configuredTenantId
|
|
77
|
+
|| !agentId || agentId !== configuredAgentId
|
|
78
|
+
|| !actorId || !occurredAt
|
|
79
|
+
|| (signal.group_id !== undefined && !groupId)) return undefined;
|
|
80
|
+
|
|
81
|
+
const root = resolvedRoot(env);
|
|
82
|
+
if (!root) return undefined;
|
|
83
|
+
const identity = [kind, tenantId, agentId, actorId, groupId, occurredAt].join('\0');
|
|
84
|
+
const candidateId = crypto.createHash('sha256').update(identity).digest('hex').slice(0, 32);
|
|
85
|
+
const target = path.resolve(root, 'agents', agentId, 'journal', 'candidates', `${candidateId}.json`);
|
|
86
|
+
const value = {
|
|
87
|
+
version: 1,
|
|
88
|
+
candidate_id: candidateId,
|
|
89
|
+
status: 'pending',
|
|
90
|
+
kind,
|
|
91
|
+
tenant_id: tenantId,
|
|
92
|
+
agent_id: agentId,
|
|
93
|
+
actor_id: actorId,
|
|
94
|
+
...(groupId ? { group_id: groupId } : {}),
|
|
95
|
+
occurred_at: occurredAt,
|
|
96
|
+
source: 'trusted_channel_metadata',
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
candidate_id: candidateId,
|
|
100
|
+
created: createJsonOnce(root, target, value),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { recordIdentityJournalCandidate };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const IDENTITY_HEADING_RE = /(?:soul|seele|who i am|how i am|wie ich|wer ich|personality|persoenlichkeit|persönlichkeit|voice|stimme|values|werte|relationship|beziehung|relationships|odnos|ko sam|kakav sam|preferences|vorlieben|goals|ziele)/iu;
|
|
4
|
+
const PROJECTION_MARKER = '... (soul projected from the complete unchanged source file)';
|
|
5
|
+
|
|
6
|
+
function splitSoul(text) {
|
|
7
|
+
const matches = [...text.matchAll(/^##\s+.+$/gmu)];
|
|
8
|
+
if (matches.length === 0) return { preamble: text, sections: [] };
|
|
9
|
+
const preamble = text.slice(0, matches[0].index).trimEnd();
|
|
10
|
+
const sections = matches.map((match, index) => {
|
|
11
|
+
const start = match.index;
|
|
12
|
+
const end = matches[index + 1]?.index ?? text.length;
|
|
13
|
+
const raw = text.slice(start, end).trim();
|
|
14
|
+
const newline = raw.indexOf('\n');
|
|
15
|
+
return {
|
|
16
|
+
heading: newline === -1 ? raw : raw.slice(0, newline).trimEnd(),
|
|
17
|
+
body: newline === -1 ? '' : raw.slice(newline + 1).trim(),
|
|
18
|
+
identity: IDENTITY_HEADING_RE.test(match[0]),
|
|
19
|
+
index,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
return { preamble, sections };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function allocateBodies(sections, budget) {
|
|
26
|
+
if (budget <= 0) return sections.map(() => '');
|
|
27
|
+
const weights = sections.map((section) => section.identity ? 3 : 1);
|
|
28
|
+
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
29
|
+
let remaining = budget;
|
|
30
|
+
return sections.map((section, index) => {
|
|
31
|
+
const remainingWeight = weights.slice(index).reduce((sum, weight) => sum + weight, 0);
|
|
32
|
+
const fairShare = index === sections.length - 1
|
|
33
|
+
? remaining
|
|
34
|
+
: Math.floor(remaining * weights[index] / remainingWeight);
|
|
35
|
+
const take = Math.min(section.body.length, Math.max(0, fairShare));
|
|
36
|
+
remaining -= take;
|
|
37
|
+
return section.body.slice(0, take).trimEnd();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function projectSoulText(value, maxChars = 4000) {
|
|
42
|
+
const text = typeof value === 'string' ? value : '';
|
|
43
|
+
const limit = Number.isFinite(maxChars) ? Math.max(0, Math.floor(maxChars)) : 4000;
|
|
44
|
+
if (text.length <= limit) return text;
|
|
45
|
+
if (limit === 0) return '';
|
|
46
|
+
|
|
47
|
+
const { preamble, sections } = splitSoul(text);
|
|
48
|
+
if (sections.length === 0) return `${text.slice(0, Math.max(0, limit - 1)).trimEnd()}…`.slice(0, limit);
|
|
49
|
+
|
|
50
|
+
const ordered = [
|
|
51
|
+
...sections.filter((section) => section.identity),
|
|
52
|
+
...sections.filter((section) => !section.identity),
|
|
53
|
+
];
|
|
54
|
+
const separatorCost = Math.max(0, ordered.length) * 2;
|
|
55
|
+
const headingCost = ordered.reduce((sum, section) => sum + section.heading.length + 1, 0);
|
|
56
|
+
const markerCost = PROJECTION_MARKER.length + 2;
|
|
57
|
+
const preambleLimit = Math.min(preamble.length, Math.max(160, Math.floor(limit * 0.2)));
|
|
58
|
+
const fixedWithoutPreamble = separatorCost + headingCost + markerCost;
|
|
59
|
+
const preambleBudget = Math.max(0, Math.min(preambleLimit, limit - fixedWithoutPreamble));
|
|
60
|
+
const projectedPreamble = preamble.slice(0, preambleBudget).trimEnd();
|
|
61
|
+
const bodyBudget = Math.max(0, limit - fixedWithoutPreamble - projectedPreamble.length);
|
|
62
|
+
const bodies = allocateBodies(ordered, bodyBudget);
|
|
63
|
+
const parts = [];
|
|
64
|
+
if (projectedPreamble) parts.push(projectedPreamble);
|
|
65
|
+
for (let index = 0; index < ordered.length; index += 1) {
|
|
66
|
+
parts.push(bodies[index] ? `${ordered[index].heading}\n${bodies[index]}` : ordered[index].heading);
|
|
67
|
+
}
|
|
68
|
+
parts.push(PROJECTION_MARKER);
|
|
69
|
+
return parts.join('\n\n').slice(0, limit).trimEnd();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
projectSoulText,
|
|
74
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -21440,6 +21440,7 @@ var init_list_directory = __esmMin((() => {
|
|
|
21440
21440
|
var { boundSystemPromptDirectoryListing, compactRepeatedConductSections, fingerprintSystemPromptContext, refreshedSystemPromptContext, resolveStableSystemPromptTimestamp } = createRequire(import.meta.url)("./bin/system-prompt-context-policy.cjs");
|
|
21441
21441
|
var { identitySystemBlock, recordChannelIdentity } = createRequire(import.meta.url)("./bin/identity-context-policy.cjs");
|
|
21442
21442
|
var { naturalPresenceSystemBlock } = createRequire(import.meta.url)("./bin/natural-presence-policy.cjs");
|
|
21443
|
+
var { projectSoulText } = createRequire(import.meta.url)("./bin/soul-organization-policy.cjs");
|
|
21443
21444
|
var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
|
|
21444
21445
|
async function prepareSystemPromptContext(kaos, brandHome, options) {
|
|
21445
21446
|
const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
|
|
@@ -28275,7 +28276,7 @@ function soulSystemBlock(env = process.env) {
|
|
|
28275
28276
|
try {
|
|
28276
28277
|
if (existsSync(path)) {
|
|
28277
28278
|
soul = readFileSync(path, "utf8").trim();
|
|
28278
|
-
|
|
28279
|
+
soul = projectSoulText(soul, SOUL_MAX_CHARS);
|
|
28279
28280
|
}
|
|
28280
28281
|
} catch {
|
|
28281
28282
|
soul = "";
|