@memtensor/memos-local-openclaw-plugin 1.0.2-beta.2 → 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 (51) 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 +104 -21
  37. package/dist/viewer/server.js.map +1 -1
  38. package/index.ts +38 -85
  39. package/package.json +1 -1
  40. package/scripts/postinstall.cjs +16 -3
  41. package/src/capture/index.ts +56 -1
  42. package/src/embedding/index.ts +13 -7
  43. package/src/ingest/providers/anthropic.ts +28 -13
  44. package/src/ingest/providers/bedrock.ts +28 -13
  45. package/src/ingest/providers/gemini.ts +28 -13
  46. package/src/ingest/providers/index.ts +112 -9
  47. package/src/ingest/providers/openai.ts +28 -13
  48. package/src/ingest/worker.ts +8 -15
  49. package/src/storage/sqlite.ts +49 -0
  50. package/src/viewer/html.ts +113 -0
  51. package/src/viewer/server.ts +101 -20
@@ -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":"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"}
@@ -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;
@@ -1003,11 +1038,13 @@ class ViewerServer {
1003
1038
  const raw = JSON.parse(node_fs_1.default.readFileSync(cfgPath, "utf-8"));
1004
1039
  const entries = raw?.plugins?.entries ?? {};
1005
1040
  const pluginEntry = entries["memos-local-openclaw-plugin"]?.config
1041
+ ?? entries["memos-local"]?.config
1006
1042
  ?? entries["memos-lite-openclaw-plugin"]?.config
1007
1043
  ?? entries["memos-lite"]?.config
1008
1044
  ?? {};
1009
1045
  const result = { ...pluginEntry };
1010
1046
  const topEntry = entries["memos-local-openclaw-plugin"]
1047
+ ?? entries["memos-local"]
1011
1048
  ?? entries["memos-lite-openclaw-plugin"]
1012
1049
  ?? entries["memos-lite"]
1013
1050
  ?? {};
@@ -1037,9 +1074,10 @@ class ViewerServer {
1037
1074
  plugins.entries = {};
1038
1075
  const entries = plugins.entries;
1039
1076
  const entryKey = entries["memos-local-openclaw-plugin"] ? "memos-local-openclaw-plugin"
1040
- : entries["memos-lite-openclaw-plugin"] ? "memos-lite-openclaw-plugin"
1041
- : entries["memos-lite"] ? "memos-lite"
1042
- : "memos-local-openclaw-plugin";
1077
+ : entries["memos-local"] ? "memos-local"
1078
+ : entries["memos-lite-openclaw-plugin"] ? "memos-lite-openclaw-plugin"
1079
+ : entries["memos-lite"] ? "memos-lite"
1080
+ : "memos-local-openclaw-plugin";
1043
1081
  if (!entries[entryKey])
1044
1082
  entries[entryKey] = { enabled: true };
1045
1083
  const entry = entries[entryKey];
@@ -1077,8 +1115,8 @@ class ViewerServer {
1077
1115
  return;
1078
1116
  }
1079
1117
  if (type === "embedding") {
1080
- await this.testEmbeddingModel(provider, model, endpoint, apiKey);
1081
- 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 });
1082
1120
  }
1083
1121
  else {
1084
1122
  await this.testChatModel(provider, model, endpoint, apiKey);
@@ -1092,6 +1130,9 @@ class ViewerServer {
1092
1130
  }
1093
1131
  });
1094
1132
  }
