blun-king-cli 9.1.335 → 9.1.337
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.
|
@@ -3,7 +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 { readProfilePersona, resolveSoulFile } = require('./profile-identity-resolution.cjs');
|
|
6
|
+
const { readProfilePersona, resolveLegacySoulFile, resolveSoulFile } = require('./profile-identity-resolution.cjs');
|
|
7
7
|
|
|
8
8
|
const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
9
9
|
const MAX_PERSONA_BYTES = 64 * 1024;
|
|
@@ -83,6 +83,43 @@ function createTextOnce(root, target, value) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
function generatedSoulText(displayName) {
|
|
87
|
+
return `# ${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`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function importLegacySoulOverGeneratedProfile(env, profileHome, profileSoulPath, displayName) {
|
|
91
|
+
let current;
|
|
92
|
+
try {
|
|
93
|
+
const stat = fs.lstatSync(profileSoulPath);
|
|
94
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PERSONA_BYTES) return false;
|
|
95
|
+
current = fs.readFileSync(profileSoulPath, 'utf8');
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (current.trim() !== generatedSoulText(displayName).trim()) return false;
|
|
100
|
+
const legacy = resolveLegacySoulFile(env);
|
|
101
|
+
if (!legacy.text || path.resolve(legacy.path) === path.resolve(profileSoulPath)) return false;
|
|
102
|
+
|
|
103
|
+
const backupPath = `${profileSoulPath}.pre-legacy-import`;
|
|
104
|
+
createTextOnce(profileHome, backupPath, current);
|
|
105
|
+
if (!fs.existsSync(backupPath) || fs.readFileSync(backupPath, 'utf8') !== current) return false;
|
|
106
|
+
|
|
107
|
+
const temporaryPath = path.join(profileHome, `.SOUL.md.import-${process.pid}-${Date.now()}`);
|
|
108
|
+
let handle;
|
|
109
|
+
try {
|
|
110
|
+
handle = fs.openSync(temporaryPath, 'wx', 0o600);
|
|
111
|
+
fs.writeFileSync(handle, `${legacy.text}\n`, 'utf8');
|
|
112
|
+
fs.fsyncSync(handle);
|
|
113
|
+
fs.closeSync(handle);
|
|
114
|
+
handle = undefined;
|
|
115
|
+
fs.renameSync(temporaryPath, profileSoulPath);
|
|
116
|
+
return true;
|
|
117
|
+
} finally {
|
|
118
|
+
if (handle !== undefined) fs.closeSync(handle);
|
|
119
|
+
try { fs.unlinkSync(temporaryPath); } catch {}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
86
123
|
function ensurePersonalityWorkspace(env = process.env) {
|
|
87
124
|
const home = resolvedHome(env);
|
|
88
125
|
const profileHome = path.resolve(String(env.BLUN_HOME ?? '').trim() || home);
|
|
@@ -134,15 +171,13 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
134
171
|
);
|
|
135
172
|
const resolvedSoul = resolveSoulFile(env);
|
|
136
173
|
const profileSoulPath = path.join(profileHome, 'SOUL.md');
|
|
174
|
+
let soulMigrated = false;
|
|
137
175
|
if (resolvedSoul.text && path.resolve(resolvedSoul.path) !== path.resolve(profileSoulPath)) {
|
|
138
176
|
createTextOnce(profileHome, profileSoulPath, `${resolvedSoul.text}\n`);
|
|
139
177
|
} else if (!resolvedSoul.text) {
|
|
140
|
-
createTextOnce(
|
|
141
|
-
profileHome,
|
|
142
|
-
profileSoulPath,
|
|
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`,
|
|
144
|
-
);
|
|
178
|
+
createTextOnce(profileHome, profileSoulPath, generatedSoulText(displayName));
|
|
145
179
|
}
|
|
180
|
+
soulMigrated = importLegacySoulOverGeneratedProfile(env, profileHome, profileSoulPath, displayName);
|
|
146
181
|
for (const relative of [
|
|
147
182
|
['agents', agentId, 'relationships'],
|
|
148
183
|
['agents', agentId, 'journal', 'candidates'],
|
|
@@ -156,7 +191,7 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
156
191
|
fs.mkdirSync(target, { recursive: true });
|
|
157
192
|
if (!isInside(root, fs.realpathSync(target))) return { ready: false, reason: 'identity_path_invalid' };
|
|
158
193
|
}
|
|
159
|
-
return { ready: true, root, tenant_id: tenantId, agent_id: agentId };
|
|
194
|
+
return { ready: true, root, tenant_id: tenantId, agent_id: agentId, soul_migrated: soulMigrated };
|
|
160
195
|
}
|
|
161
196
|
|
|
162
197
|
module.exports = { ensurePersonalityWorkspace };
|
|
@@ -77,6 +77,30 @@ function legacySoulNames(env, persona) {
|
|
|
77
77
|
return names;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function legacySoulCandidates(env = process.env, options = {}) {
|
|
81
|
+
const homeDir = options.homeDir || os.homedir();
|
|
82
|
+
const profileHome = String(env.BLUN_HOME || '').trim();
|
|
83
|
+
const sharedHome = String(env.BLUN_SHARED_HOME || '').trim();
|
|
84
|
+
const persona = options.persona || readProfilePersona(env, options).persona;
|
|
85
|
+
const roots = uniquePaths([
|
|
86
|
+
sharedHome,
|
|
87
|
+
homeDir,
|
|
88
|
+
profileHome && path.dirname(profileHome),
|
|
89
|
+
]);
|
|
90
|
+
return legacySoulNames(env, persona)
|
|
91
|
+
.flatMap((filename) => roots.map((root) => path.join(root, filename)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveLegacySoulFile(env = process.env, options = {}) {
|
|
95
|
+
const fsImpl = options.fsImpl || fs;
|
|
96
|
+
const candidates = uniquePaths(legacySoulCandidates(env, options));
|
|
97
|
+
for (const target of candidates) {
|
|
98
|
+
const text = readSafeText(target, fsImpl);
|
|
99
|
+
if (text) return { path: target, text };
|
|
100
|
+
}
|
|
101
|
+
return { path: candidates[0], text: '' };
|
|
102
|
+
}
|
|
103
|
+
|
|
80
104
|
function resolveSoulFile(env = process.env, options = {}) {
|
|
81
105
|
const fsImpl = options.fsImpl || fs;
|
|
82
106
|
const explicit = String(env.BLUN_SOUL_PATH || '').trim();
|
|
@@ -90,13 +114,7 @@ function resolveSoulFile(env = process.env, options = {}) {
|
|
|
90
114
|
const profileName = String(env.BLUN_PROFILE || '').trim().toLowerCase();
|
|
91
115
|
const isNamedProfile = profileName && profileName !== 'default';
|
|
92
116
|
const persona = readProfilePersona(env, options).persona;
|
|
93
|
-
const
|
|
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)));
|
|
117
|
+
const legacyCandidates = legacySoulCandidates(env, { ...options, persona });
|
|
100
118
|
const candidates = uniquePaths([
|
|
101
119
|
profileHome && path.join(profileHome, 'SOUL.md'),
|
|
102
120
|
...legacyCandidates,
|
|
@@ -113,5 +131,6 @@ module.exports = {
|
|
|
113
131
|
MAX_IDENTITY_FILE_BYTES,
|
|
114
132
|
identityFileCandidates,
|
|
115
133
|
readProfilePersona,
|
|
134
|
+
resolveLegacySoulFile,
|
|
116
135
|
resolveSoulFile,
|
|
117
136
|
};
|
|
@@ -56,6 +56,7 @@ function isExplicitPauseRequest(text) {
|
|
|
56
56
|
function createDirectFocusController(options = {}) {
|
|
57
57
|
const setTimer = options.setTimer ?? setTimeout;
|
|
58
58
|
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
59
|
+
const now = options.now ?? Date.now;
|
|
59
60
|
const checkpoint = options.checkpoint ?? (() => {});
|
|
60
61
|
const resume = options.resume ?? (() => {});
|
|
61
62
|
const silenceMs = options.silenceMs ?? DEFAULT_SILENCE_MS;
|
|
@@ -89,16 +90,23 @@ function createDirectFocusController(options = {}) {
|
|
|
89
90
|
finishConversation(chatId, existing);
|
|
90
91
|
return { resumeGranted: true };
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
const startsConversation = conversations.size === 0;
|
|
94
|
+
if (startsConversation) {
|
|
93
95
|
savedCheckpoint = { ...checkpointValue, chatId };
|
|
94
|
-
checkpoint(savedCheckpoint);
|
|
95
96
|
}
|
|
96
97
|
const conversation = existing ?? { chatId };
|
|
97
98
|
clearConversationTimer(conversation);
|
|
99
|
+
const wasExplicitPause = conversation.explicitPause === true;
|
|
98
100
|
conversation.explicitPause = conversation.explicitPause === true || isExplicitPauseRequest(text);
|
|
99
101
|
conversation.waitingPermission = conversation.explicitPause;
|
|
100
102
|
conversation.closeAfterReply = isConversationClose(text);
|
|
101
103
|
conversations.set(chatId, conversation);
|
|
104
|
+
savedCheckpoint = {
|
|
105
|
+
...savedCheckpoint,
|
|
106
|
+
explicitPause: conversation.explicitPause,
|
|
107
|
+
resumeAfterAt: null,
|
|
108
|
+
};
|
|
109
|
+
if (startsConversation || wasExplicitPause !== conversation.explicitPause) checkpoint(savedCheckpoint);
|
|
102
110
|
return { resumeGranted: false, explicitPause: conversation.explicitPause };
|
|
103
111
|
}
|
|
104
112
|
|
|
@@ -109,10 +117,14 @@ function createDirectFocusController(options = {}) {
|
|
|
109
117
|
clearConversationTimer(conversation);
|
|
110
118
|
if (conversation.explicitPause === true) {
|
|
111
119
|
conversation.waitingPermission = true;
|
|
120
|
+
checkpoint({ ...savedCheckpoint, explicitPause: true, resumeAfterAt: null });
|
|
112
121
|
return true;
|
|
113
122
|
}
|
|
114
123
|
const delay = conversation.closeAfterReply ? 0 : silenceMs;
|
|
115
124
|
conversation.closeAfterReply = false;
|
|
125
|
+
const resumeAfterAt = new Date(now() + delay).toISOString();
|
|
126
|
+
savedCheckpoint = { ...savedCheckpoint, explicitPause: false, resumeAfterAt };
|
|
127
|
+
checkpoint(savedCheckpoint);
|
|
116
128
|
conversation.timer = setTimer(() => {
|
|
117
129
|
finishConversation(chatId, conversation);
|
|
118
130
|
}, delay);
|
|
@@ -125,6 +137,26 @@ function createDirectFocusController(options = {}) {
|
|
|
125
137
|
savedCheckpoint = undefined;
|
|
126
138
|
}
|
|
127
139
|
|
|
140
|
+
const initialCheckpoint = options.initialCheckpoint;
|
|
141
|
+
if (initialCheckpoint?.status === 'paused' && isPrivateTelegramChat(initialCheckpoint.chatId)) {
|
|
142
|
+
const chatId = String(initialCheckpoint.chatId);
|
|
143
|
+
const explicitPause = initialCheckpoint.explicitPause === true;
|
|
144
|
+
const conversation = {
|
|
145
|
+
chatId,
|
|
146
|
+
explicitPause,
|
|
147
|
+
waitingPermission: explicitPause,
|
|
148
|
+
};
|
|
149
|
+
savedCheckpoint = { ...initialCheckpoint, chatId };
|
|
150
|
+
conversations.set(chatId, conversation);
|
|
151
|
+
const resumeAfterMs = Date.parse(String(initialCheckpoint.resumeAfterAt ?? ''));
|
|
152
|
+
if (!explicitPause && Number.isFinite(resumeAfterMs)) {
|
|
153
|
+
conversation.timer = setTimer(
|
|
154
|
+
() => finishConversation(chatId, conversation),
|
|
155
|
+
Math.max(0, resumeAfterMs - now()),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
128
160
|
return {
|
|
129
161
|
dispose,
|
|
130
162
|
isPaused: () => conversations.size > 0,
|
|
@@ -145,7 +177,13 @@ function writeDirectFocusCheckpoint(value, env = process.env) {
|
|
|
145
177
|
const record = {
|
|
146
178
|
version: 1,
|
|
147
179
|
status: value?.status === 'resumed' ? 'resumed' : 'paused',
|
|
148
|
-
pausedAt:
|
|
180
|
+
pausedAt: Number.isFinite(Date.parse(String(value?.pausedAt ?? '')))
|
|
181
|
+
? new Date(value.pausedAt).toISOString()
|
|
182
|
+
: new Date().toISOString(),
|
|
183
|
+
explicitPause: value?.explicitPause === true,
|
|
184
|
+
resumeAfterAt: Number.isFinite(Date.parse(String(value?.resumeAfterAt ?? '')))
|
|
185
|
+
? new Date(value.resumeAfterAt).toISOString()
|
|
186
|
+
: null,
|
|
149
187
|
chatId: isPrivateTelegramChat(value?.chatId) ? String(value.chatId) : null,
|
|
150
188
|
sessionId: value?.sessionId === undefined ? null : String(value.sessionId).slice(0, 160),
|
|
151
189
|
turnId: value?.turnId === undefined ? null : String(value.turnId),
|
|
@@ -160,6 +198,18 @@ function writeDirectFocusCheckpoint(value, env = process.env) {
|
|
|
160
198
|
return { path: target, record };
|
|
161
199
|
}
|
|
162
200
|
|
|
201
|
+
function readDirectFocusCheckpoint(env = process.env) {
|
|
202
|
+
const target = directFocusCheckpointPath(env);
|
|
203
|
+
try {
|
|
204
|
+
const record = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
205
|
+
if (record?.version !== 1 || (record.status !== 'paused' && record.status !== 'resumed')) return undefined;
|
|
206
|
+
return record;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error?.code === 'ENOENT') return undefined;
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
163
213
|
module.exports = {
|
|
164
214
|
DEFAULT_SILENCE_MS,
|
|
165
215
|
createDirectFocusController,
|
|
@@ -169,6 +219,7 @@ module.exports = {
|
|
|
169
219
|
isExplicitPauseRequest,
|
|
170
220
|
isPrivateTelegramChat,
|
|
171
221
|
isResumeApproval,
|
|
222
|
+
readDirectFocusCheckpoint,
|
|
172
223
|
rewriteTelegramDirectEnvelope,
|
|
173
224
|
telegramDirectMessage,
|
|
174
225
|
writeDirectFocusCheckpoint,
|
package/blun.mjs
CHANGED
|
@@ -419036,7 +419036,7 @@ registerUiCatalogFragment({
|
|
|
419036
419036
|
*/
|
|
419037
419037
|
var { projectUnaddressedTelegramContext } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs");
|
|
419038
419038
|
var { enqueueTelegramUrgent, rewriteTelegramUrgentEnvelope, telegramUrgentMessage } = createRequire(import.meta.url)("./bin/telegram-urgent-policy.cjs");
|
|
419039
|
-
var { createDirectFocusController, enqueueTelegramDirect, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
|
|
419039
|
+
var { createDirectFocusController, enqueueTelegramDirect, readDirectFocusCheckpoint, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
|
|
419040
419040
|
var { removeQueuedReloadCommands, removeQueuedSlashCommands } = createRequire(import.meta.url)("./bin/reload-queue-policy.cjs");
|
|
419041
419041
|
const MAX_BUFFERED_CONTEXT_MESSAGES = 20;
|
|
419042
419042
|
const MAX_BUFFERED_CONTEXT_CHARS = 24e3;
|
|
@@ -509485,6 +509485,9 @@ var SessionReplayRenderer = class {
|
|
|
509485
509485
|
cleanupRuntime(context) {
|
|
509486
509486
|
this.flushAssistant(context);
|
|
509487
509487
|
this.host.streamingUI.cleanupAfterReplay(context.completedToolCallIds);
|
|
509488
|
+
this.host.setAppState({ streamingPhase: "idle" });
|
|
509489
|
+
this.host.resetLivePane();
|
|
509490
|
+
this.host.requestQueueDrain();
|
|
509488
509491
|
}
|
|
509489
509492
|
renderSkillActivation(context, skill) {
|
|
509490
509493
|
const { sessionEventHandler } = this.host;
|
|
@@ -516082,6 +516085,7 @@ var BlunTUI = class {
|
|
|
516082
516085
|
deliverOne: () => this.deliverQueuedChannelHead()
|
|
516083
516086
|
});
|
|
516084
516087
|
this.directFocusController = createDirectFocusController({
|
|
516088
|
+
initialCheckpoint: readDirectFocusCheckpoint(),
|
|
516085
516089
|
checkpoint: (checkpoint) => {
|
|
516086
516090
|
try {
|
|
516087
516091
|
writeDirectFocusCheckpoint(checkpoint);
|
|
@@ -516929,7 +516933,7 @@ var BlunTUI = class {
|
|
|
516929
516933
|
return this.restoreQueuedSteer(inFlight, error);
|
|
516930
516934
|
};
|
|
516931
516935
|
try {
|
|
516932
|
-
if (!(await this.harness.withInteractiveAgent(item.agentId ?? "main", () => session.steerActive(input, expectedTurnId === void 0 ? {} : { expectedTurnId }))).accepted) return
|
|
516936
|
+
if (!(await this.harness.withInteractiveAgent(item.agentId ?? "main", () => session.steerActive(input, expectedTurnId === void 0 ? {} : { expectedTurnId }))).accepted) return this.recoverRejectedActiveSteer(inFlight);
|
|
516933
516937
|
inFlight.accepted = true;
|
|
516934
516938
|
if (inFlight.turnEnded) return restoreHead();
|
|
516935
516939
|
this.commitQueuedSteerIfReady(inFlight);
|
|
@@ -516981,6 +516985,15 @@ var BlunTUI = class {
|
|
|
516981
516985
|
this.steerQueueFlushPrefix();
|
|
516982
516986
|
this.scheduleQueueDrain();
|
|
516983
516987
|
}
|
|
516988
|
+
recoverRejectedActiveSteer(inFlight) {
|
|
516989
|
+
const restored = this.restoreQueuedSteer(inFlight);
|
|
516990
|
+
if (!this.streamingUI.hasActiveTurn()) {
|
|
516991
|
+
this.setAppState({ streamingPhase: "idle" });
|
|
516992
|
+
this.resetLivePane();
|
|
516993
|
+
this.scheduleQueueDrain();
|
|
516994
|
+
}
|
|
516995
|
+
return restored;
|
|
516996
|
+
}
|
|
516984
516997
|
restoreQueuedSteer(inFlight, error) {
|
|
516985
516998
|
if (this.queueSteerInFlight !== inFlight) return false;
|
|
516986
516999
|
this.queueSteerInFlight = void 0;
|
|
@@ -517071,7 +517084,7 @@ var BlunTUI = class {
|
|
|
517071
517084
|
const expectedTurnId = turnId !== void 0 && /^\d+$/.test(turnId) ? Number(turnId) : void 0;
|
|
517072
517085
|
session.steerActive(items.map((item) => item.text.trim()).join("\n\n"), expectedTurnId === void 0 ? {} : { expectedTurnId }).then((result) => {
|
|
517073
517086
|
if (this.queueSteerInFlight !== inFlight) return;
|
|
517074
|
-
if (!result.accepted) this.
|
|
517087
|
+
if (!result.accepted) this.recoverRejectedActiveSteer(inFlight);
|
|
517075
517088
|
else {
|
|
517076
517089
|
inFlight.accepted = true;
|
|
517077
517090
|
if (inFlight.turnEnded) this.restoreQueuedSteer(inFlight);
|