Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
25c069e
fix(tui): align RLM metadata with REPL surface
asto18089 Oct 18, 2026
105b1a6
fix(tui): correct built-in tool description claims
asto18089 Oct 18, 2026
54e2d51
fix(tui): repair registry guidance dead ends
asto18089 Oct 18, 2026
4a03e16
fix(tui): disclose agent child context isolation
asto18089 Oct 18, 2026
4fb560b
fix(tui): qualify compaction and recap text
asto18089 Oct 18, 2026
f71efb1
fix(tui): close round-1 review findings on model-facing text
asto18089 Oct 18, 2026
adf2cb5
fix(tui): close round-2 review findings on model-facing text
asto18089 Oct 18, 2026
b33e4d2
fix(tui): close round-3 review findings on model-facing text
asto18089 Oct 18, 2026
4c3229a
fix(tui): correct the workshop per-tool threshold names
asto18089 Oct 19, 2026
c0e27b8
fix(tui): close round-4 review findings on model-facing text
asto18089 Oct 19, 2026
7a8a427
fix(tui): align prompts, events, and receipts with behavior
asto18089 Oct 19, 2026
26ac287
fix(tools): disclose real limits across tool surfaces
asto18089 Oct 19, 2026
8cefcdb
fix(skills): align bundled skill texts with the engine
asto18089 Oct 19, 2026
da5e39a
fix(skills): teach the real mcp CLI forms
asto18089 Oct 19, 2026
8aed26d
fix(skills): correct alias stub legacy wording
asto18089 Oct 19, 2026
56d2b31
fix(skills): refresh superseded shipped bodies
asto18089 Oct 19, 2026
09d076c
fix(tui): gate translation override line on host
asto18089 Oct 19, 2026
c81b079
fix(tui): name the auto-review denial dead end
asto18089 Oct 19, 2026
a476a3c
test(tui): pin footer route and recap omission causes
asto18089 Oct 19, 2026
8fe46ff
fix(tools): align small disclosures with code
asto18089 Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1181,15 +1181,24 @@ exponential_base = 2.0
# ─────────────────────────────────────────────────────────────────────────────────
# Workshop / Large-Output Routing (#548)
# ─────────────────────────────────────────────────────────────────────────────────
# Tool outputs exceeding `large_output_threshold_tokens` are routed through a
# V4-Flash synthesis sub-agent. Only the synthesis reaches the parent context;
# the raw text is stored in the workshop variable `last_tool_result` so the
# parent can call `promote_to_context` later if it needs the full content.
#
# Per-tool overrides let high-volume tools (e.g. Bash) use tighter
# thresholds without changing the global default. Name tools by the identifiers
# the model actually sees — `exec_shell`, `grep_files`, and `web_search` were
# retired and an override keyed to them matches nothing.
# By default, tool outputs exceeding `large_output_threshold_tokens` are
# classified by the adaptive evidence router (#4619): host-side
# `evidence_routing` metadata steers the spill, and oversized bodies reach the
# model as evidence artifacts plus a bounded preview instead of entering the
# parent context wholesale. The legacy
# workshop-synthesis behavior (truncate to a preview, store the raw text in
# the in-memory `last_tool_result` variable) survives only behind the
# `CODEWHALE_CLASSIC_OUTPUT_ROUTING` rollback switch — and that variable is
# diagnostics-only: `promote_to_context` never shipped, so no tool can read
# it back.
#
# Per-tool overrides let high-volume tools (e.g. bash) use tighter
# thresholds without changing the global default; they apply to the adaptive
# router's threshold too. Name tools by the identifier that actually
# executes — the shell tool is `bash` (`exec_shell` was retired), `grep_files`
# is the live search name, and the web tool is `Web`. A key with a live name
# matches; `web_search` runs inside `Web` and is not a registered name, so a
# key with it (or with `exec_shell`) matches nothing.
#
# There is no per-call `raw = true` escape. It was documented here, but
# `raw = true` on a tool call does NOT bypass routing: the adaptive router
Expand All @@ -1205,8 +1214,8 @@ exponential_base = 2.0
# # read_result_max_bytes = 102400
# # tool_result_max_bytes = 102400
# [workshop.per_tool_thresholds]
# Bash = 2048 # shell output synthesised aggressively
# Web = 8192 # web results can be large; give them more room
# bash = 2048 # shell output routed aggressively
# Web = 8192 # Web results can be large; give them more room

