Subject: esengine/DeepSeek-Reasonix (product name Reasonix) Baseline commit:
627b051(Merge pull request #7368 … remote-workbench-reconnect-hardening, default branchmain-v2) License: MIT (LICENSE) Code size (baseline): 1686.gofiles;internal/non-test 216,079 lines; whole repo including tests 553,005 lines Product in one sentence: a DeepSeek-native terminal coding agent—a config- and plugin-driven extremely thin harness, a single static Go binary (CGO_ENABLED=0), tuned around DeepSeek’s automatic prefix cache, so long-session token cost stays controllable. Audience: product managers and engineers who have read at least one of this series’ Claude Code / Open Design / Hermes / Codex / OpenAI4S analyses. Rewrite note: this edition is rearranged in a five-beat motivation → constraint → rejected alternatives → choice → cost. First answer “why the developers built it, and what the spine in their head was,” then go into the code; every key design tries to say “what would happen if we didn’t.” Local baseline path:参考项目/DeepSeek-Reasonix @ 627b051
Contents
Part I · Origin: why the developers built this 1. Pain and insight: cost anxiety meets an already-there saving mechanism 2. Product position and motive evidence: the original words in README / REASONIX / SPEC 3. Repo map and stack: numeric shape and dependency direction
Part II · Mind map: how one constraint yields ten designs 4. Developer mind map: from “the prefix must stay stable” to ten designs 5. Cache-first constitution: stable prefix vs turn tail 6. Why dual models must split session
Part III · Implementation: from constraint to code 7. Main loop: Agent.Run → runToolLoop 8. Compose: drive volatile content into the turn tail 9. Compaction three rungs: soft → snip → summary (a cache reset with a posted price) 10. Tool ABI: builtin + MCP + use_capability fixed proxy 11. Permissions, sandbox, workspace fence: application rules × OS fence 12. Checkpoint / Delivery / Goal: delivery evidence and a completion protocol 13. control.Controller: one face behind every frontend 14. Memory, instructions, extensions, desktop/ACP
Part IV · Taste and bounds 15. Ten decisions, five-beat recap 16. Cross-cut (coding-agent table) 17. Honest bounds 18. Source-tour index and local reproduction
Part I · Origin: why the developers built this
Chapter 1 · Pain and insight: cost anxiety meets an already-there saving mechanism
1.1 Answer “why” first
Before reading Reasonix’s source, read its README. The Chinese README does not open with “how smart our Agent is”; it pins the product position on cost (README.zh-CN.md:42-43):
A DeepSeek-native AI coding agent for the terminal. A config- and plugin-driven extremely thin harness—a single static Go binary, tuned around DeepSeek’s prefix cache, so even long sessions keep token cost down.
This is not marketing copy. It is the starting point of the whole architecture:
- Pain: in a long session, token cost grows linearly with turns; DeepSeek’s pricing makes “keep chatting” expensive.
- Already-there mechanism: DeepSeek automatically prefix-caches—as long as the request-prefix bytes stay stable, the hit portion is discounted.
- Insight: the key to saving money is not “call the model less,” but “keep hitting the prefix.” Cache is thereby promoted from an “optimization” to an “architectural constraint.”
flowchart LR
P["Pain: long-session token cost is high"] --> I["Insight: DeepSeek auto prefix cache
stable prefix = hit = save money"]
I --> C["Constraint: system prefix must be byte-stable"]
C --> D["Corollary: volatile content out to turn tail
stable tool schema · compaction priced in the open · miss diagnosable"]
1.2 Three verifiable engineering claims
| Claim | What it looks like in code |
|---|---|
| DeepSeek-native | Provider layer keeps reasoning_content round-trip; on missing reasoning, silent retry (observeMissingToolCallReasoning at agent.go:1388-1394) |
| Cache-first | System prefix (base + tools + memory) is byte-stable across turns; volatile content rides control.Compose’s turn tail (REASONIX.md:14-16, control/input.go:181-183) |
| Thin harness + single binary | docs/SPEC.md contract: the core only knows interfaces; CGO_ENABLED=0; capability comes from config / plugin / init() self-registration |
1.3 This constraint was written as a “constitution,” not a comment
The repo’s own standing instructions (REASONIX.md:14-16):
- Cache-first: the system-prompt prefix (base prompt + tools + memory) must stay
byte-stable across turns so DeepSeek's automatic prefix cache stays warm. Never
mutate it mid-session — ride the turn tail instead (see `control.Compose`).
Even the PR template forces Cache-impact / Cache-guard metadata (REASONIX.md:64-71).
In this series only Hermes’s “prefix cache is sacred” is in the same family; Reasonix’s difference is welding it into Go runtime diagnostics + CI scripts, so “do not break the cache” becomes an executable collaboration discipline.
Chapter 2 · Product position and motive evidence: the original words in README / REASONIX / SPEC
2.1 Motive-evidence table
| Evidence | Source | What it proves |
|---|---|---|
| “tuned around DeepSeek’s prefix cache, so even long sessions keep token cost down” | README.zh-CN.md:42-43 |
Cost is the product starting point |
| “must stay byte-stable… Never mutate it mid-session” | REASONIX.md:14-16 |
The constraint is written in constitutional voice |
| “switching models inside one shared conversation would break the prefix and tank cache hits, so we don't” | docs/SPEC.md:238-240 |
Even dual models are reverse-derived by the constraint into independent sessions |
| Compaction is a “cache-reset point”; cache hit rate is a “key observability signal” | docs/SPEC.md:328-329 |
Compaction is a cache reset with a posted price |
PRs force Cache-impact / Cache-guard |
REASONIX.md:64-71 |
The constraint is welded into the collaboration process |
| “Memory added mid-session rides the turn (never the cached system prefix)” | internal/control/input.go:181-183 |
Every implementation point explains why |
| Economy tier is for “cost-sensitive tasks” | docs/COLLABORATION_MODES.zh-CN.md:86,136 |
Product tiers are cut by cost |
2.2 Product-manager view: what experience it is selling
| Selling point | Engineering landing |
|---|---|
| Long sessions use fewer tokens | Stable prefix → DeepSeek automatic prefix cache; PrefixShape / CompareShape explain cache miss (agent/cache_shape.go) |
| Usable after install | Single static binary; npm package reasonix; reasonix.example.toml |
| Many frontends, one set of behavior | control.Controller behind TUI / HTTP-SSE / Wails desktop / ACP (REASONIX.md:11-13) |
| Verifiable delivery | Delivery Profile: complete_step evidence sign-off + readiness gate (docs/GOAL_ENFORCEMENT.zh-CN.md) |
| Collaboration modes switchable | Economy / Balanced / Delivery; optional planner_model dual model (docs/COLLABORATION_MODES.zh-CN.md) |
🧠 One sentence: others are optimizing “how smart the tool call is”; Reasonix is optimizing “in this same session, can the prefix keep hitting cache”—and treating that as a product contract that cannot be breached.
Chapter 3 · Repo map and stack: numeric shape and dependency direction
3.1 Digitized project shape (baseline 627b051)
| Metric | Magnitude |
|---|---|
| Go source files | 1686 .go |
internal/ non-test |
216,079 lines |
| Whole repo including tests | 553,005 lines |
| Giant-file hearts | control/controller.go 6,560 · cli/chat_tui.go 5,154 · agent/agent.go 4,362 |
| Builtin tool contract | docs/TOOL_CONTRACT.md (compile-time builtins + full boot surface, guarded by tests) |
Layout contract given by docs/SPEC.md:
cmd/reasonix/main.go # entry; blank-import providers + builtin tools
internal/
cli/ # subcommands, assembly, exit codes
control/ # transport-agnostic Controller (behind every frontend)
agent/ # Session + harness loop + compact + cache shape
provider/ # Provider interface + openai/anthropic/responses
tool/ + tool/builtin/ # Tool interface + init() self-registration
permission/ # allow / ask / deny + bash decompose
sandbox/ # OS sandbox wrap
memory/ · instruction/ # background facts vs standing instructions
plugin/ · mcp* / capability # MCP / capability proxy
checkpoint/ · evidence/ # change rewind and delivery evidence
acp/ · bot/ · desktop* # editor protocol, bots, desktop
Dependency direction (docs/SPEC.md:54-56): cli → {agent, plugin, config} → {tool, provider}; parent packages do not import self-registering child packages.
3.2 Stack
| Layer | Tech | Location |
|---|---|---|
| Language / distribution | Go, CGO_ENABLED=0 single static binary |
Makefile, cmd/reasonix |
| Config | TOML (BurntSushi/toml) |
internal/config, reasonix.example.toml |
| TUI | Bubble Tea family fullscreen chat | internal/cli/chat_tui.go |
| Desktop | Wails (Go backend + frontend) | desktop/ |
| Protocols | ACP, HTTP/SSE serve, MCP stdio JSON-RPC |
internal/acp, internal/control, internal/plugin |
| Provider | OpenAI-compatible / Anthropic / Responses | internal/provider/* blank-import |
Entry is extremely thin (cmd/reasonix/main.go):
// Blank imports wire compile-time built-ins into their registries.
_ "reasonix/internal/provider/anthropic"
_ "reasonix/internal/provider/openai"
_ "reasonix/internal/provider/responses"
_ "reasonix/internal/tool/builtin"
3.3 Relation to Claude Code / Hermes / Codex
| Dimension | Claude Code | Hermes Agent | OpenAI Codex | Reasonix |
|---|---|---|---|---|
| Language shape | TypeScript (closed source) | Python brain + TS face | Rust workspace | Go single binary |
| Model strategy | bound to Anthropic | model-agnostic + learning loop | bound to OpenAI stack | DeepSeek-first, OpenAI-compatible pluggable |
| Context philosophy | strong prompt engineering | prefix cache sacred + memory on disk | two-layer turn + compaction | Cache-first written into the constitution + runtime shape diagnostics |
| Frontend | terminal-primary | CLI/TUI/desktop/20+ IM | CLI + app-server | Controller-unified: TUI / serve / Wails / ACP |
| Permissions | popup + rules | environment isolation, many backends | AskForApproval × OS sandbox | Policy rules + bash decompose + sandbox + workspace confine |
| Completion judgment | natural end + tools | skill/memory closed loop | three-party stop protocol | natural end + maxSteps/todo stall; Delivery evidence sign-off |
If you remember only one sentence: Reasonix = Hermes’s cache philosophy × Codex’s engineering hardness × Claude Code’s terminal-coding category, welded onto DeepSeek’s automatic prefix cache.
Part II · Mind map: how one constraint yields ten designs
Chapter 4 · Developer mind map: from “the prefix must stay stable” to ten designs
This chapter is the “map” of the whole lecture: first see the spine in the developer’s head, then go into the code. The ten items below can all be derived from “the prefix must stay byte-stable”; each also has a clear cost.
| # | Corollary | Choice | If we didn’t |
|---|---|---|---|
| 1 | System prefix must be stable | Volatile context goes into the turn tail, not system (control.Compose) |
Every interjection / memory update would cold the whole prefix |
| 2 | Tool schema is also prefix | Schema hashed after sort (normalizeToolSchemas) |
Tool-registration-order jitter causes a “false miss” |
| 3 | Interjection is inevitable | Steer explicitly pays a cache tax once (run_loop.go:276-283) |
Pretend zero-cost interjection; cache stats go false |
| 4 | Context will eventually grow | Compaction splits soft/snip/summary; compaction = cache-reset point | Unrestrained summary frequently resets cache |
| 5 | MCP inventory will change | use_capability fixed proxy isolates dynamic schema |
Installing one MCP tool colds cache once |
| 6 | bash semantics must be precise | Runtime read-only degrade BashCommandIsReadOnly |
Schema stable but semantics fuzzy; parallel/evidence get hurt |
| 7 | DeepSeek protocol has quirks | Missing reasoning silent-retry once | In thinking mode a tool round fails outright |
| 8 | Hitting the budget should not count as a crash | maxSteps → Pause, not Fatal | Long tasks lose session assets at the budget boundary |
| 9 | Many frontends must be consistent | Behavior goes into control.Controller, not the face |
Each frontend patches itself; behavior drifts |
| 10 | Constraints must be executable | SPEC / TOOL_CONTRACT / tests same-source + PR metadata | The constitution is only a wiki prop |
Chapter 5 · Cache-first constitution: stable prefix vs turn tail
5.1 Two tracks
| Track | Contents | Rule |
|---|---|---|
| Cache-stable prefix | base system + tool schemas + standing memory/instructions | Forbidden to byte-drift inside a session |
| Turn tail (Compose) | user body, goal block, plan marker, language preference, <memory-update>, background-jobs, hook context, retrieval recall |
Variable every turn; must never write back into the prefix |
Controller.Compose (control/input.go:137-212) writes this into a comment (input.go:181-183):
Memory added mid-session rides the turn (never the cached system prefix),
so it takes effect now without invalidating the prompt cache. It folds into
the system prefix on the next session, where it costs nothing per turn.
Assembly order (conceptually):
flowchart TB
IN["user text"] --> G{"running goal?"}
G -->|yes| GB["activeGoalBlock + autoResearch"]
G -->|no| P
GB --> P{"planMode?"}
P -->|yes| PM["PlanModeMarker"]
P -->|no| L
PM --> L["response/reasoning language wrap"]
L --> M["drainPending → memory-update"]
M --> J["background-jobs note"]
J --> H["hook context"]
H --> R["memory.recall Block (real user turn)"]
R --> OUT["hand to Agent.Run"]
This is the same philosophy family as Open Design’s “assemble the system prompt in bands by cache frequency” and Hermes’s “memory writes to disk take effect immediately, enter the prompt only next session.” Reasonix’s distinctive move: diagnose a miss as a structured event, rather than only warning in docs.
5.2 PrefixShape: why a miss?
```13:48:参考项目/DeepSeek-Reasonix/internal/agent/cache_shape.go // PrefixShape hashes the portions of the request prefix that influence // provider-side prompt-cache reuse. Comparing snapshots across turns // lets us explain why a cache miss happened. type PrefixShape struct { SystemHash string ToolsHash string PrefixHash string LogRewriteVersion int ToolSchemaTokens int }
`CaptureShape` **sorts then hashes** tool schema (`normalizeToolSchemas`), avoiding “order jitter” false misses. `CompareShape` gives a `system` / `tools` / `log_rewrite` reason list, plus cache hit/miss tokens from usage.
Before sending to the provider it also strips UI metadata that would cold the cache: `LocalOnly` display records do not enter `ModelMessages` (`LocalOnly` mark in `provider.go`); wall-clock and cancelled half-streams never pollute the prefix.
### 5.3 Tension with dynamic MCP tools
Once dynamic `mcp__*` tools enter the main Registry, they change the tools hash → cold cache. Reasonix’s answer (`docs/TOOL_CONTRACT.md:45-96`):
- Delivery / Planner / most sub-agents: expose a **fixed-name** proxy tool `use_capability` (list / inspect / call / decline);
- **Connecting MCP on demand does not change** that proxy’s provider-visible schema;
- Balanced’s Executor **deliberately keeps** direct `mcp__*` (accepting possible prefix change)—this is a conscious product trade-off, not an omission.
> 🧠 **Load-bearing wall**: Cache-first is not “change the prompt as little as possible.” It is “**which changes are allowed to pay the cache-miss tax**” classified in the open: steer pays the tax, memory-update does not touch the prefix, MCP inventory change is isolated by a proxy.
---
<h2 id="ch6">Chapter 6 · Why dual models must split session</h2>
### 6.1 Motive
Want plan-then-execute cost/quality balance, but switching models would break the prefix.
### 6.2 Constraint and choice
`docs/SPEC.md:238-240` says it plainly:
> switching models inside one shared conversation would break the prefix and tank cache hits, so we don't.
When `planner_model` is configured, **Planner and Executor each have an independent session** (`agent/coordinator.go`): two prefixes each cache-stable; the sessions never mix-write. This is not the same layer as UI Plan Mode (a turn-tail marker).
### 6.3 Cost
- Two prefixes, two tool surfaces, first-round cost higher;
- Capabilities discovered in the planning phase must still be directly callable after handoff (`use_capability`’s fixed proxy exactly carries this);
- Routing more complex: `executor_only` | `plan_and_execute` | `plan_for_approval` | `plan_only` (`planner_route.go`).
---
# Part III · Implementation: from constraint to code
<h2 id="ch7">Chapter 7 · Main loop: Agent.Run → runToolLoop</h2>
### 7.1 Lifecycle entry
`Agent.Run` says it clearly itself (comment starts at `agent.go:1310`, function body `1319-1378`):
1. Resolve this turn’s `maxSteps` (overridable by context);
2. Open workspace lease / steer queue / background evidence submit defer;
3. `interceptAgentStart` (extension may abort);
4. `beginRunTurn` initializes state;
5. **`return a.runToolLoop(ctx, state)`**.
No mysterious “framework base class”—an explicit state machine; policy is split across `beginRunTurn` / `handleFinalResponse` / `handleToolRound`.
### 7.2 `runToolLoop`: what happens in one round
The core loop is at `run_loop.go:274-355`:
```mermaid
sequenceDiagram
participant U as User / Controller
participant A as Agent.runToolLoop
participant P as Provider.Stream
participant T as executeOne(s)
U->>A: Run(input)
loop step = 0..maxSteps
A->>A: consumeSteer? (write into session, may accept one cache miss)
A->>A: CaptureShape(system+tools)
A->>P: streamWithMissingReasoningRecovery
P-->>A: text + reasoning + tool_calls + usage
A->>A: CompareShape → cache diagnostics
alt no tool_calls
A->>A: handleFinalResponse (incl. maybeCompact)
else has tool_calls
A->>T: handleToolRound → parallel read-only / serial write
T-->>A: tool results written into session
end
end
A-->>U: maxStepsPause or error or nil
A few heavily flavored details:
| Detail | Meaning | Location |
|---|---|---|
| Steer | Mid-turn interjection into a queue; on consume, write a user message with a guidance prefix; the comment admits “one cache miss is inevitable” | run_loop.go:276-283 |
| Missing reasoning recovery | In DeepSeek thinking mode a tool call missing reasoning_content → silent replay of the same request at most once |
run_loop.go:357-364, agent.go:1388-1394 |
| Stream recovery | Interrupted stream at most maxStreamRecoveries=3, and does not consume maxSteps (step--) |
run_loop.go:300-309 |
| Cache diagnostics | Every round CompareShape compares system/tools/log_rewrite |
run_loop.go:285-294 |
| Pause, not crash | maxStepsPause / todoStallPause: the work is already in the session; the user sending one more message continues |
agent.go:1469-1494 |
| Delivery readiness failure | Before a final answer the host checks todo/criteria/verify/signoff → FinalReadinessError, open the next round |
handleFinalResponse |
| Empty final / thinking-only | At most maxEmptyFinalBlocks nudges |
run_loop.go, agent.go:42 |
| Recovery grace | After a recovery episode is exhausted, one summarize-only lap, then RecoveryPauseError |
recovery path |
7.3 Assembly entry: boot.Build → the same Runner
| Mode | Trigger | Turn API |
|---|---|---|
| TUI | bare reasonix / chat |
async Controller.SendWithRaw |
| Headless | reasonix run / -p |
run one round synchronously; Ask fail closed against the writer; needs --auto/-y |
| ACP | reasonix acp |
blocking Controller.RunTurn (session/prompt) |
| serve / desktop / bot | their own entries | SSE / Wails / IM → the same Controller |
boot.Build (internal/boot) freezes the system prefix in one shot (output style, environment-probe snapshot, memory, skill index), then wraps an optional planner_model into a Coordinator.
7.4 Single-tool execution pipeline
executeOne (execute_one.go:65-101) is a fixed five beats:
parseToolCall— Resolve, ambiguous MCP names, duplicate success/fail loop guard, stale-anchor edit intercept; bash may degrade to read-only by args (permission.BashCommandIsReadOnly);interceptToolBefore— extension may rewrite the call;resolveToolPolicy— permission gate;prepareToolExecution— preview, mutation bookkeeping, parent write lock;finishToolExecution— actually execute + receipt.
Read-only tools may run in parallel; writes are serial—same family as Claude Code / OpenAI4S “read-only waves,” but landing on Go’s mutation observer / evidence ledger.
Chapter 8 · Compose: drive volatile content into the turn tail
8.1 Motive
The system prefix must stay byte-stable, but goal, plan, language preference, memory updates, retrieval results may change every turn.
8.2 Constraint
Anything unstable, or that need not appear, cannot enter the cache-stable prefix.
8.3 Rejected alternatives
- “Paste all dynamic content into the system prompt”: convenient but colds cache every turn, directly against the constitution;
- “Dynamic content never enters context”: goal/plan/memory updates cannot take effect immediately.
8.4 Choice
control.Compose (control/input.go:137-212) assembles in a fixed order before the real user turn: activeGoalBlock → PlanModeMarker → language wrap → <memory-update> → background-jobs → hook context → memory.recall Block.
8.5 Cost
- One extra assemble-and-strip per turn (
StripComposePrefixes); - If a frontend/extension bypasses
Composeand sends a message directly, the cache constitution is broken—so every frontend goes through the same Controller.
Chapter 9 · Compaction three rungs: soft → snip → summary (a cache reset with a posted price)
compact.go:20-37 nails the policy:
| Stage | Default threshold (fraction of context window) | What it does |
|---|---|---|
| Soft | 0.5 | Report that context is growing; still keep the cache-stable prefix |
| Tool-result snip | 0.6 | Cheaply rewrite stale tool results |
| Summary compact | 0.8 (force 0.9) | Summary fold; keep a recent tail of a fixed token budget (default 16384) |
Design points:
- Trigger by ratio, retain by absolute token budget—a large window will not over-compact; a small window will not get stuck compacting near the threshold;
- Summary uses structured headings (Standing facts / Goal / Decisions / Files / Commands / Errors / Pending), wrapped in
<compaction-summary>; - Ablation switches can turn off cache-friendly strategy for a contrast experiment (
internal/ablation); - Compact bumps
LogRewriteVersion→CompareShapewill marklog_rewrite—honestly admitting this is a cache-reset point.
Compared with Raven’s “lossless archive” and Claude Code’s “summary discard”: Reasonix explicitly stands on “cheap snip first, then pay the cache tax of one summary.”
Chapter 10 · Tool ABI: builtin + MCP + use_capability fixed proxy
10.1 Compile-time builtins (contract table)
docs/TOOL_CONTRACT.md is guarded by tests (TestBuiltinToolContractDocumentation), same-source as the runtime registry. Core surface:
| Category | Tools |
|---|---|
| Read | read_file grep glob ls code_index web_fetch |
| Write | write_file edit_file multi_edit move_file delete_range delete_symbol notebook_edit |
| Shell | bash + bash_output / wait / kill_shell |
| Task/delivery | todo_write complete_step update_goal |
Implementation side is all init() { tool.RegisterBuiltin(...) } (e.g. builtin/readfile.go:27).
10.2 Full boot surface vs Economy
Default full-token boot also hangs session / memory / skill / subagent / LSP / install / slash etc.: ask, task/fleet/parallel_tasks, memory/remember/forget, run_skill, LSP four-piece, explore/research/review/security_review, etc. (docs/TOOL_CONTRACT.md).
Delivery additionally:
use_capability: stable MCP proxy;review_report: structured review of medium/high-risk changes;- Host-side evidence: verification / diff / files / manual; a
complete_stepwith no evidence is refused outright.
Economy deliberately exposes only a thin surface (about ask/bash/read_file/write_file/edit_file/background three-piece + connect_tool_source); the rest mount on demand—trading “cannot see all tools at once” for tokens and prefix stability.
10.3 Tool-interface flavor
Tools carry capability bits such as ReadOnly, preview (Previewer), images, PlanMode classification—permissions and parallel scheduling read these bits, rather than relying on the model to “behave.” Checkpoint only tracks editing tools that carry Previewer (see Chapter 12).
Chapter 11 · Permissions, sandbox, workspace fence: application rules × OS fence
11.1 Permission Policy + Ask / Auto / Yolo
permission.Policy (permission.go):
- Rule shape aligned with Claude Code:
Tool/Tool(glob)/ legacyTool=literal(ParseRule); Decide(toolName, readOnly, args); bash takes segmented decompose (DecomposeBashCommand);BashCommandIsReadOnly: degrade “schema-writable bash” to read-only on the concrete argv (affects parallel, mutation, evidence);BashSubjectRequiresExplicitApproval: indirect execution, dangerous git, etc. force explicit approval.
Product approval modes (docs/TOOL_APPROVAL_MODES.md) are orthogonal to collaboration mode (normal/plan/goal):
| Mode | Behavioral points |
|---|---|
| Ask | Writes default to ask; headless reasonix run fail closed against the writer |
| Auto | Ordinary writers auto-allow; deny/ask rules, Plan confirm, nested/indirect Bash, MCP destructive, sensitive remember/forget still ask |
| Yolo | The only mode that can bypass the nested-Bash human gate; still does not bypass deny and sandbox |
There is also internal/guardian: an independent security-review sub-session (with a denial circuit breaker)—cannot replace user confirmation of memory writes. Hooks (internal/hook) provide PreToolUse/PostToolUse/PostToolUseFailure, plus Claude tool-name compatibility mapping.
11.2 Sandbox + confine
builtin/bash.go wraps the command in sandbox.Command:
| Platform | Bash OS sandbox |
|---|---|
| macOS | sandbox-exec (Seatbelt) |
| Linux | bwrap |
| Windows | product-fixed off, command unconfined; file-class builtins still have workspace confine |
On enforce with no backend, fail closed (do not run naked). Escape goes through sandbox.EscapeApprover. MCP stdio by default does not inherit the Bash sandbox (product default in internal/plugin/plugin.go is the host process; comments state the reason).
SECURITY.md writes the boundary clearly: workspace fence, permissions, sandbox, secrets, HTTP serve’s localhost/CORS, desktop/bot isolation, updater verification—and what does not count as a vulnerability.
11.3 Checkpoint / Rewind
Aligned with Claude Code’s Esc-Esc / /rewind narrative (docs/CHECKPOINTS.md):
- Mechanism: file snapshots (not git); sidecar
<session-id>.ckpt/; - Tracked range: editing tools that carry
Previewer(write_file/edit_file/multi_edit); - Not tracked:
bashside effects;move_filehas not yet implemented Previewer; - API:
Controller.Checkpoints/Rewind/PrepareRewind+CommitRewind(including coverage confirmation); - Explicitly not done: git-backed rollback (docs mark out of scope).
Chapter 12 · Checkpoint / Delivery / Goal: delivery evidence and a completion protocol
12.1 Three orthogonal axes
docs/GOAL_ENFORCEMENT.zh-CN.md opens:
Goal is the only cross-turn scheduler; Delivery is a pure quality gate; tool permissions and sandbox are unaffected by the Goal switch.
| Axis | Duty |
|---|---|
Goal (/goal) |
Cross-turn advance, budget, pause/resume |
| Delivery | readiness: todo / criteria / verification / review / signoff / capability… |
| Permission / Sandbox | always independent |
The model declares intent via update_goal(continue|complete|blocked); the host uses a structured ReadinessResult (agent.go:1497+) to decide whether it is truly complete. Declaring complete without evidence → open the next round, rather than believing the model’s mouth.
12.2 complete_step
The builtin tool description is fierce (TOOL_CONTRACT.md): completion must carry evidence; the host advances the todo on its behalf. Same spectrum as grok-build Goal Mode and MiMo “do not trust self-report”—Reasonix lands it with host-observed receipts.
12.3 Collaboration Profile
(docs/COLLABORATION_MODES.zh-CN.md)
| Profile | Points |
|---|---|
| Economy | single model, thinner tool surface, for cost-sensitive tasks |
| Balanced | full tool surface; optional independent planner_model |
| Delivery | Balanced + use_capability + acceptance contract + review gate |
Chapter 13 · control.Controller: one face behind every frontend
13.1 Design iron law
REASONIX.md:11-13:
One transport-agnostic
control.Controllersits behind every frontend (chat TUI, HTTP/SSE serve, Wails desktop). Add behavior to the controller, not a frontend, so all three inherit it.
In practice there are also ACP / bot entries. Unified assembly: boot.Build → Controller → agent.Runner (or Coordinator).
13.2 Path of one user message
flowchart LR
TUI["chat_tui"] --> SUB["Submit / Send"]
HTTP["serve HTTP/SSE"] --> SUB
DESK["Wails desktop"] --> SUB
ACP["ACP"] --> SUB
SUB --> ADM["admitGuardedTurn
anti-reentry / park"]
ADM --> COMP["Compose(turn tail)"]
COMP --> RUN["Coordinator? → Agent.Run"]
RUN --> EVT["event.Sink
reasoning/text/tool/notice"]
EVT --> TUI
EVT --> HTTP
EVT --> DESK
Key API family (controller.go): Send / Submit / SubmitHTTP / SubmitDisplay / RunTurn / Goal-loop variants; runGuarded does turn admission so a double-open cannot run away.
13.3 Why did Controller become a 6k-line giant file?
Because it swallowed: session restore, checkpoint, memory slash, MCP lifecycle, capability, steer fallback, yolo/plan, attachments, goal usage……all product behavior shared by every frontend. The cost is a giant file; the gain is “fix one place, TUI/desktop/HTTP all correct.” In this series OpenAI4S’s gateway.py and Codex’s turn.rs are the same “giant heart” shape.
Chapter 14 · Memory, instructions, extensions, desktop/ACP
14.1 Context Engine v2: instructions ≠ memory
Central law of docs/SESSION_MEMORY_RETRIEVAL.md:
- Standing instructions (
REASONIX.md/AGENTS.md/CLAUDE.md+.local+ ancestor directories + global): must appear on relevant turns; enter the cache-stable prefix; - Background memory: facts that can go stale; BM25 auto-recall (defaults
defaultAutoRecallLimit=4,defaultAutoRecallChars=2400) enters the turn tail; the index merges into the prefix only next session; #note//rememberwrite instruction files; theremembertool writes background facts—do not mix;- Safe writes: some scopes create-only; the rest need confirmation—Auto/Yolo/Guardian cannot bypass;
- Instruction parse: deeper directories override broader; same directory
.localwins;@pathimport limited to 5 layers and forbidden to escape.
14.2 Three extension layers: MCP · Plugin Package · Extension Protocol v1
| Layer | What it is | Trust model |
|---|---|---|
MCP (internal/plugin) |
JSON-RPC; stdio / Streamable HTTP; tool names mcp__<server>__<tool>; compatible with project .mcp.json |
precise rules + mcp_connect__*; OAuth etc. still deferred |
Plugin packages (internal/pluginpkg) |
reasonix-plugin.json; compatible with .claude-plugin / .codex-plugin; contribute skills/hooks/mcp/commands/agents/themes |
after install-and-enable, take effect by contribution surface |
Extension Protocol v1 (reasonix.extension.v1) |
NDJSON JSON-RPC sidecar; 17 frozen hook points (tool.before, permission.decision, system_prompt.build, compaction.*…); schema generation + CI drift-check |
Full trust: install is highest privilege, can override host deny; UI marks FULL TRUST |
MCP and Extension are different layers: the former attaches tools/resources; the latter attaches host-lifecycle intercepts. Full-trust Extension is an independent attack surface, and also the hardest landing of an “Open Design-style public contract” inside Reasonix (docs/EXTENSION_PROTOCOL.md).
14.3 Subagent profiles
Skill frontmatter runAs: subagent (docs/SUBAGENT_PROFILES.md): builtins explore / research / review / security_review; CLI reasonix subagent …; invocation via /<profile> or task/fleet + write_paths parallel-write isolation. Default concurrency max_subagent_concurrency=6, max_parallel_writers=3. Claude plugin agents/*.md can map to /<plugin>:agent:<name>.
14.4 Desktop and ACP
- Desktop (
desktop/): nested Wails module (keep parent moduleCGO_ENABLED=0); React+TS webview ↔ the sameController(no HTTP hop); - ACP (
internal/acp,docs/ACP.md):reasonix acp [--profile …]; NDJSON JSON-RPC;loadSession+embeddedContext; capability advertisement no image/audio; MCPhttp: true,sse: false; vendor extensions include_reasonix.io/session/steer; - VS Code extension
SivanLiu.reasonix-agentbrings up localreasonix acp; - Remote-SSH / workbench: baseline PR #7368 reconnect hardening—the product has grown from a local TUI to a remote workbench.
Part IV · Taste and bounds
Chapter 15 · Ten decisions, five-beat recap
| # | Choice | Motive | Constraint | Rejected | Cost |
|---|---|---|---|---|---|
| 1 | Volatile context into turn tail | prefix stable = save money | DeepSeek auto prefix cache hits by bytes | dump everything into system | per-turn assemble/strip cost; bypassing Compose breaks the constitution |
| 2 | Tool schema hashed after sort | avoid false miss | tool registration order is uncontrollable | hash by registration order | the sort logic itself must be test-guarded |
| 3 | Steer pays a cache tax once | interjection must be immediately visible | the model must see the new instruction | don’t interject / pretend zero cost | every interjection misses one round |
| 4 | Compaction three rungs | delay the summary reset | context will eventually grow | one-shot summary | extra snip logic and threshold tuning |
| 5 | use_capability fixed proxy |
dynamic MCP cannot move schema | MCP inventory changes anytime | register mcp__* directly into the Registry | one extra parse layer and permission names (mcp_connect__*) |
| 6 | bash runtime read-only degrade | schema stable, semantics precise | whether bash is read-only depends on argv | judge only by schema | BashCommandIsReadOnly must maintain a dangerous-command list |
| 7 | Missing reasoning silent retry | DeepSeek thinking occasionally missing reasoning | provider requires thinking replay | fail outright | one extra same-request replay; need debounce cooldown |
| 8 | maxSteps → Pause | session assets first | budget limited but work already on disk | error and start over | more pause state machine and restore paths |
| 9 | Behavior into Controller | many frontends consistent | frontend count growing | each frontend implements itself | Controller becomes a 6k-line giant file |
| 10 | Contracts executable (SPEC/TOOL_CONTRACT/PR) | constitution cannot rely on conscience | the repo will keep growing | wiki docs | CI/test cost; PR metadata requirements |
Chapter 16 · Cross-cut (coding-agent table)
| Dimension | Claude Code | Open Design | OpenAI4S | Hermes | Codex | Reasonix |
|---|---|---|---|---|---|---|
| Category | terminal coding | design host | research dual-plane | personal learning agent | official Rust CLI | DeepSeek terminal coding |
| Main loop | own | none (plugs into others) | Engine+Cell | Python loop | run_turn |
runToolLoop |
| Context | strong prompt engineering | 20-layer banded cache | science state in the kernel | prefix sacred | two-layer turn | Cache-first + shape |
| Completion signal | natural end | artifact files | finalize / submit_output | skill sedimentation | three-party stop | natural end + Delivery readiness |
| Extension | MCP/Skills | CLI adapters | Skills recipes | 74-tool plugins | MCP+crate | MCP + Plugin packages + Full-trust Extension v1 |
| Distribution | closed-source product | daemon+web | Python daemon | uv/Python | Rust binary | Go static binary |
| Permission product surface | popup+rules | host delegated | Notebook approval | environment many backends | Ask×sandbox | Ask/Auto/Yolo × sandbox (orthogonal) |
Chapter 17 · Honest bounds
- Giant-file cost:
controller.go/chat_tui.go/agent.goare all thousands of lines; newcomer onboarding is steep; - DeepSeek-first tax: reasoning recovery and cache assumptions do not necessarily hold for other gateways (there is a compatibility path, but product mindshare still leans DeepSeek);
- Executor direct MCP: Balanced Executor can still cold cache because of MCP inventory change—the docs already admit this;
- Windows Bash OS sandbox: product-fixed unconfined; file tools still have confinement;
- Checkpoint coverage: does not track bash side effects; no git-backed;
move_filepreview unfinished; - Extension full trust: install is highest privilege—capability is strong; the threat model needs the user to understand it;
- MCP long tail: OAuth,
list_changed, some scopes still deferred (docs/SPEC.md:877); - ACP capability advertisement: no image/audio; agent-side MCP SSE narrowed;
- Old branch:
v1only critical fixes; analysis / secondary development should align withmain-v2.
Chapter 18 · Source-tour index and local reproduction
| Order | Path | What to look at |
|---|---|---|
| 1 | README.zh-CN.md + docs/SPEC.md |
product position and contract |
| 2 | REASONIX.md |
the team’s own cache constitution |
| 3 | cmd/reasonix/main.go + internal/boot |
entry and boot.Build |
| 4 | internal/control/input.go → Compose |
turn tail |
| 5 | internal/agent/run_loop.go |
main loop |
| 6 | internal/agent/cache_shape.go |
miss diagnostics |
| 7 | internal/agent/compact.go + prune.go |
compaction three rungs |
| 8 | internal/agent/execute_one.go |
single-tool pipeline |
| 9 | internal/agent/coordinator.go |
dual-model dual session |
| 10 | docs/TOOL_CONTRACT.md + internal/tool/builtin/ |
tool surface |
| 11 | docs/TOOL_APPROVAL_MODES.md + permission/ + sandbox/ |
Ask/Auto/Yolo × fence |
| 12 | docs/CHECKPOINTS.md |
rewind snapshots |
| 13 | docs/GOAL_ENFORCEMENT.zh-CN.md |
completion protocol |
| 14 | docs/SESSION_MEMORY_RETRIEVAL.md |
instructions vs memory |
| 15 | docs/EXTENSION_PROTOCOL.md + PLUGIN_PACKAGES.md |
three extension layers |
| 16 | docs/ACP.md / desktop/README.md |
embed and desktop |
| 17 | internal/control/controller.go (optional) |
multi-frontend orchestration |
Local reproduction:
cd "参考项目/DeepSeek-Reasonix"
git fetch origin main-v2
git checkout main-v2
git reset --hard 627b051
# optional: run the contract test (needs local Go)
# go test ./internal/tool -run TestBuiltinToolContractDocumentation
Appendix A · Contrast with this series’ “Catalog of agent engineering patterns”
| Pattern-catalog chapter | Reasonix landing |
|---|---|
| I Prompt-cache engineering | Textbook: stabilize the prefix / move the volatile out / stabilize tools / compact with care / telemetry visible (all five axes) |
| II Stop judgment | natural stop + maxSteps/todo stall Pause + Delivery readiness |
| III Permission gates | Ask/Auto/Yolo + rules-first + bash decompose + OS sandbox (Win Bash gap) |
| IV Context compaction | snip first, summary pays the tax; soft explicitly keeps the prefix |
| V Memory system | instruction files (prefix) ≠ background facts (BM25/tail); confirmation gate cannot be bypassed by Yolo |
| VI Tool system | fixed proxy isolates dynamic MCP; Economy connect_tool_source |
| VII Sandbox | Seatbelt/bwrap; application fence → permissions → OS sandbox |
| VIII Sub-agents | Skill profile + task/fleet; write_paths parallel-write isolation |
| XII Persistence | file checkpoint rewind (not git); does not cover bash side effects |
| XIII Extension ecology | MCP + Plugin packages + Full-trust Extension Protocol v1 |
| XV Model strategy | DeepSeek-first; planner_model dual session keeps each cache |
Appendix B · One-sentence close
Reasonix is not “another Go-written Claude Code imitation.” It promoted DeepSeek’s automatic prefix cache from a billing leftover to the architectural spine: Compose split, Shape diagnostics, Capability proxy, PR metadata, Delivery evidence—all turn around “do not cold the cache.” If in this series you already understand Hermes’s cache-sacred and Open Design’s banded assembly, reading Reasonix is watching the same philosophy executed to what hardness inside a Go single-binary coding agent.
Appendix C · Restoring developer motive: from “cost anxiety” to “architectural constitution”
Chapters 1, 2, and 4 already unfolded the motive chain; here a condensed contrast table is kept for looking back.
| Motive | Evidence | Architecture it grew into |
|---|---|---|
| Long-session token cost is high | README.zh-CN.md:42-43 |
product position “tuned around prefix cache” |
| Prefix must stay byte-stable | REASONIX.md:14-16 |
constitution + PR metadata + CI |
| Volatile content must move out | control/input.go:181-183 |
Compose turn tail |
| Dual models cannot share a session | docs/SPEC.md:238-240 |
Planner / Executor independent sessions |
| Compaction must post an honest price | docs/SPEC.md:328-329 |
soft → snip → summary + LogRewriteVersion |
| Miss must be explainable | cache_shape.go |
PrefixShape / CompareShape diagnostics |
| Cost is the product axis | COLLABORATION_MODES.zh-CN.md:86,136 |
Economy / Balanced / Delivery three tiers |
Analysis baseline: 参考项目/DeepSeek-Reasonix @ 627b051 (main-v2).
Series position: a coding-agent sample; nearest neighbors Hermes (cache), Codex (sandbox orthogonal), Claude Code (category).