diff --git a/CHANGELOG.md b/CHANGELOG.md index 5892e360..259bd9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,21 @@ breaking entries are marked **BREAKING**. ## [Unreleased] +## [0.17.0] - 2026-08-31 + ### Changed - **BREAKING: removed plan-side confirm-key redaction** — `PlannedValue::Redacted`, `StatementPlan::presented_keys`, `strip_confirm_tokens`, and `redact_keys` are gone. The credential they protected was removed in 0.14.0; `--confirm=` now plans and renders like any other argument. -- `ExecContext` carries `kill_grace` and `background_job`, so a tool holding only - an `ExecContext` can spawn a child with the kernel's external-command - discipline. `tools::DEFAULT_KILL_GRACE` is 2s. Only a struct literal changes. +- **BREAKING: `ExecContext` gained two fields and is now `#[non_exhaustive]`** — + it carries `kill_grace` and `background_job`, so a tool holding only an + `ExecContext` can spawn a child with the kernel's external-command discipline. + `tools::DEFAULT_KILL_GRACE` is 2s. A struct literal outside the crate no longer + compiles: build one with `ExecContext::new` or a `with_*` constructor and set + the fields you need. `#[non_exhaustive]` is the point — the next field the + kernel adds will not break you again. - **Arithmetic diverges from bash on purpose** — overflow, an unset/empty operand, and a leading zero are errors, never a wrap or 0; strings are @@ -30,8 +36,9 @@ breaking entries are marked **BREAKING**. path resolver for a backend rooted at a host directory: sync, std-only, containment inside. `Follow::Final` follows the last component; `Follow::LinkItself` acts on the link (remove, rename, lstat, symlink). -- **`kaish_vfs::conformance`** (`conformance` feature) — 19 symlink cases any - `Filesystem` backend runs against itself via `run_all(make_root)`. +- **`kaish_vfs::conformance`** (`conformance` feature) — 20 symlink cases a + `Filesystem` backend runs against itself via `run_all(make_root)`. LocalFs, + MemoryFs, and OverlayFs run them; DevFs is exempt, having no symlinks. - **`[[ -L path ]]`** (alias `-h`) and `test -L` — true when the path is a symlink, including a dangling one. - **`cp -P`/`-L`**; `cp -r` recreates a symlink as a link with the same @@ -86,6 +93,10 @@ breaking entries are marked **BREAKING**. that says what to fix, including the validator's suggested rewrite. A context that carries information, like `Failed to read script: `, stays. +- **The glued-argv error names the word that needs quoting** — `git show + HEAD:dir/x.py` blamed `show`, an innocent token three characters earlier. + The span now covers the whole pasted run, so the advice points at the word + it is about. - **A comparison kaish cannot make is a fault, not `false`** — `[[ ]]`, `test`, and `(( ))` now agree: exit 2 where nothing reads the result as a boolean, and an abort where something does (`if`/`while`, `!`, the left operand of @@ -2568,7 +2579,8 @@ Initial public release of **kaish** (会sh) — a predictable Bourne-like shell - **REPL** (`kaish-repl`) with multi-line input, completion, and history; **MCP server** (`kaish-mcp`) exposing `kaish_execute` with help resources and structured + plain-text content blocks. - **`KernelClient` trait** + `EmbeddedClient` for in-process embedding; topic-based help system; `kaish-wasi` `wasm32-wasip1` target. -[Unreleased]: https://github.com/tobert/kaish/compare/v0.16.0...HEAD +[Unreleased]: https://github.com/tobert/kaish/compare/v0.17.0...HEAD +[0.17.0]: https://github.com/tobert/kaish/compare/v0.16.0...v0.17.0 [0.16.0]: https://github.com/tobert/kaish/compare/v0.15.0...v0.16.0 [0.15.0]: https://github.com/tobert/kaish/compare/v0.14.1...v0.15.0 [0.14.1]: https://github.com/tobert/kaish/compare/v0.14.0...v0.14.1 diff --git a/Cargo.lock b/Cargo.lock index 7b0a5b1e..939c6d00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,7 +1011,7 @@ dependencies = [ [[package]] name = "kaish-client" -version = "0.16.0" +version = "0.17.0" dependencies = [ "anyhow", "async-trait", @@ -1027,7 +1027,7 @@ dependencies = [ [[package]] name = "kaish-glob" -version = "0.16.0" +version = "0.17.0" dependencies = [ "async-trait", "ignore", @@ -1039,14 +1039,14 @@ dependencies = [ [[package]] name = "kaish-help" -version = "0.16.0" +version = "0.17.0" dependencies = [ "kaish-types", ] [[package]] name = "kaish-kernel" -version = "0.16.0" +version = "0.17.0" dependencies = [ "anyhow", "ariadne", @@ -1108,7 +1108,7 @@ dependencies = [ [[package]] name = "kaish-repl" -version = "0.16.0" +version = "0.17.0" dependencies = [ "anyhow", "chrono", @@ -1130,7 +1130,7 @@ dependencies = [ [[package]] name = "kaish-tool-api" -version = "0.16.0" +version = "0.17.0" dependencies = [ "async-trait", "clap", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "kaish-tools-host" -version = "0.16.0" +version = "0.17.0" dependencies = [ "async-trait", "clap", @@ -1150,7 +1150,7 @@ dependencies = [ [[package]] name = "kaish-types" -version = "0.16.0" +version = "0.17.0" dependencies = [ "base64", "schemars", @@ -1163,7 +1163,7 @@ dependencies = [ [[package]] name = "kaish-vfs" -version = "0.16.0" +version = "0.17.0" dependencies = [ "async-trait", "getrandom 0.3.4", @@ -1175,7 +1175,7 @@ dependencies = [ [[package]] name = "kaish-wasi" -version = "0.16.0" +version = "0.17.0" dependencies = [ "kaish-kernel", "kaish-types", diff --git a/Cargo.toml b/Cargo.toml index 181db8e8..91ab0c89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ ] [workspace.package] -version = "0.16.0" +version = "0.17.0" edition = "2024" rust-version = "1.85" authors = ["Amy Tobey "] diff --git a/README.md b/README.md index 16916add..01a99ce1 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ Construct a `Kernel`, point it at a sandbox root, call `execute()`: ```toml [dependencies] -kaish-kernel = "0.16" +kaish-kernel = "0.17" tokio = { version = "1", features = ["full"] } ``` diff --git a/crates/kaish-client/Cargo.toml b/crates/kaish-client/Cargo.toml index 5a45bb6e..966a19be 100644 --- a/crates/kaish-client/Cargo.toml +++ b/crates/kaish-client/Cargo.toml @@ -16,8 +16,8 @@ readme = "../../README.md" # Feature-neutral on purpose: the embedder decides the kernel's feature set # (kaish-web builds no-default for wasm; native hosts add localfs etc.). # Unification means enabling defaults here would trample that choice. -kaish-kernel = { path = "../kaish-kernel", version = "0.16.0", default-features = false } -kaish-types = { path = "../kaish-types", version = "0.16.0" } +kaish-kernel = { path = "../kaish-kernel", version = "0.17.0", default-features = false } +kaish-types = { path = "../kaish-types", version = "0.17.0" } # Async traits only — no runtime: the library spawns nothing itself, and a # tokio dep here would drag net/mio into wasm builds (kaish-extras). @@ -39,7 +39,7 @@ tracing = { workspace = true } # this, `cargo test -p kaish-client` alone sees a no-localfs kernel while a # whole-workspace run gets localfs unified in by the REPL, and outcomes # depend on which crates happened to build. -kaish-kernel = { path = "../kaish-kernel", version = "0.16.0", features = ["localfs"] } +kaish-kernel = { path = "../kaish-kernel", version = "0.17.0", features = ["localfs"] } proptest = { workspace = true } tempfile = { workspace = true } # Tests drive the async client API; the library itself needs no runtime — diff --git a/crates/kaish-help/Cargo.toml b/crates/kaish-help/Cargo.toml index 711735a4..a4f91ccf 100644 --- a/crates/kaish-help/Cargo.toml +++ b/crates/kaish-help/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true readme = "../../README.md" [dependencies] -kaish-types = { path = "../kaish-types", version = "0.16.0" } +kaish-types = { path = "../kaish-types", version = "0.17.0" } [lints] workspace = true diff --git a/crates/kaish-kernel/Cargo.toml b/crates/kaish-kernel/Cargo.toml index 08592300..a19a188e 100644 --- a/crates/kaish-kernel/Cargo.toml +++ b/crates/kaish-kernel/Cargo.toml @@ -14,11 +14,11 @@ readme = "../../README.md" exclude = ["tests/snapshots/"] [dependencies] -kaish-glob = { path = "../kaish-glob", version = "0.16.0" } -kaish-types = { path = "../kaish-types", version = "0.16.0", features = [] } -kaish-help = { path = "../kaish-help", version = "0.16.0" } -kaish-tool-api = { path = "../kaish-tool-api", version = "0.16.0" } -kaish-vfs = { path = "../kaish-vfs", version = "0.16.0", features = ["memory", "overlay"] } +kaish-glob = { path = "../kaish-glob", version = "0.17.0" } +kaish-types = { path = "../kaish-types", version = "0.17.0", features = [] } +kaish-help = { path = "../kaish-help", version = "0.17.0" } +kaish-tool-api = { path = "../kaish-tool-api", version = "0.17.0" } +kaish-vfs = { path = "../kaish-vfs", version = "0.17.0", features = ["memory", "overlay"] } # Async runtime — minimal base; `localfs` adds tokio/fs, `subprocess` adds # tokio/process + tokio/rt-multi-thread (see [features]). @@ -90,7 +90,7 @@ jaq-json = { workspace = true } # Optional deps, each pulled in by its capability feature: # kaish-tools-host → host, trash/directories → os-integration, # tiktoken-rs → tokens. -kaish-tools-host = { path = "../kaish-tools-host", version = "0.16.0", optional = true } +kaish-tools-host = { path = "../kaish-tools-host", version = "0.17.0", optional = true } trash = { workspace = true, optional = true } directories = { workspace = true, optional = true } tiktoken-rs = { workspace = true, optional = true } diff --git a/crates/kaish-kernel/src/ast/plan.rs b/crates/kaish-kernel/src/ast/plan.rs index 18aad2d7..53eee712 100644 --- a/crates/kaish-kernel/src/ast/plan.rs +++ b/crates/kaish-kernel/src/ast/plan.rs @@ -82,7 +82,7 @@ pub struct PlannedStatement { /// gaps: `plans[i].index == i`, always. Indexing the list by this number /// reads the statement it names. pub index: usize, - /// What the statement was asked to run, with every credential redacted. + /// What the statement was asked to run. pub plan: Plan, } diff --git a/crates/kaish-kernel/src/tools/context.rs b/crates/kaish-kernel/src/tools/context.rs index 6a0252ac..03727021 100644 --- a/crates/kaish-kernel/src/tools/context.rs +++ b/crates/kaish-kernel/src/tools/context.rs @@ -125,6 +125,11 @@ pub(crate) enum ExternalCommandOutcome { /// /// Provides access to the backend (for file operations and tool dispatch), /// scope, and other kernel state. +/// +/// Build one with [`ExecContext::new`] or one of the `with_*` constructors and +/// set the fields you need. The struct is `#[non_exhaustive]`, so the kernel can +/// add a field without breaking an embedder that never asked for it. +#[non_exhaustive] pub struct ExecContext { /// Kernel backend for I/O operations. /// diff --git a/crates/kaish-repl/Cargo.toml b/crates/kaish-repl/Cargo.toml index c8f24938..c2770f9e 100644 --- a/crates/kaish-repl/Cargo.toml +++ b/crates/kaish-repl/Cargo.toml @@ -19,9 +19,9 @@ path = "src/main.rs" [dependencies] # The REPL is the full interactive human shell: it needs every capability # (external commands + job control, git, host introspection, tokens, trash). -kaish-kernel = { path = "../kaish-kernel", version = "0.16.0", features = ["full"] } -kaish-client = { path = "../kaish-client", version = "0.16.0" } -kaish-types = { path = "../kaish-types", version = "0.16.0" } +kaish-kernel = { path = "../kaish-kernel", version = "0.17.0", features = ["full"] } +kaish-client = { path = "../kaish-client", version = "0.17.0" } +kaish-types = { path = "../kaish-types", version = "0.17.0" } # Line editing rustyline = { workspace = true } diff --git a/crates/kaish-tool-api/Cargo.toml b/crates/kaish-tool-api/Cargo.toml index 2ca3c72b..9c7ed49e 100644 --- a/crates/kaish-tool-api/Cargo.toml +++ b/crates/kaish-tool-api/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true readme = "../../README.md" [dependencies] -kaish-types = { path = "../kaish-types", version = "0.16.0", features = [] } +kaish-types = { path = "../kaish-types", version = "0.17.0", features = [] } async-trait = { workspace = true } clap = { workspace = true } diff --git a/crates/kaish-tools-host/Cargo.toml b/crates/kaish-tools-host/Cargo.toml index 9b33c68d..dba7a3db 100644 --- a/crates/kaish-tools-host/Cargo.toml +++ b/crates/kaish-tools-host/Cargo.toml @@ -13,8 +13,8 @@ categories.workspace = true readme = "../../README.md" [dependencies] -kaish-types = { path = "../kaish-types", version = "0.16.0", features = [] } -kaish-tool-api = { path = "../kaish-tool-api", version = "0.16.0" } +kaish-types = { path = "../kaish-types", version = "0.17.0", features = [] } +kaish-tool-api = { path = "../kaish-tool-api", version = "0.17.0" } async-trait = { workspace = true } clap = { workspace = true } diff --git a/crates/kaish-vfs/Cargo.toml b/crates/kaish-vfs/Cargo.toml index 756c585b..73fa4519 100644 --- a/crates/kaish-vfs/Cargo.toml +++ b/crates/kaish-vfs/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true readme = "../../README.md" [dependencies] -kaish-types = { path = "../kaish-types", version = "0.16.0", features = [] } +kaish-types = { path = "../kaish-types", version = "0.17.0", features = [] } async-trait = { workspace = true } # DevFs's /dev/urandom pulls from the OS CSPRNG. Pure (no tokio); works on # Unix/macOS/WASI. diff --git a/docs/EMBEDDING.md b/docs/EMBEDDING.md index a445eaf5..9ea4e34e 100644 --- a/docs/EMBEDDING.md +++ b/docs/EMBEDDING.md @@ -6,7 +6,7 @@ and output capture. ## Stability -kaish is pre-1.0 (currently 0.16.x, MSRV 1.85). The language has settled; +kaish is pre-1.0 (currently 0.17.x, MSRV 1.85). The language has settled; the embedding API may still change between minor versions where it improves both kaish and its embedders — [kaijutsu](https://github.com/tobert/kaijutsu) is the reference embedder. Pin a minor version and read release notes when @@ -1172,7 +1172,7 @@ PY" "redirects":[{"kind":"<<","target":{"plain":"'PY'"}}],"background":false, "heredocs":[{"index":0,"delimiter":"PY","literal":true,"strip_tabs":false, "body":{"plain":"import os\n"},"body_offset":15}]}]}}], -"kaish_version":"0.16.0","kaish_git_hash":"b27ea4dd","kaish_build_date":"2026-08-23"} +"kaish_version":"0.17.0","kaish_git_hash":"4d02cd6b","kaish_build_date":"2026-08-31"} ``` **Nothing executes and no kernel is built** — planning is a pure function of the @@ -1182,7 +1182,7 @@ source text, so it touches no filesystem and needs no capability feature. The output is always a JSON object, so a caller parses one shape whatever happened: `{"statements": [...]}` and exit **0**, or `{"errors": [...]}` and exit **2** — the same usage code a builtin returns for bad argv. Both shapes -also carry `kaish_version` (bare semver, e.g. `"0.16.0"`), `kaish_git_hash` +also carry `kaish_version` (bare semver, e.g. `"0.17.0"`), `kaish_git_hash` (short hash, or `"unknown"` when kaish was built with no `.git` present — a crates.io tarball build, for instance), and `kaish_build_date` (`YYYY-MM-DD`) at the top level, so a caller windowing measurements by diff --git a/docs/LANGUAGE.md b/docs/LANGUAGE.md index f46898d1..cbb80baa 100644 --- a/docs/LANGUAGE.md +++ b/docs/LANGUAGE.md @@ -1057,9 +1057,10 @@ echo $((2#$bits)) # a string of binary digits ### Variables and expansions -A variable name may omit the `$`. An integer works directly; `true` is 1 and -`false` is 0; a string holding one number in any spelling above is read as -that number. +A variable name may omit the `$`. An integer works directly; a variable holding +`true` reads as 1 and `false` as 0; a string holding one number in any spelling +above is read as that number. A bare `true` is a variable name, not a literal — +`$(( true + 1 ))` names `true` as unset unless you set it. ```sh count=4