refactor(engine): extract the shared runtime from the TUI [skip ci]

Move the Engine band, extension host, fixtures and shared test support into
codewhale-engine; wire TUI/CLI to that owner. Preserve human-decision
construction boundaries and lower reusable test helpers. Remove unused
copied dependencies and repair path-based guards, scripts and crate maps.

Correct the blocking scanner external-module scope leak; raised counts equal
the corrected pre-move source. Refresh embedded built-in hashes after path-only
bundle regeneration. Restore the provider-free prompt budget to the exact
pre-b984e31290 macOS bytes and representative identities; tool catalogs are
unchanged. Portable prompt-fixture pinning remains follow-up work.

Local validation: Engine/TUI/CLI clippy --tests --no-deps -D warnings passed
with the existing allow-list; production CLI check passed; fmt passed.
Focused native run: 705 passed / 83 failed initially; after hash/path repairs,
81 affected cases plus 2 existing build-relocation cases passed (83/83).
Thus 786/788 original cases pass; 2 compiled-release-image cases lack input
and are not qualified. Frozen input inventories unchanged. macOS test-link
unwind-table warnings remain. Four metric tests pass; all 55 budgets pass.
Host Node: 413 pass, 7 skip; Bun: 414 pass, 6 skip. Existing scanner checks:
34 + 33 pass. Compiled-delivery synthetic fixtures: 7 pass. Web: 42 pass.
Import, blocking, dead-code, coarse-map, version and facts checks pass.

CW-7701. No hosted CI, provider call, release build or publication.
Stage A part 2 and the old App Server/ratatui dependencies remain separate.
This commit is contained in:
CodeWhale Bot committed 2026-10-31 18:55:14 -07:00
1 parent 80b7c44916
commit e34f207d21
1090 files changed
+11886 -10447

No files matched your search

