Current section
Files
Jump to
Current section
Files
CHANGELOG.md
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.2.1] - 2026-04-25
**Patch release** — 修掉 v0.2.0 的 Reflection iteration-reset bug,并新增 Planning Pattern
与 Producer-Reviewer Reflection 两大 Agentic Design Patterns 能力。所有改动对 v0.2.0 完全
**向后兼容**;老 Plugin / 老 API 零改动即可升级。
### Fixed
- **`CMDC.Plugin.Builtin.Reflection` iteration reset 死循环** — v0.2.0 该插件在多轮评审时
`iteration` 会被意外重置,导致在某些回合顺序下 Agent 永远不会真正 finish。引入 `phase ::
:idle | :reviewing | :done` 状态机彻底修复,`:done` 之后 `:before_finish` 不再触发。
- **`CMDC.Plugin` behaviour `describe/0` callback 类型声明** — 放宽到 `String.t() | map()`,
消除 `CMDC.Plugin.Builtin.ModelRouter.describe/0` 的 dialyzer callback_type_mismatch 警告。
老插件返回 String 保持有效。
### Added — Phase 11D · Planning + Reflection(对齐 ADP Ch4+Ch6+Ch7+Ch11)
Agentic Design Patterns 对齐首批能力,基于 O'Reilly《Agentic Design Patterns》一书 Ch4 Reflection、
Ch6 Planning、Ch7 Multi-Agent Collaboration、Ch11 Goal Setting and Monitoring 四章设计。
- **`CMDC.Plan` / `CMDC.Plan.Step`(new)** — 显式 Plan 数据结构,含 `step_started/2`、`step_completed/3`、
`step_failed/3`、`step_skipped/3`、`annotate_step/3`、`approve/1`、`progress/1`、`to_markdown/1`、
`to_prompt_section/1`。每个 step 有 `:pending | :in_progress | :completed | :failed | :skipped` 状态。
- **`CMDC.Plan` replan API(ADP Ch6 适应性要求)** — `replace_steps/2` 整体重规划、`add_step/3` 追加、
`insert_step/4` 在指定锚点前/后插入、`remove_step/2` 删除。所有修改自动打 `last_replanned_at` 时戳。
- **`CMDC.Agent.State.dynamic_context_sections`(new)** — Agent 状态新增此字段,Plugin 可通过
`{:emit, {:update_system_context, key, text}, state}` 事件把内容动态注入下一轮 system prompt。
`SystemPrompt` 的 `:full / :task / :minimal` 三种模式都会合并这些动态段落。
- **`CMDC.Context.last_assistant_reply`(new)** — 只读 Context 新增此字段,Plugin / Tool 可直接拿到
最后一条 AI 回复全文,无需自己翻 `messages`。
- **`CMDC.Plugin.Builtin.Planning`(new)** — plan-first 规划插件。对较长 prompt 先 `intervene` 要求
LLM 产出 markdown checklist,`after_response` 解析为 `CMDC.Plan` 并通过 `dynamic_context_sections`
把 plan 持续注入 system prompt。失败重试(`max_plan_attempts: 2`)兜底,放弃后不阻塞主流程。
emit `{:plan_generated, %CMDC.Plan{}}`。
- **`CMDC.Plugin.Builtin.Reflection` 大重构(modified)** — 修掉 v0.2 的 iteration reset 死循环 bug:
- 新 `phase :: :idle | :reviewing | :done` 状态机,`:done` 之后 `:before_finish` 不再触发,杜绝无限循环。
- `max_reviews`(原 `max_iterations` 作为兼容别名)。`pass_signal` 支持字符串或 `%Regex{}`。
- **`reviewer_subagent: true`** — ADP Ch4+Ch7 推荐的 Producer-Reviewer 分离:启动独立 Reviewer
SubAgent(`CMDC.Agent.start_link` 新进程),可用 `reviewer_model` 指定不同模型评审主 Agent 输出,
显著降低同模型自评的认知偏差。SubAgent 失败自动降级为内联自评,永不阻塞主会话。
- emit `{:reflection_approved, %{critique, iteration}}`。
- **Agent before_prompt intervene(modified)** — `CMDC.Agent` 在 `idle` 状态处理 `{:prompt, text}`
和 `do_steer/5` 中的 `:passthrough_to_prompt` 分支,现在正确处理 `{:intervene, prompt, state}`
返回值:把 intervene prompt 作为 user 消息追加,并广播 `{:intervention, prompt}` 事件。
### Changed
- `CMDC.Agent.broadcast_emitted_events/2` 支持 3-tuple emit 事件(被包装成
`{:plugin_event, name, {a, b}}`),用于 `{:update_system_context, key, text}` 等 Plugin→Agent
状态同步。
- `CMDC.Agent.State.to_context/1` 自动填充 `last_assistant_reply`。
### Tests
- `test/cmdc/plan_test.exs` — `CMDC.Plan` 全量单测(新加 47 tests, 16 doctests)。
- `test/cmdc/plugin/builtin/planning_test.exs` — Planning 插件 14 tests。
- `test/cmdc/plugin/builtin/reflection_test.exs` — Reflection 18 tests(完整多轮循环、`phase` 状态机、
regex pass_signal、SubAgent 兜底降级)。
- `test/cmdc/agent/integration_test.exs` — 新增 Planning E2E 2 tests + Reflection SubAgent Reviewer
E2E 3 tests,含多轮 Producer-Reviewer 循环。
- 全量 **21 doctests + 1063 tests, 0 failures, 5 skipped**(v0.2.0 基线 1060 → v0.2.1 共 1063,
所有新模块单元测试累计 47 (Plan) + 14 (Planning) + 18 (Reflection) = 79,
集成测试累计 Planning E2E ×2 + Reflection SubAgent E2E ×3 = 5 条新端到端)。
- `mix dialyzer` passed successfully(v0.2.0 遗留 `model_router.ex` callback 警告一并修复)。
## [0.2.0] - 2026-04-24
This is a major feature release aligning CMDC with the 13 RFC proposals from the
Hive enterprise-agent team (April 2026) **plus** the four Phase 10 items originally
planned for v0.2 (`Steering`, `PromptMode`, `MemoryFlush`, `ModelRouter`).
All additions are **additive and backward-compatible** unless otherwise noted under
"Breaking". Integration code written for v0.1 continues to work with zero changes.
**Stats**: `+17` new features • `991` total tests (0 failures, 5 skipped) • `82.5%`
line coverage (up from 79.2%; four built-in plugins lifted from 0% → 100%) • `0` deprecations.
### Added — Facade API (RFC A1–A4)
- **`CMDC.Options.user_data`** (RFC A1) — opaque business-context map, propagated verbatim
into `CMDC.Context.user_data`. SubAgent and `Tool.Task` inherit automatically unless
the subagent explicitly sets its own `user_data`.
- **`CMDC.create_agent(messages: [...])`** (RFC A2) — inject prior history at boot so
resumed / cloned sessions continue the LLM turn sequence naturally; accepts
`%CMDC.Message{}` structs or maps.
- **`CMDC.approve/3` and `CMDC.reject/3` with `:auto_resume`** (RFC A3) — approve now
auto-resumes the turn by default (`auto_resume: true`); reject defaults to
`auto_resume: false`. Both emit `:agent_resumed` event when they actually restart a
turn, so UIs can follow without race conditions. Legacy `approve/2` / `reject/2`
still work (`auto_resume: true / false` defaults).
- **`normalize_session/1` + session-as-string everywhere** (RFC A4) — every public API
accepts `pid() | String.t()`; `subscribe/1` can target not-yet-started sessions
(common for UI pre-mount subscribe); `user_respond/3` broadcasts by session id
without SessionRegistry round-trip.
### Added — Protocol (RFC B5–B7)
- **`%CMDC.TokenUsage{}`** (RFC B5) — typed struct `{input, output, total, cache_read,
cache_write}`; all token-bearing events (`:after_response`, `:turn_complete`,
`:agent_finished`) now emit this struct. Legacy map format remains readable.
- **`CMDC.abort/2` options** (RFC B6) — `reason:` (defaults `:user_abort`, embedded in
`:agent_abort` event), `kill_tools:` (`:running` default / `:all` / `:none`),
`clear_queue:` (default `true`). Documented 4-state behavior matrix and the
`100ms within` emit guarantee for `:agent_abort`.
- **Plugin Hook × Action matrix** (RFC B7) — `docs/guides/plugin-matrix.md` with full
11×7 table plus 5 copy-paste Cookbook scenarios (block tool, rate-limit, audit
log, cost guard, steering-on-loop).
### Added — Runtime Control (RFC C8–C12)
- **`CMDC.switch_model/2`** (RFC C8) — change the LLM model mid-session; emits
`:model_switched` with `{from, to, reason, turn, ts}`. Also exposed as a Plugin
action `{:switch_model, new_model, state}` (used by the upgraded `ModelRouter`).
- **`CMDC.attach_tool/2` / `CMDC.detach_tool/2`** (RFC C9) — runtime toolset mutation.
Emits `:tool_attached` / `:tool_detached`. If the LLM later calls a detached tool,
the kernel emits `:tool_call_unknown` (no crash, no hang) and returns a tool_result
error so the next turn lets the LLM self-correct.
- **EventBus replay buffer + `subscribe(:since)`** (RFC C10) — every session keeps a
bounded ring of past events (default 500); subscribers can recover missed events on
reconnect via `CMDC.subscribe(session, since: ts | event_id)`.
- **`CMDC.status/1` extensions** (RFC C11) — now returns
`pending_tools`, `pending_approvals`, `pending_user_inputs`, `queue_sizes`
(pending prompts / steerings) so dashboards can reflect the true "what is the
agent blocked on right now".
- **`CMDC.monitor/1` + structured reason** (RFC C12) — `Process.monitor`-style API
that returns a structured reason map (`{:normal | :shutdown | :exception |
:max_turns_exceeded | :provider_timeout | {:plugin_aborted, name, why}}`) instead
of the raw OTP term. Use `CMDC.demonitor/2` to cancel.
### Added — Event Protocol (RFC D14)
- **Generic `:plugin_event` event** — unified envelope
`{:plugin_event, %{kind: atom, v: integer, session_id, occurred_at, ...}}` that all
built-in and third-party plugins use to publish business-level events to EventBus.
Subscribing to a single event gives the integrator access to every plugin's
structured output. First producer: `MemoryFlush` (`kind: :memory_flush`).
### Added — Phase 10 (original roadmap)
- **10A. Steering** — `CMDC.steer(session, instruction)` injects a mid-run "soft
interrupt" without aborting. Queued when busy (default `max=3`), applied at the
next safe point, emits `:steering_applied`. Plugins can gate via new
`:before_steering` hook.
- **10B. PromptMode** — new `CMDC.Options.prompt_mode` field with 4 values:
- `:full` (default) — full BasePrompt + identity + tools + Skills + Memory
- `:task` — compact base for focused subagents (saves ~50% tokens)
- `:minimal` — tool-name list only, no descriptions (saves ~90%)
- `:none` — completely delegated to user-provided `system_prompt`
`SubAgent` defaults to `:task` (SDK-level token savings); `Tool.Task` propagates
the mode to children; 8-layer SubAgent chain drops from ~7200 to ~2400 tokens.
- **10C. MemoryFlush plugin** — new `CMDC.Plugin.Builtin.MemoryFlush` subscribes
to the new `:before_compact` event, extracts key facts via configurable
`extract_fn` (heuristic default, LLM-pluggable), deduplicates with SHA256, and
appends to `working_dir/MEMORY.md`. Closes the loop with `MemoryLoader` for
true long-session memory across restarts. Emits both `:memory_flushed`
(internal) and `:plugin_event` (kind `:memory_flush`, per RFC D14).
- **10D. ModelRouter v2** — `Plugin.Builtin.ModelRouter` now uses
`{:switch_model, ...}` action (no more `[系统]` message pollution) and adds five
new business-friendly condition families on top of the original three runtime
conditions:
- `:token_budget_lt` — remaining budget from `user_data[:token_budget]`
- `:task_complexity` / `:task_complexity_in` — `:simple` / `:normal` / `:complex`
- `:time_of_day_in` — list of UTC-hour ranges (with injectable `now_fn`)
- `:user_tier` / `:user_tier_in` — `:free` / `:pro` / `:enterprise` / custom
- `:user_data, key, value` and `:user_data, key, op, value` (`:eq/:gt/:lt/:gte/:lte`)
Robustness: all conditions run inside a `rescue` wrapper; `user_data = nil` and
unknown condition tuples safely degrade to `false`.
### Changed
- `ModelRouter` no longer injects `[系统] 已切换到模型 X` assistant messages; uses
`{:switch_model, ...}` Plugin action + `:model_switched` event instead. Conversation
history remains clean.
- `HumanApproval` / HITL flow now emits `:agent_resumed` when `approve(auto_resume: true)`
actually restarts the turn (was previously silent).
- `CMDC.Agent` emits `:before_compact` event (new plugin hook) before Compactor runs,
giving plugins a chance to persist facts before summarization.
### Fixed
- `CMDC.subscribe("session-id")` on a not-yet-started session no longer raises
`ArgumentError`; it now succeeds and delivers events as soon as the session starts
(RFC A4).
- `Plugin.Pipeline` `{:emit, ...}` action can now emit multiple events in a single
return (`{:emit, [{name1, payload1}, ...], state}`) and accepts a 3-tuple shortcut
`{:emit, name, payload, state}`.
### Breaking
- `Plugin.Builtin.ModelRouter` behavior change: existing users who relied on the
`[系统] 已切换到模型 X` message being injected into context must migrate to subscribe
`:model_switched` events or consume the new `:switch_model` action's side effects.
Most users will experience this as a cleaner UX.
### Documentation
- `docs/guides/plugin-matrix.md` — complete 11 hooks × 7 actions matrix + 5 Cookbook
scenarios (RFC B7).
- `docs/dev/rfc/2026-04-hive-feedback-review.md` — official response to the Hive team,
documenting every accepted RFC's design decisions and migration paths.
- `llm.txt` / `llms-full.txt` — updated with PromptMode, MemoryFlush, ModelRouter v2,
`:plugin_event`, `switch_model/2`, `attach_tool/2`, `monitor/1`, `:since` replay,
and `status/1` extensions.
- `docs/dev/KNOWLEDGE.md` — added sections 5.24 / 5.25 / 5.26 documenting pitfalls
found during 10B / 10C / 10D implementation (SubAgent token explosion, memory
flush robustness, ModelRouter current_model state chain).
[0.2.0]: https://github.com/tuplehq/cmdc/releases/tag/v0.2.0
## [0.1.1] - 2026-04-08
### Fixed
- 修复 ex_doc 文档 10 处引用 warning(隐藏函数引用、不存在函数引用、LICENSE 文件缺失)
- LICENSE 文件加入 HexDocs extras
- README 许可证描述和联系方式更正
## [0.1.0] - 2026-04-08
Initial release of CMDC — Elixir Agent Kernel.
### Added
#### Core Infrastructure (L0)
- `CMDC.Options` — typed struct with NimbleOptions schema for AI-friendly agent configuration; all fields with precise `doc:` annotations
- `CMDC.Config` — application-level configuration struct with provider routing (`resolve_provider/2`)
- `CMDC.Context` — runtime execution context passed to Tools and Plugins
- `CMDC.Message` — typed message struct supporting system/user/assistant/tool_result roles; `@derive Jason.Encoder`
- `CMDC.Event` — 22 structured event types covering full agent lifecycle
- `CMDC.EventBus` — Registry-based PubSub; `subscribe/1`, `broadcast/2`, `subscribe_all/0`
- `CMDC.SubAgent` — declarative sub-agent specification struct with `nil`-means-inherit semantics
#### Core Abstractions (L1)
- `CMDC.Plugin` — behaviour with 11 event hooks and 7 action types (continue/intervene/abort/skip/block_tool/replace_tool_args/emit)
- `CMDC.Plugin.Pipeline` — priority-ordered plugin execution with short-circuit support
- `CMDC.Plugin.Registry` — plugin registration with deduplication and priority sorting
- `CMDC.Tool` — behaviour with `name/0`, `description/0`, `parameters/0`, `execute/2`; result type `{:ok, text} | {:error, text} | {:effect, term}`
- `CMDC.Sandbox` — behaviour abstracting file/command execution (8 callbacks); `CMDC.Sandbox.Local` as default OS implementation
- `CMDC.Skill` — SKILL.md discovery, loading, and system prompt injection
- `CMDC.Blueprint` — behaviour for declarative reusable agent configurations; struct pipeline helpers (`add_tools/2`, `add_plugins/2`, etc.)
- `CMDC.Provider` — thin `req_llm` wrapper; `stream/4`, `convert_messages/2`, `convert_tools/1`
- `CMDC.Provider.StreamBridge` — converts `ReqLLM.StreamResponse` into gen_statem messages
#### Agent Kernel (L2)
- `CMDC.Agent` — `gen_statem` state machine with 4-state loop: `idle → running → streaming → executing_tools`
- `CMDC.Agent.State` — runtime state with token/cost tracking, loop detection hashes, sandbox/todos/memory fields
- `CMDC.Agent.Stream` — streaming delta accumulation with XML tag extraction (`<status>`, `<title>`)
- `CMDC.Agent.ToolRunner` — parallel tool execution with built-in loop detection (exact duplicate + pattern + consecutive failure) + tool retry with `on_tool_error` Plugin hook; configurable via `:tool_max_retries` (default 0) and `:tool_retry_delay_ms` (default 500ms)
- `CMDC.Agent.Emitter` — EventBus event broadcasting with ETS ring buffer for recent event retrieval
- `CMDC.Agent.BasePrompt` — default base prompt aligned with DeepAgents `BASE_AGENT_PROMPT`
- `CMDC.Agent.SystemPrompt` — system prompt assembly: BasePrompt + user prompt + blueprint identity + skills + memory
- `CMDC.Agent.Compactor` — automatic context compaction; token-based trigger with character estimation fallback; offload to file
- `CMDC.Agent.Compactor.ArgTruncator` — pre-truncation of large tool arguments (write_file/edit_file/execute)
#### Built-in Plugins (6)
- `CMDC.Plugin.Builtin.SecurityGuard` (priority 10) — path and command blacklist enforcement
- `CMDC.Plugin.Builtin.HumanApproval` (priority 15) — HITL approval flow via `approval_required` events
- `CMDC.Plugin.Builtin.EventLogger` (priority 50) — JSON Lines session audit log
- `CMDC.Plugin.Builtin.MemoryLoader` (priority 100) — AGENTS.md loading and `<agent_memory>` injection; auto-reload on write_file/edit_file
- `CMDC.Plugin.Builtin.PatchToolCalls` (priority 120) — synthetic tool result injection for dangling tool_calls
- `CMDC.Plugin.Builtin.PromptCache` (priority 130) — Anthropic prompt caching headers; auto-skip for other providers
#### Built-in Tools (11)
- `CMDC.Tool.ReadFile` — file reading with offset/limit pagination; Sandbox proxy
- `CMDC.Tool.WriteFile` — file writing with parent directory creation; Sandbox proxy
- `CMDC.Tool.EditFile` — exact string replacement (str_replace); Sandbox proxy
- `CMDC.Tool.Shell` — shell command execution; large output auto-saved to temp file; Sandbox proxy
- `CMDC.Tool.Grep` — regex file search with line numbers and glob filtering; Sandbox proxy
- `CMDC.Tool.ListDir` — directory listing with type and size; Sandbox proxy
- `CMDC.Tool.Glob` — glob pattern file matching; Sandbox proxy
- `CMDC.Tool.Task` — OTP-process-level sub-agent spawning under SubAgent.Supervisor; broadcasts `subagent_start`/`subagent_end`
- `CMDC.Tool.WriteTodos` — Context.todos update with `todo_change` event broadcast
- `CMDC.Tool.AskUser` — pause execution, wait for `user_responded` event
- `CMDC.Tool.CompactConversation` — manual context compaction trigger
#### Integration Layer (L3)
- `CMDC.SessionServer` — per-session Supervisor tree (Agent + SubAgent.Supervisor)
- `CMDC.SubAgent.Supervisor` — DynamicSupervisor for isolated child agent processes (`:temporary` restart)
- `CMDC.MCP.Bridge` — dynamic CMDC.Tool module generation from MCP server tool definitions
- `CMDC.MCP.Client` — `anubis_mcp`-based MCP client with Registry naming per server
- `CMDC.MCP.Supervisor` — DynamicSupervisor for MCP client processes
- `CMDC.MCP.Config` — mcp.json discovery with multiple path fallbacks
- `CMDC.Memory.ETS` — ETS-backed key-value memory store with keyword search
- `CMDC.Gateway` / `CMDC.Gateway.Local` — outbound event reporting contract and local EventBus implementation
#### Public API (L4)
- `CMDC` — facade with `create_agent/1`, `prompt/2`, `collect_reply/2`, `stop/2`, `abort/1`, `subscribe/1`, `unsubscribe/1`, `approve/2`, `reject/2`, `status/1`, `agent_pid/1`, `session_id/1`
- `CMDC.Blueprint.Base` — built-in base blueprint (SecurityGuard + EventLogger + PatchToolCalls)
#### Documentation
- `README.md` — Quick Start, feature comparison table (CMDC vs DeepAgents), architecture diagram, built-in tool/plugin tables
- `llm.txt` — AI quick reference (~230 lines): all public API signatures, parameter types, common patterns
- `llms-full.txt` — AI complete reference (~999 lines): event protocol, Tool/Plugin development guide, 14 known pitfalls
[0.1.0]: https://github.com/tuplehq/cmdc/releases/tag/v0.1.0