mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-10 10:01:43 -10:00
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:
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 |
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 |
Reference in new issue
Block a user