Merge origin/main into feat/show-reasoning

Resolves conflicts after main landed the state-machine turn refactor
and the test_runner.py 9-file split:

- nanobot/agent/loop.py: take main's `_state_build`/`_persist_user_message_early`
  flow; restore the `reasoning: bool` parameter on `_build_bus_progress_callback`
  so the loop hook can mark progress as reasoning-channel without coupling to
  the answer stream.
- nanobot/cli/stream.py: keep main's configurable `bot_name`/`bot_icon` header
  while preserving the PR's `transient=True` Live + `self._console` routing
  + `_renderable()` final-render path that fixed TUI duplication.
- tests/agent/test_runner.py was deleted on main and split into 9 focused
  files; relocated all 6 reasoning tests into a new `test_runner_reasoning.py`
  matching the new layout, deduplicated the per-test `ReasoningHook` boilerplate
  through a shared `_RecordingHook` helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xubin Ren
2026-05-13 05:07:14 +00:00
co-authored by Cursor
127 changed files with 10113 additions and 5313 deletions
+22
View File
@@ -8,6 +8,8 @@ These commands work inside chat channels and interactive agent sessions:
| `/stop` | Stop the current task |
| `/restart` | Restart the bot |
| `/status` | Show bot status |
| `/model` | Show the current model and available model presets |
| `/model <preset>` | Switch the runtime model preset for future turns |
| `/dream` | Run Dream memory consolidation now |
| `/dream-log` | Show the latest Dream memory change |
| `/dream-log <sha>` | Show a specific Dream memory change |
@@ -15,6 +17,26 @@ These commands work inside chat channels and interactive agent sessions:
| `/dream-restore <sha>` | Restore memory to the state before a specific change |
| `/help` | Show available in-chat commands |
## Model Presets
Use `/model` to inspect the current runtime model:
```text
/model
```
The response shows the current model, the current preset, and the available preset names. `default` is always available and represents the model settings from `agents.defaults.*`.
To switch presets for future turns:
```text
/model fast
/model deep
/model default
```
Preset names come from the top-level `modelPresets` config. Switching is runtime-only: it does not rewrite `config.json`, and an in-progress turn keeps using the model it started with. See [Configuration: Model presets](./configuration.md#model-presets) for setup details.
## Periodic Tasks
The gateway wakes up every 30 minutes and checks `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). If the file has tasks, the agent executes them and delivers results to your most recently active chat channel.
+66
View File
@@ -53,6 +53,7 @@ IMAP_PASSWORD=your-password-here
> - **Zhipu Coding Plan**: If you're on Zhipu's coding plan, set `"apiBase": "https://open.bigmodel.cn/api/coding/paas/v4"` in your zhipu provider config.
> - **Alibaba Cloud BaiLian**: If you're using Alibaba Cloud BaiLian's OpenAI-compatible endpoint, set `"apiBase": "https://dashscope.aliyuncs.com/compatible-mode/v1"` in your dashscope provider config.
> - **Step Fun (Mainland China)**: If your API key is from Step Fun's mainland China platform (stepfun.com), set `"apiBase": "https://api.stepfun.com/v1"` in your stepfun provider config.
> - **Xiaomi MiMo thinking mode**: MiMo models (e.g. `mimo-v2.5-pro`) default to enabled thinking. Use `agents.defaults.reasoningEffort: "none"` to disable it, or `"low"` / `"medium"` / `"high"` to keep it on. Omitting the field preserves the provider's per-model default.
| Provider | Purpose | Get API Key |
|----------|---------|-------------|
@@ -656,6 +657,71 @@ That's it! Environment variables, model routing, config matching, and `nanobot s
</details>
## Model Presets
Model presets let you name a complete model configuration and switch it at runtime with `/model <preset>`.
Existing configs do not need to change. If you do not set `modelPresets` or `agents.defaults.modelPreset`, nanobot keeps using `agents.defaults.*` exactly as before.
```json
{
"agents": {
"defaults": {
"model": "openai/gpt-4.1",
"provider": "openai",
"maxTokens": 8192,
"contextWindowTokens": 128000,
"temperature": 0.1,
"modelPreset": null
}
},
"modelPresets": {
"fast": {
"model": "openai/gpt-4.1-mini",
"provider": "openai",
"maxTokens": 4096,
"contextWindowTokens": 128000,
"temperature": 0.2,
"reasoningEffort": "low"
},
"deep": {
"model": "anthropic/claude-opus-4-5",
"provider": "anthropic",
"maxTokens": 8192,
"contextWindowTokens": 200000,
"reasoningEffort": "high"
}
}
}
```
`modelPresets` is a top-level object. The keys under it (`fast`, `deep`, `coding`, etc.) are user-defined preset names. Each preset supports:
| Field | Description |
|-------|-------------|
| `model` | Model name to use for this preset. |
| `provider` | Provider name, or `"auto"` to use provider auto-detection. |
| `maxTokens` | Maximum completion/output tokens. |
| `contextWindowTokens` | Context window size used by prompt building and consolidation decisions. |
| `temperature` | Sampling temperature. |
| `reasoningEffort` | Optional reasoning/thinking setting. Provider support varies. |
`default` is reserved and always means the implicit preset built from `agents.defaults.*`; do not define `modelPresets.default`. Use `/model default` to switch back to `agents.defaults.*`.
Set `agents.defaults.modelPreset` to start with a named preset:
```json
{
"agents": {
"defaults": {
"modelPreset": "fast"
}
}
}
```
When `modelPreset` is `null` or omitted, startup uses the implicit `default` preset from `agents.defaults.*`. Runtime changes made with `/model <preset>` are not written back to `config.json`; they affect future turns until the process restarts or another model/config change replaces them.
## Channel Settings
Global settings that apply to all channels. Configure under the `channels` section in `~/.nanobot/config.json`:
+12
View File
@@ -128,6 +128,18 @@ All frames are JSON text. Each message has an `event` field.
}
```
**`runtime_model_updated`** — broadcast when the gateway runtime model changes, for example after `/model <preset>`:
```json
{
"event": "runtime_model_updated",
"model_name": "openai/gpt-4.1-mini",
"model_preset": "fast"
}
```
`model_preset` is omitted when no named preset is active. WebUI clients use this event to keep the displayed model badge in sync across slash commands, config reloads, and settings changes.
**`attached`** — confirmation for `new_chat` / `attach` inbound envelopes (see [Multi-chat multiplexing](#multi-chat-multiplexing)):
```json