diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81859576ff..47fecd4727 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Alibaba Model Studio joins the data-driven provider table as an
+ `openai-compatible` descriptor: international compatible-mode endpoint,
+ `DASHSCOPE_API_KEY` credential, live `/v1/models` discovery. Qwen 3.8
+ Flash and Qwen 3.8 Max arrive through the catalog authority — never a
+ hard-coded id.
+
### Changed
+- The launch screen is our own card take: a thin top line
+ `⑂ branch path`; a centred bordered card with the whale mark,
+ `Codewhale` + version, one announcement line only when it is true (the
+ no-model warning, or MCP news), and the menu New worktree / Resume
+ session / Changelog / Quit with their real chords right-aligned. Enter
+ runs the highlighted entry, Up/Down move it, and typing goes straight to
+ the composer. The card dissolves on the first keystroke or command
+ (≤240 ms, instant under reduced motion).
+- After the card dissolves, the working screen shows `⑂ branch path` with
+ `⋮ MCP n/m` on the right, the transcript starts with the
+ `◆ session_start` receipt (naming the configured session-start hooks),
+ and the composer's bottom rule carries `model (effort) · permission` —
+ the route's one launch reading. The posture bar and metrics line appear
+ only once a session exists.
+- Vocabulary: `fleet` is the public term and `Pod` is retired from copy —
+ roster, setup, detail, worker-runtime and managed-API messages now say
+ Fleet (`/fleet` canonical, `/pod` alias). The workflow wire accepts the
+ canonical role spellings (general/explore/planner/reviewer/implement/
+ test/advisor) with the pre-rename ones kept as load-time aliases, and
+ serializes canonical names.
+- The Operate mode-picker hint is shortened to fit 80 columns.
+
- The footer always shows the permission posture; when only one chip
fits, the permission chip outranks the mode word (#5796).
- Local Ollama: the header names a model only when the local catalog can
diff --git a/DESIGN.md b/DESIGN.md
new file mode 100644
index 0000000000..f50685fd27
--- /dev/null
+++ b/DESIGN.md
@@ -0,0 +1,280 @@
+---
+name: Codewhale
+description: Quiet, dense, navy-and-blue design system for Codewhale — the terminal shell, the website, and the docs.
+colors:
+ # brand constants (brand/*.svg, shared with the TUI palette)
+ brand-black: "#000000"
+ brand-ink: "#070c1d"
+ brand-navy: "#0c1531"
+ brand-stage: "#142352"
+ brand-ivory: "#ffffff"
+ brand-ice: "#ddeef9"
+ brand-cobalt: "#1535b2"
+ brand-blue: "#6aa6dc"
+ brand-cyan: "#78bce8"
+ ombre-start: "#1535B2"
+ ombre-end: "#6AA6DC"
+ # surface tokens — crates/tui/src/palette/tokens.rs, exported to web/app/tokens.css (generated, never hand-edit)
+ bg: "#070c1d" # ink
+ chrome: "#0c1531" # navy
+ panel: "#101c40"
+ composer: "#142352" # stage — the raised input plate
+ elevated: "#1a2c63"
+ selection: "#1e3c8f"
+ border: "#2a3f72"
+ text-body: "#f6f2e8"
+ text-soft: "#b6c0d4"
+ text-muted: "#93a0b8"
+ action: "#6aa6dc" # sky — the ombre's light end
+ action-hover: "#8fc4f8"
+ cobalt: "#1535b2" # the ombre's dark end
+ ice: "#ddeef9"
+ cyan: "#78bce8"
+ success: "#9bd66f"
+ warning: "#ff7a59"
+ error: "#ff86b2"
+ human: "#f6c453"
+typography:
+ display:
+ fontFamily: "IBM Plex Sans Condensed, ui-sans-serif, system-ui, sans-serif"
+ fontSize: "clamp(2.25rem, 4.8vw, 4.25rem)"
+ fontWeight: 600
+ lineHeight: 1
+ letterSpacing: "-0.01em"
+ heading:
+ fontFamily: "IBM Plex Sans Condensed, ui-sans-serif, system-ui, sans-serif"
+ fontSize: "clamp(1.45rem, 2.6vw, 2.35rem)"
+ fontWeight: 600
+ lineHeight: 1.08
+ letterSpacing: "-0.01em"
+ subheading:
+ fontFamily: "IBM Plex Sans Condensed, ui-sans-serif, system-ui, sans-serif"
+ fontSize: "1.12rem"
+ fontWeight: 500
+ lineHeight: 1.25
+ body:
+ fontFamily: "IBM Plex Sans, ui-sans-serif, system-ui, sans-serif"
+ fontSize: "1rem"
+ fontWeight: 400
+ lineHeight: 1.6
+ meta:
+ fontFamily: "JetBrains Mono, ui-monospace, monospace"
+ fontSize: "0.75rem"
+ fontWeight: 500
+ letterSpacing: "0.08em"
+ code:
+ fontFamily: "JetBrains Mono, ui-monospace, monospace"
+ fontSize: "0.85rem"
+ fontWeight: 400
+ lineHeight: 1.55
+rounded:
+ none: "0px"
+ sm: "4px"
+ md: "6px"
+ pill: "999px"
+spacing:
+ xs: "4px"
+ sm: "8px"
+ md: "16px"
+ lg: "24px"
+ xl: "40px"
+ section: "clamp(3rem, 7vw, 6rem)"
+components:
+ button-primary:
+ backgroundColor: "{colors.action}"
+ textColor: "{colors.bg}"
+ rounded: "{rounded.sm}"
+ padding: "10px 16px"
+ typography: "{typography.meta}"
+ button-primary-hover:
+ backgroundColor: "{colors.action-hover}"
+ textColor: "{colors.bg}"
+ button-secondary:
+ backgroundColor: "{colors.panel}"
+ textColor: "{colors.text-body}"
+ rounded: "{rounded.sm}"
+ padding: "10px 16px"
+ card:
+ backgroundColor: "{colors.panel}"
+ textColor: "{colors.text-body}"
+ rounded: "{rounded.md}"
+ padding: "16px 18px"
+ nav:
+ backgroundColor: "{colors.bg}"
+ textColor: "{colors.text-soft}"
+ height: "62px"
+---
+
+## Overview
+
+Codewhale is an open-source Rust coding agent and TUI. Everything it shows —
+the shell, the website, the docs — is built to feel like one tool: a flat navy
+field, one blue action colour, white type, thin hairlines, and nothing
+decorative. The direction in one line: **it doesn't need to look special — it
+needs to look like Codewhale.** Density over drama, facts over claims,
+documentation-grade restraint on every surface including the landing page and
+the launch hero.
+
+This file is shared design context for every 0.9.12 lane (web, docs, TUI). The
+web-specific product truth is in `PRODUCT.md` next to it.
+
+## Anti-slop rules
+
+These are hard rules, not taste notes. A surface that breaks one is wrong even
+if it looks fine in a screenshot.
+
+1. **No gradients for decoration.** The ombre `#1535B2 → #6AA6DC` lives in the
+ mark and the wordmark only. No gradient backgrounds, gradient text,
+ gradient rules, radial "spotlight" glows, or gradient borders anywhere else.
+2. **No glassmorphism.** No `backdrop-filter`, no translucent frosted panels,
+ no blur. Surfaces are opaque tokens from the palette.
+3. **No generic SaaS hero.** No three-column feature grid with icons, no logo
+ wall, no testimonials, no floating product mock at an angle, no "trusted by",
+ no neon eyebrow line. The landing page states what the tool is, shows one
+ real screenshot, and gives the install command.
+4. **No fabricated evidence.** No invented terminal transcripts, reasoning
+ traces, benchmarks, or screenshots of a UI that has not shipped. Pending
+ media is labelled pending.
+5. **No shadow theatre.** No drop shadows on cards, buttons, or figures; depth is
+ one surface step and one hairline.
+6. **No second accent.** One action blue. Cyan is bounded to the composer
+ prompt glyph; state colours carry state only; violet is 1px rules, never
+ text.
+7. **No status-chip soup, no emoji, no all-caps headings.** Labels are
+ letterspaced mono at ≥ 0.7rem and there are few of them.
+8. **No text below the floors** (12px functional, 11.2px labels) and no
+ text/background pair under 4.5:1.
+
+## Colors
+
+One palette, owned by `crates/tui/src/palette/tokens.rs`, exported to
+`web/app/tokens.css` by `scripts/export-design-tokens.py` (regenerate, never
+edit). The brand-name aliases used in conversation map onto it: black `#000`,
+ink `#070c1d` = `bg`, navy `#0c1531` = `chrome`, stage `#142352` = `composer`,
+ivory `#fff`, ice `#ddeef9`, cobalt `#1535b2`, blue/sky `#6aa6dc` = `action`,
+cyan `#78bce8`. The TUI uses the same tokens through `palette::grammar` for
+status colours. `web/app/globals.css` maps them to semantic names: `--paper`
+(bg), `--paper-deep` (panel), `--ink` (text), `--ink-soft`, `--ink-mute`,
+`--indigo` (action), `--hairline` (action at 20 % alpha).
+
+- **Field:** on the web, `bg` for the page and the transcript and `chrome`
+ for bars and the dock; in the TUI the ground is the terminal's own
+ background (`Color::Reset`), and the navy field is painted only under the
+ opt-in `ocean_treatment = "deepsea"` (alias `underwater`) column. `panel`
+ for cards and code, `composer` for the raised input plate, `elevated` only
+ for overlays. Never stack more than two surface steps in one view.
+- **Type:** `text-body` for copy, `text-soft` for secondary, `text-muted` for
+ meta. `text-dim` (#697791) is for borders only — it fails AA on the panels.
+- **Action:** one blue (`action`) for links, buttons, and focus rings; hover
+ lifts to `action-hover`. `cyan` is a bounded accent (eyebrow chrome, composer
+ prompt) — never body text, never fills.
+- **Brand ombre** `#1535B2 → #6AA6DC` exists only in the mark and wordmark.
+ Do not paint UI with it.
+- **State colours** (`success`, `warning`, `error`, `human`) carry meaning; do
+ not use them decoratively, and never convey state by colour alone.
+
+Contrast: every text/background pair in use is ≥ 4.5:1 (`text-muted` on
+`panel` 6.3:1, on `composer` 5.7:1, on `bg` 7.4:1; `action` on `bg` 7.5:1).
+
+## Typography
+
+Three faces, one job each:
+
+- **IBM Plex Sans Condensed 600** — display and all headings (`--font-display`).
+ Also the wordmark: "codewhale" in Plex Sans Condensed SemiBold, outlined to
+ paths, letter-spacing −0.01em. Tight leading (1.0–1.08), no all-caps headings.
+- **IBM Plex Sans 400/500/600** — body (`--font-body`). Measure ≤ 70ch.
+- **JetBrains Mono 400/500** — code and the mono meta rows (`--font-mono`).
+ Letterspaced uppercase mono is the only "label" style.
+
+Floors: functional text (links, nav, labels, meta, footer) never below
+**12px (0.75rem)**; letterspaced micro-labels never below **11.2px (0.7rem)**;
+legal smallprint never below 10px. Heading outline is strict: h1 → h2 → h3, no
+skipped levels; use CSS, not a lower heading tag, to make something smaller.
+
+## Shell (TUI) direction
+
+The redesigned 0.9.12 shell is the reference the website mirrors. Top to
+bottom (founder decisions, 2026-09-01/02 — see
+`codewhale-ops/design/SHELL-DESIGN-20260901.md` for the dated record):
+
+1. **Transcript first.** The conversation owns the screen: no permanent side
+ chrome, no top bar. Turns are plain text on the terminal's own ground;
+ tool output is `text-soft` on `panel`; reasoning is the reasoning tint,
+ collapsed by default.
+2. **Launch is a card, not a hero.** A thin top line (`⑂ branch path`);
+ a centred bordered card: the whale mark at left, `Codewhale` + version,
+ one announcement line only when it is true, then the menu **New
+ worktree · Resume session · Changelog · Quit** with real chords
+ right-aligned. Enter runs the highlighted entry; typing goes straight to
+ the composer; the card dissolves on the first keystroke or command.
+3. **Composer.** The raised `composer` plate directly under the transcript,
+ cyan `❯` prompt glyph. While the card is up its bottom rule carries
+ `model (effort) · permission` — the route's one launch reading.
+4. **Posture bar + metrics line** under the composer, once a session
+ exists: `▶▶ ask (Shift+Tab) · work (Tab) · 2 agents · Esc to interrupt …`
+ and `model · ctx NN% · $cost · ttft · tok/s · ↓ tokens … Ctrl+/ help`.
+ Context % lives in the metrics line only.
+5. **Bottom views, not a dock.** One region under the composer cycles
+ through agents → tasks → background → files → notepad → context → git →
+ price (Ctrl+Tab forward, Ctrl+Shift+Tab back where the terminal
+ delivers it). Zero rows when idle with nothing to show. There is no
+ rail, no sidebar, no tab band.
+
+## Layout (web)
+
+- Single content column, `.product-container` max 72rem, 1rem side padding at
+ 390px. The landing has no permanent side chrome; docs have a left contents
+ rail at ≥ 1050px that collapses into a top list below.
+- Sections are separated by one hairline and generous vertical space
+ (`spacing.section`), not by background colour changes.
+- Prose measure ≤ 70ch on docs; wide code blocks scroll horizontally inside
+ their panel rather than widening the column.
+- Breakpoints in use: 640px (hero stacks, h1 drops to `clamp(2rem, 10vw, 3rem)`),
+ 900px (hero two-column), 1050px (docs rail).
+- No horizontal overflow at 390px, ever.
+
+## Elevation & Depth
+
+Flat. Depth is expressed by one surface step (`bg` → `panel`) and one hairline.
+No drop shadows on cards or buttons; the only shadow is the raised composer
+plate in the TUI screenshot itself. No blur, no glass, no glow.
+
+## Shapes
+
+Small radii: 4px on controls, 6px on cards and code panels, 999px only on the
+GitHub-stars pill. No rounded-2xl, no circles as decoration. Whale mark is the
+only curved form.
+
+## Components (web)
+
+- **Nav:** 62px bar, `bg`, hairline below. Left: whale mark (22px) + wordmark
+ (20px) as one link labelled "Codewhale home". Centre: text links in body
+ face. Right: theme, locale, stars pill, sign in / register (mono meta), one
+ filled Install button. Collapses to a menu button below 900px.
+- **Buttons:** filled action blue (primary), panel with hairline (secondary),
+ text-only (ghost). Mono meta type, uppercase, 0.08em tracking. Visible focus
+ ring in `action` on every control.
+- **Cards / steps:** `panel` bg, hairline border, 16–18px padding, number in
+ `action` mono, h3 in display face, body in `text-soft`.
+- **Code blocks:** `panel` bg, JetBrains Mono 0.85rem, copy button top-right,
+ scroll-x inside.
+- **Eyebrow:** mono, uppercase, 0.7rem, `cyan` or `text-muted`.
+- **Footer:** `bg`, hairline top, inverted wordmark, column links at 0.75rem
+ mono; legal line at 0.7rem.
+- **Media slots:** real assets only; a `pending` slot renders a labelled empty
+ panel, never a mock.
+
+## Do's and Don'ts
+
+Do
+- Derive every fact from the repo; one owner per number.
+- Keep the whale mark and wordmark together in the nav; wordmark alone in the footer.
+- Use the display face for headings and the wordmark, nothing else.
+- Meet AA and the 12px floor before shipping any new surface.
+
+Don't
+- Fabricate terminal output, reasoning traces, testimonials, or screenshots.
+- Introduce a second accent colour, gradients on UI, shadows, or glass.
+- Add a UI library or page-local copy; extend `lib/content/` and the dictionaries.
+- Use `text-dim` for text, skip heading levels, or shrink type below the floors.
diff --git a/PRODUCT.md b/PRODUCT.md
new file mode 100644
index 0000000000..45bca23408
--- /dev/null
+++ b/PRODUCT.md
@@ -0,0 +1,107 @@
+# Product
+
+
+
+## Platform
+
+web (the public site and docs in `web/`), documenting a terminal application
+(the Rust TUI in `crates/tui`). Paths below are relative to the repository
+root.
+
+## Users
+
+Developers who run a coding agent in their own terminal against their own
+repositories: solo maintainers, small teams, and open-source contributors. They
+arrive at the site to decide whether to install, to install, and then to look up
+how a command, mode, or concept works. Many already use a competing agent and
+compare on model choice, cost, and control.
+
+## Product Purpose
+
+Codewhale is an open-source (MIT) coding agent and terminal UI written in Rust
+(Ratatui + Tokio; sandboxed tools via Bubblewrap/Seatbelt). Given a model and a
+task it reads the repository, edits files, runs the checks, and stops when the
+job is done or it needs a human. The site exists to (1) get a developer from
+"what is this" to a working install in one screen, and (2) be the canonical,
+current documentation for the shipped release. Success is an install that works
+and a docs answer found without leaving the page.
+
+## Positioning
+
+Bring your own model. Codewhale is provider-neutral: any hosted, gateway, or
+local model, and a different model per role. The user's model inventory is the
+**Fleet** (`codewhale fleet`, `/fleet`; `pod` remains a compatibility alias).
+Modes are Plan, Work, Operate; permission levels are Ask, Auto-Review, Full
+Access. The agent runs on the user's machine, in the user's terminal — there is
+no hosted runtime to sell.
+
+## Operating Context
+
+- Install: `npm install -g codewhale` (Node 18+, no Rust toolchain); source
+ builds via cargo. Facts (version, provider count, tool count, license) are
+ derived from the repository by `npm run prebuild` into
+ `web/lib/facts.generated.ts` and must never be hand-edited.
+- Docs pages mirror `docs/*.md` in the repository; `npm run check:docs`
+ verifies the mapping. Public vocabulary lives in
+ `web/lib/content/vocabulary.ts` and `docs/public-surface-facts.json`.
+- Localised through shared dictionaries in `web/lib/i18n/dictionaries/` with
+ locale-key parity enforced; no page-local copy forks.
+- The 0.9.12 shell (on the integration branch): transcript first, composer
+ plate, one info line, and a bottom dock with tabs Tasks / Agents / Context /
+ Pinned (+ ×). There is no top bar; docs that describe the shell describe the
+ dock.
+
+## Capabilities and Constraints
+
+- Public name is **Codewhale** (lowercase w). `CodeWhale` survives only in
+ compatibility identifiers (GitHub org/repo, package scopes).
+- Provider and model names are first-class and neutral; never rank providers
+ in copy.
+- The 0.9.12 shell is not yet released: there are no real screenshots or
+ video of it. `web/lib/media-manifest.ts` marks such media `pending`; the
+ site must not ship mockups as screenshots. The one real screenshot on hand is
+ `web/public/codewhale-tui.webp` (0.9.11).
+- `/context-window` does not exist on the current base; do not document it.
+- Subagent role identifiers are those the code accepts (`general`, `explore`,
+ `planner`, `reviewer`, `implement`, `test`, `advisor`, `custom`); the older
+ spellings `worker`, `scout`, `builder`, `verifier`, `consultant`, and `oracle`
+ are accepted as compatibility aliases only. Do not invent public role names.
+
+## Brand Commitments
+
+- Voice: quiet, dense, factual. Terminal vocabulary, no marketing superlatives,
+ no fabricated transcripts or reasoning traces.
+- "It doesn't need to look special — it needs to look like Codewhale."
+- Assets: the founder's whale mark traced to `brand/mark.svg` (with
+ `brand/mark-navy.svg`, `brand/mark-gradient.svg`) and the founder's rounded
+ monoline wordmark traced from `brand/wordmark0901.png` (`brand/wordmark.svg`
+ navy #142352, `brand/wordmark-inverted.svg` white; regenerated with the site
+ icons by `scripts/brand/trace-brand.py`). Web copies live in
+ `web/public/brand/`.
+- Palette, type, shell direction, and the anti-slop rules are recorded in
+ `DESIGN.md`; the colour tokens are owned by `crates/tui/src/palette/tokens.rs`
+ and exported to `web/app/tokens.css`.
+
+## Evidence on Hand
+
+- Real: GitHub stars (live), release version and changelog (generated),
+ provider/tool counts (generated), the 0.9.11 TUI screenshot.
+- Absent, do not fabricate: testimonials, customer logos, benchmarks,
+ pricing, 0.9.12 TUI media.
+
+## Product Principles
+
+1. One owner per fact: every number and command on the site is derived from
+ the repository, never typed twice.
+2. Content first: no permanent side chrome on the landing page; the docs page
+ is a reading surface, not a portal.
+3. Show only what exists: pending media stays marked pending; commands are
+ documented only once they are on the base branch.
+4. Provider-neutral, model-neutral, always.
+5. Accessibility is not negotiable: AA contrast, ≥12px functional text, real
+ heading outline, keyboard-reachable everything.
+
+## Accessibility & Inclusion
+
+WCAG 2.2 AA for text and controls. The audience includes screen-reader and
+keyboard-only developers; the site is also read at 390px on phones.
diff --git a/README.ar.md b/README.ar.md
index 5030a0d4df..eec29b63f7 100644
--- a/README.ar.md
+++ b/README.ar.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale وكيل مفتوح المصدر للبرمجة عبر الطرفية، مبني بلغة Rust ويتطور علنًا بالتعاون مع الأشخاص الذين يستخدمونه.
diff --git a/README.ca.md b/README.ca.md
index 1e46baab7c..27ca523a8a 100644
--- a/README.ca.md
+++ b/README.ca.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale és un agent de programació de codi obert per al terminal, desenvolupat amb Rust i millorat públicament amb les persones que l’utilitzen.
diff --git a/README.de.md b/README.de.md
index 1ced4efa1d..e7747a8548 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale ist ein in Rust entwickelter Open-Source-Coding-Agent für dein Terminal, der gemeinsam mit seinen Nutzerinnen und Nutzern öffentlich weiterentwickelt wird.
diff --git a/README.es-419.md b/README.es-419.md
index cfbc48cb2c..5287db9e92 100644
--- a/README.es-419.md
+++ b/README.es-419.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale es un agente de programación de código abierto para tu terminal, desarrollado en Rust y mejorado públicamente junto con las personas que lo usan.
diff --git a/README.fr.md b/README.fr.md
index be03072155..8bd2ff23cc 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale est un agent de programmation open source pour votre terminal, développé en Rust et amélioré publiquement avec les personnes qui l’utilisent.
diff --git a/README.hi.md b/README.hi.md
index 167b062362..b116ebf19b 100644
--- a/README.hi.md
+++ b/README.hi.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale आपके टर्मिनल के लिए Rust में बना एक ओपन सोर्स कोडिंग एजेंट है, जिसे इसके उपयोगकर्ताओं के साथ सार्वजनिक रूप से बेहतर बनाया जाता है।
diff --git a/README.id.md b/README.id.md
index 4c2349a78f..3a0e186646 100644
--- a/README.id.md
+++ b/README.id.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale adalah agen pemrograman sumber terbuka untuk terminal Anda, dibuat dengan Rust dan dikembangkan secara terbuka bersama orang-orang yang menggunakannya.
diff --git a/README.it.md b/README.it.md
index d14ac5190d..06049c0648 100644
--- a/README.it.md
+++ b/README.it.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale è un agente di programmazione open source per il terminale, sviluppato in Rust e migliorato pubblicamente insieme alle persone che lo utilizzano.
diff --git a/README.ja-JP.md b/README.ja-JP.md
index 0305e09255..89e047caa0 100644
--- a/README.ja-JP.md
+++ b/README.ja-JP.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale は Rust で構築された、ターミナル向けのオープンソース・コーディングエージェントです。利用者とともに、公開の場で改善を続けています。
diff --git a/README.ko-KR.md b/README.ko-KR.md
index 2b77339fec..cd36ce852f 100644
--- a/README.ko-KR.md
+++ b/README.ko-KR.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale은 Rust로 만든 터미널용 오픈 소스 코딩 에이전트로, 사용자들과 함께 공개적으로 개선해 나갑니다.
diff --git a/README.md b/README.md
index e234b6a574..81d5ab81ba 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,10 @@
Codewhale is an open source coding agent for your terminal, built in Rust and
improved in public with the people who use it.
-
+
+
+
+
[简体中文](README.zh-CN.md) · [日本語](README.ja-JP.md) · [Tiếng Việt](README.vi.md) · [Bahasa Indonesia](README.id.md) · [한국어](README.ko-KR.md) · [Español](README.es-419.md) · [Português](README.pt-BR.md) · [Русский](README.ru.md) · [Українська](README.uk.md) · [Français](README.fr.md) · [Deutsch](README.de.md) · [繁體中文](README.zh-TW.md) · [हिन्दी](README.hi.md) · [Türkçe](README.tr.md) · [Italiano](README.it.md) · [Polski](README.pl.md) · [العربية](README.ar.md) · [Català](README.ca.md)
@@ -12,6 +15,11 @@ improved in public with the people who use it.
[](https://www.npmjs.com/package/codewhale)
[](https://discord.gg/37gfS3ksug)
+
+
+
+
+
## Install
```bash
diff --git a/README.pl.md b/README.pl.md
index 93b2196a24..c62cb5de8c 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale to agent programistyczny o otwartym kodzie źródłowym do terminala, napisany w Rust i rozwijany publicznie wspólnie z osobami, które go używają.
diff --git a/README.pt-BR.md b/README.pt-BR.md
index f24651a395..40e43c24f6 100644
--- a/README.pt-BR.md
+++ b/README.pt-BR.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale é um agente de programação de código aberto para o seu terminal, desenvolvido em Rust e aprimorado publicamente com as pessoas que o utilizam.
diff --git a/README.ru.md b/README.ru.md
index 92821b3296..3fae111884 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale — это агент для программирования с открытым исходным кодом, работающий в терминале. Он написан на Rust и открыто развивается вместе со своими пользователями.
diff --git a/README.tr.md b/README.tr.md
index 76ada46877..204b7ebfaf 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale, terminaliniz için Rust ile geliştirilmiş ve kullanıcılarıyla birlikte açık biçimde iyileştirilen açık kaynaklı bir kodlama ajanıdır.
diff --git a/README.uk.md b/README.uk.md
index 16974f43df..5d02de2ecb 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale — це агент програмування з відкритим кодом для вашого термінала, створений на Rust і вдосконалюваний публічно разом із людьми, які ним користуються.
diff --git a/README.vi.md b/README.vi.md
index 89ed558bf0..ecc8f9c5f5 100644
--- a/README.vi.md
+++ b/README.vi.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale là tác nhân lập trình mã nguồn mở dành cho terminal, được xây dựng bằng Rust và được cải thiện công khai cùng những người sử dụng nó.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 92b3cd613c..98d659426c 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale 是一款面向终端的开源编程智能体,使用 Rust 构建,并与用户一起在公开协作中不断改进。
diff --git a/README.zh-TW.md b/README.zh-TW.md
index bfb5a77441..4c124429da 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -1,4 +1,4 @@
-
+
# Codewhale
Codewhale 是一款在終端機中使用的開源程式設計代理,以 Rust 打造,並與使用者一起透過公開協作持續改進。
diff --git a/brand/mark-gradient.svg b/brand/mark-gradient.svg
new file mode 100644
index 0000000000..412eb6c121
--- /dev/null
+++ b/brand/mark-gradient.svg
@@ -0,0 +1 @@
+
diff --git a/brand/mark-navy.svg b/brand/mark-navy.svg
new file mode 100644
index 0000000000..4272f677ae
--- /dev/null
+++ b/brand/mark-navy.svg
@@ -0,0 +1 @@
+
diff --git a/brand/mark.svg b/brand/mark.svg
new file mode 100644
index 0000000000..75b6a4d86d
--- /dev/null
+++ b/brand/mark.svg
@@ -0,0 +1 @@
+
diff --git a/brand/wordmark-inverted.svg b/brand/wordmark-inverted.svg
new file mode 100644
index 0000000000..e537014177
--- /dev/null
+++ b/brand/wordmark-inverted.svg
@@ -0,0 +1 @@
+
diff --git a/brand/wordmark.svg b/brand/wordmark.svg
new file mode 100644
index 0000000000..81133ab7e2
--- /dev/null
+++ b/brand/wordmark.svg
@@ -0,0 +1 @@
+
diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs
index 19aea1493f..ce110041ff 100644
--- a/crates/cli/src/lib.rs
+++ b/crates/cli/src/lib.rs
@@ -202,23 +202,23 @@ non-interactive filesystem/shell tool use, matching the supported automation
path used by stream-json wrappers.
")]
Exec(TuiPassthroughArgs),
- /// Manage durable Agent Pod runs.
+ /// Manage durable Agent fleet runs.
///
- /// `pod` is the canonical spelling. `codewhale fleet` remains accepted as
+ /// `fleet` is the canonical spelling. `codewhale pod` remains accepted as
/// a compatibility alias for the identical command: the durable ledger,
/// receipts, config tables, and `--fleet` workflow flag keep the Fleet
/// serialization name.
#[command(
- name = "pod",
- alias = "fleet",
+ name = "fleet",
+ alias = "pod",
after_help = "\
Examples:
- codewhale pod init
- codewhale pod run tasks.json --max-workers 4
- codewhale pod status
+ codewhale fleet init
+ codewhale fleet run tasks.json --max-workers 4
+ codewhale fleet status
-`codewhale fleet` is a compatibility alias for this command and dispatches
-identically, as `/fleet` does for the `/pod` slash command. What keeps the
+`codewhale pod` is a compatibility alias for this command and dispatches
+identically, as `/pod` does for the `/fleet` slash command. What keeps the
Fleet name is everything that has to stay readable across versions: the
durable ledger `.codewhale/fleet.jsonl`, saved rosters `fleets/.toml`,
the `[fleet]` and `[fleets.*]` config tables, and `workflow run --fleet`."
@@ -6491,12 +6491,12 @@ verbosity = "project-imported"
));
}
- /// Pod is the canonical customer-facing top-level command; `fleet` is a
+ /// Fleet is the canonical customer-facing top-level command; `pod` is a
/// compatibility alias that must keep dispatching to the same code path.
- /// The Fleet spelling survives on purpose in the durable ledger, saved
+ /// The Fleet spelling was always the one in the durable ledger, saved
/// roster files, config tables, and the `workflow --fleet` flag.
#[test]
- fn pod_is_the_canonical_top_level_command_and_fleet_stays_a_compatibility_alias() {
+ fn fleet_is_the_canonical_top_level_command_and_pod_stays_a_compatibility_alias() {
for tail in [
vec!["init"],
vec!["status"],
@@ -6524,8 +6524,8 @@ verbosity = "project-imported"
assert!(pod.prompt.is_empty() && fleet.prompt.is_empty(), "{tail:?}");
}
- // Help advertises Pod. The alias still resolves, but discovery has one
- // canonical answer, so `fleet` must not be listed as its own command.
+ // Help advertises fleet. The alias still resolves, but discovery has one
+ // canonical answer, so `pod` must not be listed as its own command.
let help = help_for(&["codewhale", "--help"]);
let commands = help
.lines()
@@ -6537,20 +6537,20 @@ verbosity = "project-imported"
1,
"expected exactly one entry: {commands:?}"
);
- assert!(commands[0].starts_with("pod"), "{commands:?}");
+ assert!(commands[0].starts_with("fleet"), "{commands:?}");
assert!(
- commands[0].contains("Pod"),
- "help summary should name Pod: {commands:?}"
+ commands[0].contains("fleet"),
+ "help summary should name fleet: {commands:?}"
);
assert!(
- !help.contains("Manage durable Agent Fleet runs"),
- "the old Fleet-led summary must be gone from top-level help"
+ !help.contains("Manage durable Agent Pod runs"),
+ "the retired Pod-led summary must be gone from top-level help"
);
- let pod_help = help_for(&["codewhale", "pod", "--help"]);
- assert!(pod_help.contains("Manage durable Agent Pod runs"));
- assert!(pod_help.contains("codewhale pod run tasks.json --max-workers 4"));
- assert!(pod_help.contains("codewhale fleet` is a compatibility alias"));
+ let fleet_help = help_for(&["codewhale", "fleet", "--help"]);
+ assert!(fleet_help.contains("Manage durable Agent fleet runs"));
+ assert!(fleet_help.contains("codewhale fleet run tasks.json --max-workers 4"));
+ assert!(fleet_help.contains("codewhale pod` is a compatibility alias"));
// Both spellings normalize to the canonical inner command so receipts
// and any echoed invocation never regress to the compatibility name.
diff --git a/crates/config/assets/provider_descriptors.json b/crates/config/assets/provider_descriptors.json
index f12bc50fed..7888d93017 100644
--- a/crates/config/assets/provider_descriptors.json
+++ b/crates/config/assets/provider_descriptors.json
@@ -46,6 +46,19 @@
"docs_url": "https://inference-docs.cerebras.ai/quickstart",
"credential_url": "https://cloud.cerebras.ai"
},
+ {
+ "id": "dashscope",
+ "label": "Alibaba Model Studio (DashScope)",
+ "wire": "openai-compatible",
+ "base_url": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
+ "api_key_env": "DASHSCOPE_API_KEY",
+ "default_model": "qwen3.8-flash",
+ "discovery": "models_endpoint",
+ "docs_url": "https://www.alibabacloud.com/help/en/model-studio/getting-started/models",
+ "credential_url": "https://bailian.console.alibabacloud.com/",
+ "guidance": "International Model Studio compatible-mode endpoint (OpenAI Chat Completions + GET /v1/models). Live /v1/models is the model authority: Qwen 3.8 Flash and the dated 0901 release arrive through the catalog, never a hard-coded id. Store DASHSCOPE_API_KEY, not a raw key.",
+ "aliases": ["dashscope-intl", "alibaba-model-studio", "model-studio", "qwen"]
+ },
{
"id": "command-code",
"label": "Command Code",
diff --git a/crates/config/src/descriptors.rs b/crates/config/src/descriptors.rs
index a7a9518c59..c4d685392b 100644
--- a/crates/config/src/descriptors.rs
+++ b/crates/config/src/descriptors.rs
@@ -98,8 +98,8 @@ mod tests {
fn descriptors_parse_and_command_code_is_a_row_not_a_kind() {
let rows = bundled_provider_descriptors();
assert!(
- rows.len() >= 5,
- "expected compatible hosts plus command-code"
+ rows.len() >= 6,
+ "expected compatible hosts plus command-code and dashscope"
);
for row in rows {
assert!(row.base_url.starts_with("https://"), "{}", row.id);
@@ -115,6 +115,19 @@ mod tests {
provider_descriptor("cmd-code").map(|row| row.id.as_str()),
Some("command-code")
);
+ // Alibaba Model Studio is a data-driven row: live /v1/models is the
+ // Qwen model authority, never a compiled roster.
+ let dashscope = provider_descriptor("dashscope").expect("dashscope");
+ assert_eq!(
+ dashscope.base_url,
+ "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
+ );
+ assert_eq!(dashscope.api_key_env, "DASHSCOPE_API_KEY");
+ assert_eq!(
+ provider_descriptor("qwen").map(|row| row.id.as_str()),
+ Some("dashscope"),
+ "the founder's `qwen` name resolves to the DashScope row"
+ );
}
#[test]
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs
index 1989519482..8f85f47811 100644
--- a/crates/config/src/lib.rs
+++ b/crates/config/src/lib.rs
@@ -1348,13 +1348,13 @@ pub const DEFAULT_HOTBAR_ACTIONS: [&str; HOTBAR_SLOT_COUNT as usize] = [
/// Normalize persisted action ids at the compatibility boundary.
///
/// `/pod` is the canonical public command, but existing settings may still
-/// contain the former `slash.fleet` hotbar id. Resolution and direct registry
+/// contain the former `slash.pod` hotbar id. Resolution and direct registry
/// lookup both use this helper so those slots continue to dispatch while any
/// subsequent save naturally writes the canonical id.
#[must_use]
pub fn normalize_hotbar_action_id(action_id: &str) -> &str {
match action_id {
- "slash.fleet" => "slash.pod",
+ "slash.pod" => "slash.fleet",
other => other,
}
}
diff --git a/crates/config/src/tests.rs b/crates/config/src/tests.rs
index 56cfcac073..c4675a075a 100644
--- a/crates/config/src/tests.rs
+++ b/crates/config/src/tests.rs
@@ -512,31 +512,31 @@ action = "session.compact"
}
#[test]
-fn legacy_fleet_hotbar_action_resolves_to_canonical_pod_without_rewriting_disk() {
+fn legacy_pod_hotbar_action_resolves_to_canonical_fleet_without_rewriting_disk() {
let config: ConfigToml = toml::from_str(
r#"
[[hotbar]]
slot = 3
-action = "slash.fleet"
-label = "Pod"
+action = "slash.pod"
+label = "fleet"
"#,
)
.expect("parse legacy hotbar binding");
- let resolved = config.resolve_hotbar_bindings(&["slash.pod"]);
+ let resolved = config.resolve_hotbar_bindings(&["slash.fleet"]);
assert_eq!(resolved.warnings, Vec::new());
assert_eq!(
resolved.bindings,
vec![HotbarBinding {
slot: 3,
- action: "slash.pod".to_string(),
- label: Some("Pod".to_string()),
+ action: "slash.fleet".to_string(),
+ label: Some("fleet".to_string()),
}]
);
assert_eq!(
config.hotbar.as_ref().unwrap()[0].action,
- "slash.fleet",
+ "slash.pod",
"read-time compatibility must not mutate the parsed on-disk value"
);
}
diff --git a/crates/lane/src/control.rs b/crates/lane/src/control.rs
index ba5f9ff7b7..507fce0694 100644
--- a/crates/lane/src/control.rs
+++ b/crates/lane/src/control.rs
@@ -131,7 +131,7 @@ impl ControlExecution {
pub enum ControlDomain {
/// One running Workflow, recorded in `$CODEWHALE_HOME/lanes/`.
Lane,
- /// Pod workers and runs, recorded in `/.codewhale/fleet.jsonl`.
+ /// fleet workers and runs, recorded in `/.codewhale/fleet.jsonl`.
Fleet,
}
@@ -149,7 +149,7 @@ impl ControlDomain {
pub const fn public_name(self) -> &'static str {
match self {
Self::Lane => "lane",
- Self::Fleet => "pod",
+ Self::Fleet => "fleet",
}
}
}
@@ -708,9 +708,9 @@ pub static OPERATIONS: &[OperationDescriptor] = &[
backend: BackendCapability::Implemented,
reconciles: false,
hotbar_bare_dispatch: false,
- slash_command: "pod",
- cli_invocation: "codewhale pod list",
- summary: "List durable Pod runs from the workspace ledger.",
+ slash_command: "fleet",
+ cli_invocation: "codewhale fleet list",
+ summary: "List durable fleet runs from the workspace ledger.",
},
OperationDescriptor {
operation: ControlOperation::FleetStatus,
@@ -725,8 +725,8 @@ pub static OPERATIONS: &[OperationDescriptor] = &[
backend: BackendCapability::Implemented,
reconciles: false,
hotbar_bare_dispatch: false,
- slash_command: "pod",
- cli_invocation: "codewhale pod status",
+ slash_command: "fleet",
+ cli_invocation: "codewhale fleet status",
summary: "Show durable Pod run/worker counts from the workspace ledger.",
},
OperationDescriptor {
@@ -742,9 +742,9 @@ pub static OPERATIONS: &[OperationDescriptor] = &[
backend: BackendCapability::Implemented,
reconciles: false,
hotbar_bare_dispatch: false,
- slash_command: "pod",
- cli_invocation: "codewhale pod interrupt ",
- summary: "Cancel a Pod worker's active task in the durable ledger.",
+ slash_command: "fleet",
+ cli_invocation: "codewhale fleet interrupt ",
+ summary: "Cancel a fleet worker's active task in the durable ledger.",
},
OperationDescriptor {
operation: ControlOperation::FleetRestart,
@@ -762,9 +762,9 @@ pub static OPERATIONS: &[OperationDescriptor] = &[
},
reconciles: false,
hotbar_bare_dispatch: false,
- slash_command: "pod",
- cli_invocation: "codewhale pod restart ",
- summary: "Re-lease a Pod worker's task and drive the manager loop.",
+ slash_command: "fleet",
+ cli_invocation: "codewhale fleet restart ",
+ summary: "Re-lease a fleet worker's task and drive the manager loop.",
},
OperationDescriptor {
operation: ControlOperation::FleetResume,
@@ -779,8 +779,8 @@ pub static OPERATIONS: &[OperationDescriptor] = &[
backend: BackendCapability::Implemented,
reconciles: false,
hotbar_bare_dispatch: false,
- slash_command: "pod",
- cli_invocation: "codewhale pod resume ",
+ slash_command: "fleet",
+ cli_invocation: "codewhale fleet resume ",
summary: "Reconcile a durable Pod run's orphaned leases after a manager restart.",
},
];
@@ -2396,7 +2396,7 @@ mod tests {
// The CLI and slash surfaces must name the same canonical noun.
// `domain.as_str()` is the serialization key ("fleet"), which the
// ledger, receipts, and config tables still use; the customer-
- // facing spelling is the slash command ("pod").
+ // facing spelling is the slash command ("fleet").
assert!(
descriptor.cli_invocation.contains(descriptor.slash_command),
"{} CLI invocation must name the same canonical noun as its slash command",
@@ -2443,8 +2443,8 @@ mod tests {
}
#[test]
- fn pod_is_public_while_fleet_remains_the_durable_domain_key() {
- assert_eq!(ControlDomain::Fleet.public_name(), "pod");
+ fn fleet_is_public_and_remains_the_durable_domain_key() {
+ assert_eq!(ControlDomain::Fleet.public_name(), "fleet");
assert_eq!(ControlDomain::Fleet.as_str(), "fleet");
assert_eq!(
serde_json::to_string(&ControlDomain::Fleet).expect("serialize durable domain"),
diff --git a/crates/tui/AGENTS.md b/crates/tui/AGENTS.md
index 7268f3e0ef..bf28fca84c 100644
--- a/crates/tui/AGENTS.md
+++ b/crates/tui/AGENTS.md
@@ -5,9 +5,12 @@ Read the repository guidance first.
## UI contracts
-- One owner per fact: route and context in the Tideline topbar; work in the top
- strip; receipts and the active row in the transcript; phase, cost, detail,
- mode, and permission in the merged footer.
+- One owner per fact: mode, permission and live counts in the posture bar
+ (`phase_strip.rs`, row 1 under the composer); model, context and the
+ session metrics — cost, ttft, tok/s, output tokens — in the metrics line
+ (`infoline.rs`, row 2); the roster and to-do in the work surface; receipts,
+ the active row and the phase in the transcript. Key hints come from the
+ `shell_key_routing` binding table, never from a string literal.
- Status-bar ink goes through `palette::grammar` (`docs/design/STATUS_BAR_COLOR_GRAMMAR.md`).
Do not invent an eighth semantic or spend Failure red on non-failure chrome.
- Derive state from typed enums such as `ShellPhase` and `OceanTreatment`.
diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md
index 20efe4266a..3c41bef339 100644
--- a/crates/tui/CHANGELOG.md
+++ b/crates/tui/CHANGELOG.md
@@ -7,8 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Alibaba Model Studio joins the data-driven provider table as an
+ `openai-compatible` descriptor: international compatible-mode endpoint,
+ `DASHSCOPE_API_KEY` credential, live `/v1/models` discovery. Qwen 3.8
+ Flash and Qwen 3.8 Max arrive through the catalog authority — never a
+ hard-coded id.
+
### Changed
+- The launch screen is our own card take: a thin top line
+ `⑂ branch path`; a centred bordered card with the whale mark,
+ `Codewhale` + version, one announcement line only when it is true (the
+ no-model warning, or MCP news), and the menu New worktree / Resume
+ session / Changelog / Quit with their real chords right-aligned. Enter
+ runs the highlighted entry, Up/Down move it, and typing goes straight to
+ the composer. The card dissolves on the first keystroke or command
+ (≤240 ms, instant under reduced motion).
+- After the card dissolves, the working screen shows `⑂ branch path` with
+ `⋮ MCP n/m` on the right, the transcript starts with the
+ `◆ session_start` receipt (naming the configured session-start hooks),
+ and the composer's bottom rule carries `model (effort) · permission` —
+ the route's one launch reading. The posture bar and metrics line appear
+ only once a session exists.
+- Vocabulary: `fleet` is the public term and `Pod` is retired from copy —
+ roster, setup, detail, worker-runtime and managed-API messages now say
+ Fleet (`/fleet` canonical, `/pod` alias). The workflow wire accepts the
+ canonical role spellings (general/explore/planner/reviewer/implement/
+ test/advisor) with the pre-rename ones kept as load-time aliases, and
+ serializes canonical names.
+- The Operate mode-picker hint is shortened to fit 80 columns.
+
- The footer always shows the permission posture; when only one chip
fits, the permission chip outranks the mode word (#5796).
- Local Ollama: the header names a model only when the local catalog can
diff --git a/crates/tui/assets/mark-48.png b/crates/tui/assets/mark-48.png
new file mode 100644
index 0000000000..cb290fe27c
Binary files /dev/null and b/crates/tui/assets/mark-48.png differ
diff --git a/crates/tui/assets/mark-96.png b/crates/tui/assets/mark-96.png
new file mode 100644
index 0000000000..9fa22f8f0d
Binary files /dev/null and b/crates/tui/assets/mark-96.png differ
diff --git a/crates/tui/locales/ca.json b/crates/tui/locales/ca.json
index a5890f0ce2..f78307df25 100644
--- a/crates/tui/locales/ca.json
+++ b/crates/tui/locales/ca.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Captura del ratolí activada.",
"ScreenModeMouseCaptureOff": "Captura del ratolí desactivada; el terminal controla la selecció.",
"ScreenModeUnchanged": "Pantalla sense canvis: {reason}.",
+ "ImageInputRejectedResent": "{model} no accepta imatges — s'han reenviat com a text; usa image_ocr per llegir-les",
"ConfigLabelShowThinking": "Raonament del model al xat",
"ConfigLabelThinkingHighlight": "Ressaltat de fons del raonament",
"ConfigLabelShowToolDetails": "Nivell de detall de les eines",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Mostra les execucions de flux de treball d'aquest espai de treball (llistar, cancel·lar)",
"CmdHotbarDescription": "Obre la configuració de Hotbar",
"CmdSetupDescription": "Obre el Setup amb la constitució primer",
- "CmdSubagentsDescription": "Drecera de compatibilitat per a /pod workers (subagents de la sessió actual)",
+ "CmdSubagentsDescription": "Drecera de compatibilitat per a /fleet workers (subagents de la sessió actual)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Mostra el prompt de sistema actual",
"CmdTaskDescription": "Gestiona les tasques en segon pla",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Obre l'esborrany del redactor en un editor extern",
"KbLiveTranscript": "Obre la superposició de transcripció en viu (Ctrl+Shift+T; desplaçament automàtic fixat al final)",
"KbBacktrackMessage": "Torna enrere fins a un missatge anterior de l'usuari (Esquerra/Dreta per anar pas a pas, Enter per rebobinar)",
- "KbCompleteCycleModes": "Completa /command o canvia de mode quan el redactor és buit (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Completa /command o canvia de mode quan el redactor és buit (Plan → Work → Operate)",
"KbCycleThinking": "Canvia l'esforç de raonament del proveïdor actiu (Ctrl+T)",
"KbCyclePermissions": "Canvia l'accés: Pregunta → Revisió automàtica → Accés complet (Maj+Tab)",
"KbJumpPlanAgentYolo": "Activa les ranures de la Hotbar",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Ruta del pla de membresia de Kimi Code: `{route}` (consola del pla: `{console}`; usa el model k3). Codewhale mai no importa credencials de Kimi CLI.",
"LinksTip": "Consell: usa la variable d'entorn mostrada per al teu proveïdor, o desa la clau amb `codewhale auth set --provider `.",
"SubagentsFetching": "S'estan consultant els subagents de la sessió actual...",
- "SubagentsNoCurrentSessionPodWorkers": "No hi ha treballadors de Pod a la sessió actual.",
- "SubagentsCurrentSessionPodWorkersTitle": "Treballadors de Pod de la sessió actual",
- "SubagentsCurrentSessionPodWorkerRoles": "Els rols de subagent són rols de treballadors de Pod de la sessió actual.",
- "SubagentsCurrentSessionPodWorkersStatus": "Treballadors de Pod de la sessió actual: {count} en total",
- "SubagentsEmptyGuidance": "Configureu els rols i la postura de llançament amb /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "No hi ha treballadors de flota a la sessió actual.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Treballadors de flota de la sessió actual",
+ "SubagentsCurrentSessionPodWorkerRoles": "Els rols de subagent són rols de treballadors de flota de la sessió actual.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Treballadors de flota de la sessió actual: {count} en total",
+ "SubagentsEmptyGuidance": "Configureu els rols i la postura de llançament amb /fleet.",
"SubagentsStatusRunning": "En execució",
"SubagentsStatusCompleted": "Completat",
"SubagentsStatusInterrupted": "Interromput",
@@ -718,7 +719,7 @@
"HomeHistory": "Historial:",
"HomeTokens": "Tokens:",
"HomeQueued": "En cua:",
- "HomeSubagents": "Treballadors de Pod de la sessió actual:",
+ "HomeSubagents": "Treballadors de flota de la sessió actual:",
"HomeSkill": "Skill:",
"HomeQuickActions": "Accions ràpides",
"HomeQuickLinks": "/links - Codewhale, comunitat i enllaços de proveïdors",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - Inspecciona i canvia la configuració",
"HomeQuickSettings": "/settings - Mostra la configuració persistent",
"HomeQuickModel": "/model - Canvia o mostra el model",
- "HomeQuickSubagents": "/pod workers - subagents de la sessió actual",
+ "HomeQuickSubagents": "/fleet workers - subagents de la sessió actual",
"HomeQuickTaskList": "/task list - Mostra la cua de tasques en segon pla",
"HomeQuickHelp": "/help - Mostra l'ajuda",
"HomeQuickWorkspace": "/workspace - Canvia carpetes o worktrees",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell activat amb revisió automàtica de risc",
"AppModePlanHint": "Recerca de només lectura primer — presenta un pla abans d'actuar",
"AppModeYoloHint": "Només compatibilitat — Act + Full Access, no és un mode visible",
- "AppModeOperateHint": "Posa el teu Pod a treballar en paral·lel",
+ "AppModeOperateHint": "Converteix el prompt en objectiu: workers paral·lels",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERIR --",
"VimModeVisual": "-- VISUAL --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "pedaç",
"ToolFamilyRun": "executar",
"ToolFamilyFind": "cercar",
- "ToolFamilyDelegate": "delegar",
+ "ToolFamilyDelegate": "agent",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "verificar",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Mode Operate",
"HotbarActionModeOperateDescription": "Posa el teu Pod a treballar en paral·lel.",
"HomeOperateModeTip": "Operate — posa el teu Pod a treballar en paral·lel",
- "HomeOperateModeFleetTip": " Els rols integrats usen el model d'aquesta sessió; /pod setup els personalitza",
+ "HomeOperateModeFleetTip": " Els rols integrats usen el model d'aquesta sessió; /fleet setup els personalitza",
"HelpSubtitle": "Conceptes, ordres i dreceres de teclat",
"CommandPaletteTitle": "Ordre",
"CommandPaletteSubtitle": "Cerca i executa una acció",
"ConfigSubtitle": "Configuració primer; claus en brut sota detall avançat",
"LaunchStartTitle": "Comença aquí",
- "LaunchMenuConnect": "Connecta un model",
- "LaunchMenuWork": "Treballa · carpeta actual",
- "LaunchMenuChat": "Xat · només lectura",
- "LaunchMenuTheme": "Tema",
- "LaunchMenuHelp": "Ajuda",
"LaunchWorkDescription": "Utilitza aquesta carpeta amb eines locals; els canvis segueixen la política d’aprovació.",
"LaunchChatDescription": "Només conversa i planificació; cap canvi als fitxers.",
"LaunchWorkspaceGitReady": "Espai de treball · {name} · espai Git",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "carpeta",
"LaunchProviderConfiguredShort": "proveïdor configurat",
"LaunchProviderSetupShort": "configura proveïdor",
- "LaunchMenuNewSession": "Sessió nova",
- "LaunchMenuNewWorktree": "Worktree nou",
- "LaunchMenuWorktreeCompact": "arbre",
- "LaunchMenuChatCompact": "xat",
- "LaunchMenuResumeSession": "Reprèn la sessió",
"LaunchMenuChangelog": "Registre de canvis",
- "LaunchMenuQuit": "Surt",
- "LaunchMenuUnavailable": "no disponible",
- "LaunchMenuSavedCount": "{count} desades",
"LaunchWorktreePrompt": "Anomena la branca/worktree, o prem Enter per a un nom automàtic.",
"LaunchWorktreeNeedsGit": "Un worktree nou requereix un repositori Git.",
"LaunchWorktreeNameLabel": "nom del worktree",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "El worktree ha fallat: {error}",
"LaunchNoSavedSessions": "No hi ha sessions desades per a aquest espai de treball.",
"LaunchComposerHint": "Enter envia · Shift+Enter línia nova · Esc torna",
- "LaunchComposerFocusHint": "Tab per escriure",
+ "LaunchNoModelConnected": "cap model connectat",
+ "LaunchRunCommand": "executa {command}",
+ "LaunchMcpConnectedOne": "1 servidor MCP connectat",
+ "LaunchMcpConnectedMany": "{count} servidors MCP connectats",
+ "LaunchMcpNeedsSignInOne": "1 necessita iniciar sessió",
+ "LaunchMcpNeedsSignInMany": "{count} necessiten iniciar sessió",
"PhaseIdle": "inactiu",
"PhaseDraft": "esborrany",
"PhaseWorking": "treballant",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "sortida",
"FooterHintContext": "context",
"InfoLineHelp": "ajuda",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "balenes",
"InfoLineAutomation": "automatització",
"InfoLineNotConnected": "no connectat",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "aplica",
"PickerActionSetStartupDefault": "defineix com a predeterminat d'inici",
"PickerActionPin": "fixa",
+ "PickerActionFleet": "flota",
"PickerActionCancel": "cancel·la",
"PickerActionClear": "esborra",
"PickerActionClearSearch": "esborra la cerca",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Desat a",
"FleetRosterLayerWins": "actiu",
"FleetRosterLayerIgnored": "còpia ignorada",
- "FleetReadyNotice": "Pod a punt · /pod obre rols · /pod setup ajusta els models dels membres",
+ "FleetReadyNotice": "Pod a punt · /fleet obre rols · /fleet setup ajusta els models dels membres",
"FleetProfileIdentityVerifyFailed": "No s'han pogut verificar les identitats de perfil existents ({error}); arregla el fitxer anomenat abans de desar.",
"FleetProfileIdConflict": "L'id de perfil `{id}` ja l'usa {path}; torna a redactar amb un rol diferent o elimina primer el fitxer antic.",
"FleetProfileProviderUnconfigured": "El perfil fixa el proveïdor `{provider}`, que no té credencials configurades ({env}); configura'l a /provider abans de desar.",
+ "FleetModelAdded": "S'ha afegit {route} al fleet `{fleet}`",
+ "FleetModelAddedAs": "S'ha afegit {route} com a {roles} al fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (fleet personal nou, ara seleccionat)",
+ "FleetModelAddedSelectedNote": " (ara seleccionat)",
+ "FleetModelRemoved": "S'ha eliminat {route} del fleet `{fleet}`",
+ "FleetModelRemovedRoles": "S'ha eliminat {route} ({roles}) del fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} es manté al fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "aquest és el teu model actual (la ruta de l'operador del fleet); canvia de model o fes servir /fleet save per modificar-la",
+ "FleetModelReasonAlreadyPresent": "ja és al fleet",
+ "FleetModelErrorNeedsRoute": "un model del fleet necessita un id de provider i un id de model",
+ "FleetModelErrorNoSelection": "no hi ha cap fleet seleccionat; el teu fleet és només el model de la sessió",
+ "FleetModelErrorOperatorRoute": "{route} és la ruta de l'operador del fleet `{fleet}`; canvia-la amb /fleet save, no amb remove",
+ "FleetModelErrorNotInFleet": "{route} no és al fleet `{fleet}`",
+ "FleetModelsEmpty": "El teu fleet és només el model de la sessió. Afegeix-ne un: /fleet add [role…] (o ⇧F en una fila de /model).",
+ "FleetModelsHeader": "El teu fleet `{fleet}` ({count} models)",
+ "FleetModelsBroken": "No s'ha pogut carregar el fleet seleccionat: {error}. Corregeix el fitxer o tria'n un altre amb /fleet fleets.",
+ "FleetModelsFooter": "Afegir: /fleet add [role…] · Eliminar: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} per M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "eines",
+ "FleetAddUsage": "Ús: /fleet add [role…] — p. ex. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` no és un provider configurat. Configura'l a ~/.codewhale/config.toml o canvia-hi amb /provider abans d'afegir-lo al fleet.",
+ "FleetAddModelNotServed": "{provider} no serveix `{model}` al catàleg actual; executa /models per veure què serveix, o /fleet add amb l'id exacte que llista.",
+ "FleetAddFailed": "No s'ha pogut afegir al fleet: {error}",
+ "FleetRemoveUsage": "Ús: /fleet remove ",
+ "FleetRemoveFailed": "No s'ha pogut eliminar del fleet: {error}",
+ "FleetToggleFailed": "No s'ha pogut actualitzar el fleet: {error}",
"FleetDestStepTitle": "On s'ha de desar aquest perfil?",
"FleetDestStepSubtitle": "No s'escriu res fins que ho confirmis a l'últim pas.",
"FleetDestProjectLabel": "Aquest projecte",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "L'Auto-Review ha bloquejat '{tool}' (política determinista): {reason}",
"AutoReviewReceiptHeld": "L'Auto-Review ha retingut '{tool}' sense aturar-se; denegat (cal una persona — canvia a Ask)",
"FooterHintEscInterrupt": "Esc per interrompre",
+ "PostureHintEnterAgain": "{enter} de nou per enviar ara · {steer} dirigeix",
"PermissionsPostureHeader": "Accés ara: {posture}",
"PermissionsPostureAsk": "Ask: les crides a tool que canvien autoritat, cost, abast o resultat obren un avís; les crides de només lectura provadament segures s'executen sense. Les regles ask de dalt sempre forcen un avís.",
"PermissionsPostureAuto": "Auto-Review: mai obre un avís. Una política determinista permet crides provadament segures i bloqueja de manera estricta feina de publicació o destructiva en segon pla; les crides que no pot provar segures van a un guardià del model d'una sola passada, que permet o denega amb un motiu declarat (el risc alt o crític mai s'executa automàticament; una revisió fallida denega, fail closed). Les retencions que requereixen una persona es deneguen, no s'amaguen. Cada decisió d'aquestes s'escriu a la transcripció com a nota i al registre d'auditoria.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell de només lectura",
"AgentFocusPostureShellNone": "sense shell",
"GoalReceiptSet": "Objectiu establert: \"{objective}\" · /goal mostra el progrés · /goal pause o /goal clear l'atura",
+ "GoalReceiptSetOperate": "◆ objectiu establert · Operate continua treballant fins que es verifica · /goal per editar-lo",
"GoalControlAccepted": "Control de l'objectiu desat; s'aplicarà al següent punt segur.",
"GoalControlRuntimeUnavailable": "El control de l'objectiu està desat, però el procés no està disponible. Es restaurarà després de reiniciar.",
"GoalStatusIdleHint": "no s'està executant — envia un missatge o /goal resume per continuar",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Bloqueja tota eina que requereixi aprovació.",
"ConfigChoiceDetailModeAgent": "Comença a punt per col·laborar i usar eines.",
"ConfigChoiceDetailModePlan": "Comença en un espai de planificació de només lectura.",
- "ConfigChoiceDetailModeOperate": "Comença com a coordinador que delega feina a workers acotats.",
+ "ConfigChoiceDetailModeOperate": "Operate converteix la teva petició en un objectiu i hi treballa en paral·lel: workers en segon pla per als fluxos separables, verificats abans d'aturar-se.",
"ConfigChoiceDetailPlacementTop": "Mostra Tasques, Pendents i Workers damunt de la transcripció.",
"ConfigChoiceDetailPlacementLeft": "Mostra Tasques, Pendents i Workers en una barra lateral esquerra quan el terminal és prou ample.",
"ConfigChoiceDetailPlacementRight": "Mostra Tasques, Pendents i Workers en una barra lateral dreta quan el terminal és prou ample.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "obre connectors · confia, activa o diagnostica",
"ConfigHintMcpConfigPath": "camí a mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 bloqueja agents fills; 3 és el predeterminat (mateix eix que els subagents); límit 8",
- "ConfigHintFeatureSubagents": "estat de només lectura de l'indicador de funció; /pod setup és el camí per a l'usuari",
+ "ConfigHintFeatureSubagents": "estat de només lectura de l'indicador de funció; /fleet setup és el camí per a l'usuari",
"ConfigHintFeatureWebSearch": "estat de només lectura de l'indicador de funció de les eines de cerca web",
"ConfigHintFeatureApplyPatch": "estat de només lectura de l'indicador de funció de les eines d'edició per pedaç",
"ConfigHintFeatureMcp": "estat de només lectura de l'indicador de funció de les eines MCP",
"ConfigHintFeatureExecPolicy": "estat de només lectura de l'indicador de funció de les eines de política d'execució",
"ConfigHintFeatureVisionModel": "indicador beta per al suport d'imatges de visió/model",
"ConfigHintGoalCommand": "/goal defineix objectius, pressupostos i l'estat del context de Treball",
- "ConfigHintWorkflow": "/workflow executa operacions guionades amb targetes d'execució de distribució/consolidació"
+ "ConfigHintWorkflow": "/workflow executa operacions guionades amb targetes d'execució de distribució/consolidació",
+ "LaunchMenuNewWorktree": "Nou worktree",
+ "LaunchMenuResume": "Reprendre la sessió",
+ "LaunchMenuQuit": "Surt",
+ "LaunchNoticeClaude": "Ve de Claude Code? Repreni la sessió amb ctrl+r",
+ "ReceiptSessionHooks": "hooks {count}"
}
diff --git a/crates/tui/locales/de.json b/crates/tui/locales/de.json
index 05e79ccda0..2970704094 100644
--- a/crates/tui/locales/de.json
+++ b/crates/tui/locales/de.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Mausaufnahme aktiviert.",
"ScreenModeMouseCaptureOff": "Mausaufnahme deaktiviert; das Terminal steuert die Auswahl.",
"ScreenModeUnchanged": "Bildschirm unverändert: {reason}.",
+ "ImageInputRejectedResent": "{model} akzeptiert keine Bilder — sie wurden als Text erneut gesendet; nutze image_ocr zum Lesen",
"ConfigLabelShowThinking": "Modell-Reasoning im Chat",
"ConfigLabelThinkingHighlight": "Reasoning-Hintergrund hervorheben",
"ConfigLabelShowToolDetails": "Tool-Detailgrad",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Workflow-Läufe in diesem Arbeitsbereich anzeigen (auflisten, abbrechen)",
"CmdHotbarDescription": "Hotbar-Setup öffnen",
"CmdSetupDescription": "Setup mit Verfassung zuerst öffnen",
- "CmdSubagentsDescription": "Kompatibilitäts-Kurzbefehl für /pod workers (Sub-Agenten der aktuellen Sitzung)",
+ "CmdSubagentsDescription": "Kompatibilitäts-Kurzbefehl für /fleet workers (Sub-Agenten der aktuellen Sitzung)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Aktuellen Systemprompt anzeigen",
"CmdTaskDescription": "Hintergrundaufgaben verwalten",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Composer-Entwurf in externem Editor öffnen",
"KbLiveTranscript": "Live-Transkript-Overlay öffnen (Ctrl+Shift+T; Sticky-Tail-Auto-Scroll)",
"KbBacktrackMessage": "Zu einer früheren Benutzernachricht zurückspringen (Links/Rechts schrittweise, Enter zum Zurückspulen)",
- "KbCompleteCycleModes": "/Befehl vervollständigen oder Modi wechseln, wenn der Composer leer ist (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "/Befehl vervollständigen oder Modi wechseln, wenn der Composer leer ist (Plan → Work → Operate)",
"KbCycleThinking": "Reasoning-Stufe des aktiven Providers wechseln (Ctrl+T)",
"KbCyclePermissions": "Zugriff wechseln: Nachfragen → Auto-Review → Voller Zugriff (Umschalt+Tab)",
"KbJumpPlanAgentYolo": "Hotbar-Slots auslösen",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Kimi-Code-Mitgliedschaftsroute: `{route}` (Plan-Konsole: `{console}`; Modell k3 verwenden). Codewhale importiert niemals Kimi-CLI-Zugangsdaten.",
"LinksTip": "Tipp: Die gezeigte Umgebungsvariable deines Providers verwenden oder den Schlüssel mit `codewhale auth set --provider ` speichern.",
"SubagentsFetching": "Sub-Agenten der aktuellen Sitzung werden abgerufen...",
- "SubagentsNoCurrentSessionPodWorkers": "Keine Pod-Worker in der aktuellen Sitzung.",
- "SubagentsCurrentSessionPodWorkersTitle": "Pod-Worker der aktuellen Sitzung",
- "SubagentsCurrentSessionPodWorkerRoles": "Sub-Agent-Rollen sind Pod-Worker-Rollen der aktuellen Sitzung.",
- "SubagentsCurrentSessionPodWorkersStatus": "Pod-Worker der aktuellen Sitzung: insgesamt {count}",
- "SubagentsEmptyGuidance": "Konfigurieren Sie Rollen und Startprofil mit /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "Keine Flotten-Worker in der aktuellen Sitzung.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Flotten-Worker der aktuellen Sitzung",
+ "SubagentsCurrentSessionPodWorkerRoles": "Sub-Agent-Rollen sind Flotten-Worker-Rollen der aktuellen Sitzung.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Flotten-Worker der aktuellen Sitzung: insgesamt {count}",
+ "SubagentsEmptyGuidance": "Konfigurieren Sie Rollen und Startprofil mit /fleet.",
"SubagentsStatusRunning": "Läuft",
"SubagentsStatusCompleted": "Abgeschlossen",
"SubagentsStatusInterrupted": "Unterbrochen",
@@ -718,7 +719,7 @@
"HomeHistory": "Verlauf:",
"HomeTokens": "Tokens:",
"HomeQueued": "Warteschlange:",
- "HomeSubagents": "Pod-Worker der aktuellen Sitzung:",
+ "HomeSubagents": "Flotten-Worker der aktuellen Sitzung:",
"HomeSkill": "Skill:",
"HomeQuickActions": "Schnellaktionen",
"HomeQuickLinks": "/links - Codewhale-, Community- & Provider-Links",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - Einstellungen ansehen und ändern",
"HomeQuickSettings": "/settings - Persistente Einstellungen anzeigen",
"HomeQuickModel": "/model - Modell wechseln oder anzeigen",
- "HomeQuickSubagents": "/pod workers - Sub-Agenten der aktuellen Sitzung",
+ "HomeQuickSubagents": "/fleet workers - Sub-Agenten der aktuellen Sitzung",
"HomeQuickTaskList": "/task list - Hintergrundaufgaben-Warteschlange anzeigen",
"HomeQuickHelp": "/help - Hilfe anzeigen",
"HomeQuickWorkspace": "/workspace - Ordner oder Worktrees wechseln",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell aktiv mit automatischer Risikoprüfung",
"AppModePlanHint": "Read-only-Recherche zuerst — Plan vorlegen, bevor gehandelt wird",
"AppModeYoloHint": "Nur Kompatibilität — Act + Full Access, kein sichtbarer Modus",
- "AppModeOperateHint": "Ihren Pod parallel arbeiten lassen",
+ "AppModeOperateHint": "Macht dein Prompt zum Ziel: parallele Worker, verifiziert",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "patchen",
"ToolFamilyRun": "ausführen",
"ToolFamilyFind": "suchen",
- "ToolFamilyDelegate": "delegieren",
+ "ToolFamilyDelegate": "Agent",
"ToolFamilyFanout": "verteilen",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "prüfen",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Operate-Modus",
"HotbarActionModeOperateDescription": "Ihren Pod parallel arbeiten lassen.",
"HomeOperateModeTip": "Operate — Ihren Pod parallel arbeiten lassen",
- "HomeOperateModeFleetTip": " Eingebaute Rollen nutzen das Modell dieser Sitzung; /pod setup passt sie an",
+ "HomeOperateModeFleetTip": " Eingebaute Rollen nutzen das Modell dieser Sitzung; /fleet setup passt sie an",
"HelpSubtitle": "Konzepte, Befehle und Tastenbelegung",
"CommandPaletteTitle": "Befehl",
"CommandPaletteSubtitle": "Eine Aktion finden und ausführen",
"ConfigSubtitle": "Einstellungen zuerst; Rohschlüssel im erweiterten Detail",
"LaunchStartTitle": "Hier starten",
- "LaunchMenuConnect": "Modell verbinden",
- "LaunchMenuWork": "Arbeiten · aktueller Ordner",
- "LaunchMenuChat": "Chat · schreibgeschützt",
- "LaunchMenuTheme": "Farbschema",
- "LaunchMenuHelp": "Hilfe",
"LaunchWorkDescription": "Diesen Ordner mit lokalen Werkzeugen verwenden; Änderungen folgen der Genehmigungsrichtlinie.",
"LaunchChatDescription": "Nur Unterhaltung und Planung; keine Dateiänderungen.",
"LaunchWorkspaceGitReady": "Arbeitsbereich · {name} · Git-Arbeitsbereich",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "Ordner",
"LaunchProviderConfiguredShort": "Anbieter eingerichtet",
"LaunchProviderSetupShort": "Anbieter einrichten",
- "LaunchMenuNewSession": "Neue Sitzung",
- "LaunchMenuNewWorktree": "Neuer Worktree",
- "LaunchMenuWorktreeCompact": "Arbeitsbaum",
- "LaunchMenuChatCompact": "Gespräch",
- "LaunchMenuResumeSession": "Sitzung fortsetzen",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Beenden",
- "LaunchMenuUnavailable": "nicht verfügbar",
- "LaunchMenuSavedCount": "{count} gespeichert",
"LaunchWorktreePrompt": "Branch/Worktree benennen oder Enter für einen automatischen Namen.",
"LaunchWorktreeNeedsGit": "Neuer Worktree erfordert ein Git-Repository.",
"LaunchWorktreeNameLabel": "Worktree-Name",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "Worktree fehlgeschlagen: {error}",
"LaunchNoSavedSessions": "Keine gespeicherten Sitzungen für diesen Workspace.",
"LaunchComposerHint": "Enter senden · Shift+Enter neue Zeile · Esc zurück",
- "LaunchComposerFocusHint": "Tab zum Tippen",
+ "LaunchNoModelConnected": "kein Modell verbunden",
+ "LaunchRunCommand": "{command} ausführen",
+ "LaunchMcpConnectedOne": "1 MCP-Server verbunden",
+ "LaunchMcpConnectedMany": "{count} MCP-Server verbunden",
+ "LaunchMcpNeedsSignInOne": "1 braucht Anmeldung",
+ "LaunchMcpNeedsSignInMany": "{count} brauchen Anmeldung",
"PhaseIdle": "ruhend",
"PhaseDraft": "entwurf",
"PhaseWorking": "arbeitet",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "Ausgabe",
"FooterHintContext": "Kontext",
"InfoLineHelp": "Hilfe",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "Wale",
"InfoLineAutomation": "Automatisierung",
"InfoLineNotConnected": "nicht verbunden",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "anwenden",
"PickerActionSetStartupDefault": "als Startstandard festlegen",
"PickerActionPin": "anheften",
+ "PickerActionFleet": "Flotte",
"PickerActionCancel": "abbrechen",
"PickerActionClear": "leeren",
"PickerActionClearSearch": "Suche leeren",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Gespeichert für",
"FleetRosterLayerWins": "aktiv",
"FleetRosterLayerIgnored": "ignorierte Kopie",
- "FleetReadyNotice": "Pod bereit · /pod öffnet Rollen · /pod setup passt die Modelle der Mitglieder an",
+ "FleetReadyNotice": "Pod bereit · /fleet öffnet Rollen · /fleet setup passt die Modelle der Mitglieder an",
"FleetProfileIdentityVerifyFailed": "Bestehende Profil-Identitäten konnten nicht verifiziert werden ({error}); die benannte Datei vor dem Speichern korrigieren.",
"FleetProfileIdConflict": "Profil-ID `{id}` wird bereits von {path} genutzt; mit einer anderen Rolle neu entwerfen oder zuerst die alte Datei entfernen.",
"FleetProfileProviderUnconfigured": "Profil pinnt Provider `{provider}`, für den keine Zugangsdaten konfiguriert sind ({env}); vor dem Speichern in /provider einrichten.",
+ "FleetModelAdded": "{route} zum Fleet `{fleet}` hinzugefügt",
+ "FleetModelAddedAs": "{route} als {roles} zum Fleet `{fleet}` hinzugefügt",
+ "FleetModelAddedCreatedNote": " (neues persönliches Fleet, jetzt ausgewählt)",
+ "FleetModelAddedSelectedNote": " (jetzt ausgewählt)",
+ "FleetModelRemoved": "{route} aus dem Fleet `{fleet}` entfernt",
+ "FleetModelRemovedRoles": "{route} ({roles}) aus dem Fleet `{fleet}` entfernt",
+ "FleetModelUnchanged": "{route} bleibt im Fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "das ist dein aktuelles Modell (die Operator-Route des Fleets); wechsle das Modell oder ändere sie mit /fleet save",
+ "FleetModelReasonAlreadyPresent": "bereits im Fleet",
+ "FleetModelErrorNeedsRoute": "ein Fleet-Modell braucht sowohl eine Provider-ID als auch eine Modell-ID",
+ "FleetModelErrorNoSelection": "kein Fleet ausgewählt; dein Fleet ist nur das Sitzungsmodell",
+ "FleetModelErrorOperatorRoute": "{route} ist die Operator-Route des Fleets `{fleet}`; ändere sie mit /fleet save, nicht mit remove",
+ "FleetModelErrorNotInFleet": "{route} ist nicht im Fleet `{fleet}`",
+ "FleetModelsEmpty": "Dein Fleet ist nur das Sitzungsmodell. Füge eines hinzu: /fleet add [role…] (oder ⇧F auf einer Zeile in /model).",
+ "FleetModelsHeader": "Dein Fleet `{fleet}` ({count} Modelle)",
+ "FleetModelsBroken": "Das ausgewählte Fleet konnte nicht geladen werden: {error}. Repariere die Datei oder wähle mit /fleet fleets ein anderes.",
+ "FleetModelsFooter": "Hinzufügen: /fleet add [role…] · Entfernen: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} pro M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "Tools",
+ "FleetAddUsage": "Verwendung: /fleet add [role…] — z. B. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` ist kein konfigurierter Provider. Konfiguriere ihn in ~/.codewhale/config.toml oder wechsle mit /provider zu ihm, bevor du ihn zum Fleet hinzufügst.",
+ "FleetAddModelNotServed": "{provider} bietet `{model}` im aktuellen Katalog nicht an; führe /models aus, um das Angebot zu sehen, oder /fleet add mit der exakt gelisteten ID.",
+ "FleetAddFailed": "Konnte nicht zum Fleet hinzufügen: {error}",
+ "FleetRemoveUsage": "Verwendung: /fleet remove ",
+ "FleetRemoveFailed": "Konnte nicht aus dem Fleet entfernen: {error}",
+ "FleetToggleFailed": "Konnte das Fleet nicht aktualisieren: {error}",
"FleetDestStepTitle": "Wo soll dieses Profil gespeichert werden?",
"FleetDestStepSubtitle": "Es wird nichts geschrieben, bis du im letzten Schritt bestätigst.",
"FleetDestProjectLabel": "Dieses Projekt",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review hat '{tool}' blockiert (deterministische Richtlinie): {reason}",
"AutoReviewReceiptHeld": "Auto-Review hat '{tool}' ohne Pause zurückgehalten; abgelehnt (braucht eine Person — zu Ask wechseln)",
"FooterHintEscInterrupt": "Esc zum Unterbrechen",
+ "PostureHintEnterAgain": "{enter} erneut zum Sofortsenden · {steer} steuert",
"PermissionsPostureHeader": "Zugriff jetzt: {posture}",
"PermissionsPostureAsk": "Ask: Tool-Aufrufe, die Befugnis, Kosten, Umfang oder Ergebnis ändern, öffnen eine Abfrage; nachweislich sichere Nur-Lese-Aufrufe laufen ohne. Die ask-Regeln oben erzwingen immer eine Abfrage.",
"PermissionsPostureAuto": "Auto-Review: öffnet nie eine Abfrage. Eine deterministische Richtlinie erlaubt nachweislich sichere Aufrufe und blockiert veröffentlichungsartige oder destruktive Hintergrundarbeit hart; Aufrufe, die sie nicht als sicher beweisen kann, gehen an einen einmaligen Modell-Guardian, der mit Begründung erlaubt oder ablehnt (hohes oder kritisches Risiko läuft nie automatisch; eine fehlgeschlagene Prüfung lehnt ab, fail closed). Zurückhaltungen, die eine Person erfordern, werden abgelehnt, nicht versteckt. Jede solche Entscheidung wird als Notiz ins Transkript und ins Audit-Log geschrieben.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "Nur-Lese-Shell",
"AgentFocusPostureShellNone": "keine Shell",
"GoalReceiptSet": "Ziel gesetzt: \"{objective}\" · /goal zeigt den Fortschritt · /goal pause oder /goal clear beendet es",
+ "GoalReceiptSetOperate": "◆ Ziel gesetzt · Operate arbeitet weiter, bis es verifiziert ist · /goal zum Bearbeiten",
"GoalControlAccepted": "Zielsteuerung gespeichert; sie wird am nächsten sicheren Übergang angewendet.",
"GoalControlRuntimeUnavailable": "Die Zielsteuerung ist gespeichert, aber die Laufzeit ist nicht verfügbar. Sie wird nach dem Neustart wiederhergestellt.",
"GoalStatusIdleHint": "läuft gerade nicht — Nachricht senden oder /goal resume zum Fortsetzen",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Blockiert jedes Werkzeug, das eine Freigabe braucht.",
"ConfigChoiceDetailModeAgent": "Startet bereit zur Zusammenarbeit und Werkzeugnutzung.",
"ConfigChoiceDetailModePlan": "Startet in einem schreibgeschützten Planungsbereich.",
- "ConfigChoiceDetailModeOperate": "Startet als Koordinator, der Arbeit an begrenzte Worker delegiert.",
+ "ConfigChoiceDetailModeOperate": "Operate macht aus Ihrer Eingabe ein Ziel und arbeitet es parallel ab: Hintergrund-Worker für trennbare Stränge, verifiziert bevor es stoppt.",
"ConfigChoiceDetailPlacementTop": "Zeigt Aufgaben, To-do und Worker über dem Transkript.",
"ConfigChoiceDetailPlacementLeft": "Zeigt Aufgaben, To-do und Worker in einer linken Seitenleiste, wenn das Terminal breit genug ist.",
"ConfigChoiceDetailPlacementRight": "Zeigt Aufgaben, To-do und Worker in einer rechten Seitenleiste, wenn das Terminal breit genug ist.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "Plugins öffnen · vertrauen, aktivieren oder diagnostizieren",
"ConfigHintMcpConfigPath": "Pfad zu mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 blockiert Kind-Agenten; 3 ist Standard (gleiche Achse wie Sub-Agenten); Obergrenze 8",
- "ConfigHintFeatureSubagents": "schreibgeschützter Feature-Flag-Zustand; /pod setup ist der Weg für Nutzer",
+ "ConfigHintFeatureSubagents": "schreibgeschützter Feature-Flag-Zustand; /fleet setup ist der Weg für Nutzer",
"ConfigHintFeatureWebSearch": "schreibgeschützter Feature-Flag-Zustand für Websuche-Werkzeuge",
"ConfigHintFeatureApplyPatch": "schreibgeschützter Feature-Flag-Zustand für Patch-Bearbeitungswerkzeuge",
"ConfigHintFeatureMcp": "schreibgeschützter Feature-Flag-Zustand für MCP-Werkzeuge",
"ConfigHintFeatureExecPolicy": "schreibgeschützter Feature-Flag-Zustand für Ausführungsrichtlinien-Werkzeuge",
"ConfigHintFeatureVisionModel": "Beta-Feature-Flag für Bildunterstützung von Vision/Modell",
"ConfigHintGoalCommand": "/goal setzt Ziele, Budgets und den Status des Arbeitskontexts",
- "ConfigHintWorkflow": "/workflow führt skriptgesteuerte Abläufe mit Verteilungs-/Sammel-Laufkarten aus"
+ "ConfigHintWorkflow": "/workflow führt skriptgesteuerte Abläufe mit Verteilungs-/Sammel-Laufkarten aus",
+ "LaunchMenuNewWorktree": "Neuer Worktree",
+ "LaunchMenuResume": "Sitzung fortsetzen",
+ "LaunchMenuQuit": "Beenden",
+ "LaunchNoticeClaude": "Von Claude Code gekommen? Sitzung mit ctrl+r fortsetzen",
+ "ReceiptSessionHooks": "Hooks {count}"
}
diff --git a/crates/tui/locales/en.json b/crates/tui/locales/en.json
index ab681cd68d..f456091566 100644
--- a/crates/tui/locales/en.json
+++ b/crates/tui/locales/en.json
@@ -209,7 +209,7 @@
"ConfigSectionSidebar": "Sidebar",
"ConfigSectionHistory": "History",
"ConfigSectionMcp": "MCP",
- "ConfigSectionFleet": "Pod",
+ "ConfigSectionFleet": "Fleet",
"ConfigSectionWorkflow": "Workflow",
"ConfigSectionSession": "Session",
"ConfigSectionLegacy": "Legacy",
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Mouse capture on.",
"ScreenModeMouseCaptureOff": "Mouse capture off; the terminal owns selection.",
"ScreenModeUnchanged": "Screen unchanged: {reason}.",
+ "ImageInputRejectedResent": "{model} does not accept images — resent as text; use image_ocr to read them",
"ConfigLabelShowThinking": "Model reasoning in chat",
"ConfigLabelThinkingHighlight": "Reasoning background highlight",
"ConfigLabelShowToolDetails": "Tool detail level",
@@ -308,7 +309,7 @@
"ConfigLabelMcpReconnect": "Reconnect MCP",
"ConfigLabelMcpDiagnose": "Diagnose MCP",
"ConfigLabelPluginsOpen": "Plugins",
- "ConfigLabelFleetSpawnDepth": "Pod recursion depth",
+ "ConfigLabelFleetSpawnDepth": "fleet recursion depth",
"ConfigLabelGoalCommand": "Goal command",
"ConfigLabelWorkflow": "Workflow",
"ConfigLabelFeaturePrefix": "Feature: {name}",
@@ -550,13 +551,13 @@
"CmdStructcopyClipboardAccepted": "Structural copy ({kind}, {bytes} bytes) was handed to the clipboard; if no native clipboard was reachable, a terminal write was queued instead",
"CmdStructcopyClipboardFailed": "Clipboard copy failed: {error}. Nothing was written; run again with `stdout` for an explicit text view",
"CmdStructcopyReceiptTooLarge": "Structural-copy receipt metadata exceeds the {bytes}-byte output cap; refusing to emit it",
- "CmdFleetDescription": "Inspect and set up Pod members and orchestration state",
+ "CmdFleetDescription": "Inspect and set up fleet members and orchestration state",
"CmdLaneDescription": "Inspect and control durable Lanes (running Workflows)",
"CmdWorkflowDescription": "Run a repeatable workflow when order or fan-out matters",
"CmdWorkflowsDescription": "Show workflow runs in this workspace (list, cancel)",
"CmdHotbarDescription": "Open Hotbar setup",
"CmdSetupDescription": "Open constitution-first setup",
- "CmdSubagentsDescription": "Compatibility shortcut for /pod workers (current-session sub-agents)",
+ "CmdSubagentsDescription": "Compatibility shortcut for /fleet workers (current-session sub-agents)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Show current system prompt",
"CmdTaskDescription": "Manage background tasks",
@@ -649,7 +650,7 @@
"KbExternalEditor": "Open composer draft in external editor",
"KbLiveTranscript": "Open live transcript overlay (Ctrl+Shift+T; sticky-tail auto-scroll)",
"KbBacktrackMessage": "Backtrack to a previous user message (Left/Right step, Enter to rewind)",
- "KbCompleteCycleModes": "Complete /command or cycle modes when the composer is empty (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Complete /command or cycle modes when the composer is empty (Plan → Work → Operate)",
"KbCycleThinking": "Cycle reasoning effort for the active provider (Ctrl+T)",
"KbCyclePermissions": "Cycle Access: Ask → Auto-Review → Full Access (Shift+Tab)",
"KbJumpPlanAgentYolo": "Trigger hotbar slots",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Kimi Code membership-plan route: `{route}` (plan console: `{console}`; use model k3). Codewhale never imports Kimi CLI credentials.",
"LinksTip": "Tip: Use the env var shown for your provider, or save the key with `codewhale auth set --provider `.",
"SubagentsFetching": "Fetching current-session sub-agents...",
- "SubagentsNoCurrentSessionPodWorkers": "No current-session Pod workers.",
- "SubagentsCurrentSessionPodWorkersTitle": "Current-session Pod workers",
- "SubagentsCurrentSessionPodWorkerRoles": "Sub-agent roles are current-session Pod worker roles.",
- "SubagentsCurrentSessionPodWorkersStatus": "Current-session Pod workers: {count} total",
- "SubagentsEmptyGuidance": "Configure roles and launch posture with /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "No current-session fleet workers.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Current-session fleet workers",
+ "SubagentsCurrentSessionPodWorkerRoles": "Sub-agent roles are current-session fleet worker roles.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Current-session fleet workers: {count} total",
+ "SubagentsEmptyGuidance": "Configure roles and launch posture with /fleet.",
"SubagentsStatusRunning": "Running",
"SubagentsStatusCompleted": "Completed",
"SubagentsStatusInterrupted": "Interrupted",
@@ -719,13 +720,13 @@
"SubagentsShellFull": "full",
"SubagentsBranch": "branch {branch}",
"SubagentsBranchWithWorkspace": "branch {branch} @ {workspace}",
- "SubagentsRoleWorker": "worker",
- "SubagentsRoleScout": "scout",
+ "SubagentsRoleWorker": "general",
+ "SubagentsRoleScout": "explore",
"SubagentsRolePlanner": "planner",
- "SubagentsRoleBuilder": "builder",
- "SubagentsRoleVerifier": "verifier",
+ "SubagentsRoleBuilder": "implement",
+ "SubagentsRoleVerifier": "test",
"SubagentsRoleReviewer": "reviewer",
- "SubagentsRoleConsultant": "consultant",
+ "SubagentsRoleConsultant": "advisor",
"SubagentsRoleCustom": "custom",
"HelpUnknownCommand": "Unknown command: {topic}",
"HomeDashboardTitle": "Codewhale",
@@ -735,7 +736,7 @@
"HomeHistory": "History:",
"HomeTokens": "Tokens:",
"HomeQueued": "Queued:",
- "HomeSubagents": "Current-session Pod workers:",
+ "HomeSubagents": "Current-session fleet workers:",
"HomeSkill": "Skill:",
"HomeQuickActions": "Quick Actions",
"HomeQuickLinks": "/links - Codewhale, community & provider links",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - Inspect and change settings",
"HomeQuickSettings": "/settings - Show persistent settings",
"HomeQuickModel": "/model - Switch or view model",
- "HomeQuickSubagents": "/pod workers - current-session sub-agents",
+ "HomeQuickSubagents": "/fleet workers - current-session sub-agents",
"HomeQuickTaskList": "/task list - Show background task queue",
"HomeQuickHelp": "/help - Show help",
"HomeQuickWorkspace": "/workspace - Switch folders or worktrees",
@@ -817,7 +818,7 @@
"SetupActionCustomize": "customize",
"SetupActionProvider": "provider setup",
"SetupActionModel": "model routes",
- "SetupActionFleet": "Pod setup",
+ "SetupActionFleet": "fleet setup",
"SetupActionHotbar": "hotbar setup",
"SetupActionRemote": "remote on-ramp",
"SetupActionMode": "mode picker",
@@ -842,8 +843,8 @@
"SetupStepProviderModelWhy": "Choose the first interpreter of your code: the provider and model Codewhale will work with. Working credentials are not re-entered here.",
"SetupStepTrustSandboxTitle": "Runtime posture",
"SetupStepTrustSandboxWhy": "Review trust, sandbox, approval, shell, and network policy separately from constitutional guidance.",
- "SetupStepOperateFleetTitle": "Operate and Pod",
- "SetupStepOperateFleetWhy": "Operate can use the built-in team immediately. Roles without a custom route use this session's model; Pod setup is only for customization.",
+ "SetupStepOperateFleetTitle": "Operate and fleet",
+ "SetupStepOperateFleetWhy": "Operate can use the built-in team immediately. Roles without a custom route use this session's model; fleet setup is only for customization.",
"SetupStepToolsMcpTitle": "Tools and MCP",
"SetupStepToolsMcpWhy": "Inspect optional tool and MCP readiness without blocking the constitution checkpoint.",
"SetupStepHotbarTitle": "Hotbar",
@@ -891,12 +892,12 @@
"SetupCardSandboxLabel": "Sandbox:",
"SetupCardNetworkLabel": "Network:",
"SetupOperateRuntimeLabel": "Worker runtime:",
- "SetupOperateRosterLabel": "Pod roster:",
+ "SetupOperateRosterLabel": "fleet roster:",
"SetupOperateConcurrencyLabel": "Concurrency:",
"SetupOperateReadinessLabel": "Operate readiness:",
"SetupOperateReviewHint": "Enter records this setup snapshot.",
- "SetupOperateReviewed": "Operate/Pod readiness recorded.",
- "SetupOperateNeedsActionSaved": "Operate/Pod still needs action; recorded for setup report.",
+ "SetupOperateReviewed": "Operate/fleet readiness recorded.",
+ "SetupOperateNeedsActionSaved": "Operate/fleet still needs action; recorded for setup report.",
"SetupHotbarBindingsLabel": "Hotbar bindings:",
"SetupHotbarActionsLabel": "Bindable actions:",
"SetupHotbarReviewHint": "Enter records this setup snapshot. Press H to customize slots.",
@@ -952,7 +953,7 @@
"SetupRuntimeProjectOverrideNone": "none detected for approval_policy or sandbox_mode",
"SetupReportFirstRunLabel": "First-run:",
"SetupReportUpdateLabel": "Update checkpoint:",
- "SetupReportOperateLabel": "Operate/Pod:",
+ "SetupReportOperateLabel": "Operate/fleet:",
"SetupReportSourceLabel": "Source:",
"SetupReportAutonomyLabel": "Constitution autonomy:",
"SetupReportRuntimePostureLabel": "Runtime posture:",
@@ -967,7 +968,7 @@
"SetupReportNextActionConstitution": "Complete the constitution checkpoint or choose bundled/default.",
"SetupReportNextActionProvider": "Review provider/model readiness or run /setup provider; use /provider setup for a specific provider.",
"SetupReportNextActionRuntime": "Review runtime posture or use /config.",
- "SetupReportNextActionOperate": "Review Operate/Pod readiness before durable multi-worker runs.",
+ "SetupReportNextActionOperate": "Review Operate/fleet readiness before durable multi-worker runs.",
"SetupReportNextActionRequired": "Review the remaining required setup steps.",
"SetupReportRecorded": "Setup report recorded.",
"CtxMenuTitle": " Right click ",
@@ -1011,7 +1012,7 @@
"AppModeAutoHint": "Shell enabled with automatic risk review",
"AppModePlanHint": "Read-only research first — present a plan before acting",
"AppModeYoloHint": "Compatibility only — Act + Full Access, not a visible mode",
- "AppModeOperateHint": "Put your Pod to work in parallel",
+ "AppModeOperateHint": "Turns your prompt into a goal: parallel workers, verified work",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
@@ -1121,7 +1122,7 @@
"ToolFamilyPatch": "patch",
"ToolFamilyRun": "run",
"ToolFamilyFind": "find",
- "ToolFamilyDelegate": "delegate",
+ "ToolFamilyDelegate": "agent",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "verify",
@@ -1154,8 +1155,8 @@
"NotificationSubagentCancelled": "Sub-agent cancelled",
"NotificationSubagentBudgetExhausted": "Sub-agent budget exhausted",
"FooterWorkedChip": "worked {duration}",
- "FleetDraftTitle": "Pod profile — draft by {model_label} (g saves)",
- "FleetDraftHeader": "# .codewhale/agents/{name}\n# Drafted by {model_label}, validated and bounded by Codewhale.\n# Permissions stay at the Pod floor: no shell, no trust, approval required.\n# Nothing is saved until you press g in the wizard.\n\n",
+ "FleetDraftTitle": "fleet profile — draft by {model_label} (g saves)",
+ "FleetDraftHeader": "# .codewhale/agents/{name}\n# Drafted by {model_label}, validated and bounded by Codewhale.\n# Permissions stay at the fleet floor: no shell, no trust, approval required.\n# Nothing is saved until you press g in the wizard.\n\n",
"SetupRemoteOnRampText": "Remote Runtime On-Ramp\n\n/setup shows remote runtime facts only. It does not generate deploy bundles, write credentials, call cloud CLIs, or run `remote-setup`.\n\nCurrent facts:\n- Clouds: {clouds_result}\n- Chat bridges: {bridges_result}\n- Providers: {providers_result}\n- Mode: {mode_result}\n\nTo generate a deploy bundle, run explicitly in a normal terminal:\n\n```sh\n{command}\n```\n\nThe generated RUNBOOK includes host steps for human review. `--apply` remains unimplemented; do not treat this as auto-deploy.",
"ApprovalDescSafe": "Requesting a safe/read-only operation.",
"ApprovalDescFileWrite": "Requesting to modify a file. Please confirm path and content.",
@@ -1229,19 +1230,14 @@
"SetupToolsMcpDshLabel": "DeepSeek Harness (dsh):",
"SetupToolsMcpDshRow": "DeepSeek Harness (dsh) — connected through Codewhale, never a second scheduler:\n- State: {dsh_result}\n- Read-only detection; connect/plan/launch/remove: codewhale integrations dsh status · plan · connect · launch · remove\n- Codewhale writes only $CODEWHALE_HOME/integrations/dsh; it never copies API keys or edits DSH files.",
"HotbarActionModeOperateName": "Operate mode",
- "HotbarActionModeOperateDescription": "Put your Pod to work in parallel.",
- "HomeOperateModeTip": "Operate — put your Pod to work in parallel",
- "HomeOperateModeFleetTip": " Built-in roles use this session's model; /pod setup customizes them",
+ "HotbarActionModeOperateDescription": "Put your fleet to work in parallel.",
+ "HomeOperateModeTip": "Operate — put your fleet to work in parallel",
+ "HomeOperateModeFleetTip": " Built-in roles use this session's model; /fleet setup customizes them",
"HelpSubtitle": "Concepts, commands, and keybindings",
"CommandPaletteTitle": "Command",
"CommandPaletteSubtitle": "Find and run one action",
"ConfigSubtitle": "Settings first; raw keys under advanced detail",
"LaunchStartTitle": "Start here",
- "LaunchMenuConnect": "Connect a model",
- "LaunchMenuWork": "Work · current folder",
- "LaunchMenuChat": "Chat · read-only",
- "LaunchMenuTheme": "Theme",
- "LaunchMenuHelp": "Help",
"LaunchWorkDescription": "Use this folder with local tools; changes follow your approval policy.",
"LaunchChatDescription": "Conversation and planning only; no file changes.",
"LaunchWorkspaceGitReady": "Workspace · {name} · Git workspace",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "folder",
"LaunchProviderConfiguredShort": "provider set",
"LaunchProviderSetupShort": "provider setup",
- "LaunchMenuNewSession": "New session",
- "LaunchMenuNewWorktree": "New worktree",
- "LaunchMenuWorktreeCompact": "worktree",
- "LaunchMenuChatCompact": "chat",
- "LaunchMenuResumeSession": "Resume session",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Quit",
- "LaunchMenuUnavailable": "unavailable",
- "LaunchMenuSavedCount": "{count} saved",
"LaunchWorktreePrompt": "Name the branch/worktree, or press Enter for an automatic name.",
"LaunchWorktreeNeedsGit": "New worktree requires a Git repository.",
"LaunchWorktreeNameLabel": "worktree name",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "Worktree failed: {error}",
"LaunchNoSavedSessions": "No saved sessions for this workspace.",
"LaunchComposerHint": "Enter send · Shift+Enter new line · Esc back",
- "LaunchComposerFocusHint": "Tab to type",
+ "LaunchNoModelConnected": "no model connected",
+ "LaunchRunCommand": "run {command}",
+ "LaunchMcpConnectedOne": "1 MCP server connected",
+ "LaunchMcpConnectedMany": "{count} MCP servers connected",
+ "LaunchMcpNeedsSignInOne": "1 needs sign-in",
+ "LaunchMcpNeedsSignInMany": "{count} need sign-in",
"PhaseIdle": "idle",
"PhaseDraft": "draft",
"PhaseWorking": "in the current",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "output",
"FooterHintContext": "context",
"InfoLineHelp": "help",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "whales",
"InfoLineAutomation": "automation",
"InfoLineNotConnected": "not connected",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "apply",
"PickerActionSetStartupDefault": "set startup default",
"PickerActionPin": "pin",
+ "PickerActionFleet": "fleet",
"PickerActionCancel": "cancel",
"PickerActionClear": "clear",
"PickerActionClearSearch": "clear search",
@@ -1468,12 +1464,12 @@
"ThemeTreatmentDeepseaUnavailable": "Treatment Deepsea unavailable — Terminal owns the background",
"ThemeTreatmentFlatActive": "Treatment Flat — active",
"ThemeTreatmentDeepseaActive": "Treatment Deepsea — active",
- "FleetRosterHeaderLabel": "pod",
+ "FleetRosterHeaderLabel": "fleet",
"FleetRosterTabRoster": "members",
"FleetRosterTabSetup": "setup",
"FleetRosterWorkers": "workers",
"FleetRosterMembersCount": "{count} members",
- "FleetRosterOperatorFirst": "Coordinator leads · session model runs this Pod",
+ "FleetRosterOperatorFirst": "Coordinator leads · session model runs this fleet",
"FleetRosterOperatorRow": "Coordinator · leader",
"FleetRosterShadowBadgeProjectOverride": "saved in this project",
"FleetRosterShadowBadgePersonalIgnored": "saved copy ignored",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "Saved for",
"FleetRosterLayerWins": "active",
"FleetRosterLayerIgnored": "ignored copy",
- "FleetReadyNotice": "Pod is ready · /pod opens roles · /pod setup customizes members' models",
+ "FleetReadyNotice": "fleet is ready · /fleet opens roles · /fleet setup customizes members' models",
"FleetProfileIdentityVerifyFailed": "Could not verify existing profile identities ({error}); fix the named file before saving.",
"FleetProfileIdConflict": "Profile id `{id}` is already used by {path}; redraft with a different role or remove the old file first.",
"FleetProfileProviderUnconfigured": "Profile pins provider `{provider}`, which has no configured credentials ({env}); set it up in /provider before saving.",
+ "FleetModelAdded": "Added {route} to the fleet `{fleet}`",
+ "FleetModelAddedAs": "Added {route} as {roles} to the fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (new personal fleet, now selected)",
+ "FleetModelAddedSelectedNote": " (now selected)",
+ "FleetModelRemoved": "Removed {route} from the fleet `{fleet}`",
+ "FleetModelRemovedRoles": "Removed {route} ({roles}) from the fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} stays in the fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "this is your current model (the fleet's operator route); switch models or use /fleet save to change it",
+ "FleetModelReasonAlreadyPresent": "already in the fleet",
+ "FleetModelErrorNeedsRoute": "a fleet model needs both a provider id and a model id",
+ "FleetModelErrorNoSelection": "no fleet is selected; your fleet is the session model only",
+ "FleetModelErrorOperatorRoute": "{route} is the operator route of the fleet `{fleet}`; change it with /fleet save, not remove",
+ "FleetModelErrorNotInFleet": "{route} is not in the fleet `{fleet}`",
+ "FleetModelsEmpty": "Your fleet is the session model only. Add one: /fleet add [role…] (or ⇧F on a row in /model).",
+ "FleetModelsHeader": "Your fleet `{fleet}` ({count} models)",
+ "FleetModelsBroken": "Your selected fleet could not be loaded: {error}. Fix the file or pick another with /fleet fleets.",
+ "FleetModelsFooter": "Add: /fleet add [role…] · Remove: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} per M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "tools",
+ "FleetAddUsage": "Usage: /fleet add [role…] — e.g. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` is not a configured provider. Configure it in ~/.codewhale/config.toml or switch to it with /provider before adding it to the fleet.",
+ "FleetAddModelNotServed": "{provider} does not serve `{model}` in the current catalog; run /models to see what it serves, or /fleet add with the exact id it lists.",
+ "FleetAddFailed": "Could not add to the fleet: {error}",
+ "FleetRemoveUsage": "Usage: /fleet remove ",
+ "FleetRemoveFailed": "Could not remove from the fleet: {error}",
+ "FleetToggleFailed": "Could not update the fleet: {error}",
"FleetDestStepTitle": "Where should this profile live?",
"FleetDestStepSubtitle": "Nothing is written until you confirm on the last step.",
"FleetDestProjectLabel": "This project",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review blocked '{tool}' (deterministic policy): {reason}",
"AutoReviewReceiptHeld": "Auto-Review held '{tool}' without pausing; denied (needs a person — switch to Ask)",
"FooterHintEscInterrupt": "Esc to interrupt",
+ "PostureHintEnterAgain": "{enter} again to send now · {steer} steers",
"PermissionsPostureHeader": "Access now: {posture}",
"PermissionsPostureAsk": "Ask: tool calls that change authority, cost, scope, or outcome open a prompt; proven-safe read-only calls run without one. Ask rules above always force a prompt.",
"PermissionsPostureAuto": "Auto-Review: never opens a prompt. A deterministic policy allows proven-safe calls and hard-blocks publish-like or destructive background work; calls it cannot prove safe go to a one-shot model guardian that allows or denies with a stated reason (high or critical risk never auto-runs; a failed review denies, fail closed). Holds that require a person are denied, not hidden. Each such decision is written to the transcript as a note and to the audit log.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "read-only shell",
"AgentFocusPostureShellNone": "no shell",
"GoalReceiptSet": "Goal set: \"{objective}\" · /goal shows progress · /goal pause or /goal clear stops it",
+ "GoalReceiptSetOperate": "◆ goal set · Operate keeps working until it is verified · /goal to edit",
"GoalControlAccepted": "Goal control saved; applying at the next safe boundary.",
"GoalControlRuntimeUnavailable": "Goal control is saved, but the runtime is unavailable. It will be restored after restart.",
"GoalStatusIdleHint": "not running now — send a message or /goal resume to continue",
@@ -1844,7 +1869,7 @@
"OperateBoardGantt": "gantt time →",
"ConfigCategoryAppearance": "Appearance",
"ConfigCategoryModelsProviders": "Models & providers",
- "ConfigCategoryPod": "Pod",
+ "ConfigCategoryPod": "Fleet",
"ConfigCategoryWork": "Work",
"ConfigCategoryToolsMcp": "Tools & MCP",
"ConfigCategoryTrust": "Trust",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Block every tool that requires approval.",
"ConfigChoiceDetailModeAgent": "Start ready to collaborate and use tools.",
"ConfigChoiceDetailModePlan": "Start in a read-only planning workspace.",
- "ConfigChoiceDetailModeOperate": "Start as a coordinator that delegates work to bounded workers.",
+ "ConfigChoiceDetailModeOperate": "Operate turns your prompt into a goal and works it in parallel: background workers for separable streams, verified before it stops.",
"ConfigChoiceDetailPlacementTop": "Show Tasks, To-do, and Workers above the transcript.",
"ConfigChoiceDetailPlacementLeft": "Show Tasks, To-do, and Workers in a left sidebar when the terminal is wide enough.",
"ConfigChoiceDetailPlacementRight": "Show Tasks, To-do, and Workers in a right sidebar when the terminal is wide enough.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "open plugins · trust, enable, or diagnose",
"ConfigHintMcpConfigPath": "path to mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 blocks child agents; 3 default (same axis as sub-agents); capped at 8",
- "ConfigHintFeatureSubagents": "read-only feature flag state; /pod setup is the user-facing path",
+ "ConfigHintFeatureSubagents": "read-only feature flag state; /fleet setup is the user-facing path",
"ConfigHintFeatureWebSearch": "read-only feature flag state for web search tools",
"ConfigHintFeatureApplyPatch": "read-only feature flag state for patch editing tools",
"ConfigHintFeatureMcp": "read-only feature flag state for MCP tools",
"ConfigHintFeatureExecPolicy": "read-only feature flag state for execution policy tools",
"ConfigHintFeatureVisionModel": "beta feature flag for vision/model image support",
"ConfigHintGoalCommand": "/goal sets objectives, budgets, and Work-context status",
- "ConfigHintWorkflow": "/workflow runs scripted operations with fan-out/fan-in run cards"
+ "ConfigHintWorkflow": "/workflow runs scripted operations with fan-out/fan-in run cards",
+ "LaunchMenuNewWorktree": "New worktree",
+ "LaunchMenuResume": "Resume session",
+ "LaunchMenuQuit": "Quit",
+ "LaunchNoticeClaude": "Coming from Claude Code? Resume your session with ctrl+r",
+ "ReceiptSessionHooks": "hooks {count}"
}
diff --git a/crates/tui/locales/es-419.json b/crates/tui/locales/es-419.json
index 386ded8746..24d12465e0 100644
--- a/crates/tui/locales/es-419.json
+++ b/crates/tui/locales/es-419.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Captura del mouse activada.",
"ScreenModeMouseCaptureOff": "Captura del mouse desactivada; la terminal controla la selección.",
"ScreenModeUnchanged": "Pantalla sin cambios: {reason}.",
+ "ImageInputRejectedResent": "{model} no acepta imágenes — se reenviaron como texto; usa image_ocr para leerlas",
"ConfigLabelShowThinking": "Razonamiento del modelo en el chat",
"ConfigLabelThinkingHighlight": "Resaltado de fondo del razonamiento",
"ConfigLabelShowToolDetails": "Nivel de detalle de herramientas",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "Muestra las ejecuciones de flujo de trabajo de este workspace (listar, cancelar)",
"CmdHotbarDescription": "Abrir configuración de Hotbar",
"CmdSetupDescription": "Abrir configuración con constitución primero",
- "CmdSubagentsDescription": "Atajo de compatibilidad para /pod workers (subagentes de la sesión actual)",
+ "CmdSubagentsDescription": "Atajo de compatibilidad para /fleet workers (subagentes de la sesión actual)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Mostrar el prompt de sistema actual",
"CmdTaskDescription": "Gestionar tareas en segundo plano",
@@ -649,7 +650,7 @@
"KbExternalEditor": "Abrir borrador en el editor externo",
"KbLiveTranscript": "Abrir superposición de transcripción en vivo (auto-scroll)",
"KbBacktrackMessage": "Retroceder al mensaje anterior del usuario (izquierda/derecha, Enter para rebobinar)",
- "KbCompleteCycleModes": "Completar /command o ciclar modos cuando el compositor está vacío (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Completar /command o ciclar modos cuando el compositor está vacío (Plan → Work → Operate)",
"KbCycleThinking": "Alternar el esfuerzo de razonamiento del proveedor activo (Ctrl+T)",
"KbCyclePermissions": "Cambiar acceso: Preguntar → Revisión automática → Acceso total (Shift+Tab)",
"KbJumpPlanAgentYolo": "Activar ranuras de la hotbar",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Ruta del plan de membresía Kimi Code: `{route}` (consola del plan: `{console}`; usa el modelo k3). Codewhale nunca importa credenciales de Kimi CLI.",
"LinksTip": "Tip: usa la variable de entorno mostrada para tu proveedor o guarda la clave con `codewhale auth set --provider `.",
"SubagentsFetching": "Obteniendo subagentes de la sesión actual...",
- "SubagentsNoCurrentSessionPodWorkers": "No hay trabajadores del Pod en la sesión actual.",
- "SubagentsCurrentSessionPodWorkersTitle": "Trabajadores del Pod de la sesión actual",
- "SubagentsCurrentSessionPodWorkerRoles": "Los roles de subagentes son roles de trabajadores del Pod de la sesión actual.",
- "SubagentsCurrentSessionPodWorkersStatus": "Trabajadores del Pod de la sesión actual: {count} en total",
- "SubagentsEmptyGuidance": "Configura los roles y la postura de lanzamiento con /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "No hay trabajadores del flota en la sesión actual.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Trabajadores del flota de la sesión actual",
+ "SubagentsCurrentSessionPodWorkerRoles": "Los roles de subagentes son roles de trabajadores del flota de la sesión actual.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Trabajadores del flota de la sesión actual: {count} en total",
+ "SubagentsEmptyGuidance": "Configura los roles y la postura de lanzamiento con /fleet.",
"SubagentsStatusRunning": "En ejecución",
"SubagentsStatusCompleted": "Completado",
"SubagentsStatusInterrupted": "Interrumpido",
@@ -735,7 +736,7 @@
"HomeHistory": "Historial:",
"HomeTokens": "Tokens:",
"HomeQueued": "En cola:",
- "HomeSubagents": "Workers de Pod de la sesión actual:",
+ "HomeSubagents": "Workers de flota de la sesión actual:",
"HomeSkill": "Skill:",
"HomeQuickActions": "Acciones Rápidas",
"HomeQuickLinks": "/links - Codewhale, comunidad y proveedores",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - Inspeccionar y cambiar ajustes",
"HomeQuickSettings": "/settings - Mostrar configuraciones persistentes",
"HomeQuickModel": "/model - Alternar o visualizar modelo",
- "HomeQuickSubagents": "/pod workers - subagentes de la sesión actual",
+ "HomeQuickSubagents": "/fleet workers - subagentes de la sesión actual",
"HomeQuickTaskList": "/task list - Mostrar fila de tareas en segundo plano",
"HomeQuickHelp": "/help - Mostrar ayuda",
"HomeQuickWorkspace": "/workspace - Cambiar carpetas o worktrees",
@@ -1119,7 +1120,7 @@
"ToolFamilyPatch": "parchear",
"ToolFamilyRun": "ejecutar",
"ToolFamilyFind": "buscar",
- "ToolFamilyDelegate": "delegar",
+ "ToolFamilyDelegate": "agente",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "verificar",
@@ -1229,19 +1230,14 @@
"HotbarActionModeOperateName": "Modo Operate",
"HotbarActionModeOperateDescription": "Pon tu Pod a trabajar en paralelo.",
"HomeOperateModeTip": "Operate — pon tu Pod a trabajar en paralelo",
- "HomeOperateModeFleetTip": " Los roles integrados usan el modelo de esta sesión; /pod setup los personaliza",
+ "HomeOperateModeFleetTip": " Los roles integrados usan el modelo de esta sesión; /fleet setup los personaliza",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "Pon tu Pod a trabajar en paralelo",
+ "AppModeOperateHint": "Convierte tu prompt en meta: workers paralelos y verificación",
"HelpSubtitle": "Conceptos, comandos y atajos",
"CommandPaletteTitle": "Comando",
"CommandPaletteSubtitle": "Encuentra y ejecuta una acción",
"ConfigSubtitle": "Ajustes primero; claves crudas en detalle avanzado",
"LaunchStartTitle": "Empieza aquí",
- "LaunchMenuConnect": "Conectar un modelo",
- "LaunchMenuWork": "Trabajo · carpeta actual",
- "LaunchMenuChat": "Chat · solo lectura",
- "LaunchMenuTheme": "Tema",
- "LaunchMenuHelp": "Ayuda",
"LaunchWorkDescription": "Usa esta carpeta con herramientas locales; los cambios siguen tu política de aprobación.",
"LaunchChatDescription": "Solo conversación y planificación; no se modifican archivos.",
"LaunchWorkspaceGitReady": "Espacio de trabajo · {name} · espacio Git",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "carpeta",
"LaunchProviderConfiguredShort": "proveedor configurado",
"LaunchProviderSetupShort": "configurar proveedor",
- "LaunchMenuNewSession": "Nueva sesión",
- "LaunchMenuNewWorktree": "Nuevo worktree",
- "LaunchMenuWorktreeCompact": "árbol",
- "LaunchMenuChatCompact": "charla",
- "LaunchMenuResumeSession": "Reanudar sesión",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Salir",
- "LaunchMenuUnavailable": "no disponible",
- "LaunchMenuSavedCount": "{count} guardadas",
"LaunchWorktreePrompt": "Nombra la rama/worktree, o presiona Enter para un nombre automático.",
"LaunchWorktreeNeedsGit": "Un worktree nuevo requiere un repositorio Git.",
"LaunchWorktreeNameLabel": "nombre del worktree",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "El worktree falló: {error}",
"LaunchNoSavedSessions": "No hay sesiones guardadas para este workspace.",
"LaunchComposerHint": "Enter envía · Shift+Enter nueva línea · Esc vuelve",
- "LaunchComposerFocusHint": "Tab para escribir",
+ "LaunchNoModelConnected": "ningún modelo conectado",
+ "LaunchRunCommand": "ejecuta {command}",
+ "LaunchMcpConnectedOne": "1 servidor MCP conectado",
+ "LaunchMcpConnectedMany": "{count} servidores MCP conectados",
+ "LaunchMcpNeedsSignInOne": "1 necesita iniciar sesión",
+ "LaunchMcpNeedsSignInMany": "{count} necesitan iniciar sesión",
"PhaseIdle": "inactivo",
"PhaseDraft": "borrador",
"PhaseWorking": "trabajando",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "salida",
"FooterHintContext": "contexto",
"InfoLineHelp": "ayuda",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "ballenas",
"InfoLineAutomation": "automatización",
"InfoLineNotConnected": "no conectado",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "aplicar",
"PickerActionSetStartupDefault": "establecer como predeterminado de inicio",
"PickerActionPin": "fijar",
+ "PickerActionFleet": "flota",
"PickerActionCancel": "cancelar",
"PickerActionClear": "borrar",
"PickerActionClearSearch": "borrar búsqueda",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "Guardado en",
"FleetRosterLayerWins": "activo",
"FleetRosterLayerIgnored": "copia ignorada",
- "FleetReadyNotice": "Pod listo · /pod abre roles · /pod setup ajusta los modelos de los miembros",
+ "FleetReadyNotice": "Pod listo · /fleet abre roles · /fleet setup ajusta los modelos de los miembros",
"FleetProfileIdentityVerifyFailed": "No se pudieron verificar las identidades de perfiles existentes ({error}); corrige el archivo indicado antes de guardar.",
"FleetProfileIdConflict": "El id de perfil `{id}` ya está en uso por {path}; redacta de nuevo con otro rol o elimina primero el archivo antiguo.",
"FleetProfileProviderUnconfigured": "El perfil fija el proveedor `{provider}`, que no tiene credenciales configuradas ({env}); configúralo en /provider antes de guardar.",
+ "FleetModelAdded": "{route} agregado al fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} agregado como {roles} al fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (nuevo fleet personal, ahora seleccionado)",
+ "FleetModelAddedSelectedNote": " (ahora seleccionado)",
+ "FleetModelRemoved": "{route} quitado del fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) quitado del fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} se mantiene en el fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "este es tu modelo actual (la ruta del operador del fleet); cambia de modelo o usa /fleet save para modificarla",
+ "FleetModelReasonAlreadyPresent": "ya está en el fleet",
+ "FleetModelErrorNeedsRoute": "un modelo del fleet necesita un id de provider y un id de modelo",
+ "FleetModelErrorNoSelection": "no hay ningún fleet seleccionado; tu fleet es solo el modelo de la sesión",
+ "FleetModelErrorOperatorRoute": "{route} es la ruta del operador del fleet `{fleet}`; cámbiala con /fleet save, no con remove",
+ "FleetModelErrorNotInFleet": "{route} no está en el fleet `{fleet}`",
+ "FleetModelsEmpty": "Tu fleet es solo el modelo de la sesión. Agrega uno: /fleet add [role…] (o ⇧F en una fila de /model).",
+ "FleetModelsHeader": "Tu fleet `{fleet}` ({count} modelos)",
+ "FleetModelsBroken": "No se pudo cargar el fleet seleccionado: {error}. Corrige el archivo o elige otro con /fleet fleets.",
+ "FleetModelsFooter": "Agregar: /fleet add [role…] · Quitar: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} por M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "herramientas",
+ "FleetAddUsage": "Uso: /fleet add [role…] — p. ej. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` no es un provider configurado. Configúralo en ~/.codewhale/config.toml o cambia a él con /provider antes de agregarlo al fleet.",
+ "FleetAddModelNotServed": "{provider} no sirve `{model}` en el catálogo actual; ejecuta /models para ver qué sirve, o /fleet add con el id exacto que lista.",
+ "FleetAddFailed": "No se pudo agregar al fleet: {error}",
+ "FleetRemoveUsage": "Uso: /fleet remove ",
+ "FleetRemoveFailed": "No se pudo quitar del fleet: {error}",
+ "FleetToggleFailed": "No se pudo actualizar el fleet: {error}",
"FleetDestStepTitle": "¿Dónde debe guardarse este perfil?",
"FleetDestStepSubtitle": "No se escribe nada hasta que confirmes en el último paso.",
"FleetDestProjectLabel": "Este proyecto",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review bloqueó '{tool}' (política determinista): {reason}",
"AutoReviewReceiptHeld": "Auto-Review retuvo '{tool}' sin pausar; denegado (requiere una persona — cambia a Ask)",
"FooterHintEscInterrupt": "Esc para interrumpir",
+ "PostureHintEnterAgain": "{enter} de nuevo para enviar ahora · {steer} dirige",
"PermissionsPostureHeader": "Acceso ahora: {posture}",
"PermissionsPostureAsk": "Ask: las llamadas a tool que cambian autoridad, costo, alcance o resultado abren un aviso; las llamadas de solo lectura comprobadamente seguras corren sin él. Las reglas ask de arriba siempre fuerzan un aviso.",
"PermissionsPostureAuto": "Auto-Review: nunca abre un aviso. Una política determinista permite llamadas comprobadamente seguras y bloquea de forma estricta trabajo de publicación o destructivo en segundo plano; las llamadas que no puede probar seguras van a un guardián de modelo de una sola pasada, que permite o deniega con un motivo declarado (riesgo alto o crítico nunca se ejecuta automáticamente; una revisión fallida deniega, fail closed). Las retenciones que requieren una persona se deniegan, no se ocultan. Cada decisión así se escribe en la transcripción como nota y en el registro de auditoría.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell de solo lectura",
"AgentFocusPostureShellNone": "sin shell",
"GoalReceiptSet": "Meta definida: \"{objective}\" · /goal muestra el progreso · /goal pause o /goal clear la detiene",
+ "GoalReceiptSetOperate": "◆ meta definida · Operate sigue trabajando hasta verificarla · /goal para editarla",
"GoalControlAccepted": "Control de meta guardado; se aplicará en el siguiente límite seguro.",
"GoalControlRuntimeUnavailable": "El control de meta está guardado, pero el proceso no está disponible. Se restaurará después de reiniciar.",
"GoalStatusIdleHint": "no está en ejecución — envía un mensaje o /goal resume para continuar",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Bloquea toda herramienta que requiera aprobación.",
"ConfigChoiceDetailModeAgent": "Empieza listo para colaborar y usar herramientas.",
"ConfigChoiceDetailModePlan": "Empieza en un espacio de planificación de solo lectura.",
- "ConfigChoiceDetailModeOperate": "Empieza como coordinador que delega trabajo a workers acotados.",
+ "ConfigChoiceDetailModeOperate": "Operate convierte tu pedido en una meta y la trabaja en paralelo: workers en segundo plano para flujos separables, verificados antes de detenerse.",
"ConfigChoiceDetailPlacementTop": "Muestra Tareas, Pendientes y Workers encima de la transcripción.",
"ConfigChoiceDetailPlacementLeft": "Muestra Tareas, Pendientes y Workers en una barra lateral izquierda cuando la terminal es lo bastante ancha.",
"ConfigChoiceDetailPlacementRight": "Muestra Tareas, Pendientes y Workers en una barra lateral derecha cuando la terminal es lo bastante ancha.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "abre plugins · confiar, activar o diagnosticar",
"ConfigHintMcpConfigPath": "ruta a mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 bloquea agentes hijos; 3 es lo predeterminado (mismo eje que los subagentes); tope 8",
- "ConfigHintFeatureSubagents": "estado de solo lectura del flag de función; /pod setup es la ruta para el usuario",
+ "ConfigHintFeatureSubagents": "estado de solo lectura del flag de función; /fleet setup es la ruta para el usuario",
"ConfigHintFeatureWebSearch": "estado de solo lectura del flag de función de las herramientas de búsqueda web",
"ConfigHintFeatureApplyPatch": "estado de solo lectura del flag de función de las herramientas de edición por parche",
"ConfigHintFeatureMcp": "estado de solo lectura del flag de función de las herramientas MCP",
"ConfigHintFeatureExecPolicy": "estado de solo lectura del flag de función de las herramientas de política de ejecución",
"ConfigHintFeatureVisionModel": "flag beta para soporte de imágenes de visión/modelo",
"ConfigHintGoalCommand": "/goal define objetivos, presupuestos y el estado del contexto de Trabajo",
- "ConfigHintWorkflow": "/workflow ejecuta operaciones guionadas con tarjetas de ejecución de distribución/consolidación"
+ "ConfigHintWorkflow": "/workflow ejecuta operaciones guionadas con tarjetas de ejecución de distribución/consolidación",
+ "LaunchMenuNewWorktree": "Nuevo worktree",
+ "LaunchMenuResume": "Reanudar sesión",
+ "LaunchMenuQuit": "Salir",
+ "LaunchNoticeClaude": "¿Vienes de Claude Code? Reanuda tu sesión con ctrl+r",
+ "ReceiptSessionHooks": "hooks {count}"
}
diff --git a/crates/tui/locales/fr.json b/crates/tui/locales/fr.json
index 85b39823d6..84142ea359 100644
--- a/crates/tui/locales/fr.json
+++ b/crates/tui/locales/fr.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Capture de la souris activée.",
"ScreenModeMouseCaptureOff": "Capture de la souris désactivée ; le terminal contrôle la sélection.",
"ScreenModeUnchanged": "Écran inchangé : {reason}.",
+ "ImageInputRejectedResent": "{model} n’accepte pas les images — elles ont été renvoyées sous forme de texte ; utilisez image_ocr pour les lire",
"ConfigLabelShowThinking": "Raisonnement du modèle dans le chat",
"ConfigLabelThinkingHighlight": "Surlignage du fond du raisonnement",
"ConfigLabelShowToolDetails": "Niveau de détail des outils",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Afficher les exécutions de workflow de cet espace de travail (lister, annuler)",
"CmdHotbarDescription": "Ouvrir la configuration Hotbar",
"CmdSetupDescription": "Ouvrir la configuration axée constitution",
- "CmdSubagentsDescription": "Raccourci de compatibilité pour /pod workers (sous-agents de la session actuelle)",
+ "CmdSubagentsDescription": "Raccourci de compatibilité pour /fleet workers (sous-agents de la session actuelle)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Afficher le prompt système actuel",
"CmdTaskDescription": "Gérer les tâches en arrière-plan",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Ouvrir le brouillon du composer dans un éditeur externe",
"KbLiveTranscript": "Ouvrir la transcription en direct (Ctrl+Shift+T ; défilement automatique)",
"KbBacktrackMessage": "Revenir à un message utilisateur précédent (Gauche/Droite pour naviguer, Enter pour rembobiner)",
- "KbCompleteCycleModes": "Compléter une /commande ou cycler les modes quand le composer est vide (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Compléter une /commande ou cycler les modes quand le composer est vide (Plan → Work → Operate)",
"KbCycleThinking": "Cycler l'effort de raisonnement du fournisseur actif (Ctrl+T)",
"KbCyclePermissions": "Changer l'accès : Demander → Revue auto → Accès complet (Maj+Tab)",
"KbJumpPlanAgentYolo": "Déclencher les slots de la hotbar",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Route du plan d'adhésion Kimi Code : `{route}` (console du plan : `{console}` ; utilisez le modèle k3). Codewhale n'importe jamais les identifiants du Kimi CLI.",
"LinksTip": "Astuce : utilisez la variable d'environnement indiquée pour votre fournisseur, ou enregistrez la clé avec `codewhale auth set --provider `.",
"SubagentsFetching": "Récupération des sous-agents de la session actuelle...",
- "SubagentsNoCurrentSessionPodWorkers": "Aucun agent d’exécution du Pod dans la session actuelle.",
- "SubagentsCurrentSessionPodWorkersTitle": "Agents d’exécution du Pod de la session actuelle",
- "SubagentsCurrentSessionPodWorkerRoles": "Les rôles de sous-agents sont des rôles d’agents d’exécution du Pod de la session actuelle.",
- "SubagentsCurrentSessionPodWorkersStatus": "Agents d’exécution du Pod de la session actuelle : {count} au total",
- "SubagentsEmptyGuidance": "Configurez les rôles et le profil de lancement avec /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "Aucun agent d’exécution de la flotte dans la session actuelle.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Agents d’exécution de la flotte de la session actuelle",
+ "SubagentsCurrentSessionPodWorkerRoles": "Les rôles de sous-agents sont des rôles d’agents d’exécution de la flotte de la session actuelle.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Agents d’exécution de la flotte de la session actuelle : {count} au total",
+ "SubagentsEmptyGuidance": "Configurez les rôles et le profil de lancement avec /fleet.",
"SubagentsStatusRunning": "En cours",
"SubagentsStatusCompleted": "Terminé",
"SubagentsStatusInterrupted": "Interrompu",
@@ -718,7 +719,7 @@
"HomeHistory": "Historique :",
"HomeTokens": "Tokens :",
"HomeQueued": "En file :",
- "HomeSubagents": "Workers du Pod de la session actuelle :",
+ "HomeSubagents": "Workers de la flotte de la session actuelle :",
"HomeSkill": "Skill :",
"HomeQuickActions": "Actions rapides",
"HomeQuickLinks": "/links - Liens Codewhale, communauté et fournisseurs",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - Inspecter et modifier les paramètres",
"HomeQuickSettings": "/settings - Afficher les paramètres persistants",
"HomeQuickModel": "/model - Changer ou afficher le modèle",
- "HomeQuickSubagents": "/pod workers - sous-agents de la session actuelle",
+ "HomeQuickSubagents": "/fleet workers - sous-agents de la session actuelle",
"HomeQuickTaskList": "/task list - Afficher la file de tâches en arrière-plan",
"HomeQuickHelp": "/help - Afficher l'aide",
"HomeQuickWorkspace": "/workspace - Changer de dossier ou de worktree",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell activé avec revue automatique des risques",
"AppModePlanHint": "Recherche en lecture seule d'abord — présentez un plan avant d'agir",
"AppModeYoloHint": "Compatibilité uniquement — Act + Full Access, pas un mode visible",
- "AppModeOperateHint": "Faites travailler votre Pod en parallèle",
+ "AppModeOperateHint": "Transforme le prompt en objectif : workers parallèles, vérifié",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERTION --",
"VimModeVisual": "-- VISUEL --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "corriger",
"ToolFamilyRun": "exécuter",
"ToolFamilyFind": "chercher",
- "ToolFamilyDelegate": "déléguer",
+ "ToolFamilyDelegate": "agent",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "vérifier",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Mode Operate",
"HotbarActionModeOperateDescription": "Faites travailler votre Pod en parallèle.",
"HomeOperateModeTip": "Operate — faites travailler votre Pod en parallèle",
- "HomeOperateModeFleetTip": " Les rôles intégrés utilisent le modèle de cette session ; /pod setup les personnalise",
+ "HomeOperateModeFleetTip": " Les rôles intégrés utilisent le modèle de cette session ; /fleet setup les personnalise",
"HelpSubtitle": "Concepts, commandes et raccourcis clavier",
"CommandPaletteTitle": "Commande",
"CommandPaletteSubtitle": "Trouvez et exécutez une action",
"ConfigSubtitle": "Réglages d'abord ; clés brutes dans le détail avancé",
"LaunchStartTitle": "Commencer ici",
- "LaunchMenuConnect": "Connecter un modèle",
- "LaunchMenuWork": "Travail · dossier actuel",
- "LaunchMenuChat": "Discussion · lecture seule",
- "LaunchMenuTheme": "Thème",
- "LaunchMenuHelp": "Aide",
"LaunchWorkDescription": "Utilisez ce dossier avec les outils locaux ; les modifications suivent votre politique d’approbation.",
"LaunchChatDescription": "Discussion et planification uniquement ; aucun fichier n’est modifié.",
"LaunchWorkspaceGitReady": "Espace de travail · {name} · espace Git",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "dossier",
"LaunchProviderConfiguredShort": "fournisseur configuré",
"LaunchProviderSetupShort": "configurer fournisseur",
- "LaunchMenuNewSession": "Nouvelle session",
- "LaunchMenuNewWorktree": "Nouveau worktree",
- "LaunchMenuWorktreeCompact": "arbre",
- "LaunchMenuChatCompact": "discussion",
- "LaunchMenuResumeSession": "Reprendre la session",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Quitter",
- "LaunchMenuUnavailable": "indisponible",
- "LaunchMenuSavedCount": "{count} enregistrées",
"LaunchWorktreePrompt": "Nommez la branche/worktree, ou appuyez sur Enter pour un nom automatique.",
"LaunchWorktreeNeedsGit": "Un nouveau worktree nécessite un dépôt Git.",
"LaunchWorktreeNameLabel": "nom du worktree",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "Échec du worktree : {error}",
"LaunchNoSavedSessions": "Aucune session enregistrée pour ce workspace.",
"LaunchComposerHint": "Entrée envoie · Maj+Entrée nouvelle ligne · Échap retour",
- "LaunchComposerFocusHint": "Tab pour écrire",
+ "LaunchNoModelConnected": "aucun modèle connecté",
+ "LaunchRunCommand": "lancez {command}",
+ "LaunchMcpConnectedOne": "1 serveur MCP connecté",
+ "LaunchMcpConnectedMany": "{count} serveurs MCP connectés",
+ "LaunchMcpNeedsSignInOne": "1 nécessite une connexion",
+ "LaunchMcpNeedsSignInMany": "{count} nécessitent une connexion",
"PhaseIdle": "inactif",
"PhaseDraft": "brouillon",
"PhaseWorking": "travail en cours",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "sortie",
"FooterHintContext": "contexte",
"InfoLineHelp": "aide",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "baleines",
"InfoLineAutomation": "automatisation",
"InfoLineNotConnected": "non connecté",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "appliquer",
"PickerActionSetStartupDefault": "définir comme valeur par défaut au démarrage",
"PickerActionPin": "épingler",
+ "PickerActionFleet": "flotte",
"PickerActionCancel": "annuler",
"PickerActionClear": "effacer",
"PickerActionClearSearch": "effacer la recherche",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Enregistré pour",
"FleetRosterLayerWins": "actif",
"FleetRosterLayerIgnored": "copie ignorée",
- "FleetReadyNotice": "Pod prêt · /pod ouvre les rôles · /pod setup ajuste les modèles des membres",
+ "FleetReadyNotice": "Pod prêt · /fleet ouvre les rôles · /fleet setup ajuste les modèles des membres",
"FleetProfileIdentityVerifyFailed": "Impossible de vérifier les identités des profils existants ({error}) ; corrigez le fichier indiqué avant d'enregistrer.",
"FleetProfileIdConflict": "L'id de profil `{id}` est déjà utilisé par {path} ; rédigez un nouveau brouillon avec un rôle différent ou supprimez d'abord l'ancien fichier.",
"FleetProfileProviderUnconfigured": "Le profil épingle le fournisseur `{provider}`, qui n'a pas d'identifiants configurés ({env}) ; configurez-le dans /provider avant d'enregistrer.",
+ "FleetModelAdded": "{route} ajouté au fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} ajouté en tant que {roles} au fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (nouveau fleet personnel, désormais sélectionné)",
+ "FleetModelAddedSelectedNote": " (désormais sélectionné)",
+ "FleetModelRemoved": "{route} retiré du fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) retiré du fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} reste dans le fleet `{fleet}` : {reason}",
+ "FleetModelReasonOperatorRoute": "c'est votre modèle actuel (la route opérateur du fleet) ; changez de modèle ou utilisez /fleet save pour la modifier",
+ "FleetModelReasonAlreadyPresent": "déjà dans le fleet",
+ "FleetModelErrorNeedsRoute": "un modèle du fleet a besoin d'un id de provider et d'un id de modèle",
+ "FleetModelErrorNoSelection": "aucun fleet n'est sélectionné ; votre fleet se limite au modèle de la session",
+ "FleetModelErrorOperatorRoute": "{route} est la route opérateur du fleet `{fleet}` ; modifiez-la avec /fleet save, pas avec remove",
+ "FleetModelErrorNotInFleet": "{route} n'est pas dans le fleet `{fleet}`",
+ "FleetModelsEmpty": "Votre fleet se limite au modèle de la session. Ajoutez-en un : /fleet add [role…] (ou ⇧F sur une ligne de /model).",
+ "FleetModelsHeader": "Votre fleet `{fleet}` ({count} modèles)",
+ "FleetModelsBroken": "Impossible de charger le fleet sélectionné : {error}. Corrigez le fichier ou choisissez-en un autre avec /fleet fleets.",
+ "FleetModelsFooter": "Ajouter : /fleet add [role…] · Retirer : /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} par M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "outils",
+ "FleetAddUsage": "Usage : /fleet add [role…] — ex. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` n'est pas un provider configuré. Configurez-le dans ~/.codewhale/config.toml ou basculez dessus avec /provider avant de l'ajouter au fleet.",
+ "FleetAddModelNotServed": "{provider} ne sert pas `{model}` dans le catalogue actuel ; lancez /models pour voir ce qu'il sert, ou /fleet add avec l'id exact listé.",
+ "FleetAddFailed": "Impossible d'ajouter au fleet : {error}",
+ "FleetRemoveUsage": "Usage : /fleet remove ",
+ "FleetRemoveFailed": "Impossible de retirer du fleet : {error}",
+ "FleetToggleFailed": "Impossible de mettre à jour le fleet : {error}",
"FleetDestStepTitle": "Où ce profil doit-il être enregistré ?",
"FleetDestStepSubtitle": "Rien n'est écrit tant que vous ne confirmez pas à la dernière étape.",
"FleetDestProjectLabel": "Ce projet",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review a bloqué '{tool}' (politique déterministe) : {reason}",
"AutoReviewReceiptHeld": "Auto-Review a retenu '{tool}' sans pause ; refusé (une personne est requise — passez en Ask)",
"FooterHintEscInterrupt": "Échap pour interrompre",
+ "PostureHintEnterAgain": "{enter} à nouveau pour envoyer maintenant · {steer} pilote",
"PermissionsPostureHeader": "Accès actuel : {posture}",
"PermissionsPostureAsk": "Ask : les appels de tool qui modifient l'autorité, le coût, la portée ou le résultat ouvrent une invite ; les appels en lecture seule prouvés sûrs s'exécutent sans. Les règles ask ci-dessus forcent toujours une invite.",
"PermissionsPostureAuto": "Auto-Review : n'ouvre jamais d'invite. Une politique déterministe autorise les appels prouvés sûrs et bloque fermement les travaux de publication ou destructifs en arrière-plan ; les appels qu'elle ne peut prouver sûrs vont à un gardien de modèle en une passe, qui autorise ou refuse avec un motif (un risque élevé ou critique ne s'exécute jamais automatiquement ; un examen échoué refuse, fail closed). Les retenues exigeant une personne sont refusées, pas cachées. Chaque décision est écrite dans la transcription sous forme de note et dans le journal d'audit.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell en lecture seule",
"AgentFocusPostureShellNone": "sans shell",
"GoalReceiptSet": "Objectif défini : « {objective} » · /goal affiche la progression · /goal pause ou /goal clear l'arrête",
+ "GoalReceiptSetOperate": "◆ objectif défini · Operate continue jusqu'à vérification · /goal pour le modifier",
"GoalControlAccepted": "Commande d'objectif enregistrée ; elle sera appliquée au prochain point sûr.",
"GoalControlRuntimeUnavailable": "La commande d'objectif est enregistrée, mais le moteur est indisponible. Elle sera restaurée après le redémarrage.",
"GoalStatusIdleHint": "à l'arrêt pour l'instant — envoyez un message ou /goal resume pour continuer",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Bloque tout outil qui requiert une approbation.",
"ConfigChoiceDetailModeAgent": "Démarre prêt à collaborer et à utiliser des outils.",
"ConfigChoiceDetailModePlan": "Démarre dans un espace de planification en lecture seule.",
- "ConfigChoiceDetailModeOperate": "Démarre en coordinateur qui délègue le travail à des workers bornés.",
+ "ConfigChoiceDetailModeOperate": "Operate transforme votre demande en objectif et y travaille en parallèle : workers en arrière-plan pour les flux séparables, vérifiés avant de s'arrêter.",
"ConfigChoiceDetailPlacementTop": "Affiche Tâches, À faire et Workers au-dessus de la transcription.",
"ConfigChoiceDetailPlacementLeft": "Affiche Tâches, À faire et Workers dans une barre latérale gauche quand le terminal est assez large.",
"ConfigChoiceDetailPlacementRight": "Affiche Tâches, À faire et Workers dans une barre latérale droite quand le terminal est assez large.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "ouvre les plugins · faire confiance, activer ou diagnostiquer",
"ConfigHintMcpConfigPath": "chemin vers mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 bloque les agents enfants ; 3 par défaut (même axe que les sous-agents) ; plafond 8",
- "ConfigHintFeatureSubagents": "état en lecture seule du drapeau de fonctionnalité ; /pod setup est le chemin pour l'utilisateur",
+ "ConfigHintFeatureSubagents": "état en lecture seule du drapeau de fonctionnalité ; /fleet setup est le chemin pour l'utilisateur",
"ConfigHintFeatureWebSearch": "état en lecture seule du drapeau de fonctionnalité des outils de recherche web",
"ConfigHintFeatureApplyPatch": "état en lecture seule du drapeau de fonctionnalité des outils d'édition par patch",
"ConfigHintFeatureMcp": "état en lecture seule du drapeau de fonctionnalité des outils MCP",
"ConfigHintFeatureExecPolicy": "état en lecture seule du drapeau de fonctionnalité des outils de politique d'exécution",
"ConfigHintFeatureVisionModel": "drapeau bêta pour la prise en charge des images vision/modèle",
"ConfigHintGoalCommand": "/goal définit les objectifs, les budgets et l'état du contexte de Travail",
- "ConfigHintWorkflow": "/workflow exécute des opérations scriptées avec des cartes d'exécution de distribution/regroupement"
+ "ConfigHintWorkflow": "/workflow exécute des opérations scriptées avec des cartes d'exécution de distribution/regroupement",
+ "LaunchMenuNewWorktree": "Nouveau worktree",
+ "LaunchMenuResume": "Reprendre la session",
+ "LaunchMenuQuit": "Quitter",
+ "LaunchNoticeClaude": "Vous venez de Claude Code ? Reprenez votre session avec ctrl+r",
+ "ReceiptSessionHooks": "hooks {count}"
}
diff --git a/crates/tui/locales/hi.json b/crates/tui/locales/hi.json
index 0116a52b19..75546f4af2 100644
--- a/crates/tui/locales/hi.json
+++ b/crates/tui/locales/hi.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "माउस कैप्चर चालू।",
"ScreenModeMouseCaptureOff": "माउस कैप्चर बंद; चयन टर्मिनल के नियंत्रण में है।",
"ScreenModeUnchanged": "स्क्रीन अपरिवर्तित: {reason}।",
+ "ImageInputRejectedResent": "{model} इमेज स्वीकार नहीं करता — उन्हें टेक्स्ट के रूप में फिर भेजा गया; पढ़ने के लिए image_ocr का उपयोग करें",
"ConfigLabelShowThinking": "चैट में मॉडल रीज़निंग",
"ConfigLabelThinkingHighlight": "रीज़निंग पृष्ठभूमि हाइलाइट",
"ConfigLabelShowToolDetails": "टूल विवरण स्तर",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "इस वर्कस्पेस के वर्कफ़्लो रन दिखाएँ (सूची, रद्द करें)",
"CmdHotbarDescription": "Hotbar सेटअप खोलें",
"CmdSetupDescription": "संविधान-प्रथम सेटअप खोलें",
- "CmdSubagentsDescription": "/pod workers के लिए संगतता शॉर्टकट (वर्तमान सत्र के उप-एजेंट)",
+ "CmdSubagentsDescription": "/fleet workers के लिए संगतता शॉर्टकट (वर्तमान सत्र के उप-एजेंट)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "वर्तमान सिस्टम प्रॉम्प्ट दिखाएँ",
"CmdTaskDescription": "बैकग्राउंड कार्य प्रबंधित करें",
@@ -632,7 +633,7 @@
"KbExternalEditor": "कम्पोज़र मसौदा बाहरी संपादक में खोलें",
"KbLiveTranscript": "लाइव ट्रांसक्रिप्ट ओवरले खोलें (Ctrl+Shift+T; स्टिकी-टेल ऑटो-स्क्रॉल)",
"KbBacktrackMessage": "पिछले उपयोगकर्ता संदेश पर वापस जाएँ (Left/Right कदम, रिवाइंड के लिए Enter)",
- "KbCompleteCycleModes": "कम्पोज़र खाली होने पर /command पूरा करें या मोड बदलें (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "कम्पोज़र खाली होने पर /command पूरा करें या मोड बदलें (Plan → Work → Operate)",
"KbCycleThinking": "सक्रिय प्रोवाइडर के लिए रीज़निंग स्तर बदलें (Ctrl+T)",
"KbCyclePermissions": "एक्सेस बदलें: पूछें → स्वतः समीक्षा → पूर्ण एक्सेस (Shift+Tab)",
"KbJumpPlanAgentYolo": "Hotbar स्लॉट ट्रिगर करें",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Kimi Code मेंबरशिप-प्लान रूट: `{route}` (प्लान कंसोल: `{console}`; मॉडल k3 उपयोग करें)। Codewhale Kimi CLI क्रेडेंशियल कभी इम्पोर्ट नहीं करता।",
"LinksTip": "सुझाव: अपने प्रोवाइडर के लिए दिखाया गया env var उपयोग करें, या `codewhale auth set --provider ` से कुंजी सहेजें।",
"SubagentsFetching": "वर्तमान सत्र के उप-एजेंट प्राप्त हो रहे हैं...",
- "SubagentsNoCurrentSessionPodWorkers": "वर्तमान सत्र में कोई Pod वर्कर नहीं है।",
- "SubagentsCurrentSessionPodWorkersTitle": "वर्तमान सत्र के Pod वर्कर",
- "SubagentsCurrentSessionPodWorkerRoles": "उप-एजेंट भूमिकाएँ वर्तमान सत्र की Pod वर्कर भूमिकाएँ हैं।",
- "SubagentsCurrentSessionPodWorkersStatus": "वर्तमान सत्र के Pod वर्कर: कुल {count}",
- "SubagentsEmptyGuidance": "/pod के साथ भूमिकाएँ और लॉन्च मुद्रा कॉन्फ़िगर करें।",
+ "SubagentsNoCurrentSessionPodWorkers": "वर्तमान सत्र में कोई बेड़ा वर्कर नहीं है।",
+ "SubagentsCurrentSessionPodWorkersTitle": "वर्तमान सत्र के बेड़ा वर्कर",
+ "SubagentsCurrentSessionPodWorkerRoles": "उप-एजेंट भूमिकाएँ वर्तमान सत्र की बेड़ा वर्कर भूमिकाएँ हैं।",
+ "SubagentsCurrentSessionPodWorkersStatus": "वर्तमान सत्र के बेड़ा वर्कर: कुल {count}",
+ "SubagentsEmptyGuidance": "/fleet के साथ भूमिकाएँ और लॉन्च मुद्रा कॉन्फ़िगर करें।",
"SubagentsStatusRunning": "चल रहा है",
"SubagentsStatusCompleted": "पूर्ण",
"SubagentsStatusInterrupted": "बाधित",
@@ -718,7 +719,7 @@
"HomeHistory": "इतिहास:",
"HomeTokens": "टोकन:",
"HomeQueued": "कतारबद्ध:",
- "HomeSubagents": "वर्तमान सत्र के Pod वर्कर:",
+ "HomeSubagents": "वर्तमान सत्र के बेड़ा वर्कर:",
"HomeSkill": "स्किल:",
"HomeQuickActions": "त्वरित क्रियाएँ",
"HomeQuickLinks": "/links - Codewhale, समुदाय और प्रोवाइडर लिंक",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - सेटिंग्स देखें और बदलें",
"HomeQuickSettings": "/settings - स्थायी सेटिंग्स दिखाएँ",
"HomeQuickModel": "/model - मॉडल बदलें या देखें",
- "HomeQuickSubagents": "/pod workers - वर्तमान सत्र के उप-एजेंट",
+ "HomeQuickSubagents": "/fleet workers - वर्तमान सत्र के उप-एजेंट",
"HomeQuickTaskList": "/task list - बैकग्राउंड कार्य कतार दिखाएँ",
"HomeQuickHelp": "/help - मदद दिखाएँ",
"HomeQuickWorkspace": "/workspace - फ़ोल्डर या worktree बदलें",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "स्वचालित जोखिम समीक्षा के साथ शेल सक्षम",
"AppModePlanHint": "पहले रीड-ओनली शोध — कार्रवाई से पहले योजना प्रस्तुत करें",
"AppModeYoloHint": "केवल संगतता — Act + Full Access, दृश्य मोड नहीं",
- "AppModeOperateHint": "अपने Pod को समानांतर काम पर लगाएँ",
+ "AppModeOperateHint": "प्रॉम्प्ट को लक्ष्य बनाता है: समानांतर वर्कर, सत्यापन के साथ",
"VimModeNormal": "-- सामान्य --",
"VimModeInsert": "-- डालना --",
"VimModeVisual": "-- विज़ुअल --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "पैच",
"ToolFamilyRun": "रन",
"ToolFamilyFind": "खोज",
- "ToolFamilyDelegate": "सौंपना",
+ "ToolFamilyDelegate": "एजेंट",
"ToolFamilyFanout": "फ़ैनआउट",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "सत्यापन",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Operate मोड",
"HotbarActionModeOperateDescription": "अपने Pod को समानांतर काम पर लगाएँ।",
"HomeOperateModeTip": "Operate — अपने Pod को समानांतर काम पर लगाएँ",
- "HomeOperateModeFleetTip": " बिल्ट-इन भूमिकाएँ इस सत्र का मॉडल इस्तेमाल करती हैं; /pod setup उन्हें अनुकूलित करता है",
+ "HomeOperateModeFleetTip": " बिल्ट-इन भूमिकाएँ इस सत्र का मॉडल इस्तेमाल करती हैं; /fleet setup उन्हें अनुकूलित करता है",
"HelpSubtitle": "अवधारणाएँ, कमांड और कीबाइंडिंग",
"CommandPaletteTitle": "कमांड",
"CommandPaletteSubtitle": "एक कार्रवाई खोजें और चलाएँ",
"ConfigSubtitle": "पहले सेटिंग्स; रॉ कुंजियाँ उन्नत विवरण में",
"LaunchStartTitle": "यहाँ से शुरू करें",
- "LaunchMenuConnect": "मॉडल कनेक्ट करें",
- "LaunchMenuWork": "काम · मौजूदा फ़ोल्डर",
- "LaunchMenuChat": "चैट · केवल पढ़ने के लिए",
- "LaunchMenuTheme": "थीम",
- "LaunchMenuHelp": "मदद",
"LaunchWorkDescription": "इस फ़ोल्डर में स्थानीय टूल इस्तेमाल करें; बदलाव आपकी स्वीकृति नीति के अनुसार होंगे।",
"LaunchChatDescription": "सिर्फ़ बातचीत और योजना; फ़ाइलों में कोई बदलाव नहीं।",
"LaunchWorkspaceGitReady": "वर्कस्पेस · {name} · Git वर्कस्पेस",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "फ़ोल्डर",
"LaunchProviderConfiguredShort": "प्रोवाइडर सेट",
"LaunchProviderSetupShort": "प्रोवाइडर सेटअप",
- "LaunchMenuNewSession": "नया सत्र",
- "LaunchMenuNewWorktree": "नया worktree",
- "LaunchMenuWorktreeCompact": "कार्य-ट्री",
- "LaunchMenuChatCompact": "बातचीत",
- "LaunchMenuResumeSession": "सत्र फिर शुरू करें",
"LaunchMenuChangelog": "चेंजलॉग",
- "LaunchMenuQuit": "बाहर निकलें",
- "LaunchMenuUnavailable": "अनुपलब्ध",
- "LaunchMenuSavedCount": "{count} सहेजे",
"LaunchWorktreePrompt": "ब्रांच/worktree का नाम दें, या स्वचालित नाम के लिए Enter दबाएँ।",
"LaunchWorktreeNeedsGit": "नए worktree के लिए Git रिपॉज़िटरी आवश्यक।",
"LaunchWorktreeNameLabel": "worktree नाम",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "worktree विफल: {error}",
"LaunchNoSavedSessions": "इस वर्कस्पेस के लिए कोई सहेजा सत्र नहीं।",
"LaunchComposerHint": "Enter भेजें · Shift+Enter नई पंक्ति · Esc वापस",
- "LaunchComposerFocusHint": "टाइप करने के लिए Tab",
+ "LaunchNoModelConnected": "कोई मॉडल कनेक्ट नहीं",
+ "LaunchRunCommand": "{command} चलाएँ",
+ "LaunchMcpConnectedOne": "1 MCP सर्वर कनेक्टेड",
+ "LaunchMcpConnectedMany": "{count} MCP सर्वर कनेक्टेड",
+ "LaunchMcpNeedsSignInOne": "1 को साइन-इन चाहिए",
+ "LaunchMcpNeedsSignInMany": "{count} को साइन-इन चाहिए",
"PhaseIdle": "निष्क्रिय",
"PhaseDraft": "मसौदा",
"PhaseWorking": "काम चल रहा",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "आउटपुट",
"FooterHintContext": "कॉन्टेक्स्ट",
"InfoLineHelp": "मदद",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "व्हेल",
"InfoLineAutomation": "स्वचालन",
"InfoLineNotConnected": "कनेक्ट नहीं है",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "लागू करें",
"PickerActionSetStartupDefault": "आरंभिक डिफ़ॉल्ट के रूप में सेट करें",
"PickerActionPin": "पिन करें",
+ "PickerActionFleet": "फ़्लीट",
"PickerActionCancel": "रद्द करें",
"PickerActionClear": "साफ़ करें",
"PickerActionClearSearch": "खोज साफ़ करें",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "सहेजा गया स्थान",
"FleetRosterLayerWins": "सक्रिय",
"FleetRosterLayerIgnored": "अनदेखी प्रतिलिपि",
- "FleetReadyNotice": "Pod तैयार · /pod भूमिकाएँ खोलता है · /pod setup सदस्यों के मॉडल समायोजित करता है",
+ "FleetReadyNotice": "Pod तैयार · /fleet भूमिकाएँ खोलता है · /fleet setup सदस्यों के मॉडल समायोजित करता है",
"FleetProfileIdentityVerifyFailed": "मौजूदा प्रोफ़ाइल पहचान सत्यापित नहीं हो सकी ({error}); सहेजने से पहले नामित फ़ाइल ठीक करें।",
"FleetProfileIdConflict": "प्रोफ़ाइल id `{id}` पहले से {path} इस्तेमाल कर रहा है; दूसरी भूमिका से फिर मसौदा बनाएँ या पहले पुरानी फ़ाइल हटाएँ।",
"FleetProfileProviderUnconfigured": "प्रोफ़ाइल प्रोवाइडर `{provider}` पिन करती है, जिसके क्रेडेंशल कॉन्फ़िगर नहीं ({env}); सहेजने से पहले /provider में सेटअप करें।",
+ "FleetModelAdded": "{route} को fleet `{fleet}` में जोड़ा गया",
+ "FleetModelAddedAs": "{route} को {roles} के रूप में fleet `{fleet}` में जोड़ा गया",
+ "FleetModelAddedCreatedNote": " (नया व्यक्तिगत fleet, अब चयनित)",
+ "FleetModelAddedSelectedNote": " (अब चयनित)",
+ "FleetModelRemoved": "{route} को fleet `{fleet}` से हटाया गया",
+ "FleetModelRemovedRoles": "{route} ({roles}) को fleet `{fleet}` से हटाया गया",
+ "FleetModelUnchanged": "{route} fleet `{fleet}` में बना रहता है: {reason}",
+ "FleetModelReasonOperatorRoute": "यह आपका वर्तमान मॉडल है (fleet का operator रूट); मॉडल बदलें या इसे बदलने के लिए /fleet save का उपयोग करें",
+ "FleetModelReasonAlreadyPresent": "पहले से fleet में है",
+ "FleetModelErrorNeedsRoute": "fleet मॉडल के लिए provider id और model id दोनों ज़रूरी हैं",
+ "FleetModelErrorNoSelection": "कोई fleet चयनित नहीं है; आपका fleet केवल सत्र मॉडल है",
+ "FleetModelErrorOperatorRoute": "{route} fleet `{fleet}` का operator रूट है; इसे remove से नहीं, /fleet save से बदलें",
+ "FleetModelErrorNotInFleet": "{route} fleet `{fleet}` में नहीं है",
+ "FleetModelsEmpty": "आपका fleet केवल सत्र मॉडल है। एक जोड़ें: /fleet add [role…] (या /model की किसी पंक्ति पर ⇧F)।",
+ "FleetModelsHeader": "आपका fleet `{fleet}` ({count} मॉडल)",
+ "FleetModelsBroken": "चयनित fleet लोड नहीं हो सका: {error}। फ़ाइल ठीक करें या /fleet fleets से दूसरा चुनें।",
+ "FleetModelsFooter": "जोड़ें: /fleet add [role…] · हटाएँ: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} प्रति M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "टूल",
+ "FleetAddUsage": "उपयोग: /fleet add [role…] — जैसे /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` कोई कॉन्फ़िगर किया गया provider नहीं है। इसे fleet में जोड़ने से पहले ~/.codewhale/config.toml में कॉन्फ़िगर करें या /provider से इस पर स्विच करें।",
+ "FleetAddModelNotServed": "{provider} वर्तमान कैटलॉग में `{model}` उपलब्ध नहीं कराता; यह क्या देता है देखने के लिए /models चलाएँ, या सूची में दिए सटीक id के साथ /fleet add करें।",
+ "FleetAddFailed": "fleet में जोड़ा नहीं जा सका: {error}",
+ "FleetRemoveUsage": "उपयोग: /fleet remove ",
+ "FleetRemoveFailed": "fleet से हटाया नहीं जा सका: {error}",
+ "FleetToggleFailed": "fleet अपडेट नहीं हो सका: {error}",
"FleetDestStepTitle": "यह प्रोफ़ाइल कहाँ सहेजी जाए?",
"FleetDestStepSubtitle": "अंतिम चरण में पुष्टि करने तक कुछ भी नहीं लिखा जाता।",
"FleetDestProjectLabel": "यह प्रोजेक्ट",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review ने '{tool}' को अवरुद्ध किया (नियतात्मक नीति): {reason}",
"AutoReviewReceiptHeld": "Auto-Review ने बिना रुके '{tool}' को रोका; अस्वीकृत (व्यक्ति चाहिए — Ask पर जाएँ)",
"FooterHintEscInterrupt": "रोकने के लिए Esc",
+ "PostureHintEnterAgain": "अभी भेजने के लिए {enter} फिर दबाएँ · {steer} निर्देशित करता है",
"PermissionsPostureHeader": "वर्तमान एक्सेस: {posture}",
"PermissionsPostureAsk": "Ask: अधिकार, लागत, दायरा या परिणाम बदलने वाली tool कॉल प्रॉम्प्ट खोलती हैं; सिद्ध-सुरक्षित केवल-पढ़ने वाली कॉल बिना प्रॉम्प्ट चलती हैं। ऊपर के ask नियम हमेशा प्रॉम्प्ट लागू करते हैं।",
"PermissionsPostureAuto": "Auto-Review: कभी प्रॉम्प्ट नहीं खोलता। नियतात्मक नीति सिद्ध-सुरक्षित कॉल की अनुमति देती है और प्रकाशन-जैसे या विनाशकारी पृष्ठभूमि कार्य को कड़ाई से अवरुद्ध करती है; जिन कॉल को सुरक्षित सिद्ध नहीं किया जा सकता वे एक-बार के मॉडल गार्जियन के पास जाती हैं जो कारण बताकर अनुमति देता या अस्वीकार करता है (उच्च या गंभीर जोखिम कभी स्वतः नहीं चलता; असफल समीक्षा fail closed के तहत अस्वीकार करती है)। जिन रोकों के लिए व्यक्ति चाहिए वे छिपाई नहीं जातीं, अस्वीकार की जाती हैं। ऐसा हर निर्णय ट्रांसक्रिप्ट में नोट के रूप में और ऑडिट लॉग में लिखा जाता है।",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "केवल-पढ़ने शेल",
"AgentFocusPostureShellNone": "शेल नहीं",
"GoalReceiptSet": "लक्ष्य सेट: \"{objective}\" · /goal प्रगति दिखाता है · /goal pause या /goal clear इसे रोकता है",
+ "GoalReceiptSetOperate": "◆ लक्ष्य सेट · Operate सत्यापित होने तक काम करता रहेगा · संपादित करने के लिए /goal",
"GoalControlAccepted": "लक्ष्य नियंत्रण सहेजा गया; इसे अगले सुरक्षित विराम पर लागू किया जाएगा।",
"GoalControlRuntimeUnavailable": "लक्ष्य नियंत्रण सहेजा गया है, लेकिन रनटाइम उपलब्ध नहीं है। पुनः आरंभ के बाद इसे बहाल किया जाएगा।",
"GoalStatusIdleHint": "अभी नहीं चल रहा — जारी रखने के लिए संदेश भेजें या /goal resume",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "अनुमोदन चाहने वाले हर टूल को रोकता है।",
"ConfigChoiceDetailModeAgent": "सहयोग और टूल उपयोग के लिए तैयार अवस्था में शुरू होता है।",
"ConfigChoiceDetailModePlan": "केवल पढ़ने योग्य योजना क्षेत्र में शुरू होता है।",
- "ConfigChoiceDetailModeOperate": "समन्वयक के रूप में शुरू होता है जो सीमित worker को काम सौंपता है।",
+ "ConfigChoiceDetailModeOperate": "Operate आपके प्रॉम्प्ट को लक्ष्य बनाकर उस पर समानांतर काम करता है: अलग की जा सकने वाली धाराओं के लिए पृष्ठभूमि worker, रुकने से पहले सत्यापित।",
"ConfigChoiceDetailPlacementTop": "कार्य, करने योग्य और Worker को ट्रांसक्रिप्ट के ऊपर दिखाता है।",
"ConfigChoiceDetailPlacementLeft": "टर्मिनल पर्याप्त चौड़ा होने पर कार्य, करने योग्य और Worker को बाएँ साइडबार में दिखाता है।",
"ConfigChoiceDetailPlacementRight": "टर्मिनल पर्याप्त चौड़ा होने पर कार्य, करने योग्य और Worker को दाएँ साइडबार में दिखाता है।",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "प्लगइन खोलें · भरोसा करें, सक्षम करें या निदान करें",
"ConfigHintMcpConfigPath": "mcp.json का पथ",
"ConfigHintFleetMaxSpawnDepth": "0 बाल एजेंट रोकता है; डिफ़ॉल्ट 3 (उप-एजेंट के समान अक्ष); अधिकतम 8",
- "ConfigHintFeatureSubagents": "केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति; /pod setup उपयोगकर्ता के लिए मार्ग है",
+ "ConfigHintFeatureSubagents": "केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति; /fleet setup उपयोगकर्ता के लिए मार्ग है",
"ConfigHintFeatureWebSearch": "वेब खोज टूल की केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति",
"ConfigHintFeatureApplyPatch": "पैच संपादन टूल की केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति",
"ConfigHintFeatureMcp": "MCP टूल की केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति",
"ConfigHintFeatureExecPolicy": "निष्पादन नीति टूल की केवल पढ़ने योग्य फ़ीचर फ़्लैग स्थिति",
"ConfigHintFeatureVisionModel": "दृष्टि/मॉडल छवि समर्थन के लिए बीटा फ़ीचर फ़्लैग",
"ConfigHintGoalCommand": "/goal लक्ष्य, बजट और कार्य-संदर्भ स्थिति सेट करता है",
- "ConfigHintWorkflow": "/workflow वितरण/समेकन रन कार्ड के साथ स्क्रिप्टेड संचालन चलाता है"
+ "ConfigHintWorkflow": "/workflow वितरण/समेकन रन कार्ड के साथ स्क्रिप्टेड संचालन चलाता है",
+ "LaunchMenuNewWorktree": "नया वर्कट्री",
+ "LaunchMenuResume": "सेशन फिर से शुरू करें",
+ "LaunchMenuQuit": "बंद करें",
+ "LaunchNoticeClaude": "Claude Code से आए हैं? ctrl+r से सेशन फिर से शुरू करें",
+ "ReceiptSessionHooks": "हुक {count}"
}
diff --git a/crates/tui/locales/id.json b/crates/tui/locales/id.json
index 57c4c38521..532bb4d859 100644
--- a/crates/tui/locales/id.json
+++ b/crates/tui/locales/id.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Penangkapan mouse aktif.",
"ScreenModeMouseCaptureOff": "Penangkapan mouse nonaktif; terminal mengatur seleksi.",
"ScreenModeUnchanged": "Layar tidak berubah: {reason}.",
+ "ImageInputRejectedResent": "{model} tidak menerima gambar — gambar dikirim ulang sebagai teks; gunakan image_ocr untuk membacanya",
"ConfigLabelShowThinking": "Penalaran model di chat",
"ConfigLabelThinkingHighlight": "Sorotan latar penalaran",
"ConfigLabelShowToolDetails": "Tingkat detail alat",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Tampilkan eksekusi alur kerja di workspace ini (daftar, batalkan)",
"CmdHotbarDescription": "Buka penyiapan Hotbar",
"CmdSetupDescription": "Buka penyiapan konstitusi-dulu",
- "CmdSubagentsDescription": "Pintasan kompatibilitas untuk /pod workers (subagen sesi saat ini)",
+ "CmdSubagentsDescription": "Pintasan kompatibilitas untuk /fleet workers (subagen sesi saat ini)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Tampilkan prompt sistem saat ini",
"CmdTaskDescription": "Kelola tugas latar belakang",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Buka draf komposer di editor eksternal",
"KbLiveTranscript": "Buka overlay transkrip langsung (Ctrl+Shift+T; auto-scroll mengikuti ekor)",
"KbBacktrackMessage": "Mundur ke pesan pengguna sebelumnya (Left/Right berpindah, Enter untuk memutar balik)",
- "KbCompleteCycleModes": "Lengkapi /command atau ganti mode saat komposer kosong (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Lengkapi /command atau ganti mode saat komposer kosong (Plan → Work → Operate)",
"KbCycleThinking": "Ganti upaya penalaran untuk penyedia aktif (Ctrl+T)",
"KbCyclePermissions": "Ubah akses: Tanya → Tinjauan otomatis → Akses penuh (Shift+Tab)",
"KbJumpPlanAgentYolo": "Picu slot hotbar",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Rute paket keanggotaan Kimi Code: `{route}` (konsol paket: `{console}`; gunakan model k3). Codewhale tidak pernah mengimpor kredensial Kimi CLI.",
"LinksTip": "Kiat: Gunakan env var yang ditampilkan untuk penyedia Anda, atau simpan kunci dengan `codewhale auth set --provider `.",
"SubagentsFetching": "Mengambil subagen sesi saat ini...",
- "SubagentsNoCurrentSessionPodWorkers": "Tidak ada pekerja Pod di sesi saat ini.",
- "SubagentsCurrentSessionPodWorkersTitle": "Pekerja Pod sesi saat ini",
- "SubagentsCurrentSessionPodWorkerRoles": "Peran subagen adalah peran pekerja Pod sesi saat ini.",
- "SubagentsCurrentSessionPodWorkersStatus": "Pekerja Pod sesi saat ini: total {count}",
- "SubagentsEmptyGuidance": "Konfigurasikan peran dan postur peluncuran dengan /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "Tidak ada pekerja armada di sesi saat ini.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Pekerja armada sesi saat ini",
+ "SubagentsCurrentSessionPodWorkerRoles": "Peran subagen adalah peran pekerja armada sesi saat ini.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Pekerja armada sesi saat ini: total {count}",
+ "SubagentsEmptyGuidance": "Konfigurasikan peran dan postur peluncuran dengan /fleet.",
"SubagentsStatusRunning": "Berjalan",
"SubagentsStatusCompleted": "Selesai",
"SubagentsStatusInterrupted": "Terganggu",
@@ -718,7 +719,7 @@
"HomeHistory": "Riwayat:",
"HomeTokens": "Token:",
"HomeQueued": "Dalam antrean:",
- "HomeSubagents": "Worker Pod sesi saat ini:",
+ "HomeSubagents": "Worker armada sesi saat ini:",
"HomeSkill": "Keterampilan:",
"HomeQuickActions": "Aksi Cepat",
"HomeQuickLinks": "/links - Tautan Codewhale, komunitas & penyedia",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - Periksa dan ubah pengaturan",
"HomeQuickSettings": "/settings - Tampilkan pengaturan persisten",
"HomeQuickModel": "/model - Ganti atau lihat model",
- "HomeQuickSubagents": "/pod workers - subagen sesi saat ini",
+ "HomeQuickSubagents": "/fleet workers - subagen sesi saat ini",
"HomeQuickTaskList": "/task list - Tampilkan antrean tugas latar belakang",
"HomeQuickHelp": "/help - Tampilkan bantuan",
"HomeQuickWorkspace": "/workspace - Ganti folder atau worktree",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell aktif dengan tinjauan risiko otomatis",
"AppModePlanHint": "Riset baca-saja dulu — ajukan rencana sebelum bertindak",
"AppModeYoloHint": "Hanya kompatibilitas — Act + akses penuh, bukan mode yang terlihat",
- "AppModeOperateHint": "Kerahkan Pod Anda untuk bekerja paralel",
+ "AppModeOperateHint": "Mengubah prompt menjadi goal: worker paralel, terverifikasi",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- SISIP --",
"VimModeVisual": "-- VISUAL --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "tambal",
"ToolFamilyRun": "jalankan",
"ToolFamilyFind": "cari",
- "ToolFamilyDelegate": "delegasi",
+ "ToolFamilyDelegate": "agen",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "verifikasi",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Mode Operate",
"HotbarActionModeOperateDescription": "Kerahkan Pod Anda untuk bekerja paralel.",
"HomeOperateModeTip": "Operate — kerahkan Pod Anda untuk bekerja paralel",
- "HomeOperateModeFleetTip": " Peran bawaan memakai model sesi ini; /pod setup menyesuaikannya",
+ "HomeOperateModeFleetTip": " Peran bawaan memakai model sesi ini; /fleet setup menyesuaikannya",
"HelpSubtitle": "Konsep, perintah, dan keybinding",
"CommandPaletteTitle": "Perintah",
"CommandPaletteSubtitle": "Temukan dan jalankan satu aksi",
"ConfigSubtitle": "Pengaturan dulu; kunci mentah di detail lanjutan",
"LaunchStartTitle": "Mulai di sini",
- "LaunchMenuConnect": "Hubungkan model",
- "LaunchMenuWork": "Kerja · folder saat ini",
- "LaunchMenuChat": "Chat · hanya baca",
- "LaunchMenuTheme": "Tema",
- "LaunchMenuHelp": "Bantuan",
"LaunchWorkDescription": "Gunakan folder ini dengan alat lokal; perubahan mengikuti kebijakan persetujuan Anda.",
"LaunchChatDescription": "Hanya percakapan dan perencanaan; tidak ada perubahan file.",
"LaunchWorkspaceGitReady": "Ruang kerja · {name} · ruang kerja Git",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "folder",
"LaunchProviderConfiguredShort": "penyedia dikonfigurasi",
"LaunchProviderSetupShort": "siapkan penyedia",
- "LaunchMenuNewSession": "Sesi baru",
- "LaunchMenuNewWorktree": "Worktree baru",
- "LaunchMenuWorktreeCompact": "pohon kerja",
- "LaunchMenuChatCompact": "obrolan",
- "LaunchMenuResumeSession": "Lanjutkan sesi",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Keluar",
- "LaunchMenuUnavailable": "tidak tersedia",
- "LaunchMenuSavedCount": "{count} tersimpan",
"LaunchWorktreePrompt": "Beri nama branch/worktree, atau tekan Enter untuk nama otomatis.",
"LaunchWorktreeNeedsGit": "Worktree baru memerlukan repositori Git.",
"LaunchWorktreeNameLabel": "nama worktree",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "Worktree gagal: {error}",
"LaunchNoSavedSessions": "Tidak ada sesi tersimpan untuk workspace ini.",
"LaunchComposerHint": "Enter kirim · Shift+Enter baris baru · Esc kembali",
- "LaunchComposerFocusHint": "Tab untuk mengetik",
+ "LaunchNoModelConnected": "tidak ada model terhubung",
+ "LaunchRunCommand": "jalankan {command}",
+ "LaunchMcpConnectedOne": "1 server MCP terhubung",
+ "LaunchMcpConnectedMany": "{count} server MCP terhubung",
+ "LaunchMcpNeedsSignInOne": "1 perlu masuk",
+ "LaunchMcpNeedsSignInMany": "{count} perlu masuk",
"PhaseIdle": "diam",
"PhaseDraft": "draf",
"PhaseWorking": "bekerja",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "output",
"FooterHintContext": "konteks",
"InfoLineHelp": "bantuan",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "paus",
"InfoLineAutomation": "otomatisasi",
"InfoLineNotConnected": "tidak terhubung",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "terapkan",
"PickerActionSetStartupDefault": "tetapkan sebagai default saat mulai",
"PickerActionPin": "sematkan",
+ "PickerActionFleet": "armada",
"PickerActionCancel": "batal",
"PickerActionClear": "bersihkan",
"PickerActionClearSearch": "hapus pencarian",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Disimpan untuk",
"FleetRosterLayerWins": "aktif",
"FleetRosterLayerIgnored": "salinan diabaikan",
- "FleetReadyNotice": "Pod siap · /pod membuka peran · /pod setup menyetel model anggota",
+ "FleetReadyNotice": "Pod siap · /fleet membuka peran · /fleet setup menyetel model anggota",
"FleetProfileIdentityVerifyFailed": "Tidak dapat memverifikasi identitas profil yang ada ({error}); perbaiki file yang disebutkan sebelum menyimpan.",
"FleetProfileIdConflict": "Id profil `{id}` sudah dipakai oleh {path}; buat ulang draf dengan peran berbeda atau hapus file lama dulu.",
"FleetProfileProviderUnconfigured": "Profil mengunci provider `{provider}` yang belum punya kredensial terkonfigurasi ({env}); atur di /provider sebelum menyimpan.",
+ "FleetModelAdded": "{route} ditambahkan ke fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} ditambahkan sebagai {roles} ke fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (fleet pribadi baru, kini dipilih)",
+ "FleetModelAddedSelectedNote": " (kini dipilih)",
+ "FleetModelRemoved": "{route} dihapus dari fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) dihapus dari fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} tetap di fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "ini model Anda saat ini (rute operator fleet); ganti model atau gunakan /fleet save untuk mengubahnya",
+ "FleetModelReasonAlreadyPresent": "sudah ada di fleet",
+ "FleetModelErrorNeedsRoute": "model fleet memerlukan id provider dan id model",
+ "FleetModelErrorNoSelection": "tidak ada fleet yang dipilih; fleet Anda hanya model sesi",
+ "FleetModelErrorOperatorRoute": "{route} adalah rute operator fleet `{fleet}`; ubah dengan /fleet save, bukan remove",
+ "FleetModelErrorNotInFleet": "{route} tidak ada di fleet `{fleet}`",
+ "FleetModelsEmpty": "Fleet Anda hanya model sesi. Tambahkan satu: /fleet add [role…] (atau ⇧F pada baris di /model).",
+ "FleetModelsHeader": "Fleet Anda `{fleet}` ({count} model)",
+ "FleetModelsBroken": "Fleet yang dipilih tidak dapat dimuat: {error}. Perbaiki berkasnya atau pilih yang lain dengan /fleet fleets.",
+ "FleetModelsFooter": "Tambah: /fleet add [role…] · Hapus: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} per M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "alat",
+ "FleetAddUsage": "Penggunaan: /fleet add [role…] — mis. /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` bukan provider yang dikonfigurasi. Konfigurasikan di ~/.codewhale/config.toml atau beralih ke sana dengan /provider sebelum menambahkannya ke fleet.",
+ "FleetAddModelNotServed": "{provider} tidak menyediakan `{model}` di katalog saat ini; jalankan /models untuk melihat yang tersedia, atau /fleet add dengan id persis yang tercantum.",
+ "FleetAddFailed": "Tidak dapat menambahkan ke fleet: {error}",
+ "FleetRemoveUsage": "Penggunaan: /fleet remove ",
+ "FleetRemoveFailed": "Tidak dapat menghapus dari fleet: {error}",
+ "FleetToggleFailed": "Tidak dapat memperbarui fleet: {error}",
"FleetDestStepTitle": "Di mana profil ini harus disimpan?",
"FleetDestStepSubtitle": "Tidak ada yang ditulis sampai Anda mengonfirmasi di langkah terakhir.",
"FleetDestProjectLabel": "Proyek ini",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review memblokir '{tool}' (kebijakan deterministik): {reason}",
"AutoReviewReceiptHeld": "Auto-Review menahan '{tool}' tanpa berhenti; ditolak (perlu manusia — beralih ke Ask)",
"FooterHintEscInterrupt": "Esc untuk menghentikan",
+ "PostureHintEnterAgain": "{enter} lagi untuk kirim sekarang · {steer} mengarahkan",
"PermissionsPostureHeader": "Akses saat ini: {posture}",
"PermissionsPostureAsk": "Ask: panggilan tool yang mengubah wewenang, biaya, cakupan, atau hasil membuka prompt; panggilan hanya-baca yang terbukti aman berjalan tanpa prompt. Aturan ask di atas selalu memaksa prompt.",
"PermissionsPostureAuto": "Auto-Review: tidak pernah membuka prompt. Kebijakan deterministik mengizinkan panggilan yang terbukti aman dan memblokir keras pekerjaan latar belakang yang bersifat publikasi atau destruktif; panggilan yang tidak dapat dibuktikan aman diteruskan ke penjaga model sekali jalan yang mengizinkan atau menolak dengan alasan (risiko tinggi atau kritis tidak pernah berjalan otomatis; tinjauan gagal berarti ditolak, fail closed). Penahanan yang memerlukan manusia ditolak, bukan disembunyikan. Setiap keputusan seperti itu ditulis ke transkrip sebagai catatan dan ke log audit.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell hanya-baca",
"AgentFocusPostureShellNone": "tanpa shell",
"GoalReceiptSet": "Tujuan ditetapkan: \"{objective}\" · /goal menampilkan progres · /goal pause atau /goal clear menghentikannya",
+ "GoalReceiptSetOperate": "◆ tujuan ditetapkan · Operate terus bekerja sampai terverifikasi · /goal untuk mengubah",
"GoalControlAccepted": "Kontrol tujuan disimpan; akan diterapkan pada batas aman berikutnya.",
"GoalControlRuntimeUnavailable": "Kontrol tujuan tersimpan, tetapi runtime tidak tersedia. Kontrol akan dipulihkan setelah dimulai ulang.",
"GoalStatusIdleHint": "tidak sedang berjalan — kirim pesan atau /goal resume untuk melanjutkan",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Memblokir setiap alat yang memerlukan persetujuan.",
"ConfigChoiceDetailModeAgent": "Mulai dalam keadaan siap berkolaborasi dan memakai alat.",
"ConfigChoiceDetailModePlan": "Mulai di ruang perencanaan hanya-baca.",
- "ConfigChoiceDetailModeOperate": "Mulai sebagai koordinator yang mendelegasikan kerja ke worker terbatas.",
+ "ConfigChoiceDetailModeOperate": "Operate mengubah prompt Anda menjadi tujuan dan mengerjakannya secara paralel: worker latar belakang untuk alur yang bisa dipisah, diverifikasi sebelum berhenti.",
"ConfigChoiceDetailPlacementTop": "Menampilkan Tugas, Daftar tugas, dan Worker di atas transkrip.",
"ConfigChoiceDetailPlacementLeft": "Menampilkan Tugas, Daftar tugas, dan Worker di bilah samping kiri saat terminal cukup lebar.",
"ConfigChoiceDetailPlacementRight": "Menampilkan Tugas, Daftar tugas, dan Worker di bilah samping kanan saat terminal cukup lebar.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "membuka plugin · percayai, aktifkan, atau diagnosis",
"ConfigHintMcpConfigPath": "jalur ke mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 memblokir agen anak; bawaan 3 (sumbu yang sama dengan sub-agen); batas 8",
- "ConfigHintFeatureSubagents": "status bendera fitur hanya-baca; /pod setup adalah jalur untuk pengguna",
+ "ConfigHintFeatureSubagents": "status bendera fitur hanya-baca; /fleet setup adalah jalur untuk pengguna",
"ConfigHintFeatureWebSearch": "status bendera fitur hanya-baca untuk alat pencarian web",
"ConfigHintFeatureApplyPatch": "status bendera fitur hanya-baca untuk alat penyuntingan patch",
"ConfigHintFeatureMcp": "status bendera fitur hanya-baca untuk alat MCP",
"ConfigHintFeatureExecPolicy": "status bendera fitur hanya-baca untuk alat kebijakan eksekusi",
"ConfigHintFeatureVisionModel": "bendera fitur beta untuk dukungan gambar visi/model",
"ConfigHintGoalCommand": "/goal mengatur tujuan, anggaran, dan status konteks Kerja",
- "ConfigHintWorkflow": "/workflow menjalankan operasi terskrip dengan kartu jalan penyebaran/penggabungan"
+ "ConfigHintWorkflow": "/workflow menjalankan operasi terskrip dengan kartu jalan penyebaran/penggabungan",
+ "LaunchMenuNewWorktree": "Worktree baru",
+ "LaunchMenuResume": "Lanjutkan sesi",
+ "LaunchMenuQuit": "Keluar",
+ "LaunchNoticeClaude": "Datang dari Claude Code? Lanjutkan sesi dengan ctrl+r",
+ "ReceiptSessionHooks": "hook {count}"
}
diff --git a/crates/tui/locales/ja.json b/crates/tui/locales/ja.json
index 29623da548..9c34e0ad74 100644
--- a/crates/tui/locales/ja.json
+++ b/crates/tui/locales/ja.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "マウスキャプチャ:オン。",
"ScreenModeMouseCaptureOff": "マウスキャプチャ:オフ。選択はターミナルが管理します。",
"ScreenModeUnchanged": "画面は変更されませんでした:{reason}。",
+ "ImageInputRejectedResent": "{model} は画像を受け付けないため、画像をテキストとして再送しました。画像を読むには image_ocr を使用してください",
"ConfigLabelShowThinking": "会話にモデル推論を表示",
"ConfigLabelThinkingHighlight": "推論の背景ハイライト",
"ConfigLabelShowToolDetails": "ツール詳細レベル",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "このワークスペースのワークフロー実行を表示(一覧・キャンセル)",
"CmdHotbarDescription": "Hotbar設定を開く",
"CmdSetupDescription": "憲法ファーストのセットアップを開く",
- "CmdSubagentsDescription": "/pod workers への互換ショートカット(現在のセッションのサブエージェント)",
+ "CmdSubagentsDescription": "/fleet workers への互換ショートカット(現在のセッションのサブエージェント)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "現在のシステムプロンプトを表示",
"CmdTaskDescription": "バックグラウンドタスクを管理",
@@ -649,7 +650,7 @@
"KbExternalEditor": "作成中の下書きを外部エディターで開く",
"KbLiveTranscript": "ライブ会話履歴オーバーレイを開く(自動追尾スクロール)",
"KbBacktrackMessage": "前のユーザーメッセージに戻る(左右でステップ、Enter で巻き戻し)",
- "KbCompleteCycleModes": "/command を補完、または入力欄が空のときモードを切り替え(Plan → Act → Operate)",
+ "KbCompleteCycleModes": "/command を補完、または入力欄が空のときモードを切り替え(Plan → Work → Operate)",
"KbCycleThinking": "現在のプロバイダーの推論強度を順に切り替え (Ctrl+T)",
"KbCyclePermissions": "アクセスを切り替え:確認 → 自動レビュー → フルアクセス(Shift+Tab)",
"KbJumpPlanAgentYolo": "ホットバースロットを起動",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Kimi Code メンバーシッププランのルート: `{route}`(プランコンソール: `{console}`、モデル k3 を使用)。Codewhale が Kimi CLI の資格情報を取り込むことはありません。",
"LinksTip": "ヒント: 表示されたプロバイダー用の環境変数を使うか、`codewhale auth set --provider ` でキーを保存してください。",
"SubagentsFetching": "現在のセッションのサブエージェントを取得中...",
- "SubagentsNoCurrentSessionPodWorkers": "現在のセッションに Podワーカーはいません。",
- "SubagentsCurrentSessionPodWorkersTitle": "現在のセッションのPodワーカー",
- "SubagentsCurrentSessionPodWorkerRoles": "サブエージェントの役割は、現在のセッションのPodワーカーの役割です。",
- "SubagentsCurrentSessionPodWorkersStatus": "現在のセッションのPodワーカー: 合計{count}",
- "SubagentsEmptyGuidance": "/pod で役割と起動方針を設定します。",
+ "SubagentsNoCurrentSessionPodWorkers": "現在のセッションに 艦隊ワーカーはいません。",
+ "SubagentsCurrentSessionPodWorkersTitle": "現在のセッションの艦隊ワーカー",
+ "SubagentsCurrentSessionPodWorkerRoles": "サブエージェントの役割は、現在のセッションの艦隊ワーカーの役割です。",
+ "SubagentsCurrentSessionPodWorkersStatus": "現在のセッションの艦隊ワーカー: 合計{count}",
+ "SubagentsEmptyGuidance": "/fleet で役割と起動方針を設定します。",
"SubagentsStatusRunning": "実行中",
"SubagentsStatusCompleted": "完了",
"SubagentsStatusInterrupted": "中断",
@@ -735,7 +736,7 @@
"HomeHistory": "履歴:",
"HomeTokens": "トークン:",
"HomeQueued": "キュー:",
- "HomeSubagents": "現在のセッションのPodワーカー:",
+ "HomeSubagents": "現在のセッションの艦隊ワーカー:",
"HomeSkill": "スキル:",
"HomeQuickActions": "クイックアクション",
"HomeQuickLinks": "/links - Codewhale、コミュニティ、プロバイダー",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - 設定を確認・変更",
"HomeQuickSettings": "/settings - 永続化された設定を表示",
"HomeQuickModel": "/model - モデルを切り替え・確認",
- "HomeQuickSubagents": "/pod workers - 現在のセッションのサブエージェント",
+ "HomeQuickSubagents": "/fleet workers - 現在のセッションのサブエージェント",
"HomeQuickTaskList": "/task list - バックグラウンドタスクキューを表示",
"HomeQuickHelp": "/help - ヘルプを表示",
"HomeQuickWorkspace": "/workspace - フォルダや worktree を切り替え",
@@ -1119,7 +1120,7 @@
"ToolFamilyPatch": "パッチ",
"ToolFamilyRun": "実行",
"ToolFamilyFind": "検索",
- "ToolFamilyDelegate": "委任",
+ "ToolFamilyDelegate": "エージェント",
"ToolFamilyFanout": "ファンアウト",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "検証",
@@ -1229,19 +1230,14 @@
"HotbarActionModeOperateName": "Operate モード",
"HotbarActionModeOperateDescription": "Pod を並列で動かします。",
"HomeOperateModeTip": "Operate — Pod を並列で動かす",
- "HomeOperateModeFleetTip": " 組み込みロールはこのセッションのモデルを使用。/pod setup でカスタマイズ",
+ "HomeOperateModeFleetTip": " 組み込みロールはこのセッションのモデルを使用。/fleet setup でカスタマイズ",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "Pod を並列で動かします",
+ "AppModeOperateHint": "プロンプトをゴールに変え、並列ワーカーで検証しながら進めます",
"HelpSubtitle": "概念・コマンド・キーバインド",
"CommandPaletteTitle": "コマンド",
"CommandPaletteSubtitle": "操作を見つけて実行",
"ConfigSubtitle": "設定を先に。生キーは高度な詳細へ",
"LaunchStartTitle": "ここから始める",
- "LaunchMenuConnect": "モデルを接続",
- "LaunchMenuWork": "作業 · 現在のフォルダー",
- "LaunchMenuChat": "チャット · 読み取り専用",
- "LaunchMenuTheme": "テーマ",
- "LaunchMenuHelp": "ヘルプ",
"LaunchWorkDescription": "このフォルダーでローカルツールを使います。変更は承認ポリシーに従います。",
"LaunchChatDescription": "会話と計画のみ。ファイルは変更しません。",
"LaunchWorkspaceGitReady": "ワークスペース · {name} · Git ワークスペース",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "フォルダー",
"LaunchProviderConfiguredShort": "プロバイダー設定済み",
"LaunchProviderSetupShort": "プロバイダー設定",
- "LaunchMenuNewSession": "新規セッション",
- "LaunchMenuNewWorktree": "新規ワークツリー",
- "LaunchMenuWorktreeCompact": "ワークツリー",
- "LaunchMenuChatCompact": "チャット",
- "LaunchMenuResumeSession": "セッションを再開",
"LaunchMenuChangelog": "更新履歴",
- "LaunchMenuQuit": "終了",
- "LaunchMenuUnavailable": "利用不可",
- "LaunchMenuSavedCount": "{count} 件保存済み",
"LaunchWorktreePrompt": "ブランチ/ワークツリーに名前を付けるか、Enter を押して自動命名にします。",
"LaunchWorktreeNeedsGit": "新規ワークツリーには Git リポジトリが必要です。",
"LaunchWorktreeNameLabel": "ワークツリー名",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "ワークツリーの作成に失敗: {error}",
"LaunchNoSavedSessions": "このワークスペースには保存済みセッションがありません。",
"LaunchComposerHint": "Enter 送信 · Shift+Enter 改行 · Esc 戻る",
- "LaunchComposerFocusHint": "Tab で入力",
+ "LaunchNoModelConnected": "モデル未接続",
+ "LaunchRunCommand": "{command} を実行",
+ "LaunchMcpConnectedOne": "MCP サーバー 1 台接続済み",
+ "LaunchMcpConnectedMany": "MCP サーバー {count} 台接続済み",
+ "LaunchMcpNeedsSignInOne": "1 台はサインインが必要",
+ "LaunchMcpNeedsSignInMany": "{count} 台はサインインが必要",
"PhaseIdle": "待機",
"PhaseDraft": "下書き",
"PhaseWorking": "処理中",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "出力",
"FooterHintContext": "コンテキスト",
"InfoLineHelp": "ヘルプ",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "クジラ",
"InfoLineAutomation": "自動化",
"InfoLineNotConnected": "未接続",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "適用",
"PickerActionSetStartupDefault": "起動時の既定値に設定",
"PickerActionPin": "ピン留め",
+ "PickerActionFleet": "フリート",
"PickerActionCancel": "キャンセル",
"PickerActionClear": "クリア",
"PickerActionClearSearch": "検索をクリア",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "保存先",
"FleetRosterLayerWins": "有効",
"FleetRosterLayerIgnored": "無視されたコピー",
- "FleetReadyNotice": "Pod の準備完了 · /pod でロールを開く · /pod setup でメンバーのモデルを調整",
+ "FleetReadyNotice": "Pod の準備完了 · /fleet でロールを開く · /fleet setup でメンバーのモデルを調整",
"FleetProfileIdentityVerifyFailed": "既存プロファイルの識別情報を確認できません({error})。記載のファイルを修正してから保存してください。",
"FleetProfileIdConflict": "プロファイル id `{id}` は {path} で既に使用されています。別のロールで作り直すか、先に古いファイルを削除してください。",
"FleetProfileProviderUnconfigured": "プロファイルはプロバイダー `{provider}` を指定していますが、認証情報が未設定です({env})。保存する前に /provider で設定してください。",
+ "FleetModelAdded": "{route} を fleet `{fleet}` に追加しました",
+ "FleetModelAddedAs": "{route} を {roles} として fleet `{fleet}` に追加しました",
+ "FleetModelAddedCreatedNote": "(新しい個人 fleet、選択済み)",
+ "FleetModelAddedSelectedNote": "(選択済み)",
+ "FleetModelRemoved": "{route} を fleet `{fleet}` から削除しました",
+ "FleetModelRemovedRoles": "{route}({roles})を fleet `{fleet}` から削除しました",
+ "FleetModelUnchanged": "{route} は fleet `{fleet}` に残ります: {reason}",
+ "FleetModelReasonOperatorRoute": "これは現在のモデル(fleet のオペレーター経路)です。モデルを切り替えるか /fleet save で変更してください",
+ "FleetModelReasonAlreadyPresent": "すでに fleet に含まれています",
+ "FleetModelErrorNeedsRoute": "fleet のモデルにはプロバイダー ID とモデル ID の両方が必要です",
+ "FleetModelErrorNoSelection": "fleet が選択されていません。あなたの fleet はセッションのモデルのみです",
+ "FleetModelErrorOperatorRoute": "{route} は fleet `{fleet}` のオペレーター経路です。削除ではなく /fleet save で変更してください",
+ "FleetModelErrorNotInFleet": "{route} は fleet `{fleet}` に含まれていません",
+ "FleetModelsEmpty": "あなたの fleet はセッションのモデルのみです。追加: /fleet add [role…](または /model の行で ⇧F)。",
+ "FleetModelsHeader": "あなたの fleet `{fleet}`({count} モデル)",
+ "FleetModelsBroken": "選択中の fleet を読み込めません: {error}。ファイルを修正するか /fleet fleets で別の fleet を選んでください。",
+ "FleetModelsFooter": "追加: /fleet add [role…] · 削除: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} / 100万トークン",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "ツール",
+ "FleetAddUsage": "使い方: /fleet add [role…] — 例: /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` は設定済みのプロバイダーではありません。fleet に追加する前に ~/.codewhale/config.toml で設定するか、/provider で切り替えてください。",
+ "FleetAddModelNotServed": "{provider} は現在のカタログで `{model}` を提供していません。/models で提供モデルを確認するか、表示される正確な ID で /fleet add してください。",
+ "FleetAddFailed": "fleet に追加できませんでした: {error}",
+ "FleetRemoveUsage": "使い方: /fleet remove ",
+ "FleetRemoveFailed": "fleet から削除できませんでした: {error}",
+ "FleetToggleFailed": "fleet を更新できませんでした: {error}",
"FleetDestStepTitle": "このプロファイルをどこに保存しますか?",
"FleetDestStepSubtitle": "最後のステップで確定するまで何も書き込まれません。",
"FleetDestProjectLabel": "このプロジェクト",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review が '{tool}' をブロックしました(決定論的ポリシー): {reason}",
"AutoReviewReceiptHeld": "Auto-Review は一時停止せずに '{tool}' を保留し、拒否しました(人の判断が必要 — Ask に切り替え)",
"FooterHintEscInterrupt": "Esc で中断",
+ "PostureHintEnterAgain": "もう一度 {enter} で今すぐ送信 · {steer} で割り込み",
"PermissionsPostureHeader": "現在のアクセス: {posture}",
"PermissionsPostureAsk": "Ask: 権限・コスト・範囲・結果を変える tool 呼び出しはプロンプトを開きます。安全と証明された読み取り専用の呼び出しはプロンプトなしで実行されます。上記の ask ルールは常にプロンプトを強制します。",
"PermissionsPostureAuto": "Auto-Review: プロンプトは開きません。決定論的ポリシーが安全と証明された呼び出しを許可し、公開系や破壊的なバックグラウンド作業をハードブロックします。安全と証明できない呼び出しは一回限りのモデルガーディアンに送られ、理由付きで許可または拒否されます(高・重大リスクは自動実行されません。審査失敗はフェイルクローズで拒否)。人の判断が必要な保留は隠されず拒否されます。各判断はトランスクリプトのノートと監査ログに記録されます。",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "読み取り専用シェル",
"AgentFocusPostureShellNone": "シェル不可",
"GoalReceiptSet": "目標を設定: 「{objective}」 · /goal で進捗表示 · /goal pause または /goal clear で停止",
+ "GoalReceiptSetOperate": "◆ 目標を設定 · Operate は検証されるまで作業を続けます · 編集は /goal",
"GoalControlAccepted": "目標操作を保存しました。次の安全な区切りで適用します。",
"GoalControlRuntimeUnavailable": "目標操作は保存されていますが、ランタイムを利用できません。再起動後に復元します。",
"GoalStatusIdleHint": "現在は実行中ではありません — メッセージを送るか /goal resume で続行",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "承認が必要なツールをすべてブロックします。",
"ConfigChoiceDetailModeAgent": "ツールを使って協働できる状態で開始します。",
"ConfigChoiceDetailModePlan": "読み取り専用の計画ワークスペースで開始します。",
- "ConfigChoiceDetailModeOperate": "境界付きワーカーに作業を委任するコーディネーターとして開始します。",
+ "ConfigChoiceDetailModeOperate": "Operate はプロンプトを目標にして並列で進めます。分離できる流れはバックグラウンドワーカーに任せ、停止前に検証します。",
"ConfigChoiceDetailPlacementTop": "タスク・To-do・ワーカーをトランスクリプトの上に表示します。",
"ConfigChoiceDetailPlacementLeft": "端末が十分に広いとき、タスク・To-do・ワーカーを左サイドバーに表示します。",
"ConfigChoiceDetailPlacementRight": "端末が十分に広いとき、タスク・To-do・ワーカーを右サイドバーに表示します。",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "プラグインを開く · 信頼・有効化・診断",
"ConfigHintMcpConfigPath": "mcp.json へのパス",
"ConfigHintFleetMaxSpawnDepth": "0 で子エージェントを禁止。既定 3 (サブエージェントと同じ軸)。上限 8",
- "ConfigHintFeatureSubagents": "読み取り専用の機能フラグ状態。/pod setup がユーザー向けの経路です",
+ "ConfigHintFeatureSubagents": "読み取り専用の機能フラグ状態。/fleet setup がユーザー向けの経路です",
"ConfigHintFeatureWebSearch": "Web 検索ツールの読み取り専用の機能フラグ状態",
"ConfigHintFeatureApplyPatch": "パッチ編集ツールの読み取り専用の機能フラグ状態",
"ConfigHintFeatureMcp": "MCP ツールの読み取り専用の機能フラグ状態",
"ConfigHintFeatureExecPolicy": "実行ポリシーツールの読み取り専用の機能フラグ状態",
"ConfigHintFeatureVisionModel": "ビジョン/モデル画像サポートのベータ機能フラグ",
"ConfigHintGoalCommand": "/goal は目標・予算・作業コンテキストの状態を設定します",
- "ConfigHintWorkflow": "/workflow はファンアウト/ファンインの実行カード付きでスクリプト化した操作を実行します"
+ "ConfigHintWorkflow": "/workflow はファンアウト/ファンインの実行カード付きでスクリプト化した操作を実行します",
+ "LaunchMenuNewWorktree": "新しいワークツリー",
+ "LaunchMenuResume": "セッションを再開",
+ "LaunchMenuQuit": "終了",
+ "LaunchNoticeClaude": "Claude Code からのお乗り換えですか?ctrl+r でセッションを再開できます",
+ "ReceiptSessionHooks": "フック {count}"
}
diff --git a/crates/tui/locales/ko.json b/crates/tui/locales/ko.json
index 8b1926626a..b2f9a9cc8a 100644
--- a/crates/tui/locales/ko.json
+++ b/crates/tui/locales/ko.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "마우스 캡처 켜짐.",
"ScreenModeMouseCaptureOff": "마우스 캡처 꺼짐; 선택은 터미널이 담당합니다.",
"ScreenModeUnchanged": "화면 변경 없음: {reason}.",
+ "ImageInputRejectedResent": "{model}은(는) 이미지를 지원하지 않아 텍스트로 다시 보냈습니다. image_ocr를 사용해 읽으세요",
"ConfigLabelShowThinking": "채팅에 모델 추론 표시",
"ConfigLabelThinkingHighlight": "추론 배경 강조",
"ConfigLabelShowToolDetails": "도구 세부 수준",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "이 작업 공간의 워크플로 실행 표시 (목록, 취소)",
"CmdHotbarDescription": "핫바 설정을 엽니다",
"CmdSetupDescription": "헌법 우선 설정을 엽니다",
- "CmdSubagentsDescription": "/pod workers용 호환 단축 명령 (현재 세션의 하위 에이전트)",
+ "CmdSubagentsDescription": "/fleet workers용 호환 단축 명령 (현재 세션의 하위 에이전트)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "현재 시스템 프롬프트를 표시합니다",
"CmdTaskDescription": "백그라운드 작업을 관리합니다",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Kimi Code 멤버십 플랜 경로: `{route}` (플랜 콘솔: `{console}`; 모델 k3 사용). Codewhale은 Kimi CLI 자격 증명을 절대 가져오지 않습니다.",
"LinksTip": "팁: 프로바이더에 표시된 환경 변수를 사용하거나, `codewhale auth set --provider `로 키를 저장하세요.",
"SubagentsFetching": "현재 세션의 하위 에이전트를 가져오는 중...",
- "SubagentsNoCurrentSessionPodWorkers": "현재 세션에 Pod 워커가 없습니다.",
- "SubagentsCurrentSessionPodWorkersTitle": "현재 세션의 Pod 워커",
- "SubagentsCurrentSessionPodWorkerRoles": "하위 에이전트 역할은 현재 세션의 Pod 워커 역할입니다.",
- "SubagentsCurrentSessionPodWorkersStatus": "현재 세션의 Pod 워커: 총 {count}명",
- "SubagentsEmptyGuidance": "/pod에서 역할과 시작 설정을 구성하세요.",
+ "SubagentsNoCurrentSessionPodWorkers": "현재 세션에 플릿 워커가 없습니다.",
+ "SubagentsCurrentSessionPodWorkersTitle": "현재 세션의 플릿 워커",
+ "SubagentsCurrentSessionPodWorkerRoles": "하위 에이전트 역할은 현재 세션의 플릿 워커 역할입니다.",
+ "SubagentsCurrentSessionPodWorkersStatus": "현재 세션의 플릿 워커: 총 {count}명",
+ "SubagentsEmptyGuidance": "/fleet에서 역할과 시작 설정을 구성하세요.",
"SubagentsStatusRunning": "실행 중",
"SubagentsStatusCompleted": "완료됨",
"SubagentsStatusInterrupted": "중단됨",
@@ -735,7 +736,7 @@
"HomeHistory": "기록:",
"HomeTokens": "토큰:",
"HomeQueued": "대기 중:",
- "HomeSubagents": "현재 세션의 Pod 워커:",
+ "HomeSubagents": "현재 세션의 플릿 워커:",
"HomeSkill": "스킬:",
"HomeQuickActions": "빠른 작업",
"HomeQuickLinks": "/links - Codewhale, 커뮤니티 및 프로바이더",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - 설정 확인 및 변경",
"HomeQuickSettings": "/settings - 영구 설정 표시",
"HomeQuickModel": "/model - 모델 전환 또는 확인",
- "HomeQuickSubagents": "/pod workers - 현재 세션의 하위 에이전트",
+ "HomeQuickSubagents": "/fleet workers - 현재 세션의 하위 에이전트",
"HomeQuickTaskList": "/task list - 백그라운드 작업 대기열 표시",
"HomeQuickHelp": "/help - 도움말 표시",
"HomeQuickWorkspace": "/workspace - 폴더 또는 worktree 전환",
@@ -1011,7 +1012,7 @@
"AppModeAutoHint": "셸 사용 가능, 자동 위험도 검토 적용",
"AppModePlanHint": "먼저 읽기 전용으로 조사 — 실행 전에 계획을 제시합니다",
"AppModeYoloHint": "호환성 유지용 — 실행 + 전체 권한이며 실제로 표시되는 모드는 아닙니다",
- "AppModeOperateHint": "Pod를 병렬로 작업에 투입합니다",
+ "AppModeOperateHint": "프롬프트를 목표로 바꿔 병렬 워커로 검증하며 진행합니다",
"VimModeNormal": "-- 일반 --",
"VimModeInsert": "-- 입력 --",
"VimModeVisual": "-- 비주얼 --",
@@ -1121,7 +1122,7 @@
"ToolFamilyPatch": "패치",
"ToolFamilyRun": "실행",
"ToolFamilyFind": "찾기",
- "ToolFamilyDelegate": "위임",
+ "ToolFamilyDelegate": "에이전트",
"ToolFamilyFanout": "팬아웃",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "검증",
@@ -1231,17 +1232,12 @@
"HotbarActionModeOperateName": "운영 모드",
"HotbarActionModeOperateDescription": "Pod를 병렬로 작업에 투입합니다.",
"HomeOperateModeTip": "Operate — Pod를 병렬로 작업에 투입",
- "HomeOperateModeFleetTip": " 내장 역할은 이 세션의 모델을 사용합니다. /pod setup에서 커스터마이징",
+ "HomeOperateModeFleetTip": " 내장 역할은 이 세션의 모델을 사용합니다. /fleet setup에서 커스터마이징",
"HelpSubtitle": "개념, 명령어, 단축키",
"CommandPaletteTitle": "명령",
"CommandPaletteSubtitle": "동작을 찾아 실행하세요",
"ConfigSubtitle": "설정을 우선 표시하고, 원시 키는 고급 정보에서 확인",
"LaunchStartTitle": "여기서 시작",
- "LaunchMenuConnect": "모델 연결",
- "LaunchMenuWork": "작업 · 현재 폴더",
- "LaunchMenuChat": "채팅 · 읽기 전용",
- "LaunchMenuTheme": "테마",
- "LaunchMenuHelp": "도움말",
"LaunchWorkDescription": "이 폴더에서 로컬 도구를 사용합니다. 변경은 승인 정책을 따릅니다.",
"LaunchChatDescription": "대화와 계획만 수행하며 파일은 변경하지 않습니다.",
"LaunchWorkspaceGitReady": "작업 공간 · {name} · Git 작업 공간",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "폴더",
"LaunchProviderConfiguredShort": "공급자 설정됨",
"LaunchProviderSetupShort": "공급자 설정",
- "LaunchMenuNewSession": "새 세션",
- "LaunchMenuNewWorktree": "새 워크트리",
- "LaunchMenuWorktreeCompact": "작업 트리",
- "LaunchMenuChatCompact": "채팅",
- "LaunchMenuResumeSession": "세션 재개",
"LaunchMenuChangelog": "변경 이력",
- "LaunchMenuQuit": "종료",
- "LaunchMenuUnavailable": "사용 불가",
- "LaunchMenuSavedCount": "{count}개 저장됨",
"LaunchWorktreePrompt": "브랜치/워크트리 이름을 입력하거나, Enter를 누르면 자동으로 이름이 지정됩니다.",
"LaunchWorktreeNeedsGit": "새 워크트리는 Git 저장소가 필요합니다.",
"LaunchWorktreeNameLabel": "워크트리 이름",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "워크트리 생성 실패: {error}",
"LaunchNoSavedSessions": "이 작업 공간에는 저장된 세션이 없습니다.",
"LaunchComposerHint": "Enter 전송 · Shift+Enter 새 줄 · Esc 뒤로",
- "LaunchComposerFocusHint": "Tab으로 입력",
+ "LaunchNoModelConnected": "연결된 모델 없음",
+ "LaunchRunCommand": "{command} 실행",
+ "LaunchMcpConnectedOne": "MCP 서버 1개 연결됨",
+ "LaunchMcpConnectedMany": "MCP 서버 {count}개 연결됨",
+ "LaunchMcpNeedsSignInOne": "1개는 로그인 필요",
+ "LaunchMcpNeedsSignInMany": "{count}개는 로그인 필요",
"PhaseIdle": "유휴",
"PhaseDraft": "초안",
"PhaseWorking": "작업 중",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "출력",
"FooterHintContext": "컨텍스트",
"InfoLineHelp": "도움말",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "고래",
"InfoLineAutomation": "자동화",
"InfoLineNotConnected": "연결 안 됨",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "적용",
"PickerActionSetStartupDefault": "시작 기본값으로 설정",
"PickerActionPin": "고정",
+ "PickerActionFleet": "플릿",
"PickerActionCancel": "취소",
"PickerActionClear": "지우기",
"PickerActionClearSearch": "검색 지우기",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "저장 위치",
"FleetRosterLayerWins": "활성",
"FleetRosterLayerIgnored": "무시된 사본",
- "FleetReadyNotice": "Pod 준비 완료 · /pod로 역할 열기 · /pod setup으로 멤버 모델 조정",
+ "FleetReadyNotice": "Pod 준비 완료 · /fleet로 역할 열기 · /fleet setup으로 멤버 모델 조정",
"FleetProfileIdentityVerifyFailed": "기존 프로필 식별 정보를 확인할 수 없습니다({error}). 표시된 파일을 수정한 뒤 저장하세요.",
"FleetProfileIdConflict": "프로필 id `{id}`는 이미 {path}에서 사용 중입니다. 다른 역할로 다시 작성하거나 이전 파일을 먼저 제거하세요.",
"FleetProfileProviderUnconfigured": "프로필이 자격 증명이 설정되지 않은 공급자 `{provider}`를 지정합니다({env}). 저장하기 전에 /provider에서 설정하세요.",
+ "FleetModelAdded": "{route}을(를) fleet `{fleet}`에 추가했습니다",
+ "FleetModelAddedAs": "{route}을(를) {roles} 역할로 fleet `{fleet}`에 추가했습니다",
+ "FleetModelAddedCreatedNote": " (새 개인 fleet, 현재 선택됨)",
+ "FleetModelAddedSelectedNote": " (현재 선택됨)",
+ "FleetModelRemoved": "{route}을(를) fleet `{fleet}`에서 제거했습니다",
+ "FleetModelRemovedRoles": "{route}({roles})을(를) fleet `{fleet}`에서 제거했습니다",
+ "FleetModelUnchanged": "{route}은(는) fleet `{fleet}`에 그대로 남습니다: {reason}",
+ "FleetModelReasonOperatorRoute": "현재 사용 중인 모델(fleet의 operator 경로)입니다. 모델을 전환하거나 /fleet save로 변경하세요",
+ "FleetModelReasonAlreadyPresent": "이미 fleet에 있습니다",
+ "FleetModelErrorNeedsRoute": "fleet 모델에는 provider ID와 모델 ID가 모두 필요합니다",
+ "FleetModelErrorNoSelection": "선택된 fleet이 없습니다. 현재 fleet은 세션 모델뿐입니다",
+ "FleetModelErrorOperatorRoute": "{route}은(는) fleet `{fleet}`의 operator 경로입니다. remove가 아니라 /fleet save로 변경하세요",
+ "FleetModelErrorNotInFleet": "{route}은(는) fleet `{fleet}`에 없습니다",
+ "FleetModelsEmpty": "현재 fleet은 세션 모델뿐입니다. 추가: /fleet add [role…] (또는 /model의 행에서 ⇧F).",
+ "FleetModelsHeader": "내 fleet `{fleet}` (모델 {count}개)",
+ "FleetModelsBroken": "선택된 fleet을 불러올 수 없습니다: {error}. 파일을 고치거나 /fleet fleets로 다른 fleet을 선택하세요.",
+ "FleetModelsFooter": "추가: /fleet add [role…] · 제거: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} / 100만 토큰",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "도구",
+ "FleetAddUsage": "사용법: /fleet add [role…] — 예: /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}`은(는) 설정된 provider가 아닙니다. fleet에 추가하기 전에 ~/.codewhale/config.toml에서 설정하거나 /provider로 전환하세요.",
+ "FleetAddModelNotServed": "{provider}은(는) 현재 카탈로그에서 `{model}`을(를) 제공하지 않습니다. /models로 제공 모델을 확인하거나, 표시된 정확한 ID로 /fleet add 하세요.",
+ "FleetAddFailed": "fleet에 추가하지 못했습니다: {error}",
+ "FleetRemoveUsage": "사용법: /fleet remove ",
+ "FleetRemoveFailed": "fleet에서 제거하지 못했습니다: {error}",
+ "FleetToggleFailed": "fleet을 업데이트하지 못했습니다: {error}",
"FleetDestStepTitle": "이 프로필을 어디에 저장할까요?",
"FleetDestStepSubtitle": "마지막 단계에서 확인하기 전까지는 아무것도 기록되지 않습니다.",
"FleetDestProjectLabel": "이 프로젝트",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review가 '{tool}'을(를) 차단했습니다 (결정적 정책): {reason}",
"AutoReviewReceiptHeld": "Auto-Review가 일시 중지 없이 '{tool}'을(를) 보류했습니다; 거부됨 (사람이 필요 — Ask로 전환)",
"FooterHintEscInterrupt": "Esc로 중단",
+ "PostureHintEnterAgain": "{enter} 한 번 더로 지금 보내기 · {steer}는 방향 지시",
"PermissionsPostureHeader": "현재 액세스: {posture}",
"PermissionsPostureAsk": "Ask: 권한, 비용, 범위 또는 결과를 바꾸는 tool 호출은 프롬프트를 엽니다. 안전이 입증된 읽기 전용 호출은 프롬프트 없이 실행됩니다. 위의 ask 규칙은 항상 프롬프트를 강제합니다.",
"PermissionsPostureAuto": "Auto-Review: 프롬프트를 열지 않습니다. 결정적 정책이 안전이 입증된 호출을 허용하고 게시성 또는 파괴적 백그라운드 작업을 강제 차단합니다. 안전을 입증할 수 없는 호출은 1회성 모델 가디언에게 넘어가 사유와 함께 허용 또는 거부됩니다(높음 또는 치명적 위험은 절대 자동 실행되지 않으며, 검토 실패 시 페일 클로즈로 거부). 사람이 필요한 보류는 숨기지 않고 거부합니다. 이런 결정은 모두 대화 기록에 노트로, 감사 로그에 기록됩니다.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "읽기 전용 셸",
"AgentFocusPostureShellNone": "셸 없음",
"GoalReceiptSet": "목표 설정: \"{objective}\" · /goal 로 진행 상황 확인 · /goal pause 또는 /goal clear 로 중지",
+ "GoalReceiptSetOperate": "◆ 목표 설정 · Operate는 검증될 때까지 계속 작업합니다 · 수정은 /goal",
"GoalControlAccepted": "목표 제어를 저장했습니다. 다음 안전한 경계에서 적용합니다.",
"GoalControlRuntimeUnavailable": "목표 제어는 저장되었지만 런타임을 사용할 수 없습니다. 다시 시작한 후 복원됩니다.",
"GoalStatusIdleHint": "지금은 실행 중이 아님 — 메시지를 보내거나 /goal resume 로 계속",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "승인이 필요한 모든 도구를 차단합니다.",
"ConfigChoiceDetailModeAgent": "협업하고 도구를 사용할 준비 상태로 시작합니다.",
"ConfigChoiceDetailModePlan": "읽기 전용 계획 작업 공간에서 시작합니다.",
- "ConfigChoiceDetailModeOperate": "제한된 작업자에게 작업을 위임하는 조정자로 시작합니다.",
+ "ConfigChoiceDetailModeOperate": "Operate는 프롬프트를 목표로 바꿔 병렬로 진행합니다. 분리 가능한 흐름은 백그라운드 작업자에게 맡기고, 멈추기 전에 검증합니다.",
"ConfigChoiceDetailPlacementTop": "작업, 할 일, 작업자를 대화 기록 위에 표시합니다.",
"ConfigChoiceDetailPlacementLeft": "터미널이 충분히 넓을 때 작업, 할 일, 작업자를 왼쪽 사이드바에 표시합니다.",
"ConfigChoiceDetailPlacementRight": "터미널이 충분히 넓을 때 작업, 할 일, 작업자를 오른쪽 사이드바에 표시합니다.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "플러그인 열기 · 신뢰, 활성화 또는 진단",
"ConfigHintMcpConfigPath": "mcp.json 경로",
"ConfigHintFleetMaxSpawnDepth": "0은 하위 에이전트 차단. 기본 3 (하위 에이전트와 같은 축). 최대 8",
- "ConfigHintFeatureSubagents": "읽기 전용 기능 플래그 상태. /pod setup이 사용자용 경로",
+ "ConfigHintFeatureSubagents": "읽기 전용 기능 플래그 상태. /fleet setup이 사용자용 경로",
"ConfigHintFeatureWebSearch": "웹 검색 도구의 읽기 전용 기능 플래그 상태",
"ConfigHintFeatureApplyPatch": "패치 편집 도구의 읽기 전용 기능 플래그 상태",
"ConfigHintFeatureMcp": "MCP 도구의 읽기 전용 기능 플래그 상태",
"ConfigHintFeatureExecPolicy": "실행 정책 도구의 읽기 전용 기능 플래그 상태",
"ConfigHintFeatureVisionModel": "비전/모델 이미지 지원의 베타 기능 플래그",
"ConfigHintGoalCommand": "/goal은 목표, 예산, 작업 컨텍스트 상태를 설정",
- "ConfigHintWorkflow": "/workflow는 팬아웃/팬인 실행 카드와 함께 스크립트화된 작업을 실행"
+ "ConfigHintWorkflow": "/workflow는 팬아웃/팬인 실행 카드와 함께 스크립트화된 작업을 실행",
+ "LaunchMenuNewWorktree": "새 워크트리",
+ "LaunchMenuResume": "세션 재개",
+ "LaunchMenuQuit": "종료",
+ "LaunchNoticeClaude": "Claude Code에서 오셨나요? ctrl+r로 세션을 다시 시작하세요",
+ "ReceiptSessionHooks": "훅 {count}"
}
diff --git a/crates/tui/locales/pt-BR.json b/crates/tui/locales/pt-BR.json
index 7d2f27101c..f3aa2e3a1e 100644
--- a/crates/tui/locales/pt-BR.json
+++ b/crates/tui/locales/pt-BR.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Captura do mouse ativada.",
"ScreenModeMouseCaptureOff": "Captura do mouse desativada; o terminal controla a seleção.",
"ScreenModeUnchanged": "Tela inalterada: {reason}.",
+ "ImageInputRejectedResent": "{model} não aceita imagens — reenviado como texto; use image_ocr para lê-las",
"ConfigLabelShowThinking": "Raciocínio do modelo no chat",
"ConfigLabelThinkingHighlight": "Destaque do fundo do raciocínio",
"ConfigLabelShowToolDetails": "Nível de detalhe das ferramentas",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "Mostrar as execuções de fluxo de trabalho deste workspace (listar, cancelar)",
"CmdHotbarDescription": "Abrir configuração da Hotbar",
"CmdSetupDescription": "Abrir configuração com constituição primeiro",
- "CmdSubagentsDescription": "Atalho de compatibilidade para /pod workers (subagentes da sessão atual)",
+ "CmdSubagentsDescription": "Atalho de compatibilidade para /fleet workers (subagentes da sessão atual)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Exibir o prompt de sistema atual",
"CmdTaskDescription": "Gerenciar tarefas em segundo plano",
@@ -649,7 +650,7 @@
"KbExternalEditor": "Abrir rascunho no editor externo",
"KbLiveTranscript": "Abrir sobreposição de transcrição ao vivo (auto-scroll)",
"KbBacktrackMessage": "Retroceder para mensagem anterior do usuário (esquerda/direita, Enter para rebobinar)",
- "KbCompleteCycleModes": "Completar /command ou ciclar modos quando o compositor estiver vazio (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Completar /command ou ciclar modos quando o compositor estiver vazio (Plan → Work → Operate)",
"KbCycleThinking": "Alternar o esforço de raciocínio do provedor ativo (Ctrl+T)",
"KbCyclePermissions": "Alternar acesso: Perguntar → Revisão automática → Acesso total (Shift+Tab)",
"KbJumpPlanAgentYolo": "Acionar slots da hotbar",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Rota do plano de assinatura Kimi Code: `{route}` (console do plano: `{console}`; use o modelo k3). O Codewhale nunca importa credenciais do Kimi CLI.",
"LinksTip": "Dica: use a variável de ambiente mostrada para seu provedor ou salve a chave com `codewhale auth set --provider `.",
"SubagentsFetching": "Buscando subagentes da sessão atual...",
- "SubagentsNoCurrentSessionPodWorkers": "Não há trabalhadores do Pod na sessão atual.",
- "SubagentsCurrentSessionPodWorkersTitle": "Trabalhadores do Pod da sessão atual",
- "SubagentsCurrentSessionPodWorkerRoles": "As funções de subagentes são funções de trabalhadores do Pod da sessão atual.",
- "SubagentsCurrentSessionPodWorkersStatus": "Trabalhadores do Pod da sessão atual: {count} no total",
- "SubagentsEmptyGuidance": "Configure as funções e a postura de lançamento com /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "Não há trabalhadores da frota na sessão atual.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Trabalhadores da frota da sessão atual",
+ "SubagentsCurrentSessionPodWorkerRoles": "As funções de subagentes são funções de trabalhadores da frota da sessão atual.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Trabalhadores da frota da sessão atual: {count} no total",
+ "SubagentsEmptyGuidance": "Configure as funções e a postura de lançamento com /fleet.",
"SubagentsStatusRunning": "Em execução",
"SubagentsStatusCompleted": "Concluído",
"SubagentsStatusInterrupted": "Interrompido",
@@ -735,7 +736,7 @@
"HomeHistory": "Histórico:",
"HomeTokens": "Tokens:",
"HomeQueued": "Enfileirado:",
- "HomeSubagents": "Workers do Pod da sessão atual:",
+ "HomeSubagents": "Workers da frota da sessão atual:",
"HomeSkill": "Skill:",
"HomeQuickActions": "Ações Rápidas",
"HomeQuickLinks": "/links - Codewhale, comunidade e provedores",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - Inspecionar e alterar configurações",
"HomeQuickSettings": "/settings - Exibir configurações persistentes",
"HomeQuickModel": "/model - Alternar ou visualizar modelo",
- "HomeQuickSubagents": "/pod workers - subagentes da sessão atual",
+ "HomeQuickSubagents": "/fleet workers - subagentes da sessão atual",
"HomeQuickTaskList": "/task list - Exibir fila de tarefas em segundo plano",
"HomeQuickHelp": "/help - Exibir ajuda",
"HomeQuickWorkspace": "/workspace - Trocar pastas ou worktrees",
@@ -1119,7 +1120,7 @@
"ToolFamilyPatch": "corrigir",
"ToolFamilyRun": "executar",
"ToolFamilyFind": "buscar",
- "ToolFamilyDelegate": "delegar",
+ "ToolFamilyDelegate": "agente",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "verificar",
@@ -1229,19 +1230,14 @@
"HotbarActionModeOperateName": "Modo Operate",
"HotbarActionModeOperateDescription": "Coloque seu Pod para trabalhar em paralelo.",
"HomeOperateModeTip": "Operate — coloque seu Pod para trabalhar em paralelo",
- "HomeOperateModeFleetTip": " Papéis integrados usam o modelo desta sessão; /pod setup os personaliza",
+ "HomeOperateModeFleetTip": " Papéis integrados usam o modelo desta sessão; /fleet setup os personaliza",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "Coloque seu Pod para trabalhar em paralelo",
+ "AppModeOperateHint": "Transforma seu prompt em meta: workers paralelos, verificação",
"HelpSubtitle": "Conceitos, comandos e atalhos",
"CommandPaletteTitle": "Comando",
"CommandPaletteSubtitle": "Encontre e execute uma ação",
"ConfigSubtitle": "Configurações primeiro; chaves brutas no detalhe avançado",
"LaunchStartTitle": "Comece aqui",
- "LaunchMenuConnect": "Conectar um modelo",
- "LaunchMenuWork": "Trabalho · pasta atual",
- "LaunchMenuChat": "Chat · somente leitura",
- "LaunchMenuTheme": "Tema",
- "LaunchMenuHelp": "Ajuda",
"LaunchWorkDescription": "Use esta pasta com ferramentas locais; as alterações seguem sua política de aprovação.",
"LaunchChatDescription": "Somente conversa e planejamento; nenhum arquivo é alterado.",
"LaunchWorkspaceGitReady": "Workspace · {name} · workspace Git",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "pasta",
"LaunchProviderConfiguredShort": "provedor configurado",
"LaunchProviderSetupShort": "configurar provedor",
- "LaunchMenuNewSession": "Nova sessão",
- "LaunchMenuNewWorktree": "Novo worktree",
- "LaunchMenuWorktreeCompact": "árvore",
- "LaunchMenuChatCompact": "conversa",
- "LaunchMenuResumeSession": "Retomar sessão",
"LaunchMenuChangelog": "Changelog",
- "LaunchMenuQuit": "Sair",
- "LaunchMenuUnavailable": "indisponível",
- "LaunchMenuSavedCount": "{count} salvas",
"LaunchWorktreePrompt": "Nomeie a branch/worktree ou pressione Enter para um nome automático.",
"LaunchWorktreeNeedsGit": "Novo worktree requer um repositório Git.",
"LaunchWorktreeNameLabel": "nome do worktree",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "Falha no worktree: {error}",
"LaunchNoSavedSessions": "Nenhuma sessão salva para este workspace.",
"LaunchComposerHint": "Enter envia · Shift+Enter nova linha · Esc volta",
- "LaunchComposerFocusHint": "Tab para digitar",
+ "LaunchNoModelConnected": "nenhum modelo conectado",
+ "LaunchRunCommand": "execute {command}",
+ "LaunchMcpConnectedOne": "1 servidor MCP conectado",
+ "LaunchMcpConnectedMany": "{count} servidores MCP conectados",
+ "LaunchMcpNeedsSignInOne": "1 precisa de login",
+ "LaunchMcpNeedsSignInMany": "{count} precisam de login",
"PhaseIdle": "ocioso",
"PhaseDraft": "rascunho",
"PhaseWorking": "trabalhando",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "saída",
"FooterHintContext": "contexto",
"InfoLineHelp": "ajuda",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "baleias",
"InfoLineAutomation": "automação",
"InfoLineNotConnected": "não conectado",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "aplicar",
"PickerActionSetStartupDefault": "definir como padrão de inicialização",
"PickerActionPin": "fixar",
+ "PickerActionFleet": "frota",
"PickerActionCancel": "cancelar",
"PickerActionClear": "limpar",
"PickerActionClearSearch": "limpar busca",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "Salvo em",
"FleetRosterLayerWins": "ativo",
"FleetRosterLayerIgnored": "cópia ignorada",
- "FleetReadyNotice": "Pod pronto · /pod abre papéis · /pod setup ajusta os modelos dos membros",
+ "FleetReadyNotice": "Pod pronto · /fleet abre papéis · /fleet setup ajusta os modelos dos membros",
"FleetProfileIdentityVerifyFailed": "Não foi possível verificar as identidades dos perfis existentes ({error}); corrija o arquivo indicado antes de salvar.",
"FleetProfileIdConflict": "O id de perfil `{id}` já está em uso por {path}; redija novamente com outro papel ou remova o arquivo antigo primeiro.",
"FleetProfileProviderUnconfigured": "O perfil fixa o provedor `{provider}`, que não tem credenciais configuradas ({env}); configure-o em /provider antes de salvar.",
+ "FleetModelAdded": "{route} adicionado ao fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} adicionado como {roles} ao fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (novo fleet pessoal, agora selecionado)",
+ "FleetModelAddedSelectedNote": " (agora selecionado)",
+ "FleetModelRemoved": "{route} removido do fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) removido do fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} permanece no fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "este é o seu modelo atual (a rota do operador do fleet); troque de modelo ou use /fleet save para alterá-la",
+ "FleetModelReasonAlreadyPresent": "já está no fleet",
+ "FleetModelErrorNeedsRoute": "um modelo do fleet precisa de um id de provider e de um id de modelo",
+ "FleetModelErrorNoSelection": "nenhum fleet está selecionado; seu fleet é apenas o modelo da sessão",
+ "FleetModelErrorOperatorRoute": "{route} é a rota do operador do fleet `{fleet}`; altere-a com /fleet save, não com remove",
+ "FleetModelErrorNotInFleet": "{route} não está no fleet `{fleet}`",
+ "FleetModelsEmpty": "Seu fleet é apenas o modelo da sessão. Adicione um: /fleet add [role…] (ou ⇧F em uma linha de /model).",
+ "FleetModelsHeader": "Seu fleet `{fleet}` ({count} modelos)",
+ "FleetModelsBroken": "Não foi possível carregar o fleet selecionado: {error}. Corrija o arquivo ou escolha outro com /fleet fleets.",
+ "FleetModelsFooter": "Adicionar: /fleet add [role…] · Remover: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} por M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "ferramentas",
+ "FleetAddUsage": "Uso: /fleet add [role…] — ex.: /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` não é um provider configurado. Configure-o em ~/.codewhale/config.toml ou mude para ele com /provider antes de adicioná-lo ao fleet.",
+ "FleetAddModelNotServed": "{provider} não serve `{model}` no catálogo atual; execute /models para ver o que ele serve, ou /fleet add com o id exato listado.",
+ "FleetAddFailed": "Não foi possível adicionar ao fleet: {error}",
+ "FleetRemoveUsage": "Uso: /fleet remove ",
+ "FleetRemoveFailed": "Não foi possível remover do fleet: {error}",
+ "FleetToggleFailed": "Não foi possível atualizar o fleet: {error}",
"FleetDestStepTitle": "Onde este perfil deve ficar?",
"FleetDestStepSubtitle": "Nada é gravado até você confirmar no último passo.",
"FleetDestProjectLabel": "Este projeto",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "O Auto-Review bloqueou '{tool}' (política determinística): {reason}",
"AutoReviewReceiptHeld": "O Auto-Review reteve '{tool}' sem pausar; negado (precisa de uma pessoa — mude para Ask)",
"FooterHintEscInterrupt": "Esc para interromper",
+ "PostureHintEnterAgain": "{enter} de novo para enviar agora · {steer} direciona",
"PermissionsPostureHeader": "Acesso agora: {posture}",
"PermissionsPostureAsk": "Ask: chamadas de tool que alteram autoridade, custo, escopo ou resultado abrem um prompt; chamadas somente leitura comprovadamente seguras rodam sem ele. As regras ask acima sempre forçam um prompt.",
"PermissionsPostureAuto": "Auto-Review: nunca abre um prompt. Uma política determinística permite chamadas comprovadamente seguras e bloqueia de forma rígida trabalho de publicação ou destrutivo em segundo plano; chamadas que não consegue provar seguras vão a um guardião de modelo de uma única rodada, que permite ou nega com um motivo declarado (risco alto ou crítico nunca roda automaticamente; uma revisão falha nega, fail closed). Retenções que exigem uma pessoa são negadas, não ocultadas. Cada decisão dessas é escrita na transcrição como nota e no log de auditoria.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell somente leitura",
"AgentFocusPostureShellNone": "sem shell",
"GoalReceiptSet": "Meta definida: \"{objective}\" · /goal mostra o progresso · /goal pause ou /goal clear interrompe",
+ "GoalReceiptSetOperate": "◆ meta definida · Operate continua trabalhando até ser verificada · /goal para editar",
"GoalControlAccepted": "Controle da meta salvo; será aplicado no próximo limite seguro.",
"GoalControlRuntimeUnavailable": "O controle da meta está salvo, mas o runtime não está disponível. Ele será restaurado após reiniciar.",
"GoalStatusIdleHint": "parada no momento — envie uma mensagem ou /goal resume para continuar",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Bloqueia toda ferramenta que exige aprovação.",
"ConfigChoiceDetailModeAgent": "Começa pronto para colaborar e usar ferramentas.",
"ConfigChoiceDetailModePlan": "Começa em um espaço de planejamento somente leitura.",
- "ConfigChoiceDetailModeOperate": "Começa como coordenador que delega trabalho a workers limitados.",
+ "ConfigChoiceDetailModeOperate": "Operate transforma seu pedido em uma meta e trabalha nela em paralelo: workers em segundo plano para fluxos separáveis, verificados antes de parar.",
"ConfigChoiceDetailPlacementTop": "Mostra Tarefas, A fazer e Workers acima da transcrição.",
"ConfigChoiceDetailPlacementLeft": "Mostra Tarefas, A fazer e Workers em uma barra lateral esquerda quando o terminal é largo o bastante.",
"ConfigChoiceDetailPlacementRight": "Mostra Tarefas, A fazer e Workers em uma barra lateral direita quando o terminal é largo o bastante.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "abre plugins · confiar, ativar ou diagnosticar",
"ConfigHintMcpConfigPath": "caminho para mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 bloqueia agentes filhos; 3 é o padrão (mesmo eixo dos subagentes); limite 8",
- "ConfigHintFeatureSubagents": "estado somente leitura do flag de recurso; /pod setup é o caminho para o usuário",
+ "ConfigHintFeatureSubagents": "estado somente leitura do flag de recurso; /fleet setup é o caminho para o usuário",
"ConfigHintFeatureWebSearch": "estado somente leitura do flag de recurso das ferramentas de busca na web",
"ConfigHintFeatureApplyPatch": "estado somente leitura do flag de recurso das ferramentas de edição por patch",
"ConfigHintFeatureMcp": "estado somente leitura do flag de recurso das ferramentas MCP",
"ConfigHintFeatureExecPolicy": "estado somente leitura do flag de recurso das ferramentas de política de execução",
"ConfigHintFeatureVisionModel": "flag beta para suporte a imagens de visão/modelo",
"ConfigHintGoalCommand": "/goal define objetivos, orçamentos e o status do contexto de Trabalho",
- "ConfigHintWorkflow": "/workflow executa operações roteirizadas com cartões de execução de distribuição/consolidação"
+ "ConfigHintWorkflow": "/workflow executa operações roteirizadas com cartões de execução de distribuição/consolidação",
+ "LaunchMenuNewWorktree": "Novo worktree",
+ "LaunchMenuResume": "Retomar sessão",
+ "LaunchMenuQuit": "Sair",
+ "LaunchNoticeClaude": "Vem do Claude Code? Retome sua sessão com ctrl+r",
+ "ReceiptSessionHooks": "hooks {count}"
}
diff --git a/crates/tui/locales/ru.json b/crates/tui/locales/ru.json
index 91109d5b5f..8963869513 100644
--- a/crates/tui/locales/ru.json
+++ b/crates/tui/locales/ru.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Захват мыши включён.",
"ScreenModeMouseCaptureOff": "Захват мыши выключен; выбором управляет терминал.",
"ScreenModeUnchanged": "Экран без изменений: {reason}.",
+ "ImageInputRejectedResent": "{model} не принимает изображения — они отправлены повторно как текст; используйте image_ocr для чтения",
"ConfigLabelShowThinking": "Рассуждения модели в чате",
"ConfigLabelThinkingHighlight": "Подсветка фона рассуждений",
"ConfigLabelShowToolDetails": "Детализация инструментов",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Показать запуски воркфлоу в этой рабочей области (список, отмена)",
"CmdHotbarDescription": "Открыть настройку Hotbar",
"CmdSetupDescription": "Открыть настройку, начиная с конституции",
- "CmdSubagentsDescription": "Совместимый ярлык для /pod workers (субагенты текущего сеанса)",
+ "CmdSubagentsDescription": "Совместимый ярлык для /fleet workers (субагенты текущего сеанса)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Показать текущий системный промпт",
"CmdTaskDescription": "Управление фоновыми задачами",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Открыть черновик во внешнем редакторе",
"KbLiveTranscript": "Открыть живую ленту поверх (Ctrl+Shift+T; автопрокрутка с привязкой к концу)",
"KbBacktrackMessage": "Вернуться к предыдущему сообщению пользователя (Left/Right — шаг, Enter — откат)",
- "KbCompleteCycleModes": "Дополнить /command или переключить режим при пустом поле ввода (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Дополнить /command или переключить режим при пустом поле ввода (Plan → Work → Operate)",
"KbCycleThinking": "Перебор уровня рассуждений активного провайдера (Ctrl+T)",
"KbCyclePermissions": "Сменить доступ: Спрашивать → Авто-проверка → Полный доступ (Shift+Tab)",
"KbJumpPlanAgentYolo": "Активировать слоты Hotbar",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Маршрут плана подписки Kimi Code: `{route}` (консоль плана: `{console}`; используйте модель k3). Codewhale никогда не импортирует учётные данные Kimi CLI.",
"LinksTip": "Совет: используйте переменную окружения вашего провайдера или сохраните ключ командой `codewhale auth set --provider `.",
"SubagentsFetching": "Получение субагентов текущего сеанса...",
- "SubagentsNoCurrentSessionPodWorkers": "В текущем сеансе нет воркеров Pod.",
- "SubagentsCurrentSessionPodWorkersTitle": "Воркеры Pod текущего сеанса",
- "SubagentsCurrentSessionPodWorkerRoles": "Роли субагентов — это роли воркеров Pod текущего сеанса.",
- "SubagentsCurrentSessionPodWorkersStatus": "Воркеры Pod текущего сеанса: всего {count}",
- "SubagentsEmptyGuidance": "Настройте роли и параметры запуска через /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "В текущем сеансе нет воркеров флота.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Воркеры флота текущего сеанса",
+ "SubagentsCurrentSessionPodWorkerRoles": "Роли субагентов — это роли воркеров флота текущего сеанса.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Воркеры флота текущего сеанса: всего {count}",
+ "SubagentsEmptyGuidance": "Настройте роли и параметры запуска через /fleet.",
"SubagentsStatusRunning": "Выполняется",
"SubagentsStatusCompleted": "Завершено",
"SubagentsStatusInterrupted": "Прервано",
@@ -718,7 +719,7 @@
"HomeHistory": "История:",
"HomeTokens": "Токены:",
"HomeQueued": "В очереди:",
- "HomeSubagents": "Воркеры Pod текущего сеанса:",
+ "HomeSubagents": "Воркеры флота текущего сеанса:",
"HomeSkill": "Навык:",
"HomeQuickActions": "Быстрые действия",
"HomeQuickLinks": "/links - ссылки на Codewhale, сообщество и провайдеров",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - просмотр и изменение настроек",
"HomeQuickSettings": "/settings - показать постоянные настройки",
"HomeQuickModel": "/model - сменить или показать модель",
- "HomeQuickSubagents": "/pod workers - субагенты текущего сеанса",
+ "HomeQuickSubagents": "/fleet workers - субагенты текущего сеанса",
"HomeQuickTaskList": "/task list - показать очередь фоновых задач",
"HomeQuickHelp": "/help - показать справку",
"HomeQuickWorkspace": "/workspace - Переключить папку или worktree",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell включён с автоматической проверкой рисков",
"AppModePlanHint": "Сначала исследование только для чтения — план до начала действий",
"AppModeYoloHint": "Только для совместимости — Act + Full Access, не видимый режим",
- "AppModeOperateHint": "Параллельная работа Pod",
+ "AppModeOperateHint": "Превращает запрос в цель: параллельные агенты, проверка",
"VimModeNormal": "-- ОБЫЧНЫЙ --",
"VimModeInsert": "-- ВСТАВКА --",
"VimModeVisual": "-- ВЫДЕЛЕНИЕ --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "патч",
"ToolFamilyRun": "запуск",
"ToolFamilyFind": "поиск",
- "ToolFamilyDelegate": "делегирование",
+ "ToolFamilyDelegate": "агент",
"ToolFamilyFanout": "распределение",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "проверка",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Режим Operate",
"HotbarActionModeOperateDescription": "Параллельная работа Pod.",
"HomeOperateModeTip": "Operate — параллельная работа Pod",
- "HomeOperateModeFleetTip": " Встроенные роли используют модель этой сессии; /pod setup настраивает их",
+ "HomeOperateModeFleetTip": " Встроенные роли используют модель этой сессии; /fleet setup настраивает их",
"HelpSubtitle": "Концепции, команды и сочетания клавиш",
"CommandPaletteTitle": "Команда",
"CommandPaletteSubtitle": "Найти и выполнить одно действие",
"ConfigSubtitle": "Сначала настройки; сырые ключи — в расширенных деталях",
"LaunchStartTitle": "Начните здесь",
- "LaunchMenuConnect": "Подключить модель",
- "LaunchMenuWork": "Работа · текущая папка",
- "LaunchMenuChat": "Чат · только чтение",
- "LaunchMenuTheme": "Тема",
- "LaunchMenuHelp": "Справка",
"LaunchWorkDescription": "Используйте локальные инструменты в этой папке; изменения подчиняются политике подтверждений.",
"LaunchChatDescription": "Только разговор и планирование; файлы не изменяются.",
"LaunchWorkspaceGitReady": "Рабочая область · {name} · область Git",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "папка",
"LaunchProviderConfiguredShort": "провайдер настроен",
"LaunchProviderSetupShort": "настройка провайдера",
- "LaunchMenuNewSession": "Новая сессия",
- "LaunchMenuNewWorktree": "Новый worktree",
- "LaunchMenuWorktreeCompact": "рабочее дерево",
- "LaunchMenuChatCompact": "беседа",
- "LaunchMenuResumeSession": "Возобновить сессию",
"LaunchMenuChangelog": "Список изменений",
- "LaunchMenuQuit": "Выход",
- "LaunchMenuUnavailable": "недоступно",
- "LaunchMenuSavedCount": "сохранено: {count}",
"LaunchWorktreePrompt": "Назовите ветку/worktree или нажмите Enter для автоматического имени.",
"LaunchWorktreeNeedsGit": "Новый worktree требует репозиторий Git.",
"LaunchWorktreeNameLabel": "имя worktree",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "Ошибка worktree: {error}",
"LaunchNoSavedSessions": "Нет сохранённых сессий для этой рабочей области.",
"LaunchComposerHint": "Enter — отправить · Shift+Enter — новая строка · Esc — назад",
- "LaunchComposerFocusHint": "Tab — ввести текст",
+ "LaunchNoModelConnected": "модель не подключена",
+ "LaunchRunCommand": "выполните {command}",
+ "LaunchMcpConnectedOne": "подключён 1 сервер MCP",
+ "LaunchMcpConnectedMany": "подключено серверов MCP: {count}",
+ "LaunchMcpNeedsSignInOne": "1 требует входа",
+ "LaunchMcpNeedsSignInMany": "{count} требуют входа",
"PhaseIdle": "простой",
"PhaseDraft": "черновик",
"PhaseWorking": "работа",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "вывод",
"FooterHintContext": "контекст",
"InfoLineHelp": "справка",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "киты",
"InfoLineAutomation": "автоматизация",
"InfoLineNotConnected": "не подключено",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "применить",
"PickerActionSetStartupDefault": "установить как значение по умолчанию при запуске",
"PickerActionPin": "закрепить",
+ "PickerActionFleet": "флот",
"PickerActionCancel": "отмена",
"PickerActionClear": "очистить",
"PickerActionClearSearch": "очистить поиск",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Сохранено для",
"FleetRosterLayerWins": "действует",
"FleetRosterLayerIgnored": "игнорируемая копия",
- "FleetReadyNotice": "Pod готов · /pod открывает роли · /pod setup настраивает модели участников",
+ "FleetReadyNotice": "Pod готов · /fleet открывает роли · /fleet setup настраивает модели участников",
"FleetProfileIdentityVerifyFailed": "Не удалось проверить существующие идентификаторы профилей ({error}); исправьте указанный файл перед сохранением.",
"FleetProfileIdConflict": "Идентификатор профиля `{id}` уже используется в {path}; пересоздайте черновик с другой ролью или сначала удалите старый файл.",
"FleetProfileProviderUnconfigured": "Профиль закрепляет провайдера `{provider}`, для которого нет настроенных учётных данных ({env}); настройте его в /provider перед сохранением.",
+ "FleetModelAdded": "{route} добавлен во fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} добавлен как {roles} во fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (новый личный fleet, теперь выбран)",
+ "FleetModelAddedSelectedNote": " (теперь выбран)",
+ "FleetModelRemoved": "{route} удалён из fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) удалён из fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} остаётся во fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "это ваша текущая модель (маршрут оператора fleet); переключите модель или измените его через /fleet save",
+ "FleetModelReasonAlreadyPresent": "уже во fleet",
+ "FleetModelErrorNeedsRoute": "модели fleet нужны и id провайдера, и id модели",
+ "FleetModelErrorNoSelection": "fleet не выбран; ваш fleet — только модель сессии",
+ "FleetModelErrorOperatorRoute": "{route} — маршрут оператора fleet `{fleet}`; меняйте его через /fleet save, а не remove",
+ "FleetModelErrorNotInFleet": "{route} нет во fleet `{fleet}`",
+ "FleetModelsEmpty": "Ваш fleet — только модель сессии. Добавьте модель: /fleet add [role…] (или ⇧F на строке в /model).",
+ "FleetModelsHeader": "Ваш fleet `{fleet}` (моделей: {count})",
+ "FleetModelsBroken": "Не удалось загрузить выбранный fleet: {error}. Исправьте файл или выберите другой через /fleet fleets.",
+ "FleetModelsFooter": "Добавить: /fleet add [role…] · Удалить: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} за M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "инструменты",
+ "FleetAddUsage": "Использование: /fleet add [role…] — например /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` не является настроенным провайдером. Настройте его в ~/.codewhale/config.toml или переключитесь на него через /provider, прежде чем добавлять во fleet.",
+ "FleetAddModelNotServed": "{provider} не предоставляет `{model}` в текущем каталоге; выполните /models, чтобы увидеть доступные модели, или /fleet add с точным id из списка.",
+ "FleetAddFailed": "Не удалось добавить во fleet: {error}",
+ "FleetRemoveUsage": "Использование: /fleet remove ",
+ "FleetRemoveFailed": "Не удалось удалить из fleet: {error}",
+ "FleetToggleFailed": "Не удалось обновить fleet: {error}",
"FleetDestStepTitle": "Где сохранить этот профиль?",
"FleetDestStepSubtitle": "Ничего не записывается, пока вы не подтвердите на последнем шаге.",
"FleetDestProjectLabel": "Этот проект",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review заблокировал '{tool}' (детерминированная политика): {reason}",
"AutoReviewReceiptHeld": "Auto-Review удержал '{tool}' без паузы; отклонено (нужен человек — переключитесь на Ask)",
"FooterHintEscInterrupt": "Esc — прервать",
+ "PostureHintEnterAgain": "{enter} ещё раз — отправить сейчас · {steer} направляет",
"PermissionsPostureHeader": "Доступ сейчас: {posture}",
"PermissionsPostureAsk": "Ask: вызовы tool, меняющие полномочия, стоимость, охват или результат, открывают запрос; доказанно безопасные вызовы только для чтения выполняются без него. Правила ask выше всегда принудительно показывают запрос.",
"PermissionsPostureAuto": "Auto-Review: никогда не открывает запрос. Детерминированная политика разрешает доказанно безопасные вызовы и жёстко блокирует публикационную или разрушительную фоновую работу; вызовы, безопасность которых доказать нельзя, отправляются одноразовому модельному стражу, который разрешает или отклоняет с указанием причины (высокий или критический риск никогда не выполняется автоматически; неудачная проверка отклоняет по принципу fail closed). Удержания, требующие человека, отклоняются, а не скрываются. Каждое такое решение записывается заметкой в транскрипт и в журнал аудита.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell только чтение",
"AgentFocusPostureShellNone": "без shell",
"GoalReceiptSet": "Цель задана: «{objective}» · /goal показывает прогресс · /goal pause или /goal clear останавливает",
+ "GoalReceiptSetOperate": "◆ цель задана · Operate работает, пока она не будет проверена · /goal для правки",
"GoalControlAccepted": "Команда цели сохранена и будет применена на следующей безопасной границе.",
"GoalControlRuntimeUnavailable": "Команда цели сохранена, но среда выполнения недоступна. Она будет восстановлена после перезапуска.",
"GoalStatusIdleHint": "сейчас не выполняется — отправьте сообщение или /goal resume, чтобы продолжить",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Блокирует каждый инструмент, требующий одобрения.",
"ConfigChoiceDetailModeAgent": "Начинает в готовности сотрудничать и использовать инструменты.",
"ConfigChoiceDetailModePlan": "Начинает в пространстве планирования только для чтения.",
- "ConfigChoiceDetailModeOperate": "Начинает как координатор, делегирующий работу ограниченным исполнителям.",
+ "ConfigChoiceDetailModeOperate": "Operate превращает запрос в цель и ведёт её параллельно: фоновые исполнители для разделимых потоков, проверка перед остановкой.",
"ConfigChoiceDetailPlacementTop": "Показывает задачи, список дел и исполнителей над стенограммой.",
"ConfigChoiceDetailPlacementLeft": "Показывает задачи, список дел и исполнителей в левой боковой панели, когда терминал достаточно широк.",
"ConfigChoiceDetailPlacementRight": "Показывает задачи, список дел и исполнителей в правой боковой панели, когда терминал достаточно широк.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "открыть плагины · доверять, включать или диагностировать",
"ConfigHintMcpConfigPath": "путь к mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 запрещает дочерних агентов; 3 по умолчанию (та же ось, что у субагентов); не больше 8",
- "ConfigHintFeatureSubagents": "состояние флага функции только для чтения; /pod setup — путь для пользователя",
+ "ConfigHintFeatureSubagents": "состояние флага функции только для чтения; /fleet setup — путь для пользователя",
"ConfigHintFeatureWebSearch": "состояние флага функции только для чтения для инструментов веб-поиска",
"ConfigHintFeatureApplyPatch": "состояние флага функции только для чтения для инструментов правки патчами",
"ConfigHintFeatureMcp": "состояние флага функции только для чтения для инструментов MCP",
"ConfigHintFeatureExecPolicy": "состояние флага функции только для чтения для инструментов политики выполнения",
"ConfigHintFeatureVisionModel": "бета-флаг поддержки изображений для зрения/модели",
"ConfigHintGoalCommand": "/goal задаёт цели, бюджеты и состояние рабочего контекста",
- "ConfigHintWorkflow": "/workflow выполняет сценарные операции с карточками запуска распараллеливания/слияния"
+ "ConfigHintWorkflow": "/workflow выполняет сценарные операции с карточками запуска распараллеливания/слияния",
+ "LaunchMenuNewWorktree": "Новый worktree",
+ "LaunchMenuResume": "Продолжить сессию",
+ "LaunchMenuQuit": "Выход",
+ "LaunchNoticeClaude": "Пришли из Claude Code? Возобновите сессию через ctrl+r",
+ "ReceiptSessionHooks": "хуков: {count}"
}
diff --git a/crates/tui/locales/uk.json b/crates/tui/locales/uk.json
index 1e9e5fbb94..9d29fb084c 100644
--- a/crates/tui/locales/uk.json
+++ b/crates/tui/locales/uk.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Захоплення миші ввімкнено.",
"ScreenModeMouseCaptureOff": "Захоплення миші вимкнено; вибором керує термінал.",
"ScreenModeUnchanged": "Екран без змін: {reason}.",
+ "ImageInputRejectedResent": "{model} не приймає зображення — їх повторно надіслано як текст; скористайтеся image_ocr, щоб їх прочитати",
"ConfigLabelShowThinking": "Міркування моделі в чаті",
"ConfigLabelThinkingHighlight": "Підсвічування тла міркувань",
"ConfigLabelShowToolDetails": "Рівень деталізації інструментів",
@@ -552,7 +553,7 @@
"CmdWorkflowsDescription": "Показати запуски робочого процесу в цьому робочому просторі (список, скасувати)",
"CmdHotbarDescription": "Відкрити налаштування Hotbar",
"CmdSetupDescription": "Відкрити налаштування, що починається з конституції",
- "CmdSubagentsDescription": "Сумісне скорочення для /pod workers (субагенти поточного сеансу)",
+ "CmdSubagentsDescription": "Сумісне скорочення для /fleet workers (субагенти поточного сеансу)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Показати поточний системний промпт",
"CmdTaskDescription": "Керувати фоновими завданнями",
@@ -632,7 +633,7 @@
"KbExternalEditor": "Відкрити чернетку композера у зовнішньому редакторі",
"KbLiveTranscript": "Відкрити накладення живого транскрипту (Ctrl+Shift+T; автопрокручування до кінця)",
"KbBacktrackMessage": "Повернутися до попереднього повідомлення користувача (Left/Right — крок, Enter — перемотати)",
- "KbCompleteCycleModes": "Доповнити /command або перемикати режими, коли композер порожній (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Доповнити /command або перемикати режими, коли композер порожній (Plan → Work → Operate)",
"KbCycleThinking": "Перемикати рівень міркувань для активного провайдера (Ctrl+T)",
"KbCyclePermissions": "Змінити доступ: Питати → Автоогляд → Повний доступ (Shift+Tab)",
"KbJumpPlanAgentYolo": "Активувати слоти Hotbar",
@@ -669,11 +670,11 @@
"LinksKimiCodeRouteNote": "Маршрут плану підписки Kimi Code: `{route}` (консоль плану: `{console}`; використовуйте модель k3). Codewhale ніколи не імпортує облікові дані Kimi CLI.",
"LinksTip": "Порада: використовуйте змінну середовища, показану для вашого провайдера, або збережіть ключ командою `codewhale auth set --provider `.",
"SubagentsFetching": "Отримання субагентів поточного сеансу...",
- "SubagentsNoCurrentSessionPodWorkers": "У поточному сеансі немає воркерів Pod.",
- "SubagentsCurrentSessionPodWorkersTitle": "Воркери Pod поточного сеансу",
- "SubagentsCurrentSessionPodWorkerRoles": "Ролі субагентів — це ролі воркерів Pod поточного сеансу.",
- "SubagentsCurrentSessionPodWorkersStatus": "Воркери Pod поточного сеансу: усього {count}",
- "SubagentsEmptyGuidance": "Налаштуйте ролі та параметри запуску через /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "У поточному сеансі немає воркерів флоту.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Воркери флоту поточного сеансу",
+ "SubagentsCurrentSessionPodWorkerRoles": "Ролі субагентів — це ролі воркерів флоту поточного сеансу.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Воркери флоту поточного сеансу: усього {count}",
+ "SubagentsEmptyGuidance": "Налаштуйте ролі та параметри запуску через /fleet.",
"SubagentsStatusRunning": "Виконується",
"SubagentsStatusCompleted": "Завершено",
"SubagentsStatusInterrupted": "Перервано",
@@ -718,7 +719,7 @@
"HomeHistory": "Історія:",
"HomeTokens": "Токени:",
"HomeQueued": "У черзі:",
- "HomeSubagents": "Воркери Pod поточного сеансу:",
+ "HomeSubagents": "Воркери флоту поточного сеансу:",
"HomeSkill": "Навичка:",
"HomeQuickActions": "Швидкі дії",
"HomeQuickLinks": "/links - посилання Codewhale, спільноти та провайдерів",
@@ -726,7 +727,7 @@
"HomeQuickConfig": "/config - переглянути й змінити налаштування",
"HomeQuickSettings": "/settings - показати постійні налаштування",
"HomeQuickModel": "/model - змінити або переглянути модель",
- "HomeQuickSubagents": "/pod workers - субагенти поточного сеансу",
+ "HomeQuickSubagents": "/fleet workers - субагенти поточного сеансу",
"HomeQuickTaskList": "/task list - показати чергу фонових завдань",
"HomeQuickHelp": "/help - показати довідку",
"HomeQuickWorkspace": "/workspace - Перемкнути теку або worktree",
@@ -988,7 +989,7 @@
"AppModeAutoHint": "Shell увімкнено з автоматичною перевіркою ризиків",
"AppModePlanHint": "Спочатку дослідження лише для читання — подайте план перед діями",
"AppModeYoloHint": "Лише для сумісності — Act + Full Access, не видимий режим",
- "AppModeOperateHint": "Залучіть Pod до паралельної роботи",
+ "AppModeOperateHint": "Перетворює запит на ціль: паралельні агенти, перевірка",
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
@@ -1098,7 +1099,7 @@
"ToolFamilyPatch": "патч",
"ToolFamilyRun": "запуск",
"ToolFamilyFind": "пошук",
- "ToolFamilyDelegate": "делегування",
+ "ToolFamilyDelegate": "агент",
"ToolFamilyFanout": "розгалуження",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "перевірка",
@@ -1208,17 +1209,12 @@
"HotbarActionModeOperateName": "Режим Operate",
"HotbarActionModeOperateDescription": "Залучіть Pod до паралельної роботи.",
"HomeOperateModeTip": "Operate — паралельна робота Pod",
- "HomeOperateModeFleetTip": " Вбудовані ролі використовують модель цієї сесії; /pod setup налаштовує їх",
+ "HomeOperateModeFleetTip": " Вбудовані ролі використовують модель цієї сесії; /fleet setup налаштовує їх",
"HelpSubtitle": "Концепції, команди та комбінації клавіш",
"CommandPaletteTitle": "Команда",
"CommandPaletteSubtitle": "Знайдіть і виконайте одну дію",
"ConfigSubtitle": "Спочатку налаштування; сирі ключі — в розширених деталях",
"LaunchStartTitle": "Почніть тут",
- "LaunchMenuConnect": "Підключити модель",
- "LaunchMenuWork": "Робота · поточна папка",
- "LaunchMenuChat": "Чат · лише читання",
- "LaunchMenuTheme": "Тема",
- "LaunchMenuHelp": "Довідка",
"LaunchWorkDescription": "Використовуйте локальні інструменти в цій папці; зміни підпорядковуються політиці схвалення.",
"LaunchChatDescription": "Лише розмова й планування; файли не змінюються.",
"LaunchWorkspaceGitReady": "Робочий простір · {name} · простір Git",
@@ -1231,15 +1227,7 @@
"LaunchWorkspaceFolderShort": "папка",
"LaunchProviderConfiguredShort": "провайдер налаштований",
"LaunchProviderSetupShort": "налаштувати провайдера",
- "LaunchMenuNewSession": "Нова сесія",
- "LaunchMenuNewWorktree": "Новий worktree",
- "LaunchMenuWorktreeCompact": "робоче дерево",
- "LaunchMenuChatCompact": "розмова",
- "LaunchMenuResumeSession": "Відновити сесію",
"LaunchMenuChangelog": "Журнал змін",
- "LaunchMenuQuit": "Вийти",
- "LaunchMenuUnavailable": "недоступно",
- "LaunchMenuSavedCount": "{count} збережено",
"LaunchWorktreePrompt": "Назвіть гілку/worktree або натисніть Enter для автоматичної назви.",
"LaunchWorktreeNeedsGit": "Новий worktree потребує репозиторію Git.",
"LaunchWorktreeNameLabel": "назва worktree",
@@ -1252,7 +1240,12 @@
"LaunchWorktreeFailed": "Worktree не вдався: {error}",
"LaunchNoSavedSessions": "Немає збережених сесій для цього робочого простору.",
"LaunchComposerHint": "Enter — надіслати · Shift+Enter — новий рядок · Esc — назад",
- "LaunchComposerFocusHint": "Tab — почати введення",
+ "LaunchNoModelConnected": "модель не підключено",
+ "LaunchRunCommand": "виконайте {command}",
+ "LaunchMcpConnectedOne": "підключено 1 сервер MCP",
+ "LaunchMcpConnectedMany": "підключено серверів MCP: {count}",
+ "LaunchMcpNeedsSignInOne": "1 потребує входу",
+ "LaunchMcpNeedsSignInMany": "{count} потребують входу",
"PhaseIdle": "очікування",
"PhaseDraft": "чернетка",
"PhaseWorking": "робота",
@@ -1277,6 +1270,8 @@
"FooterHintOutput": "вивід",
"FooterHintContext": "контекст",
"InfoLineHelp": "довідка",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "кити",
"InfoLineAutomation": "автоматизація",
"InfoLineNotConnected": "не підключено",
@@ -1372,6 +1367,7 @@
"PickerActionApply": "застосувати",
"PickerActionSetStartupDefault": "встановити як значення за замовчуванням під час запуску",
"PickerActionPin": "закріпити",
+ "PickerActionFleet": "флот",
"PickerActionCancel": "скасувати",
"PickerActionClear": "очистити",
"PickerActionClearSearch": "очистити пошук",
@@ -1459,10 +1455,37 @@
"FleetRosterLayersLabel": "Збережено для",
"FleetRosterLayerWins": "чинний",
"FleetRosterLayerIgnored": "проігнорована копія",
- "FleetReadyNotice": "Pod готовий · /pod відкриває ролі · /pod setup налаштовує моделі учасників",
+ "FleetReadyNotice": "Pod готовий · /fleet відкриває ролі · /fleet setup налаштовує моделі учасників",
"FleetProfileIdentityVerifyFailed": "Не вдалося перевірити наявні ідентичності профілів ({error}); виправте названий файл перед збереженням.",
"FleetProfileIdConflict": "Ідентифікатор профілю `{id}` уже використовується в {path}; створіть чернетку з іншою роллю або спочатку видаліть старий файл.",
"FleetProfileProviderUnconfigured": "Профіль закріплює провайдера `{provider}`, для якого не налаштовано облікові дані ({env}); налаштуйте його в /provider перед збереженням.",
+ "FleetModelAdded": "{route} додано до fleet `{fleet}`",
+ "FleetModelAddedAs": "{route} додано як {roles} до fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (новий особистий fleet, тепер вибрано)",
+ "FleetModelAddedSelectedNote": " (тепер вибрано)",
+ "FleetModelRemoved": "{route} вилучено з fleet `{fleet}`",
+ "FleetModelRemovedRoles": "{route} ({roles}) вилучено з fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} залишається у fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "це ваша поточна модель (маршрут оператора fleet); перемкніть модель або змініть його через /fleet save",
+ "FleetModelReasonAlreadyPresent": "уже у fleet",
+ "FleetModelErrorNeedsRoute": "моделі fleet потрібні і id провайдера, і id моделі",
+ "FleetModelErrorNoSelection": "fleet не вибрано; ваш fleet — лише модель сесії",
+ "FleetModelErrorOperatorRoute": "{route} — маршрут оператора fleet `{fleet}`; змінюйте його через /fleet save, а не remove",
+ "FleetModelErrorNotInFleet": "{route} немає у fleet `{fleet}`",
+ "FleetModelsEmpty": "Ваш fleet — лише модель сесії. Додайте модель: /fleet add [role…] (або ⇧F на рядку в /model).",
+ "FleetModelsHeader": "Ваш fleet `{fleet}` (моделей: {count})",
+ "FleetModelsBroken": "Не вдалося завантажити вибраний fleet: {error}. Виправте файл або виберіть інший через /fleet fleets.",
+ "FleetModelsFooter": "Додати: /fleet add [role…] · Вилучити: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} за M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "інструменти",
+ "FleetAddUsage": "Використання: /fleet add [role…] — наприклад /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` не є налаштованим провайдером. Налаштуйте його в ~/.codewhale/config.toml або перемкніться на нього через /provider, перш ніж додавати до fleet.",
+ "FleetAddModelNotServed": "{provider} не надає `{model}` у поточному каталозі; виконайте /models, щоб побачити доступні моделі, або /fleet add із точним id зі списку.",
+ "FleetAddFailed": "Не вдалося додати до fleet: {error}",
+ "FleetRemoveUsage": "Використання: /fleet remove ",
+ "FleetRemoveFailed": "Не вдалося вилучити з fleet: {error}",
+ "FleetToggleFailed": "Не вдалося оновити fleet: {error}",
"FleetDestStepTitle": "Де зберегти цей профіль?",
"FleetDestStepSubtitle": "Нічого не записується, доки ви не підтвердите на останньому кроці.",
"FleetDestProjectLabel": "Цей проєкт",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review заблокував '{tool}' (детермінована політика): {reason}",
"AutoReviewReceiptHeld": "Auto-Review утримав '{tool}' без паузи; відхилено (потрібна людина — перемкніться на Ask)",
"FooterHintEscInterrupt": "Esc — перервати",
+ "PostureHintEnterAgain": "{enter} ще раз — надіслати зараз · {steer} спрямовує",
"PermissionsPostureHeader": "Доступ зараз: {posture}",
"PermissionsPostureAsk": "Ask: виклики tool, що змінюють повноваження, вартість, обсяг або результат, відкривають запит; доведено безпечні виклики лише для читання виконуються без нього. Правила ask вище завжди примусово показують запит.",
"PermissionsPostureAuto": "Auto-Review: ніколи не відкриває запит. Детермінована політика дозволяє доведено безпечні виклики та жорстко блокує публікаційну або руйнівну фонову роботу; виклики, безпечність яких довести не можна, передаються одноразовому модельному вартовому, який дозволяє або відхиляє з указанням причини (високий або критичний ризик ніколи не виконується автоматично; невдала перевірка відхиляє за принципом fail closed). Утримання, що потребують людини, відхиляються, а не приховуються. Кожне таке рішення записується нотаткою в транскрипт і в журнал аудиту.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell лише читання",
"AgentFocusPostureShellNone": "без shell",
"GoalReceiptSet": "Ціль задано: «{objective}» · /goal показує прогрес · /goal pause або /goal clear зупиняє",
+ "GoalReceiptSetOperate": "◆ ціль задано · Operate працює, доки її не буде перевірено · /goal для редагування",
"GoalControlAccepted": "Команду цілі збережено; її буде застосовано на наступній безпечній межі.",
"GoalControlRuntimeUnavailable": "Команду цілі збережено, але середовище виконання недоступне. Її буде відновлено після перезапуску.",
"GoalStatusIdleHint": "зараз не виконується — надішліть повідомлення або /goal resume, щоб продовжити",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Блокує кожен інструмент, який потребує схвалення.",
"ConfigChoiceDetailModeAgent": "Починає в готовності співпрацювати та використовувати інструменти.",
"ConfigChoiceDetailModePlan": "Починає в просторі планування лише для читання.",
- "ConfigChoiceDetailModeOperate": "Починає як координатор, що делегує роботу обмеженим виконавцям.",
+ "ConfigChoiceDetailModeOperate": "Operate перетворює запит на ціль і веде її паралельно: фонові виконавці для розділюваних потоків, перевірка перед зупинкою.",
"ConfigChoiceDetailPlacementTop": "Показує завдання, список справ і виконавців над стенограмою.",
"ConfigChoiceDetailPlacementLeft": "Показує завдання, список справ і виконавців у лівій бічній панелі, коли термінал достатньо широкий.",
"ConfigChoiceDetailPlacementRight": "Показує завдання, список справ і виконавців у правій бічній панелі, коли термінал достатньо широкий.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "відкрити плагіни · довіряти, вмикати або діагностувати",
"ConfigHintMcpConfigPath": "шлях до mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 забороняє дочірніх агентів; 3 типово (та сама вісь, що й у субагентів); не більше 8",
- "ConfigHintFeatureSubagents": "стан прапорця функції лише для читання; /pod setup — шлях для користувача",
+ "ConfigHintFeatureSubagents": "стан прапорця функції лише для читання; /fleet setup — шлях для користувача",
"ConfigHintFeatureWebSearch": "стан прапорця функції лише для читання для інструментів вебпошуку",
"ConfigHintFeatureApplyPatch": "стан прапорця функції лише для читання для інструментів правки патчами",
"ConfigHintFeatureMcp": "стан прапорця функції лише для читання для інструментів MCP",
"ConfigHintFeatureExecPolicy": "стан прапорця функції лише для читання для інструментів політики виконання",
"ConfigHintFeatureVisionModel": "бета-прапорець підтримки зображень для зору/моделі",
"ConfigHintGoalCommand": "/goal задає цілі, бюджети та стан робочого контексту",
- "ConfigHintWorkflow": "/workflow виконує сценарні операції з картками запуску розпаралелення/злиття"
+ "ConfigHintWorkflow": "/workflow виконує сценарні операції з картками запуску розпаралелення/злиття",
+ "LaunchMenuNewWorktree": "Новий worktree",
+ "LaunchMenuResume": "Відновити сесію",
+ "LaunchMenuQuit": "Вихід",
+ "LaunchNoticeClaude": "Прийшли з Claude Code? Відновіть сесію через ctrl+r",
+ "ReceiptSessionHooks": "гачків: {count}"
}
diff --git a/crates/tui/locales/vi.json b/crates/tui/locales/vi.json
index 49c088dc4b..a3fb2c92ed 100644
--- a/crates/tui/locales/vi.json
+++ b/crates/tui/locales/vi.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "Đã bật bắt chuột.",
"ScreenModeMouseCaptureOff": "Đã tắt bắt chuột; thiết bị đầu cuối quản lý việc chọn.",
"ScreenModeUnchanged": "Màn hình không thay đổi: {reason}.",
+ "ImageInputRejectedResent": "{model} không chấp nhận hình ảnh — đã gửi lại dưới dạng văn bản; hãy dùng image_ocr để đọc chúng",
"ConfigLabelShowThinking": "Hiển thị suy luận trong trò chuyện",
"ConfigLabelThinkingHighlight": "Tô nền phần suy luận",
"ConfigLabelShowToolDetails": "Mức chi tiết công cụ",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "Hiển thị các lần chạy quy trình làm việc trong không gian làm việc này (danh sách, hủy)",
"CmdHotbarDescription": "Mở thiết lập Hotbar",
"CmdSetupDescription": "Mở thiết lập ưu tiên hiến pháp",
- "CmdSubagentsDescription": "Phím tắt tương thích cho /pod workers (tác nhân phụ của phiên hiện tại)",
+ "CmdSubagentsDescription": "Phím tắt tương thích cho /fleet workers (tác nhân phụ của phiên hiện tại)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "Hiển thị prompt hệ thống hiện tại",
"CmdTaskDescription": "Quản lý các nhiệm vụ chạy ngầm",
@@ -649,7 +650,7 @@
"KbExternalEditor": "Mở bản nháp trong trình soạn thảo bên ngoài",
"KbLiveTranscript": "Mở lớp phủ bản ghi trực tiếp (tự động cuộn theo đuôi)",
"KbBacktrackMessage": "Quay lại tin nhắn trước đó của người dùng (nhấn Trái/Phải để chuyển bước, Enter để lùi lại)",
- "KbCompleteCycleModes": "Hoàn thành /command hoặc chuyển chế độ khi khung soạn thảo trống (Plan → Act → Operate)",
+ "KbCompleteCycleModes": "Hoàn thành /command hoặc chuyển chế độ khi khung soạn thảo trống (Plan → Work → Operate)",
"KbCycleThinking": "Luân phiên mức suy luận của nhà cung cấp đang hoạt động (Ctrl+T)",
"KbCyclePermissions": "Chuyển quyền truy cập: Hỏi → Tự động xem lại → Toàn quyền (Shift+Tab)",
"KbJumpPlanAgentYolo": "Kích hoạt các ô hotbar",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Tuyến gói thành viên Kimi Code: `{route}` (bảng điều khiển gói: `{console}`; dùng mô hình k3). Codewhale không bao giờ nhập thông tin xác thực Kimi CLI.",
"LinksTip": "Mẹo: Dùng biến môi trường được hiển thị cho nhà cung cấp, hoặc lưu khóa bằng `codewhale auth set --provider `.",
"SubagentsFetching": "Đang lấy tác nhân phụ của phiên hiện tại...",
- "SubagentsNoCurrentSessionPodWorkers": "Không có tác nhân thực thi Pod trong phiên hiện tại.",
- "SubagentsCurrentSessionPodWorkersTitle": "Tác nhân thực thi Pod của phiên hiện tại",
- "SubagentsCurrentSessionPodWorkerRoles": "Vai trò tác nhân phụ là vai trò tác nhân thực thi Pod của phiên hiện tại.",
- "SubagentsCurrentSessionPodWorkersStatus": "Tác nhân thực thi Pod của phiên hiện tại: tổng cộng {count}",
- "SubagentsEmptyGuidance": "Cấu hình vai trò và thiết lập khởi chạy bằng /pod.",
+ "SubagentsNoCurrentSessionPodWorkers": "Không có tác nhân thực thi hạm đội trong phiên hiện tại.",
+ "SubagentsCurrentSessionPodWorkersTitle": "Tác nhân thực thi hạm đội của phiên hiện tại",
+ "SubagentsCurrentSessionPodWorkerRoles": "Vai trò tác nhân phụ là vai trò tác nhân thực thi hạm đội của phiên hiện tại.",
+ "SubagentsCurrentSessionPodWorkersStatus": "Tác nhân thực thi hạm đội của phiên hiện tại: tổng cộng {count}",
+ "SubagentsEmptyGuidance": "Cấu hình vai trò và thiết lập khởi chạy bằng /fleet.",
"SubagentsStatusRunning": "Đang chạy",
"SubagentsStatusCompleted": "Đã hoàn tất",
"SubagentsStatusInterrupted": "Bị gián đoạn",
@@ -735,7 +736,7 @@
"HomeHistory": "Lịch sử:",
"HomeTokens": "Token:",
"HomeQueued": "Trong hàng đợi:",
- "HomeSubagents": "Worker Pod của phiên hiện tại:",
+ "HomeSubagents": "Worker hạm đội của phiên hiện tại:",
"HomeSkill": "Kỹ năng:",
"HomeQuickActions": "Hành động nhanh",
"HomeQuickLinks": "/links - Codewhale, cộng đồng và nhà cung cấp",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - Xem và đổi cài đặt",
"HomeQuickSettings": "/settings - Hiển thị các cài đặt liên tục",
"HomeQuickModel": "/model - Xem hoặc chuyển đổi mô hình",
- "HomeQuickSubagents": "/pod workers - tác nhân phụ của phiên hiện tại",
+ "HomeQuickSubagents": "/fleet workers - tác nhân phụ của phiên hiện tại",
"HomeQuickTaskList": "/task list - Hiển thị hàng đợi nhiệm vụ ngầm",
"HomeQuickHelp": "/help - Hiển thị trợ giúp",
"HomeQuickWorkspace": "/workspace - Đổi thư mục hoặc worktree",
@@ -1119,7 +1120,7 @@
"ToolFamilyPatch": "vá",
"ToolFamilyRun": "chạy",
"ToolFamilyFind": "tìm",
- "ToolFamilyDelegate": "ủy quyền",
+ "ToolFamilyDelegate": "tác nhân",
"ToolFamilyFanout": "fanout",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "xác minh",
@@ -1229,19 +1230,14 @@
"HotbarActionModeOperateName": "Chế độ Operate",
"HotbarActionModeOperateDescription": "Cho Pod của bạn làm việc song song.",
"HomeOperateModeTip": "Operate — cho Pod của bạn làm việc song song",
- "HomeOperateModeFleetTip": " Vai trò tích hợp dùng mô hình của phiên này; /pod setup để tùy chỉnh",
+ "HomeOperateModeFleetTip": " Vai trò tích hợp dùng mô hình của phiên này; /fleet setup để tùy chỉnh",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "Cho Pod của bạn làm việc song song",
+ "AppModeOperateHint": "Biến prompt thành mục tiêu: worker song song, có xác minh",
"HelpSubtitle": "Khái niệm, lệnh và phím tắt",
"CommandPaletteTitle": "Lệnh",
"CommandPaletteSubtitle": "Tìm và chạy một thao tác",
"ConfigSubtitle": "Cài đặt trước; khóa thô ở chi tiết nâng cao",
"LaunchStartTitle": "Bắt đầu tại đây",
- "LaunchMenuConnect": "Kết nối mô hình",
- "LaunchMenuWork": "Làm việc · thư mục hiện tại",
- "LaunchMenuChat": "Trò chuyện · chỉ đọc",
- "LaunchMenuTheme": "Chủ đề",
- "LaunchMenuHelp": "Trợ giúp",
"LaunchWorkDescription": "Dùng công cụ cục bộ trong thư mục này; mọi thay đổi tuân theo chính sách phê duyệt của bạn.",
"LaunchChatDescription": "Chỉ trò chuyện và lập kế hoạch; không thay đổi tệp.",
"LaunchWorkspaceGitReady": "Không gian làm việc · {name} · không gian Git",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "thư mục",
"LaunchProviderConfiguredShort": "đã đặt nhà cung cấp",
"LaunchProviderSetupShort": "thiết lập nhà cung cấp",
- "LaunchMenuNewSession": "Phiên mới",
- "LaunchMenuNewWorktree": "Worktree mới",
- "LaunchMenuWorktreeCompact": "cây làm việc",
- "LaunchMenuChatCompact": "trò chuyện",
- "LaunchMenuResumeSession": "Tiếp tục phiên",
"LaunchMenuChangelog": "Nhật ký thay đổi",
- "LaunchMenuQuit": "Thoát",
- "LaunchMenuUnavailable": "không khả dụng",
- "LaunchMenuSavedCount": "{count} đã lưu",
"LaunchWorktreePrompt": "Đặt tên cho nhánh/worktree, hoặc nhấn Enter để dùng tên tự động.",
"LaunchWorktreeNeedsGit": "Worktree mới yêu cầu một kho Git.",
"LaunchWorktreeNameLabel": "tên worktree",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "Worktree thất bại: {error}",
"LaunchNoSavedSessions": "Không có phiên đã lưu cho workspace này.",
"LaunchComposerHint": "Enter gửi · Shift+Enter dòng mới · Esc quay lại",
- "LaunchComposerFocusHint": "Tab để nhập",
+ "LaunchNoModelConnected": "chưa kết nối mô hình",
+ "LaunchRunCommand": "chạy {command}",
+ "LaunchMcpConnectedOne": "1 máy chủ MCP đã kết nối",
+ "LaunchMcpConnectedMany": "{count} máy chủ MCP đã kết nối",
+ "LaunchMcpNeedsSignInOne": "1 cần đăng nhập",
+ "LaunchMcpNeedsSignInMany": "{count} cần đăng nhập",
"PhaseIdle": "rảnh",
"PhaseDraft": "nháp",
"PhaseWorking": "đang xử lý",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "đầu ra",
"FooterHintContext": "ngữ cảnh",
"InfoLineHelp": "trợ giúp",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "cá voi",
"InfoLineAutomation": "tự động hóa",
"InfoLineNotConnected": "chưa kết nối",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "áp dụng",
"PickerActionSetStartupDefault": "đặt làm mặc định khi khởi động",
"PickerActionPin": "ghim",
+ "PickerActionFleet": "đội hình",
"PickerActionCancel": "hủy",
"PickerActionClear": "xóa",
"PickerActionClearSearch": "xóa tìm kiếm",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "Đã lưu tại",
"FleetRosterLayerWins": "đang hiệu lực",
"FleetRosterLayerIgnored": "bản sao bị bỏ qua",
- "FleetReadyNotice": "Pod sẵn sàng · /pod mở vai trò · /pod setup chỉnh mô hình của thành viên",
+ "FleetReadyNotice": "Pod sẵn sàng · /fleet mở vai trò · /fleet setup chỉnh mô hình của thành viên",
"FleetProfileIdentityVerifyFailed": "Không thể xác minh danh tính hồ sơ hiện có ({error}); hãy sửa tệp được nêu trước khi lưu.",
"FleetProfileIdConflict": "Id hồ sơ `{id}` đã được {path} sử dụng; hãy soạn lại với vai trò khác hoặc xóa tệp cũ trước.",
"FleetProfileProviderUnconfigured": "Hồ sơ chỉ định nhà cung cấp `{provider}` chưa có thông tin xác thực ({env}); hãy thiết lập trong /provider trước khi lưu.",
+ "FleetModelAdded": "Đã thêm {route} vào fleet `{fleet}`",
+ "FleetModelAddedAs": "Đã thêm {route} với vai trò {roles} vào fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": " (fleet cá nhân mới, đã được chọn)",
+ "FleetModelAddedSelectedNote": " (đã được chọn)",
+ "FleetModelRemoved": "Đã gỡ {route} khỏi fleet `{fleet}`",
+ "FleetModelRemovedRoles": "Đã gỡ {route} ({roles}) khỏi fleet `{fleet}`",
+ "FleetModelUnchanged": "{route} vẫn ở trong fleet `{fleet}`: {reason}",
+ "FleetModelReasonOperatorRoute": "đây là mô hình hiện tại của bạn (tuyến operator của fleet); hãy đổi mô hình hoặc dùng /fleet save để thay đổi",
+ "FleetModelReasonAlreadyPresent": "đã có trong fleet",
+ "FleetModelErrorNeedsRoute": "một mô hình trong fleet cần cả id provider lẫn id mô hình",
+ "FleetModelErrorNoSelection": "chưa chọn fleet nào; fleet của bạn chỉ gồm mô hình của phiên",
+ "FleetModelErrorOperatorRoute": "{route} là tuyến operator của fleet `{fleet}`; hãy thay đổi bằng /fleet save, không phải remove",
+ "FleetModelErrorNotInFleet": "{route} không có trong fleet `{fleet}`",
+ "FleetModelsEmpty": "Fleet của bạn chỉ gồm mô hình của phiên. Thêm một mô hình: /fleet add [role…] (hoặc ⇧F trên một hàng trong /model).",
+ "FleetModelsHeader": "Fleet `{fleet}` của bạn ({count} mô hình)",
+ "FleetModelsBroken": "Không thể tải fleet đã chọn: {error}. Hãy sửa tệp hoặc chọn fleet khác bằng /fleet fleets.",
+ "FleetModelsFooter": "Thêm: /fleet add [role…] · Gỡ: /fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} mỗi M",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "công cụ",
+ "FleetAddUsage": "Cách dùng: /fleet add [role…] — ví dụ /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` không phải provider đã cấu hình. Hãy cấu hình trong ~/.codewhale/config.toml hoặc chuyển sang nó bằng /provider trước khi thêm vào fleet.",
+ "FleetAddModelNotServed": "{provider} không cung cấp `{model}` trong danh mục hiện tại; chạy /models để xem nó cung cấp gì, hoặc /fleet add với đúng id được liệt kê.",
+ "FleetAddFailed": "Không thể thêm vào fleet: {error}",
+ "FleetRemoveUsage": "Cách dùng: /fleet remove ",
+ "FleetRemoveFailed": "Không thể gỡ khỏi fleet: {error}",
+ "FleetToggleFailed": "Không thể cập nhật fleet: {error}",
"FleetDestStepTitle": "Hồ sơ này nên được lưu ở đâu?",
"FleetDestStepSubtitle": "Không có gì được ghi cho đến khi bạn xác nhận ở bước cuối.",
"FleetDestProjectLabel": "Dự án này",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review đã chặn '{tool}' (chính sách tất định): {reason}",
"AutoReviewReceiptHeld": "Auto-Review đã giữ lại '{tool}' mà không tạm dừng; đã từ chối (cần con người — chuyển sang Ask)",
"FooterHintEscInterrupt": "Esc để ngắt",
+ "PostureHintEnterAgain": "{enter} lần nữa để gửi ngay · {steer} điều hướng",
"PermissionsPostureHeader": "Quyền truy cập hiện tại: {posture}",
"PermissionsPostureAsk": "Ask: các lệnh gọi tool làm thay đổi thẩm quyền, chi phí, phạm vi hoặc kết quả sẽ mở lời nhắc; các lệnh chỉ đọc đã được chứng minh an toàn chạy mà không cần lời nhắc. Các quy tắc ask ở trên luôn buộc phải nhắc.",
"PermissionsPostureAuto": "Auto-Review: không bao giờ mở lời nhắc. Chính sách tất định cho phép các lệnh đã được chứng minh an toàn và chặn cứng công việc nền mang tính xuất bản hoặc phá hủy; các lệnh không thể chứng minh an toàn được chuyển đến bộ giám hộ mô hình một lần, cho phép hoặc từ chối kèm lý do (rủi ro cao hoặc nghiêm trọng không bao giờ tự chạy; xem xét thất bại sẽ từ chối, fail closed). Các lệnh cần người quyết định bị từ chối chứ không bị ẩn. Mỗi quyết định như vậy được ghi vào bản ghi dưới dạng ghi chú và vào nhật ký kiểm toán.",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "shell chỉ đọc",
"AgentFocusPostureShellNone": "không shell",
"GoalReceiptSet": "Đã đặt mục tiêu: \"{objective}\" · /goal hiển thị tiến độ · /goal pause hoặc /goal clear để dừng",
+ "GoalReceiptSetOperate": "◆ đã đặt mục tiêu · Operate tiếp tục làm việc cho đến khi được xác minh · /goal để sửa",
"GoalControlAccepted": "Đã lưu điều khiển mục tiêu; sẽ áp dụng tại ranh giới an toàn tiếp theo.",
"GoalControlRuntimeUnavailable": "Điều khiển mục tiêu đã được lưu nhưng runtime hiện không khả dụng. Trạng thái sẽ được khôi phục sau khi khởi động lại.",
"GoalStatusIdleHint": "hiện không chạy — gửi tin nhắn hoặc /goal resume để tiếp tục",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "Chặn mọi công cụ cần phê duyệt.",
"ConfigChoiceDetailModeAgent": "Bắt đầu ở trạng thái sẵn sàng cộng tác và dùng công cụ.",
"ConfigChoiceDetailModePlan": "Bắt đầu trong không gian lập kế hoạch chỉ đọc.",
- "ConfigChoiceDetailModeOperate": "Bắt đầu với vai trò điều phối, giao việc cho các worker có giới hạn.",
+ "ConfigChoiceDetailModeOperate": "Operate biến yêu cầu của bạn thành mục tiêu và làm song song: worker nền cho các luồng tách được, được xác minh trước khi dừng.",
"ConfigChoiceDetailPlacementTop": "Hiện Tác vụ, Việc cần làm và Worker phía trên bản ghi.",
"ConfigChoiceDetailPlacementLeft": "Hiện Tác vụ, Việc cần làm và Worker ở thanh bên trái khi terminal đủ rộng.",
"ConfigChoiceDetailPlacementRight": "Hiện Tác vụ, Việc cần làm và Worker ở thanh bên phải khi terminal đủ rộng.",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "mở plugin · tin cậy, bật hoặc chẩn đoán",
"ConfigHintMcpConfigPath": "đường dẫn tới mcp.json",
"ConfigHintFleetMaxSpawnDepth": "0 chặn tác nhân con; mặc định 3 (cùng trục với tác nhân con); tối đa 8",
- "ConfigHintFeatureSubagents": "trạng thái cờ tính năng chỉ đọc; /pod setup là đường dành cho người dùng",
+ "ConfigHintFeatureSubagents": "trạng thái cờ tính năng chỉ đọc; /fleet setup là đường dành cho người dùng",
"ConfigHintFeatureWebSearch": "trạng thái cờ tính năng chỉ đọc cho công cụ tìm kiếm web",
"ConfigHintFeatureApplyPatch": "trạng thái cờ tính năng chỉ đọc cho công cụ chỉnh sửa bằng patch",
"ConfigHintFeatureMcp": "trạng thái cờ tính năng chỉ đọc cho công cụ MCP",
"ConfigHintFeatureExecPolicy": "trạng thái cờ tính năng chỉ đọc cho công cụ chính sách thực thi",
"ConfigHintFeatureVisionModel": "cờ tính năng beta cho hỗ trợ hình ảnh thị giác/mô hình",
"ConfigHintGoalCommand": "/goal đặt mục tiêu, ngân sách và trạng thái ngữ cảnh Công việc",
- "ConfigHintWorkflow": "/workflow chạy các thao tác theo kịch bản với thẻ chạy phân tán/gộp"
+ "ConfigHintWorkflow": "/workflow chạy các thao tác theo kịch bản với thẻ chạy phân tán/gộp",
+ "LaunchMenuNewWorktree": "Worktree mới",
+ "LaunchMenuResume": "Tiếp tục phiên",
+ "LaunchMenuQuit": "Thoát",
+ "LaunchNoticeClaude": "Đến từ Claude Code? Tiếp tục phiên với ctrl+r",
+ "ReceiptSessionHooks": "hook {count}"
}
diff --git a/crates/tui/locales/zh-Hans.json b/crates/tui/locales/zh-Hans.json
index 78f2594ebb..167ea863f8 100644
--- a/crates/tui/locales/zh-Hans.json
+++ b/crates/tui/locales/zh-Hans.json
@@ -276,6 +276,7 @@
"ScreenModeMouseCaptureOn": "鼠标捕获已开启。",
"ScreenModeMouseCaptureOff": "鼠标捕获已关闭;选择由终端管理。",
"ScreenModeUnchanged": "屏幕未改变:{reason}。",
+ "ImageInputRejectedResent": "{model} 不接受图像,已将图像替换为文本重新发送;请使用 image_ocr 读取图像",
"ConfigLabelShowThinking": "对话中显示模型推理",
"ConfigLabelThinkingHighlight": "推理背景高亮",
"ConfigLabelShowToolDetails": "工具详情级别",
@@ -556,7 +557,7 @@
"CmdWorkflowsDescription": "显示此工作区的工作流运行(列表、取消)",
"CmdHotbarDescription": "打开 Hotbar 设置",
"CmdSetupDescription": "打开以宪章为核心的设置",
- "CmdSubagentsDescription": "/pod workers 的兼容快捷方式(当前会话子代理)",
+ "CmdSubagentsDescription": "/fleet workers 的兼容快捷方式(当前会话子代理)",
"CmdAdvisorDescription": "Toggle background advisor watcher on/off for this session",
"CmdSystemDescription": "显示当前系统提示词",
"CmdTaskDescription": "管理后台任务",
@@ -649,7 +650,7 @@
"KbExternalEditor": "在外部编辑器中打开撰写中的草稿",
"KbLiveTranscript": "打开实时对话覆盖层(Ctrl+Shift+T;尾部自动滚动)",
"KbBacktrackMessage": "回退到之前的用户消息(左右键步进,Enter 回退)",
- "KbCompleteCycleModes": "补全 /command,或在输入框为空时切换模式(Plan → Act → Operate)",
+ "KbCompleteCycleModes": "补全 /command,或在输入框为空时切换模式(Plan → Work → Operate)",
"KbCycleThinking": "轮换当前提供商的推理强度 (Ctrl+T)",
"KbCyclePermissions": "切换访问级别:询问 → 自动审查 → 完全访问(Shift+Tab)",
"KbJumpPlanAgentYolo": "触发快捷栏槽位",
@@ -686,11 +687,11 @@
"LinksKimiCodeRouteNote": "Kimi Code 会员套餐路由:`{route}`(套餐控制台:`{console}`;使用模型 k3)。Codewhale 绝不会导入 Kimi CLI 凭据。",
"LinksTip": "提示:使用所显示提供商的环境变量,或通过 `codewhale auth set --provider ` 保存密钥。",
"SubagentsFetching": "正在获取当前会话子代理...",
- "SubagentsNoCurrentSessionPodWorkers": "当前会话没有 Pod 工作器。",
- "SubagentsCurrentSessionPodWorkersTitle": "当前会话的 Pod 工作器",
- "SubagentsCurrentSessionPodWorkerRoles": "子代理角色是当前会话的 Pod 工作器角色。",
- "SubagentsCurrentSessionPodWorkersStatus": "当前会话的 Pod 工作器:共 {count} 个",
- "SubagentsEmptyGuidance": "使用 /pod 配置角色和启动设置。",
+ "SubagentsNoCurrentSessionPodWorkers": "当前会话没有舰队工作器。",
+ "SubagentsCurrentSessionPodWorkersTitle": "当前会话的舰队工作器",
+ "SubagentsCurrentSessionPodWorkerRoles": "子代理角色是当前会话的舰队工作器角色。",
+ "SubagentsCurrentSessionPodWorkersStatus": "当前会话的舰队工作器:共 {count} 个",
+ "SubagentsEmptyGuidance": "使用 /fleet 配置角色和启动设置。",
"SubagentsStatusRunning": "运行中",
"SubagentsStatusCompleted": "已完成",
"SubagentsStatusInterrupted": "已中断",
@@ -735,7 +736,7 @@
"HomeHistory": "历史:",
"HomeTokens": "令牌:",
"HomeQueued": "队列:",
- "HomeSubagents": "当前会话的 Pod 工作器:",
+ "HomeSubagents": "当前会话的舰队工作器:",
"HomeSkill": "技能:",
"HomeQuickActions": "快捷操作",
"HomeQuickLinks": "/links - Codewhale、社区与提供商链接",
@@ -743,7 +744,7 @@
"HomeQuickConfig": "/config - 查看与修改设置",
"HomeQuickSettings": "/settings - 显示已保存的设置",
"HomeQuickModel": "/model - 切换或查看模型",
- "HomeQuickSubagents": "/pod workers - 当前会话子代理",
+ "HomeQuickSubagents": "/fleet workers - 当前会话子代理",
"HomeQuickTaskList": "/task list - 显示后台任务队列",
"HomeQuickHelp": "/help - 显示帮助",
"HomeQuickWorkspace": "/workspace - 切换目录或 worktree",
@@ -1119,7 +1120,7 @@
"ToolFamilyPatch": "修补",
"ToolFamilyRun": "运行",
"ToolFamilyFind": "搜索",
- "ToolFamilyDelegate": "委派",
+ "ToolFamilyDelegate": "代理",
"ToolFamilyFanout": "扇出",
"ToolFamilyRlm": "rlm",
"ToolFamilyVerify": "验证",
@@ -1229,19 +1230,14 @@
"HotbarActionModeOperateName": "Operate 模式",
"HotbarActionModeOperateDescription": "让 Pod 并行开展工作。",
"HomeOperateModeTip": "Operate — 让 Pod 并行开展工作",
- "HomeOperateModeFleetTip": " 内置角色使用本会话的模型;/pod setup 可自定义",
+ "HomeOperateModeFleetTip": " 内置角色使用本会话的模型;/fleet setup 可自定义",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "让 Pod 并行开展工作",
+ "AppModeOperateHint": "把提示词变成目标:并行工作器,完成前先验证",
"HelpSubtitle": "概念、命令与快捷键",
"CommandPaletteTitle": "命令",
"CommandPaletteSubtitle": "查找并执行一个操作",
"ConfigSubtitle": "设置优先;原始键在高级详情",
"LaunchStartTitle": "从这里开始",
- "LaunchMenuConnect": "连接模型",
- "LaunchMenuWork": "工作 · 当前文件夹",
- "LaunchMenuChat": "聊天 · 只读",
- "LaunchMenuTheme": "主题",
- "LaunchMenuHelp": "帮助",
"LaunchWorkDescription": "在此文件夹中使用本地工具;更改遵循你的审批策略。",
"LaunchChatDescription": "仅对话和规划;不会更改文件。",
"LaunchWorkspaceGitReady": "工作区 · {name} · Git 工作区",
@@ -1254,15 +1250,7 @@
"LaunchWorkspaceFolderShort": "文件夹",
"LaunchProviderConfiguredShort": "提供商已配置",
"LaunchProviderSetupShort": "设置提供商",
- "LaunchMenuNewSession": "新建会话",
- "LaunchMenuNewWorktree": "新建工作树",
- "LaunchMenuWorktreeCompact": "工作树",
- "LaunchMenuChatCompact": "聊天",
- "LaunchMenuResumeSession": "恢复会话",
"LaunchMenuChangelog": "更新日志",
- "LaunchMenuQuit": "退出",
- "LaunchMenuUnavailable": "不可用",
- "LaunchMenuSavedCount": "已保存 {count} 个",
"LaunchWorktreePrompt": "为分支/工作树命名,或按 Enter 使用自动名称。",
"LaunchWorktreeNeedsGit": "新建工作树需要 Git 仓库。",
"LaunchWorktreeNameLabel": "工作树名称",
@@ -1275,7 +1263,12 @@
"LaunchWorktreeFailed": "工作树创建失败:{error}",
"LaunchNoSavedSessions": "此工作区没有已保存的会话。",
"LaunchComposerHint": "Enter 发送 · Shift+Enter 换行 · Esc 返回",
- "LaunchComposerFocusHint": "Tab 输入",
+ "LaunchNoModelConnected": "未连接模型",
+ "LaunchRunCommand": "运行 {command}",
+ "LaunchMcpConnectedOne": "已连接 1 个 MCP 服务器",
+ "LaunchMcpConnectedMany": "已连接 {count} 个 MCP 服务器",
+ "LaunchMcpNeedsSignInOne": "1 个需要登录",
+ "LaunchMcpNeedsSignInMany": "{count} 个需要登录",
"PhaseIdle": "空闲",
"PhaseDraft": "草稿",
"PhaseWorking": "工作中",
@@ -1300,6 +1293,8 @@
"FooterHintOutput": "输出",
"FooterHintContext": "上下文",
"InfoLineHelp": "帮助",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "鲸鱼",
"InfoLineAutomation": "自动化",
"InfoLineNotConnected": "未连接",
@@ -1395,6 +1390,7 @@
"PickerActionApply": "应用",
"PickerActionSetStartupDefault": "设为启动默认值",
"PickerActionPin": "固定",
+ "PickerActionFleet": "船队",
"PickerActionCancel": "取消",
"PickerActionClear": "清除",
"PickerActionClearSearch": "清除搜索",
@@ -1482,10 +1478,37 @@
"FleetRosterLayersLabel": "保存位置",
"FleetRosterLayerWins": "生效",
"FleetRosterLayerIgnored": "被忽略的副本",
- "FleetReadyNotice": "Pod 已就绪 · /pod 打开角色 · /pod setup 调整成员模型",
+ "FleetReadyNotice": "Pod 已就绪 · /fleet 打开角色 · /fleet setup 调整成员模型",
"FleetProfileIdentityVerifyFailed": "无法校验现有配置标识({error});请先修复列出的文件再保存。",
"FleetProfileIdConflict": "配置 id `{id}` 已被 {path} 占用;请重新起草为不同的角色或先移除旧文件。",
"FleetProfileProviderUnconfigured": "配置指定的 provider `{provider}` 尚未配置凭据({env});请先在 /provider 中设置,再保存。",
+ "FleetModelAdded": "已将 {route} 添加到 fleet `{fleet}`",
+ "FleetModelAddedAs": "已将 {route} 作为 {roles} 添加到 fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": "(新的个人 fleet,已选中)",
+ "FleetModelAddedSelectedNote": "(已选中)",
+ "FleetModelRemoved": "已将 {route} 从 fleet `{fleet}` 中移除",
+ "FleetModelRemovedRoles": "已将 {route}({roles})从 fleet `{fleet}` 中移除",
+ "FleetModelUnchanged": "{route} 保留在 fleet `{fleet}` 中:{reason}",
+ "FleetModelReasonOperatorRoute": "这是你当前的模型(fleet 的操作员路由);请切换模型或用 /fleet save 更改",
+ "FleetModelReasonAlreadyPresent": "已在 fleet 中",
+ "FleetModelErrorNeedsRoute": "fleet 模型需要同时提供 provider ID 和模型 ID",
+ "FleetModelErrorNoSelection": "未选择 fleet;你的 fleet 仅包含会话模型",
+ "FleetModelErrorOperatorRoute": "{route} 是 fleet `{fleet}` 的操作员路由;请用 /fleet save 更改,而不是移除",
+ "FleetModelErrorNotInFleet": "{route} 不在 fleet `{fleet}` 中",
+ "FleetModelsEmpty": "你的 fleet 仅包含会话模型。添加一个:/fleet add [role…](或在 /model 的某一行按 ⇧F)。",
+ "FleetModelsHeader": "你的 fleet `{fleet}`({count} 个模型)",
+ "FleetModelsBroken": "无法加载所选 fleet:{error}。请修复文件,或用 /fleet fleets 选择另一个。",
+ "FleetModelsFooter": "添加:/fleet add [role…] · 移除:/fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} 每百万 token",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "工具",
+ "FleetAddUsage": "用法:/fleet add [role…] — 例如 /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` 不是已配置的 provider。请先在 ~/.codewhale/config.toml 中配置,或用 /provider 切换到它,再添加到 fleet。",
+ "FleetAddModelNotServed": "{provider} 在当前目录中不提供 `{model}`;运行 /models 查看它提供的模型,或用其列出的精确 ID 执行 /fleet add。",
+ "FleetAddFailed": "无法添加到 fleet:{error}",
+ "FleetRemoveUsage": "用法:/fleet remove ",
+ "FleetRemoveFailed": "无法从 fleet 移除:{error}",
+ "FleetToggleFailed": "无法更新 fleet:{error}",
"FleetDestStepTitle": "这个配置文件应保存在哪里?",
"FleetDestStepSubtitle": "在最后一步确认之前不会写入任何内容。",
"FleetDestProjectLabel": "此项目",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review 已阻止 '{tool}'(确定性策略):{reason}",
"AutoReviewReceiptHeld": "Auto-Review 未暂停即搁置了 '{tool}';已拒绝(需要人工决定 — 切换到 Ask)",
"FooterHintEscInterrupt": "Esc 中断",
+ "PostureHintEnterAgain": "再按 {enter} 立即发送 · {steer} 引导",
"PermissionsPostureHeader": "当前访问级别:{posture}",
"PermissionsPostureAsk": "Ask:会改变权限、成本、范围或结果的 tool 调用会弹出提示;已证明安全的只读调用无需提示即可运行。上面的 ask 规则始终强制提示。",
"PermissionsPostureAuto": "Auto-Review:从不弹出提示。确定性策略允许已证明安全的调用,并硬性阻止类似发布或破坏性的后台工作;无法证明安全的调用交给一次性模型守护者,其给出理由后允许或拒绝(高或严重风险绝不自动运行;审查失败即拒绝,故障关闭)。需要人工决定的搁置会被拒绝而非隐藏。每个此类决定都会作为备注写入对话记录和审计日志。",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "只读 shell",
"AgentFocusPostureShellNone": "无 shell",
"GoalReceiptSet": "已设定目标:“{objective}” · /goal 查看进度 · /goal pause 或 /goal clear 停止",
+ "GoalReceiptSetOperate": "◆ 已设定目标 · Operate 会持续工作直到验证完成 · /goal 可编辑",
"GoalControlAccepted": "目标控制已保存;将在下一个安全边界应用。",
"GoalControlRuntimeUnavailable": "目标控制已保存,但运行时当前不可用。重启后将恢复该状态。",
"GoalStatusIdleHint": "当前未运行 — 发送消息或 /goal resume 以继续",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "阻止所有需要审批的工具。",
"ConfigChoiceDetailModeAgent": "以可协作并使用工具的状态开始。",
"ConfigChoiceDetailModePlan": "在只读的规划工作区中开始。",
- "ConfigChoiceDetailModeOperate": "作为协调者开始,把工作委派给受限的工作者。",
+ "ConfigChoiceDetailModeOperate": "Operate 把你的提示变成目标并行推进:可拆分的流交给后台工作者,停止前先验证。",
"ConfigChoiceDetailPlacementTop": "在对话记录上方显示任务、待办和工作者。",
"ConfigChoiceDetailPlacementLeft": "终端足够宽时,在左侧边栏显示任务、待办和工作者。",
"ConfigChoiceDetailPlacementRight": "终端足够宽时,在右侧边栏显示任务、待办和工作者。",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "打开插件 · 信任、启用或诊断",
"ConfigHintMcpConfigPath": "mcp.json 的路径",
"ConfigHintFleetMaxSpawnDepth": "0 禁止子代理;默认 3(与子代理同一维度);上限 8",
- "ConfigHintFeatureSubagents": "只读的功能开关状态;/pod setup 是面向用户的路径",
+ "ConfigHintFeatureSubagents": "只读的功能开关状态;/fleet setup 是面向用户的路径",
"ConfigHintFeatureWebSearch": "网页搜索工具的只读功能开关状态",
"ConfigHintFeatureApplyPatch": "补丁编辑工具的只读功能开关状态",
"ConfigHintFeatureMcp": "MCP 工具的只读功能开关状态",
"ConfigHintFeatureExecPolicy": "执行策略工具的只读功能开关状态",
"ConfigHintFeatureVisionModel": "视觉/模型图像支持的测试版功能开关",
"ConfigHintGoalCommand": "/goal 设置目标、预算和工作上下文状态",
- "ConfigHintWorkflow": "/workflow 运行带扇出/扇入运行卡片的脚本化操作"
+ "ConfigHintWorkflow": "/workflow 运行带扇出/扇入运行卡片的脚本化操作",
+ "LaunchMenuNewWorktree": "新建工作树",
+ "LaunchMenuResume": "恢复会话",
+ "LaunchMenuQuit": "退出",
+ "LaunchNoticeClaude": "从 Claude Code 过来?按 ctrl+r 恢复会话",
+ "ReceiptSessionHooks": "钩子 {count}"
}
diff --git a/crates/tui/locales/zh-Hant.json b/crates/tui/locales/zh-Hant.json
index 1eb6700cd1..d9ff2e5768 100644
--- a/crates/tui/locales/zh-Hant.json
+++ b/crates/tui/locales/zh-Hant.json
@@ -90,7 +90,7 @@
"AppModeAuto": "自動",
"AppModeAutoHint": "啟用 shell,並自動進行風險審查",
"AppModeOperate": "Operate",
- "AppModeOperateHint": "讓 Pod 並行展開工作",
+ "AppModeOperateHint": "把提示詞變成目標:並行工作器,完成前先驗證",
"AppModePlan": "Plan",
"AppModePlanHint": "先唯讀調研,行動前提出計畫",
"AppModeYolo": "完全存取(已棄用標籤)",
@@ -440,7 +440,7 @@
"CmdStructcopyReceiptTooLarge": "結構化複製回執元資料超過 {bytes} 字節的輸出上限;拒絕輸出",
"CmdStructcopyUnavailable": "所選{kind}無法使用;未複製任何內容",
"CmdStructcopyUsageError": "結構化複製請求無效。用法:{usage}",
- "CmdSubagentsDescription": "/pod workers 的相容捷徑(目前工作階段子代理)",
+ "CmdSubagentsDescription": "/fleet workers 的相容捷徑(目前工作階段子代理)",
"CmdAdvisorDescription": "切換此工作階段的背景 advisor 監看器",
"CmdSystemDescription": "顯示目前系統提示詞",
"CmdTaskDescription": "管理後台任務",
@@ -555,6 +555,7 @@
"ScreenModeMouseCaptureOn": "滑鼠擷取已開啟。",
"ScreenModeMouseCaptureOff": "滑鼠擷取已關閉;選取由終端機管理。",
"ScreenModeUnchanged": "畫面未變更:{reason}。",
+ "ImageInputRejectedResent": "{model} 不接受圖片,已將圖片替換為文字重新傳送;請使用 image_ocr 讀取圖片",
"ConfigLabelLocale": "語言",
"ConfigLabelLowMotion": "減少動態效果",
"ConfigLabelManagedAllowShell": "Shell 存取(受管理)",
@@ -815,6 +816,33 @@
"FleetProfileIdConflict": "設定 id `{id}` 已被 {path} 佔用;請重新起草為不同的角色或先移除舊檔案。",
"FleetProfileIdentityVerifyFailed": "無法校驗現有設定標識({error});請先修復列出的檔案再儲存。",
"FleetProfileProviderUnconfigured": "設定指定的 provider `{provider}` 尚未設定憑據({env});請先在 /provider 中設定,再儲存。",
+ "FleetModelAdded": "已將 {route} 加入 fleet `{fleet}`",
+ "FleetModelAddedAs": "已將 {route} 作為 {roles} 加入 fleet `{fleet}`",
+ "FleetModelAddedCreatedNote": "(新的個人 fleet,已選取)",
+ "FleetModelAddedSelectedNote": "(已選取)",
+ "FleetModelRemoved": "已將 {route} 從 fleet `{fleet}` 移除",
+ "FleetModelRemovedRoles": "已將 {route}({roles})從 fleet `{fleet}` 移除",
+ "FleetModelUnchanged": "{route} 保留在 fleet `{fleet}` 中:{reason}",
+ "FleetModelReasonOperatorRoute": "這是你目前的模型(fleet 的操作員路由);請切換模型或用 /fleet save 變更",
+ "FleetModelReasonAlreadyPresent": "已在 fleet 中",
+ "FleetModelErrorNeedsRoute": "fleet 模型需要同時提供 provider ID 和模型 ID",
+ "FleetModelErrorNoSelection": "未選取 fleet;你的 fleet 僅包含工作階段模型",
+ "FleetModelErrorOperatorRoute": "{route} 是 fleet `{fleet}` 的操作員路由;請用 /fleet save 變更,而不是移除",
+ "FleetModelErrorNotInFleet": "{route} 不在 fleet `{fleet}` 中",
+ "FleetModelsEmpty": "你的 fleet 僅包含工作階段模型。新增一個:/fleet add [role…](或在 /model 的某一列按 ⇧F)。",
+ "FleetModelsHeader": "你的 fleet `{fleet}`({count} 個模型)",
+ "FleetModelsBroken": "無法載入所選的 fleet:{error}。請修正檔案,或用 /fleet fleets 選擇另一個。",
+ "FleetModelsFooter": "新增:/fleet add [role…] · 移除:/fleet remove ",
+ "FleetModelsFactPrice": "${input}/{output} 每百萬 token",
+ "FleetModelsFactContext": "{context}k ctx",
+ "FleetModelsFactTools": "工具",
+ "FleetAddUsage": "用法:/fleet add [role…] — 例如 /fleet add openrouter z-ai/glm-5.3-flash explore",
+ "FleetAddProviderUnconfigured": "`{provider}` 不是已設定的 provider。請先在 ~/.codewhale/config.toml 中設定,或用 /provider 切換到它,再加入 fleet。",
+ "FleetAddModelNotServed": "{provider} 在目前的目錄中不提供 `{model}`;執行 /models 查看它提供的模型,或用其列出的精確 ID 執行 /fleet add。",
+ "FleetAddFailed": "無法加入 fleet:{error}",
+ "FleetRemoveUsage": "用法:/fleet remove ",
+ "FleetRemoveFailed": "無法從 fleet 移除:{error}",
+ "FleetToggleFailed": "無法更新 fleet:{error}",
"FleetDestStepTitle": "這個設定檔應儲存在哪裡?",
"FleetDestStepSubtitle": "在最後一步確認之前不會寫入任何內容。",
"FleetDestProjectLabel": "此專案",
@@ -844,7 +872,7 @@
"FleetReviewSavesTo": "儲存到",
"FleetModelRowBlockedNotice": "無法選擇: {reason}。請在 /provider 中設定或選擇其他列。",
"FleetDestProjectDisabledSave": "本次工作階段已停用專案設定檔 (--no-project-config);未儲存任何內容。請選擇個人,或不帶該參數重新啟動。",
- "FleetReadyNotice": "Pod 已就緒 · /pod 開啟角色 · /pod setup 調整成員模型",
+ "FleetReadyNotice": "Pod 已就緒 · /fleet 開啟角色 · /fleet setup 調整成員模型",
"FleetRosterHeaderLabel": "pod",
"FleetRosterMembersCount": "{count} 個成員",
"FleetRosterOperatorFirst": "協調者統領 · 工作階段模型驅動此 Pod",
@@ -864,6 +892,8 @@
"FooterBalancePrefix": "餘額",
"FooterHintContext": "上下文",
"InfoLineHelp": "幫助",
+ "InfoLineContext": "ctx",
+ "InfoLineTtft": "ttft",
"InfoLineWhales": "鯨魚",
"InfoLineAutomation": "自動化",
"InfoLineNotConnected": "未連線",
@@ -929,7 +959,7 @@
"HomeMode": "模式:",
"HomeModeTips": "模式提示",
"HomeModel": "模型:",
- "HomeOperateModeFleetTip": " 內置角色使用本工作階段的模型;/pod setup 可自定義",
+ "HomeOperateModeFleetTip": " 內置角色使用本工作階段的模型;/fleet setup 可自定義",
"HomeOperateModeTip": "Operate — 讓 Pod 並行展開工作",
"HomePlanModeChecklistTip": " 提交計畫與待做進程後,再切到 Act 或 Operate",
"HomePlanModeTip": "Plan — 實現前先調研與設計",
@@ -944,10 +974,10 @@
"HomeQuickModel": "/model - 切換或檢視模型",
"HomeQuickSettings": "/settings - 顯示已儲存的設定",
"HomeQuickSkills": "/skills - 列出可用技能",
- "HomeQuickSubagents": "/pod workers - 目前工作階段子代理",
+ "HomeQuickSubagents": "/fleet workers - 目前工作階段子代理",
"HomeQuickTaskList": "/task list - 顯示後台任務佇列",
"HomeSkill": "技能:",
- "HomeSubagents": "目前工作階段的 Pod 工作器:",
+ "HomeSubagents": "目前工作階段的艦隊工作器:",
"HomeTokens": "令牌:",
"HomeWorkspace": "工作區:",
"HomeYoloModeCaution": " 破壞性操作可能立即執行;不確定時用 Ask",
@@ -1006,7 +1036,7 @@
"KbCommandPalette": "開啟命令面板",
"KbCompactInspector": "開啟壓縮會話上下文檢查器",
"KbCompactContext": "壓縮對話上下文",
- "KbCompleteCycleModes": "補全 /command,或在輸入框為空時切換模式(Plan → Act → Operate)",
+ "KbCompleteCycleModes": "補全 /command,或在輸入框為空時切換模式(Plan → Work → Operate)",
"KbContextMenu": "開啟上下文操作選單,用於粘貼、選擇、訊息詳情、上下文和說明",
"KbCopySelection": "複製 Codewhale 目前選中內容",
"KbCyclePermissions": "切換存取層級:詢問 → 自動審查 → 完整存取(Shift+Tab)",
@@ -1061,11 +1091,6 @@
"LaunchHintOpen": "開啟",
"LaunchMenuChangelog": "更新日誌",
"LaunchStartTitle": "從這裡開始",
- "LaunchMenuConnect": "連線模型",
- "LaunchMenuWork": "工作 · 目前資料夾",
- "LaunchMenuChat": "聊天 · 唯讀",
- "LaunchMenuTheme": "主題",
- "LaunchMenuHelp": "說明",
"LaunchWorkDescription": "在此資料夾中使用本機工具;變更遵循你的核准政策。",
"LaunchChatDescription": "僅對話和規劃;不會變更檔案。",
"LaunchWorkspaceGitReady": "工作區 · {name} · Git 工作區",
@@ -1078,17 +1103,14 @@
"LaunchWorkspaceFolderShort": "資料夾",
"LaunchProviderConfiguredShort": "供應商已設定",
"LaunchProviderSetupShort": "設定供應商",
- "LaunchMenuNewSession": "新建工作階段",
- "LaunchMenuNewWorktree": "新建工作樹",
- "LaunchMenuWorktreeCompact": "工作樹",
- "LaunchMenuChatCompact": "聊天",
- "LaunchMenuQuit": "退出",
- "LaunchMenuResumeSession": "還原工作階段",
- "LaunchMenuSavedCount": "已儲存 {count} 個",
- "LaunchMenuUnavailable": "無法使用",
"LaunchNoSavedSessions": "此工作區沒有已儲存的工作階段。",
"LaunchComposerHint": "Enter 傳送 · Shift+Enter 換行 · Esc 返回",
- "LaunchComposerFocusHint": "Tab 輸入",
+ "LaunchNoModelConnected": "未連線模型",
+ "LaunchRunCommand": "執行 {command}",
+ "LaunchMcpConnectedOne": "已連線 1 個 MCP 伺服器",
+ "LaunchMcpConnectedMany": "已連線 {count} 個 MCP 伺服器",
+ "LaunchMcpNeedsSignInOne": "1 個需要登入",
+ "LaunchMcpNeedsSignInMany": "{count} 個需要登入",
"LaunchSavedSessionSingular": "已儲存 1 個工作階段",
"LaunchSavedSessionsPlural": "已儲存 {count} 個工作階段",
"LaunchTipFlags": "提示:-w 開啟工作區;-r 直接還原",
@@ -1186,6 +1208,7 @@
"PickerActionApply": "套用",
"PickerActionSetStartupDefault": "設為啟動預設值",
"PickerActionPin": "釘選",
+ "PickerActionFleet": "船隊",
"PickerActionBrowseAll": "瀏覽全部",
"PickerActionCancel": "取消",
"PickerActionClear": "清除",
@@ -1565,11 +1588,11 @@
"StepfunPlanApiKeyHint": "使用 StepFun Step Plan 訂閱時,請使用為 Step Plan 簽發的金鑰,而不是按量付費金鑰。",
"StepfunPlanRouteHint": "此路由使用 {route},並從你的 Step Plan 額度中扣除。",
"SubagentsFetching": "正在取得目前工作階段子代理...",
- "SubagentsNoCurrentSessionPodWorkers": "目前工作階段沒有 Pod 工作器。",
- "SubagentsCurrentSessionPodWorkersTitle": "目前工作階段的 Pod 工作器",
- "SubagentsCurrentSessionPodWorkerRoles": "子代理角色是目前工作階段的 Pod 工作器角色。",
- "SubagentsCurrentSessionPodWorkersStatus": "目前工作階段的 Pod 工作器:共 {count} 個",
- "SubagentsEmptyGuidance": "使用 /pod 設定角色與啟動設定。",
+ "SubagentsNoCurrentSessionPodWorkers": "目前工作階段沒有艦隊工作器。",
+ "SubagentsCurrentSessionPodWorkersTitle": "目前工作階段的艦隊工作器",
+ "SubagentsCurrentSessionPodWorkerRoles": "子代理角色是目前工作階段的艦隊工作器角色。",
+ "SubagentsCurrentSessionPodWorkersStatus": "目前工作階段的艦隊工作器:共 {count} 個",
+ "SubagentsEmptyGuidance": "使用 /fleet 設定角色與啟動設定。",
"SubagentsStatusRunning": "執行中",
"SubagentsStatusCompleted": "已完成",
"SubagentsStatusInterrupted": "已中斷",
@@ -1611,7 +1634,7 @@
"ThemeTreatmentDeepseaActive": "效果 Deepsea — 已啟用",
"ThemeTreatmentDeepseaUnavailable": "效果 Deepsea 無法使用 — 背景由終端接管",
"ThinkingControlledByAutoRouting": "思考由自動模型路由控制;請先選擇固定模型。",
- "ToolFamilyDelegate": "委派",
+ "ToolFamilyDelegate": "代理",
"ToolFamilyFanout": "扇出",
"ToolFamilyFind": "搜尋",
"ToolFamilyGeneric": "工具",
@@ -1763,6 +1786,7 @@
"AutoReviewReceiptDeterministicBlocked": "Auto-Review 已封鎖 '{tool}'(確定性策略):{reason}",
"AutoReviewReceiptHeld": "Auto-Review 未暫停即擱置了 '{tool}';已拒絕(需要人工決定 — 切換到 Ask)",
"FooterHintEscInterrupt": "Esc 中斷",
+ "PostureHintEnterAgain": "再按 {enter} 立即傳送 · {steer} 引導",
"PermissionsPostureHeader": "目前存取層級:{posture}",
"PermissionsPostureAsk": "Ask:會改變權限、成本、範圍或結果的 tool 呼叫會開啟提示;已證明安全的唯讀呼叫無需提示即可執行。上面的 ask 規則始終強制提示。",
"PermissionsPostureAuto": "Auto-Review:從不開啟提示。確定性策略允許已證明安全的呼叫,並硬性封鎖類似發佈或破壞性的背景工作;無法證明安全的呼叫交給一次性模型守護者,其給出理由後允許或拒絕(高或嚴重風險絕不自動執行;審查失敗即拒絕,故障關閉)。需要人工決定的擱置會被拒絕而非隱藏。每個此類決定都會作為備註寫入對話記錄和稽核日誌。",
@@ -1792,6 +1816,7 @@
"AgentFocusPostureShellReadOnly": "唯讀 shell",
"AgentFocusPostureShellNone": "無 shell",
"GoalReceiptSet": "已設定目標:「{objective}」 · /goal 查看進度 · /goal pause 或 /goal clear 停止",
+ "GoalReceiptSetOperate": "◆ 已設定目標 · Operate 會持續工作直到驗證完成 · /goal 可編輯",
"GoalControlAccepted": "目標控制已儲存;將在下一個安全邊界套用。",
"GoalControlRuntimeUnavailable": "目標控制已儲存,但執行階段目前無法使用。重新啟動後將還原此狀態。",
"GoalStatusIdleHint": "目前未執行 — 傳送訊息或 /goal resume 以繼續",
@@ -1922,7 +1947,7 @@
"ConfigChoiceDetailNever": "封鎖所有需要核准的工具。",
"ConfigChoiceDetailModeAgent": "以可協作並使用工具的狀態開始。",
"ConfigChoiceDetailModePlan": "在唯讀的規劃工作區中開始。",
- "ConfigChoiceDetailModeOperate": "作為協調者開始,把工作委派給受限的工作者。",
+ "ConfigChoiceDetailModeOperate": "Operate 把你的提示變成目標並行推進:可拆分的流交給背景工作者,停止前先驗證。",
"ConfigChoiceDetailPlacementTop": "在對話記錄上方顯示任務、待辦和工作者。",
"ConfigChoiceDetailPlacementLeft": "終端機夠寬時,在左側欄顯示任務、待辦和工作者。",
"ConfigChoiceDetailPlacementRight": "終端機夠寬時,在右側欄顯示任務、待辦和工作者。",
@@ -1986,12 +2011,17 @@
"ConfigHintPluginsOpen": "開啟外掛 · 信任、啟用或診斷",
"ConfigHintMcpConfigPath": "mcp.json 的路徑",
"ConfigHintFleetMaxSpawnDepth": "0 禁止子代理;預設 3(與子代理同一維度);上限 8",
- "ConfigHintFeatureSubagents": "唯讀的功能開關狀態;/pod setup 是面向使用者的路徑",
+ "ConfigHintFeatureSubagents": "唯讀的功能開關狀態;/fleet setup 是面向使用者的路徑",
"ConfigHintFeatureWebSearch": "網頁搜尋工具的唯讀功能開關狀態",
"ConfigHintFeatureApplyPatch": "修補程式編輯工具的唯讀功能開關狀態",
"ConfigHintFeatureMcp": "MCP 工具的唯讀功能開關狀態",
"ConfigHintFeatureExecPolicy": "執行政策工具的唯讀功能開關狀態",
"ConfigHintFeatureVisionModel": "視覺/模型影像支援的測試版功能開關",
"ConfigHintGoalCommand": "/goal 設定目標、預算和工作情境狀態",
- "ConfigHintWorkflow": "/workflow 執行帶扇出/扇入執行卡片的腳本化操作"
+ "ConfigHintWorkflow": "/workflow 執行帶扇出/扇入執行卡片的腳本化操作",
+ "LaunchMenuNewWorktree": "新增工作樹",
+ "LaunchMenuResume": "恢復會話",
+ "LaunchMenuQuit": "結束",
+ "LaunchNoticeClaude": "從 Claude Code 過來?按 ctrl+r 恢復會話",
+ "ReceiptSessionHooks": "掛鉤 {count}"
}
diff --git a/crates/tui/src/commands/groups/config/config.rs b/crates/tui/src/commands/groups/config/config.rs
index f2a54f7ff6..aabf1caad6 100644
--- a/crates/tui/src/commands/groups/config/config.rs
+++ b/crates/tui/src/commands/groups/config/config.rs
@@ -644,16 +644,19 @@ pub fn sidebar(app: &mut App, arg: Option<&str>) -> CommandResult {
_ => None,
};
let panel = match value.as_str() {
- "tasks" | "activity" | "live" | "running" => {
- Some(crate::tui::work_surface::RailPanel::Tasks)
- }
+ "tasks" | "activity" | "live" | "running" | "pinned" | "work" | "plan"
+ | "todos" => Some(crate::tui::work_surface::RailPanel::Tasks),
"agents" | "subagents" | "sub-agents" => {
Some(crate::tui::work_surface::RailPanel::Agents)
}
- "context" | "session" => Some(crate::tui::work_surface::RailPanel::Context),
- "pinned" | "work" | "plan" | "todos" => {
- Some(crate::tui::work_surface::RailPanel::Pinned)
+ "background" | "shells" | "jobs" => {
+ Some(crate::tui::work_surface::RailPanel::Background)
}
+ "files" | "changes" => Some(crate::tui::work_surface::RailPanel::Files),
+ "notepad" | "notes" => Some(crate::tui::work_surface::RailPanel::Notepad),
+ "context" | "session" => Some(crate::tui::work_surface::RailPanel::Context),
+ "git" | "branch" => Some(crate::tui::work_surface::RailPanel::Git),
+ "price" | "cost" => Some(crate::tui::work_surface::RailPanel::Price),
_ => None,
};
match (placement, panel) {
@@ -673,7 +676,7 @@ pub fn sidebar(app: &mut App, arg: Option<&str>) -> CommandResult {
}
}
(None, Some(panel)) => {
- app.work_surface.panel = panel;
+ crate::tui::work_surface::select_dock_panel(app, panel);
if persist {
let result = set_config_value(app, "rail_panel", panel.as_setting(), true);
if result.is_error {
@@ -2101,6 +2104,11 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
model
};
app.set_model_selection(model.clone());
+ app.fleet_roster_stale |= crate::fleet::members::auto_enroll_fleet_model(
+ &app.workspace,
+ app.provider_identity_for_persistence(),
+ &model,
+ );
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
@@ -2786,6 +2794,11 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
) && let Some(ref model) = settings.default_model
{
app.set_model_selection(model.clone());
+ app.fleet_roster_stale |= crate::fleet::members::auto_enroll_fleet_model(
+ &app.workspace,
+ app.provider_identity_for_persistence(),
+ model,
+ );
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
diff --git a/crates/tui/src/commands/groups/core/core.rs b/crates/tui/src/commands/groups/core/core.rs
index f62c186254..e4a0a3bff6 100644
--- a/crates/tui/src/commands/groups/core/core.rs
+++ b/crates/tui/src/commands/groups/core/core.rs
@@ -369,6 +369,11 @@ pub fn model(app: &mut App, model_name: Option<&str>) -> CommandResult {
app.provider_models
.insert(provider_identity.clone(), model_id.clone());
app.enable_provider_model(&provider_identity, &model_id);
+ app.fleet_roster_stale |= crate::fleet::members::auto_enroll_fleet_model(
+ &app.workspace,
+ &provider_identity,
+ &model_id,
+ );
// Route changes are temporary by default: nothing is written here.
// The route-save prompt offers the explicit persistence choices.
app.note_session_route_change(&provider_identity, &model_id);
diff --git a/crates/tui/src/commands/groups/core/fleet.rs b/crates/tui/src/commands/groups/core/fleet.rs
index cea8ea9f24..bba391c2e0 100644
--- a/crates/tui/src/commands/groups/core/fleet.rs
+++ b/crates/tui/src/commands/groups/core/fleet.rs
@@ -1,9 +1,9 @@
//! `/pod` command (`/fleet` remains a compatibility alias).
//!
-//! Pod = who. Bare `/pod` (and `/pod roster`) opens the familiar roster
-//! surface for the selected Pod; `/pod setup` opens the authoring wizard.
+//! Fleet = who. Bare `/pod` (and `/pod roster`) opens the familiar roster
+//! surface for the selected Fleet; `/pod setup` opens the authoring wizard.
//! `/pod pods` (compatibility alias: `fleets`; other aliases: `saved`, `manage`)
-//! opens the named-Pod picker
+//! opens the named-Fleet picker
//! for switching between saved configurations — never the primary face.
//! `/pod list|status|interrupt|resume` are control-plane verbs that run
//! against the **durable** workspace ledger through the shared contract in
@@ -11,7 +11,7 @@
//!
//! `/pod status` used to show the current TUI session's sub-agents. That was
//! a different thing wearing the same name: session sub-agents are not the
-//! durable Pod ledger, and a run started by `codewhale pod run` never
+//! durable Fleet ledger, and a run started by `codewhale pod run` never
//! appeared. The session view is still reachable as `/pod workers` (and
//! `/subagents`), now labelled as what it is.
@@ -19,16 +19,17 @@ use codewhale_lane::control::operations_for_domain;
use codewhale_lane::{ControlDomain, ControlOperation, ControlSurface};
use crate::commands::traits::{CommandInfo, RegisterCommand};
+use crate::config::Config;
use crate::fleet::control::execute_fleet_control;
-use crate::localization::MessageId;
+use crate::localization::{Locale, MessageId, tr};
use crate::tui::app::{App, AppAction};
use super::CommandResult;
pub(in crate::commands) const COMMAND_INFO: CommandInfo = CommandInfo {
- name: "pod",
- aliases: &["fleet", "loadout", "party"],
- usage: "/pod [members|setup|pods|workers|save|save-as|list|status|runs|interrupt |resume ]",
+ name: "fleet",
+ aliases: &["pod", "loadout", "party"],
+ usage: "/fleet [members|models|add [role…]|remove |setup|fleets|workers|save|save-as|list|status|runs|interrupt |resume ]",
description_id: MessageId::CmdFleetDescription,
};
@@ -36,15 +37,15 @@ pub(in crate::commands) struct FleetCmd;
fn help_text() -> String {
let mut out = String::from(
- "Usage: /pod [members|setup|pods|workers|save|save-as|list|status|runs|interrupt |resume ]\n\n\
- Pod is who. /pod (or /pod members) opens the Pod member list and orchestration state — \
- each member's role, model, and access. /pod setup opens the authoring wizard. \
- /pod pods (or saved/manage) switches between named saved Pods; /pod fleets remains \
+ "Usage: /fleet [members|setup|fleets|workers|save|save-as|list|status|runs|interrupt |resume ]\n\n\
+ Fleet is who. /fleet (or /fleet members) opens the fleet member list and orchestration \
+ state — each member's role, model, and access. /fleet setup opens the authoring wizard. \
+ /fleet fleets (or saved/manage) switches between named saved fleets; /fleet pods remains \
accepted as a compatibility alias.\n\n\
- /pod list, status, interrupt, and resume act on the durable .codewhale/fleet.jsonl \
- ledger for this workspace — the same records `codewhale pod` reads and writes. \
- /pod workers (and /subagents) shows sub-agents in the current TUI session only, which \
- is a different set: it does not include durable Pod runs. /fleet and `codewhale fleet` \
+ /fleet list, status, interrupt, and resume act on the durable .codewhale/fleet.jsonl \
+ ledger for this workspace — the same records `codewhale fleet` reads and writes. \
+ /fleet workers (and /subagents) shows sub-agents in the current TUI session only, which \
+ is a different set: it does not include durable fleet runs. /pod and `codewhale pod` \
remain accepted as compatibility aliases; the ledger file, saved rosters, and config \
tables keep the Fleet name.\n",
);
@@ -69,6 +70,158 @@ fn split_verb(arg: Option<&str>) -> Option<(&str, Option<&str>)> {
})
}
+fn fleet_models_text(app: &App) -> String {
+ use crate::fleet::members::fleet_models;
+ let locale = app.ui_locale;
+ let models = match fleet_models(&app.workspace) {
+ Ok(models) if models.is_empty() => {
+ return tr(locale, MessageId::FleetModelsEmpty).into_owned();
+ }
+ Ok(models) => models,
+ // A selected fleet that cannot be read is named, never shown as
+ // "the session model only".
+ Err(error) => {
+ return tr(locale, MessageId::FleetModelsBroken).replace("{error}", &error.to_string());
+ }
+ };
+ let mut lines = vec![
+ tr(locale, MessageId::FleetModelsHeader)
+ .replace("{fleet}", &models[0].fleet)
+ .replace("{count}", &models.len().to_string()),
+ ];
+ for member in &models {
+ let provider = crate::config::ApiProvider::parse(&member.provider);
+ let facts = provider
+ .and_then(|p| crate::provider_lake::catalog_offering_for_model(p, &member.model))
+ .map(|row| {
+ let mut parts = Vec::new();
+ if let Some(cost) = row.cost.as_ref()
+ && let (Some(input), Some(output)) = (cost.input, cost.output)
+ {
+ parts.push(
+ tr(locale, MessageId::FleetModelsFactPrice)
+ .replace("{input}", &format!("{input:.2}"))
+ .replace("{output}", &format!("{output:.2}")),
+ );
+ }
+ if let Some(limit) = row.limit.as_ref()
+ && let Some(context) = limit.context
+ {
+ parts.push(
+ tr(locale, MessageId::FleetModelsFactContext)
+ .replace("{context}", &(context / 1000).to_string()),
+ );
+ }
+ if row.tool_call == Some(true) {
+ parts.push(tr(locale, MessageId::FleetModelsFactTools).into_owned());
+ }
+ parts.join(" · ")
+ })
+ .filter(|facts| !facts.is_empty())
+ .map(|facts| format!(" · {facts}"))
+ .unwrap_or_default();
+ lines.push(format!(
+ " {}/{} · {}{facts}",
+ member.provider,
+ member.model,
+ member.roles_label()
+ ));
+ }
+ lines.push(tr(locale, MessageId::FleetModelsFooter).into_owned());
+ lines.join("\n")
+}
+
+/// Whether `provider_id` names a provider the user has configured — active
+/// route, explicit `[providers.]` table, or usable credentials — in the
+/// **live** `config`, the same source the `/provider` and `/model` pickers
+/// consult. A startup snapshot goes stale after an in-session provider change.
+fn provider_id_is_configured(app: &App, config: &Config, provider_id: &str) -> bool {
+ let provider_id = provider_id.trim();
+ if provider_id.is_empty() {
+ return false;
+ }
+ if let Some(provider) = crate::config::ApiProvider::parse(provider_id) {
+ return crate::config::provider_is_configured_for_active(
+ config,
+ provider,
+ app.api_provider,
+ );
+ }
+ // Named custom provider: allow the active custom route, or any explicit
+ // `[providers.]` table.
+ if app.api_provider == crate::config::ApiProvider::Custom
+ && app
+ .provider_identity_for_persistence()
+ .eq_ignore_ascii_case(provider_id)
+ {
+ return true;
+ }
+ config.providers.as_ref().is_some_and(|providers| {
+ providers
+ .custom
+ .keys()
+ .any(|name| name.eq_ignore_ascii_case(provider_id))
+ })
+}
+
+/// The localized reason `provider` may not enter the fleet, or `None` when
+/// it is configured. Shared by `/fleet add` and the picker's ⇧F so a locked
+/// or unauthenticated provider row is refused on both surfaces alike.
+#[must_use]
+pub(crate) fn fleet_provider_rejection(
+ app: &App,
+ config: &Config,
+ provider: &str,
+) -> Option {
+ (!provider_id_is_configured(app, config, provider)).then(|| {
+ tr(app.ui_locale, MessageId::FleetAddProviderUnconfigured).replace("{provider}", provider)
+ })
+}
+
+/// The localized reason a typed `/fleet add` route is refused when a known
+/// provider's catalog is non-empty and does not list `model`. The picker
+/// skips this check: its rows already come from the catalog or a live list.
+#[must_use]
+pub(crate) fn fleet_catalog_rejection(
+ locale: Locale,
+ provider: &str,
+ model: &str,
+) -> Option {
+ let known = crate::config::ApiProvider::parse(provider)?;
+ let served = crate::provider_lake::all_catalog_models_for_provider(known);
+ (!served.is_empty() && !served.iter().any(|id| id.eq_ignore_ascii_case(model))).then(|| {
+ tr(locale, MessageId::FleetAddModelNotServed)
+ .replace("{provider}", provider)
+ .replace("{model}", model)
+ })
+}
+
+/// `/fleet add [role…]`: parse here; the write happens in
+/// the UI's `AppAction::FleetAddModel` arm, which holds the live config for
+/// provider validation and the engine handle for the roster refresh.
+fn fleet_add(app: &App, target: Option<&str>) -> CommandResult {
+ let mut words = target.unwrap_or_default().split_whitespace();
+ let (Some(provider), Some(model)) = (words.next(), words.next()) else {
+ return CommandResult::error(tr(app.ui_locale, MessageId::FleetAddUsage));
+ };
+ CommandResult::action(AppAction::FleetAddModel {
+ provider: provider.to_string(),
+ model: model.to_string(),
+ roles: words.map(str::to_string).collect(),
+ })
+}
+
+fn fleet_remove(app: &App, target: Option<&str>) -> CommandResult {
+ let mut words = target.unwrap_or_default().split_whitespace();
+ let (Some(provider), Some(model)) = (words.next(), words.next()) else {
+ return CommandResult::error(tr(app.ui_locale, MessageId::FleetRemoveUsage));
+ };
+ CommandResult::action(AppAction::FleetRemoveModel {
+ provider: provider.to_string(),
+ model: model.to_string(),
+ })
+}
+
fn run_control(app: &App, operation: ControlOperation, target: Option<&str>) -> CommandResult {
let receipt = execute_fleet_control(ControlSurface::Slash, &app.workspace, operation, target);
let rendered = receipt.render();
@@ -86,16 +239,16 @@ impl RegisterCommand for FleetCmd {
fn execute(app: &mut App, arg: Option<&str>) -> CommandResult {
let Some((verb, target)) = split_verb(arg) else {
- // Primary face: the familiar roster for the selected Pod.
- // Named-Pod switching lives under /pod pods — never between
- // the operator and their Pod.
+ // Primary face: the familiar roster for the selected fleet.
+ // Named-fleet switching lives under /fleet fleets — never between
+ // the operator and their fleet.
return CommandResult::action(AppAction::OpenFleetRoster);
};
match verb {
"save" | "update" => {
// Explicit persistence of the pending session route into the
- // selected Pod's operator. Only an explicit command can
- // write a saved Pod after an in-session route change.
+ // selected fleet's operator. Only an explicit command can
+ // write a saved fleet after an in-session route change.
let message = app.apply_route_save_choice(
crate::tui::views::route_save_prompt::RouteSaveChoice::UpdateFleet,
);
@@ -110,10 +263,15 @@ impl RegisterCommand for FleetCmd {
_ => {}
}
match verb {
+ // The fleet as models (design §10 F1): what the person added,
+ // provider-exact, with the roles each model fills.
+ "models" | "model" => CommandResult::message(fleet_models_text(app)),
+ "add" => fleet_add(app, target),
+ "remove" | "rm" | "drop" => fleet_remove(app, target),
"members" | "member" | "roster" | "party" | "loadout" | "roles" | "role"
| "profiles" | "profile" => CommandResult::action(AppAction::OpenFleetRoster),
"setup" | "edit" | "new" => CommandResult::action(AppAction::OpenFleetSetup),
- // Named saved Pods — secondary surface for multi-Pod pick/switch.
+ // Named saved fleets — secondary surface for multi-fleet pick/switch.
// Deliberately not "list": that verb is the durable ledger (#4022).
"pods" | "fleets" | "saved" | "manage" => {
CommandResult::action(AppAction::OpenFleetList)
@@ -124,8 +282,8 @@ impl RegisterCommand for FleetCmd {
other => match ControlOperation::parse_verb(ControlDomain::Fleet, other) {
Some(operation) => run_control(app, operation, target),
None => CommandResult::error(format!(
- "Unknown /pod target '{other}'. Use members, setup, pods, list, status, \
- workers, interrupt , or resume . /pod fleets remains \
+ "Unknown /fleet target '{other}'. Use members, setup, fleets, list, status, \
+ workers, interrupt , or resume . /pod pods remains \
accepted for compatibility."
)),
},
@@ -156,6 +314,124 @@ mod tests {
app
}
+ fn isolated_workspace() -> (tempfile::TempDir, crate::test_support::EnvVarGuard, PathBuf) {
+ let temp = tempfile::tempdir().expect("tempdir");
+ let home = temp.path().join("home");
+ let guard = crate::test_support::EnvVarGuard::set("CODEWHALE_HOME", home.as_os_str());
+ let workspace = temp.path().join("repo");
+ std::fs::create_dir_all(&workspace).expect("workspace");
+ (temp, guard, workspace)
+ }
+
+ #[test]
+ fn fleet_add_and_remove_parse_into_actions_the_ui_applies_with_the_live_config() {
+ let mut app = test_app();
+ let added = FleetCmd::execute(&mut app, Some("add openrouter z-ai/glm-5.3-flash explore"));
+ assert_eq!(
+ added.action,
+ Some(AppAction::FleetAddModel {
+ provider: "openrouter".to_string(),
+ model: "z-ai/glm-5.3-flash".to_string(),
+ roles: vec!["explore".to_string()],
+ })
+ );
+ assert!(!added.is_error && added.message.is_none(), "{added:?}");
+
+ let removed = FleetCmd::execute(&mut app, Some("remove openrouter z-ai/glm-5.3-flash"));
+ assert_eq!(
+ removed.action,
+ Some(AppAction::FleetRemoveModel {
+ provider: "openrouter".to_string(),
+ model: "z-ai/glm-5.3-flash".to_string(),
+ })
+ );
+
+ for arg in ["add openrouter", "add", "remove openrouter", "remove"] {
+ let usage = FleetCmd::execute(&mut app, Some(arg));
+ assert!(usage.is_error && usage.action.is_none(), "{arg}: {usage:?}");
+ assert!(
+ usage
+ .message
+ .as_deref()
+ .unwrap_or_default()
+ .contains("Usage: /fleet"),
+ "{arg}: {usage:?}"
+ );
+ }
+ }
+
+ #[test]
+ fn fleet_models_lists_the_selected_fleet_and_names_a_broken_selection() {
+ let _lock = crate::test_support::lock_test_env();
+ let (_temp, _home, workspace) = isolated_workspace();
+ let mut app = app_in(workspace.clone());
+
+ let empty = FleetCmd::execute(&mut app, Some("models"));
+ assert!(
+ empty
+ .message
+ .as_deref()
+ .unwrap_or_default()
+ .contains("session model only"),
+ "got: {empty:?}"
+ );
+
+ crate::fleet::members::add_fleet_model(
+ &workspace,
+ "openrouter",
+ "z-ai/glm-5.3-flash",
+ &["explore".to_string()],
+ )
+ .expect("add");
+ let listed = FleetCmd::execute(&mut app, Some("models"))
+ .message
+ .unwrap_or_default();
+ assert!(
+ listed.contains("openrouter/z-ai/glm-5.3-flash · explore"),
+ "got: {listed}"
+ );
+ assert!(
+ listed.starts_with("Your fleet `My fleet` (1 models)"),
+ "got: {listed}"
+ );
+
+ // A selected fleet whose file is gone is a broken selection, and
+ // `/fleet models` says so instead of "session model only".
+ let selected = crate::fleet::store::resolve_selected_fleet(&workspace)
+ .expect("ok")
+ .expect("selected");
+ std::fs::remove_file(&selected.path).expect("remove");
+ let broken = FleetCmd::execute(&mut app, Some("models"))
+ .message
+ .unwrap_or_default();
+ assert!(broken.contains("could not be loaded"), "got: {broken}");
+ assert!(!broken.contains("session model only"), "got: {broken}");
+ }
+
+ #[test]
+ fn fleet_add_rejections_come_from_the_live_config_and_the_catalog() {
+ let app = test_app();
+ let mut live = Config::default();
+ assert!(
+ fleet_provider_rejection(&app, &live, "unknown-provider")
+ .is_some_and(|reason| reason.contains("not a configured provider"))
+ );
+ assert!(fleet_provider_rejection(&app, &live, "openrouter").is_some());
+ // An in-session credential change is visible without restarting.
+ live.provider_config_for_mut(crate::config::ApiProvider::Openrouter)
+ .api_key = Some("test-key".to_string());
+ assert_eq!(fleet_provider_rejection(&app, &live, "openrouter"), None);
+
+ assert!(
+ fleet_catalog_rejection(Locale::En, "anthropic", "not-a-real-model")
+ .is_some_and(|reason| reason.contains("does not serve"))
+ );
+ assert_eq!(
+ fleet_catalog_rejection(Locale::En, "some-custom-provider", "anything"),
+ None
+ );
+ }
+
#[test]
fn pod_command_opens_roster_view() {
let mut app = test_app();
@@ -183,8 +459,8 @@ mod tests {
let mut pod_app = test_app();
let mut fleet_app = test_app();
- let pod = crate::commands::execute("/pod pods", &mut pod_app);
- let fleet = crate::commands::execute("/pod fleets", &mut fleet_app);
+ let pod = crate::commands::execute("/fleet fleets", &mut pod_app);
+ let fleet = crate::commands::execute("/pod pods", &mut fleet_app);
assert_eq!(pod.action, Some(AppAction::OpenFleetList));
assert_eq!(pod.action, fleet.action);
@@ -291,17 +567,21 @@ mod tests {
assert!(!result.is_error);
assert!(result.action.is_none());
let message = result.message.as_deref().unwrap_or_default();
- for surface in ["/pod members", "/pod setup", "/pod pods", "/pod status"] {
+ for surface in [
+ "/fleet members",
+ "/fleet setup",
+ "/fleet fleets",
+ "/fleet status",
+ ] {
assert!(message.contains(surface), "help must describe {surface}");
}
assert!(
- message
- .contains("/fleet and `codewhale fleet` remain accepted as compatibility aliases"),
+ message.contains("/pod and `codewhale pod` remain accepted as compatibility aliases"),
"help must document the one-way compatibility boundary"
);
assert!(
- message.contains("/pod fleets remains accepted as a compatibility alias"),
- "help must disclose the saved-Pod compatibility alias"
+ message.contains("/fleet pods remains accepted as a compatibility alias"),
+ "help must disclose the saved-fleet compatibility alias"
);
assert!(
message.contains("config tables keep the Fleet name"),
@@ -309,7 +589,7 @@ mod tests {
);
for truth in [
"current TUI session",
- "codewhale pod status",
+ "codewhale fleet status",
".codewhale/fleet.jsonl",
] {
assert!(message.contains(truth), "help must distinguish {truth}");
@@ -335,30 +615,30 @@ mod tests {
result
.message
.as_deref()
- .is_some_and(|message| message.contains("Unknown /pod target 'bogus'"))
+ .is_some_and(|message| message.contains("Unknown /fleet target 'bogus'"))
);
assert!(
result
.message
.as_deref()
- .is_some_and(|message| message.contains("Use members, setup, pods"))
+ .is_some_and(|message| message.contains("Use members, setup, fleets"))
);
}
#[test]
fn fleet_aliases_are_registered_on_command_info() {
- assert_eq!(FleetCmd::info().name, "pod");
- assert!(FleetCmd::info().aliases.contains(&"fleet"));
+ assert_eq!(FleetCmd::info().name, "fleet");
+ assert!(FleetCmd::info().aliases.contains(&"pod"));
assert!(FleetCmd::info().aliases.contains(&"loadout"));
- assert!(FleetCmd::info().usage.contains("pods"));
+ assert!(FleetCmd::info().usage.contains("fleets"));
assert!(FleetCmd::info().usage.contains("workers"));
assert!(FleetCmd::info().usage.contains("save-as"));
- assert!(!FleetCmd::info().usage.contains("fleets"));
+ assert!(!FleetCmd::info().usage.contains("pods"));
}
#[test]
fn pod_and_legacy_fleet_invocations_dispatch_identically() {
- for invocation in ["/pod", "/fleet"] {
+ for invocation in ["/fleet", "/pod"] {
let mut app = test_app();
let result = crate::commands::execute(invocation, &mut app);
assert_eq!(
@@ -369,16 +649,16 @@ mod tests {
assert!(!result.is_error, "{invocation}");
}
- let canonical = crate::commands::get_command_info("pod").expect("canonical /pod");
+ let canonical = crate::commands::get_command_info("fleet").expect("canonical /fleet");
let compatibility =
crate::commands::get_command_info("fleet").expect("compatibility /fleet");
assert!(std::ptr::eq(canonical, compatibility));
- assert_eq!(compatibility.name, "pod");
+ assert_eq!(compatibility.name, "fleet");
let workspace = tempfile::tempdir().expect("workspace");
let mut pod_app = app_in(workspace.path().to_path_buf());
let mut fleet_app = app_in(workspace.path().to_path_buf());
- let pod_status = crate::commands::execute("/pod status", &mut pod_app);
+ let pod_status = crate::commands::execute("/fleet status", &mut pod_app);
let fleet_status = crate::commands::execute("/fleet status", &mut fleet_app);
assert_eq!(pod_status.action, fleet_status.action);
assert_eq!(pod_status.message, fleet_status.message);
@@ -389,17 +669,17 @@ mod tests {
fn slash_command_and_cli_agree_on_fleet_verb_ids() {
for descriptor in operations_for_domain(ControlDomain::Fleet) {
assert_eq!(descriptor.slash_command, COMMAND_INFO.name);
- assert_eq!(descriptor.hotbar_action_id(), "slash.pod");
+ assert_eq!(descriptor.hotbar_action_id(), "slash.fleet");
assert!(
COMMAND_INFO.usage.contains(descriptor.verb) || descriptor.verb == "restart",
- "/pod usage must document {} or declare it CLI-only",
+ "/fleet usage must document {} or declare it CLI-only",
descriptor.verb
);
assert!(descriptor.offers(ControlSurface::Cli));
}
assert!(
!COMMAND_INFO.requires_required_argument(),
- "/pod must stay directly runnable from the palette and hotbar"
+ "/fleet must stay directly runnable from the palette and hotbar"
);
}
}
diff --git a/crates/tui/src/commands/groups/core/mod.rs b/crates/tui/src/commands/groups/core/mod.rs
index 9ba3cb6024..ceea48d5e7 100644
--- a/crates/tui/src/commands/groups/core/mod.rs
+++ b/crates/tui/src/commands/groups/core/mod.rs
@@ -17,7 +17,7 @@ mod core;
mod effort;
mod exit;
mod feedback;
-mod fleet;
+pub(in crate::commands) mod fleet;
mod help;
mod hf;
mod home;
diff --git a/crates/tui/src/commands/mod.rs b/crates/tui/src/commands/mod.rs
index 129899bbfb..06192f9056 100644
--- a/crates/tui/src/commands/mod.rs
+++ b/crates/tui/src/commands/mod.rs
@@ -26,6 +26,9 @@ use std::sync::OnceLock;
pub use traits::CommandInfo;
// Long-standing public paths that predate the group layout.
+/// `/fleet add` and the picker's ⇧F share these gates; the UI applies them
+/// against the live `Config`.
+pub(crate) use groups::core::fleet::{fleet_catalog_rejection, fleet_provider_rejection};
pub use groups::project::share;
#[cfg(test)]
pub(crate) use groups::session::rename_with_manager as rename_session_with_manager;
@@ -1405,7 +1408,14 @@ mod tests {
let result = execute("/rail pinned", &mut app);
assert!(!result.is_error);
- assert_eq!(app.work_surface.panel, RailPanel::Pinned);
+ assert_eq!(
+ app.work_surface.panel,
+ RailPanel::Tasks,
+ "pinned folded into the tasks view"
+ );
+ let result = execute("/rail files", &mut app);
+ assert!(!result.is_error);
+ assert_eq!(app.work_surface.panel, RailPanel::Files);
let result = execute("/sidebar on", &mut app);
assert!(!result.is_error);
diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs
index 462521bf82..1ab73e3a71 100644
--- a/crates/tui/src/config.rs
+++ b/crates/tui/src/config.rs
@@ -7624,6 +7624,18 @@ impl Config {
self.fleet.clone().unwrap_or_default()
}
+ /// The person's fleet as models (design MODEL-ROUTING-CATALOG §10 F1):
+ /// every exact provider + model route in the selected fleet for
+ /// `workspace`, with the roles each fills. `Ok(empty)` = the session
+ /// model only; `Err` = a selected fleet that cannot be read. This is the
+ /// seam the operator-awareness slice (F2) reads.
+ pub fn fleet_members(
+ &self,
+ workspace: &Path,
+ ) -> Result, crate::fleet::store::FleetStoreError> {
+ crate::fleet::members::fleet_models(workspace)
+ }
+
/// Parsed `[workflow]` table, or product defaults when the table is absent
/// (#4128 / Section 2.11). Automatic launch, approval, isolation, and
/// activity-persistence consumers should read through this accessor so
diff --git a/crates/tui/src/config_ui.rs b/crates/tui/src/config_ui.rs
index 6b53261c2c..b95c045ed0 100644
--- a/crates/tui/src/config_ui.rs
+++ b/crates/tui/src/config_ui.rs
@@ -272,7 +272,7 @@ pub enum UiThemeValue {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum OceanTreatmentValue {
- #[serde(alias = "ombre")]
+ #[serde(alias = "ombre", alias = "underwater")]
Deepsea,
Flat,
}
diff --git a/crates/tui/src/core/engine.rs b/crates/tui/src/core/engine.rs
index e5022a8216..6749650475 100644
--- a/crates/tui/src/core/engine.rs
+++ b/crates/tui/src/core/engine.rs
@@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex as StdMutex};
use std::time::{Duration, Instant};
use anyhow::Result;
+use codewhale_config::route::CapabilityState;
use codewhale_execpolicy::{AskForApproval, ExecPolicyContext};
use codewhale_protocol::runtime::DynamicToolSpec;
use futures_util::StreamExt;
@@ -46,7 +47,7 @@ use crate::route_runtime::{
};
use crate::tools::goal::{
GoalPauseReason, GoalSnapshot, GoalStatus, SharedGoalState, explicit_goal_directive,
- new_shared_goal_state,
+ new_shared_goal_state, operate_goal_from_prompt,
};
use crate::tools::plan::{SharedPlanState, new_shared_plan_state};
use crate::tools::shell::{SharedShellManager, new_shared_shell_manager};
@@ -4633,25 +4634,40 @@ impl Engine {
// runtime text, recalled memory, handoffs, and pasted multi-line
// transcripts cannot create a goal.
//
+ // Operate turns an ordinary work prompt into the goal through this
+ // same path when the host reports no unfinished goal
+ // (`operate_goal_from_prompt` owns the "is this real work?" rule).
+ // `GoalState::create` still refuses while an unfinished goal exists,
+ // so a paused or blocked goal is never silently replaced.
+ //
// KV-cache effect: this only selects the already-existing volatile
// contributor. It adds no new stable-prefix text.
- let explicit_goal_result = if provenance.can_authorize_work() {
- explicit_goal_directive(&content).map(|directive| {
- self.config
- .goal_state
- .lock()
- .map_err(|_| "goal state lock poisoned".to_string())
- .and_then(|mut state| {
- state
- .create(directive.objective, None)
- .map_err(str::to_string)?;
- Ok(state.snapshot())
- })
- })
+ let goal_request = if provenance.can_authorize_work() {
+ explicit_goal_directive(&content)
+ .map(|directive| (directive, true))
+ .or_else(|| {
+ let host_goal_unfinished =
+ goal_objective.is_some() && goal_status != GoalStatus::Complete;
+ (mode == AppMode::Operate && !host_goal_unfinished)
+ .then(|| operate_goal_from_prompt(&content))
+ .flatten()
+ .map(|directive| (directive, false))
+ })
} else {
None
};
- if let Some(result) = explicit_goal_result {
+ if let Some((directive, explicit)) = goal_request {
+ let result = self
+ .config
+ .goal_state
+ .lock()
+ .map_err(|_| "goal state lock poisoned".to_string())
+ .and_then(|mut state| {
+ state
+ .create(directive.objective, None)
+ .map_err(str::to_string)?;
+ Ok(state.snapshot())
+ });
match result {
Ok(snapshot) => {
goal_objective.clone_from(&snapshot.objective);
@@ -4662,7 +4678,7 @@ impl Engine {
// even when this model would otherwise reply only in prose.
let _ = self.tx_event.send(Event::GoalUpdated { snapshot }).await;
}
- Err(error) => {
+ Err(error) if explicit => {
let _ = self
.tx_event
.send(Event::status(format!(
@@ -4670,6 +4686,8 @@ impl Engine {
)))
.await;
}
+ // Operate keeps the unfinished goal it already has.
+ Err(_) => {}
}
}
@@ -4984,6 +5002,22 @@ impl Engine {
});
}
+ // The Operate contract (docs/MODES.md) precedes the first Operate
+ // prompt. KV-cache effect: append-only history, one user-role runtime
+ // message; it is derived from the session log rather than a flag so a
+ // cleared, restored, or compacted session gets it again exactly once
+ // and every later Operate turn does not repeat it.
+ if mode == AppMode::Operate
+ && !self
+ .session
+ .messages
+ .iter()
+ .any(crate::runtime_handoff::is_operate_contract_message)
+ {
+ self.session
+ .add_message(crate::runtime_handoff::operate_contract_runtime_message());
+ }
+
self.session
.working_set
.observe_user_message(&content, &self.session.workspace);
@@ -7642,7 +7676,7 @@ use context::{
MAX_CONTEXT_RECOVERY_ATTEMPTS, MIN_RECENT_MESSAGES_TO_KEEP,
effective_max_output_tokens_for_route, emergency_trim_budget,
extract_compaction_summary_prompt, is_context_length_error_message,
- route_context_budget_for_route, summarize_text,
+ is_image_input_rejection_message, route_context_budget_for_route, summarize_text,
};
#[cfg(test)]
use context::{context_input_budget_for_provider, effective_max_output_tokens};
diff --git a/crates/tui/src/core/engine/context.rs b/crates/tui/src/core/engine/context.rs
index 469b24165b..9a74129376 100644
--- a/crates/tui/src/core/engine/context.rs
+++ b/crates/tui/src/core/engine/context.rs
@@ -583,9 +583,27 @@ pub(super) fn is_context_length_error_message(message: &str) -> bool {
|| (lower.contains("requested") && lower.contains("tokens") && lower.contains("maximum"))
}
+pub(super) fn is_image_input_rejection_message(message: &str) -> bool {
+ let lower = message.to_lowercase();
+ let image_signal = lower.contains("image_url")
+ || lower.contains("content.type")
+ || lower.contains("content type")
+ || lower.contains("does not support image")
+ || lower.contains("image input")
+ || lower.contains("unsupported modality")
+ || lower
+ .split(|character: char| !character.is_alphanumeric())
+ .any(|term| term == "vision");
+ let rejection_signal = lower.contains("400")
+ || lower.contains("invalid")
+ || lower.contains("unsupported")
+ || lower.contains("not support");
+ image_signal && rejection_signal
+}
+
#[cfg(test)]
mod tests {
- use super::emergency_trim_budget;
+ use super::{emergency_trim_budget, is_image_input_rejection_message};
#[test]
fn emergency_trim_budget_leaves_a_hysteresis_margin() {
@@ -596,4 +614,19 @@ mod tests {
assert_eq!(emergency_trim_budget(4), 4);
assert_eq!(emergency_trim_budget(0), 0);
}
+
+ #[test]
+ fn image_rejection_classifier_matches_provider_400s() {
+ assert!(is_image_input_rejection_message(
+ r#"request (400): {"error":{"code":"1214","message":"messages.content.type 参数非法, 取值范围 ['text']"}}"#
+ ));
+ assert!(is_image_input_rejection_message(
+ "Invalid content type. image_url is only supported by certain models."
+ ));
+ assert!(!is_image_input_rejection_message("Model not exist."));
+ assert!(!is_image_input_rejection_message("invalid revision id"));
+ assert!(!is_image_input_rejection_message(
+ "This model's maximum context length is 131072 tokens."
+ ));
+ }
}
diff --git a/crates/tui/src/core/engine/streaming.rs b/crates/tui/src/core/engine/streaming.rs
index 13d925da7c..ccd404e751 100644
--- a/crates/tui/src/core/engine/streaming.rs
+++ b/crates/tui/src/core/engine/streaming.rs
@@ -401,7 +401,19 @@ fn find_first_start_marker(text: &str) -> Option<(usize, usize, &'static str)> {
.min_by_key(|(idx, _, _)| *idx)
}
+/// Cheap rejection: every marker prefix ends with the marker's own first
+/// byte, so a text without any marker's first byte cannot end with one.
+/// Every tool-call marker starts with `<` or `[`, so this is one scan of a
+/// usually short delta and keeps per-token `ends_with` probing off the hot
+/// path for plain prose deltas.
+fn fast_reject_marker_text(text: &str) -> bool {
+ !text.bytes().any(|b| b == b'<' || b == b'[')
+}
+
fn trailing_marker_prefix_len(text: &str, markers: &[&str]) -> usize {
+ if fast_reject_marker_text(text) {
+ return 0;
+ }
markers
.iter()
.flat_map(|marker| {
@@ -421,6 +433,9 @@ fn trailing_marker_prefix_len(text: &str, markers: &[&str]) -> usize {
}
fn trailing_start_marker_prefix_len(text: &str) -> usize {
+ if fast_reject_marker_text(text) {
+ return 0;
+ }
TOOL_CALL_MARKER_PAIRS
.iter()
.flat_map(|(marker, _)| {
diff --git a/crates/tui/src/core/engine/tests.rs b/crates/tui/src/core/engine/tests.rs
index a6fe0d1a9f..914af1923a 100644
--- a/crates/tui/src/core/engine/tests.rs
+++ b/crates/tui/src/core/engine/tests.rs
@@ -3157,6 +3157,245 @@ async fn explicit_natural_goal_activates_before_provider_request() {
run_task.await.expect("engine task");
}
+/// Drive one turn in `mode` and report what the goal path did before the
+/// provider was called: the objective published by `GoalUpdated` (if any),
+/// whether the engine goal state is active, and how many Operate contract
+/// messages the session log holds afterwards.
+async fn operate_goal_probe(mode: AppMode, prompt: &str) -> (Option, bool, usize) {
+ let request_entered = std::sync::Arc::new(tokio::sync::Notify::new());
+ let release_request = std::sync::Arc::new(tokio::sync::Notify::new());
+ let model = std::sync::Arc::new(FirstRequestGatedGoalModelClient {
+ calls: std::sync::atomic::AtomicUsize::new(0),
+ request_entered: std::sync::Arc::clone(&request_entered),
+ release_request: std::sync::Arc::clone(&release_request),
+ });
+ let config = goal_custom_route_config();
+ let client: crate::core::model_client::SharedModelClient = model.clone();
+ let (engine, handle) = Engine::new_with_model_client(
+ EngineConfig {
+ model: "local-model".to_string(),
+ max_steps: 1,
+ snapshots_enabled: false,
+ terminal_chrome_enabled: false,
+ ..EngineConfig::default()
+ },
+ &config,
+ client,
+ );
+ let goal_state = engine.config.goal_state.clone();
+ let run_task = tokio::spawn(engine.run());
+
+ handle
+ .send(Op::SendMessage {
+ content: prompt.to_string(),
+ mode,
+ route: resolved_route_for_test(&config, "local-model"),
+ compaction: Box::new(CompactionConfig::default()),
+ goal_objective: None,
+ goal_token_budget: None,
+ goal_status: crate::tools::goal::GoalStatus::Active,
+ reasoning_effort: None,
+ reasoning_effort_auto: false,
+ auto_model: false,
+ allow_shell: false,
+ trust_mode: false,
+ auto_approve: false,
+ approval_mode: crate::tui::approval::ApprovalMode::Suggest,
+ translation_enabled: false,
+ allowed_tools: None,
+ dynamic_tools: Vec::new(),
+ hook_executor: None,
+ verbosity: None,
+ provenance: UserInputProvenance::ExternalUser,
+ })
+ .await
+ .expect("send probe turn");
+
+ let mut published_objective = None;
+ loop {
+ let event = tokio::time::timeout(model_turn_event_timeout(), async {
+ handle.rx_event.write().await.recv().await
+ })
+ .await
+ .expect("probe event timeout")
+ .expect("probe event");
+ match event {
+ Event::GoalUpdated { snapshot } if snapshot.status == "active" => {
+ published_objective = snapshot.objective;
+ }
+ Event::TurnStarted { .. } => break,
+ _ => {}
+ }
+ }
+ tokio::time::timeout(model_turn_event_timeout(), request_entered.notified())
+ .await
+ .expect("provider request was never entered");
+ let active = goal_state.lock().expect("goal lock").is_active();
+ if active {
+ // Stop autonomous continuation after the one provider call.
+ handle
+ .send(Op::SetGoalStatus {
+ status: crate::tools::goal::GoalStatus::Paused,
+ clear: false,
+ })
+ .await
+ .expect("queue goal pause");
+ }
+ release_request.notify_one();
+ let snapshot = tokio::time::timeout(model_turn_event_timeout(), handle.get_session_snapshot())
+ .await
+ .expect("probe turn did not settle")
+ .expect("probe session snapshot");
+ let contracts = snapshot
+ .messages
+ .iter()
+ .filter(|message| crate::runtime_handoff::is_operate_contract_message(message))
+ .count();
+ assert_eq!(model.calls.load(std::sync::atomic::Ordering::SeqCst), 1);
+
+ handle.send(Op::Shutdown).await.expect("shutdown engine");
+ run_task.await.expect("engine task");
+ (published_objective, active, contracts)
+}
+
+#[tokio::test]
+async fn operate_turns_a_work_prompt_into_the_goal_but_work_mode_does_not() {
+ let prompt =
+ "Migrate the settings loader to the new config crate and keep the old keys readable";
+
+ let (objective, active, contracts) = operate_goal_probe(AppMode::Operate, prompt).await;
+ assert_eq!(
+ objective.as_deref(),
+ Some(prompt),
+ "Operate must publish the prompt as the goal before the provider call"
+ );
+ assert!(active, "Operate goal must be active in engine state");
+ assert_eq!(contracts, 1, "Operate appends its contract exactly once");
+
+ let (objective, active, contracts) = operate_goal_probe(AppMode::Agent, prompt).await;
+ assert_eq!(objective, None, "Work must not promote a prompt to a goal");
+ assert!(!active);
+ assert_eq!(contracts, 0, "Work never sees the Operate contract");
+
+ let (objective, active, contracts) =
+ operate_goal_probe(AppMode::Operate, "thanks, looks good").await;
+ assert_eq!(objective, None, "chat stays chat even in Operate");
+ assert!(!active);
+ assert_eq!(contracts, 1, "the contract is about the mode, not the goal");
+}
+
+#[tokio::test]
+async fn operate_contract_is_appended_once_and_an_existing_goal_is_never_replaced() {
+ let first_entered = std::sync::Arc::new(tokio::sync::Notify::new());
+ let release_first = std::sync::Arc::new(tokio::sync::Notify::new());
+ let model = std::sync::Arc::new(IndexedGatedGoalModelClient {
+ calls: std::sync::atomic::AtomicUsize::new(0),
+ gates: HashMap::from([(
+ 1,
+ (
+ std::sync::Arc::clone(&first_entered),
+ std::sync::Arc::clone(&release_first),
+ ),
+ )]),
+ max_calls: 2,
+ });
+ let config = goal_custom_route_config();
+ let client: crate::core::model_client::SharedModelClient = model.clone();
+ let (engine, handle) = Engine::new_with_model_client(
+ EngineConfig {
+ model: "local-model".to_string(),
+ max_steps: 1,
+ snapshots_enabled: false,
+ terminal_chrome_enabled: false,
+ ..EngineConfig::default()
+ },
+ &config,
+ client,
+ );
+ let goal_state = engine.config.goal_state.clone();
+ let run_task = tokio::spawn(engine.run());
+
+ let send = |content: &str, goal_objective: Option, goal_status| Op::SendMessage {
+ content: content.to_string(),
+ mode: AppMode::Operate,
+ route: resolved_route_for_test(&config, "local-model"),
+ compaction: Box::new(CompactionConfig::default()),
+ goal_objective,
+ goal_token_budget: None,
+ goal_status,
+ reasoning_effort: None,
+ reasoning_effort_auto: false,
+ auto_model: false,
+ allow_shell: false,
+ trust_mode: false,
+ auto_approve: false,
+ approval_mode: crate::tui::approval::ApprovalMode::Suggest,
+ translation_enabled: false,
+ allowed_tools: None,
+ dynamic_tools: Vec::new(),
+ hook_executor: None,
+ verbosity: None,
+ provenance: UserInputProvenance::ExternalUser,
+ };
+
+ let first =
+ "Migrate the settings loader to the new config crate and keep the old keys readable";
+ handle
+ .send(send(first, None, crate::tools::goal::GoalStatus::Active))
+ .await
+ .expect("send first Operate turn");
+ tokio::time::timeout(model_turn_event_timeout(), first_entered.notified())
+ .await
+ .expect("first provider request was never entered");
+ assert_eq!(
+ goal_state.lock().expect("goal lock").objective(),
+ Some(first)
+ );
+ handle
+ .send(Op::SetGoalStatus {
+ status: crate::tools::goal::GoalStatus::Paused,
+ clear: false,
+ })
+ .await
+ .expect("queue goal pause");
+ release_first.notify_one();
+ let _ = tokio::time::timeout(model_turn_event_timeout(), handle.get_session_snapshot())
+ .await
+ .expect("first turn did not settle")
+ .expect("first session snapshot");
+
+ // Second Operate prompt while the (paused) goal is still unfinished: the
+ // host reports that goal, so the prompt is ordinary work under it.
+ handle
+ .send(send(
+ "Refactor the provider table so it survives a config reload",
+ Some(first.to_string()),
+ crate::tools::goal::GoalStatus::Paused,
+ ))
+ .await
+ .expect("send second Operate turn");
+ let snapshot = tokio::time::timeout(model_turn_event_timeout(), handle.get_session_snapshot())
+ .await
+ .expect("second turn did not settle")
+ .expect("second session snapshot");
+ assert_eq!(model.calls.load(std::sync::atomic::Ordering::SeqCst), 2);
+ let contracts = snapshot
+ .messages
+ .iter()
+ .filter(|message| crate::runtime_handoff::is_operate_contract_message(message))
+ .count();
+ assert_eq!(
+ contracts, 1,
+ "the contract must not repeat on later Operate turns"
+ );
+ let goal = goal_state.lock().expect("goal lock").snapshot();
+ assert_eq!(goal.objective.as_deref(), Some(first));
+ assert_eq!(goal.status, "paused");
+
+ handle.send(Op::Shutdown).await.expect("shutdown engine");
+ run_task.await.expect("engine task");
+}
+
fn without_named_custom_route(mut config: Config) -> Config {
config
.providers
@@ -11031,10 +11270,25 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
"\"finish_reason\":\"stop\"}]}\n\n",
"data: [DONE]\n\n",
);
+ // Operate turns the work prompt into a goal, so after the approved shell
+ // runs, the model must seal the goal through the same `update_goal` tool
+ // a live Operate turn uses; only then does the final "done" arrive.
+ let goal_seal_marker = "goal-seal-receipt-0902";
+ let goal_sse = concat!(
+ "data: {\"id\":\"chatcmpl-operate-goal\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[",
+ "{\"index\":0,\"id\":\"call_operate_goal\",\"type\":\"function\",\"function\":{\"name\":\"update_goal\",",
+ "\"arguments\":\"{\\\"status\\\":\\\"complete\\\",\\\"evidence\\\":\\\"goal-seal-receipt-0902: operate-mode-approved.txt written\\\",",
+ "\\\"verification\\\":{\\\"status\\\":\\\"passed\\\",\\\"check\\\":\\\"cat operate-mode-approved.txt\\\",\\\"summary\\\":\\\"fixture contains operate-approved\\\"}}\"}}",
+ "]},\"finish_reason\":null}]}\n\n",
+ "data: {\"id\":\"chatcmpl-operate-goal\",\"choices\":[{\"index\":0,\"delta\":{},",
+ "\"finish_reason\":\"tool_calls\"}]}\n\n",
+ "data: [DONE]\n\n",
+ );
Mock::given(method("POST"))
.and(path("/v1/chat/completions"))
- .and(body_string_contains("operate-mode-approved.txt"))
+ .and(body_string_contains(goal_seal_marker))
+ .and(body_string_contains("tokens_used"))
.respond_with(
ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
@@ -11044,17 +11298,43 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
.with_priority(1)
.mount(&server)
.await;
+ // The first `update_goal` call only loads the deferred tool; the retry —
+ // the request carrying the deferral receipt — is the one that executes.
Mock::given(method("POST"))
.and(path("/v1/chat/completions"))
+ .and(body_string_contains("was deferred and has now been loaded"))
.respond_with(
ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
- .set_body_string(tool_call_sse),
+ .set_body_string(goal_sse),
)
.expect(1)
.with_priority(2)
.mount(&server)
.await;
+ Mock::given(method("POST"))
+ .and(path("/v1/chat/completions"))
+ .and(body_string_contains("operate-mode-approved.txt"))
+ .respond_with(
+ ResponseTemplate::new(200)
+ .insert_header("content-type", "text/event-stream")
+ .set_body_string(goal_sse),
+ )
+ .expect(1)
+ .with_priority(3)
+ .mount(&server)
+ .await;
+ Mock::given(method("POST"))
+ .and(path("/v1/chat/completions"))
+ .respond_with(
+ ResponseTemplate::new(200)
+ .insert_header("content-type", "text/event-stream")
+ .set_body_string(tool_call_sse),
+ )
+ .expect(1)
+ .with_priority(3)
+ .mount(&server)
+ .await;
let api_config = Config {
api_key: Some("test-key".to_string()),
@@ -11113,6 +11393,18 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
Event::ApprovalRequired { id, tool_name, .. } => {
saw_approval = true;
assert_eq!(tool_name, "Bash");
+ // Operate turned this work prompt into the goal; pause it
+ // before the turn ends so the goal-continuation loop cannot
+ // queue passes nobody answers in this mock. The goal-complete
+ // `update_goal` response below stays: it is the receipt a
+ // real Operate turn seals with.
+ handle_for_approval
+ .send(Op::SetGoalStatus {
+ status: crate::tools::goal::GoalStatus::Paused,
+ clear: false,
+ })
+ .await
+ .expect("queue goal pause");
handle_for_approval
.approve_tool_call(id)
.await
diff --git a/crates/tui/src/core/engine/turn_loop.rs b/crates/tui/src/core/engine/turn_loop.rs
index 49cfcd3cbd..e0b91796ae 100644
--- a/crates/tui/src/core/engine/turn_loop.rs
+++ b/crates/tui/src/core/engine/turn_loop.rs
@@ -653,6 +653,7 @@ impl Engine {
// that owes work never finishes silently.
let mut final_report_sent = false;
let mut context_recovery_attempts = 0u8;
+ let mut image_rejection_recovered = false;
let mut tool_policy = tool_policy;
let mut mode = tool_policy.mode;
let mut questions_allowed = tool_policy.allows_questions();
@@ -1408,6 +1409,25 @@ impl Engine {
context_recovery_attempts = context_recovery_attempts.saturating_add(1);
continue;
}
+ if is_image_input_rejection_message(&message)
+ && self.active_route_capabilities.image_input
+ != CapabilityState::Unsupported
+ && !image_rejection_recovered
+ {
+ image_rejection_recovered = true;
+ self.active_route_capabilities.image_input = CapabilityState::Unsupported;
+ crate::logging::warn(format!(
+ "model {} rejected image content; resending with images replaced by text",
+ self.session.model
+ ));
+ let status = crate::localization::tr(
+ crate::localization::resolve_locale(&self.config.locale_tag),
+ crate::localization::MessageId::ImageInputRejectedResent,
+ )
+ .replace("{model}", &self.session.model);
+ let _ = self.tx_event.send(Event::status(status)).await;
+ continue;
+ }
turn_error = Some(message.clone());
let _ = self
.tx_event
@@ -4611,17 +4631,25 @@ impl Engine {
&& let Some(tool_state) = tool_uses.get_mut(tool_idx)
{
tool_state.input_buffer.push_str(&partial_json);
- crate::logging::info(format!(
- "Tool '{}' input delta: {} (buffer now: {})",
- tool_state.name, partial_json, tool_state.input_buffer
- ));
- if let Some(value) = parse_tool_input(&tool_state.input_buffer) {
- tool_state.input = value.clone();
+ // Verbose-only: the eager format! here copied the
+ // whole accumulated buffer on every JSON delta
+ // (O(n²) per tool call) for a log that is
+ // usually disabled.
+ if crate::logging::is_verbose() {
crate::logging::info(format!(
- "Tool '{}' input parsed: {:?}",
- tool_state.name, value
+ "Tool '{}' input delta: {} (buffer now: {})",
+ tool_state.name, partial_json, tool_state.input_buffer
));
}
+ if let Some(value) = parse_tool_input(&tool_state.input_buffer) {
+ tool_state.input = value.clone();
+ if crate::logging::is_verbose() {
+ crate::logging::info(format!(
+ "Tool '{}' input parsed: {:?}",
+ tool_state.name, value
+ ));
+ }
+ }
}
}
},
diff --git a/crates/tui/src/doctor_fix.rs b/crates/tui/src/doctor_fix.rs
index beb8477674..e6da90b1a8 100644
--- a/crates/tui/src/doctor_fix.rs
+++ b/crates/tui/src/doctor_fix.rs
@@ -303,7 +303,7 @@ fn disable_mcp_server(config_path: &Path, server_name: &str) -> Result<()> {
pub(crate) fn print_fix_plan(plan: &DoctorFixPlan) {
use colored::Colorize;
- let (sky_r, sky_g, sky_b) = crate::palette::WHALE_INFO_RGB;
+ let (sky_r, sky_g, sky_b) = crate::palette::WHALE_ACTION_RGB;
println!("{}", "Repair plan (--fix):".bold());
if plan.is_empty() {
println!(" {} nothing to repair", "✓".truecolor(sky_r, sky_g, sky_b));
@@ -336,7 +336,7 @@ pub(crate) fn confirm_fix(plan: &DoctorFixPlan) -> bool {
pub(crate) fn print_apply_results(results: &[(DoctorFixAction, DoctorFixOutcome)]) -> bool {
use colored::Colorize;
- let (aqua_r, aqua_g, aqua_b) = crate::palette::WHALE_INFO_RGB;
+ let (aqua_r, aqua_g, aqua_b) = crate::palette::WHALE_ACTION_RGB;
let (red_r, red_g, red_b) = crate::palette::WHALE_ERROR_RGB;
println!("{}", "Repair results:".bold());
let mut all_applied = true;
diff --git a/crates/tui/src/fleet/control.rs b/crates/tui/src/fleet/control.rs
index 3e6a9af10b..696ef7c80f 100644
--- a/crates/tui/src/fleet/control.rs
+++ b/crates/tui/src/fleet/control.rs
@@ -859,8 +859,8 @@ mod tests {
FleetManager::open(dir.path()).unwrap();
assert_eq!(
ControlOperation::FleetStatus.descriptor().cli_invocation,
- "codewhale pod status",
- "Pod remains the canonical public command"
+ "codewhale fleet status",
+ "fleet remains the canonical public command"
);
let mut rendered = std::collections::BTreeSet::new();
for surface in ControlSurface::ALL {
diff --git a/crates/tui/src/fleet/exact.rs b/crates/tui/src/fleet/exact.rs
index 8980f86b3f..e3f72ac30e 100644
--- a/crates/tui/src/fleet/exact.rs
+++ b/crates/tui/src/fleet/exact.rs
@@ -49,6 +49,7 @@ use crate::fleet::profile::AgentProfile;
use crate::fleet::roster::{FleetRoster, ProfileOrigin};
use crate::llm_client::LlmClient;
use crate::models::Role;
+use crate::tools::subagent::public_role_label;
use crate::tui::app::ReasoningEffort;
/// Where exact Pod definitions and Reasoning Router profiles are looked up,
@@ -580,14 +581,14 @@ pub(crate) fn posture_role_for(ceiling: PermissionCeiling) -> &'static str {
if !ceiling.tools {
// No tools at all; the narrowest posture, and the allowlist is empty
// anyway.
- return "scout";
+ return "explore";
}
if ceiling.write {
- return "builder";
+ return "implement";
}
match ceiling.shell {
- ShellCeiling::None | ShellCeiling::ReadOnly => "scout",
- ShellCeiling::Full => "verifier",
+ ShellCeiling::None | ShellCeiling::ReadOnly => "explore",
+ ShellCeiling::Full => "test",
}
}
@@ -1362,7 +1363,7 @@ impl ExactFleetWorkflow {
Ok(ExactMemberBinding {
member_id: member.id.clone(),
- member_role: member.role.clone(),
+ member_role: public_role_label(&member.role),
route: route.clone(),
requires_router: member.requested_reasoning.is_auto(),
authority: ChildAuthority::from_runtime_role(&member.role, session),
@@ -1868,11 +1869,11 @@ mod shell_ceiling_tests {
fn the_existing_verifier_and_full_ceilings_are_unchanged() {
let verifier = ChildAuthority::clamp(ceiling(false, ShellCeiling::Full), session());
assert!(denies_raw_shell(&verifier));
- assert_eq!(verifier.posture_role, "verifier");
+ assert_eq!(verifier.posture_role, "test");
let full = ChildAuthority::clamp(ceiling(true, ShellCeiling::Full), session());
assert!(!denies_raw_shell(&full));
- assert_eq!(full.posture_role, "builder");
+ assert_eq!(full.posture_role, "implement");
}
#[test]
@@ -1963,7 +1964,7 @@ mod shell_ceiling_tests {
assert!(!authority.ceiling.write);
assert_eq!(authority.ceiling.shell, ShellCeiling::ReadOnly);
assert_eq!(authority.write_authority, "read_only");
- assert_eq!(authority.posture_role, "builder");
+ assert_eq!(authority.posture_role, "implement");
assert!(denies_raw_shell(&authority));
for mutating in ["write_file", "apply_patch"] {
assert!(
@@ -2095,7 +2096,7 @@ permissions = "read_only"
assert_eq!(launch.model, "glm-5");
assert_eq!(launch.thinking, "max");
assert_eq!(launch.member_id, "implementer");
- assert_eq!(launch.member_role, "builder");
+ assert_eq!(launch.member_role, "implement");
assert_eq!(launch.reasoning.requested(), RequestedReasoning::Auto);
assert_eq!(
launch.reasoning.source(),
@@ -2752,7 +2753,7 @@ permissions = "read_only"
assert_eq!(authority.ceiling.delegation_depth, 0);
assert_eq!(authority.write_authority, "read_only");
assert_eq!(authority.max_depth, 0);
- assert_eq!(authority.posture_role, "scout");
+ assert_eq!(authority.posture_role, "explore");
assert!(!authority.disallowed_tools.is_empty());
}
@@ -3192,7 +3193,7 @@ call_reasoning = "low"
assert_eq!(receipt.fleet, "workspace/glm-pair");
assert_eq!(receipt.schema_kind, "exact");
assert_eq!(receipt.member_id, "implementer");
- assert_eq!(receipt.member_role, "builder");
+ assert_eq!(receipt.member_role, "implement");
assert_eq!(receipt.provider, "zai");
assert_eq!(receipt.model, "glm-5");
assert_eq!(receipt.requested_reasoning, "auto");
diff --git a/crates/tui/src/fleet/executor.rs b/crates/tui/src/fleet/executor.rs
index efccfb38be..a508e8545e 100644
--- a/crates/tui/src/fleet/executor.rs
+++ b/crates/tui/src/fleet/executor.rs
@@ -1,9 +1,9 @@
-//! Pod executor — runs a Pod worker as a real `codewhale exec` subprocess.
+//! Fleet executor — runs a Fleet worker as a real `codewhale exec` subprocess.
//!
-//! A Pod worker IS a headless `codewhale exec` run. There is no separate
-//! "Pod worker" execution engine: the sub-agent runtime, full tool surface,
+//! A Fleet worker IS a headless `codewhale exec` run. There is no separate
+//! "Fleet worker" execution engine: the sub-agent runtime, full tool surface,
//! and recursion depth all come from the one `codewhale exec` runtime, so
-//! Pods and sub-agents are one substrate (not two moving targets).
+//! Fleets and sub-agents are one substrate (not two moving targets).
//!
//! This module is the bridge:
//! - [`build_worker_exec_command`] turns a `FleetTaskSpec` + `FleetExecConfig`
@@ -217,7 +217,7 @@ pub(crate) fn authority_envelope_for_worker(
if spec.runtime_profile.permissions.write {
let manifest = spec.launch_manifest.as_ref().ok_or_else(|| {
anyhow::anyhow!(
- "write-capable Pod worker '{}' has no launch manifest",
+ "write-capable Fleet worker '{}' has no launch manifest",
spec.worker_id
)
})?;
@@ -347,7 +347,7 @@ fn build_worker_exec_command_from_prompt(
args.push("--tool-authority-json".to_string());
args.push(
serde_json::to_string(authority)
- .expect("validated Pod tool authority envelope must serialize"),
+ .expect("validated Fleet tool authority envelope must serialize"),
);
}
@@ -695,7 +695,7 @@ impl FleetExecutor {
FleetHostSpec::Docker { image, .. } => {
return Err(super::host::FleetHostError {
kind: super::host::FleetHostErrorKind::Configuration,
- message: format!("docker Pod workers are not wired yet (image {image})"),
+ message: format!("docker Fleet workers are not wired yet (image {image})"),
});
}
};
@@ -753,7 +753,7 @@ impl FleetExecutor {
};
if let Some(key) = ssh_key {
let adapter = self.ssh_adapters.get_mut(&key).ok_or_else(|| {
- anyhow::anyhow!("tracked SSH Pod worker {worker_id} has no host adapter")
+ anyhow::anyhow!("tracked SSH Fleet worker {worker_id} has no host adapter")
})?;
adapter.stop_worker(worker_id)?;
} else {
@@ -1143,7 +1143,7 @@ mod tests {
.unwrap();
let prompt = cmd.args.last().unwrap();
- assert!(prompt.contains("Pod profile: reviewer"));
+ assert!(prompt.contains("Fleet profile: reviewer"));
assert!(prompt.contains("Focus on defects, regressions, and missing tests."));
}
diff --git a/crates/tui/src/fleet/identity.rs b/crates/tui/src/fleet/identity.rs
index 51154b7e93..cea97ba3de 100644
--- a/crates/tui/src/fleet/identity.rs
+++ b/crates/tui/src/fleet/identity.rs
@@ -20,6 +20,7 @@ use super::roster::{FleetRoster, ProfileOrigin};
use super::store::{
FleetFile, FleetScope, MemberCapability, load_fleet_at, resolve_selected_fleet,
};
+use crate::tools::subagent::public_role_label;
/// Maximum member rows one model-visible roster query returns. The total and
/// truncation flag stay visible so a large local roster is never mistaken for
@@ -42,7 +43,7 @@ pub fn load_effective_roster(
Ok(selected) => selected,
Err(_) => {
return FleetRoster::failed(
- "Selected Pod is missing or unreadable; inspect /pod and repair or clear the selection.",
+ "Selected Fleet is missing or unreadable; inspect /pod and repair or clear the selection.",
);
}
};
@@ -57,11 +58,22 @@ pub fn load_effective_roster(
Err(_) => {
let name = bounded_fleet_label(&selected.name);
return FleetRoster::failed(format!(
- "Selected {} Pod `{name}` is invalid or unreadable; inspect /pod and repair or clear the selection.",
+ "Selected {} Fleet `{name}` is invalid or unreadable; inspect /pod and repair or clear the selection.",
selected.scope.label()
));
}
};
+ if fleet.operator.is_none()
+ && fleet
+ .members
+ .iter()
+ .all(|member| member.role.trim().is_empty())
+ {
+ return plugins.map_or_else(
+ || FleetRoster::load(fleet_config, workspace),
+ |plugins| FleetRoster::load_with_plugins(fleet_config, workspace, plugins),
+ );
+ }
roster_from_fleet(&fleet, selected.scope, &selected.path)
}
@@ -80,9 +92,9 @@ pub fn roster_from_fleet(fleet: &FleetFile, scope: FleetScope, source: &Path) ->
.map(|member| {
let role = member.role.trim();
let role = if role.is_empty() {
- member.id.trim()
+ member.id.trim().to_string()
} else {
- role
+ public_role_label(role)
};
// A selected Fleet is one atomic routing policy. An explicit
// member pair wins; otherwise the Fleet operator pair is the
@@ -112,9 +124,9 @@ pub fn roster_from_fleet(fleet: &FleetFile, scope: FleetScope, source: &Path) ->
description: None,
requires: member.requires.clone(),
profile: FleetProfile {
- slot: FleetSlot::from_name(role),
+ slot: FleetSlot::from_name(&role),
role: FleetRole {
- name: role.to_string(),
+ name: role,
description: None,
instructions: member.instructions.clone(),
},
@@ -169,7 +181,7 @@ impl FleetMemberIdentity {
member_id: bounded_identity_field(&member.id),
display_name: trimmed_owned(member.display_name.as_deref())
.map(|value| bounded_identity_field(&value)),
- role: bounded_identity_field(member_role(member)),
+ role: bounded_identity_field(&public_role_label(member_role(member))),
provider_id,
model_name: friendly_model_name(member).map(|value| bounded_identity_field(&value)),
model_id,
@@ -276,10 +288,10 @@ pub fn friendly_model_name(member: &AgentProfile) -> Option {
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum FleetSelectorError {
- #[error("Pod member selector cannot be blank")]
+ #[error("Fleet member selector cannot be blank")]
Blank,
#[error(
- "Pod member selector `{selector}` is ambiguous; choose one member explicitly: {candidates}"
+ "Fleet member selector `{selector}` is ambiguous; choose one member explicitly: {candidates}"
)]
Ambiguous {
selector: String,
@@ -327,18 +339,30 @@ pub fn resolve_member_in_profiles<'a>(
return Ok(Some(member));
}
+ if kind
+ .as_deref()
+ .is_none_or(|kind| kind == "member" || kind == "id")
+ && let Some(member) = profiles.iter().find(|member| {
+ public_role_label(&member.id).eq_ignore_ascii_case(&public_role_label(value))
+ })
+ {
+ return Ok(Some(member));
+ }
+
let mut candidates = Vec::new();
for member in profiles {
let matches = match kind.as_deref() {
Some("member" | "id") => false,
Some("name") => matches_display_name(member, value),
- Some("role") => member_role(member).eq_ignore_ascii_case(value),
+ Some("role") => public_role_label(member_role(member))
+ .eq_ignore_ascii_case(&public_role_label(value)),
Some("model") => matches_model(member, value),
Some("route") => matches_route(member, value),
Some(_) => false,
None => {
matches_display_name(member, value)
- || member_role(member).eq_ignore_ascii_case(value)
+ || public_role_label(member_role(member))
+ .eq_ignore_ascii_case(&public_role_label(value))
|| matches_model(member, value)
|| matches_route(member, value)
|| friendly_model_name(member)
@@ -522,7 +546,7 @@ mod tests {
);
let projected = roster.get("scout-one").expect("case-insensitive id");
assert_eq!(projected.display_name.as_deref(), Some("Flash Scout"));
- assert_eq!(projected.profile.role.name, "scout");
+ assert_eq!(projected.profile.role.name, "explore");
assert_eq!(projected.profile.provider.as_deref(), Some("deepseek"));
assert_eq!(
projected.profile.model.as_deref(),
@@ -625,6 +649,24 @@ mod tests {
}
}
+ #[test]
+ fn canonical_role_selectors_resolve_legacy_builtin_member_ids() {
+ let roster = FleetRoster::built_ins_only();
+
+ assert_eq!(
+ resolve_member(&roster, "explore")
+ .expect("canonical role selector")
+ .map(|member| member.id.as_str()),
+ Some("scout")
+ );
+ assert_eq!(
+ resolve_member(&roster, "advisor")
+ .expect("canonical role selector")
+ .map(|member| member.id.as_str()),
+ Some("consultant")
+ );
+ }
+
#[test]
fn selected_v2_friendly_name_selector_is_deterministic() {
let fleet = FleetFile {
@@ -710,8 +752,8 @@ mod tests {
let roster = load_effective_roster(&FleetConfigToml::default(), workspace.path(), None);
assert!(roster.members().is_empty());
- let error = roster.load_error().expect("visible selected-Pod error");
- assert!(error.contains("Selected folder Pod `Broken`"), "{error}");
+ let error = roster.load_error().expect("visible selected-Fleet error");
+ assert!(error.contains("Selected folder Fleet `Broken`"), "{error}");
assert!(!error.contains(&workspace.path().display().to_string()));
assert!(!error.contains("must pin both provider and model"));
assert!(!error.contains("provider ="));
diff --git a/crates/tui/src/fleet/manager.rs b/crates/tui/src/fleet/manager.rs
index 3c6086eb3d..ec3257002e 100644
--- a/crates/tui/src/fleet/manager.rs
+++ b/crates/tui/src/fleet/manager.rs
@@ -135,10 +135,10 @@ pub struct FleetExecutorTickReport {
#[derive(Debug, thiserror::Error)]
pub enum FleetControlError {
/// The exact worker exists (or does not) but has no leased task to cancel.
- #[error("worker {worker_id} has no running Pod task")]
+ #[error("worker {worker_id} has no running Fleet task")]
NoActiveTask { worker_id: String },
/// No durable run with that exact id in this workspace's ledger.
- #[error("no Pod run with id {run_id}")]
+ #[error("no Fleet run with id {run_id}")]
UnknownRun { run_id: String },
}
@@ -371,7 +371,7 @@ impl FleetManager {
validate_task_spec_document(&doc)?;
let roster = self.agent_roster();
if let Some(error) = roster.load_error() {
- bail!("cannot create Pod run: {error}");
+ bail!("cannot create Fleet run: {error}");
}
worker_runtime::freeze_fleet_task_members(
&mut doc.tasks,
@@ -474,7 +474,7 @@ impl FleetManager {
lifecycle,
FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled
) {
- bail!("Pod run {} is already terminal ({lifecycle:?})", run_id.0);
+ bail!("Fleet run {} is already terminal ({lifecycle:?})", run_id.0);
}
if !matches!(lifecycle, FleetRunStatus::Running) {
self.ledger
@@ -539,7 +539,7 @@ impl FleetManager {
.runs
.get(&run_id.0)
.cloned()
- .ok_or_else(|| anyhow!("Pod run {} does not exist", run_id.0))?;
+ .ok_or_else(|| anyhow!("Fleet run {} does not exist", run_id.0))?;
let worker_ids = worker_ids_for_run(&run, max_workers);
for task in active_tasks_for_run(&state, run_id) {
@@ -626,7 +626,7 @@ impl FleetManager {
&record.spec.worker_id,
status,
Some(format!(
- "Pod task {} is {}",
+ "Fleet task {} is {}",
current.entry.task_id, status_label
)),
);
@@ -688,7 +688,7 @@ impl FleetManager {
Some(manager) => Some(
manager
.try_write()
- .map_err(|_| anyhow!("Pod coordination state is busy; retry resume"))?,
+ .map_err(|_| anyhow!("Fleet coordination state is busy; retry resume"))?,
),
None => None,
};
@@ -727,7 +727,7 @@ impl FleetManager {
let manager_lock_path = self.manager_lock_path(run_id);
if let Some(parent) = manager_lock_path.parent() {
std::fs::create_dir_all(parent)
- .with_context(|| format!("creating Pod manager lock dir {}", parent.display()))?;
+ .with_context(|| format!("creating Fleet manager lock dir {}", parent.display()))?;
}
let lock_file = OpenOptions::new()
.create(true)
@@ -735,7 +735,9 @@ impl FleetManager {
.read(true)
.write(true)
.open(&manager_lock_path)
- .with_context(|| format!("opening Pod manager lock {}", manager_lock_path.display()))?;
+ .with_context(|| {
+ format!("opening Fleet manager lock {}", manager_lock_path.display())
+ })?;
let mut manager_lock = fd_lock::RwLock::new(lock_file);
let standby_interval = tick_interval
.min(Duration::from_millis(100))
@@ -747,7 +749,7 @@ impl FleetManager {
if observed_owner {
if self.run_has_open_work(run_id)? {
bail!(
- "Pod manager for run {} exited with open work; wait for stale reconciliation before resuming",
+ "Fleet manager for run {} exited with open work; wait for stale reconciliation before resuming",
run_id.0
);
}
@@ -770,7 +772,7 @@ impl FleetManager {
Err(err) => {
return Err(err).with_context(|| {
format!(
- "locking Pod manager ownership {}",
+ "locking Fleet manager ownership {}",
manager_lock_path.display()
)
});
@@ -791,7 +793,7 @@ impl FleetManager {
if executor.worker_ids().is_empty() {
return Err(error).with_context(|| {
format!(
- "scheduling Pod run {} after draining owned workers",
+ "scheduling Fleet run {} after draining owned workers",
run_id.0
)
});
@@ -799,7 +801,7 @@ impl FleetManager {
tracing::warn!(
run_id = %run_id.0,
error = %error,
- "Pod scheduling paused while already-leased workers continue"
+ "Fleet scheduling paused while already-leased workers continue"
);
}
// A separate `fleet interrupt` process can make the ledger
@@ -1036,7 +1038,7 @@ impl FleetManager {
Some("operator"),
)?;
if !cancelled {
- bail!("worker {worker_id} no longer has that running Pod task");
+ bail!("worker {worker_id} no longer has that running Fleet task");
}
self.refresh_run_status(&task.entry.run_id)?;
self.inspect_worker(worker_id)
@@ -1047,12 +1049,12 @@ impl FleetManager {
let Some(task) = active_task_for_worker(&state, worker_id)
.or_else(|| latest_task_for_worker(&state, worker_id))
else {
- bail!("worker {worker_id} has no Pod task to restart");
+ bail!("worker {worker_id} has no Fleet task to restart");
};
let run = state
.runs
.get(&task.entry.run_id.0)
- .ok_or_else(|| anyhow!("Pod run {} does not exist", task.entry.run_id.0))?;
+ .ok_or_else(|| anyhow!("Fleet run {} does not exist", task.entry.run_id.0))?;
let max_workers = run
.max_workers
.unwrap_or_else(|| run.worker_specs.len().max(1))
@@ -1060,7 +1062,7 @@ impl FleetManager {
let mut coordination_guard = match &self.sub_agent_manager {
Some(manager) => {
let Ok(guard) = manager.try_write() else {
- bail!("Pod worker {worker_id} coordination state is busy; retry restart");
+ bail!("Fleet worker {worker_id} coordination state is busy; retry restart");
};
Some(guard)
}
@@ -1097,7 +1099,9 @@ impl FleetManager {
.task_specs
.iter()
.find(|spec| spec.id == task.entry.task_id)
- .ok_or_else(|| anyhow!("Pod task {} does not exist", task.entry.task_id))?;
+ .ok_or_else(|| {
+ anyhow!("Fleet task {} does not exist", task.entry.task_id)
+ })?;
self.prepare_registered_restart_generation(
guard, &state, task, task_spec, worker_id,
)?;
@@ -1132,7 +1136,7 @@ impl FleetManager {
let run = state
.runs
.get(&task.entry.run_id.0)
- .ok_or_else(|| anyhow!("Pod run {} does not exist", task.entry.run_id.0))?;
+ .ok_or_else(|| anyhow!("Fleet run {} does not exist", task.entry.run_id.0))?;
let worker_spec = run
.worker_specs
.iter()
@@ -1161,17 +1165,17 @@ impl FleetManager {
);
let record = coordination
.get_worker_record(worker_id)
- .ok_or_else(|| anyhow!("Pod worker {worker_id} has no registered launch spec"))?;
+ .ok_or_else(|| anyhow!("Fleet worker {worker_id} has no registered launch spec"))?;
let current_generation = task.entry.attempts.max(1);
let next_generation = current_generation
.checked_add(1)
- .ok_or_else(|| anyhow!("Pod worker {worker_id} exhausted launch generations"))?;
+ .ok_or_else(|| anyhow!("Fleet worker {worker_id} exhausted launch generations"))?;
let registered_generation = record
.spec
.launch_manifest
.as_ref()
.map(|manifest| manifest.generation)
- .ok_or_else(|| anyhow!("Pod worker {worker_id} has no persisted launch manifest"))?;
+ .ok_or_else(|| anyhow!("Fleet worker {worker_id} has no persisted launch manifest"))?;
match registered_generation {
generation if generation == current_generation => {
@@ -1194,7 +1198,7 @@ impl FleetManager {
}
generation => {
bail!(
- "Pod worker {worker_id} persisted launch generation {generation} does not match ledger attempt {current_generation} or its prepared retry {next_generation}"
+ "Fleet worker {worker_id} persisted launch generation {generation} does not match ledger attempt {current_generation} or its prepared retry {next_generation}"
);
}
}
@@ -1239,7 +1243,7 @@ impl FleetManager {
pub fn stop_run(&self, run_id: &FleetRunId) -> Result {
let state = self.ledger.rebuild_state()?;
if !state.runs.contains_key(&run_id.0) {
- bail!("Pod run {} does not exist", run_id.0);
+ bail!("Fleet run {} does not exist", run_id.0);
}
let now = timestamp();
let mut stopped = 0usize;
@@ -1284,7 +1288,7 @@ impl FleetManager {
.runs
.get(&entry.run_id.0)
.cloned()
- .ok_or_else(|| anyhow!("Pod run {} does not exist", entry.run_id.0))?;
+ .ok_or_else(|| anyhow!("Fleet run {} does not exist", entry.run_id.0))?;
let worker_spec = run
.worker_specs
.iter()
@@ -1329,7 +1333,7 @@ impl FleetManager {
run_id = %entry.run_id.0,
task_id = %entry.task_id,
error = %error,
- "Pod worker coordination is not currently admissible"
+ "Fleet worker coordination is not currently admissible"
);
return Ok(false);
}
@@ -1402,7 +1406,7 @@ impl FleetManager {
.runs
.get(&run_id.0)
.cloned()
- .ok_or_else(|| anyhow!("Pod run {} does not exist", run_id.0))?;
+ .ok_or_else(|| anyhow!("Fleet run {} does not exist", run_id.0))?;
let roster = self.agent_roster();
let mut started = 0usize;
for task in active_tasks_for_run(&state, run_id) {
@@ -1460,7 +1464,7 @@ impl FleetManager {
record.spec
}
Some(None) => {
- bail!("Pod worker {worker_id} has no coordination-registered launch spec")
+ bail!("Fleet worker {worker_id} has no coordination-registered launch spec")
}
None => expected_launch_spec,
};
@@ -1670,7 +1674,7 @@ impl FleetManager {
FleetWorkerEventPayload::Completed { .. } => FleetTaskLedgerStatus::Completed,
FleetWorkerEventPayload::Failed { .. } => FleetTaskLedgerStatus::Failed,
FleetWorkerEventPayload::Cancelled { .. } => FleetTaskLedgerStatus::Cancelled,
- _ => bail!("Pod executor outcome must contain a terminal worker event"),
+ _ => bail!("Fleet executor outcome must contain a terminal worker event"),
};
for tail_payload in tail_payloads {
if is_terminal_payload(&tail_payload) {
@@ -1966,14 +1970,14 @@ impl FleetManager {
let abs_path = self.workspace.join(&rel_path);
if let Some(parent) = abs_path.parent() {
std::fs::create_dir_all(parent)
- .with_context(|| format!("creating Pod artifact dir {}", parent.display()))?;
+ .with_context(|| format!("creating Fleet artifact dir {}", parent.display()))?;
}
let contents = format!(
"run_id={}\ntask_id={}\ntask_name={}\nworker_id={}\nstatus=started\n",
run_id.0, task_spec.id, task_spec.name, worker_id
);
std::fs::write(&abs_path, contents)
- .with_context(|| format!("writing Pod worker log {}", abs_path.display()))?;
+ .with_context(|| format!("writing Fleet worker log {}", abs_path.display()))?;
let size_bytes = std::fs::metadata(&abs_path).ok().map(|m| m.len());
Ok(FleetArtifactRef {
kind: FleetArtifactKind::Log,
@@ -2018,7 +2022,7 @@ impl FleetManager {
};
self.ledger
.update_run_status(run_id, status, ×tamp())
- .context("updating Pod run status")
+ .context("updating Fleet run status")
}
fn status_from_state(
@@ -2105,7 +2109,7 @@ fn default_local_worker_with_name(worker_id: &str, index: usize) -> FleetWorkerS
id: worker_id.to_string(),
name: format!("Local worker {index}"),
host: FleetHostSpec::Local,
- // Legacy ledgers may carry `trust_level`; new Pod workers leave
+ // Legacy ledgers may carry `trust_level`; new Fleet workers leave
// execution authority entirely to Runtime policy.
trust_level: None,
labels: BTreeMap::new(),
@@ -2379,7 +2383,7 @@ fn validate_registered_launch_spec(
) -> Result<()> {
let Some(registered_manifest) = registered.launch_manifest.as_ref() else {
bail!(
- "Pod worker {} has no persisted launch manifest",
+ "Fleet worker {} has no persisted launch manifest",
registered.worker_id
);
};
@@ -2387,7 +2391,7 @@ fn validate_registered_launch_spec(
|| !registered_prompt_matches_expected(®istered.objective, &expected.objective)
{
bail!(
- "Pod worker {} has an inconsistent persisted prompt",
+ "Fleet worker {} has an inconsistent persisted prompt",
registered.worker_id
);
}
@@ -2407,7 +2411,7 @@ fn validate_registered_launch_spec(
}
if registered_identity != expected_identity {
bail!(
- "Pod worker {} persisted launch spec does not match the exact task lease and attempt",
+ "Fleet worker {} persisted launch spec does not match the exact task lease and attempt",
registered.worker_id
);
}
@@ -2451,7 +2455,7 @@ fn validate_task_cwd_for_host(
.map_err(anyhow::Error::new)?;
if task_cwd != workspace_root {
bail!(
- "SSH Pod workers do not yet support nested workspace.root values; task cwd '{}' cannot be mapped safely beneath the remote working_directory",
+ "SSH Fleet workers do not yet support nested workspace.root values; task cwd '{}' cannot be mapped safely beneath the remote working_directory",
task_cwd.display()
);
}
@@ -3698,7 +3702,7 @@ mod tests {
let inspection = manager.inspect_worker(&report.worker_ids[0]).unwrap();
assert_eq!(
inspection.role.as_deref(),
- Some("consultant"),
+ Some("advisor"),
"inspection must not emit compatibility alias {alias}"
);
let state = manager.rebuild_state().unwrap();
@@ -3709,7 +3713,7 @@ mod tests {
.and_then(|worker| worker.role.as_deref());
assert_eq!(
persisted_role,
- Some("consultant"),
+ Some("advisor"),
"new durable task must not persist compatibility alias {alias}"
);
}
@@ -4786,9 +4790,9 @@ esac
assert_eq!(
roles,
BTreeSet::from([
- "builder".to_string(),
- "scout".to_string(),
- "verifier".to_string()
+ "implement".to_string(),
+ "explore".to_string(),
+ "test".to_string()
])
);
assert_eq!(state.receipts.len(), 10);
@@ -4859,11 +4863,11 @@ esac
"receipt {key} should record explicit tool names"
);
match route.role.as_deref() {
- Some("builder") => {
+ Some("implement") | Some("builder") => {
assert!(permissions.write, "builder receipt {key} should write");
assert_eq!(permissions.shell, "full");
}
- Some("scout") => {
+ Some("explore") | Some("scout") => {
assert!(
!permissions.write,
"scout receipt {key} must stay read-only"
@@ -4875,7 +4879,7 @@ esac
// profile, so headers and ledgers cannot overclaim.
assert_eq!(permissions.shell, "read_only");
}
- Some("verifier") => {
+ Some("test") | Some("verifier") => {
assert!(
!permissions.write,
"verifier receipt {key} must stay read-only"
@@ -5213,7 +5217,7 @@ esac
assert!(
error
.to_string()
- .contains("Selected folder Pod `Broken` is invalid or unreadable"),
+ .contains("Selected folder Fleet `Broken` is invalid or unreadable"),
"{error:#}"
);
assert!(manager.rebuild_state().unwrap().runs.is_empty());
diff --git a/crates/tui/src/fleet/members.rs b/crates/tui/src/fleet/members.rs
new file mode 100644
index 0000000000..bb7ff1c25b
--- /dev/null
+++ b/crates/tui/src/fleet/members.rs
@@ -0,0 +1,903 @@
+//! The fleet as a list of models (design MODEL-ROUTING-CATALOG §10, F1).
+//!
+//! A person builds their fleet by adding models from the providers they have
+//! configured; the operator model later picks sub-agent routes from that list
+//! only. The store is the selected fleet file (`fleet/store.rs`): its operator
+//! route and every member that pins an exact `provider` + `model`. Nothing
+//! here invents a second member store — a fleet model is a fleet member, and
+//! the roles a model fills are the member rows that pin it.
+
+use std::path::Path;
+
+use super::store::{
+ FleetFile, FleetMember, FleetScope, FleetStoreError, load_fleet_at, load_fleet_in_scope,
+ resolve_selected_fleet, save_fleet, set_selected, slugify,
+};
+use crate::localization::{Locale, MessageId, tr};
+use crate::tools::subagent::public_role_label;
+
+/// Default name for the personal fleet created by the first `/fleet add` or
+/// ⇧F on a row in `/model` when no fleet is selected yet.
+pub const DEFAULT_FLEET_NAME: &str = "My fleet";
+
+/// One model in the fleet: an exact route plus the roles that pin it.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FleetModel {
+ /// Exact provider id (a `[providers.]` key or a built-in id).
+ pub provider: String,
+ /// Exact model id on that provider's route.
+ pub model: String,
+ /// Roles whose member rows pin this route; `operator` for the fleet's
+ /// own route. Empty when the model was added without a role.
+ pub roles: Vec,
+ /// The fleet this model belongs to.
+ pub fleet: String,
+}
+
+impl FleetModel {
+ #[must_use]
+ pub fn matches(&self, provider: &str, model: &str) -> bool {
+ self.provider.eq_ignore_ascii_case(provider.trim())
+ && self.model.eq_ignore_ascii_case(model.trim())
+ }
+
+ /// `roles` joined for a one-line label, or `member` when none.
+ #[must_use]
+ pub fn roles_label(&self) -> String {
+ if self.roles.is_empty() {
+ "member".to_string()
+ } else {
+ self.roles
+ .iter()
+ .map(|role| public_role_label(role))
+ .collect::>()
+ .join(" · ")
+ }
+ }
+}
+
+/// Why a membership change wrote nothing.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum UnchangedReason {
+ /// The route is the fleet's operator route (your current model while
+ /// this fleet is selected) and has no member row of its own; `⇧F` and a
+ /// role-less `/fleet add` leave it alone instead of duplicating it.
+ OperatorRoute,
+ /// Every requested role already pins the route.
+ AlreadyPresent,
+}
+
+/// What a membership change did, for the receipt line.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FleetModelChange {
+ Added {
+ fleet: String,
+ /// The default personal fleet was created for this add.
+ created_fleet: bool,
+ /// The fleet was not selected before this add and is now.
+ selected_fleet: bool,
+ roles: Vec,
+ },
+ Removed {
+ fleet: String,
+ roles: Vec,
+ },
+ /// Nothing was written.
+ Unchanged {
+ fleet: String,
+ reason: UnchangedReason,
+ },
+}
+
+/// Why a membership change could not be made. Typed so the surface that
+/// shows it picks the person's locale; `Display` is the English form for logs.
+#[derive(Debug)]
+pub enum FleetModelError {
+ /// Provider id or model id was blank.
+ NeedsRoute,
+ /// No fleet is selected, so there is nothing to remove from.
+ NoSelection,
+ /// The route is the fleet's operator route, which `/fleet save` changes.
+ OperatorRoute { route: String, fleet: String },
+ /// No member row pins the route.
+ NotInFleet { route: String, fleet: String },
+ /// The selected fleet could not be resolved, read, parsed, or written.
+ Store(FleetStoreError),
+}
+
+impl From for FleetModelError {
+ fn from(error: FleetStoreError) -> Self {
+ Self::Store(error)
+ }
+}
+
+impl FleetModelError {
+ /// The person-facing explanation in `locale`.
+ #[must_use]
+ pub fn message(&self, locale: Locale) -> String {
+ match self {
+ Self::NeedsRoute => tr(locale, MessageId::FleetModelErrorNeedsRoute).into_owned(),
+ Self::NoSelection => tr(locale, MessageId::FleetModelErrorNoSelection).into_owned(),
+ Self::OperatorRoute { route, fleet } => {
+ tr(locale, MessageId::FleetModelErrorOperatorRoute)
+ .replace("{route}", route)
+ .replace("{fleet}", fleet)
+ }
+ Self::NotInFleet { route, fleet } => tr(locale, MessageId::FleetModelErrorNotInFleet)
+ .replace("{route}", route)
+ .replace("{fleet}", fleet),
+ Self::Store(error) => error.to_string(),
+ }
+ }
+}
+
+impl std::fmt::Display for FleetModelError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.message(Locale::En))
+ }
+}
+
+impl std::error::Error for FleetModelError {}
+
+/// The selected fleet's models, operator first, then members in file order.
+/// Members that inherit the session route (no pin) are not models of their
+/// own and are skipped.
+///
+/// `Ok(empty)` means no fleet is selected (or the selected one has no pinned
+/// route): the caller states "your fleet is the session model only". `Err`
+/// means a fleet *is* selected but cannot be resolved or read — a broken
+/// explicit selection, which must never be shown as "no selection".
+pub fn fleet_models(workspace: &Path) -> Result, FleetStoreError> {
+ let Some(selected) = resolve_selected_fleet(workspace)? else {
+ return Ok(Vec::new());
+ };
+ let (fleet, _scope) = load_fleet_at(&selected.path)?;
+ Ok(models_of(&fleet))
+}
+
+/// Project a fleet file into its models with roles unioned per exact route.
+#[must_use]
+pub fn models_of(fleet: &FleetFile) -> Vec {
+ let mut models: Vec = Vec::new();
+ let mut push = |provider: &str, model: &str, role: Option<&str>| {
+ let role = role.map(str::trim).filter(|r| !r.is_empty());
+ if let Some(existing) = models.iter_mut().find(|m| m.matches(provider, model)) {
+ if let Some(role) = role
+ && !existing.roles.iter().any(|r| r.eq_ignore_ascii_case(role))
+ {
+ existing.roles.push(role.to_string());
+ }
+ return;
+ }
+ models.push(FleetModel {
+ provider: provider.trim().to_string(),
+ model: model.trim().to_string(),
+ roles: role.map(|r| vec![r.to_string()]).unwrap_or_default(),
+ fleet: fleet.name.clone(),
+ });
+ };
+ if let Some(operator) = fleet.operator.as_ref() {
+ push(&operator.provider, &operator.model, Some("operator"));
+ }
+ for member in &fleet.members {
+ let (Some(provider), Some(model)) = (member.provider.as_deref(), member.model.as_deref())
+ else {
+ continue;
+ };
+ // A member added without a role carries an id derived from the model
+ // and an empty role; it is a model in the fleet, not a role.
+ let role = (!member.role.trim().is_empty()).then_some(member.role.as_str());
+ push(provider, model, role);
+ }
+ models
+}
+
+fn member_pins(member: &FleetMember, provider: &str, model: &str) -> bool {
+ member
+ .provider
+ .as_deref()
+ .is_some_and(|p| p.eq_ignore_ascii_case(provider))
+ && member
+ .model
+ .as_deref()
+ .is_some_and(|id| id.eq_ignore_ascii_case(model))
+}
+
+/// Add `provider/model` to the selected fleet, one member row per role.
+///
+/// With no fleet selected, the personal [`DEFAULT_FLEET_NAME`] fleet is used —
+/// loaded when it already exists, created otherwise — and selected only once
+/// the add has been written. Roles are deduplicated; when every requested
+/// role already pins the route nothing is rewritten and the change is
+/// [`FleetModelChange::Unchanged`].
+pub fn add_fleet_model(
+ workspace: &Path,
+ provider: &str,
+ model: &str,
+ roles: &[String],
+) -> Result {
+ let provider = provider.trim();
+ let model = model.trim();
+ if provider.is_empty() || model.is_empty() {
+ return Err(FleetModelError::NeedsRoute);
+ }
+ let target = selected_or_default(workspace)?;
+ let SelectedOrDefault {
+ mut fleet,
+ scope,
+ created_fleet,
+ needs_select,
+ } = target;
+ let mut unique: Vec = Vec::with_capacity(roles.len());
+ for role in roles.iter().map(|r| r.trim()).filter(|r| !r.is_empty()) {
+ if !unique.iter().any(|seen| seen.eq_ignore_ascii_case(role)) {
+ unique.push(role.to_string());
+ }
+ }
+ let roles = unique;
+ let select = |needs_select: bool| -> Result<(), FleetModelError> {
+ if needs_select {
+ set_selected(DEFAULT_FLEET_NAME, FleetScope::Personal, workspace)?;
+ }
+ Ok(())
+ };
+ if roles.is_empty() && is_operator_route(&fleet, provider, model) {
+ select(needs_select)?;
+ return Ok(FleetModelChange::Unchanged {
+ fleet: fleet.name,
+ reason: UnchangedReason::OperatorRoute,
+ });
+ }
+ let model_slug = slugify(model);
+ let wanted: Vec