jinzd-ai-cli 0.4.88 → 0.4.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{batch-7XCYSPJU.js → batch-3MJ56YAA.js} +2 -2
- package/dist/chat-index-QKFH7ZP6.js +17 -0
- package/dist/chat-index-W2UZ34ZI.js +18 -0
- package/dist/{chunk-PFYAAX2S.js → chunk-2DXY7UGF.js} +16 -63
- package/dist/chunk-5S3PIG5O.js +453 -0
- package/dist/{chunk-QT2KNL3V.js → chunk-AB2LA33A.js} +1 -1
- package/dist/chunk-ANYYM4CF.js +460 -0
- package/dist/{chunk-P6EQZKKG.js → chunk-BJXGZFE6.js} +1 -1
- package/dist/{chunk-L3MBIO36.js → chunk-DJGP7AR6.js} +5 -106
- package/dist/{chunk-V3NMERIB.js → chunk-EEEAFWNK.js} +1 -1
- package/dist/{chunk-YDHIU24C.js → chunk-G65IDWVP.js} +76 -3
- package/dist/chunk-JV5N65KN.js +50 -0
- package/dist/chunk-KHYD3WXE.js +52 -0
- package/dist/{chunk-CQQQFNND.js → chunk-KJLJPUY2.js} +6 -4
- package/dist/{chunk-GTKJUEBS.js → chunk-MO7MWNWC.js} +6 -4
- package/dist/{chunk-XMA222FQ.js → chunk-PASCDYMH.js} +17 -63
- package/dist/{chunk-VGXNE37B.js → chunk-WPQ4D6T3.js} +1 -1
- package/dist/electron-server.js +187 -104
- package/dist/{hub-IR4INXSU.js → hub-B7NJSCWF.js} +1 -1
- package/dist/index.js +158 -19
- package/dist/{run-tests-FQHDUYOG.js → run-tests-2DYVHTIH.js} +2 -2
- package/dist/{run-tests-JVWIGY7P.js → run-tests-37FEBJTR.js} +1 -1
- package/dist/{semantic-MYAXLDCZ.js → semantic-3KJPAUW6.js} +3 -2
- package/dist/{semantic-ICJ536BG.js → semantic-YDRPPVWK.js} +3 -2
- package/dist/{server-UWKRV5DK.js → server-FCTPLKGO.js} +121 -13
- package/dist/{server-HTVVWKFN.js → server-S6JYNMMF.js} +7 -5
- package/dist/{task-orchestrator-6MI6LD7T.js → task-orchestrator-K6HDX4YE.js} +7 -5
- package/dist/{vector-store-UR7IARXB.js → vector-store-NDUFLNGN.js} +2 -1
- package/dist/{vector-store-YTVHACBV.js → vector-store-QARQ2P6D.js} +2 -1
- package/dist/web/client/app.js +201 -0
- package/dist/web/client/index.html +24 -0
- package/package.json +1 -1
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
pathTokens,
|
|
5
5
|
rebuildSemanticIndex,
|
|
6
6
|
semanticSearch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MO7MWNWC.js";
|
|
8
8
|
import "./chunk-BJAT4GNC.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-PASCDYMH.js";
|
|
10
|
+
import "./chunk-JV5N65KN.js";
|
|
10
11
|
export {
|
|
11
12
|
buildEmbeddingText,
|
|
12
13
|
hasSemanticIndex,
|
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
pathTokens,
|
|
6
6
|
rebuildSemanticIndex,
|
|
7
7
|
semanticSearch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KJLJPUY2.js";
|
|
9
9
|
import "./chunk-6VRJGH25.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-2DXY7UGF.js";
|
|
11
|
+
import "./chunk-KHYD3WXE.js";
|
|
11
12
|
export {
|
|
12
13
|
buildEmbeddingText,
|
|
13
14
|
hasSemanticIndex,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AuthManager
|
|
4
|
+
} from "./chunk-BYNY5JPB.js";
|
|
2
5
|
import {
|
|
3
6
|
HALLUCINATION_CORRECTION_MESSAGE,
|
|
4
7
|
McpManager,
|
|
@@ -20,10 +23,10 @@ import {
|
|
|
20
23
|
persistToolRound,
|
|
21
24
|
rebuildExtraMessages,
|
|
22
25
|
setupProxy
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DJGP7AR6.js";
|
|
24
27
|
import {
|
|
25
28
|
ConfigManager
|
|
26
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-AB2LA33A.js";
|
|
27
30
|
import {
|
|
28
31
|
ToolExecutor,
|
|
29
32
|
ToolRegistry,
|
|
@@ -41,14 +44,16 @@ import {
|
|
|
41
44
|
spawnAgentContext,
|
|
42
45
|
truncateOutput,
|
|
43
46
|
undoStack
|
|
44
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-G65IDWVP.js";
|
|
45
48
|
import "./chunk-2ZD3YTVM.js";
|
|
46
49
|
import "./chunk-4BKXL7SM.js";
|
|
50
|
+
import "./chunk-ANYYM4CF.js";
|
|
47
51
|
import "./chunk-NHNWUBXB.js";
|
|
48
|
-
import "./chunk-
|
|
52
|
+
import "./chunk-KJLJPUY2.js";
|
|
49
53
|
import "./chunk-6VRJGH25.js";
|
|
50
|
-
import "./chunk-
|
|
51
|
-
import "./chunk-
|
|
54
|
+
import "./chunk-2DXY7UGF.js";
|
|
55
|
+
import "./chunk-KHYD3WXE.js";
|
|
56
|
+
import "./chunk-EEEAFWNK.js";
|
|
52
57
|
import {
|
|
53
58
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
54
59
|
AUTHOR,
|
|
@@ -67,10 +72,7 @@ import {
|
|
|
67
72
|
SKILLS_DIR_NAME,
|
|
68
73
|
VERSION,
|
|
69
74
|
buildUserIdentityPrompt
|
|
70
|
-
} from "./chunk-
|
|
71
|
-
import {
|
|
72
|
-
AuthManager
|
|
73
|
-
} from "./chunk-BYNY5JPB.js";
|
|
75
|
+
} from "./chunk-WPQ4D6T3.js";
|
|
74
76
|
|
|
75
77
|
// src/web/server.ts
|
|
76
78
|
import express from "express";
|
|
@@ -620,6 +622,12 @@ var SessionHandler = class _SessionHandler {
|
|
|
620
622
|
}
|
|
621
623
|
return;
|
|
622
624
|
}
|
|
625
|
+
case "memory_search":
|
|
626
|
+
return this.handleMemorySearch(msg.query, msg.topK, msg.minScore, msg.excludeCurrentSession);
|
|
627
|
+
case "memory_status_request":
|
|
628
|
+
return this.handleMemoryStatus();
|
|
629
|
+
case "memory_rebuild":
|
|
630
|
+
return this.handleMemoryRebuild(Boolean(msg.full));
|
|
623
631
|
case "auto_pause_response": {
|
|
624
632
|
const resolve3 = this.pendingAutoPause.get(msg.requestId);
|
|
625
633
|
if (resolve3) {
|
|
@@ -1993,7 +2001,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1993
2001
|
const root = process.cwd();
|
|
1994
2002
|
const { loadIndex, clearIndex } = await import("./store-S24SPPDZ.js");
|
|
1995
2003
|
const { indexProject } = await import("./indexer-C7QYYHSZ.js");
|
|
1996
|
-
const { loadVectorStore, clearVectorStore } = await import("./vector-store-
|
|
2004
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-QARQ2P6D.js");
|
|
1997
2005
|
if (sub === "status") {
|
|
1998
2006
|
const idx = loadIndex(root);
|
|
1999
2007
|
const vec = loadVectorStore(root);
|
|
@@ -2042,7 +2050,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2042
2050
|
message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
|
|
2043
2051
|
});
|
|
2044
2052
|
try {
|
|
2045
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
2053
|
+
const { rebuildSemanticIndex } = await import("./semantic-YDRPPVWK.js");
|
|
2046
2054
|
const stats = await rebuildSemanticIndex(root);
|
|
2047
2055
|
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
2048
2056
|
this.send({
|
|
@@ -2229,7 +2237,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2229
2237
|
case "test": {
|
|
2230
2238
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2231
2239
|
try {
|
|
2232
|
-
const { executeTests } = await import("./run-tests-
|
|
2240
|
+
const { executeTests } = await import("./run-tests-2DYVHTIH.js");
|
|
2233
2241
|
const argStr = args.join(" ").trim();
|
|
2234
2242
|
let testArgs = {};
|
|
2235
2243
|
if (argStr) {
|
|
@@ -2743,6 +2751,106 @@ Add .md files to create commands.` });
|
|
|
2743
2751
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
2744
2752
|
}
|
|
2745
2753
|
}
|
|
2754
|
+
// ── B4 chat memory recall (v0.4.90+) ──────────────────────────────
|
|
2755
|
+
// Lazy-imported so the 117 MB embedder stays out of the load path for
|
|
2756
|
+
// clients that never open the Memory panel.
|
|
2757
|
+
async handleMemorySearch(query, topK, minScore, excludeCurrent) {
|
|
2758
|
+
const q = (query ?? "").trim();
|
|
2759
|
+
if (!q) {
|
|
2760
|
+
this.send({ type: "error", message: "Memory search: query is empty." });
|
|
2761
|
+
return;
|
|
2762
|
+
}
|
|
2763
|
+
try {
|
|
2764
|
+
const { searchChatMemory, loadChatIndex } = await import("./chat-index-W2UZ34ZI.js");
|
|
2765
|
+
const loaded = loadChatIndex();
|
|
2766
|
+
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
2767
|
+
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
const hits = await searchChatMemory(q, {
|
|
2771
|
+
topK: topK ?? 8,
|
|
2772
|
+
minScore: minScore ?? 0.2,
|
|
2773
|
+
excludeSessionId: excludeCurrent ? this.sessions.current?.id : void 0
|
|
2774
|
+
});
|
|
2775
|
+
this.send({
|
|
2776
|
+
type: "memory_hits",
|
|
2777
|
+
query: q,
|
|
2778
|
+
hits: hits.map((h) => ({
|
|
2779
|
+
id: h.chunk.id,
|
|
2780
|
+
sessionId: h.chunk.sessionId,
|
|
2781
|
+
sessionTitle: h.chunk.sessionTitle,
|
|
2782
|
+
provider: h.chunk.provider,
|
|
2783
|
+
model: h.chunk.model,
|
|
2784
|
+
timestamp: h.chunk.timestamp,
|
|
2785
|
+
score: h.score,
|
|
2786
|
+
text: h.chunk.text,
|
|
2787
|
+
startMessageIdx: h.chunk.startMessageIdx,
|
|
2788
|
+
endMessageIdx: h.chunk.endMessageIdx
|
|
2789
|
+
}))
|
|
2790
|
+
});
|
|
2791
|
+
} catch (err) {
|
|
2792
|
+
this.send({
|
|
2793
|
+
type: "error",
|
|
2794
|
+
message: `Memory search failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
async handleMemoryStatus() {
|
|
2799
|
+
try {
|
|
2800
|
+
const { getChatIndexStatus } = await import("./chat-index-W2UZ34ZI.js");
|
|
2801
|
+
const s = getChatIndexStatus();
|
|
2802
|
+
this.send({
|
|
2803
|
+
type: "memory_status",
|
|
2804
|
+
exists: s.exists,
|
|
2805
|
+
chunks: s.chunks,
|
|
2806
|
+
sessions: s.sessions,
|
|
2807
|
+
built: s.built,
|
|
2808
|
+
model: s.model,
|
|
2809
|
+
vecFileSizeBytes: s.vecFileSizeBytes,
|
|
2810
|
+
chunksFileSizeBytes: s.chunksFileSizeBytes
|
|
2811
|
+
});
|
|
2812
|
+
} catch (err) {
|
|
2813
|
+
this.send({
|
|
2814
|
+
type: "error",
|
|
2815
|
+
message: `Memory status failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2816
|
+
});
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
async handleMemoryRebuild(full) {
|
|
2820
|
+
try {
|
|
2821
|
+
this.send({
|
|
2822
|
+
type: "info",
|
|
2823
|
+
message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
|
|
2824
|
+
});
|
|
2825
|
+
const { buildChatIndex } = await import("./chat-index-W2UZ34ZI.js");
|
|
2826
|
+
const stats = await buildChatIndex({
|
|
2827
|
+
full,
|
|
2828
|
+
onProgress: (p) => {
|
|
2829
|
+
this.send({
|
|
2830
|
+
type: "memory_rebuild_progress",
|
|
2831
|
+
stage: p.stage,
|
|
2832
|
+
processed: p.processed,
|
|
2833
|
+
total: p.total
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
});
|
|
2837
|
+
this.send({
|
|
2838
|
+
type: "memory_rebuild_done",
|
|
2839
|
+
chunksTotal: stats.chunksTotal,
|
|
2840
|
+
chunksAdded: stats.chunksAdded,
|
|
2841
|
+
chunksRemoved: stats.chunksRemoved,
|
|
2842
|
+
sessionsIndexed: stats.sessionsIndexed,
|
|
2843
|
+
sessionsSkipped: stats.sessionsSkipped,
|
|
2844
|
+
durationMs: stats.durationMs
|
|
2845
|
+
});
|
|
2846
|
+
await this.handleMemoryStatus();
|
|
2847
|
+
} catch (err) {
|
|
2848
|
+
this.send({
|
|
2849
|
+
type: "error",
|
|
2850
|
+
message: `Memory rebuild failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2746
2854
|
sendSessionMessages() {
|
|
2747
2855
|
const session = this.sessions.current;
|
|
2748
2856
|
if (!session) return;
|
|
@@ -3,17 +3,19 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
schemaToJsonSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-G65IDWVP.js";
|
|
7
7
|
import "./chunk-2ZD3YTVM.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
|
+
import "./chunk-ANYYM4CF.js";
|
|
9
10
|
import "./chunk-NHNWUBXB.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-KJLJPUY2.js";
|
|
11
12
|
import "./chunk-6VRJGH25.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-2DXY7UGF.js";
|
|
14
|
+
import "./chunk-KHYD3WXE.js";
|
|
15
|
+
import "./chunk-EEEAFWNK.js";
|
|
14
16
|
import {
|
|
15
17
|
VERSION
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-WPQ4D6T3.js";
|
|
17
19
|
|
|
18
20
|
// src/mcp/server.ts
|
|
19
21
|
import { createInterface } from "readline";
|
|
@@ -4,17 +4,19 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-G65IDWVP.js";
|
|
8
8
|
import "./chunk-2ZD3YTVM.js";
|
|
9
9
|
import "./chunk-4BKXL7SM.js";
|
|
10
|
+
import "./chunk-ANYYM4CF.js";
|
|
10
11
|
import "./chunk-NHNWUBXB.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-KJLJPUY2.js";
|
|
12
13
|
import "./chunk-6VRJGH25.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-2DXY7UGF.js";
|
|
15
|
+
import "./chunk-KHYD3WXE.js";
|
|
16
|
+
import "./chunk-EEEAFWNK.js";
|
|
15
17
|
import {
|
|
16
18
|
SUBAGENT_ALLOWED_TOOLS
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WPQ4D6T3.js";
|
|
18
20
|
|
|
19
21
|
// src/hub/task-orchestrator.ts
|
|
20
22
|
import { createInterface } from "readline";
|
package/dist/web/client/app.js
CHANGED
|
@@ -187,6 +187,10 @@ function handleServerMessage(msg) {
|
|
|
187
187
|
case 'tools_list': renderToolsList(msg); switchSidebarTab('tools'); break;
|
|
188
188
|
case 'export_data': handleExportData(msg); break;
|
|
189
189
|
case 'memory_content': handleMemoryContent(msg); break;
|
|
190
|
+
case 'memory_hits': renderMemoryHits(msg); break;
|
|
191
|
+
case 'memory_status': renderMemoryStatus(msg); break;
|
|
192
|
+
case 'memory_rebuild_progress': renderMemoryRebuildProgress(msg); break;
|
|
193
|
+
case 'memory_rebuild_done': renderMemoryRebuildDone(msg); break;
|
|
190
194
|
case 'auth_required': showAuthScreen(msg.hasUsers); break;
|
|
191
195
|
case 'auth_result': handleAuthResult(msg); break;
|
|
192
196
|
case 'info': addInfoMessage(msg.message); break;
|
|
@@ -2869,3 +2873,200 @@ function showEnableAuthDialog() {
|
|
|
2869
2873
|
})
|
|
2870
2874
|
.catch(err => alert('Failed: ' + err.message));
|
|
2871
2875
|
}
|
|
2876
|
+
|
|
2877
|
+
// ── B4 Chat Memory recall panel (v0.4.90+) ────────────────────────────
|
|
2878
|
+
// UI for the semantic index built from all past sessions. The tab is
|
|
2879
|
+
// opt-in: we only request a status refresh + wire handlers when the
|
|
2880
|
+
// user actually clicks 🧠 Memory, so unused sessions pay zero cost.
|
|
2881
|
+
|
|
2882
|
+
let memoryPanelInit = false;
|
|
2883
|
+
let memoryStatusCache = null;
|
|
2884
|
+
|
|
2885
|
+
function initMemoryPanel() {
|
|
2886
|
+
if (memoryPanelInit) return;
|
|
2887
|
+
memoryPanelInit = true;
|
|
2888
|
+
|
|
2889
|
+
const input = document.getElementById('memory-search-input');
|
|
2890
|
+
const btnSearch = document.getElementById('btn-memory-search');
|
|
2891
|
+
const btnRefresh = document.getElementById('btn-memory-refresh');
|
|
2892
|
+
const btnRebuild = document.getElementById('btn-memory-rebuild');
|
|
2893
|
+
|
|
2894
|
+
const runSearch = () => {
|
|
2895
|
+
const q = (input?.value || '').trim();
|
|
2896
|
+
if (!q) return;
|
|
2897
|
+
const excl = document.getElementById('memory-exclude-current')?.checked;
|
|
2898
|
+
const hitsEl = document.getElementById('memory-hits');
|
|
2899
|
+
if (hitsEl) {
|
|
2900
|
+
hitsEl.innerHTML = '<div class="text-xs opacity-40 text-center py-4">Searching…</div>';
|
|
2901
|
+
}
|
|
2902
|
+
send({
|
|
2903
|
+
type: 'memory_search',
|
|
2904
|
+
query: q,
|
|
2905
|
+
topK: 8,
|
|
2906
|
+
minScore: 0.2,
|
|
2907
|
+
excludeCurrentSession: !!excl,
|
|
2908
|
+
});
|
|
2909
|
+
};
|
|
2910
|
+
|
|
2911
|
+
btnSearch?.addEventListener('click', runSearch);
|
|
2912
|
+
input?.addEventListener('keydown', (e) => {
|
|
2913
|
+
if (e.key === 'Enter') {
|
|
2914
|
+
e.preventDefault();
|
|
2915
|
+
runSearch();
|
|
2916
|
+
}
|
|
2917
|
+
});
|
|
2918
|
+
|
|
2919
|
+
btnRefresh?.addEventListener('click', () => {
|
|
2920
|
+
send({ type: 'memory_rebuild', full: false });
|
|
2921
|
+
});
|
|
2922
|
+
btnRebuild?.addEventListener('click', () => {
|
|
2923
|
+
if (!confirm('Full rebuild re-embeds every chat chunk. On first run this downloads ~117 MB and can take minutes. Continue?')) return;
|
|
2924
|
+
send({ type: 'memory_rebuild', full: true });
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
send({ type: 'memory_status_request' });
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
function renderMemoryStatus(msg) {
|
|
2931
|
+
memoryStatusCache = msg;
|
|
2932
|
+
const el = document.getElementById('memory-status');
|
|
2933
|
+
if (!el) return;
|
|
2934
|
+
if (!msg.exists) {
|
|
2935
|
+
el.textContent = 'Index not built — click ⟳ Rebuild to create it.';
|
|
2936
|
+
el.className = 'text-xs opacity-70 truncate';
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
const built = msg.built ? msg.built.replace('T', ' ').slice(0, 16) : '-';
|
|
2940
|
+
const mb = ((msg.vecFileSizeBytes + msg.chunksFileSizeBytes) / 1024 / 1024).toFixed(2);
|
|
2941
|
+
el.textContent = `${msg.chunks} chunks · ${msg.sessions} sessions · ${mb} MB · built ${built}`;
|
|
2942
|
+
el.className = 'text-xs opacity-50 truncate';
|
|
2943
|
+
el.title = `model: ${msg.model || '-'}`;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
function renderMemoryHits(msg) {
|
|
2947
|
+
const el = document.getElementById('memory-hits');
|
|
2948
|
+
if (!el) return;
|
|
2949
|
+
if (msg.indexMissing) {
|
|
2950
|
+
el.innerHTML = '<div class="text-xs opacity-60 text-center py-4">Chat index is empty — click ⟳ Rebuild first.</div>';
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
if (!msg.hits || msg.hits.length === 0) {
|
|
2954
|
+
el.innerHTML = `<div class="text-xs opacity-60 text-center py-4">No memories matched "${escapeHtml(msg.query)}".<br>Try a broader phrase or lower minScore.</div>`;
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
// Cache hit texts on the container so inject buttons can find them by id
|
|
2958
|
+
// without us having to embed full-text in data-* attrs (would bloat DOM).
|
|
2959
|
+
memoryHitsCache = new Map();
|
|
2960
|
+
for (const h of msg.hits) memoryHitsCache.set(h.id, h);
|
|
2961
|
+
|
|
2962
|
+
// Top toolbar: inject top-3 / clear results.
|
|
2963
|
+
const header = `
|
|
2964
|
+
<div class="flex items-center gap-1 text-xs pb-1 mb-1 border-b border-base-content/10">
|
|
2965
|
+
<span class="opacity-60 flex-1">${msg.hits.length} hit(s) for "${escapeHtml(msg.query)}"</span>
|
|
2966
|
+
<button class="btn btn-xs btn-primary btn-outline" id="btn-memory-inject-top3" title="Inject top 3 hits into chat input as a quoted context block">➕ Inject top 3</button>
|
|
2967
|
+
</div>
|
|
2968
|
+
`;
|
|
2969
|
+
|
|
2970
|
+
const items = msg.hits.map((h) => {
|
|
2971
|
+
const ts = (h.timestamp || '').replace('T', ' ').slice(0, 16);
|
|
2972
|
+
const title = h.sessionTitle || h.sessionId.slice(0, 8);
|
|
2973
|
+
const score = typeof h.score === 'number' ? h.score.toFixed(3) : '-';
|
|
2974
|
+
const preview = h.text.length > 500 ? h.text.slice(0, 500) + '…' : h.text;
|
|
2975
|
+
return `
|
|
2976
|
+
<div class="memory-hit card bg-base-100 border border-base-content/10 p-2 hover:border-primary/40"
|
|
2977
|
+
data-hit-id="${escapeHtml(h.id)}">
|
|
2978
|
+
<div class="flex items-center gap-1 text-xs opacity-70 mb-1">
|
|
2979
|
+
<span class="font-semibold truncate flex-1">${escapeHtml(title)}</span>
|
|
2980
|
+
<span class="badge badge-xs">${score}</span>
|
|
2981
|
+
</div>
|
|
2982
|
+
<div class="text-xs opacity-50 mb-1 flex items-center gap-2">
|
|
2983
|
+
<span class="flex-1">${ts} · session ${escapeHtml(h.sessionId.slice(0, 8))}</span>
|
|
2984
|
+
<button class="btn btn-xs btn-ghost memory-hit-inject" title="Inject this hit into chat input">➕</button>
|
|
2985
|
+
<button class="btn btn-xs btn-ghost memory-hit-load" title="Load this session in current tab">↗</button>
|
|
2986
|
+
</div>
|
|
2987
|
+
<div class="text-xs whitespace-pre-wrap leading-snug opacity-90" style="max-height: 8em; overflow: hidden;">${escapeHtml(preview)}</div>
|
|
2988
|
+
</div>
|
|
2989
|
+
`;
|
|
2990
|
+
}).join('');
|
|
2991
|
+
el.innerHTML = header + items;
|
|
2992
|
+
|
|
2993
|
+
document.getElementById('btn-memory-inject-top3')?.addEventListener('click', (e) => {
|
|
2994
|
+
e.stopPropagation();
|
|
2995
|
+
injectMemoryHits(msg.hits.slice(0, 3));
|
|
2996
|
+
});
|
|
2997
|
+
el.querySelectorAll('.memory-hit').forEach((card) => {
|
|
2998
|
+
const hitId = card.getAttribute('data-hit-id');
|
|
2999
|
+
const h = hitId ? memoryHitsCache.get(hitId) : null;
|
|
3000
|
+
if (!h) return;
|
|
3001
|
+
card.querySelector('.memory-hit-inject')?.addEventListener('click', (e) => {
|
|
3002
|
+
e.stopPropagation();
|
|
3003
|
+
injectMemoryHits([h]);
|
|
3004
|
+
});
|
|
3005
|
+
card.querySelector('.memory-hit-load')?.addEventListener('click', (e) => {
|
|
3006
|
+
e.stopPropagation();
|
|
3007
|
+
loadSessionInTab(h.sessionId, h.sessionTitle || '');
|
|
3008
|
+
});
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
/**
|
|
3013
|
+
* Inject one or more memory hits into the chat input as a quoted context
|
|
3014
|
+
* block. User can then edit or add their question before sending, giving
|
|
3015
|
+
* them full control over what lands in the AI's context (vs automatic
|
|
3016
|
+
* injection which is surprising).
|
|
3017
|
+
*/
|
|
3018
|
+
function injectMemoryHits(hits) {
|
|
3019
|
+
if (!hits || hits.length === 0) return;
|
|
3020
|
+
const lines = ['> 🧠 Recalled from past sessions:', '>'];
|
|
3021
|
+
for (const h of hits) {
|
|
3022
|
+
const ts = (h.timestamp || '').replace('T', ' ').slice(0, 16);
|
|
3023
|
+
const title = h.sessionTitle || h.sessionId.slice(0, 8);
|
|
3024
|
+
const body = h.text.length > 800 ? h.text.slice(0, 800) + '…' : h.text;
|
|
3025
|
+
lines.push(`> **${title}** · ${ts}`);
|
|
3026
|
+
for (const ln of body.split('\n')) lines.push(`> ${ln}`);
|
|
3027
|
+
lines.push('>');
|
|
3028
|
+
}
|
|
3029
|
+
lines.push('');
|
|
3030
|
+
const block = lines.join('\n');
|
|
3031
|
+
const existing = userInput?.value || '';
|
|
3032
|
+
if (userInput) {
|
|
3033
|
+
// Prepend — user's in-progress question stays after the quoted block.
|
|
3034
|
+
userInput.value = block + existing;
|
|
3035
|
+
// Fire input event so the auto-resize textarea adapts its height.
|
|
3036
|
+
userInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3037
|
+
userInput.focus();
|
|
3038
|
+
// Move caret to end so user can keep typing.
|
|
3039
|
+
const len = userInput.value.length;
|
|
3040
|
+
try { userInput.setSelectionRange(len, len); } catch { /* ignore */ }
|
|
3041
|
+
}
|
|
3042
|
+
addInfoMessage(`🧠 Injected ${hits.length} memory hit(s) into input. Edit and send when ready.`);
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
let memoryHitsCache = new Map();
|
|
3046
|
+
|
|
3047
|
+
function renderMemoryRebuildProgress(msg) {
|
|
3048
|
+
const el = document.getElementById('memory-status');
|
|
3049
|
+
if (!el) return;
|
|
3050
|
+
if (msg.stage === 'embedding' && msg.total) {
|
|
3051
|
+
el.textContent = `Embedding ${msg.processed || 0} / ${msg.total} chunks…`;
|
|
3052
|
+
} else if (msg.stage !== 'done') {
|
|
3053
|
+
el.textContent = `${msg.stage}…`;
|
|
3054
|
+
}
|
|
3055
|
+
el.className = 'text-xs opacity-70 truncate';
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
function renderMemoryRebuildDone(msg) {
|
|
3059
|
+
const el = document.getElementById('memory-status');
|
|
3060
|
+
if (el) {
|
|
3061
|
+
el.textContent = `✓ ${msg.chunksTotal} chunks (+${msg.chunksAdded} / −${msg.chunksRemoved}) · ${msg.sessionsIndexed} re-indexed · ${(msg.durationMs / 1000).toFixed(1)}s`;
|
|
3062
|
+
el.className = 'text-xs opacity-70 truncate';
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
// Lazy-init memory panel on first click of the 🧠 Memory sidebar tab.
|
|
3067
|
+
// switchSidebarTab already toggles visibility — we just need to bootstrap.
|
|
3068
|
+
document.querySelectorAll('.sidebar-tab').forEach(btn => {
|
|
3069
|
+
if (btn.dataset.tab === 'memory') {
|
|
3070
|
+
btn.addEventListener('click', () => initMemoryPanel());
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="branches" title="Conversation branches (B2)">🌿 Branches</button>
|
|
109
109
|
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="tools">🔧 Tools</button>
|
|
110
110
|
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="files">📁 Files</button>
|
|
111
|
+
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="memory" title="Chat memory semantic recall (B4, v0.4.90)">🧠 Memory</button>
|
|
111
112
|
</div>
|
|
112
113
|
<!-- Sessions tab -->
|
|
113
114
|
<div id="tab-sessions" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden">
|
|
@@ -158,6 +159,29 @@
|
|
|
158
159
|
<div class="text-xs opacity-40 text-center py-4">Click tab to load</div>
|
|
159
160
|
</div>
|
|
160
161
|
</div>
|
|
162
|
+
|
|
163
|
+
<!-- Memory tab (B4, v0.4.90+) -->
|
|
164
|
+
<div id="tab-memory" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden hidden">
|
|
165
|
+
<div class="p-2 border-b border-base-content/10 flex flex-col gap-1">
|
|
166
|
+
<div class="flex items-center gap-1">
|
|
167
|
+
<input id="memory-search-input" type="text" class="input input-xs input-bordered flex-1 min-w-0" placeholder="Search past chats (semantic)…">
|
|
168
|
+
<button id="btn-memory-search" class="btn btn-xs btn-primary btn-outline flex-shrink-0" title="Search">🔍</button>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="flex items-center gap-1 text-xs">
|
|
171
|
+
<label class="flex items-center gap-1 opacity-70 cursor-pointer">
|
|
172
|
+
<input id="memory-exclude-current" type="checkbox" class="checkbox checkbox-xs">
|
|
173
|
+
<span>exclude current</span>
|
|
174
|
+
</label>
|
|
175
|
+
<span class="flex-1"></span>
|
|
176
|
+
<button id="btn-memory-refresh" class="btn btn-xs btn-ghost" title="Incremental refresh">↻</button>
|
|
177
|
+
<button id="btn-memory-rebuild" class="btn btn-xs btn-ghost" title="Full rebuild (slow first run)">⟳ Rebuild</button>
|
|
178
|
+
</div>
|
|
179
|
+
<div id="memory-status" class="text-xs opacity-50 truncate">Loading index status…</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div id="memory-hits" class="flex-1 overflow-y-auto p-2 flex flex-col gap-2 text-sm">
|
|
182
|
+
<div class="text-xs opacity-40 text-center py-4">Type a query above to recall past chats.</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
161
185
|
</aside>
|
|
162
186
|
<!-- Sidebar resize handle -->
|
|
163
187
|
<div id="sidebar-resize" class="sidebar-resize-handle" title="Drag to resize sidebar"></div>
|