@memtensor/memos-local-openclaw-plugin 1.0.4-beta.13 → 1.0.4-beta.15

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/index.ts CHANGED
@@ -466,7 +466,10 @@ const memosLocalPlugin = {
466
466
  };
467
467
  const role = rawRole === "user" || rawRole === "assistant" || rawRole === "tool" || rawRole === "system" ? rawRole : undefined;
468
468
  const minScore = typeof rawMinScore === "number" ? Math.max(0.35, Math.min(1, rawMinScore)) : undefined;
469
- const searchScope = resolveMemorySearchScope(rawScope);
469
+ let searchScope = resolveMemorySearchScope(rawScope);
470
+ if (searchScope === "local" && ctx.config?.sharing?.enabled) {
471
+ searchScope = "all";
472
+ }
470
473
  const searchLimit = typeof maxResults === "number" ? Math.max(1, Math.min(20, Math.round(maxResults))) : 10;
471
474
 
472
475
  const agentId = currentAgentId;
@@ -1818,6 +1821,38 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
1818
1821
  ctx.log.debug(`auto-recall: query="${query.slice(0, 80)}"`);
1819
1822
 
1820
1823
  const result = await engine.search({ query, maxResults: 10, minScore: 0.45, ownerFilter: recallOwnerFilter });
1824
+
1825
+ // Hub fallback helper: search team shared memories when local search has no relevant results
1826
+ const hubFallback = async (): Promise<SearchHit[]> => {
1827
+ if (!ctx.config?.sharing?.enabled) return [];
1828
+ try {
1829
+ const hubResult = await hubSearchMemories(store, ctx, { query, maxResults: 10, scope: "all" });
1830
+ if (hubResult.hits.length === 0) return [];
1831
+ ctx.log.debug(`auto-recall: hub fallback returned ${hubResult.hits.length} hit(s)`);
1832
+ return hubResult.hits.map((h) => ({
1833
+ summary: h.summary,
1834
+ original_excerpt: h.excerpt || h.summary,
1835
+ ref: { sessionKey: "", chunkId: h.remoteHitId, turnId: "", seq: 0 },
1836
+ score: 0.9,
1837
+ taskId: null,
1838
+ skillId: null,
1839
+ origin: "hub-remote" as const,
1840
+ source: { ts: h.source.ts, role: h.source.role, sessionKey: "" },
1841
+ }));
1842
+ } catch (err) {
1843
+ ctx.log.debug(`auto-recall: hub fallback failed (${err})`);
1844
+ return [];
1845
+ }
1846
+ };
1847
+
1848
+ if (result.hits.length === 0) {
1849
+ // Local found nothing — try hub before giving up
1850
+ const hubHits = await hubFallback();
1851
+ if (hubHits.length > 0) {
1852
+ result.hits.push(...hubHits);
1853
+ ctx.log.debug(`auto-recall: local empty, using ${hubHits.length} hub hit(s)`);
1854
+ }
1855
+ }
1821
1856
  if (result.hits.length === 0) {
1822
1857
  ctx.log.debug("auto-recall: no memory candidates found");
1823
1858
  const dur = performance.now() - recallT0;
@@ -1881,22 +1916,36 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
1881
1916
  const indexSet = new Set(filterResult.relevant);
1882
1917
  filteredHits = result.hits.filter((_, i) => indexSet.has(i + 1));
1883
1918
  } else {
1884
- ctx.log.debug("auto-recall: LLM filter returned no relevant hits");
1885
- const dur = performance.now() - recallT0;
1886
- store.recordToolCall("memory_search", dur, true);
1887
- store.recordApiLog("memory_search", { type: "auto_recall", query }, JSON.stringify({
1888
- candidates: result.hits.map(h => ({ score: h.score, role: h.source.role, summary: h.summary, content: h.original_excerpt, origin: h.origin || "local" })),
1889
- filtered: []
1890
- }), dur, true);
1891
- if (query.length > 50) {
1892
- const noRecallHint =
1893
- "## Memory system ACTION REQUIRED\n\n" +
1894
- "Auto-recall found no relevant results for a long query. " +
1895
- "You MUST call `memory_search` now with a shortened query (2-5 key words) before answering. " +
1896
- "Do NOT skip this step. Do NOT answer without searching first.";
1897
- return { prependContext: noRecallHint };
1919
+ ctx.log.debug("auto-recall: LLM filter returned no relevant local hits, trying hub fallback");
1920
+ const hubHits = await hubFallback();
1921
+ if (hubHits.length > 0) {
1922
+ ctx.log.debug(`auto-recall: hub fallback provided ${hubHits.length} hit(s) after local filter yielded 0`);
1923
+ filteredHits = hubHits;
1924
+ } else {
1925
+ const dur = performance.now() - recallT0;
1926
+ store.recordToolCall("memory_search", dur, true);
1927
+ store.recordApiLog("memory_search", { type: "auto_recall", query }, JSON.stringify({
1928
+ candidates: result.hits.map(h => ({ score: h.score, role: h.source.role, summary: h.summary, content: h.original_excerpt, origin: h.origin || "local" })),
1929
+ filtered: []
1930
+ }), dur, true);
1931
+ if (query.length > 50) {
1932
+ const noRecallHint =
1933
+ "## Memory system — ACTION REQUIRED\n\n" +
1934
+ "Auto-recall found no relevant results for a long query. " +
1935
+ "You MUST call `memory_search` now with a shortened query (2-5 key words) before answering. " +
1936
+ "Do NOT skip this step. Do NOT answer without searching first.";
1937
+ return { prependContext: noRecallHint };
1938
+ }
1939
+ return;
1898
1940
  }
1899
- return;
1941
+ }
1942
+ }
1943
+
1944
+ if (!sufficient && filteredHits.length > 0 && ctx.config?.sharing?.enabled) {
1945
+ const hubSupp = await hubFallback();
1946
+ if (hubSupp.length > 0) {
1947
+ ctx.log.debug(`auto-recall: local insufficient, supplementing with ${hubSupp.length} hub hit(s)`);
1948
+ filteredHits.push(...hubSupp);
1900
1949
  }
1901
1950
  }
