@deeplake/hivemind 0.7.52 → 0.7.54

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.52"
9
+ "version": "0.7.54"
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.52",
15
+ "version": "0.7.54",
16
16
  "source": {
17
17
  "source": "git-subdir",
18
18
  "url": "https://github.com/activeloopai/hivemind.git",
19
19
  "path": "claude-code",
20
- "sha": "f8a757482c192ad29790eb63a1be9d07c5633f18"
20
+ "sha": "0c6a32fe771809bd04f9ae7ff9790d216494afd5"
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.52",
4
+ "version": "0.7.54",
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);
@@ -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.52".length > 0 ? "0.7.52" : null;
1783
+ return "0.7.54".length > 0 ? "0.7.54" : 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.52"
57
+ "version": "0.7.54"
58
58
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hivemind",
3
- "version": "0.7.52",
3
+ "version": "0.7.54",
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.52",
3
+ "version": "0.7.54",
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,