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.
@@ -1,70 +1,27 @@
1
- // src/symbols/vector-store.ts
2
- import fs2 from "fs";
3
- import path2 from "path";
4
- import os2 from "os";
5
- import crypto from "crypto";
1
+ import {
2
+ EMBEDDING_DIM
3
+ } from "./chunk-JV5N65KN.js";
6
4
 
7
- // src/symbols/embedder.ts
5
+ // src/symbols/vector-store.ts
6
+ import fs from "fs";
8
7
  import path from "path";
9
8
  import os from "os";
10
- import fs from "fs";
11
- var EMBEDDING_MODEL_ID = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
12
- var EMBEDDING_DIM = 384;
13
- var pipelinePromise = null;
14
- function cacheDir() {
15
- return path.join(os.homedir(), ".aicli", "models");
16
- }
17
- async function getEmbedder() {
18
- if (pipelinePromise) return pipelinePromise;
19
- pipelinePromise = (async () => {
20
- const mod = await import("@huggingface/transformers");
21
- const dir = cacheDir();
22
- fs.mkdirSync(dir, { recursive: true });
23
- mod.env.cacheDir = dir;
24
- mod.env.allowRemoteModels = true;
25
- mod.env.allowLocalModels = true;
26
- const pipe = await mod.pipeline("feature-extraction", EMBEDDING_MODEL_ID, {
27
- // Keep the ONNX session in float32; int8 quantization exists but the
28
- // quality drop on short code identifiers is noticeable.
29
- dtype: "fp32"
30
- });
31
- return pipe;
32
- })();
33
- return pipelinePromise;
34
- }
35
- async function embed(texts) {
36
- if (texts.length === 0) return [];
37
- const pipe = await getEmbedder();
38
- const out = await pipe(texts, { pooling: "mean", normalize: true });
39
- const batch = texts.length;
40
- const dim = EMBEDDING_DIM;
41
- const rows = new Array(batch);
42
- for (let i = 0; i < batch; i++) {
43
- rows[i] = new Float32Array(out.data.buffer, out.data.byteOffset + i * dim * 4, dim).slice();
44
- }
45
- return rows;
46
- }
47
- async function embedOne(text) {
48
- const [vec] = await embed([text]);
49
- return vec;
50
- }
51
-
52
- // src/symbols/vector-store.ts
9
+ import crypto from "crypto";
53
10
  var MAGIC = 1094927190;
54
11
  var VERSION = 1;
55
12
  var HEADER_BYTES = 16;
56
13
  function indexDir() {
57
- return path2.join(os2.homedir(), ".aicli", "index");
14
+ return path.join(os.homedir(), ".aicli", "index");
58
15
  }
