Commit Graph

214 Commits

Author SHA1 Message Date
CodeWhale Bot f11657e77f fix(mistral): isolate the reasoning wire contract
Scope Mistral's polymorphic reasoning and replay behavior to exact first-party HTTPS routes, preserve stored thinking across real prompt construction, and keep DeepSeek's sanitizer from injecting a second dialect into tool-call history.

Align the current model registry, provider-scoped model override, generated facts, docs, and focused route-isolation tests. Split the large stream decoder test module so the source-structure gate remains below budget.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-08 17:23:14 -07:00
Xavier Pestel f157d34a51 feat: add Mistral AI as a first-class provider route
Wire Mistral AI / la Plateforme into the shared provider registry, TUI
provider enum, provider-scoped config/env overrides, static model
registry, context-window metadata, reasoning wiring, docs, and
examples. The route uses Mistral's OpenAI-compatible Chat Completions
endpoint at https://api.mistral.ai/v1 with 'mistral-code-latest' as
the default model (Codestral coding model, 256K context).

Model IDs verified live against https://api.mistral.ai/v1/models: the
static registry ships 'mistral-code-latest' (accepts 'codestral-latest'
as alias for backward compatibility), 'mistral-medium-latest',
'mistral-small-latest', 'magistral-small-latest', and
'mistral-large-latest'. All models report 262144 (256K) context on
/v1/models except mistral-code-latest at 256000; earlier drafts of
this PR had those windows reversed.

Reasoning is wired end-to-end for the three models that advertise
'reasoning: true' on /v1/models — mistral-medium-latest,
mistral-small-latest, and magistral-small-latest. Codewhale sends
'reasoning_effort' (Mistral currently accepts 'none' or 'high' only;
intermediate tiers return HTTP 400 code 3051), parses the polymorphic
'content: [{type: thinking, thinking: [{type: text, text: ...}],
closed: bool}, {type: text, text: ...}]' shape emitted by reasoning
models, and replays the thinking trace back into multi-turn history
per docs.mistral.ai/capabilities/reasoning. Non-reasoning models
(mistral-code-latest, mistral-large-latest) never receive the field
because Mistral would reject it. FIM (/v1/fim/completions) is not
wired.

Provider aliases: mistral-ai, mistralai, la-plateforme. Env vars:
MISTRAL_API_KEY, MISTRAL_BASE_URL, MISTRAL_MODEL. Auth via API key
from https://console.mistral.ai/api-keys, config, or 'codewhale auth
set'.

Test env-poisoning: EnvGuard captures/removes/restores MISTRAL_* so
tests stay reproducible when a user has these vars exported in their
shell.

Validation:
- cargo fmt --all -- --check
- cargo clippy --workspace --all-targets --all-features --locked (with
  the documented allow list) -- No issues found
- cargo test --workspace --all-features --locked -- 22 pre-existing
  failures in crates/tui git-shell tests (worktree init failing on
  'git commit' in isolated tempdirs), verified identical count on
  origin/main at 91bca01a9 and unrelated to this change
- python3 scripts/check-provider-registry.py -- passed
- codewhale --provider mistral --model mistral-medium-latest exec
  against api.mistral.ai returned a correct reasoning-mode response
- codewhale --provider mistral --model mistral-large-latest exec
  succeeded without HTTP 400 code 3051 (verifies the model-aware
  reasoning gate)
- TUI smoke previously validated: /status shows mistral +
  mistral-code-latest, /provider lists Mistral, tool call end-to-end

Assisted by Codex CLI for implementation and multiple Oracle review
passes (correctness + convention + Hunter's inline review) that
surfaced the ProviderArg clap enum gap, the ModelRegistry silent
fallthrough to DeepSeek, the Codestral context-window regression, the
EnvGuard env-poisoning flake, and the model-ID / context-window /
reasoning-support mistakes from the initial docs-slug pass now
corrected against the live /v1/models catalog.
2026-08-09 02:00:32 +02:00
CodeWhale Bot 580668bd3e docs: document auto model in config.example.toml and CHANGELOG
When model = auto, dispatcher analyses prompt and selects pro vs flash.
2026-08-07 04:34:12 -07:00
Hmbown f6bd6c1753 feat(agent): Scout replaces the user-facing "faster" control
One visible fast exploratory role: Scout. The agent tool schema no
longer advertises `model_strength` (parsing survives for compatibility
and maps onto the Scout policy); the workflow tool schema text and
config.example.toml copy now speak in Scout terms.

- fleet/scout.rs: resolve_scout_route with an explicit order — a pinned
  Scout always wins and survives operator changes; an unpinned Scout
  gets the provider's documented fast sibling (the existing
  provider_router_candidates tables: DeepSeek pro/flash, Z.ai → GLM-5-
  Turbo, Claude → Haiku, provider-specific wire spellings) VERIFIED
  against the merged catalog before it is ever suggested; no verified
  companion means deliberate inheritance, never an invented fallback;
  no session route at all is Unavailable with a precise reason.
- The Fleet detail view shows the resolved Scout route before a run:
  `scout → provider/model (pinned | catalog suggestion | inherits
  session route)`.
- Receipts: legacy `faster`/`fast` values still parse and resolve
  through the same Scout policy.

Tests: 5 scout resolution tests (pin wins + survives operator change,
verified companion, inheritance with no sibling, unavailable reason,
catalog-verification honesty gate) and the schema-vocabulary test now
asserts model_strength's absence while keeping the closed role enum.
cargo test -p codewhale-tui --bin codewhale-tui: 9800 passed, 0 failed,
9 ignored. cargo fmt clean.
2026-08-04 23:29:11 -07:00
Hmbown 08fcefcc0a docs(config): stop the workshop block documenting things that are not true
Three lies in one comment block, all the same family the tool sweep just
closed:

- `raw = true` was documented as a per-call bypass for output routing. It is
  not. The adaptive router takes it as `_raw_bypass` and ignores it; it is
  honoured only under the legacy CODEWHALE_CLASSIC_OUTPUT_ROUTING switch, and
  no tool advertises it. A user setting it got routing anyway, silently.
- The per-tool override example keys off `exec_shell`, `grep_files`, and
  `web_search`, all retired. An override written from this example matches
  nothing and does nothing, with no error.

Names the live tools instead and says plainly that the escape hatch is not
one. `raw` itself is left alone pending a decision on whether it should exist.
2026-08-04 04:38:50 -07:00
Hmbown 50b96929b6 feat(update): throttle the startup update check and name the right command
The TUI has told users about new releases since #3961/#14, but it asked
GitHub on every single launch and always advertised `codewhale update` --
which is the wrong command for most installs and actively harmful for some.
This adds the "throttled" half of #5053 and fixes the wording.

Throttling. `codewhale-release::check` caches the answer in
~/.codewhale/update-check.json and reuses it for `check_interval_hours`
(default 24). The cache stores the *tag we last saw*, not a "checked
recently" flag: a user on a stale binary still sees the notice on every
launch while the network is touched once a day. Caching only a timestamp
would have hidden the notice for the whole interval, which is the opposite
of the point. A failed check is deliberately not cached, so an outage does
not suppress the notice until tomorrow.

