blun-king-cli 9.1.16 → 9.1.18

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 CHANGED
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
9
9
  installiert:
10
10
 
11
11
  ```powershell
12
- npm install -g blun-king-cli@9.1.1
12
+ npm install -g blun-king-cli@9.1.18
13
13
  ```
14
14
 
15
15
  ## Reproduzierbares Staging und Packen
package/bin/blun.js CHANGED
File without changes
File without changes
package/bin/king.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/blun.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:39e4b4359446de9a47446e631b853adaafc78c8097e76296c6826ec91ee9902d
2
+ // BLUN_BUILD_INPUT_SHA256:e2258991ea4b7ce19aa0b6147dfc25e8b2f1945acc329856a2a2d00ad594a825
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 (Mayk/Dieter/Angel
74398
- * 03.08.2026): instead of a fabricated percentage, the compaction indicator
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,7 @@ 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";
336450
336456
  const ACCOUNT_MEMORY_MAX_FACT_CHARS = 1e3;
336451
336457
  //#endregion
336452
336458
  //#region src/utils/account-memory.ts
@@ -336464,6 +336470,19 @@ function createAccountMemoryClient(auth, options = {}) {
336464
336470
  const fetchImpl = options.fetch ?? globalThis.fetch;
336465
336471
  const timeoutMs = options.timeoutMs ?? 4e3;
336466
336472
  return {
336473
+ async getConsentStatus() {
336474
+ return readAccountMemoryConsentStatus(await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_PREFERENCES_URL, { method: "GET" }, timeoutMs, readBoundedJson));
336475
+ },
336476
+ async setConsent(enabled) {
336477
+ if (readAccountMemoryConsentStatus(await requestWithOAuth(auth, fetchImpl, "https://account.blun.ai/api/account/settings", {
336478
+ method: "PATCH",
336479
+ headers: { "Content-Type": "application/json" },
336480
+ body: JSON.stringify({ data_controls: {
336481
+ allow_memory: enabled,
336482
+ memory_consent_asked: true
336483
+ } })
336484
+ }, timeoutMs, readBoundedJson)) !== (enabled ? "enabled" : "disabled")) throw new Error("Account memory returned an invalid response.");
336485
+ },
336467
336486
  async loadSystemContext() {
336468
336487
  return renderAccountMemoryContext(await requestWithOAuth(auth, fetchImpl, ACCOUNT_MEMORY_READ_URL, { method: "GET" }, timeoutMs, readBoundedJson));
336469
336488
  },
@@ -336476,6 +336495,15 @@ function createAccountMemoryClient(auth, options = {}) {
336476
336495
  }
336477
336496
  };
336478
336497
  }
336498
+ function readAccountMemoryConsentStatus(payload) {
336499
+ const settings = unwrapPayload(payload)?.["settings"];
336500
+ const dataControls = isRecord$13(settings) ? settings["data_controls"] : void 0;
336501
+ if (!isRecord$13(dataControls)) throw new Error("Account memory returned an invalid response.");
336502
+ if (dataControls["memory_consent_asked"] !== true) return "never_asked";
336503
+ if (dataControls["allow_memory"] === true) return "enabled";
336504
+ if (dataControls["allow_memory"] === false) return "disabled";
336505
+ throw new Error("Account memory returned an invalid response.");
336506
+ }
336479
336507
  async function requestWithOAuth(auth, fetchImpl, url, init, timeoutMs, consume) {
336480
336508
  const controller = new AbortController();
336481
336509
  let timeout;
@@ -407807,12 +407835,20 @@ function installRainbowDance(requestRender) {
407807
407835
  if (currentDanceController === dance) setRainbowDance(void 0);
407808
407836
  };
407809
407837
  }
407838
+ function getRainbowDanceView() {
407839
+ return currentDanceView;
407840
+ }
407810
407841
  function isRainbowDancing() {
407811
407842
  return currentDanceView?.colored === true;
407812
407843
  }
