Files
HomeAgent/docs/zh/plan.md
root bc9ef15eb0 三层回退恢复机制(L0写前留档/L1恢复梯子/L2离线回滚)+ guard 父守护
- L0: files 插件写受保护系统路径(/etc 等)前自动留档,AbstractBeforeWrite 到 data/file_baseline
- L1: failback 受限 worker 执行恢复梯子 probe→还原DNS/proxy→还原LLM配置+ReloadFromConfig→probe,N轮有界
- L2: tracker changeset 持久化原文 blob,guard 离线 RollbackFromDisk 回滚 agentfs;SystemSnapshot 支撑
- guard 父守护: 心跳 IPC(PING/ACK unix socket, 文件心跳回退)、失败计数、退出码协议(42/43/44)、最后手段
- 发行版路径适配: system.protected_paths/network_paths 可注入,默认面向主流 Linux
- Windows 兼容: guard.go/failback.go 加 //go:build linux, guard_windows.go 提供 no-op 桩
- 修复: guard.yaml last_resort 键冲突、changeset Content 不落盘导致离线回滚丢原文

Build 全绿, vet 干净, system/recovery/ipc/tracker 单元测试全过
2026-08-05 16:00:08 +08:00

25 KiB
Raw Blame History

WebUI 布局与配置归位修复计划

一、背景

上一轮 SDK 接口化改造完成并部署后,用户指出三个问题:

  1. WebUI 窄屏布局损坏:顶部 <nav> 为桌面式横排(标题 + 6 tab + 连接指示器 + 语言 + 主题), 窄屏断点仅缩小字号不换行,body { overflow-x:hidden } 直接把溢出的 tab 裁掉不可点击。
  2. OpenClaw skills 目录被注册为核心配置core.skills.path"OpenClaw 技能存储目录")注册在核心 配置表(internal/config/registry.go),但全仓无任何读取方(死配置);实际生效路径是 clawhubadapter 自己的 skills_dir 配置(config_clawhubadapter 表 + core.daemon.data_dir/skills 兜底)。 技能目录是 clawhubadapter 适配加载的领域,不应属于核心配置。
  3. clawhubadapter 加载的微信插件成为独立配置项:设置页出现 channels.wechat.*(核心表)、 plugin.wechat.*config_wechat 表)、config_openclaw_weixin(空表)等多处微信配置, 全部为历史残留——当前代码零引用OC 技能的配置实际在其自身 ~/.openclaw/openclaw.json。 设置页会把核心表全部键 + 全部插件表当作配置组展示,导致残留以"独立配置项"形态出现。

二、修复计划

# 动作 位置 风险
A 窄屏导航修复:<768px 下 nav 横向滚动、h1 缩写、连接指示器简化body 溢出裁切改为 nav 内滚动 cmd/gui/renderer/style.css
B 删除 core.skills.path 核心配置注册set + RegisterDef 两处) internal/config/registry.go 无(无读取方)
C 备份后清理残留配置:channels.wechat.* 键、config_wechat / config_openclaw_weixin / config_openclaw 表(含微信 token先备份 生产库 /home/newqqagent/config.db 低(当前代码不读)

三、实施记录

步骤 Awebui 窄屏导航修复(已完成)

  • 修复对象为 webui HTTP 服务真正前端 internal/plugins/webui/dashboard.htmlgo:embed 内嵌, 登录后 / 返回104KBcmd/gui 是独立 electron 客户端,非 webui 一部分)。
  • <768px 断点:nav { overflow-x:auto; scrollbar-width:none; flex-wrap:nowrap } + ::-webkit-scrollbar { display:none } nav a { white-space:nowrap; flex-shrink:0 }nav h1 { font-size:0 }(保留 logo 图、隐藏文字,弥补窄屏空间); nav > div { flex-shrink:0 } 右侧语言/主题/退出按钮不压缩。
  • 顺带在 cmd/guielectron 客户端)同步了窄屏样式与消息来源徽标(app.js/style.css,客户端窗口缩放同样受益; 客户端需另行构建 electron 应用才生效)。
  • 验证:部署后 / 返回的 dashboard 含 scrollbar-width:none/font-size:0/::-webkit-scrollbar 规则。

