npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mio-agent-runtime

v0.15.0

Published

Mio Agent Runtime installer and MCP intelligence layer

Readme

mio-agent-runtime

Mio Agent Runtime 的可发布 CLI 包。

安装一次,所有 Agent 自动接入 Mio。

安装

npm install -g mio-agent-runtime

新机器 / 升级:请安装 >=0.5.23(5 host 被动观察 + trace 查询 + digest 价值管线; 0.5.23 修复了 digest 写回污染其它项目 workspace 的问题)。 旧版本只有主动 MCP 规则注入,没有「无感存/取」。安装后执行一次 mio init && mio install <host>。

命令

mio init                    Initialize MIO_HOME
mio config show             Show effective settings and where each value comes from
mio config llm --url U --model M [--key K]   Store LLM endpoint/key/model in config.json
mio config llm --clear      Remove the stored LLM config (env vars still apply)
mio config path             Print the config.json path
mio mcp                     Start Mio MCP server (stdio)
mio install <host>          Install Mio into a host (codex|opencode|workbuddy|hermes|claude)
mio status                  Show runtime and adapter status (also reports pending auto-claims)
mio agents                  List installed host adapters
mio agents list             List observed agents (from agents.jsonl, --project X)
mio agents report           Report per-agent task/memory/reuse telemetry (--agent X, --project Y)
mio agents register --agent-id X   Register an observed agent (--yes to apply; previews by default)
mio agents evaluation       ADR-017 evaluation metrics: route adoption, behaviour change, recall quality, data hygiene (--project X, --since ISO)
mio evolution status        Show composed evolution module health
mio evolution report        Cross-agent evolution report: ecosystem, agents, memory health, suggestions (--period 24h|7d|30d|all)
mio evolution shadow record      Record a shadow comparison sample (--legacy/--modular JSON; both required)
mio evolution dual-write record  Record a dual-write comparison sample (--legacy-result/--modular-result JSON; required)
mio evolution cutover readiness   Assess shadow/dual-write cutover readiness (--min-shadow-runs/--max-mismatch-rate)
mio evolution authority plan      Preview a gated authority switch plan (--readiness JSON; required)
mio evolution migration plan      Preview state migration diffs (--legacy-records/--modular-records JSON; required)
mio evolution cutover apply --dry-run   Dry-run a cutover plan (--plan JSON and --dry-run are both required)
mio observe                 Watch WorkBuddy transcripts and auto-ingest task outcomes (foreground)
mio observe --start [--research]|--stop|--status|--once   Manage the background observer daemon (--research also starts the research scheduler)
mio recall "<query>"        Search Mio memory from the terminal (--project/--scope/--kind/--tags/--limit)
mio traces                  Show recent observer traces (--type/--outcome/--agent/--since/--limit/--compact)
mio prune --days 30         Trim old traces/queries/reuse records and observe.log (--dry-run to preview; --memory needs --yes)
mio digest --days 7         Aggregate traces/memory/reuse into an actionable report (--write-back feeds agent context files; --json)
mio remember "<content>"    Write a memory record from the terminal (--kind/--tags/--scope/--project)
mio memory analyze          Report duplicates, low-quality records and the kind histogram (--project/--limit)
mio memory archive --ids a,b    Archive records, hiding them from recall/analyze (--yes required; reversible)
mio memory restore --ids a,b    Un-archive previously archived records
mio memory forget --ids a,b     PERMANENTLY delete records (--yes required; writes an audit entry; not undoable)
mio memory merge --ids a,b  Merge duplicate records into one survivor (--keep/--allow-divergent; --yes required)
mio memory migrate --ids a,b --scope global|project   Move records between the project and global layers
mio policy check "<action>" Check the historical risk of an action before running it (--project; reads global MIO_HOME)
mio creativity status        Show creativity hypothesis counts, recent top ideas, adoption join (reads global MIO_HOME)
mio creativity list          List creativity hypotheses (--status active|validated|rejected|draft, --limit N)
mio creativity generate      Generate hypotheses from 2+ --source "name|content" (or --from-insights; calls an LLM)
mio creativity ferment       Review and refine active hypotheses (calls an LLM)
mio creativity adopt         Record an adoption event for a stored hypothesis (writes traces only)
mio idea generate --goal "..."   Grounded idea pipeline: auto sources + novelty gate, persisted (calls an LLM)
mio insight status           Insight counts: total, reported, unreported, high-value (needs @akemi-mio/insight)
mio insight list             List insights (--unreported, --min-score N, --detector X, --limit N)
mio insight generate         Generate insights from context (--memory "kind|content"/--summary "text"; calls an LLM)
mio insight mark-reported    Mark insights as reported (--ids a,b)
mio observer <view>          Observer research pipeline views (research pipeline, not the observe daemon):
                             status|world-model|trends|research|insights|essays|dag (--base-dir DIR)
mio observer collect         Fetch from the configured sources (--sources a,b/--keywords k1,k2/--limit N)
mio observer ferment         Run the fermentation engine (--session morning|afternoon|night)
mio observer pipeline        Run the research DAG (previews by default; --run executes, --mode neutral|analytical|creative)
mio observer serve           Keep the research scheduler running (previews with --dry-run; Ctrl+C stops it)
mio observer ingest --trace-id T --event-type E   Record a trace event (--payload JSON/--outcome)
mio observer subscribe --event-types a,b          Subscribe to events (--yes to apply; previews by default)
mio observer digest                               New events since the last digest (advances the cursor)
mio phase0 report            Show the Phase 0 validation report (--project X, --format markdown)
mio host capabilities        Show what each host supports and whether it is installed (--json)
mio task route "<task>"      Which verified experiences apply to this task (--project/--scope/--limit)
mio task record-outcome --outcome success   Record a task outcome (--yes to apply; previews by default)
mio --json status           Machine-readable status
mio --json agents           Machine-readable agents
mio --json evolution status Machine-readable evolution module health
mio --version                Print the version and exit (-V)

输出流与退出码

所有命令族共用同一套约定(12 个族一致,且每个族都有测试钉住):

| 情形 | stdout | stderr | 退出码 | |---|---|---|---| | 子命令写错 / 必填参数缺失 | 空 | 报错行 + 完整用法 | 1 | | 裸命令(如 mio memory) | 用法 | 空 | 1 | | --help | 用法 | 空 | 0 |

  • 失败时 stdout 必须为空,因为 mio --json 的调用方读的是 stdout——用法文本落在那里 会被当成 payload 去解析。所以缺参时先给出原因(--legacy is required),紧接着把用法与 示例一起打到 stderr。
  • 裸命令不是错误,只是"没说清要做什么",所以用法走 stdout;但仍以 1 结束, 这样脚本不会把"什么都没做"当成成功。它打印的用法与 <命令> --help 完全相同, 区别只在退出码(1 vs 0)。
  • 例外:mio config 与 mio agents 的裸命令是真命令(显示配置 / 列 agent),退出 0。
  • mio help 与 <命令> --help 都走 stdout 并退出 0。

MCP 工具

MCP 服务端在 5 大域共暴露 52 个工具:

记忆(11)

mio.memory.query · mio.memory.record · mio.memory.archive · mio.memory.merge · mio.memory.migrate · mio.memory.analyze · mio.memory.forget · mio.experience.list · mio.experience.confirm · mio.experience.reuse · mio.policy.check

观察管线(15)

