@memtensor/memos-local-openclaw-plugin 1.0.4-beta.0 → 1.0.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memtensor/memos-local-openclaw-plugin",
3
- "version": "1.0.4-beta.0",
3
+ "version": "1.0.4-beta.2",
4
4
  "description": "MemOS Local memory plugin for OpenClaw — full-write, hybrid-recall, progressive retrieval",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -59,6 +59,74 @@ export async function getHubStatus(store: SqliteStore, config: MemosLocalConfig)
59
59
  const conn = store.getClientHubConnection();
60
60
  const hubAddress = conn?.hubUrl || config.sharing?.client?.hubAddress || "";
61
61
  const userToken = conn?.userToken || config.sharing?.client?.userToken || "";
62
+
63
+ if (conn && conn.userId && (!userToken || userToken === "")) {
64
+ const teamToken = config.sharing?.client?.teamToken ?? "";
65
+ if (hubAddress && teamToken) {
66
+ try {
67
+ const result = await hubRequestJson(normalizeHubUrl(hubAddress), "", "/api/v1/hub/join", {
68
+ method: "POST",
69
+ body: JSON.stringify({ teamToken, username: conn.username || "user" }),
70
+ }) as any;
71
+ if (result.status === "pending") {
72
+ return {
73
+ connected: false,
74
+ hubUrl: normalizeHubUrl(hubAddress),
75
+ user: {
76
+ id: conn.userId,
77
+ username: conn.username || "",
78
+ role: "member",
79
+ status: "pending",
80
+ groups: [],
81
+ },
82
+ };
83
+ }
84
+ if (result.status === "active" && result.userToken) {
85
+ store.setClientHubConnection({
86
+ hubUrl: normalizeHubUrl(hubAddress),
87
+ userId: String(result.userId),
88
+ username: conn.username || "",
89
+ userToken: result.userToken,
90
+ role: "member",
91
+ connectedAt: Date.now(),
92
+ });
93
+ const me = await hubRequestJson(normalizeHubUrl(hubAddress), result.userToken, "/api/v1/hub/me", { method: "GET" }) as any;
94
+ return {
95
+ connected: true,
96
+ hubUrl: normalizeHubUrl(hubAddress),
97
+ user: {
98
+ id: String(me.id),
99
+ username: String(me.username ?? ""),
100
+ role: String(me.role ?? "member") as UserRole,
101
+ status: String(me.status ?? "active"),
102
+ groups: Array.isArray(me.groups)
103
+ ? me.groups.map((group: any) => ({
104
+ id: String(group.id),
105
+ name: String(group.name),
106
+ description: typeof group.description === "string" ? group.description : undefined,
107
+ }))
108
+ : [],
109
+ },
110
+ };
111
+ }
112
+ if (result.status === "rejected") {
113
+ return {
114
+ connected: false,
115
+ hubUrl: normalizeHubUrl(hubAddress),
116
+ user: {
117
+ id: conn.userId,
118
+ username: conn.username || "",
119
+ role: "member",
120
+ status: "rejected",
121
+ groups: [],
122
+ },
123
+ };
124
+ }
125
+ } catch { /* fall through */ }
126
+ }
127
+ return { connected: false, user: null };
128
+ }
129
+
62
130
  if (!hubAddress || !userToken) {
63
131
  return { connected: false, user: null };
64
132
  }
@@ -107,6 +175,23 @@ export async function autoJoinHub(
107
175
  body: JSON.stringify({ teamToken, username, deviceName: hostname }),
108
176
  }) as any;
109
177
 
178
+ if (result.status === "pending") {
179
+ log.info(`Join request submitted, awaiting admin approval. userId=${result.userId}`);
180
+ store.setClientHubConnection({
181
+ hubUrl,
182
+ userId: String(result.userId),
183
+ username,
184
+ userToken: "",
185
+ role: "member",
186
+ connectedAt: Date.now(),
187
+ });
188
+ throw new PendingApprovalError(result.userId);
189
+ }
190
+
191
+ if (result.status === "rejected") {
192
+ throw new Error(`Join request was rejected by the Hub admin.`);
193
+ }
194
+
110
195
  if (!result.userToken) {
111
196
  throw new Error(`Hub join failed: ${JSON.stringify(result)}`);
112
197
  }
@@ -122,3 +207,12 @@ export async function autoJoinHub(
122
207
  });
123
208
  return store.getClientHubConnection()!;