1133
+ serveModelHealth(res) {
1134
+ this.jsonResponse(res, { models: providers_1.modelHealth.getAll() });
1135
+ }
1095
1136
  serveFallbackModel(res) {
1096
1137
  try {
1097
1138
  const cfgPath = this.getOpenClawConfigPath();
@@ -1174,7 +1215,7 @@ class ViewerServer {
1174
1215
  }
1175
1216
  async testEmbeddingModel(provider, model, endpoint, apiKey) {
1176
1217
  if (provider === "local") {
1177
- return;
1218
+ return 384;
1178
1219
  }
1179
1220
  const baseUrl = (endpoint || "https://api.openai.com/v1").replace(/\/+$/, "");
1180
1221
  const embUrl = baseUrl.endsWith("/embeddings") ? baseUrl : `${baseUrl}/embeddings`;
@@ -1187,39 +1228,59 @@ class ViewerServer {
1187
1228
  const resp = await fetch(baseUrl.replace(/\/v\d+.*/, "/v2/embed"), {
1188
1229
  method: "POST",
1189
1230
  headers,
1190
- 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"] }),
1191
1232
  signal: AbortSignal.timeout(15_000),
1192
1233
  });
1193
1234
  if (!resp.ok) {
1194
1235
  const txt = await resp.text();
1195
1236
  throw new Error(`Cohere embed ${resp.status}: ${txt}`);
1196
1237
  }
1197
- 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;
1198
1244
  }
1199
1245
  if (provider === "gemini") {
1200
1246
  const url = `https://generativelanguage.googleapis.com/v1/models/${model || "text-embedding-004"}:embedContent?key=${apiKey}`;
1201
1247
  const resp = await fetch(url, {
1202
1248
  method: "POST",
1203
1249
  headers: { "Content-Type": "application/json" },
1204
- body: JSON.stringify({ content: { parts: [{ text: "test" }] } }),
1250
+ body: JSON.stringify({ content: { parts: [{ text: "test embedding vector" }] } }),
1205
1251
  signal: AbortSignal.timeout(15_000),
1206
1252
  });
1207
1253
  if (!resp.ok) {
1208
1254
  const txt = await resp.text();
1209
1255
  throw new Error(`Gemini embed ${resp.status}: ${txt}`);
1210
1256
  }
1211
- 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;
1212
1263
  }
1213
1264
  const resp = await fetch(embUrl, {
1214
1265
  method: "POST",
1215
1266
  headers,
1216
- 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" }),
1217
1268
  signal: AbortSignal.timeout(15_000),
1218
1269
  });
1219
1270
  if (!resp.ok) {
1220
1271
  const txt = await resp.text();
1221
1272
  throw new Error(`${resp.status}: ${txt}`);
1222
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;
1223
1284
  }
1224
1285
  async testChatModel(provider, model, endpoint, apiKey) {
1225
1286
  const baseUrl = (endpoint || "https://api.openai.com/v1").replace(/\/+$/, "");
@@ -1288,6 +1349,28 @@ class ViewerServer {
1288
1349
  const home = process.env.HOME || process.env.USERPROFILE || "";
1289
1350
  return node_path_1.default.join(home, ".openclaw");
1290
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
+ }
1291
1374
  handleMigrateScan(res) {
1292
1375
  try {
1293
1376
  const ocHome = this.getOpenClawHome();
@@ -1351,8 +1434,9 @@ class ViewerServer {
1351
1434
  try {
1352
1435
  const raw = JSON.parse(node_fs_1.default.readFileSync(cfgPath, "utf-8"));
1353
1436
  const pluginCfg = raw?.plugins?.entries?.["memos-local-openclaw-plugin"]?.config ??
1354
- raw?.plugins?.entries?.["memos-lite"]?.config ??
1355
- raw?.plugins?.entries?.["memos-lite-openclaw-plugin"]?.config ?? {};
1437
+ raw?.plugins?.entries?.["memos-local"]?.config ??
1438
+ raw?.plugins?.entries?.["memos-lite-openclaw-plugin"]?.config ??
1439
+ raw?.plugins?.entries?.["memos-lite"]?.config ?? {};
1356
1440
  const emb = pluginCfg.embedding;
1357
1441
  hasEmbedding = !!(emb && emb.provider);
1358
1442
  const sum = pluginCfg.summarizer;
@@ -1536,17 +1620,16 @@ class ViewerServer {
1536
1620
  let totalErrors = 0;
1537
1621
  const cfgPath = this.getOpenClawConfigPath();
1538
1622
  let summarizerCfg;
1539
- let strongCfg;
1540
1623
  try {
1541
1624
  const raw = JSON.parse(node_fs_1.default.readFileSync(cfgPath, "utf-8"));
1542
1625
  const pluginCfg = raw?.plugins?.entries?.["memos-local-openclaw-plugin"]?.config ??
1543
- raw?.plugins?.entries?.["memos-lite"]?.config ??
1544
- raw?.plugins?.entries?.["memos-lite-openclaw-plugin"]?.config ?? {};
1626
+ raw?.plugins?.entries?.["memos-local"]?.config ??
1627
+ raw?.plugins?.entries?.["memos-lite-openclaw-plugin"]?.config ??
1628
+ raw?.plugins?.entries?.["memos-lite"]?.config ?? {};
1545
1629
  summarizerCfg = pluginCfg.summarizer;
1546
- strongCfg = pluginCfg.skillEvolution?.summarizer;
1547
1630
  }
1548
1631
  catch { /* no config */ }
1549
- const summarizer = new providers_1.Summarizer(summarizerCfg, this.log, strongCfg);
1632
+ const summarizer = new providers_1.Summarizer(summarizerCfg, this.log);
1550
1633
  // Phase 1: Import SQLite memory chunks
1551
1634
  if (importSqlite) {
1552
1635
  const memoryDir = node_path_1.default.join(ocHome, "memory");
@@ -1665,8 +1748,8 @@ class ViewerServer {
1665
1748
  mergeCount: 0,
1666
1749
  lastHitAt: null,
1667
1750
  mergeHistory: "[]",
1668
- createdAt: row.updated_at * 1000,
1669
- updatedAt: row.updated_at * 1000,
1751
+ createdAt: normalizeTimestamp(row.updated_at),
1752
+ updatedAt: normalizeTimestamp(row.updated_at),
1670
1753
  };
1671
1754
  this.store.insertChunk(chunk);
1672
1755
  if (embedding && dedupStatus === "active") {