37924b295b
feat(webui): 总览底部源码区改为独立的「开源许可」框(协议 + 全文 + 源码)
...
此前总览底部只在 KPI 卡里挂了一行小链接(.ov-foot),既看不出受什么许可
约束,也看不出 AGPL 网络服务场景下的义务。现在单独成一张卡:
开销许可
许可协议 AGPL-3.0-only → GNU 官方全文
源码仓库 <source_url> → 仓库
网络服务条款(§13):把修改后的版本作为网络服务对外提供时,
必须向使用者提供取得对应源码的途径。
内核侧(License 是新事实,不能只靠前端写死):
- internal/meta:新增 License(SPDX 标识)与 LicenseURL,都可 -ldflags 覆盖。
LicenseURL 默认指向 GNU 官方 AGPL-3.0 全文页 —— 与仓库托管方、分支名、
文件路径都无关,换仓库/换分支不会失效。
- internal/sdk/status.go 的 BuildStatus:新增 License / LicenseURL 两个
json 字段(additive,旧消费方忽略未知字段即可)。
❗ 注意 internal/sdk 不受公开接口冻结约束(docs/git-branching.md §六),
本次未触碰 third_party/homeagent-sdk/sdk/。
- internal/agent/core/status.go:从 meta 填充。
前端:
- 骨架里 .ov-foot 换成独立的 <div class="card" id="ov-legal">(放在 KPI 卡之后)。
- 网络条款那一段按许可标识是否含 AGPL 决定是否渲染,不硬写协议名。
- 内容对一次构建是常量,沿用 __html 比对,填一次后不再重建(不引入闪烁)。
验证(现网 1400x920,CDP 实测):
- /api/v1/kernel 的 build 现在带 license="AGPL-3.0-only"、
license_url="https://www.gnu.org/licenses/agpl-3.0.html "
- 卡片为真框:class=card、border 1px、radius 14px;总览结构 = rt-panel | card | ov-legal
- 两个链接均为真 <a>,target=_blank + rel=noopener noreferrer
- updateOverview() 再跑一次,卡片子节点身份不变(不重建、不闪)
- 回归:KPI 版本副行、阶段管道 5 节点/5 列/6 SVG、队列 5 行×5 格 均正常
- 无横向溢出;总览/侧栏/顶栏渲染文本无 emoji
- go vet 干净;agent/core、plugins/webui、plugins 全量测试通过
2026-09-14 17:48:54 +08:00
e4d69fa140
feat(webui): 总览改版 —— 阶段管道滑块 / per-agent 负载环 / 通道→agent 拓扑与光点
...
总览页此前是一堆数字与文字块,看不出「这一轮走到哪、谁忙、消息从哪进哪出」。
本次把运行态面板改成以图形为主:
- 阶段管道:七阶段滑块,由 SSE stage 事件驱动,当前阶段高亮、滑块滑过去;
一轮结束(after_output 或 2.5s 无事件)自动回到空闲,不做假动画。
- 队列与中断栈:沿用五条进度条(L1–L4 + 排队),中断栈补一条深度进度条。
- Agent 拓扑:改成「每 agent 一条横带」——左 inputch、中 agent 节点(圆环 = 负载)、
右 outputch,连线即路由;删掉旧的「归属框 + 单个内核盒」画法(看得出哪个子接了哪条输入)。
- 光点动画:channel_input(新增轻量 SSE 事件)沿 inputch→agent 连线跑;
agent_output 沿 agent→outputch 连线跑。用 SMIL animateMotion,不需要 rAF 循环。
- 负载:由该 agent **自己的**调度器积压(排队 / 四级中断 / 中断栈)按级别加权折算,
环形图展示。为此把驻留子的调度器积压透出到状态面(SDK 纯追加字段)。
后端:sdk.ResidentStatus / core.ResidentInfo 增加子 agent 调度器积压四项;
WebUI SSE 增加 channel_input 轻量事件(只带通道名与 agent id,不带正文)。
顺带收口对话区视觉(页签改分段控件、消息间距/气泡区分、输入区分隔线)。
2026-09-14 15:33:23 +08:00
43536ba326
fix(webui): 运行态面板不再闪、通道分配带归属(含驻留子)、改图形化
...
三个用户可见问题,逐个说明根因与改法。
1) 首页「一闪一闪」——运行态每 3s 轮询一次,renderRuntime 无条件重建
#rt-panel 的 innerHTML:数据没变也把整块 DOM(含各级条的 transition)
推倒重来。改法:缓存数据签名(**不含 uptime**——它每秒都变,带上等于没缓存),
签名相同直接 return,一个字节都不动。另:renderOverview 会整块重建
#rt-panel(面板本身是空的),所以那里必须让签名失效,否则空面板填不上。
2) 通道分配只显示内核/根 agent,看不见驻留子——根因是状态面只暴露了设备能力
(KernelStatus.Channels,来自 iom.ListChannels),而「这条输入归谁」是
ChannelRegistry 的属性(InputChannel.Owner/Capacity/Output),从未出过内核。
而登记表本来就是根 agent 与驻留子**共用同一份**,所以数据一直都在,只是没画。
改法:KernelStatus 新增 InputChannels(+ sdk.InputChannelInfo),
/api/v1/runtime 带出 input_channels;前端把它按 owner 分进「归属容器」,
驻留子即使一条 inputch 都没划到也照样出现在图里(否则"子存在但看不见"
与"子不存在"无法区分),并显示其 allowed_outputs / 轮次 / 上下文满标记。
3) 「这些信息明明可以图形化」——四级中断的登记/抢占由纯文本改成并排迷你条;
通道分配用归属容器 + 容量滑块(轨道/填充/把手/读数),并把回程通道、
注册插件做成胶囊标签。设备能力拓扑(原有)保留。
验证:go build/vet 干净;go test ./internal/agent/... ./internal/plugin/...
./internal/sdk/... ./cmd/... 全绿;node --check dashboard.js 语法通过。
TestRuntimeEndpoint 扩展为同时钉住 input_channels 的归属与「驻留子划走的那条」。
2026-09-14 10:55:45 +08:00
c321388a21
fix(scheduler): 安全点重新求值中断队列 + 抢占/背压计数修正 + 停机补终态
...
对照 docs/zh/input-scheduler-design.md 原文修四处(前两处是真缺陷,后两处是
观测面与设计承诺不一致),均配回归用例:
1. §4.3/§5.2「临界区结束后的第一个安全点重新求值」此前**没有实现**:
全仓唯一的武装点是 registerInterrupt,凡被拦成「入队」的中断只能等当前任务
自然结束。可达症状:WebUI 终止按钮连按两次,第二次落在 2s 抢占冷却窗内 →
入队 → 再也不会被求值。修:runTaskSteps 的安全点先 rearmPending()——
判据与 registerInterrupt 完全同一套(canPreempt + 冷却 + 临界区闸门)。
2. PreemptsByLevel 的语义是「进入 immediate 槽的次数」,但计数发生在
setImmediateLocked 之前:immediate 是单槽,同一安全点前到达的两条同级中断里
被降级的那条也被计成抢占。修:setImmediateLocked 只在真占住槽时返回 true,
计数随之为真;同时把「降级入队」的责任收归调用方,消除同一任务被入队两次的
隐患(实测该隐患会让中断任务执行两次、Executed 虚高)。
3. 状态面 Preempted 此前拿 Stats.Suspended 顶替,与 preempts_by_level 自相矛盾。
修:Preempted = Σ PreemptsByLevel[1..4]。
4. §4.4/Q4「满时阻塞发送方 + 计数并打日志」只做了阻塞:pumpInbox 满时直接返回,
一个字都不计。修:新增 Stats.Backpressure(+DTO 字段) 与只报一次的状态翻转日志;
同时显式处理 enqueue 返回值(静默丢弃会让同步调用方永久挂起)。
另:Stop() 停机前排空待办——给从未运行与已挂起的、带 ResponseCh 的任务补
skipped 终态,否则 cli/clawhubadapter 这类无超时同步注入方永久挂起(§7 I5、§11.3 X4)。
emitResponse 的 ResponseCh 写入改为非阻塞 + 告警,避免一行写错就卡死调度器 goroutine。
验证:go build/vet 干净;go test -count=1 ./internal/agent/... ./internal/plugin/...
./internal/sdk/... ./cmd/... 全绿;go test -race ./internal/agent/core/ ./internal/sdk/ 干净。
新增 scheduler_rearm_test.go 六个用例(冷却期满重新求值/同级降级不计数/Preempted 求和/
停机补终态/背压计数与翻转/pumpInbox 满计数)。
2026-09-14 10:39:25 +08:00
1ce3a917a5
feat(status+webui): 运行态图形化 —— 排队/四级中断队列/中断栈/驻留子/通道拓扑
...
需求:首页不该只有文字,要能一眼看出内核在忙什么——排队消息数、各级中断
排队与中断栈、驻留子 agent 数量;这些要向**内部 SDK 暴露接口**,供 WebUI 等应用
展示;通道划分也要能画出来。
## 一、内核状态面(internal/sdk,内部 SDK,不受公开 SDK 冻结约束)
* SchedulerStatus 补:
- interrupt_queues[5]:**四级中断队列各自的深度**(下标即级别 1..4,下标 0 恒 0,
这样 level 能直接当数组下标用)。此前只有 pending_interrupts 总数,
看不出"堵在 L1 还是 L4"——四级是抢占优先级,堵在哪级是完全不同的运行状态。
- immediate:刚抢占成功、下一个安全点立即运行的那个中断(此前完全不可见)。
- suspend_frames:中断栈的帧(栈底→栈顶,只给任务标识),depth 之外还能看出
"谁被谁打断"。
- interrupts_by_level / preempts_by_level:各级累计登记数与抢占成功数。
* KernelStatus 补 residents(驻留子运行时视图:状态/轮次/上下文已满/输入通道/允许输出)。
刻意**不带**每个驻留子的 inputch 登记明细——状态面会被反复轮询,明细会让
每次 /status 背上几十 KB;只给表大小,要明细走专门接口。
* ChannelInfo 补 direction(in/out/io)、description、tools、output_caps、caps_text。
此前 collectKernelStatus 只透传 Name/Type,把描述/工具/能力**全丢了**,
前端只能画出一排光秃秃的名字。
## 二、WebUI
* 新增只读 `/api/v1/runtime`:只回运行态三件事(scheduler/residents/channels),
实测 **1.0KB**(/kernel 是 30KB 级)——所以能 3 秒轮询做"实时"感,
而不必反复拉全量状态。
* 首页新增「运行态」面板(纯 CSS + 内联 SVG,前端仍无构建链):
- 四个数字块:排队任务 / 待处理中断 / 中断栈(深度/上限) / 驻留子 Agent,带占比条;
- 四级中断队列条形图:每级"深度 · 登记/抢占",四级语义**照抄内核**
(L4 内核独占 / L3 交互 / L2 消息 / L1 后台),不自己起名字;
- 中断栈层叠图(栈顶在上)+ ⚡ 立即运行项;
- 通道拓扑:输入通道 → 内核 → 输出通道,双向通道两侧都出现,能力以胶囊标签显示。
* 3 秒轮询只在总览页可见时才发请求;切回总览时 renderAll 会立刻补一次。
## 验证
* 新增 TestSchedulerStatusExposesLevelsAndStack(四级队列/立即项/栈帧/各级计数映射,
并断言"未使用的级别必须为 0"与"下标 0 恒 0")、TestChannelInfoCarriesTopology、
TestRuntimeEndpoint(形状 + 不携带 tools/plugins + 无状态源时 503)。
* go build / vet / agent+core+sdk+plugin+webui 全量测试绿。
* 真实浏览器实测(CDP 驱动,注入运行态样本走真实渲染路径):
数字块 [3, 5, 2/4, 2];四级条 L4=1/L3=2/L2=1/L1=1 与数据一致;
栈帧按"栈底→栈顶"渲染且标出栈顶;通道左右分列、io 通道两侧都出现。
2026-09-14 09:05:14 +08:00
86a702b3b0
fix(scheduler)!: 中断栈语义(嵌套抢占 LIFO),并修掉抢占空转
...
用户指正:存在**中断被中断**的场景,所以被打断的现场要进**中断栈**。
我此前把 suspendPool 明确写成“不是栈、按优先级取”,是错的。
改动:
- suspendPool 改名 suspendStack,恢复纪律改为**严格 LIFO(只比栈顶)**;
栈内不做优先级重排——嵌套抢占天然使栈自底向上基础级递增,
且“后被打断的先恢复”才是栈语义。取出即弹栈。
- 修掉一个由此暴露的真 bug(抢占空转):一次抢占生效后,被挂起的原任务
会因饥饿防护提升有效级,与抢占者同级;此时若按“先到先服务”,原任务
(入队更早)会被立刻选回,抢占者永远排不到 —— 抢占等于没发生。
现在**同级时 pendingInterrupts 优先于其它两类**,保证抢占必然生效。
- 状态 DTO:SuspendPool/suspend_pool → SuspendStack/suspend_stack
- 设计稿:§2 用语更正(它**就是**中断栈)、§4.1 选择函数(候选只含栈顶 +
pending 同级优先,并说明为何必需)、§6.2/§6.3/§9/§11 用例同步
测试新增 scheduler_stack_test.go 3 项:
- 嵌套 L1→L2→L3,恢复严格 LIFO(B 先于 A)
- 只比栈顶:人为构造“栈底 L3、栈顶 L2”,必须取栈顶(区分两种实现)
- 嵌套下的深度上限
验收:agent 全量 + -race;全仓 build/vet 通过
2026-09-13 06:30:23 +08:00
f11de37bf2
feat(scheduler): M7 可观测性 + 压力测试 + 端到端测试
...
设计依据 docs/zh/input-scheduler-design.md §11.5(O1/O2)、§11.6(E1/E2)。
- 可观测性:KernelStatus 新增 Scheduler 段(running/三集合深度/计数/
深度上限),由 GetKernelStatus 从 DumpScheduler 原子快照填充;
新增 events.EventScheduler,挂起/恢复各发一条(action/task/level)
- TaskKind.String() 便于日志与状态输出
- 新增 scheduler_e2e_test.go 3 项:
· 压力:200 排队输入 + 50 中断全部经真实 loop 执行,结束时三集合排空、
LLM 调用数精确等于输入数、无 Rejected
· 可观测性:挂起/恢复事件齐备,状态快照计数一致
· 端到端:完整启动 schedulerLoop+interceptLoop,经真实 channel 投递
L1 任务与 L4 中断,验证「LLM 流式中断 → 挂起 → 中断先完成 → 原任务恢复」
整条链路(LLM 调用数 = 丢弃1+中断1+恢复1+常规1)
- 验收:agent 全量 + -race;全仓 build/vet 通过
2026-09-13 00:45:28 +08:00
5aaae93367
feat(healthcheck): 内核状态快照报出内核版本号与 ONNX 模型启用状态
...
healthcheck_kernel 此前没有任何「ONNX 模型是否在用」的信息,只报「向量可用/不可用」,
分不清「统一多模态空间已加载」与「退回到词嵌入/TF-IDF 路径」;人格卡要求
「版本以运行时快照为准」,也缺一个可查字段(build.version 早就在,但没人知道)。
- KernelStatus 新增 onnx 段:enabled / provider / dim / fingerprint / modalities / reason。
判据取 Loaded()(provider 真正打开且元数据合法),**不是**「配置里写了 provider」
—— 后者在模型缺失 / 运行时缺失时也为真,报出去就是假绿。
- 未启用时 reason 给**具体原因**:未配置(说明会走回退路径)/ 打开失败的具体错误。
homed 把「配置的 provider 名」与「打开失败原因」透传给 Agent,仅供状态报告。
- ProviderAdapter 新增 Modalities()(可选能力,按接口断言取用,不改公开契约)。
- healthcheck_kernel 的工具描述同步说明它回答这两件事。
**顺带修一个真实 panic**:collectKernelStatus 的 knowledge 是**接口**参数,
(*knowledge.Store)(nil) 塞进接口后 `ks != nil` 仍为真 → 调 List() 直接 panic,
而 healthcheck_kernel 正是走这条路径(panic 发生在工具 goroutine 里)。
GetKernelStatus 改为先按具体指针判空、再赋给接口;并加刻画测试钉住这个成因
(一旦不再 panic 说明参数形状已变,守卫与该测试应同步删除)。
验证:单测 4 例(已启用 / 打开失败 / 未配置 / 未加载)+ 刻画测试;
隔离实例 E2E 7/7:正例 provider=chineseclip → enabled=true、dim=512、模态 2;
反例 provider=nonexistent → enabled=false 且 reason 含具体错误与 provider 名,
真实对话仍通。
2026-09-12 14:56:25 +08:00
dc80855540
feat(status): 暴露构建源码地址,WebUI 状态页给出 AGPL §13 的源码入口
...
选了 AGPL-3.0-only 之后,§13(Remote Network Interaction)就不只是声明问题:
把修改过的版本作为网络服务提供出去时,必须给使用者取得 Corresponding Source 的机会。
只在仓库里放 LICENSE 并不自动满足这一条——**使用者拿到的是服务,不是仓库**。
所以把它做进产品里,而不是写进文档就算完:
- `internal/meta.SourceURL`:本次构建对应的源码地址,默认指向本仓库。
注释里写明「修改后对外部署的分支必须改指向自己的仓库」,并给出 -ldflags 覆盖方式
(`-X .../internal/meta.SourceURL=<你的仓库>`),不需要改源码。
- `BuildStatus.SourceURL`(`json:"source_url,omitempty"`)+ 内核状态填充:
走已有的 `/api/v1/kernel` 构建身份链路,不新增端点。
- WebUI 状态页在「版本」行下渲染「源码 / Source」链接(URL 经 escHtml 转义后进属性;
`target="_blank" rel="noopener noreferrer"`)。
- 用 `omitempty`:未注入该值的旧构建不会在 JSON 里多出一个空字段。
验证:
- `internal/plugins/webui/dashboard.html` 的两个内联 script 块 `node --check` 均通过;
- 全仓库 `go build ./...` 通过,`internal/meta/meta.go` gofmt 干净
(`internal/agent/core/status.go` 有**既有**的 gofmt 差异,与本改动无关,按纪律未整体重排);
- 端到端:本地构建 homed → 冷启动 → `GET /api/v1/kernel` 的 `build.source_url`
实测为 `https://gitcode.com/JianFeeeee/HomeAgent `。
2026-09-12 09:41:39 +08:00
f91b27aedb
fix: WebUI 版本显示 + Windows 交叉编译 + 发布脚本三处回归
...
## WebUI 版本链路修复
问题:handler.go:949 报的是 sdk.SDKVersion,那条链最终指向
SDK 仓 meta.Version 的硬编码值,与 -ldflags 注入的内核版本
完全不相交。构建时间、commit hash 全部丢失。
dashboard.html 兜底值是 '0.1.0'——碰巧版本号相等时不显眼,
一旦不等就报错。
修复:
- KernelStatus 新增 BuildStatus 字段(Version/Commit/BuildTime/SDKCompatible/KernelName)
取自 internal/meta(-ldflags 注入点),与接口冻结无关(KernelStatus 只在 internal/sdk)
- /api/v1/status 改用 meta.Version,另加 sdk_version 字段暴露 SDK 版本
- dashboard.html 概览卡显示 HomeAgent vX.Y.Z + commit/日期/SDK 兼容版本,
去掉 || '0.1.0' 误导性兜底
## Windows 交叉编译修复
问题:internal/plugin/dynamic_proc_windows.go(Part 1 的桩,610e9d0)只定义了
tryLoadProc,但平台中立的 registry.go 还在调 loadProc / closeProcHost——这两个
函数只在 dynamic_proc_unix.go 里。Windows 下整个 homed 从 Part 1 起编译不过。
plan.md §12.5 声称「Windows 只做了交叉编译,无真机验证」——实际是连编译都没通过。
修复:dynamic_proc_unix.go / dynamic_proc_windows.go 合并为平台中立的
dynamic_proc.go(文件内无任何平台专属调用,proc 包内部通过
shmalloc_* / evtfd_* / shmpass_* / procattr_* 各自带构建标签处理差异)。
## 发布脚本三处回归修复
deploy/packaging/build.sh(从 package/build.sh 移到 deploy/packaging/ 后):
1. PROJECT_ROOT 少一层目录(.. → ../..),产物落进 deploy/build/ 而非根目录
2. initconfig 从未被构建,但 installer.nsi 和 package-linux.sh 都引用它
3. GO 兜底路径指向 /home/jianf/go1.26.5(陈旧硬编码)改为 command -v go
4. electron-builder --config package.json 校验整个文件导致 devDependencies 被判为 unknown
property,去掉 --config 让它从 build 键读配置
deploy/packaging/package-linux.sh:
1. build_go() 补上 initconfig 构建步骤
2. GO 兜底路径同步修复
知识库 3 条重写 + 1 条新增:
- homeagent_identity:v0.9.0 C ABI → v1.0.0 子进程
- homeagent_architecture:全篇重写为子进程架构(三面通信、Supervisor 台账、
崩溃自愈、权限三道闸)
- homeagent_recent_updates:在 v0.9.0 前插入 v1.0.0 主线摘要
- changelog_v1.0.0(新建):6 类缺陷消除、架构、实测、已知限制、迁移指引
2026-09-03 15:38:31 +08:00
c7ee45d6e1
refactor: remove core skill direct loading, skills owned by clawhubadapter only
...
- Drop skill.NewManager from homed bootstrap; skills dir no longer core-managed
- Remove GetInjectedPrompt system-prompt injection (skills are not first-class)
- Delete internal/skill package, SkillAPI, webui /api/v1/skills, status skills block
- ConfigRegistry: plugin config tables now created only via RegisterDef; arbitrary
scope Set/Get no longer implicitly creates config_<name> tables (fixes stray
config_today_task table from SKILL directory name being used as a scope)
2026-08-02 11:40:13 +08:00
dbbd73b930
refactor: migrate built-in plugins to SDK-only interface
...
- Six-phase plan complete: webui/cli/healthcheck/pluginmgr/clawhubadapter
now interact with the kernel exclusively via internal/sdk interfaces;
all Configure() calls and package-level global injection removed
- buildSDK in internal/plugin/registry.go is the single assembly point
- Add internal/sdk/events.go exporting event types/constants
- Fix ProviderManager cooldown sharing: LuaAdaptedProvider.Name() now
returns the source name instead of lua_<adapter>, so multiple sources
sharing an adapter (single script load via shared VM AdapterCache) no
longer share failure-cooldown state
- Verified: build/vet/tests green, deployed to homeagent.service with
full plugin capability testing via local OpenAI-compatible mock
2026-08-01 12:17:17 +08:00