@deeplake/hivemind 0.7.65 → 0.7.67

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.
@@ -6,18 +6,18 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
9
- "version": "0.7.65"
9
+ "version": "0.7.67"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "hivemind",
14
14
  "description": "Persistent shared memory powered by Deeplake — captures all session activity and provides cross-session, cross-agent memory search",
15
- "version": "0.7.65",
15
+ "version": "0.7.67",
16
16
  "source": {
17
17
  "source": "git-subdir",
18
18
  "url": "https://github.com/activeloopai/hivemind.git",
19
19
  "path": "claude-code",
20
- "sha": "ea40de3f7d6cd61861e8ee735f9aa73cd7f8f933"
20
+ "sha": "7d1de6ab6b8a13bc52305e69d1a8ccc0b6e4d92a"
21
21
  },
22
22
  "homepage": "https://github.com/activeloopai/hivemind"
23
23
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hivemind",
3
3
  "description": "Cloud-backed persistent memory powered by Deeplake — read, write, and share memory across Claude Code sessions and agents",
4
- "version": "0.7.65",
4
+ "version": "0.7.67",
5
5
  "author": {
6
6
  "name": "Activeloop",
7
7
  "url": "https://deeplake.ai"
package/bundle/cli.js CHANGED
@@ -382,9 +382,11 @@ function isHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
382
382
  const cmd = h.command;
383
383
  if (typeof cmd !== "string")
384
384
  return false;
385
- if (cmd.includes(`${pluginDir}/bundle/`))
385
+ const nCmd = cmd.replace(/\\/g, "/");
386
+ const nPluginDir = pluginDir.replace(/\\/g, "/");
387
+ if (nCmd.includes(`${nPluginDir}/bundle/`))
386
388
  return true;
387
- return HIVEMIND_BUNDLE_FILES.some((f) => cmd.includes(`/bundle/${f}`));
389
+ return HIVEMIND_BUNDLE_FILES.some((f) => nCmd.includes(`/bundle/${f}`));
388
390
  });
389
391
  }
390
392
  function isForeignHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
@@ -398,7 +400,7 @@ function isForeignHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
398
400
  const cmd = h.command;
399
401
  if (typeof cmd !== "string")
400
402
  return false;
401
- return !cmd.includes(`${pluginDir}/bundle/`);
403
+ return !cmd.replace(/\\/g, "/").includes(`${pluginDir.replace(/\\/g, "/")}/bundle/`);
402
404
  });
403
405
  }