59
16
  function projectHash(root) {
60
- return crypto.createHash("sha1").update(path2.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
17
+ return crypto.createHash("sha1").update(path.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
61
18
  }
62
19
  function vecPath(root) {
63
- return path2.join(indexDir(), `${projectHash(root)}.vec`);
20
+ return path.join(indexDir(), `${projectHash(root)}.vec`);
64
21
  }
65
22
  function emptyVectorStore(root) {
66
23
  return {
67
- root: path2.resolve(root),
24
+ root: path.resolve(root),
68
25
  count: 0,
69
26
  dim: EMBEDDING_DIM,
70
27
  vectors: new Float32Array(0),
@@ -79,7 +36,7 @@ function saveVectorStore(root, indices, vectors) {
79
36
  }
80
37
  const count = indices.length;
81
38
  const dir = indexDir();
82
- fs2.mkdirSync(dir, { recursive: true });
39
+ fs.mkdirSync(dir, { recursive: true });
83
40
  const totalBytes = HEADER_BYTES + count * 4 + count * EMBEDDING_DIM * 4;
84
41
  const buf = Buffer.alloc(totalBytes);
85
42
  buf.writeUInt32LE(MAGIC, 0);
@@ -90,15 +47,15 @@ function saveVectorStore(root, indices, vectors) {
90
47
  Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
91
48
  const target = vecPath(root);
92
49
  const tmp = `${target}.tmp`;
93
- fs2.writeFileSync(tmp, buf);
94
- fs2.renameSync(tmp, target);
50
+ fs.writeFileSync(tmp, buf);
51
+ fs.renameSync(tmp, target);
95
52
  }
96
53
  function loadVectorStore(root) {
97
54
  const p = vecPath(root);
98
- if (!fs2.existsSync(p)) return null;
55
+ if (!fs.existsSync(p)) return null;
99
56
  let buf;
100
57
  try {
101
- buf = fs2.readFileSync(p);
58
+ buf = fs.readFileSync(p);
102
59
  } catch {
103
60
  return null;
104
61
  }
@@ -119,12 +76,12 @@ function loadVectorStore(root) {
119
76
  buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
120
77
  )
121
78
  );
122
- return { root: path2.resolve(root), count, dim, vectors, symbolIdx };
79
+ return { root: path.resolve(root), count, dim, vectors, symbolIdx };
123
80
  }
124
81
  function clearVectorStore(root) {
125
82
  const p = vecPath(root);
126
83
  try {
127
- if (fs2.existsSync(p)) fs2.unlinkSync(p);
84
+ if (fs.existsSync(p)) fs.unlinkSync(p);
128
85
  } catch {
129
86
  }
130
87
  }
@@ -156,9 +113,6 @@ function searchVectorStore(store, queryVec, k) {
156
113
  }
157
114
 
158
115
  export {
159
- EMBEDDING_DIM,
160
- embed,
161
- embedOne,
162
116
  emptyVectorStore,
163
117
  saveVectorStore,
164
118
  loadVectorStore,
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-E7YC4GWV.js";
11
+ } from "./chunk-AKCXRW2Q.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.89";
9
+ var VERSION = "0.4.91";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -23,7 +23,7 @@ import {
23
23
  } from "./chunk-6VRJGH25.js";
24
24
  import {
25
25
  runTestsTool
26
- } from "./chunk-TKYNTXKB.js";
26
+ } from "./chunk-IGNYJUZU.js";
27
27
  import {
28
28
  CONFIG_DIR_NAME,
29
29
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -31,7 +31,7 @@ import {
31
31
  SUBAGENT_ALLOWED_TOOLS,
32
32
  SUBAGENT_DEFAULT_MAX_ROUNDS,
33
33
  SUBAGENT_MAX_ROUNDS_LIMIT
34
- } from "./chunk-E7YC4GWV.js";
34
+ } from "./chunk-AKCXRW2Q.js";
35
35
 
36
36
  // src/tools/types.ts
37
37
  function isFileWriteTool(name) {
@@ -36,18 +36,21 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-4WVXTADR.js";
39
+ } from "./chunk-XQHCCV3A.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
43
- } from "./chunk-GTKJUEBS.js";
43
+ } from "./chunk-MO7MWNWC.js";
44
44
  import {
45
45
  loadIndex
46
46
  } from "./chunk-BJAT4GNC.js";
47
+ import "./chunk-PASCDYMH.js";
47
48
  import {
48
- EMBEDDING_DIM,
49
- embedOne
50
- } from "./chunk-XMA222FQ.js";
49
+ loadChatIndex,
50
+ redactJson,
51
+ searchChatMemory
52
+ } from "./chunk-5S3PIG5O.js";
53
+ import "./chunk-JV5N65KN.js";
51
54
 
52
55
  // src/web/server.ts
53
56
  import express from "express";
@@ -435,8 +438,8 @@ ${err}`
435
438
  return EnvLoader.getDefaultProvider() ?? this.config.defaultProvider;
436
439
  }
437
440
  /** 点分路径读取配置值,如 `ui.theme` → config.ui.theme */
438
- getByPath(path4) {
439
- const keys = path4.split(".");
441
+ getByPath(path3) {
442
+ const keys = path3.split(".");
440
443
  let current = this.config;
441
444
  for (const key of keys) {
442
445
  if (current == null || typeof current !== "object") return void 0;
@@ -445,8 +448,8 @@ ${err}`
445
448
  return current;