+3 -3
View File
@@ -73,14 +73,14 @@ filter = 'binary(telemetry_kill_switch_dispatch)'
test-group = 'telemetry-contract'
[[profile.default.overrides]]
filter = 'package(codewhale-tui) & kind(lib) & test(/^fleet::manager::tests::/)'
filter = 'package(codewhale-engine) & kind(lib) & test(/^fleet::manager::tests::/)'
test-group = 'fleet-manager-lifecycle'
[[profile.default.overrides]]
# DSH preparation also launches the isolated host against the shared hermetic
# home. OCR/PDF consumers also launch real hosts outside extension_host's
# namespace. Keep their process lifecycle checks in this existing serial lane.
filter = 'package(codewhale-tui) & kind(lib) & (test(/^extension_host::/) | test(/^tools::image_ocr::host_tests::/) | test(/^tools::pdf::host_tests::/) | test(/^core::engine::.*extension/) | test(/^plugins::install::dsh::/) | test(/^plugins::tests::dsh_/) | test(/^runtime_api::tests::dsh_package_preview_then_exact_install_over_http$/) | test(/^commands::contract::tests::plugin_adapter_dsh_preview_runs_from_live_runtime$/) | test(/^commands::groups::plugins::tests::dsh_import_reviews_without_installing_then_installs_the_exact_bundle$/))'
filter = '(package(codewhale-engine) | package(codewhale-tui)) & kind(lib) & (test(/^extension_host::/) | test(/^tools::image_ocr::host_tests::/) | test(/^tools::pdf::host_tests::/) | test(/^core::engine::.*extension/) | test(/^plugins::install::dsh::/) | test(/^plugins::tests::dsh_/) | test(/^runtime_api::tests::dsh_package_preview_then_exact_install_over_http$/) | test(/^commands::contract::tests::plugin_adapter_dsh_preview_runs_from_live_runtime$/) | test(/^commands::groups::plugins::tests::dsh_import_reviews_without_installing_then_installs_the_exact_bundle$/))'
test-group = 'extension-host'
[[profile.default.overrides]]
@@ -116,5 +116,5 @@ path = "junit.xml"
report-name = "codewhale-native-host"
[[profile.ci.overrides]]
filter = 'package(codewhale-tui) & kind(lib) & (test(/^extension_host::tests::compiled_native_host_cannot_read_secrets_or_write_outside_its_data_dir$/) | test(/^extension_host::tests::compiled_native_host_memory_cap_is_enforced$/))'
filter = 'package(codewhale-engine) & kind(lib) & (test(/^extension_host::tests::compiled_native_host_cannot_read_secrets_or_write_outside_its_data_dir$/) | test(/^extension_host::tests::compiled_native_host_memory_cap_is_enforced$/))'
junit.store-success-output = true
+7 -7
View File
@@ -3,9 +3,9 @@
# produces different compiled binaries on Windows vs Linux/macOS.
# Cover nested mode/approval/personality prompts — a top-level *.md glob alone
# left modes/*.md on CRLF under Windows autocrlf and blew the agent token budget.
crates/tui/src/prompts/**/*.md text eol=lf
crates/tui/src/prompts/*.md text eol=lf
crates/tui/src/prompts/*.txt text eol=lf
crates/engine/src/prompts/**/*.md text eol=lf
crates/engine/src/prompts/*.md text eol=lf
crates/engine/src/prompts/*.txt text eol=lf
# The rest of the include_str!() inputs. `* text=auto` below converts these to
# CRLF on a Windows checkout, which is invisible for the ones that are only
@@ -58,12 +58,12 @@ scripts/fixtures/dsh-web-app/** text eol=lf
# the generator's LF output (protocol/tests.rs), and its dist bundle is
# include_bytes!()'d and digest-checked at launch; a CRLF Windows checkout
# failed the former and would embed different bytes.
crates/tui/extension-host/src/protocol.generated.ts text eol=lf
crates/tui/extension-host/dist/** text eol=lf
crates/engine/extension-host/src/protocol.generated.ts text eol=lf
crates/engine/extension-host/dist/** text eol=lf
# Reviewed extension-host fixtures carry exact source SHA-256 receipts.
# A Windows checkout must not rewrite their admitted source closure to CRLF.
crates/tui/tests/fixtures/extension_host/** text eol=lf
crates/tui/extension-host/src/dsh/dsh-message-compat.js text eol=lf
crates/engine/tests/fixtures/extension_host/** text eol=lf
crates/engine/extension-host/src/dsh/dsh-message-compat.js text eol=lf
# Rustfmt writes LF; keep Rust sources stable across Windows/Linux/macOS.
*.rs text eol=lf
+9 -9
View File
@@ -172,7 +172,7 @@ jobs:
# push to main still runs it unconditionally as the pre-release
# safety net for anything this filter misses.
case "${path}" in
crates/app-server/*|crates/tui/src/runtime_api*|crates/tui/src/runtime_mobile.html|crates/tui/src/runtime_threads*|crates/tui/src/main.rs|scripts/mobile-smoke.sh|.github/workflows/ci.yml|Cargo.lock|Cargo.toml)
crates/app-server/*|crates/tui/src/runtime_api*|crates/tui/src/runtime_mobile.html|crates/engine/src/runtime_threads*|crates/tui/src/main.rs|scripts/mobile-smoke.sh|.github/workflows/ci.yml|Cargo.lock|Cargo.toml)
mobile=true
;;
esac
@@ -327,7 +327,7 @@ jobs:
# testing; then reject any bundle drift. Node 22 matches the host floor.
run: |
set -euo pipefail
cd crates/tui/extension-host
cd crates/engine/extension-host
npm ci --ignore-scripts
npm test
npm run typecheck
@@ -344,7 +344,7 @@ jobs:
# suites only; the Rust host tests run on Node.
run: |
set -euo pipefail
cd crates/tui/extension-host
cd crates/engine/extension-host
npm run test:bun
extension-host-runtimes:
@@ -359,7 +359,7 @@ jobs:
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: crates/tui/extension-host
working-directory: crates/engine/extension-host
shell: bash
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -384,7 +384,7 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: extension-host-runtime-${{ matrix.os }}
path: crates/tui/extension-host/dist/builtin-modules.json
path: crates/engine/extension-host/dist/builtin-modules.json
if-no-files-found: error
vscode-extension:
@@ -470,7 +470,7 @@ jobs:
# codewhale-runtime owns `safe_label` (its hostile-authority test)
# since RS-2; add the next package when another safety module moves.
run: |
sh scripts/with-hermetic-test-home.sh cargo nextest run -p codewhale-tui -p codewhale-runtime --lib --locked --no-tests=fail -E 'test(auto_review) | test(authority) | test(sandbox)'
sh scripts/with-hermetic-test-home.sh cargo nextest run -p codewhale-engine -p codewhale-tui -p codewhale-runtime --lib --locked --no-tests=fail -E 'test(auto_review) | test(authority) | test(sandbox)'
sh scripts/with-hermetic-test-home.sh cargo test -p codewhale-execpolicy --locked
lint:
@@ -896,7 +896,7 @@ jobs:
set -euo pipefail
bun_binary="$(bun -p 'process.execPath')"
host_binary="$(node -p "require('path').join(process.env.RUNNER_TEMP, 'codewhale-extension-host' + (process.platform === 'win32' ? '.exe' : ''))")"
compiled_info="$(node crates/tui/extension-host/compile-host.mjs --bun "$bun_binary" --output "$host_binary")"
compiled_info="$(node crates/engine/extension-host/compile-host.mjs --bun "$bun_binary" --output "$host_binary")"
printf 'CODEWHALE_COMPILED_HOST_TEST_BINARY=%s\n' "$host_binary" >> "$GITHUB_ENV"
node - "$host_binary" "$bun_binary" "$compiled_info" >> "$GITHUB_ENV" <<'JS'
const fs = require('node:fs'), crypto = require('node:crypto');
@@ -908,7 +908,7 @@ jobs:
console.log('CODEWHALE_COMPILED_HOST_TEST_SHA256=' + digest(process.argv[2]));
console.log('CODEWHALE_COMPILED_HOST_TEST_BUN=' + process.argv[3]);
console.log('CODEWHALE_COMPILED_HOST_TEST_RUNTIME_SHA256=' + digest(process.argv[3]));
console.log('CODEWHALE_COMPILED_HOST_TEST_BUNDLE_SHA256=' + digest('crates/tui/extension-host/dist/codewhale-extension-host.mjs'));
console.log('CODEWHALE_COMPILED_HOST_TEST_BUNDLE_SHA256=' + digest('crates/engine/extension-host/dist/codewhale-extension-host.mjs'));
JS
# The required hermetic full invocation executes the shared actual
# compiled Native secret/history/write scenario on each OS; Windows
@@ -1149,7 +1149,7 @@ jobs:
python3 scripts/compiled-host-native-receipt.py \
--junit target/nextest/ci/junit.xml \
--compiled-image "$CODEWHALE_COMPILED_HOST_TEST_BINARY" \
--bundle crates/tui/extension-host/dist/codewhale-extension-host.mjs \
--bundle crates/engine/extension-host/dist/codewhale-extension-host.mjs \
--bun "$CODEWHALE_COMPILED_HOST_TEST_BUN" --source-sha "$GITHUB_SHA" \
--image-platform "$CODEWHALE_COMPILED_HOST_TEST_PLATFORM" --image-arch "$CODEWHALE_COMPILED_HOST_TEST_ARCH" \
--host-sha256 "$CODEWHALE_COMPILED_HOST_TEST_SHA256" \
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- { name: root, directory: . }
- { name: web, directory: web }
- { name: telemetry-ingest, directory: telemetry-ingest }
- { name: extension-host, directory: crates/tui/extension-host }
- { name: extension-host, directory: crates/engine/extension-host }
- { name: vscode, directory: extensions/vscode }
- { name: feishu-bridge, directory: integrations/feishu-bridge }
- { name: telegram-bridge, directory: integrations/telegram-bridge }
+2 -2
View File
@@ -480,7 +480,7 @@ Contributor pull requests still merge as pull requests, as described in
## Project Structure
Codewhale is a Cargo workspace with one Engine implementation in
`crates/tui/src/core/engine/`. The public `codewhale` executable links the
`crates/engine/src/core/engine/`. The public `codewhale` executable links the
TUI/runtime library; interactive sessions, noninteractive runs and the Runtime
API share that Engine.
@@ -556,7 +556,7 @@ A well-structured PR follows a consistent pattern. Recent exemplars include:
- **#386** — `/init` command: new `crates/tui/src/commands/groups/project/init.rs` module, project-type detection,
AGENTS.md generation, command registration in `commands/mod.rs`, localization strings.
- **#389** — Inline LSP diagnostics: LSP subsystem in `crates/lsp/src/`, engine hooks in
`crates/tui/src/core/engine/lsp_hooks.rs`, config toggle, test coverage.
`crates/engine/src/core/engine/lsp_hooks.rs`, config toggle, test coverage.
- **#387** — Self-update: new `crates/cli/src/update.rs` module, CLI subcommand registration,
HTTP download + SHA256 verification + atomic binary replacement.
- **#393** — `/share` session URL: new `crates/tui/src/commands/groups/project/share.rs`, HTML rendering,
Generated
+115
View File
@@ -918,6 +918,7 @@ dependencies = [
"codewhale-app-server",
"codewhale-build-support",
"codewhale-config",
"codewhale-engine",
"codewhale-execpolicy",
"codewhale-lane",
"codewhale-paths",
@@ -1046,6 +1047,119 @@ dependencies = [
"wait-timeout",
]
[[package]]
name = "codewhale-engine"
version = "0.10.2"
dependencies = [
"anyhow",
"arboard",
"async-stream",
"async-trait",
"axum",
"base64 0.23.1",
"chrono",
"clap",
"codewhale-agent",
"codewhale-agent-contract",
"codewhale-app-server",
"codewhale-build-support",
"codewhale-command-contract",
"codewhale-commands",
"codewhale-config",
"codewhale-core",
"codewhale-dependencies",
"codewhale-error-taxonomy",
"codewhale-execpolicy",
"codewhale-lane",
"codewhale-localization",
"codewhale-lsp",
"codewhale-mcp",
"codewhale-memory",
"codewhale-models",
"codewhale-net",
"codewhale-palette",
"codewhale-paths",
"codewhale-plugins",
"codewhale-protocol",
"codewhale-ratatui",
"codewhale-release",
"codewhale-repl",
"codewhale-runtime",
"codewhale-sandbox",
"codewhale-sanitize",
"codewhale-secrets",
"codewhale-skill-install",
"codewhale-skills",
"codewhale-snapshot",
"codewhale-telemetry",
"codewhale-tools",
"codewhale-util",
"codewhale-workflow",
"codewhale-workflow-js",
"crossterm",
"cucumber",
"dirs 7.0.0",
"encoding_rs",
"fd-lock",
"futures-util",
"globset",
"htmd",
"ignore",
"image",
"jsonschema",
"libc",
"liblzma",
"lru",
"oauth2",
"objc2",
"objc2-foundation",
"parking_lot",
"portable-pty",
"pretty_assertions",
"proc-macro2",
"ratatui",
"regex",
"reqwest",
"ring",
"rio-vt",
"rmcp",
"rquickjs",
"rusqlite",
"rustls",
"schemars",
"semver",
"serde",
"serde_json",
"sha2 0.11.1",
"shell-words",
"shellexpand",
"shlex 2.0.1",
"similar",
"syn 3.0.9",
"tar",
"tempfile",
"thiserror 2.0.21",
"tiny_http",
"tokio",
"tokio-tungstenite 0.30.0",
"tokio-util",
"toml 1.1.4+spec-1.1.0",
"toml_edit 0.25.15+spec-1.1.0",
"tower-http 0.7.1",
"tracing",
"tracing-subscriber",
"unicode-normalization",
"unicode-width",
"url",
"urlencoding",
"uuid",
"wait-timeout",
"webbrowser",
"windows",
"windows-sys 0.61.2",
"wiremock",
]
[[package]]
name = "codewhale-error-taxonomy"
version = "0.10.2"
@@ -1593,6 +1707,7 @@ dependencies = [
"codewhale-config",
"codewhale-core",
"codewhale-dependencies",
"codewhale-engine",
"codewhale-error-taxonomy",
"codewhale-execpolicy",
"codewhale-hooks",
+1
View File
@@ -11,6 +11,7 @@ members = [
"crates/commands",
"crates/config",
"crates/core",
"crates/engine",
"crates/error-taxonomy",
"crates/execpolicy",
"crates/hooks",
+7 -7
View File
@@ -68,7 +68,7 @@ The full Apache License 2.0 text ships in this repository at
## Cordis, schemastery and cosmokit (Shigma, via DeepSeek Harness) — MIT
The embedded extension host bundle
(`crates/tui/extension-host/dist/codewhale-extension-host.mjs`) contains these
(`crates/engine/extension-host/dist/codewhale-extension-host.mjs`) contains these
packages, as published by DeepSeek Harness:
- `@deepseek-ai/cordis` 4.0.4
@@ -105,9 +105,9 @@ SOFTWARE.
## DeepSeek Harness utility packages (`dsh-util-values`, `dsh-tools`, message compatibility) — MIT
The same bundle contains `@deepseek-ai/dsh-util-values` 0.1.7-alpha.2, and
`crates/tui/extension-host/src/dsh/dsh-tools-compat.js` contains verbatim
`crates/engine/extension-host/src/dsh/dsh-tools-compat.js` contains verbatim
excerpts of `@deepseek-ai/dsh-tools` 0.2.1-rc.2 (`lib/index.js`), under the
same licence text. `crates/tui/extension-host/src/dsh/dsh-message-compat.js`
same licence text. `crates/engine/extension-host/src/dsh/dsh-message-compat.js`
contains verbatim excerpts of `@deepseek-ai/dsh-brand` 0.2.1-rc.2,
`@deepseek-ai/dsh-llm` 0.2.1-rc.2, `@deepseek-ai/dsh-session` 0.2.1-rc.2,
`@deepseek-ai/dsh-system-prompt` 0.2.1-rc.2,
@@ -147,7 +147,7 @@ materialises (`~/.codewhale/extension-host/<sha256>/LICENSES.txt`).
## Model Context Protocol SDK and bundled dependencies
The pinned builtin MCP bundle (`crates/tui/extension-host/dist/builtin/mcp.mjs`)
The pinned builtin MCP bundle (`crates/engine/extension-host/dist/builtin/mcp.mjs`)
contains the following packages. The MCP packages are labeled Apache-2.0 and
carry the upstream licensing-transition statement and full Apache-2.0 text
reproduced below. The other packages are labeled MIT. Their exact distributed
@@ -730,7 +730,7 @@ SOFTWARE.
The host and its offline composition reviewer also contain these packages.
Vendored composition adaptations are recorded in
`crates/tui/extension-host/src/dsh/upstream/UPSTREAM.json`.
`crates/engine/extension-host/src/dsh/upstream/UPSTREAM.json`.
### `@deepseek-ai/cordis-plugin-group` 1.0.2
@@ -882,9 +882,9 @@ Upstream: <https://github.com/deepseek-ai/deepseek-harness>, commit
Preset discovery and metadata, hook matching and dialect codecs, and additive
persona templates run through receipt-backed inputs. Core retains selection,
prompt assembly, sessions, approvals and writable roots. Original persona source
hashes are recorded in `crates/tui/extension-host/src/dsh/persona.UPSTREAM.json`.
hashes are recorded in `crates/engine/extension-host/src/dsh/persona.UPSTREAM.json`.
The identical upstream license text for all six packages is also carried in
`crates/tui/extension-host/dist/LICENSES.txt`.
`crates/engine/extension-host/dist/LICENSES.txt`.
```
MIT License
@@ -82,6 +82,7 @@ fn cached_build_scripts_classify_the_current_manifest_and_require_it() {
let fixture = Fixture::new();
for (name, source) in [
("cli", include_str!("../../cli/build.rs")),
("engine", include_str!("../../engine/build.rs")),
("tui", include_str!("../../tui/build.rs")),
] {
let original = fixture.0.path().join(format!("{name} original"));
+1
View File
@@ -30,6 +30,7 @@ anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
codewhale-tui = { path = "../tui", version = "0.10.2", default-features = false }
codewhale-engine = { path = "../engine", version = "0.10.2" }
codewhale-app-server = { path = "../app-server", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
+12 -11
View File
@@ -921,7 +921,7 @@ pub fn export_bundle(
// Root model aliases are route-relative on import; reconcile them with
// the canonical provider slots so the bundle never conflicts with
// itself, without reparsing unrelated preserved extras as `Config`.
codewhale_tui::route_preferences::scrub_root_model_aliases_for_export(&mut document)?;
codewhale_engine::route_preferences::scrub_root_model_aliases_for_export(&mut document)?;
}
for (key, value) in document {
if let Some(value) = sanitize_export_value(&key, &value) {
@@ -1218,7 +1218,8 @@ fn prepare_import(
rendered = store.rendered_body()?;
&rendered
};
let mut document = codewhale_tui::route_preferences::prepare_document(store.path(), original)?;
let mut document =
codewhale_engine::route_preferences::prepare_document(store.path(), original)?;
let mut candidate = config_from_document(&document.to_string())?;
let mut model_edits = Vec::<(String, String, String)>::new();
let mut selected_provider = None;
@@ -1248,7 +1249,7 @@ fn prepare_import(
continue;
}
collect_model_edits(&dotted, key, value, &mut model_edits)?;
if codewhale_tui::route_preferences::is_route_key(key) {
if codewhale_engine::route_preferences::is_route_key(key) {
continue;
}
apply_config_value(&mut candidate, key, value)?;
@@ -1260,7 +1261,7 @@ fn prepare_import(
// Definitions precede the exact final selector. Root aliases then target
// that selected route, so an old provider slot cannot mask an imported model.
if let Some(provider) = selected_provider {
codewhale_tui::route_preferences::set_document(
codewhale_engine::route_preferences::set_document(
store.path(),
&mut document,
"provider",
@@ -1269,12 +1270,12 @@ fn prepare_import(
}
model_edits.sort_by_key(|(_, key, _)| !key.starts_with("providers."));
for (_, key, value) in &model_edits {
codewhale_tui::route_preferences::set_document(store.path(), &mut document, key, value)?;
codewhale_engine::route_preferences::set_document(store.path(), &mut document, key, value)?;
}
let final_value: toml::Value = toml::from_str(&document.to_string())?;
for (dotted, key, value) in &model_edits {
let mut replay = document.clone();
codewhale_tui::route_preferences::set_document(store.path(), &mut replay, key, value)?;
codewhale_engine::route_preferences::set_document(store.path(), &mut replay, key, value)?;
if toml::from_str::<toml::Value>(&replay.to_string())? != final_value {
plan.conflicting.push(dotted.clone());
}
@@ -1323,7 +1324,7 @@ fn collect_model_edits(
value: &toml::Value,
edits: &mut Vec<(String, String, String)>,
) -> Result<()> {
if key != "provider" && codewhale_tui::route_preferences::is_route_key(key) {
if key != "provider" && codewhale_engine::route_preferences::is_route_key(key) {
let value = value
.as_str()
.ok_or_else(|| anyhow!("config entry {dotted:?} must be a string"))?;
@@ -2182,13 +2183,13 @@ verbosity = "normal"
later_settings
);
assert_eq!(
codewhale_tui::route_preferences::get(&path, "provider")
codewhale_engine::route_preferences::get(&path, "provider")
.unwrap()
.as_deref(),
Some("zai")
);
assert_eq!(
codewhale_tui::route_preferences::get(&path, "model")
codewhale_engine::route_preferences::get(&path, "model")
.unwrap()
.as_deref(),
Some("GLM-5.3")
@@ -2503,7 +2504,7 @@ verbosity = "normal"
);
assert_eq!(store.config.provider_id(), "OpenAI");
assert_eq!(
codewhale_tui::route_preferences::get(&path, "provider")
codewhale_engine::route_preferences::get(&path, "provider")
.unwrap()
.as_deref(),
Some("OpenAI")
@@ -2549,7 +2550,7 @@ verbosity = "normal"
store.reload().unwrap();
assert_eq!(store.config.provider_id(), "deepseek-cn");
assert_eq!(
codewhale_tui::route_preferences::get(&path, "model")
codewhale_engine::route_preferences::get(&path, "model")
.unwrap()
.as_deref(),
Some(expected_model)
+39 -22
View File
@@ -89,7 +89,7 @@ fn parse_provider_identifier(value: &str) -> std::result::Result<String, String>
)]
struct Cli {
#[command(flatten)]
runtime_options: codewhale_tui::RuntimeOptions,
runtime_options: codewhale_engine::runtime_options::RuntimeOptions,
#[arg(
long,
value_name = "PROVIDER",
@@ -176,7 +176,7 @@ struct Cli {
}
impl std::ops::Deref for Cli {
type Target = codewhale_tui::RuntimeOptions;
type Target = codewhale_engine::runtime_options::RuntimeOptions;
fn deref(&self) -> &Self::Target {
&self.runtime_options
}
@@ -2636,7 +2636,10 @@ fn run() -> Result<()> {
// process or argument owner. Keep its narrow command contract.
let mut argv = vec!["pet".to_string()];
argv.extend(args.args);
let code = codewhale_tui::run(codewhale_tui::RuntimeOptions::default(), argv);
let code = codewhale_tui::run(
codewhale_engine::runtime_options::RuntimeOptions::default(),
argv,
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
0
} else {
@@ -4955,7 +4958,10 @@ fn run_auth_command_with_secrets_and_runtime(
}
AuthCommand::XaiDevice => {
let argv = vec!["auth".to_string(), "xai-device".to_string()];
let code = codewhale_tui::run(codewhale_tui::RuntimeOptions::default(), argv);
let code = codewhale_tui::run(
codewhale_engine::runtime_options::RuntimeOptions::default(),
argv,
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
0
} else {
@@ -4969,7 +4975,7 @@ fn run_auth_command_with_secrets_and_runtime(
"claude-revoke"
};
let code = codewhale_tui::run(
codewhale_tui::RuntimeOptions::default(),
codewhale_engine::runtime_options::RuntimeOptions::default(),
vec!["auth".to_string(), route.to_string()],
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
@@ -4980,7 +4986,10 @@ fn run_auth_command_with_secrets_and_runtime(
}
AuthCommand::Chatgpt => {
let argv = vec!["auth".to_string(), "chatgpt".to_string()];
let code = codewhale_tui::run(codewhale_tui::RuntimeOptions::default(), argv);
let code = codewhale_tui::run(
codewhale_engine::runtime_options::RuntimeOptions::default(),
argv,
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
0
} else {
@@ -4989,7 +4998,10 @@ fn run_auth_command_with_secrets_and_runtime(
}
AuthCommand::ChatgptRevoke => {
let argv = vec!["auth".to_string(), "chatgpt-revoke".to_string()];
let code = codewhale_tui::run(codewhale_tui::RuntimeOptions::default(), argv);
let code = codewhale_tui::run(
codewhale_engine::runtime_options::RuntimeOptions::default(),
argv,
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
0
} else {
@@ -4998,7 +5010,10 @@ fn run_auth_command_with_secrets_and_runtime(
}
AuthCommand::Orcarouter => {
let argv = vec!["auth".to_string(), "orcarouter".to_string()];
let code = codewhale_tui::run(codewhale_tui::RuntimeOptions::default(), argv);
let code = codewhale_tui::run(
codewhale_engine::runtime_options::RuntimeOptions::default(),
argv,
);
std::process::exit(if code == std::process::ExitCode::SUCCESS {
0
} else {
@@ -5495,9 +5510,10 @@ fn run_config_command(
}
match command {
ConfigCommand::Get { key } => {
if per_run_overrides.is_empty() && codewhale_tui::route_preferences::is_route_key(&key)
if per_run_overrides.is_empty()
&& codewhale_engine::route_preferences::is_route_key(&key)
{
if let Some(value) = codewhale_tui::route_preferences::get(store.path(), &key)? {
if let Some(value) = codewhale_engine::route_preferences::get(store.path(), &key)? {
println!("{value}");
return Ok(());
}
@@ -5560,8 +5576,8 @@ fn run_config_command(
bail!("key not found: {key}");
}
ConfigCommand::Set { key, value } => {
if codewhale_tui::route_preferences::is_route_key(&key) {
codewhale_tui::route_preferences::set(store.path(), &key, &value)?;
if codewhale_engine::route_preferences::is_route_key(&key) {
codewhale_engine::route_preferences::set(store.path(), &key, &value)?;
store.reload()?;
println!("set {key}");
return Ok(());
@@ -5640,8 +5656,8 @@ fn run_config_command(
Ok(())
}
ConfigCommand::Unset { key } => {
if codewhale_tui::route_preferences::is_route_key(&key) {
codewhale_tui::route_preferences::unset(store.path(), &key)?;
if codewhale_engine::route_preferences::is_route_key(&key) {
codewhale_engine::route_preferences::unset(store.path(), &key)?;
store.reload()?;
println!("unset {key}");
return Ok(());
@@ -5952,7 +5968,7 @@ fn model_set_route(
resolved_runtime.base_url.clone(),
)));
}
let (route, _, _) = codewhale_tui::route_preferences::selected_route(store.path())?;
let (route, _, _) = codewhale_engine::route_preferences::selected_route(store.path())?;
Ok(ProviderKind::parse_config_identity(&route).map(|provider| {
let base_url = store
.config
@@ -5979,7 +5995,8 @@ fn run_model_command(
match command {
ModelCommand::List { provider } => {
let filter = model_command_provider_hint(provider, top_level_provider);
for (provider, models) in codewhale_tui::route_preferences::list_models(store, filter)?
for (provider, models) in
codewhale_engine::route_preferences::list_models(store, filter)?
{
for model in models {
println!("{model} ({})", provider.as_str());
@@ -6008,7 +6025,7 @@ fn run_model_command(
resolved_runtime.model_source,
codewhale_config::ModelSource::Cli | codewhale_config::ModelSource::Env
) {
Some(codewhale_tui::route_preferences::selected_route(
Some(codewhale_engine::route_preferences::selected_route(
store.path(),
)?)
} else {
@@ -6048,7 +6065,7 @@ fn run_model_command(
.clone()
.unwrap_or_else(|| kind.provider().default_base_url().into())
};
let candidate = codewhale_tui::route_preferences::resolve_model(
let candidate = codewhale_engine::route_preferences::resolve_model(
store.path(),
identity,
&base,
@@ -6120,7 +6137,7 @@ fn run_model_command(
.clone()
.unwrap_or_else(|| provider.provider().default_base_url().into())
};
let candidate = codewhale_tui::route_preferences::resolve_model(
let candidate = codewhale_engine::route_preferences::resolve_model(
store.path(),
identity,
&base,
@@ -6189,7 +6206,7 @@ fn run_model_command(
} else {
expanded
};
codewhale_tui::route_preferences::set(store.path(), "model", canonical)?;
codewhale_engine::route_preferences::set(store.path(), "model", canonical)?;
store.reload()?;
println!("Default model set to '{canonical}'");
Ok(())
@@ -7997,7 +8014,7 @@ verbosity = "concise"
.unwrap();
assert_eq!(store.config.providers.zai.model.as_deref(), Some("GLM-5.1"));
assert_eq!(
codewhale_tui::route_preferences::get(&path, "model")
codewhale_engine::route_preferences::get(&path, "model")
.unwrap()
.as_deref(),
Some("GLM-5.1")
@@ -13180,7 +13197,7 @@ verbosity = "concise"
]);
assert_eq!(
cli.runtime_options,
codewhale_tui::RuntimeOptions::default()
codewhale_engine::runtime_options::RuntimeOptions::default()
);
assert_eq!(
root_tui_passthrough(&cli).unwrap(),
+2 -2
View File
@@ -7,7 +7,7 @@
//! - `~/.codewhale/sessions/` — saved session JSON files (tool call history)
//! - `~/.codewhale/tasks/runtime/events/` — runtime thread JSONL event streams
//! - `~/.codewhale/sessions/<id>/runtime/events/` — session-scoped runtime
//! stores (`default_runtime_store_root` in `crates/tui/src/runtime_threads.rs`)
//! stores (`default_runtime_store_root` in `crates/engine/src/runtime_threads.rs`)
//!
//! `CODEWHALE_RUNTIME_DIR` / `DEEPSEEK_RUNTIME_DIR` is an *exclusive* store root
//! for every Runtime store in the writing process, so when it is set the reader
@@ -823,7 +823,7 @@ fn read_session_file(path: &Path, since: Option<DateTime<Utc>>, rollup: &mut Rol
/// Every Runtime event directory this install can have written to.
///
/// Mirrors `default_runtime_store_root` / `runtime_dir_override` in
/// `crates/tui/src/runtime_threads.rs`: the task-scoped store lives at
/// `crates/engine/src/runtime_threads.rs`: the task-scoped store lives at
/// `<tasks>/runtime`, a session-scoped store at `<sessions>/<id>/runtime`, and
/// an explicit `CODEWHALE_RUNTIME_DIR` replaces both. Missing directories are
/// simply empty; the walk stays inside the resolved state roots.
@@ -4,7 +4,7 @@
"providers": [
{
"provider": "deepseek",
"pricing_withheld": "DeepSeek bills by time of day (off-peak rates and a weekend rule). The time-aware table in crates/tui/src/pricing.rs prices these routes; one flat catalog rate would disagree with it."
"pricing_withheld": "DeepSeek bills by time of day (off-peak rates and a weekend rule). The time-aware table in crates/engine/src/pricing.rs prices these routes; one flat catalog rate would disagree with it."
},
{
"provider": "xiaomi-mimo",
@@ -28,7 +28,7 @@
},
{
"provider": "stepfun",
"pricing_withheld": "The same model ids serve Step Plan quota and pay-as-you-go keys. Endpoint-scoped rates live in crates/tui/src/pricing.rs."
"pricing_withheld": "The same model ids serve Step Plan quota and pay-as-you-go keys. Endpoint-scoped rates live in crates/engine/src/pricing.rs."
}
],
"models": [
@@ -171,7 +171,7 @@
"default": "always_on"
}
],
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/tui/src/client/chat.rs); Models.dev lists a toggle."
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/engine/src/client/chat.rs); Models.dev lists a toggle."
},
{
"provider": "modelstudio-token-plan-anthropic",
@@ -185,7 +185,7 @@
"default": "always_on"
}
],
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/tui/src/client/chat.rs); Models.dev lists a toggle."
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/engine/src/client/chat.rs); Models.dev lists a toggle."
},
{
"provider": "modelstudio-coding-plan",
@@ -199,7 +199,7 @@
"default": "always_on"
}
],
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/tui/src/client/chat.rs); Models.dev lists a toggle."
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/engine/src/client/chat.rs); Models.dev lists a toggle."
},
{
"provider": "modelstudio-coding-plan-anthropic",
@@ -213,7 +213,7 @@
"default": "always_on"
}
],
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/tui/src/client/chat.rs); Models.dev lists a toggle."
"reason": "qwen3.8-max is thinking-only (owner's Token Plan console, ec4a5aeb2; Codewhale never sends it a thinking switch, see modelstudio_model_is_thinking_only in crates/engine/src/client/chat.rs); Models.dev lists a toggle."
},
{
"provider": "meta",
@@ -343,7 +343,7 @@
"output_per_m": 0.8,
"cache_read_per_m": 0.25
},
"reason": "Arcee publishes no cache rate (docs.arcee.ai/get-started/pricing), so cache hits bill at the input rate, as in the reviewed table in crates/tui/src/pricing.rs; Models.dev lists a 0.06 cache-read rate."
"reason": "Arcee publishes no cache rate (docs.arcee.ai/get-started/pricing), so cache hits bill at the input rate, as in the reviewed table in crates/engine/src/pricing.rs; Models.dev lists a 0.06 cache-read rate."
},
{
"provider": "fireworks",
@@ -353,7 +353,7 @@
"output_per_m": 3.48,
"cache_read_per_m": 0.145
},
"reason": "Fireworks hosts DeepSeek V4 Pro at its own published rates, as in the reviewed table in crates/tui/src/pricing.rs; Models.dev lists a flat 1.2/1.2 rate with a 0.6 cache-read rate for this deployment."
"reason": "Fireworks hosts DeepSeek V4 Pro at its own published rates, as in the reviewed table in crates/engine/src/pricing.rs; Models.dev lists a flat 1.2/1.2 rate with a 0.6 cache-read rate for this deployment."
}
],
"reviewed": {
@@ -4582,7 +4582,7 @@
"crates/config/src/opencode_go.rs": "31e304f38ead417e95c193f08ceb3662e607742bfd45ddb0daef9b8a2d3e8471",
"crates/config/src/provider_defaults.rs": "2ca4bd17f253897ce87ac18c3b1bfec7e2ebfc652ea9ca68582de1f9fc20044f",
"crates/tui/src/config/models.rs": "9fc84a4fa8fd8a8d175391a53b3816a8bc8b4a14ca2fd3ab6d58e826bed9747e",
"crates/tui/src/pricing.rs": "31606a3c11c03bf317899e5200c6d849b32c471d7edb5f4eb27d23fdbce67059",
"crates/engine/src/pricing.rs": "31606a3c11c03bf317899e5200c6d849b32c471d7edb5f4eb27d23fdbce67059",
"https://docs.mistral.ai/studio/conversations/reasoning": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference; verified 2026-10-03: exact magistral-small-latest is explicitly listed as a native reasoning model (deprecated), historical wire capability only; no availability/default inference",
"https://docs.mistral.ai/vibe/code/cli/configuration": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference",
"https://docs.mistral.ai/models/mistral-medium-3-5-26-04": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference",
+1 -1
View File
@@ -12049,7 +12049,7 @@
"crates/config/src/opencode_go.rs": "31e304f38ead417e95c193f08ceb3662e607742bfd45ddb0daef9b8a2d3e8471",
"crates/config/src/provider_defaults.rs": "2ca4bd17f253897ce87ac18c3b1bfec7e2ebfc652ea9ca68582de1f9fc20044f",
"crates/tui/src/config/models.rs": "9fc84a4fa8fd8a8d175391a53b3816a8bc8b4a14ca2fd3ab6d58e826bed9747e",
"crates/tui/src/pricing.rs": "31606a3c11c03bf317899e5200c6d849b32c471d7edb5f4eb27d23fdbce67059",
"crates/engine/src/pricing.rs": "31606a3c11c03bf317899e5200c6d849b32c471d7edb5f4eb27d23fdbce67059",
"https://docs.mistral.ai/studio/conversations/reasoning": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference; verified 2026-10-03: exact magistral-small-latest is explicitly listed as a native reasoning model (deprecated), historical wire capability only; no availability/default inference",
"https://docs.mistral.ai/vibe/code/cli/configuration": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference",
"https://docs.mistral.ai/models/mistral-medium-3-5-26-04": "verified 2026-10-02: explicit reasoning support for mistral-medium-latest/mistral-medium-3-5 and mistral-small-latest/mistral-small-2603; no family-prefix inference",
+2 -2
View File
@@ -80,7 +80,7 @@ struct AllowedTurnLoop {
}
const ALLOWED_TURN_LOOPS: &[AllowedTurnLoop] = &[AllowedTurnLoop {
path: "crates/tui/src/core/engine/turn_loop.rs",
path: "crates/engine/src/core/engine/turn_loop.rs",
owner: "run_turn",
why: "THE turn loop. `Engine::run_turn` is the single agent loop; \
docs/ARCHITECTURE.md and AGENTS.md both name it as the owner.",
@@ -1328,7 +1328,7 @@ fn rlm_cannot_reintroduce_client_only_authority_or_a_direct_provider_producer()
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("tui/src");
.join("engine/src");
for path in [
"rlm/bridge.rs",
"rlm/turn.rs",
+240
View File
@@ -0,0 +1,240 @@
[package]
name = "codewhale-engine"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "Codewhale engine: turn loop, tools, agents, context, sessions and model clients"
# Test sources, extension-host build inputs and dev-only assets are kept for
# CI and local runs; the published package omits them (crates.io 10 MiB cap).
exclude = [
"tests/**",
"src/**/tests.rs",
"src/**/tests/**",
"src/**/*_tests.rs",
"src/**/test_cases_*.rs",
"src/**/*_legacy.rs",
"src/**/*test_support.rs",
"src/**/*acceptance.rs",
"src/llm_client/mock.rs",
"src/compaction/survival_contract.rs",
"src/compaction/SURVIVAL_CONTRACT.md",
"src/compaction/fixtures/**",
"src/pricing/default_coverage_unpriced.json",
"extension-host/test/**",
"extension-host/node_modules/**",
"extension-host/build.mjs",
"extension-host/compile-host.mjs",
"extension-host/package-lock.json",
"extension-host/scripts/**",
"extension-host/src/**/*.ts",
"extension-host/src/dsh/dsh-tools-compat.js",
"extension-host/dist/agent-presets.mjs",
"extension-host/dist/avatars.mjs",
"extension-host/dist/builtin-modules.json",
"extension-host/dist/protocol.mjs",
"extension-host/dist/rpc.mjs",
"extension-host/dist/shell-hooks.mjs",
"extension-host/dist/skills.mjs",
"extension-host/dist/stock-adapters.mjs",
]
[lints]
workspace = true
[features]
default = []
# Test-only behavior (sealed homes, mock model client, no keychain) for this
# crate's own tests and for dependents' tests that drive engine code.
test-support = [
"codewhale-runtime/test-support",
"codewhale-runtime/test-thread-scoped-state",
"codewhale-config/test-support",
"codewhale-agent-contract/test-support",
"codewhale-dependencies/test-support",
"codewhale-error-taxonomy/test-support",
"codewhale-lsp/test-support",
"codewhale-models/test-support",
"codewhale-plugins/test-support",
"codewhale-sandbox/test-support",
"codewhale-skill-install/test-support",
"codewhale-skills/test-support",
"codewhale-snapshot/test-support",
"codewhale-util/test-support",
"dep:wiremock",
]
long-running-tests = []
test-thread-scoped-state = []
[lib]
name = "codewhale_engine"
path = "src/lib.rs"
doctest = false
[dependencies]
# Independent terminal kit, pinned and vendored for reproducible binary builds.
codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.1.3" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-memory = { path = "../memory", version = "0.10.2" }
anyhow.workspace = true
codewhale-agent = { path = "../agent", version = "0.10.2" }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2" }
codewhale-app-server = { path = "../app-server", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-commands = { path = "../commands", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
codewhale-mcp = { path = "../mcp", version = "0.10.2" }
codewhale-models = { path = "../models", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-palette = { path = "../palette", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.2" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2" }
codewhale-lsp = { path = "../lsp", version = "0.10.2" }
codewhale-repl = { path = "../repl", version = "0.10.2" }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2" }
codewhale-plugins = { path = "../plugins", version = "0.10.2" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2" }
codewhale-skills = { path = "../skills", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2" }
codewhale-tools = { path = "../tools", version = "0.10.2" }
codewhale-workflow = { path = "../workflow", version = "0.10.2" }
codewhale-workflow-js = { path = "../workflow-js", version = "0.10.2" }
async-stream = "0.3.6"
async-trait.workspace = true
base64 = "0.23.1"
axum.workspace = true
clap.workspace = true
crossterm = "0.29"
encoding_rs.workspace = true
fd-lock = "4.0.4"
dirs.workspace = true
futures-util = "0.3.31"
oauth2 = { version = "5", default-features = false }
# No ratatui-core pin: ColorCompatBackend answers `get_cursor_position()`
# from tracked state (ratatui#2640's recommended workaround), so the CPR query
# inside `Terminal::clear()` no longer races the input loop. See
# `tui/color_compat.rs`.
ratatui = { version = "=0.30.2", features = ["unstable-rendered-line-info"] }
regex = "1.11"
reqwest = { workspace = true, features = ["blocking", "stream", "form", "http2"] }
rquickjs.workspace = true
rmcp = { version = "3.7.0", default-features = false, features = ["auth", "client", "server", "transport-io"] }
rustls.workspace = true
similar = { version = "3", features = ["unicode"] }
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order", "raw_value"] }
shellexpand = "3"
# Grammar-exact Rust parsing for the post-edit syntax gate (#6204). `syn` is
# already in the lockfile as a proc-macro build dependency; this reuses that
# same version rather than vendoring a second parser (#6151). `span-locations`
# is what turns a parse failure into the `line:column` the model needs — the
# feature is inert without it.
syn = { version = "3.0.3", default-features = false, features = ["full", "parsing"] }
proc-macro2 = { version = "1.0.107", features = ["span-locations"] }
toml.workspace = true
toml_edit.workspace = true
tokio.workspace = true
tokio-util = { version = "0.7.16", features = ["io"] }
unicode-width = "0.2"
unicode-normalization = "0.1"
urlencoding = "2.1"
# Structcopy uses the URL parser directly so its portable closure needs no HTTP client.
url = "2.5"
uuid.workspace = true
chrono.workspace = true
tempfile.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
tower-http.workspace = true
wait-timeout = "0.2"
webbrowser = "1.0"
shlex = "2.0.1"
globset = "0.4"
ignore = "0.4"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }
# Already a regular dependency of codewhale-workflow-js (so already linked into
# this binary); the extension host validates tool input against the schema the
# plugin registered with it.
jsonschema.workspace = true
htmd = "0.5.4"
# lru >= 0.18.2 fixes RUSTSEC-2026-0253 (panic-unsafe `pop()`).
lru = "0.18"
parking_lot = "0.12"
tar = "0.4"
# Vendored static liblzma keeps the full-fidelity session archive export
# (`session_export`) hermetic on every release target; no system xz headers.
# liblzma is the maintained, API-compatible continuation of xz2.
liblzma = { version = "0.4", features = ["static"] }
sha2.workspace = true
# Reuse the locked cryptographic implementation for exact-call user decisions.
ring = "0.17"
semver.workspace = true
codewhale-localization = { path = "../localization", version = "0.10.2" }
shell-words = "1.1.1"
wiremock = { version = "0.6", optional = true }
[build-dependencies]
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
[dev-dependencies]
codewhale-skills = { path = "../skills", version = "0.10.2", features = ["test-support"] }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2", features = ["test-support"] }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2", features = ["test-support"] }
codewhale-lsp = { path = "../lsp", version = "0.10.2", features = ["test-support"] }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2", features = ["test-support"] }
codewhale-config = { path = "../config", version = "0.10.2", features = ["schema", "test-support"] }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2", features = [
"test-support",
] }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = [
"test-support",
"test-thread-scoped-state",
] }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2", features = ["test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.2", features = ["test-support"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.2", features = ["test-support"] }
codewhale-models = { path = "../models", version = "0.10.2", features = [
"test-support",
] }
cucumber = "0.23.0"
wiremock = "0.6"
tiny_http = "0.12"
pretty_assertions = "1.4"
rio-vt = "0.5.1"
rusqlite.workspace = true
schemars = { version = "1.2.1", features = ["derive", "preserve_order"] }
tokio-tungstenite = "0.30"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61.2", features = ["Wdk_Storage_FileSystem", "Win32_System_IO", "Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Security_Isolation", "Win32_Storage_FileSystem", "Win32_System_LibraryLoader", "Win32_System_Pipes", "Win32_System_Threading", "Win32_System_SystemServices", "Win32_System_WindowsProgramming"] }
[target.'cfg(any(target_os = "macos", target_os = "windows", all(target_os = "linux", not(target_env = "ohos"))))'.dependencies]
arboard = { version = "3.4", features = ["wayland-data-control"] }
[target.'cfg(not(target_env = "ohos"))'.dependencies]
portable-pty = "0.9"
[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6.3"
objc2-foundation = { version = "0.3.2", default-features = false, features = ["std", "NSArray", "NSDictionary", "NSError", "NSObject", "NSString", "NSURL"] }
[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.62", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Environment", "Win32_System_JobObjects", "Win32_System_Registry", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging"] }
+11
View File
@@ -0,0 +1,11 @@
use std::path::PathBuf;
fn main() {
// Shared target directories may reuse this executable in another worktree.
let manifest_dir = PathBuf::from(
std::env::var_os("CARGO_MANIFEST_DIR").expect("Cargo sets CARGO_MANIFEST_DIR"),
);
println!("cargo:rerun-if-env-changed=CARGO_MANIFEST_DIR");
codewhale_build_support::declare_rerun_conditions(&manifest_dir);
codewhale_build_support::emit_build_version(&manifest_dir, env!("CARGO_PKG_VERSION"));
}
File renamed without changes.
@@ -59,7 +59,7 @@ for (const id of builtinIds) {
legalComments: 'none',
charset: 'utf8',
logLevel: 'warning',
banner: { js: `// codewhale built-in host module ${id} — generated by crates/tui/extension-host/build.mjs; do not edit.` },
banner: { js: `// codewhale built-in host module ${id} — generated by crates/engine/extension-host/build.mjs; do not edit.` },
})
builtinMetafiles.push(built.metafile)
if (id === 'mcp') {
@@ -94,7 +94,7 @@ const hostBuild = await build({
// pinned table agree (`tier.ts`, `builtinModuleDigests`).
define: { __BUILTIN_MODULE_DIGESTS__: JSON.stringify(builtinDigests) },
banner: {
js: '// codewhale-extension-host — generated by crates/tui/extension-host/build.mjs; do not edit.\n// Third-party notices: LICENSES.txt next to this file.',
js: '// codewhale-extension-host — generated by crates/engine/extension-host/build.mjs; do not edit.\n// Third-party notices: LICENSES.txt next to this file.',
},
})
@@ -152,7 +152,7 @@ await build({
legalComments: 'none',
charset: 'utf8',
logLevel: 'warning',
banner: { js: '// generated by crates/tui/extension-host/build.mjs from src/protocol.ts; do not edit.' },
banner: { js: '// generated by crates/engine/extension-host/build.mjs from src/protocol.ts; do not edit.' },
})
// Test-only RPC peer, for the unit tests of its cancellation. Not embedded.
@@ -168,7 +168,7 @@ await build({
legalComments: 'none',
charset: 'utf8',
logLevel: 'warning',
banner: { js: '// generated by crates/tui/extension-host/build.mjs from src/rpc.ts; do not edit.' },
banner: { js: '// generated by crates/engine/extension-host/build.mjs from src/rpc.ts; do not edit.' },
})
// Test-only skill-root shim, bundled so tests remain dependency-free before npm ci.
@@ -185,7 +185,7 @@ await build({
legalComments: 'none',
charset: 'utf8',
logLevel: 'warning',
banner: { js: '// generated by crates/tui/extension-host/build.mjs from src/shims/skills.ts; do not edit.' },
banner: { js: '// generated by crates/engine/extension-host/build.mjs from src/shims/skills.ts; do not edit.' },
})
// Test-only avatar-pack shim, bundled so tests remain dependency-free before npm ci.
@@ -202,7 +202,7 @@ await build({
legalComments: 'none',
charset: 'utf8',
logLevel: 'warning',
banner: { js: '// generated by crates/tui/extension-host/build.mjs from src/shims/avatars.ts; do not edit.' },
banner: { js: '// generated by crates/engine/extension-host/build.mjs from src/shims/avatars.ts; do not edit.' },
})
// Source-focused raw roster tests use the same discovery/receipt adapter.
@@ -326,7 +326,7 @@ entries.sort((a, b) => (a.name < b.name ? -1 : 1))
const sections = [
'Third-party software bundled into codewhale-extension-host.mjs, dsh-composition-review.mjs and builtin/*.mjs.',
'Generated by crates/tui/extension-host/build.mjs from the bundler metafile; do not edit.',
'Generated by crates/engine/extension-host/build.mjs from the bundler metafile; do not edit.',
'',
'Packages:',
...entries.map((entry) => ` ${entry.name}@${entry.version} (${entry.license})`),
@@ -1,5 +1,5 @@
Third-party software bundled into codewhale-extension-host.mjs, dsh-composition-review.mjs and builtin/*.mjs.
Generated by crates/tui/extension-host/build.mjs from the bundler metafile; do not edit.
Generated by crates/engine/extension-host/build.mjs from the bundler metafile; do not edit.
Packages:
@deepseek-ai/[email protected] (MIT)
@@ -1,4 +1,4 @@
// generated by crates/tui/extension-host/build.mjs from src/shims/avatars.ts; do not edit.
// generated by crates/engine/extension-host/build.mjs from src/shims/avatars.ts; do not edit.
// node_modules/@deepseek-ai/cosmokit/lib/index.js
function isNullable(value) {
@@ -0,0 +1,6 @@
{
"modules": {
"harness": "ac65aa44a3824dcdb0060db426bf530a088fd593e58b9f4cfdaefbc09a5fa037",
"mcp": "f9735707e49e349ee38f791fa3c6f3379fde43cad5044f381f8e313286ea3d39"
}
}
@@ -1,4 +1,4 @@
// codewhale built-in host module harness — generated by crates/tui/extension-host/build.mjs; do not edit.
// codewhale built-in host module harness — generated by crates/engine/extension-host/build.mjs; do not edit.
// src/dsh/upstream/hooks/hook-protocol/src/matcher.ts
function isMatchAll(matcher) {
@@ -1,4 +1,4 @@
// codewhale built-in host module mcp — generated by crates/tui/extension-host/build.mjs; do not edit.
// codewhale built-in host module mcp — generated by crates/engine/extension-host/build.mjs; do not edit.
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
@@ -1,4 +1,4 @@
// codewhale-extension-host — generated by crates/tui/extension-host/build.mjs; do not edit.
// codewhale-extension-host — generated by crates/engine/extension-host/build.mjs; do not edit.
// Third-party notices: LICENSES.txt next to this file.
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -19,7 +19,7 @@ var __export = (target, all) => {
var define_BUILTIN_MODULE_DIGESTS_default;
var init_define_BUILTIN_MODULE_DIGESTS = __esm({
"<define:__BUILTIN_MODULE_DIGESTS__>"() {
define_BUILTIN_MODULE_DIGESTS_default = { harness: "ad24207f93d963dcffe1967f6863f416117d9acd67cdfccbcef59c12ccb39f92", mcp: "a01be482b32e24d50267b466b206b793e3d64b8692fb0961f9be85862a639892" };
define_BUILTIN_MODULE_DIGESTS_default = { harness: "ac65aa44a3824dcdb0060db426bf530a088fd593e58b9f4cfdaefbc09a5fa037", mcp: "f9735707e49e349ee38f791fa3c6f3379fde43cad5044f381f8e313286ea3d39" };
}
});
@@ -1,4 +1,4 @@
// generated by crates/tui/extension-host/build.mjs from src/protocol.ts; do not edit.
// generated by crates/engine/extension-host/build.mjs from src/protocol.ts; do not edit.
// src/protocol.generated.ts
var PROTOCOL_VERSION = 1;
@@ -1,4 +1,4 @@
// generated by crates/tui/extension-host/build.mjs from src/rpc.ts; do not edit.
// generated by crates/engine/extension-host/build.mjs from src/rpc.ts; do not edit.
// src/protocol.generated.ts
var MAGIC_ASCII = "CWX1";
@@ -1,4 +1,4 @@
// generated by crates/tui/extension-host/build.mjs from src/shims/skills.ts; do not edit.
// generated by crates/engine/extension-host/build.mjs from src/shims/skills.ts; do not edit.
// node_modules/@deepseek-ai/cosmokit/lib/index.js
function isNullable(value) {
File renamed without changes.
@@ -1,4 +1,4 @@
// @generated from the Rust protocol types in crates/tui/src/extension_host/protocol.rs
// @generated from the Rust protocol types in crates/engine/src/extension_host/protocol.rs
// by `extension_host::protocol::tests`. Do not edit: change the Rust side, re-record with
// CODEWHALE_CONFORMANCE_UPDATE=1 cargo test -p codewhale-tui --lib extension_host::protocol
// and rebuild dist/ with `npm run build`.
@@ -1,13 +1,13 @@
/**
* Codewhale extension-host protocol, version 1.
*
* The Rust serde types in `crates/tui/src/extension_host/protocol.rs` are the
* The Rust serde types in `crates/engine/src/extension_host/protocol.rs` are the
* source of truth. The constants, the method table, every params shape and the
* wire types come from `protocol.generated.ts`, which a Rust test renders from
* those types and fails on when the committed file drifts. Hand-written here:
* the frame codec, the JSON-RPC envelope checks, and the one rule Rust applies
* beyond its types (`host/hello`'s runtime name). Both sides also parse the
* shared corpus in `crates/tui/tests/fixtures/extension_host/protocol`.
* shared corpus in `crates/engine/tests/fixtures/extension_host/protocol`.
*
* Frame: 4-byte magic `CWX1`, u32 little-endian payload length, UTF-8 JSON.
* Envelope: JSON-RPC 2.0.
Loaded 100 of 1090 files, more files were not shown because too many files have changed in this diff. Show more