@aki_riko/hymem-dsh 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @aki_riko/hymem-dsh —— Hy-Memory 的 DSH 适配层(单插件)。
3
+ *
4
+ * 四块职责:
5
+ * 1. tools 7 个记忆工具 → Hy-Memory HTTP
6
+ * 2. soul 文件式 SOUL 注入 system prompt(静止:只有 compaction/end 才重读)
7
+ * 3. resident compaction/end 后一次性注入常驻记忆(数据来自 Hy-Memory)
8
+ * 4. curator 定时整理(后续版本;本版留接口)
9
+ *
10
+ * 设计铁律(2026-10-26):
11
+ * - 不依赖 memoryd / dream,Hy-Memory 是唯一后端
12
+ * - SOUL 静止、不动态注入,检索只靠主动 tool call
13
+ * - 子代理会话不注入 SOUL/MEMORY(防人格污染)
14
+ */
15
+ import type { Context } from "@deepseek-ai/cordis";
16
+ export declare const name = "riko-hymem";
17
+ export declare const inject: string[];
18
+ export type Config = Record<string, unknown>;
19
+ export declare function apply(ctx: Context, config?: Config): void;
package/dist/index.js ADDED
@@ -0,0 +1,158 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { resolve } from "node:path";
4
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
5
+ import { resolveConfig, isWorkspaceAllowed, isTopLevelSession } from "./config.js";
6
+ import { HyClient } from "./client.js";
7
+ import { buildHyTools } from "./tools.js";
8
+ import { setupCapture } from "./capture.js";
9
+ import { renderResident } from "./resident.js";
10
+ export const name = "riko-hymem";
11
+ export const inject = ["tools"];
12
+ const SOUL_SECTION_NAME = "riko-soul";
13
+ const SOUL_SECTION_ORDER = 100;
14
+ function makeLogger(ctx) {
15
+ const anyCtx = ctx;
16
+ const l = anyCtx.logger;
17
+ return {
18
+ warn: (m) => l?.warn(m),
19
+ error: (m) => l?.error(m),
20
+ info: (m) => l?.info(m),
21
+ };
22
+ }
23
+ /** 注入进 prompt 的文本要转义,避免被当成 XML 指令执行。 */
24
+ function escapeXmlData(text) {
25
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
26
+ }
27
+ /** 在每个顶层 Agent 的 ctx 上挂 hook(DSH 的 agent/created 模式)。 */
28
+ function registerPerAgent(ctx, event, hook, workspaceAllow) {
29
+ const host = ctx;
30
+ host.on("agent/created", (payload) => {
31
+ const header = payload?.agent?.session?.header;
32
+ if (!isTopLevelSession(header))
33
+ return;
34
+ if (!isWorkspaceAllowed(workspaceAllow, header?.cwd))
35
+ return;
36
+ const agentCtx = payload.agent.ctx;
37
+ agentCtx.on(event, hook);
38
+ });
39
+ }
40
+ export function apply(ctx, config = {}) {
41
+ const cfg = resolveConfig(config);
42
+ const logger = makeLogger(ctx);
43
+ const client = new HyClient({
44
+ baseUrl: cfg.hyMemoryUrl,
45
+ userId: cfg.userId,
46
+ timeoutMs: cfg.timeoutMs,
47
+ logger,
48
+ });
49
+ // ---------- 1. 记忆工具 ----------
50
+ if (cfg.toolsEnabled) {
51
+ const tools = buildHyTools(client);
52
+ const registry = ctx.tools;
53
+ for (const tool of tools) {
54
+ registry.register(tool);
55
+ }
56
+ logger.info?.(`riko-hymem: 已注册记忆工具 (user_id=${cfg.userId}, url=${cfg.hyMemoryUrl})`);
57
+ }
58
+ // ---------- 2. SOUL 注入 ----------
59
+ if (cfg.soulEnabled) {
60
+ const soulPath = cfg.soulFile
61
+ ? resolve(cfg.soulFile)
62
+ : fileURLToPath(new URL("../soul.md", import.meta.url));
63
+ let soulText = "";
64
+ const loadSoul = (reason) => {
65
+ try {
66
+ soulText = readFileSync(soulPath, "utf8");
67
+ logger.info?.(`riko-hymem: SOUL 已读取 (${reason}, ${soulText.length} 字符)`);
68
+ }
69
+ catch (error) {
70
+ // 读失败保留旧文本:prompt 前缀绝不能抖动(否则 prompt cache 全废)
71
+ logger.warn(`riko-hymem: SOUL 读取失败,保留旧缓存 ${error instanceof Error ? error.message : String(error)}`);
72
+ }
73
+ };
74
+ loadSoul("startup");
75
+ // SOUL 静止:只有 compact 结束才重读(爹地要求)
76
+ ctx.on("session/event", (session, event) => {
77
+ if (event?.type !== "compaction/end")
78
+ return;
79
+ if (!isTopLevelSession(session?.header))
80
+ return;
81
+ loadSoul("compaction/end");
82
+ });
83
+ const soulHook = async (assembly, context, next) => {
84
+ const result = (await next());
85
+ const ctxInfo = context;
86
+ const header = ctxInfo.agent?.session?.header;
87
+ if (!isTopLevelSession(header))
88
+ return result;
89
+ if (!isWorkspaceAllowed(cfg.workspaceAllow, header?.cwd))
90
+ return result;
91
+ if (cfg.targetAgent && String(ctxInfo.agent?.id ?? "") !== cfg.targetAgent)
92
+ return result;
93
+ if (result.sections.some((s) => s.name === SOUL_SECTION_NAME))
94
+ return result;
95
+ if (!soulText.trim())
96
+ return result;
97
+ result.sections.push({
98
+ name: SOUL_SECTION_NAME,
99
+ order: SOUL_SECTION_ORDER,
100
+ text: `<agent_memory:soul version="0">\n${escapeXmlData(soulText)}\n</agent_memory:soul>`,
101
+ interpolate: false,
102
+ });
103
+ return result;
104
+ };
105
+ registerPerAgent(ctx, "system-prompt/assemble", soulHook, cfg.workspaceAllow);
106
+ }
107
+ // ---------- 3. 常驻记忆(compact 后一次)----------
108
+ if (cfg.residentEnabled) {
109
+ let pending = false;
110
+ ctx.on("session/event", (session, event) => {
111
+ if (event?.type !== "compaction/end")
112
+ return;
113
+ if (!isTopLevelSession(session?.header))
114
+ return;
115
+ pending = true;
116
+ });
117
+ const residentHook = async (payload, next) => {
118
+ const decision = (await next());
119
+ if (!pending)
120
+ return decision;
121
+ pending = false;
122
+ const p = payload;
123
+ if (!isTopLevelSession(p.agent?.session?.header))
124
+ return decision;
125
+ if (decision?.kind !== "enter" || p.signal?.aborted)
126
+ return decision;
127
+ try {
128
+ const text = await renderResident(client, cfg.residentBudgetChars, logger, p.signal);
129
+ if (!text)
130
+ return decision;
131
+ const message = createUserMessage({
132
+ content: [
133
+ {
134
+ type: "text",
135
+ text: `<agent_memory:resident>\n${escapeXmlData(text)}\n</agent_memory:resident>`,
136
+ },
137
+ ],
138
+ // dsh-llm 的 source 类型是封闭枚举:注入的记忆按"系统提供的召回"标记。
139
+ source: { kind: "system-prompt", form: "recall" },
140
+ });
141
+ return { ...decision, messages: [message, ...(decision.messages ?? [])] };
142
+ }
143
+ catch (error) {
144
+ logger.warn(`riko-hymem: 常驻记忆注入失败 ${error instanceof Error ? error.message : String(error)}`);
145
+ return decision;
146
+ }
147
+ };
148
+ registerPerAgent(ctx, "agent/pre-step", residentHook, cfg.workspaceAllow);
149
+ }
150
+ // ---------- 4. 自动捕获(默认关,写入交给 hy-sync)----------
151
+ if (cfg.captureEnabled) {
152
+ const cleanupCapture = setupCapture(ctx, client, cfg, logger);
153
+ ctx.effect(() => cleanupCapture);
154
+ logger.info?.(`riko-hymem: 已启用消息捕获 (workspaceAllow=${JSON.stringify(cfg.workspaceAllow)})`);
155
+ }
156
+ logger.info?.(`riko-hymem: 插件加载完成 tools=${cfg.toolsEnabled} soul=${cfg.soulEnabled} resident=${cfg.residentEnabled} url=${cfg.hyMemoryUrl}`);
157
+ }
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resident(常驻记忆)渲染器。
3
+ *
4
+ * 设计(2026-10-26 定稿):
5
+ * - compact 后注入一次,平时零占用
6
+ * - 数据全部来自 Hy-Memory,不再依赖 memoryd / dream
7
+ * - 三源合并:L0 画像 + L6 行为模式(图库)+ L2 关键事实 / L7 未完成打算
8
+ * - 按字符预算截断,超预算先砍细节
9
+ */
10
+ import type { HyClient } from "./client.js";
11
+ import type { Logger } from "./types.js";
12
+ export declare function renderResident(client: HyClient, budgetChars: number, logger: Logger, signal?: AbortSignal): Promise<string | null>;
@@ -0,0 +1,59 @@
1
+ function textOf(value) {
2
+ return typeof value === "string" ? value.trim() : "";
3
+ }
4
+ export async function renderResident(client, budgetChars, logger, signal) {
5
+ const blocks = [];
6
+ // 1) L0:爹地的稳定信息
7
+ try {
8
+ const r = await client.getUserProfile({}, signal);
9
+ const data = (r.data ?? {});
10
+ const profile = data.profile;
11
+ if (profile && typeof profile === "object") {
12
+ const lines = Object.entries(profile)
13
+ .map(([key, value]) => [key, textOf(value)])
14
+ .filter(([, value]) => value.length > 0)
15
+ .map(([key, value]) => `- ${key}:${value}`);
16
+ if (lines.length > 0)
17
+ blocks.push(`## 爹地的稳定信息\n${lines.join("\n")}`);
18
+ }
19
+ }
20
+ catch (error) {
21
+ logger.warn(`resident: user_profile 读取失败 ${error instanceof Error ? error.message : String(error)}`);
22
+ }
23
+ // 2) L6 / L2 / L7:一次 list 全拿
24
+ try {
25
+ const r = await client.list({ limit: 100 }, signal);
26
+ const data = (r.data ?? {});
27
+ const nodes = Array.isArray(data.graph?.nodes) ? data.graph.nodes : [];
28
+ const memories = Array.isArray(data.vdb?.memories) ? data.vdb.memories : [];
29
+ const patterns = nodes.map((n) => textOf(n?.content)).filter((t) => t.length > 0);
30
+ if (patterns.length > 0) {
31
+ blocks.push("## 行为模式(跨会话归纳)\n" + patterns.map((t) => `- ${t}`).join("\n"));
32
+ }
33
+ const facts = memories
34
+ .filter((m) => m?.layer === "l2_fact")
35
+ .map((m) => textOf(m?.content))
36
+ .filter((t) => t.length > 0);
37
+ if (facts.length > 0) {
38
+ blocks.push("## 关键事实\n" + facts.map((t) => `- ${t}`).join("\n"));
39
+ }
40
+ const plans = memories
41
+ .filter((m) => m?.layer === "l7_intention")
42
+ .map((m) => textOf(m?.content))
43
+ .filter((t) => t.length > 0);
44
+ if (plans.length > 0) {
45
+ blocks.push("## 之前留下的打算\n" + plans.map((t) => `- ${t}`).join("\n"));
46
+ }
47
+ }
48
+ catch (error) {
49
+ logger.warn(`resident: list 读取失败 ${error instanceof Error ? error.message : String(error)}`);
50
+ }
51
+ if (blocks.length === 0)
52
+ return null;
53
+ let text = "# 长期记忆(Hy-Memory 自动渲染)\n\n" + blocks.join("\n\n");
54
+ if (text.length > budgetChars) {
55
+ text = text.slice(0, budgetChars).trimEnd() + "\n- …(已达预算上限,其余略)";
56
+ }
57
+ return text;
58
+ }
59
+ //# sourceMappingURL=resident.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resident.js","sourceRoot":"","sources":["../src/resident.ts"],"names":[],"mappings":"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"}
package/dist/soul.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ export declare const TWIN_SOUL_SECTION_NAME = "twin-soul";
3
+ export declare const TWIN_SOUL_SECTION_ORDER = 100;
4
+ /** Default template lives beside the package's dist/ directory and is user-editable. */
5
+ export declare function defaultSoulFilePath(): string;
6
+ export interface FileSoulWatcher {
7
+ getText(): string;
8
+ close(): void;
9
+ }
10
+ /** Create a hot-reloading watcher for a local soul file. */
11
+ export declare function createFileSoulWatcher(soulFile?: string, onChange?: () => void, logger?: {
12
+ warn(msg: string): void;
13
+ }): FileSoulWatcher;
14
+ /**
15
+ * @deprecated B1 编排收口:默认只走内核 Soul(order 10300);
16
+ * 全局 twin-soul(order 100)平行注入已废弃。若需文件覆盖,使用 config.fileSoulEnabled opt-in。
17
+ */
18
+ export declare function registerTwinSoul(ctx: Context, soulFile?: string): void;
package/dist/soul.js ADDED
@@ -0,0 +1,151 @@
1
+ /**
2
+ * File-based twin personality.
3
+ *
4
+ * B1 编排收口:内核 Soul(order 10300,按 agent 隔离)是单一真相源。
5
+ * 本模块提供文件监听与读取辅助(createFileSoulWatcher);旧版全局平行注入
6
+ * registerTwinSoul 已废弃,默认不再调用。
7
+ */
8
+ import { readFile, readFileSync, watch } from "node:fs";
9
+ import { basename, dirname, resolve } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ export const TWIN_SOUL_SECTION_NAME = "twin-soul";
12
+ export const TWIN_SOUL_SECTION_ORDER = 100;
13
+ /** Default template lives beside the package's dist/ directory and is user-editable. */
14
+ export function defaultSoulFilePath() {
15
+ const configured = process.env.RIKO_DSH_SOUL_FILE;
16
+ return configured
17
+ ? resolve(configured)
18
+ : fileURLToPath(new URL("../soul.md", import.meta.url));
19
+ }
20
+ function warn(ctx, message) {
21
+ const logger = ctx.logger;
22
+ logger?.warn?.(`riko-dsh: ${message}`);
23
+ }
24
+ /** Create a hot-reloading watcher for a local soul file. */
25
+ export function createFileSoulWatcher(soulFile = defaultSoulFilePath(), onChange, logger) {
26
+ const path = resolve(soulFile);
27
+ let soulText = "";
28
+ try {
29
+ soulText = readFileSync(path, "utf8");
30
+ }
31
+ catch (error) {
32
+ logger?.warn(`soul 文件无法读取: ${path} (${error instanceof Error ? error.message : String(error)})`);
33
+ }
34
+ let watcher;
35
+ let reloadTimer;
36
+ const reload = () => {
37
+ readFile(path, "utf8", (error, nextText) => {
38
+ if (error) {
39
+ if (error.code === "ENOENT") {
40
+ nextText = "";
41
+ }
42
+ else {
43
+ logger?.warn(`soul 文件重载失败: ${path} (${error.message})`);
44
+ return;
45
+ }
46
+ }
47
+ if (nextText === soulText)
48
+ return;
49
+ soulText = nextText;
50
+ try {
51
+ onChange?.();
52
+ }
53
+ catch (emitError) {
54
+ logger?.warn(`system prompt 更新通知失败: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
55
+ }
56
+ });
57
+ };
58
+ try {
59
+ watcher = watch(dirname(path), (_eventType, filename) => {
60
+ if (filename !== null && filename.toString() !== basename(path))
61
+ return;
62
+ if (reloadTimer !== undefined)
63
+ clearTimeout(reloadTimer);
64
+ reloadTimer = setTimeout(() => {
65
+ reloadTimer = undefined;
66
+ reload();
67
+ }, 40);
68
+ });
69
+ watcher.on("error", (error) => logger?.warn(`soul 文件监听失败: ${error.message}`));
70
+ }
71
+ catch (error) {
72
+ logger?.warn(`soul 文件监听无法启动: ${path} (${error instanceof Error ? error.message : String(error)})`);
73
+ }
74
+ return {
75
+ getText: () => soulText,
76
+ close: () => {
77
+ if (reloadTimer !== undefined)
78
+ clearTimeout(reloadTimer);
79
+ watcher?.close();
80
+ },
81
+ };
82
+ }
83
+ /**
84
+ * @deprecated B1 编排收口:默认只走内核 Soul(order 10300);
85
+ * 全局 twin-soul(order 100)平行注入已废弃。若需文件覆盖,使用 config.fileSoulEnabled opt-in。
86
+ */
87
+ export function registerTwinSoul(ctx, soulFile = defaultSoulFilePath()) {
88
+ const path = resolve(soulFile);
89
+ ctx.effect(() => {
90
+ let soulText = "";
91
+ try {
92
+ soulText = readFileSync(path, "utf8");
93
+ }
94
+ catch (error) {
95
+ warn(ctx, `soul 文件无法读取: ${path} (${error instanceof Error ? error.message : String(error)})`);
96
+ }
97
+ const removeSection = ctx.systemPrompt.section({
98
+ name: TWIN_SOUL_SECTION_NAME,
99
+ order: TWIN_SOUL_SECTION_ORDER,
100
+ text: () => soulText,
101
+ interpolate: false,
102
+ });
103
+ let watcher;
104
+ let reloadTimer;
105
+ const reload = () => {
106
+ readFile(path, "utf8", (error, nextText) => {
107
+ if (error) {
108
+ if (error.code === "ENOENT") {
109
+ nextText = "";
110
+ }
111
+ else {
112
+ warn(ctx, `soul 文件重载失败: ${path} (${error.message})`);
113
+ return;
114
+ }
115
+ }
116
+ if (nextText === soulText)
117
+ return;
118
+ soulText = nextText;
119
+ try {
120
+ ctx.emit("system-prompt/change");
121
+ }
122
+ catch (emitError) {
123
+ warn(ctx, `system prompt 更新通知失败: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
124
+ }
125
+ });
126
+ };
127
+ try {
128
+ watcher = watch(dirname(path), (_eventType, filename) => {
129
+ if (filename !== null && filename.toString() !== basename(path))
130
+ return;
131
+ if (reloadTimer !== undefined)
132
+ clearTimeout(reloadTimer);
133
+ reloadTimer = setTimeout(() => {
134
+ reloadTimer = undefined;
135
+ reload();
136
+ }, 40);
137
+ });
138
+ watcher.on("error", (error) => warn(ctx, `soul 文件监听失败: ${error.message}`));
139
+ }
140
+ catch (error) {
141
+ warn(ctx, `soul 文件监听无法启动: ${path} (${error instanceof Error ? error.message : String(error)})`);
142
+ }
143
+ return () => {
144
+ if (reloadTimer !== undefined)
145
+ clearTimeout(reloadTimer);
146
+ watcher?.close();
147
+ removeSection();
148
+ };
149
+ }, "riko-dsh.twin-soul");
150
+ }
151
+ //# sourceMappingURL=soul.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"soul.js","sourceRoot":"","sources":["../src/soul.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ToolDefinition } from "@deepseek-ai/dsh-tools";
2
+ import type { HyClient } from "./client.js";
3
+ export declare function buildHyTools(client: HyClient): ToolDefinition[];
package/dist/tools.js ADDED
@@ -0,0 +1,183 @@
1
+ import { defineTool } from "@deepseek-ai/dsh-tools";
2
+ const RESULT_SCHEMA = {
3
+ type: "object",
4
+ additionalProperties: false,
5
+ properties: {
6
+ ok: { type: "boolean", required: true },
7
+ data: { type: "json" },
8
+ error: {
9
+ type: "object",
10
+ additionalProperties: false,
11
+ properties: {
12
+ code: { type: "string", required: true },
13
+ message: { type: "string", required: true },
14
+ },
15
+ },
16
+ },
17
+ };
18
+ function renderResult(_args, value) {
19
+ return [{ type: "text", text: JSON.stringify(value) }];
20
+ }
21
+ function toToolResult(r) {
22
+ if (r.ok) {
23
+ return { ok: true, data: (r.data ?? null) };
24
+ }
25
+ return { ok: false, error: r.error };
26
+ }
27
+ export function buildHyTools(client) {
28
+ return [
29
+ defineTool({
30
+ name: "memory_search",
31
+ description: "搜索相关的长期记忆。根据 query 检索语义或词法相关的记忆列表。",
32
+ parameters: {
33
+ query: { type: "string", required: true, description: "描述要查找的记忆内容" },
34
+ limit: { type: "number", description: "返回数量上限,默认 5" },
35
+ user_ids: { type: "json", description: "可选的用户 ID 列表(例如 [\"riko\"],默认当前配置的用户)" },
36
+ scene: {
37
+ type: "string",
38
+ description: "检索场景:chat(闲聊)或 productivity(coding/工作)。查技术经验时用 productivity 能命中 coding 记忆链路",
39
+ },
40
+ intention_limit: { type: "number", description: "返回多少条 L7 前瞻意图/计划,默认 0" },
41
+ search_mode: { type: "string", description: "检索模式,默认 default;hybrid 强调词法+向量融合" },
42
+ queries: {
43
+ type: "json",
44
+ description: "多查询数组:同一意图换个说法多路检索再融合,例如 [\"记忆架构\",\"插件怎么拆\"]",
45
+ },
46
+ },
47
+ output: { schema: RESULT_SCHEMA, render: renderResult },
48
+ async execute(args, exec) {
49
+ if (!args.query || typeof args.query !== "string" || !args.query.trim()) {
50
+ return { ok: false, error: { code: "INVALID_FIELD", message: "query 必填且不能为空" } };
51
+ }
52
+ const userIds = Array.isArray(args.user_ids)
53
+ ? args.user_ids.filter((u) => typeof u === "string" && u.trim().length > 0)
54
+ : undefined;
55
+ return toToolResult(await client.search({
56
+ query: args.query.trim(),
57
+ limit: args.limit,
58
+ user_ids: userIds,
59
+ scene: args.scene,
60
+ intention_limit: args.intention_limit,
61
+ search_mode: args.search_mode,
62
+ queries: Array.isArray(args.queries)
63
+ ? args.queries.filter((q) => typeof q === "string")
64
+ : undefined,
65
+ }, exec?.signal));
66
+ },
67
+ }),
68
+ defineTool({
69
+ name: "memory_get",
70
+ description: "根据记忆 ID 获取单条记忆详情。ID 须来自 memory_search 或记忆工具的回执。",
71
+ parameters: {
72
+ id: { type: "string", required: true, description: "记忆 ID (UUID 格式)" },
73
+ },
74
+ output: { schema: RESULT_SCHEMA, render: renderResult },
75
+ async execute(args, exec) {
76
+ if (!args.id || typeof args.id !== "string" || !args.id.trim()) {
77
+ return { ok: false, error: { code: "INVALID_FIELD", message: "id 必填且不能为空" } };
78
+ }
79
+ return toToolResult(await client.get(args.id.trim(), exec?.signal));
80
+ },
81
+ }),
82
+ defineTool({
83
+ name: "memory_profile",
84
+ description: "读取用户的 L0 长期画像(姓名、职业、所在地、雇主等稳定字段)。想知道\"用户是谁\"或做个性化称呼时先调这个,比 search 更省 token。",
85
+ parameters: {
86
+ user_id: { type: "string", description: "可选用户 ID,默认当前配置的用户" },
87
+ },
88
+ output: { schema: RESULT_SCHEMA, render: renderResult },
89
+ async execute(args, exec) {
90
+ return toToolResult(await client.getUserProfile({ user_id: args.user_id?.trim() || undefined }, exec?.signal));
91
+ },
92
+ }),
93
+ defineTool({
94
+ name: "memory_remember",
95
+ description: "持久保存记忆到 Hy-Memory。两种用法:(1) 官方主推——传 messages 数组(完整对话),由 SDK 自己提取原子事实,带上下文更准;(2) 简化——传 text 一句话。二者选一。",
96
+ parameters: {
97
+ text: { type: "string", description: "要保存的记忆正文(与 messages 二选一)" },
98
+ messages: {
99
+ type: "json",
100
+ description: "官方主推方式:传完整对话消息数组(每项含 role 和 content),由 SDK 自己提取事实;与 text 二选一",
101
+ },
102
+ user_id: { type: "string", description: "可选用户 ID,默认当前配置" },
103
+ session_id: { type: "string", description: "可选会话 ID,传 messages 时建议带上" },
104
+ },
105
+ output: { schema: RESULT_SCHEMA, render: renderResult },
106
+ async execute(args, exec) {
107
+ const hasMessages = Array.isArray(args.messages) && args.messages.length > 0;
108
+ const hasText = typeof args.text === "string" && args.text.trim().length > 0;
109
+ if (!hasMessages && !hasText) {
110
+ return {
111
+ ok: false,
112
+ error: { code: "INVALID_FIELD", message: "text 或 messages 必须提供一个" },
113
+ };
114
+ }
115
+ // 2026-10-26 审计修正:
116
+ // 纯 text 是调用方已经提炼好的事实 → 走 add_extracted,跳过 LLM 提取(1-2 秒)
117
+ // messages 是原始对话 → 走 add,让 SDK 自己提取
118
+ if (hasText && !hasMessages) {
119
+ return toToolResult(await client.addExtracted({
120
+ contents: args.text.trim(),
121
+ user_id: args.user_id?.trim() || undefined,
122
+ session_id: args.session_id?.trim() || undefined,
123
+ }, exec?.signal));
124
+ }
125
+ return toToolResult(await client.add({
126
+ messages: args.messages,
127
+ user_id: args.user_id?.trim() || undefined,
128
+ session_id: args.session_id?.trim() || undefined,
129
+ }, exec?.signal));
130
+ },
131
+ }),
132
+ defineTool({
133
+ name: "memory_correct",
134
+ description: "纠正或更新已有的记忆内容。将指定 ID 的记忆内容替换为新的文本。",
135
+ parameters: {
136
+ id: { type: "string", required: true, description: "需要更新的记忆 ID" },
137
+ text: { type: "string", required: true, description: "纠正后的新记忆文本" },
138
+ },
139
+ output: { schema: RESULT_SCHEMA, render: renderResult },
140
+ async execute(args, exec) {
141
+ if (!args.id || typeof args.id !== "string" || !args.id.trim()) {
142
+ return { ok: false, error: { code: "INVALID_FIELD", message: "id 必填且不能为空" } };
143
+ }
144
+ if (!args.text || typeof args.text !== "string" || !args.text.trim()) {
145
+ return { ok: false, error: { code: "INVALID_FIELD", message: "text 必填且不能为空" } };
146
+ }
147
+ return toToolResult(await client.update(args.id.trim(), {
148
+ text: args.text.trim(),
149
+ }, exec?.signal));
150
+ },
151
+ }),
152
+ defineTool({
153
+ name: "memory_forget",
154
+ description: "删除或遗忘指定的记忆内容。",
155
+ parameters: {
156
+ id: { type: "string", required: true, description: "需要删除的记忆 ID" },
157
+ },
158
+ output: { schema: RESULT_SCHEMA, render: renderResult },
159
+ async execute(args, exec) {
160
+ if (!args.id || typeof args.id !== "string" || !args.id.trim()) {
161
+ return { ok: false, error: { code: "INVALID_FIELD", message: "id 必填且不能为空" } };
162
+ }
163
+ return toToolResult(await client.delete(args.id.trim(), exec?.signal));
164
+ },
165
+ }),
166
+ defineTool({
167
+ name: "memory_list",
168
+ description: "列出当前用户的记忆列表(分页查看)。",
169
+ parameters: {
170
+ limit: { type: "number", description: "返回数量上限,默认 20" },
171
+ user_id: { type: "string", description: "可选的用户 ID,默认当前配置的用户" },
172
+ },
173
+ output: { schema: RESULT_SCHEMA, render: renderResult },
174
+ async execute(args, exec) {
175
+ return toToolResult(await client.list({
176
+ limit: args.limit,
177
+ user_id: args.user_id?.trim() || undefined,
178
+ }, exec?.signal));
179
+ },
180
+ }),
181
+ ];
182
+ }
183
+ //# sourceMappingURL=tools.js.map