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.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