Repository navigation
Releases: ygy2200/agenthub
Release list
AgentHub v2.41.238
R897·预算/语义桥流水把诊断文本写进了 backup 列,note 永远为空(1 轮,2026-11-05,workbuddy)
①实锤:真库 brain_nudge_skip 6 条(error 4 / prompt 2)note 全空,used=6/6 sig=… 全落在 backup 列——三处 journal_add 把诊断串当成了第 5 个位置参数,而签名是 (root, agent, action, target, backup, note, via)。后果不是少一句话:B098 要量「error 通道被预算掐了几次、掐在哪个签名」,照 note 查只能得空;B077 的语义桥连 origin 都没落地,分不清真实会话与探针。
②修法:三处改 note=;语义桥 note 补 origin={origin}。
③验证:3 条新用例(prompt 跳过:note 有 used/session 且 backup 为空;error 跳过:note 有 used/sig;语义桥:note 带 origin=probe),旧码上三条全红(note 恒空)。
④顺带:test_nudge_budget_r837.py 里 test_error_row_bypasses_budget 重复定义了两遍(后一份把前一份遮死,用例数不变谁也没发现),删掉重复的一份。
⑤边界:历史 6 行不回填——流水是哈希链,改历史会断链;只修写入侧。
⑥回滚:git revert;历史数据零改动。
AgentHub v2.41.233
R886–R888·让大脑自己点名「没裁决的记忆取代」(3 轮,2026-11-05,workbuddy)
①R886:新增 tools/supersede_declaration_audit.py 只读量具(扫正文「取代记忆#N」、排除自述/机制说明、四档启发式分类、输出待判定/已裁决/目标不存在 + distinct_live_targets)+ 15 条用例;先红 6 条(夹具目标 id 写错、记忆 #561 自指污染被当 4 条假声明)后转绿。真库 559 条记忆 / 30 条声明 / 13 个目标仍 live。②R887:核心逻辑收编进 ops.supersede_declaration_audit(桌面 exe 只打包主程序+bundle,tools/ 不在包里,放外面会静默失明),接入 stale_health 让每次体检自动点名;守三条边界——待判定是提示不判红、泄漏硬不变量优先、审计异常 fail-open 但留痕;CLI 退化成薄包装。③R888:hub_ops_run 补上 R870 漏掉的第四个「旧进程自述」出口——R887 deploy 16/16 后 MCP 侧仍吐旧摘要且毫无征兆,现在明说「结论由旧代码算出,不代表当前源码」。④验证:R886 15 + R887 8 + R888 4 + stale 回归 19 + 相关回归 40 = 86 passed;py_compile 0;真库 ops.run_check("stale_health") 227.1ms、summary 自动带「取代声明未裁决 15 组(涉及 13 条旧记忆)」、detail 给裁决入口;deploy 16/16。⑤回滚:git revert a37788a / 3482ca9 / ee4defc,无 schema/数据/产物变更,量具只读。⑥边界:启发式分类不是裁决(#13「中…」漏判),13 条须逐条人工判定,禁止自动回填;外部依据见 output/迭代依据-外部取证与取代声明断层-2026-11-05.md(graymatter ADR-007 / OpenViking #5267 / ClawBrain #17 / hermes-agent #78307 / LoCoMo 审计)。
AgentHub v2.41.232
R884·最近团队踩坑进简报:别人花过的时间,开局就该看见(1 轮,2026-11-05,qoder)
①实锤:共同记忆不能等「轮到自己踩」才被检索到;其他 agent 7 天内刚沉淀的 lesson 在开工时不可见。②修法:session_brief 增「最近团队踩坑」段,取 kind=lesson 且 updated 7 天内、排除自己/旧坑/机制说明,最多 2 条,与规则段、项目记忆、置顶去重;stats.lessons 可观测。③验证:新增 test_team_lessons_brief_r884.py 4 条(自动进/排除自己旧坑机制/上限/与规则去重)4 passed;简报回归 30 passed;真库简报 3068 字符未截断,实际带出 workbuddy 两条 lesson。④回滚:git revert。
AgentHub v2.41.228
R880·写入侧声明通道:让 7 家都会写「必做」(1 轮,2026-11-05,workbuddy)
①实锤:R879 给注入加了 required/supportive 策略层,但 tags=必做/指令 在真库是 0 条——引导块(v7)不提、MCP 描述不提,写记忆的 agent 不知道能声明硬约束;机制只有代码知道=等于没有。②修法:BOOTSTRAP_VERSION 7→8,bootstrap_text 新增【以后必须照做的规则才标「必做」】条(写明 hub_memory_write 的 tags 用法、回报「注入标【必须照做】、动手前先落实」、边界「普通观察/一次性踩坑/机制说明别标」、kind=preference 同效);hub_memory_write TOOLS 描述同步;test_bootstrap_v7_r858.py 版本哨兵由字面量 "7" 改 >=7 下界(R299 教训:每次升版拆钉子会退化成机械动作)。③验证:新增 test_bootstrap_v8_r880.py 5 条(版本/动作与收益相邻/防乱标/描述同步/旧块替换且用户文本存活),在 v7 上先全红、改后 5 passed;引导相关 14 passed(v8 5 + v7 4 + r299 5)。④回滚:git revert;已 refresh 到各家的 v8 块再跑一次 cli.py refresh 回写。本轮与 R879 攒批发版,依据见 output/迭代台账-R880-2026-11-05.md。
AgentHub v2.41.225
R876·B087 会话关闭后的离线逐轮回填:补扫等不到的事件,账不该永远悬着(1 轮,2026-11-04,qoder)
①实锤:R875 后仍被点名的 2 个「现行」会话全是 R874 修复前关闭的——2f0a2ec0(qoder,25h,单轮真对话 94KB)与 b1b25ce0(workbuddy,22h,511KB)。补扫靠 UserPromptSubmit 触发,会话一关再也没人叫它;transcript 明明还在磁盘上,6 条注入却永远停在未观测。这不是「口径没上线」,是「有证据没人去取」。②修法一:判定要有轮上界。mark_recall_used 增加 not_after(开区间),回填一轮只判 [本轮开口, 下一轮开口) 之间的注入——只设下界会把后一轮的产出拿来判前一轮(R851 假信号的反向形态:后发生的话替先发生的注入领功)。③修法二:新增 tools/adherence_backfill.py。找 transcript(.qoder-cn / .claude / .workbuddy 三家,文件名严格等于 session)→ 按真人开口切轮 → 逐轮判定;tool_result、task-notification、reasoning/*_result 都不算开口或证据(R873 口径);默认 dry-run,--apply 才写账,流水 src=backfill;--min-idle-h(默认 2h)防抢跑:最后注入还很新的会话可能仍活着,交给在线补扫。④修法三:补扫兜底目录补上 workbuddy(R874 只搜了 qoder/claude,workbuddy 桌面事件不带路径时继续失明)。⑤ops 口径:_grade 认 src=backfill——回填读的也是 transcript,同一档证据,不能算「饿着」。
验证:新增 tests/unit/test_adherence_backfill_r876.py 5 条(区间上下界 / 两轮不借尸 / workbuddy 通知不算轮 / 缺文件诚实 / 活跃会话不抢跑)+ R874 钩子套件补 1 条 workbuddy 兜底(4 passed);py_compile 通过。真库回填(备份 _hub/brain-backups/brain-pre-r876-backfill-20261104-194120.db):11 个会话判 63 条、用上 13 条(含历史账 #506/#531/#113/#169/#505/#164/#440/#11/#146/#221);报表未观测 58→3(尾巴 3 · 断档 0)、判过 57→112、被用 19→32、观测口径 33.3%→28.6%(分母变大后更诚实)、账面 16.5%→27.8%;ops 由红转绿:20 个生产会话 19 个判定过(回填 30 · 补扫 12),transcript 证据 86%。
回滚:git revert 本轮提交后 cli.py deploy;数据层回填是真实判定(used/decided_at/adopt_count),要退回就从上述备份库拷回(会丢掉回填后的判定)。
AgentHub v2.41.221
R871·B086 稀有词门槛随候选池缩放:小库/窄池的自动回忆不再结构性失明(1 轮,2026-11-04,qoder)
①实锤:R826 的绝对门槛 raw idf ≥ 2.0 是按 40 条候选标定的(df≈5.5)。候选池只有 1 条时,一个「只在这一条里出现」的词 raw idf 也只有 log(2/1.5)=0.29——门槛在数学上就够不着,记录通道整批被判弱相关。真库只读复核:问「工作台每组只显示二十条没有出口」时 rec#3636 在记录候选第 1、hit=5,prompt_recall 的 record_ids 却是空。②口径:新增 _recall_rare_threshold(n),按与旧标定相同的出现率缩放——df ≤ max(1, 14%·n) 视为稀有;n=40 时 1.90(与 2.0 同量级),n=1 时 0.29(命中即可)。记忆与记录两条通道同口径。③边界:放宽只针对小池;池子大时泛词仍被 df 压零,12 条各不相同但全含查询词的记忆照样一条不注入(R826 底线不退)。
验证:新增 tests/unit/test_recall_rare_pool_r871.py 3 条(门槛随池单调 / 窄池唯一记录进 record_ids / 宽池泛词仍不注入);相邻回忆套件 53 passed;live recall_eval 7/11 → 8/11(rec#3636 从 top3 空到命中);剩余 3 条漏召回均为「问法不含答案原词」的语义缺口,按 R861 边界不放开语义救援。
回滚:git revert 本轮提交后 cli.py deploy;只想恢复固定门槛就删 _recall_rare_threshold 并把两处调用改回 _RECALL_RARE_IDF。
AgentHub v2.41.219
R868·B084 主动搜过的记忆不该被曝光免疫静音:use_count≥3 推翻「推了也没用」(1 轮,2026-11-04,qoder)
①实锤:曝光免疫(R830)只判 inject_count≥5 + adopt_count=0 + 30 天内,不看 use_count。真库 #113 被推 5 次零采纳、却被主动搜索 24 次,仍被判「推了也没用」;#164(use 4)、#221(use 17)同款。真库 muted_total=3;查询「AGENT_ADAPTERS 接入抽象」修前 memory_ids=[112,128,116](#113 被静音),「外部基准 Graphiti Mem0 对齐」同款。②口径:主动搜索不是「采纳」,但它比被动曝光强——use_count≥3 足以证明有人真的在用它,is_exposure_immune 直接放行;阈值 2 仍按 R830 静音,不能放宽成「搜过一次就免死」。③两条通道同款:_immune_sql 补 COALESCE(use_count,0)<?,否则 SQL 推送通道与 Python 判定对同一记忆给出相反结论(治理统计与实际推送不一致)。冷记忆仍按 30 天自愈窗口放行一次。
验证:新增 tests/unit/test_immune_search_use_r868.py 4 条(use=3 解禁 / use=2 仍静音 / 纯函数边界含缺省字段 / recall_for 与治理统计同口径);相邻免疫/采纳/预算 25 passed。真库只读复核:muted_total 3→0,两条查询 muted 1→0 且 #113/#164 回到 memory_ids;部署副本复核同款。
回滚:git revert 本轮提交后 cli.py deploy;只想退回旧口径就删掉 IMMUNE_USE_MIN 判定与 _immune_sql 的 use_count 条件。
AgentHub v2.41.212
R861·B035 语义读路搬进钩子进程 + 语义救援准入收敛 + 量具不再污染曝光账(1 轮,2026-11-04,qoder)
①B035 的正解不是「让 prompt_recall 也语义」,而是「把语义做便宜到能在每次新起的钩子进程里跑」:原来语义第三路只有 search_memories 用得上——torch+ST 单是 import 就要 7.1s(实测)、常驻数百 MB,而 UserPromptSubmit 钩子每次都是新进程,于是 prompt_recall 里 semantic 写死 False,语义在这条路上等于不存在。本轮把 bge-small-zh-v1.5 导出 ONNX 并做 int8 动态量化(22.8 MB,CLS pooling + L2 归一化,dim 512):import onnxruntime 0.057s、冷启动(含建 session 与首条编码)0.2–0.35s、之后单条编码 3–9ms,钩子路径终于付得起。新增 emb_onnx.py 是可选能力——权重缺失、tokenizer 坏了、onnxruntime 没装,任何一处失败都静默回落词法,主链路不受影响(用例逐条制造这些失败)。
②换了编码器就必须让向量空间自己声明身份:embeddings.model 从本轮起存标签 bge-small-zh-v1.5\|onnx-int8,_store_emb 写、_semantic_rank 读时按标签过滤、内存缓存键也带标签——否则 torch 时代的旧向量会和 ONNX 新向量放在一起算余弦,两套空间根本不通用,症状是「语义莫名变差」却没有任何报错。tools/rebuild_emb_onnx.py 按标签重建:全库 1735 条记忆 / 535 条向量 = 100% 覆盖,旧标签向量清掉 61 条。
③部署链路要带模型,否则「本机能跑、部署副本没有」:SERVER_FILES 增至 16 项(加 emb_onnx.py),MODEL_DIR_REL=models/bge-small-zh-v1.5、MODEL_FILES=(model.int8.onnx, tokenizer.json),deploy_server 对模型做粘性复制(源里有就覆盖、源里没有就保留目标已有的)——打包版源码目录不带权重,按「源没有就不复制」会把目标模型删掉,这正是从 exe 部署时的形态,补用例钉住。exe 的 bundle_files() 不含模型;同时 build.bat 排除了 onnxruntime / tokenizers / sentence_transformers——这三样是被惰性 import 静态分析打进去的,而 numpy 已排除、权重也不打包,它们在 exe 里永远起不来。桌面端 exe 因此从 102,277,448 B 降到 54,406,493 B(实测冒烟:窗口 4.7s 出现、稳态 262MB、无残留进程、用户 config 不变)。
④语义救援第一版直接把两条旧护栏顶红:_semantic_rescue 原准入是 not picked(词法没挑出强的就语义上),门禁实测 test_recall_quality_r825/test_recall_strength_r826 双双失败——6 条近义记忆以 0.77–0.80 的相似度被当结论注入,而这两条护栏的结论恰恰是「泛词命中时只该给『去查』提示」。中间试过按稀有词(pool idf ≥ 2.0)放行,也没成立:R825 的查询里「回事」是库里没有的填充词(idf 2.64),照样过线。最终准入改成 not picked and not scored:只有词法一条候选都没有(结构性失明)才走语义;有候选但没挑出强的属于 R825/R826 的 nudge 领地,语义不掺和。补一条用例用哨兵函数钉死「泛化弱命中时 _semantic_rescue 不许被调用」。
⑤量具自己把记忆推成了免疫,这是本轮最该记的一笔:live 评测从 8/11 掉到 6/11,一开始怀疑语义没生效,查库才发现是我们自己的探针以 origin=live 注入,把 #353/#457/#487 等推到 inject_count=5 / adopt=0,触发 30 天免疫(IMMUNE_INJECT_MIN=5)。两面修:_bump_inject 只在 origin == "live" 时计数(eval/probe 一律不计),tools/exposure_probe_pollution_purge_r861.py 先 conn.backup() 整库、再把 9 行计数归零并写 journal(可追溯、可回滚)。修完 live eval 回到 8/11;把救援收窄后回到 7/11——与词法基线相同,即 prompt_recall 这条路上语义的净收益是 0,如实记下,不拿「语义上线了」当成绩。
⑥语义真正变现的地方是 search_memories:同一批 10 条记忆评测用例,semantic=True 的 top-3 8/10,词法 7/10(#457 被提到 top1;#463/#492 两案仍缺,转 B071/B072)。
⑦仪表补丁:三处 nudge 分支(叮嘱/泛词/仅项目近况)从不上报 terms,eval miss 记录里恒为 terms=[],归因时看不到查询词命中什么——补上。校准:无意义句 max 相似度 0.5942 vs 真实查询 0.5823–0.7511,0.62 门槛留得住。
验证:新增 20 条用例(test_emb_onnx_r861 11 / test_prompt_recall_semantic_bridge_r861 9,先红后绿:救援准入那条第一版就是红的);test_recall_quality_r825 + test_recall_strength_r826 + 语义桥 21 passed;live recall_eval 7/11 = 63.6%;部署副本验收(生效层):~/.agenthub/mcp_server 里 ONNX tag 生效、真库 _semantic_rank("回归门禁怎么跑才算正式") top1 = #457 / 0.6281(0.32s 含冷建 session)、6 条真实取样中只有 1 条走到救援且返回 0 条(结构性触发条件);三层门禁 unit 1120 · gui 233 · e2e 5 全绿(_gate_r861c.log,gate rc=0)。
回滚:git revert 本轮提交后 cli.py deploy。只想关语义而不回滚 → AGENTHUB_ONNX=0;曝光账若被误清 → 用 _hub/brain-backups/ 里对应的 pre-exposure-reset 备份覆盖回去(工具每次都先备份)。
AgentHub v2.41.211
R860·B069+B070 迭代基建:草稿公开入口 + 按层跑门禁(1 轮,2026-11-04,qoder)
①B069 草稿公开缺入口:v2.41.209 的 Release 草稿带着 102,261,450 B 的 exe 在 GitHub 上挂着,公开它只需要一次 PATCH draft=false,而 publish.py 只有「建 Release + 上传附件」一条路——要么重跑 --upload-only 白传 102 MB(慢代理下再抖一次就留下「公开但没附件」的半成品),要么写一次性脚本绕 API。缺的是入口不是能力。补 --publish-only [--tag vX]:只做三件事(草稿在不在、附件非空、翻 draft),不对当前工作树跑发布闸门——补发旧版本时版本号必然对不上,拿今天的树去卡昨天的产物只会逼人绕过它;真正该卡的是「附件非空」,空附件公开正是 v2.41.159 / v2.41.77 那两次事故的形态。已公开的版本再跑一次非零退出(幂等,但不静默成功);--tag 让补发旧版本不必先改版本号。
第一版栽在真 API 上:GET /releases/tags/<tag> 对草稿一律返回 404(草稿对访客不存在,对按 tag 查的端点也一样),而我写的假 API 让 by-tag 端点连草稿也返回——第一次实跑 --publish-only --tag v2.41.209 直接报「没有 Release」。改成从列表(?per_page=100,含草稿)里捞,捞不到再退回 by-tag;假 API 也按真行为对草稿 404,并补两条用例(草稿只从列表可见 / 列表翻页后可退回)。
②B070 门禁没有按层入口:改一条 unit 用例也要陪跑整条三层(v2.41.210 实测约 20 分钟),本轮为了验证两个入口改了两遍代码,就要等两遍。补 --layer unit[,gui,e2e](可逗号、可重复):非法层名报错退出(rc=2)而不是静默跑全部——静默跑全部在 CI 里等于「配错了也全绿」,而这入口存在的意义就是缩短反馈,配错的代价必须看得见;只跑部分层时汇总明确写「不是全量门禁」,防止一张截图被当成三层结论。实测 unit 单层 1098 条 5 分钟跑完(原来三层 20 分钟)。
③补发布账:本轮用新入口把已传完附件的 v2.41.209 草稿公开,再构建并发布 v2.41.211(桌面 exe 同步更新)——「草稿公开」这条路径第一次在真库上走通,同时验证了它拒绝空附件。
验证:新增 15 条用例(test_publish_only_r860 10 / test_runall_layer_r860 5,先红后绿;两组都出现过「用例绿了、真环境没通」——假 API 掩盖了草稿 404,断言把目录目标当成层名)。--layer unit 实跑 1098 passed rc=0;--layer unit,nope → rc=2 且点名 nope;三层门禁 unit 1100 · gui 233 · e2e 5 全绿(_gate_r860.log,gate rc=0)。Release 验收物=未登录能看到的公开页(releases/tag/v2.41.209、v2.41.211)+ 直链 HEAD 的 Content-Length 与本地 exe 同尺寸。
回滚:git revert 本轮提交后 cli.py deploy;已公开的 Release 想撤回就改回 draft(GitHub → Edit → 勾回 draft),不动源码。
AgentHub v2.41.209
R858·B058+B063+B064 引导块 v7:把「登记换来什么」交给写入口自己说;命中率分母只算有形状的触发;门禁 unit 超时不再贴边(1 轮,2026-11-04,qoder)
①B058 引导块只写了义务、没写收益:v6 里那句「出错/受阻/踩坑:hub_report_error(…) 登记,undo 写回滚方式,方便任何人查询和撤销」对 agent 来说是一条手续;而这套机制真正的价值——下次再撞同签名报错时钩子会自动把上次的修法递到你眼前——只存在于代码和部署副本的提示文案里。本轮写 v7 的当天就现场验证了这件事:一个 shell 表达式写错,钩子当场递出「解决后顺手一句 hub_report_error…下次同签名就自动命中」,那句话来自 ~/.agenthub/hooks 的副本,引导块里以前根本没这句。B060/B061 修通之后这条收益第一次真的兑现得起来(等值通道有数据了),指引才第一次不是在承诺空头。同时补上 B040 的 tags=机制说明:讲这套机制自己的正文天然全是报错词和文件名,不带标记就会被当成「上次这么修的」递回来——以前只有代码知道这枚标记,agent 写记忆时不会带,代价由事后清洗来还(R853 洗公共账本 181 行里 116 行是自家评测/探针写的,就是那个成本)。BOOTSTRAP_VERSION 6→7;旧 v6 块整块替换不是追加,主人既有内容一个字不许丢(用例钉)。
②B063 命中率的分母里掺着「根本不是报错」的触发:ops._error_debt 的 rate = answered*100//asked,asked = answered + miss_rows,而 miss_rows 含 (no_signature)(钩子被触发了但正文抽不出错误形状:一句人话的失败、URL、非异常退出);分子却只数「有签名且有货」。真库近 7 天 29 次触发里 15 次不是可识别报错,于是报出 13%——既不能说大脑不行(一半触发压根不是报错),也不能当趋势用(口径随钩子触发面漂)。error_debt 新增 miss_keyed / miss_unshaped / asked_shaped(asked 保持总触发量,不偷改已有口径),ops 的百分比改用 asked_shaped,无形状触发单列一句「另有 N 次非报错触发,不进命中率」;全是非报错时不报任何百分比——把钩子触发面当知识缺口是假信号。真库读数 13% → 28%(4/14),变化逐条可解释。
③B064 门禁自己撞出来的:unit 层整批跑(1000+ 用例),LAYERS 里 300s 是贴边值——空载实测 256s,本轮带并行负载跑到 300s 被杀。junit 是 pytest 在汇总阶段才落盘的,进程被杀就没有文件,_junit_counts 返回 None,于是整层报 「unit: FAIL(0 tests;1 个目标异常)」,而日志里明明跑到 97% 且打出一个 F:红了哪条完全不知道,只能整层重跑再等 4 分钟;更糟的是「超时」和「套件是空的」在报告里长得一模一样。超时该抓的是真卡死,不是给套件限速——unit 层 300→900s,实测基线写进 UNIT_MEASURED_S,守护用例要求余量 ≥3×(谁降回贴边值谁红);另补一条「真卡死必须被抓成 timed_out、且不重跑(R231 契约)」。
验证:新增 3 个文件 9 条用例(引导块 4 / 分母 3 / 门禁 2),逐条先在 v6 与旧码上红;test_hint_param_r299.py::test_bootstrap_version_bumped_for_stale_detection 原本钉死字面量 "6",那颗钉子每次改内容都要去拆,拆久了就退化成「顺手把数字改掉」,反而不再是哨兵——改成只断言真不变量「块里的版本标记 == BOOTSTRAP_VERSION」,每轮的实际版本号由那一轮自己的用例钉(v7 见 test_bootstrap_v7_r858.py)。三层门禁 unit 1041 · gui 233 · e2e 5 全绿(run_all.py 改后整层重跑,gui/e2e 与 unit 同一次代码状态);py_compile 通过;真库 hub_ops_run(error_debt) 读出新句式。回滚:代码 git revert;引导块回到 v6 需再跑一次 cli.py refresh(各家文件写前都有备份)。GitHub 发布仍受阻(订阅节点全灭,见 v2.41.208 行的实测记录),本轮只更新桌面端 exe 与部署副本。