404
406
  function mergeHooks(existing, ours, pluginDir = PLUGIN_DIR) {
@@ -707,7 +709,9 @@ function isHivemindEntry(entry) {
707
709
  if (!entry || typeof entry !== "object")
708
710
  return false;
709
711
  const cmd = entry.command;
710
- return typeof cmd === "string" && cmd.includes("/.cursor/hivemind/bundle/");
712
+ if (typeof cmd !== "string")
713
+ return false;
714
+ return cmd.replace(/\\/g, "/").includes("/.cursor/hivemind/bundle/");
711
715
  }
712
716
  function mergeHooks2(existing) {
713
717
  const root = existing ?? { version: 1, hooks: {} };
@@ -4816,9 +4820,6 @@ function traceSql(msg) {
4816
4820
  log4(msg);
4817
4821
  }
4818
4822
  var _signalledBalanceExhausted = false;
4819
- var _signalledLowBalance = false;
4820
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
4821
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
4822
4823
  function maybeSignalBalanceExhausted(status, bodyText) {
4823
4824
  if (status !== 402)
4824
4825
  return;
@@ -4834,40 +4835,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
4834
4835
  transient: true,
4835
4836
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
4836
4837
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
4837
- dedupKey: { reason: "balance-zero" }
4838
+ dedupKey: { reason: "balance-zero" },
4839
+ // User-facing billing notice → user channel only. Never the model's
4840
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
4841
+ // is a prompt-injection pattern external agents flag.
4842
+ userVisibleOnly: true
4838
4843
  }).catch((e) => {
4839
4844
  log4(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
4840
4845
  });
4841
4846
  }
4842
- function signalLowBalanceFromHeader(resp) {
4843
- if (_signalledLowBalance)
4844
- return;
4845
- const raw = resp.headers?.get?.(BALANCE_HEADER);
4846
- if (!raw)
4847
- return;
4848
- if (!/^-?\d+$/.test(raw.trim()))
4849
- return;
4850
- const balance = Number(raw.trim());
4851
- if (!Number.isFinite(balance))
4852
- return;
4853
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
4854
- return;
4855
- if (balance <= 0)
4856
- return;
4857
- _signalledLowBalance = true;
4858
- log4(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
4859
- enqueueNotification({
4860
- id: "low-balance-warning",
4861
- severity: "warn",
4862
- transient: true,
4863
- title: "Your org's Hivemind balance is running low",
4864
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
4865
- dedupKey: { reason: "low-balance" }
4866
- }).catch((e) => {
4867
- _signalledLowBalance = false;
4868
- log4(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
4869
- });
4870
- }
4871
4847
  function billingUrl() {
4872
4848
  try {
4873
4849
  const c = loadCredentials();
@@ -4993,7 +4969,6 @@ var DeeplakeApi = class {
4993
4969
  }
4994
4970
  throw lastError;
4995
4971
  }
4996
- signalLowBalanceFromHeader(resp);
4997
4972
  if (resp.ok) {
4998
4973
  const raw = await resp.json();
4999
4974
  if (!raw?.rows || !raw?.columns)
@@ -5154,7 +5129,6 @@ var DeeplakeApi = class {
5154
5129
  ...deeplakeClientHeader()
5155
5130
  }
5156
5131
  });
5157
- signalLowBalanceFromHeader(resp);
5158
5132
  if (resp.ok) {
5159
5133
  const data = await resp.json();
5160
5134
  return {
@@ -7535,6 +7509,14 @@ var CACHE_TTL_MS = 60 * 60 * 1e3;
7535
7509
  function cacheFilePath() {
7536
7510
  return join28(homedir11(), ".deeplake", "hivemind-stats-cache.json");
7537
7511
  }
7512
+ var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
7513
+ function parseBalanceHeader(resp) {
7514
+ const raw = resp.headers?.get?.(BALANCE_HEADER);
7515
+ if (!raw || !/^-?\d+$/.test(raw.trim()))
7516
+ return null;
7517
+ const n = Number(raw.trim());
7518
+ return Number.isFinite(n) ? n : null;
7519
+ }
7538
7520
  function cacheScopeKey(creds) {
7539
7521
  return JSON.stringify({
7540
7522
  apiUrl: creds.apiUrl ?? DEFAULT_API_URL3,
@@ -7614,7 +7596,8 @@ async function fetchOrgStats(creds) {
7614
7596
  }
7615
7597
  const data = {
7616
7598
  org: scopeFromServer(body.org),
7617
- user: scopeFromServer(body.user)
7599
+ user: scopeFromServer(body.user),
7600
+ balanceCents: parseBalanceHeader(resp)
7618
7601
  };
7619
7602
  writeCache2(scopeKey, data);
7620
7603
  log5(`fetched org stats from ${apiUrl}`);
@@ -478,9 +478,6 @@ function traceSql(msg) {
478
478
  log3(msg);
479
479
  }
480
480
  var _signalledBalanceExhausted = false;
481
- var _signalledLowBalance = false;
482
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
483
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
484
481
  function maybeSignalBalanceExhausted(status, bodyText) {
485
482
  if (status !== 402)
486
483
  return;
@@ -496,40 +493,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
496
493
  transient: true,
497
494
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
498
495
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
499
- dedupKey: { reason: "balance-zero" }
496
+ dedupKey: { reason: "balance-zero" },
497
+ // User-facing billing notice → user channel only. Never the model's
498
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
499
+ // is a prompt-injection pattern external agents flag.
500
+ userVisibleOnly: true
500
501
  }).catch((e) => {
501
502
  log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
502
503
  });
503
504
  }
504
- function signalLowBalanceFromHeader(resp) {
505
- if (_signalledLowBalance)
506
- return;
507
- const raw = resp.headers?.get?.(BALANCE_HEADER);
508
- if (!raw)
509
- return;
510
- if (!/^-?\d+$/.test(raw.trim()))
511
- return;
512
- const balance = Number(raw.trim());
513
- if (!Number.isFinite(balance))
514
- return;
515
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
516
- return;
517
- if (balance <= 0)
518
- return;
519
- _signalledLowBalance = true;
520
- log3(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
521
- enqueueNotification({
522
- id: "low-balance-warning",
523
- severity: "warn",
524
- transient: true,
525
- title: "Your org's Hivemind balance is running low",
526
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
527
- dedupKey: { reason: "low-balance" }
528
- }).catch((e) => {
529
- _signalledLowBalance = false;
530
- log3(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
531
- });
532
- }
533
505
  function billingUrl() {
534
506
  try {
535
507
  const c = loadCredentials();
@@ -655,7 +627,6 @@ var DeeplakeApi = class {
655
627
  }
656
628
  throw lastError;
657
629
  }
658
- signalLowBalanceFromHeader(resp);
659
630
  if (resp.ok) {
660
631
  const raw = await resp.json();
661
632
  if (!raw?.rows || !raw?.columns)
@@ -816,7 +787,6 @@ var DeeplakeApi = class {
816
787
  ...deeplakeClientHeader()
817
788
  }
818
789
  });
819
- signalLowBalanceFromHeader(resp);
820
790
  if (resp.ok) {
821
791
  const data = await resp.json();
822
792
  return {
@@ -1005,6 +975,12 @@ var DeeplakeApi = class {
1005
975
  }
1006
976
  };
1007
977
 
978
+ // dist/src/utils/project-name.js
979
+ import { basename } from "node:path";
980
+ function projectNameFromCwd(cwd) {
981
+ return basename(cwd ?? "") || "unknown";
982
+ }
983
+
1008
984
  // dist/src/utils/session-path.js
1009
985
  function buildSessionPath(config, sessionId) {
1010
986
  const workspace = config.workspaceId ?? "default";
@@ -1530,9 +1506,9 @@ function embeddingsDisabled() {
1530
1506
  // dist/src/embeddings/self-heal.js
1531
1507
  import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, readlinkSync, renameSync as renameSync3, rmSync, symlinkSync, statSync as statSync2 } from "node:fs";
1532
1508
  import { homedir as homedir8 } from "node:os";
1533
- import { basename, dirname as dirname2, join as join9 } from "node:path";
1509
+ import { basename as basename2, dirname as dirname2, join as join9 } from "node:path";
1534
1510
  function ensurePluginNodeModulesLink(opts) {
1535
- if (basename(opts.bundleDir) !== "bundle") {
1511
+ if (basename2(opts.bundleDir) !== "bundle") {
1536
1512
  return { kind: "not-bundle-layout", bundleDir: opts.bundleDir };
1537
1513
  }
1538
1514
  const target = opts.sharedNodeModules ?? join9(homedir8(), ".hivemind", "embed-deps", "node_modules");
@@ -1731,7 +1707,7 @@ function releaseLock(sessionId) {
1731
1707
  }
1732
1708
 
1733
1709
  // dist/src/hooks/codex/spawn-wiki-worker.js
1734
- import { spawn as spawn2, execSync } from "node:child_process";
1710
+ import { execSync } from "node:child_process";
1735
1711
  import { fileURLToPath } from "node:url";
1736
1712
  import { dirname as dirname4, join as join13 } from "node:path";
1737
1713
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
@@ -1797,6 +1773,26 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
1797
1773
  return null;
1798
1774
  }
1799
1775
 
1776
+ // dist/src/utils/spawn-detached.js
1777
+ import { spawn as nodeSpawn } from "node:child_process";
1778
+ function spawnDetachedNodeWorker(workerPath, args = [], deps = {}) {
1779
+ const spawn2 = deps.spawn ?? nodeSpawn;
1780
+ const execPath = deps.execPath ?? process.execPath;
1781
+ try {
1782
+ const child = spawn2(execPath, [workerPath, ...args], {
1783
+ detached: true,
1784
+ stdio: ["ignore", "ignore", "ignore"],
1785
+ // Suppress the transient console window Windows would otherwise pop for
1786
+ // the detached worker. No-op on POSIX.
1787
+ windowsHide: true
1788
+ });
1789
+ child.on("error", () => {
1790
+ });
1791
+ child.unref();
1792
+ } catch {
1793
+ }
1794
+ }
1795
+
1800
1796
  // dist/src/hooks/codex/spawn-wiki-worker.js
1801
1797
  var HOME = homedir10();
1802
1798
  var wikiLogger = makeWikiLogger(join13(HOME, ".codex", "hooks"));
@@ -1860,7 +1856,7 @@ function findCodexBin() {
1860
1856
  }
1861
1857
  function spawnCodexWikiWorker(opts) {
1862
1858
  const { config, sessionId, cwd, bundleDir, reason } = opts;
1863
- const projectName = cwd.split("/").pop() || "unknown";
1859
+ const projectName = projectNameFromCwd(cwd);
1864
1860
  const tmpDir = join13(tmpdir2(), `deeplake-wiki-${sessionId}-${Date.now()}`);
1865
1861
  mkdirSync8(tmpDir, { recursive: true });
1866
1862
  const pluginVersion = getInstalledVersion(bundleDir, ".codex-plugin") ?? "";
@@ -1884,10 +1880,7 @@ function spawnCodexWikiWorker(opts) {
1884
1880
  }));
1885
1881
  wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
1886
1882
  const workerPath = join13(bundleDir, "wiki-worker.js");
1887
- spawn2("nohup", ["node", workerPath, configFile], {
1888
- detached: true,
1889
- stdio: ["ignore", "ignore", "ignore"]
1890
- }).unref();
1883
+ spawnDetachedNodeWorker(workerPath, [configFile]);
1891
1884
  wikiLog(`${reason}: spawned summary worker for ${sessionId}`);
1892
1885
  }
1893
1886
  function bundleDirFromImportMeta(importMetaUrl) {
@@ -1956,7 +1949,7 @@ async function main() {
1956
1949
  const sessionPath = buildSessionPath(config, input.session_id);
1957
1950
  const line = JSON.stringify(entry);
1958
1951
  log5(`writing to ${sessionPath}`);
1959
- const projectName = (input.cwd ?? "").split("/").pop() || "unknown";
1952
+ const projectName = projectNameFromCwd(input.cwd);
1960
1953
  const filename = sessionPath.split("/").pop() ?? "";
1961
1954
  const jsonForSql = line.replace(/'/g, "''");
1962
1955
  const embedding = embeddingsDisabled() ? null : await new EmbedClient({ daemonEntry: resolveEmbedDaemonPath() }).embed(line, "document");
@@ -741,9 +741,6 @@ function traceSql(msg) {
741
741
  log3(msg);
742
742
  }
743
743
  var _signalledBalanceExhausted = false;
744
- var _signalledLowBalance = false;
745
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
746
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
747
744
  function maybeSignalBalanceExhausted(status, bodyText) {
748
745
  if (status !== 402)
749
746
  return;
@@ -759,40 +756,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
759
756
  transient: true,
760
757
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
761
758
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
762
- dedupKey: { reason: "balance-zero" }
759
+ dedupKey: { reason: "balance-zero" },
760
+ // User-facing billing notice → user channel only. Never the model's
761
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
762
+ // is a prompt-injection pattern external agents flag.
763
+ userVisibleOnly: true
763
764
  }).catch((e) => {
764
765
  log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
765
766
  });
766
767
  }
767
- function signalLowBalanceFromHeader(resp) {
768
- if (_signalledLowBalance)
769
- return;
770
- const raw = resp.headers?.get?.(BALANCE_HEADER);
771
- if (!raw)
772
- return;
773
- if (!/^-?\d+$/.test(raw.trim()))
774
- return;
775
- const balance = Number(raw.trim());
776
- if (!Number.isFinite(balance))
777
- return;
778
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
779
- return;
780
- if (balance <= 0)
781
- return;
782
- _signalledLowBalance = true;
783
- log3(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
784
- enqueueNotification({
785
- id: "low-balance-warning",
786
- severity: "warn",
787
- transient: true,
788
- title: "Your org's Hivemind balance is running low",
789
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
790
- dedupKey: { reason: "low-balance" }
791
- }).catch((e) => {
792
- _signalledLowBalance = false;
793
- log3(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
794
- });
795
- }
796
768
  function billingUrl() {
797
769
  try {
798
770
  const c = loadCredentials();
@@ -918,7 +890,6 @@ var DeeplakeApi = class {
918
890
  }
919
891
  throw lastError;
920
892
  }
921
- signalLowBalanceFromHeader(resp);
922
893
  if (resp.ok) {
923
894
  const raw = await resp.json();
924
895
  if (!raw?.rows || !raw?.columns)
@@ -1079,7 +1050,6 @@ var DeeplakeApi = class {
1079
1050
  ...deeplakeClientHeader()
1080
1051
  }
1081
1052
  });
1082
- signalLowBalanceFromHeader(resp);
1083
1053
  if (resp.ok) {
1084
1054
  const data = await resp.json();
1085
1055
  return {
@@ -468,9 +468,6 @@ function traceSql(msg) {
468
468
  log3(msg);
469
469
  }
470
470
  var _signalledBalanceExhausted = false;
471
- var _signalledLowBalance = false;
472
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
473
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
474
471
  function maybeSignalBalanceExhausted(status, bodyText) {
475
472
  if (status !== 402)
476
473
  return;
@@ -486,40 +483,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
486
483
  transient: true,
487
484
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
488
485
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
489
- dedupKey: { reason: "balance-zero" }
486
+ dedupKey: { reason: "balance-zero" },
487
+ // User-facing billing notice → user channel only. Never the model's
488
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
489
+ // is a prompt-injection pattern external agents flag.
490
+ userVisibleOnly: true
490
491
  }).catch((e) => {
491
492
  log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
492
493
  });
493
494
  }
494
- function signalLowBalanceFromHeader(resp) {
495
- if (_signalledLowBalance)
496
- return;
497
- const raw = resp.headers?.get?.(BALANCE_HEADER);
498
- if (!raw)
499
- return;
500
- if (!/^-?\d+$/.test(raw.trim()))
501
- return;
502
- const balance = Number(raw.trim());
503
- if (!Number.isFinite(balance))
504
- return;
505
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
506
- return;
507
- if (balance <= 0)
508
- return;
509
- _signalledLowBalance = true;
510
- log3(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
511
- enqueueNotification({
512
- id: "low-balance-warning",
513
- severity: "warn",
514
- transient: true,
515
- title: "Your org's Hivemind balance is running low",
516
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
517
- dedupKey: { reason: "low-balance" }
518
- }).catch((e) => {
519
- _signalledLowBalance = false;
520
- log3(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
521
- });
522
- }
523
495
  function billingUrl() {
524
496
  try {
525
497
  const c = loadCredentials();
@@ -645,7 +617,6 @@ var DeeplakeApi = class {
645
617
  }
646
618
  throw lastError;
647
619
  }
648
- signalLowBalanceFromHeader(resp);
649
620
  if (resp.ok) {
650
621
  const raw = await resp.json();
651
622
  if (!raw?.rows || !raw?.columns)
@@ -806,7 +777,6 @@ var DeeplakeApi = class {
806
777
  ...deeplakeClientHeader()
807
778
  }
808
779
  });
809
- signalLowBalanceFromHeader(resp);
810
780
  if (resp.ok) {
811
781
  const data = await resp.json();
812
782
  return {
@@ -484,9 +484,6 @@ function traceSql(msg) {
484
484
  log3(msg);
485
485
  }
486
486
  var _signalledBalanceExhausted = false;
487
- var _signalledLowBalance = false;
488
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
489
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
490
487
  function maybeSignalBalanceExhausted(status, bodyText) {
491
488
  if (status !== 402)
492
489
  return;
@@ -502,40 +499,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
502
499
  transient: true,
503
500
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
504
501
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
505
- dedupKey: { reason: "balance-zero" }
502
+ dedupKey: { reason: "balance-zero" },
503
+ // User-facing billing notice → user channel only. Never the model's
504
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
505
+ // is a prompt-injection pattern external agents flag.
506
+ userVisibleOnly: true
506
507
  }).catch((e) => {
507
508
  log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
508
509
  });
509
510
  }
510
- function signalLowBalanceFromHeader(resp) {
511
- if (_signalledLowBalance)
512
- return;
513
- const raw = resp.headers?.get?.(BALANCE_HEADER);
514
- if (!raw)
515
- return;
516
- if (!/^-?\d+$/.test(raw.trim()))
517
- return;
518
- const balance = Number(raw.trim());
519
- if (!Number.isFinite(balance))
520
- return;
521
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
522
- return;
523
- if (balance <= 0)
524
- return;
525
- _signalledLowBalance = true;
526
- log3(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
527
- enqueueNotification({
528
- id: "low-balance-warning",
529
- severity: "warn",
530
- transient: true,
531
- title: "Your org's Hivemind balance is running low",
532
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
533
- dedupKey: { reason: "low-balance" }
534
- }).catch((e) => {
535
- _signalledLowBalance = false;
536
- log3(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
537
- });
538
- }
539
511
  function billingUrl() {
540
512
  try {
541
513
  const c = loadCredentials();
@@ -661,7 +633,6 @@ var DeeplakeApi = class {
661
633
  }
662
634
  throw lastError;
663
635
  }
664
- signalLowBalanceFromHeader(resp);
665
636
  if (resp.ok) {
666
637
  const raw = await resp.json();
667
638
  if (!raw?.rows || !raw?.columns)
@@ -822,7 +793,6 @@ var DeeplakeApi = class {
822
793
  ...deeplakeClientHeader()
823
794
  }
824
795
  });
825
- signalLowBalanceFromHeader(resp);
826
796
  if (resp.ok) {
827
797
  const data = await resp.json();
828
798
  return {
@@ -479,9 +479,6 @@ function traceSql(msg) {
479
479
  log3(msg);
480
480
  }
481
481
  var _signalledBalanceExhausted = false;
482
- var _signalledLowBalance = false;
483
- var LOW_BALANCE_THRESHOLD_CENTS = 200;
484
- var BALANCE_HEADER = "X-Activeloop-Balance-Cents";
485
482
  function maybeSignalBalanceExhausted(status, bodyText) {
486
483
  if (status !== 402)
487
484
  return;
@@ -497,40 +494,15 @@ function maybeSignalBalanceExhausted(status, bodyText) {
497
494
  transient: true,
498
495
  title: "Hivemind credits exhausted \u2014 top up to keep capturing",
499
496
  body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
500
- dedupKey: { reason: "balance-zero" }
497
+ dedupKey: { reason: "balance-zero" },
498
+ // User-facing billing notice → user channel only. Never the model's
499
+ // additionalContext: a "top up at <url>" instruction in the agent prompt
500
+ // is a prompt-injection pattern external agents flag.
501
+ userVisibleOnly: true
501
502
  }).catch((e) => {
502
503
  log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
503
504
  });
504
505
  }
505
- function signalLowBalanceFromHeader(resp) {
506
- if (_signalledLowBalance)
507
- return;
508
- const raw = resp.headers?.get?.(BALANCE_HEADER);
509
- if (!raw)
510
- return;
511
- if (!/^-?\d+$/.test(raw.trim()))
512
- return;
513
- const balance = Number(raw.trim());
514
- if (!Number.isFinite(balance))
515
- return;
516
- if (balance >= LOW_BALANCE_THRESHOLD_CENTS)
517
- return;
518
- if (balance <= 0)
519
- return;
520
- _signalledLowBalance = true;
521
- log3(`balance below threshold (${balance}\xA2) \u2014 enqueuing low-balance banner`);
522
- enqueueNotification({
523
- id: "low-balance-warning",
524
- severity: "warn",
525
- transient: true,
526
- title: "Your org's Hivemind balance is running low",
527
- body: `Only $${(balance / 100).toFixed(2)} of prepaid balance remains. Admins can top up at ${billingUrl()}; otherwise ask an org admin to top up before requests start failing.`,
528
- dedupKey: { reason: "low-balance" }
529
- }).catch((e) => {
530
- _signalledLowBalance = false;
531
- log3(`enqueue low-balance failed: ${e instanceof Error ? e.message : String(e)}`);
532
- });
533
- }
534
506
  function billingUrl() {
535
507
  try {
536
508
  const c = loadCredentials();
@@ -656,7 +628,6 @@ var DeeplakeApi = class {
656
628
  }
657
629
  throw lastError;
658
630
  }
659
- signalLowBalanceFromHeader(resp);
660
631
  if (resp.ok) {
661
632
  const raw = await resp.json();
662
633
  if (!raw?.rows || !raw?.columns)
@@ -817,7 +788,6 @@ var DeeplakeApi = class {
817
788
  ...deeplakeClientHeader()
818
789
  }
819
790
  });
820
- signalLowBalanceFromHeader(resp);
821
791
  if (resp.ok) {
822
792
  const data = await resp.json();
823
793
  return {
@@ -1006,6 +976,12 @@ var DeeplakeApi = class {
1006
976
  }
1007
977
  };
1008
978
 
979
+ // dist/src/utils/project-name.js
980
+ import { basename } from "node:path";
981
+ function projectNameFromCwd(cwd) {
982
+ return basename(cwd ?? "") || "unknown";
983
+ }
984
+
1009
985
  // dist/src/utils/stdin.js
1010
986
  function readStdin() {
1011
987
  return new Promise((resolve2, reject) => {
@@ -1149,7 +1125,7 @@ async function createPlaceholder(api, table, sessionId, cwd, userName, orgName,
1149
1125
  return;
1150
1126
  }
1151
1127
  const now = (/* @__PURE__ */ new Date()).toISOString();
1152
- const projectName = cwd.split("/").pop() ?? "unknown";
1128
+ const projectName = projectNameFromCwd(cwd);
1153
1129
  const sessionSource = `/sessions/${userName}/${userName}_${orgName}_${workspaceId}_${sessionId}.jsonl`;
1154
1130
  const content = [
1155
1131
  `# Session ${sessionId}`,