446
449
  }
447
450
  /** 点分路径写入配置值,自动类型转换(boolean/number/string)并持久化 */
448
- setByPath(path4, rawValue) {
449
- const keys = path4.split(".");
451
+ setByPath(path3, rawValue) {
452
+ const keys = path3.split(".");
450
453
  if (keys.length === 0) return;
451
454
  let value = rawValue;
452
455
  if (rawValue === "true") value = true;
@@ -465,7 +468,7 @@ ${err}`
465
468
  const result = ConfigSchema.safeParse(draft);
466
469
  if (!result.success) {
467
470
  const firstErr = result.error.errors[0];
468
- throw new ConfigError(`Invalid config value for "${path4}": ${firstErr?.message ?? "validation failed"}`);
471
+ throw new ConfigError(`Invalid config value for "${path3}": ${firstErr?.message ?? "validation failed"}`);
469
472
  }
470
473
  this.config = result.data;
471
474
  this.save();
@@ -3057,104 +3060,6 @@ var Session = class _Session {
3057
3060
  }
3058
3061
  };
3059
3062
 
3060
- // src/security/redactor.ts
3061
- var DEFAULT_PATTERNS = [
3062
- // password: xxx / password = xxx / password="xxx"
3063
- // Covers YAML / JSON / shell-ish / env-file forms.
3064
- { kind: "password", regex: /\b(password|passwd|pwd)\s*[:=]\s*["']?([^\s"',;{}]{4,200})["']?/gi },
3065
- // PGPASSWORD=xxx (explicit bash env-var form, separate rule because no quotes usually)
3066
- { kind: "pgpassword-env", regex: /\b(PGPASSWORD)=([^\s"']{4,200})/g },
3067
- // JDBC/PG/MySQL/Mongo connection strings with inline credentials
3068
- // postgresql://user:pass@host/db → redact pass
3069
- { kind: "db-uri-password", regex: /(\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^:\s]+:)([^@\s]+)(@)/gi },
3070
- // Anthropic API keys
3071
- { kind: "anthropic-key", regex: /(sk-ant-[a-zA-Z0-9_-]{90,})/g },
3072
- // OpenAI / generic sk- keys — requires length ≥32 to avoid eating short identifiers
3073
- { kind: "openai-key", regex: /(sk-(?:proj-)?[a-zA-Z0-9_-]{32,})/g },
3074
- // GitHub personal access tokens
3075
- { kind: "github-pat", regex: /\b(ghp_[a-zA-Z0-9]{36})\b/g },
3076
- { kind: "github-oauth", regex: /\b(gho_[a-zA-Z0-9]{36})\b/g },
3077
- { kind: "github-install", regex: /\b(ghs_[a-zA-Z0-9]{36})\b/g },
3078
- // Slack tokens
3079
- { kind: "slack-bot", regex: /\b(xoxb-\d+-\d+-[a-zA-Z0-9]+)\b/g },
3080
- { kind: "slack-user", regex: /\b(xoxp-\d+-\d+-\d+-[a-zA-Z0-9]+)\b/g },
3081
- // AWS access key IDs (AKIA...) and secret access keys are context-dependent;
3082
- // we only catch the ID because secret key alone is indistinguishable from random base64.
3083
- { kind: "aws-access-key-id", regex: /\b(AKIA[0-9A-Z]{16})\b/g },
3084
- // Google API keys
3085
- { kind: "google-api-key", regex: /\b(AIza[0-9A-Za-z_-]{35})\b/g },
3086
- // Generic "api_key": "..." / "apiKey": "..." / api-key=xxx
3087
- { kind: "api-key", regex: /\b(api[_-]?key)\s*[:=]\s*["']?([a-zA-Z0-9_\-.]{16,200})["']?/gi },
3088
- // Generic token: xxx (only when value looks token-shaped; avoids eating human prose)
3089
- { kind: "token", regex: /\b(token|access[_-]?token|bearer[_-]?token)\s*[:=]\s*["']?([a-zA-Z0-9_\-.]{20,300})["']?/gi },
3090
- // Bearer <token> in Authorization headers
3091
- { kind: "bearer", regex: /\b(Authorization:\s*Bearer\s+)([a-zA-Z0-9_\-.=]{20,500})/g },
3092
- // Private key PEM blocks — catch the header+footer together
3093
- { kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
3094
- ];
3095
- function render(placeholder, kind) {
3096
- return placeholder.replace("{kind}", kind);
3097
- }
3098
- function redactString(input, options) {
3099
- if (!options.enabled || !input) return { redacted: input, hits: [] };
3100
- const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
3101
- const patterns = [
3102
- ...options.patterns ?? DEFAULT_PATTERNS,
3103
- ...(options.customRegexes ?? []).flatMap((src, i) => {
3104
- try {
3105
- const flags = src.match(/^\/.*\/([gimsuy]*)$/)?.[1] ?? "";
3106
- const body = src.replace(/^\/(.*)\/[gimsuy]*$/, "$1");
3107
- const regex = new RegExp(body, flags.includes("g") ? flags : flags + "g");
3108
- return [{ kind: `custom-${i}`, regex }];
3109
- } catch {
3110
- return [];
3111
- }
3112
- })
3113
- ];
3114
- let redacted = input;
3115
- const hits = [];
3116
- for (const { kind, regex } of patterns) {
3117
- const rx = new RegExp(regex.source, regex.flags);
3118
- redacted = redacted.replace(rx, (...args) => {
3119
- const match = args[0];
3120
- const probe = new RegExp(rx.source).exec(match);
3121
- const captureCount = probe ? probe.length - 1 : 0;
3122
- const g1 = captureCount >= 1 ? args[1] : void 0;
3123
- const g2 = captureCount >= 2 ? args[2] : void 0;
3124
- const offset = args[1 + captureCount];
3125
- if (captureCount >= 2 && typeof g2 === "string") {
3126
- hits.push({ kind, start: offset + (g1?.length ?? 0), length: g2.length, secret: g2 });
3127
- return `${g1}${render(placeholder, kind)}`;
3128
- }
3129
- hits.push({ kind, start: offset, length: match.length, secret: g1 ?? match });
3130
- return render(placeholder, kind);
3131
- });
3132
- }
3133
- return { redacted, hits };
3134
- }
3135
- function redactJson(value, options) {
3136
- if (!options.enabled) return { value, hits: [] };
3137
- const allHits = [];
3138
- function walk(v) {
3139
- if (typeof v === "string") {
3140
- const r = redactString(v, options);
3141
- allHits.push(...r.hits);
3142
- return r.redacted;
3143
- }
3144
- if (Array.isArray(v)) return v.map(walk);
3145
- if (v && typeof v === "object") {
3146
- const out = {};
3147
- for (const [k, vv] of Object.entries(v)) {
3148
- out[k] = walk(vv);
3149
- }
3150
- return out;
3151
- }
3152
- return v;
3153
- }
3154
- const redacted = walk(value);
3155
- return { value: redacted, hits: allHits };
3156
- }
3157
-
3158
3063
  // src/session/session-manager.ts
3159
3064
  function safeDate(value) {
3160
3065
  const d = new Date(value);
@@ -4358,8 +4263,8 @@ function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "co
4358
4263
  if (rule.when) {
4359
4264
  if (rule.when.dangerLevel && rule.when.dangerLevel !== dangerLevel) continue;
4360
4265
  if (rule.when.pathPattern) {
4361
- const path4 = String(args["path"] ?? args["command"] ?? "");
4362
- if (!path4.includes(rule.when.pathPattern)) continue;
4266
+ const path3 = String(args["path"] ?? args["command"] ?? "");
4267
+ if (!path3.includes(rule.when.pathPattern)) continue;
4363
4268
  }
4364
4269
  }
4365
4270
  return rule.action;
@@ -7859,92 +7764,6 @@ ${lines.join("\n")}`;
7859
7764
  }
7860
7765
  };
