jinzd-ai-cli 0.4.89 → 0.4.91
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/README.md +6 -2
- package/README.zh-CN.md +6 -2
- package/dist/{batch-UMQYXVKG.js → batch-KZQHKPE5.js} +2 -2
- package/dist/chat-index-QKFH7ZP6.js +17 -0
- package/dist/chunk-5S3PIG5O.js +453 -0
- package/dist/{chunk-E7YC4GWV.js → chunk-AKCXRW2Q.js} +1 -1
- package/dist/{chunk-TKYNTXKB.js → chunk-IGNYJUZU.js} +1 -1
- package/dist/chunk-JV5N65KN.js +50 -0
- package/dist/{chunk-GTKJUEBS.js → chunk-MO7MWNWC.js} +6 -4
- package/dist/{chunk-FVRLRIKC.js → chunk-MYQANQ6F.js} +2 -2
- package/dist/{chunk-XMA222FQ.js → chunk-PASCDYMH.js} +17 -63
- package/dist/{chunk-3O3U3L5W.js → chunk-US6MQO6W.js} +1 -1
- package/dist/{chunk-4WVXTADR.js → chunk-XQHCCV3A.js} +1 -1
- package/dist/{chunk-ABPT6XCI.js → chunk-Z2V6EYPQ.js} +2 -2
- package/dist/electron-server.js +127 -202
- package/dist/{hub-4P2BH57W.js → hub-A66CLTFF.js} +1 -1
- package/dist/index.js +23 -16
- package/dist/{run-tests-TGGXTOFF.js → run-tests-KHJ6YCPH.js} +1 -1
- package/dist/{run-tests-5TO5G3YH.js → run-tests-MKBVRMBA.js} +2 -2
- package/dist/{semantic-MYAXLDCZ.js → semantic-3KJPAUW6.js} +3 -2
- package/dist/{server-U2BBLP4Y.js → server-B6U5GMZ7.js} +3 -3
- package/dist/{server-NG7AEAD5.js → server-J7BPHI7D.js} +112 -6
- package/dist/{task-orchestrator-ODU45UQG.js → task-orchestrator-JB5TZNBK.js} +3 -3
- package/dist/{vector-store-UR7IARXB.js → vector-store-NDUFLNGN.js} +2 -1
- package/dist/web/client/app.js +201 -0
- package/dist/web/client/index.html +24 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,10 +30,10 @@ import {
|
|
|
30
30
|
saveDevState,
|
|
31
31
|
sessionHasMeaningfulContent,
|
|
32
32
|
setupProxy
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-MYQANQ6F.js";
|
|
34
34
|
import {
|
|
35
35
|
ConfigManager
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-US6MQO6W.js";
|
|
37
37
|
import {
|
|
38
38
|
ToolExecutor,
|
|
39
39
|
ToolRegistry,
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
spawnAgentContext,
|
|
50
50
|
theme,
|
|
51
51
|
undoStack
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-Z2V6EYPQ.js";
|
|
53
53
|
import "./chunk-2ZD3YTVM.js";
|
|
54
54
|
import {
|
|
55
55
|
fileCheckpoints
|
|
@@ -67,7 +67,7 @@ import "./chunk-KJLJPUY2.js";
|
|
|
67
67
|
import "./chunk-6VRJGH25.js";
|
|
68
68
|
import "./chunk-2DXY7UGF.js";
|
|
69
69
|
import "./chunk-KHYD3WXE.js";
|
|
70
|
-
import "./chunk-
|
|
70
|
+
import "./chunk-IGNYJUZU.js";
|
|
71
71
|
import {
|
|
72
72
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
73
73
|
AUTHOR,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
SKILLS_DIR_NAME,
|
|
90
90
|
VERSION,
|
|
91
91
|
buildUserIdentityPrompt
|
|
92
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-AKCXRW2Q.js";
|
|
93
93
|
|
|
94
94
|
// src/index.ts
|
|
95
95
|
import { program } from "commander";
|
|
@@ -227,7 +227,7 @@ var Renderer = class {
|
|
|
227
227
|
console.log(theme.dim(" Gemini (Google) \xB7 Zhipu (GLM) \xB7 OpenRouter \xB7 Ollama (Local, no API key)"));
|
|
228
228
|
console.log(HR);
|
|
229
229
|
const mcpToolCount = mcpInfo?.tools ?? 0;
|
|
230
|
-
const toolTotal =
|
|
230
|
+
const toolTotal = 29 + pluginCount + mcpToolCount;
|
|
231
231
|
const extras = [];
|
|
232
232
|
if (pluginCount > 0) extras.push(`${pluginCount} plugin(s)`);
|
|
233
233
|
if (mcpToolCount > 0) extras.push(`${mcpToolCount} MCP`);
|
|
@@ -261,14 +261,15 @@ var Renderer = class {
|
|
|
261
261
|
console.log(tool("get_outline", "Enumerate all top-level declarations in one source file"));
|
|
262
262
|
console.log(tool("find_references", "Search indexed files for references to a symbol name"));
|
|
263
263
|
console.log(tool("search_code", 'Semantic (meaning-based) code search via local embeddings \u2014 "grep by meaning", bilingual'));
|
|
264
|
+
console.log(tool("recall_memory", 'Semantic recall over past chat sessions \u2014 "remember when we discussed X" across history (v0.4.89+)'));
|
|
264
265
|
console.log(HR);
|
|
265
|
-
console.log(theme.dim(" REPL Commands (
|
|
266
|
+
console.log(theme.dim(" REPL Commands (43):"));
|
|
266
267
|
console.log(theme.dim(" /help /about /provider /model /clear /compact /plan /session"));
|
|
267
268
|
console.log(theme.dim(" /system /context /status /search /undo /export /copy /paste"));
|
|
268
269
|
console.log(theme.dim(" /cost /init /skill /tools /plugins /mcp /config /checkpoint"));
|
|
269
|
-
console.log(theme.dim(" /review /security-review /rewind /commands /test
|
|
270
|
-
console.log(theme.dim(" /add-dir /memory /profile /doctor /bug /think
|
|
271
|
-
console.log(theme.dim(" /branch /index /yolo /exit"));
|
|
270
|
+
console.log(theme.dim(" /review /security-review /security /rewind /commands /test"));
|
|
271
|
+
console.log(theme.dim(" /scaffold /add-dir /memory /profile /doctor /bug /think"));
|
|
272
|
+
console.log(theme.dim(" /diff /fork /branch /index /yolo /exit"));
|
|
272
273
|
console.log(HR);
|
|
273
274
|
console.log(theme.dim(" Key Features:"));
|
|
274
275
|
console.log(feat("Agentic loop (up to 200 tool-call rounds, configurable via config/CLI, final answer streamed)"));
|
|
@@ -338,6 +339,12 @@ var Renderer = class {
|
|
|
338
339
|
console.log(feat("Anthropic Batches API (A3, v0.4.73+): aicli batch submit/list/status/results/cancel \u2014 50% off, 24h window"));
|
|
339
340
|
console.log(feat("Session Replay (B1, v0.4.71+): Web UI \u{1F3AC} button \u2014 timeline view of every message, tool call, reasoning, and cache-aware token usage"));
|
|
340
341
|
console.log(feat("Conversation Branching (B2, v0.4.74+): /branch list/new/switch/delete/rename \u2014 fork the conversation at any message; Web UI replay \u{1F33F} fork-here button"));
|
|
342
|
+
console.log(feat("Branch tree sidebar (B2 polish, v0.4.75+): Web UI \u{1F33F} Branches tab with tree-indented picker, click to switch, hover to rename/delete, + Fork button"));
|
|
343
|
+
console.log(feat("Cross-branch ops (B3, v0.4.80+): /branch diff and /branch cherry-pick \u2014 compare branches and pick messages across forks; v0.4.81 accepts id/title/prefix"));
|
|
344
|
+
console.log(feat("MCP Server mode (E1, v0.4.84+): aicli mcp-serve \u2014 reverse aicli into an MCP server so Claude Desktop/Cursor/any MCP client can use its 26 built-in tools (including find_symbol/search_code)"));
|
|
345
|
+
console.log(feat("Session sensitive-data redaction (v0.4.88+): unified redactor scrubs passwords/tokens/keys from every message before it hits disk; /security status and scan to audit"));
|
|
346
|
+
console.log(feat('Chat memory recall (B4, v0.4.89+): "human-like long-term memory" \u2014 semantic index over all past sessions + recall_memory AI tool + /memory rebuild|refresh|status|recall, AI auto-recalls on "last time"/"\u4E0A\u6B21"'));
|
|
347
|
+
console.log(feat("Web UI Memory panel (B4, v0.4.90+): \u{1F9E0} Memory sidebar tab \u2014 cross-session semantic search with \u2795 Inject-to-input (quotes hit into chat box for user-reviewed recall) and \u2197 jump-to-session"));
|
|
341
348
|
console.log();
|
|
342
349
|
}
|
|
343
350
|
printPrompt(provider, _model) {
|
|
@@ -982,7 +989,7 @@ function createDefaultCommands() {
|
|
|
982
989
|
" /scaffold <description> - Generate project scaffolding with AI",
|
|
983
990
|
" /add-dir [path|remove] - Add/remove a directory from AI context",
|
|
984
991
|
" /profile [show|set|clear] - View or edit your identity (AI knows who you are)",
|
|
985
|
-
" /memory [show|add|clear] -
|
|
992
|
+
" /memory [show|add|clear|path|rebuild|refresh|status|recall <q>|index-clear] - Persistent memory + chat memory recall index (v0.4.89+)",
|
|
986
993
|
" /doctor - Health check (API keys, config, MCP status)",
|
|
987
994
|
" /bug [--copy] - Generate bug report template (--copy to clipboard)",
|
|
988
995
|
" /diff [--stats] - Show all file modifications in this session",
|
|
@@ -2602,7 +2609,7 @@ ${hint}` : "")
|
|
|
2602
2609
|
usage: "/test [command|filter]",
|
|
2603
2610
|
async execute(args, ctx) {
|
|
2604
2611
|
try {
|
|
2605
|
-
const { executeTests } = await import("./run-tests-
|
|
2612
|
+
const { executeTests } = await import("./run-tests-MKBVRMBA.js");
|
|
2606
2613
|
const argStr = args.join(" ").trim();
|
|
2607
2614
|
let testArgs = {};
|
|
2608
2615
|
if (argStr) {
|
|
@@ -6715,7 +6722,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6715
6722
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6716
6723
|
process.exit(1);
|
|
6717
6724
|
}
|
|
6718
|
-
const { startWebServer } = await import("./server-
|
|
6725
|
+
const { startWebServer } = await import("./server-J7BPHI7D.js");
|
|
6719
6726
|
await startWebServer({ port, host: options.host });
|
|
6720
6727
|
});
|
|
6721
6728
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6838,7 +6845,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
6838
6845
|
});
|
|
6839
6846
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
6840
6847
|
try {
|
|
6841
|
-
const batch = await import("./batch-
|
|
6848
|
+
const batch = await import("./batch-KZQHKPE5.js");
|
|
6842
6849
|
switch (action) {
|
|
6843
6850
|
case "submit":
|
|
6844
6851
|
if (!arg) {
|
|
@@ -6881,7 +6888,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
6881
6888
|
}
|
|
6882
6889
|
});
|
|
6883
6890
|
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
|
|
6884
|
-
const { startMcpServer } = await import("./server-
|
|
6891
|
+
const { startMcpServer } = await import("./server-B6U5GMZ7.js");
|
|
6885
6892
|
await startMcpServer({
|
|
6886
6893
|
allowDestructive: !!options.allowDestructive,
|
|
6887
6894
|
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
@@ -7008,7 +7015,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
7008
7015
|
}),
|
|
7009
7016
|
config.get("customProviders")
|
|
7010
7017
|
);
|
|
7011
|
-
const { startHub } = await import("./hub-
|
|
7018
|
+
const { startHub } = await import("./hub-A66CLTFF.js");
|
|
7012
7019
|
await startHub(
|
|
7013
7020
|
{
|
|
7014
7021
|
topic: topic ?? "",
|
|
@@ -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,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
schemaToJsonSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z2V6EYPQ.js";
|
|
7
7
|
import "./chunk-2ZD3YTVM.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import "./chunk-ANYYM4CF.js";
|
|
@@ -12,10 +12,10 @@ import "./chunk-KJLJPUY2.js";
|
|
|
12
12
|
import "./chunk-6VRJGH25.js";
|
|
13
13
|
import "./chunk-2DXY7UGF.js";
|
|
14
14
|
import "./chunk-KHYD3WXE.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-IGNYJUZU.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AKCXRW2Q.js";
|
|
19
19
|
|
|
20
20
|
// src/mcp/server.ts
|
|
21
21
|
import { createInterface } from "readline";
|
|
@@ -23,10 +23,10 @@ import {
|
|
|
23
23
|
persistToolRound,
|
|
24
24
|
rebuildExtraMessages,
|
|
25
25
|
setupProxy
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-MYQANQ6F.js";
|
|
27
27
|
import {
|
|
28
28
|
ConfigManager
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-US6MQO6W.js";
|
|
30
30
|
import {
|
|
31
31
|
ToolExecutor,
|
|
32
32
|
ToolRegistry,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
spawnAgentContext,
|
|
45
45
|
truncateOutput,
|
|
46
46
|
undoStack
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-Z2V6EYPQ.js";
|
|
48
48
|
import "./chunk-2ZD3YTVM.js";
|
|
49
49
|
import "./chunk-4BKXL7SM.js";
|
|
50
50
|
import "./chunk-ANYYM4CF.js";
|
|
@@ -53,7 +53,7 @@ import "./chunk-KJLJPUY2.js";
|
|
|
53
53
|
import "./chunk-6VRJGH25.js";
|
|
54
54
|
import "./chunk-2DXY7UGF.js";
|
|
55
55
|
import "./chunk-KHYD3WXE.js";
|
|
56
|
-
import "./chunk-
|
|
56
|
+
import "./chunk-IGNYJUZU.js";
|
|
57
57
|
import {
|
|
58
58
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
59
59
|
AUTHOR,
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
SKILLS_DIR_NAME,
|
|
73
73
|
VERSION,
|
|
74
74
|
buildUserIdentityPrompt
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-AKCXRW2Q.js";
|
|
76
76
|
|
|
77
77
|
// src/web/server.ts
|
|
78
78
|
import express from "express";
|
|
@@ -622,6 +622,12 @@ var SessionHandler = class _SessionHandler {
|
|
|
622
622
|
}
|
|
623
623
|
return;
|
|
624
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));
|
|
625
631
|
case "auto_pause_response": {
|
|
626
632
|
const resolve3 = this.pendingAutoPause.get(msg.requestId);
|
|
627
633
|
if (resolve3) {
|
|
@@ -2231,7 +2237,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2231
2237
|
case "test": {
|
|
2232
2238
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2233
2239
|
try {
|
|
2234
|
-
const { executeTests } = await import("./run-tests-
|
|
2240
|
+
const { executeTests } = await import("./run-tests-MKBVRMBA.js");
|
|
2235
2241
|
const argStr = args.join(" ").trim();
|
|
2236
2242
|
let testArgs = {};
|
|
2237
2243
|
if (argStr) {
|
|
@@ -2745,6 +2751,106 @@ Add .md files to create commands.` });
|
|
|
2745
2751
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
2746
2752
|
}
|
|
2747
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
|
+
}
|
|
2748
2854
|
sendSessionMessages() {
|
|
2749
2855
|
const session = this.sessions.current;
|
|
2750
2856
|
if (!session) return;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-Z2V6EYPQ.js";
|
|
8
8
|
import "./chunk-2ZD3YTVM.js";
|
|
9
9
|
import "./chunk-4BKXL7SM.js";
|
|
10
10
|
import "./chunk-ANYYM4CF.js";
|
|
@@ -13,10 +13,10 @@ import "./chunk-KJLJPUY2.js";
|
|
|
13
13
|
import "./chunk-6VRJGH25.js";
|
|
14
14
|
import "./chunk-2DXY7UGF.js";
|
|
15
15
|
import "./chunk-KHYD3WXE.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-IGNYJUZU.js";
|
|
17
17
|
import {
|
|
18
18
|
SUBAGENT_ALLOWED_TOOLS
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-AKCXRW2Q.js";
|
|
20
20
|
|
|
21
21
|
// src/hub/task-orchestrator.ts
|
|
22
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>
|