|
|
cb32032f76
|
feat(scheduler)!: 中断/排队两类别模型 + 插件声明 L1-L3、L4 内核独占
用户澄清推翻了早期设计的三处前提,本提交按新模型重做调度核心(行为有意变化):
1) 类别由注入 API 决定,与通道名无关
- InjectInterrupt* -> TaskInterrupt(带级别,可被严格更高级中断打断)
- InjectText*/InjectInputSync*/内核自循环 -> TaskQueued(无级别,可被任何中断打断)
- 删除按通道名推断的 taskLevel():qq 走 InjectInterruptTextOpts,本就是中断
2) 级别只属于中断
- 插件在 InjectOptions.Priority 声明 L1-L3(空/非法降级 L1,声明 L4 夹到 L3)
- L4 内核独占:新增 raiseKernelInterrupt(panic / selfip);requestKernelPreempt 不夹取
- panic 现在产生一条带 kernel 标记的 L4 中断;L4 自身 panic 不再产生新 L4(防自我放大)
3) 选择结构:四容器固定次序,删除统一比较器
- immediate(抢占者立即运行)-> 中断队列 L4..L1 -> 栈顶(与队头比级别) -> 排队 FIFO
- 删除 pickTaskIndex/taskBefore 与“同级 pending 优先”补丁(根因是抢占者进了队列)
- 中断栈上界改为结构推论 = 4(= 中断级数);删除“超限转 pendingInterrupts”降级
公开 SDK(feature 分支有意新增,纯追加):InjectOptions.Priority + PriorityL1/2/3;
内核 io / proc 桥 / 插件模板同步透传。
设计稿 §2/§3/§4.1/§6.3/§9/§11/§12/§13/§15 按新模型重写。
验收:go build/vet 干净;go test ./... 37 包 ok 0 FAIL;-race 全绿;
e2e(抢占-挂起-恢复)+ 压力(200 排队 + 50 中断,L1/L2/L3 轮转)通过。
|
2026-09-13 07:00:51 +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 |
|
|
|
d4764e3682
|
fix(scheduler)!: D1 更正为「中断从上一个任务之前的完整状态开始」,并实现现场合回
用户明确语义(我此前对 D1 的解析就是错的——当时回答里的“A”指的是 git 选项,
D1 实际要的是方案 B):
中断打断时,上个任务到达以来的所有上下文现场被保护(含 toolcall),
然后中断在「上个任务前的那个完整状态」上开始运行;
中断结束后再把被挂起的任务与其上下文现场加载回中断任务之上,并继续运行。
实现:
- 删除 SeedMsgs 与 D1=A 的“只读前缀”机制:中断任务不再继承被打断任务的任何内容,
它就是普通新任务,正常走完整 prepare(system prompt + timeline + 自己的输入)
- TaskFrame 新增 PrefixLen(基础前缀长度)与 InputBlocks;
stepPrepare 在 buildMessages 之后记录 PrefixLen
- 新增 rebaseFramePrefix:恢复时重建基础前缀(中断已提交进 a.context,
重建的 timeline 含中断效果=“加载回中断之上”),再把本任务自己的尾部
(Stage 上下文 + 工具轮产物 + 占位)接回;并补回 IsInterrupt 标记与多模态块
- resumeTask 在 runTaskSteps 之前调用 rebaseFramePrefix
- 设计稿 §5.3 改写为「已定:D1=B」并写明实现对应;§6.2 补“重建前缀→接回尾部”;
§12 的 D1 行更新
测试:
- TestPreempt_HigherPreemptsAndResumes 改为断言「中断不继承、恢复后看得见中断内容」
- 新增 TestPreempt_ResumeRebaseRestoresTailDecorations(前缀重建后尾部装饰补回)
- 原 TestPreempt_SeedPathDoesNotLeakInterruptFlag 随之删除(机制已不存在)
验收:agent 全量 + -race;全仓 build/vet 通过
|
2026-09-13 06:24:20 +08:00 |
|
|
|
8372f5bd8f
|
fix(scheduler)!: 撤掉“优先级=可配置策略表”的错误设计,回归内核内部属性
用户指正:**优先级是内核内部属性**,不是配置项,更不该由插件声明。
我此前把它建模成“策略表 + 字符串解析”,甚至准备接配置中心
(core.agent.priority.<channel>)——方向性错误,故整体撤销。
撤销:
- 删除 ParseLevel(字符串解析只服务于“外部可配”这个错误前提)
- 删除 AgentConfig.PriorityLookup / Agent.priorityLookup 及 taskLevel 中的查表分支;
taskLevel 回归为纯内核内部规则(cli/webui/http→L3,system/_consolidation_→L1,
其余 L1),注释明确“不对外暴露、不做运维可调项”
- 设计稿 §3.2 改写为“内核内部属性,不做成配置项”,并删除 §15 里
“ChannelDef.Priority / InjectOptions.Priority 进公开 SDK”这一方向(同属外化)
- 未触碰配置中心(registry.go/main.go 的优先级配置一行未加)
同时落地 D6(与本撤销无关、此前遗漏的承诺):
- AgentConfig.MaxToolTurns + runTaskSteps 在发起新一轮 LLM 前按 f.Turn 收尾;
0 = 不限;cmd/homed/main.go 从既有 core.agent.max_tool_turns 取值
- 新增 task_test.go 2 项:上限 3 时恰好跑 3 批工具/3 次 LLM 并收尾;
0 = 不限(跑完脚本)
验收:agent 全量 + -race;全仓 build/vet 通过
|
2026-09-13 06:17:49 +08:00 |
|
|
|
98d67559d1
|
fix(scheduler): 补齐 M3 与设计稿的两处语义偏离(发现即修)
两处都不是风格差异,而是真的偏离设计语义(其一为回归),
已按“先写判据确认失败、再修”的方式处理,判据保留为回归测试。
1. 空闲时到达的中断永远不会被处理(设计 §5.1 ③ 未落地)
调度器空闲时只阻塞在 select{InputChan, selfInputCh, ctx.Done},
而 pendingInterrupts 不是 channel——interceptLoop 把中断入队后
没有任何东西唤醒调度器,中断要等“下一条输入”才被看到。
修复:调度器加 wake channel,enqueueInterrupt 非阻塞 signalWake,
空闲分支增加 wake 分支。
2. 临界区内只“不让位”却仍被“取消”(设计 §4.3/§5.2)
requestPreempt 不判临界区,interceptLoop 照常 cancelLLM,
于是正在流式的记忆整理被中断,stepLLM 以 error 提前结束——
整理任务被砍掉一半,而设计要求的是“请求排队等它结束”。
修复:scheduler 增加原子 critical 标志(帧仍只由调度器读写),
runInputTask 在 prepare 后设置、结束(含挂起)时清除;
requestPreempt 在临界区内不 arm、不取消,中断只入队。
- 新增 scheduler_regression_test.go 2 项(先失败后通过)
- 验收:agent 全量 + -race;全仓 vet 通过
|
2026-09-13 06:10:44 +08:00 |
|
|
|
6bd3313a22
|
docs(scheduler): 回写 M1–M7 实现状态与 5 处实现期偏差
- 里程碑表补提交号与验收结果
- 记下与设计稿的偏差:M3 拆分、a.mu 移除、interceptCh 删除、
M0 伪时钟未做(用阻塞 provider 替代)、工具执行临界区由结构保证
|
2026-09-13 00:48:20 +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 |
|
|
|
4e4e0ad656
|
feat(scheduler): M6 任务级回执 + 断链点统一为终态事件
设计依据 docs/zh/input-scheduler-design.md §7(I5)、§11.3(X1/X2/X4)。
- 新增 emitSkippedReply:跳过路径(去重命中、空输入)给同步调用方一个
skipped 终态,但**不发 agent_output 事件**(避免 WebUI 聊天记录凭空多出
空消息)。修复前 cli/clawhubadapter 这类无超时的同步注入在去重命中时永久挂起
- 回执按任务归属:中断任务的回执只写自己的 ResponseCh,绝不误投给被挂起的等待者;
被挂起任务恢复并结束后才拿到自己的回执
- 新增 task_terminal_test.go 3 项:X1 回执不误投、X2 空输入有 skipped 终态、
X4 无超时同步调用方 1s 内拿到终态(回归判据)
- 更新 M3a 去重用例:由「不得回执」改为「必须有 skipped 终态」
- 验收:agent 全量 + -race;全仓 build/vet 通过
|
2026-09-13 00:39:56 +08:00 |
|
|
|
a971fc8877
|
feat(scheduler): M5 饥饿防护(抢占计数提升有效级 + 抢占冷却)
设计依据 docs/zh/input-scheduler-design.md §9、§11.5(G1/G2)。
- Task 增加 PreemptCount / LastPreemptAt;effectiveLevel(t) =
min(L4, Level + min(PreemptCount, 2)):被抢占越多越“值钱”,
逐步追上抢占它的流,但封顶 L4 因而抢不过真正的紧急输入
- 选择函数 taskBefore 改用有效级;requestPreempt 与 preemptGrantedFor
同样以有效级比较
- 抢占冷却 preemptCooldown=2s:刚被抢占的任务期内不再被抢占,
避免同一任务被反复打断到永不完结
- 新增 scheduler_starvation_test.go 4 项:提升与封顶、冷却期内不得再抢占、
提升后同级不得抢占而更高可、选择函数确实用有效级
- 验收:agent 全量 + -race;全仓 build/vet 通过
|
2026-09-13 00:38:30 +08:00 |
|
|
|
7565248f61
|
feat(scheduler): M4 临界区显式化 + 清掉被 pendingInterrupts 取代的 interceptCh
设计依据 docs/zh/input-scheduler-design.md §4.3、§11.1(P5/P6)。
- 临界区语义显式化:让位检查**只在 step 之间**做,执行中的 step
(工具 RPC / ONNX / CAS 落盘)天然不可抢占;_consolidation_ 整任务
经 inCriticalSection() 判为不可抢占(它直接改图库)
- 删除 interceptCh 与 drainInterrupts:M3b 起中断一律走 pendingInterrupts,
旧的「同行注入 + 三处 drain + 批次放弃」已无写入者,属死代码
- 新增 scheduler_critical_test.go 3 项:
P5/P6 工具执行中 arm 了让位信号也不得挂起、必须等工具返回后的安全点;
_consolidation_ 判为临界区;无抢占时同批工具必须全部执行(新语义回归)
- 验收:agent 全量 + -race;全仓 build/vet 通过
|
2026-09-13 00:34:39 +08:00 |
|
|
|
c69a1f11af
|
feat(scheduler): M3a+M3b 任务生命周期重构 + 四级优先级抢占
设计依据 docs/zh/input-scheduler-design.md §3–§8、§14。
M3a(行为等价的所有权重构):
- processInput 拆为 prepareInputTask / runTaskSteps / finishInputTask,
帧覆盖 prepare→step…→finish;提交与回执只在 finish 段发生一次,
为安全点挂起做准备(挂起不重复提交)
- process() 不再持 a.mu(挂起不能持锁),a.mu 字段随之移除
- TaskFrame 增加任务层现场(Evt/CleanInput/IsInterrupt/StartedAt/Terminal/
Level/SeedMsgs)与 taskTerminal / outcomeSuspended
- 新增 task_lifecycle_test.go 5 项:正常恰好一次终态、去重 skipped、
on_input 短路、错误终态、consolidation 路由
M3b(优先级与抢占):
- interceptLoop 重写:只做「收中断 → 定级 → requestPreempt → 必要时取消
LLM」,绝不触碰帧(不变量 I2);三条降级路径与 interceptCh 兜底退场,
改为统一的 pendingInterrupts
- scheduler:pendingInterrupts / suspendPool / 让位信号,nextRef 在三集合上
按统一排序键取值;深度上限 4(canSuspend 在安全点拦下)
- 抢占判据 incoming.level > running.level;相等与更低只入队
- 安全点只在 step 之间;执行中的 step(工具 RPC/ONNX/CAS)天然不可抢占;
_consolidation_ 整任务视为临界区
- 恢复走 resumeTask:从 frame.Step 继续,不重跑 prepare
- D1=A:suspend 把被打断任务的只读前缀交给抢占比它的中断任务(SeedMsgs)
- 新增 scheduler_preempt_test.go 5 项:抢占-挂起-恢复(含 R1/R5)、同级更低
不抢占、深度上限、空闲中断不丢、seed 路径不污染标志位
验收:agent 全量 + -race 通过;全仓 build/vet 通过
|
2026-09-13 00:25:52 +08:00 |
|
|
|
716ee46471
|
docs(scheduler): M3 拆为 M3a(所有权重构)/M3b(抢占语义)
真正的中途挂起要求帧跨越 prepare→step…→finish 全生命周期;若只让
process() 可挂起,processInput 会在挂起返回后继续 context.Append 与
emitResponse,造成重复提交。故 M3 分为两步:M3a 行为等价的所有权重构
(含移除 process() 整轮持有的 a.mu),M3b 再引入优先级与抢占。
|
2026-09-12 23:51:13 +08:00 |
|
|
|
7082a50365
|
feat(scheduler): M2 调度器骨架(就绪队列 + 选择函数 + 快照 + 任务级 panic 隔离)
设计依据 docs/zh/input-scheduler-design.md §14 M2。
- 新增 scheduler.go:四级 Level 常量(默认 L1,显式才是特权)、
Task/TaskKind、scheduler(有界队列/running/统计)、纯函数 pickTaskIndex
(排序键 -Level → EnqueuedAt → ID)、schedulerLoop/pumpInbox/executeTask、
DumpScheduler 原子快照
- eventLoop 退场,职责由 schedulerLoop 承担;M2 全部任务为 L1,
因而行为等价于原先的 channel FIFO
- 每任务 panic 隔离(不变量 I6):panic 只失败该任务,调度器存活,
取代原先「重启整个循环」
- Start() 改起 schedulerLoop;interceptLoop 暂不动(M3 重写)
- 新增 scheduler_test.go:Q1 排序 4 组、Q4 背压、生命周期、K1 panic 隔离、
O1 快照一致性、Level 取值契约
- 验收:agent 全量 + -race 通过
|
2026-09-12 23:49:55 +08:00 |
|
|
|
9a588780b9
|
refactor(scheduler): M1 把 process() 拆成 step 状态机 + TaskFrame(行为等价)
设计依据 docs/zh/input-scheduler-design.md §14 M1。
- 新增 task.go:Step 游标、TaskFrame,以及 stepPrepare/stepLLM/
stepToolBegin/stepToolExec/stepToolAfter/stepTurnEnd 六个 step;
原函数内嵌的 provider 回退/重试抽为 resolveProviders + callLLMWithFallback
- process() 改为驱动状态机的薄壳:签名不变,调用方(processInput/
processConsolidation/测试)零改动
- StepToolExec 显式标注为临界区:工具副作用不可回滚,执行中不是安全点
- 步数上限护栏:转移缺失时以错误退出而非死循环
- 新增 task_test.go:R3(工具往返结果与配对正确)、X3(多轮必然终止)、
批中途中断必须放弃剩余工具、未知 step 必须失败退出
- 验收:既有 agent 全量测试通过;-race 通过(TMPDIR 指向真实磁盘,
/tmp tmpfs 已 98% 满会导致链接失败,与代码无关)
|
2026-09-12 23:47:12 +08:00 |
|
|
|
06edff1af2
|
docs(scheduler): 输入调度器设计稿(四级优先级/可抢占/现场保存 + 测试点与里程碑)
背景:现状排队与中断两条语义建立在串行 eventLoop 上,存在队头阻塞、
中断三条隐式降级路径、回执无任务归属、断链点静默、背压策略分裂、
假取消、不可观测七个已确认问题。
设计:四级内核预定义优先级(严格大于才抢占)、显式临界区、
单调度线程 + 单中断线程、step 化任务帧与安全点、suspendPool/pendingInterrupts/
readyQueue 三集合统一选择函数、任务级 responseCh。
含 11 组测试点(优先级/恢复/回执/队列/深度饥饿并发/端到端),
测试方式与预期结果逐条写明;M0–M7 里程碑逐步实现。
公开 SDK 在 v1 保持冻结(diff 必须为 0)。
|
2026-09-12 23:39:10 +08:00 |
|
|
|
163c5f70b3
|
chore(sdk-mirror): 同步 vendored SDK 到 SDK main(browser_search 现代 Bing 版式解析修复)
镜像追平 SDK main(`ebd700e`):
- browser 示例:browser_search 三处根因修复(www.bing.com 302 → cn.bing.com;
标题取 h2 > a 而非块内第一个 <a>;摘要兼容 p.b_lineclamp*)并解开 /ck/a 跳转包装,
解析不出结果时显式报错;附真实响应夹具与 5 项单测;版本 2.4.0 → 2.4.1。
注:SDK 仓新增的 example/deepsearch(联网检索 + SearXNG 生命周期托管)与
example/vikunja(任务管理)按本仓策略(.gitignore 忽略 example/ 下未跟踪文件)不入镜像。
|
2026-09-12 23:39:10 +08:00 |
|
|
|
ca5e62f775
|
docs(plan): 记忆与多模态目标纠正(媒体为一等节点、同一指纹空间、音频 unsupported)
把计划里过时的描述式索引路线改正:媒体/媒体块是 L3 一等节点与原生边,
multimodal doc/context 的向量与裁剪须与 text/媒体块在同一指纹空间共同参与;
音频在该模型下明确 unsupported(不做文本描述式索引)。
|
2026-09-12 20:21:01 +08:00 |
|
|
|
07e08352b9
|
feat(waiter,devicebridge): 设备桥连接带授权态,命令处理器类型统一
- `runDeviceBridgeLoop`/`connectDeviceBridge` 增加 `authorized` 入参(未授权不再尝试建立桥连);
- `Bridge.OnCmd` 的处理器类型改为具名 `BridgeCmdHandler`,与 waiter 侧签名对齐。
|
2026-09-12 20:21:01 +08:00 |
|
|
|
8f40b91dea
|
feat(ohos): 设备桥会话管理、状态/设备信息载荷与能力路由完善
- 新增 `DeviceBridgeSession.ets`:会话生命周期(创建/复用/回收)与 TTS 会话显式 shutdown;
- `BridgeCaps`/`BridgeRouter` 与内核实际支持的本机命令保持一一对应,补齐 status/deviceinfo;
- 页面与状态存储调整(DevicePage/Index/SettingsPage/StatusStore/SubPage)、新增 ScreensuePage;
- module.json5 与 string.json 同步(新增页面与文案)。
|
2026-09-12 20:21:01 +08:00 |
|
|
|
d1662e77cf
|
chore(sdk-mirror): 同步 vendored SDK 到 SDK main(qq 插件重写、browser 会话超时必填、路牌 1.3.0)
核心仓 `third_party/homeagent-sdk` 是 SDK 仓的 vendored 副本,内容以 SDK 仓 main 为准
(本仓 `.gitignore` 忽略 example/ 下的未跟踪文件,已跟踪的镜像文件用 `git add -u` 更新)。
本次把镜像追平到 SDK main(`8c10b7e`):
- qq 插件重写(+498 行):`output_send` 回声/自激路径的根因修复与去重;
- browser 示例:交互式会话 `timeout` 由默认 10m 改为**必填**(附参数校验与测试);
- `meta.Version` 1.2.0 → **1.3.0**(SDK v1.2.0 已定版,SDK main 按纪律推进到下一个中版本)。
|
2026-09-12 20:21:01 +08:00 |
|
|
|
dc4982464d
|
fix(packaging): SHA256SUMS 只列本批产物,且用平铺名(此前会带上历史版本、且与附件名不符)
v1.2.2 出包时发现:dist/ 跨多次构建累积,而清单用 `find $DIST_DIR` 全目录扫,
于是 SHA256SUMS 里混进了 1.2.0/1.2.1 的包名——用户从发布页下载这份清单后
`sha256sum -c` 必然报「文件缺失」(那些包并不在本页)。
两处一起修:
- 按本批 `$PKG_VERSION` 过滤(只列这次真正打出来的产物);
- 名字用 basename(平铺名),与发布页附件名一致;哈希取真实路径(此前若直接
对 basename 求哈希会找不到文件——就在 `deb/`、`tar/` 子目录里)。
验证:对含 1.2.0/1.2.1/1.2.2 的 dist/ 跑新逻辑 → 4 条(旧逻辑 12 条);
并拿发布页真下载的 SHA256SUMS 逐条核对四个产物的实际哈希 → 全部一致。
|
2026-09-12 19:18:26 +08:00 |
|
|
|
fe883d5362
|
test(plugins): 真实插件产物先校验平台再 exec,错误平台给可读提示而不是 exec format error
排查知识库改动是否引入回归时,internal/plugins 6 个测试全红、报
"fork/exec .../plugin.bin: exec format error"。查了半小时才发现与代码无关:
早前验证跨平台示例构建时,最后一次构建(darwin/arm64)把
`example/*/build/plugin.bin` 覆盖成了 Mach-O arm64,而 realPluginBinary
只按文件名找候选、**不校验平台**,于是拿 macOS 产物在本机 exec。
两个陷阱一起堵:
- 校验魔数(ELF / Mach-O / PE),平台不符则 **SKIP 并给出可直接粘贴的重建命令**
(`hmapdev build --target <host> --no-bundle`),不再用误导性的 exec format error;
- 明确写出「产物缺失即 skip」的语义,避免"全绿其实什么都没验"
(本次对照实验里,改动前的 worktree 因无产物而全绿,看着像通过)。
反向验证:把 weather 产物换成 PE 假头 → 相关测试转为 SKIP 且提示平台与重建命令 ✓。
|
2026-09-12 18:38:24 +08:00 |
|
|
|
5fbd6514c2
|
fix(knowledge): 知识库检索改为「稠密 + 词法」两路融合(真实 KB 自检索 MRR 0.271→0.376)
追「实例看起来没更新」时发现知识库检索本身也不可信,先把病因查清再动手:
- **两段式召回不是瓶颈**:Store 的结果与全量暴力 cosine 完全一致;
- **真因是向量没有区分度**:词向量取平均后各向异性明显,真实 KB(33 条)上自检索
top-1 只有 15%、前两名平均只差 0.013,排序基本是噪声;
- 且全为停用词的查询会得到**空向量**("最近更新"),直接搜不出任何东西。
先在真实数据上把候选方案量了一遍(用自检索 top-1 / MRR)再动手:IDF 维度加权零收益、
去均值反而更差,**都不做**;唯一有收益的是与词法路(TF-IDF)融合。
改动:
- `Store` 增设词法路索引,`Search` 融合两路:各自按**查询内最大值**归一化后加权。
权重 0.5 由权重扫描定:1.0(旧行为)MRR 0.271 / 0.8→0.354 / 0.7→0.358 / **0.5→0.376** /
0.3→0.336 / 0.0→0.307;语义查询也从"全是 openharmony 噪声"变成命中正确条目
(「首启人格门禁」→changelog_v1.2.1、「插件怎么开发和部署」→plugin_dev_build);
- `vector.Store` 的候选中选阈值改为**可设**(默认 0.05 保持既有行为):TF-IDF 余弦量级
只有 0.0~0.2,沿用 0.05 会把词法路有效候选**静默砍掉**——这一条正是 0.376→0.197 的
差距来源,且当时没有任何报错;
- Add/Remove/scanAll/ReindexWithVectorizer 同步维护两路;分数相同时按名字定序(结果可重复)。
**顺带修一个真实毛病**:Add/Remove 原先用**无追踪的 goroutine** 写索引(因为
writeIndex→BuildTree 会 RLock,而调用方持写锁,同步调用会死锁)→ 失败只打日志,
且与调用方竞态(测试的临时目录清理就撞上了)。改为持锁就地 flush
(buildTreeLocked / writeIndexLocked)。
判据(不依赖人工标注问答对):新增 `internal/knowledge/rankdiag_test.go`,用**自检索
top-1 / MRR** 量区分度,`KB_DIAG=1` 跑、`KB_DIAG_ASSERT=1` 断言(MRR ≥ 0.34)。
另有不依赖真实数据的单测 6 条(空稠密向量靠词法路救回、稠密并列时词法路定序、
词法路阈值接线、Add/Remove 双路一致、并列时确定性、空库不 panic)。
**反向验证**(证明判据真能发现缺陷):权重退回 1.0、词法路阈值改回 0.05、
把阈值写死回 0.05 —— 对应测试逐条变红。另:我第一版夹具余弦 0.365/0.273 远高于阈值,
注入缺陷也不报错(等于没验),故加了「夹具前提」断言并改成两层判据
(语义层由 vector 包测试证明、接线层由知识库测试钉住)。
顺带纳入上一轮漏提交的 `TestAddOverwriteReplacesVector`(同名覆盖必须摘掉旧向量,
生产改动当时已提交,测试一直未入库)。
|
2026-09-12 18:14:22 +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 |
|
|
|
46e014a8ca
|
feat(persona): 首启人格门禁跨通道化 + 内核 persona_set 工具
WebUI 首启向导只覆盖 WebUI 这一条通道,而「人格该问一次」是所有通道的事:
走 QQ / CLI / ACP / 邮件来的人永远见不到那个向导,人格就永远是没确认过。
- 门禁移到 buildSystemPrompt(每轮重建 → WebUI/QQ/CLI/ACP/邮件全覆盖),
以 core.internal.persona_initialized 为准:未确认时要求模型主动询问用户
(默认 / 自定义 / 以后再说),确认后该段消失;personaStore 为 nil 时静默关闭。
- 新增内核内置工具 persona_set(mode=default|custom|later[, content]),
落库逻辑与 WebUI 向导**共用 internal/config**(一个实现 + 两个薄入口:
ConfigRegistry 直连 / 插件侧 SettingsAPI),避免两套语义各自漂移。
- AgentConfig 增加 PersonaStore 接口,cmd/homed 用 RegistryPersonaStore 实现。
- 非法输入(未知 mode / custom 空内容)在打标记**之前**拒绝:否则标记置位、
向导被跳过,用户再没机会设。
E2E(隔离实例 + 真实 LLM 往返走 /v1/chat/completions,/var/tmp/persona/e2e.sh)9/9 PASS:
未确认时模型主动询问 → 用户答「用默认的」→ 模型调用 persona_set 落库并置位标记
→ 之后不再追问;反向对照(清标记 + 清会话上下文 + 重启)重新开始询问,
排除了「同一段对话里已问过」这一混淆。
|
2026-09-12 13:57:06 +08:00 |
|
|
|
ea21803acb
|
docs(branching): 明确开发者文档的发布归属——以 rel 分支的形态为准,再合入 main
用户裁定:开发者文档应当在每个 rel 分支被修正为对应 rel 的形式,随后合入 main。
新增 §二.7,写清:
- 规则与做法(release 上按本版口径改 → cherry-pick 到 main,遵守 §三 只 pick 不 merge)
- 为什么不能直接改 main:main 语义是「下一个未发布版本」;assets/docs 会随发行包
分发并在 WebUI 被阅读,服务的是「这一版」;版本号/工具名/机制有无都随版变动
- main 上描述「下一版才有」的行为必须显式标注(如「(下一版)」)
- 反例表(本仓真实踩过):人格卡写死 v0.9.0 + 已删除的 C ABI、架构文档把已移除的
描述式索引/引用计数写成现行、README 停在旧版本
- 配套硬约束:任何会被当作事实的文本不得写死版本号,须插值或读运行时快照并加测试
|
2026-09-12 13:05:24 +08:00 |
|
|
|
10367b384e
|
feat(webui): 首启人格向导(默认 / 自定义 / 稍后)+ 一次性标记
接续人格配置项化(597f07c):现在人格是 core.agent.personal_prompt,
本次加上「首启问一次」的界面,之后不再打扰。
后端(GET/POST /api/v1/persona):
- GET → {initialized, current_prompt, file_override}
未设置时 current_prompt 回落到内置默认模板;存在 personal/personal.md
时报告 file_override(它会覆盖配置项,向导据此提示用户)
- POST → {"mode":"default"|"custom"|"later","content":"…"}
写配置 + 打一次性标记 core.internal.persona_initialized;
custom 返回 restart_required=true(人格在启动时载入);
「稍后」= 保留当前默认 + 打标记,**绝不阻塞任何流程**
- 空内容的 custom 与未知 mode 一律 400,且**不打标记**(否则向导会被跳过)
前端(dashboard.html):
- 首启拉一次 /api/v1/persona,未初始化则弹向导(复用一直没人用的 .confirm-* 样式)
- 「自定义…」第一次点击展开文本域并预填当前人格,再次点击才提交(避免误提交)
- 中英双语走既有 __() 机制;保存失败/空内容用 toast 提示
测试:TestPersonaWizardFlow(首启状态、later 打标记不改人格、custom 写入 + 需重启、
空内容与未知 mode 被拒且不打标记)、TestPersonaWizardReportsFileOverride。
E2E(真实实例):首启 initialized=false → POST later → initialized=true,
config 中标记=1、人格键为默认模板;前端页面含向导函数。
|
2026-09-12 13:02:18 +08:00 |
|
|
|
ce8bc27db8
|
docs(architecture): 记忆流转图对齐统一多模态空间
流程图里 Context/Prune/DocStore 三行仍只写 StaticEmbedder 与 TF-IDF,
读起来像"向量化只有词嵌入一条路",与 1.2.0 实际(多模态统一空间为主,
带 fingerprint;词嵌入/TF-IDF 是降级层)不符。
- Context Append:补三层向量层级说明
- Context Prune:改为 DenseCosine(仅同指纹比较)→ StaticEmbedder 回退
- DocStore:改为稠密向量 + dense_fp 同指纹要求(不符即重算)
- 中英双版同步
|
2026-09-12 12:46:21 +08:00 |
|
|
|
7318a10828
|
feat(persona): 人格设定配置项化 + 默认模板契约测试 + 腐坏告警
起因(v1.2.0 压测):线上实例内核日志/接口都报 1.2.0,agent 被问版本时却按人格卡
自述 v0.9.0 + C ABI v2(该机制 v1.0.0 已删除)。根因是人格只有「文件」一个来源且无人
维护——写死的版本号必然随发版腐坏。
改动:
1. 新增配置项 core.agent.personal_prompt(多行文本),默认值为内置模板
config.DefaultPersonaPrompt,随其它默认值同批播种(老安装不注入,语义不变)
2. 默认模板**不含任何版本号字面量**,并显式要求「被问到版本/构建信息时以运行时快照
(healthcheck_kernel)为准」——从根上消掉这类腐坏
3. 人格来源优先级:personal/personal.md(存在且非空)> 配置项 > 无
启动日志明确打印来源;文件含腐坏内容(版本号字面量 / 已删除机制的说法)时告警并
建议迁移到配置项
4. internal/agent.PersonaStaleHints:腐坏检测(版本号正则 + 已删除机制词表)
契约测试(防复发):
- TestDefaultPersonaPromptHasNoVersionLiterals:默认模板不得含 v?\d+\.\d+\.\d+,
且必须含「运行时快照」要求
- TestPersonaPromptRegisteredWithDefault:注册存在、默认值一致、播种真的写入
- TestPersonaStaleHints:线上人格卡原文必须被识别(v0.9.0 / C ABI v2),干净文本不误报
验证:go build ./cmd/homed ok;go vet 三个包 ok;go test ./internal/config ./internal/agent ok;
端到端两场景(无文件→来源=配置项 1307 字节;有旧文件→来源=文件 + 告警列出 v0.9.0 与 C ABI v2)。
|
2026-09-12 12:42:43 +08:00 |
|
|
|
b15d0bd114
|
refactor(plugin)!: 重编提示与模板路径改用 hmapdev;文档全面对齐 1.2.0
工具链在 SDK 1.2.0 更名为 hmapdev(原 plugindev)。核心侧三处功能耦合同步:
1. 用户可见报错:旧 C ABI 产物 / 协议版本不匹配 / 共享段版本不匹配
三处「请用配套 plugindev 重编」→ hmapdev(对应两条测试断言同步)
2. e2e_template_test 的模板路径改为 tools/hmapdev/templates,
并保留旧路径回退(旧 SDK 检出仍能跑测试)
3. 注释与文档同步
文档更新(用户可见面):
- assets/docs/{zh,en}/PLUGIN_DEV.md:工具链章节整体改为 hmapdev,
补改名说明与 SDK 存储目录迁移;命令示例全部更新
- assets/docs/{zh,en}/ARCHITECTURE.md:**流程图与章节对齐 v1.2.0** ——
· 向量化章节改为三层降级:统一多模态空间(主)→ 词嵌入 → TF-IDF(回退),
写明「同指纹且同维度才参与融合」
· 媒体记忆章节重写:媒体是一等记忆块(无独立 GC / 无引用计数 / 无描述式索引 /
正文不再写 media marker),并写明 reembedStaleMedia 的跨空间迁移与写回
- README{,_EN}.md、docs/zh/plugin-interface-matrix.md:工具名与模板路径同步
(历史条目标注「当时名为 plugindev」)
验证:go test ./internal/plugin/ ./internal/plugin/proc/ ok,
含 4 条真实模板 E2E(模板路径切换后仍通过)。
|
2026-09-12 12:42:43 +08:00 |
|
|
|
34628720f2
|
fix(memory): 修 beta.2 压测发现的三个向量/文档缺陷
来源:v1.2.0-beta.2 全方位压测(报告 /var/tmp/stress/REPORT.md)
1. 文档向量迁移结果不落盘(生产已复现)
- BuildDenseIndex 改完内存不置 dirty;docStore.Stop() 全仓无调用者 → flush 成死代码
- 后果:每次启动重算同一批文档(线上 496 篇约 17s),磁盘 dense_fp 永不收敛
- 修:迁移当场落盘(抽出 flushLocked 以免重入锁)+ main.go 关停链 defer docStore.Stop()
- 生产证据:线上 488 篇文档仅 4 篇含 dense_fp,且这 4 篇均为运行期 Insert 的新文档
2. 块指纹对但维度错时污染文档向量(健壮性缺口)
- denseFor 只校验 b.Fingerprint,不校验长度;FuseVectors 取最大维度并跳过长度不符者
→ 512 维文本 + 2048 维块 = 2048 维且打上当前指纹
→ 该文档在检索侧被长度守卫永久跳过,且每次启动重算(不收敛)
- 修:denseFor 要求 len(b.Vector) == 空间维度
- 可达性:内核两个块产出点均成对取自同一行(it.Vec ↔ it.VecModel),故属防御性修复
3. Insert 与 loadAll 的 ID 约定不对称(低)
- Insert 落盘任意 <id>.json,loadAll 只加载 doc_ 前缀 → 自定义 ID 文档重启后静默消失
- 修:loadAll 只要求 .json(空 ID 仍跳过)
回归测试 4 条:迁移跨重启落盘 + 已对齐 0 重算(用向量空间调用计数判定,
不靠日志)、坏块不参与融合且同维度正常块仍参与、自定义 ID 可加载、Stop 落盘。
反向验证(纪律要求):临时回退本次修复后,前 3 条均变红且报错正是缺陷签名
(dim=999/space-OLD-999 永不收敛、文档向量 2048 维、文件重启后消失);恢复后全绿。
验证:go build ./cmd/homed ok;go vet ./internal/memory/document ./cmd/homed ok;
go test ./internal/memory/... 7 包全绿。
|
2026-09-12 11:05:11 +08:00 |
|
|
|
85223fc1c9
|
docs(matrix): 记录 v1.2.x 的接口扩展,以及本次审计抓出的两处漂移
§九 原本只写到 v1.1.x。补上 1.2.0 的接口增量(注入侧的 InjectOptions 与六个
*Opts 变体、ContextPolicy 取值、ChannelDef.ContextPolicy 与它的 JSON tag),
并明确一件容易被误读的事:**「接口纯追加」不等于「无需重编」**——同版把插件运行
协议升到了 2(fd3 布局改变),协议不匹配会在握手时被明确拒绝,这两件事必须分开说。
同时把这次扩展自己抓出来的两处漂移入档(都属于本节第 3 条要防的类型):
1. 模板接线守卫 `TestProcTemplate_CoversAllCoreMethods` 红了:模板不再发
io.injectTextNoMem(改走 io.injectText + NoMemory),而内核保留该 id 是刻意的
向后兼容面。修的是判据(显式 deprecated 表 + 反向保护)。
2. mocksdk 与公共 SDK 机械求差,差集为旧的三参数 InjectInputSync(通道类插件闭环
要调的方法);git log -S 证实从来就缺,已补齐。
验证表按**本机实跑结果**填写:示例 vet 17/17、plugindev 测试全绿、
sdk -race -count=5 通过、mocksdk 差集为空。
|
2026-09-12 09:41:39 +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 |
|
|
|
0b1c201b5c
|
docs: README 更新到 v1.2.0,并纠正已在 v1.2.0 删除的机制
README 的「项目状态」停在 **v1.1.1**,而且其中两条描述与当前实现**相反**:
「媒体以 `[<mime> <短digest>] <描述>` 标记存在于纯文本记忆中,描述是可检索的
语义记忆」与「引用计数式 GC(有引用者绝不删)」——这两套机制正是 v1.2.0 拆掉的。
本次不重写历史条目(它们记录了演进),而是:
1. 「项目状态」顶部新增 **v1.2.0** 条目:统一多模态向量空间(模型中立 SPI +
Chinese-CLIP 默认,含「纯文本语义弱于 MLLM 型嵌入器」这一已知代价)、
媒体升为图记忆一等节点(并写明具体删掉了什么)、数据面全量走共享内存
(协议 2、不支持滚动升级)、注入标志位、发行包默认启用 ONNX 且随包模型、
homed 改走 WSL2、以及三个安装链静默失败的修复。
2. 在 v1.2.0 条目后加一条显式提示:下方历史条目中的「描述式索引」与
「媒体引用计数式 GC」**已在 v1.2.0 移除**——避免读者按旧文档理解现行行为。
3. 「下载」:Windows 安装器的版本号改到 v1.2.0,并写明自 v1.2.0 起因 homed
不再支持 Windows 原生,安装器改为引导到 WSL2 并在其中按 Linux 方式安装;
同时注明安装器含 AGPL 许可页。
中英双份同步。SDK 仓 README 另在 SDK 仓提交(d893bfa)。
|
2026-09-12 09:27:28 +08:00 |
|
|
|
5e10f012cb
|
docs(license): 核心仓采用 AGPL-3.0-only,并写进包内与安装器
本仓此前**没有任何许可文件**,README 里也没有许可声明,而 rpm 元数据里甚至写着
`--license "Proprietary"`(与我们实际的分发意图相反)。
## 选择了什么
`LICENSE`:GNU Affero 通用公共许可证第 3 版官方全文(gnu.org 正本,
661 行 / 34523 字节,
sha256 0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0)。
选 AGPL-3.0-only 的理由:GPL 家族里**传染性最强**的一档,并且不允许选后续版本。
它比 GPL-3.0 多出 §13(Remote Network Interaction)——通过网络提供服务时也要向
使用者提供源码。这正是「最严格」在 GPL 家族里的落点。
依赖许可已核对为全部宽松且兼容:go-sqlite3 / gojieba / gopher-lua / bubbletea /
bubbles / lipgloss / yaml.v3(MIT)、golang.org/x/{sys,text}(BSD-3)、
Chinese-CLIP 产物(Apache-2.0,与 GPLv3+/AGPLv3 双向兼容)、ONNX Runtime(MIT)。
没有 GPL-2.0-only 这类与 AGPL 不兼容的依赖。
## 落在哪些地方
- `README.md` / `README_EN.md`:新增「许可 / License」章节,写明 §13 的含义、
插件因**静态链接 SDK 源码**而成为衍生作品须同许可发布、以及随包第三方组件清单
- `deploy/packaging/package-linux.sh`:
· 新增 `stage_license()`,**四个变体(full/server/client/tar)全带**
`/usr/share/doc/homeagent/{LICENSE,copyright}`(copyright 为 DEP-5 机器可读格式,
含第三方条目)
· fpm 的 `--license "Proprietary"` → `"AGPL-3.0-only"`
- `deploy/packaging/installer.nsi`:新增 MUI 许可页
(`..\..\LICENSE`,NSIS 以 .nsi 所在目录解析相对路径)
- `third_party/homeagent-sdk/LICENSE`:vendored SDK 的许可一并入库 —— 本仓
`.gitignore` 有意不镜像 SDK 的 README/tools/package/example,但依赖的许可
应当随依赖可见
## 待办(下一步)
README 的「项目状态」仍停在 v1.1.1,且写着已被 v1.2.0 **删除**的机制
(引用计数式 GC、`[<mime> <digest>] <描述>` 描述式索引)——单独一个提交修。
|
2026-09-12 09:17:03 +08:00 |
|
|
|
727768e083
|
docs(git): 修正上一提交的错字(但声两仓 → 但两仓)
|
2026-09-12 09:01:59 +08:00 |
|
|
|
0247206dfe
|
docs(git): 写清「两仓 main 同步推进」的前提,修正 SDK 路牌
§七.4 原来只说「在 1.1.x 线发布期间,两仓 main 上的值都是 1.2.0」,容易被读成
「两仓 main 永远同值」,我正是据此把 SDK main 也推到了 1.3.0(已回退为 1.2.0)。
补写前提:推进以**该中版本已正式发布**为条件。
- 核心切出 release/v1.2.x 后 1.2.0 归发布线所有 → main 立即到 1.3.0(beta 也算占号)
- SDK 因 §七.2(beta 不发 SDK)要等核心正式 tag 才定版 → 在那之前 main 停在 1.2.0
并明确:**此阶段核心 main(1.3.0) 与 SDK main(1.2.0) 故意不对称**,
不是遗漏同步。§三 的 SDK 表行同步修正。
|
2026-09-12 09:01:34 +08:00 |
|
|
|
fbd3adabea
|
fix(sdk-mirror): vendored SDK 路牌回到 1.2.0(SDK 不跟 beta 发版)
SDK 仓 44bd915 把 main 的 meta.Version 推到 1.3.0 是错的(12cabcb 已改回 1.2.0)。
按 §七.2,beta 不伴随 SDK 发版:SDK 1.2.0 要等核心的**正式** tag 才定版打 tag
(§七.3),在那之前 1.2.0 仍是 SDK 尚未发布的中版本,路牌不得越过它。
核心 main 的 1.3.0 不受影响(1.2.0 已归 release/v1.2.x 所有),两仓在此阶段
故意不对称——这一点已写进 SDK meta.go 的注释,避免再被「对齐」回去。
本提交只同步 core 里跟踪的镜像:sdk/ 目录与 SDK 仓仍然逐字节一致。
|
2026-09-12 09:00:41 +08:00 |
|
|
|
f778c613f1
|
docs(git): 1.2.x tag 历史补上 v1.2.0-beta.1
release/v1.2.x 已按 §2.4 走出 beta 通道:tag v1.2.0-beta.1(提交 215804c),
注释里写明通道、不兼容点与验收证据;beta 阶段不发 SDK(§七.2)。
正式 tag 待试运行无回退问题后再打,届时同步 SDK 仓。
同时把 tag 名资产整理记录在案:历史大写 tag V0.8.0 / V0.7.1 已按用户确认删除
(提交本身未受影响),现在主仓 tag 全部符合 SemVer 小写 v 约定。
|
2026-09-12 08:54:09 +08:00 |
|
|
|
7a418346a4
|
build(packaging): 统一 -buildvcs=false,版本/提交只认 ldflags 注入
发布分支的产物上出现了 `vcs.revision=1715b5c`——一个本机任何仓库都不存在的提交。
原因:VCS 信息**不进 build cache key**(Go 文档明确说明 VCS 变化不会触发重建),
命中缓存时会把上一次的 revision 一并带回来。
而 build.sh 本来就用 ldflags 注入 meta.Version / meta.Commit(权威来源),
所以这个额外信号既不可靠又会误导溯源:拿 `go version -m` 去查源码提交,会指向
一个幽灵提交——正是本项目一直在治的「静默不一致」。
处置:三处 go build 统一 `-buildvcs=false`,并在 LDFLAGS 旁写明溯源方法
(`strings homed | grep -x '<短 hash>'`,meta.Commit 是字符串常量)。
验证:重新构建 homed linux/amd64 →
· `go version -m` 中 vcs.revision 0 处(此前 1 处且是错误值)
· strings 中恰好 1 处等于当前 HEAD 短 hash
· `-tags=onnxruntime` 仍在
|
2026-09-12 08:42:18 +08:00 |
|
|
|
a070cb563d
|
docs(git): 分支对齐更新到 2026-09-12(1.2.x 线开启)
- main 路牌调到 1.3.0;release/v1.2.x 承载 1.2.0(vendored SDK 定版 1.2.0)
- release/v1.1.x 按 §2.6 退役(保留供追溯);两个历史 feature 分支
(memory-media / plugin-proc-migration)已从远端删除,旧表待删项清掉
- 新增「1.2.x 发布线 tag 历史」表(当前尚无 tag),并写明它与存量插件
**不兼容**(RPC 协议 2、fd3 布局改变、不支持滚动升级)以及
**不满足 §2.4 跳级条件**的理由(改动面大,非单点修复)
- SDK 仓的 release/v1.2.x 尚未创建:按 §七.3 随核心**正式** tag 一起做
|
2026-09-12 08:39:05 +08:00 |
|
|
|
685ed7e4a0
|
fix(knowledge): 覆盖同名条目时摘掉旧向量
从一次真实的知识库更新里发现:在线实例更新一个已有条目之后,
knowledge_count=32 而 vector_count=33——多出来的那一条是上一版的副本。
成因:vector.Store.Insert 是**追加**语义(s.docs = append + index.Add),不按 id 去重;
而 Store.Add 走的是「写 content.md + 覆盖 items[id] + Insert 向量」。
文件与内存条目都被正确替换了,只有向量索引多留了一份。
危害不在于多占内存:**检索可能命中已被替换掉的旧内容**,而且完全静默——
条目数看起来是对的,只有向量数比条目数多。
修法:Insert 之前先 s.vec.Remove(id)(Remove 已按 id 过滤 docs 与倒排索引)。
回归测试 TestAddOverwriteReplacesVector 钉住 knowledge_count / vector_count /
content.md 三者都必须只剩新版。
注:该文件在 origin/main 上本就有 32 行 gofmt 差异(结构体字段注释对齐),
不属本次改动,按纪律不做整体重排。
|
2026-09-12 08:30:22 +08:00 |
|
|
|
dd99105d66
|
fix(build): GUI 输出目录用 --config.directories.output,-o 是 --mac 的别名
electron-builder 的 `-o` 是 `--mac`/`--macos` 的短别名(见 --help 的
Building 段),不是 output。于是 `-o "$BUILD_DIR"` 被当成 macOS 的 target
列表,报:
⨯ Unknown target: /home/program/trueagent/build
路径被 lowercase 后去匹配 target 名表,所以错误信息里的路径是全小写的
——这也是它看起来像「路径错」而实际是「参数位置错」的原因,v1.0.1 与
v1.0.3 两次发布都因此手工组装过 GUI。
改用 --config.directories.output=<dir>,已实测确认产物落在指定目录。
同时把 GUI 构建失败降级为警告:homed/waiter/initconfig 是发布主体,
而 GUI 依赖 electron 运行时下载(离线机器、arm64 缺缓存都会失败)。
set -euo pipefail 下不接住的话,一个可选组件会让整轮跨平台构建全废——
v1.0.3 就是这样只产出了 linux/amd64 三个二进制、arm64 与 windows
压根没跑到。
|
2026-09-12 08:26:12 +08:00 |
|
|
|
1fb5700662
|
chore(meta): main 的版本路牌推到 1.3.0
按 docs/git-branching.md §2.1,main 的 meta.Version 始终是**下一个未发布中版本**。
1.2.x 线已开(release/v1.2.x 承载 1.2.0),所以 main 指向 1.3.0。
它标记「main 正在积攒 1.3 的东西」,不表示 1.3.0 已经存在——1.3.0 没有任何 tag。
1.2.0 已由 release/v1.2.x 承载;main 不能再挂着 1.2.0,否则 main 的版本号
就与某一次发布的版本号相同,违反 §2.1「main 的版本号不是任何一次发布的版本号」。
两仓同步:SDK 仓 main 也在同一时刻推到 1.3.0(commit 44bd915)——SDK 版本跟随
核心中版本(§七.1),且两仓 main 都表示下一个未发布中版本(§七.4)。
本 commit 同时把 vendored SDK 镜像(third_party/homeagent-sdk/meta/meta.go)
更新到与 SDK 仓一致。
SDKCompatibleVersion 保持 1.2.0:那是本内核**实际实现并兼容的最高 SDK 接口版本**,
与「下一个未发布中版本」是两件事(参见 e4be966 时 main 的 1.2.0 / SDK 兼容 1.1.0)。
**此 commit 不 cherry-pick 到发布分支**(§五:版本号 bump 不跨分支搬)。
|
2026-09-12 08:25:30 +08:00 |
|
|
|
f1091676f8
|
style: gofmt 两处对齐(graphmedia_test.go 注释、tfidf.go 结构体字段)
这两处是本次特性分支带入的格式回归(origin/main 上不脏)。
纯空白/对齐调整,无语义变化;不整体重排文件。
|
2026-09-12 08:23:30 +08:00 |
|
|
|
eb4762a2b8
|
merge: 多模态统一向量空间与子进程数据面收敛(feature/multimodal-embedding)
合入 43 个提交,主线内容:
- 统一多模态向量空间:公共 provider SPI(pkg/embedding)+ 注册表,
chineseclip 为 text+image 默认空间(512 维,Apache-2.0),qwen3vl 保留
- 共享内存接管全部数据面:工具调用帧、Cleaner、输入/输出 lane、媒体块、
文档/知识正文;RPC 只传偏移描述符(协议版本 2)
- 图记忆原生媒体节点:媒体是一等节点与边,删除 media_refs 与描述式索引
- 注入行为可声明 no_memory / context_policy(默认不裁剪)
- SDK 1.2.0:注入标志位纯追加 + 示例 hmap 随发版
- 发行包默认启用 ONNX 向量空间;本批起模型与运行库随 server/full 包发布
- homed 放弃 Windows 原生支持,改走 WSL2
|
2026-09-12 08:21:34 +08:00 |
|
|
|
4d2028fbef
|
chore(sdk): 同步 vendored SDK 镜像到 1.2.0
core 仓里跟踪的 SDK 镜像落后于 third_party/homeagent-sdk(那是个独立仓库):
已提交的 sdk/memory.go 仍带 MediaAttachment.Description,而 SDK 仓 b2eafdf 已把它
删掉(媒体不再以文本描述参与索引)。磁盘上的文件其实就是 SDK 仓当前的版本,
只是 core 的索引没有跟上——于是**干净检出 main 拿到的是一个与它构建所用 SDK
不一致的镜像**。
clean worktree 能编译(内核已不再引用 Description),所以这个不一致不会被构建
发现,只能靠比对发现——属于本项目一直在治的「静默不一致」。
同步内容:sdk/memory.go 与 SDK 仓 HEAD 逐字节一致。
|
2026-09-12 08:21:21 +08:00 |
|
|
|
d4c5e808c7
|
feat(packaging): 模型与 ONNX Runtime 随 server/full 包发布
模型与运行库是发行版能力的一部分,不做成「装完再自己下载」:
- package-linux.sh:新增 stage_multimodal_assets(),打 server/full 前校验产物
SHA256SUMS、逐文件非空、运行库架构与目标一致,缺一即失败;client 包不含。
顺带修掉三个让打包在最后一步才炸的既有缺陷:
· 版本串直接取 git describe(v1.0.0-68-gxxx-dirty)不是合法包版本——deb 要求
数字开头、rpm 不允许 '-'。以前只有显式 VERSION=1.0.3 才打得出来;默认路径
从来没通过过。现在归一化,非数字开头时显式报错。
· 三处 mktemp -d 落在 /tmp(本机 9.8GB tmpfs),而 staging 要复制 719MB 模型,
中途 ENOSPC;报错文本指向某个 .onnx 文件,看着像资产坏了。改为落在与构建产物
同盘的 build/.stage-tmp。
· 开工前删掉旧的 SHA256SUMS:失败时脚本直接退出、不重算,留着像在为残缺产物背书。
- setup.sh:把包内 /usr/lib/homeagent/models/chinese-clip-vit-b16-onnx 软链到
<dataDir>/models/…(不复制 754MB、保持 dataDir 可迁移、已有自定义目录不覆盖)
- homeagent.service:ExecStart 改 /usr/bin/homed(deb 装在那里,此前写 /usr/local/bin,
装了也不会被 unit 用上)、加 ONNXRUNTIME_DIR 与 StateDirectory、MemoryMax 2G→8G
(实测常驻约 4.5GB,2G 会在首次全量建索引时被 cgroup OOM)
- control-{full,server}:补 libstdc++6 / libgcc-s1(libonnxruntime.so 需要)
- providers/{chineseclip,qwen3vl}:findOnnxLib 支持 ONNXRUNTIME_DIR / ONNX_ML_DIR
与包内 /usr/lib/homeagent/onnxruntime,随包的运行库才真的会被用上
- postinst:修掉两个让「装完即用」失效的点——它检查 /lib/systemd/system/ 下的 unit
而 deb 装到 /etc/systemd/system/,于是 daemon-reload/enable **从未执行**;以及
setup.sh 的失败被 `|| true` 吞掉(正是 initconfig 静默缺陷被藏住的原因)。现在
三个候选路径都查、失败可见并给出补救命令、首装 start / 升级 restart。
- docs/zh/multimodal-space.md:新增「随包分发」一节,并修正播种判据的说明
验收(从真实 deb 走一遍,不是读脚本):
- 包内 initconfig 已是动态链接,凭据真的写进 config.db
- 解包 → 按 postinst 顺序跑 setup.sh → 包内 homed 冷启动:
multimodal space active: provider=chineseclip dim=512 fp=cd2a495cf990
modalities=[text image]
- 全新安装的默认值确实被播种(core.plugin.dir / provider / model_dir 都在)
- 真实对话拿到回复(3.4s,回复中含唯一标记)
- 包内模型 SHA256SUMS 5/5 通过;包内 ORT 与源同 sha256;server 包 722MB
(旧版 17MB,差额即模型与运行库);full 包同样含全部资产;client 包不含
|
2026-09-12 08:10:44 +08:00 |
|
|
|
5299e18cd8
|
fix(config): 全新安装的默认值播种判据改为显式标记
播种判据曾经是「config 表为空」。而发行包的 postinst 先跑 setup.sh →
initconfig,后者会写一行 webui.listen_addr,于是**全新安装**被判定为
"已有配置"并整体跳过播种:没有 core.plugin.dir(装完 0 个插件)、没有
core.memory.* 路径、也没有随包模型对应的多模态 provider——754MB 产物与
24MB 运行库全成死重量。同样的机制此前已在协议 2 迁移演练里被观察到。
也不能改成"每次都补缺键":老安装升级时被注进新默认值,会让它突然去加载
一个 1.8GB 的模型,那是刻意要避免的静默变重。
故改为显式标记 core.internal.seed_version:
有标记 → 已播种,返回
无标记但有 core.daemon.data_dir → 老安装,只补标记、不播种
两者都没有 → 全新安装,播种并打标记
测试:TestSeedDefaultsAfterInitconfigPrepopulate(精确复现 initconfig 的
那一行写入)、TestSeedDefaultsDoesNotInjectIntoLegacyInstall。
|
2026-09-12 08:10:34 +08:00 |
|
|
|
6a3439a49c
|
fix(initconfig): 必须带 cgo 构建,且失败不再静默
cmd/initconfig 通过 database/sql 使用 mattn/go-sqlite3,而 build.sh 一直用
CGO_ENABLED=0 构建它:该库在非 cgo 下退化成 static_mock.go 里的桩,sql.Open
是惰性的所以不报错、第一次 Exec 才失败,而 main.go 丢掉了所有返回值。合起来
是一个完全静默的空操作——打印凭据、退出码 0、config.db 里一个字节都没写。
安装脚本把这份凭据写进 credentials.txt,用户照着登录必然失败,全程无报错。
- build.sh: initconfig 改 CGO_ENABLED=1,并写明为何不能图省事去掉 cgo
- main.go: 每个 Exec 都检查;写完**回读比对**(不看返回码,看真实落盘内容),
不一致即非零退出
反向验证:仍用 CGO_ENABLED=0 构建时,现在 stderr 报
"Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work"
且 exit 1(此前是 exit 0 并把凭据照打印出来)。
|
2026-09-12 08:10:26 +08:00 |
|