@memtensor/memos-local-openclaw-plugin 0.3.3 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,g+9NA2mHf,CAAC"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,ohlOA0qHf,CAAC"}
@@ -478,6 +478,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
478
478
  .log-io-section+.log-io-section{border-top:1px dashed var(--border)}
479
479
  [data-theme="light"] .log-io-content{background:rgba(0,0,0,.04)}
480
480
  [data-theme="light"] .log-summary-query{background:rgba(59,130,246,.06)}
481
+ .settings-group{margin-bottom:8px}
482
+ .settings-group-title{font-size:15px;font-weight:700;color:var(--text);margin:0 0 12px 0;padding:0;letter-spacing:.02em}
483
+ .settings-group .settings-section{margin-bottom:16px}
484
+ .settings-group .settings-section:last-child{margin-bottom:0}
481
485
  .settings-section{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:24px 28px}
482
486
  .settings-section h3{font-size:13px;font-weight:700;color:var(--text);margin-bottom:16px;display:flex;align-items:center;gap:8px}
483
487
  .settings-section h3 .icon{font-size:16px;opacity:.8}
@@ -903,6 +907,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
903
907
 
904
908
  <!-- ─── Settings View ─── -->
905
909
  <div class="settings-view" id="settingsView">
910
+ <div class="settings-group" id="settingsModelConfig">
911
+ <h2 class="settings-group-title"><span data-i18n="settings.modelconfig">Model Configuration</span></h2>
906
912
  <div class="settings-section">
907
913
  <h3><span class="icon">\u{1F4E1}</span> <span data-i18n="settings.embedding">Embedding Model</span></h3>
908
914
  <div class="settings-grid">
@@ -966,6 +972,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
966
972
  </div>
967
973
  </div>
968
974
  </div>
975
+ </div>
969
976
 
970
977
  <div class="settings-section">
971
978
  <h3><span class="icon">\u{1F527}</span> <span data-i18n="settings.skill">Skill Evolution</span></h3>
@@ -987,6 +994,36 @@ input,textarea,select{font-family:inherit;font-size:inherit}
987
994
  <input type="number" id="cfgSkillMinChunks" placeholder="6">
988
995
  </div>
989
996
  </div>
997
+ <div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border)">
998
+ <h4 style="font-size:12px;font-weight:600;color:var(--text-sec);margin-bottom:12px"><span data-i18n="settings.skill.model">Skill Dedicated Model</span></h4>
999
+ <div class="field-hint" style="margin-bottom:12px" data-i18n="settings.skill.model.hint">If not configured, the main Summarizer Model above will be used for skill generation. Configure a dedicated model here for higher quality skill output.</div>
1000
+ <div class="settings-grid">
1001
+ <div class="settings-field">
1002
+ <label data-i18n="settings.provider">Provider</label>
1003
+ <select id="cfgSkillProvider">
1004
+ <option value="">— <span data-i18n="settings.skill.usemain">Use main summarizer</span> —</option>
1005
+ <option value="openai_compatible">OpenAI Compatible</option>
1006
+ <option value="openai">OpenAI</option>
1007
+ <option value="anthropic">Anthropic</option>
1008
+ <option value="gemini">Gemini</option>
1009
+ <option value="azure_openai">Azure OpenAI</option>
1010
+ <option value="bedrock">Bedrock</option>
1011
+ </select>
1012
+ </div>
1013
+ <div class="settings-field">
1014
+ <label data-i18n="settings.model">Model</label>
1015
+ <input type="text" id="cfgSkillModel" placeholder="e.g. claude-4.6-opus">
1016
+ </div>
1017
+ <div class="settings-field full-width">
1018
+ <label>Endpoint</label>
1019
+ <input type="text" id="cfgSkillEndpoint" placeholder="https://...">
1020
+ </div>
1021
+ <div class="settings-field">
1022
+ <label>API Key</label>
1023
+ <input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
1024
+ </div>
1025
+ </div>
1026
+ </div>
990
1027
  </div>
991
1028
 
992
1029
  <div class="settings-section">