Suppression. Checks are skipped without touching the network in CI
(CI, GITHUB_ACTIONS, GITLAB_CI, ...) and on CODEWHALE_NO_UPDATE_CHECK or
NO_UPDATE_NOTIFIER. Values of "", 0, false, no, off do not count as set, so
a `CI=false` export does not disable checks for ordinary users. The
decision is factored into a pure `resolve_version_check_source` so this
repo's own CI run does not change the answer under test.

Install-method awareness. `codewhale-release::install` classifies the
running binary from its path -- npm (node_modules), Homebrew (Cellar /
linuxbrew), cargo (~/.cargo/bin), or a plain release binary -- and the
notice now names that manager's command. Package-managed installs also get
an explicit warning against `codewhale update`: overwriting a binary
Homebrew or npm owns leaves the manager describing a version that is no
longer on disk, and its next upgrade silently reverts the user. `codewhale
update` itself prints the same warning before proceeding; it warns rather
than refuses, since the download still yields a working binary and refusing
would break workflows that have relied on it. Homebrew intentionally points
at the legacy `deepseek-tui` formula -- no `codewhale` formula is published
yet, and naming one that does not exist would hand the user a failing
command.

Nothing is installed without the user asking. The check remains
fire-and-forget: it never delays startup and never blocks a turn.

Still open on #5053: the one-chord update-and-relaunch. Left out rather
than half-wired -- running a package manager on the user's behalf from
inside the TUI needs a confirmation surface and a clean re-exec path that
this change does not build.

Tests: 6 new in codewhale-release (cache freshness, clock skew, atomic
round-trip, corrupt cache, install detection), 5 new in the TUI (CI
suppression, cache hit answers offline, failure is not cached,
install-specific wording), 1 in the CLI updater.
2026-08-04 01:44:26 -07:00
Hmbown d6fa973675 docs(memory): say that memory_path is an anchor, not the file written
`config.example.toml` shipped `memory_path = "~/.codewhale/memory.md"` and
three docs implied that file is what gets written. Under the Native backend
— the only backend — the filename is discarded and the store is re-rooted to
`<parent>/memory/global/MEMORY.md`. Users who pointed the setting at the
native layout path double-nested the tree.

States the re-rooting explicitly in the example config, CONFIGURATION.md and
MEMORY.md, and names the resolved path for the shipped default.
2026-08-04 00:41:40 -07:00
Hmbown 0bc9f02ee4 feat(telemetry): ship the ingest endpoint as the resolved default
The first-party ingest service is deployed at
https://telemetry.codewhale.net/v1/telemetry (Cloudflare Worker, source in
telemetry-ingest/). Until now `telemetry_endpoint` resolved to `None` no
matter what, so even a user who answered Enable at the first-run notice was
writing to `dryrun.jsonl` and contacting nobody. Wire the default.

`DEFAULT_TELEMETRY_ENDPOINT` is applied in `resolve_runtime_options`, not as
a serde default on `ConfigToml`, so `get_value`/`list_values` still report an
unconfigured key as unconfigured and the four config verbs round-trip
unchanged. Precedence is unchanged in shape: environment, then config file,
then — new — the shipped default.

This changes *where* an enabled session's batches go, never *whether* a
session collects. Telemetry is still opt-in and off by default; the endpoint
is read only after `telemetry` resolved true, which requires the first-run
notice to have been answered with Enable. `CODEWHALE_TELEMETRY=0`,
`telemetry = false`, and a recorded decline are all upstream of this line and
all still hard floors.

The local dry-run sink stays reachable through an explicitly *empty*
endpoint, in the config file or the environment. That required dropping the
env-layer `.filter(non-empty)`: with a default behind it, discarding an
emptied `CODEWHALE_TELEMETRY_ENDPOINT=` would have fallen through to the
shipped endpoint — the exact opposite of what anyone typing it means.

Three tests pin the behavior: the default by literal value (so it cannot
drift), a config-file and environment value each beating it, and empty
resolving to `None` from both sources. `TelemetryEnvGuard` now also clears
the endpoint variables, so an ambient value in a developer's or CI's
environment cannot make the default assertion vacuous.
2026-08-03 22:56:30 -07:00
Hmbown f2ba5d15e4 docs: make the tool-surface, subagent, and config references match the code
docs/TOOL_SURFACE.md carried four claims the runtime's own tests contradict:

1. "The default-active policy contains exactly these ten names" listing
   `update_plan`. `DEFAULT_ACTIVE_NATIVE_TOOLS`
   (crates/tui/src/core/engine/tool_catalog.rs:44-58) has eight entries and
   `update_plan` is not among them — it appears nowhere in tool_catalog.rs. The
   policy is nine (those eight plus synthetic `tool_search`), eight with memory
   disabled. `update_plan` is registered (crates/tui/src/tools/plan.rs:401) but
   reachable only through `tool_search`; the tool table now says so.
2. "A memory-disabled or Moraine-fallback runtime". There is no Moraine
   fallback — docs/MEMORY.md:11-13 records the removal, and
   crates/tui/src/prompts.rs:2445-2449 is a test asserting MEMORY_GUIDANCE must
   not contain the word.
