blun-king-cli 9.1.258 → 9.1.260

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,6 +2,7 @@ const fs = require('node:fs');
2
2
  const os = require('node:os');
3
3
  const path = require('node:path');
4
4
  const { recordIdentityJournalCandidate } = require('./identity-journal-policy.cjs');
5
+ const { personalityContextEnabled } = require('./personality-mode.cjs');
5
6
 
6
7
  const MAX_FILE_BYTES = 64 * 1024;
7
8
  const DEFAULT_MAX_CHARS = 1800;
@@ -11,10 +12,6 @@ const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
11
12
  const TELEGRAM_SUBJECT_RE = /^\d{1,32}$/u;
12
13
  const TELEGRAM_CHAT_RE = /^-?\d{1,32}$/u;
13
14
 
14
- function enabled(env) {
15
- return /^(?:1|true)$/iu.test(String(env.BLUN_IDENTITY_CONTEXT ?? '').trim());
16
- }
17
-
18
15
  function autoGraphEnabled(env) {
19
16
  return /^(?:1|true)$/iu.test(String(env.BLUN_IDENTITY_AUTO_GRAPH ?? '').trim());
20
17
  }
@@ -158,7 +155,7 @@ function trustedTimestamp(value) {
158
155
  }
159
156
 
160
157
  function recordChannelIdentity(envelope, env = process.env) {
161
- if (!autoGraphEnabled(env) || envelope?.source !== 'telegram') return undefined;
158
+ if (!personalityContextEnabled(env) || !autoGraphEnabled(env) || envelope?.source !== 'telegram') return undefined;
162
159
  const meta = envelope?.meta;
163
160
  if (!meta || typeof meta !== 'object') return undefined;
164
161
  const subjectId = String(meta.user_id ?? '').trim();
@@ -226,7 +223,7 @@ function recordChannelIdentity(envelope, env = process.env) {
226
223
  }
227
224
 
228
225
  function identitySystemBlock(env = process.env) {
229
- if (!enabled(env)) return '';
226
+ if (!personalityContextEnabled(env)) return '';
230
227
  const root = resolveRoot(env);
231
228
  if (!root) return '';
232
229
  const manifest = readJson(root, ['manifest.json']);
@@ -1,11 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ const { personalityContextEnabled } = require('./personality-mode.cjs');
4
+
3
5
  const NATURAL_PRESENCE_BLOCK = `## Natural presence
4
6
 
5
7
  Keep the loaded soul intact and let it shape the voice. Speak like an ongoing teammate, using only real shared context; never invent history or feelings. During active work, the current task and evidence come first. Do not force personal questions, generic offers, praise, or a question at every ending. Silence and a clean ending are valid.`;
6
8
 
7
- function naturalPresenceSystemBlock() {
8
- return NATURAL_PRESENCE_BLOCK;
9
+ const PERSONALITY_CURIOSITY_BLOCK = `When personality context is enabled and the conversation is relaxed rather than task-focused, show genuine curiosity: if it fits, ask one natural, optional personal question about their partner, family, work, home, interests, or what matters to them now. Never turn the conversation into an interview or stack questions. Do not repeat an unanswered question or infer an answer. During active work, do not interrupt the task; wait for a natural opening.`;
10
+
11
+ function naturalPresenceSystemBlock(env = process.env) {
12
+ if (!personalityContextEnabled(env)) return NATURAL_PRESENCE_BLOCK;
13
+ return `${NATURAL_PRESENCE_BLOCK}\n\n${PERSONALITY_CURIOSITY_BLOCK}`;
9
14
  }
10
15
 
11
16
  module.exports = {
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const MAX_PERSONA_BYTES = 64 * 1024;
8
+
9
+ function resolvedHome(env) {
10
+ const configured = String(env.BLUN_SHARED_HOME ?? env.BLUN_HOME ?? '').trim();
11
+ return path.resolve(configured || path.join(os.homedir(), '.blun'));
12
+ }
13
+
14
+ function persistedPersonalityChoice(env) {
15
+ const personaPath = path.join(resolvedHome(env), 'persona.json');
16
+ try {
17
+ const stat = fs.lstatSync(personaPath);
18
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PERSONA_BYTES) return undefined;
19
+ const persona = JSON.parse(fs.readFileSync(personaPath, 'utf8'));
20
+ return typeof persona?.personalityEnabled === 'boolean' ? persona.personalityEnabled : undefined;
21
+ } catch {
22
+ return undefined;
23
+ }
24
+ }
25
+
26
+ function personalityContextEnabled(env = process.env) {
27
+ const persisted = persistedPersonalityChoice(env);
28
+ if (persisted !== undefined) return persisted;
29
+ return /^(?:1|true)$/iu.test(String(env.BLUN_IDENTITY_CONTEXT ?? '').trim());
30
+ }
31
+
32
+ module.exports = {
33
+ personalityContextEnabled,
34
+ };
35
+
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 { personalityContextEnabled } = createRequire(import.meta.url)("./bin/personality-mode.cjs");
21443
21444
  var { projectSoulText } = createRequire(import.meta.url)("./bin/soul-organization-policy.cjs");
21444
21445
  var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
21445
21446
  async function prepareSystemPromptContext(kaos, brandHome, options) {
@@ -413851,6 +413852,10 @@ registerUiCatalogFragment({
413851
413852
  "swarmPermission.manual.description": "Keep approvals on. BLUN may stop and wait for you during the swarm task.",
413852
413853
  "settings.actionStyle.label": "Output style",
413853
413854
  "settings.actionStyle.description": "Choose how BLUN responds and presents its work.",
413855
+ "settings.personality.label": "Personality",
413856
+ "settings.personality.description": "Controls relationship context, journal, and natural curiosity.",
413857
+ "personality.on.description": "Uses relationships and the journal and asks at most one personal question when it fits.",
413858
+ "personality.off.description": "Keeps Soul, agent rules, and task focus without relationship or curiosity additions.",
413854
413859
  "actionStyle.title": "Preferred output style",
413855
413860
  "actionStyle.scope": "This setting changes how BLUN responds. Plan and permission rules still take priority.",
413856
413861
  "actionStyle.default.label": "Default",
@@ -413927,6 +413932,10 @@ registerUiCatalogFragment({
413927
413932
  "swarmPermission.manual.description": "Freigaben beibehalten. BLUN kann den Swarm-Auftrag anhalten und auf dich warten.",
413928
413933
  "settings.actionStyle.label": "Ausgabestil",
413929
413934
  "settings.actionStyle.description": "Lege fest, wie BLUN antwortet und seine Arbeit darstellt.",
413935
+ "settings.personality.label": "Persönlichkeit",
413936
+ "settings.personality.description": "Steuert Beziehungskontext, Journal und natürliche Neugier.",
413937
+ "personality.on.description": "Bezieht Beziehungen und Journal ein und stellt bei passender Gelegenheit höchstens eine persönliche Frage.",
413938
+ "personality.off.description": "Behält Seele, Agentenregeln und Arbeitsfokus bei, ohne Beziehungs- oder Neugierzusätze.",
413930
413939
  "actionStyle.title": "Bevorzugter Ausgabestil",
413931
413940
  "actionStyle.scope": "Diese Einstellung bestimmt, wie BLUN antwortet. Plan- und Berechtigungsregeln haben weiterhin Vorrang.",
413932
413941
  "actionStyle.default.label": "Standard",
@@ -414003,6 +414012,10 @@ registerUiCatalogFragment({
414003
414012
  "swarmPermission.manual.description": "Mantener activadas las aprobaciones. BLUN puede detenerse y esperar tu intervención durante la tarea de Swarm.",
414004
414013
  "settings.actionStyle.label": "Estilo de salida",
414005
414014
  "settings.actionStyle.description": "Elige cómo responde BLUN y cómo presenta su trabajo.",
414015
+ "settings.personality.label": "Personalidad",
414016
+ "settings.personality.description": "Controla el contexto de las relaciones, el diario y la curiosidad natural.",
414017
+ "personality.on.description": "Tiene en cuenta las relaciones y el diario, y hace como máximo una pregunta personal cuando encaja.",
414018
+ "personality.off.description": "Mantiene el alma, las reglas del agente y el foco en la tarea, sin añadidos de relaciones ni curiosidad.",
414006
414019
  "actionStyle.title": "Estilo de salida preferido",
414007
414020
  "actionStyle.scope": "Esta opción determina cómo responde BLUN. Las reglas del plan y de permisos siguen teniendo prioridad.",
414008
414021
  "actionStyle.default.label": "Predeterminado",
@@ -414079,6 +414092,10 @@ registerUiCatalogFragment({
414079
414092
  "swarmPermission.manual.description": "Conserver les approbations. BLUN peut s’arrêter et attendre votre réponse pendant la tâche en essaim.",
414080
414093
  "settings.actionStyle.label": "Style de réponse",
414081
414094
  "settings.actionStyle.description": "Choisissez la manière dont BLUN répond et présente son travail.",
414095
+ "settings.personality.label": "Personnalité",
414096
+ "settings.personality.description": "Contrôle le contexte relationnel, le journal et la curiosité naturelle.",
414097
+ "personality.on.description": "Prend en compte les relations et le journal, et pose au plus une question personnelle lorsque le contexte s’y prête.",
414098
+ "personality.off.description": "Conserve l’âme, les règles de l’agent et l’attention portée à la tâche, sans ajout relationnel ni curiosité.",
414082
414099
  "actionStyle.title": "Style de réponse préféré",
414083
414100
  "actionStyle.scope": "Ce réglage détermine la manière dont BLUN répond. Les règles du plan et des autorisations restent prioritaires.",
414084
414101
  "actionStyle.default.label": "Par défaut",
@@ -414155,6 +414172,10 @@ registerUiCatalogFragment({
414155
414172
  "swarmPermission.manual.description": "Behåll godkännanden aktiverade. BLUN kan stanna och vänta på dig under Swarm-uppgiften.",
414156
414173
  "settings.actionStyle.label": "Svarsstil",
414157
414174
  "settings.actionStyle.description": "Välj hur BLUN svarar och presenterar sitt arbete.",
414175
+ "settings.personality.label": "Personlighet",
414176
+ "settings.personality.description": "Styr relationssammanhang, journal och naturlig nyfikenhet.",
414177
+ "personality.on.description": "Tar hänsyn till relationer och journalen och ställer högst en personlig fråga när det passar.",
414178
+ "personality.off.description": "Behåller själ, agentregler och uppgiftsfokus utan tillägg för relationer eller nyfikenhet.",
414158
414179
  "actionStyle.title": "Önskad svarsstil",
414159
414180
  "actionStyle.scope": "Den här inställningen styr hur BLUN svarar. Plan- och behörighetsregler har fortfarande företräde.",
414160
414181
  "actionStyle.default.label": "Standard",
@@ -414231,6 +414252,10 @@ registerUiCatalogFragment({
414231
414252
  "swarmPermission.manual.description": "Ponechte schválení zapnuto. BLUN se může během úlohy roje zastavit a čekat na vás.",
414232
414253
  "settings.actionStyle.label": "Styl odpovědí",
414233
414254
  "settings.actionStyle.description": "Zvolte, jak BLUN odpovídá a jak prezentuje svou práci.",
414255
+ "settings.personality.label": "Osobnost",
414256
+ "settings.personality.description": "Řídí kontext vztahů, deník a přirozenou zvídavost.",
414257
+ "personality.on.description": "Zohledňuje vztahy a deník a ve vhodnou chvíli položí nejvýše jednu osobní otázku.",
414258
+ "personality.off.description": "Zachová duši, pravidla agenta a soustředění na úkol bez vztahových či zvídavých doplňků.",
414234
414259
  "actionStyle.title": "Preferovaný styl odpovědí",
414235
414260
  "actionStyle.scope": "Toto nastavení určuje, jak BLUN odpovídá. Pravidla plánu a oprávnění mají i nadále přednost.",
414236
414261
  "actionStyle.default.label": "Výchozí",
@@ -416513,6 +416538,30 @@ var PermissionSelectorComponent = class extends ChoicePickerComponent {
416513
416538
  }
416514
416539
  };
416515
416540
  //#endregion
416541
+ //#region src/tui/components/dialogs/personality-selector.ts
416542
+ var PersonalitySelectorComponent = class extends ChoicePickerComponent {
416543
+ constructor(opts) {
416544
+ super({
416545
+ title: uiText("settings.personality.label"),
416546
+ options: [
416547
+ {
416548
+ value: "on",
416549
+ label: uiText("updates.on.label"),
416550
+ description: uiText("personality.on.description")
416551
+ },
416552
+ {
416553
+ value: "off",
416554
+ label: uiText("updates.off.label"),
416555
+ description: uiText("personality.off.description")
416556
+ }
416557
+ ],
416558
+ currentValue: opts.currentValue,
416559
+ onSelect: opts.onSelect,
416560
+ onCancel: opts.onCancel
416561
+ });
416562
+ }
416563
+ };
416564
+ //#endregion
416516
416565
  //#region src/tui/components/dialogs/settings-selector.ts
416517
416566
  function settingsOptions() {
416518
416567
  return [
@@ -416531,6 +416580,11 @@ function settingsOptions() {
416531
416580
  label: uiText("settings.actionStyle.label"),
416532
416581
  description: uiText("settings.actionStyle.description")
416533
416582
  },
416583
+ {
416584
+ value: "personality",
416585
+ label: uiText("settings.personality.label"),
416586
+ description: uiText("settings.personality.description")
416587
+ },
416534
416588
  {
416535
416589
  value: "permission",
416536
416590
  label: uiText("settings.permission.label"),
@@ -416564,7 +416618,7 @@ function settingsOptions() {
416564
416618
  ];
416565
416619
  }
416566
416620
  function isSettingsSelection(value) {
416567
- return value === "effort" || value === "action-style" || value === "theme" || value === "appearance" || value === "editor" || value === "permission" || value === "experiments" || value === "usage" || value === "inline-suggest";
416621
+ return value === "effort" || value === "action-style" || value === "personality" || value === "theme" || value === "appearance" || value === "editor" || value === "permission" || value === "experiments" || value === "usage" || value === "inline-suggest";
416568
416622
  }
416569
416623
  var SettingsSelectorComponent = class extends ChoicePickerComponent {
416570
416624
  constructor(opts) {
@@ -420398,6 +420452,28 @@ function showOutputStylePicker(host) {
420398
420452
  }
420399
420453
  }));
420400
420454
  }
420455
+ function showPersonalityPicker(host) {
420456
+ host.mountEditorReplacement(new PersonalitySelectorComponent({
420457
+ currentValue: personalityContextEnabled() ? "on" : "off",
420458
+ onSelect: (value) => {
420459
+ host.restoreEditorAfter(() => applyPersonalityChoice(host, value === "on"));
420460
+ },
420461
+ onCancel: () => {
420462
+ host.restoreEditor();
420463
+ }
420464
+ }));
420465
+ }
420466
+ async function applyPersonalityChoice(host, enabled) {
420467
+ try {
420468
+ setPersonalityEnabled(enabled);
420469
+ await refreshPersonaInSession(host);
420470
+ } catch (error) {
420471
+ host.showError(uiText("session.persona.note.saveFailed", { error: formatErrorMessage$2(error) }));
420472
+ return;
420473
+ }
420474
+ const state = enabled ? uiText("updates.on.label") : uiText("updates.off.label");
420475
+ host.showStatus(`${uiText("settings.personality.label")}: ${state}`, "success");
420476
+ }
420401
420477
  async function applyActionStyleChoice(host, style) {
420402
420478
  const previous = host.state.appState.actionStyle ?? "default";
420403
420479
  const session = host.requireSession();
@@ -420493,6 +420569,9 @@ function handleSettingsSelection(host, value) {
420493
420569
  case "action-style":
420494
420570
  showOutputStylePicker(host);
420495
420571
  return;
420572
+ case "personality":
420573
+ showPersonalityPicker(host);
420574
+ return;
420496
420575
  case "permission":
420497
420576
  showPermissionPicker(host);
420498
420577
  return;
@@ -426838,6 +426917,14 @@ function setLanguage(language) {
426838
426917
  mkdirSync(getDataDir(), { recursive: true });
426839
426918
  writeFileSync(personaFilePath(), `${JSON.stringify(next, null, 2)}\n`, "utf8");
426840
426919
  }
426920
+ function setPersonalityEnabled(enabled) {
426921
+ const next = {
426922
+ ...readPersona(),
426923
+ personalityEnabled: enabled
426924
+ };
426925
+ mkdirSync(getDataDir(), { recursive: true });
426926
+ writeFileSync(personaFilePath(), `${JSON.stringify(next, null, 2)}\n`, "utf8");
426927
+ }
426841
426928
  /** Append a note about how the user likes the agent to be (persona growth). */
426842
426929
  function addPersonaNote(note) {
426843
426930
  const current = readPersona();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.258",
3
+ "version": "9.1.260",
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": {