步骤 B删除 core.skills.path 核心配置(已完成)

  • 删除 internal/config/registry.go 两处:set("core.skills.path", ...)SeedDefaultsreg(ConfigDef{Key:"core.skills.path", ...})(定义注册)。
  • 理由该键全仓无读取方grep 仅命中注册处),实际生效路径是 clawhubadapter 的 skills_dir config_clawhubadapter 表 + core.daemon.data_dir/skills 兜底)。技能目录属 clawhubadapter 适配领域。
  • 验证:grep -rn "skills.path" --include="*.go" 零命中;部署后设置页无 core.skills.path

步骤 C清理生产库残留配置已完成

  • 操作前 sqlite3 .backup /tmp/opencode/config.db.pre-clean.bak(含微信 token 数据)。
  • 删除:configchannels.wechat.* 3 键 + core.skills.path 键;DROP TABLE config_wechat / config_openclaw_weixin / config_openclaw三者均为历史残留当前代码零引用clawhubadapter 实际 使用 config_clawhubadapter 表OC 技能配置在其自身 ~/.openclaw/openclaw.json)。
  • 验证:设置页总键数 138→129无 wechat/weixin/skills.path 残留,plugin.clawhubadapter.skills_dir / simulator_dir 正常;服务 healthcheck ready、clawhubadapter "OC plugin manager started"。

SDK Stop 注册接口RegisterStopHandler计划

一、背景

2026-08-01 20:00 起生产 homeagent 进入崩溃循环(fatal error: thread exhaustion systemd 重启计数 61+)。排查定位为 SDK 示例插件 calendar(示例源码在 SDK 仓库 example/calendar,生产以 plugin.so 形态加载)三个缺陷叠加:

  1. 农历引擎 3 个 bugdaysInLunarYear 位循环 i > 0i > 0x8、缺闰月天数、 lunarToSolar 内层重复加闰月)→ lunarToSolar(2026,4,12) 返回 2062-11-16(偏移 36 年), 农历重复事件(lunar_yearly)的 next 被生成到遥远错误日期。
  2. cleanupPastEvents 保留过时重复事件 → 每 30s ticker 对已到点的重复事件再生成一份 next 事件从 7 个爆炸到 45612 个15MB events.json
  3. 无提醒投递保护15018 份同时到点的事件一次性 go sdk.InjectInterruptText(...) 投递 → interrupt 风暴 → goroutine/线程耗尽。

处置:修复农历引擎 3 处 + next 去重 + 清理过时重复事件,用新版 SDK 仓库 + 新版 plugindev 工具链 重建 calendar_linux_amd64.hmap,经 webui POST /api/v1/plugins(透明代理到 pluginmgr 安装接口) 重装,重启验证收敛(事件 4 个、next 正确生成 2027-05-17、0 崩溃)。

过程中暴露的能力缺口SDK 只有 Plugin 接口的 Name/Start/Stop没有 stop 注册接口 RegisterStopHandler/OnStop 均不存在SDK v0.7.2/v0.8.0/master 一致)。插件停止时只能在自己的 Stop() 里写清理逻辑SDK 层无法统一执行"停止时清理"回调calendar 的 Stop() { p.saveEvents() } 还会用陈旧内存把已清理的数据写回磁盘(曾导致删除的重复事件复活)。

二、计划