407813
407844
  function renderDanceFooterModel(modelLabel) {
407814
407845
  return rainbowText(modelLabel, getDanceRainbowPalette(), currentDanceView?.phase ?? 0);
407815
407846
  }
407847
+ function renderDanceWelcomeWordmark(rows) {
407848
+ const phase = currentDanceView?.phase ?? 0;
407849
+ const palette = getDanceRainbowPalette();
407850
+ return rows.map((row, index) => rainbowText(row, palette, phase + index * 3, true));
407851
+ }
407816
407852
  /**
407817
407853
  * Drives the rainbow: a single timer advances a shared `phase` and asks the UI
407818
407854
  * to repaint. Lives independently of any component, so the welcome banner
@@ -412627,12 +412663,12 @@ registerUiCatalogFragment({
412627
412663
  //#region src/tui/utils/channel-injection.ts
412628
412664
  /**
412629
412665
  * Channel inbound injection into the VISIBLE interactive TUI session
412630
- * (Otto gate 49 / Angel spec 51).
412666
+ * for channel delivery.
412631
412667
  *
412632
412668
  * A remote message (Telegram) must NEVER travel through handleUserInput():
412633
412669
  * no slash-command dispatch (a Telegram "/new" must not open a session), no
412634
412670
  * bash mode, no local editor history — and the transcript shows a clean user
412635
- * line ("Telegram · Mayk <text>") while the MODEL receives the full
412671
+ * line ("Telegram · User <text>") while the MODEL receives the full
412636
412672
  * <channel …> payload (plus the channel preamble once per TUI process).
412637
412673
  *
412638
412674
  * This module is dependency-free on purpose: verify-telegram.ts drives the
@@ -412669,7 +412705,7 @@ function bufferContext(state, chatId, tag) {
412669
412705
  while (messages.length > MAX_BUFFERED_CONTEXT_MESSAGES || chars > MAX_BUFFERED_CONTEXT_CHARS) chars -= messages.shift()?.length ?? 0;
412670
412706
  buffers.set(chatId, messages);
412671
412707
  }
412672
- /** Muted origin prefix for the transcript line, e.g. "Telegram · Mayk". */
412708
+ /** Muted origin prefix for the transcript line, e.g. "Telegram · User". */
412673
412709
  function channelOrigin(envelope) {
412674
412710
  return `Telegram · ${envelope.meta.user ?? envelope.meta.chat_id}`;
412675
412711
  }
