@deeplake/hivemind 0.7.53 → 0.7.60

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.53"
9
+ "version": "0.7.60"
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.53",
15
+ "version": "0.7.60",
16
16
  "source": {
17
17
  "source": "git-subdir",
18
18
  "url": "https://github.com/activeloopai/hivemind.git",
19
19
  "path": "claude-code",
20
- "sha": "2f9768a8c9ed54952bdc272261f2a527f5482fe2"
20
+ "sha": "16a718fb94219d96ad5686ca414504d694643bc9"
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.53",
4
+ "version": "0.7.60",
5
5
  "author": {
6
6
  "name": "Activeloop",
7
7
  "url": "https://deeplake.ai"
package/README.md CHANGED
@@ -279,6 +279,8 @@ This plugin captures session activity and stores it in your Deeplake workspace:
279
279
  | `HIVEMIND_SESSIONS_TABLE` | `sessions` | SQL table for per-event session capture |
280
280
  | `HIVEMIND_MEMORY_PATH` | `~/.deeplake/memory` | Path that triggers interception |
281
281
  | `HIVEMIND_CAPTURE` | `true` | Set to `false` to disable capture |
282
+ | `HIVEMIND_CAPTURE_ONLY_CLI` | — | Set to `true` to capture only interactive CLI sessions. Sessions spawned by the Claude Agent SDK (Python/TypeScript) are skipped — their `CLAUDE_CODE_ENTRYPOINT` is `sdk-py` / `sdk-ts`, so they fail the substring check for `cli`. |
283
+ | `HIVEMIND_SKILLIFY_EVERY_N_TURNS` | `20` | Assistant turns between auto skill-mining attempts. Lower = more frequent mining (cheaper sessions, noisier output); higher = fewer attempts on longer histories. |
282
284
  | `HIVEMIND_EMBEDDINGS` | `true` | Set to `false` to force lexical-only mode |
283
285
  | `HIVEMIND_DEBUG` | — | Set to `1` for verbose hook debug logs |
284
286
 
package/bundle/cli.js CHANGED
@@ -5094,6 +5094,23 @@ var DeeplakeApi = class {
5094
5094
  this._tablesCache = [...tables];
5095
5095
  return tables;
5096
5096
  }
5097
+ /**
5098
+ * Like listTables() but returns null when the list could NOT be trusted
5099
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
5100
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
5101
+ * failed lookup (null): on [] they can safely skip the read (no table → no
5102
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
5103
+ * lookup blip doesn't drop a read of a table that really exists.
5104
+ */
5105
+ async knownTablesOrNull() {
5106
+ if (this._tablesCache)
5107
+ return [...this._tablesCache];
5108
+ const { tables, cacheable } = await this._fetchTables();
5109
+ if (!cacheable)
5110
+ return null;
5111
+ this._tablesCache = [...tables];
5112
+ return [...tables];
5113
+ }
5097
5114
  async _fetchTables() {
5098
5115
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
5099
5116
  try {
@@ -9044,21 +9061,25 @@ async function runPull(opts) {
9044
9061
  skillName: opts.skillName
9045
9062
  });
9046
9063
  let rows = [];
9047
- try {
9048
- rows = await opts.query(sql);
9049
- } catch (e) {
9050
- if (isMissingTableError2(e?.message)) {
9051
- rows = [];
9052
- } else if (isMissingContributorsColumnError(e?.message)) {
9053
- const legacySql = buildPullSql({
9054
- tableName: opts.tableName,
9055
- users: opts.users,
9056
- skillName: opts.skillName,
9057
- includeContributors: false
9058
- });
9059
- rows = await opts.query(legacySql);
9060
- } else {
9061
- throw e;
9064
+ if (opts.tableExists && !opts.tableExists(opts.tableName)) {
9065
+ rows = [];
9066
+ } else {
9067
+ try {
9068
+ rows = await opts.query(sql);
9069
+ } catch (e) {
9070
+ if (isMissingTableError2(e?.message)) {
9071
+ rows = [];
9072
+ } else if (isMissingContributorsColumnError(e?.message)) {
9073
+ const legacySql = buildPullSql({
9074
+ tableName: opts.tableName,
9075
+ users: opts.users,
9076
+ skillName: opts.skillName,
9077
+ includeContributors: false
9078
+ });
9079
+ rows = await opts.query(legacySql);
9080
+ } else {
9081
+ throw e;
9082
+ }
9062
9083
  }
9063
9084
  }
9064
9085
  const latest = selectLatestPerName(rows);
@@ -11221,24 +11242,33 @@ async function renderContextBlock(query, input, opts = {}) {
11221
11242
  const log8 = opts.log ?? (() => {
11222
11243
  });
11223
11244
  try {
11245
+ const tableExists = opts.tableExists;
11224
11246
  let rules = [];
11225
- try {
11226
- rules = await listRules(query, input.rulesTable, {
11227
- status: "active",
11228
- limit: Math.max(maxRules * 4, maxRules + 1)
11229
- });
11230
- } catch (rulesErr) {
11231
- const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
11232
- log8(`render-context-block: rules unavailable (continuing): ${rmsg}`);
11247
+ if (tableExists && !tableExists(input.rulesTable)) {
11248
+ log8(`render-context-block: rules table "${input.rulesTable}" not present \u2014 skipping read`);
11249
+ } else {
11250
+ try {
11251
+ rules = await listRules(query, input.rulesTable, {
11252
+ status: "active",
11253
+ limit: Math.max(maxRules * 4, maxRules + 1)
11254
+ });
11255
+ } catch (rulesErr) {
11256
+ const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
11257
+ log8(`render-context-block: rules unavailable (continuing): ${rmsg}`);
11258
+ }
11233
11259
  }
11234
11260
  let goals = [];
11235
- try {
11236
- goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
11237
- limit: Math.max(maxGoals * 4, maxGoals + 1)
11238
- });
11239
- } catch (goalsErr) {
11240
- const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
11241
- log8(`render-context-block: goals unavailable (continuing): ${gmsg}`);
11261
+ if (tableExists && !tableExists(input.goalsTable)) {
11262
+ log8(`render-context-block: goals table "${input.goalsTable}" not present \u2014 skipping read`);
11263
+ } else {
11264
+ try {
11265
+ goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
11266
+ limit: Math.max(maxGoals * 4, maxGoals + 1)
11267
+ });
11268
+ } catch (goalsErr) {
11269
+ const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
11270
+ log8(`render-context-block: goals unavailable (continuing): ${gmsg}`);
11271
+ }
11242
11272
  }
11243
11273
  const rulesShown = rules.slice(0, maxRules);
11244
11274
  const rulesHidden = Math.max(0, rules.length - maxRules);
@@ -11353,11 +11383,13 @@ async function runContextCommand(args) {
11353
11383
  throw new Error("unreachable");
11354
11384
  }
11355
11385
  const api = new DeeplakeApi(cfg.token, cfg.apiUrl, cfg.orgId, cfg.workspaceId, cfg.tableName);
11386
+ const known = await api.knownTablesOrNull();
11387
+ const tableExists = known ? (name) => known.includes(name) : void 0;
11356
11388
  const block = await renderContextBlock((sql) => api.query(sql), {
11357
11389
  rulesTable: cfg.rulesTableName,
11358
11390
  goalsTable: cfg.goalsTableName,
11359
11391
  currentUser: cfg.userName
11360
- });
11392
+ }, { tableExists });
11361
11393
  if (!block) {
11362
11394
  console.error("(no active rules or open goals)");
11363
11395
  return;
@@ -756,6 +756,23 @@ var DeeplakeApi = class {
756
756
  this._tablesCache = [...tables];
757
757
  return tables;
758
758
  }
759
+ /**
760
+ * Like listTables() but returns null when the list could NOT be trusted
761
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
762
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
763
+ * failed lookup (null): on [] they can safely skip the read (no table → no
764
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
765
+ * lookup blip doesn't drop a read of a table that really exists.
766
+ */
767
+ async knownTablesOrNull() {
768
+ if (this._tablesCache)
769
+ return [...this._tablesCache];
770
+ const { tables, cacheable } = await this._fetchTables();
771
+ if (!cacheable)
772
+ return null;
773
+ this._tablesCache = [...tables];
774
+ return [...tables];
775
+ }
759
776
  async _fetchTables() {
760
777
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
761
778
  try {
@@ -1019,6 +1019,23 @@ var DeeplakeApi = class {
1019
1019
  this._tablesCache = [...tables];
1020
1020
  return tables;
1021
1021
  }
1022
+ /**
1023
+ * Like listTables() but returns null when the list could NOT be trusted
1024
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
1025
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
1026
+ * failed lookup (null): on [] they can safely skip the read (no table → no
1027
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
1028
+ * lookup blip doesn't drop a read of a table that really exists.
1029
+ */
1030
+ async knownTablesOrNull() {
1031
+ if (this._tablesCache)
1032
+ return [...this._tablesCache];
1033
+ const { tables, cacheable } = await this._fetchTables();
1034
+ if (!cacheable)
1035
+ return null;
1036
+ this._tablesCache = [...tables];
1037
+ return [...tables];
1038
+ }
1022
1039
  async _fetchTables() {
1023
1040
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1024
1041
  try {
@@ -746,6 +746,23 @@ var DeeplakeApi = class {
746
746
  this._tablesCache = [...tables];
747
747
  return tables;
748
748
  }
749
+ /**
750
+ * Like listTables() but returns null when the list could NOT be trusted
751
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
752
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
753
+ * failed lookup (null): on [] they can safely skip the read (no table → no
754
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
755
+ * lookup blip doesn't drop a read of a table that really exists.
756
+ */
757
+ async knownTablesOrNull() {
758
+ if (this._tablesCache)
759
+ return [...this._tablesCache];
760
+ const { tables, cacheable } = await this._fetchTables();
761
+ if (!cacheable)
762
+ return null;
763
+ this._tablesCache = [...tables];
764
+ return [...tables];
765
+ }
749
766
  async _fetchTables() {
750
767
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
751
768
  try {
@@ -762,6 +762,23 @@ var DeeplakeApi = class {
762
762
  this._tablesCache = [...tables];
763
763
  return tables;
764
764
  }
765
+ /**
766
+ * Like listTables() but returns null when the list could NOT be trusted
767
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
768
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
769
+ * failed lookup (null): on [] they can safely skip the read (no table → no
770
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
771
+ * lookup blip doesn't drop a read of a table that really exists.
772
+ */
773
+ async knownTablesOrNull() {
774
+ if (this._tablesCache)
775
+ return [...this._tablesCache];
776
+ const { tables, cacheable } = await this._fetchTables();
777
+ if (!cacheable)
778
+ return null;
779
+ this._tablesCache = [...tables];
780
+ return [...tables];
781
+ }
765
782
  async _fetchTables() {
766
783
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
767
784
  try {
@@ -757,6 +757,23 @@ var DeeplakeApi = class {
757
757
  this._tablesCache = [...tables];
758
758
  return tables;
759
759
  }
760
+ /**
761
+ * Like listTables() but returns null when the list could NOT be trusted
762
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
763
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
764
+ * failed lookup (null): on [] they can safely skip the read (no table → no
765
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
766
+ * lookup blip doesn't drop a read of a table that really exists.
767
+ */
768
+ async knownTablesOrNull() {
769
+ if (this._tablesCache)
770
+ return [...this._tablesCache];
771
+ const { tables, cacheable } = await this._fetchTables();
772
+ if (!cacheable)
773
+ return null;
774
+ this._tablesCache = [...tables];
775
+ return [...tables];
776
+ }
760
777
  async _fetchTables() {
761
778
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
762
779
  try {
@@ -996,6 +996,23 @@ var DeeplakeApi = class {
996
996
  this._tablesCache = [...tables];
997
997
  return tables;
998
998
  }
999
+ /**
1000
+ * Like listTables() but returns null when the list could NOT be trusted
1001
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
1002
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
1003
+ * failed lookup (null): on [] they can safely skip the read (no table → no
1004
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
1005
+ * lookup blip doesn't drop a read of a table that really exists.
1006
+ */
1007
+ async knownTablesOrNull() {
1008
+ if (this._tablesCache)
1009
+ return [...this._tablesCache];
1010
+ const { tables, cacheable } = await this._fetchTables();
1011
+ if (!cacheable)
1012
+ return null;
1013
+ this._tablesCache = [...tables];
1014
+ return [...tables];
1015
+ }
999
1016
  async _fetchTables() {
1000
1017
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1001
1018
  try {
@@ -1675,21 +1692,25 @@ async function runPull(opts) {
1675
1692
  skillName: opts.skillName
1676
1693
  });
1677
1694
  let rows = [];
1678
- try {
1679
- rows = await opts.query(sql);
1680
- } catch (e) {
1681
- if (isMissingTableError(e?.message)) {
1682
- rows = [];
1683
- } else if (isMissingContributorsColumnError(e?.message)) {
1684
- const legacySql = buildPullSql({
1685
- tableName: opts.tableName,
1686
- users: opts.users,
1687
- skillName: opts.skillName,
1688
- includeContributors: false
1689
- });
1690
- rows = await opts.query(legacySql);
1691
- } else {
1692
- throw e;
1695
+ if (opts.tableExists && !opts.tableExists(opts.tableName)) {
1696
+ rows = [];
1697
+ } else {
1698
+ try {
1699
+ rows = await opts.query(sql);
1700
+ } catch (e) {
1701
+ if (isMissingTableError(e?.message)) {
1702
+ rows = [];
1703
+ } else if (isMissingContributorsColumnError(e?.message)) {
1704
+ const legacySql = buildPullSql({
1705
+ tableName: opts.tableName,
1706
+ users: opts.users,
1707
+ skillName: opts.skillName,
1708
+ includeContributors: false
1709
+ });
1710
+ rows = await opts.query(legacySql);
1711
+ } else {
1712
+ throw e;
1713
+ }
1693
1714
  }
1694
1715
  }
1695
1716
  const latest = selectLatestPerName(rows);
@@ -1832,24 +1853,38 @@ async function autoPullSkills(deps = {}) {
1832
1853
  return { pulled: 0, skipped: true, reason: "not-logged-in" };
1833
1854
  }
1834
1855
  let query;
1856
+ let discoverTableExists = async () => void 0;
1835
1857
  if (deps.queryFn) {
1836
1858
  query = deps.queryFn;
1837
1859
  } else {
1838
1860
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.skillsTableName);
1839
1861
  query = (sql) => api.query(sql);
1862
+ discoverTableExists = async () => {
1863
+ const known = await api.knownTablesOrNull();
1864
+ return known ? (name) => known.includes(name) : void 0;
1865
+ };
1840
1866
  }
1841
1867
  const install = deps.install ?? "global";
1842
1868
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1843
1869
  try {
1844
- const summary = await withTimeout(runPull({
1845
- query,
1846
- tableName: config.skillsTableName,
1847
- install,
1848
- cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
1849
- users: [],
1850
- dryRun: false,
1851
- force: false
1852
- }), timeoutMs);
1870
+ const summary = await withTimeout(
1871
+ // Table discovery + pull share one budget: if `GET /tables` hangs the
1872
+ // whole thing times out and we degrade, instead of blocking startup.
1873
+ (async () => {
1874
+ const tableExists = await discoverTableExists();
1875
+ return runPull({
1876
+ query,
1877
+ tableName: config.skillsTableName,
1878
+ install,
1879
+ cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
1880
+ users: [],
1881
+ dryRun: false,
1882
+ force: false,
1883
+ tableExists
1884
+ });
1885
+ })(),
1886
+ timeoutMs
1887
+ );
1853
1888
  log4(`pulled scanned=${summary.scanned} wrote=${summary.wrote} skipped=${summary.skipped}`);
1854
1889
  return { pulled: summary.wrote, skipped: false };
1855
1890
  } catch (e) {
@@ -67450,6 +67450,23 @@ var DeeplakeApi = class {
67450
67450
  this._tablesCache = [...tables];
67451
67451
  return tables;
67452
67452
  }
67453
+ /**
67454
+ * Like listTables() but returns null when the list could NOT be trusted
67455
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
67456
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
67457
+ * failed lookup (null): on [] they can safely skip the read (no table → no
67458
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
67459
+ * lookup blip doesn't drop a read of a table that really exists.
67460
+ */
67461
+ async knownTablesOrNull() {
67462
+ if (this._tablesCache)
67463
+ return [...this._tablesCache];
67464
+ const { tables, cacheable } = await this._fetchTables();
67465
+ if (!cacheable)
67466
+ return null;
67467
+ this._tablesCache = [...tables];
67468
+ return [...tables];
67469
+ }
67453
67470
  async _fetchTables() {
67454
67471
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
67455
67472
  try {
@@ -761,6 +761,23 @@ var DeeplakeApi = class {
761
761
  this._tablesCache = [...tables];
762
762
  return tables;
763
763
  }
764
+ /**
765
+ * Like listTables() but returns null when the list could NOT be trusted
766
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
767
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
768
+ * failed lookup (null): on [] they can safely skip the read (no table → no
769
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
770
+ * lookup blip doesn't drop a read of a table that really exists.
771
+ */
772
+ async knownTablesOrNull() {
773
+ if (this._tablesCache)
774
+ return [...this._tablesCache];
775
+ const { tables, cacheable } = await this._fetchTables();
776
+ if (!cacheable)
777
+ return null;
778
+ this._tablesCache = [...tables];
779
+ return [...tables];
780
+ }
764
781
  async _fetchTables() {
765
782
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
766
783
  try {
@@ -756,6 +756,23 @@ var DeeplakeApi = class {
756
756
  this._tablesCache = [...tables];
757
757
  return tables;
758
758
  }
759
+ /**
760
+ * Like listTables() but returns null when the list could NOT be trusted
761
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
762
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
763
+ * failed lookup (null): on [] they can safely skip the read (no table → no
764
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
765
+ * lookup blip doesn't drop a read of a table that really exists.
766
+ */
767
+ async knownTablesOrNull() {
768
+ if (this._tablesCache)
769
+ return [...this._tablesCache];
770
+ const { tables, cacheable } = await this._fetchTables();
771
+ if (!cacheable)
772
+ return null;
773
+ this._tablesCache = [...tables];
774
+ return [...tables];
775
+ }
759
776
  async _fetchTables() {
760
777
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
761
778
  try {
@@ -1019,6 +1019,23 @@ var DeeplakeApi = class {
1019
1019
  this._tablesCache = [...tables];
1020
1020
  return tables;
1021
1021
  }
1022
+ /**
1023
+ * Like listTables() but returns null when the list could NOT be trusted
1024
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
1025
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
1026
+ * failed lookup (null): on [] they can safely skip the read (no table → no
1027
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
1028
+ * lookup blip doesn't drop a read of a table that really exists.
1029
+ */
1030
+ async knownTablesOrNull() {
1031
+ if (this._tablesCache)
1032
+ return [...this._tablesCache];
1033
+ const { tables, cacheable } = await this._fetchTables();
1034
+ if (!cacheable)
1035
+ return null;
1036
+ this._tablesCache = [...tables];
1037
+ return [...tables];
1038
+ }
1022
1039
  async _fetchTables() {
1023
1040
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1024
1041
  try {
@@ -746,6 +746,23 @@ var DeeplakeApi = class {
746
746
  this._tablesCache = [...tables];
747
747
  return tables;
748
748
  }
749
+ /**
750
+ * Like listTables() but returns null when the list could NOT be trusted
751
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
752
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
753
+ * failed lookup (null): on [] they can safely skip the read (no table → no
754
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
755
+ * lookup blip doesn't drop a read of a table that really exists.
756
+ */
757
+ async knownTablesOrNull() {
758
+ if (this._tablesCache)
759
+ return [...this._tablesCache];
760
+ const { tables, cacheable } = await this._fetchTables();
761
+ if (!cacheable)
762
+ return null;
763
+ this._tablesCache = [...tables];
764
+ return [...tables];
765
+ }
749
766
  async _fetchTables() {
750
767
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
751
768
  try {
@@ -755,6 +755,23 @@ var DeeplakeApi = class {
755
755
  this._tablesCache = [...tables];
756
756
  return tables;
757
757
  }
758
+ /**
759
+ * Like listTables() but returns null when the list could NOT be trusted
760
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
761
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
762
+ * failed lookup (null): on [] they can safely skip the read (no table → no
763
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
764
+ * lookup blip doesn't drop a read of a table that really exists.
765
+ */
766
+ async knownTablesOrNull() {
767
+ if (this._tablesCache)
768
+ return [...this._tablesCache];
769
+ const { tables, cacheable } = await this._fetchTables();
770
+ if (!cacheable)
771
+ return null;
772
+ this._tablesCache = [...tables];
773
+ return [...tables];
774
+ }
758
775
  async _fetchTables() {
759
776
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
760
777
  try {
@@ -811,6 +811,23 @@ var DeeplakeApi = class {
811
811
  this._tablesCache = [...tables];
812
812
  return tables;
813
813
  }
814
+ /**
815
+ * Like listTables() but returns null when the list could NOT be trusted
816
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
817
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
818
+ * failed lookup (null): on [] they can safely skip the read (no table → no
819
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
820
+ * lookup blip doesn't drop a read of a table that really exists.
821
+ */
822
+ async knownTablesOrNull() {
823
+ if (this._tablesCache)
824
+ return [...this._tablesCache];
825
+ const { tables, cacheable } = await this._fetchTables();
826
+ if (!cacheable)
827
+ return null;
828
+ this._tablesCache = [...tables];
829
+ return [...tables];
830
+ }
814
831
  async _fetchTables() {
815
832
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
816
833
  try {
@@ -1056,24 +1073,33 @@ async function renderContextBlock(query, input, opts = {}) {
1056
1073
  const log7 = opts.log ?? (() => {
1057
1074
  });
1058
1075
  try {
1076
+ const tableExists = opts.tableExists;
1059
1077
  let rules = [];
1060
- try {
1061
- rules = await listRules(query, input.rulesTable, {
1062
- status: "active",
1063
- limit: Math.max(maxRules * 4, maxRules + 1)
1064
- });
1065
- } catch (rulesErr) {
1066
- const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
1067
- log7(`render-context-block: rules unavailable (continuing): ${rmsg}`);
1078
+ if (tableExists && !tableExists(input.rulesTable)) {
1079
+ log7(`render-context-block: rules table "${input.rulesTable}" not present \u2014 skipping read`);
1080
+ } else {
1081
+ try {
1082
+ rules = await listRules(query, input.rulesTable, {
1083
+ status: "active",
1084
+ limit: Math.max(maxRules * 4, maxRules + 1)
1085
+ });
1086
+ } catch (rulesErr) {
1087
+ const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
1088
+ log7(`render-context-block: rules unavailable (continuing): ${rmsg}`);
1089
+ }
1068
1090
  }
1069
1091
  let goals = [];
1070
- try {
1071
- goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
1072
- limit: Math.max(maxGoals * 4, maxGoals + 1)
1073
- });
1074
- } catch (goalsErr) {
1075
- const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
1076
- log7(`render-context-block: goals unavailable (continuing): ${gmsg}`);
1092
+ if (tableExists && !tableExists(input.goalsTable)) {
1093
+ log7(`render-context-block: goals table "${input.goalsTable}" not present \u2014 skipping read`);
1094
+ } else {
1095
+ try {
1096
+ goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
1097
+ limit: Math.max(maxGoals * 4, maxGoals + 1)
1098
+ });
1099
+ } catch (goalsErr) {
1100
+ const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
1101
+ log7(`render-context-block: goals unavailable (continuing): ${gmsg}`);
1102
+ }
1077
1103
  }
1078
1104
  const rulesShown = rules.slice(0, maxRules);
1079
1105
  const rulesHidden = Math.max(0, rules.length - maxRules);
@@ -1912,21 +1938,25 @@ async function runPull(opts) {
1912
1938
  skillName: opts.skillName
1913
1939
  });
1914
1940
  let rows = [];
1915
- try {
1916
- rows = await opts.query(sql);
1917
- } catch (e) {
1918
- if (isMissingTableError(e?.message)) {
1919
- rows = [];
1920
- } else if (isMissingContributorsColumnError(e?.message)) {
1921
- const legacySql = buildPullSql({
1922
- tableName: opts.tableName,
1923
- users: opts.users,
1924
- skillName: opts.skillName,
1925
- includeContributors: false
1926
- });
1927
- rows = await opts.query(legacySql);
1928
- } else {
1929
- throw e;
1941
+ if (opts.tableExists && !opts.tableExists(opts.tableName)) {
1942
+ rows = [];
1943
+ } else {
1944
+ try {
1945
+ rows = await opts.query(sql);
1946
+ } catch (e) {
1947
+ if (isMissingTableError(e?.message)) {
1948
+ rows = [];
1949
+ } else if (isMissingContributorsColumnError(e?.message)) {
1950
+ const legacySql = buildPullSql({
1951
+ tableName: opts.tableName,
1952
+ users: opts.users,
1953
+ skillName: opts.skillName,
1954
+ includeContributors: false
1955
+ });
1956
+ rows = await opts.query(legacySql);
1957
+ } else {
1958
+ throw e;
1959
+ }
1930
1960
  }
1931
1961
  }
1932
1962
  const latest = selectLatestPerName(rows);
@@ -2069,24 +2099,38 @@ async function autoPullSkills(deps = {}) {
2069
2099
  return { pulled: 0, skipped: true, reason: "not-logged-in" };
2070
2100
  }
2071
2101
  let query;
2102
+ let discoverTableExists = async () => void 0;
2072
2103
  if (deps.queryFn) {
2073
2104
  query = deps.queryFn;
2074
2105
  } else {
2075
2106
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.skillsTableName);
2076
2107
  query = (sql) => api.query(sql);
2108
+ discoverTableExists = async () => {
2109
+ const known = await api.knownTablesOrNull();
2110
+ return known ? (name) => known.includes(name) : void 0;
2111
+ };
2077
2112
  }
2078
2113
  const install = deps.install ?? "global";
2079
2114
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2080
2115
  try {
2081
- const summary = await withTimeout(runPull({
2082
- query,
2083
- tableName: config.skillsTableName,
2084
- install,
2085
- cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
2086
- users: [],
2087
- dryRun: false,
2088
- force: false
2089
- }), timeoutMs);
2116
+ const summary = await withTimeout(
2117
+ // Table discovery + pull share one budget: if `GET /tables` hangs the
2118
+ // whole thing times out and we degrade, instead of blocking startup.
2119
+ (async () => {
2120
+ const tableExists = await discoverTableExists();
2121
+ return runPull({
2122
+ query,
2123
+ tableName: config.skillsTableName,
2124
+ install,
2125
+ cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
2126
+ users: [],
2127
+ dryRun: false,
2128
+ force: false,
2129
+ tableExists
2130
+ });
2131
+ })(),
2132
+ timeoutMs
2133
+ );
2090
2134
  log5(`pulled scanned=${summary.scanned} wrote=${summary.wrote} skipped=${summary.skipped}`);
2091
2135
  return { pulled: summary.wrote, skipped: false };
2092
2136
  } catch (e) {
@@ -2244,11 +2288,13 @@ async function main() {
2244
2288
  } else {
2245
2289
  log6("placeholder + schema ensure skipped (HIVEMIND_CAPTURE=false)");
2246
2290
  }
2291
+ const known = await api.knownTablesOrNull();
2292
+ const tableExists = known ? (name) => known.includes(name) : void 0;
2247
2293
  rulesBlock = await renderContextBlock((sql) => api.query(sql), {
2248
2294
  rulesTable: config.rulesTableName,
2249
2295
  goalsTable: config.goalsTableName,
2250
2296
  currentUser: config.userName
2251
- }, { log: log6 });
2297
+ }, { log: log6, tableExists });
2252
2298
  }
2253
2299
  } catch (e) {
2254
2300
  log6(`placeholder failed: ${e.message}`);
@@ -67450,6 +67450,23 @@ var DeeplakeApi = class {
67450
67450
  this._tablesCache = [...tables];
67451
67451
  return tables;
67452
67452
  }
67453
+ /**
67454
+ * Like listTables() but returns null when the list could NOT be trusted
67455
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
67456
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
67457
+ * failed lookup (null): on [] they can safely skip the read (no table → no
67458
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
67459
+ * lookup blip doesn't drop a read of a table that really exists.
67460
+ */
67461
+ async knownTablesOrNull() {
67462
+ if (this._tablesCache)
67463
+ return [...this._tablesCache];
67464
+ const { tables, cacheable } = await this._fetchTables();
67465
+ if (!cacheable)
67466
+ return null;
67467
+ this._tablesCache = [...tables];
67468
+ return [...tables];
67469
+ }
67453
67470
  async _fetchTables() {
67454
67471
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
67455
67472
  try {
@@ -755,6 +755,23 @@ var DeeplakeApi = class {
755
755
  this._tablesCache = [...tables];
756
756
  return tables;
757
757
  }
758
+ /**
759
+ * Like listTables() but returns null when the list could NOT be trusted
760
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
761
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
762
+ * failed lookup (null): on [] they can safely skip the read (no table → no
763
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
764
+ * lookup blip doesn't drop a read of a table that really exists.
765
+ */
766
+ async knownTablesOrNull() {
767
+ if (this._tablesCache)
768
+ return [...this._tablesCache];
769
+ const { tables, cacheable } = await this._fetchTables();
770
+ if (!cacheable)
771
+ return null;
772
+ this._tablesCache = [...tables];
773
+ return [...tables];
774
+ }
758
775
  async _fetchTables() {
759
776
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
760
777
  try {
@@ -1019,6 +1019,23 @@ var DeeplakeApi = class {
1019
1019
  this._tablesCache = [...tables];
1020
1020
  return tables;
1021
1021
  }
1022
+ /**
1023
+ * Like listTables() but returns null when the list could NOT be trusted
1024
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
1025
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
1026
+ * failed lookup (null): on [] they can safely skip the read (no table → no
1027
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
1028
+ * lookup blip doesn't drop a read of a table that really exists.
1029
+ */
1030
+ async knownTablesOrNull() {
1031
+ if (this._tablesCache)
1032
+ return [...this._tablesCache];
1033
+ const { tables, cacheable } = await this._fetchTables();
1034
+ if (!cacheable)
1035
+ return null;
1036
+ this._tablesCache = [...tables];
1037
+ return [...tables];
1038
+ }
1022
1039
  async _fetchTables() {
1023
1040
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1024
1041
  try {
@@ -746,6 +746,23 @@ var DeeplakeApi = class {
746
746
  this._tablesCache = [...tables];
747
747
  return tables;
748
748
  }
749
+ /**
750
+ * Like listTables() but returns null when the list could NOT be trusted
751
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
752
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
753
+ * failed lookup (null): on [] they can safely skip the read (no table → no
754
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
755
+ * lookup blip doesn't drop a read of a table that really exists.
756
+ */
757
+ async knownTablesOrNull() {
758
+ if (this._tablesCache)
759
+ return [...this._tablesCache];
760
+ const { tables, cacheable } = await this._fetchTables();
761
+ if (!cacheable)
762
+ return null;
763
+ this._tablesCache = [...tables];
764
+ return [...tables];
765
+ }
749
766
  async _fetchTables() {
750
767
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
751
768
  try {
@@ -755,6 +755,23 @@ var DeeplakeApi = class {
755
755
  this._tablesCache = [...tables];
756
756
  return tables;
757
757
  }
758
+ /**
759
+ * Like listTables() but returns null when the list could NOT be trusted
760
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
761
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
762
+ * failed lookup (null): on [] they can safely skip the read (no table → no
763
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
764
+ * lookup blip doesn't drop a read of a table that really exists.
765
+ */
766
+ async knownTablesOrNull() {
767
+ if (this._tablesCache)
768
+ return [...this._tablesCache];
769
+ const { tables, cacheable } = await this._fetchTables();
770
+ if (!cacheable)
771
+ return null;
772
+ this._tablesCache = [...tables];
773
+ return [...tables];
774
+ }
758
775
  async _fetchTables() {
759
776
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
760
777
  try {
@@ -810,6 +810,23 @@ var DeeplakeApi = class {
810
810
  this._tablesCache = [...tables];
811
811
  return tables;
812
812
  }
813
+ /**
814
+ * Like listTables() but returns null when the list could NOT be trusted
815
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
816
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
817
+ * failed lookup (null): on [] they can safely skip the read (no table → no
818
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
819
+ * lookup blip doesn't drop a read of a table that really exists.
820
+ */
821
+ async knownTablesOrNull() {
822
+ if (this._tablesCache)
823
+ return [...this._tablesCache];
824
+ const { tables, cacheable } = await this._fetchTables();
825
+ if (!cacheable)
826
+ return null;
827
+ this._tablesCache = [...tables];
828
+ return [...tables];
829
+ }
813
830
  async _fetchTables() {
814
831
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
815
832
  try {
@@ -1055,24 +1072,33 @@ async function renderContextBlock(query, input, opts = {}) {
1055
1072
  const log7 = opts.log ?? (() => {
1056
1073
  });
1057
1074
  try {
1075
+ const tableExists = opts.tableExists;
1058
1076
  let rules = [];
1059
- try {
1060
- rules = await listRules(query, input.rulesTable, {
1061
- status: "active",
1062
- limit: Math.max(maxRules * 4, maxRules + 1)
1063
- });
1064
- } catch (rulesErr) {
1065
- const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
1066
- log7(`render-context-block: rules unavailable (continuing): ${rmsg}`);
1077
+ if (tableExists && !tableExists(input.rulesTable)) {
1078
+ log7(`render-context-block: rules table "${input.rulesTable}" not present \u2014 skipping read`);
1079
+ } else {
1080
+ try {
1081
+ rules = await listRules(query, input.rulesTable, {
1082
+ status: "active",
1083
+ limit: Math.max(maxRules * 4, maxRules + 1)
1084
+ });
1085
+ } catch (rulesErr) {
1086
+ const rmsg = rulesErr instanceof Error ? rulesErr.message : String(rulesErr);
1087
+ log7(`render-context-block: rules unavailable (continuing): ${rmsg}`);
1088
+ }
1067
1089
  }
1068
1090
  let goals = [];
1069
- try {
1070
- goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
1071
- limit: Math.max(maxGoals * 4, maxGoals + 1)
1072
- });
1073
- } catch (goalsErr) {
1074
- const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
1075
- log7(`render-context-block: goals unavailable (continuing): ${gmsg}`);
1091
+ if (tableExists && !tableExists(input.goalsTable)) {
1092
+ log7(`render-context-block: goals table "${input.goalsTable}" not present \u2014 skipping read`);
1093
+ } else {
1094
+ try {
1095
+ goals = await listOpenGoals(query, input.goalsTable, input.currentUser, {
1096
+ limit: Math.max(maxGoals * 4, maxGoals + 1)
1097
+ });
1098
+ } catch (goalsErr) {
1099
+ const gmsg = goalsErr instanceof Error ? goalsErr.message : String(goalsErr);
1100
+ log7(`render-context-block: goals unavailable (continuing): ${gmsg}`);
1101
+ }
1076
1102
  }
1077
1103
  const rulesShown = rules.slice(0, maxRules);
1078
1104
  const rulesHidden = Math.max(0, rules.length - maxRules);
@@ -1911,21 +1937,25 @@ async function runPull(opts) {
1911
1937
  skillName: opts.skillName
1912
1938
  });
1913
1939
  let rows = [];
1914
- try {
1915
- rows = await opts.query(sql);
1916
- } catch (e) {
1917
- if (isMissingTableError(e?.message)) {
1918
- rows = [];
1919
- } else if (isMissingContributorsColumnError(e?.message)) {
1920
- const legacySql = buildPullSql({
1921
- tableName: opts.tableName,
1922
- users: opts.users,
1923
- skillName: opts.skillName,
1924
- includeContributors: false
1925
- });
1926
- rows = await opts.query(legacySql);
1927
- } else {
1928
- throw e;
1940
+ if (opts.tableExists && !opts.tableExists(opts.tableName)) {
1941
+ rows = [];
1942
+ } else {
1943
+ try {
1944
+ rows = await opts.query(sql);
1945
+ } catch (e) {
1946
+ if (isMissingTableError(e?.message)) {
1947
+ rows = [];
1948
+ } else if (isMissingContributorsColumnError(e?.message)) {
1949
+ const legacySql = buildPullSql({
1950
+ tableName: opts.tableName,
1951
+ users: opts.users,
1952
+ skillName: opts.skillName,
1953
+ includeContributors: false
1954
+ });
1955
+ rows = await opts.query(legacySql);
1956
+ } else {
1957
+ throw e;
1958
+ }
1929
1959
  }
1930
1960
  }
1931
1961
  const latest = selectLatestPerName(rows);
@@ -2068,24 +2098,38 @@ async function autoPullSkills(deps = {}) {
2068
2098
  return { pulled: 0, skipped: true, reason: "not-logged-in" };
2069
2099
  }
2070
2100
  let query;
2101
+ let discoverTableExists = async () => void 0;
2071
2102
  if (deps.queryFn) {
2072
2103
  query = deps.queryFn;
2073
2104
  } else {
2074
2105
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.skillsTableName);
2075
2106
  query = (sql) => api.query(sql);
2107
+ discoverTableExists = async () => {
2108
+ const known = await api.knownTablesOrNull();
2109
+ return known ? (name) => known.includes(name) : void 0;
2110
+ };
2076
2111
  }
2077
2112
  const install = deps.install ?? "global";
2078
2113
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2079
2114
  try {
2080
- const summary = await withTimeout(runPull({
2081
- query,
2082
- tableName: config.skillsTableName,
2083
- install,
2084
- cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
2085
- users: [],
2086
- dryRun: false,
2087
- force: false
2088
- }), timeoutMs);
2115
+ const summary = await withTimeout(
2116
+ // Table discovery + pull share one budget: if `GET /tables` hangs the
2117
+ // whole thing times out and we degrade, instead of blocking startup.
2118
+ (async () => {
2119
+ const tableExists = await discoverTableExists();
2120
+ return runPull({
2121
+ query,
2122
+ tableName: config.skillsTableName,
2123
+ install,
2124
+ cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
2125
+ users: [],
2126
+ dryRun: false,
2127
+ force: false,
2128
+ tableExists
2129
+ });
2130
+ })(),
2131
+ timeoutMs
2132
+ );
2089
2133
  log5(`pulled scanned=${summary.scanned} wrote=${summary.wrote} skipped=${summary.skipped}`);
2090
2134
  return { pulled: summary.wrote, skipped: false };
2091
2135
  } catch (e) {
@@ -2229,11 +2273,13 @@ async function main() {
2229
2273
  } else {
2230
2274
  log6("placeholder + schema ensure skipped (HIVEMIND_CAPTURE=false)");
2231
2275
  }
2276
+ const known = await api.knownTablesOrNull();
2277
+ const tableExists = known ? (name) => known.includes(name) : void 0;
2232
2278
  rulesBlock = await renderContextBlock((sql) => api.query(sql), {
2233
2279
  rulesTable: config.rulesTableName,
2234
2280
  goalsTable: config.goalsTableName,
2235
2281
  currentUser: config.userName
2236
- }, { log: log6 });
2282
+ }, { log: log6, tableExists });
2237
2283
  }
2238
2284
  } catch (e) {
2239
2285
  log6(`placeholder failed: ${e.message}`);
@@ -67450,6 +67450,23 @@ var DeeplakeApi = class {
67450
67450
  this._tablesCache = [...tables];
67451
67451
  return tables;
67452
67452
  }
67453
+ /**
67454
+ * Like listTables() but returns null when the list could NOT be trusted
67455
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
67456
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
67457
+ * failed lookup (null): on [] they can safely skip the read (no table → no
67458
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
67459
+ * lookup blip doesn't drop a read of a table that really exists.
67460
+ */
67461
+ async knownTablesOrNull() {
67462
+ if (this._tablesCache)
67463
+ return [...this._tablesCache];
67464
+ const { tables, cacheable } = await this._fetchTables();
67465
+ if (!cacheable)
67466
+ return null;
67467
+ this._tablesCache = [...tables];
67468
+ return [...tables];
67469
+ }
67453
67470
  async _fetchTables() {
67454
67471
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
67455
67472
  try {
@@ -23961,6 +23961,23 @@ var DeeplakeApi = class {
23961
23961
  this._tablesCache = [...tables];
23962
23962
  return tables;
23963
23963
  }
23964
+ /**
23965
+ * Like listTables() but returns null when the list could NOT be trusted
23966
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
23967
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
23968
+ * failed lookup (null): on [] they can safely skip the read (no table → no
23969
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
23970
+ * lookup blip doesn't drop a read of a table that really exists.
23971
+ */
23972
+ async knownTablesOrNull() {
23973
+ if (this._tablesCache)
23974
+ return [...this._tablesCache];
23975
+ const { tables, cacheable } = await this._fetchTables();
23976
+ if (!cacheable)
23977
+ return null;
23978
+ this._tablesCache = [...tables];
23979
+ return [...tables];
23980
+ }
23964
23981
  async _fetchTables() {
23965
23982
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
23966
23983
  try {
@@ -772,6 +772,21 @@ var DeeplakeApi = class {
772
772
  if (cacheable) this._tablesCache = [...tables];
773
773
  return tables;
774
774
  }
775
+ /**
776
+ * Like listTables() but returns null when the list could NOT be trusted
777
+ * (the fetch failed / was non-cacheable). Callers gating a read on table
778
+ * existence use this to tell a genuinely-empty workspace ([]) apart from a
779
+ * failed lookup (null): on [] they can safely skip the read (no table → no
780
+ * 42P01), on null they must fall back to SELECT-then-catch so a transient
781
+ * lookup blip doesn't drop a read of a table that really exists.
782
+ */
783
+ async knownTablesOrNull() {
784
+ if (this._tablesCache) return [...this._tablesCache];
785
+ const { tables, cacheable } = await this._fetchTables();
786
+ if (!cacheable) return null;
787
+ this._tablesCache = [...tables];
788
+ return [...tables];
789
+ }
775
790
  async _fetchTables() {
776
791
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
777
792
  try {
@@ -1780,7 +1795,7 @@ function extractLatestVersion(body) {
1780
1795
  return typeof v === "string" && v.length > 0 ? v : null;
1781
1796
  }
1782
1797
  function getInstalledVersion() {
1783
- return "0.7.53".length > 0 ? "0.7.53" : null;
1798
+ return "0.7.60".length > 0 ? "0.7.60" : null;
1784
1799
  }
1785
1800
  function isNewer(latest, current) {
1786
1801
  const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
@@ -54,5 +54,5 @@
54
54
  }
55
55
  }
56
56
  },
57
- "version": "0.7.53"
57
+ "version": "0.7.60"
58
58
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hivemind",
3
- "version": "0.7.53",
3
+ "version": "0.7.60",
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.53",
3
+ "version": "0.7.60",
4
4
  "description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
5
5
  "type": "module",
6
6
  "repository": {
@@ -41,7 +41,9 @@
41
41
  "dup": "jscpd src",
42
42
  "audit:openclaw": "node scripts/audit-openclaw-bundle.mjs",
43
43
  "pack:check": "node scripts/pack-check.mjs",
44
+ "rebuild:native": "node scripts/ensure-tree-sitter.mjs",
44
45
  "ci": "npm run typecheck && npm run dup && npm test",
46
+ "postinstall": "node scripts/ensure-tree-sitter.mjs",
45
47
  "prepare": "husky && npm run build",
46
48
  "prepack": "npm run build"
47
49
  },
@@ -58,11 +60,13 @@
58
60
  "deeplake": "^0.3.30",
59
61
  "js-yaml": "^4.1.1",
60
62
  "just-bash": "^2.14.0",
61
- "tree-sitter": "^0.21.1",
62
- "tree-sitter-typescript": "^0.23.2",
63
63
  "yargs-parser": "^22.0.0",
64
64
  "zod": "^4.3.6"
65
65
  },
66
+ "optionalDependencies": {
67
+ "tree-sitter": "^0.21.1",
68
+ "tree-sitter-typescript": "^0.23.2"
69
+ },
66
70
  "devDependencies": {
67
71
  "@types/js-yaml": "^4.0.9",
68
72
  "@types/node": "^25.0.0",