@@ -1342,6 +1379,7 @@ const I18N={
1342
1379
  'logs.ago':'ago',
1343
1380
  'tab.import':'\u{1F4E5} Import',
1344
1381
  'tab.settings':'\u2699 Settings',
1382
+ 'settings.modelconfig':'Model Configuration',
1345
1383
  'settings.embedding':'Embedding Model',
1346
1384
  'settings.summarizer':'Summarizer Model',
1347
1385
  'settings.skill':'Skill Evolution',
@@ -1601,6 +1639,7 @@ const I18N={
1601
1639
  'logs.ago':'前',
1602
1640
  'tab.import':'\u{1F4E5} 导入',
1603
1641
  'tab.settings':'\u2699 设置',
1642
+ 'settings.modelconfig':'模型配置',
1604
1643
  'settings.embedding':'嵌入模型',
1605
1644
  'settings.summarizer':'摘要模型',
1606
1645
  'settings.skill':'技能进化',
@@ -2452,6 +2491,12 @@ async function loadConfig(){
2452
2491
  document.getElementById('cfgSkillConfidence').value=sk.minConfidence||'';
2453
2492
  document.getElementById('cfgSkillMinChunks').value=sk.minChunksForEval||'';
2454
2493
 
2494
+ const skSum=sk.summarizer||{};
2495
+ document.getElementById('cfgSkillProvider').value=skSum.provider||'';
2496
+ document.getElementById('cfgSkillModel').value=skSum.model||'';
2497
+ document.getElementById('cfgSkillEndpoint').value=skSum.endpoint||'';
2498
+ document.getElementById('cfgSkillApiKey').value=skSum.apiKey||'';
2499
+
2455
2500
  document.getElementById('cfgViewerPort').value=cfg.viewerPort||'';
2456
2501
 
2457
2502
  const tel=cfg.telemetry||{};
@@ -2485,6 +2530,14 @@ async function saveConfig(){
2485
2530
  const mc=document.getElementById('cfgSkillConfidence').value.trim();if(mc) cfg.skillEvolution.minConfidence=Number(mc);
2486
2531
  const mk=document.getElementById('cfgSkillMinChunks').value.trim();if(mk) cfg.skillEvolution.minChunksForEval=Number(mk);
2487
2532
 
2533
+ const skP=document.getElementById('cfgSkillProvider').value;
2534
+ if(skP){
2535
+ cfg.skillEvolution.summarizer={provider:skP};
2536
+ const sv=document.getElementById('cfgSkillModel').value.trim();if(sv) cfg.skillEvolution.summarizer.model=sv;
2537
+ const se=document.getElementById('cfgSkillEndpoint').value.trim();if(se) cfg.skillEvolution.summarizer.endpoint=se;
2538
+ const sk=document.getElementById('cfgSkillApiKey').value.trim();if(sk) cfg.skillEvolution.summarizer.apiKey=sk;
2539
+ }
2540
+
2488
2541
  const vp=document.getElementById('cfgViewerPort').value.trim();
2489
2542
  if(vp) cfg.viewerPort=Number(vp);
2490
2543
 
@@ -2828,7 +2881,17 @@ async function loadStats(){
2828
2881
  }
2829
2882
  document.getElementById('statSessions').textContent=d.totalSessions;
2830
2883
  document.getElementById('statEmbeddings').textContent=d.totalEmbeddings;
2831
- const days=d.timeRange.earliest?Math.max(1,Math.round((new Date(d.timeRange.latest)-new Date(d.timeRange.earliest))/(86400000))):0;
2884
+ let days=0;
2885
+ if(d.timeRange&&d.timeRange.earliest!=null&&d.timeRange.latest!=null){
2886
+ let e=Number(d.timeRange.earliest), l=Number(d.timeRange.latest);
2887
+ if(Number.isFinite(e)&&Number.isFinite(l)){
2888
+ if(e<1e12) e*=1000;
2889
+ if(l<1e12) l*=1000;
2890
+ days=Math.round((l-e)/86400000);
2891
+ days=Math.max(0,Math.min(36500,days));
2892
+ if(days===0) days=1;
2893
+ }
2894
+ }
2832
2895
  document.getElementById('statTimeSpan').textContent=days;
2833
2896
 
2834
2897
  const provEl=document.getElementById('embeddingStatus');
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2mHlB,CAAC"}
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/viewer/html.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0qHlB,CAAC"}
package/index.ts CHANGED
@@ -967,6 +967,7 @@ const memosLocalPlugin = {
967
967
  port: viewerPort,
968
968
  log: ctx.log,
969
969
  dataDir: stateDir,
970
+ ctx,
970
971
  });
971
972
 
972
973
  // ─── Service lifecycle ───
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memtensor/memos-local-openclaw-plugin",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
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
@@ -41,6 +41,7 @@ export function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, stateD
41
41
  rrfK: cfg.recall?.rrfK ?? DEFAULTS.rrfK,
42
42
  mmrLambda: cfg.recall?.mmrLambda ?? DEFAULTS.mmrLambda,
43
43
  recencyHalfLifeDays: cfg.recall?.recencyHalfLifeDays ?? DEFAULTS.recencyHalfLifeDays,
44
+ vectorSearchMaxChunks: cfg.recall?.vectorSearchMaxChunks ?? DEFAULTS.vectorSearchMaxChunks,
44
45
  },
45
46
  dedup: {
46
47
  similarityThreshold: cfg.dedup?.similarityThreshold ?? DEFAULTS.dedupSimilarityThreshold,
@@ -46,7 +46,10 @@ export class RecallEngine {
46
46
  if (query) {
47
47
  try {
48
48
  const queryVec = await this.embedder.embedQuery(query);
49
- vecCandidates = vectorSearch(this.store, queryVec, candidatePool);
49
+ const maxChunks = recallCfg.vectorSearchMaxChunks && recallCfg.vectorSearchMaxChunks > 0
50
+ ? recallCfg.vectorSearchMaxChunks
51
+ : undefined;
52
+ vecCandidates = vectorSearch(this.store, queryVec, candidatePool, maxChunks);
50
53
  } catch (err) {
51
54
  this.ctx.log.warn(`Vector search failed, using FTS only: ${err}`);
52
55
  }
@@ -38,6 +38,8 @@ export class SqliteStore {
38
38
  ON chunks(session_key, turn_id, seq);
39
39
  CREATE INDEX IF NOT EXISTS idx_chunks_created
40
40
  ON chunks(created_at);
41
+ CREATE INDEX IF NOT EXISTS idx_chunks_session_created
42
+ ON chunks(session_key, created_at, seq);
41
43
 
42
44
  CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
43
45
  summary,
@@ -103,9 +105,14 @@ export class SqliteStore {
103
105
  this.migrateMergeFields();
104
106
  this.migrateApiLogs();
105
107
  this.migrateDedupStatus();
108
+ this.migrateChunksIndexesForRecall();
106
109
  this.log.debug("Database schema initialized");
107
110
  }
108
111
 
112
+ private migrateChunksIndexesForRecall(): void {
113
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_chunks_dedup_created ON chunks(dedup_status, created_at DESC)");
114
+ }
115
+
109
116
  private migrateTaskId(): void {
110
117
  const cols = this.db.prepare("PRAGMA table_info(chunks)").all() as Array<{ name: string }>;
111
118
  if (!cols.some((c) => c.name === "task_id")) {
@@ -648,6 +655,24 @@ export class SqliteStore {
648
655
  }));
649
656
  }
650
657
 
658
+ /** Like getAllEmbeddings but only for the most recent N chunks (uses idx_chunks_dedup_created). Use for vector search cap to avoid full scan. */
659
+ getRecentEmbeddings(limit: number): Array<{ chunkId: string; vector: number[] }> {
660
+ if (limit <= 0) return this.getAllEmbeddings();
661
+ const rows = this.db.prepare(
662
+ `SELECT e.chunk_id, e.vector, e.dimensions
663
+ FROM chunks c
664
+ JOIN embeddings e ON e.chunk_id = c.id
665
+ WHERE c.dedup_status = 'active'
666
+ ORDER BY c.created_at DESC
667
+ LIMIT ?`,
668
+ ).all(limit) as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
669
+
670
+ return rows.map((r) => ({
671
+ chunkId: r.chunk_id,
672
+ vector: Array.from(new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions)),
673
+ }));
674
+ }
675
+
651
676
  getEmbedding(chunkId: string): number[] | null {
652
677
  const row = this.db.prepare(
653
678
  "SELECT vector, dimensions FROM embeddings WHERE chunk_id = ?",
@@ -20,16 +20,18 @@ export interface VectorHit {
20
20
  }
21
21
 
22
22
  /**
23
- * Brute-force vector search over all stored embeddings.
24
- * For local single-user usage the dataset is small enough that
25
- * a full scan with SIMD-friendly Float32 math is sufficient.
23
+ * Brute-force vector search over stored embeddings.
24
+ * When maxChunks > 0, only searches the most recent maxChunks chunks (uses index; avoids full scan as data grows).
26
25
  */
27
26
  export function vectorSearch(
28
27
  store: SqliteStore,
29
28
  queryVec: number[],
30
29
  topK: number,
30
+ maxChunks?: number,
31
31
  ): VectorHit[] {
32
- const all = store.getAllEmbeddings();
32
+ const all = maxChunks != null && maxChunks > 0
33
+ ? store.getRecentEmbeddings(maxChunks)
34
+ : store.getAllEmbeddings();
33
35
  const scored: VectorHit[] = all.map((row) => ({
34
36
  chunkId: row.chunkId,
35
37
  score: cosineSimilarity(queryVec, row.vector),
package/src/types.ts CHANGED
@@ -251,6 +251,8 @@ export interface MemosLocalConfig {
251
251
  rrfK?: number;
252
252
  mmrLambda?: number;
253
253
  recencyHalfLifeDays?: number;
254
+ /** Cap vector search to this many most recent chunks. 0 = no cap (search all; may get slower with 200k+ chunks). If you set a cap for performance, use a large value (e.g. 200000–300000) so older memories are still in the window; FTS always searches all. */
255
+ vectorSearchMaxChunks?: number;
254
256
  };
255
257
  dedup?: {
256
258
  similarityThreshold?: number;
@@ -272,6 +274,7 @@ export const DEFAULTS = {
272
274
  rrfK: 60,
273
275
  mmrLambda: 0.7,
274
276
  recencyHalfLifeDays: 14,
277
+ vectorSearchMaxChunks: 0,
275
278
  dedupSimilarityThreshold: 0.93,
276
279
  evidenceWrapperTag: "STORED_MEMORY",
277
280
  excerptMinChars: 200,
@@ -475,6 +475,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
475
475
  .log-io-section+.log-io-section{border-top:1px dashed var(--border)}
476
476
  [data-theme="light"] .log-io-content{background:rgba(0,0,0,.04)}
477
477
  [data-theme="light"] .log-summary-query{background:rgba(59,130,246,.06)}
478
+ .settings-group{margin-bottom:8px}
479
+ .settings-group-title{font-size:15px;font-weight:700;color:var(--text);margin:0 0 12px 0;padding:0;letter-spacing:.02em}
480
+ .settings-group .settings-section{margin-bottom:16px}
481
+ .settings-group .settings-section:last-child{margin-bottom:0}
478
482
  .settings-section{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:24px 28px}
479
483
  .settings-section h3{font-size:13px;font-weight:700;color:var(--text);margin-bottom:16px;display:flex;align-items:center;gap:8px}
480
484
  .settings-section h3 .icon{font-size:16px;opacity:.8}
@@ -900,6 +904,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
900
904
 
901
905
  <!-- ─── Settings View ─── -->
902
906
  <div class="settings-view" id="settingsView">
907
+ <div class="settings-group" id="settingsModelConfig">
908
+ <h2 class="settings-group-title"><span data-i18n="settings.modelconfig">Model Configuration</span></h2>
903
909
  <div class="settings-section">
904
910
  <h3><span class="icon">\u{1F4E1}</span> <span data-i18n="settings.embedding">Embedding Model</span></h3>
905
911
  <div class="settings-grid">
@@ -963,6 +969,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
963
969
  </div>
964
970
  </div>
965
971
  </div>
972
+ </div>
966
973
 
967
974
  <div class="settings-section">
968
975
  <h3><span class="icon">\u{1F527}</span> <span data-i18n="settings.skill">Skill Evolution</span></h3>
@@ -984,6 +991,36 @@ input,textarea,select{font-family:inherit;font-size:inherit}
984
991
  <input type="number" id="cfgSkillMinChunks" placeholder="6">
985
992
  </div>
986
993
  </div>
994
+ <div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border)">
995
+ <h4 style="font-size:12px;font-weight:600;color:var(--text-sec);margin-bottom:12px"><span data-i18n="settings.skill.model">Skill Dedicated Model</span></h4>
996
+ <div class="field-hint" style="margin-bottom:12px" data-i18n="settings.skill.model.hint">If not configured, the main Summarizer Model above will be used for skill generation. Configure a dedicated model here for higher quality skill output.</div>
997
+ <div class="settings-grid">
998
+ <div class="settings-field">
999
+ <label data-i18n="settings.provider">Provider</label>
1000
+ <select id="cfgSkillProvider">
1001
+ <option value="">— <span data-i18n="settings.skill.usemain">Use main summarizer</span> —</option>
1002
+ <option value="openai_compatible">OpenAI Compatible</option>
1003
+ <option value="openai">OpenAI</option>
1004
+ <option value="anthropic">Anthropic</option>
1005
+ <option value="gemini">Gemini</option>
1006
+ <option value="azure_openai">Azure OpenAI</option>
1007
+ <option value="bedrock">Bedrock</option>
1008
+ </select>
1009
+ </div>
1010
+ <div class="settings-field">
1011
+ <label data-i18n="settings.model">Model</label>
1012
+ <input type="text" id="cfgSkillModel" placeholder="e.g. claude-4.6-opus">
1013
+ </div>
1014
+ <div class="settings-field full-width">
1015
+ <label>Endpoint</label>
1016
+ <input type="text" id="cfgSkillEndpoint" placeholder="https://...">
1017
+ </div>
1018
+ <div class="settings-field">
1019
+ <label>API Key</label>
1020
+ <input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
1021
+ </div>
1022
+ </div>
1023
+ </div>
987
1024
  </div>
988
1025
 
989
1026
  <div class="settings-section">
@@ -1339,6 +1376,7 @@ const I18N={
1339
1376
  'logs.ago':'ago',
1340
1377
  'tab.import':'\u{1F4E5} Import',
1341
1378
  'tab.settings':'\u2699 Settings',
1379
+ 'settings.modelconfig':'Model Configuration',
1342
1380
  'settings.embedding':'Embedding Model',
1343
1381
  'settings.summarizer':'Summarizer Model',
1344
1382
  'settings.skill':'Skill Evolution',
@@ -1598,6 +1636,7 @@ const I18N={
1598
1636
  'logs.ago':'前',
1599
1637
  'tab.import':'\u{1F4E5} 导入',
1600
1638
  'tab.settings':'\u2699 设置',
1639
+ 'settings.modelconfig':'模型配置',
1601
1640
  'settings.embedding':'嵌入模型',
1602
1641
  'settings.summarizer':'摘要模型',
1603
1642
  'settings.skill':'技能进化',
@@ -2449,6 +2488,12 @@ async function loadConfig(){
2449
2488
  document.getElementById('cfgSkillConfidence').value=sk.minConfidence||'';
2450
2489
  document.getElementById('cfgSkillMinChunks').value=sk.minChunksForEval||'';
2451
2490
 
2491
+ const skSum=sk.summarizer||{};
2492
+ document.getElementById('cfgSkillProvider').value=skSum.provider||'';
2493
+ document.getElementById('cfgSkillModel').value=skSum.model||'';
2494
+ document.getElementById('cfgSkillEndpoint').value=skSum.endpoint||'';
2495
+ document.getElementById('cfgSkillApiKey').value=skSum.apiKey||'';
2496
+
2452
2497
  document.getElementById('cfgViewerPort').value=cfg.viewerPort||'';
2453
2498
 
2454
2499
  const tel=cfg.telemetry||{};
@@ -2482,6 +2527,14 @@ async function saveConfig(){
2482
2527
  const mc=document.getElementById('cfgSkillConfidence').value.trim();if(mc) cfg.skillEvolution.minConfidence=Number(mc);
2483
2528
  const mk=document.getElementById('cfgSkillMinChunks').value.trim();if(mk) cfg.skillEvolution.minChunksForEval=Number(mk);
2484
2529
 
2530
+ const skP=document.getElementById('cfgSkillProvider').value;
2531
+ if(skP){
2532
+ cfg.skillEvolution.summarizer={provider:skP};
2533
+ const sv=document.getElementById('cfgSkillModel').value.trim();if(sv) cfg.skillEvolution.summarizer.model=sv;
2534
+ const se=document.getElementById('cfgSkillEndpoint').value.trim();if(se) cfg.skillEvolution.summarizer.endpoint=se;
2535
+ const sk=document.getElementById('cfgSkillApiKey').value.trim();if(sk) cfg.skillEvolution.summarizer.apiKey=sk;
2536
+ }
2537
+
2485
2538
  const vp=document.getElementById('cfgViewerPort').value.trim();
2486
2539
  if(vp) cfg.viewerPort=Number(vp);
2487
2540
 
@@ -2825,7 +2878,17 @@ async function loadStats(){
2825
2878
  }
2826
2879
  document.getElementById('statSessions').textContent=d.totalSessions;
2827
2880
  document.getElementById('statEmbeddings').textContent=d.totalEmbeddings;
2828
- const days=d.timeRange.earliest?Math.max(1,Math.round((new Date(d.timeRange.latest)-new Date(d.timeRange.earliest))/(86400000))):0;
2881
+ let days=0;
2882
+ if(d.timeRange&&d.timeRange.earliest!=null&&d.timeRange.latest!=null){
2883
+ let e=Number(d.timeRange.earliest), l=Number(d.timeRange.latest);
2884
+ if(Number.isFinite(e)&&Number.isFinite(l)){
2885
+ if(e<1e12) e*=1000;
2886
+ if(l<1e12) l*=1000;
2887
+ days=Math.round((l-e)/86400000);
2888
+ days=Math.max(0,Math.min(36500,days));
2889
+ if(days===0) days=1;
2890
+ }
2891
+ }
2829
2892
  document.getElementById('statTimeSpan').textContent=days;
2830
2893
 
2831
2894
  const provEl=document.getElementById('embeddingStatus');