7861
7766
 
7862
- // src/memory/chat-index.ts
7863
- import fs2 from "fs";
7864
- import path3 from "path";
7865
- import os from "os";
7866
- import crypto from "crypto";
7867
- var MEMORY_DIR_NAME = "memory-index";
7868
- var CHUNKS_FILE = "chunks.json";
7869
- var VECTORS_FILE = "vectors.vec";
7870
- var VEC_MAGIC = 1094929750;
7871
- var VEC_VERSION = 1;
7872
- var VEC_HEADER_BYTES = 16;
7873
- function memoryIndexDir() {
7874
- return path3.join(os.homedir(), ".aicli", MEMORY_DIR_NAME);
7875
- }
7876
- function chunksPath() {
7877
- return path3.join(memoryIndexDir(), CHUNKS_FILE);
7878
- }
7879
- function vectorsPath() {
7880
- return path3.join(memoryIndexDir(), VECTORS_FILE);
7881
- }
7882
- function readVectorsFile(expectedCount) {
7883
- const p = vectorsPath();
7884
- if (!fs2.existsSync(p)) return null;
7885
- let buf;
7886
- try {
7887
- buf = fs2.readFileSync(p);
7888
- } catch {
7889
- return null;
7890
- }
7891
- if (buf.length < VEC_HEADER_BYTES) return null;
7892
- const magic = buf.readUInt32LE(0);
7893
- const version = buf.readUInt32LE(4);
7894
- const count = buf.readUInt32LE(8);
7895
- const dim = buf.readUInt32LE(12);
7896
- if (magic !== VEC_MAGIC || version !== VEC_VERSION || dim !== EMBEDDING_DIM) return null;
7897
- if (count !== expectedCount) return null;
7898
- const expected = VEC_HEADER_BYTES + count * dim * 4;
7899
- if (buf.length !== expected) return null;
7900
- return new Float32Array(
7901
- buf.buffer.slice(buf.byteOffset + VEC_HEADER_BYTES, buf.byteOffset + expected)
7902
- );
7903
- }
7904
- function readIndexFile() {
7905
- const p = chunksPath();
7906
- if (!fs2.existsSync(p)) return null;
7907
- try {
7908
- const raw = fs2.readFileSync(p, "utf-8");
7909
- const data = JSON.parse(raw);
7910
- if (data.version !== 1) return null;
7911
- return data;
7912
- } catch {
7913
- return null;
7914
- }
7915
- }
7916
- function loadChatIndex() {
7917
- const idx = readIndexFile();
7918
- if (!idx) return null;
7919
- const vectors = readVectorsFile(idx.chunks.length);
7920
- if (!vectors) return null;
7921
- return { idx, vectors };
7922
- }
7923
- async function searchChatMemory(query, options = {}) {
7924
- const topK = options.topK ?? 5;
7925
- const minScore = options.minScore ?? 0.25;
7926
- const loaded = loadChatIndex();
7927
- if (!loaded || loaded.idx.chunks.length === 0) return [];
7928
- const { idx, vectors } = loaded;
7929
- const { redacted } = redactString(query, { enabled: true });
7930
- const qvec = await embedOne(redacted);
7931
- const candidates = [];
7932
- for (let i = 0; i < idx.chunks.length; i++) {
7933
- const c = idx.chunks[i];
7934
- if (options.sessionId && c.sessionId !== options.sessionId) continue;
7935
- if (options.excludeSessionId && c.sessionId === options.excludeSessionId) continue;
7936
- let score = 0;
7937
- const base = i * EMBEDDING_DIM;
7938
- for (let d = 0; d < EMBEDDING_DIM; d++) {
7939
- score += vectors[base + d] * qvec[d];
7940
- }
7941
- if (score < minScore) continue;
7942
- candidates.push({ chunk: c, score });
7943
- }
7944
- candidates.sort((a, b) => b.score - a.score);
7945
- return candidates.slice(0, topK);
7946
- }
7947
-
7948
7767
  // src/tools/builtin/recall-memory.ts