3. A "Replay-only aliases" table promising "saved transcripts, sessions, and
   recorded automation replay without migration" for 23 names, 16 of which are
   asserted REMOVED at crates/tui/src/tools/registry.rs:2066-2088 ("{retired}
   must stay removed") and 6 more at :2290-2304 ("{alias} must be removed").
   Split into a "Removed spellings" section (with the registry.rs:313-316 note
   that resolve has no fuzzy step, so those calls fail rather than dispatch) and
   a "Replay-only aliases" section holding only what is still registered:
   apply_patch, task_*, github_*, automation_*, rlm_*, checklist_*/todo_*.
4. A "Release verification" block whose three cargo filters name tests that do
   not exist (`rg` finds those three strings only in that doc). `cargo test`
   exits 0 with "0 passed; N filtered out" on a filter that matches nothing, so
   a release engineer following it got three green checkmarks having verified
   nothing. Replaced with the real names —
   `shell_surface_contains_only_the_canonical_bash_tool` (registry.rs:2290) and
   `runtime_task_families_expose_only_canonical_tools` (registry.rs:2333) — plus
   the receipt test, and a warning about the silent-pass failure mode.

docs/RUNTIME_SIMPLIFICATION_DESIGN.md repeats errors 1 and 3 and is designated
authoritative by docs/TOOL_LIFECYCLE.md:3-7, but carries no status marker. Given
a status banner naming both divergences and pointing at TOOL_SURFACE.md; the
"Rejected alternatives" provenance is worth keeping, so not deleted.

docs/SUBAGENTS.md:
- "a bounded queue of up to 200 running plus queued sub-agents by default" —
  `MAX_SUBAGENT_ADMISSION` is 1024 (crates/tui/src/config/subagent_limits.rs:21),
  which is what docs/TOOL_SURFACE.md:182 already said. The 64/128 concurrency
  figures on the same page were correct and are untouched.
- The memory section described a `memory.md` that does not exist and omitted the
  `scope` parameter. crates/tui/src/tools/remember.rs:165 states the legacy
  single-file path was removed in v0.9.4; writes go through
  `NativeMemoryStore::remember(scope, workspace_id, note)` (remember.rs:77-108).

config.example.toml documented two key sets that do not exist. Neither struct has
`deny_unknown_fields`, so both were silently discarded rather than rejected:
- `[advisor] max_tool_pairs` / `system_prompt`. `AdvisorConfigToml`
  (crates/config/src/lib.rs:2369-2394) has enabled, max_tool_calls (default 10,
  clamped 1-50 — the doc said 8, max 32), rate_limit_secs, dedup_window_secs,
  and model. `model` was undocumented; now it is.
- `[fleet.profiles.*.permissions] allow_tools` / `deny_tools`.
  `FleetProfilePermissions` (lib.rs:1966-1977) has allow_shell, trust,
  approval_required. `rg 'allow_tools|deny_tools' crates/` finds nothing. The
  example value was `"exec_shell"`, itself a removed tool name.

docs/CONFIGURATION.md: deleted the "Parsed but currently unused" section. Its one
entry, `tools_file`, is not parsed by anything — the field was removed in
346bfe3b6 and the doc bullet was orphaned. Repo-wide `rg` finds the string only
in that section, and nothing links a #parsed-but-currently-unused anchor.

docs/TTC_DESIGN.md said implementation "is deferred beyond v0.9.0". The `verify`
tool shipped and is default-on (crates/tui/src/tools/verify.rs,
features.rs:262, registry.rs:1040-1041 with verify_tool_enabled defaulted true).
Retitled as landed-in-part; capability (B) is still genuinely deferred, so the
doc stays. Its interface line said `with_verify(critic)`; the real signature is
`with_verify_tool(client, model)` (registry.rs:886).

docs/skills/README.md advertised `gh-plan-issues`, deleted in 18de2ebc0, and
credited these skills to "the v0.8.61 release" at a 0.9.4 release.

docs/architecture/provider-model-settings-v091.md pinned
`provider_is_configured` to config.rs:8625-8669; it is at :10160 and that region
is now unrelated code. Replaced with the symbol name, since config.rs is under
active edit.

docs/architecture/command-dispatch.md:133 claimed EPIC-002 was "ready for PR".
The PR (#3706) merged and #2870 closed 2026-08-01. Line 145 was an empty
"Current Evidence (Draft)" heading with no content; removed.

.gitignore: `git check-ignore -v` attributes .claude/settings.json,
scheduled_tasks.lock, worktrees/, and *.local.* to the blanket `.claude/` at
line 126, not to the specific rules above them. Dropped the redundant ones and
annotated why the HANDOFF_/CODEMAP_ patterns are deliberately kept.
2026-08-03 22:51:59 -07:00
Hmbown e9f63a6c10 test(tui): pin the two off switches apart at process level, and correct the copy that conflated them
`a_run_scoped_kill_switch_preserves_a_consenting_users_state` runs the shipped
binary three times with `CODEWHALE_TELEMETRY` set to each spelling of "off"
against a seeded, consenting home, and asserts the directory comes back
byte-identical with no tombstone — then writes `telemetry = false` to the same
home and asserts that one *does* wipe. Both halves matter: a test where the two
switches are merely both silent would pass on the old, destructive behavior.

`config.example.toml` and `docs/CONFIGURATION.md` each repeated the "permanent
tombstone" claim `docs/TELEMETRY.md` made and could not keep. They now say what
is true and testable — the tombstone stands for as long as the `false` that
produced it stands, the config key outranks `--telemetry true` and
`CODEWHALE_TELEMETRY=1`, and the environment variable erases nothing.

`every_event()` gets the note it has needed since it was written: it is
hand-maintained, every red-line walk starts from it, and nothing in this file
can make the compiler extend it. `Event::is_bounded`'s exhaustive match is what
actually catches a new variant, and the note says so rather than implying a
guarantee the fixture list does not carry.
2026-08-03 22:31:23 -07:00
Hmbown 132ab14826 fix(web): extend the CJK heading line-break rule to ja and ko
`html[lang="zh"] h1/h2/h3` relaxed `overflow-wrap: anywhere` so Chinese
headings stop stranding punctuation on a line of its own. ja needs exactly the
same rule and never had it — capping the ocean headings made it visible, with
`コマンド 1 つで始める。` breaking between `1` and `つ`. ko wants it too: it has
real word boundaries and should break on them rather than anywhere.

Extended rather than duplicated, per the rule that CJK overrides are extended
and never routed around.

Verified by eye at /ja and /ko, 1440px and 390px; no horizontal page scroll in
en/zh/ja/ko at 390px. npm test (235), lint, check:locales, check:docs.
2026-08-03 21:31:30 -07:00
Hmbown dfb739faa1 docs: correct the reasoning-tier keybinding and record the Moonshot CN endpoint
Both findings come from @vFONGv's Windows beginner guide (PR #5229), verified
against the code before landing.

config.example.toml claimed "Shift+Tab in the TUI cycles between off / high /
max". That is stale: crates/tui/src/tui/app.rs:2370 emits the notice
"Shift+Tab now cycles permissions — reasoning effort moved to Ctrl+T". A user
following the config comment would cycle their permission posture while trying
to change reasoning depth, which is the more consequential of the two. His
guide had it right and our own example config had it wrong.

docs/PROVIDERS.md gains his China-region Moonshot finding: a China-region key
needs base_url = "https://api.moonshot.cn/v1" or it fails authentication on the
default international host, and editing base_url alone does not take effect
until `codewhale auth set` is re-run. `api.moonshot.cn` appeared nowhere in the
repo, so this is new information. Recorded as attributed user field evidence
rather than a tested route — we have no China-region key to verify it.

Harvested from PR #5229

Co-authored-by: vFONGv <21223725+vFONGv@users.noreply.github.com>
2026-08-03 20:49:00 -07:00
Hmbown 3cb6fd3b7e feat(models): add GLM-5.3 as a first-class Z.ai model
Owner directive: add GLM-5.3 everywhere GLM models appear.

Scope is deliberately narrower than "everywhere glm-5.2 appears", and the
reason is the whole point of this commit. GLM-5.3 is NOT live on the Z.ai
API — the owner's own credential was used to query it and the live roster
returns glm-4.5, glm-4.5-air, glm-4.6, glm-4.7, glm-5, glm-5-turbo, glm-5.1,
glm-5.2 and nothing further. Zhipu has published no GLM-5.3 identifier,
endpoint, limit, rate, or capability list.

So this wires the model where we can be honest about it, and nowhere else:

- First-party Z.ai row (GLM-5.3) and its OpenRouter mirror (z-ai/glm-5.3):
  catalog, aliases, model registry, picker lists, context/output limits,
  reasoning classification, and the tiered-effort wire path.
- Every capability and limit is INHERITED field-for-field from the verified
  glm-5.2 row (1M context, 131072 output, reasoning with effort high/max).
  Nothing is invented.
- No pricing. Z.ai has published no GLM-5.3 rate, and inheriting 5.2's would
  fabricate one, so every price surface reports unknown. Pinned by
  glm_5_3_has_no_hardcoded_price, which says in its own comment not to "fix"
  it by copying 5.2's row.
- GLM-5.2 remains the default for every provider, profile and fleet role.
  Adding a model does not move anyone's route.
- One greppable marker in models_dev.bundled.json `_meta.pending_release_metadata`
  records the inheritance and the scope, so correcting the id or the limits
  when Z.ai ships is a single-place edit.

Deliberately NOT added: OpenCode Zen, OpenCode Go, Alibaba Model Studio, and
TelecomJS rosters. Those tables transcribe what a third-party gateway
publishes. Metadata inheritance is not evidence of third-party availability —
there is no glm-5.2 value to copy, because the fact in question is roster
membership, not a limit. An honesty audit caught six such claims after the
first pass (including docs wording that read as a promise that these gateways
serve it); all six were removed rather than re-valued, and each roster now
carries a dated comment naming the evidence that would justify adding it.
The TelecomJS arm is additionally annotated as a frozen pre-refresh snapshot
(it still lists a GLM-5.0 we do not otherwise model) that must be refreshed
wholesale, not hand-extended.

`is_exact_zai_glm_5_2_route` became `is_exact_zai_tiered_effort_route`, since
the tiered top-level reasoning_effort path is now a family property rather
than one model's.

Receipts, all exit 0: cargo fmt --all --check; cargo test -p codewhale-config;
cargo test -p codewhale-tui --bin codewhale-tui; cargo test -p codewhale-workflow;
cargo test -p codewhale-cli; cargo test -p codewhale-agent;
cargo clippy -p codewhale-config -p codewhale-cli.
End-to-end: `codewhale model list` shows GLM-5.3 alongside GLM-5.2 with the
default unchanged, and `model resolve` maps the alias set correctly.
2026-08-03 18:59:41 -07:00
Copilot 84cbbd8132 feat(advisor): add opt-in background advisor watcher for live turns (#3982) (#5139)
* Initial plan

* feat(advisor): add optional background advisor watcher (issue #3982)

Implements the opt-in advisor/watcher mode requested in issue #3982.
The advisor fires fire-and-forget after each successfully-completed turn
that contains tool calls, reads a bounded slice of recent tool-call/result
pairs from the session transcript, makes a concise LLM call, and emits an
advisory note into the transcript status area.

Key design properties (Shape B — reuses existing subagent primitives):
- Off by default (enabled = false); toggle via /advisor on|off or
  [advisor] enabled = true in config.toml
- Bounded input: max 8 tool-call/result pairs by default (max 32)
- Rate-limited: one emission per 60 s by default (configurable)
- Deduplicated: identical notes within 300 s window are suppressed;
  advisor responses of exactly "ok" are silently dropped
- Child-failure isolated: advisor errors are logged, never surfaced as
  parent turn failures (spawn_supervised wraps the task)
- Policy-bounded: read-only reviewer prompt, no tool access, 256 tokens

New files:
- crates/tui/src/tools/subagent/advisor.rs — AdvisorConfig, EmissionGuard,
  ToolCallPair, extract_tool_call_pairs(), build_advisor_prompt(),
  run_advisor_for_turn(); 10 unit tests covering all acceptance criteria
- crates/tui/src/commands/groups/core/advisor.rs — /advisor command

Changed files:
- crates/config/src/lib.rs: AdvisorConfigToml TOML schema struct
- crates/tui/src/core/events.rs: Event::AdvisoryNote variant
- crates/tui/src/core/ops.rs: Op::SetAdvisorEnabled op
- crates/tui/src/tools/subagent/mod.rs: pub mod advisor + re-exports
- crates/tui/src/core/engine.rs: advisor_config in EngineConfig + Engine
  struct field + op handler + post-TurnComplete spawn hook
- crates/tui/src/tui/app/types.rs: AppAction::SetAdvisorEnabled
- crates/tui/src/tui/ui.rs: AppAction handler + Event::AdvisoryNote
  display + advisor_config wired in build_engine_config
- crates/tui/src/config.rs: advisor field in Config struct + merge
- crates/tui/src/main.rs: advisor_config in headless EngineConfig
- crates/tui/src/runtime_threads.rs: advisor_config in runtime EngineConfig
- crates/tui/src/commands/groups/core/mod.rs: register /advisor command
- crates/tui/src/localization.rs: CmdAdvisorDescription message ID
- crates/tui/locales/*.json (en + 13 complete locales): translation key
- config.example.toml: [advisor] section documentation

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-03 05:49:06 -07:00
Copilot 396c58a690 feat(config): multiple named operator-scoped Fleet configurations (v0.9.4) (#5137)
* Initial plan

* feat(config): add multiple named operator-scoped Fleet configurations (#5039)

Adds support for multiple named durable Fleet configurations in the config TOML,
each scoped to an operator identity. The existing [fleet] table remains the
backward-compatible default.

New public types:
- `NamedFleetConfigToml`: a [fleets.<name>] entry with a required `operator`
  field plus independent trust/role/profile/exec settings. Exposes
  `resolve_role()` and `as_fleet_config()` for unified usage.
- `FleetResolutionError`: typed, actionable errors (UnknownFleet,
  UnknownOperator, AmbiguousOperator) with human-readable Display messages
  that list available options rather than failing silently.

New methods on `ConfigToml`:
- `resolve_fleet(name)`: returns the named fleet or `UnknownFleet` error with
  available names listed.
- `resolve_fleet_for_operator(operator)`: returns the unique fleet owned by an
  operator; `UnknownOperator` if none matches, `AmbiguousOperator` if more
  than one matches (caller must name a fleet explicitly).

New `fleets` field on `ConfigToml`: `BTreeMap<String, NamedFleetConfigToml>`,
serialized under [fleets.*] keys. Skipped when empty so legacy configs are
byte-for-byte unchanged.

config.example.toml updated with full named-fleet documentation and examples,
including the selection-precedence comment.

14 new tests added to crates/config/src/tests.rs covering: legacy-only,
mixed (legacy + named), multiple named fleets, resolve_fleet/operator success
and error paths, error message content, as_fleet_config view, and round-trip
serialization. All 489 tests pass.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-03 05:48:54 -07:00
Hunter Bown 237215a560 fix(tui): retry sub-agent API timeouts with backoff; raise default timeout to 600s (#5210)
A per-step create_message call that exceeded step_api_timeout went
straight to Interrupted with zero retries, so one live-but-slow provider
call killed an entire child (dogfood: a 6-agent fan-out wiped out one by
one at the 120s wall, FINISH-0.9.4 entries #39/#40). Fold the timeout
arm into the retry machinery:

- SUBAGENT_API_TIMEOUT_MAX_RETRIES (5) per-step timeout budget with
  exponential backoff (1s base, x2, 30s cap, +/-20% jitter via the
  llm_client UUID-entropy idiom) and the same ModelWait progress event
  style as the transient-provider retry path.
- After exhaustion, behavior is unchanged: Interrupted with the
  checkpoint preserved for continuation (api_timeout).
- DEFAULT_SUBAGENT_API_TIMEOUT_SECS 120 -> 600 and clamp ceiling
  1800 -> 3600; doc comments, config.example.toml, and docs updated
  (including the stale ~/.deepseek/config.toml reference). The resolved
  default heartbeat rises to 630s via the existing api+30s floor.
- Tests: deterministic backoff sequence + jitter bounds, retry-then-
  success and retry-then-exhaustion integration coverage (new
  always_delayed_chat_client helper; backoff shrinkable in tests),
  config clamp literals (600 default, 3600 accepted, 3601 clamped).
2026-08-03 03:15:14 -07:00
copilot-swe-agent[bot] 0aa3e10b0c feat: first-class Alibaba Cloud Model Studio provider (Token Plan + Coding Plan, OpenAI + Anthropic dialects)
Adds 4 ProviderKind variants (ModelstudioTokenPlan, ModelstudioTokenPlanAnthropic,
ModelstudioCodingPlan, ModelstudioCodingPlanAnthropic) with full integration across
codewhale-config and codewhale-tui.

Changes:
- provider_defaults.rs: 5 Model Studio constants (2 base URLs + 2 Anthropic base URLs + default model)
- provider_kind.rs: 4 new variants, ALL array 37→41
- provider.rs: 4 provider structs + credential_help + PROVIDER_REGISTRY 37→41; display order test updated
- lib.rs: ProvidersToml fields, for_provider/mut, base_url/default_model/passes_model_through, EnvRuntimeOverrides
- route/offering.rs: MODELSTUDIO_TEXT_MODELS const (8 models), 4 provider offering blocks
- route/tests.rs + tests.rs: Anthropic variant test fixes
- tui/src/config.rs: ApiProvider enum, KIND/FROM_KIND lookups, ProvidersConfig fields, all match arms,
  provider_passes_model_through, env var handling, http header/model mutation blocks, merge fn
- tui/src/config/models.rs: DEFAULT_MODELSTUDIO_* and MODELSTUDIO_*_ANTHROPIC_BASE_URL constants
- tui/src/client.rs: reasoning effort match arms for all 3 effort levels
- tui/src/config_persistence.rs: provider_base_url_table_key arms
- tui/src/tui/ui.rs: api_key storage arm
- config.example.toml: 2 provider sections with Anthropic dialect comments
- docs/PROVIDERS.md: first-class provider section replacing workaround; 4 table entries

All 475 codewhale-config tests pass. codewhale-tui compiles cleanly.

Agent-assisted implementation.
2026-08-02 22:16:32 -07:00
Hmbown 4d2458770a docs: truth pass — drop phantom config, dead keys, inverted defaults
Verified each claim against the code before editing:

- [capacity]: the 15-key controller was documented in CONFIGURATION.md
  and config.example.toml but has zero matches anywhere under crates/
  — silently ignored on parse. Removed, along with the link to the
  nonexistent docs/capacity_controller.md and two prose mentions.
- [context] seam keys: verbatim_window_turns, l1/l2/l3_threshold and
  seam_model are all 'Ignored (was: …)' in crates/tui/src/config.rs
  (2036-2048); docs presented them as a live opt-in. Both files now
  document only the live keys (enabled, project_pack) and mark the
  seam keys ignored.
- Settings keys: show_thinking / thinking_default_expanded /
  cost_currency were misfiled in config.example.toml; they belong to
  ~/.codewhale/settings.toml (settings.rs:344,414) and Config has no
  deny_unknown_fields, so users got silently ignored settings.
  Replaced with a pointer comment.
- Hotbar default was inverted in docs: KEYBINDINGS.md and
  config.example.toml claimed fresh configs show the default bar;
  since #3807 a missing hotbar key renders no bar (sidebar.rs:192-195).
- MCP tool naming: docs/MCP.md claimed mcp_deepseek_shell; the rule
  is mcp_{server}_{tool} (mcp.rs:3022) with default server name
  codewhale, i.e. mcp_codewhale_shell.
- MODES.md said 'DeepSeek-TUI'; memory_path bullet described the
  deleted legacy single-file fallback — both fixed for the native
  store (see 1135a1e65).

Evidence: cross-surface-tech-debt-audit-2026-08-03.md findings 74-80;
§11.3 docs-truth row.
2026-08-02 20:59:24 -07:00
Hmbown 0b6fed2799 Merge PR #4960: add safe permission rule management
Integrate Turisla’s verified permissions listing and snapshot-bound removal flow into the v0.9.2 release candidate while preserving the contributor commit and review history.
2026-07-28 22:16:37 -07:00
Hmbown 6db76d67f4 fix(tui): surface provider context overrides
Show configured and effective provider context windows in /config and its audit/help paths, including the route-limit source. Document Kimi plan-tier caps and prove a 256K override drives compaction, the context meter, and preflight input budgeting from the same resolved limits.
2026-07-28 21:50:08 -07:00
greyfreedom 862be61f7d feat(permissions): add safe rule list and removal
Add /permissions listing with active source, matcher, scope, and current workspace applicability while keeping /config ask-rules compatible.

Gate removal behind a snapshot token and serialize append/remove through the same atomic permissions lock. Reload the live user ruleset without clearing session approvals.

Refs #1186
2026-07-29 12:21:00 +08:00
Hmbown 2b294c3332 docs(tui): document thinking expansion default
Expose the setting in the shipped example and configuration/accessibility references, including its interaction with show_thinking and the Space toggle.
2026-07-27 12:06:38 -07:00
Hmbown 462858207b merge: provider-truth billing harvest
Composes the #4797 cost-truth repair (HEAD) with the provider-truth
harvest (codex/v092-ptruth-harvest). Both lanes close truth-critical
blockers; where they touched the same seam the rule applied was: one
receipt type per job, classification computed from the dispatched
receipt, and the fail-closed answer wherever the two disagreed.

route_billing.rs
- Kept the harvest's single `classify(provider, identity, base_url,
  product)` and its `capture_product`/`RouteProduct` credential truth.
  It fully subsumes the cost lane's `minimax_billing`, which only read
  `mode`; the harvest reads the same mode plus non-secret key-shape
  provenance and never opens the keyring. Dropped `minimax_billing`,
  `stepfun_billing`, `uses_zai_coding_plan`.
- Added `subscription_plan` to the MiniMax plan modes so the cost
  lane's documented spelling is not silently discarded as unprovable.
- `for_endpoint_without_config` is now a thin wrapper over `classify`
  with no identity and an unproven product, instead of a second copy of
  the endpoint rules. Same fail-closed contract, one implementation.
- Moved the cost lane's endpoint gate into `classify`'s catch-all
  (`endpoint_shaped_payg_billing`): a first-party or aggregator provider
  on an unrecognized host is Unknown, not metered-by-provider-name
  (#4318). This also fixes the harvest's noted hole where an empty
  endpoint fell through to metered.
- `billing_surface_for_dispatch` kept as-is (three live callers) and now
  benefits from the harvest's `for_route`.

core/events.rs, core/engine.rs
- `TurnRoute` carries both layers, documented at the definition because
  they are captured at different instants and answer different
  questions: `base_url`/`billing_product`/`provider_identity` are the
  DispatchedReceipt frozen at client-freeze (readable from TurnStarted),
  while `billing: Option<RouteBillingEnvelope>` is the wire-boundary
  envelope that must be structurally absent for an undispatched route.
- The envelope's `billing_mode` is now classified from that same frozen
  receipt via `for_dispatched_receipt` rather than a second ambient
  `for_route` read, so the two halves cannot disagree.

subagent mailbox/mod, subagent_routing
- Kept the cost lane's `source_id` + `route: EffectiveRouteEnvelope` on
  `MailboxMessage::TokenUsage` and dropped the harvest's parallel
  `billing: Option<ChildBillingProvenance>` field. The envelope is the
  child's dispatch receipt: the client it ran on froze provider,
  identity, endpoint fingerprint, billing surface and billing mode at
  construction, and `RouteBillingMode` has the same variant set as
  `ChildBillingProvenance` plus strictly more evidence. Child provenance
  still wins; it just travels on the richer receipt.
- The turn-end mailbox barrier (seal/drain/await before TurnComplete)
  and its exactly-once detached-child accounting are unchanged.

tui/tool_routing.rs
- Kept the cost lane's path: bill from the child's own
  `EffectiveRouteEnvelope`, rehydrated from the complete `child_*`
  metadata emitted by all three real producers (review, verify, rlm).
  The harvest's reader was explicitly unwired ("no tool producer emits
  the keys yet") and its parent-inheritance fallback is contradicted by
  the cost lane's tested contract
  (`legacy_child_usage_metadata_fails_closed_without_parent_route_fallback`),
  which is the stronger, fail-closed one: incomplete child metadata is
  Unknown and reported as missing spend, never inherited.
- Consequently the harvest's `ActiveTurnMetadata` receipt mirror
  (`billing_identity`/`billing_product`/`billing_base_url` and
  `dispatched_receipt()`) had no production consumer and was removed;
  `TurnRoute::cost_envelope()` is the same receipt one layer down and is
  already consumed.

Dead code after composition (no -D warnings, nothing silently kept)
- `ChildBillingProvenance`, `static_subscription_label`,
  `for_child_route_receipt`, `ChildParentRoute`, `ChildRouteClaim` are
  now `#[cfg(test)]`-gated with a note at each definition explaining
  that the wired child receipt is `EffectiveRouteEnvelope`. Their tests
  are kept as the executable record of the serialization and
  identity-comparison contracts.

Tests changed, and why
- `child_route_billing_fails_closed_for_every_ambiguous_provider`: the
  cost lane expected Metered for PAYG aggregators and an exact
  subscription label for OpenaiCodex/OpencodeGo children. The harvest's
  `for_child_route` returns Unknown for every non-local cross-provider
  child without provenance. The harvest's contract is stronger — a
  provider name is not evidence of what a turn billed, and Unknown
  (unlike a subscription label) keeps the turn in `/cost`'s money
  coverage denominator — so the weaker expectations were updated, and a
  same-provider inheritance case was added.
- `minimax_requires_an_explicit_saved_billing_mode`: expected label
  changed from the generic "MiniMax subscription plan" to the harvest's
  "MiniMax Token Plan quota", which names the actual product.

docs/PROVIDERS.md: kept the cost lane's StepFun billing-route setup row
and the harvest's MiniMax product-split row; the harvest's Moonshot
product-split and K3-clamp rows auto-merged.

Verification: route_billing 48, pricing 64, cost 87, receipt 157,
subagent 507, subagent_routing 18, tool_routing 11, prompt_suggestion
23, engine 461, model_inventory 17, tui::app 383, ui::tests 653,
config:: 458, mailbox 34 — all 0 failures. `cargo fmt --all -- --check`
clean; CI clippy (--workspace --all-features --locked, five -A allows)
clean; check-tui-locale-parity.py PASS.

Note: `failed_paused_dispatch_preserves_app_checkpoint_state_and_engine_gate`
overflows the default 2 MiB test stack under batch parallelism and
passes with RUST_MIN_STACK=32M. It is an unmodified HEAD test and a
pre-existing stack-depth papercut, not a merge regression.
2026-07-27 05:31:18 -07:00
Hmbown 6307ddfe8e docs: state route-scoped endpoints, product-scoped billing, and the K3 clamps
Documentation caught up with the provider-truth behaviour this branch lands, so
the docs stop describing guarantees the code no longer makes (or never did):

- CONFIGURATION: `CODEWHALE_BASE_URL` is the **active** route's endpoint. A
  pinned request resolves provider table → provider-scoped variable → provider
  default and never inherits the session host; a custom route with no
  `base_url` fails closed on the loopback placeholder. The legacy root
  `base_url` stays shared between the DeepSeek identities when the user wrote
  it, and belongs to one identity when the environment did.
- PROVIDERS/Moonshot: `kimi-for-coding-highspeed` joins the membership roster,
  the mutual endpoint rejection is stated, and billing is described as the
  endpoint-decided split it now is — metered direct platform, Kimi Code quota on
  the exact membership endpoint, `cost: unknown` for gateways and neighbouring
  Kimi paths — including that an imported token with no configured `base_url`
  still bills as membership quota, and that a finished turn is billed from its
  own dispatch receipt rather than a later config re-read.
- PROVIDERS/MiniMax: billing comes from the credential product, not the
  endpoint; keyring-held keys are deliberately not read and leave the route
  `cost: unknown` rather than assuming pay-as-you-go.
- PROVIDERS/reasoning: both exact K3 routes clamp `off` to `low`, but for
  different reasons — the membership roster declares K3 always-thinking, while
  the direct-platform clamp is defensive because the API documents no `off`
  state and the live entitlement is unknown. Stated as rationale only: this
  build does not yet emit distinct status-line receipts for the two.
- config.example.toml lists the high-speed membership id.

Locale parity: PASS (en 1177/1177, zh-Hans 1177/1177 complete; no new keys).

Harvested from the provider-truth lane.
2026-07-27 04:56:08 -07:00
Hmbown 2fae5f3b83 merge: visual program slices (#4813 #4808 #4823 #4817 #4807)
# Conflicts:
#	crates/tui/src/tui/session_picker.rs
2026-07-27 04:39:32 -07:00
Hmbown de4cba31bb feat(notifications): add opt-in per-event sound cues
#4817. `[notifications.event_sound]` maps each notification event to a
fixed terminal-bell cue (one or two `\x07` bytes). Functional signals, not
audio design: no assets, no dependencies, and inert on terminals that
ignore BEL, so the whole feature is a platform-safe no-op wherever sound
is unavailable.

Off by default. The decision order is disabled → quiet → not in the
allow-list → `turn-complete` deferred to the existing `completion_sound`
channel (so the two never double-ding) → per-event rate limit → play.
`decide` takes the caller's `now_ms`, so rate limiting is deterministic
and testable rather than wall-clock dependent; the runtime wiring's
epoch-millis clock and its non-monotonicity are documented at the seam.

Wired into `notify_done_to` and initialized from config alongside
`set_completion_sound`. Documents the schema, the full cue table, and the
decision order in docs/CONFIGURATION.md and config.example.toml.

Two changes relative to the reviewed draft, both to keep the process-global
policy honest under test:
- `handle_notification_kind_to` takes an injected sink instead of writing
  to `io::stdout()` directly, matching `notify_done_to`. No test path can
  BEL a real terminal, and the notification module's existing byte-capture
  tests now see any cue that would be emitted.
- The one test that touches the process-global `POLICY` `OnceLock`
  serializes on the crate's `lock_test_env()` and restores the default
  policy before releasing the lock, so no later test inherits an enabled
  policy.

Fourteen sound-policy tests plus two config-parsing tests.
2026-07-27 04:36:28 -07:00
Hmbown ca678c3539 feat(tui): thread the seven new Locale variants through the TUI surfaces
Adds Ca/De/Fr/Id/Hi/Ru/Uk to the Locale enum, tag/display/all/shipped
tables, parse_locale prefix matching, and the thinking-translation and
hidden-translation status strings, then wires them through the surfaces
that enumerate locales: setup, onboarding language picker, config_ui,
settings, the subagent locale seam, the status-picker copy guard, and the
tool card.

Notable behavior changes:

- The onboarding language picker's hotkeys now run 1..=9 then a, b, … so
  sixteen options stay single-keystroke selectable; the contiguity test
  follows the same rule.
- The German status-picker title is "Statuszeile" — "Status" is a German
  word, so that locale gets an exact-match assertion rather than the
  "contains no English 'Status'" guard the other locales use.
- Catalan "models" is the correct translation of the English picker
  action, so it is exempted from the differs-from-English fallback guard.
- The prompt seam is deliberately unchanged: a new cache-stability test
  locks the locale-bookend set to zh-Hans/ja/pt-BR/vi so adding a UI pack
  can never silently change model-visible prompt bytes, while the
  translation seam names every shipped locale canonically.
- Script-purity tests hold ru/uk apart (no Russian-only letters in uk, no
  Ukrainian-only letters in ru, no copied sentences) and require hi to be
  genuinely Devanagari.

Harvested from the v0.9.2 localization lane (#3091, #3092, #3093, #4788,
#4789, #4790, #4791).
2026-07-27 02:41:01 -07:00
Hunter B 5e3d84307a fix(hooks): make the TUI lifecycle contract truthful
Run observer and background hooks through bounded supervisors, enforce timeout containment and reaping, and preserve one hook session identity across runtime rebinds. Carry real tool exit codes into after/error events, fire tool-scoped on_error hooks for every settled failure, and keep message-submit stdin and queue recovery exact without parking the terminal loop.

Document the implemented TUI-only event, steering, shell_env, timeout, environment, and audit contracts without implying headless or public-API coverage.

Verified: hook filter 144/144; message_submit 29/29; reported_tool_exit_code 1/1; on_error filter 11/11; TUI locale parity; README locale links; cargo fmt --check; git diff --check.
2026-07-26 23:59:44 -07:00
Hunter B 2286907518 merge: harvest OpenCode Zen provider support
Preserve @snail-vs's full contributor history from PR #4467 and include the review follow-up that isolates its provider environment test. This local candidate merge is not pushed and remains subject to the combined v0.9.2 release gates.
2026-07-26 14:06:08 -07:00
greyfreedom dc3eb1a926 feat(tui): persist exact repo-scoped allow grants
Let approval cards remember eligible safe shell and file-write approvals as exact allow rules bound to one repository, without weakening deny, ask, or safety-floor precedence.

Refs #1186 and #2242.

Harvested from PR #4761 by @greyfreedom

Co-authored-by: greyfreedom <11493871+greyfreedom@users.noreply.github.com>
2026-07-26 11:06:53 -07:00
snailoniu 90177b9050 docs(providers): document OpenCode Zen routes
Document Zen as a separate API-key provider with model-scoped Responses, Messages, and Chat Completions groups. Record the unsupported Gemini boundary and teach the provider registry drift check about the manual model-aware provider implementation.

Signed-off-by: snailoniu <snailoniu@gmail.com>
2026-07-26 21:33:01 +08:00
Hunter Bown 5789eca509 feat(tools): add agent-callable adversarial verify critic (#4861)
* feat(tools): add agent-callable verify critic

Add an opt-out, bounded, toolless adversarial critic that agents can invoke before claiming non-trivial work complete.

Closes #4196.

Harvested from PR #4199 by @Hmbown

Co-authored-by: Hmbown <101357273+Hmbown@users.noreply.github.com>

* fix(tools): make verify evidence and risk accounting complete

Include uncommitted worktree changes when verifying from a base, keep staged diffs valid, and fail safe on medium findings.

Closes #4196.

Harvested from PR #4199 by @Hmbown

Co-authored-by: Hmbown <101357273+Hmbown@users.noreply.github.com>

* chore(web): refresh generated tool facts

* docs(web): refresh public surface tool count
2026-07-26 03:36:45 -07:00
Hunter B 35dccdbd92 fix(kimi): align K3 reasoning and global route truth
Keep exact K3 routes always-thinking for stale raw Off callers, distinguish dispatched Auto from an omitted provider-default setting, and restore the global Kimi platform links to the .ai domain.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-20 07:02:39 -07:00
Hunter B 661dd69c5a fix(kimi): enforce exact K3 route contracts
Keep bare membership K3 on its lowest thinking tier when Off is selected while leaving Auto to the provider default. Scope direct K3 output metadata, token naming, and fixed-sampling omissions to the exact first-party route.\n\nAdd streaming and non-streaming request-capture coverage, route provenance, neighbor non-regressions, and canonical Kimi platform documentation links.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-20 07:02:38 -07:00
Hunter B 8e429b7c3e fix(tui): enforce direct Moonshot K3 reasoning contract
Scope direct K3 behavior to the exact Moonshot endpoint and model, normalize unsupported reasoning tiers, and preserve always-thinking replay. Share final request shaping across stream and non-stream paths with production-boundary coverage for direct, membership, and neighboring routes.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-20 07:02:38 -07:00
Hunter B 4576b6692e docs(kimi): document the exact K3 routes
Show bare k3 on the Kimi Code membership endpoint, distinguish the direct kimi-k3 route, and document the entitlement-aware 1M context override without removing valid K2.7 compatibility.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-20 07:02:38 -07:00
Hunter B bf405139ba security(web): remove compiled Metaso key
Require an operator-supplied Metaso key from search configuration or METASO_API_KEY. Fail closed before backend fallback when it is missing, and update configuration guidance to match.
2026-07-19 09:49:09 -07:00
Hunter B 2de3ff5f37 fix(web): close shared guard review gaps
Disable ambient proxies on DNS-pinned fetches, require trusted fake-IP hosts and benchmark CIDRs together, and keep private address classes fail-closed. Enforce web.run cache namespaces, exercise the real redirect and click paths, and replace the broad same-root spam heuristic with the known #964 spam-domain evidence so site-scoped and public-suffix results remain valid.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-18 21:30:27 -07:00
Hunter B 9f62b04e42 docs(auth): document external credential consent
Document disabled, read-only, and reserved managed access semantics for credentials owned by Codex and Grok CLIs. Point Kimi users only to the supported API-key flow and explain Codewhale-owned xAI device-login storage.

Refs #4507

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-18 07:39:57 -07:00
Hunter B 7faa73e23c fix(providers): refresh OpenCode Go chat models
Expose the provider's Chat Completions allowlist from shared config and project Grok 4.5 and Kimi K3 through route resolution, registry, live-catalog, and picker paths. Keep Messages-only entries excluded.

Document the current route and cover bare and provider-prefixed selectors without making provider requests.

Fixes #4527

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-18 07:15:07 -07:00
Hunter B 14680b11ed fix(auth): fail closed on legacy Kimi imports
Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-17 14:36:16 -07:00
Hunter B fbb97dd020 feat(providers): add OpenCode Go chat route
Add first-class OpenCode Go configuration, credentials, model selection, routing, billing copy, documentation, and live catalog filtering for the eight models documented on the Chat Completions protocol.

Enforce the Chat-only model cutline centrally so Messages-only models cannot cross-route or bypass validation through a custom base URL.

Harvested from PR #773 by @zhangweiii and PR #1050 by @sternelee.
Refs #1481 by @seanthefuturegorilla.

Co-authored-by: zhangweiii <31117497+zhangweiii@users.noreply.github.com>
Co-authored-by: Sterne Lee <6971752+sternelee@users.noreply.github.com>
Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-16 16:37:47 -07:00
Hunter B 29d3f0c321 release: prepare Codewhale v0.9.0
Integrate the underwater TUI, message-first Operate, Fleet and Workflow reliability, expanded model/provider catalog, exact custom-route restoration, docs-first site, localization, packaging, and release metadata for the v0.9.0 candidate.

Harden endpoint-bound credential provenance, approval and goal UX, Fleet attempt fencing and crash recovery, large-workspace mention discovery, Kimi budgeting, and release asset/version gates. Include the stopship Fleet and Workflow fixtures used by release dogfood.

Verified with workspace fmt/check/clippy/tests on Rust 1.88, release-script and npm suites, 18-crate publish dry run, production web build, Docker build check, secret scan, dependency audit, and protected-state hash validation.
2026-07-15 23:44:37 -07:00
Nightt abf3e23993 fix(scorecard): preserve StepFun billing surfaces
Classify the actual StepFun endpoint as PAYG or Step Plan without persisting raw URLs, then carry that non-secret provenance through turn-end hooks, runtime records, usage aggregation, and offline scorecards.

Price only the exact StepFun PAYG fallback route with the official cache rate. Route-blind background and foreign-provider calls now fail closed so subscription quota cannot be reported as token spend.

Validation: cargo fmt --all -- --check; RUSTFLAGS=-D warnings cargo check -p codewhale-tui --bin codewhale-tui --locked; targeted scorecard/pricing/runtime/hook tests; full TUI suite (6408 passed, 2 ignored before the final narrow gate, then gate-specific regression tests passed).
Signed-off-by: Nightt <87569709+nightt5879@users.noreply.github.com>
2026-07-14 16:34:44 +08:00
Hunter B 25c59a9866 feat(models): add GPT-5.6 and Muse Spark routes
Add the OpenAI GPT-5.6 family and Meta Model API's Muse Spark 1.1 across provider metadata, configuration, secrets, CLI selection, catalogs, model limits, pricing, reasoning payloads, and documentation.

Verified with config, agent, secrets, CLI, catalog, and focused TUI tests. This local branch intentionally has no upstream.
2026-07-09 11:38:30 -07:00
Hunter B 44135b451c feat(provider): xAI OAuth via Grok CLI auth + device-code (#4257)
API-key path already shipped. Add residual OAuth for [providers.xai]
auth_mode = "oauth": reuse ~/.grok/auth.json (GROK_HOME / GROK_AUTH_PATH),
refresh against auth.x.ai/oauth2/token before expiry, and expose a native
device-code login entry point that prints verification URL + user code
without a localhost callback. API-key remains the reliable fallback when
SuperGrok OAuth returns 403.
2026-07-09 03:01:57 -07:00
Hunter B 630f525e55 fix(workflow): align access model — coordinator VM, 16 concurrent, 1k/run
Match dynamic Workflow product constraints: the script remains a sandboxed
coordinator (no FS/shell/network), sub-agents do the real work with inherited
tools, write-capable Workflow children auto-accept Suggest-level file edits
while shell/network stay gated, concurrent live children cap at 16, and the
per-run agent lifetime stays at 1_000. Config defaults and docs follow.
2026-07-09 01:04:07 -07:00
Hunter B adbdb32e51 feat(config): add [workflow] keys and product defaults (#4128)
Introduce WorkflowConfigToml as the first-class config surface for automatic
Workflow launch, write approval, child/isolation budgets, and completed
activity persistence. Defaults match Section 2.11 product knobs; omitted
tables resolve through Config::workflow_config(), and explicit overrides
round-trip via TOML load/serialize.

Closes #4128
2026-07-08 23:15:43 -07:00
Hunter B 266ede2a63 feat(provider): add xAI API-key route (#4257)
Add xAI as a first-class OpenAI-compatible provider with XAI_API_KEY, XAI_BASE_URL, XAI_MODEL, Grok aliases, catalog entries, and docs/example config coverage.

This intentionally ships the low-risk API-key path from #4257; xAI OAuth/device-code support remains follow-up work.
2026-07-08 19:43:05 -07:00
Hunter B 3730ce5564 fix(tui): align Multitask→Operate tests with invisible YOLO
DEFAULT_HOTBAR now binds mode.operate (not mode.yolo), and assertions
expect Act-labeled YOLO shorthand plus Agent prompt markers so macOS CI
matches the folded mode roster.

Signed-off-by: Hunter B <hmbown@gmail.com>
2026-07-08 11:59:50 -07:00