1902
1951
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memtensor/memos-local-openclaw-plugin",
3
- "version": "1.0.4-beta.13",
3
+ "version": "1.0.4-beta.15",
4
4
  "description": "MemOS Local memory plugin for OpenClaw — full-write, hybrid-recall, progressive retrieval",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/src/config.ts CHANGED
@@ -128,7 +128,8 @@ export function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, stateD
128
128
  userToken: cfg.sharing?.client?.userToken ?? "",
129
129
  teamToken: cfg.sharing?.client?.teamToken ?? "",
130
130
  pendingUserId: cfg.sharing?.client?.pendingUserId ?? "",
131
- } : { hubAddress: "", userToken: "", teamToken: "", pendingUserId: "" };
131
+ nickname: cfg.sharing?.client?.nickname ?? "",
132
+ } : { hubAddress: "", userToken: "", teamToken: "", pendingUserId: "", nickname: "" };
132
133
  return { enabled, role, hub, client, capabilities: sharingCapabilities };
133
134
  })(),
134
135
  };
package/src/hub/server.ts CHANGED
@@ -314,6 +314,7 @@ export class HubServer {
314
314
  { userId: user.id, username: user.username, role: user.role, status: user.status },
315
315
  this.authSecret,
316
316
  );
317
+ this.userManager.approveUser(user.id, token);
317
318
  return this.json(res, 200, { status: "active", userToken: token });
318
319
  }
319
320
  return this.json(res, 200, { status: user.status });