mio.observer.world_model · mio.observer.trends · mio.observer.research · mio.observer.insights · mio.observer.status · mio.observer.collect · mio.observer.ferment · mio.observer.pipeline · mio.observer.essays · mio.observer.dag · mio.observer.ingest · mio.observer.subscribe · mio.observer.digest mio.trace.query — 按类型/结果/agent/项目/时间窗口查询 trace 日志(任务结果、工具错误);CLI 上也可通过 mio traces 调用 mio.digest.generate — 把近期数据聚合成可执行的 digest(agent 成功率、项目活跃度、错误热点、复用证据、建议);CLI 上也可通过 mio digest 调用

洞察自省(4)

mio.insight.status · mio.insight.list · mio.insight.mark_reported · mio.insight.generate

创意引擎(6)

mio.creativity.generate · mio.creativity.ferment · mio.creativity.adopt · mio.creativity.list · mio.creativity.status · mio.idea.generate

任务、Agent 与演化(16)

mio.task.route · mio.task.record_outcome · mio.agent.register · mio.agent.list · mio.agent.report · mio.agent.evaluation · mio.host.capabilities · mio.evolution.status · mio.evolution.report · mio.evolution.shadow.record · mio.evolution.dual_write.record · mio.evolution.cutover.readiness · mio.evolution.cutover.apply · mio.evolution.migration.plan · mio.evolution.authority.plan · mio.phase0.report

包

mio-agent-runtime 依赖发布在 @akemi-mio scope 下的 10 个包(声明于 package.json,均为 0.1.0)。它们仅在源码变更时才发布,所以普通的 CLI 发布不会重新发布这些包。

| 依赖 | 说明 | |---|---| | @akemi-mio/runtime-contracts | 共享类型定义 | | @akemi-mio/runtime-foundation | 日志、EventBus、记忆 schema | | @akemi-mio/experience-memory | 经验记录与检索 | | @akemi-mio/evolution-learning | 演化学习引擎 | | @akemi-mio/evolution-strategy | 演化策略引擎 | | @akemi-mio/evolution-safety | 安全护栏 | | @akemi-mio/evolution-scheduler | 演化调度器 | | @akemi-mio/observer | ObserverService —— 多源数据采集、趋势分析、深度研究、世界模型、发酵、DAG 状态机、自演化引擎(唯一运行时依赖 undici,只用于走代理采集) | | @akemi-mio/insight | InsightService —— 基于 LLM 的洞察生成、冲突/漂移/重复/停滞目标/摩擦检测器、在场服务、洞察打分(零 npm 依赖) | | @akemi-mio/creativity | IdeaGenerator / NoveltyScorer / SourceAggregator —— mio idea generate 与创意引擎的生成算法(依赖 @akemi-mio/core) |

以下 @akemi-mio 包同样位于本 monorepo 中并独立发布,但不是本 CLI 的依赖——其源码从不 import 它们,因此未声明在 package.json 里:

  • @akemi-mio/analysis — ModuleScanner,对包源码树做静态分析(零依赖)
  • @akemi-mio/messaging — ExternalMessageGateway,入站消息路由 + 通知分发(零依赖)
  • @akemi-mio/reasoning — 纯函数打分规划器,用于推理指令
  • @akemi-mio/resource-control — 资源预算、后台任务运行器
  • @akemi-mio/agent-persona — 人格漂移控制、内容分类

它们可直接被其它 Node 项目消费。mio-agent-runtime 自身是 CLI + MCP server 包。

曾经不发布的两个包(现已发布)

@akemi-mio/core 与 @akemi-mio/creativity 自 [email protected] 起发布到 npm(0.1.0)。@akemi-mio/creativity 是本 CLI 的直接依赖,@akemi-mio/core 经由它传递引入;其它 Node 项目可直接安装这两个包。