# 动作 位置 风险
1 公共 SDK PluginSDKRegisterStopHandler(fn func()) + RunStopHandlers()(幂等、后注册先执行),两处同步 third_party/homeagent-sdk/sdk/plugin.go、SDK 仓库 sdk/plugin.go 低(纯新增,内置 SDK 内嵌透传)
2 内核 Registry 保存每插件 SDK 引用(sdkRefsStopAll/ReloadOne/DisablePluginStop() 前执行 RunStopHandlers internal/plugin/registry.go 中(生命周期路径,需回归 reload/disable
3 工具链 plugindevz_bridge 模板 bridgeState 存 SDKStopPluginRunStopHandlers()plugin.Stop()init 脚手架模板加演示 SDK 仓库 tools/plugindev/templates.gotemplates/main.go.tmpl
4 内置示例插件演示(如 timerticker 停止改为 stop handler internal/plugins/timer/plugin.go
5 外部示例插件同步(example/calendarsaveEvents 改由 stop handler 执行,验证 z_bridge 链路;其余 example 加演示) SDK 仓库 example/*
6 文档同步SDK README 生命周期章节 + 主仓插件开发文档 SDK 仓库 README.md/README_EN.md

三、实施记录

  1. SDK 公共层(RegisterStopHandler + RunStopHandlers:后注册先执行、执行后清空幂等)已落地 third_party/homeagent-sdk/sdk/plugin.go,并同步到 SDK 仓库 /tmp/opencode/sdk-repo/sdk/plugin.go(两处一致)。
  2. 内核 Registryinternal/plugin/registry.go)新增 sdkRefs map[string]*sdk.PluginSDK + runStopHandlers loadOne 注册、StopAll/ReloadOne/DisablePluginStop() 前执行(共 4 处调用点)。
  3. 工具链 plugindevSDK 仓库linux tmplLinuxBridgego_stop_pluginRunStopHandlers()plg.Stop() windows tmplBridgebridgeStatesdk 字段、StopPlugin 同链路;tmplPluginGo + main.go.tmpl 脚手架加 RegisterStopHandler 演示。plugindev 重新编译通过GOPATH=/root/go
  4. 内置 timer 插件演示:close(p.stopCh) 移入 stop handlerStop()wg.Wait()
  5. 外部示例:example/calendarsaveEvents 改为 s.RegisterStopHandler(p.saveEvents) Stop() 删除写盘调用(持久化交由 stop handler避免陈旧内存复活已删事件
  6. 文档SDK 仓库 README.md/README_EN.md 生命周期章节补充 RegisterStopHandler 说明。
  7. 构建测试:主仓 go build ./... + go test ./internal/sdk/... ./internal/plugin/... 全绿; SDK 仓库 go build ./... + go test ./sdk/... 全绿。
  8. 生产部署验证:新 plugindev--no-bundle重建 calendar_linux_amd64.hmapmd5 084e97c0…strings 确认 go_stop_plugin → RunStopHandlers → saveEvents 编译进 plugin.sowebui API 删旧装新;重装新内核 homed含 sdkRefs/runStopHandlers两次重启事件稳定 3 个不复活、events.json mtime 与 stop 时刻吻合 saveEvents 经 stop handler 真实执行、0 次 thread exhaustion、服务 active。

clawhubadapter OpenClaw 通道插件兼容修复计划

一、背景

生产 core.llm.provider 已是 mock LLM 源(core.llm.sources.mocktestbase_url http://127.0.0.1:18080/v1、model mock-model、adapter openaimock LLM 服务常驻运行。 借助 mock 通道插件/tmp/opencode/mock-skills/mock-wechat/,完全复刻 openclaw-weixin 的 真实注册格式 register(api) → api.registerChannel({ plugin: ChannelPlugin }))放入生产 skills 目录 端到端复现,得出如下结论:

已验证可用链路manager 加载 mock 插件 → Go 端识别 ocplugin mock-wechat handled by manager → 注册工具 mock-wechat_read_mock_wechat_input/mock-wechat_mock_echoRegisterOutputChannel("mock-wechat") → mock 自推消息经 channel_input 通知 → [agent] interrupt from manager/mock-wechat → mock LLM 正常回复195ms

复现的核心缺陷(真实通道插件 wechat/dingding"根本不可用"的根因):

  1. 输出断链manager tools/call 通道分支只认 channelPlugin.outbound.sendText/sendMedia (旧格式),真实 ChannelPluginopenclaw-weixin 等)无 outbound → tools/call mock-wechat → error: "channel mock-wechat has no output handler"
  2. 生命周期静止manager mock api 从不调用 gateway.startAccount/stopAccount,也无 api.runtime/channelRuntime → 通道插件加载后永不启动(不登录、不轮询、不收消息)。
  3. 输入依赖错位:真实插件把消息经 channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher 推送manager 完全无此对象),而不是调 api.submitInput
  4. stdout 污染:插件 console.log 直接进 JSON-RPC 流Go 端 readLoop 跳过非 JSON 行,有丢通知风险。

wechat 通道的心跳机制openclaw-weixin/dist/index.js pollLoop448 行起):每账号一个常驻 pollLoop,循环 POST ilink/bot/getupdatesbody {get_updates_buf},超时 35s——长轮询即心跳 服务器收到 poll 请求即知通道在线,新消息随 poll 响应 push 回来;超时视为空响应继续轮询,真错误延时 5s 重试。与 gateway 生命周期强绑定

  • pollLoop 只由 gateway.startAccount(ctx) 启动;不被调用 → 心跳/收消息全断(服务器侧认为通道离线)。
  • startAccount 末尾 await new Promise(()=>{}) 永久挂起——OC gateway 靠它配合 health-monitor startAccount 退出 → 判定账号崩溃 → 重启账号。manager 调 startAccount 必须 fire-and-forget
  • 停靠 gateway.stopAccount(ctx)ctx.account.accountId 定位),停止时经 statusSinksctx.setStatus({running:false, connected:false, lastStopAt});启动即上报 ctx.getStatus()/ctx.setStatus({...running:true, connected:true, lastStartAt})——connected 是 gateway 判断账号存活的依据。
  • sendTypingilink/bot/sendtyping + typing_ticket是打字指示非心跳无需支持。

二、修复计划

# 动作 位置 风险
A manager 提供 gateway 生命周期桥channel 插件注册后自动 gateway.startAccount(ctx)fire-and-forget不等待挂起的 Promise构造完整 ctx {account, cfg, channelRuntime, getStatus, setStatus}Go 端 channel_stop 通知 → stopAccount internal/plugins/clawhubadapter/manager/main.js
B 实现 channelRuntime mockreply.dispatchReplyWithBufferedBlockDispatcherdeliver 回调 → channel_output 通知送 Go 端)、getPollsOC 通用通道轮询输入)、call 透传 同上
C tools/call 通道分支改造:无 outbound 的 ChannelPlugin 改走 channelRuntime 事件式发送agent 输出 → deliver不再报 "no output handler" 同上
D 状态上报透传:setStatuschannel_status 通知 → Go 端可查health-monitor 语义startAccount 保持挂起) 同上
E stdout 卫生:插件 console.log 重定向 stderr或 JSON-RPC 流感知封装),杜绝污染 同上
F Go 端:channel_output/channel_status 通知接入(事件分发),通道输出 handler 保持 sp.CallTool internal/plugins/clawhubadapter/registry.goplugin.go
G 端到端验证mock 通道插件 + mock LLM生产环境临时放入/移出 skills 目录)复跑全链路(登录启动→收消息→回复→出站→停止) 生产

三、实施记录

(逐步填写)

  1. manager/main.js — 通道运行时与生命周期桥(已完成,独立运行验证)

    • makeChannelRuntime(chName, ch)reply.dispatchReplyWithBufferedBlockDispatcher(opts) 提取 dispatcherOptions.deliver/typingCallbacksctx.AccountId 挂到 ch.deliverers,随后 notify('channel_input', {channel, payload:{content: BodyForAgent||Body, from, sessionKey, accountId, messageSid, chatType, raw}}) 入站;返回 dispatchersendNow/addToBuffer/sendBuffer/closeBufferchatPolls/getPolls 空转(防断连误判)、call 转发 channel_output 通知。
    • startChannels(name)channel 插件注册后自动枚举账号(config.listAccountIdsresolveAccount 缺省 ['default']),构造完整 ctx {account, cfg, channelRuntime, getStatus, setStatus} fire-and-forgetgateway.startAccount(真实插件会永久挂起,绝不等待);崩溃/状态变更经 channel_status 通知透传health-monitor 语义startAccount 不退出=账号存活)。
    • stopChannels(name):逐个账号 gateway.stopAccount;进程 SIGTERM/SIGINT 时统一执行优雅停靠。
    • tools/call 通道分支:保留 outbound旧格式→ 新增 deliver 事件式发送 deliverItem 按 accountId 取 deliver + typingCallbacks.onReplyStart/onCleanup 包裹)→ 无 deliver 时降级 channel_output 通知 → 兜底报错。不再出现 "has no output handler"。
    • stdout 卫生:全局 console.log 重定向 stderrJSON-RPC 流仅承载协议帧。
  2. mock 插件升级(/tmp/opencode/mock-skills/mock-wechat/index.js:完全复刻真实 weixin 行为—— gateway.startAccount 永久挂起 + setStatus 上报 + setInterval 心跳轮询 + 800ms 后经 dispatchReplyWithBufferedBlockDispatcher 推送入站deliver 本地记录发送);stopAccount 停轮询+状态置否。

  3. manager 独立运行验证(通过)channel_status 启动上报running=true connected=true tools/call mock-wechat{"status":"sent","via":"channelRuntime.deliver"},插件 deliver 收到 text="hello from agent" 且 typing onReplyStart/onCleanup 正确包裹;心跳 poll #1-4 常驻; SIGTERM → stopAccount called 退出码 0插件 console 输出全部走 stderr协议流零污染

  4. Go 端通知接入plugin.go translateAndRegister + registry.go 状态缓存)channel_statuschannelStatus map可查+ 日志;channel_output 降级事件日志。go build ./... 通过。

  5. 回复闭环修复(同步注入)channel_input → InjectInterruptText 的 InputEvent 不带 ResponseCh internal/agent/io/channel.go:276agent 回复在 emitResponseeventloop.go:384被静默丢弃。 改为 s.InjectInputSync(pluginName, channel, "text", payload)(内部 SDK 已有 4 参版本,返回 *OutputEvent)同步等待回复 → 提取 Payload["content"]sp.CallTool(channel, {payload, meta}) → manager tools/call → deliver → 插件发送 → 微信送达。公共 SDK IOInjector 同步补 InjectInputSync(source, channel, text) stringioAdapter 实现,供外部插件一致使用)。

  6. mock LLM 恒定文本化/opt/llm-mock/mock_server.py decide() 删除工具调用分支,一律回文本 "无论收到什么消息都通过微信插件发送"),保证每条入站消息回复必然走通道输出。

  7. 生产微信闭环验证(通过):用户微信发"你好..." → pollLoop 收到 → dispatchReply → InjectInputSync → mock LLM 回文本 → CallTool(wechat) → deliver → POST ilink/bot/sendmessagestatus=200 message_id=7489545365740590088,微信收到"mock已收到消息长度 324 字符。"

  8. 通用性审查(无硬编码)manager/plugin.go/registry.go 均无 weixin/wechat 特判,全部按 OC 规范 字段实现gateway/config/capabilities/channelRuntime/deliver/typingCallbacks。修正规范签名参数 约定:listAccountIds(cfg)resolveAccount(cfg, accountId) 正确传 cfg。

  9. 已知边界非硬编码架构性a) channelRuntime.getPolls/chatPolls 返回空 msgs——依赖 runtime 轮询输入的通用通道型插件收不到消息weixin/dingding 类自带 pollLoop 的通道不受影响); b) gatewayMethods 登录流程web.login.start/QR 扫码未实现CLI 有 stub通道凭 token 配置直连c) startAccount ctx 提供 account/channelRuntime/cfg/getStatus/setStatus 核心字段。

  10. 补充边界 a) getPolls/chatPolls 消息源(已完成)manager makeChannelRuntimechatPolls/getPolls 改为读 ch.pollQueues(按 accountId 队列poll 取走即消费);新增 channel/send RPCGo 端注入 → 队列 → 插件轮询取走Go 端 SendToChannel(channel, payload)

    • ChannelSender() 单例Start 时置位。验证mock-poll 插件(纯 chatPolls 轮询型)—— channel/send{"status":"queued"}[mock-poll] poll got msg → dispatchReply → channel_input 入站完整content/from/sessionKey/accountId/messageSid/chatType。 微信链路回归正常Polling started + channel_status running=true
  11. 边界 b) 登录流程核实(已解决,无需实现):真实登录机制是 SKILL 脚本旁路—— weixin-openclaw-login SKILL 的 scripts/get-login-url.jsilink 二维码 URL+ poll-login-status.py(轮询扫码状态)→ agent 经 exec 执行 → 拿 bot_token 写入 ~/.openclaw-weixin/account.json2026-07-28 17:31 创建token 有效)→ 插件启动 resolveAccountData 直读。不依赖 manager gatewayMethodsweb.login.start 等 OC gateway 协议 stub 不影响真实使用)。

  12. clawhubadapter 全量管理接口(已完成并验证):向 agent 暴露完整插件/通道管理面——

    • 新工具:clawhubadapter_plugin_info(类型/工具/关联通道详情)、plugin_reloadreloadPluginchannel_list(全部注册通道 + 实时状态)、channel_sendSendToChannel 投递)、 channel_start/channel_stopmanager channel/startchannel/stop RPC
    • manager 新增 RPCplugins/channelsregisteredChannels 摘要含 status/accountschannel/startfire-and-forget startChannels 恢复账号)、channel/stopstopAccount 按 channel 全停或按 accountId 单停,省略 channel 则全部停止)。
    • Go 侧 channelSummary(mgr) 合并 manager 注册信息与 channel_status 实时缓存(缓存优先)。
    • 端到端验证生产实例LLM 为本地 mock 源):微信发"你好通道..." → agent 执行 channel_list- wechat | plugin=openclaw-weixin type=text running=true connected=true accounts=[default] → 回复送达;发"注入..." → 执行 channel_send → "消息已投递到通道 wechat" → 回复送达。standalone manager 另验证 channel/startmock-wechat startAccount 重新执行、心跳恢复)与 channel/stopstopAccount called
  13. 插件删除回调 onRemove 全套(已完成并验证)RegisterOnRemoveHandler(仅卸载触发、 重载/禁用不触发,与 stop handler 互补——stop 每次停止都执行。registry.RemovePlugin 流程: stop handlers → Stop → runOnRemoveHandlers → 移除 plugins/sdkRefs/instances → UnregisterPluginTools → 配置清理。配置清理含两层:ConfigRegistry.RemovePlugin 删除 defs 中 plugin.<name>.* 配置项定义 + DROP config_<name> 插件配置表 含用户设置值ListPlugins 基于 config_% 表枚举故配置区完全消失);已用临时程序验证 before: defs=1/plugins=[timer] → after: defs=0/plugins=[]。示例盘点SDK 仓库 15 个): calendarevents.json、memomemos.json、rss订阅数据目录、weather缓存目录已加 filesfilesDir 为用户配置的访问根目录,默认 /、bili/qq用户下载资产、 ocr函数内 defer RemoveAll 自清理按语义不加plugindev 模板 main.go.tmpl + README.tmpl 含 onRemove 演示SDK README/README_EN 生命周期文档补"删除清理onRemove"小节。

  14. [2026-08-03] SDK 工具链/打包/重装 + dlclose 修复:

    • 工具链源码位置澄清SDK 仓完整内容位于 third_party/homeagent-sdk主仓 .gitignore 仅跟踪 sdk/meta/go.mod"两个远程仓库各取所需"/tmp/opencode/sdk-repo 为工作克隆,远程=gitcode
    • 工具链支持公共 IOInjector.InjectInputSyncCORE_INJECT_INPUT_SYNC=47C 桥 dispatchIO callString 回传回复文本);主仓 cabi loader case 47 用内部 4 参版 InjectInputSync 取 OutputEvent.Payload["content"] setResultmeta.go ID 47 + loader.go主仓 3f252ed
    • plugindev 构建环境GOMODCACHE=/root/go/pkg/modyaegi 缓存所在、GOPROXY=off。
    • 工具链打包 memoplg.json BOM 去除、name_en "Memo/Notes"→"Memo"toSnake 不处理斜杠, name_en 带 / 会使 hmap 名含子路径报错bundle=true 时走全平台交叉编译(本机无 darwin 工具链),打包用 build --no-bundle --target linux/amd64;产物 dist/memo_linux_amd64.hmap。
    • 重装pluginmgr HTTP API127.0.0.1:9876DELETE /plugins/memo 卸载(走内核 RemovePlugin
      • onRemove→ POST /plugins binary body 传 hmap返回 installed+checksum生效用 webui POST /api/v1/plugins/reloadX-API-Key生产 admin123
    • 关键 bugLinux dlopen 同路径复用旧句柄——RemovePlugin/ReloadOne 只 Stop 不 dlclose 插件二进制更新后重载仍执行旧代码(生产 memo 装新版仍注册旧 3 工具)。修复: cabiPlugin.Close()handle.Close+ Registry.closeDynamic 在卸载/重载时调用(主仓 649e312。 生产 homed-new7 验证memo 6 工具memo_todo_add/complete/list + memo_memo_create/list/delete 注册正常wechat 通道 running。
    • SDK 仓推送 b6e30f9工具链 47 + 重建 bin 二进制 + memo plg.json + sdk/plugin.go 注释精简)。
  15. [2026-08-03] 嵌套 git 恢复 + 工作区清理 + codegraph 索引修正:

    • 嵌套 git 恢复third_party/homeagent-sdk 原本是"单仓库双提交"(目录内嵌套 .git 推 gitcode homeagent-sdk 仓,主仓 git 跟踪 sdk/meta/go.mod 推 HomeAgent 仓),嵌套 .git 此前被误删; 已从 /tmp/opencode/sdk-repo 复制 .git 恢复remote=homeagent-sdk.gitHEAD=b6e30f9工作区干净 主仓 git 不受影响。以后 SDK 改动直接在 third_party 内 git commit+pushSDK 仓推送仍用带凭据 URL https://JianFeeeee:BCkb32xBuLxWD9P4MmU8ydZ5@gitcode.com/JianFeeeee/homeagent-sdk.git 不再经 /tmp 中转。
    • /tmp 清理:删除 /tmp/opencode/sdk-repo、hasdk-fresh、plugindev-new、plugindev_new、mock-run、 lunartestSDK 中转/临时目录);保留 homed-new*生产二进制备份、mock-skills 等非 SDK 内容。
    • replace 修正go.mod 第 17 行已是 ./third_party/homeagent-sdk正确package-linux.sh prepare_gomod 优先用 $PROJECT_ROOT/third_party/homeagent-sdk仅缺失时才 clone /tmp/homeagent-sdk 兜底(主仓 108faac
    • codegraph 索引修正:根目录 codegraph.jsonPROJECT_CONFIG_FILENAME配 includeIgnored+include: ["third_party/homeagent-sdk"]codegraph index 后 Files 179→233 third_party 文件 7→61tools/plugindev 与 sdk/plugin.goInjectInputSync 等)均可查询 (此前嵌套 SDK 仓被主仓 .gitignore 挡在索引外codegraph sync 不感知配置变更,需 index 全量重建。