@@ -412768,7 +412804,7 @@ registerUiCatalogFragment({
412768
412804
  /**
412769
412805
  * Telegram channel attach mode — the visible TUI session takes over delivery.
412770
412806
  *
412771
- * Design (Otto gate 49, "ein Poller, Lease-Routing"):
412807
+ * Design: one poller with lease routing.
412772
412808
  * - The bridge (plugins/telegram dist/bridge.mjs) stays the ONLY getUpdates
412773
412809
  * poller at all times. The TUI never talks to Telegram itself.
412774
412810
  * - While this controller runs, it holds a lease: tui.pid in the channel
@@ -413228,7 +413264,7 @@ var TelegramChannelController = class {
413228
413264
  for (const candidate of candidates) if (candidate !== void 0 && existsSync(candidate)) return candidate;
413229
413265
  }
413230
413266
  /**
413231
- * The reply path must live in THIS session (Otto gate 49): without the
413267
+ * The reply path must live in THIS session: without the
413232
413268
  * telegram MCP the model can read channel messages but its answers never
413233
413269
  * reach the chat — warn loudly instead of failing silently.
413234
413270
  */
@@ -422061,17 +422097,23 @@ const BLUN_GLYPHS = {
422061
422097
  "█ █"
422062
422098
  ]
422063
422099
  };
422064
- function renderBlunWordmark(blueHex) {
422065
- const white = chalk.bold.hex("#ffffff");
422066
- const blue = chalk.bold.hex(blueHex);
422100
+ function blunWordmarkParts() {
422067
422101
  const rows = [];
422068
422102
  for (let i = 0; i < 5; i++) {
422069
422103
  const bl = `${BLUN_GLYPHS["B"][i]} ${BLUN_GLYPHS["L"][i]}`;
422070
422104
  const un = `${BLUN_GLYPHS["U"][i]} ${BLUN_GLYPHS["N"][i]}`;
422071
- rows.push(white(bl) + " " + blue(un));
422105
+ rows.push({
422106
+ bl,
422107
+ un
422108
+ });
422072
422109
  }
422073
422110
  return rows;
422074
422111
  }
422112
+ function renderBlunWordmark(blueHex) {
422113
+ const white = chalk.bold.hex("#ffffff");
422114
+ const blue = chalk.bold.hex(blueHex);
422115
+ return blunWordmarkParts().map(({ bl, un }) => white(bl) + " " + blue(un));
422116
+ }
422075
422117
  function renderHelpLine(text, dim, command) {
422076
422118
  const token = "/help";
422077
422119
  const tokenIndex = text.indexOf(token);
@@ -422102,7 +422144,7 @@ var WelcomeComponent = class {
422102
422144
  const pad = " ";
422103
422145
  const dim = chalk.hex(currentTheme.palette.textDim);
422104
422146
  const labelStyle = chalk.hex(currentTheme.palette.textDim);
422105
- const wordmarkRows = renderBlunWordmark(primaryHex);
422147
+ const wordmarkRows = getRainbowDanceView()?.colored === true ? renderDanceWelcomeWordmark(blunWordmarkParts().map(({ bl, un }) => `${bl} ${un}`)) : renderBlunWordmark(primaryHex);
422106
422148
  const tagline = chalk.bold.hex(currentTheme.palette.textStrong)(uiText("welcome.tagline"));
422107
422149
  const helpLine = isLoggedOut ? chalk.hex(currentTheme.palette.warning)(uiText("welcome.start")) : renderHelpLine(uiText("welcome.help"), dim, chalk.bold.hex(primaryHex));
422108
422150
  const directoryLabel = uiText("welcome.label.directory");
@@ -422122,7 +422164,7 @@ var WelcomeComponent = class {
422122
422164
  renderLabel(directoryLabel) + chalk.hex(currentTheme.palette.text)(this.state.workDir),
422123
422165
  renderLabel(sessionLabel) + chalk.hex(currentTheme.palette.text)(this.state.sessionId),
422124
422166
  renderLabel(modelLabel) + chalk.bold.hex(primaryHex)(modelValue),
422125
- renderLabel(versionLabel) + chalk.hex(currentTheme.palette.text)(`${PRODUCT_NAME} 1.0`)
422167
+ renderLabel(versionLabel) + chalk.hex(currentTheme.palette.text)(`${PRODUCT_NAME} ${this.state.version}`)
422126
422168
  ];
422127
422169
  if (this.state.mcpServersSummary) infoLines.push(renderLabel(mcpLabel) + chalk.hex(currentTheme.palette.text)(this.state.mcpServersSummary));
422128
422170
  const contentLines = [
@@ -486823,8 +486865,8 @@ var PrefixedWrappedLine = class {
486823
486865
  };
486824
486866
  /** BLUN code surface — the same colored full-width band the ActivityPane uses
486825
486867
  * (WORK_BG #16233a), wrapped around a Write preview so code King writes sits on
486826
- * a coloured background over the FULL width (Papa 05.07: "der bunte Hintergrund
486827
- * beim Coden"). applyBackgroundToLine pads to width and survives the inner
486868
+ * a coloured background over the FULL width. applyBackgroundToLine pads to
486869
+ * width and survives the inner
486828
486870
  * syntax-highlight ANSI resets; the line count is unchanged so the pinned
486829
486871
  * editor below never moves. */
486830
486872
  const CODE_SURFACE_BG = "#16233a";
@@ -486832,7 +486874,7 @@ var CodeSurfaceComponent = class extends Container {
486832
486874
  fixedLines;
486833
486875
  fixedRows;
486834
486876
  /**
486835
- * Fixed mode (Papa 05.07: "chatfenster wandert auf und ab beim coden"):
486877
+ * Fixed mode prevents the chat area from moving while tools run:
486836
486878
  * while Write args STREAM, the preview must have a CONSTANT physical height —
486837
486879
  * wrapped long lines and collapsing blank lines made the band grow/shrink
486838
486880
  * every delta, which bounced the whole frame once the transcript filled the
@@ -486865,7 +486907,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
486865
486907
  * True once the STREAMING preview rendered the fixed-height code band.
486866
486908
  * The collapsed FINAL preview then keeps exactly that height (padded band),
486867
486909
  * so the result landing never changes the block height — no short transcript
486868
- * jump per tool call (Papa 05.07: "bleibt manchmal kurz nicht stehen").
486910
+ * jump per tool call.
486869
486911
  */
486870
486912
  streamedFixedBand = false;
486871
486913
  toolCall;
@@ -487041,7 +487083,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
487041
487083
  * rief SOFORT rebuildBody+requestRender. Weil die Body-Hoehe sich dabei um
487042
487084
  * eine Zeile aendert, verschob der BottomPinnedTUI-Filler die Eingabe fuer
487043
487085
  * einen Frame — das sporadische 1-Zeilen-Zucken NUR waehrend Tool-Aktivitaet
487044
- * (Papa: "das passiert nicht immer"). appendLiveOutput hatte den Debounce
487086
+ * appendLiveOutput previously had the debounce
487045
487087
  * schon; hier fehlte er. Daten sofort, Neu-Render coalesced (50ms).
487046
487088
  */
487047
487089
  progressRenderTimer;
@@ -496055,7 +496097,7 @@ function isRateLimitError(error) {
496055
496097
  /**
496056
496098
  * F2 Tippen-mit-Vorschlag — Completion-Client.
496057
496099
  *
496058
- * ANBIETERNEUTRAL seit 04.08.2026 (Papa: "Wir bauen alles selbst").
496100
+ * Provider-neutral since 2026-08-04.
496059
496101
  * Vorher stand hier eine fest verdrahtete Route zu einem Fremdanbieter:
496060
496102
  * Bei jedem Tippen ging ein Datei-Ausschnitt dorthin, ohne dass der Nutzer
496061
496103
  * etwas abgeschickt hat. Das widersprach der Zusage, dass nichts den Rechner
@@ -497334,7 +497376,11 @@ var BottomPinnedTUI = class extends TUI {
497334
497376
  }
497335
497377
  paintSurface(lines, width) {
497336
497378
  if (!this.surfaceEnabled) return lines;
497337
- return lines.map((line) => applyBackgroundToLine(line, width, (text) => currentTheme.bg("surface", text)));
497379
+ try {
497380
+ return lines.map((line) => applyBackgroundToLine(line, width, (text) => currentTheme.bg("surface", text)));
497381
+ } catch {
497382
+ return lines;
497383
+ }
497338
497384
  }
497339
497385
  /**
497340
497386
  * Children from this index on (editor, footer) are pinned to the bottom.
@@ -497354,7 +497400,7 @@ var BottomPinnedTUI = class extends TUI {
497354
497400
  chromeFromIndex = Number.POSITIVE_INFINITY;
497355
497401
  /**
497356
497402
  * Rows of the pinned block that are an OVERLAY (the editor's autocomplete
497357
- * menu). The menu must not shift the transcript (Papa 05.07: "aber / das
497403
+ * menu). The menu must not shift the transcript (the
497358
497404
  * ändert es trotzdem noch") — instead it covers the bottommost transcript
497359
497405
  * rows: we drop that many content rows so every remaining row keeps its
497360
497406
  * exact position while the menu is open.
@@ -497437,7 +497483,7 @@ var BottomPinnedTUI = class extends TUI {
497437
497483
  const pinAt = this.pinFromIndex;
497438
497484
  if (!Number.isFinite(pinAt) || pinAt < 0 || pinAt >= this.children.length) {
497439
497485
  this.setMouseTracking(false);
497440
- return this.paintSurface(super.render(width), width);
497486
+ return super.render(width);
497441
497487
  }
497442
497488
  try {
497443
497489
  const blocks = this.children.map((child) => child.render(width));
@@ -507184,6 +507230,7 @@ var BlunTUI = class {
507184
507230
  scrollbackController;
507185
507231
  personalMemoryController;
507186
507232
  accountMemory;
507233
+ accountMemoryEnabledForSession = false;
507187
507234
  pendingAccountMemoryExtractions = /* @__PURE__ */ new Set();
507188
507235
  managedQuotaWarningController;
507189
507236
  managedQuotaWarningPersistence = Promise.resolve();
@@ -507489,7 +507536,10 @@ var BlunTUI = class {
507489
507536
  }
507490
507537
  if (this.aborted) return;
507491
507538
  if (this.session === void 0 && this.state.startupState !== "picker") return;
507492
- if (this.session !== void 0) await this.applyAccountMemoryContext(this.session);
507539
+ if (this.session !== void 0) {
507540
+ await this.refreshPersonalMemory(true);
507541
+ await this.authFlow.refreshManagedQuotaWindows();
507542
+ }
507493
507543
  this.showTmuxKeyboardWarningIfNeeded();
507494
507544
  this.startTelegramChannel();
507495
507545
  if (this.state.startupState === "picker") {
@@ -508093,10 +508143,10 @@ var BlunTUI = class {
508093
508143
  this.state.ui.requestRender();
508094
508144
  }
508095
508145
  /**
508096
- * Remote channel inbound (Telegram) into THIS visible session — Otto gate 49.
508146
+ * Remote channel inbound (Telegram) into THIS visible session.
508097
508147
  * Never routes through handleUserInput: no slash/bash dispatch (a Telegram
508098
508148
  * "/new" is just text for the model), no local editor history. The transcript
508099
- * shows a clean user line with a muted origin prefix (Angel spec 51); the
508149
+ * shows a clean user line with a muted origin prefix; the
508100
508150
  * model receives the full <channel …> payload. Busy turns queue via the same
508101
508151
  * queuedMessages mechanic as typed input; a completed tool/step boundary
508102
508152
  * steers one FIFO head into the active turn without interrupting it.
@@ -508522,12 +508572,34 @@ var BlunTUI = class {
508522
508572
  refreshManagedQuotaWindows() {
508523
508573
  this.authFlow.refreshManagedQuotaWindows();
508524
508574
  }
508525
- async refreshPersonalMemory(_promptIfNeverAsked = false) {
508526
- if (this.session !== void 0) await this.applyAccountMemoryContext(this.session);
508575
+ async refreshPersonalMemory(promptIfNeverAsked = false) {
508576
+ const session = this.session;
508577
+ if (session === void 0) return;
508578
+ this.accountMemoryEnabledForSession = false;
508579
+ try {
508580
+ let consentStatus = await this.accountMemory.getConsentStatus();
508581
+ if (consentStatus === "never_asked" && promptIfNeverAsked) {
508582
+ const consent = await ensureStartupPersonalMemoryConsent({
508583
+ host: this,
508584
+ consentStatus,
508585
+ copy: startupPersonalMemoryConsentCopy(),
508586
+ updateSettings: async (patch) => {
508587
+ await this.accountMemory.setConsent(patch.memory_enabled === true);
508588
+ }
508589
+ });
508590
+ if (consent === "persistence-failed") this.showStatus(startupPersonalMemoryConsentCopy().persistenceFailed, "warning");
508591
+ consentStatus = consent === "enabled" || consent === "disabled" ? consent : "never_asked";
508592
+ }
508593
+ this.accountMemoryEnabledForSession = consentStatus === "enabled";
508594
+ } catch (error) {
508595
+ this.reportAccountMemoryIssue("Account memory consent unavailable", error);
508596
+ }
508597
+ await this.applyAccountMemoryContext(session);
508527
508598
  }
508528
508599
  async applyAccountMemoryContext(session) {
508529
508600
  try {
508530
- await session.setAccountMemoryContext(await this.accountMemory.loadSystemContext());
508601
+ const context = this.accountMemoryEnabledForSession ? await this.accountMemory.loadSystemContext() : void 0;
508602
+ await session.setAccountMemoryContext(context);
508531
508603
  } catch (error) {
508532
508604
  try {
508533
508605
  await session.setAccountMemoryContext(void 0);
@@ -508536,7 +508608,7 @@ var BlunTUI = class {
508536
508608
  }
508537
508609
  }
508538
508610
  extractAccountMemory(text) {
508539
- if (text.trim().length === 0) return;
508611
+ if (!this.accountMemoryEnabledForSession || text.trim().length === 0) return;
508540
508612
  let pending;
508541
508613
  pending = this.accountMemory.extract(text).catch((error) => {
508542
508614
  this.reportAccountMemoryIssue("Account memory update failed", error);
@@ -509464,7 +509536,7 @@ var BlunTUI = class {
509464
509536
  /**
509465
509537
  * Presence label next to the thinking spinner — uses the persona name the
509466
509538
  * user gave the agent, falling back to the model name "King". E.g. "Sven
509467
- * denkt…" / "King arbeitet…". Angel's persona statusline spec.
509539
+ * denkt…" / "King arbeitet…" persona status line.
509468
509540
  */
509469
509541
  personaVerbLabel(mode) {
509470
509542
  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.16",
3
+ "version": "9.1.18",
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 / Mayk Biletti"
48
+ "author": "BLUN"
48
49
  }
@@ -59318,13 +59318,32 @@ function defaultAccess() {
59318
59318
  pending: {}
59319
59319
  };
59320
59320
  }
59321
+ function normalizeIdList(value) {
59322
+ if (!Array.isArray(value)) return [];
59323
+ return [...new Set(value.filter((entry) => typeof entry === "string" || typeof entry === "number").map((entry) => String(entry).trim()).filter((entry) => entry.length > 0))];
59324
+ }
59325
+ function normalizeGroups(value) {
59326
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
59327
+ const groups = {};
59328
+ for (const [chatId, rawPolicy] of Object.entries(value)) {
59329
+ if (rawPolicy === null || typeof rawPolicy !== "object" || Array.isArray(rawPolicy)) continue;
59330
+ const policy = rawPolicy;
59331
+ const alwaysAllowFrom = normalizeIdList(policy.alwaysAllowFrom);
59332
+ groups[chatId] = {
59333
+ requireMention: policy.requireMention !== false,
59334
+ allowFrom: normalizeIdList(policy.allowFrom),
59335
+ ...alwaysAllowFrom.length > 0 ? { alwaysAllowFrom } : {}
59336
+ };
59337
+ }
59338
+ return groups;
59339
+ }
59321
59340
  function readAccess() {
59322
59341
  try {
59323
59342
  const parsed = JSON.parse(readFileSync(accessFile(), "utf8"));
59324
59343
  return {
59325
59344
  dmPolicy: parsed.dmPolicy ?? "pairing",
59326
- allowFrom: parsed.allowFrom ?? [],
59327
- groups: parsed.groups ?? {},
59345
+ allowFrom: normalizeIdList(parsed.allowFrom),
59346
+ groups: normalizeGroups(parsed.groups),
59328
59347
  pending: parsed.pending ?? {},
59329
59348
  mentionPatterns: parsed.mentionPatterns,
59330
59349
  ignorePatterns: parsed.ignorePatterns,
@@ -59333,8 +59352,8 @@ function readAccess() {
59333
59352
  textChunkLimit: parsed.textChunkLimit,
59334
59353
  chunkMode: parsed.chunkMode
59335
59354
  };
59336
- } catch (err) {
59337
- if (err.code === "ENOENT") return defaultAccess();
59355
+ } catch (error) {
59356
+ if (error.code === "ENOENT") return defaultAccess();
59338
59357
  try {
59339
59358
  renameSync(accessFile(), `${accessFile()}.corrupt-${Date.now()}`);
59340
59359
  } catch {}
@@ -67292,7 +67311,7 @@ function normalize(s) {
67292
67311
  }
67293
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/;
67294
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}$/;
67295
- const PRESENCE_ONLY = /^(ja +papa|hier +papa|bin +da|bin +hier|hier +bin +ich|ich +bin +da|hi +papa|hallo +papa|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|danke +papa|freut +mich|yes +papa|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
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}$/;
67296
67315
  /**
67297
67316
  * True when this reply text, sent into a GROUP, is pure meta/acknowledgement
67298
67317
  * noise that must not be delivered. Length-guarded so genuine longer answers
@@ -73135,13 +73135,32 @@ function defaultAccess() {
73135
73135
  pending: {}
73136
73136
  };
73137
73137
  }
73138
+ function normalizeIdList(value) {
73139
+ if (!Array.isArray(value)) return [];
73140
+ return [...new Set(value.filter((entry) => typeof entry === "string" || typeof entry === "number").map((entry) => String(entry).trim()).filter((entry) => entry.length > 0))];
73141
+ }
73142
+ function normalizeGroups(value) {
73143
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
73144
+ const groups = {};
73145
+ for (const [chatId, rawPolicy] of Object.entries(value)) {
73146
+ if (rawPolicy === null || typeof rawPolicy !== "object" || Array.isArray(rawPolicy)) continue;
73147
+ const policy = rawPolicy;
73148
+ const alwaysAllowFrom = normalizeIdList(policy.alwaysAllowFrom);
73149
+ groups[chatId] = {
73150
+ requireMention: policy.requireMention !== false,
73151
+ allowFrom: normalizeIdList(policy.allowFrom),
73152
+ ...alwaysAllowFrom.length > 0 ? { alwaysAllowFrom } : {}
73153
+ };
73154
+ }
73155
+ return groups;
73156
+ }
73138
73157
  function readAccess() {
73139
73158
  try {
73140
73159
  const parsed = JSON.parse(readFileSync(accessFile(), "utf8"));
73141
73160
  return {
73142
73161
  dmPolicy: parsed.dmPolicy ?? "pairing",
73143
- allowFrom: parsed.allowFrom ?? [],
73144
- groups: parsed.groups ?? {},
73162
+ allowFrom: normalizeIdList(parsed.allowFrom),
73163
+ groups: normalizeGroups(parsed.groups),
73145
73164
  pending: parsed.pending ?? {},
73146
73165
  mentionPatterns: parsed.mentionPatterns,
73147
73166
  ignorePatterns: parsed.ignorePatterns,
@@ -73150,8 +73169,8 @@ function readAccess() {
73150
73169
  textChunkLimit: parsed.textChunkLimit,
73151
73170
  chunkMode: parsed.chunkMode
73152
73171
  };
73153
- } catch (err) {
73154
- if (err.code === "ENOENT") return defaultAccess();
73172
+ } catch (error) {
73173
+ if (error.code === "ENOENT") return defaultAccess();
73155
73174
  try {
73156
73175
  renameSync(accessFile(), `${accessFile()}.corrupt-${Date.now()}`);
73157
73176
  } catch {}
@@ -73448,7 +73467,7 @@ function normalize(s) {
73448
73467
  }
73449
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/;
73450
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}$/;
73451
- const PRESENCE_ONLY = /^(ja +papa|hier +papa|bin +da|bin +hier|hier +bin +ich|ich +bin +da|hi +papa|hallo +papa|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|danke +papa|freut +mich|yes +papa|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
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}$/;
73452
73471
  /**
73453
73472
  * True when this reply text, sent into a GROUP, is pure meta/acknowledgement
73454
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 = /^(ja +papa|hier +papa|bin +da|bin +hier|hier +bin +ich|ich +bin +da|hi +papa|hallo +papa|was +brauchst +du|was +gibt +es|was +liegt +an|wie +kann +ich +helfen|bereit|ich +warte|warte +auf|melde +mich|danke +papa|freut +mich|yes +papa|here|standing +by|ready|awaiting|what +do +you +need)\b[\s\S]{0,40}$/;
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