diff --git a/README.md b/README.md index 9b130df0..7b99a30d 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ flowchart TB subgraph G[③ Graph 图数据库] DB[(SQLite)] IDX[Indexer 向量+jieba→BFS depth=2] -->|【记忆索引】| SP - MEM[memory_recall/commit/merge/purge/edit] + MEM[stablemem_*/coldcontext_*] SOC[person_query/set_trait] subgraph SC[场面识别:声明 + 涌现] FE[① 指纹 chan/peer/tool/topic/part
权重 1.0/1.0/0.8/0.4/0.2] @@ -196,7 +196,7 @@ flowchart TB end SP[System Prompt] -->|顺序组装| LLM LLM[LLM] -->|coldcontext_query| Q2 - LLM -->|memory_recall| MEM + LLM -->|stablemem_recall| MEM ``` 详细说明见 [`assets/docs/zh/ARCHITECTURE.md`](assets/docs/zh/ARCHITECTURE.md)。 @@ -329,7 +329,7 @@ internal/ > inline/small **30.4µs**、frame/small 51.5µs、inline/large 767µs、frame/large 398µs。 > 保留原文不修改,以免伪造历史。 -**v1.1.1** — 多模态贯通**插件边界**。v1.1.0 让记忆系统支持了二进制多媒体节点,但那条链路只对内核自己开放;本版打通到插件与模型。公开 SDK 新增媒体字段与三个媒体注入接口(配套 [SDK v1.1.0](https://github.com/JianFeeeee/hologramagentsdk/tree/v1.1.0),整条 1.1.x 线共用),内核实现对应四个 RPC。桥接层此前在**静默裁字段**:插件交进来的 `Confidence`/类型/`SentenceText` 全被丢弃、`Doc` 只留三个字段、`Remove` 不解引用(媒体永久算「被引用」,GC 收不掉)。`processTextInput`/`processMediaInput` 归一成一条 `processInput`,媒体路径由此获得它一直缺的去重、`no_memory`、通道 `Cleaner`、中断语义、`EventRawInput`。修掉三处真实缺陷:**用户发的图从来没出现在 WebUI 聊天记录里**(媒体路径发布 map 而订阅方断言 string)、**`memory_commit` 的 `sentence_text` 从未暴露给模型**(而它是媒体绑定链的必经环节)、**`PluginSDK` 两处并发竞态**(`-race` 实测 11 处,插件重载瞬间偶发 nil 解引用崩溃)。 +**v1.1.1** — 多模态贯通**插件边界**。v1.1.0 让记忆系统支持了二进制多媒体节点,但那条链路只对内核自己开放;本版打通到插件与模型。公开 SDK 新增媒体字段与三个媒体注入接口(配套 [SDK v1.1.0](https://github.com/JianFeeeee/hologramagentsdk/tree/v1.1.0),整条 1.1.x 线共用),内核实现对应四个 RPC。桥接层此前在**静默裁字段**:插件交进来的 `Confidence`/类型/`SentenceText` 全被丢弃、`Doc` 只留三个字段、`Remove` 不解引用(媒体永久算「被引用」,GC 收不掉)。`processTextInput`/`processMediaInput` 归一成一条 `processInput`,媒体路径由此获得它一直缺的去重、`no_memory`、通道 `Cleaner`、中断语义、`EventRawInput`。修掉三处真实缺陷:**用户发的图从来没出现在 WebUI 聊天记录里**(媒体路径发布 map 而订阅方断言 string)、**`memory_commit` 的 `sentence_text` 从未暴露给模型**(注:该工具现名 `stablemem_commit`,本条保留 1.1.1 发布时的原名)(而它是媒体绑定链的必经环节)、**`PluginSDK` 两处并发竞态**(`-race` 实测 11 处,插件重载瞬间偶发 nil 解引用崩溃)。 **v1.1.0** — 记忆系统支持**二进制多媒体节点**。内容寻址媒体存储(CAS + SQLite 元数据 + 磁盘 blob,`Get` always 重校 digest),贯通 L0(上下文事件)/L2(文档)/L3(图谱句子)三层,引用计数式 GC(有引用者绝不删)。视觉模型生成的描述文本是持久语义记忆,blob 只是可被容量 GC 淘汰的缓存。 diff --git a/README_EN.md b/README_EN.md index 14ae40c6..334b17eb 100644 --- a/README_EN.md +++ b/README_EN.md @@ -166,7 +166,7 @@ flowchart TB subgraph G[③ Graph Database] DB[(SQLite)] IDX[Indexer vector+jieba→BFS depth=2] -->|Memory Index| SP - MEM[memory_recall/commit/merge/purge/edit] + MEM[stablemem_*/coldcontext_*] SOC[person_query/set_trait] subgraph SC[Scene recognition: declared + emergent] FE[① fingerprint chan/peer/tool/topic/part
weights 1.0/1.0/0.8/0.4/0.2] @@ -187,7 +187,7 @@ flowchart TB end SP[System Prompt] -->|Sequential assembly| LLM LLM[LLM] -->|coldcontext_query| Q2 - LLM -->|memory_recall| MEM + LLM -->|stablemem_recall| MEM ``` See [`assets/docs/en/ARCHITECTURE.md`](assets/docs/en/ARCHITECTURE.md) for details. @@ -336,7 +336,7 @@ plugin were discarded, `Doc` kept only three fields, and `Remove` never released path the dedup, `no_memory`, channel `Cleaner`, interrupt semantics and correct `EventRawInput` it had always lacked. Three real defects fixed: **user-sent images never appeared in the WebUI chat log** (the media path published a map while the subscriber asserted a string), -**`memory_commit`'s `sentence_text` had never been exposed to the model** (though it is the +**`memory_commit`'s `sentence_text` had never been exposed to the model** (note: this tool is now named `stablemem_commit`; the original name is kept for the v1.1.1 release note) (though it is the mandatory link in the media binding chain), and **two data races in `PluginSDK`** (11 reported by `-race`; in production this showed up as sporadic nil-dereference crashes during plugin reload). diff --git a/assets/docs/zh/ARCHITECTURE.md b/assets/docs/zh/ARCHITECTURE.md index 82094f43..d7a619de 100644 --- a/assets/docs/zh/ARCHITECTURE.md +++ b/assets/docs/zh/ARCHITECTURE.md @@ -228,12 +228,13 @@ L2 不是 L1 的摘要层,也不是另一种数据形态——它就是同样 ③ Graph (图数据库 — 顶层仍是三元组,节点即记忆块) SQLite — memory_blocks(节点) + memory_block_edges(边) 三元组 = 【节点】【关系边】【节点】;putTripleBlocksTx 落为 src --edge_type--> dst - 写入: memory_commit / 冷文档蒸馏 / Pipeline 规则蒸馏 / memory_merge + 写入: stablemem_commit / 冷文档蒸馏 / Pipeline 规则蒸馏 / stablemem_merge 读取: ├── 自动召回: Indexer.BuildContext(input) │ → CleanText → 向量实体搜索 + jieba关键词 → SQLite LIKE + BFS depth=2 │ → 【记忆索引】→ system prompt - └── LLM主动: memory_recall / memory_merge / memory_purge / memory_edit / memory_delete_entity + └── LLM主动: stablemem_recall / stablemem_merge / stablemem_purge / + stablemem_edit / stablemem_delete_entity Social: person_query / set_trait / relate (包装 GraphDB) ④ 四个独立心跳循环(各自独立的 ticker 和配置间隔) @@ -356,20 +357,26 @@ Core ◀─入向审查── 外部 | 工具 | 作用 | | ------ | ------ | -| `memory_recall` | 从 Graph 召回 | -| `memory_commit` | 写入 Graph 三元组 | -| `memory_merge` | 合并两个实体节点 | -| `memory_purge` | 删除指定实体 | -| `memory_edit` | 编辑已有实体/关系 | -| `memory_delete_entity` | 删除实体及其所有关系 | -| `memory_introspect` | 查看记忆统计 | +| `stablemem_recall` | 从 Graph 召回 | +| `stablemem_commit` | 写入 Graph 三元组 | +| `stablemem_merge` | 合并两个实体节点 | +| `stablemem_block_merge` | 合并两个记忆块 | +| `stablemem_purge` | 删除指定实体 | +| `stablemem_edit` | 编辑已有实体/关系 | +| `stablemem_delete_entity` | 删除实体及其所有关系 | +| `stablemem_introspect` | 查看记忆统计 | | `coldcontext_query` | 从冷上下文(cold context)召回 | | `coldcontext_commit` | 写入冷上下文 | -`memory_commit` 与 `coldcontext_commit` 自 v1.1.1 起接受 `media_digests`,并由内核把 +> ★ 工具名以 `stablemem_` / `coldcontext_` 为前缀(v1.2 起,旧的 +> `memory_*` / `doc_*` 已废弃)。权威定义在 +> `internal/agent/core/tooldefs.go`;核对命令: +> `grep -rhoE '"(stablemem|coldcontext)_[a-z_]+"' --include=*.go internal | sort -u` + +`stablemem_commit` 与 `coldcontext_commit` 自 v1.1.1 起接受 `media_digests`,并由内核把 `[ <短digest>] <描述>` 标记补进句子/正文——**标记由内核拼,模型只给 digest**。 要求调用方知道格式,等于让一个拼写错误静默切断引用绑定而全链路无人报错。 -`memory_commit` 同时新增 `sentence_text`:媒体引用挂在句子上,没有句子就无处可挂。 +`stablemem_commit` 同时新增 `sentence_text`:媒体引用挂在句子上,没有句子就无处可挂。 ### 媒体记忆(块 union:媒体即块) diff --git a/assets/docs/zh/OVERVIEW.md b/assets/docs/zh/OVERVIEW.md index bba320c2..5df0572d 100644 --- a/assets/docs/zh/OVERVIEW.md +++ b/assets/docs/zh/OVERVIEW.md @@ -46,7 +46,7 @@ CLI 需要终端面板时,**订阅事件总线**(`EventTerminalOutput` + `Ev - **无独立 GC、无引用计数、无 keep-set**:记忆块遵循单层不变量——Context → Document → Graph 是块的**迁移**,不是复制、也不靠引用保活;**删除块即删内容** - **v1.1.1 起贯通插件边界**:插件可通过 `InsertWithMedia` / `InjectInputMedia` 读写媒体, - 模型可用 `memory_commit` / `coldcontext_commit` 的 `media_digests` 参数关联媒体 + 模型可用 `stablemem_commit` / `coldcontext_commit` 的 `media_digests` 参数关联媒体 - **旧机制已整体拆除**:此前把视觉模型生成的描述写进正文、以 `[ <短digest>] <描述>` 标记 参与检索的**描述式索引**,以及 `media_refs` 引用计数,均已在 v1.2.0 删除——描述是模型生成的 二手信息,检索「别人转述的图片」不如检索图片自己的向量 diff --git a/docs/zh/future-plan.md b/docs/zh/future-plan.md new file mode 100644 index 00000000..4649febc --- /dev/null +++ b/docs/zh/future-plan.md @@ -0,0 +1,373 @@ +# 未来计划(Future Work) + +> **本文是唯一登记「尚未实现 / 已规划未做 / 已知缺口」的文档。** +> +> 建立于 2026-10-31。此前这些内容散落在各设计文档的「未做」「待补」「遗留」小节里, +> 时间一长就与代码脱节(有的已完成却仍写着未做,有的未做却被当成已完成)。 +> +> ## 收录规则 +> +> **只收两类:** +> 1. **代码里确实没有的能力**(已用 codegraph / 实测确认) +> 2. **有机制但零生产调用方**(本仓最常见的缺陷形态,见 §0) +> +> **不收:** +> - 已经在做的事(那不是"计划",是"进行中",写在对应设计文档里) +> - 历史决策与被否决的方案(那是归档,写在 `legacy-issues.md` 等历史文档里) +> +> ## 状态标记 +> +> | 标记 | 含义 | +> |---|---| +> | 🔴 | 有真实影响,应当优先做 | +> | 🟡 | 该做,不紧急 | +> | ⚪ | 有价值但优先级低 / 依赖前置条件 | +> | ⏸ | 主动搁置(有明确理由,不是忘了) | + +--- + +## 〇、先说清一个反复出现的缺陷形态:**机制 ≠ 接线** + +本仓已有至少 **5 例**「代码写对了、判据全绿、生产里从不执行」的缺陷。 +它们比 bug 更难发现,因为: + +- 策略表存在、判定函数正确、日志里一条拒绝记录都没有 ⇒ 看起来「没人违规」 +- 判据全绿,因为判据**直接调用**被测量的函数 ⇒ 测不到"有没有人调它" + +| # | 能力 | 机制 | 曾有的缺陷 | 现状 | +|---|---|---|---|---| +| 1 | 工具黑白名单 `Policy.CheckTool` | 完整 | `Agent.security` 恒 nil,闸门恒放行 | ✅ 2026-10-30 修复(依赖图补 `FacSecurity`) | +| 2 | 主体策略 `CheckToolFor` | 完整 | 0 生产调用方 | ✅ 2026-10-30 修复(父为子声明) | +| 3 | 插件命名档位 `ActivateProfile` | 完整 | **0 生产调用方** | 🔴 **未修**,见 §1.1 | +| 4 | 敏感词 / 注入预警 `ScanContent` | 完整(254 行) | **0 生产调用方** | 🔴 **未修**,见 §1.2 | +| 5 | 沙箱 `CheckPath` / `CheckNetwork` | 完整 | **0 生产调用方** | 🔴 **未修**,见 §1.3 | + +**做任何新能力之前,先问一句:这条链的每一跳,生产里真的有人调吗?** + +--- + +## 一、安全(security) + +### 1.1 🔴 插件命名档位没有生效点 + +**现状**:插件侧链路是完整的—— + +``` +QQ 插件 Start: s.Security().DeclareProfiles([qq.owner, qq.guest]) ✅ 内核登记了 +QQ 插件注入: InjectOptions{SecurityProfile: "qq.guest"} ✅ payload 里写进去了 +内核 activateSecurityProfile(payload) ❌ 零调用方 +CheckTool 按档位判定 ❌ 从未激活 ⇒ 恒"无档位" +``` + +实测确认: + +- `codegraph_callers("activateSecurityProfile")` → 4 个调用方,**全部是测试** +- `prepareInputTask` 逐条消费 `evt.Payload` 的各键(`no_memory` / `interrupt` / + `priority` / `scene` / `scene_policy` / `content` …),**唯独没有 `security_profile`** +- 生产日志里 `security profiles declared: qq.owner, qq.guest` 是**真的**(声明成功了), + 但没有一条拒绝记录 —— 因为档位永远没被激活 + +**影响**:QQ 的 owner / guest 差别对待**在生产中完全失效**。访客能用的工具与所有者相同。 + +**要做的**: + +1. 在 `prepareInputTask` 里读 `evt.Payload["security_profile"]`,调 `activateSecurityProfile` +2. **必须自带归位**:不带档位的输入要显式清空(`ActivateProfile("")`), + 否则进程级 `active` 会泄漏到下一轮(guest 的收窄误伤后续输入;方向相反就是提权) +3. 判据必须走**真实输入路径**(`runInputTask`),不能直接调 `activateSecurityProfile` + +**已知的架构限制**(解决前必须知道): + +`security.Runtime` 的 active profile 是**进程级单例**,不按任务隔离。 +主 agent / 驻留子 / rtchild 同进程并发时,两个不同档位的轮次**交错**就无法表达。 + +``` +A 轮: 注入 guest 档 ─┐ + ├─ 共享一个 active ⇒ 后写者覆盖先写者 +B 轮: 注入 owner 档 ─┘ +``` + +正确形态是把档位挂到 `TaskFrame` 上按任务隔离,但那要先改 +`security.Runtime.CheckTool` 的签名(它当前无任务上下文)。 +**在此之前只保证串行场景正确。** + +### 1.2 🔴 敏感词 / 提示词注入预警从未运行 + +**现状**:`internal/security/content.go`(254 行)实现了完整的 +`ContentPolicy{Scan, BlockedBy}`,`Runtime.ScanContent` 也写好了, +但**全仓零生产调用方**(只剩 2 条测试在调)。 + +**影响**: + +- 配置里写的敏感词一条都不生效 +- 模型被提示词注入时内核不预警、不阻断 +- 插件通过 `SecurityProfile.SensitiveWords` 声明的档位敏感词同样不生效 + +**要做的**:在**入向边界**接线。合理位置是 `executeToolCallOutcome` 的 defer +(那里已经有 `security.Global().Replace(...)` 做凭据脱敏,同一个位置、 +同一条 defer 分支),以及插件注入的输入在进 `prepareInputTask` 之前。 + +**注意**:不要只接一处。入向有两条路——工具结果、插件输入事件。只接一条会 +留下「工具的返回被扫了、插件消息没扫」的缺口,而后者才是注入的主要入口。 + +### 1.3 🔴 沙箱约束从未运行 + +**现状**: + +| 能力 | 机制 | 生产调用方 | +|---|---|---| +| `Policy.CheckPath(方向, 路径)` | 完整,拦 `..` 逃逸 | **0** | +| `Policy.CheckNetwork()` | 完整 | **0** | +| `Runtime.CheckPath` / `Runtime.CheckNetwork` | 薄包装 | **0**(只被测试调) | + +配置项 `core.security.sandbox.*` 全套已注册(`enabled` / `read_roots` / +`write_roots` / `allow_network`),`ApplyPolicyFromConfig` 也确实把它们装进了 +`Policy` —— 但**没有任何模块拿它判路径**。 + +**当前唯一真实生效的路径约束**在 files 插件自己的 `pathWithinSandbox` +(`internal/plugins/files/plugin.go`)—— 这正是设计文档批判的 +「每个插件各写一套」形态。 + +**要做的**:两个选择,需先定方向。 + +- **A(内核)**:在工具参数校验之后、分派之前,对带路径/URL 的参数做 `CheckPath` / `CheckNetwork` +- **B(插件)**:把 `CheckPath` 经 SDK 暴露给插件,让每个 IO 插件自己调(**不推荐** —— + 会重新长回"各写一套") + +倾向 A,但要先解决"哪些参数算路径"的判定(不能瞎猜 `command` 里的字符串)。 + +### 1.4 🟡 插件无法声明主体策略 + +**现状**:公开 SDK 的 `SecurityAPI` 只有 `DeclareProfile` / `DeclareProfiles` +(命名档位)。`Runtime.DeclarePrincipalPolicy` 存在但**未暴露给插件**。 + +**区别**:档位是「按输入携带的参数选」,主体策略是「按主体 ID/标签授予」。 +QQ 需要前者(owner vs guest 由发送者身份选),但一个需要「按具体用户 id 授予 +工具」的插件需要后者。 + +**注意**:暴露前要想清 scope。`scope="tag"` 是**部署级**语义,让插件声明 tag 策略 +会让插件有能力影响全站 —— 那是个提权口子。倾向只暴露 `scope="id"`。 + +### 1.5 🟡 凭据库(变量库)的六个未决项 + +从 `kernel-secret-firewall-design.md` §5 迁来(原 T5–T10)。**T1–T4 已落地**, +不再重列;下面六项代码里确实没有。 + +| # | 事项 | 为什么要做 | +|---|---|---| +| T9 | **不自动淘汰的规模上限** | 变量库落盘文件只增不减。需要**可观测**的降级(超 N 条时告警),而不是静默删某条 | +| T10 | **提交工具的泄露面** | "主动提交"要把明文交给内核 ⇒ 明文会进会话/日志。应优先支持 `${ENV}` / `@file:` 两条**不明文入会话**的路径 | +| T5 | **压缩对别名的影响** | 上下文压缩的摘要可能把 `@{sec_1}` 当普通文本;压缩后别名是否仍有效未验证 | +| T6 | **明文在内存中的擦除** | Go 字符串不可变 ⇒ 只能擦可写副本。要先定"努力擦除"还是"不承诺" | +| T7 | **引用外部文件、内核只持路径** | SSH 私钥这类"内核拿不到字节"的场景,可能需要只持路径的形态 | +| T8 | **入向回填的安全边界** | 回填后 agent 可把 `@{别名}` 写进**任意**命令参数 ⇒ 等于"在任意命令里用明文"。这**是必需的**(否则没法用密钥),但"能做什么"因此回到**命令本身**。要定:要不要限制哪些工具允许触发回填 | + +### 1.6 🟡 硬私有的判定目前靠插件自己 + +**现状**:QQ 的 `hard_private_tools` 由插件读配置后通过 `DenyTools` 声明 +(而不是内核的 `HardPrivateTools` 语义)。 + +差异:`DenyTools` 是「这个主体不能用」,`HardPrivateTools` 是「**任何**用户主体不能用」。 +后者防的是「用户偷看私人数据」,而内部执行体(父的委派)本就有完整权限 —— +即使用错了也不会误伤内部,但语义是对的。 + +**要做的**:QQ 插件把 `hard_private_tools` 从 `DenyTools` 改成 +`HardPrivateTools`。纯插件改动,声明链路已贯通。 + +--- + +## 二、内核模块化 + +### 2.1 🔴 device 模块是空壳 —— 工具执行仍在 agent + +**现状**: + +| 项 | 设计意图 | 实际 | +|---|---|---| +| 工具枚举 | device 拥有 | ✅ `device.Runtime.Tools()` 有,但**唯一调用方是它自己的 ToolCount()** | +| 工具路由 | device 拥有 | ❌ `toolcall.go` 实际调的是 `toolroute.Dispatch`,不是 `device.Dispatch` | +| 工具执行 | device 拥有 | ❌ `device.Runtime.Execute` **0 生产调用方** | +| inputch / outputch | device 拥有 | ❌ 仍在 `internal/agent/io` | +| 安全过境 | device 是唯一落点 | ❌ device 拿到的 `ks.Security` 没有任何执行路径会用到 | + +`ks.Device` 与 `Agent.MountDevice` 的产物没有任何读取方 —— +`agent.Device()` 除了自己没人调。 + +**这不是"漏接线",是刻意的分阶段**(见 `internal/kernelmod/device/mod.go` 包注释): +第一阶段先立边界与闸门,第二阶段再搬实现。**文档应当如实这么写,而不是写成已完成。** + +**要做的(第二阶段)**:把 `tooldefs` / `toolcall` 的实现迁进 device。前置条件: + +- device 对 Agent 内置处理器的窄接口(`api.BuiltinToolHandlers` 已有雏形) +- 工具执行中的上下文/超时/取消/panic 恢复要一起搬 +- 记忆策略(`ContextPolicy` / `RecallPolicy`)的归属要定清 +- 独立工具(memgc 等)无 device 时的退化路径 + +### 2.2 🟡 `device` 设施无人消费 + +`device` 模块 `Provides FacDevice`,但**没有任何模块 `Requires` 它**(实测)。 +它是图的叶子。 + +**含义**:漏挂 device 模块时,所有判据仍绿(见 `bootgraph_test.go` 里 +`TestEveryRegisteredModuleIsCompiledIn` 的注释已经写明这类盲区)。 +在第二阶段完成前,这属于预期状态;完成后应有模块依赖它。 + +### 2.3 🟡 声明↔真实读取的漂移判据缺失 + +**历史**:设计文档 §2.7.3 点名了 `TestDeclarationsTraceToRealCallSites` +(「声明与真实签名不漂移」),它随 `3864f36`(删死包 `kernelboot/modules`) +迁包时**没有搬过来**,现在仓库里不存在。 + +**为什么它重要**:`kernelmod/agent` 的 `Requires()` 曾经少了 `FacSecurity` —— +代码读 `ks.Security` 却不在图上声明。**这条判据正是用来抓这种漂移的**, +它缺失期间,`agent→security` 这条边断了整整一轮发布。 + +**要做的**:写一条判据,扫描各模块 `Init` 及同目录非测试代码里对 +`ks.` 的读取,与 `Requires()` 的设施集合比对,报告「读了但没声明」。 + +**已知的例外**(判据要放行,不能误报): + +- **Group 0 产物**(`Cfg` / `CfgReg` / `DataDir` / `AgentWorkDir` / `CLISocket` / + `HTTPAddr`)—— 它们不由任何可调度模块提供,靠"阶段前提"保证。 + 这类靠各模块自己的 nil 检查守(见 `nilcheck_test.go`) +- **自产自读**(如 text-memory 读自己的 `ks.TextMemory`) +- **KernelState 的方法**(`AddCloser` / `AddQuiescer`),不是字段 + +### 2.4 ⚪ `deploy/depgraph-decls.json` 已无消费者 + +手写声明清单,生成器早已改为读模块的 `Requires()/Provides()`。文件仍在仓库里, +是误导性遗留(新人会以为要维护它)。 + +**要做的**:删文件,或改成明确的 deprecated 说明。 + +--- + +## 三、记忆 + +### 3.1 🟡 `OversizeToolReports` / `oversizeHintFor` 零消费者 + +**现状**:`toolresult_budget.go` 统计了「本任务有多少次超大工具结果」, +但除了它自己没人读。`offloadToolResult`(超长结果转存 + 句柄回读)**已经接好**了, +所以主路径不受影响。 + +**缺的是**:把统计接进调度器/下一轮决策,让模型知道"你这次查询太宽了"。 + +### 3.2 ⚪ 图库 `memory_blocks.data` 单表承载媒体 + +**现状**:媒体字节直接存在 `memory_blocks.data` BLOB(块 union 化,CAS 已淘汰)。 +生产实测:330MB 库中 299MB 是这个列(563 个有负载的块,去重后 556 个 —— 重复率低,不是问题)。 + +**观察,非缺陷**:这是刻意的 union 化设计(元数据与负载同行)。若将来库膨胀, +优化点是分表或外置 blob 存储,不是"加回 CAS"。 + +--- + +## 四、日志与运维 + +### 4.1 🔴 日志保留策略只清 1/4 的归档类型 + +**现状**(实测,见 `internal/log/retention.go`): + +`cleanupBeforeMonths`(默认 `n_months` / 3)**只处理 `month_` 和 `year_`**, +完全不碰 `week_` 和裸 `homed_*.log`。 + +探针实测(cutoff 3 个月): + +``` +输入: month_2026-18 month_2026-02 week_2026-W18 week_2026-W44 year_2026 +保留后: month_2026-18 week_2026-W18 week_2026-W44 + (month_2026-02 与 year_2026 被删,符合预期;半年前的 week_ 永远留着) +``` + +**更硬的证据:`month_*.tar.gz` 的命名根本没有月份。** + +`compressMonthly` 用 `key := fmt.Sprintf("%s-%s", m[1], m[2][:2])` +(m[1]=年,m[2]=ISO 周)⇒ 产出 `month_2026-27` / `month_2026-38`。 +而 `cleanupBeforeMonths` 用 `time.Parse("2006-01", f[6:13])` 解析 ⇒ +**月=27 越界解析失败 ⇒ `continue` ⇒ 一个都不删**。 + +生产目录里 `month_2026-27` ~ `month_2026-39` 全都是这种畸形名, +且 `compressMonthly` 遇到同名文件会 `continue` —— 这 13 个文件永远不会被合并或清理。 + +**为什么判据没抓到**:`retention_test.go` 的 5 条判据是**手写**合规文件名 +(`month_%04d-%02d`),从没跑过 `compressMonthly` 的真实产物。 + +**要做的**: + +1. `compressMonthly` 用真实月份做键(从周名推出所属月,或改用 `month_YYYY-MM`) +2. `cleanupBeforeMonths` 覆盖 `week_` 与裸 `homed_*.log` +3. 判据必须**先跑 `compressMonthly` 拿产物、再跑 retention 删它** —— + 手写文件名的判据测不到命名不一致 + +### 4.2 🟡 生产日志存量未清 + +生产 `/home/hologramagent/log`:491 个裸 `homed_*.log`(75MB),其中 **459 个是 +2026-10-26 崩溃循环**留下的。`week_2026-W43` 只含 1 个文件(应为 22 个)—— +因为那批崩溃日志在周压缩跑之前就被 `fileExists` 判断挡住了。 + +retention 修好之后这些会被逐步清掉,但不会立刻(要等跨月压缩)。 + +--- + +## 五、文档与工程卫生 + +### 5.1 🟡 README / ARCHITECTURE 的工具名陈旧 + +实际工具名是 `stablemem_*` / `coldcontext_*`,但对外文档仍写 `memory_recall` / +`memory_commit` / `doc_query`。 + +| 文件 | 处数 | 性质 | +|---|---|---| +| `assets/docs/zh/ARCHITECTURE.md` | 6 | **对外文档,必须改** | +| `assets/docs/zh/OVERVIEW.md` | 1 | 对外文档 | +| `README.md` / `README_EN.md` | 3 / 3 | 对外文档 | +| `docs/zh/resident-subagent-design.md` | 2 | 活设计文档 | + +**2026-10-31 状态**:已修正(见 §六 变更记录)。 + +### 5.2 🟡 plan.md 不进 git,却被 43 个 .go 文件当"单一事实来源"引用 + +- `.gitignore` 第 101 行忽略 `/plan.md` +- 43 个 `.go` 文件的注释里写着「见 plan.md §X」 +- 而 plan.md 的状态表**已经谎报过一次**(把 `SetSecurity` 记成已完成, + 实际生产 0 调用方) + +**要做的**:要么把 plan.md 纳入 git 并建立"状态表必须有判据背书"的规则, +要么把引用改为指向具体设计文档而不是 plan.md。 + +### 5.3 ⚪ CI 不跑 gofmt + +`make lint` 有 golangci-lint(warn-only,本机未安装),但 `ci.yml` 没有 gofmt 步骤。 +2026-10-30 曾检测到 1 处漂移(`rtchild_route_test.go`),已顺手清掉。 + +### 5.4 ⚪ 工作区体积 + +`build/` 7.2G + `dist/` 6.6G + `.npm-cache` 5G = 约 20G(git 只跟踪 1,313 文件)。 +`dist/_old-20261017` 3.2G 是历史发行包。 + +--- + +## 六、分散在各设计文档里的待办(索引) + +下面这些待办**留在原文档**里更合适(它们与那份文档的实验/设计强耦合), +这里只做索引,避免"想找待办要翻 30 篇文档"。 + +| 待办 | 在原文档 | 性质 | +|---|---|---| +| 测试模板库(压力/接口/编译预置模板) | `commondev-design.md` §4「尚未做」 | ⚪ 机制已落地,模板按需补 | +| 记忆召回「形态 3 / 形态 4」两个缺口 | `recall-capability-tiers.md` §「待补的能力」 | 🟡 需新实验,方向已列 | +| 判据口径与契约对齐 | `abstention-criterion.md` §「待办」 | 🟡 | +| 中断子系统 `maxQueue` 对中断队列的约束 | `interrupt-subsystem-split-plan.md` §「阶段 1.5 遗留」 | ⚪ | +| 工具并行化的遗留项 | `toolcall-parallel-execution-plan.md` §「遗留项状态」 | ⚪ | +| 遗留表退役的剩余读方 | `legacy-table-retirement.md` §八 / §十二 | ⚪ 迁移收尾 | +| 生产召回验证的 3 条遗留 sentence→块 边 | `production-recall-validation.md` | ⚪ 非迁移产生 | +| 遗留问题清单 | `legacy-issues.md` §二「真正待办」 | ⚪ 长期清单 | + +--- + +## 七、变更记录 + +| 日期 | 变更 | +|---|---| +| 2026-10-30 | 工具策略闸接线(依赖图补 `agent→security`);父为子声明主体策略 + 撤销 | +| 2026-10-31 | 建立本文;全量校订 35 篇文档的状态声明与失真表述;修正对外文档的工具名;从 `kernel-secret-firewall-design.md` §5 迁入 T5–T10 | diff --git a/docs/zh/input-scheduler-design.md b/docs/zh/input-scheduler-design.md index 95277480..b97a6cc9 100644 --- a/docs/zh/input-scheduler-design.md +++ b/docs/zh/input-scheduler-design.md @@ -1,5 +1,15 @@ # 输入调度器设计(四级中断优先级 · 两类别 · 可抢占 · 现场保存) +> **状态:已实现并部署。** 两类别、L1–L4、可抢占与现场保存、中断栈、 +> 任务级回执、`preemptCooldown` 均在生产运行(`internal/agent/core/scheduler.go`)。 +> +> ★ 一个下游的真实差异(2026-10-31 补记):rtchild(实时子 agent)**不走**这套阶梯, +> 而是用单槽覆盖(最新者胜)。原因:四级中断的目标函数是「公平、不饥饿」, +> rt 要的是「可饿死、只要最新」——两者目标相反,不该共用机制。 +> 详见 `plan.md` 十一.三 B。 +> +> **未实现项**不在本文维护,见 [`future-plan.md`](./future-plan.md)。 +> > **模型更正(2026-09-13,据用户澄清重写 §2/§3/§4.1/§6.3/§9/§11/§12)** > > 本稿早期版本把「四级优先级」当成了**所有任务**的通用优先级,并按通道名 diff --git a/docs/zh/kernel-modularization-design.md b/docs/zh/kernel-modularization-design.md index 2f814e06..720c0137 100644 --- a/docs/zh/kernel-modularization-design.md +++ b/docs/zh/kernel-modularization-design.md @@ -1,8 +1,16 @@ # 内核模块化与并行装配设计 -> **状态**:设计已定(2026-10-16),实现分阶段推进。 +> **状态**:**已实现并部署**(设计 2026-10-16,落地完成 2026-10-24,2026-10-31 全量校订)。 > 本文是**唯一的装配架构权威**。任何新增内核模块或插件都必须遵循本文的依赖声明契约。 > +> **本文与 `future-plan.md` 的分工**:本文只写**已落地的事实与设计理由**; +> 未实现的事项(如 device 第二阶段、漂移判据补回)一律登记在 +> [`future-plan.md`](./future-plan.md),本文不重复维护「未做」列表 —— +> 那份列表历史上滞后过一次(§2.7.4 曾写「main 仍走串行代码」而实际早已接完)。 +> +> **数字类断言的取数方式**:模块数/层数一律用 `go run ./cmd/homed-depgraph -print` +> 现场取,**不要凭文档记忆** —— 本文历史上写错过一次(17 模块 vs 实际 20)。 +> > 关联文档:`kernel-secret-firewall-design.md`(安全模块的装配点约束是本文 §2.3 的实例)、 > `resident-subagent-design.md`(inputch 共享总线是 §5 的既有先例)。 @@ -418,7 +426,7 @@ CGO_ENABLED=1 go build ... \ 3. **Step 4 后续增量**:把 12 个 `init*` 迁成 `Module`,声明清单即废止, 生成器改为 import 模块登记包(那时"清单与代码不符"**结构上不可能**) -> **★ 当前已产出的分层结果(实测)**: +> **★ 当时的推演结果(2026-10-16,已过时,仅作设计推演保留)**: > > ``` > 第 0 层(1 个): config @@ -427,7 +435,12 @@ CGO_ENABLED=1 go build ... \ > 第 3 层(1 个): agent > ``` > -> 对比现在硬编码的 **12 步串行** —— 这是本设计给出的**并行度提升的直接证据**。 +> 对比当时硬编码的 **12 步串行** —— 这是本设计给出的**并行度提升的直接证据**。 +> +> ⚠️ **这 4 层 / 14 模块是 2026-10-16 的状态,不是现状。** +> 现状是 **6 层 / 20 模块**(含 `update` / `migration` / `onnx-parser` / `device`), +> 随时用 `go run ./cmd/homed-depgraph -print` 取,权威数字见 §2.7。 +> 保留这段是为了留下「并行度确实提升了」的原始论证,**不要拿它当前情况用**。 ## 2.6 syncbus:插件与内核的状态同步(插件调度的唯一驱动) @@ -738,11 +751,27 @@ loadOne(name): `internal/kernelmod/all` 的聚合 import 把**全部模块引入编译**后,各模块在自己 文件中用 Requires() 把 **main() 里隐含在语句顺序中的依赖关系**变成显式声明。 -实测分层结果(2026-10-24 复核:`go test ./internal/kernelmod/all/ -run TestLayeringYieldsParallelism -v`): -``` -6 层 / 17 模块,最宽层 9 个可并行(原来是 17 步严格串行) -``` +> **实测分层结果(2026-10-31 复核)**: +> 命令 `go run ./cmd/homed-depgraph -print` +> +> ``` +> 第 0 层(3 个,可并行): config, kernel-api, update +> 第 1 层(10 个,可并行): doc-store, knowledge-migration, lua, memory, +> migration, multimodal, onnx-parser, security, +> supervisor, tracker +> 第 2 层(3 个,可并行): knowledge-store, llm, text-memory +> 第 3 层(1 个): stage-host +> 第 4 层(2 个,可并行): agent, plugin-host +> 第 5 层(1 个): device +> → 20 个模块 / 6 层,最宽层 10 个可并行 +> digest bd520ec9(图与二进制配套由内嵌 hash 校验) +> ``` +> +> ⚠️ **历史数字**:本节早期版本写的是「6 层 / 17 模块,最宽层 9 个」。 +> 那是 2026-10-24 的事实;此后新增了 `update`(第 0 层)、`migration`、 +> `onnx-parser`(第 1 层)与 `device`(第 5 层)。**当前数字以上方代码块为准**, +> 任何时候都可以用上面那条命令重新取,不要凭文档记忆。 #### 2.7.1 ★ cfg 的归属:修掉了"无主模块"(用户指出的问题) @@ -783,28 +812,58 @@ platform-check → heartbeat-early → logging → data-dirs (`cmd/homed/bootstrap_order_test.go` 断言真实相对行号)。 判据 `TestGroup0StaysOutOfScheduler` 保证 Group 0 与可调度模块**不重叠**。 -#### 2.7.3 判据 + 变异(全部咬住) +#### 2.7.3 判据 + 变异(当时全部咬住) -| 判据 | 守什么 | +> **⚠️ 本节是 2026-10-16 的历史记录,判据名已随迁包发生变化。** +> 2026-10-24 的 `3864f36` 删除了死包 `internal/kernelboot/modules`, +> 整机启动图判据迁到 `internal/kernelmod/all`。迁移时**只搬了 5 条整机判据** +> (`TestRealRegistryAssemblesWholeMachine` / `TestLayerTopologyIsRespected` / +> `TestShutdownIsStrictlyReverseLayerOrder` / `TestShutdownLeavesNoResidue` / +> `TestEveryRegisteredModuleIsCompiledIn`),下表里另外几条**没搬**。 +> +> 下表左栏是**当时**的名字,右栏是**现在**仓库里真实的判据。 +> 找不到对应物的那 4 条已登记到 [`future-plan.md` §2.3](./future-plan.md)。 + +| 当时写的判据 | 现在真实存在的位置 | |---|---| -| `TestDeclaredLayeringMatchesRealTopology` | 9 条关键顺序(每条都来自**真实签名**,非抄写法顺序) | -| `TestLayeringYieldsRealParallelism` | 分层确实带来并行(≥2 模块同层) | -| `TestKernelAPIIsInFirstLayer` | `initKernelAPI()` 真无依赖 | -| `TestGroup0StaysOutOfScheduler` | 副作用顺序不被误放进图 | -| `TestConfigModuleRunsAfterGroup0` | cfg 的**阶段前提**(安全块在 Group 0 里、config 不在) | -| `TestDeclarationsTraceToRealCallSites` | **声明与真实签名不漂移**(改了签名要改声明) | -| `TestNoOrphanFacilities` | 设施名无重复提供者 | +| `TestDeclaredLayeringMatchesRealTopology` | `kernelmod/all/registry_test.go` 的 `TestLayeringPreservesCriticalOrder`(10 条关键顺序) | +| `TestLayeringYieldsRealParallelism` | 同上 `TestLayeringYieldsParallelism`(仅改名) | +| `TestKernelAPIIsInFirstLayer` | 同上 `TestFirstLayerHasNoDependencies` | +| `TestGroup0StaysOutOfScheduler` | ❌ **不存在**(已登记 future-plan §2.3) | +| `TestConfigModuleRunsAfterGroup0` | ❌ **不存在**(同上) | +| `TestDeclarationsTraceToRealCallSites` | ❌ **不存在** — 它正是能拦住 `agent→security` 这类「读了没声明」漂移的判据,见 future-plan §2.3 | +| `TestNoOrphanFacilities` | ❌ **不存在**(`kernelboot.Build` 会检重名,但不是独立判据) | -**变异 5/5 咬住**:漏 lua→llm、漏 migration→store、config 挪回 Group 0、 +**变异 5/5 咬住**(当时的验收):漏 lua→llm、漏 migration→store、config 挪回 Group 0、 去掉 security、签名漂移。其中**变异 3 正是"无主模块"的复现**。 -#### 2.7.4 未做(Step 4b 的剩余) +> ★ **2026-10-31 补记**:`TestDeclarationsTraceToRealCallSites` 缺失的代价已经真实现过 —— +> `kernelmod/agent` 的 `Requires()` 漏了 `FacSecurity`,代码读 `ks.Security` +> 却不在图上声明,导致工具策略闸在生产里恒放行一整轮发布。详见 future-plan §2.3。 -声明层已完成且可验证,但 `main.go` **仍走原来的串行代码**。 -下一步是把 `Run(plan)` 真接进 main(届时 17 步变 6 层), -并让 `loadPluginsScheduled` 取代 `loadPlugins`(syncbus 驱动插件)。 -这两步都要改生产启动路径 ⇒ 需要更谨慎的验证(含真机启动对比)。 +#### 2.7.4 Step 4b 已完成(原文写「未做」,2026-10-31 更正) + +> **⚠️ 本节原标题是「2.7.4 未做(Step 4b 的剩余)」,内容是错的。** +> 它写着「`main.go` **仍走原来的串行代码**,下一步是把 `Run(plan)` 真接进 main」。 +> 实际 `cmd/homed/main.go:229` 早已调用 `RunKernelModules` → `kernelboot.Run`, +> 插件调度也已归位到 `plugin-host` 模块(见 §8)。 +> 下面的「完成证据」是 2026-10-31 实测。 + +**两条当时规划的剩余步骤,现在都完成了:** + +| 当时写的「下一步」 | 完成证据 | +|---|---| +| 把 `Run(plan)` 真接进 main(17 步变 6 层) | `cmd/homed/main.go:229` `RunKernelModules(ks, hooks)` → `cmd/homed/kmod_runtime.go:120` `kernelboot.Run(plan, ...)`;生产日志逐层打印 `kernel assembly layer N complete` | +| 让 `loadPluginsScheduled` 取代 `loadPlugins`(syncbus 驱动插件) | 插件调度已归位 `internal/kernelmod/pluginhost`(§8);生产日志 `[pluginhost] ... tools_ready published` | + +**结构性判据**(守这两步不退化的): + +- `cmd/homed/bootstrap_order_test.go` —— 断言 `RunKernelModules(` 在 main 里的**真实相对行号** + (必须晚于 Group 0 的安全块与 `ToConfig`) +- `internal/kernelmod/all/bootgraph_test.go` 的 5 条整机判据 —— 真实登记表装配、 + 层间有序、逆序关闭、无残留、模块不漏挂 + ### 2.8 完整启动流程与真实依赖(逐行核对,2026-10-16) diff --git a/docs/zh/kernel-secret-firewall-design.md b/docs/zh/kernel-secret-firewall-design.md index ed9b4264..f8ffb3bc 100644 --- a/docs/zh/kernel-secret-firewall-design.md +++ b/docs/zh/kernel-secret-firewall-design.md @@ -1,6 +1,35 @@ # 内核安全模块(Secret Firewall)设计 · 计划追加 -> **状态:已实现(持续审计中)。** 本文的安全模块、配置加密、变量库统一与 C1–C8 判据已落地;E-1/E-2/E-3/E-4 已完成;剩余项见文末实现边界。本文保留原始设计与逐条审计记录。 +> **状态:部分实现。** 凭据防火墙、配置加密、变量库统一与 C1–C8 判据已落地并**在生产线生效**; +> 但**三条能力机制完整却零生产调用方**(敏感词/注入预警、路径沙箱、网络闸)。 +> 本文保留原始设计与逐条审计记录;未实现项统一登记在 +> [`future-plan.md`](./future-plan.md) §一。 +> +> ## 接线状态表(2026-10-31 实测,逐个查生产调用方) +> +> 本仓反复吃亏在「机制写了、判据绿了、生产里从不执行」。所以这里不写✅/❌定性, +> 只写**实测的生产调用点**: +> +> | 能力 | 机制 | 生产调用点 | 状态 | +> |---|---|---|---| +> | 出向凭据拦截 `PrepareOutbound` | 完整 | `eventguard.go` / 各界代理 | ✅ 生效 | +> | 入向凭据清洗 `ScrubInbound` | 完整 | `eventguard.go`、`events/bus.go`、`device/mod.go` | ✅ 生效 | +> | 执行前回填 `FillForExecution` | 完整 | stage 管道 | ✅ 生效 | +> | 工具黑白名单 `CheckTool` | 完整 | `agent/core/security_profile.go`(主 agent) | ✅ **2026-10-30 接通** | +> | 按主体授权 `CheckToolFor` | 完整 | `agent/core/security_profile.go`(子 agent) | ✅ **2026-10-30 接通** | +> | 插件命名档位 `DeclareProfile` | 完整 | `sdk/security_impl.go`、`proc/corehandler_security.go` | ✅ 声明侧通 | +> | 插件命名档位 `ActivateProfile` | 完整 | `agent/core/security_profile.go` 内的 `activateSecurityProfile` | 🔴 **该函数零调用方** ⇒ 档位永不激活,见 future-plan §1.1 | +> | 敏感词/注入 `ScanContent` | 完整(254 行) | **无** | 🔴 配置里的敏感词一条不生效,见 future-plan §1.2 | +> | 路径沙箱 `CheckPath` | 完整 | **无**(只被 Runtime 自己调 Policy) | 🔴 见 future-plan §1.3 | +> | 网络闸 `CheckNetwork` | 完整 | **无**(同上) | 🔴 见 future-plan §1.3 | +> | 事件总线授权 + 载荷过闸 | 完整 | `security/mod.go`(唯一装配点) | ✅ 生效 | +> +> ★ **历史的教训(两轮都错)**: +> 第一轮把「`Agent.security` 从未被赋值」诊断成「少一个注入点」而补了 `SetSecurity()`, +> 但生产装配链从不调它;第二轮才看清根因是 **`kernelmod/agent` 的 `Requires()` 没声明 +> `FacSecurity`** —— 依赖图没理由把 security 的产物交给 agent。 +> 所以「接线」必须在**依赖图层面**表达,不能靠事后注入。 +> > 记录一条由用户纠正后确立的**架构决定**,以及它带来的简化与下沉。 > > 撰写时间:2026-10-14。触发:用户在讨论 commondev 加密 shell 时提出两点纠正。 @@ -387,19 +416,24 @@ internal/security/ 里的一个**进程级单例**(或挂在 ProviderManager ## 5. 待办与未决 -| # | 事项 | 说明 | +> ★ **2026-10-31 迁出说明**:本节里**仍未实现**的事项已全部迁到 +> [`future-plan.md`](./future-plan.md) §一「安全」。下面保留原表作为**设计讨论的历史记录** +> (它们的推理过程本身有价值),但**不要再把这张表当待办清单用** —— +> 它历史上滞后过(T3/T4 已落地却仍挂在表里)。 +> 每行的「现状」列是 2026-10-31 实测结果。 + +| # | 事项 | 现状(2026-10-31) | |---|---|---| -| **T1** | ~~修 `splitKeyValue` 对 `assigned_secret` 的键名丢失~~ | ✅ **已修**(插件侧;迁内核时可一并搬)。根因:键名后常带空格(`api_key = "..."`),旧判据把“含空格”当成“不像键名”就放弃拆分 ⇒ 整段被当 secret。已配变异验证 | -| **T9** | **不自动淘汰的规模上限** | 落盘文件只增不减(除非人主动删)。需定一个**可观测**的降级策略(如超 N 条时**告警**,而不是静默删某条) | -| **T10** | **提交工具的泄露面** | “主动提交”要把明文交给内核 —— 与 `enc_store` 同问题(明文会进会话/日志)。 - 应优先支持 `${ENV}` / `@file:` 两条不明文入会话的路径 | -| **T2** | 把前稿的 `intercept.go` / `encstore.go` 从插件**迁到内核** | 模式库可复用,别名/存储需重写为内核形态。★ **迁后本插件的 `enc_*` 工具族与加密 shell 应大幅删减**(用户:插件对安全机制无感知) | -| **T3** | **密钥从哪来**(内核侧) | 内核是长驻进程;密钥须由部署方提供(systemd 环境变量)还是内核自持密钥文件?未定 | -| **T4** | 变量**持久化与生命周期** | ✅ **已定**:落盘加密持久化(重启不丢)、**不自动淘汰**、可主动删(§2.4)。剩余:落盘位置与 masterKey 来源(同 T3) | -| **T5** | 压缩(compaction)对别名的影响 | 摘要可能把 `@{sec_1}` 当成普通文本;压缩后别名是否仍有效? | -| **T6** | **明文在内存中的擦除** | Go 字符串不可变 ⇒ 只能擦可写副本。要定"努力擦除"还是"不承诺" | -| **T7** | 替代方案评估 | 内核是否可能拿不到 secret(如 SSH 密钥文件)⇒ 也许该支持"引用外部文件、内核只持路径" | -| **T8** | **入向回填的安全边界** | 回填后 agent 可把 `@{别名}` 写进**任意**命令参数 ⇒ 等于获得“在任意命令里用明文”的能力。这**正是必需的**(否则没法用密钥),但“能做什么”的边界因此回到**命令本身**。要定:要不要限制哪些工具允许触发回填? | +| **T1** | ~~修 `splitKeyValue` 对 `assigned_secret` 的键名丢失~~ | ✅ **已修**(插件侧;迁内核时可一并搬) | +| **T2** | 把前稿的 `intercept.go` / `encstore.go` 从插件**迁到内核** | ✅ **已迁**(`internal/security/gate.go` + `store.go`;插件对安全机制无感知) | +| **T3** | 密钥从哪来 | ✅ **已定且已落地**:内嵌主密钥(构建期 `-ldflags`)+ 磁盘钥回退 + keyring(`internal/security/key.go`) | +| **T4** | 变量持久化与生命周期 | ✅ **已落地**:落盘加密持久化(`security/variables.json`),不自动淘汰 | +| **T9** | 不自动淘汰的规模上限 | ❌ **未做** → future-plan §1.5 | +| **T10** | 提交工具的泄露面(`${ENV}` / `@file:` 不明文路径) | ❌ **未做** → future-plan §1.5 | +| **T5** | 压缩对别名的影向 | ❌ **未做** → future-plan §1.5 | +| **T6** | 明文在内存中的擦除 | ❌ **未做**(Go 字符串不可变,需先定“努力擦除” vs “不承诺”)→ future-plan §1.5 | +| **T7** | 引用外部文件、内核只持路径 | ❌ **未做** → future-plan §1.5 | +| **T8** | 入向回填的安全边界(哪些工具允许触发回填) | ❌ **未做** → future-plan §1.5 | ### 5.1 ★ 配置落盘加密 [已完成 · 真机验证] diff --git a/docs/zh/resident-subagent-design.md b/docs/zh/resident-subagent-design.md index fb82f431..75893218 100644 --- a/docs/zh/resident-subagent-design.md +++ b/docs/zh/resident-subagent-design.md @@ -1,5 +1,16 @@ # 驻留式子 Agent 设计(轻量内核 · 两级记忆 · 父子中断) +> **状态:已实现并部署。** 驻留子、两级记忆、父子 L3/L4 中断、残余任务四态均已落地。 +> 本轮(2026-10-31)校订时补充的下沉事实: +> +> - 安全:驻留子会**继承父的安全运行时实例**,但**权限不继承** —— 父在 +> `SpawnResident` 时通过 `ResidentOptions.AllowedTools / DenyTools` +> **为这个子单独声明**主体策略,内核持有并执行;子销毁时撤销。 +> 工具面参数名:`allowed_tools` / `deny_tools`。 +> - 本文正文里的 `memory_recall` 等旧工具名已统一改为现行的 `stablemem_*`。 +> +> **未实现项**不在本文维护,见 [`future-plan.md`](./future-plan.md)。 +> > **前置**:本文建立在《输入调度器设计》(`docs/zh/input-scheduler-design.md`)之上。 > 那里已经落地了:两类别输入(中断 / 排队)、四级中断优先级、可抢占、 > 现场保存/恢复、中断栈(LIFO,结构上界 4 帧)、任务级回执、panic→L4。 @@ -502,7 +513,7 @@ | **A 记忆整理流水线** | `distill.go`(10):`archiveLoop` / `reviewLoop` / `mergeLoop` / `detectEntityMerge` / `reviewRelations` / `archiveColdDocs` | `Recall`, `ClearSentenceID`, `CleanupOrphanedSentences` | **root-only**(后台定时器) | | **B 记忆块 + 媒体桥** | `graphmedia.go`(18)、`medialoop.go`(4) | `PutMemoryBlocks`, `AddMemoryBlockEdge`, `BlocksForNode`, `PutDocumentNode`, `MemoryBlocks`, `MigrateLegacyMediaEntities`, `mediaContextFor*` | **root-only** | | **C 记忆整理工具** | `toolcall.go::executeMemoryTool`(8) | `Introspect`, `MergeEntities`, `DeleteEntity`, `Purge`, `Commit` | **root-only**(`memory_merge`/`memory_delete_entity`/`memory_block_merge`/`memory_purge`/`memory_edit`/`memory_stats`) | -| **D 共同面** | `graphmedia.go:114`(自动写入)、`toolcall.go:130`(`memory_recall`) | **只有 `Recall` + `Commit`** | 根与子都要 | +| **D 共同面** | `graphmedia.go:114`(自动写入)、`toolcall.go`(`stablemem_recall`) | **只有 `Recall` + `Commit`** | 根与子都要 | | **E 判空/状态** | 22 处 `if a.memory != nil` + `GetKernelStatus` + `buildToolDefs` | — | 既有关卡 | ⇒ **子 agent 需要的记忆面只有 `Recall` + `Commit`**;其余全是"整理记忆 / 记忆整理流水线" @@ -527,7 +538,7 @@ type GraphMemory interface { (`executeMemoryTool` 开头已经是 `if a.memory == nil { return "图记忆系统不可用" }`)。 - 唯一要拆的是**自动写入路径** `commitTriplesWithMedia`: 图部分 → `a.graph.Commit`;块/媒体部分 → 由 `a.memory != nil` 守卫。 -- 工具表:`memory_recall`(读)对子开放;整理类 +- 工具表:`stablemem_recall`(读)对子开放;整理类 (`memory_merge`/`memory_delete_entity`/`memory_block_merge`/`memory_purge`/`memory_edit`/`memory_stats`) **不进子的工具表**(而不是让它们进去再报"不可用")。 - 轻量 profile 另外不接线的装配:doc 记忆 / context 动态上下文(`pruneOnInput` 等)/