124
209
  }
210
+
211
+ export class PendingApprovalError extends Error {
212
+ public readonly userId: string;
213
+ constructor(userId: string) {
214
+ super("Awaiting admin approval");
215
+ this.name = "PendingApprovalError";
216
+ this.userId = userId;
217
+ }
218
+ }
package/src/config.ts CHANGED
@@ -117,22 +117,22 @@ export function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, stateD
117
117
  : undefined;
118
118
  })(),
119
119
  } : undefined,
120
- sharing: {
121
- enabled: cfg.sharing?.enabled ?? false,
122
- role: cfg.sharing?.role ?? "client",
123
- hub: {
120
+ sharing: (() => {
121
+ const role = cfg.sharing?.role ?? "client";
122
+ const enabled = cfg.sharing?.enabled ?? false;
123
+ const hub = role === "hub" ? {
124
124
  port: cfg.sharing?.hub?.port ?? 18800,
125
125
  teamName: cfg.sharing?.hub?.teamName ?? "",
126
126
  teamToken: cfg.sharing?.hub?.teamToken ?? "",
127
- },
128
- client: {
127
+ } : { port: 18800, teamName: "", teamToken: "" };
128
+ const client = role === "client" ? {
129
129
  hubAddress: cfg.sharing?.client?.hubAddress ?? "",
130
130
  userToken: cfg.sharing?.client?.userToken ?? "",
131
131
  teamToken: cfg.sharing?.client?.teamToken ?? "",
132
132
  pendingUserId: cfg.sharing?.client?.pendingUserId ?? "",
133
- },
134
- capabilities: sharingCapabilities,
135
- },
133
+ } : { hubAddress: "", userToken: "", teamToken: "", pendingUserId: "" };
134
+ return { enabled, role, hub, client, capabilities: sharingCapabilities };
135
+ })(),
136
136
  };
137
137
  }
138
138
 
package/src/hub/server.ts CHANGED
@@ -192,17 +192,32 @@ export class HubServer {
192
192
  return this.json(res, 403, { error: "invalid_team_token" });
193
193
  }
194
194
  const username = String(body.username || `user-${randomUUID().slice(0, 8)}`);
195
+ const existingUsers = this.opts.store.listHubUsers();
196
+ const existingUser = existingUsers.find(u => u.username === username);
197
+ if (existingUser) {
198
+ if (existingUser.status === "active") {
199
+ const token = issueUserToken(
200
+ { userId: existingUser.id, username: existingUser.username, role: existingUser.role, status: "active" },
201
+ this.authSecret,
202
+ );
203
+ this.userManager.approveUser(existingUser.id, token);
204
+ return this.json(res, 200, { status: "active", userId: existingUser.id, userToken: token });
205
+ }
206
+ if (existingUser.status === "pending") {
207
+ return this.json(res, 200, { status: "pending", userId: existingUser.id });
208
+ }
209
+ if (existingUser.status === "rejected") {
210
+ this.userManager.resetToPending(existingUser.id);
211
+ this.opts.log.info(`Hub: rejected user "${username}" (${existingUser.id}) re-applied, reset to pending`);
212
+ return this.json(res, 200, { status: "pending", userId: existingUser.id });
213
+ }
214
+ }
195
215
  const user = this.userManager.createPendingUser({
196
216
  username,
197
217
  deviceName: typeof body.deviceName === "string" ? body.deviceName : undefined,
198
218
  });
199
- const token = issueUserToken(
200
- { userId: user.id, username, role: "member", status: "active" },
201
- this.authSecret,
202
- );
203
- this.userManager.approveUser(user.id, token);
204
- this.opts.log.info(`Hub: auto-approved user "${username}" (${user.id})`);
205
- return this.json(res, 200, { status: "active", userId: user.id, userToken: token });
219
+ this.opts.log.info(`Hub: user "${username}" (${user.id}) registered as pending, awaiting admin approval`);
220
+ return this.json(res, 200, { status: "pending", userId: user.id });
206
221
  }
207
222
 
