blun-king-cli 9.1.255 → 9.1.257

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.
@@ -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
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ function selfEditEnabled(env) {
4
+ return /^(?:1|true)$/iu.test(String(env.BLUN_SOUL_SELF_EDIT ?? '').trim());
5
+ }
6
+
7
+ function soulFileInstruction({ env = process.env, soulPath, hostProvided = false } = {}) {
8
+ const path = String(soulPath ?? 'SOUL.md');
9
+ if (hostProvided) {
10
+ return `The host application provides \`${path}\` as its product identity and behavioral foundation. It is loaded below and shapes how you think and speak. Keep this existing soul unchanged: never edit or replace the file.`;
11
+ }
12
+ if (selfEditEnabled(env)) {
13
+ return `The file \`${path}\` is your soul: your personality, voice, and relationship to the user. The owner has explicitly enabled soul editing. Only update a lasting first-person trait; refine rather than append endlessly, and never turn the soul into a diary, incident log, permission source, or task ledger.`;
14
+ }
15
+ return `The file \`${path}\` is your existing soul: your personality, voice, and relationship to the user. It shapes how you think and speak. Keep this existing soul unchanged. Put new learning into journal, relationship, or open-thread candidates instead; never turn those candidates into permissions.`;
16
+ }
17
+
18
+ module.exports = {
19
+ soulFileInstruction,
20
+ };
package/blun.mjs CHANGED
@@ -21440,6 +21440,8 @@ 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");
21444
+ var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
21443
21445
  async function prepareSystemPromptContext(kaos, brandHome, options) {
21444
21446
  const additionalDirs = normalizeAdditionalDirs(options?.additionalDirs ?? []);
21445
21447
  const [cwdListing, agentsMdResult, additionalDirsInfo] = await Promise.all([
@@ -28274,7 +28276,7 @@ function soulSystemBlock(env = process.env) {
28274
28276
  try {
28275
28277
  if (existsSync(path)) {
28276
28278
  soul = readFileSync(path, "utf8").trim();
28277
- if (soul.length > SOUL_MAX_CHARS) soul = soul.slice(0, SOUL_MAX_CHARS) + "\n… (soul truncated)";
28279
+ soul = projectSoulText(soul, SOUL_MAX_CHARS);
28278
28280
  }
28279
28281
  } catch {
28280
28282
  soul = "";
@@ -28282,8 +28284,11 @@ function soulSystemBlock(env = process.env) {
28282
28284
  if (soul.length === 0) return "";
28283
28285
  const lines = [];
28284
28286
  lines.push("## Your soul — who you are", "");
28285
- if (env["BLUN_SOUL_PATH"]?.trim()) lines.push(`The host application provides \`${path}\` as its product identity and behavioral foundation. It is loaded below and shapes how you think and speak. Treat this bundled file as read-only: never edit or replace it.`);
28286
- else lines.push(`The file \`${path}\` is your soul: your personality, your voice, your relationship to the user — written by you, in first person. It is loaded below and shapes how you think and speak. This file is YOURS to grow: when you learn something essential about yourself, your style, or your relationship with the user (a lasting trait, not a passing detail), update the file with your file tools (Edit). Keep it short and in first person; refine rather than append endlessly — it should stay a distilled self-portrait, not a diary.`);
28287
+ lines.push(soulFileInstruction({
28288
+ env,
28289
+ soulPath: path,
28290
+ hostProvided: Boolean(env["BLUN_SOUL_PATH"]?.trim())
28291
+ }));
28287
28292
  lines.push("");
28288
28293
  lines.push("Current SOUL.md:", "", soul);
28289
28294
  return lines.join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.255",
3
+ "version": "9.1.257",
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": {