Ctrl+B backgrounds the foreground shell directly (#3032), leaving the
two-step shell-control modal dead code that fails clippy -Dwarnings.
Delete ShellControlView/ShellControlChoice, the ModalKind and ViewEvent
variants, and open_shell_control; repoint the default-paste regression
test at HelpView; update the Ctrl+B keybinding description in all
locales to describe the new direct-background behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three targeted error-message improvements extracted from community
PR #2933 (author cy2311), with additional model-not-found annotation:
1. dispatch.rs format_tool_error: pass through self-explanatory messages
that already name the cause (mode switch, allow_shell, feature flag,
denied by user) instead of appending a conflicting generic suffix.
Fixes the Plan-mode double-message (#2657).
2. subagent/mod.rs session-name conflict: include elapsed time
(started Ns ago / NmNs ago) so the parent can distinguish a live
worker from a stale/failed earlier spawn (#2656).
3. subagent/mod.rs annotate_child_model_error: catch model-not-found
patterns (Model Not Exist, does not exist, no such model, etc.) in
the raw error text even when the taxonomy classifies them as
Internal rather than Authorization/State (#2653).
Closes#2653, #2656, #2657.
Credit: cy2311 for the dispatch.rs and subagent conflict hunks from #2933.
Co-authored-by: cy2311 <29836092+cy2311@users.noreply.github.com>
Three providers previously had silent no-ops for all reasoning-effort
tiers — the user toggled thinking on/off and nothing changed on the
wire. This commit wires them:
- Atlascloud: now speaks the DeepSeek dialect (thinking + reasoning_effort
fields) since it serves DeepSeek models. All three arms (off, low–high,
xhigh/max) updated.
- Moonshot/Kimi: emits thinking: {type: disabled/enabled} for off/on.
Kimi-k2.6 natively supports this field.
- Ollama: emits think: false/true for off/on. Sent through the
OpenAI-compatible /v1/chat/completions endpoint.
Providers that remain unchanged: Openai, WanjieArk, OpenaiCodex
(chat path), Arcee, Huggingface.
Three incremental improvements to the hooks control plane:
1. ToolCallBeforeStdout parser: hooks can now emit a JSON decision on
stdout — {"decision": "allow"|"deny"|"ask", "reason": "...",
"updatedInput": {...}, "additionalContext": "..."}. Non-JSON or empty
stdout retains legacy passthrough (allow). Exit code 2 still hard-denies
regardless of stdout.
2. Glob matchers for ToolName conditions: `name = "mcp__*"` now matches
all MCP tools. Uses regex::escape + `*` → `.*` pattern, same
convention as execpolicy/matcher.rs. Exact names keep working.
3. Project-local hooks: `HooksConfig::load_with_project(global, workspace)`
reads `.codewhale/hooks.toml` and appends its hooks after global.
Malformed file logs a warning and falls back to global-only.
Extends apply_model_template() to substitute model-specific facts from
runtime lookups instead of hardcoded V4 claims:
- {context_window_note}: resolved from context_window_for_model();
if unknown, emits fallback wording instead of guessing
- {subagent_economics}: resolved from input_cost_note() (new public
pricing helper); cost-agnostic fallback when pricing is unknown
- {model_thinking_note}: gated on model_supports_reasoning();
empty string when the model does not emit thinking tokens
The hardcoded "1M-token window", "$0.14/M Flash", and V4 thinking
strategy will only reach models whose capability lookups return those
values (DeepSeek V4 family). Non-DeepSeek models get accurate facts
or honest "unknown" wording.
Adds input_cost_note() to pricing.rs — returns a one-line sub-agent
cost description, or None when pricing is unavailable.
Three changes to fix provider capability reporting:
1. Delete the Openai/Atlascloud/Moonshot early-return arm in
provider_capability() so these providers use the generic model-based
path. Moonshot models now correctly report 262,144 context window
and thinking_supported: true (via models.rs lookups).
2. Delete the Ollama hardcoded arm so Ollama also uses model-based
lookups. The generic fallback now uses 8192 for Ollama (conservative
for small local models) instead of the 128K default.
3. Ollama fallback: when context_window_for_model returns None and
the provider is Ollama, default to 8192 instead of
LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS (128K).
Two targeted fixes to close the Kimi/Moonshot reasoning gap:
1. chat.rs: add ApiProvider::Moonshot to provider_accepts_reasoning_content
so Kimi thinking traces stream as Thinking blocks instead of leaking
as plain answer text on Moonshot's native endpoint.
2. models.rs: add kimi-* prefix match to model_supports_reasoning so
bare Moonshot-native IDs (kimi-k2.6, kimi-*-thinking, etc.) are
recognized as reasoning-capable without requiring the OpenRouter-style
moonshotai/ prefix.
These two changes together ensure is_reasoning_model_for_stream returns
true for Kimi models on Moonshot, fixing the RC dialet gap.
Two changes to let non-DeepSeek providers use their own model IDs:
1. config.rs: add requested_model_for_provider() — DeepSeek providers
use strict normalize_model_name(); all others accept any non-empty
string via normalize_custom_model_id().
2. subagent/mod.rs: normalize_requested_subagent_model() now takes an
ApiProvider parameter and delegates to requested_model_for_provider.
Error messages name the active provider and list accepted model IDs
from model_completion_names_for_provider() instead of hardcoding
"Expected a DeepSeek model id".
parse_optional_subagent_model() keeps basic trimming-only validation;
provider-aware checks are deferred to the spawn path where the runtime
is available.
- setup-vm.sh: bump RELEASE_TAG default to v0.8.57, add gh CLI install
step (official APT repo) and 4G swapfile creation (idempotent)
- agent-session.sh: new sourceable helper that exports the provider key
from /etc/codewhale/runtime.env for interactive agent sessions
- README.md: update version refs, add agent-session.sh to layout, add
Autonomous agent loop section with full pick->PR commands
The droplet ops (binary upgrade, PAT setup, first end-to-end issue run)
are documented as the next steps for the operator.
Headless exec hardening for benchmark/CI/droplet use:
- New CLI flags: --allowed-tools, --disallowed-tools, --max-turns, --append-system-prompt
- Add disallowed_tools to EngineConfig + command_denies_tool() helper
- run_exec_agent threads all four flags into EngineConfig and Op::SendMessage
- needs_engine now includes flag presence for standalone exec use
Adds mouse-click dispatch for sidebar rows:
- Add click_action: Option<String> to SidebarHoverRow (app.rs)
- Extend sidebar_hover_rows() and render_sidebar_section() to carry
row_actions: Vec<Option<String>>
- Add task_panel_row_actions(): background task rows get /task show <id>
and /task cancel <id>
- Add agent_panel_row_actions(): agent rows get /subagents
- Add sidebar_click_action() in mouse_ui.rs: resolves mouse position
to an action from the sidebar hover state
- Wire into MouseEventKind::Down(Left): dispatches ViewEvent::
CommandPaletteSelected → existing slash-command pipeline
Reuses the existing command dispatch backbone — no new ViewEvent variants.
Keyboard parity preserved; all click actions are also reachable via
existing slash commands.
Adds the foundation for working OSC 8 hyperlinks in the transcript:
- LinkRegion struct: (row, col_start, col_end, target) for a contiguous
run of linked cells on one terminal row
- write_osc8_open/close: emit OSC 8 escapes directly through a Write
impl (bypassing ratatui's buffer which strips ESC bytes)
- FRAME_LINKS thread-local: passes link regions from the render closure
to ColorCompatBackend::draw(), where OSC 8 escapes are emitted
out-of-band through the backend's Write impl
- ColorCompatBackend integration: draw() reads FRAME_LINKS, emits OSC 8
open/close around linked cells
The markdown renderer still uses the inline Span::content approach
(known broken); the sentinel-color buffer-scan integration is a
follow-up. This PR delivers the emission path and thread-local
plumbing so the remaining work is confined to link detection in the
render closure.
Previously Ctrl+B opened a two-step ShellControlView menu (Background /
Cancel). Now it directly calls request_foreground_shell_background(),
backgrounding the running foreground shell in one keystroke.
When no foreground shell is running, the existing status message
("No foreground shell command to background") provides the hint.
The ShellControlView and open_shell_control() remain available as a
programmatic entry point for views/tests.
Three targeted changes to reduce low-value detail in the default
compact/Live transcript view:
1. ExecCell: suppress "(no output)" line in Live mode. The success
header already conveys the outcome; Transcript mode keeps it for
exports/clipboard/pager.
2. ExecCell: suppress sub-second timing in Live mode. Calls under 1s
show no timing line; Transcript mode always shows exact timing.
3. render_preserved_output_mode: suppress "(no output)" for empty output
in Live mode. Same rationale — the header carries the signal.
Full command text, complete output, and exact timing remain available
in Transcript mode (pager, clipboard export, Alt+V detail view).
Three changes to replace raw UUIDs/hex-ids with stable user-facing labels:
1. Turn label: Add turn_counter to App, display "Turn N" instead of the
raw runtime_turn_id UUID prefix. Full UUID preserved in hover text.
2. Agent labels: Add agent_counter + agent_label_map to App. Populated
on AgentSpawned; sidebar rows use "Agent 1", "Agent 2" etc. instead
of agent_<hex>. Nicknames and user-assigned names still take priority.
3. Step counter: Add format_step_counter() helper. When max_steps is
u32::MAX (the unbounded sentinel), renders "step 16" instead of the
meaningless "step 16/4294967295". Concrete step budgets still show
the denominator.
When 4+ sub-agents run concurrently, each AgentProgress event triggers
a full terminal redraw via received_engine_event → needs_redraw. The
render loop saturates, sidebar recomputation dominates the frame budget,
and terminal input events (including Ctrl+C) are starved.
Limit progress-driven redraws to at most one per 100ms per agent. The
status-animation timer (80ms cadence) still guarantees sidebar updates.
Agent state is recorded immediately; the sidebar picks it up on the next
permitted redraw.
Adds last_agent_progress_redraw field to App to track throttle state.
- Constitution: new preamble (A / Rule Number 6), personality tier removed
(8 tiers instead of 9), elevated constitutional prose with shall/shall-not
- YAML constitution (constitution.yaml) as structured source of truth with
indentation encoding tier precedence
- Python renderer (render_constitution.py) for YAML -> markdown conversion
- prompts.rs: load constitution.md instead of base.md + calm.md overlay
- #2664: state.db default path prefers .codewhale/ over .deepseek/ with
legacy fallback so existing installs keep session history
- #2644/#2664: update stale doc comments referencing deepseek paths
- #3007: provider rejection error now shows source (CLI flag vs config),
lists supported providers, and gives specific fix instructions
SavedSession.messages is Vec<Message>, not AppendLog — .clone() already
returns Vec<Message>, so .into() was a no-op conversion that triggered
clippy::useless_conversion in CI lint.
- Wire AppendLog as the backing store for Session.messages
- Add Deref, From impls, and explicit mutation methods to AppendLog
- Narrow API: remove DerefMut, add push_batch/truncate_to/trim_front/clear/last_mut
- Update all direct message assignments to use .into() conversions
- Update tests to deref through AppendLog for comparisons
Rebased onto upstream/main (v0.8.57) to resolve merge conflicts.
- prompt_persist tests wrote through the developer's real
~/.codewhale/prompt_cache and raced each other (the eviction test could
delete another test's entry). cache_dir() now honors
CODEWHALE_PROMPT_CACHE_DIR and the tests run serialized against private
tempdirs.
- The raw TCP mock servers in mcp tests answered one request per socket
but never advertised 'Connection: close', so reqwest pooled dead
keep-alive connections and retried POSTs failed under parallel-suite
load with 'connection closed before message completed' (~50% failure
rate on the full suite).
The workflow hardcoded install boilerplate plus a contributor list that
had already drifted (v0.8.56's release thanked people 'for shaping
v0.9.0'). The body now comes from scripts/release/generate-release-body.sh:
static install/verify sections plus the tagged version's changelog section,
which already carries the per-release credits.
- scripts/release/prepare-release.sh bumps workspace + crate pins + npm
wrapper + README install tags, refreshes Cargo.lock, regenerates the
TUI changelog slice and web facts, then runs check-versions.sh
- check-versions.sh now also gates web/lib/facts.generated.ts and the
README install-tag examples (both drifted silently before)
- .cnb.yml validates the pushed tag against Cargo.toml before generating
mirror release notes
- RELEASE_CHECKLIST/RUNBOOK updated accordingly (v0.8.56 needed 9 fix
commits for exactly these sync points)
When the host sleeps while a model response is streaming, the connection
dies on wake with 'Stream read error: error decoding response body' and
the turn was lost. The engine now stamps every stream chunk with both
monotonic and wall-clock time; Instant pauses across a suspend while
SystemTime does not, so a >10s divergence on a stream error identifies a
sleep/wake cycle. In that case the partial output is discarded and the
identical request is re-issued (sharing the existing MAX_STREAM_RETRIES=3
budget) instead of failing the turn. Ordinary network flakes keep the
deliberate no-retry-after-content policy from #103.