# ─────────────────────────────────────────────────────────────────────────────────
# Profile Example (for multiple environments)
Expand Down Expand Up @@ -1528,10 +1537,10 @@ default_text_model = "deepseek-ai/deepseek-v4-pro"
# omitted entirely, the runtime uses these product defaults. Later launch,
# approval, and activity-persistence paths all read through this one model.
# [workflow]
# # Allow the parent agent to auto-launch Workflow for multi-agent work.
# # Set false to require an explicit `/workflow` opt-in.
# # Reserved: currently not consumed by launch gating — auto-start approval
# # is governed by auto_start_read_only / require_approval_for_writes.
# automatic = true
# # Auto-start read-only plans without an approval card when automatic is on.
# # Auto-start read-only plans without an approval card.
# auto_start_read_only = true
# # Require an approval card before write/shell/network/high-budget launches.
# require_approval_for_writes = true
Expand Down
10 changes: 5 additions & 5 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,13 +2292,13 @@ impl NamedFleetConfigToml {
/// See `config.example.toml` for documentation.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WorkflowConfigToml {
/// Allow the parent agent to auto-launch Workflow for multi-agent work.
/// Product default is on; set `false` to require explicit `/workflow`.
/// Reserved: currently not consumed by launch gating — auto-start
/// approval is governed by [`Self::auto_start_read_only`] and
/// [`Self::require_approval_for_writes`]. Product default is on.
#[serde(default = "default_workflow_automatic")]
pub automatic: bool,
/// When automatic launch is enabled, start read-only child plans without
/// an approval card. Write/shell/network plans still consult
/// [`Self::require_approval_for_writes`].
/// Start read-only child plans without an approval card. Write/shell/
/// network plans still consult [`Self::require_approval_for_writes`].
#[serde(default = "default_workflow_auto_start_read_only")]
pub auto_start_read_only: bool,
/// Require an operator approval card before launching plans that write,
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/assets/skills-catalog-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"in_model_catalogue": "true when the skill renders as an ambient catalogue line",
"shadowed_aliases": "aliases that collide with another canonical bundled name; the canonical skill wins resolution"
},
"generation": "10",
"generation": "11",
"skills": [
{
"name": "skill-creator",
Expand Down
12 changes: 8 additions & 4 deletions crates/tui/assets/skills/documents/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
name: documents
description: Compatibility alias for the docx skill. Prefer loading docx.
description: Compatibility stub owning the legacy name documents; the docx skill provides the workflow.
aliases-for: docx
---

# documents (alias)
# documents (compatibility stub)

This skill is a compatibility alias for **docx**.
This stub exists so the legacy name `documents` keeps resolving. It contains no
workflow of its own.

Load and follow the `docx` skill body for the actual workflow.
If the `docx` skill is installed and enabled, load_skill("docx") returns
it directly. If loading `docx` returns this stub again, `docx` is not
installed or is disabled — tell the user, and continue with generic guidance
for this file type instead of retrying.
2 changes: 1 addition & 1 deletion crates/tui/assets/skills/docx/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ invocation: model+user
Use for `.docx` / Word-style documents (memos, reports, letters, templates).

## Compatibility
`documents` is a compatibility alias for this skill.
The legacy name `documents` resolves to a stub that redirects here; loading `docx` directly is equivalent.

## Workflow
1. Prefer `.docx` in the workspace.
Expand Down
7 changes: 5 additions & 2 deletions crates/tui/assets/skills/feishu/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ Use this skill when the user asks for Feishu, Lark, or "飞书" integration work
configured.
4. For MCP, build or configure a server that exposes narrow tools such as
`send_message`, `read_doc`, `append_sheet_row`, or `query_bitable`.
5. Register the MCP server with `codewhale mcp add`, then run
`codewhale mcp validate` and `codewhale mcp tools`; connected tools surface deferred — activate them via `tool_search`.
5. Register the MCP server with `codewhale mcp add` (stdio form:
`codewhale mcp add <name> --command <command> [--arg <arg>] ...`; HTTP
servers take `--url <url>`), then run `codewhale mcp validate` and
`codewhale mcp tools`; connected tools surface deferred — activate them
via `tool_search`.
6. Verify with a dry run, sandbox document, or read-back call before sending
externally visible messages.

Expand Down
3 changes: 3 additions & 0 deletions crates/tui/assets/skills/mcp-builder/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ codewhale mcp validate
codewhale mcp tools
```

(In a running session, `/mcp add …` is the interactive equivalent of
`codewhale mcp add`; it is a TUI command, not a shell command.)

HTTP/SSE entries can include per-server headers in `~/.codewhale/mcp.json` when
credentials or custom routing headers are required.

Expand Down
9 changes: 6 additions & 3 deletions crates/tui/assets/skills/mcp-discovery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ entirely; check your tool list and follow step 1 either way.
need to be exact. When such a candidate exists, you **must start it and inspect
its tools before** using `bash`, local programs, custom code, or a manual
implementation. The availability or familiarity of a local alternative is
not a reason to skip the candidate. Skip Registry use only when every entry is
clearly irrelevant, or when a matching server fails to start after the retry
described below.
not a reason to skip the candidate. Skip Registry use only when no
entry plausibly covers the task (if a refined query still returns
nothing, the visible snapshot has no relevant entry — on a fresh
install it may still be downloading, so proceed with local tools
rather than retrying), or when a matching server fails to start
after the retry described below.
3. **Install + run transactionally.** If `start_registry_mcp_server` is not in
your tool list after `registry_sync` succeeded, activate it via
`tool_search` as in step 1; if that fails, registry starts are
Expand Down
4 changes: 2 additions & 2 deletions crates/tui/assets/skills/pdf/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ Use this skill for any task where a PDF is the primary input or output.
watermark, redact, fill forms, encrypt/decrypt, or create.
2. Preserve originals. Write outputs with explicit names.
3. Use the most reliable available tool:
- the built-in `read` tool for basic text extraction from PDFs
- `pdftotext`, `pdfinfo`, `qpdf`, or `mutool` when installed
- `pdftotext` for text extraction, `pdfinfo` for page counts, and
`qpdf` or `mutool` for split/merge/rotate — via `bash` when installed
- Python libraries such as `pypdf`, `pdfplumber`, `PyMuPDF`, or
`reportlab` when available
- OCR tools only for scanned pages
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/assets/skills/pptx/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ invocation: model+user
Use for PowerPoint / `.pptx` slide decks.

## Compatibility
`presentations` is a compatibility alias for this skill.
The legacy name `presentations` resolves to a stub that redirects here; loading `pptx` directly is equivalent.

## Workflow
1. Choose a simple layout system and stick to it.
Expand Down
12 changes: 8 additions & 4 deletions crates/tui/assets/skills/presentations/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
name: presentations
description: Compatibility alias for the pptx skill. Prefer loading pptx.
description: Compatibility stub owning the legacy name presentations; the pptx skill provides the workflow.
aliases-for: pptx
---

# presentations (alias)
# presentations (compatibility stub)

This skill is a compatibility alias for **pptx**.
This stub exists so the legacy name `presentations` keeps resolving. It contains no
workflow of its own.

Load and follow the `pptx` skill body for the actual workflow.
If the `pptx` skill is installed and enabled, load_skill("pptx") returns
it directly. If loading `pptx` returns this stub again, `pptx` is not
installed or is disabled — tell the user, and continue with generic guidance
for this file type instead of retrying.
4 changes: 2 additions & 2 deletions crates/tui/assets/skills/skill-installer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Codewhale skill.
2. Prefer Codewhale's native `/skill` commands over ad hoc copying.
3. For GitHub/community skills, inspect the `SKILL.md` before recommending
trust. Treat scripts and companion files as untrusted until reviewed.
4. After installing, tell the user to restart the session if the skill does not
appear immediately in the available-skills list.
4. Installed skills are picked up immediately; if one does not appear, reopen
the skill list rather than restarting.
5. If a skill conflicts by name with a workspace skill, explain that workspace
skill directories take precedence over global `~/.codewhale/skills`, with
`~/.deepseek/skills` retained as a legacy fallback.
Expand Down
12 changes: 8 additions & 4 deletions crates/tui/assets/skills/spreadsheets/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
name: spreadsheets
description: Compatibility alias for the xlsx skill. Prefer loading xlsx.
description: Compatibility stub owning the legacy name spreadsheets; the xlsx skill provides the workflow.
aliases-for: xlsx
---

# spreadsheets (alias)
# spreadsheets (compatibility stub)

This skill is a compatibility alias for **xlsx**.
This stub exists so the legacy name `spreadsheets` keeps resolving. It contains no
workflow of its own.

Load and follow the `xlsx` skill body for the actual workflow.
If the `xlsx` skill is installed and enabled, load_skill("xlsx") returns
it directly. If loading `xlsx` returns this stub again, `xlsx` is not
installed or is disabled — tell the user, and continue with generic guidance
for this file type instead of retrying.
4 changes: 2 additions & 2 deletions crates/tui/assets/skills/xlsx/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ invocation: model+user
Use for spreadsheets, tabular analysis, formulas, and charts in workbooks.

## Compatibility
`spreadsheets` is a compatibility alias for this skill.
The legacy name `spreadsheets` resolves to a stub that redirects here; loading `xlsx` directly is equivalent.

## Workflow
1. Prefer structured tables with clear headers.
2. Keep formulas auditable.
3. Verify by reopening and checking key cells/aggregates.
3. Verify by reopening and checking key cells/aggregates. Script-written workbooks carry no cached formula values — formula cells may read empty until a spreadsheet app recalculates; verify literal cells and audit formulas by inspection.
97 changes: 87 additions & 10 deletions crates/tui/src/client/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,15 +1199,17 @@ pub(crate) fn build_chat_wire_body(
let note = function.get_mut("parameters").and_then(|parameters| {
crate::tools::schema_sanitize::sanitize_for_xai_parameters(parameters)
});
if let Some(note) = note
&& let Some(description) = function
.get_mut("description")
.and_then(|d| d.as_str().map(str::to_string))
{
function.insert(
"description".to_string(),
json!(format!("{description} {note}")),
);
if let Some(note) = note {
let description = function
.get("description")
.and_then(Value::as_str)
.unwrap_or_default();
let description = if description.is_empty() {
note
} else {
format!("{description} {note}")
};
function.insert("description".to_string(), json!(description));
}
}
}
Expand Down Expand Up @@ -7092,7 +7094,7 @@ mod image_block_wire_tests {
//! `{"type":"image_url","image_url":{"url":…}}`.
use super::{ApiProvider, build_chat_messages, build_chat_wire_body};
use crate::models::Role;
use crate::models::{ContentBlock, ImageUrlContent, Message, MessageRequest};
use crate::models::{ContentBlock, ImageUrlContent, Message, MessageRequest, Tool};

const DATA_URL: &str = "data:image/png;base64,QUJD";

Expand Down Expand Up @@ -8171,6 +8173,81 @@ mod image_block_wire_tests {
"media from an ambiguous duplicate-ID batch must not survive: {wire:?}"
);
}

/// The xAI lane must land the schema-constraint note even when a tool
/// carries an empty description — mirroring the Responses and Anthropic
/// lanes, which substitute the note instead of dropping it.
#[test]
fn xai_chat_lands_constraint_note_when_tool_description_is_empty() {
let union_schema = serde_json::json!({
"type": "object",
"properties": {
"patch": {"type": "string"},
"replace": {"type": "array"},
"changes": {"type": "array"}
},
"oneOf": [
{"required": ["patch"]},
{"required": ["replace"]},
{"required": ["changes"]}
]
});
let tool = |name: &str, description: &str| Tool {
tool_type: None,
name: name.to_string(),
description: description.to_string(),
input_schema: union_schema.clone(),
allowed_callers: None,
defer_loading: None,
input_examples: None,
strict: None,
cache_control: None,
};
let request = MessageRequest {
model: "grok-4".to_string(),
messages: vec![Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: "apply a patch".to_string(),
cache_control: None,
}],
}],
max_tokens: 128,
system: None,
tools: Some(vec![
tool("apply_patch", ""),
tool("lookup", "Lookup things."),
]),
tool_choice: None,
metadata: None,
thinking: None,
reasoning_effort: None,
stream: None,
temperature: None,
top_p: None,
};

let body = build_chat_wire_body(
&request,
ApiProvider::Xai,
crate::config::DEFAULT_XAI_BASE_URL,
false,
)
.expect("xAI wire body");

const NOTE: &str = "Exactly one of these parameter groups must be provided: `changes` | `patch` | `replace`.";
let tools = body.body["tools"].as_array().expect("tools array");
assert_eq!(tools.len(), 2, "both tools must survive sanitization");
assert_eq!(
tools[0]["function"]["description"], NOTE,
"an empty description must be substituted with the note, not dropped or space-prefixed"
);
assert_eq!(
tools[1]["function"]["description"],
format!("Lookup things. {NOTE}"),
"a non-empty description keeps the appended-note form"
);
}
}

#[cfg(test)]
Expand Down
8 changes: 6 additions & 2 deletions crates/tui/src/compaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,9 @@ from earlier turns.";
/// This intentionally follows Codex's `templates/compact/summary_prefix.md`:
/// the checkpoint is a user-history item, never standing system-prompt prose.
const SUMMARY_HEADER: &str = "Another language model started to solve this problem and produced \
a summary of its thinking process. You also have access to the state of the tools that were used \
by that language model. Use this to build on the work that has already been done and avoid \
a summary of its thinking process. You also have access to the recent tool state from the last \
round before compaction (older tool results were pruned — rerun a command if you need output \
that is no longer visible). Use this to build on the work that has already been done and avoid \
duplicating work. Here is the summary produced by the other language model, use the information \
in this summary to assist with your own analysis:";

Expand Down Expand Up @@ -2342,6 +2343,9 @@ mod tests {
let text = &blocks[0].text;
assert!(text.contains(FIXED_SUMMARY));
assert!(text.contains("Another language model"));
// Round-2 review pin: the header must keep disclosing that older
// tool results do not survive compaction.
assert!(text.contains("older tool results were pruned"));

// Replacement history keeps older user turns, then the open round
// verbatim (user + assistant + tools), then one checkpoint.
Expand Down
Loading
Loading