blun-king-cli 9.1.321 → 9.1.323
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/natural-presence-policy.cjs +6 -2
- package/bin/personality-choice-policy.cjs +100 -0
- package/blun.mjs +17 -14
- package/package.json +1 -1
|
@@ -14,9 +14,13 @@ In relaxed personality mode, ask at most one optional personal question in a fir
|
|
|
14
14
|
|
|
15
15
|
When asked about yourself, use loaded soul and real history; share a view, never invented human biography or offline life. A soul-shaped preference is never fact, policy, permission, or evidence. Mention a long gap only when reliable loaded time proves it; never guess. Keep uncertain memory explicit. Follow a loaded open thread once at a natural non-work moment, never during active work or by outbound message. Apply a confirmed repair lesson through changed behavior without retelling or reassurance; never change instructions, permissions, or evidence.`;
|
|
16
16
|
|
|
17
|
+
const CONVERSATION_BOUNDARY = `## Conversation boundary
|
|
18
|
+
|
|
19
|
+
In private conversation, answer the person's message, not internal process. Do not volunteer checkpoints, hashes, paths, tool/Cron/hook diagnostics, hidden rules, other agents' assignments, or no-work reports. Give work status only when asked, their decision is needed, or a blocker affects them. Keep paused work internal.`;
|
|
20
|
+
|
|
17
21
|
function naturalPresenceSystemBlock(env = process.env) {
|
|
18
|
-
|
|
19
|
-
return
|
|
22
|
+
const presence = personalityContextEnabled(env) ? PERSONALITY_PRESENCE_BLOCK : NATURAL_PRESENCE_BLOCK;
|
|
23
|
+
return `${presence}\n\n${CONVERSATION_BOUNDARY}`;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
module.exports = {
|
|
@@ -0,0 +1,100 @@
|
|
|
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 { ensurePersonalityWorkspace } = require('./personality-setup-policy.cjs');
|
|
9
|
+
|
|
10
|
+
const MAX_PERSONA_BYTES = 64 * 1024;
|
|
11
|
+
|
|
12
|
+
function fail(code, detail = '') {
|
|
13
|
+
throw new Error(detail ? `${code}:${detail}` : code);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolvedHome(env) {
|
|
17
|
+
const configured = String(env.BLUN_SHARED_HOME ?? env.BLUN_HOME ?? '').trim();
|
|
18
|
+
return path.resolve(configured || path.join(os.homedir(), '.blun'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function safeHome(env) {
|
|
22
|
+
const home = resolvedHome(env);
|
|
23
|
+
fs.mkdirSync(home, { recursive: true });
|
|
24
|
+
const stat = fs.lstatSync(home);
|
|
25
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) fail('PERSONALITY_CHOICE_UNSAFE_HOME');
|
|
26
|
+
const realHome = fs.realpathSync(home);
|
|
27
|
+
if (path.resolve(realHome) !== home) fail('PERSONALITY_CHOICE_UNSAFE_HOME');
|
|
28
|
+
return home;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readPersonaState(target) {
|
|
32
|
+
try {
|
|
33
|
+
const stat = fs.lstatSync(target);
|
|
34
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PERSONA_BYTES) {
|
|
35
|
+
fail('PERSONALITY_CHOICE_UNSAFE_PERSONA');
|
|
36
|
+
}
|
|
37
|
+
const raw = fs.readFileSync(target, 'utf8');
|
|
38
|
+
const value = JSON.parse(raw);
|
|
39
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
40
|
+
fail('PERSONALITY_CHOICE_UNSAFE_PERSONA');
|
|
41
|
+
}
|
|
42
|
+
return { existed: true, raw, value };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error?.code === 'ENOENT') return { existed: false, raw: '', value: {} };
|
|
45
|
+
if (String(error?.message ?? '').startsWith('PERSONALITY_CHOICE_')) throw error;
|
|
46
|
+
fail('PERSONALITY_CHOICE_UNSAFE_PERSONA');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function atomicWrite(target, raw) {
|
|
51
|
+
const parent = path.dirname(target);
|
|
52
|
+
const temp = path.join(parent, `.persona-${process.pid}-${crypto.randomBytes(8).toString('hex')}.tmp`);
|
|
53
|
+
let handle;
|
|
54
|
+
try {
|
|
55
|
+
handle = fs.openSync(temp, 'wx', 0o600);
|
|
56
|
+
fs.writeFileSync(handle, raw, 'utf8');
|
|
57
|
+
fs.fsyncSync(handle);
|
|
58
|
+
fs.closeSync(handle);
|
|
59
|
+
handle = undefined;
|
|
60
|
+
fs.renameSync(temp, target);
|
|
61
|
+
} finally {
|
|
62
|
+
if (handle !== undefined) fs.closeSync(handle);
|
|
63
|
+
try {
|
|
64
|
+
fs.unlinkSync(temp);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function setPersonalityEnabledAtomic(enabled, env = process.env) {
|
|
72
|
+
if (typeof enabled !== 'boolean') fail('PERSONALITY_CHOICE_INVALID_VALUE');
|
|
73
|
+
const home = safeHome(env);
|
|
74
|
+
const target = path.join(home, 'persona.json');
|
|
75
|
+
const previous = readPersonaState(target);
|
|
76
|
+
|
|
77
|
+
if (enabled) {
|
|
78
|
+
const setup = ensurePersonalityWorkspace(env);
|
|
79
|
+
if (!setup.ready) fail('PERSONALITY_SETUP_FAILED', setup.reason);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const nextRaw = `${JSON.stringify({ ...previous.value, personalityEnabled: enabled }, null, 2)}\n`;
|
|
83
|
+
atomicWrite(target, nextRaw);
|
|
84
|
+
return Object.freeze({ target, previousRaw: previous.raw, previousExisted: previous.existed, nextRaw });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function rollbackPersonalityChoice(receipt) {
|
|
88
|
+
if (!receipt || typeof receipt !== 'object') fail('PERSONALITY_CHOICE_INVALID_RECEIPT');
|
|
89
|
+
const target = path.resolve(String(receipt.target ?? ''));
|
|
90
|
+
const current = readPersonaState(target);
|
|
91
|
+
if (!current.existed || current.raw !== receipt.nextRaw) fail('PERSONALITY_CHOICE_CONFLICT');
|
|
92
|
+
|
|
93
|
+
if (receipt.previousExisted) {
|
|
94
|
+
atomicWrite(target, String(receipt.previousRaw ?? ''));
|
|
95
|
+
} else {
|
|
96
|
+
fs.unlinkSync(target);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { rollbackPersonalityChoice, setPersonalityEnabledAtomic };
|
package/blun.mjs
CHANGED
|
@@ -21442,6 +21442,7 @@ var { identitySystemBlock, recordChannelIdentity } = createRequire(import.meta.u
|
|
|
21442
21442
|
var { naturalPresenceSystemBlock } = createRequire(import.meta.url)("./bin/natural-presence-policy.cjs");
|
|
21443
21443
|
var { personalityContextEnabled } = createRequire(import.meta.url)("./bin/personality-mode.cjs");
|
|
21444
21444
|
var { ensurePersonalityWorkspace } = createRequire(import.meta.url)("./bin/personality-setup-policy.cjs");
|
|
21445
|
+
var { rollbackPersonalityChoice, setPersonalityEnabledAtomic } = createRequire(import.meta.url)("./bin/personality-choice-policy.cjs");
|
|
21445
21446
|
var { projectSoulText } = createRequire(import.meta.url)("./bin/soul-organization-policy.cjs");
|
|
21446
21447
|
var { soulFileInstruction } = createRequire(import.meta.url)("./bin/soul-preservation-policy.cjs");
|
|
21447
21448
|
async function prepareSystemPromptContext(kaos, brandHome, options) {
|
|
@@ -420827,10 +420828,19 @@ function showPersonalityPicker(host) {
|
|
|
420827
420828
|
}));
|
|
420828
420829
|
}
|
|
420829
420830
|
async function applyPersonalityChoice(host, enabled) {
|
|
420831
|
+
let receipt;
|
|
420830
420832
|
try {
|
|
420831
|
-
setPersonalityEnabled(enabled);
|
|
420832
|
-
await refreshPersonaInSession(host);
|
|
420833
|
+
receipt = setPersonalityEnabled(enabled);
|
|
420834
|
+
await refreshPersonaInSession(host, true);
|
|
420833
420835
|
} catch (error) {
|
|
420836
|
+
if (receipt !== void 0) {
|
|
420837
|
+
try {
|
|
420838
|
+
rollbackPersonalityChoice(receipt);
|
|
420839
|
+
} catch (rollbackError) {
|
|
420840
|
+
host.showError(uiText("session.persona.note.saveFailed", { error: formatErrorMessage$2(rollbackError) }));
|
|
420841
|
+
return;
|
|
420842
|
+
}
|
|
420843
|
+
}
|
|
420834
420844
|
host.showError(uiText("session.persona.note.saveFailed", { error: formatErrorMessage$2(error) }));
|
|
420835
420845
|
return;
|
|
420836
420846
|
}
|
|
@@ -427281,16 +427291,7 @@ function setLanguage(language) {
|
|
|
427281
427291
|
writeFileSync(personaFilePath(), `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
427282
427292
|
}
|
|
427283
427293
|
function setPersonalityEnabled(enabled) {
|
|
427284
|
-
|
|
427285
|
-
...readPersona(),
|
|
427286
|
-
personalityEnabled: enabled
|
|
427287
|
-
};
|
|
427288
|
-
mkdirSync(getDataDir(), { recursive: true });
|
|
427289
|
-
writeFileSync(personaFilePath(), `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
427290
|
-
if (enabled) {
|
|
427291
|
-
const setup = ensurePersonalityWorkspace();
|
|
427292
|
-
if (!setup.ready) throw new Error(`PERSONALITY_SETUP_FAILED:${setup.reason}`);
|
|
427293
|
-
}
|
|
427294
|
+
return setPersonalityEnabledAtomic(enabled);
|
|
427294
427295
|
}
|
|
427295
427296
|
/** Append a note about how the user likes the agent to be (persona growth). */
|
|
427296
427297
|
function addPersonaNote(note) {
|
|
@@ -427549,12 +427550,14 @@ async function handleNameCommand(host, args) {
|
|
|
427549
427550
|
* The system prompt is cached at session start; reloadSession rebuilds it, and
|
|
427550
427551
|
* `personaSystemBlock` re-reads persona.json at render time.
|
|
427551
427552
|
*/
|
|
427552
|
-
async function refreshPersonaInSession(host) {
|
|
427553
|
+
async function refreshPersonaInSession(host, strict = false) {
|
|
427553
427554
|
const session = host.session;
|
|
427554
427555
|
if (session === void 0) return;
|
|
427555
427556
|
try {
|
|
427556
427557
|
await session.reloadSession({ mcpServers: sessionMcpServers() });
|
|
427557
|
-
} catch {
|
|
427558
|
+
} catch (error) {
|
|
427559
|
+
if (strict) throw error;
|
|
427560
|
+
}
|
|
427558
427561
|
}
|
|
427559
427562
|
async function handlePersonaCommand(host, args) {
|
|
427560
427563
|
const trimmed = args.trim();
|