blun-king-cli 9.1.17 → 9.1.19
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/README.md +1 -1
- package/bin/blun.js +0 -0
- package/bin/core-bootstrap.js +0 -0
- package/bin/king.js +0 -0
- package/bin/launcher-mode.js +0 -0
- package/bin/launcher-runtime.js +0 -0
- package/bin/plugin-bootstrap.js +0 -0
- package/bin/private-paths.js +0 -0
- package/bin/standard-tools-bootstrap.js +0 -0
- package/bin/update-lease.js +0 -0
- package/bin/update-notice.js +0 -0
- package/blun.mjs +152 -76
- package/package.json +3 -2
- package/telegram-plugin/dist/bridge.mjs +1 -1
- package/telegram-plugin/dist/mcp-server.mjs +1 -1
- package/telegram-plugin/dist/noise.mjs +1 -1
package/README.md
CHANGED
package/bin/blun.js
CHANGED
|
File without changes
|
package/bin/core-bootstrap.js
CHANGED
|
File without changes
|
package/bin/king.js
CHANGED
|
File without changes
|
package/bin/launcher-mode.js
CHANGED
|
File without changes
|
package/bin/launcher-runtime.js
CHANGED
|
File without changes
|
package/bin/plugin-bootstrap.js
CHANGED
|
File without changes
|
package/bin/private-paths.js
CHANGED
|
File without changes
|
|
File without changes
|
package/bin/update-lease.js
CHANGED
|
File without changes
|
package/bin/update-notice.js
CHANGED
|
File without changes
|
package/blun.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// BLUN_BUILD_INPUT_SHA256:
|
|
2
|
+
// BLUN_BUILD_INPUT_SHA256:b991ad9b5744a4c7edb806fc71b837f5d58205791bc5979c4e05b6b5f3564456
|
|
3
3
|
import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
4
4
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
5
5
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
@@ -74394,8 +74394,8 @@ var init_kosong_llm = __esmMin((() => {
|
|
|
74394
74394
|
//#endregion
|
|
74395
74395
|
//#region ../../packages/agent-core/src/agent/compaction/typical-duration.ts
|
|
74396
74396
|
/**
|
|
74397
|
-
* Experience-based expectation for compaction duration
|
|
74398
|
-
*
|
|
74397
|
+
* Experience-based expectation for compaction duration: instead of a
|
|
74398
|
+
* fabricated percentage, the compaction indicator
|
|
74399
74399
|
* may show the median duration of past completed runs for the same
|
|
74400
74400
|
* provider+model — but only when the data actually supports it.
|
|
74401
74401
|
*
|
|
@@ -232242,9 +232242,14 @@ var WINDOWS_MCP_TOOL_PREFIX, DesktopControlAlwaysAskPermissionPolicy;
|
|
|
232242
232242
|
var init_desktop_control_always_ask = __esmMin((() => {
|
|
232243
232243
|
WINDOWS_MCP_TOOL_PREFIX = "mcp__windows-mcp__";
|
|
232244
232244
|
DesktopControlAlwaysAskPermissionPolicy = class {
|
|
232245
|
+
agent;
|
|
232245
232246
|
name = "desktop-control-always-ask";
|
|
232247
|
+
constructor(agent) {
|
|
232248
|
+
this.agent = agent;
|
|
232249
|
+
}
|
|
232246
232250
|
evaluate(context) {
|
|
232247
232251
|
if (!context.toolCall.name.toLowerCase().startsWith(WINDOWS_MCP_TOOL_PREFIX)) return;
|
|
232252
|
+
if (this.agent.permission.mode === "yolo") return;
|
|
232248
232253
|
return { kind: "ask" };
|
|
232249
232254
|
}
|
|
232250
232255
|
};
|
|
@@ -232873,7 +232878,7 @@ function createPermissionDecisionPolicies(agent) {
|
|
|
232873
232878
|
new PlanModeGuardDenyPermissionPolicy(agent),
|
|
232874
232879
|
new UserConfiguredDenyPermissionPolicy(agent),
|
|
232875
232880
|
new PersonalMemoryMutationAlwaysAskPermissionPolicy(),
|
|
232876
|
-
new DesktopControlAlwaysAskPermissionPolicy(),
|
|
232881
|
+
new DesktopControlAlwaysAskPermissionPolicy(agent),
|
|
232877
232882
|
new AutoModeApprovePermissionPolicy(agent),
|
|
232878
232883
|
new SessionApprovalHistoryPermissionPolicy(agent),
|
|
232879
232884
|
new UserConfiguredAskPermissionPolicy(agent),
|
|
@@ -336447,6 +336452,8 @@ function buildSkillSlashCommands(skills) {
|
|
|
336447
336452
|
//#region src/constant/account-memory.ts
|
|
336448
336453
|
const ACCOUNT_MEMORY_READ_URL = "https://chat.blun.ai/api/blun-memory?persona_id=king&surface=global";
|
|
336449
336454
|
const ACCOUNT_MEMORY_EXTRACT_URL = "https://chat.blun.ai/api/blun-memory/extract?persona_id=king&surface=global";
|
|
336455
|
+
const ACCOUNT_MEMORY_PREFERENCES_URL = "https://account.blun.ai/api/me/preferences";
|
|
336456
|
+
const ACCOUNT_MEMORY_SETTINGS_URL = "https://account.blun.ai/api/account/settings";
|
|
336450
336457
|
const ACCOUNT_MEMORY_MAX_FACT_CHARS = 1e3;
|
|
336451
336458
|
//#endregion
|
|
336452
336459
|
//#region src/utils/account-memory.ts
|
|
@@ -336463,11 +336470,34 @@ function describeAccountMemoryError(error) {
|
|
|
336463
336470
|
function createAccountMemoryClient(auth, options = {}) {
|
|
336464
336471
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
336465
336472
|
const timeoutMs = options.timeoutMs ?? 4e3;
|
|
336473
|
+
let consentStatus;
|
|
336474
|
+
const getConsentStatus = async () => {
|
|
336475
|
+
consentStatus = readAccountMemoryConsentStatus(await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_PREFERENCES_URL, { method: "GET" }, timeoutMs, readBoundedJson));
|
|
336476
|
+
return consentStatus;
|
|
336477
|
+
};
|
|
336478
|
+
const hasExplicitConsent = async () => {
|
|
336479
|
+
return (consentStatus ?? await getConsentStatus()) === "enabled";
|
|
336480
|
+
};
|
|
336466
336481
|
return {
|
|
336482
|
+
getConsentStatus,
|
|
336483
|
+
async setConsent(enabled) {
|
|
336484
|
+
const stored = readAccountMemoryConsentStatus(await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_SETTINGS_URL, {
|
|
336485
|
+
method: "PATCH",
|
|
336486
|
+
headers: { "Content-Type": "application/json" },
|
|
336487
|
+
body: JSON.stringify({ data_controls: {
|
|
336488
|
+
allow_memory: enabled,
|
|
336489
|
+
memory_consent_asked: true
|
|
336490
|
+
} })
|
|
336491
|
+
}, timeoutMs, readBoundedJson));
|
|
336492
|
+
if (stored !== (enabled ? "enabled" : "disabled")) throw new Error("Account memory returned an invalid response.");
|
|
336493
|
+
consentStatus = stored;
|
|
336494
|
+
},
|
|
336467
336495
|
async loadSystemContext() {
|
|
336496
|
+
if (!await hasExplicitConsent()) return void 0;
|
|
336468
336497
|
return renderAccountMemoryContext(await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_READ_URL, { method: "GET" }, timeoutMs, readBoundedJson));
|
|
336469
336498
|
},
|
|
336470
336499
|
async extract(text) {
|
|
336500
|
+
if (!await hasExplicitConsent()) return;
|
|
336471
336501
|
await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_EXTRACT_URL, {
|
|
336472
336502
|
method: "POST",
|
|
336473
336503
|
headers: { "Content-Type": "application/json" },
|
|
@@ -336476,6 +336506,15 @@ function createAccountMemoryClient(auth, options = {}) {
|
|
|
336476
336506
|
}
|
|
336477
336507
|
};
|
|
336478
336508
|
}
|
|
336509
|
+
function readAccountMemoryConsentStatus(payload) {
|
|
336510
|
+
const settings = unwrapPayload(payload)?.["settings"];
|
|
336511
|
+
const dataControls = isRecord$13(settings) ? settings["data_controls"] : void 0;
|
|
336512
|
+
if (!isRecord$13(dataControls)) throw new Error("Account memory returned an invalid response.");
|
|
336513
|
+
if (dataControls["memory_consent_asked"] !== true) return "never_asked";
|
|
336514
|
+
if (dataControls["allow_memory"] === true) return "enabled";
|
|
336515
|
+
if (dataControls["allow_memory"] === false) return "disabled";
|
|
336516
|
+
throw new Error("Account memory returned an invalid response.");
|
|
336517
|
+
}
|
|
336479
336518
|
async function requestWithOAuth(auth, fetchImpl, url, init, timeoutMs, consume) {
|
|
336480
336519
|
const controller = new AbortController();
|
|
336481
336520
|
let timeout;
|
|
@@ -397147,42 +397186,42 @@ registerUiCatalogFragment({
|
|
|
397147
397186
|
registerUiCatalogFragment({
|
|
397148
397187
|
en: {
|
|
397149
397188
|
"startupPersonalMemory.title": "Personal memory",
|
|
397150
|
-
"startupPersonalMemory.details": "When
|
|
397189
|
+
"startupPersonalMemory.details": "When personal memory is enabled, BLUN automatically extracts useful information from your chats and can use it in future conversations. This stays off until you enable it.",
|
|
397151
397190
|
"startupPersonalMemory.enable": "Enable memory",
|
|
397152
397191
|
"startupPersonalMemory.decline": "Do not enable",
|
|
397153
397192
|
"startupPersonalMemory.persistenceFailed": "Memory settings could not be saved. Personal memory remains off."
|
|
397154
397193
|
},
|
|
397155
397194
|
de: {
|
|
397156
397195
|
"startupPersonalMemory.title": "Persönliches Gedächtnis",
|
|
397157
|
-
"startupPersonalMemory.details": "Wenn
|
|
397196
|
+
"startupPersonalMemory.details": "Wenn das persönliche Gedächtnis aktiviert ist, übernimmt BLUN automatisch nützliche Informationen aus deinen Chats und kann sie in späteren Gesprächen verwenden. Die Funktion bleibt deaktiviert, bis du sie einschaltest.",
|
|
397158
397197
|
"startupPersonalMemory.enable": "Gedächtnis aktivieren",
|
|
397159
397198
|
"startupPersonalMemory.decline": "Nicht aktivieren",
|
|
397160
397199
|
"startupPersonalMemory.persistenceFailed": "Die Gedächtniseinstellungen konnten nicht gespeichert werden. Das persönliche Gedächtnis bleibt deaktiviert."
|
|
397161
397200
|
},
|
|
397162
397201
|
es: {
|
|
397163
397202
|
"startupPersonalMemory.title": "Memoria personal",
|
|
397164
|
-
"startupPersonalMemory.details": "Cuando
|
|
397203
|
+
"startupPersonalMemory.details": "Cuando activas la memoria personal, BLUN extrae automáticamente información útil de tus chats y puede utilizarla en conversaciones futuras. La función permanece desactivada hasta que la actives.",
|
|
397165
397204
|
"startupPersonalMemory.enable": "Activar la memoria",
|
|
397166
397205
|
"startupPersonalMemory.decline": "No activar",
|
|
397167
397206
|
"startupPersonalMemory.persistenceFailed": "No se pudo guardar la configuración de la memoria. La memoria personal permanece desactivada."
|
|
397168
397207
|
},
|
|
397169
397208
|
fr: {
|
|
397170
397209
|
"startupPersonalMemory.title": "Mémoire personnelle",
|
|
397171
|
-
"startupPersonalMemory.details": "Lorsque vous
|
|
397210
|
+
"startupPersonalMemory.details": "Lorsque vous activez la mémoire personnelle, BLUN extrait automatiquement les informations utiles de vos conversations et peut les réutiliser ultérieurement. La fonction reste désactivée tant que vous ne l’activez pas.",
|
|
397172
397211
|
"startupPersonalMemory.enable": "Activer la mémoire",
|
|
397173
397212
|
"startupPersonalMemory.decline": "Ne pas activer",
|
|
397174
397213
|
"startupPersonalMemory.persistenceFailed": "Les réglages de la mémoire n’ont pas pu être enregistrés. La mémoire personnelle reste désactivée."
|
|
397175
397214
|
},
|
|
397176
397215
|
sv: {
|
|
397177
397216
|
"startupPersonalMemory.title": "Personligt minne",
|
|
397178
|
-
"startupPersonalMemory.details": "När du
|
|
397217
|
+
"startupPersonalMemory.details": "När du aktiverar det personliga minnet hämtar BLUN automatiskt användbar information från dina chattar och kan använda den i framtida samtal. Funktionen är avstängd tills du aktiverar den.",
|
|
397179
397218
|
"startupPersonalMemory.enable": "Aktivera minnet",
|
|
397180
397219
|
"startupPersonalMemory.decline": "Aktivera inte",
|
|
397181
397220
|
"startupPersonalMemory.persistenceFailed": "Minnesinställningarna kunde inte sparas. Det personliga minnet förblir avstängt."
|
|
397182
397221
|
},
|
|
397183
397222
|
cs: {
|
|
397184
397223
|
"startupPersonalMemory.title": "Osobní paměť",
|
|
397185
|
-
"startupPersonalMemory.details": "Když
|
|
397224
|
+
"startupPersonalMemory.details": "Když zapnete osobní paměť, BLUN automaticky získává užitečné informace z vašich chatů a může je použít v budoucích konverzacích. Funkce zůstává vypnutá, dokud ji nezapnete.",
|
|
397186
397225
|
"startupPersonalMemory.enable": "Zapnout paměť",
|
|
397187
397226
|
"startupPersonalMemory.decline": "Nezapínat",
|
|
397188
397227
|
"startupPersonalMemory.persistenceFailed": "Nastavení paměti se nepodařilo uložit. Osobní paměť zůstává vypnutá."
|
|
@@ -397441,8 +397480,8 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
|
|
|
397441
397480
|
aliases: [],
|
|
397442
397481
|
descriptionKey: "startupPersonalMemory.title",
|
|
397443
397482
|
priority: 60,
|
|
397444
|
-
argumentHint: "
|
|
397445
|
-
availability: "
|
|
397483
|
+
argumentHint: "status|on|off",
|
|
397484
|
+
availability: "always"
|
|
397446
397485
|
},
|
|
397447
397486
|
{
|
|
397448
397487
|
name: "plugins",
|
|
@@ -407807,12 +407846,20 @@ function installRainbowDance(requestRender) {
|
|
|
407807
407846
|
if (currentDanceController === dance) setRainbowDance(void 0);
|
|
407808
407847
|
};
|
|
407809
407848
|
}
|
|
407849
|
+
function getRainbowDanceView() {
|
|
407850
|
+
return currentDanceView;
|
|
407851
|
+
}
|
|
407810
407852
|
function isRainbowDancing() {
|
|
407811
407853
|
return currentDanceView?.colored === true;
|
|
407812
407854
|
}
|
|
407813
407855
|
function renderDanceFooterModel(modelLabel) {
|
|
407814
407856
|
return rainbowText(modelLabel, getDanceRainbowPalette(), currentDanceView?.phase ?? 0);
|
|
407815
407857
|
}
|
|
407858
|
+
function renderDanceWelcomeWordmark(rows) {
|
|
407859
|
+
const phase = currentDanceView?.phase ?? 0;
|
|
407860
|
+
const palette = getDanceRainbowPalette();
|
|
407861
|
+
return rows.map((row, index) => rainbowText(row, palette, phase + index * 3, true));
|
|
407862
|
+
}
|
|
407816
407863
|
/**
|
|
407817
407864
|
* Drives the rainbow: a single timer advances a shared `phase` and asks the UI
|
|
407818
407865
|
* to repaint. Lives independently of any component, so the welcome banner
|
|
@@ -412627,12 +412674,12 @@ registerUiCatalogFragment({
|
|
|
412627
412674
|
//#region src/tui/utils/channel-injection.ts
|
|
412628
412675
|
/**
|
|
412629
412676
|
* Channel inbound injection into the VISIBLE interactive TUI session
|
|
412630
|
-
*
|
|
412677
|
+
* for channel delivery.
|
|
412631
412678
|
*
|
|
412632
412679
|
* A remote message (Telegram) must NEVER travel through handleUserInput():
|
|
412633
412680
|
* no slash-command dispatch (a Telegram "/new" must not open a session), no
|
|
412634
412681
|
* bash mode, no local editor history — and the transcript shows a clean user
|
|
412635
|
-
* line ("Telegram ·
|
|
412682
|
+
* line ("Telegram · User <text>") while the MODEL receives the full
|
|
412636
412683
|
* <channel …> payload (plus the channel preamble once per TUI process).
|
|
412637
412684
|
*
|
|
412638
412685
|
* This module is dependency-free on purpose: verify-telegram.ts drives the
|
|
@@ -412669,7 +412716,7 @@ function bufferContext(state, chatId, tag) {
|
|
|
412669
412716
|
while (messages.length > MAX_BUFFERED_CONTEXT_MESSAGES || chars > MAX_BUFFERED_CONTEXT_CHARS) chars -= messages.shift()?.length ?? 0;
|
|
412670
412717
|
buffers.set(chatId, messages);
|
|
412671
412718
|
}
|
|
412672
|
-
/** Muted origin prefix for the transcript line, e.g. "Telegram ·
|
|
412719
|
+
/** Muted origin prefix for the transcript line, e.g. "Telegram · User". */
|
|
412673
412720
|
function channelOrigin(envelope) {
|
|
412674
412721
|
return `Telegram · ${envelope.meta.user ?? envelope.meta.chat_id}`;
|
|
412675
412722
|
}
|
|
@@ -412768,7 +412815,7 @@ registerUiCatalogFragment({
|
|
|
412768
412815
|
/**
|
|
412769
412816
|
* Telegram channel attach mode — the visible TUI session takes over delivery.
|
|
412770
412817
|
*
|
|
412771
|
-
* Design
|
|
412818
|
+
* Design: one poller with lease routing.
|
|
412772
412819
|
* - The bridge (plugins/telegram dist/bridge.mjs) stays the ONLY getUpdates
|
|
412773
412820
|
* poller at all times. The TUI never talks to Telegram itself.
|
|
412774
412821
|
* - While this controller runs, it holds a lease: tui.pid in the channel
|
|
@@ -413228,7 +413275,7 @@ var TelegramChannelController = class {
|
|
|
413228
413275
|
for (const candidate of candidates) if (candidate !== void 0 && existsSync(candidate)) return candidate;
|
|
413229
413276
|
}
|
|
413230
413277
|
/**
|
|
413231
|
-
* The reply path must live in THIS session
|
|
413278
|
+
* The reply path must live in THIS session: without the
|
|
413232
413279
|
* telegram MCP the model can read channel messages but its answers never
|
|
413233
413280
|
* reach the chat — warn loudly instead of failing silently.
|
|
413234
413281
|
*/
|
|
@@ -416497,37 +416544,26 @@ registerUiCatalogFragment({
|
|
|
416497
416544
|
//#region src/tui/commands/memory.ts
|
|
416498
416545
|
async function handleMemoryCommand(host, args, dependencies) {
|
|
416499
416546
|
const parsed = parseMemoryCommand(args);
|
|
416500
|
-
if (parsed === void 0)
|
|
416501
|
-
|
|
416502
|
-
|
|
416503
|
-
|
|
416504
|
-
|
|
416505
|
-
|
|
416506
|
-
|
|
416507
|
-
|
|
416508
|
-
}
|
|
416509
|
-
|
|
416547
|
+
if (parsed === void 0) {
|
|
416548
|
+
host.showStatus("/memory status|on|off");
|
|
416549
|
+
return;
|
|
416550
|
+
}
|
|
416551
|
+
const client = (dependencies ?? defaultDependencies()).createClient(host);
|
|
416552
|
+
if (parsed.action === "status") {
|
|
416553
|
+
host.showStatus(`/memory ${await client.getConsentStatus()}`);
|
|
416554
|
+
return;
|
|
416555
|
+
}
|
|
416556
|
+
const enabled = parsed.action === "on";
|
|
416557
|
+
await client.setConsent(enabled);
|
|
416558
|
+
await host.refreshPersonalMemory?.(false);
|
|
416559
|
+
host.showStatus(`/memory ${enabled ? "enabled" : "disabled"}`);
|
|
416510
416560
|
}
|
|
416511
416561
|
function parseMemoryCommand(args) {
|
|
416512
|
-
const
|
|
416513
|
-
if (
|
|
416514
|
-
return {
|
|
416515
|
-
action: "remember",
|
|
416516
|
-
content
|
|
416517
|
-
};
|
|
416562
|
+
const action = args.trim().toLowerCase();
|
|
416563
|
+
if (action === "status" || action === "on" || action === "off") return { action };
|
|
416518
416564
|
}
|
|
416519
|
-
function defaultDependencies(
|
|
416520
|
-
return {
|
|
416521
|
-
remember: rememberExplicitPersonalMemory,
|
|
416522
|
-
createClient: () => {
|
|
416523
|
-
const client = createPersonalMemoryBrokerClient(host.harness);
|
|
416524
|
-
return {
|
|
416525
|
-
getSettings: () => client.getSettings(),
|
|
416526
|
-
request: (method, path, body) => client.request(method, path, body)
|
|
416527
|
-
};
|
|
416528
|
-
},
|
|
416529
|
-
getManagedQuota: () => host.harness.auth.getManagedQuota(DEFAULT_OAUTH_PROVIDER_NAME)
|
|
416530
|
-
};
|
|
416565
|
+
function defaultDependencies() {
|
|
416566
|
+
return { createClient: (host) => createAccountMemoryClient(host.harness.auth) };
|
|
416531
416567
|
}
|
|
416532
416568
|
//#endregion
|
|
416533
416569
|
//#region src/tui/utils/sanitize-mcp-display.copy.ts
|
|
@@ -417612,9 +417648,9 @@ function pluginTrustLabel(plugin) {
|
|
|
417612
417648
|
if (plugin.source !== "zip-url" || plugin.originalSource === void 0) return "third-party";
|
|
417613
417649
|
try {
|
|
417614
417650
|
const url = new URL(plugin.originalSource);
|
|
417615
|
-
if (url.protocol !== "https:"
|
|
417616
|
-
if (url
|
|
417617
|
-
if (url.pathname.startsWith("/blun/plugins/curated/")) return "curated";
|
|
417651
|
+
if (url.protocol !== "https:") return "third-party";
|
|
417652
|
+
if (isOfficialPluginUrl(url)) return "official";
|
|
417653
|
+
if (url.hostname === "blun.ai" && url.pathname.startsWith("/blun/plugins/curated/") || url.hostname === "cdn.blun.ai" && url.pathname.startsWith("/blun-king/plugins/curated/")) return "curated";
|
|
417618
417654
|
return "third-party";
|
|
417619
417655
|
} catch {
|
|
417620
417656
|
return "third-party";
|
|
@@ -417630,12 +417666,14 @@ function isOfficialPluginSource(source) {
|
|
|
417630
417666
|
const trimmed = source.trim();
|
|
417631
417667
|
if (!trimmed.startsWith("https://")) return false;
|
|
417632
417668
|
try {
|
|
417633
|
-
|
|
417634
|
-
return url.hostname === "blun.ai" && url.pathname.startsWith("/blun/plugins/official/");
|
|
417669
|
+
return isOfficialPluginUrl(new URL(trimmed));
|
|
417635
417670
|
} catch {
|
|
417636
417671
|
return false;
|
|
417637
417672
|
}
|
|
417638
417673
|
}
|
|
417674
|
+
function isOfficialPluginUrl(url) {
|
|
417675
|
+
return url.hostname === "blun.ai" && url.pathname.startsWith("/blun/plugins/official/") || url.hostname === "cdn.blun.ai" && url.pathname.startsWith("/blun-king/plugins/official/");
|
|
417676
|
+
}
|
|
417639
417677
|
function hostFromUrl(raw) {
|
|
417640
417678
|
try {
|
|
417641
417679
|
const url = new URL(raw);
|
|
@@ -422061,17 +422099,23 @@ const BLUN_GLYPHS = {
|
|
|
422061
422099
|
"█ █"
|
|
422062
422100
|
]
|
|
422063
422101
|
};
|
|
422064
|
-
function
|
|
422065
|
-
const white = chalk.bold.hex("#ffffff");
|
|
422066
|
-
const blue = chalk.bold.hex(blueHex);
|
|
422102
|
+
function blunWordmarkParts() {
|
|
422067
422103
|
const rows = [];
|
|
422068
422104
|
for (let i = 0; i < 5; i++) {
|
|
422069
422105
|
const bl = `${BLUN_GLYPHS["B"][i]} ${BLUN_GLYPHS["L"][i]}`;
|
|
422070
422106
|
const un = `${BLUN_GLYPHS["U"][i]} ${BLUN_GLYPHS["N"][i]}`;
|
|
422071
|
-
rows.push(
|
|
422107
|
+
rows.push({
|
|
422108
|
+
bl,
|
|
422109
|
+
un
|
|
422110
|
+
});
|
|
422072
422111
|
}
|
|
422073
422112
|
return rows;
|
|
422074
422113
|
}
|
|
422114
|
+
function renderBlunWordmark(blueHex) {
|
|
422115
|
+
const white = chalk.bold.hex("#ffffff");
|
|
422116
|
+
const blue = chalk.bold.hex(blueHex);
|
|
422117
|
+
return blunWordmarkParts().map(({ bl, un }) => white(bl) + " " + blue(un));
|
|
422118
|
+
}
|
|
422075
422119
|
function renderHelpLine(text, dim, command) {
|
|
422076
422120
|
const token = "/help";
|
|
422077
422121
|
const tokenIndex = text.indexOf(token);
|
|
@@ -422102,7 +422146,7 @@ var WelcomeComponent = class {
|
|
|
422102
422146
|
const pad = " ";
|
|
422103
422147
|
const dim = chalk.hex(currentTheme.palette.textDim);
|
|
422104
422148
|
const labelStyle = chalk.hex(currentTheme.palette.textDim);
|
|
422105
|
-
const wordmarkRows = renderBlunWordmark(primaryHex);
|
|
422149
|
+
const wordmarkRows = getRainbowDanceView()?.colored === true ? renderDanceWelcomeWordmark(blunWordmarkParts().map(({ bl, un }) => `${bl} ${un}`)) : renderBlunWordmark(primaryHex);
|
|
422106
422150
|
const tagline = chalk.bold.hex(currentTheme.palette.textStrong)(uiText("welcome.tagline"));
|
|
422107
422151
|
const helpLine = isLoggedOut ? chalk.hex(currentTheme.palette.warning)(uiText("welcome.start")) : renderHelpLine(uiText("welcome.help"), dim, chalk.bold.hex(primaryHex));
|
|
422108
422152
|
const directoryLabel = uiText("welcome.label.directory");
|
|
@@ -422122,7 +422166,7 @@ var WelcomeComponent = class {
|
|
|
422122
422166
|
renderLabel(directoryLabel) + chalk.hex(currentTheme.palette.text)(this.state.workDir),
|
|
422123
422167
|
renderLabel(sessionLabel) + chalk.hex(currentTheme.palette.text)(this.state.sessionId),
|
|
422124
422168
|
renderLabel(modelLabel) + chalk.bold.hex(primaryHex)(modelValue),
|
|
422125
|
-
renderLabel(versionLabel) + chalk.hex(currentTheme.palette.text)(`${PRODUCT_NAME}
|
|
422169
|
+
renderLabel(versionLabel) + chalk.hex(currentTheme.palette.text)(`${PRODUCT_NAME} ${this.state.version}`)
|
|
422126
422170
|
];
|
|
422127
422171
|
if (this.state.mcpServersSummary) infoLines.push(renderLabel(mcpLabel) + chalk.hex(currentTheme.palette.text)(this.state.mcpServersSummary));
|
|
422128
422172
|
const contentLines = [
|
|
@@ -486823,8 +486867,8 @@ var PrefixedWrappedLine = class {
|
|
|
486823
486867
|
};
|
|
486824
486868
|
/** BLUN code surface — the same colored full-width band the ActivityPane uses
|
|
486825
486869
|
* (WORK_BG #16233a), wrapped around a Write preview so code King writes sits on
|
|
486826
|
-
* a coloured background over the FULL width
|
|
486827
|
-
*
|
|
486870
|
+
* a coloured background over the FULL width. applyBackgroundToLine pads to
|
|
486871
|
+
* width and survives the inner
|
|
486828
486872
|
* syntax-highlight ANSI resets; the line count is unchanged so the pinned
|
|
486829
486873
|
* editor below never moves. */
|
|
486830
486874
|
const CODE_SURFACE_BG = "#16233a";
|
|
@@ -486832,7 +486876,7 @@ var CodeSurfaceComponent = class extends Container {
|
|
|
486832
486876
|
fixedLines;
|
|
486833
486877
|
fixedRows;
|
|
486834
486878
|
/**
|
|
486835
|
-
* Fixed mode
|
|
486879
|
+
* Fixed mode prevents the chat area from moving while tools run:
|
|
486836
486880
|
* while Write args STREAM, the preview must have a CONSTANT physical height —
|
|
486837
486881
|
* wrapped long lines and collapsing blank lines made the band grow/shrink
|
|
486838
486882
|
* every delta, which bounced the whole frame once the transcript filled the
|
|
@@ -486865,7 +486909,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
486865
486909
|
* True once the STREAMING preview rendered the fixed-height code band.
|
|
486866
486910
|
* The collapsed FINAL preview then keeps exactly that height (padded band),
|
|
486867
486911
|
* so the result landing never changes the block height — no short transcript
|
|
486868
|
-
* jump per tool call
|
|
486912
|
+
* jump per tool call.
|
|
486869
486913
|
*/
|
|
486870
486914
|
streamedFixedBand = false;
|
|
486871
486915
|
toolCall;
|
|
@@ -487041,7 +487085,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
487041
487085
|
* rief SOFORT rebuildBody+requestRender. Weil die Body-Hoehe sich dabei um
|
|
487042
487086
|
* eine Zeile aendert, verschob der BottomPinnedTUI-Filler die Eingabe fuer
|
|
487043
487087
|
* einen Frame — das sporadische 1-Zeilen-Zucken NUR waehrend Tool-Aktivitaet
|
|
487044
|
-
*
|
|
487088
|
+
* appendLiveOutput previously had the debounce
|
|
487045
487089
|
* schon; hier fehlte er. Daten sofort, Neu-Render coalesced (50ms).
|
|
487046
487090
|
*/
|
|
487047
487091
|
progressRenderTimer;
|
|
@@ -488710,9 +488754,11 @@ function webSessionUrl(origin, sessionId, token) {
|
|
|
488710
488754
|
//#endregion
|
|
488711
488755
|
//#region src/tui/commands/dispatch.ts
|
|
488712
488756
|
function dispatchInput(host, text) {
|
|
488713
|
-
|
|
488757
|
+
const parsed = parseSlashInput(text);
|
|
488758
|
+
if (parsed !== null) {
|
|
488714
488759
|
const isBusy = host.state.appState.streamingPhase !== "idle" || host.state.appState.isCompacting;
|
|
488715
|
-
|
|
488760
|
+
const canRunAlongsideMain = (parsed.name === "btw" || parsed.name === "memory") && !host.state.appState.isCompacting;
|
|
488761
|
+
if (host.deferUserMessages || isBusy && !canRunAlongsideMain) {
|
|
488716
488762
|
host.enqueueSlashCommand(text);
|
|
488717
488763
|
return Promise.resolve();
|
|
488718
488764
|
}
|
|
@@ -496055,7 +496101,7 @@ function isRateLimitError(error) {
|
|
|
496055
496101
|
/**
|
|
496056
496102
|
* F2 Tippen-mit-Vorschlag — Completion-Client.
|
|
496057
496103
|
*
|
|
496058
|
-
*
|
|
496104
|
+
* Provider-neutral since 2026-08-04.
|
|
496059
496105
|
* Vorher stand hier eine fest verdrahtete Route zu einem Fremdanbieter:
|
|
496060
496106
|
* Bei jedem Tippen ging ein Datei-Ausschnitt dorthin, ohne dass der Nutzer
|
|
496061
496107
|
* etwas abgeschickt hat. Das widersprach der Zusage, dass nichts den Rechner
|
|
@@ -497334,7 +497380,11 @@ var BottomPinnedTUI = class extends TUI {
|
|
|
497334
497380
|
}
|
|
497335
497381
|
paintSurface(lines, width) {
|
|
497336
497382
|
if (!this.surfaceEnabled) return lines;
|
|
497337
|
-
|
|
497383
|
+
try {
|
|
497384
|
+
return lines.map((line) => applyBackgroundToLine(line, width, (text) => currentTheme.bg("surface", text)));
|
|
497385
|
+
} catch {
|
|
497386
|
+
return lines;
|
|
497387
|
+
}
|
|
497338
497388
|
}
|
|
497339
497389
|
/**
|
|
497340
497390
|
* Children from this index on (editor, footer) are pinned to the bottom.
|
|
@@ -497354,7 +497404,7 @@ var BottomPinnedTUI = class extends TUI {
|
|
|
497354
497404
|
chromeFromIndex = Number.POSITIVE_INFINITY;
|
|
497355
497405
|
/**
|
|
497356
497406
|
* Rows of the pinned block that are an OVERLAY (the editor's autocomplete
|
|
497357
|
-
* menu). The menu must not shift the transcript (
|
|
497407
|
+
* menu). The menu must not shift the transcript (the
|
|
497358
497408
|
* ändert es trotzdem noch") — instead it covers the bottommost transcript
|
|
497359
497409
|
* rows: we drop that many content rows so every remaining row keeps its
|
|
497360
497410
|
* exact position while the menu is open.
|
|
@@ -497437,7 +497487,7 @@ var BottomPinnedTUI = class extends TUI {
|
|
|
497437
497487
|
const pinAt = this.pinFromIndex;
|
|
497438
497488
|
if (!Number.isFinite(pinAt) || pinAt < 0 || pinAt >= this.children.length) {
|
|
497439
497489
|
this.setMouseTracking(false);
|
|
497440
|
-
return
|
|
497490
|
+
return super.render(width);
|
|
497441
497491
|
}
|
|
497442
497492
|
try {
|
|
497443
497493
|
const blocks = this.children.map((child) => child.render(width));
|
|
@@ -507184,6 +507234,7 @@ var BlunTUI = class {
|
|
|
507184
507234
|
scrollbackController;
|
|
507185
507235
|
personalMemoryController;
|
|
507186
507236
|
accountMemory;
|
|
507237
|
+
accountMemoryEnabledForSession = false;
|
|
507187
507238
|
pendingAccountMemoryExtractions = /* @__PURE__ */ new Set();
|
|
507188
507239
|
managedQuotaWarningController;
|
|
507189
507240
|
managedQuotaWarningPersistence = Promise.resolve();
|
|
@@ -507489,7 +507540,10 @@ var BlunTUI = class {
|
|
|
507489
507540
|
}
|
|
507490
507541
|
if (this.aborted) return;
|
|
507491
507542
|
if (this.session === void 0 && this.state.startupState !== "picker") return;
|
|
507492
|
-
if (this.session !== void 0)
|
|
507543
|
+
if (this.session !== void 0) {
|
|
507544
|
+
await this.refreshPersonalMemory(true);
|
|
507545
|
+
await this.authFlow.refreshManagedQuotaWindows();
|
|
507546
|
+
}
|
|
507493
507547
|
this.showTmuxKeyboardWarningIfNeeded();
|
|
507494
507548
|
this.startTelegramChannel();
|
|
507495
507549
|
if (this.state.startupState === "picker") {
|
|
@@ -507748,8 +507802,8 @@ var BlunTUI = class {
|
|
|
507748
507802
|
ui.addChild(this.state.activityContainer);
|
|
507749
507803
|
ui.addChild(this.state.todoPanelContainer);
|
|
507750
507804
|
ui.addChild(this.state.queueContainer);
|
|
507751
|
-
ui.addChild(this.state.btwPanelContainer);
|
|
507752
507805
|
ui.addChild(this.state.quotaWarningContainer);
|
|
507806
|
+
ui.addChild(this.state.btwPanelContainer);
|
|
507753
507807
|
ui.addChild(this.state.editorContainer);
|
|
507754
507808
|
if (ui instanceof BottomPinnedTUI) {
|
|
507755
507809
|
ui.pinFromIndex = ui.children.indexOf(this.state.quotaWarningContainer);
|
|
@@ -508093,10 +508147,10 @@ var BlunTUI = class {
|
|
|
508093
508147
|
this.state.ui.requestRender();
|
|
508094
508148
|
}
|
|
508095
508149
|
/**
|
|
508096
|
-
* Remote channel inbound (Telegram) into THIS visible session
|
|
508150
|
+
* Remote channel inbound (Telegram) into THIS visible session.
|
|
508097
508151
|
* Never routes through handleUserInput: no slash/bash dispatch (a Telegram
|
|
508098
508152
|
* "/new" is just text for the model), no local editor history. The transcript
|
|
508099
|
-
* shows a clean user line with a muted origin prefix
|
|
508153
|
+
* shows a clean user line with a muted origin prefix; the
|
|
508100
508154
|
* model receives the full <channel …> payload. Busy turns queue via the same
|
|
508101
508155
|
* queuedMessages mechanic as typed input; a completed tool/step boundary
|
|
508102
508156
|
* steers one FIFO head into the active turn without interrupting it.
|
|
@@ -508522,12 +508576,34 @@ var BlunTUI = class {
|
|
|
508522
508576
|
refreshManagedQuotaWindows() {
|
|
508523
508577
|
this.authFlow.refreshManagedQuotaWindows();
|
|
508524
508578
|
}
|
|
508525
|
-
async refreshPersonalMemory(
|
|
508526
|
-
|
|
508579
|
+
async refreshPersonalMemory(promptIfNeverAsked = false) {
|
|
508580
|
+
const session = this.session;
|
|
508581
|
+
if (session === void 0) return;
|
|
508582
|
+
this.accountMemoryEnabledForSession = false;
|
|
508583
|
+
try {
|
|
508584
|
+
let consentStatus = await this.accountMemory.getConsentStatus();
|
|
508585
|
+
if (consentStatus === "never_asked" && promptIfNeverAsked) {
|
|
508586
|
+
const consent = await ensureStartupPersonalMemoryConsent({
|
|
508587
|
+
host: this,
|
|
508588
|
+
consentStatus,
|
|
508589
|
+
copy: startupPersonalMemoryConsentCopy(),
|
|
508590
|
+
updateSettings: async (patch) => {
|
|
508591
|
+
await this.accountMemory.setConsent(patch.memory_enabled === true);
|
|
508592
|
+
}
|
|
508593
|
+
});
|
|
508594
|
+
if (consent === "persistence-failed") this.showStatus(startupPersonalMemoryConsentCopy().persistenceFailed, "warning");
|
|
508595
|
+
consentStatus = consent === "enabled" || consent === "disabled" ? consent : "never_asked";
|
|
508596
|
+
}
|
|
508597
|
+
this.accountMemoryEnabledForSession = consentStatus === "enabled";
|
|
508598
|
+
} catch (error) {
|
|
508599
|
+
this.reportAccountMemoryIssue("Account memory consent unavailable", error);
|
|
508600
|
+
}
|
|
508601
|
+
await this.applyAccountMemoryContext(session);
|
|
508527
508602
|
}
|
|
508528
508603
|
async applyAccountMemoryContext(session) {
|
|
508529
508604
|
try {
|
|
508530
|
-
|
|
508605
|
+
const context = this.accountMemoryEnabledForSession ? await this.accountMemory.loadSystemContext() : void 0;
|
|
508606
|
+
await session.setAccountMemoryContext(context);
|
|
508531
508607
|
} catch (error) {
|
|
508532
508608
|
try {
|
|
508533
508609
|
await session.setAccountMemoryContext(void 0);
|
|
@@ -508536,7 +508612,7 @@ var BlunTUI = class {
|
|
|
508536
508612
|
}
|
|
508537
508613
|
}
|
|
508538
508614
|
extractAccountMemory(text) {
|
|
508539
|
-
if (text.trim().length === 0) return;
|
|
508615
|
+
if (!this.accountMemoryEnabledForSession || text.trim().length === 0) return;
|
|
508540
508616
|
let pending;
|
|
508541
508617
|
pending = this.accountMemory.extract(text).catch((error) => {
|
|
508542
508618
|
this.reportAccountMemoryIssue("Account memory update failed", error);
|
|
@@ -509464,7 +509540,7 @@ var BlunTUI = class {
|
|
|
509464
509540
|
/**
|
|
509465
509541
|
* Presence label next to the thinking spinner — uses the persona name the
|
|
509466
509542
|
* user gave the agent, falling back to the model name "King". E.g. "Sven
|
|
509467
|
-
* denkt…" / "King arbeitet…"
|
|
509543
|
+
* denkt…" / "King arbeitet…" persona status line.
|
|
509468
509544
|
*/
|
|
509469
509545
|
personaVerbLabel(mode) {
|
|
509470
509546
|
return uiText(mode === "thinking" ? "blunTui.activity.thinking" : "blunTui.activity.working", { name: personaName() ?? "King" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blun-king-cli",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.19",
|
|
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": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"x64",
|
|
23
23
|
"arm64"
|
|
24
24
|
],
|
|
25
|
+
"dependencies": {},
|
|
25
26
|
"optionalDependencies": {
|
|
26
27
|
"@mariozechner/clipboard": "^0.3.9",
|
|
27
28
|
"node-pty": "^1.1.0"
|
|
@@ -44,5 +45,5 @@
|
|
|
44
45
|
"cli",
|
|
45
46
|
"assistant"
|
|
46
47
|
],
|
|
47
|
-
"author": "BLUN
|
|
48
|
+
"author": "BLUN"
|
|
48
49
|
}
|
|
@@ -67311,7 +67311,7 @@ function normalize(s) {
|
|
|
67311
67311
|
}
|
|
67312
67312
|
const STRONG_META = /\b(keine (gruppen ?)?antwort|gar keine antwort|halte (mich|die klappe)|(bin|bleibe) (jetzt )?still|ich schweige|schweige|stille|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht (direkt )?an mich( gerichtet)?|nicht meine lane|keine reaktion|no ?action|kein wort|silence|staying (silent|out|quiet)|keeping (quiet|out)|no (response|reply|answer|comment)|nothing (to (add|do|report)|for me)|not (for me|my lane|addressed to me|directed at me)|holding back|standing by|no need to (respond|reply))\b/;
|
|
67313
67313
|
const SHORT_CONFIRM = /^(verstanden|kapiert|ok|okay|alles klar|geht klar|angekommen|seh sie|ich seh sie|gemerkt|passt|erledigt notiert|notiert|no ?action|understood|got it|noted|acknowledged|roger|received|silence|still)\b[\s\S]{0,48}$/;
|
|
67314
|
-
const PRESENCE_ONLY = /^(
|
|
67314
|
+
const PRESENCE_ONLY = /^(bin +da|bin +hier|hier +bin +ich|ich +bin +da|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|freut +mich|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
|
|
67315
67315
|
/**
|
|
67316
67316
|
* True when this reply text, sent into a GROUP, is pure meta/acknowledgement
|
|
67317
67317
|
* noise that must not be delivered. Length-guarded so genuine longer answers
|
|
@@ -73467,7 +73467,7 @@ function normalize(s) {
|
|
|
73467
73467
|
}
|
|
73468
73468
|
const STRONG_META = /\b(keine (gruppen ?)?antwort|gar keine antwort|halte (mich|die klappe)|(bin|bleibe) (jetzt )?still|ich schweige|schweige|stille|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht (direkt )?an mich( gerichtet)?|nicht meine lane|keine reaktion|no ?action|kein wort|silence|staying (silent|out|quiet)|keeping (quiet|out)|no (response|reply|answer|comment)|nothing (to (add|do|report)|for me)|not (for me|my lane|addressed to me|directed at me)|holding back|standing by|no need to (respond|reply))\b/;
|
|
73469
73469
|
const SHORT_CONFIRM = /^(verstanden|kapiert|ok|okay|alles klar|geht klar|angekommen|seh sie|ich seh sie|gemerkt|passt|erledigt notiert|notiert|no ?action|understood|got it|noted|acknowledged|roger|received|silence|still)\b[\s\S]{0,48}$/;
|
|
73470
|
-
const PRESENCE_ONLY = /^(
|
|
73470
|
+
const PRESENCE_ONLY = /^(bin +da|bin +hier|hier +bin +ich|ich +bin +da|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|freut +mich|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
|
|
73471
73471
|
/**
|
|
73472
73472
|
* True when this reply text, sent into a GROUP, is pure meta/acknowledgement
|
|
73473
73473
|
* noise that must not be delivered. Length-guarded so genuine longer answers
|
|
@@ -22,7 +22,7 @@ function normalize(s) {
|
|
|
22
22
|
}
|
|
23
23
|
const STRONG_META = /\b(keine (gruppen ?)?antwort|gar keine antwort|halte (mich|die klappe)|(bin|bleibe) (jetzt )?still|ich schweige|schweige|stille|nichts (zu tun|zu melden|fur mich|fuer mich)|nicht (direkt )?an mich( gerichtet)?|nicht meine lane|keine reaktion|no ?action|kein wort|silence|staying (silent|out|quiet)|keeping (quiet|out)|no (response|reply|answer|comment)|nothing (to (add|do|report)|for me)|not (for me|my lane|addressed to me|directed at me)|holding back|standing by|no need to (respond|reply))\b/;
|
|
24
24
|
const SHORT_CONFIRM = /^(verstanden|kapiert|ok|okay|alles klar|geht klar|angekommen|seh sie|ich seh sie|gemerkt|passt|erledigt notiert|notiert|no ?action|understood|got it|noted|acknowledged|roger|received|silence|still)\b[\s\S]{0,48}$/;
|
|
25
|
-
const PRESENCE_ONLY = /^(
|
|
25
|
+
const PRESENCE_ONLY = /^(bin +da|bin +hier|hier +bin +ich|ich +bin +da|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|freut +mich|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
|
|
26
26
|
/**
|
|
27
27
|
* True when this reply text, sent into a GROUP, is pure meta/acknowledgement
|
|
28
28
|
* noise that must not be delivered. Length-guarded so genuine longer answers
|