@@ -1490,7 +1490,10 @@ export class SqliteStore {
1490
1490
  const conditions: string[] = [];
1491
1491
  const params: unknown[] = [];
1492
1492
  if (opts.status) { conditions.push("status = ?"); params.push(opts.status); }
1493
- if (opts.owner) { conditions.push("owner = ?"); params.push(opts.owner); }
1493
+ if (opts.owner) {
1494
+ conditions.push("(owner = ? OR (owner = 'public' AND id IN (SELECT task_id FROM local_shared_tasks WHERE original_owner = ?)))");
1495
+ params.push(opts.owner, opts.owner);
1496
+ }
1494
1497
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1495
1498
 
1496
1499
  const countRow = this.db.prepare(`SELECT COUNT(*) as c FROM tasks ${whereClause}`).get(...params) as { c: number };
@@ -3599,14 +3599,16 @@ function selectSharingRole(role){
3599
3599
  var tp=document.getElementById('sharingTeamPanel');
3600
3600
  var ap=document.getElementById('sharingAdminPanel');
3601
3601
  if(role==='client'){
3602
- if(sp) sp.style.display='none';
3602
+ if(sp) { sp.style.display='none'; sp.innerHTML=''; }
3603
3603
  if(tp) tp.style.display='none';
3604
3604
  if(ap) ap.style.display='none';
3605
3605
  }else{
3606
- if(sp) sp.style.display='';
3606
+ if(sp) { sp.style.display='none'; sp.innerHTML=''; }
3607
3607
  if(tp) tp.style.display='';
3608
3608
  if(ap) ap.style.display='';
3609
3609
  }
3610
+ _lastSettingsFingerprint='';
3611
+ setTimeout(function(){ loadSharingStatus(true); },200);
3610
3612
  if(role==='hub'){
3611
3613
  var tk=document.getElementById('cfgHubTeamToken');
3612
3614
  if(!tk.value.trim()) tk.value=_genToken(18);
@@ -3719,6 +3721,7 @@ function switchView(view){
3719
3721
 
3720
3722
  function onMemoryScopeChange(){
3721
3723
  memorySearchScope=document.getElementById('memorySearchScope')?.value||'local';
3724
+ try{localStorage.setItem('memos_memorySearchScope',memorySearchScope);}catch(e){}
3722
3725
  currentPage=1;
3723
3726
  activeSession=null;activeRole='';
3724
3727
  _lastMemoriesFingerprint='';
@@ -3844,6 +3847,7 @@ function renderSharingSidebar(data){
3844
3847
  html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username||'-')+'</span>';
3845
3848
  if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
3846
3849
  html+='</div>';
3850
+ html+='<div style="margin-top:8px"><button class="btn btn-sm btn-primary" onclick="retryHubJoin()" style="font-size:11px">'+t('sharing.retryJoin')+'</button></div>';
3847
3851
  statusEl.innerHTML=html;
3848
3852
  hintEl.textContent=t('sharing.rejected.hint');
3849
3853
  }else if(conn.removed&&conn.user){
@@ -3852,6 +3856,7 @@ function renderSharingSidebar(data){
3852
3856
  html+='<span class="label">'+t('sharing.sidebar.identity')+'</span><span class="value">'+esc(conn.user.username||'-')+'</span>';
3853
3857
  if(conn.teamName) html+='<span class="label">'+t('sharing.team')+'</span><span class="value">'+esc(conn.teamName)+'</span>';
3854
3858
  html+='</div>';
3859
+ html+='<div style="margin-top:8px"><button class="btn btn-sm btn-primary" onclick="retryHubJoin()" style="font-size:11px">'+t('sharing.retryJoin')+'</button></div>';
3855
3860
  statusEl.innerHTML=html;
3856
3861
  hintEl.textContent=t('sharing.removed.hint');
3857
3862
  }else if(conn.connected&&conn.user){
@@ -3888,6 +3893,9 @@ function renderSharingSettings(data){
3888
3893
  if(!data||!data.enabled){
3889
3894
  statusEl.innerHTML='';teamEl.innerHTML='';adminEl.innerHTML='';
3890
3895
  if(panelsWrap) panelsWrap.style.display='none';
3896
+ var adminNavTab0=document.querySelector('.tab[data-view="admin"]');
3897
+ if(adminNavTab0) adminNavTab0.style.display='none';
3898
+ if(_activeView==='admin') switchView('memories');
3891
3899
  return;
3892
3900
  }
3893
3901
  if(panelsWrap) panelsWrap.style.display='';
@@ -3895,9 +3903,20 @@ function renderSharingSettings(data){
3895
3903
  var user=conn.user||{};
3896
3904
  var actualRole=data.role||_sharingRole||'client';
3897
3905
  if(data.role) _sharingRole=data.role;
3906
+ var prevIsAdmin=!!window._isHubAdmin;
3898
3907
  var isAdmin=(data.admin&&data.admin.canManageUsers)||(conn.connected&&user.role==='admin')||(actualRole==='hub');
3899
3908
  window._isHubAdmin=isAdmin;
3900
3909
  if(isAdmin) startAdminPoll();
3910
+ var adminNavTab=document.querySelector('.tab[data-view="admin"]');
3911
+ if(adminNavTab){
3912
+ var showTab=(actualRole==='hub')||(conn.connected);
3913
+ adminNavTab.style.display=showTab?'':'none';
3914
+ if(!showTab&&_activeView==='admin') switchView('memories');
3915
+ }
3916
+ if(prevIsAdmin&&!isAdmin&&_activeView==='admin'){
3917
+ _lastAdminFingerprint='';
3918
+ loadAdminData();
3919
+ }
3901
3920
  var hubAdminBtn=document.getElementById('hubAdminEntryBtn');
3902
3921
 
3903
3922
  if(actualRole==='hub'){
@@ -4418,7 +4437,14 @@ async function adminToggleRole(userId,newRole){
4418
4437
  try{
4419
4438
  var r=await fetch('/api/sharing/change-role',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userId:userId,role:newRole})});
4420
4439
  var d=await r.json();
4421
- if(d.ok){toast(t('toast.roleChanged'),'success');_lastAdminFingerprint='';loadAdminData();}
4440
+ if(d.ok){
4441
+ toast(t('toast.roleChanged'),'success');
4442
+ _lastAdminFingerprint='';
4443
+ _lastSettingsFingerprint='';
4444
+ _lastSidebarFingerprint='';
4445
+ await loadSharingStatus(false);
4446
+ loadAdminData();
4447
+ }
4422
4448
  else if(d.error==='cannot_demote_owner'){toast(t('admin.ownerHint'),'error');}
4423
4449
  else{toast(d.error||t('toast.roleChangeFail'),'error');}
4424
4450
  }catch(e){toast(t('toast.roleChangeFail')+': '+e.message,'error');}
@@ -8840,6 +8866,14 @@ async function checkForUpdate(){
8840
8866
  }
8841
8867
 
8842
8868
  /* ─── Init ─── */
8869
+ try{
8870
+ var savedScope=localStorage.getItem('memos_memorySearchScope');
8871
+ if(savedScope&&(savedScope==='local'||savedScope==='allLocal'||savedScope==='hub')){
8872
+ memorySearchScope=savedScope;
8873
+ var scopeEl=document.getElementById('memorySearchScope');
8874
+ if(scopeEl) scopeEl.value=savedScope;
8875
+ }
8876
+ }catch(e){}
8843
8877
  document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target.id==='modalOverlay')closeModal()});
8844
8878
  document.getElementById('searchInput').addEventListener('keydown',e=>{if(e.key==='Escape'){e.target.value='';currentPage=1;if(memorySearchScope==='hub')loadHubMemories();else loadMemories();}});
8845
8879
  applyI18n();