Skip to content

v0.1.17

Choose a tag to compare

@github-actions github-actions released this 02 Jul 06:28
· 35 commits to main since this release
4fbf396

新增

  • P4.17 MCP 远程传输 guanlan mcp --transport http(Streamable HTTP,见 docs/P4.17-MCP远程传输.md
    —— 给 P4.10 stdio-only MCP 宿主补第二种传输:官方 Streamable HTTP(spec 2025-03-26 起、取代 legacy
    HTTP+SSE)。同一套只读工具、只换传输tools.py 零改、build_mcp 仅加一个 allow_ask 注册门(把
    ask 的注册包进 if allow_ask:,六个零 LLM 工具无条件注册照旧),host/port/无状态/DNS-rebinding/token
    校验全部接线收在 serve_mcp 的 http 分支(_http_security/_BearerTokenMiddleware/_build_http_app/
    _serve_http)。安全默认:默认仍绑 127.0.0.1(沿用决策P4-4)、非环回强制 bearer token(从
    --auth-token-env 指定的环境变量读、绝不命令行明文/落盘;最小 ASGI 中间件 hmac.compare_digest 常量
    时间比对、失败 401、不引 OAuth)否则拒启 EXIT_USAGEask over http 默认关
    allow_ask=(transport=='stdio' or --allow-ask):stdio 永远七工具、http 默认六、--allow-ask 才七,避
    任意网络客户端触发付费 LLM 子进程的成本/DoS 放大 + 子进程写探问放大)、无状态 stateless_http(无
    Mcp-Session-Id、无事件重放)、DNS-rebinding 白名单派生自 --host/--allowed-host(可重复,反代对外
    域名须显式补入否则被拒)、TLS 外置。完整 OAuth(MCP 2025-06-18)/ 多租户 source 级 scoping 显式留给
    E2
    零依赖变更——已实测装的 mcp SDK(mcp>=1.27,<2)原生支持 transport="streamable-http" 且自带
    starlette/uvicorn 硬依赖,[mcp] extra 与 SDK 下界均不改。新增 CLI 旗标
    --transport {stdio,http}/--host/--port/--auth-token-env/--allowed-host/--allow-ask(默认全部
    向后兼容 P4.10:不带 --transport 即 stdio 字节等价)。测试:tests/test_mcp.py P4.17 套件(ask 门控、
    绑定红线拒启、token 闸 401/放行、rebinding 白名单派生、真 uvicorn + streamable-http 客户端端到端往返)+
    tests/test_cli.py 旗标解析。编号辨析:属 P4 宿主家族兄弟半阶段(非 P4.10.1 补漏、非新里程碑、非直接
    E2)。连带更新 DESIGN §7(P4 行 + E2 前哨行改「已实现」)、CLAUDE.md status/命令注释、docs/P4.17-MCP远程 传输.md(标题去「· 草案」、状态改「已实现」)。

  • 指令层:告知 Agent 可直接写 ```mermaid 围栏图 —— AGENTAO.md 新增「图表(流程图等直接写
    mermaid)」节、conventions.md 新增「§图表(mermaid 直绘)」(带最小示例)、SKILL.md ingest 工作流补
    指针:表达流程/时序/状态/类/架构/关系等结构时,Agent 直接在正文写 mermaid 围栏块即可,无需调用任何
    外部画图/生图工具——围栏块本身就是产物,Web 宿主(P4.13)浏览器内 strict 渲染成图、CLI 回退字面源码
    (markdown 唯一事实不破)。明确边界:新综合的关系图用 mermaid 直绘,raw/ 源里已有的插图仍按图片
    引用、不重画;图里实体/概念正文仍须 [[wikilink]] 互链(mermaid 节点文本不进 wikilink/召回解析);
    guanlan graphgraph.json/html(确定性 wikilink 关系图)无关。纯指令/文档,零代码改动,解锁的
    是 P4.13 已落地的既有渲染能力。同步补 examples/AGENTAO.md(init 模板单源)与本地库 kbs/{jindu,jingzhen}
    AGENTAO.mdinit 不覆盖既有文件,存量库需就地补)。

  • Web 正文/对话里的 raw/<slug>.md 引用联成只读 raw 源链 —— wiki 单页(/api/page)与对话答案
    渲染时,把指向现存 raw/<slug>.md 的引用联成可点的 a.rawlink[data-raw]:点击在右栏内联
    调既有 /api/raw/file 只读渲染那篇原始素材(复用单页历史栈,一键「←」回引用它的原页;左栏对话气泡
    里点亦切右栏,与既有 [[wikilink]] 行为一致)。识别四种写法(口径由用户敲定):① 裸路径串
    raw/x.md(plain text 行内);② 整段恰好是 raw/x.md 的行内 code;③ [[raw/x]](wikilink,带/不带
    .md、支持 |别名);④ markdown 链接 [文字](raw/x.md)(改写 <a href>、保留链接文字/内联格式、容
    ./ 前缀)。与 [[wikilink]] 同纪律——仅真实存在的 raw 文件联链,缺失标灰 span.rawlink.broken
    (dashed 下划线区别于 wikilink 的 dotted)。

    • 纯前端复用既有只读端点render.py 三处处理器共用 raw/*.md 真实文件名集(wiki.parent/raw
      推出、每渲染重扫,同 _stem_to_path 纪律)——_RawPathTreeprocessor(裸路径串 + markdown 链接 href,
      树上处理故按祖先跳过 a/code/pre)、_CodePathLinkTreeprocessor 内 raw 分支、_resolve_wikilink
      raw/ 前缀拦截;右栏新增 {kind:"raw"} 视图(banner 标明「原始素材·只读」+ raw/ 路径 +
      「在新标签打开」逃生口经 /?raw= 弹出 SPA,供并排对照源)。后端 /api/raw/file·/api/raw 零改动
      (reader 下仍注册)。
    • 关键正确性raw/x.md 即便存在同名 wiki 页也联到 raw 源、绝不误链 wiki(raw 分支先于 link_stem
      页解析);裸路径串改用 treeprocessor(而非 inline)按祖先跳过 <a>——根除 [raw/x.md](url) 在链接
      文字里被回灌再联、套出非法嵌套 <a> 的隐患;[**强调**](raw/x.md) 内联格式不丢;外链/绝对路径
      https://…/raw/y.md/raw/x.md)不动;cat raw/x.md 命令 code 与围栏块字面保留;不给 wiki
      不联链(保 P4 既有姿态)。新增 4 条 i18n 键 raw.{banner,openInTab,loading,openFail}(中英平价);
      测试见 tests/test_web.py
    • 多智能体评审修复(同一变更内):span 纳入 _RAW_SKIP_SUBTREE——杜绝 [raw/exists.md](raw/missing.md)
      在断链灰 span 内再联出可点且指向另一源的 rawlink;basename 解析改为「按存在性试 <名><名>.md
      _lookup_raw)——含内部点的 stem(raw/1.示例报告)正确命中,且 markdown 链接指向.md 资产
      raw/report.pdfraw/images/x.png)时原样保留 href、不再毁成断链 span;去掉 wikilink display 的
      AtomicString 包裹——恢复 [[页|**别名**]] 别名内联格式渲染(旧实现的既有 wikilink 回归);裸路径尾界
      (?![\w\-]) 放行句末 .raw/x.md. 仍联链);_raw_name_index 精确文件名键 + 无冲突小写兜底(镜像
      link_resolution_index fold 纪律)——大小写敏感盘上 raw/Foo.md/raw/foo.md 各按真实名解析、零串台;
      markdown 链接改写保留作者 title_linkify 尾段插入改 O(n) 偏移跟踪(去 list.index 的 O(n²))。

变更

  • Web 翻 auto 后的确认 UI 收进单框 —— 用户点「本会话起自动放行」(confirm_mode 翻 auto)后,原先
    「恢复逐次确认」按钮与「本会话已自动放行 ASK 操作(仍只写 wiki/workspace,raw/ 与 AGENTAO.md 仍只读)」
    安全提示是确认框下方另起的一条独立黄条;现把两者都并进「Agent 请求执行操作(请审阅)」确认框内:
    恢复钮就近放在置灰的「拒绝」钮前,提示文案作为框内一行(.interaction-automode-inline)。点「恢复逐次
    确认」后框内如实回滚——note.remove() 撤掉已失真的提示行、去掉「本会话起自动放行」前的 (移除
    .chosen)、钮后内联「已恢复逐次确认。」。冷启刷新时页面已是 auto(无对应确认框可挂钮)仍走原来的独立
    黄条带钮路径不变。实现上抽出共享的 makeRestoreButton() 工厂(确认框内一处、独立黄条一处共用),
    scripts/smoke_p415.py 同步把定位从独立 .interaction-automode 改为框内 .interaction-automode-inline
    纯前端、零服务端改动;写边界与 /confirm-mode {ask} 可逆语义不变。

修复

  • 毒会话状态文件(goal sidecar / 会话快照)不再把会话或整个侧栏打成永久 500(反向评审 gbrain v0.42.53
    探针 + 后续 code-review 加固)
    —— 一份手改/半写成非对象 JSONnull/[]/标量)或坏 UTF-8 字节
    的状态文件会让上层 data.items()/data.get(...)AttributeError/UnicodeDecodeError,逃逸出只接
    (OSError, JSONDecodeError) 的容错网,把读它的端点打成 500。统一补齐三条读路径的毒值容错:

    • read_goalweb/goal_io.py)加 isinstance(data, dict) 守卫 → 非对象 goal sidecar 退化为「无目标」,
      不再经 ConversationStore.restore()/cold_info/GET /api/info 持续 500(违其 docstring 承诺)。
    • _prune_old_snapshotsweb/chat_support.py)加 isinstance 守卫 + 把 UnicodeDecodeError 并入 catch →
      非对象/坏字节的兄弟快照在每轮 save 前的卫生步被跳过,不再崩 save(形同 gbrain「整循环崩在 checkpoint
      写」)、不再令 _save 静默不落盘丢会话。
    • ConversationStoreweb/conversation_store.py)新增 _safe_list_sessions 包装 agentao list_sessions
      的 per-file .get 毒值抛错(其 except (IOError, JSONDecodeError) 不接 AttributeError/UnicodeDecodeError),
      list/_disk_session(喂 restore/messages_for)改走它 → 一份坏快照不再 500 整个会话侧栏
      GET /api/conversations 与冷会话 restore/messages(降级为「该坏文件不计入盘 catalog」),并给两处
      load_session catch 补 race 兜底。
    • 回归测试:test_read_goal_tolerates_non_object_json / test_poison_goal_sidecar_degrades_cold_info_not_500
      / test_prune_old_snapshots_tolerates_non_dict_session / test_prune_old_snapshots_tolerates_bad_utf8_bytes
      / test_poison_session_snapshot_degrades_list_not_500

    (本轮另一处「convert 子进程强制 UTF-8 解码」的改动经 code-review 判定方向错误并已回退:skill 子进程裸
    print 发 locale 编码字节,父端强解 UTF-8 反而打断 matched-locale 下的中文路径往返、且 stderr surrogateescape
    会漏孤 surrogate 把 Web 解析端点 500 —— _run_converter 维持 text=True locale-faithful 行为。convert 编码
    的真正修法(子进程两端协同强制 UTF-8 + stderr errors=replace + pypdf/LANG parity 测试验证)连同续跑循环无
    进展探测 / convert 无超时等结论,见 docs/backlog/notes/gbrain-v0.42.53-反向审计-guanlan缺陷.md。)

Full Changelog: v0.1.16...v0.1.17