ningzhi-agent-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CLAUDE.md +197 -0
  2. package/README.md +35 -0
  3. package/dist/agent/agent.d.ts +24 -0
  4. package/dist/agent/agent.js +432 -0
  5. package/dist/agent/cli.d.ts +1 -0
  6. package/dist/agent/cli.js +209 -0
  7. package/dist/agent/colors.d.ts +19 -0
  8. package/dist/agent/colors.js +92 -0
  9. package/dist/agent/commands.d.ts +7 -0
  10. package/dist/agent/commands.js +73 -0
  11. package/dist/agent/config.d.ts +38 -0
  12. package/dist/agent/config.js +119 -0
  13. package/dist/agent/context.d.ts +17 -0
  14. package/dist/agent/context.js +146 -0
  15. package/dist/agent/db.d.ts +25 -0
  16. package/dist/agent/db.js +185 -0
  17. package/dist/agent/hooks/engine.d.ts +27 -0
  18. package/dist/agent/hooks/engine.js +122 -0
  19. package/dist/agent/hooks/index.d.ts +1 -0
  20. package/dist/agent/hooks/index.js +11 -0
  21. package/dist/agent/mcp/client.d.ts +31 -0
  22. package/dist/agent/mcp/client.js +138 -0
  23. package/dist/agent/mcp/index.d.ts +4 -0
  24. package/dist/agent/mcp/index.js +27 -0
  25. package/dist/agent/mcp/wrapper.d.ts +7 -0
  26. package/dist/agent/mcp/wrapper.js +26 -0
  27. package/dist/agent/model.d.ts +5 -0
  28. package/dist/agent/model.js +35 -0
  29. package/dist/agent/permission/dangerous-path.json +115 -0
  30. package/dist/agent/permission/exec.d.ts +14 -0
  31. package/dist/agent/permission/exec.js +35 -0
  32. package/dist/agent/permission/is-dangerous-path.d.ts +8 -0
  33. package/dist/agent/permission/is-dangerous-path.js +96 -0
  34. package/dist/agent/permission/is-safe-domains.d.ts +1 -0
  35. package/dist/agent/permission/is-safe-domains.js +145 -0
  36. package/dist/agent/permission/network.d.ts +8 -0
  37. package/dist/agent/permission/network.js +14 -0
  38. package/dist/agent/permission/read.d.ts +9 -0
  39. package/dist/agent/permission/read.js +17 -0
  40. package/dist/agent/permission/util.d.ts +11 -0
  41. package/dist/agent/permission/util.js +347 -0
  42. package/dist/agent/permission/write.d.ts +11 -0
  43. package/dist/agent/permission/write.js +21 -0
  44. package/dist/agent/prompt.d.ts +1 -0
  45. package/dist/agent/prompt.js +52 -0
  46. package/dist/agent/skills/oppose/SKILL.md +100 -0
  47. package/dist/agent/skills/planner/SKILL.md +90 -0
  48. package/dist/agent/skills.d.ts +8 -0
  49. package/dist/agent/skills.js +103 -0
  50. package/dist/agent/tools/agent_tool.d.ts +3 -0
  51. package/dist/agent/tools/agent_tool.js +44 -0
  52. package/dist/agent/tools/exec_tool.d.ts +3 -0
  53. package/dist/agent/tools/exec_tool.js +33 -0
  54. package/dist/agent/tools/load_skill_tool.d.ts +3 -0
  55. package/dist/agent/tools/load_skill_tool.js +16 -0
  56. package/dist/agent/tools/memory_create_tool.d.ts +6 -0
  57. package/dist/agent/tools/memory_create_tool.js +39 -0
  58. package/dist/agent/tools/memory_delete_tool.d.ts +3 -0
  59. package/dist/agent/tools/memory_delete_tool.js +28 -0
  60. package/dist/agent/tools/memory_retrieve_tool.d.ts +4 -0
  61. package/dist/agent/tools/memory_retrieve_tool.js +26 -0
  62. package/dist/agent/tools/profile_update_tool.d.ts +3 -0
  63. package/dist/agent/tools/profile_update_tool.js +51 -0
  64. package/dist/agent/tools/read_file_tool.d.ts +3 -0
  65. package/dist/agent/tools/read_file_tool.js +16 -0
  66. package/dist/agent/tools/run_js_tool.d.ts +3 -0
  67. package/dist/agent/tools/run_js_tool.js +47 -0
  68. package/dist/agent/tools/run_py_tool.d.ts +3 -0
  69. package/dist/agent/tools/run_py_tool.js +37 -0
  70. package/dist/agent/tools/web_fetch_tool.d.ts +3 -0
  71. package/dist/agent/tools/web_fetch_tool.js +28 -0
  72. package/dist/agent/tools/web_search_tool.d.ts +3 -0
  73. package/dist/agent/tools/web_search_tool.js +38 -0
  74. package/dist/agent/tools/write_file_tool.d.ts +4 -0
  75. package/dist/agent/tools/write_file_tool.js +15 -0
  76. package/dist/agent/tools.d.ts +14 -0
  77. package/dist/agent/tools.js +218 -0
  78. package/dist/agent/utils.d.ts +11 -0
  79. package/dist/agent/utils.js +43 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.js +81 -0
  82. package/dist/install.d.ts +1 -0
  83. package/dist/install.js +150 -0
  84. package/package.json +56 -0
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.color = void 0;
37
+ exports.initColors = initColors;
38
+ exports.formatToolLog = formatToolLog;
39
+ // chalk 实例,懒加载,未初始化时为 null
40
+ let _chalk = null;
41
+ // 初始化颜色模块,必须在首次使用前调用一次
42
+ async function initColors() {
43
+ const mod = await Promise.resolve().then(() => __importStar(require("chalk")));
44
+ _chalk = mod.default;
45
+ }
46
+ // chalk 未就绪时返回的空实现:任意链式调用都返回原字符串,避免抛错
47
+ function createPlainFallback() {
48
+ // get 拦截属性访问(如 .red、.bold),返回新的 fallback,使链式调用不会断
49
+ // apply 拦截函数调用(如 chalk("text")),返回首个参数原样输出
50
+ const handler = {
51
+ get: () => createPlainFallback(),
52
+ apply: (_target, _thisArg, args) => args[0] ?? "",
53
+ };
54
+ // 目标函数本身也实现为「返回首个参数」,与 apply 行为保持一致
55
+ const fn = (...args) => args[0] ?? "";
56
+ // 用 Proxy 包裹函数,模拟 chalk 的链式 + 调用接口
57
+ return new Proxy(fn, handler);
58
+ }
59
+ // 获取 chalk 实例,未初始化则降级为空实现
60
+ function ck() {
61
+ if (!_chalk) {
62
+ return createPlainFallback();
63
+ }
64
+ return _chalk;
65
+ }
66
+ // 统一的颜色输出工具集合
67
+ exports.color = {
68
+ banner: (text) => ck().cyanBright.bold(text),
69
+ userPrefix: () => ck().greenBright.bold("You: "),
70
+ aiPrefix: () => ck().cyanBright.bold("AI: "),
71
+ goodbye: (text) => ck().yellowBright(text),
72
+ cancelled: (text) => ck().yellowBright.bold(text),
73
+ error: (text) => ck().redBright.bold(text),
74
+ // 工具调用日志相关样式
75
+ toolTag: () => ck().magentaBright("[Tool]"), // magentaBright 粉色高亮
76
+ toolName: (name) => ck().yellowBright(name), // yellowBright 黄色高亮
77
+ toolAction: () => ck().gray("called:"), // gray 灰色
78
+ toolArg: (arg) => ck().cyan(arg), // cyan 青色
79
+ gray: (text) => ck().gray(text),
80
+ tokenInfo: (text) => ck().gray(text),
81
+ warn: (text) => ck().yellow(text),
82
+ info: (text) => ck().blueBright(text),
83
+ good: (text) => ck().greenBright(text),
84
+ };
85
+ // 格式化工具调用日志:形如 [Tool] toolName called: detail
86
+ function formatToolLog(name, detail) {
87
+ const parts = [exports.color.toolTag(), exports.color.toolName(name), exports.color.toolAction()];
88
+ if (detail) {
89
+ parts.push(exports.color.toolArg(detail));
90
+ }
91
+ return "\n" + parts.join(" ");
92
+ }
@@ -0,0 +1,7 @@
1
+ export declare let threadId: string;
2
+ export interface ChatCommand {
3
+ name: string;
4
+ description: string;
5
+ execute(args: string[]): void | Promise<void>;
6
+ }
7
+ export declare const commands: Map<string, ChatCommand>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.commands = exports.threadId = void 0;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const cli_table3_1 = __importDefault(require("cli-table3"));
9
+ const colors_1 = require("./colors");
10
+ const db_1 = require("./db");
11
+ const agent_1 = require("./agent");
12
+ exports.threadId = (0, node_crypto_1.randomUUID)();
13
+ exports.commands = new Map();
14
+ // 新建会话
15
+ exports.commands.set("new", {
16
+ name: "new",
17
+ description: "Start a new chat session",
18
+ execute() {
19
+ exports.threadId = (0, node_crypto_1.randomUUID)();
20
+ console.log(colors_1.color.goodbye(`\nNew session started ${exports.threadId}.\n`));
21
+ },
22
+ });
23
+ // 列出最近会话
24
+ exports.commands.set("sessions", {
25
+ name: "sessions",
26
+ description: "List recent chat sessions",
27
+ execute() {
28
+ const sessions = (0, db_1.listRecentSessions)();
29
+ if (sessions.length === 0) {
30
+ console.log("\n暂无聊天记录\n");
31
+ return;
32
+ }
33
+ const table = new cli_table3_1.default({
34
+ head: ["thread_id", "最后用户输入的问题", "时间"],
35
+ });
36
+ for (const s of sessions) {
37
+ table.push([s.thread_id, s.last_question, s.last_ts]);
38
+ }
39
+ console.log("\n" + table.toString() + "\n");
40
+ },
41
+ });
42
+ // 切换会话
43
+ exports.commands.set("rewind", {
44
+ name: "rewind",
45
+ description: "Restore a chat session by thread_id",
46
+ execute(args) {
47
+ const id = args[0];
48
+ if (!id) {
49
+ console.log(colors_1.color.error("\nError: please provide a thread_id. Usage: /rewind <thread_id>\n"));
50
+ return;
51
+ }
52
+ if (!(0, db_1.threadIdExists)(id)) {
53
+ console.log(colors_1.color.error(`\nError: thread_id "${id}" not found.\n`));
54
+ return;
55
+ }
56
+ exports.threadId = id;
57
+ console.log(colors_1.color.goodbye(`\nRestored session ${exports.threadId}.\n`));
58
+ },
59
+ });
60
+ // 压缩上下文
61
+ exports.commands.set("compact", {
62
+ name: "compact",
63
+ description: "Compress context for the current session",
64
+ async execute() {
65
+ const result = await (0, agent_1.compressContext)(exports.threadId);
66
+ if (result.didCompress) {
67
+ console.log(colors_1.color.goodbye(`\nContext compressed (count: ${result.count}).\n`));
68
+ }
69
+ else {
70
+ console.log("\nNo context to compress.\n");
71
+ }
72
+ },
73
+ });
@@ -0,0 +1,38 @@
1
+ export declare const WORKSPACE_DIR: string;
2
+ export declare const CONFIG_PATH: string;
3
+ interface ModelConfig {
4
+ model: string;
5
+ apiKey: string;
6
+ baseURL: string;
7
+ }
8
+ export interface HookConfig {
9
+ matcher: string;
10
+ command: string;
11
+ }
12
+ export interface HooksConfig {
13
+ hooks: {
14
+ PreToolUse?: HookConfig[];
15
+ PostToolUse?: HookConfig[];
16
+ SessionStart?: HookConfig[];
17
+ };
18
+ }
19
+ export interface McpServerConfig {
20
+ command?: string;
21
+ args?: string[];
22
+ env?: Record<string, string>;
23
+ url?: string;
24
+ headers?: Record<string, string>;
25
+ }
26
+ interface NingzhiConfig {
27
+ model?: ModelConfig;
28
+ env?: Record<string, string>;
29
+ hooks?: HooksConfig["hooks"];
30
+ mcpServers?: Record<string, McpServerConfig>;
31
+ }
32
+ export declare function loadConfig(): NingzhiConfig;
33
+ export declare function getModelConfig(): ModelConfig;
34
+ export declare function getEnv(key: string): string | undefined;
35
+ export declare function getHooksConfig(): HooksConfig;
36
+ export declare function getMCPServerConfig(): Record<string, McpServerConfig>;
37
+ export declare function clearConfigCache(): void;
38
+ export {};
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CONFIG_PATH = exports.WORKSPACE_DIR = void 0;
37
+ exports.loadConfig = loadConfig;
38
+ exports.getModelConfig = getModelConfig;
39
+ exports.getEnv = getEnv;
40
+ exports.getHooksConfig = getHooksConfig;
41
+ exports.getMCPServerConfig = getMCPServerConfig;
42
+ exports.clearConfigCache = clearConfigCache;
43
+ const fs = __importStar(require("node:fs"));
44
+ const path = __importStar(require("node:path"));
45
+ const os = __importStar(require("node:os"));
46
+ exports.WORKSPACE_DIR = path.join(os.homedir(), ".ningzhiAgentCli");
47
+ exports.CONFIG_PATH = path.join(exports.WORKSPACE_DIR, "ningzhi.json");
48
+ let cachedConfig = null;
49
+ function loadConfig() {
50
+ if (cachedConfig) {
51
+ return cachedConfig;
52
+ }
53
+ if (!fs.existsSync(exports.CONFIG_PATH)) {
54
+ throw new Error(`找不到配置文件 ${exports.CONFIG_PATH}\n\n请创建该文件并配置相关信息,例如:\n{\n "model": {\n "model": "kimi-k2.6",\n "apiKey": "your-api-key",\n "baseURL": "https://api.moonshot.cn/v1"\n },\n "env": {\n "TAVILY_API_KEY": "your-tavily-api-key"\n }\n}`);
55
+ }
56
+ let config;
57
+ try {
58
+ const content = fs.readFileSync(exports.CONFIG_PATH, "utf-8");
59
+ config = JSON.parse(content);
60
+ }
61
+ catch (e) {
62
+ throw new Error(`配置文件 ${exports.CONFIG_PATH} 解析失败:${e instanceof Error ? e.message : String(e)}`);
63
+ }
64
+ cachedConfig = config;
65
+ return config;
66
+ }
67
+ function getModelConfig() {
68
+ const config = loadConfig();
69
+ if (!config.model || typeof config.model !== "object") {
70
+ throw new Error(`配置文件 ${exports.CONFIG_PATH} 中缺少 model 对象,请参考 https://chat.ningzhi2.site/getStart`);
71
+ }
72
+ if (!config.model.model) {
73
+ throw new Error(`配置文件 ${exports.CONFIG_PATH} 中缺少 model.model 字段,请参考 https://chat.ningzhi2.site/getStart`);
74
+ }
75
+ if (!config.model.apiKey) {
76
+ throw new Error(`配置文件 ${exports.CONFIG_PATH} 中缺少 model.apiKey 字段,请参考 https://chat.ningzhi2.site/getStart`);
77
+ }
78
+ if (!config.model.baseURL) {
79
+ throw new Error(`配置文件 ${exports.CONFIG_PATH} 中缺少 model.baseURL 字段,请参考 https://chat.ningzhi2.site/getStart`);
80
+ }
81
+ return config.model;
82
+ }
83
+ function getEnv(key) {
84
+ const config = loadConfig();
85
+ return config.env?.[key];
86
+ }
87
+ function getHooksConfig() {
88
+ try {
89
+ const config = loadConfig();
90
+ return { hooks: config.hooks || {} };
91
+ }
92
+ catch {
93
+ return { hooks: {} };
94
+ }
95
+ }
96
+ function getMCPServerConfig() {
97
+ try {
98
+ const content = fs.readFileSync(exports.CONFIG_PATH, "utf-8");
99
+ const parsed = JSON.parse(content);
100
+ if (!parsed.mcpServers || typeof parsed.mcpServers !== "object") {
101
+ console.warn('[MCP] ningzhi.json missing "mcpServers" field, skipping');
102
+ return {};
103
+ }
104
+ return parsed.mcpServers;
105
+ }
106
+ catch (err) {
107
+ // `ENOENT` 是 Node.js 中一个标准的系统错误码,全称是 Error NO ENTry (无此条目/文件不存在)
108
+ // 当调用`readFileSync` 时,如果`ningzhi.json` 文件 不存在 ,
109
+ // Node.js 会抛出一个带有`err.code === "ENOENT"` 的错误。
110
+ if (err.code === "ENOENT") {
111
+ return {};
112
+ }
113
+ console.warn(`[MCP] Failed to load ningzhi.json: ${err.message}, skipping`);
114
+ return {};
115
+ }
116
+ }
117
+ function clearConfigCache() {
118
+ cachedConfig = null;
119
+ }
@@ -0,0 +1,17 @@
1
+ import { BaseMessage } from "@langchain/core/messages";
2
+ export declare function getModelContextLimit(): number;
3
+ /**
4
+ * 压缩消息为简洁的摘要
5
+ * @param messages 消息数组
6
+ * @param existingSummary 已存在的摘要
7
+ * @returns 压缩后的摘要
8
+ */
9
+ export declare function compressMessages(messages: BaseMessage[], existingSummary: string | null): Promise<string>;
10
+ /**
11
+ * 计算安全的压缩边界索引,确保保留区域内的 tool_calls 和 ToolMessage 配对完整。
12
+ * 防止 破坏了消息列表的完整度( ToolMessage 匹配不到 对应的 AIMessage ),导致报错。
13
+ *
14
+ * 策略:从 messages.length - minKeep 开始,如果边界切在了 ToolMessage 或其对应的
15
+ * AIMessage 之后,就把边界往前移动,直到整个工具调用单元都被保留。
16
+ */
17
+ export declare function findSafeCompressionIndex(messages: BaseMessage[], minKeep: number): number;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getModelContextLimit = getModelContextLimit;
4
+ exports.compressMessages = compressMessages;
5
+ exports.findSafeCompressionIndex = findSafeCompressionIndex;
6
+ const messages_1 = require("@langchain/core/messages");
7
+ const model_1 = require("./model");
8
+ const config_1 = require("./config");
9
+ const MODEL_CONTEXT_LIMITS = {
10
+ // Moonshot / Kimi
11
+ "moonshot-v1-8k": 8192,
12
+ "moonshot-v1-32k": 32768,
13
+ "moonshot-v1-128k": 131072,
14
+ "kimi-k2.6": 256000,
15
+ // DeepSeek
16
+ "deepseek-chat": 1048576,
17
+ "deepseek-reasoner": 1048576,
18
+ "deepseek-v3": 65536,
19
+ "deepseek-v3.1": 131072,
20
+ "deepseek-v4": 1048576,
21
+ "deepseek-r1": 65536,
22
+ // MiniMax
23
+ "minimax-text-01": 1048576,
24
+ "minimax-m1": 1048576,
25
+ "minimax-m3": 1048576,
26
+ // GLM (智谱)
27
+ "glm-4": 131072,
28
+ "glm-4-plus": 131072,
29
+ "glm-4-air": 131072,
30
+ "glm-4-flash": 131072,
31
+ "glm-4v": 131072,
32
+ // Qwen (Alibaba)
33
+ "qwen-max": 32768,
34
+ "qwen-plus": 131072,
35
+ "qwen-plus-2025-07-28": 1048576,
36
+ "qwen-turbo": 1048576,
37
+ "qwen-long": 10485760,
38
+ // Xiaomi MiMo
39
+ "mimo-7b": 32768,
40
+ "mimo-v2.5": 1048576,
41
+ };
42
+ function getModelContextLimit() {
43
+ const modelName = (0, config_1.getModelConfig)().model || "";
44
+ return MODEL_CONTEXT_LIMITS[modelName.toLowerCase()] || 128000;
45
+ }
46
+ /**
47
+ * 格式化消息为压缩格式
48
+ * @param messages 消息数组
49
+ * @returns 压缩后的消息字符串
50
+ */
51
+ function formatMessagesForCompression(messages) {
52
+ return messages
53
+ .map((msg) => {
54
+ const role = msg.type;
55
+ // 格式化消息内容为字符串
56
+ let content = typeof msg.content === "string"
57
+ ? msg.content
58
+ : JSON.stringify(msg.content);
59
+ // 如果是 AI 消息且有工具调用,添加工具调用信息
60
+ if (role === "ai" && msg.tool_calls?.length) {
61
+ content += `\n[Tool Calls: ${JSON.stringify(msg.tool_calls)}]`;
62
+ }
63
+ // 格式化消息为压缩格式, [消息角色]:消息内容
64
+ return `[${role}] ${content}`;
65
+ })
66
+ .join("\n\n");
67
+ }
68
+ /**
69
+ * 压缩消息为简洁的摘要
70
+ * @param messages 消息数组
71
+ * @param existingSummary 已存在的摘要
72
+ * @returns 压缩后的摘要
73
+ */
74
+ async function compressMessages(messages, existingSummary) {
75
+ // 如果消息数组为空,直接返回已存在的摘要
76
+ if (messages.length === 0) {
77
+ return existingSummary || "";
78
+ }
79
+ // 格式化消息为压缩格式
80
+ const text = formatMessagesForCompression(messages);
81
+ // 构建压缩提示
82
+ const prompt = `请将以下对话内容压缩为一段简洁的摘要,保留关键信息、决策和结论:\n\n${text}`;
83
+ // 调用大模型压缩摘要
84
+ const model = (0, model_1.createModel)({ streaming: false });
85
+ // 处理大模型响应
86
+ // 如果响应内容是字符串,直接返回;如果是对象,转换为字符串
87
+ const response = await model.invoke([new messages_1.HumanMessage(prompt)]);
88
+ const newSummary = typeof response.content === "string"
89
+ ? response.content
90
+ : JSON.stringify(response.content);
91
+ // 如果已存在摘要,添加到新摘要中
92
+ if (existingSummary) {
93
+ return `${existingSummary}\n\n[后续对话摘要]\n${newSummary}`;
94
+ }
95
+ // 如果已不存在摘要,直接返回新摘要
96
+ return newSummary;
97
+ }
98
+ /**
99
+ * 计算安全的压缩边界索引,确保保留区域内的 tool_calls 和 ToolMessage 配对完整。
100
+ * 防止 破坏了消息列表的完整度( ToolMessage 匹配不到 对应的 AIMessage ),导致报错。
101
+ *
102
+ * 策略:从 messages.length - minKeep 开始,如果边界切在了 ToolMessage 或其对应的
103
+ * AIMessage 之后,就把边界往前移动,直到整个工具调用单元都被保留。
104
+ */
105
+ function findSafeCompressionIndex(messages, minKeep) {
106
+ // 初始化压缩边界索引为 messages.length - minKeep
107
+ let index = Math.max(0, messages.length - minKeep);
108
+ // 收集当前保留区域内所有 ToolMessage 的 tool_call_id
109
+ const toolCallIdsInKeepRegion = new Set();
110
+ // 从保留区域开始,向后遍历,收集所有 ToolMessage 的 tool_call_id
111
+ for (let i = index; i < messages.length; i++) {
112
+ if (messages[i].type === "tool") {
113
+ toolCallIdsInKeepRegion.add(messages[i].tool_call_id);
114
+ }
115
+ }
116
+ // 往前移动 index,直到所有 toolCallIdsInKeepRegion 都能在保留区域内找到对应的 AIMessage
117
+ while (index > 0) {
118
+ // 获取前一条消息
119
+ const prevMsg = messages[index - 1];
120
+ // 【1】如果前一条是 ToolMessage,它也会被纳入保留区,所以加入待匹配集合
121
+ if (prevMsg.type === "tool") {
122
+ toolCallIdsInKeepRegion.add(prevMsg.tool_call_id);
123
+ index--;
124
+ continue;
125
+ }
126
+ // 【2】如果前一条是 AIMessage 且有 tool_calls,检查它 是否匹配 保留区内的 ToolMessage
127
+ if (messages_1.AIMessage.isInstance(prevMsg) && prevMsg.tool_calls?.length) {
128
+ const hasMatchingCall = prevMsg.tool_calls.some((call) => call.id && toolCallIdsInKeepRegion.has(call.id));
129
+ // 如果前一条 AIMessage 匹配了保留区内的 ToolMessage,就保留它
130
+ if (hasMatchingCall) {
131
+ // 这个 AIMessage 必须保留,index 前移
132
+ index--;
133
+ // 这个 AIMessage 可能还有其他 tool_calls,它们对应的 ToolMessage 也必须在保留区
134
+ for (const call of prevMsg.tool_calls) {
135
+ if (call.id)
136
+ toolCallIdsInKeepRegion.add(call.id);
137
+ }
138
+ continue;
139
+ }
140
+ }
141
+ // 【3】如果前一条不是 ToolMessage,并且,前一条不是 AIMessage 或者其 tool_calls 为空/匹配不上,就直接跳出循环
142
+ break;
143
+ }
144
+ // 返回最终的压缩边界索引 《=== 压缩边界的 结束索引 前移 《=== 向前嗅探匹配的 ToolMessage 和 AIMessage 配对完整
145
+ return index;
146
+ }
@@ -0,0 +1,25 @@
1
+ export declare const DB_PATH: string;
2
+ export interface SessionRow {
3
+ thread_id: string;
4
+ last_question: string;
5
+ last_ts: string;
6
+ }
7
+ export interface MemorySearchResult {
8
+ id: number;
9
+ type: string;
10
+ content: string;
11
+ keywords: string | null;
12
+ importance: number;
13
+ session_id: string | null;
14
+ created_at: string;
15
+ updated_at: string;
16
+ final_score: number;
17
+ }
18
+ export declare function searchMemories(query: string[], limit?: number): MemorySearchResult[];
19
+ export declare function threadIdExists(threadId: string): boolean;
20
+ export declare function initDb(): void;
21
+ /**
22
+ * 列出最近 10 个会话(线程)
23
+ * @returns 会话列表
24
+ */
25
+ export declare function listRecentSessions(): SessionRow[];