7949
7768
  function formatHit(h, i) {
7950
7769
  const ts = h.chunk.timestamp.slice(0, 16).replace("T", " ");
@@ -9240,9 +9059,9 @@ function getDevStatePath() {
9240
9059
  return join10(homedir4(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
9241
9060
  }
9242
9061
  function loadDevState() {
9243
- const path4 = getDevStatePath();
9244
- if (!existsSync17(path4)) return null;
9245
- const content = readFileSync11(path4, "utf-8").trim();
9062
+ const path3 = getDevStatePath();
9063
+ if (!existsSync17(path3)) return null;
9064
+ const content = readFileSync11(path3, "utf-8").trim();
9246
9065
  return content || null;
9247
9066
  }
9248
9067
 
@@ -9795,6 +9614,12 @@ var SessionHandler = class _SessionHandler {
9795
9614
  }
9796
9615
  return;
9797
9616
  }
9617
+ case "memory_search":
9618
+ return this.handleMemorySearch(msg.query, msg.topK, msg.minScore, msg.excludeCurrentSession);
9619
+ case "memory_status_request":
9620
+ return this.handleMemoryStatus();
9621
+ case "memory_rebuild":
9622
+ return this.handleMemoryRebuild(Boolean(msg.full));
9798
9623
  case "auto_pause_response": {
9799
9624
  const resolve6 = this.pendingAutoPause.get(msg.requestId);
9800
9625
  if (resolve6) {
@@ -11168,7 +10993,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11168
10993
  const root = process.cwd();
11169
10994
  const { loadIndex: loadIndex2, clearIndex } = await import("./store-247B3TAU.js");
11170
10995
  const { indexProject: indexProject2 } = await import("./indexer-O5FCGFBJ.js");
11171
- const { loadVectorStore, clearVectorStore } = await import("./vector-store-UR7IARXB.js");
10996
+ const { loadVectorStore, clearVectorStore } = await import("./vector-store-NDUFLNGN.js");
11172
10997
  if (sub === "status") {
11173
10998
  const idx = loadIndex2(root);
11174
10999
  const vec = loadVectorStore(root);
@@ -11217,7 +11042,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11217
11042
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
11218
11043
  });
11219
11044
  try {
11220
- const { rebuildSemanticIndex } = await import("./semantic-MYAXLDCZ.js");
11045
+ const { rebuildSemanticIndex } = await import("./semantic-3KJPAUW6.js");
11221
11046
  const stats = await rebuildSemanticIndex(root);
11222
11047
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
11223
11048
  this.send({
@@ -11404,7 +11229,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11404
11229
  case "test": {
11405
11230
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11406
11231
  try {
11407
- const { executeTests } = await import("./run-tests-TGGXTOFF.js");
11232
+ const { executeTests } = await import("./run-tests-KHJ6YCPH.js");
11408
11233
  const argStr = args.join(" ").trim();
11409
11234
  let testArgs = {};
11410
11235
  if (argStr) {
@@ -11918,6 +11743,106 @@ Add .md files to create commands.` });
11918
11743
  this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
11919
11744
  }
11920
11745
  }
11746
+ // ── B4 chat memory recall (v0.4.90+) ──────────────────────────────
11747
+ // Lazy-imported so the 117 MB embedder stays out of the load path for
11748
+ // clients that never open the Memory panel.
11749
+ async handleMemorySearch(query, topK, minScore, excludeCurrent) {
11750
+ const q = (query ?? "").trim();
11751
+ if (!q) {
11752
+ this.send({ type: "error", message: "Memory search: query is empty." });
11753
+ return;
11754
+ }
11755
+ try {
11756
+ const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-QKFH7ZP6.js");
11757
+ const loaded = loadChatIndex2();
11758
+ if (!loaded || loaded.idx.chunks.length === 0) {
11759
+ this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
11760
+ return;
11761
+ }
11762
+ const hits = await searchChatMemory2(q, {
11763
+ topK: topK ?? 8,
11764
+ minScore: minScore ?? 0.2,
11765
+ excludeSessionId: excludeCurrent ? this.sessions.current?.id : void 0
11766
+ });
11767
+ this.send({
11768
+ type: "memory_hits",
11769
+ query: q,
11770
+ hits: hits.map((h) => ({
11771
+ id: h.chunk.id,
11772
+ sessionId: h.chunk.sessionId,
11773
+ sessionTitle: h.chunk.sessionTitle,
11774
+ provider: h.chunk.provider,
11775
+ model: h.chunk.model,
11776
+ timestamp: h.chunk.timestamp,
11777
+ score: h.score,
11778
+ text: h.chunk.text,
11779
+ startMessageIdx: h.chunk.startMessageIdx,
11780
+ endMessageIdx: h.chunk.endMessageIdx
11781
+ }))
11782
+ });
11783
+ } catch (err) {
11784
+ this.send({
11785
+ type: "error",
11786
+ message: `Memory search failed: ${err instanceof Error ? err.message : String(err)}`
11787
+ });
11788
+ }
11789
+ }
11790
+ async handleMemoryStatus() {
11791
+ try {
11792
+ const { getChatIndexStatus } = await import("./chat-index-QKFH7ZP6.js");
11793
+ const s = getChatIndexStatus();
11794
+ this.send({
11795
+ type: "memory_status",
11796
+ exists: s.exists,
11797
+ chunks: s.chunks,
11798
+ sessions: s.sessions,
11799
+ built: s.built,
11800
+ model: s.model,
11801
+ vecFileSizeBytes: s.vecFileSizeBytes,
11802
+ chunksFileSizeBytes: s.chunksFileSizeBytes
11803
+ });
11804
+ } catch (err) {
11805
+ this.send({
11806
+ type: "error",
11807
+ message: `Memory status failed: ${err instanceof Error ? err.message : String(err)}`
11808
+ });
11809
+ }
11810
+ }
11811
+ async handleMemoryRebuild(full) {
11812
+ try {
11813
+ this.send({
11814
+ type: "info",
11815
+ 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"
11816
+ });
11817
+ const { buildChatIndex } = await import("./chat-index-QKFH7ZP6.js");
11818
+ const stats = await buildChatIndex({
11819
+ full,
11820
+ onProgress: (p) => {
11821
+ this.send({
11822
+ type: "memory_rebuild_progress",
11823
+ stage: p.stage,
11824
+ processed: p.processed,
11825
+ total: p.total
11826
+ });
11827
+ }
11828
+ });
11829
+ this.send({
11830
+ type: "memory_rebuild_done",
11831
+ chunksTotal: stats.chunksTotal,
11832
+ chunksAdded: stats.chunksAdded,
11833
+ chunksRemoved: stats.chunksRemoved,
11834
+ sessionsIndexed: stats.sessionsIndexed,
11835
+ sessionsSkipped: stats.sessionsSkipped,
11836
+ durationMs: stats.durationMs
11837
+ });
11838
+ await this.handleMemoryStatus();
11839
+ } catch (err) {
11840
+ this.send({
11841
+ type: "error",
11842
+ message: `Memory rebuild failed: ${err instanceof Error ? err.message : String(err)}`
11843
+ });
11844
+ }
11845
+ }
11921
11846
  sendSessionMessages() {
11922
11847
  const session = this.sessions.current;
11923
11848
  if (!session) return;
@@ -385,7 +385,7 @@ ${content}`);
385
385
  }
386
386
  }
387
387
  async function runTaskMode(config, providers, configManager, topic) {
388
- const { TaskOrchestrator } = await import("./task-orchestrator-ODU45UQG.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-JB5TZNBK.js");
389
389
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
390
390
  let interrupted = false;
391
391
  const onSigint = () => {