Merge pull request #4953 from Hmbown/codex/v092-operate-startup

fix(tui): expose Operate startup mode and refresh session capture
This commit is contained in:
Hunter Bown authored and GitHub committed 2026-07-28 01:33:47 -07:00
commit 8232805572
9 files changed
+60 -48

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 430 KiB

+1 -1
View File
@@ -376,7 +376,7 @@ pub struct Settings {
/// session directly; resume remains available in-session.
#[serde(default)]
pub launch_screen: bool,
/// Default mode: "agent" or "plan". Legacy permission
/// Default mode: "agent" (Act), "plan", or "operate". Legacy permission
/// shorthands are accepted for migration but never advertised as modes.
pub default_mode: String,
/// Sidebar width as percentage of terminal width
+28 -9
View File
@@ -2927,7 +2927,7 @@ fn config_hint_for_key(key: &str) -> &'static str {
"fill the model reasoning background; the dashed rail remains visible when off"
}
"synchronized_output" => "auto | on | off; terminal redraw pacing, not model speed",
"default_mode" => "agent | plan",
"default_mode" => "act (agent) | plan | operate",
"sidebar_width" => "10..=50",
"sidebar_focus" => "auto | work | tasks | agents | context | hidden",
"max_history" => "integer (0 allowed)",
@@ -3009,7 +3009,7 @@ fn config_choice_values(key: &str, provider: ApiProvider) -> Option<Vec<String>>
"approval_mode" => vec!["ask", "auto-review", "full-access", "never"],
"permission_posture" => vec!["ask", "auto-review", "full-access"],
"approval_policy" => vec!["use-tui-default", "ask", "auto-review", "never"],
"default_mode" => vec!["agent", "plan"],
"default_mode" => vec!["agent", "plan", "operate"],
"reasoning_effort" if provider == ApiProvider::OpenaiCodex => {
vec!["default", "low", "medium", "high", "xhigh"]
}
@@ -3081,8 +3081,7 @@ fn canonical_config_choice(key: &str, value: &str) -> String {
},
"default_mode" => match normalized.as_str() {
"plan" => "plan".to_string(),
// Old saved Operate/YOLO values are represented by the safe
// startup workspace; permission posture is shown separately.
"operate" | "operation" | "ops" => "operate".to_string(),
_ => "agent".to_string(),
},
"locale" => normalize_configured_locale(value)
@@ -3103,8 +3102,9 @@ fn config_choice_label(locale: Locale, key: &str, value: &str) -> String {
("approval_policy", "use-tui-default") => "Use TUI permission default".to_string(),
("approval_mode" | "permission_posture", "full-access") => "Full Access".to_string(),
("approval_mode" | "approval_policy", "never") => "Never".to_string(),
("default_mode", "agent") => "Agent".to_string(),
("default_mode", "agent") => "Act".to_string(),
("default_mode", "plan") => "Plan (read only)".to_string(),
("default_mode", "operate") => "Operate".to_string(),
("work_surface_placement", "top") => "Top".to_string(),
("work_surface_placement", "left") => "Left sidebar".to_string(),
("work_surface_placement", "right") => "Right sidebar".to_string(),
@@ -3155,6 +3155,9 @@ fn config_choice_detail(locale: Locale, key: &str, value: &str) -> Cow<'static,
}
("default_mode", "agent") => "Start ready to collaborate and use tools.",
("default_mode", "plan") => "Start in a read-only planning workspace.",
("default_mode", "operate") => {
"Start as a coordinator that delegates work to bounded workers."
}
("work_surface_placement", "top") => "Show Tasks, To-do, and Workers above the transcript.",
("work_surface_placement", "left") => {
"Show Tasks, To-do, and Workers in a left sidebar when the terminal is wide enough."
@@ -6119,17 +6122,23 @@ base_url = "https://api.xiaomimimo.com/v1"
let edit = view.editing.as_ref().expect("choice editor");
assert_eq!(
edit.choices.as_deref(),
Some(&["agent".to_string(), "plan".to_string()][..])
Some(
&[
"agent".to_string(),
"plan".to_string(),
"operate".to_string(),
][..]
)
);
assert!(
edit.choices
.as_ref()
.expect("startup choices")
.iter()
.all(|choice| choice != "operate" && choice != "yolo")
.all(|choice| choice != "yolo")
);
let _ = view.handle_key(KeyEvent::new(KeyCode::Char('2'), KeyModifiers::NONE));
let _ = view.handle_key(KeyEvent::new(KeyCode::Char('3'), KeyModifiers::NONE));
let apply = view.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
match apply {
ViewAction::Emit(ViewEvent::ConfigUpdated {
@@ -6138,11 +6147,21 @@ base_url = "https://api.xiaomimimo.com/v1"
persist,
}) => {
assert_eq!(key, "default_mode");
assert_eq!(value, "plan");
assert_eq!(value, "operate");
assert!(persist);
}
other => panic!("expected startup choice update, got {other:?}"),
}
assert_eq!(
canonical_config_choice("default_mode", "Operate"),
"operate"
);
assert_eq!(
config_choice_label(Locale::En, "default_mode", "operate"),
"Operate"
);
assert!(!config_choice_detail(Locale::En, "default_mode", "operate").is_empty());
}
#[test]
+5 -5
View File
@@ -138,11 +138,11 @@
"screenshot": {
"readme": "assets/screenshot.png",
"website": "web/public/codewhale-tui.png",
"sourceVersion": "0.9.1",
"sourceCommit": "4d197626d72b4bd27e1abf4eed92e86e914414a8",
"terminal": "120x32",
"sourceVersion": null,
"sourceCommit": null,
"terminal": "unrecorded",
"theme": "Blue Stage dark",
"capture": "sealed local Ollama route rendered from the real v0.9.1 TUI in a 120x32 tmux PTY and rasterized with VHS",
"sources": ["crates/tui", "docs/evidence/v091-empty-work-surface-receipt.md"]
"capture": "current Codewhale session; exact binary identity was not recorded, so this image makes no published-release or exact-candidate claim",
"sources": ["crates/tui"]
}
}
+2 -2
View File
@@ -48,10 +48,10 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
locale,
title: isZh
? "Codewhale — 潜入数据与代码的深海,让你不必亲自下潜"
: "Codewhale — Dive into the deep of data and code so you don't have to.",
: "Codewhale — Dive into the deep so you don't have to.",
description: isZh
? "数据与代码如海。Codewhale 是给你杠杆的终端智能体——读取、修改、验证,让普通人也能用 LLM 把东西做出来。运行在你自己的机器上;Rust 编写,MIT 许可。"
: "We are entering a world with an ocean of data and code. Codewhale dives into the deep so you don't have to — a terminal agent that gives ordinary people the leverage of LLMs to build things. Runs on your machine. Rust, MIT.",
: "Codewhale dives into the deep so you don't have to — a terminal agent that gives ordinary people the leverage of LLMs to build things. Runs on your machine. Rust, MIT.",
});
}
+7 -15
View File
@@ -7,7 +7,6 @@ import { getFacts } from "@/lib/facts";
import { fill, getHome } from "@/lib/i18n/dictionaries";
const REPO = "https://github.com/Hmbown/CodeWhale";
const CAPTURED_SESSION_VERSION = "0.9.1";
// Revalidate against source-proven runtime facts without giving up static edge
// caching. `getFacts()` rejects legacy or older KV snapshots.
@@ -167,31 +166,24 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
<Whale size={20} />
Codewhale TUI
</span>
<span>v{CAPTURED_SESSION_VERSION}</span>
<span>{isZh ? "当前会话" : "Current session"}</span>
</div>
<Image
src="/codewhale-tui.png"
alt={
isZh
? `Codewhale v${CAPTURED_SESSION_VERSION} 的全新终端会话,使用本地 Ollama 路由且没有空的 Work 栏`
: foreign
? fill(d.screenshotAlt, { version: CAPTURED_SESSION_VERSION })
: `Fresh Codewhale v${CAPTURED_SESSION_VERSION} terminal session using a local Ollama route, with no empty Work bar`
? "Codewhale 当前终端会话,显示 Operate 模式、鲸鱼、输入区和状态栏"
: "Current Codewhale terminal session showing Operate mode, the whale, composer, and footer"
}
width={1280}
height={720}
width={1562}
height={1256}
sizes="(max-width: 900px) calc(100vw - 2rem), 58vw"
priority
/>
<figcaption>
{isZh
? `v${CAPTURED_SESSION_VERSION} 已发布版 · 本地 Ollama 路由 · Plan / Act / Operate`
: foreign
? fill(d.figcaption, {
version: CAPTURED_SESSION_VERSION,
state: d.publishedRelease,
})
: `v${CAPTURED_SESSION_VERSION} published release · local Ollama route · Plan / Act / Operate`}
? "当前 Codewhale 会话 · Operate 模式 · Ask 权限姿态"
: "Current Codewhale session · Operate mode · Ask permission posture"}
</figcaption>
</figure>
</div>
+1 -1
View File
@@ -8,7 +8,7 @@ export const SITE_NAME = "Codewhale";
/** The one-line product identity, used as the default OG image alt text. */
export const IDENTITY_PHRASE =
"An ocean of data and code — Codewhale dives so you don't have to.";
"Codewhale dives into the deep so you don't have to.";
/** Shared OG card rendered by app/opengraph-image.tsx (1200×630 PNG). */
const OG_IMAGE = {
+16 -15
View File
@@ -82,9 +82,10 @@ type PublicSurfaceMatrix = {
screenshot: {
readme: string;
website: string;
sourceVersion: string;
sourceCommit: string;
sourceVersion: string | null;
sourceCommit: string | null;
terminal: string;
capture: string;
sources: string[];
};
[key: string]: unknown;
@@ -135,18 +136,18 @@ describe("public surface contracts", () => {
expect(matrix.latestPublishedRelease).not.toHaveProperty("toolCount");
expect(matrix.surfaces).not.toHaveProperty("stable");
expect(matrix.surfaces.availableInSourceCandidate).toContain("Web client");
// A real screenshot may truthfully lag the source candidate until the
// release's visual capture is refreshed. It must identify the version it
// actually shows and may never claim a version newer than the candidate.
// A capture may identify an exact build or explicitly remain an unversioned
// current session. Never infer release provenance from pixels alone.
const screenshotSourceVersion = matrix.screenshot.sourceVersion;
expect(screenshotSourceVersion).not.toBeNull();
if (screenshotSourceVersion === null) {
throw new Error("the public screenshot must identify its source version");
if (screenshotSourceVersion !== null) {
expect(comparableVersion(screenshotSourceVersion)).toBeLessThanOrEqual(
comparableVersion(matrix.sourceCandidate.version),
);
expect(matrix.screenshot.sourceCommit).toMatch(/^[0-9a-f]{40}$/);
} else {
expect(matrix.screenshot.sourceCommit).toBeNull();
expect(matrix.screenshot.capture).toContain("no published-release or exact-candidate claim");
}
expect(comparableVersion(screenshotSourceVersion)).toBeLessThanOrEqual(
comparableVersion(matrix.sourceCandidate.version),
);
expect(matrix.screenshot.sourceCommit).toMatch(/^[0-9a-f]{40}$/);
});
it("backs product and install claims with package and documentation content", () => {
@@ -503,15 +504,15 @@ done
const digest = (image: Buffer) => createHash("sha256").update(image).digest("hex");
expect(digest(readmeImage)).toBe(digest(websiteImage));
expect(pngDimensions(readmeImage)).toEqual([1280, 720]);
expect(pngDimensions(readmeImage)).toEqual([1562, 1256]);
expect(statSync(new URL(matrix.screenshot.readme, root)).size).toBeLessThan(500_000);
expect(matrix.screenshot.terminal).toBe("120x32");
expect(matrix.screenshot.terminal).toBe("unrecorded");
const readme = text("README.md");
const homepage = text("web/app/[locale]/page.tsx");
expect(readme).toContain("assets/screenshot.png");
expect(homepage).toContain('src="/codewhale-tui.png"');
expect(homepage).toContain("with no empty Work bar");
expect(homepage).toContain("Current Codewhale session");
});
it("keeps reduced motion static without hiding the reasoning trace", () => {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 430 KiB