@deeplake/hivemind 0.7.51 → 0.7.53

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.51"
9
+ "version": "0.7.53"
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.51",
15
+ "version": "0.7.53",
16
16
  "source": {
17
17
  "source": "git-subdir",
18
18
  "url": "https://github.com/activeloopai/hivemind.git",
19
19
  "path": "claude-code",
20
- "sha": "c0c861150a00e85bc4fd21120ae2a331417468d0"
20
+ "sha": "2f9768a8c9ed54952bdc272261f2a527f5482fe2"
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.51",
4
+ "version": "0.7.53",
5
5
  "author": {
6
6
  "name": "Activeloop",
7
7
  "url": "https://deeplake.ai"
package/bundle/cli.js CHANGED
@@ -4270,7 +4270,14 @@ async function switchOrg(orgId, orgName) {
4270
4270
  const creds = loadCredentials();
4271
4271
  if (!creds)
4272
4272
  throw new Error("Not logged in. Run deeplake login first.");
4273
- saveCredentials({ ...creds, orgId, orgName });
4273
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
4274
+ const tokenName = `deeplake-plugin-switch-${Date.now()}`;
4275
+ const tokenData = await apiPost("/users/me/tokens", {
4276
+ name: tokenName,
4277
+ duration: 365 * 24 * 3600,
4278
+ organization_id: orgId
4279
+ }, creds.token, apiUrl);
4280
+ saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
4274
4281
  }
4275
4282
  async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
4276
4283
  const raw = await apiGet("/workspaces", token, apiUrl, orgId);
@@ -9665,7 +9672,45 @@ function writeLocalManifest(m, path = LOCAL_MANIFEST_PATH) {
9665
9672
  mkdirSync21(dirname15(path), { recursive: true });
9666
9673
  writeFileSync24(path, JSON.stringify(m, null, 2));
9667
9674
  }
9675
+ function countLocalManifestEntries(path = LOCAL_MANIFEST_PATH) {
9676
+ const m = readLocalManifest(path);
9677
+ return Array.isArray(m?.entries) ? m.entries.length : 0;
9678
+ }
9668
9679
  var LATEST_RUN_WINDOW_MS = 5 * 60 * 1e3;
9680
+ function getLatestInsightEntry(path = LOCAL_MANIFEST_PATH) {
9681
+ const m = readLocalManifest(path);
9682
+ if (!m || !Array.isArray(m.entries))
9683
+ return null;
9684
+ let newestTs = Number.NEGATIVE_INFINITY;
9685
+ for (const e of m.entries) {
9686
+ if (!e)
9687
+ continue;
9688
+ const ts = Date.parse(e.created_at ?? "");
9689
+ if (Number.isFinite(ts) && ts > newestTs)
9690
+ newestTs = ts;
9691
+ }
9692
+ if (!Number.isFinite(newestTs))
9693
+ return null;
9694
+ let best = null;
9695
+ let bestTs = Number.NEGATIVE_INFINITY;
9696
+ let bestIsPrimary = false;
9697
+ for (const e of m.entries) {
9698
+ if (!e || typeof e.insight !== "string" || e.insight.trim().length === 0)
9699
+ continue;
9700
+ const ts = Date.parse(e.created_at ?? "");
9701
+ if (!Number.isFinite(ts))
9702
+ continue;
9703
+ if (newestTs - ts > LATEST_RUN_WINDOW_MS)
9704
+ continue;
9705
+ const isPrimary = e.primary === true;
9706
+ if (!best || isPrimary && !bestIsPrimary || isPrimary === bestIsPrimary && ts > bestTs) {
9707
+ best = e;
9708
+ bestTs = ts;
9709
+ bestIsPrimary = isPrimary;
9710
+ }
9711
+ }
9712
+ return best;
9713
+ }
9669
9714
 
9670
9715
  // dist/src/commands/mine-local.js
9671
9716
  import { unlinkSync as unlinkSync12 } from "node:fs";
@@ -10012,18 +10057,24 @@ async function runMineLocalImpl(args) {
10012
10057
  const force = takeBoolFlag(work, "--force");
10013
10058
  const dryRun = takeBoolFlag(work, "--dry-run");
10014
10059
  const nRaw = takeFlagValue(work, "--n");
10060
+ const onlyAgent = takeFlagValue(work, "--only");
10015
10061
  if (loadManifest2() && !force) {
10016
10062
  console.error(`Local skills have already been mined on this machine.`);
10017
10063
  console.error(`Manifest: ${MANIFEST_PATH}`);
10018
10064
  console.error(`Pass --force to re-mine.`);
10019
10065
  process.exit(1);
10020
10066
  }
10021
- const installs = detectInstalledAgents();
10022
- if (installs.length === 0) {
10067
+ const installsAll = detectInstalledAgents();
10068
+ if (installsAll.length === 0) {
10023
10069
  console.error(`No agent session directories detected. Run a session first.`);
10024
10070
  process.exit(1);
10025
10071
  }
10026
- console.log(`Detected installed agents: ${installs.map((i) => i.agent).join(", ")}`);
10072
+ const installs = onlyAgent ? installsAll.filter((i) => i.agent === onlyAgent) : installsAll;
10073
+ if (installs.length === 0) {
10074
+ console.error(`No '${onlyAgent}' session directory detected. Skipping mine-local.`);
10075
+ process.exit(1);
10076
+ }
10077
+ console.log(`Detected installed agents: ${installs.map((i) => i.agent).join(", ")}${onlyAgent ? ` (filtered to ${onlyAgent})` : ""}`);
10027
10078
  const host = detectHostAgent();
10028
10079
  const fallback = installs[0].agent;
10029
10080
  const gateAgent = gateAgentFor(host, fallback, installs);
@@ -11461,7 +11512,7 @@ async function runUpdate(opts = {}) {
11461
11512
  }
11462
11513
  log(`Update available: ${current} \u2192 ${latest}`);
11463
11514
  const detected = opts.installKindOverride ?? detectInstallKind();
11464
- const spawn3 = opts.spawn ?? defaultSpawn;
11515
+ const spawn5 = opts.spawn ?? defaultSpawn;
11465
11516
  switch (detected.kind) {
11466
11517
  case "npm-global": {
11467
11518
  if (opts.dryRun) {
@@ -11476,7 +11527,7 @@ async function runUpdate(opts = {}) {
11476
11527
  try {
11477
11528
  log(`Upgrading via npm\u2026`);
11478
11529
  try {
11479
- spawn3("npm", ["install", "-g", `${PKG_NAME}@latest`]);
11530
+ spawn5("npm", ["install", "-g", `${PKG_NAME}@latest`]);
11480
11531
  } catch (e) {
11481
11532
  warn(`npm install failed: ${e.message}`);
11482
11533
  warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
@@ -11485,7 +11536,7 @@ async function runUpdate(opts = {}) {
11485
11536
  log(``);
11486
11537
  log(`Refreshing agent bundles\u2026`);
11487
11538
  try {
11488
- spawn3("hivemind", ["install", "--skip-auth"]);
11539
+ spawn5("hivemind", ["install", "--skip-auth"]);
11489
11540
  } catch (e) {
11490
11541
  warn(`Agent refresh failed: ${e.message}`);
11491
11542
  warn(`Run manually: hivemind install`);
@@ -11536,6 +11587,325 @@ async function runUpdate(opts = {}) {
11536
11587
  }
11537
11588
  }
11538
11589
 
11590
+ // dist/src/cli/install-scan.js
11591
+ import { spawn as spawn4 } from "node:child_process";
11592
+ import { existsSync as existsSync39, readFileSync as readFileSync35, readdirSync as readdirSync9, unlinkSync as unlinkSync14 } from "node:fs";
11593
+ import { homedir as homedir26 } from "node:os";
11594
+ import { join as join49 } from "node:path";
11595
+
11596
+ // dist/src/skillify/advisor.js
11597
+ import { spawn as spawn3 } from "node:child_process";
11598
+ import { existsSync as existsSync38 } from "node:fs";
11599
+ var ADVISOR_TIMEOUT_MS = 6e4;
11600
+ var MAX_CANDIDATES = 20;
11601
+ function buildAdvisorPrompt(candidates) {
11602
+ const lines = [
11603
+ "You are reviewing skill candidates extracted from a user's coding sessions.",
11604
+ "Pick the ONE candidate whose `insight` field is most useful to show the user as a",
11605
+ "concrete finding from their past work. Reply on EXACTLY ONE LINE.",
11606
+ "",
11607
+ "GOOD insights are:",
11608
+ " - Concrete and counted (cite specific numbers, file names, durations)",
11609
+ ' - About a real coding mistake or pattern the USER made (in 2nd person \u2014 "You did X")',
11610
+ " - Actionable: the user can change behavior based on knowing this",
11611
+ "",
11612
+ "BAD insights (REJECT these) are:",
11613
+ ' - Meta-commentary about why the skill was saved ("User explicitly requested...")',
11614
+ " - Vague / generic engineering platitudes the user already knows",
11615
+ " - About someone other than the user (a teammate, a third party)",
11616
+ ' - Hypothetical ("could lead to...", "might cause...") rather than observed',
11617
+ "",
11618
+ "Output format \u2014 STRICT, one line only:",
11619
+ " PICK: <number 1-N>",
11620
+ "OR",
11621
+ " REJECT_ALL: <short reason why every candidate failed>",
11622
+ "",
11623
+ "Candidates:"
11624
+ ];
11625
+ for (const [i, c] of candidates.entries()) {
11626
+ lines.push(`${i + 1}. name=${c.skill_name} insight=${JSON.stringify((c.insight ?? "").slice(0, 400))}`);
11627
+ }
11628
+ return lines.join("\n");
11629
+ }
11630
+ function parseAdvisorOutput(raw, candidates) {
11631
+ const cleaned = raw.trim();
11632
+ const pickMatch = cleaned.match(/PICK:\s*(\d+)/i);
11633
+ if (pickMatch) {
11634
+ const idx = parseInt(pickMatch[1], 10) - 1;
11635
+ if (idx >= 0 && idx < candidates.length) {
11636
+ return {
11637
+ pickedSkillName: candidates[idx].skill_name,
11638
+ reason: cleaned,
11639
+ rawOutput: raw
11640
+ };
11641
+ }
11642
+ }
11643
+ const rejectMatch = cleaned.match(/REJECT_ALL:\s*(.+)/i);
11644
+ if (rejectMatch) {
11645
+ return { pickedSkillName: null, reason: rejectMatch[1].trim(), rawOutput: raw };
11646
+ }
11647
+ return { pickedSkillName: null, reason: `unparseable advisor output: ${cleaned.slice(0, 120)}`, rawOutput: raw };
11648
+ }
11649
+ function runAdvisorGate(prompt, claudeBin) {
11650
+ return new Promise((resolve6, reject) => {
11651
+ const child = spawn3(claudeBin, [
11652
+ "-p",
11653
+ "--no-session-persistence",
11654
+ "--model",
11655
+ "sonnet",
11656
+ "--permission-mode",
11657
+ "bypassPermissions"
11658
+ ], {
11659
+ stdio: ["pipe", "pipe", "pipe"],
11660
+ env: { ...process.env, HIVEMIND_WIKI_WORKER: "1", HIVEMIND_CAPTURE: "false" }
11661
+ });
11662
+ let stdout = "";
11663
+ let stderr = "";
11664
+ let settled = false;
11665
+ const finish = (err, out) => {
11666
+ if (settled)
11667
+ return;
11668
+ settled = true;
11669
+ if (err)
11670
+ reject(err);
11671
+ else
11672
+ resolve6(out);
11673
+ };
11674
+ const timer = setTimeout(() => {
11675
+ try {
11676
+ child.kill("SIGKILL");
11677
+ } catch {
11678
+ }
11679
+ finish(new Error(`advisor timed out after ${ADVISOR_TIMEOUT_MS}ms`), "");
11680
+ }, ADVISOR_TIMEOUT_MS);
11681
+ child.stdout.on("data", (b) => {
11682
+ stdout += b.toString("utf-8");
11683
+ });
11684
+ child.stderr.on("data", (b) => {
11685
+ stderr += b.toString("utf-8");
11686
+ });
11687
+ child.on("error", (e) => {
11688
+ clearTimeout(timer);
11689
+ finish(e, "");
11690
+ });
11691
+ child.on("close", (code) => {
11692
+ clearTimeout(timer);
11693
+ if (code !== 0) {
11694
+ finish(new Error(`advisor CLI exit ${code}; stderr=${stderr.slice(0, 200)}`), "");
11695
+ } else {
11696
+ finish(null, stdout);
11697
+ }
11698
+ });
11699
+ child.stdin.on("error", (e) => {
11700
+ clearTimeout(timer);
11701
+ finish(e, "");
11702
+ });
11703
+ child.stdin.end(prompt);
11704
+ });
11705
+ }
11706
+ async function runAdvisor(manifestPath3 = LOCAL_MANIFEST_PATH) {
11707
+ const m = readLocalManifest(manifestPath3);
11708
+ if (!m || !Array.isArray(m.entries))
11709
+ return null;
11710
+ const insightBearing = m.entries.filter((e) => e && typeof e.insight === "string" && e.insight.trim().length > 0);
11711
+ if (insightBearing.length === 0)
11712
+ return null;
11713
+ if (insightBearing.length === 1) {
11714
+ insightBearing[0].primary = true;
11715
+ writeLocalManifest(m, manifestPath3);
11716
+ return {
11717
+ pickedSkillName: insightBearing[0].skill_name,
11718
+ reason: "trivial pick (single candidate)",
11719
+ rawOutput: ""
11720
+ };
11721
+ }
11722
+ const claudeBin = findAgentBin("claude_code");
11723
+ if (!claudeBin || !existsSync38(claudeBin)) {
11724
+ return null;
11725
+ }
11726
+ const ranked = [...insightBearing].sort((a, b) => (b.created_at ?? "").localeCompare(a.created_at ?? "")).slice(0, MAX_CANDIDATES);
11727
+ const prompt = buildAdvisorPrompt(ranked);
11728
+ let raw;
11729
+ try {
11730
+ raw = await runAdvisorGate(prompt, claudeBin);
11731
+ } catch (err) {
11732
+ return {
11733
+ pickedSkillName: null,
11734
+ reason: `advisor invocation failed: ${err.message}`,
11735
+ rawOutput: ""
11736
+ };
11737
+ }
11738
+ const result = parseAdvisorOutput(raw, ranked);
11739
+ if (result.pickedSkillName) {
11740
+ for (const e of m.entries) {
11741
+ if (e && e.primary === true)
11742
+ delete e.primary;
11743
+ }
11744
+ for (const e of m.entries) {
11745
+ if (e && e.skill_name === result.pickedSkillName) {
11746
+ e.primary = true;
11747
+ break;
11748
+ }
11749
+ }
11750
+ writeLocalManifest(m, manifestPath3);
11751
+ }
11752
+ return result;
11753
+ }
11754
+
11755
+ // dist/src/cli/install-scan.js
11756
+ function claudeProjectsDir() {
11757
+ return join49(homedir26(), ".claude", "projects");
11758
+ }
11759
+ function manifestPath2() {
11760
+ return join49(homedir26(), ".claude", "hivemind", "local-mined.json");
11761
+ }
11762
+ var SCAN_TIMEOUT_MS = 3e5;
11763
+ var INSTALL_SCAN_SESSION_COUNT = 10;
11764
+ function manifestIsTrulyEmpty() {
11765
+ const p = manifestPath2();
11766
+ if (!existsSync39(p))
11767
+ return false;
11768
+ try {
11769
+ const m = JSON.parse(readFileSync35(p, "utf-8"));
11770
+ return Array.isArray(m.entries) && m.entries.length === 0;
11771
+ } catch {
11772
+ return false;
11773
+ }
11774
+ }
11775
+ function hasLocalClaudeSessions() {
11776
+ const projectsDir = claudeProjectsDir();
11777
+ if (!existsSync39(projectsDir))
11778
+ return false;
11779
+ let subdirs;
11780
+ try {
11781
+ subdirs = readdirSync9(projectsDir);
11782
+ } catch {
11783
+ return false;
11784
+ }
11785
+ for (const sub of subdirs) {
11786
+ let files;
11787
+ try {
11788
+ files = readdirSync9(join49(projectsDir, sub));
11789
+ } catch {
11790
+ continue;
11791
+ }
11792
+ if (files.some((f) => f.endsWith(".jsonl")))
11793
+ return true;
11794
+ }
11795
+ return false;
11796
+ }
11797
+ function canOfferInstallScan() {
11798
+ const bin = findAgentBin("claude_code");
11799
+ if (!bin || !existsSync39(bin))
11800
+ return false;
11801
+ if (!hasLocalClaudeSessions())
11802
+ return false;
11803
+ if (existsSync39(manifestPath2()))
11804
+ return false;
11805
+ return true;
11806
+ }
11807
+ function unlinkManifestIfCorrupt() {
11808
+ const p = manifestPath2();
11809
+ if (!existsSync39(p))
11810
+ return;
11811
+ if (readLocalManifest(p) === null) {
11812
+ try {
11813
+ unlinkSync14(p);
11814
+ } catch {
11815
+ }
11816
+ }
11817
+ }
11818
+ function runInstallScan() {
11819
+ return new Promise((resolve6) => {
11820
+ const cliPath = process.argv[1];
11821
+ if (!cliPath || !existsSync39(cliPath)) {
11822
+ resolve6({ insight: null, skillsCount: 0 });
11823
+ return;
11824
+ }
11825
+ const child = spawn4(process.execPath, [
11826
+ cliPath,
11827
+ "skillify",
11828
+ "mine-local",
11829
+ "--n",
11830
+ String(INSTALL_SCAN_SESSION_COUNT),
11831
+ // The install copy advertises a "Claude Code" scan, so filter
11832
+ // the mine-local picker to claude_code sessions. Without this,
11833
+ // mine-local walks every installed agent (Codex, Cursor,
11834
+ // Hermes, pi) and could surface an insight from a Codex
11835
+ // session despite what we promised — codex PR #198 P2.
11836
+ "--only",
11837
+ "claude_code"
11838
+ ], {
11839
+ stdio: ["ignore", "ignore", "ignore"],
11840
+ // HIVEMIND_CAPTURE=false: the spawned mine-local would otherwise
11841
+ // try to capture its own activity, which is a no-op without
11842
+ // credentials but spams the log. Keep it quiet.
11843
+ env: { ...process.env, HIVEMIND_CAPTURE: "false" }
11844
+ });
11845
+ let settled = false;
11846
+ const finish = (result) => {
11847
+ if (settled)
11848
+ return;
11849
+ settled = true;
11850
+ resolve6(result);
11851
+ };
11852
+ const timer = setTimeout(() => {
11853
+ try {
11854
+ child.kill("SIGKILL");
11855
+ } catch {
11856
+ }
11857
+ unlinkManifestIfCorrupt();
11858
+ finish({ insight: null, skillsCount: 0 });
11859
+ }, SCAN_TIMEOUT_MS);
11860
+ child.on("close", async (code) => {
11861
+ clearTimeout(timer);
11862
+ if (code !== 0) {
11863
+ unlinkManifestIfCorrupt();
11864
+ finish({ insight: null, skillsCount: 0 });
11865
+ return;
11866
+ }
11867
+ let advisorResult = null;
11868
+ try {
11869
+ advisorResult = await runAdvisor();
11870
+ } catch {
11871
+ }
11872
+ const advisorRejected = advisorResult !== null && advisorResult.pickedSkillName === null;
11873
+ let entry = null;
11874
+ if (!advisorRejected) {
11875
+ try {
11876
+ entry = getLatestInsightEntry();
11877
+ } catch {
11878
+ }
11879
+ }
11880
+ let skillsCount = 0;
11881
+ try {
11882
+ skillsCount = countLocalManifestEntries();
11883
+ } catch {
11884
+ }
11885
+ if (!entry && manifestIsTrulyEmpty()) {
11886
+ try {
11887
+ unlinkSync14(manifestPath2());
11888
+ } catch {
11889
+ }
11890
+ skillsCount = 0;
11891
+ }
11892
+ finish({ insight: entry, skillsCount });
11893
+ });
11894
+ child.on("error", () => {
11895
+ clearTimeout(timer);
11896
+ unlinkManifestIfCorrupt();
11897
+ finish({ insight: null, skillsCount: 0 });
11898
+ });
11899
+ });
11900
+ }
11901
+ function formatScanResult(entry) {
11902
+ const rawInsight = (entry.insight ?? "").replace(/\s+/g, " ").trim();
11903
+ const insight = rawInsight.length > 280 ? rawInsight.slice(0, 277).replace(/\s\S*$/, "") + "\u2026" : rawInsight;
11904
+ return `\u2713 Found a pattern in your past sessions:
11905
+ \u{1F4CC} ${insight}
11906
+ \u2728 Skill \`${entry.skill_name}\` ready to catch it next time`;
11907
+ }
11908
+
11539
11909
  // dist/src/cli/index.js
11540
11910
  var AUTH_SUBCOMMANDS = /* @__PURE__ */ new Set([
11541
11911
  "whoami",
@@ -11717,14 +12087,44 @@ async function runAuthGate(args) {
11717
12087
  log("Or run `hivemind login` after install.");
11718
12088
  return;
11719
12089
  }
12090
+ let foundInsight = null;
12091
+ if (canOfferInstallScan()) {
12092
+ log("");
12093
+ log("\u{1F41D} Want me to scan your recent Claude Code sessions for repeatable mistakes?");
12094
+ log("Takes 2-4 minutes. Scans 10 sessions in parallel using your Claude Code subscription.");
12095
+ log("");
12096
+ const scanOk = await confirm("Scan now?", true);
12097
+ if (scanOk) {
12098
+ log("");
12099
+ log("Scanning your 10 most-recent sessions (up to 5 min). Be patient \u2014 haiku is running in the background.");
12100
+ const { insight, skillsCount } = await runInstallScan();
12101
+ log("");
12102
+ if (insight && insight.insight && insight.insight.trim().length > 0) {
12103
+ log(formatScanResult(insight));
12104
+ foundInsight = { skill_name: insight.skill_name };
12105
+ } else if (skillsCount > 0) {
12106
+ log(`Mined ${skillsCount} skill${skillsCount === 1 ? "" : "s"} locally \u2014 they'll be available in your next claude session.`);
12107
+ log("(No banner-quality insight to surface here \u2014 the gate is conservative on what gets the top-line.)");
12108
+ } else {
12109
+ log("No repeatable patterns found in this scan. (That's OK \u2014 the gate is conservative.)");
12110
+ }
12111
+ }
12112
+ }
11720
12113
  log("");
11721
- log("\u{1F41D} One more step to unlock Hivemind");
11722
- log("");
11723
- log("To enable shared memory and auto-learning across your agents,");
11724
- log("we need to sign you in. Your traces will be securely stored in");
11725
- log("your private Hivemind, so all your agents can recall them.");
11726
- log("");
11727
- log("You can later connect your own cloud storage like S3/GCS/Azure Blob.");
12114
+ if (foundInsight) {
12115
+ log("\u{1F41D} Sign in to keep this skill across machines and share it with your team.");
12116
+ log("");
12117
+ log(`Without sign-in, \`${foundInsight.skill_name}\` lives only on this machine and`);
12118
+ log("won't follow you to a new laptop or be shared with teammates who'd benefit.");
12119
+ } else {
12120
+ log("\u{1F41D} One more step to unlock Hivemind");
12121
+ log("");
12122
+ log("To enable shared memory and auto-learning across your agents,");
12123
+ log("we need to sign you in. Your traces will be securely stored in");
12124
+ log("your private Hivemind, so all your agents can recall them.");
12125
+ log("");
12126
+ log("You can later connect your own cloud storage like S3/GCS/Azure Blob.");
12127
+ }
11728
12128
  log("");
11729
12129
  const yes = await confirm("Sign in now?", true);
11730
12130
  let signedIn = false;
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
259
259
  const creds = loadCredentials();
260
260
  if (!creds)
261
261
  throw new Error("Not logged in. Run deeplake login first.");
262
- saveCredentials({ ...creds, orgId, orgName });
262
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
263
+ const tokenName = `deeplake-plugin-switch-${Date.now()}`;
264
+ const tokenData = await apiPost("/users/me/tokens", {
265
+ name: tokenName,
266
+ duration: 365 * 24 * 3600,
267
+ organization_id: orgId
268
+ }, creds.token, apiUrl);
269
+ saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
263
270
  }
264
271
  async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
265
272
  const raw = await apiGet("/workspaces", token, apiUrl, orgId);
@@ -92,6 +92,65 @@ function loadCredentials() {
92
92
  return null;
93
93
  }
94
94
  }
95
+ function saveCredentials(creds) {
96
+ mkdirSync2(configDir(), { recursive: true, mode: 448 });
97
+ writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
98
+ }
99
+
100
+ // dist/src/commands/auth.js
101
+ var DEFAULT_API_URL = "https://api.deeplake.ai";
102
+ function decodeJwtPayload(token) {
103
+ try {
104
+ const parts = token.split(".");
105
+ if (parts.length !== 3)
106
+ return null;
107
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
108
+ while (payload.length % 4)
109
+ payload += "=";
110
+ return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ async function apiPost(path, body, token, apiUrl, orgId) {
116
+ const headers = {
117
+ Authorization: `Bearer ${token}`,
118
+ "Content-Type": "application/json",
119
+ ...deeplakeClientHeader()
120
+ };
121
+ if (orgId)
122
+ headers["X-Activeloop-Org-Id"] = orgId;
123
+ const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
124
+ if (!resp.ok)
125
+ throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
126
+ return resp.json();
127
+ }
128
+ async function healDriftedOrgToken(creds, log6 = () => {
129
+ }) {
130
+ if (!creds.token || !creds.orgId)
131
+ return creds;
132
+ const payload = decodeJwtPayload(creds.token);
133
+ const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
134
+ if (!claimOrg || claimOrg === creds.orgId)
135
+ return creds;
136
+ log6(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
137
+ try {
138
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
139
+ const tokenName = `deeplake-plugin-heal-${Date.now()}`;
140
+ const tokenData = await apiPost("/users/me/tokens", {
141
+ name: tokenName,
142
+ duration: 365 * 24 * 3600,
143
+ organization_id: creds.orgId
144
+ }, creds.token, apiUrl);
145
+ const healed = { ...creds, token: tokenData.token.token };
146
+ saveCredentials(healed);
147
+ log6(`token re-minted for org=${creds.orgId}`);
148
+ return healed;
149
+ } catch (err) {
150
+ log6(`token re-mint failed (continuing with stale token): ${err.message}`);
151
+ return creds;
152
+ }
153
+ }
95
154
 
96
155
  // dist/src/utils/stdin.js
97
156
  function readStdin() {
@@ -1827,13 +1886,14 @@ async function main() {
1827
1886
  if (process.env.HIVEMIND_WIKI_WORKER === "1")
1828
1887
  return;
1829
1888
  const input = await readStdin();
1830
- const creds = loadCredentials();
1889
+ let creds = loadCredentials();
1831
1890
  if (!creds?.token) {
1832
1891
  log5("no credentials found \u2014 run auth login to authenticate");
1833
1892
  const auto = maybeAutoMineLocal();
1834
1893
  log5(`auto-mine: ${auto.triggered ? "triggered (background)" : `skipped (${auto.reason})`}`);
1835
1894
  } else {
1836
1895
  log5(`credentials loaded: org=${creds.orgName ?? creds.orgId}`);
1896
+ creds = await healDriftedOrgToken(creds, log5);
1837
1897
  }
1838
1898
  if (creds?.token) {
1839
1899
  const setupScript = join17(__bundleDir, "session-start-setup.js");
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
259
259
  const creds = loadCredentials();
260
260
  if (!creds)
261
261
  throw new Error("Not logged in. Run deeplake login first.");
262
- saveCredentials({ ...creds, orgId, orgName });
262
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
263
+ const tokenName = `deeplake-plugin-switch-${Date.now()}`;
264
+ const tokenData = await apiPost("/users/me/tokens", {
265
+ name: tokenName,
266
+ duration: 365 * 24 * 3600,
267
+ organization_id: orgId
268
+ }, creds.token, apiUrl);
269
+ saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
263
270
  }
264
271
  async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
265
272
  const raw = await apiGet("/workspaces", token, apiUrl, orgId);
@@ -91,6 +91,65 @@ function loadCredentials() {
91
91
  return null;
92
92
  }
93
93
  }
94
+ function saveCredentials(creds) {
95
+ mkdirSync2(configDir(), { recursive: true, mode: 448 });
96
+ writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
97
+ }
98
+
99
+ // dist/src/commands/auth.js
100
+ var DEFAULT_API_URL = "https://api.deeplake.ai";
101
+ function decodeJwtPayload(token) {
102
+ try {
103
+ const parts = token.split(".");
104
+ if (parts.length !== 3)
105
+ return null;
106
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
107
+ while (payload.length % 4)
108
+ payload += "=";
109
+ return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ async function apiPost(path, body, token, apiUrl, orgId) {
115
+ const headers = {
116
+ Authorization: `Bearer ${token}`,
117
+ "Content-Type": "application/json",
118
+ ...deeplakeClientHeader()
119
+ };
120
+ if (orgId)
121
+ headers["X-Activeloop-Org-Id"] = orgId;
122
+ const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
123
+ if (!resp.ok)
124
+ throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
125
+ return resp.json();
126
+ }
127
+ async function healDriftedOrgToken(creds, log7 = () => {
128
+ }) {
129
+ if (!creds.token || !creds.orgId)
130
+ return creds;
131
+ const payload = decodeJwtPayload(creds.token);
132
+ const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
133
+ if (!claimOrg || claimOrg === creds.orgId)
134
+ return creds;
135
+ log7(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
136
+ try {
137
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
138
+ const tokenName = `deeplake-plugin-heal-${Date.now()}`;
139
+ const tokenData = await apiPost("/users/me/tokens", {
140
+ name: tokenName,
141
+ duration: 365 * 24 * 3600,
142
+ organization_id: creds.orgId
143
+ }, creds.token, apiUrl);
144
+ const healed = { ...creds, token: tokenData.token.token };
145
+ saveCredentials(healed);
146
+ log7(`token re-minted for org=${creds.orgId}`);
147
+ return healed;
148
+ } catch (err) {
149
+ log7(`token re-mint failed (continuing with stale token): ${err.message}`);
150
+ return creds;
151
+ }
152
+ }
94
153
 
95
154
  // dist/src/config.js
96
155
  import { readFileSync as readFileSync3, existsSync } from "node:fs";
@@ -2156,13 +2215,14 @@ async function main() {
2156
2215
  const input = await readStdin();
2157
2216
  const sessionId = resolveSessionId(input);
2158
2217
  const cwd = resolveCwd(input);
2159
- const creds = loadCredentials();
2218
+ let creds = loadCredentials();
2160
2219
  if (!creds?.token) {
2161
2220
  log6("no credentials found");
2162
2221
  const auto = maybeAutoMineLocal();
2163
2222
  log6(`auto-mine: ${auto.triggered ? "triggered (background)" : `skipped (${auto.reason})`}`);
2164
2223
  } else {
2165
2224
  log6(`credentials loaded: org=${creds.orgName ?? creds.orgId}`);
2225
+ creds = await healDriftedOrgToken(creds, log6);
2166
2226
  }
2167
2227
  await autoUpdate(creds, { agent: "cursor" });
2168
2228
  const current = getInstalledVersion(__bundleDir, ".claude-plugin");
@@ -259,7 +259,14 @@ async function switchOrg(orgId, orgName) {
259
259
  const creds = loadCredentials();
260
260
  if (!creds)
261
261
  throw new Error("Not logged in. Run deeplake login first.");
262
- saveCredentials({ ...creds, orgId, orgName });
262
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
263
+ const tokenName = `deeplake-plugin-switch-${Date.now()}`;
264
+ const tokenData = await apiPost("/users/me/tokens", {
265
+ name: tokenName,
266
+ duration: 365 * 24 * 3600,
267
+ organization_id: orgId
268
+ }, creds.token, apiUrl);
269
+ saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
263
270
  }
264
271
  async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
265
272
  const raw = await apiGet("/workspaces", token, apiUrl, orgId);
@@ -90,6 +90,65 @@ function loadCredentials() {
90
90
  return null;
91
91
  }
92
92
  }
93
+ function saveCredentials(creds) {
94
+ mkdirSync2(configDir(), { recursive: true, mode: 448 });
95
+ writeFileSync2(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
96
+ }
97
+
98
+ // dist/src/commands/auth.js
99
+ var DEFAULT_API_URL = "https://api.deeplake.ai";
100
+ function decodeJwtPayload(token) {
101
+ try {
102
+ const parts = token.split(".");
103
+ if (parts.length !== 3)
104
+ return null;
105
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
106
+ while (payload.length % 4)
107
+ payload += "=";
108
+ return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+ async function apiPost(path, body, token, apiUrl, orgId) {
114
+ const headers = {
115
+ Authorization: `Bearer ${token}`,
116
+ "Content-Type": "application/json",
117
+ ...deeplakeClientHeader()
118
+ };
119
+ if (orgId)
120
+ headers["X-Activeloop-Org-Id"] = orgId;
121
+ const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
122
+ if (!resp.ok)
123
+ throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
124
+ return resp.json();
125
+ }
126
+ async function healDriftedOrgToken(creds, log7 = () => {
127
+ }) {
128
+ if (!creds.token || !creds.orgId)
129
+ return creds;
130
+ const payload = decodeJwtPayload(creds.token);
131
+ const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
132
+ if (!claimOrg || claimOrg === creds.orgId)
133
+ return creds;
134
+ log7(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
135
+ try {
136
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
137
+ const tokenName = `deeplake-plugin-heal-${Date.now()}`;
138
+ const tokenData = await apiPost("/users/me/tokens", {
139
+ name: tokenName,
140
+ duration: 365 * 24 * 3600,
141
+ organization_id: creds.orgId
142
+ }, creds.token, apiUrl);
143
+ const healed = { ...creds, token: tokenData.token.token };
144
+ saveCredentials(healed);
145
+ log7(`token re-minted for org=${creds.orgId}`);
146
+ return healed;
147
+ } catch (err) {
148
+ log7(`token re-mint failed (continuing with stale token): ${err.message}`);
149
+ return creds;
150
+ }
151
+ }
93
152
 
94
153
  // dist/src/config.js
95
154
  import { readFileSync as readFileSync3, existsSync } from "node:fs";
@@ -2146,10 +2205,12 @@ async function main() {
2146
2205
  const input = await readStdin();
2147
2206
  const sessionId = input.session_id ?? `hermes-${Date.now()}`;
2148
2207
  const cwd = input.cwd ?? process.cwd();
2149
- const creds = loadCredentials();
2208
+ let creds = loadCredentials();
2150
2209
  const captureEnabled = process.env.HIVEMIND_CAPTURE !== "false";
2151
2210
  if (!creds?.token) {
2152
2211
  maybeAutoMineLocal();
2212
+ } else {
2213
+ creds = await healDriftedOrgToken(creds, log6);
2153
2214
  }
2154
2215
  await autoUpdate(creds, { agent: "hermes" });
2155
2216
  const current = getInstalledVersion(__bundleDir, ".claude-plugin");
@@ -52,6 +52,17 @@ function hivemindInstallIDHeader() {
52
52
 
53
53
  // src/commands/auth.ts
54
54
  var DEFAULT_API_URL = "https://api.deeplake.ai";
55
+ function decodeJwtPayload(token) {
56
+ try {
57
+ const parts = token.split(".");
58
+ if (parts.length !== 3) return null;
59
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
60
+ while (payload.length % 4) payload += "=";
61
+ return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
55
66
  async function apiGet(path, token, apiUrl, orgId) {
56
67
  const headers = {
57
68
  Authorization: `Bearer ${token}`,
@@ -63,6 +74,17 @@ async function apiGet(path, token, apiUrl, orgId) {
63
74
  if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
64
75
  return resp.json();
65
76
  }
77
+ async function apiPost(path, body, token, apiUrl, orgId) {
78
+ const headers = {
79
+ Authorization: `Bearer ${token}`,
80
+ "Content-Type": "application/json",
81
+ ...deeplakeClientHeader()
82
+ };
83
+ if (orgId) headers["X-Activeloop-Org-Id"] = orgId;
84
+ const resp = await fetch(`${apiUrl}${path}`, { method: "POST", headers, body: JSON.stringify(body) });
85
+ if (!resp.ok) throw new Error(`API ${resp.status}: ${await resp.text().catch(() => "")}`);
86
+ return resp.json();
87
+ }
66
88
  async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
67
89
  const resp = await fetch(`${apiUrl}/auth/device/code`, {
68
90
  method: "POST",
@@ -101,7 +123,38 @@ async function listOrgs(token, apiUrl = DEFAULT_API_URL) {
101
123
  async function switchOrg(orgId, orgName) {
102
124
  const creds = loadCredentials();
103
125
  if (!creds) throw new Error("Not logged in. Run deeplake login first.");
104
- saveCredentials({ ...creds, orgId, orgName });
126
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
127
+ const tokenName = `deeplake-plugin-switch-${Date.now()}`;
128
+ const tokenData = await apiPost("/users/me/tokens", {
129
+ name: tokenName,
130
+ duration: 365 * 24 * 3600,
131
+ organization_id: orgId
132
+ }, creds.token, apiUrl);
133
+ saveCredentials({ ...creds, orgId, orgName, token: tokenData.token.token });
134
+ }
135
+ async function healDriftedOrgToken(creds, log4 = () => {
136
+ }) {
137
+ if (!creds.token || !creds.orgId) return creds;
138
+ const payload = decodeJwtPayload(creds.token);
139
+ const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : void 0;
140
+ if (!claimOrg || claimOrg === creds.orgId) return creds;
141
+ log4(`token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} \u2014 re-minting`);
142
+ try {
143
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL;
144
+ const tokenName = `deeplake-plugin-heal-${Date.now()}`;
145
+ const tokenData = await apiPost("/users/me/tokens", {
146
+ name: tokenName,
147
+ duration: 365 * 24 * 3600,
148
+ organization_id: creds.orgId
149
+ }, creds.token, apiUrl);
150
+ const healed = { ...creds, token: tokenData.token.token };
151
+ saveCredentials(healed);
152
+ log4(`token re-minted for org=${creds.orgId}`);
153
+ return healed;
154
+ } catch (err) {
155
+ log4(`token re-mint failed (continuing with stale token): ${err.message}`);
156
+ return creds;
157
+ }
105
158
  }
106
159
  async function listWorkspaces(token, apiUrl = DEFAULT_API_URL, orgId) {
107
160
  const raw = await apiGet("/workspaces", token, apiUrl, orgId);
@@ -1727,7 +1780,7 @@ function extractLatestVersion(body) {
1727
1780
  return typeof v === "string" && v.length > 0 ? v : null;
1728
1781
  }
1729
1782
  function getInstalledVersion() {
1730
- return "0.7.51".length > 0 ? "0.7.51" : null;
1783
+ return "0.7.53".length > 0 ? "0.7.53" : null;
1731
1784
  }
1732
1785
  function isNewer(latest, current) {
1733
1786
  const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
@@ -1752,6 +1805,7 @@ async function checkForUpdate(logger) {
1752
1805
  }
1753
1806
  var authPending = false;
1754
1807
  var authUrl = null;
1808
+ var driftHealPromise = null;
1755
1809
  var pendingUpdate = null;
1756
1810
  var justAuthenticated = false;
1757
1811
  async function requestAuth() {
@@ -2010,6 +2064,16 @@ function normalizeVirtualPath(p) {
2010
2064
  }
2011
2065
  async function getApi() {
2012
2066
  if (api) return api;
2067
+ if (!driftHealPromise) {
2068
+ driftHealPromise = (async () => {
2069
+ try {
2070
+ const creds = await loadCredentials2();
2071
+ if (creds?.token) await healDriftedOrgToken(creds);
2072
+ } catch {
2073
+ }
2074
+ })();
2075
+ }
2076
+ await driftHealPromise;
2013
2077
  const config = await loadConfig();
2014
2078
  if (!config) {
2015
2079
  if (!authPending) await requestAuth();
@@ -54,5 +54,5 @@
54
54
  }
55
55
  }
56
56
  },
57
- "version": "0.7.51"
57
+ "version": "0.7.53"
58
58
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hivemind",
3
- "version": "0.7.51",
3
+ "version": "0.7.53",
4
4
  "type": "module",
5
5
  "description": "Hivemind — cloud-backed persistent shared memory for AI agents, powered by DeepLake",
6
6
  "license": "Apache-2.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeplake/hivemind",
3
- "version": "0.7.51",
3
+ "version": "0.7.53",
4
4
  "description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
5
5
  "type": "module",
6
6
  "repository": {
@@ -93,6 +93,52 @@ function loadCreds(): Creds | null {
93
93
  }
94
94
  }
95
95
 
96
+ // Inline copies of decodeJwtPayload + healDriftedOrgToken (the shared helpers
97
+ // live in src/commands/auth.ts, but pi extensions ship as raw .ts with no
98
+ // shared-module imports — kept in lockstep with that file).
99
+ function decodeJwtPayloadInline(token: string): Record<string, unknown> | null {
100
+ try {
101
+ const parts = token.split(".");
102
+ if (parts.length !== 3) return null;
103
+ let payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
104
+ while (payload.length % 4) payload += "=";
105
+ return JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
106
+ } catch { return null; }
107
+ }
108
+
109
+ async function healDriftedOrgTokenInline(creds: Creds): Promise<Creds> {
110
+ if (!creds.token || !creds.orgId) return creds;
111
+ const payload = decodeJwtPayloadInline(creds.token);
112
+ const claimOrg = payload && typeof payload.org_id === "string" ? payload.org_id : undefined;
113
+ if (!claimOrg || claimOrg === creds.orgId) return creds;
114
+ logHm(`session_start: token org drift detected: jwt.org_id=${claimOrg} creds.orgId=${creds.orgId} — re-minting`);
115
+ try {
116
+ const resp = await fetch(`${creds.apiUrl}/users/me/tokens`, {
117
+ method: "POST",
118
+ headers: { Authorization: `Bearer ${creds.token}`, "Content-Type": "application/json" },
119
+ body: JSON.stringify({
120
+ name: `deeplake-plugin-heal-${Date.now()}`,
121
+ duration: 365 * 24 * 3600,
122
+ organization_id: creds.orgId,
123
+ }),
124
+ });
125
+ if (!resp.ok) throw new Error(`API ${resp.status}: ${(await resp.text().catch(() => "")).slice(0, 200)}`);
126
+ const data = await resp.json() as { token: { token: string } };
127
+ const newToken = data.token.token;
128
+ // Read + merge + write the WHOLE creds file so we don't drop fields pi
129
+ // doesn't model (e.g. savedAt). Atomic via writeFileSync with mode 0o600.
130
+ const path = join(homedir(), ".deeplake", "credentials.json");
131
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
132
+ raw.token = newToken;
133
+ writeFileSync(path, JSON.stringify(raw, null, 2), { mode: 0o600 });
134
+ logHm(`session_start: token re-minted for org=${creds.orgId}`);
135
+ return { ...creds, token: newToken };
136
+ } catch (e: any) {
137
+ logHm(`session_start: token re-mint failed (continuing with stale token): ${e?.message ?? e}`);
138
+ return creds;
139
+ }
140
+ }
141
+
96
142
  const MEMORY_TABLE = process.env.HIVEMIND_TABLE ?? "memory";
97
143
  const SESSIONS_TABLE = process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions";
98
144
 
@@ -1076,11 +1122,12 @@ export default function hivemindExtension(pi: ExtensionAPI): void {
1076
1122
 
1077
1123
  pi.on("session_start", async (_event: any, _ctx: any) => {
1078
1124
  logHm(`session_start: fired (capture=${captureEnabled}, embed=${process.env.HIVEMIND_EMBEDDINGS !== "false"}, table=${SESSIONS_TABLE})`);
1079
- const creds = loadCreds();
1125
+ let creds = loadCreds();
1080
1126
  if (!creds) {
1081
1127
  logHm(`session_start: no credentials at ~/.deeplake/credentials.json — capture disabled this session`);
1082
1128
  } else {
1083
1129
  logHm(`session_start: creds org=${creds.orgName ?? creds.orgId} ws=${creds.workspaceId}`);
1130
+ creds = await healDriftedOrgTokenInline(creds);
1084
1131
  }
1085
1132
 
1086
1133
  // Centralized autoupdate: shells out to `hivemind update` (npm-based,