宿主说明

  • OpenCode 通过 ~/.config/opencode/opencode.json 做 MCP 注册,通过 ~/.config/opencode/AGENTS.md 做全局指令。mio install opencode 会写入这两个文件,并向 AGENTS.md 注入 Mio 使用规则,使 OpenCode 自动调用 Mio 的记忆/观察/策略。若 OpenCode 已在运行,重启一次以重新加载这两个文件。mio observe 还会被动跟踪 OpenCode 会话数据库(opencode db),无需模型主动调用 Mio 工具。
  • Hermes 使用 hermes mcp add mio-intelligence --command node --args <server> 来注册 Mio MCP server。Hermes 有原生记忆并读取 AGENTS.md Context Files,因此无需注入 Mio 规则也能检索。
  • Claude Code:mio install claude 在用户级 ~/.claude.json(mcpServers)注册 Mio MCP server——不像项目级 .mcp.json 需要交互式审批——并向 ~/.claude/CLAUDE.md(自动加载的用户记忆)与 <workspace>/CLAUDE.md(项目记忆)注入 Mio 使用规则。重启运行中的 Claude Code 会话以重新加载两者。mio observe 被动跟踪 <config-dir>/projects/<munged-cwd>/*.jsonl 会话记录:跳过 sidechain(子 agent)行,每个用户 prompt 结束上一轮,tool_result.is_error 行记为错误 trace,成功轮次的摘要写回 workspace 的 CLAUDE.md 的 MIO_CONTEXT 块,供下一会话被动取用。可用 CLAUDE_CONFIG_DIR 覆盖配置目录。
  • WorkBuddy 使用 ~/.workbuddy/mcp.json,并要求每个自定义 MCP server 在 ~/.workbuddy/mcp-approvals.json 中审批通过。mio install workbuddy 会写入这两个文件,并向 ~/.workbuddy/CODEBUDDY.md(用户级记忆规则,由 WorkBuddy 自动加载进每次会话;CLI 硬编码了 CodeBuddy 产品名,所以文件名是 CODEBUDDY.md 而非 WORKBUDDY.md)以及 <workspace>/AGENTS.md(项目级)注入 Mio 使用规则。若 WorkBuddy 已在运行,重启一次以重新加载审批与记忆规则文件。

被动观察器(mio observe)

WorkBuddy 的默认模型不一定会主动调用 Mio MCP 工具,即使规则已注入 (CODEBUDDY.md / AGENTS.md)。mio observe 是一个不依赖模型自觉的 兜底:它持续监听 ~/.workbuddy/projects/**/*.jsonl 会话记录,把每个任务的 结果(task_outcome)、工具调用错误(error)以及完成任务的摘要自动写入 MIO_HOME/traces.jsonl 和 MIO_HOME/memory.jsonl(与 MCP 服务同一套 schema,source=workbuddy-observer)。

  • mio install <host> 会自动执行一次回填并启动后台观察器。

  • 除了写入 MIO_HOME,观察器还会把任务摘要写回 WorkBuddy 自己的记忆文件 (<workspace>/.workbuddy/memory/YYYY-MM-DD.md,标为 ## Mio 自动摘要)。 WorkBuddy 的系统提示要求模型每次会话维护并阅读这些文件,所以下一次会话 WorkBuddy 会被动取到 Mio 记录的内容——单 Agent 的「存 + 取」闭环不依赖 模型调用 Mio 工具。

  • 已处理的文件偏移记录在 MIO_HOME/observe-state.json,不会重复写入。

  • 只回填最近 24h 内更新的会话,避免把历史记录灌入记忆库。

Context 块的写入范围(MIO_CONTEXT)

<workspace>/AGENTS.md(或 CLAUDE.md)里的 MIO_CONTEXT 块最多保留 6 条(CONTEXT_MAX),按时间倒序。三条规则保证真实历史不被挤掉:

  • 同一条内容只占一个位置。重复写入只把它移到最前。旧版本会把同一行 反复插入,占满全部 6 个位置,把真实任务摘要整体挤出(存量 observe-state.json 里的重复条目会在下次写入时自动清理)。
  • 同一项目只对应一个桶。Context 键取自各 host 转录里的 cwd 字段, 而不同 Agent 的拼写不一致(D:\work\code\x、d:\work\code\x、 D:/work/code/x)。这些写法现在会被归一到统一形式(盘符大写 + 反斜杠), 等价桶在读取和写入时自动合并——否则同一个项目会被拆成多个桶,各自 只保留 6 条,互相看不到对方的历史。存量分裂键无需迁移脚本, loadState 会在下次加载时合并。
  • mio digest --write-back 只写回有数据的项目。写回的是一行项目维度 摘要(digest(7d): <项目> N 任务 M% 成功),不是全局统计;observe-state 里没有对应 digest 数据的 workspace 会被直接跳过,不会被写入。旧版本把 同一条全局 headline 广播给所有历史项目,导致无关仓库(如 ComfyUI)被污染。

桶合并后若超过 CONTEXT_MAX,按条目内嵌的时间戳(YYYY/M/D HH:MM:SS) 截断,保留最新的,而不是按存储位置丢弃。

需要强制指定目标时用 --cwd <path> 或 --project <name>;--cwd 优先。

OpenCode 会话不依赖模型自觉:mio observe 通过 opencode db 增量读取 ~/.local/share/opencode/opencode.db(Windows 下该文件被 OpenCode 独占锁定, 必须经 opencode 自带 CLI 读取),按 part 表的 rowid 做增量游标,只传输 text/tool 两种 part,把每个用户回合的 task_outcome 与工具错误写入 MIO_HOME/traces.jsonl / memory.jsonl(source=opencode-observer),并同样 把摘要写回 <workspace>/AGENTS.md 的 MIO_CONTEXT 块,供 OpenCode 下一会话 被动取用。首次启动只回填最近 24h 的 part,避免历史会话灌入记忆库。

Hermes(Nous hermes-agent)同理:mio observe 以只读方式增量读取 <LocalAppData>/hermes/state.db(messages.id 自增游标,无需调用 hermes CLI), 把每个用户回合的 task_outcome 写入 MIO_HOME(source=hermes-observer)。 Hermes 本身有原生记忆,Mio 只负责让 Hermes 经验进入跨 Agent 生态;仅当会话 发生在真实 git 仓库时才写回该仓库的 AGENTS.md,避免污染用户主目录。

MIO_HOME

默认 ~/.mio-intelligence,可通过环境变量 MIO_HOME 覆盖。

包结构

packages/mio-cli
├── bin/mio.js
├── adapters/
│   ├── codex.js
│   ├── hermes.js
│   ├── opencode.js
│   ├── workbuddy.js
│   └── claude-code.js
├── observe/
│   └── observer.js
├── server/
│   ├── host-capabilities.js
│   ├── evolution-cutover.js
│   ├── runtime-modules.js
│   ├── creativity-engine.js
│   ├── memory-store.js
│   ├── experience-store.js
│   ├── policy-store.js
│   ├── retention.js
│   ├── digest.js
│   └── mio-intelligence-mcp/index.js
└── package.json

server/memory-store.js 是记忆查询/记录排序(Latin token + CJK bigram 打分、project/global 作用域分层、复用证据加权)的唯一实现。MCP 服务端(mio.memory.query / mio.memory.record)与 CLI(mio recall / mio remember)都经过它,因此各入口的排序结果完全一致。同一个模块还实现了卫生操作——analyze / archive / merge / migrate——所以 mio.memory.analyze 与 mio memory analyze 不可能各自漂移。server/experience-store.js 与 server/policy-store.js 对 mio.experience.* 与 mio.policy.check 遵循同样的模式;策略存储复用了记忆存储的分词器,因此策略风险证据与相关记忆排序在构造上就与 mio.memory.query 一致。server/creativity-engine.js 是 mio.creativity.* 背后的共享实现——CLI 的 mio creativity status/list 与 MCP 工具调用的是同一个 CreativityEngine,因此假设计数在入口间不会漂移。server/agent-store.js 是 mio.agent.list / register / report 背后的共享实现;CLI 的 mio agents list/report 与 MCP 工具读取的是同一个存储,因此被观察 agent 的遥测数据在任何地方都相同。server/llm-client.js 是 chatJson 的唯一实现,MCP 服务端(创意引擎 + insight)与 CLI(mio creativity generate/ferment)共用,因此两边调用的是同一个模型与同一份配置(环境变量 > MIO_HOME/config.json 的 llm 块 > 内置默认,见「LLM 配置」)。server/query-log.js 是 queries.jsonl 的唯一读写实现——记忆存储在 queryMemory 时记录查询,任务存储在 auto-claim 时消费它把 task_outcome 归因到先前的查询;两者不能互相引用(会成环),所以日志自成一模块、由调用方把同一个实例交给两个 store。server/digest.js 把 traces/memory/reuse 聚合成可执行报告(同样以 mio.digest.generate 暴露),server/retention.js 驱动 mio prune(按年龄/过期裁剪并备份;没有显式的 --memory --yes 绝不触碰 memory.jsonl)。

记忆卫生

mio.memory.analyze 能诊断重复项与低质量记录,但此前唯一的处置途径是通过 MCP 服务端。mio memory 子命令补上了这个缺口:

mio memory analyze                       # 需要关注什么?
mio memory archive --ids mem_a,mem_b     # 预览(不加 --yes = 不写入)
mio memory archive --ids mem_a,mem_b --yes   # 执行
mio memory restore --ids mem_a           # 撤销
mio memory merge --ids mem_a,mem_b --yes # 把重复项合并为一个幸存者
mio memory migrate --ids mem_a --scope global   # 提升到 global 层

说明:

  • 归档可逆,不是删除。被归档的记录仍以 archived: true 留在 memory.jsonl 中,并从 recall 与 analyze 中消失。mio prune --memory 仍是唯一会永久删除记录的操作,且仍需要 --yes。
  • archive 默认先预览。不加 --yes 时打印将要发生的变更并以退出码 1 结束;--json 模式同样如此,脚本无法意外归档。预览中会展示 id,拼写错误在此被捕获。
  • id 既可写作 --ids a,b,也可作裸位置参数(mio memory archive mem_a mem_b)。id 中绝不含逗号。
  • 无变更时以非零退出。若每个 id 都未知、已归档或属于其它项目,命令会报告 not found / already archived 并以退出码 1 结束,而非假装成功。
  • 重复项使用 Latin token 与 CJK bigram 的 Jaccard 相似度,阈值 0.75,用并查集聚类,使 A~B, B~C 坍缩为一组。活跃记录超过 1500 条时跳过扫描,改为报告 duplicatesSkipped,避免在 O(n²) 工作上卡死。

彻底遗忘(mio memory forget)

上面所有操作都是可逆的:archive 只是打上 archived: true,merge 基于 archive, migrate 只改作用域。forget 是唯一不可逆的操作——它把记录真正删除。

mio memory forget --ids mem_secret --project demo            # 预览
mio memory forget --ids mem_secret --project demo --yes      # 真正删除
Forget preview: 1 id(s) (project=demo)
  mem_secret  secret token abc123

WARNING: this permanently deletes the record(s) and cannot be undone.
An audit entry (excerpt only) is written to memory-forget-audit.jsonl.
Reversible alternative: mio memory archive --ids mem_secret --yes
Re-run with --yes to delete permanently.

几条硬性保证(均有测试覆盖):

  • 预览显示"将要消失的是什么",而不只是 id——这是唯一删了就没了的操作。
  • 审计先行:先把条目写进 <MIO_HOME>/memory-forget-audit.jsonl 再删除, 所以任何一次删除事后都能回答"删掉了什么"。
  • 审计只存摘要,不存全文(excerpt 取前 120 字符 + contentLength + kind), 不会在你要求删除之后又悄悄把全文留下来。
  • 跨项目绝不误删:id 属于其它项目时按 not found 处理,原记录保留。
  • 全部未命中以退出码 1 结束,避免一次 typo 被当成成功。
  • 预览与 --json 里都会给出可逆替代方案(mio memory archive)。

除非记录必须真的消失(例如误存了凭据),否则请优先用 archive。

合并重复项

analyze 会报告重复组但无法消解——逐个手动归档会让幸存者丢失它吸收的那些内容。mio memory merge 解决了这个问题:

mio memory merge --ids mem_a,mem_b --yes                    # 最新者胜出
mio memory merge --ids mem_a,mem_b --keep mem_a --yes       # 指定幸存者

默认只合并字节完全一致的内容。 这是刻意为之的安全立场,而非疏漏:在真实 860 条记录的存储上实测,22 个重复组中有 19 个字节完全一致,3 个不一致。拼接分歧内容会破坏它们——一个真实案例产生了一条同时携带 Token 来源 和 凭证来源 同一字段、还多了重复表头的记录。因此当组内成员不一致时,merge 会拒绝并打印候选,而不是猜测:

mio memory merge --ids mem_a,mem_b --yes
# No merge applied: content differs across records; refusing to concatenate
#   - mem_a (1167 chars, 2026-09-09T14:25:56.735Z)
#   - mem_b (1174 chars, 2026-09-09T14:31:00.898Z)

要消解这样的组,必须指定保留哪份内容,这正是 --keep 的用途;--allow-divergent 表示承认其它成员是被丢弃而非合并:

mio memory merge --ids mem_a,mem_b --keep mem_b --allow-divergent --yes

合并会写入的内容:

  • 幸存者原样保留自己的正文,并获得 supersedes: [<archived ids>]、mergedAt、mergedCount,以及合并后的标签列表,不丢失任何检索关键词。
  • 其它成员以 archiveReason: "merged-into:<survivor>" 归档,并带上 mergedInto 指针。
  • 它可逆:mio memory restore --ids <archived ids> 能把整组恢复。审计字段保留在记录上,因此合并历史在撤销后依然存在。
  • 没有可合并内容时的重跑会以退出码 1 报告 nothing left to merge——不会为它没归档的记录打印撤销命令。

策略检查

mio.policy.check 一直能从 trace 日志回答「这个动作历史上是否有风险?」,但此前只通过 MCP 提供。mio policy check 把它带到了终端:

mio policy check "npm publish"
mio policy check "git reset --hard" --project akemi-mio
mio policy check "npm publish" --json
Policy check: "npm publish" (project=akemi-mio)
risk: HIGH (0.5)
evidence: 2 matching trace(s), 1 failure(s)
outcomes: failure=1 success=1

Recent failures:
  - E403 token lacks bypass_2fa

Related memory:
  - mem_pub  npm publish requires a granular token with bypass_2fa enabled

Historically risky: ... 

说明:

  • 它读取全局 MIO_HOME,而非每项目目录。 MCP 服务端把数据目录解析为 MIO_DATA_DIR || cwd/.mio-intelligence,因此从任意目录发起的 MCP 调用看到的是空存储;CLI 刻意指向累积的全局日志。mio mcp 是例外:它先把 MIO_DATA_DIR 钉到 MIO_HOME 再启动服务端,于是 stdio 入口与终端读写同一份数据。注意:各桌面宿主适配器(codex、opencode、workbuddy、hermes、claude-code)在启动 MCP 服务端时会显式传入各自的 MIO_DATA_DIR(指向宿主的配置目录),因此它们不共享 CLI 的 MIO_HOME;只有 mio mcp(直接 stdio 连接)才与终端读写同一份数据。用 --project 把 trace 集合限定到某个项目。

  • 风险即失败占比,>= 0.4 为高,>= 0.2 为中。retry 与 aborted 与 failure、error 一并计为失败。无匹配历史报告 UNKNOWN,与 LOW(干净记录)不同。

  • 匹配可能被通用 token 带偏。 匹配是对动作 token 的宽松 OR,所以像 task 这样的动作几乎匹配每条 trace,因为关键词 task 几乎出现在所有 payload 中。当动作中每个 token 都至少出现在半数候选 trace 里时,CLI 会打印警告,而不是让等级自说自话:

    WARNING: low-signal match. Every token in this action (task) appears in
    most traces regardless of subject, so the level above is not meaningful.
    Try a more specific action, e.g. mio policy check "npm publish".

    这把弱点暴露在了 CLI 层;MCP 的匹配行为不变,且 diagnostics 是新增字段,既有消费者继续可用。

  • 小样本会标注。 匹配 trace 少于 5 条时打印 NOTE: only N matching trace(s); treat the level above as a weak signal.

  • 属于动作的 flag 会被保留。 只有 --project 与 --json 作为选项被消费,因此 git reset --hard 会原样透传,而非被截断成 git reset。

创意引擎

mio.creativity.status 与 mio.creativity.list 自引擎落地起就在 MCP 面上,但此前没有终端入口。mio creativity 把只读一侧带到了 CLI:

mio creativity status
mio creativity list --status rejected --limit 10
mio creativity list --json
Creativity engine:
  hypotheses: 5  combos: 3  experiments: 1
  active: 2  validated: 1  rejected: 1  draft: 1
  adoption: 2 adopted (events=2, tags=2) — derived join, not a metric

Recent top ideas:
- Plugin architecture  (novelty=80 feasibility=70 impact=90 score=240)
    idea_1787936687555_0

说明:

  • 它委托给与 MCP 服务端相同的 CreativityEngine,因此终端的 mio creativity list 看到的是与 mio.creativity.list 工具完全一致的假设。存储位于 <MIO_HOME>/creativity/creativity-hypotheses.jsonl;CLI 指向全局 MIO_HOME,与 mio policy check 一致。
  • list 默认只返回最新 20 条,与 mio.creativity.list 的 schema 承诺一致;--limit 0(或 MCP 的 limit: 0)才是「全部」。存储很大时默认全量返回会把工具结果撑爆。
  • status 计入 draft。 draft 是发酵中的真实状态(ferment 会同时处理 draft 与 active),此前漏计导致 status 与 list --status draft 对不上。
  • 空存储是正常状态。 引擎运行之前,status 报告全零计数,list 显示 "No hypotheses match." 两者都是有效输出,不是错误。
  • 缓存会在文件变化时失效。 三个 JSONL 的读取按 (size, mtime) 校验,因此另一个进程写入后长驻的 MCP 服务端无需重启就能看到——与 server/insight-store.js 同一套守卫。

采用证据链(adopt / status / list)

假设的「采用」不在存储里,而是三轴分离的证据链——store 只管评审,采用是假设与外部世界的关系:

  • 评审轴(creativity-hypotheses.jsonl):draft → active → validated / rejected,假说自身的生命周期,由 generate / ferment 推进。
  • 声称轴(traces.jsonl):mio.creativity.adopt(MCP mio.creativity.adopt)追加 creativity.adopt 事件,payload.hypothesisId 即声称;只写事件、绝不碰 memory,校验失败零写入。
  • 证据轴(memory.jsonl):mio memory record ... --hypothesis-id <uuid>(MCP 参数 hypothesisId)把 hypothesis:<uuid> 注入记录 tags;mio memory query --tags hypothesis:<uuid> 即按标签召回。AGENTS.md 规范:采用假设做决策时 memory.record 必带 hypothesisId——这是把决策与假设挂上钩的唯一写入口。

status / list 在读侧把两轴 join 成派生计数(mio.creativity.status 返回):

"adoption": {
  "adopted": 2, "claimed": 3, "evidenced": 2,
  "metric": false,
  "note": "derived join of creativity.adopt events and hypothesis:<id> memory tags — informational; no rate or threshold by design",
  "sources": {
    "events": { "file": "traces.jsonl", "durable": true },
    "memory": { "file": "memory.jsonl", "durable": true }
  }
}
  • claimed / evidenced 是两轴各自的 distinct id 原始计数(可以含已不在存储中的悬空 id);adopted = 两集合并集与存储假设 id 的交集——只有它回答「存下来的假设里哪些被采用过」。原始与 join 并报,悬空证据因此可见而不是被静默吞掉。
  • metric: false 是承重字段:这是信息性派生 join,不是 ADR-017 评估指标——没有 adoption rate、没有阈值。sources 照 routeAdoption 的模式注明每个轴的背书文件与 durable 性,于是「0 adopted(真没人用)」与「证据文件没留存(不可测)」能区分开。
  • list 每行带 adopted: boolean,文本模式在状态后显示 [adopted] 标记;JSON 输出即引擎结果,MCP 与 CLI 两侧天然一致。

生成与发酵(generate / ferment)

这两个子命令会调用 LLM,mio creativity 同样支持它们——用的是共享的 LLM 客户端 (server/llm-client.js),与 MCP 服务端完全同一实现:

mio creativity generate --source "auth|token rotation" --source "cache|write-through"
mio creativity generate --from-insights --source "auth|token rotation"
mio creativity ferment --limit 3
  • --source "名称|内容" 至少两个:引擎是把概念两两配对来产生新假设的, 一个来源在构造上就不可能产出组合(此时不会调用 LLM,直接返回 need at least 2 sources)。显式来源不足 2 个时,本地自动来源(memory / traces / 已存假设;MCP 侧还含 insight 与 observer 趋势)会先把列表补齐到 2——显式 --source 永远排在前面且不被替换;补齐后仍不足 2 个才报上面的错。
  • --from-insights(MCP 侧为 fromInsights: true)用已存洞察补足概念来源: 读取 mio insight list 能看到的同一份存储,按分数取前 10 条映射成 { name, content, type: 'insight' },再与显式的 --source 合并。它只在 @akemi-mio/insight 已安装且至少存有一条洞察时有意义,否则明确报错而不是 静默当作「没有来源」。因为来源可由它提供,mio.creativity.generate 的 sources 不再是 schema 必填项。
  • 判重粒度是(素材对 × --strategy)。 同一对素材在 explore / signal / stable 下各算一次实验——「409 就换 strategy 重试」是有效的降级而不是空转; 三种 strategy 都试过、且没有新素材时才真正耗尽,此时返回的 reason 会带上 strategy、来源数与候选对数(外加 strategy 与 pairsAvailable: 0 字段), 调用方可以据此区分「换个 strategy 有用」与「该补新素材了」。素材身份取名称 去空白后的前 120 个字符(--source 省略 | 时 name 会退化成整段描述, 描述尾部微调不得绕过判重);没有 strategy 字段的旧记录仍挡住所有 strategy。
  • ferment 会复核 active / draft 假设并更新分数;verdict=promote 且总分 > 200 时 升为 validated,verdict=reject 则标记为 rejected。
  • 失败的 LLM 调用会出现在结果里,不再被吞掉。 返回值带 errors (generate 是 pair + 原因,ferment 是 id + 原因),CLI 也会在正文后 打印 N pair(s) failed:。此前 catch {} 让「端点挂了」与「这一对确实没有 新意」长得一模一样——ideas: [] / Nothing fermented 两种输出都无法区分。

目标驱动的 idea 流水线(mio idea generate)

一个 goal 直接产出可实验的假设(MCP 面为 mio.idea.generate),来源全程本地优先组装:

mio idea generate --goal "降低 MCP 调用延迟" --context "近三天 p95 上升" --constraint "不引入新依赖"
mio idea generate --goal "扩大命令覆盖" --json
  • 来源顺序:goal → context → constraints → 记忆 grounding(取前 3 条,scope: all)→ 本地自动来源(memory / traces / 已存假设,MCP 侧还含 insight 与 observer 趋势),按 origin 去重;凑不足 2 个时返回 reason 文案而不是报错。
  • 每条 idea 都持久化到 <MIO_HOME>/creativity/creativity-hypotheses.jsonl(draft,带 provenance:strategy / technique / relatedMemoryIds / 命中来源 / generatedAt),绝不写入 Mio memory。
  • novelty 门禁:与已 rejected 假设近似重复的候选落盘为 rejected 并带 rejectionReason;与近期假设相似的只降分。拒绝的假设同样入库,供后续去重。
  • --num 钳制在 1..3(默认 3);--json 返回完整结构 { ideas, generatedAt, groundedWith, persistedIds }。

LLM 配置

只有 5 条命令会调用大模型:mio creativity generate / ferment、 mio idea generate、mio insight generate、mio observer ferment。它们共用 server/llm-client.js 这一份实现,配置也共用同一套解析顺序:

环境变量  >  MIO_HOME/config.json 的 llm 块  >  内置默认值

写入配置文件(换机器/换终端不必重设):

mio config llm --url http://localhost:11434/v1/chat/completions \
               --model qwen2.5:14b
# 需要鉴权时再加 --key <token>;清空用 --clear

查看当前生效值及其来源:

mio config show

show 会逐字段标注该值来自环境变量、config.json 还是内置默认, 并在环境变量遮挡了文件配置时明确警告——只报值不报来源,正是 「改了 config.json 却毫无变化」的常见成因。

也可以用环境变量(优先级更高,便于单次覆盖):

| 变量 | 说明 | |---|---| | LLM_API_URL | OpenAI 兼容端点(默认 opencode zen) | | LLM_KEY | Bearer token;留空表示无需鉴权(本地 Ollama 常见) | | LLM_CHAT_MODEL | 模型 id,回退到 LLM_MODEL |

LLM_API_URL=http://localhost:11434/v1/chat/completions mio creativity ferment

未配置任何 LLM 时,命令会先提示它将要调用默认端点,再继续—— 不会静默地把请求发到你没配的地方。

洞察自省与观察管线

mio.insight.* 与 mio.observer.* 此前都只有 MCP 入口:agent 会话里能读到,终端里看不到。mio insight 与 mio observer 补上了这两块的终端入口。

# 洞察自省(需要可选包 @akemi-mio/insight)
mio insight status
mio insight list --unreported --min-score 0.7
mio insight mark-reported --ids ins_1,ins_2
mio insight generate --memory "decision|把记忆解析抽到共享 store" --summary "刚发了 0.7.0"

# 观察研究管线(纯文件读取,不依赖可选包)
mio observer status
mio observer trends --limit 5
mio observer trends --date 2026-09-12
mio observer dag --days 14
mio observer essays --type published
mio observer world-model
mio observer research --json
mio observer trends --base-dir /path/to/.local/observer

说明:

  • 两者都委托给与 MCP 服务端完全相同的共享实现 —— server/insight-store.js 与 server/observer-store.js。这是 memory-store.js / experience-store.js / policy-store.js / creativity-engine.js / agent-store.js 一路沿用的同一个模式:一份实现、两个入口,因此不可能各自漂移。

  • insight generate 曾经是 MCP 专有(mio.insight.generate):它要调 LLM,CLI 一度把它当作未知子命令拒绝——现在四个 insight 子命令在终端上都有入口;--memory 与 --summary 至少给一个,否则在任何 LLM 调用之前就拒绝。observer collect / observer ferment 此前也被一起挡在 CLI 之外,理由是「它们属于 daemon」——这个理由不成立:observe/observer.js 那条 daemon 只 tail WorkBuddy 的 transcript,从不调用这两者。现在它们有了终端入口,默认走全部已配置源:

    mio observer collect                                  # 全部已配置源
    mio observer collect --sources rss,github --limit 20  # 指定源
    mio observer collect --sources rss --keywords mcp,cli # 关键词 OR 过滤
    mio observer ferment --session morning                # morning|afternoon|night

    两者都依赖可选包 @akemi-mio/observer(未安装时提示 not installed,不是空结果)。collect 会联网抓取,ferment 需要 LLM;它们不做预览,因为预览意味着把数据抓两遍。单个源失败不会中断整次运行——该源会以 errors: ... 出现在输出里,其余源照常统计。collect 会联网抓取,ferment 需要 LLM;它们不做预览,因为预览意味着把数据抓两遍。单个源失败不会中断整次运行——该源会以 errors: ... 出现在输出里,其余源照常统计。

  • mio observer pipeline 是研究 DAG 的入口(collect → trend → tension → research → multi-brain → compose → world model → publish → self-evolve)。此前 runPipeline / tickPipeline / forcePipeline 在 mio-agent-runtime 里没有任何调用方,所以 trends / research / insights 默认永远是空的,只有手动实例化服务才能跑通。它同样依赖 @akemi-mio/observer,会联网并调用多次 LLM,因此和 subscribe 一样默认只预览(读今天的 DAG 状态与解析后的 LLM 端点,不构造服务、不建目录),--run 才真正执行:

    mio observer pipeline                    # 预览(离线、秒回)
    mio observer pipeline --run              # 执行完整 DAG
    mio observer pipeline --mode creative --run
  • mio observer serve 让"日更引擎"真正自动跑起来(D6)。ObserverService.start() 早就配好了各源的采集间隔、60 秒一次的管道 tick 与 5 秒后的首次 tick,并在"今天的 DAG 已 COMPLETED"时跳过——但 runtime 里没有任何长驻进程调用过它,这些闸门一次都没生效过,trends / research / insights 只有手动 mio observer pipeline --run 才会出现。现在有了宿主:

    mio observer serve              # 前台常驻:采集 + 每日一次完整 DAG(Ctrl+C 停)
    mio observer serve --dry-run    # 只打印计划:baseDir / LLM / tick 周期 / 锁文件
    mio observe --start --research  # 顺带把调度器作为第二个受 pid 管理的子进程拉起
    • --dry-run 不构造服务、不建目录、不联网,因此也是文档门禁实跑的参数(SAFE_ARGS['observer:serve'])。
    • --research 是显式开关:默认 mio observe --start 只启动转写观察守护,不会悄悄开始花 LLM 调用。研究子进程日志写到 <MIO_HOME>/research.log(用 append fd 而不是管道:没人读的管道写满 64KB 就会把调度器卡死),mio observe --status --json 给出它的 pid 与日志路径,mio observe --stop 一并停掉两者。
    • 研究数据仍落在启动它的那个工作目录的 <cwd>/.local/observer(与 mio observer status 一致):子进程显式收到 --base-dir,因为转写守护 spawn 时 cwd 被钉成 MIO_HOME,若在守护进程里直接起调度,会多出一套谁也读不到的观察目录。
    • 刻意不提供 mio.observer.serve 这类 MCP 工具:每个连上的 MCP server 都会各起一个调度器,客户端一重启就多一个。
    • ObserverService 内的 pipelineRunning 只防同进程并发;serve 子进程、守护与手动 --run 是三个进程,所以 runPipeline 现在还要抢 <baseDir>/dag/pipeline.lock(O_EXCL 建文件 + pid / mtime TTL,持有者已死即可接管)。锁被占时直接返回 null,不排队。
  • 采集会走系统与环境里配置的代理(D7)。6 个采集源共 9 处请求此前直接用全局 fetch,而 Node 的 fetch 不读任何代理配置——所以浏览器能打开的页面,采集全部超时(issue #3:Windows 开着系统代理、直连不通时,weibo / rss / hackernews / douyin / bilibili / github-trending 全军覆没)。现在统一走 @akemi-mio/observer 的 httpFetch():

    • 代理按顺序取:HTTPS_PROXY / HTTP_PROXY / ALL_PROXY(大小写皆可)→ Windows 系统代理(reg query 读 HKCU Internet Settings 的 ProxyEnable / ProxyServer / ProxyOverride,进程内缓存 30 秒)。ProxyServer 的 h=..;https=.. 与单值两种写法都认,端口读实时注册表——issue 里写的 7990 早已不是本机端口,硬编码必然过期。
    • 绕过:NO_PROXY 与 ProxyOverride,支持 *、<local>、*.zhihu.com、127.*、裸域名(含子域)。
    • 解析不到代理就走原生 fetch,与从前逐字节一致;解析到代理时用 undici 的 ProxyAgent(CONNECT 隧道,https 与 http 目标同样处理)。代理连不上(ECONNREFUSED / ENOTFOUND / EHOSTUNREACH)会回退直连并记 proxy_fallback_direct,避免"系统代理开着但客户端已关"把原本能通的直连也搞坏。
    • ObserverLlmService 不在这条通路上(issue 只讲采集失败)。
  • 观察者的 LLM 现在与 creativity / insight 共用同一份配置。此前 ObserverLlmService 把 Ollama 地址与模型写死(localhost:11434 / qwen2.5:7b,只认 OBSERVER_* 环境变量),完全无视 mio config llm —— 于是 mio config llm 配好的 deepseek 只对 server 侧生效,观察管道仍然空转。现在 observer-store.js 通过共享的 server/llm-client.js 解析配置并注入:用户配置过(config.json 的 llm 或 LLM_* 环境变量)就用它,否则保持 @akemi-mio/observer 自己的 LLM_* / OBSERVER_* / 本地 Ollama 回退,不会把既有本地 Ollama 用户静默改道到托管默认值。端点按 URL 形态自动选择传输:含 /chat/completions 走 OpenAI 兼容协议,否则按 Ollama 处理。

  • mio observer ingest 记录任意 trace 事件(tool_call / error / retry / task_outcome):

    mio observer ingest --trace-id t1 --event-type tool_call --payload '{"tool":"Bash"}' --project demo

    它是追加而非修改,所以像 mio remember 一样直接写入、不做预览;但缺 --trace-id / --event-type,或 --payload 不是合法 JSON 对象时,会在写入任何东西之前拒绝。 只想记录任务结果时用 mio task record-outcome 更合适——它更专用,还会一并更新该 agent 的 taskCount / successCount / failureCount。

订阅与摘要(subscribe / digest)

mio observer subscribe --event-types tool_call,error --project demo        # 预览
mio observer subscribe --event-types tool_call,error --project demo --yes  # 订阅
mio observer ingest --trace-id t1 --event-type tool_call --project demo
mio observer digest --project demo
Observer digest: 1 event(s) (project=demo, agent=cli)
  subscriptions: 1 active, 1 matched
- tool_call trace=t1
Note: this advances the cursor; the next digest returns only newer events.
  • subscribe 是写操作,默认只预览(exit 1、不落盘),--yes 才写。相同的 agent+project+事件类型+主题视为同一条订阅,重复执行是续期而非新增一行。

  • digest 是基于游标的:只返回上次投递之后的新事件,并推进游标。 所以「第二次跑返回 0 条」是正确行为,不是坏了——输出里也明确写了这一点, 免得让人误以为订阅失效了。

  • 因此 digest 无法预览(不跑就不知道有什么),但它会写游标这件事在输出与 mio observer help 里都写明了。

  • 订阅、traces、游标都在 <MIO_HOME> 下(与研究管线的 .local/observer 不同), 所以这组能力放在 server/subscription-store.js,与 observer-store.js 分开。

  • 数据位置不同,这是有意的。 洞察存储在 <MIO_HOME>/insights/insights.json,与 mio recall / mio policy check 同处全局 MIO_HOME;观察研究管线则是按项目的,默认 <cwd>/.local/observer(与 MCP 服务端的默认值一致),可用 --base-dir 覆盖。

  • @akemi-mio/insight 是可选依赖。 未安装时 mio insight status 会失败并提示 @akemi-mio/insight not installed,而不是报告一个「看起来没有洞察」的全零结果——全零会掩盖「引擎根本没装」这件事。

  • 观察管线的空目录是正常状态。 管线没跑过时,observer status 各阶段计数为 0 并提示 "No pipeline data yet.",其余视图显示 "No ... found.",都是有效输出而非错误。

演化(evolution)

mio.evolution.* 一族也是「一份实现、两个入口」,但它和前面几节有一处形态差异: 参数是内联的 JSON 快照,不是文件路径。 这是这一族最常被误用的地方——--legacy 指的是 「旧实现这次跑出来的结果」,不是某个待读取的文件。

mio evolution status
mio evolution report --period 7d

# 影子比对:把「旧实现」和「模块化实现」各自的输出喂进来做深比较
mio evolution shadow record --legacy '{"modules":7}' --modular '{"modules":7}'
mio evolution shadow record --project akemi-mio --label "status parity" \
  --legacy '{"modules":7}' --modular '{"modules":9}'

# 双写样本:--authoritative 声明谁是权威(省略即 legacy),两边各给一份写入结果
mio evolution dual-write record --authoritative legacy \
  --legacy-result '{"id":"mem-1","ok":true}' \
  --modular-result '{"id":"mem-1","ok":true}'

# 就绪度读的是上面两个台账,不是你「觉得」跑了几次
mio evolution cutover readiness
mio evolution cutover readiness --min-shadow-runs 5 --max-mismatch-rate 0.1
Shadow comparison shadow_1789912345678_ab12cd: mismatch
diffs=1

Cutover readiness for akemi-mio: fail
shadow=2 dual-write=0
reasons: shadow samples 2/5; shadow mismatch rate 0.5; dual-write has no samples

各子命令的参数:

| 子命令 | 必填 | 可选 | |---|---|---| | status | 无 | 无 | | report | 无 | --period(24h / 7d / 30d / all)、--project | | shadow record | --legacy <json>、--modular <json> | --project、--label、--input <json> | | dual-write record | --legacy-result <json>、--modular-result <json> | --authoritative(legacy 或 modular,默认 legacy)、--project、--label、--record <json> | | cutover readiness | 无 | --project、--min-shadow-runs N、--max-mismatch-rate N | | authority plan | --readiness <json> | --from、--to | | migration plan | --legacy-records <json>、--modular-records <json> | 无 | | cutover apply | --dry-run 和 --plan <json> | --project |

说明:

  • 缺参时会先给出原因(--legacy is required),紧接着打印完整用法与示例,包括「参数是内联 JSON」这件事。用法写到 stderr、stdout 保持为空(这是全命令族的约定,见上面 输出流与退出码 一节)——所以 mio --json 的调用方拿到的是空输出, 而不是把用法文本当成 JSON 去解析。值不是合法 JSON 时同理,报 --legacy must be valid JSON。 子命令写漏了(如 mio evolution cutover)会明确说缺哪个:needs 'readiness' or 'apply'。
  • cutover apply 是 dry-run only,而且 --dry-run 必须显式写上。 只给 --plan 会被拒绝 (authority switch apply is dry-run only; pass dryRun: true ...),只给 --dry-run 则报 --plan is required。它不会真的切换权威,返回里 applied 恒为 false。
  • 记样本会立刻影响 cutover readiness。 记录落在 <MIO_HOME>/evolution_shadow.jsonl 与 <MIO_HOME>/evolution_dual_write.jsonl,readiness 读的就是这两个文件,所以别拿测试数据 往真实 MIO_HOME 里写:shadow mismatch rate 会被污染,之后真正的切换判据跟着失真。 想试跑就把 MIO_HOME 指到临时目录。
  • 两边深比较相等 → matched;不等 → mismatch 并给出 diffs=N。
  • --project 省略时取当前 git 仓库名,与 mio recall / mio policy check 的默认口径一致。
  • --json 是全局 flag,必须写在子命令前面:mio --json evolution cutover readiness。 写在后面不生效。

Agent

本仓库里有两种不同的「agent」概念,CLI 把它们分开以免混淆:

  • 已安装的宿主适配器(mio agents,无子命令)来自 config.json——哪些宿主(codex/opencode/workbuddy/hermes/claude)已通过 mio install 接好。这是配置状态。
  • 被观察的 agent(mio agents list / mio agents report)来自 agents.jsonl,并交叉引用 traces.jsonl、memory.jsonl 与 experience_reuse.jsonl。这是运行时遥测:每个 agent 跑了多少任务、成功多少、产生了多少记忆与已验证复用。
mio agents                      # 已安装的宿主适配器
mio agents list                # 被观察的 agent(--project 限定范围)
mio agents report              # 每 agent 的任务/记忆/复用遥测
mio agents report --agent codex
Agent report (project=akemi-mio): 2 agent(s)
codex (mcp)  idle
   tasks: 5 total, 5 success, 0 failure (100% success)
   memories: 88  experience reuses: 54 (verified 6)
   last seen: 2026-09-06 00:57:03 | sessions=2

两个子命令都委托给 server/agent-store.js——与 mio.agent.list / mio.agent.report / mio.agent.register MCP 工具相同的存储,因此终端与 MCP 服务端报告一致的遥测,不可能漂移。list 读取 agents.jsonl;report 叠加 trace/memory/reuse 的交叉引用。

评估期指标(mio agents evaluation)

ADR-017 用四项指标决定控制平面是继续扩展还是回滚。实现见 server/evaluation-store.js,MCP 侧为 mio.agent.evaluation。

| 指标 | 口径 | 数据来源 | |---|---|---| | 路由采纳率 | 命中且被采纳 / 命中 | queries.jsonl ⚠️ | | 行为改变率 | confirmed 且 behaviorChanged / 全部 reuse | experience_reuse.jsonl | | 召回质量 | 结果导向 reuse 的查询 / 有结果的查询 | queries.jsonl ⚠️ | | 数据卫生 | 未确认 auto-claim / 全部 reuse | experience_reuse.jsonl |

无样本时输出 no data,而不是 0%:0% 的意思是「测过了、很差」, no data 才是「没测」。--json 下对应 null,客户端必须区分这两种情况。

报告会带出 Phase 0 基线作对照。 ADR 要求这些数字「对照 Phase 0 基线 2026-08-17」来读,但基线此前只作为散文存在于 ADR 里,报告给的是没有参照点的裸值。 现在 baseline 字段(--json)与输出里的 baseline … 行会并列给出基线值。 基线以 observed / required 形式陈述(与 phase0.js 的 current/required 渲染一致)。 这一条对 verified 尤其重要:它已从基线的 6 跌到 0,裸看只是一句「0」, 对照读才是「比起步时少了 6、跌破门槛」。

⚠️ 前两项指标读的是一个设计上就会变空的缓冲区。queries.jsonl 不是评估日志, 而是 auto-claim 的关联缓冲(见 server/query-log.js):最多 200 条,超过 expiresAt(默认 1 小时)就被清理,retention.js 也把它列在 EXPIRY_BASED_FILES。 所以这两项在实际使用中几乎总是 no data——不是因为路由没人用,而是因为它的证据 被有意设计成不长期保留。报告里会打印一句说明,避免读者把「缓冲区为空」误读成 「功能没被使用」。

⚠️ 行为改变率停在 0% 通常是确认纪律问题,不是功能问题。未确认的 auto-claim 既不进记忆排序也不进任务路由,是「哑重」;mio.experience.confirm 会把 behaviorChanged 置为 true,从而把它们转入有效证据。真实数据核查发现:确认这一步 在生产里从未被执行过(全部 reuse 记录的 confirmed 字段缺省),于是行为改变率恒为 0%、数据卫生恒为「93% 未确认」。因此 mio status 现在会直接报出全局待确认数以及 其中跨 Agent 的条数:

mio status
mio experience list --status pending          # 看具体是哪些
mio experience confirm --ids a,b,c             # 确认(见下方口径)

⚠️ 但「待确认 20 条」不等于「该确认 20 条」,不要批量全确认。 ADR-017 的确认口径 (docs/adr-017-mio-agent-control-plane.md,原文出现两次)是:

仅跨 Agent 且真实改变行为才 experience.confirm;同 Agent / 弱关联 / 自报 一律不确认,避免数据污染。

同 Agent 的 auto-claim(如 codex -> codex)在定义上就不可能满足这条,所以原始 pending 数高估了可确认的积压。mio status 因此同时报出跨 Agent 的条数,例如 Pending auto-claims: 20 (12 cross-agent)——那 8 条同 Agent 的不应被确认。

跨 Agent 也只是必要条件:确认前仍须人工核对该 target agent 的真实输出是否 明确引用了 source 记忆并据此改变方案。把不合条件的记录一并 confirm,正是这条 ADR 要防的数据污染,而且会让行为改变率这个指标本身失真。

注册(mio agents register)

register 是这一组里唯一的写操作,因此沿用与 mio memory archive 相同的约定: 默认只预览,加 --yes 才落盘,--json 模式同样受限。

mio agents register --agent-id my-agent --project demo --capabilities code,test
mio agents register --agent-id my-agent --project demo --yes    # 真正写入
Register preview: my-agent (project=demo, host=mcp)
  will create a new agent record
  capabilities: code, test
Re-run with --yes to apply.
  • 预览时不会创建 agents.jsonl——这点有测试钉住(预览跑完文件不存在)。
  • 已存在时预览会显示 will update existing agent (sessionCount 4 -> 5), 而不是含糊地说"将注册"。
  • --yes 首次创建(sessionCount: 1),再次执行则更新 lastSeenAt + sessionCount,不会产生重复行。
  • 更新时若省略 --capabilities,原有能力保留(只有显式传入非空列表才覆盖)。

与 mio host capabilities 的区别

第三个与 host 相关的命令是 mio host capabilities,它回答的是另两个问题: 每个宿主支持什么能力(静态表),以及此刻是否真的装上了(实时探测各 adapter)。

Host capabilities: 5 host(s)
  codex      not installed
             mcp-tools, memory, observer-ingest, policy-check, experience-reuse, runtime-status
  opencode   installed
             mcp-tools, memory, observer-ingest, policy-check, experience-reuse, runtime-status

它与 mio agents 不是一回事,两者不一致时本身就是有用的诊断信号:

| 命令 | 数据来源 | 含义 | |---|---|---| | mio agents | config.json | 记录"曾经安装过"(含安装时间) | | mio host capabilities | 实时探测 adapter | 本机现在是否真的装上了 |

例如配置文件还在、但宿主目录已被删掉时,mio agents 仍会列出它,而 host capabilities 显示 not installed。

任务路由(mio task route)

回答的是"这类任务历史上谁做成过、按哪条已验证经验走":把已验证的复用经验 (confirmed + reuse + behaviorChanged + outcomeImproved 四项全真)与任务描述 做相关性匹配,再叠加 digest 快照里的 agent 健康信号。

mio task route "publish npm package"
mio task route "mio.experience.confirm 复用证据" --project akemi-mio --limit 3
mio task route "deploy service" --json
Task route: "mio.experience.confirm 复用证据" (project=akemi-mio, scope=project)
verified routes: 5

Routes (apply the top match first):
1. [score 23.5] mem_1786932717070_22577ed0f57a — reused 2x (confirmed)
   新增 mio.experience.confirm(worktree ...):把 source=auto_claim 的复用证据升级为已确认...
   codex -> opencode

与 MCP 唯一的行为差异:不写查询日志

mio.task.route 会把这次查询写进 queries.jsonl——这是 Phase 0 自动认领 (auto-claim)机制的输入:之后的 task_outcome 才能把"路由命中→任务成功" 关联成复用证据。这个写入是承重的,不能去掉。

但终端里的 mio task route 是一次查看,每跑一次就往查询日志塞一条是不对的。 所以 store 提供了 persistQuery 开关:

| 调用方 | persistQuery | 是否写 queries.jsonl | |---|---|---| | MCP mio.task.route | 默认 true | ✅ 写(保持原行为) | | CLI mio task route | false | ❌ 不写 |

测试里两条都钉住了:CLI 跑完 queries.jsonl 不存在;同一 store 传 persistQuery: true 则确实写入——证明差异来自开关,而不是代码路径坏了。

记录结果(mio task record-outcome)

把任务结果写回证据库,与上面的路由形成闭环: 路由 → 执行 → 记录结果 → 变成新的复用证据。

mio task record-outcome --outcome success --task "deploy service" --summary "all green"
mio task record-outcome --outcome failure --task "deploy service" --yes    # 真正写入

它做三件事:写一条 task_outcome trace → 触发 auto-claim(把此前匹配的查询 关联成复用证据)→ 更新该 agent 的 taskCount / successCount / failureCount。

Record outcome preview: success (project=demo, agent=cli)
  task: deploy it
  agent not registered: trace only, agent registry untouched
  register it with: mio agents register --agent-id cli --yes
Re-run with --yes to apply.

同样是写操作,因此同样默认只预览:

  • 预览不会创建 traces.jsonl(有测试钉住)。
  • --outcome 非法时在写入任何东西之前就拒绝——不会出现"写了一半"。
  • 预览会告诉你 agent 是否已注册:未注册时明确说"只写 trace、不更新注册表" 并给出注册命令,而不是默默少做一件事。
  • 已注册时预览显示 will update agent (taskCount 4 -> 5, successCount 3 -> 4)。

发布验证

发布运行时包之前,先运行:

npm run verify:pack-install --workspace mio-agent-runtime

对于 CI 或可通过 npm run 复现的发布证据,用环境变量设定确定性目录:

MIO_PACK_ARTIFACT_DIR=./dist/mio-pack-artifacts \
MIO_PACK_INSTALL_DIR=./dist/mio-pack-install \
MIO_PACK_REPORT_PATH=./dist/mio-pack-report.json \
npm run verify:pack-install --workspace mio-agent-runtime

PowerShell:

$env:MIO_PACK_ARTIFACT_DIR = './dist/mio-pack-artifacts'
$env:MIO_PACK_INSTALL_DIR = './dist/mio-pack-install'
$env:MIO_PACK_REPORT_PATH = './dist/mio-pack-report.json'
npm run verify:pack-install --workspace mio-agent-runtime

直接调用脚本时也支持命令行 flag:

node packages/mio-cli/scripts/verify-packed-runtime.js \
  --artifact-dir ./dist/mio-pack-artifacts \
  --install-dir ./dist/mio-pack-install \
  --report-path ./dist/mio-pack-report.json