旧 plan.md(1474 行)是「历史工单 + 路线图」混合档,混入两类错误: 1. 已实现却仍标 TODO(§13.12 L3 多模态、§11.4 Lua 快照锁、§12.2 setToolBlocks、 §13.11 主体 11 项) 2. 根本不属于本仓的跨项目工单(§13.9 在 llmsproxy 仓、§13.10 在 agentmail 仓) 本次回到源码逐项核实(grep 符号存在性 / go build / go test / go test -race), 新档只保留经证据确认的本仓待办,每条附 file:line 与可验证的验收条件。 - §一 结论速览(10 真待办 / 4 生产验证 / 8 已关闭 / 2 跨仓) - §二 真待办 P0-P2:RuntimeManager 分组 worker、reload 语义说谎、arena 无 Grow/Shrink、事件环零真实负载、工具超时措辞、handleAgentAction 501、 Lua 独立 ABI、Windows 无真机验证、homed heap 常驻、3 个测试缺陷 - §三 已关闭 8 项(附源码证据,防复活) - §四 生产部署后验证 4 项(代码已就绪) - §五 跨仓工单归属说明 - §六 明确「不做」的决定
18 KiB
HomeAgent 遗留问题清单
本文定位(2026-09-24 重写):本文只写仍未完成、且经源码核实确属本仓库的问题。 上一版 plan.md 是 1474 行的「历史工单 + 路线图」混合档,混入了大量已解决记录、 以及根本不属于本仓库的跨项目工单(见 §五),本次全部剔除。
每一条的判定方法:不是继承旧档的复选框,而是回到源码逐项核实 (
grep定符号存在性、go build ./...、go test ./...、go test -race)。 旧档里标记「未做」但代码已落地的项、以及引用别仓代码的项,统一进 §三/§五 并附证据。本文不写:已完成项的定位过程(那是 git log 和 commit message 的职责)、 无实测支撑的性能断言、任何凭据/生产路径。
一、结论速览
| 类别 | 数量 | 去向 |
|---|---|---|
| 真正待办(本仓库、代码层可动) | 10 项 | §二 |
| 生产部署后验证(代码已就绪,需现场跑) | 4 项 | §四 |
| 已关闭 / 已实现(旧档误标为 TODO) | 8 项 | §三 |
| 跨项目工单(不属本仓,已在别处解决或归档) | 2 项 | §五 |
当前健康度:go build ./... 通过;go test ./... 有 3 个 FAIL,全部是
测试自身缺陷(见 P2-10),非产品代码问题;go test -race 于 internal/lua、
internal/plugin/** 全绿。
二、真正待办
按「影响面 × 可验证性」排序。每条给出证据(file:line)与验收。
P0-1 §13.7 RuntimeManager + 分组 worker(架构演进,唯一大件)
现状:RuntimeManager / worker_group / WorkerGroup 在全仓库
(含 .go / .json / .md,排除 plan.md 自身)零引用 —— 该能力从未实现。
当前是一插件一子进程:internal/plugin/proc/plugin.go:134 的 Spawn
按插件各起一个进程。
为什么要做:每个外部插件一个进程 → N 个插件 = N 个常驻进程 + N 份 transport,进程数随插件线性涨。目标是一个 RuntimeManager + 少量 worker + 多插件共享 transport + 每插件独立 PluginContext(独立身份,共享管道)。
证据:
internal/plugin/proc/plugin.go:134(Spawn,逐插件起进程)- manifest 无
worker_group字段:third_party/homeagent-sdk/example/a2a/plugin.json的键集合为author/description/entry/name/name_en/name_zh/platforms/tags/version
子任务:
RuntimeManager类型:worker 池 + 调度Worker类型:一个进程,承载多个插件,共享RuntimeClientPluginContext类型:每插件独立身份(能力门、ownerID、工具命名空间) 挂在同一 transport 上- manifest 增
worker_group字段(缺省 = 全部归同一 worker,兼容迁移) - 高风险插件可声明独立
worker_group
验收:
- 缺省分组行为与现状逐字节等价(现有 proc 测试全绿,不改协议)
- 两个插件同 worker 时,各自
ReclaimOwner只回收自己的 arena 块 - 一插件崩溃不带走同 worker 的另一插件(或明确:带走,并写进文档)
- manifest 无
worker_group的旧插件可原样加载(向后兼容)
注:
.pi/subagents/missions/里有一份针对本项的只读架构评审产物, 可作为设计输入;其中结论尚未落地为代码。
P0-2 §11.6 reload 语义仍在说谎
现状:plugin_install 依旧回 reload_required,而不可热重载的插件
(Go plugin.Open 路径、DF_1_NODELETE)会被假装重载成功。
证据:
internal/plugins/pluginmgr/plugin.go:528 / :548 / :755—— 仍返回reload_required- 全仓库无
DF_1_NODELETE/NODELETE检测(grep为空) internal/plugin/registry.go:823-824注释已自认:Go plugin.Open 路径 (dynamicPlugin)不可卸载,跳过
子任务:
- ELF 检测
DF_1_NODELETE→ 标记插件「不可热重载」 (internal/plugin/dynamic_loader_unix.go) ReloadOne对这类插件返回「需重启 homed」,停止假装成功(registry.go:785)plugin_install返回restart_required替代误导性的reload_required(pluginmgr/plugin.go)
验收:
- 装一个
DF_1_NODELETE插件后,调用方拿到restart_required,不是reload_required ReloadOne在该插件上返回明确错误,且摘除旧注册面(工具/stage/output) 仍已完成,不留悬空闭包
P1-3 §13.2 arena 容量固定,无 Grow/Shrink
现状:内核独占的变长分配器已落地(first-fit + 邻块合并 + owner 校验), 但容量固定 4MB,用尽即调用失败,不再退回内联。
证据:
internal/plugin/proc/arena.go:99:const arenaDefaultCapacity = 4 * 1024 * 1024arena.go:263 / :283:超限直接报错
为什么当时不做:跨进程 remap 会让正在读的对端 SIGSEGV。所以这不是
「补个函数」,而是要先解决对端可见的地址稳定性。
可选路径(择一,需先定方案再动手):
- a) 预映射大虚拟区间(未触碰页不占物理内存),容量「逻辑无限」
- b) 段表 + 多段拼接:新块分配在新段,不必 remap 旧段
- c) 维持固定容量,但把超限错误做成可执行指引(告诉插件该怎么分片)
验收(按所选路径定):
- 超过当前 4MB 的单次 payload 仍能送达,或收到带指引的明确错误
- 任何情况下对端不 SIGSEGV
P1-4 §12.3 事件环:机制完成,零真实负载检验
现状:事件环(区内 segment + eventfd)与 events.subscribe 能力已完成,
但没有一个真实外部插件订阅 stage / tool_call 事件,dropped 计数
在长跑下的行为也无人观察。
证据:
internal/plugin/proc/capability.go:149-150(subscribe/unsubscribe 已授权)internal/plugin/proc/corehandler.go:46-58(EvtRingSubscriber接口)- 无对应 example 插件(
third_party/homeagent-sdk/example/无事件订阅样例)
子任务:
- 写一个订阅
stage/tool_call事件的 example 插件,跑真实负载 - 长跑观察
dropped是否异常增长(环 cap 溢出告警是否够用)
验收:
- example 插件能稳定收到事件并正确反序列化
- 压测下
dropped有上界且可观测
P1-5 §11.2 工具超时措辞误导 + browser 插件超时聚集
现状:内核侧工具超时文案仍写「已取消」,但进程内 cgo 插件根本取消不了 (OS 线程永久占用)——这是措辞与事实不符。
证据:
internal/agent/core/toolcall.go:42:fmt.Sprintf("工具 %s 执行超时(60秒),已取消", tc.Name)internal/plugin/proc/process.go:27 / :114 / :577:注释自认 cgo 路径 「超时后 OS 线程永久占用,现网已泄漏 26 次」
子任务:
- 措辞改「已放弃等待(插件仍在后台运行,其占用的线程无法回收)」
- 排查
browser插件为何频繁 60s 超时(旧档记录 22/26 次集中于此); 真取消能力依赖子进程模型(与 P0-1 相关)
验收:
- 超时文案不再暗示「已取消」
- browser 超时率下降到可解释水平,或给出根因
与 P0-1 的关系:把内部 cgo 插件也迁到子进程后,这条的严重性自动消除。
P1-6 §13.11 尾项:handleAgentAction 直接 501
现状:WebUI 的 agent 操作接口所有 action 一律返回 501 Not Implemented,
是明确的未接线桩。
证据:
internal/plugins/webui/handler_agents.go:239:writeJSON(w, http.StatusNotImplemented, ..."action not implemented by supervisor")
子任务:决定该端点该做什么——要么接线到 supervisor 的真实动作 (stop/restart/snapshot 等),要么从 UI 撤掉入口,不要留一个必然失败的按钮。
验收:
- UI 上不存在「点了必 501」的入口,或该入口真的能动作
说明:旧档 §13.11 的「11 项」其余各项已实现(见 §三.6),仅此一项是真缺口。
P2-7 §12.4 Lua 仍走独立 ABI(进程内解释器)
现状:Lua 插件在内核进程内跑 gopher-lua,不走 proc 通道,是三套 ABI 里唯一没收敛的。代价是:Lua 插件崩溃 = 内核崩溃,且无共享内存数据面。
证据:
internal/plugin/lua_plugin.go:947(makeStageHandler,进程内)internal/plugin/lua_plugin.go:128 / :329(register_stage直接注册到内核 SDK)
子任务:评估「Lua 走 proc 通道」的代价(解释器进程启动开销 vs 隔离收益), 据此决定收敛还是明确保留为独立 ABI 并写进文档。
验收:
- 有明确决策(收敛 / 永久保留),且文档与实际一致
P2-8 §11.5 / §12.5 Windows 只有交叉编译,无真机验证
现状:Windows 侧共享内存(CreateFileMappingW)、事件通知
(CreateEventW)、命名对象传递均已实现(桩已合回平台中立文件),但
从未在 Windows 真机端到端跑过。
证据:
internal/plugin/dynamic_proc.go:17-27注释:平台差异已全部封装, 桩已删除- 无 Windows CI / 真机记录
子任务:
- 找一台 Windows 机器跑端到端
- 特别验证命名对象的撞名防护(名字带 PID + 递增序号)
验收:
- Windows 下
homed能加载外部插件并完成工具调用往返 - 并发起多个插件时命名对象不撞
P2-9 §11.9 homed 主 heap 常驻未解释
现状:旧档记录 homed 主 heap 有约 2.36GB 常驻,来源未定位。
配置侧已有缓解手段(embedding_model_path 支持 #topN 限词向量数量),
但未确认现状是否仍存在。
证据:
internal/config/registry.go:856:embedding_model_path描述已写明#topN可「控制常驻内存」- 无
GOMEMLIMIT相关设置(grep为空)
子任务:
- 现场
pprof定位常驻来源(是否仍为双模型加载) - 若确认,评估是否加
GOMEMLIMIT或默认#topN
验收:
- 给出常驻内存的构成分解(哪块占多少)
- 有明确取舍结论(可接受 / 需优化 / 已优化)
P2-10 仓库卫生:三个真实的测试缺陷(3 FAIL)
现状:go test ./... 有 3 个 FAIL,根因都是测试自身缺陷,
不是「环境玄学」,也都可修:
- PTY 用例要求可打开的
/dev/ptmx:容器里节点存在但open被EACCES拒(实测PermissionError: [Errno 13]),而用例没有环境探测、 直接t.Fatal→ 应用t.Skip或 build tag 隔离,而不是硬 FAIL - 测试端口硬编码
127.0.0.1:9890→ 并行/残留实例即bind: address already in use,波及无关用例 TestRestoreFileFromBaseline写真实系统路径且忽略错误:internal/system/system_test.go:83的 target 是"/etc/RestoreFileFromBaseline.test.tmp",而用例内os.WriteFile(target, ...)不检查 err(os.WriteFile(target, []byte("v1"), 0644)); 在/etc不可写的环境(实测本机 root 也被拒)里,前面的写全部静默失败, 留档内容为空/不存在,到RestoreFileFromBaseline就报expected restore to happen。根因是测试写死真实路径 + 吞错误, 不是RestoreFileFromBaseline实现有问题。
证据:
internal/plugins/integration_test.go:262 / :318 / :376(PTY 三例)internal/plugins/remotedevice/plugin.go:27:const defaultAddr = "127.0.0.1:9890"(测试沿用固定端口)- 实测输出:
open /dev/ptmx: permission denied、listen tcp 127.0.0.1:9890: bind: address already in use
子任务:
- PTY 用例:无
/dev/ptmx时t.Skip(环境能力探测,不静默) - remotedevice 相关测试改用
:0让 OS 分配端口,或测试内随机端口 TestRestoreFileFromBaseline:改用可写的临时路径(并保留IsProtectedPath语义所需的显式前缀,用IsProtectedPathExplicit), 且每一步 WriteFile 都检查 err;顺带审计同类「写真实系统路径」的测试
验收:
- 在无 PTY 权限、
/etc不可写的环境里,go test ./...全绿 (受限用例显式 skip,不是静默通过) - 重复/并行跑不再端口冲突
三、已关闭 / 已实现(旧档误标,防复活)
逐条给出「旧档怎么说」与「源码实际怎样」,不要再往待办里加。
1. §13.12 L3 原生多模态 —— 已实现
- 旧档:标「未做」,要求 media 成为一等图节点 + contains/depicts/derived_from 原生边
- 实际:
internal/memory/graph.go:169起建memory_blocks(含modality/payload_digest/mime/vector/fingerprint/scene)+memory_block_edges(source_kind/target_kind/edge_type), 以及scenes/scene_features/scene_refs;internal/agent/core/graphmedia.go(310 行)实现migrateLegacyGraphMedia/attachBlocksToSentence/linkBlocksToDocument/commitTriplesWithMedia;graphmedia_test.go21 个测试 - 结论:关闭
2. §13.9 llmsproxy 上下文溢出感知 —— 不属本仓(见 §五.1)
3. §13.10 AgentMail 三个 bug —— 不属本仓(见 §五.2)
4. §11.4 Lua stage 快照缺读锁(DATA RACE)—— 已修
- 旧档:要求加
sc.RLock()/sc.RUnlock()包裹快照构造 - 实际:
internal/plugin/proc/shmcodec.go:42 / :326 / :425已在captureLocal/WriteDirty前后持读锁 - 实测:
go test -race ./internal/lua/... ./internal/plugin/...全绿 - 结论:关闭
5. §12.2 io.setToolBlocks 内核侧是桩 —— 已实现
- 旧档:要求实现内核侧 handler + 补 example
- 实际:
internal/plugin/proc/corehandler_inject.go:132实现MethodIOSetToolBlocks;模板putArena→blocks_ref;e2e_template_test.go:371用真实 SDK 模板验证 - 结论:关闭
6. §13.11 WebUI 修复清单 —— 主体已实现,仅剩 P1-6
逐项核实:Last-Event-ID 重放(handler_chat.go:710)、请求超时
(handler_chat.go:592 等 300s)、XSS 消毒(dashboard.js:252 DOMPurify)、
renderAll 增量(dashboard.js:34 / :452 / :1361 增量游标 + 流式增量)、
handleKnowledge 不再吞错(handler_memory.go:122 起逐分支返回错误)、
CSS/DesignSystem(dashboard.css:3 起 sakura/frost 令牌)、
GUI 重构(cmd/gui/renderer/app.js)—— 上述均已落地。
仅 handleAgentAction 501 是真缺口(已列 P1-6)。
7. §12.5 Windows 桩未删 / 旧档称「交叉编译通过」 —— 已收敛
- 实际:
internal/plugin/dynamic_proc.go已合为平台中立单文件, 平台差异封装在shmalloc_*/evtfd_*/shmpass_*/procattr_*(各带 构建标签);旧桩已删。真机验证仍缺(已列 P2-8)
8. §12.7 cmd/ohos/.../SettingsPage.ets 有未提交改动 —— 已提交
- 实际:
git status --short于工作区全清(含cmd/ohos/)
四、生产部署后验证(代码已就绪,本就无法在仓库内完成)
这些不是待开发项,是「必须落到生产实例才能确认」的验收。仓库内有
脚本 scripts/verify_deploy.sh [data_dir] 可一键检查前两条。
- §0.1 healthcheck 隔离:部署后
knowledge/、memory/graph.db、memory/documents/不再出现_hc_*残留 - 图记忆去重:
relations重复率归零,跑一周不新增重复 - §13.5 / §13.6 QQ 端到端:真实 QQ 消息注入与输出经共享内存通道正常
(小 payload 内联、大 payload 走
text_ref/frame) - §0.2 agentcli 不泛滥:QQ 消息在 agentcli 无自喂送风暴时能被正常响应
五、跨项目工单(不属本仓,旧档误并入)
旧 plan.md 把别仓的工单写成本仓 TODO,导致「查无此代码却挂着未完成」。 移出并说明归属:
1. §13.9「llmsproxy 上下文溢出感知」
- 旧档写:补
OVERFLOW_PATTERNS("Context window is full")、 AUTO 截断宽度80→160、go test ./internal/ai/... - 事实:本仓没有
internal/ai/;OVERFLOW_PATTERNS/clientUpstreamErr/OneLine(...,80)都在/home/program/llmsproxy(internal/gateway/chat.go) - 现状:该仓已把宽度改成 160(
chat.go:634注释「160 而不是 80」) - 归属:llmsproxy 仓,与本仓无关
2. §13.10「AgentMail 三个 bug」
- 旧档写:提示词修正 /
InReplyTo/relay_key ≤ 64 字节 - 事实:AgentMail 是独立仓
/home/program/agentmail(relay_key见server/internal/handler/permission.go:32 / :90 / :382) - 现状:
relay_key上限已实现为 160 字节并带测试 (permission.go:90、relay_test.go:55),in_reply_to语义见forward.go:225 - 归属:agentmail 仓
若这两仓也要纳入统一管理,应各自建 plan,不要塞进本仓文档。
六、明确「不做」的决定(避免反复挂账)
- §13.13 反向结果入共享内存:原设想把
doc.query/llm.chat的大结果 也搬进段。核实后:本仓根本不存在llm.chat(llm.*只映射 listSources/setSource/currentSource);唯一可能返回大结果的doc.query被CapDocMemory能力门挡着,且无任何外部插件使用。不做, 而不是留成永久 TODO。
重写时间:2026-09-24 · 核实方式:源码 grep / build / test / race
旧档备份于 /tmp/plan.md.bak-*(仅作对照,内容已判定过时)