@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.
package/LICENSE ADDED
@@ -0,0 +1,55 @@
1
+ Apache License
2
+
3
+ Version 2.0, January 2004
4
+
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
16
+
17
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
18
+
19
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
20
+
21
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
22
+
23
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
24
+
25
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
26
+
27
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
28
+
29
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
30
+
31
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
32
+
33
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
34
+
35
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
36
+
37
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
38
+
39
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
40
+
41
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
42
+
43
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
46
+
47
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
48
+
49
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
50
+
51
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
52
+
53
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
54
+
55
+ END OF TERMS AND CONDITIONS
package/README.md CHANGED
@@ -1,3 +1,104 @@
1
- # Temporary Holding Version
1
+ # @aki_riko/hymem-dsh
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Hy-Memory 的 DSH(DeepSeek Harness)适配层** —— 一个插件同时提供:
4
+
5
+ 1. **记忆工具**(7 个)— Agent 主动检索/写入 Hy-Memory
6
+ 2. **SOUL 注入** — 人格层进 system prompt,静止不抖动
7
+ 3. **常驻记忆注入** — compaction 后一次注入(项目画像 / 行为模式 / 关键事实)
8
+ 4. **自动捕获**(可选)— 会话消息写入 Hy-Memory
9
+
10
+ 不依赖 memoryd / dream,**Hy-Memory 是唯一记忆后端**。
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ npm install @aki_riko/hymem-dsh
16
+ ```
17
+
18
+ ## 前置:启动 Hy-Memory
19
+
20
+ 本插件只做适配,记忆服务需自行运行(默认端口 19527):
21
+
22
+ ```bash
23
+ python -m hy_memory.server # 或你自己的 wrapper
24
+ ```
25
+
26
+ 健康检查:`curl http://127.0.0.1:19527/api/v1/health`
27
+
28
+ ## 挂进 DSH profile
29
+
30
+ 在 profile 的 `package.json` 里:
31
+
32
+ ```json
33
+ {
34
+ "dependencies": { "@aki_riko/hymem-dsh": "^0.1.0" },
35
+ "dsh": { "profile": { "bundles": ["@aki_riko/hymem-dsh"] } }
36
+ }
37
+ ```
38
+
39
+ 在 profile 的 `cordis.patch.yml` 里加实例:
40
+
41
+ ```yaml
42
+ - id: riko-hymem
43
+ use: "@aki_riko/hymem-dsh"
44
+ config:
45
+ hyMemoryUrl: http://127.0.0.1:19527
46
+ userId: aki # 人类用户 ID
47
+ agentId: riko # AI 自己的 ID
48
+ toolsEnabled: true
49
+ soulEnabled: true
50
+ residentEnabled: true
51
+ workspaceAllow:
52
+ - C:\\Riko的空间
53
+ ```
54
+
55
+ ## 配置项
56
+
57
+ | 字段 | 默认 | 说明 |
58
+ |---|---|---|
59
+ | `hyMemoryUrl` | `http://127.0.0.1:19527` | Hy-Memory 服务地址 |
60
+ | `userId` | `aki` | **人类用户** ID(填反会污染记忆) |
61
+ | `agentId` | `riko` | **AI** 的 ID |
62
+ | `toolsEnabled` | `true` | 注册 7 个记忆工具 |
63
+ | `soulEnabled` | `true` | 注入 SOUL |
64
+ | `soulFile` | 包内 `soul.md` | SOUL 文件路径(可用 `RIKO_SOUL_FILE` 环境变量) |
65
+ | `targetAgent` | 空 | 只对指定 agent 注入 SOUL |
66
+ | `residentEnabled` | `true` | compaction 后注入常驻记忆 |
67
+ | `residentBudgetChars` | `6000` | 常驻记忆字符预算 |
68
+ | `captureEnabled` | `false` | 自动捕获会话消息写入 |
69
+ | `workspaceAllow` | `[]` | 工作区白名单,空=不限 |
70
+ | `timeoutMs` | `15000` | HTTP 超时 |
71
+
72
+ ## 工具
73
+
74
+ | 工具 | 作用 |
75
+ |---|---|
76
+ | `memory_search` | 语义/词法/图混合检索 |
77
+ | `memory_get` | 按 ID 取记忆 |
78
+ | `memory_remember` | 写入(`text` 单句快写 / `messages` 整段精写) |
79
+ | `memory_correct` | 纠正已有记忆 |
80
+ | `memory_forget` | 删除记忆 |
81
+ | `memory_list` | 列表 |
82
+ | `memory_profile` | 读 L0 用户画像 |
83
+
84
+ ## 设计铁律
85
+
86
+ - **SOUL 静止**:会话开始读一次进 system prompt,之后不重拉;只有 `compaction/end` 才重读(否则 prompt cache 前缀抖动)
87
+ - **不动态注入检索结果**:检索只由 Agent 主动调工具,不做每步 Prefetch
88
+ - **子代理不注入**:subagent/fork 会话不接收 SOUL 与常驻记忆,防人格污染
89
+ - **失败回退旧缓存**:SOUL 读失败保留上一次内容,绝不跳过注入
90
+
91
+ ## 常驻记忆渲染
92
+
93
+ `compaction/end` 后拉一次 Hy-Memory,按预算合并三段:
94
+
95
+ ```
96
+ ## 爹地的稳定信息 ← L0 user_profile
97
+ ## 行为模式(跨会话归纳)← L6 schema(图库)
98
+ ## 关键事实 ← L2 事实
99
+ ## 之前留下的打算 ← L7 intention
100
+ ```
101
+
102
+ ## License
103
+
104
+ Apache-2.0
@@ -0,0 +1,4 @@
1
+ # hymem-dsh bundle patch —— 故意为空。
2
+ # 实例配置放在 profile 的 cordis.patch.yml 里(entry id: riko-hymem)。
3
+ # 文件必须存在且合法,否则 DSH 会把本包标成「没有声明组合包」异常。
4
+ []
@@ -0,0 +1,22 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { HyClient } from "./client.js";
3
+ import type { Logger, ResolvedConfig } from "./types.js";
4
+ /** 子会话隔离判定:subagent 与 fork 会话不应作为直接用户记忆捕获。 */
5
+ export declare function isSubagentSessionHeader(header: {
6
+ origin?: unknown;
7
+ parentSession?: unknown;
8
+ } | undefined): boolean;
9
+ /** 仅接受顶级宿主会话,排除子会话与分支会话。 */
10
+ export declare function isCapturableSessionHeader(header: {
11
+ origin?: unknown;
12
+ parentSession?: unknown;
13
+ } | undefined): boolean;
14
+ /** 判定是否为 QQ 桥接注入的唤醒 prompt,防垃圾内容入库。 */
15
+ export declare function isBridgeInjected(text: string): boolean;
16
+ /** 提取 user/message 内容中的正文文本。 */
17
+ export declare function extractText(content: unknown): string;
18
+ /**
19
+ * 设置消息捕获机制:监听 session/event 中的用户消息并异步转存至 Hy-Memory。
20
+ * 包含去重机制(同一 event 只送一次)和工作区白名单校验。
21
+ */
22
+ export declare function setupCapture(ctx: Context, client: HyClient, cfg: ResolvedConfig, logger?: Logger): () => void;
@@ -0,0 +1,98 @@
1
+ import { isWorkspaceAllowed } from "./config.js";
2
+ /** 子会话隔离判定:subagent 与 fork 会话不应作为直接用户记忆捕获。 */
3
+ export function isSubagentSessionHeader(header) {
4
+ return header?.origin === "subagent" || header?.parentSession !== undefined;
5
+ }
6
+ /** 仅接受顶级宿主会话,排除子会话与分支会话。 */
7
+ export function isCapturableSessionHeader(header) {
8
+ return header !== undefined && !isSubagentSessionHeader(header);
9
+ }
10
+ /** 判定是否为 QQ 桥接注入的唤醒 prompt,防垃圾内容入库。 */
11
+ export function isBridgeInjected(text) {
12
+ if (!text.includes("【当前角色】"))
13
+ return false;
14
+ return (text.includes("【会话令牌】") ||
15
+ text.includes("【本次消息快照】") ||
16
+ text.includes("qq_get_prompt") ||
17
+ text.includes("【当前唤醒】"));
18
+ }
19
+ /** 提取 user/message 内容中的正文文本。 */
20
+ export function extractText(content) {
21
+ if (content === undefined || content === null)
22
+ return "";
23
+ if (typeof content === "string")
24
+ return content.trim();
25
+ if (Array.isArray(content)) {
26
+ return content
27
+ .filter((b) => typeof b === "object" && b !== null)
28
+ .filter((b) => b.type === "text" && typeof b.text === "string")
29
+ .map((b) => b.text)
30
+ .join("\n")
31
+ .trim();
32
+ }
33
+ return "";
34
+ }
35
+ /**
36
+ * 设置消息捕获机制:监听 session/event 中的用户消息并异步转存至 Hy-Memory。
37
+ * 包含去重机制(同一 event 只送一次)和工作区白名单校验。
38
+ */
39
+ export function setupCapture(ctx, client, cfg, logger) {
40
+ const seenKeys = new Set();
41
+ const keyQueue = [];
42
+ const MAX_SEEN = 2000;
43
+ function markSeen(key) {
44
+ if (seenKeys.has(key))
45
+ return false;
46
+ seenKeys.add(key);
47
+ keyQueue.push(key);
48
+ if (keyQueue.length > MAX_SEEN) {
49
+ const oldest = keyQueue.shift();
50
+ if (oldest)
51
+ seenKeys.delete(oldest);
52
+ }
53
+ return true;
54
+ }
55
+ const handler = (session, event) => {
56
+ try {
57
+ if (!isCapturableSessionHeader(session?.header))
58
+ return;
59
+ if (!isWorkspaceAllowed(cfg.workspaceAllow, session?.header?.cwd))
60
+ return;
61
+ if (event?.type !== "user/message")
62
+ return;
63
+ const data = event.data;
64
+ if (!data || data.source?.kind !== "user")
65
+ return;
66
+ const text = extractText(data.content);
67
+ if (!text || text.length === 0)
68
+ return;
69
+ if (isBridgeInjected(text))
70
+ return;
71
+ const sessionId = session?.id ?? "unknown";
72
+ const seq = event.seq !== undefined ? String(event.seq) : "unknown";
73
+ const eventKey = `${sessionId}:${seq}:${text.slice(0, 32)}`;
74
+ if (!markSeen(eventKey))
75
+ return;
76
+ void client
77
+ .add({ text, user_id: cfg.userId })
78
+ .then((res) => {
79
+ if (!res.ok) {
80
+ logger?.warn?.(`riko-hy: capture add warning: ${res.error?.message ?? "unknown error"}`);
81
+ }
82
+ })
83
+ .catch((err) => {
84
+ logger?.warn?.(`riko-hy: capture add exception: ${err instanceof Error ? err.message : String(err)}`);
85
+ });
86
+ }
87
+ catch (e) {
88
+ logger?.warn?.(`riko-hy: capture event handler error: ${e instanceof Error ? e.message : String(e)}`);
89
+ }
90
+ };
91
+ const off = ctx.on("session/event", handler);
92
+ return () => {
93
+ if (typeof off === "function") {
94
+ off();
95
+ }
96
+ };
97
+ }
98
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,8CAA8C;AAC9C,MAAM,UAAU,uBAAuB,CACrC,MAAiE;IAEjE,OAAO,MAAM,EAAE,MAAM,KAAK,UAAU,IAAI,MAAM,EAAE,aAAa,KAAK,SAAS,CAAC;AAC9E,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,yBAAyB,CACvC,MAAiE;IAEjE,OAAO,MAAM,KAAK,SAAS,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,OAAO,CACL,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACxB,CAAC;AACJ,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACzD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,CAAC,EAA2C,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC;aAC3F,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAc,CAAC;aAC5B,IAAI,CAAC,IAAI,CAAC;aACV,IAAI,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAY,EACZ,MAAgB,EAChB,GAAmB,EACnB,MAAe;IAEf,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC;IAEtB,SAAS,QAAQ,CAAC,GAAW;QAC3B,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACpC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,MAAM;gBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,CACd,OAA8F,EAC9F,KAAkG,EAClG,EAAE;QACF,IAAI,CAAC;YACH,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,MAAM,CAAC;gBAAE,OAAO;YACxD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC;gBAAE,OAAO;YAE1E,IAAI,KAAK,EAAE,IAAI,KAAK,cAAc;gBAAE,OAAO;YAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACxB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,MAAM;gBAAE,OAAO;YAElD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACvC,IAAI,gBAAgB,CAAC,IAAI,CAAC;gBAAE,OAAO;YAEnC,MAAM,SAAS,GAAG,OAAO,EAAE,EAAE,IAAI,SAAS,CAAC;YAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpE,MAAM,QAAQ,GAAG,GAAG,SAAS,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAE5D,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,OAAO;YAEhC,KAAK,MAAM;iBACR,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;iBAClC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;gBACZ,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,EAAE,IAAI,EAAE,CAAC,iCAAiC,GAAG,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE,CAAC,CAAC;gBAC3F,CAAC;YACH,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACtB,MAAM,EAAE,IAAI,EAAE,CAAC,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxG,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,EAAE,IAAI,EAAE,CAAC,yCAAyC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxG,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,GAAG,GAAI,GAAiE,CAAC,EAAE,CAAC,eAAe,EAAE,OAAgB,CAAC,CAAC;IAErH,OAAO,GAAG,EAAE;QACV,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;YAC9B,GAAG,EAAE,CAAC;QACR,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { ApiResult, Logger } from "./types.js";
2
+ export interface HyClientOptions {
3
+ baseUrl: string;
4
+ userId: string;
5
+ /** AI 自身标识(agent_id) */
6
+ agentId?: string;
7
+ timeoutMs?: number;
8
+ logger?: Logger;
9
+ }
10
+ export declare class HyClient {
11
+ readonly baseUrl: string;
12
+ readonly userId: string;
13
+ readonly agentId: string;
14
+ readonly timeoutMs: number;
15
+ private readonly logger?;
16
+ constructor(opts: HyClientOptions);
17
+ private request;
18
+ /** POST /api/v1/search — 搜索记忆 */
19
+ search(params: {
20
+ query: string;
21
+ limit?: number;
22
+ user_ids?: string[];
23
+ agent_ids?: string[];
24
+ scene?: string;
25
+ layers?: string[];
26
+ queries?: string[];
27
+ search_mode?: string;
28
+ intention_limit?: number;
29
+ workspace_id?: string;
30
+ }, signal?: AbortSignal): Promise<ApiResult>;
31
+ /** GET /api/v1/memories/{id} — 获取单条记忆 */
32
+ get(id: string, signal?: AbortSignal): Promise<ApiResult>;
33
+ /** POST /api/v1/add — 写入记忆
34
+ * 2026-10-26:支持官方主推的 messages 数组(完整对话 + session_id + metadata),
35
+ * SDK 会自己从原文里提取原子事实;只传 text 是简化用法。 */
36
+ add(params: {
37
+ text?: string;
38
+ messages?: Array<{
39
+ role: string;
40
+ content: string;
41
+ }>;
42
+ user_id?: string;
43
+ agent_id?: string;
44
+ session_id?: string;
45
+ metadata?: Record<string, unknown>;
46
+ memory_at?: string;
47
+ }, signal?: AbortSignal): Promise<ApiResult>;
48
+ /** POST /api/v1/add_extracted — 写入已提炼好的事实(跳过 LLM 提取,1-2 秒) */
49
+ addExtracted(params: {
50
+ contents: string | string[];
51
+ user_id?: string;
52
+ agent_id?: string;
53
+ session_id?: string;
54
+ metadata?: Record<string, unknown>;
55
+ memory_at?: string;
56
+ }, signal?: AbortSignal): Promise<ApiResult>;
57
+ /** POST /api/v1/user_profile — 读取 L0 用户长期画像(姓名/职业/所在地等稳定字段) */
58
+ getUserProfile(params?: {
59
+ user_id?: string;
60
+ }, signal?: AbortSignal): Promise<ApiResult>;
61
+ /** PUT /api/v1/memories/{id} — 更新记忆 */
62
+ update(id: string, params: {
63
+ text: string;
64
+ }, signal?: AbortSignal): Promise<ApiResult>;
65
+ /** DELETE /api/v1/memories/{id} — 删除记忆 */
66
+ delete(id: string, signal?: AbortSignal): Promise<ApiResult>;
67
+ /** POST /api/v1/list — 记忆列表 */
68
+ list(params?: {
69
+ limit?: number;
70
+ user_id?: string;
71
+ agent_id?: string;
72
+ }, signal?: AbortSignal): Promise<ApiResult>;
73
+ }
package/dist/client.js ADDED
@@ -0,0 +1,196 @@
1
+ function combineSignal(timeoutMs, signal) {
2
+ const ctrl = new AbortController();
3
+ const timer = setTimeout(() => ctrl.abort(new Error(`Request timed out after ${timeoutMs}ms`)), timeoutMs);
4
+ let removeListener;
5
+ if (signal) {
6
+ if (signal.aborted) {
7
+ clearTimeout(timer);
8
+ ctrl.abort(signal.reason);
9
+ }
10
+ else {
11
+ const onAbort = () => {
12
+ clearTimeout(timer);
13
+ ctrl.abort(signal.reason);
14
+ };
15
+ signal.addEventListener("abort", onAbort, { once: true });
16
+ removeListener = () => signal.removeEventListener("abort", onAbort);
17
+ }
18
+ }
19
+ return {
20
+ signal: ctrl.signal,
21
+ cleanup: () => {
22
+ clearTimeout(timer);
23
+ removeListener?.();
24
+ },
25
+ };
26
+ }
27
+ export class HyClient {
28
+ baseUrl;
29
+ userId;
30
+ agentId;
31
+ timeoutMs;
32
+ logger;
33
+ constructor(opts) {
34
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
35
+ this.userId = opts.userId;
36
+ this.agentId = opts.agentId ?? "riko";
37
+ // 2026-10-26:Hy-Memory 的 add 含异步 LLM 提取,实测 10-13 秒;15s 会超时。
38
+ this.timeoutMs = opts.timeoutMs ?? 120000;
39
+ this.logger = opts.logger;
40
+ }
41
+ async request(method, path, body, outerSignal) {
42
+ const url = `${this.baseUrl}${path}`;
43
+ const { signal, cleanup } = combineSignal(this.timeoutMs, outerSignal);
44
+ try {
45
+ const res = await fetch(url, {
46
+ method,
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ },
50
+ body: body !== undefined ? JSON.stringify(body) : undefined,
51
+ signal,
52
+ });
53
+ const text = await res.text();
54
+ let data = null;
55
+ if (text) {
56
+ try {
57
+ data = JSON.parse(text);
58
+ }
59
+ catch {
60
+ data = text;
61
+ }
62
+ }
63
+ if (res.ok) {
64
+ return { ok: true, data: data };
65
+ }
66
+ const errorObj = typeof data === "object" && data !== null
67
+ ? data
68
+ : null;
69
+ const msg = errorObj?.error_message ||
70
+ errorObj?.message ||
71
+ errorObj?.error ||
72
+ (typeof data === "string" ? data : `HTTP ${res.status}`);
73
+ const code = res.status === 404
74
+ ? "NOT_FOUND"
75
+ : res.status === 400
76
+ ? "INVALID_FIELD"
77
+ : res.status === 409
78
+ ? "STATE_CONFLICT"
79
+ : `HTTP_${res.status}`;
80
+ return { ok: false, error: { code, message: String(msg) } };
81
+ }
82
+ catch (err) {
83
+ const isAbort = outerSignal?.aborted;
84
+ const message = err instanceof Error ? err.message : String(err);
85
+ return {
86
+ ok: false,
87
+ error: {
88
+ code: isAbort ? "ABORTED" : "NETWORK_ERROR",
89
+ message,
90
+ },
91
+ };
92
+ }
93
+ finally {
94
+ cleanup();
95
+ }
96
+ }
97
+ /** POST /api/v1/search — 搜索记忆 */
98
+ async search(params, signal) {
99
+ const userIds = params.user_ids && params.user_ids.length > 0 ? params.user_ids : [this.userId];
100
+ const agentIds = params.agent_ids && params.agent_ids.length > 0 ? params.agent_ids : [this.agentId];
101
+ const payload = {
102
+ query: params.query,
103
+ user_ids: userIds,
104
+ // 2026-10-26 审计修正:Ultra 模式的 L6 Schema / 图重排按 user::agent 隔离,
105
+ // 不传 agent_ids 会落到 default_agent,永远检索不到图里的 schema 节点。
106
+ agent_ids: agentIds,
107
+ limit: params.limit ?? 5,
108
+ };
109
+ if (params.scene)
110
+ payload.scene = params.scene;
111
+ if (params.layers && params.layers.length > 0)
112
+ payload.layers = params.layers;
113
+ if (params.queries && params.queries.length > 0)
114
+ payload.queries = params.queries;
115
+ if (params.search_mode)
116
+ payload.search_mode = params.search_mode;
117
+ if (params.intention_limit != null)
118
+ payload.intention_limit = params.intention_limit;
119
+ if (params.workspace_id)
120
+ payload.workspace_id = params.workspace_id;
121
+ return this.request("POST", "/api/v1/search", payload, signal);
122
+ }
123
+ /** GET /api/v1/memories/{id} — 获取单条记忆 */
124
+ async get(id, signal) {
125
+ return this.request("GET", `/api/v1/memories/${encodeURIComponent(id)}`, undefined, signal);
126
+ }
127
+ /** POST /api/v1/add — 写入记忆
128
+ * 2026-10-26:支持官方主推的 messages 数组(完整对话 + session_id + metadata),
129
+ * SDK 会自己从原文里提取原子事实;只传 text 是简化用法。 */
130
+ async add(params, signal) {
131
+ const payload = {
132
+ user_id: params.user_id || this.userId,
133
+ agent_id: params.agent_id || this.agentId,
134
+ };
135
+ if (params.messages && params.messages.length > 0) {
136
+ payload.messages = params.messages;
137
+ payload.input_kind = "raw_conversation";
138
+ }
139
+ else {
140
+ payload.text = params.text;
141
+ payload.data = params.text;
142
+ }
143
+ if (params.agent_id)
144
+ payload.agent_id = params.agent_id;
145
+ if (params.session_id)
146
+ payload.session_id = params.session_id;
147
+ if (params.metadata)
148
+ payload.metadata = params.metadata;
149
+ if (params.memory_at)
150
+ payload.memory_at = params.memory_at;
151
+ return this.request("POST", "/api/v1/add", payload, signal);
152
+ }
153
+ /** POST /api/v1/add_extracted — 写入已提炼好的事实(跳过 LLM 提取,1-2 秒) */
154
+ async addExtracted(params, signal) {
155
+ const payload = {
156
+ contents: params.contents,
157
+ user_id: params.user_id || this.userId,
158
+ agent_id: params.agent_id || this.agentId,
159
+ };
160
+ if (params.session_id)
161
+ payload.session_id = params.session_id;
162
+ if (params.metadata)
163
+ payload.metadata = params.metadata;
164
+ if (params.memory_at)
165
+ payload.memory_at = params.memory_at;
166
+ return this.request("POST", "/api/v1/add_extracted", payload, signal);
167
+ }
168
+ /** POST /api/v1/user_profile — 读取 L0 用户长期画像(姓名/职业/所在地等稳定字段) */
169
+ async getUserProfile(params = {}, signal) {
170
+ return this.request("POST", "/api/v1/user_profile", { user_id: params.user_id || this.userId }, signal);
171
+ }
172
+ /** PUT /api/v1/memories/{id} — 更新记忆 */
173
+ async update(id, params, signal) {
174
+ return this.request("PUT", `/api/v1/memories/${encodeURIComponent(id)}`, {
175
+ content: params.text,
176
+ text: params.text,
177
+ }, signal);
178
+ }
179
+ /** DELETE /api/v1/memories/{id} — 删除记忆 */
180
+ async delete(id, signal) {
181
+ return this.request("DELETE", `/api/v1/memories/${encodeURIComponent(id)}`, undefined, signal);
182
+ }
183
+ /** POST /api/v1/list — 记忆列表 */
184
+ async list(params, signal) {
185
+ const payload = {
186
+ user_id: params?.user_id || this.userId,
187
+ limit: params?.limit ?? 20,
188
+ };
189
+ // L6 schema 存在图库里,只有带 agent_id 才会返回 graph.nodes
190
+ const agentId = params?.agent_id ?? this.agentId;
191
+ if (agentId)
192
+ payload.agent_id = agentId;
193
+ return this.request("POST", "/api/v1/list", payload, signal);
194
+ }
195
+ }
196
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAWA,SAAS,aAAa,CAAC,SAAiB,EAAE,MAAoB;IAC5D,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,2BAA2B,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC3G,IAAI,cAAwC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC,CAAC;YACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,cAAc,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,GAAG,EAAE;YACZ,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,cAAc,EAAE,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,QAAQ;IACV,OAAO,CAAS;IAChB,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,SAAS,CAAS;IACV,MAAM,CAAU;IAEjC,YAAY,IAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;QACtC,4DAA4D;QAC5D,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,IAAY,EACZ,IAAc,EACd,WAAyB;QAEzB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC3D,MAAM;aACP,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,IAAI,GAAY,IAAI,CAAC;YACzB,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC;oBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,GAAG,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAED,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAS,EAAE,CAAC;YACvC,CAAC;YAED,MAAM,QAAQ,GACZ,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;gBACvC,CAAC,CAAE,IAAqE;gBACxE,CAAC,CAAC,IAAI,CAAC;YACX,MAAM,GAAG,GACP,QAAQ,EAAE,aAAa;gBACvB,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,KAAK;gBACf,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3D,MAAM,IAAI,GACR,GAAG,CAAC,MAAM,KAAK,GAAG;gBAChB,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG;oBAClB,CAAC,CAAC,eAAe;oBACjB,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG;wBAClB,CAAC,CAAC,gBAAgB;wBAClB,CAAC,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;YAE/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,WAAW,EAAE,OAAO,CAAC;YACrC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe;oBAC3C,OAAO;iBACR;aACF,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,KAAK,CAAC,MAAM,CACV,MAWC,EACD,MAAoB;QAEpB,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChG,MAAM,QAAQ,GACZ,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtF,MAAM,OAAO,GAA4B;YACvC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,OAAO;YACjB,6DAA6D;YAC7D,sDAAsD;YACtD,SAAS,EAAE,QAAQ;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;SACzB,CAAC;QACF,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC/C,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9E,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAClF,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACjE,IAAI,MAAM,CAAC,eAAe,IAAI,IAAI;YAAE,OAAO,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;QACrF,IAAI,MAAM,CAAC,YAAY;YAAE,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACpE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACjE,CAAC;IAED,yCAAyC;IACzC,KAAK,CAAC,GAAG,CAAC,EAAU,EAAE,MAAoB;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC9F,CAAC;IAED;;2CAEuC;IACvC,KAAK,CAAC,GAAG,CACP,MAQC,EACD,MAAoB;QAEpB,MAAM,OAAO,GAA4B;YACvC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM;YACtC,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;SAC1C,CAAC;QACF,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACnC,OAAO,CAAC,UAAU,GAAG,kBAAkB,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YAC3B,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACxD,IAAI,MAAM,CAAC,UAAU;YAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAC9D,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,YAAY,CAChB,MAOC,EACD,MAAoB;QAEpB,MAAM,OAAO,GAA4B;YACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM;YACtC,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;SAC1C,CAAC;QACF,IAAI,MAAM,CAAC,UAAU;YAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAC9D,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,cAAc,CAClB,SAA+B,EAAE,EACjC,MAAoB;QAEpB,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,EACN,sBAAsB,EACtB,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,EAC1C,MAAM,CACP,CAAC;IACJ,CAAC;IAED,uCAAuC;IACvC,KAAK,CAAC,MAAM,CACV,EAAU,EACV,MAAwB,EACxB,MAAoB;QAEpB,OAAO,IAAI,CAAC,OAAO,CACjB,KAAK,EACL,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAC5C;YACE,OAAO,EAAE,MAAM,CAAC,IAAI;YACpB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,EACD,MAAM,CACP,CAAC;IACJ,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,MAAoB;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IACjG,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,IAAI,CACR,MAAgE,EAChE,MAAoB;QAEpB,MAAM,OAAO,GAA4B;YACvC,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI,CAAC,MAAM;YACvC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE;SAC3B,CAAC;QACF,gDAAgD;QAChD,MAAM,OAAO,GAAG,MAAM,EAAE,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC;QACjD,IAAI,OAAO;YAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { ResolvedConfig } from "./types.js";
2
+ /** 工作区路径规范化:统一分隔符、去尾斜杠、小写(Windows 不区分大小写)。 */
3
+ export declare function normalizeWorkspacePath(p: string): string;
4
+ /** 会话 cwd 是否落在工作区白名单内;名单为空表示不限制。 */
5
+ export declare function isWorkspaceAllowed(workspaceAllow: string[], cwd: string | undefined): boolean;
6
+ /** 顶层会话判定:子代理(subagent/fork)不注入 SOUL / MEMORY,避免人格污染。 */
7
+ export declare function isTopLevelSession(header: unknown): boolean;
8
+ /** 规范化解析传入的配置对象。 */
9
+ export declare function resolveConfig(raw?: Record<string, unknown>): ResolvedConfig;
package/dist/config.js ADDED
@@ -0,0 +1,61 @@
1
+ /** 工作区路径规范化:统一分隔符、去尾斜杠、小写(Windows 不区分大小写)。 */
2
+ export function normalizeWorkspacePath(p) {
3
+ return p.replace(/[\\/]+/g, "\\").replace(/\\+$/, "").toLowerCase();
4
+ }
5
+ /** 会话 cwd 是否落在工作区白名单内;名单为空表示不限制。 */
6
+ export function isWorkspaceAllowed(workspaceAllow, cwd) {
7
+ if (workspaceAllow.length === 0)
8
+ return true;
9
+ if (typeof cwd !== "string" || cwd.length === 0)
10
+ return false;
11
+ const current = normalizeWorkspacePath(cwd);
12
+ return workspaceAllow.some((allowed) => {
13
+ const root = normalizeWorkspacePath(allowed);
14
+ return current === root || current.startsWith(root + "\\");
15
+ });
16
+ }
17
+ /** 顶层会话判定:子代理(subagent/fork)不注入 SOUL / MEMORY,避免人格污染。 */
18
+ export function isTopLevelSession(header) {
19
+ if (!header || typeof header !== "object")
20
+ return true;
21
+ const h = header;
22
+ if (h.parentSession)
23
+ return false;
24
+ if (typeof h.origin === "string" && h.origin.toLowerCase().includes("subagent"))
25
+ return false;
26
+ return true;
27
+ }
28
+ /** 规范化解析传入的配置对象。 */
29
+ export function resolveConfig(raw = {}) {
30
+ const url = typeof raw.hyMemoryUrl === "string" && raw.hyMemoryUrl.trim()
31
+ ? raw.hyMemoryUrl.trim()
32
+ : "http://127.0.0.1:19527";
33
+ // 身份铁律:user_id 代表人类用户(爹地),agent_id 代表 AI(Riko)。
34
+ const userId = typeof raw.userId === "string" && raw.userId.trim() ? raw.userId.trim() : "aki";
35
+ const agentId = typeof raw.agentId === "string" && raw.agentId.trim() ? raw.agentId.trim() : "riko";
36
+ const workspaceAllow = Array.isArray(raw.workspaceAllow)
37
+ ? raw.workspaceAllow.filter((w) => typeof w === "string" && w.trim().length > 0)
38
+ : [];
39
+ const timeoutMs = typeof raw.timeoutMs === "number" && raw.timeoutMs > 0 ? raw.timeoutMs : 15000;
40
+ const soulFile = typeof raw.soulFile === "string" && raw.soulFile.trim()
41
+ ? raw.soulFile.trim()
42
+ : (process.env.RIKO_SOUL_FILE ?? "");
43
+ const residentBudgetChars = typeof raw.residentBudgetChars === "number" && raw.residentBudgetChars > 0
44
+ ? raw.residentBudgetChars
45
+ : 6000;
46
+ return {
47
+ hyMemoryUrl: url,
48
+ userId,
49
+ agentId,
50
+ toolsEnabled: raw.toolsEnabled !== false,
51
+ captureEnabled: raw.captureEnabled === true,
52
+ workspaceAllow,
53
+ timeoutMs,
54
+ soulEnabled: raw.soulEnabled !== false,
55
+ soulFile,
56
+ targetAgent: typeof raw.targetAgent === "string" ? raw.targetAgent.trim() : "",
57
+ residentEnabled: raw.residentEnabled !== false,
58
+ residentBudgetChars,
59
+ };
60
+ }
61
+ //# sourceMappingURL=config.js.map