208
223
  if (req.method === "POST" && routePath === "/api/v1/hub/registration-status") {
@@ -297,6 +312,7 @@ export class HubServer {
297
312
  // ── Group management ──
298
313
 
299
314
  if (req.method === "GET" && routePath === "/api/v1/hub/groups") {
315
+ if (auth.role !== "admin") return this.json(res, 403, { error: "forbidden" });
300
316
  const groups = this.opts.store.listHubGroups();
301
317
  return this.json(res, 200, { groups });
302
318
  }
@@ -321,6 +337,7 @@ export class HubServer {
321
337
  const groupId = decodeURIComponent(groupDetailMatch[1]);
322
338
 
323
339
  if (req.method === "GET") {
340
+ if (auth.role !== "admin") return this.json(res, 403, { error: "forbidden" });
324
341
  const group = this.opts.store.getHubGroupById(groupId);
325
342
  if (!group) return this.json(res, 404, { error: "not_found" });
326
343
  const members = this.opts.store.listHubGroupMembers(groupId);
@@ -408,15 +425,8 @@ export class HubServer {
408
425
  if (!sourceChunkId) return this.json(res, 400, { error: "missing_source_chunk_id" });
409
426
  const existing = this.opts.store.getHubMemoryBySource(auth.userId, sourceChunkId);
410
427
  const memoryId = existing?.id ?? randomUUID();
411
- const visibility = m.visibility === "group" ? "group" : "public";
412
- let resolvedGroupId: string | null = null;
413
- if (visibility === "group") {
414
- const gid = String(m.groupId || "");
415
- if (!gid) return this.json(res, 400, { error: "missing_group_id" });
416
- const group = this.opts.store.getHubGroupById(gid);
417
- if (!group) return this.json(res, 404, { error: "group_not_found" });
418
- resolvedGroupId = gid;
419
- }
428
+ const visibility = "public";
429
+ const resolvedGroupId: string | null = null;
420
430
  const now = Date.now();
421
431
  this.opts.store.upsertHubMemory({
422
432
  id: memoryId,
@@ -576,7 +586,7 @@ export class HubServer {
576
586
  if (!sourceSkillId) return this.json(res, 400, { error: "missing_skill_id" });
577
587
  const existing = this.opts.store.getHubSkillBySource(auth.userId, sourceSkillId);
578
588
  const skillId = existing?.id ?? randomUUID();
579
- const visibility = body?.visibility === "group" ? "group" : "public";
589
+ const visibility = "public";
580
590
  this.opts.store.upsertHubSkill({
581
591
  id: skillId,
582
592
  sourceSkillId,
@@ -584,7 +594,7 @@ export class HubServer {
584
594
  name: String(metadata.name || sourceSkillId),
585
595
  description: String(metadata.description || ""),
586
596
  version: Number(metadata.version || 1),
587
- groupId: visibility === "group" ? String(body?.groupId || "") || null : null,
597
+ groupId: null,
588
598
  visibility,
589
599
  bundle: JSON.stringify(body?.bundle ?? {}),
590
600
  qualityScore: metadata.qualityScore == null ? null : Number(metadata.qualityScore),
@@ -598,10 +608,6 @@ export class HubServer {
598
608
  if (skillBundleMatch) {
599
609
  const skill = this.opts.store.getHubSkillById(decodeURIComponent(skillBundleMatch[1]));
600
610
  if (!skill) return this.json(res, 404, { error: "not_found" });
601
- const user = this.opts.store.getHubUser(auth.userId);
602
- const groups = new Set((user?.groups ?? []).map((group) => group.id));
603
- const allowed = skill.visibility === "public" || (skill.groupId != null && groups.has(skill.groupId));
604
- if (!allowed) return this.json(res, 403, { error: "forbidden" });
605
611
  return this.json(res, 200, {
606
612
  skillId: skill.id,
607
613
  metadata: {
@@ -123,4 +123,17 @@ export class HubUserManager {
123
123
  this.store.upsertHubUser(updated);
124
124
  return updated;
125
125
  }
126
+
127
+ resetToPending(userId: string): ManagedHubUser | null {
128
+ const user = this.store.getHubUser(userId);
129
+ if (!user) return null;
130
+ const updated = {
131
+ ...user,
132
+ status: "pending" as const,
133
+ tokenHash: "",
134
+ approvedAt: null,
135
+ };
136
+ this.store.upsertHubUser(updated);
137
+ return updated;
138
+ }
126
139
  }
@@ -1874,24 +1874,16 @@ export class SqliteStore {
1874
1874
  const limit = options?.maxResults ?? 10;
1875
1875
  const userId = options?.userId ?? "";
1876
1876
  const rows = this.db.prepare(`
1877
- SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, hg.name as group_name, hu.username as owner_name,
1877
+ SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, '' as group_name, hu.username as owner_name,
1878
1878
  bm25(hub_chunks_fts) as rank
1879
1879
  FROM hub_chunks_fts f
1880
1880
  JOIN hub_chunks hc ON hc.rowid = f.rowid
1881
1881
  JOIN hub_tasks ht ON ht.id = hc.hub_task_id
1882
- LEFT JOIN hub_groups hg ON hg.id = ht.group_id
1883
1882
  LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
1884
1883
  WHERE hub_chunks_fts MATCH ?
1885
- AND (
1886
- ht.visibility = 'public'
1887
- OR EXISTS (
1888
- SELECT 1 FROM hub_group_members gm
1889
- WHERE gm.group_id = ht.group_id AND gm.user_id = ?
1890
- )
1891
- )
1892
1884
  ORDER BY rank
1893
1885
  LIMIT ?
1894
- `).all(sanitizeFtsQuery(query), userId, limit) as HubSearchRow[];
1886
+ `).all(sanitizeFtsQuery(query), limit) as HubSearchRow[];
1895
1887
  return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
1896
1888
  }
1897
1889
 
@@ -1916,12 +1908,7 @@ export class SqliteStore {
1916
1908
  FROM hub_embeddings he
1917
1909
  JOIN hub_chunks hc ON hc.id = he.chunk_id
1918
1910
  JOIN hub_tasks ht ON ht.id = hc.hub_task_id
1919
- WHERE ht.visibility = 'public'
1920
- OR EXISTS (
1921
- SELECT 1 FROM hub_group_members gm
1922
- WHERE gm.group_id = ht.group_id AND gm.user_id = ?
1923
- )
1924
- `).all(userId) as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
1911
+ `).all() as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
1925
1912
  return rows.map(r => ({
1926
1913
  chunkId: r.chunk_id,
1927
1914
  vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
@@ -1930,22 +1917,14 @@ export class SqliteStore {
1930
1917
 
1931
1918
  getVisibleHubSearchHitByChunkId(chunkId: string, userId: string): HubSearchRow | null {
1932
1919
  const row = this.db.prepare(`
1933
- SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, hg.name as group_name, hu.username as owner_name,
1920
+ SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, '' as group_name, hu.username as owner_name,
1934
1921
  0 as rank
1935
1922
  FROM hub_chunks hc
1936
1923
  JOIN hub_tasks ht ON ht.id = hc.hub_task_id
1937
- LEFT JOIN hub_groups hg ON hg.id = ht.group_id
1938
1924
  LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
1939
1925
  WHERE hc.id = ?
1940
- AND (
1941
- ht.visibility = 'public'
1942
- OR EXISTS (
1943
- SELECT 1 FROM hub_group_members gm
1944
- WHERE gm.group_id = ht.group_id AND gm.user_id = ?
1945
- )
1946
- )
1947
1926
  LIMIT 1
1948
- `).get(chunkId, userId) as HubSearchRow | undefined;
1927
+ `).get(chunkId) as HubSearchRow | undefined;
1949
1928
  return row ?? null;
1950
1929
  }
1951
1930
 
@@ -1961,38 +1940,24 @@ export class SqliteStore {
1961
1940
  let rows: HubSkillSearchRow[];
1962
1941
  if (sanitized) {
1963
1942
  rows = this.db.prepare(`
1964
- SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, hg.name AS group_name, hu.username AS owner_name, hs.quality_score,
1943
+ SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, '' AS group_name, hu.username AS owner_name, hs.quality_score,
1965
1944
  bm25(hub_skills_fts) as rank
1966
1945
  FROM hub_skills_fts f
1967
1946
  JOIN hub_skills hs ON hs.rowid = f.rowid
1968
- LEFT JOIN hub_groups hg ON hg.id = hs.group_id
1969
1947
  LEFT JOIN hub_users hu ON hu.id = hs.source_user_id
1970
1948
  WHERE hub_skills_fts MATCH ?
1971
- AND (
1972
- hs.visibility = 'public'
1973
- OR EXISTS (
1974
- SELECT 1 FROM hub_group_members gm
1975
- WHERE gm.group_id = hs.group_id AND gm.user_id = ?
1976
- )
1977
- )
1978
1949
  ORDER BY rank
1979
1950
  LIMIT ?
1980
- `).all(sanitized, userId, limit) as HubSkillSearchRow[];
1951
+ `).all(sanitized, limit) as HubSkillSearchRow[];
1981
1952
  } else {
1982
1953
  rows = this.db.prepare(`
1983
- SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, hg.name AS group_name, hu.username AS owner_name, hs.quality_score,
1954
+ SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, '' AS group_name, hu.username AS owner_name, hs.quality_score,
1984
1955
  0 as rank
1985
1956
  FROM hub_skills hs
1986
- LEFT JOIN hub_groups hg ON hg.id = hs.group_id
1987
1957
  LEFT JOIN hub_users hu ON hu.id = hs.source_user_id
1988
- WHERE hs.visibility = 'public'
1989
- OR EXISTS (
1990
- SELECT 1 FROM hub_group_members gm
1991
- WHERE gm.group_id = hs.group_id AND gm.user_id = ?
1992
- )
1993
1958
  ORDER BY hs.updated_at DESC
1994
1959
  LIMIT ?
1995
- `).all(userId, limit) as HubSkillSearchRow[];
1960
+ `).all(limit) as HubSkillSearchRow[];
1996
1961
  }
1997
1962
  return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
1998
1963
  }
@@ -2003,19 +1968,13 @@ export class SqliteStore {
2003
1968
 
2004
1969
  listVisibleHubTasks(userId: string, limit = 40): Array<{ id: string; sourceTaskId: string; sourceUserId: string; title: string; summary: string; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; chunkCount: number; createdAt: number; updatedAt: number }> {
2005
1970
  const rows = this.db.prepare(`
2006
- SELECT t.*, u.username AS owner_name, g.name AS group_name,
1971
+ SELECT t.*, u.username AS owner_name, NULL AS group_name,
2007
1972
  (SELECT COUNT(*) FROM hub_chunks c WHERE c.hub_task_id = t.id) AS chunk_count
2008
1973
  FROM hub_tasks t
2009
1974
  LEFT JOIN hub_users u ON u.id = t.source_user_id
2010
- LEFT JOIN hub_groups g ON g.id = t.group_id
2011
- WHERE t.visibility = 'public'
2012
- OR EXISTS (
2013
- SELECT 1 FROM hub_group_members gm
2014
- WHERE gm.group_id = t.group_id AND gm.user_id = ?
2015
- )
2016
1975
  ORDER BY t.updated_at DESC
2017
1976
  LIMIT ?
2018
- `).all(userId, limit) as any[];
1977
+ `).all(limit) as any[];
2019
1978
  return rows.map(r => ({
2020
1979
  id: r.id, sourceTaskId: r.source_task_id, sourceUserId: r.source_user_id,
2021
1980
  title: r.title, summary: r.summary, groupId: r.group_id, groupName: r.group_name ?? null,
@@ -2048,18 +2007,12 @@ export class SqliteStore {
2048
2007
 
2049
2008
  listVisibleHubSkills(userId: string, limit = 40): Array<{ id: string; sourceSkillId: string; sourceUserId: string; name: string; description: string; version: number; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; qualityScore: number | null; createdAt: number; updatedAt: number }> {
2050
2009
  const rows = this.db.prepare(`
2051
- SELECT s.*, u.username AS owner_name, g.name AS group_name
2010
+ SELECT s.*, u.username AS owner_name, NULL AS group_name
2052
2011
  FROM hub_skills s
2053
2012
  LEFT JOIN hub_users u ON u.id = s.source_user_id
2054
- LEFT JOIN hub_groups g ON g.id = s.group_id
2055
- WHERE s.visibility = 'public'
2056
- OR EXISTS (
2057
- SELECT 1 FROM hub_group_members gm
2058
- WHERE gm.group_id = s.group_id AND gm.user_id = ?
2059
- )
2060
2013
  ORDER BY s.updated_at DESC
2061
2014
  LIMIT ?
2062
- `).all(userId, limit) as any[];
2015
+ `).all(limit) as any[];
2063
2016
  return rows.map(r => ({
2064
2017
  id: r.id, sourceSkillId: r.source_skill_id, sourceUserId: r.source_user_id,
2065
2018
  name: r.name, description: r.description, version: r.version,
@@ -2149,23 +2102,15 @@ export class SqliteStore {
2149
2102
  const sanitized = sanitizeFtsQuery(query);
2150
2103
  if (!sanitized) return [];
2151
2104
  const rows = this.db.prepare(`
2152
- SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, hg.name as group_name, hu.username as owner_name,
2105
+ SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, '' as group_name, hu.username as owner_name,
2153
2106
  bm25(hub_memories_fts) as rank
2154
2107
  FROM hub_memories_fts f
2155
2108
  JOIN hub_memories hm ON hm.rowid = f.rowid
2156
- LEFT JOIN hub_groups hg ON hg.id = hm.group_id
2157
2109
  LEFT JOIN hub_users hu ON hu.id = hm.source_user_id
2158
2110
  WHERE hub_memories_fts MATCH ?
2159
- AND (
2160
- hm.visibility = 'public'
2161
- OR EXISTS (
2162
- SELECT 1 FROM hub_group_members gm
2163
- WHERE gm.group_id = hm.group_id AND gm.user_id = ?
2164
- )
2165
- )
2166
2111
  ORDER BY rank
2167
2112
  LIMIT ?
2168
- `).all(sanitized, userId, limit) as HubMemorySearchRow[];
2113
+ `).all(sanitized, limit) as HubMemorySearchRow[];
2169
2114
  return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
2170
2115
  }
2171
2116
 
@@ -2174,12 +2119,7 @@ export class SqliteStore {
2174
2119
  SELECT hme.memory_id, hme.vector, hme.dimensions
2175
2120
  FROM hub_memory_embeddings hme
2176
2121
  JOIN hub_memories hm ON hm.id = hme.memory_id
2177
- WHERE hm.visibility = 'public'
2178
- OR EXISTS (
2179
- SELECT 1 FROM hub_group_members gm
2180
- WHERE gm.group_id = hm.group_id AND gm.user_id = ?
2181
- )
2182
- `).all(userId) as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
2122
+ `).all() as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
2183
2123
  return rows.map(r => ({
2184
2124
  memoryId: r.memory_id,
2185
2125
  vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
@@ -2188,38 +2128,24 @@ export class SqliteStore {
2188
2128
 
2189
2129
  getVisibleHubSearchHitByMemoryId(memoryId: string, userId: string): HubMemorySearchRow | null {
2190
2130
  const row = this.db.prepare(`
2191
- SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, hg.name as group_name, hu.username as owner_name,
2131
+ SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, '' as group_name, hu.username as owner_name,
2192
2132
  0 as rank
2193
2133
  FROM hub_memories hm
2194
- LEFT JOIN hub_groups hg ON hg.id = hm.group_id
2195
2134
  LEFT JOIN hub_users hu ON hu.id = hm.source_user_id
2196
2135
  WHERE hm.id = ?
2197
- AND (
2198
- hm.visibility = 'public'
2199
- OR EXISTS (
2200
- SELECT 1 FROM hub_group_members gm
2201
- WHERE gm.group_id = hm.group_id AND gm.user_id = ?
2202
- )
2203
- )
2204
2136
  LIMIT 1
2205
- `).get(memoryId, userId) as HubMemorySearchRow | undefined;
2137
+ `).get(memoryId) as HubMemorySearchRow | undefined;
2206
2138
  return row ?? null;
2207
2139
  }
2208
2140
 
2209
2141
  listVisibleHubMemories(userId: string, limit = 40): Array<{ id: string; sourceChunkId: string; sourceUserId: string; role: string; summary: string; kind: string; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; createdAt: number; updatedAt: number }> {
2210
2142
  const rows = this.db.prepare(`
2211
- SELECT m.*, u.username AS owner_name, g.name AS group_name
2143
+ SELECT m.*, u.username AS owner_name, NULL AS group_name
2212
2144
  FROM hub_memories m
2213
2145
  LEFT JOIN hub_users u ON u.id = m.source_user_id
2214
- LEFT JOIN hub_groups g ON g.id = m.group_id
2215
- WHERE m.visibility = 'public'
2216
- OR EXISTS (
2217
- SELECT 1 FROM hub_group_members gm
2218
- WHERE gm.group_id = m.group_id AND gm.user_id = ?
2219
- )
2220
2146
  ORDER BY m.updated_at DESC
2221
2147
  LIMIT ?
2222
- `).all(userId, limit) as any[];
2148
+ `).all(limit) as any[];
2223
2149
  return rows.map(r => ({
2224
2150
  id: r.id, sourceChunkId: r.source_chunk_id, sourceUserId: r.source_user_id,
2225
2151
  role: r.role, summary: r.summary, kind: r.kind,