@memtensor/memos-local-openclaw-plugin 1.0.2-beta.3 → 1.0.2-beta.4

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.
Files changed (50) hide show
  1. package/dist/capture/index.d.ts.map +1 -1
  2. package/dist/capture/index.js +41 -1
  3. package/dist/capture/index.js.map +1 -1
  4. package/dist/embedding/index.d.ts.map +1 -1
  5. package/dist/embedding/index.js +20 -7
  6. package/dist/embedding/index.js.map +1 -1
  7. package/dist/ingest/providers/anthropic.d.ts.map +1 -1
  8. package/dist/ingest/providers/anthropic.js +28 -13
  9. package/dist/ingest/providers/anthropic.js.map +1 -1
  10. package/dist/ingest/providers/bedrock.d.ts.map +1 -1
  11. package/dist/ingest/providers/bedrock.js +28 -13
  12. package/dist/ingest/providers/bedrock.js.map +1 -1
  13. package/dist/ingest/providers/gemini.d.ts.map +1 -1
  14. package/dist/ingest/providers/gemini.js +28 -13
  15. package/dist/ingest/providers/gemini.js.map +1 -1
  16. package/dist/ingest/providers/index.d.ts +19 -0
  17. package/dist/ingest/providers/index.d.ts.map +1 -1
  18. package/dist/ingest/providers/index.js +98 -10
  19. package/dist/ingest/providers/index.js.map +1 -1
  20. package/dist/ingest/providers/openai.d.ts.map +1 -1
  21. package/dist/ingest/providers/openai.js +28 -13
  22. package/dist/ingest/providers/openai.js.map +1 -1
  23. package/dist/ingest/worker.d.ts.map +1 -1
  24. package/dist/ingest/worker.js +8 -14
  25. package/dist/ingest/worker.js.map +1 -1
  26. package/dist/storage/sqlite.d.ts +14 -0
  27. package/dist/storage/sqlite.d.ts.map +1 -1
  28. package/dist/storage/sqlite.js +42 -0
  29. package/dist/storage/sqlite.js.map +1 -1
  30. package/dist/viewer/html.d.ts +1 -1
  31. package/dist/viewer/html.d.ts.map +1 -1
  32. package/dist/viewer/html.js +113 -0
  33. package/dist/viewer/html.js.map +1 -1
  34. package/dist/viewer/server.d.ts +3 -0
  35. package/dist/viewer/server.d.ts.map +1 -1
  36. package/dist/viewer/server.js +92 -14
  37. package/dist/viewer/server.js.map +1 -1
  38. package/index.ts +38 -85
  39. package/package.json +1 -1
  40. package/src/capture/index.ts +56 -1
  41. package/src/embedding/index.ts +13 -7
  42. package/src/ingest/providers/anthropic.ts +28 -13
  43. package/src/ingest/providers/bedrock.ts +28 -13
  44. package/src/ingest/providers/gemini.ts +28 -13
  45. package/src/ingest/providers/index.ts +112 -9
  46. package/src/ingest/providers/openai.ts +28 -13
  47. package/src/ingest/worker.ts +8 -15
  48. package/src/storage/sqlite.ts +49 -0
  49. package/src/viewer/html.ts +113 -0
  50. package/src/viewer/server.ts +92 -16
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,qgnQAiqIf,CAAC"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,w1yQAkxIf,CAAC"}
@@ -529,6 +529,28 @@ input,textarea,select{font-family:inherit;font-size:inherit}
529
529
  [data-theme="light"] .settings-actions .btn-primary:hover{background:rgba(79,70,229,.1);border-color:#4f46e5}
530
530
  .settings-saved{display:inline-flex;align-items:center;gap:6px;color:var(--green);font-size:12px;font-weight:600;opacity:0;transition:opacity .3s}
531
531
  .settings-saved.show{opacity:1}
532
+ .model-health-bar{margin-bottom:20px;border-radius:var(--radius-lg);overflow:hidden}
533
+ .mh-table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}
534
+ .mh-table th{text-align:left;padding:6px 12px;font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);border-bottom:1px solid var(--border)}
535
+ .mh-table td{padding:8px 12px;border-bottom:1px solid var(--border);vertical-align:middle}
536
+ .mh-table tr:last-child td{border-bottom:none}
537
+ .mh-table tr:hover td{background:rgba(99,102,241,.025)}
538
+ .mh-table .mh-cell-name{display:flex;align-items:center;gap:8px;font-weight:500;color:var(--text)}
539
+ .mh-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;display:inline-block}
540
+ .mh-dot.ok{background:#22c55e;box-shadow:0 0 0 2px rgba(34,197,94,.15)}
541
+ .mh-dot.degraded{background:#f59e0b;box-shadow:0 0 0 2px rgba(245,158,11,.15)}
542
+ .mh-dot.error{background:#ef4444;box-shadow:0 0 0 2px rgba(239,68,68,.15);animation:healthPulse 2s ease infinite}
543
+ .mh-dot.unknown{background:#94a3b8;box-shadow:0 0 0 2px rgba(148,163,184,.15)}
544
+ .mh-badge{display:inline-block;padding:2px 7px;border-radius:10px;font-size:10px;font-weight:600;letter-spacing:.02em}
545
+ .mh-badge.ok{background:rgba(34,197,94,.1);color:#16a34a}
546
+ .mh-badge.degraded{background:rgba(245,158,11,.1);color:#d97706}
547
+ .mh-badge.error{background:rgba(239,68,68,.1);color:#dc2626}
548
+ .mh-badge.unknown{background:rgba(148,163,184,.1);color:#64748b}
549
+ .mh-model-name{color:var(--text-muted);font-size:11px;font-family:var(--font-mono,'SFMono-Regular',Consolas,monospace)}
550
+ .mh-err-text{font-size:11px;color:var(--rose);max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:help}
551
+ .mh-time{font-size:10px;color:var(--text-muted);white-space:nowrap}
552
+ .mh-empty{padding:16px;font-size:12px;color:var(--text-muted);text-align:center}
553
+ @keyframes healthPulse{0%,100%{opacity:1}50%{opacity:.4}}
532
554
  .migrate-log-item{display:flex;align-items:flex-start;gap:10px;padding:8px 14px;border-bottom:1px solid var(--border);animation:migrateFadeIn .3s ease}
533
555
  .migrate-log-item:last-child{border-bottom:none}
534
556
  .migrate-log-item .log-icon{flex-shrink:0;width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;margin-top:2px}
@@ -943,6 +965,9 @@ input,textarea,select{font-family:inherit;font-size:inherit}
943
965
  <div class="settings-view" id="settingsView">
944
966
  <div class="settings-group" id="settingsModelConfig">
945
967
  <h2 class="settings-group-title"><span data-i18n="settings.modelconfig">Model Configuration</span></h2>
968
+ <div class="model-health-bar" id="modelHealthBar">
969
+ <div style="font-size:12px;color:var(--text-muted);width:100%">Loading model status...</div>
970
+ </div>
946
971
  <div class="settings-section">
947
972
  <h3><span class="icon">\u{1F4E1}</span> <span data-i18n="settings.embedding">Embedding Model</span></h3>
948
973
  <div class="settings-grid">
@@ -2068,6 +2093,7 @@ function switchView(view){
2068
2093
  } else if(view==='settings'){
2069
2094
  settingsView.classList.add('show');
2070
2095
  loadConfig();
2096
+ loadModelHealth();
2071
2097
  } else if(view==='import'){
2072
2098
  migrateView.classList.add('show');
2073
2099
  if(!window._migrateRunning) migrateScan();
@@ -2749,6 +2775,93 @@ async function toggleSkillPublic(id,setPublic){
2749
2775
  }
2750
2776
  }
2751
2777
 
2778
+ /* ─── Model Health Status ─── */
2779
+
2780
+ const HEALTH_ROLE_LABELS={
2781
+ 'embedding':'Embedding',
2782
+ 'summarize':'Summarizer',
2783
+ 'filterRelevant':'Memory Filter',
2784
+ 'judgeDedup':'Dedup Judge',
2785
+ 'summarizeTask':'Task Summarizer',
2786
+ 'judgeNewTopic':'Topic Judge'
2787
+ };
2788
+
2789
+ function classifyError(msg){
2790
+ if(!msg) return '';
2791
+ if(msg.indexOf('\u989D\u5EA6\u5DF2\u7528\u5C3D')>=0||msg.indexOf('quota')>=0||msg.indexOf('RemainQuota')>=0) return 'API quota exhausted';
2792
+ if(msg.indexOf('401')>=0||msg.indexOf('Unauthorized')>=0) return 'Auth failed (401)';
2793
+ if(msg.indexOf('timeout')>=0||msg.indexOf('Timeout')>=0) return 'Request timed out';
2794
+ if(msg.indexOf('429')>=0) return 'Rate limited (429)';
2795
+ if(msg.indexOf('ECONNREFUSED')>=0) return 'Connection refused';
2796
+ if(msg.indexOf('ENOTFOUND')>=0) return 'DNS resolution failed';
2797
+ if(msg.indexOf('403')>=0) return 'Forbidden (403)';
2798
+ return msg.length>50?msg.slice(0,47)+'...':msg;
2799
+ }
2800
+
2801
+ function shortenModel(s){return s?s.replace('openai_compatible/','').replace('openai/',''):'\u2014';}
2802
+
2803
+ async function loadModelHealth(){
2804
+ var bar=document.getElementById('modelHealthBar');
2805
+ if(!bar) return;
2806
+ try{
2807
+ var r=await fetch('/api/model-health');
2808
+ if(!r.ok){bar.innerHTML='<div class="mh-empty">Health data unavailable</div>';return;}
2809
+ var d=await r.json();
2810
+ var models=d.models||[];
2811
+ if(models.length===0){
2812
+ bar.innerHTML='<div class="mh-empty">No model calls recorded yet</div>';
2813
+ return;
2814
+ }
2815
+ var order=['embedding','summarize','filterRelevant','judgeDedup','summarizeTask','judgeNewTopic'];
2816
+ models.sort(function(a,b){var ai=order.indexOf(a.role),bi=order.indexOf(b.role);if(ai<0)ai=99;if(bi<0)bi=99;return ai-bi;});
2817
+
2818
+ var h='<table class="mh-table"><thead><tr>';
2819
+ h+='<th style="width:30px"></th><th>Role</th><th>Status</th><th>Model</th><th>Issue</th><th style="text-align:right">Updated</th>';
2820
+ h+='</tr></thead><tbody>';
2821
+
2822
+ for(var i=0;i<models.length;i++){
2823
+ var m=models[i];
2824
+ var st=m.status||'unknown';
2825
+ var label=HEALTH_ROLE_LABELS[m.role]||m.role;
2826
+ var badgeText=st==='ok'?'OK':st==='degraded'?'Degraded':st==='error'?'Error':'\u2014';
2827
+ var ago='';
2828
+ if(st==='ok'&&m.lastSuccess) ago=timeAgo(m.lastSuccess);
2829
+ else if(m.lastError) ago=timeAgo(m.lastError);
2830
+
2831
+ h+='<tr>';
2832
+ h+='<td><span class="mh-dot '+st+'"></span></td>';
2833
+ h+='<td><span style="font-weight:500">'+escapeHtml(label)+'</span></td>';
2834
+ h+='<td><span class="mh-badge '+st+'">'+badgeText+'</span></td>';
2835
+ h+='<td><span class="mh-model-name">'+escapeHtml(shortenModel(m.model))+'</span></td>';
2836
+
2837
+ var issue='';
2838
+ if((st==='error'||st==='degraded')&&m.lastErrorMessage){
2839
+ var shortErr=classifyError(m.lastErrorMessage);
2840
+ if(m.failedModel&&m.failedModel!==m.model) issue=shortenModel(m.failedModel)+': ';
2841
+ issue+=shortErr;
2842
+ if(m.consecutiveErrors>1) issue+=' ('+m.consecutiveErrors+'x)';
2843
+ }
2844
+ if(issue) h+='<td><span class="mh-err-text" title="'+escapeHtml(m.lastErrorMessage||'')+'">'+escapeHtml(issue)+'</span></td>';
2845
+ else h+='<td><span style="color:var(--text-muted);font-size:11px">\u2014</span></td>';
2846
+
2847
+ h+='<td style="text-align:right"><span class="mh-time">'+(ago||'\u2014')+'</span></td>';
2848
+ h+='</tr>';
2849
+ }
2850
+ h+='</tbody></table>';
2851
+ bar.innerHTML=h;
2852
+ }catch(e){
2853
+ bar.innerHTML='<div class="mh-empty">Failed to load model health</div>';
2854
+ }
2855
+ }
2856
+
2857
+ function timeAgo(ts){
2858
+ var diff=Date.now()-ts;
2859
+ if(diff<60000) return 'just now';
2860
+ if(diff<3600000) return Math.floor(diff/60000)+'m ago';
2861
+ if(diff<86400000) return Math.floor(diff/3600000)+'h ago';
2862
+ return Math.floor(diff/86400000)+'d ago';
2863
+ }
2864
+
2752
2865
  /* ─── Settings / Config ─── */
2753
2866
  async function loadConfig(){
2754
2867
  try{
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiqIlB,CAAC"}
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkxIlB,CAAC"}
@@ -31,6 +31,7 @@ export declare class ViewerServer {
31
31
  private ppSSEClients;
32
32
  constructor(opts: ViewerServerOptions);
33
33
  start(): Promise<string>;
34
+ private autoCleanupPolluted;
34
35
  stop(): void;
35
36
  getResetToken(): string;
36
37
  private loadAuth;
@@ -73,6 +74,7 @@ export declare class ViewerServer {
73
74
  private serveConfig;
74
75
  private handleSaveConfig;
75
76
  private handleTestModel;
77
+ private serveModelHealth;
76
78
  private serveFallbackModel;
77
79
  private findPluginPackageJson;
78
80
  private handleUpdateCheck;
@@ -81,6 +83,7 @@ export declare class ViewerServer {
81
83
  private serveLogs;
82
84
  private serveLogTools;
83
85
  private getOpenClawHome;
86
+ private handleCleanupPolluted;
84
87
  private handleMigrateScan;
85
88
  private broadcastSSE;
86
89
  private handleMigrateStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/viewer/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/viewer/server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQ7C,OAAO,KAAK,EAAE,MAAM,EAAS,aAAa,EAAE,MAAM,UAAU,CAAC;AAS7D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAOD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAgB;IAErC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAuB;IAC1D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,cAAc,CAW8G;IACpI,OAAO,CAAC,mBAAmB,CAA6B;IAExD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CACyG;IACxH,OAAO,CAAC,YAAY,CAA6B;gBAErC,IAAI,EAAE,mBAAmB;IAarC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAoBxB,OAAO,CAAC,mBAAmB;IAgB3B,IAAI,IAAI,IAAI;IAKZ,aAAa,IAAI,MAAM;IAMvB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,cAAc;IAUtB,OAAO,KAAK,UAAU,GAErB;IAID,OAAO,CAAC,aAAa;IAwFrB,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,eAAe;IA6CvB,OAAO,CAAC,UAAU;YAmEJ,WAAW;IAwEzB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,OAAO;IAuBf,OAAO,CAAC,kBAAkB;IAyC1B,OAAO,CAAC,qBAAqB;IA+B7B,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,WAAW;IA8BnB,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,qBAAqB;YAef,iBAAiB;YAmCjB,kBAAkB;YAsElB,aAAa;IAkD3B,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,qBAAqB;IAsB7B,OAAO,CAAC,iBAAiB;IA0GzB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,kBAAkB;YA0EZ,YAAY;IA6Y1B,OAAO,CAAC,iBAAiB;IAqDzB,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,cAAc;YAOR,cAAc;IA2J5B,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,YAAY;CAIrB"}
@@ -19,6 +19,11 @@ const engine_1 = require("../recall/engine");
19
19
  const evolver_1 = require("../skill/evolver");
20
20
  const html_1 = require("./html");
21
21
  const uuid_1 = require("uuid");
22
+ function normalizeTimestamp(ts) {
23
+ if (ts < 1e12)
24
+ return ts * 1000;
25
+ return ts;
26
+ }
22
27
  class ViewerServer {
23
28
  server = null;
24
29
  store;
@@ -66,10 +71,28 @@ class ViewerServer {
66
71
  this.server.listen(this.port, "127.0.0.1", () => {
67
72
  const addr = this.server.address();
68
73
  const actualPort = typeof addr === "object" && addr ? addr.port : this.port;
74
+ this.autoCleanupPolluted();
69
75
  resolve(`http://127.0.0.1:${actualPort}`);
70
76
  });
71
77
  });
72
78
  }
79
+ autoCleanupPolluted() {
80
+ try {
81
+ const polluted = this.store.findPollutedUserChunks();
82
+ let deleted = 0;
83
+ for (const { id } of polluted) {
84
+ if (this.store.deleteChunk(id))
85
+ deleted++;
86
+ }
87
+ const fixed = this.store.fixMixedUserChunks();
88
+ if (deleted > 0 || fixed > 0) {
89
+ this.log.info(`Auto-cleanup: removed ${deleted} polluted chunks, fixed ${fixed} mixed user+assistant chunks`);
90
+ }
91
+ }
92
+ catch (err) {
93
+ this.log.warn(`Auto-cleanup failed: ${err}`);
94
+ }
95
+ }
73
96
  stop() {
74
97
  this.server?.close();
75
98
  this.server = null;
@@ -212,12 +235,16 @@ class ViewerServer {
212
235
  this.handleSaveConfig(req, res);
213
236
  else if (p === "/api/test-model" && req.method === "POST")
214
237
  this.handleTestModel(req, res);
238
+ else if (p === "/api/model-health" && req.method === "GET")
239
+ this.serveModelHealth(res);
215
240
  else if (p === "/api/fallback-model" && req.method === "GET")
216
241
  this.serveFallbackModel(res);
217
242
  else if (p === "/api/update-check" && req.method === "GET")
218
243
  this.handleUpdateCheck(res);
219
244
  else if (p === "/api/auth/logout" && req.method === "POST")
220
245
  this.handleLogout(req, res);
246
+ else if (p === "/api/cleanup-polluted" && req.method === "POST")
247
+ this.handleCleanupPolluted(res);
221
248
  else if (p === "/api/migrate/scan" && req.method === "GET")
222
249
  this.handleMigrateScan(res);
223
250
  else if (p === "/api/migrate/start" && req.method === "POST")
@@ -491,7 +518,15 @@ class ViewerServer {
491
518
  const total = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
492
519
  const sessions = db.prepare("SELECT COUNT(DISTINCT session_key) as count FROM chunks").get();
493
520
  const roles = db.prepare("SELECT role, COUNT(*) as count FROM chunks GROUP BY role").all();
494
- const timeRange = db.prepare("SELECT MIN(created_at) as earliest, MAX(created_at) as latest FROM chunks").get();
521
+ const timeRange = db.prepare("SELECT MIN(created_at) as earliest, MAX(created_at) as latest FROM chunks WHERE dedup_status = 'active'").get();
522
+ const MIN_VALID_TS = 1704067200000; // 2024-01-01
523
+ if (timeRange.earliest != null && timeRange.earliest < MIN_VALID_TS) {
524
+ timeRange.earliest = db.prepare("SELECT MIN(created_at) as v FROM chunks WHERE dedup_status = 'active' AND created_at >= ?").get(MIN_VALID_TS);
525
+ timeRange.earliest = timeRange.earliest?.v ?? null;
526
+ }
527
+ if (timeRange.latest != null && timeRange.latest < MIN_VALID_TS) {
528
+ timeRange.latest = null;
529
+ }
495
530
  let embCount = 0;
496
531
  try {
497
532
  embCount = db.prepare("SELECT COUNT(*) as count FROM embeddings").get().count;
@@ -1080,8 +1115,8 @@ class ViewerServer {
1080
1115
  return;
1081
1116
  }
1082
1117
  if (type === "embedding") {
1083
- await this.testEmbeddingModel(provider, model, endpoint, apiKey);
1084
- this.jsonResponse(res, { ok: true, detail: `${provider}/${model}` });
1118
+ const dims = await this.testEmbeddingModel(provider, model, endpoint, apiKey);
1119
+ this.jsonResponse(res, { ok: true, detail: `${provider}/${model}`, dimensions: dims });
1085
1120
  }
1086
1121
  else {
1087
1122
  await this.testChatModel(provider, model, endpoint, apiKey);
@@ -1095,6 +1130,9 @@ class ViewerServer {
1095
1130
  }
1096
1131
  });
1097
1132
  }
1133
+ serveModelHealth(res) {
1134
+ this.jsonResponse(res, { models: providers_1.modelHealth.getAll() });
1135
+ }
1098
1136
  serveFallbackModel(res) {
1099
1137
  try {
1100
1138
  const cfgPath = this.getOpenClawConfigPath();
@@ -1177,7 +1215,7 @@ class ViewerServer {
1177
1215
  }
1178
1216
  async testEmbeddingModel(provider, model, endpoint, apiKey) {
1179
1217
  if (provider === "local") {
1180
- return;
1218
+ return 384;
1181
1219
  }
1182
1220
  const baseUrl = (endpoint || "https://api.openai.com/v1").replace(/\/+$/, "");
1183
1221
  const embUrl = baseUrl.endsWith("/embeddings") ? baseUrl : `${baseUrl}/embeddings`;
@@ -1190,39 +1228,59 @@ class ViewerServer {
1190
1228
  const resp = await fetch(baseUrl.replace(/\/v\d+.*/, "/v2/embed"), {
1191
1229
  method: "POST",
1192
1230
  headers,
1193
- body: JSON.stringify({ texts: ["test"], model: model || "embed-english-v3.0", input_type: "search_query", embedding_types: ["float"] }),
1231
+ body: JSON.stringify({ texts: ["test embedding vector"], model: model || "embed-english-v3.0", input_type: "search_query", embedding_types: ["float"] }),
1194
1232
  signal: AbortSignal.timeout(15_000),
1195
1233
  });
1196
1234
  if (!resp.ok) {
1197
1235
  const txt = await resp.text();
1198
1236
  throw new Error(`Cohere embed ${resp.status}: ${txt}`);
1199
1237
  }
1200
- return;
1238
+ const json = await resp.json();
1239
+ const vecs = json?.embeddings?.float;
1240
+ if (!Array.isArray(vecs) || vecs.length === 0 || !Array.isArray(vecs[0]) || vecs[0].length === 0) {
1241
+ throw new Error("Cohere returned empty embedding vector");
1242
+ }
1243
+ return vecs[0].length;
1201
1244
  }
1202
1245
  if (provider === "gemini") {
1203
1246
  const url = `https://generativelanguage.googleapis.com/v1/models/${model || "text-embedding-004"}:embedContent?key=${apiKey}`;
1204
1247
  const resp = await fetch(url, {
1205
1248
  method: "POST",
1206
1249
  headers: { "Content-Type": "application/json" },
1207
- body: JSON.stringify({ content: { parts: [{ text: "test" }] } }),
1250
+ body: JSON.stringify({ content: { parts: [{ text: "test embedding vector" }] } }),
1208
1251
  signal: AbortSignal.timeout(15_000),
1209
1252
  });
1210
1253
  if (!resp.ok) {
1211
1254
  const txt = await resp.text();
1212
1255
  throw new Error(`Gemini embed ${resp.status}: ${txt}`);
1213
1256
  }
1214
- return;
1257
+ const json = await resp.json();
1258
+ const vec = json?.embedding?.values;
1259
+ if (!Array.isArray(vec) || vec.length === 0) {
1260
+ throw new Error("Gemini returned empty embedding vector");
1261
+ }
1262
+ return vec.length;
1215
1263
  }
1216
1264
  const resp = await fetch(embUrl, {
1217
1265
  method: "POST",
1218
1266
  headers,
1219
- body: JSON.stringify({ input: ["test"], model: model || "text-embedding-3-small" }),
1267
+ body: JSON.stringify({ input: ["test embedding vector"], model: model || "text-embedding-3-small" }),
1220
1268
  signal: AbortSignal.timeout(15_000),
1221
1269
  });
1222
1270
  if (!resp.ok) {
1223
1271
  const txt = await resp.text();
1224
1272
  throw new Error(`${resp.status}: ${txt}`);
1225
1273
  }
1274
+ const json = await resp.json();
1275
+ const data = json?.data;
1276
+ if (!Array.isArray(data) || data.length === 0) {
1277
+ throw new Error("API returned no embedding data");
1278
+ }
1279
+ const vec = data[0]?.embedding;
1280
+ if (!Array.isArray(vec) || vec.length === 0) {
1281
+ throw new Error(`API returned empty embedding vector (got ${JSON.stringify(vec)?.slice(0, 100)})`);
1282
+ }
1283
+ return vec.length;
1226
1284
  }
1227
1285
  async testChatModel(provider, model, endpoint, apiKey) {
1228
1286
  const baseUrl = (endpoint || "https://api.openai.com/v1").replace(/\/+$/, "");
@@ -1291,6 +1349,28 @@ class ViewerServer {
1291
1349
  const home = process.env.HOME || process.env.USERPROFILE || "";
1292
1350
  return node_path_1.default.join(home, ".openclaw");
1293
1351
  }
1352
+ handleCleanupPolluted(res) {
1353
+ try {
1354
+ const polluted = this.store.findPollutedUserChunks();
1355
+ let deleted = 0;
1356
+ for (const { id, reason } of polluted) {
1357
+ if (this.store.deleteChunk(id)) {
1358
+ deleted++;
1359
+ this.log.info(`Cleaned polluted chunk ${id}: ${reason}`);
1360
+ }
1361
+ }
1362
+ const fixed = this.store.fixMixedUserChunks();
1363
+ this.log.info(`Cleanup: removed ${deleted} polluted, fixed ${fixed} mixed chunks`);
1364
+ res.writeHead(200, { "Content-Type": "application/json" });
1365
+ res.end(JSON.stringify({ deleted, fixed, total: polluted.length }));
1366
+ }
1367
+ catch (err) {
1368
+ const msg = err instanceof Error ? err.message : String(err);
1369
+ this.log.error(`handleCleanupPolluted error: ${msg}`);
1370
+ res.writeHead(500, { "Content-Type": "application/json" });
1371
+ res.end(JSON.stringify({ error: msg }));
1372
+ }
1373
+ }
1294
1374
  handleMigrateScan(res) {
1295
1375
  try {
1296
1376
  const ocHome = this.getOpenClawHome();
@@ -1540,7 +1620,6 @@ class ViewerServer {
1540
1620
  let totalErrors = 0;
1541
1621
  const cfgPath = this.getOpenClawConfigPath();
1542
1622
  let summarizerCfg;
1543
- let strongCfg;
1544
1623
  try {
1545
1624
  const raw = JSON.parse(node_fs_1.default.readFileSync(cfgPath, "utf-8"));
1546
1625
  const pluginCfg = raw?.plugins?.entries?.["memos-local-openclaw-plugin"]?.config ??
@@ -1548,10 +1627,9 @@ class ViewerServer {
1548
1627
  raw?.plugins?.entries?.["memos-lite-openclaw-plugin"]?.config ??
1549
1628
  raw?.plugins?.entries?.["memos-lite"]?.config ?? {};
1550
1629
  summarizerCfg = pluginCfg.summarizer;
1551
- strongCfg = pluginCfg.skillEvolution?.summarizer;
1552
1630
  }
1553
1631
  catch { /* no config */ }
1554
- const summarizer = new providers_1.Summarizer(summarizerCfg, this.log, strongCfg);
1632
+ const summarizer = new providers_1.Summarizer(summarizerCfg, this.log);
1555
1633
  // Phase 1: Import SQLite memory chunks
1556
1634
  if (importSqlite) {
1557
1635
  const memoryDir = node_path_1.default.join(ocHome, "memory");
@@ -1670,8 +1748,8 @@ class ViewerServer {
1670
1748
  mergeCount: 0,
1671
1749
  lastHitAt: null,
1672
1750
  mergeHistory: "[]",
1673
- createdAt: row.updated_at * 1000,
1674
- updatedAt: row.updated_at * 1000,
1751
+ createdAt: normalizeTimestamp(row.updated_at),
1752
+ updatedAt: normalizeTimestamp(row.updated_at),
1675
1753
  };
1676
1754
  this.store.insertChunk(chunk);
1677
1755
  if (embedding && dedupStatus === "active") {