diff --git a/.github/workflows/cli-binaries.yml b/.github/workflows/cli-binaries.yml index ebbb9b606..ea72dd3a4 100644 --- a/.github/workflows/cli-binaries.yml +++ b/.github/workflows/cli-binaries.yml @@ -180,7 +180,7 @@ jobs: class Bashkit < Formula desc "Virtual bash interpreter with sandboxed execution" homepage "${{ github.server_url }}/${{ github.repository }}" - license "Apache-2.0" + license "MIT" on_macos do if Hardware::CPU.arm? @@ -254,8 +254,8 @@ jobs: cp bashkit.rb tap/Formula/bashkit.rb cd tap - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "Mykhailo Chalyi" + git config user.email "mike@chaliy.name" git add Formula/bashkit.rb git diff --cached --quiet && echo "No changes to commit" && exit 0 git commit -m "bashkit ${VERSION}" diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7250c3d..b324593cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,301 @@ # Changelog +## [0.19.0] - 2026-10-15 + +### Highlights + +- **Interactive terminals for applications and agents.** In-process terminals provide screen text, scrollback, input, interrupts, and editor/pager commands; Rust, Python, and JavaScript bindings expose the terminal alongside `TerminalTool` ([#2509](https://github.com/everruns/bashkit/pull/2509), [#2518](https://github.com/everruns/bashkit/pull/2518), [#2521](https://github.com/everruns/bashkit/pull/2521), [#2523](https://github.com/everruns/bashkit/pull/2523)). +- **Real CPython in the sandbox.** The opt-in `cpython` feature runs CPython 3.14 as `python3` through WebAssembly, with VFS access and HTTP routed through Bashkit's egress boundary. The existing lightweight Python runtime remains available ([#2515](https://github.com/everruns/bashkit/pull/2515), [#2568](https://github.com/everruns/bashkit/pull/2568)). +- **Broader shell and utility compatibility.** Concurrent pipelines and background jobs, a default root filesystem, symlink traversal, additional everyday commands, and GNU-oriented awk/coreutils behavior expand the scripts Bashkit can run ([#2524](https://github.com/everruns/bashkit/pull/2524), [#2525](https://github.com/everruns/bashkit/pull/2525), [#2526](https://github.com/everruns/bashkit/pull/2526), [#2527](https://github.com/everruns/bashkit/pull/2527), [#2541](https://github.com/everruns/bashkit/pull/2541), [#2583](https://github.com/everruns/bashkit/pull/2583)). +- **Resource and dependency hardening.** Expanded allocation, recursion, input, and output accounting makes resource refusals explicit across the interpreter and builtins; dependency updates address published advisories ([#2452](https://github.com/everruns/bashkit/pull/2452), [#2468](https://github.com/everruns/bashkit/pull/2468), [#2725](https://github.com/everruns/bashkit/pull/2725), [#2733](https://github.com/everruns/bashkit/pull/2733), [#2734](https://github.com/everruns/bashkit/pull/2734), [#2696](https://github.com/everruns/bashkit/pull/2696)). +- **Breaking change: ScriptedTool moves to its own package.** Rust callers replace `bashkit`'s `scripted_tool` feature and imports with `bashkit-scripted-tool = "0.19"` / `bashkit_scripted_tool`; replace `builder.tool_registry(registry)` with `registry.install(builder)`. Python imports move from `bashkit` to `bashkit.scripted`; JavaScript imports move from `@everruns/bashkit` to `@everruns/bashkit/scripted`. See the [migration guide](https://github.com/everruns/bashkit/blob/v0.19.0/docs/migrating-scripted-tool.md) ([#2519](https://github.com/everruns/bashkit/pull/2519), [#2520](https://github.com/everruns/bashkit/pull/2520)). + +### What's Changed + +* fix(hextools): report a missing operand the way real od does ([#2438](https://github.com/everruns/bashkit/pull/2438)) by @chaliy +* fix(parser): keep `}` inside a word unless it stands alone ([#2439](https://github.com/everruns/bashkit/pull/2439)) by @chaliy +* fix(builtins): report unreadable operands with the real errno text ([#2440](https://github.com/everruns/bashkit/pull/2440)) by @chaliy +* fix(grep): send read diagnostics to stderr and exit 2 ([#2441](https://github.com/everruns/bashkit/pull/2441)) by @chaliy +* fix(grep): align with GNU grep and gate the spec suite against it ([#2442](https://github.com/everruns/bashkit/pull/2442)) by @chaliy +* fix(interpreter): terminate command-resolution diagnostics with a newline ([#2443](https://github.com/everruns/bashkit/pull/2443)) by @chaliy +* fix(head,tail): support GNU count forms (negative, suffixes, tail -c) ([#2449](https://github.com/everruns/bashkit/pull/2449)) by @chaliy +* fix(interpreter): read file for $(file redirects ([#2546](https://github.com/everruns/bashkit/pull/2546)) by @chaliy +* fix(awk): newline continuation after control headers, regex operands ([#2554](https://github.com/everruns/bashkit/pull/2554)) by @chaliy +* fix(interpreter): bound nested bash/sh child shells ([#2549](https://github.com/everruns/bashkit/pull/2549)) by @chaliy +* feat(builtins): make, a GNU make subset run in the sandbox shell ([#2551](https://github.com/everruns/bashkit/pull/2551)) by @chaliy +* fix(builtins): option gaps from gap telemetry (tar, gzip -c, grep -I/-r paths, join -o, xargs -r) ([#2555](https://github.com/everruns/bashkit/pull/2555)) by @chaliy +* fix(parser): literal # mid-word and bash-style [[ =~ ]] regex operand ([#2553](https://github.com/everruns/bashkit/pull/2553)) by @chaliy +* feat(eval): gap telemetry from replayed agent sessions ([#2552](https://github.com/everruns/bashkit/pull/2552)) by @chaliy +* fix(parser): quotes and patterns inside ${...} operands ([#2556](https://github.com/everruns/bashkit/pull/2556)) by @chaliy +* fix(builtins): set -n stops execution, find -ls, tar strips leading / ([#2557](https://github.com/everruns/bashkit/pull/2557)) by @chaliy +* fix(arith): assign to array elements in ((...)), let and $((...)) ([#2558](https://github.com/everruns/bashkit/pull/2558)) by @chaliy +* fix(tar,ls): tar -P/-T/--, ls quoting styles (-b, -N, -Q, --quoting-style) ([#2559](https://github.com/everruns/bashkit/pull/2559)) by @chaliy +* feat(awk): run gawk, mawk and nawk as the awk builtin ([#2561](https://github.com/everruns/bashkit/pull/2561)) by @chaliy +* fix(builtins): usage errors fail the command, not the script ([#2560](https://github.com/everruns/bashkit/pull/2560)) by @chaliy +* fix(lexer): keep $'\x1e' and $'\x1f' bytes as data ([#2562](https://github.com/everruns/bashkit/pull/2562)) by @chaliy +* fix(parser): keep backslash-escaped $ literal in unquoted words ([#2563](https://github.com/everruns/bashkit/pull/2563)) by @chaliy +* fix(parser): keep quoted and escaped glob characters literal ([#2564](https://github.com/everruns/bashkit/pull/2564)) by @chaliy +* feat(cpython): HTTP from Python through bashkit egress ([#2568](https://github.com/everruns/bashkit/pull/2568)) by @chaliy +* fix(expansion): match ?, multi-star and lone * in ${x#pat} and ${x%pat} ([#2565](https://github.com/everruns/bashkit/pull/2565)) by @chaliy +* feat(cpython): own requests and httpx modules over the HTTP bridge ([#2572](https://github.com/everruns/bashkit/pull/2572)) by @chaliy +* feat(builtins): sudo and busybox run their command ([#2567](https://github.com/everruns/bashkit/pull/2567)) by @chaliy +* feat(cpython): xz snapshot, preload urllib.request and parsers, fix doctest ([#2573](https://github.com/everruns/bashkit/pull/2573)) by @chaliy +* feat(builtins): add pr ([#2571](https://github.com/everruns/bashkit/pull/2571)) by @chaliy +* fix(jq): keep object keys in input order ([#2577](https://github.com/everruns/bashkit/pull/2577)) by @chaliy +* fix(parser): case inside $( ) and [[ == [[:class:]] ]] patterns ([#2578](https://github.com/everruns/bashkit/pull/2578)) by @chaliy +* fix(interpreter): brace expansion before other expansions ([#2579](https://github.com/everruns/bashkit/pull/2579)) by @chaliy +* fix(expansion): pattern, case and transform operators on array elements ([#2576](https://github.com/everruns/bashkit/pull/2576)) by @chaliy +* feat(builtins): add fmt on the vendored uutils engine ([#2569](https://github.com/everruns/bashkit/pull/2569)) by @chaliy +* feat(interpreter): stream yes and seq into pipelines with SIGPIPE ([#2566](https://github.com/everruns/bashkit/pull/2566)) by @chaliy +* feat(rootfs): add /proc/self, /proc/1, /proc/uptime and /proc/mounts ([#2570](https://github.com/everruns/bashkit/pull/2570)) by @chaliy +* fix(builtins): shift status, ~+/~- tilde prefixes, shopt -o ([#2580](https://github.com/everruns/bashkit/pull/2580)) by @chaliy +* fix(parser): heredoc backslash escapes; read without -r ([#2581](https://github.com/everruns/bashkit/pull/2581)) by @chaliy +* feat(interpreter): stream cat through pipelines ([#2584](https://github.com/everruns/bashkit/pull/2584)) by @chaliy +* feat(cpython): drop low-use stdlib modules for agent scripts ([#2582](https://github.com/everruns/bashkit/pull/2582)) by @chaliy +* fix(network): classify NAT64/6to4 by embedded IPv4 and block reserved ranges ([#2574](https://github.com/everruns/bashkit/pull/2574)) by @chaliy +* test(spec): import 1,044 bash-verified cases from the BashBox test suite ([#2575](https://github.com/everruns/bashkit/pull/2575)) by @chaliy +* fix(interpreter): special params $0, $-, BASH_SUBSHELL, positional slices ([#2586](https://github.com/everruns/bashkit/pull/2586)) by @chaliy +* feat(awk): rewrite awk to match gawk 5.2 ([#2583](https://github.com/everruns/bashkit/pull/2583)) by @chaliy +* feat(builtins): stream grep and tr through pipelines ([#2587](https://github.com/everruns/bashkit/pull/2587)) by @chaliy +* fix(interpreter): set -e ignored contexts reach functions, subshells, $( ) ([#2590](https://github.com/everruns/bashkit/pull/2590)) by @chaliy +* fix(awk): visit for-in keys in gawk's storage order ([#2588](https://github.com/everruns/bashkit/pull/2588)) by @chaliy +* fix(parser): redirect and heredoc combos; function redirects stop streaming ([#2589](https://github.com/everruns/bashkit/pull/2589)) by @chaliy +* fix(builtins): getopts honors local OPTIND/OPTARG ([#2591](https://github.com/everruns/bashkit/pull/2591)) by @chaliy +* fix(jq): follow jq's input stream, per-file names and per-input errors ([#2592](https://github.com/everruns/bashkit/pull/2592)) by @chaliy +* fix(interpreter): exec >file redirects the shell's own output ([#2593](https://github.com/everruns/bashkit/pull/2593)) by @chaliy +* fix(jq): print numbers and value errors like jq ([#2595](https://github.com/everruns/bashkit/pull/2595)) by @chaliy +* fix(interpreter): expand array literals like command words ([#2594](https://github.com/everruns/bashkit/pull/2594)) by @chaliy +* fix(sed): GNU character escapes, -z joins, unreadable inputs and per-line loop budget ([#2600](https://github.com/everruns/bashkit/pull/2600)) by @chaliy +* fix(cli): exit 2 on syntax errors like bash ([#2598](https://github.com/everruns/bashkit/pull/2598)) by @chaliy +* fix(interpreter): eval syntax errors exit 2 with bash's line prefix ([#2596](https://github.com/everruns/bashkit/pull/2596)) by @chaliy +* fix(jq): null-path assignment, array growth, join, scan groups, from_entries and tostream like jq ([#2602](https://github.com/everruns/bashkit/pull/2602)) by @chaliy +* fix(interpreter): toggle case, prefix-name splitting, mapfile options ([#2601](https://github.com/everruns/bashkit/pull/2601)) by @chaliy +* feat(jq): halt/debug inside the command, -a/--raw-output0/--seq/--stream, $__loc__, ?// and date/base64 fixes ([#2606](https://github.com/everruns/bashkit/pull/2606)) by @chaliy +* fix(parser): [[ a < b ]] compares strings, not a redirection ([#2603](https://github.com/everruns/bashkit/pull/2603)) by @chaliy +* fix(builtins): GNU parity for sort, xargs, nl, fold, od, du and 20 more coreutils (166 BashBox cases) ([#2585](https://github.com/everruns/bashkit/pull/2585)) by @chaliy +* feat(parser): print function bodies like bash and close BashBox parser gaps (45 cases) ([#2599](https://github.com/everruns/bashkit/pull/2599)) by @chaliy +* fix(grep): match GNU grep 3.11 output, options and POSIX regex dialects ([#2609](https://github.com/everruns/bashkit/pull/2609)) by @chaliy +* fix(interpreter): arithmetic errors report and fail instead of giving 0 ([#2604](https://github.com/everruns/bashkit/pull/2604)) by @chaliy +* fix(parser): accept {arr[i]} as an fd-variable redirect ([#2605](https://github.com/everruns/bashkit/pull/2605)) by @chaliy +* test: Debian-oracle scoreboard in CI with per-tool floors (+ sed -f regression it caught) ([#2614](https://github.com/everruns/bashkit/pull/2614)) by @chaliy +* fix(declare): bash declaration engine, locals, attributes and set options (105 BashBox cases) ([#2597](https://github.com/everruns/bashkit/pull/2597)) by @chaliy +* test(spec): unset scope cases from the bash oracle ([#2607](https://github.com/everruns/bashkit/pull/2607)) by @chaliy +* test: bash-parity oracle scoreboard with recorded real-bash output ([#2618](https://github.com/everruns/bashkit/pull/2618)) by @chaliy +* fix(parser): {arr[i]} is an fd variable in every redirect position ([#2617](https://github.com/everruns/bashkit/pull/2617)) by @chaliy +* fix(builtins): return, unset -v and read reject bad arguments ([#2613](https://github.com/everruns/bashkit/pull/2613)) by @chaliy +* fix(interpreter): exec N>&- closes the descriptor for real ([#2610](https://github.com/everruns/bashkit/pull/2610)) by @chaliy +* fix(grep): bound recursive searches and skip directory cycles ([#2619](https://github.com/everruns/bashkit/pull/2619)) by @chaliy +* fix(jq): bound stream event traversal and allocation ([#2621](https://github.com/everruns/bashkit/pull/2621)) by @chaliy +* feat(interpreter): deliver signals the shell sends itself ([#2608](https://github.com/everruns/bashkit/pull/2608)) by @chaliy +* test(cpython): bashkit snapshot round-trips python3 output ([#2623](https://github.com/everruns/bashkit/pull/2623)) by @chaliy +* fix(interpreter): named file descriptors ({var}>file) and <> like bash ([#2624](https://github.com/everruns/bashkit/pull/2624)) by @chaliy +* fix(interpreter): redirection errors read like bash ([#2611](https://github.com/everruns/bashkit/pull/2611)) by @chaliy +* fix(od): bound row widths and budget output growth ([#2620](https://github.com/everruns/bashkit/pull/2620)) by @chaliy +* fix(builtins): xargs and empty pipes give stdin at EOF; printf %q keeps UTF-8 text ([#2628](https://github.com/everruns/bashkit/pull/2628)) by @chaliy +* fix(builtins): test and [ reject the operands bash rejects ([#2612](https://github.com/everruns/bashkit/pull/2612)) by @chaliy +* test(bash-oracle): drop cases that measure the host, not bash ([#2625](https://github.com/everruns/bashkit/pull/2625)) by @chaliy +* fix(interpreter): ${v@a} prints every attribute letter ([#2626](https://github.com/everruns/bashkit/pull/2626)) by @chaliy +* fix(interpreter): loop/return scoping, trap listings, [[ ]] operands and builtin parity from the BashBox corpus ([#2629](https://github.com/everruns/bashkit/pull/2629)) by @chaliy +* fix(interpreter): ${#v} counts bytes under the C locale ([#2627](https://github.com/everruns/bashkit/pull/2627)) by @chaliy +* fix(interpreter): exec inside a subshell redirects the subshell ([#2615](https://github.com/everruns/bashkit/pull/2615)) by @chaliy +* feat(interpreter): child shells count SHLVL like bash ([#2622](https://github.com/everruns/bashkit/pull/2622)) by @chaliy +* fix(interpreter): ERR/RETURN trap scoping and N>&word redirects like bash ([#2631](https://github.com/everruns/bashkit/pull/2631)) by @chaliy +* fix(interpreter): extglob, nocasematch, globstar, substrings and $'...' quoting like bash ([#2632](https://github.com/everruns/bashkit/pull/2632)) by @chaliy +* fix(interpreter): diagnostics carry bash's "$0: line N:" prefix ([#2630](https://github.com/everruns/bashkit/pull/2630)) by @chaliy +* fix(builtins): printf -v, readonly assignment, quoted $@ fields and */ globs like bash ([#2633](https://github.com/everruns/bashkit/pull/2633)) by @chaliy +* fix(interpreter): route builtin, command-not-found and $(...) stderr like bash ([#2634](https://github.com/everruns/bashkit/pull/2634)) by @chaliy +* fix(interpreter): match bash stderr for set -x, syntax errors and TIMEFORMAT ([#2635](https://github.com/everruns/bashkit/pull/2635)) by @chaliy +* fix(interpreter): model bash fork suppression for $SHLVL ([#2636](https://github.com/everruns/bashkit/pull/2636)) by @chaliy +* fix(jq): make join linear in the number of elements ([#2637](https://github.com/everruns/bashkit/pull/2637)) by @chaliy +* fix(interpreter): run coprocesses concurrently over in-process pipes ([#2638](https://github.com/everruns/bashkit/pull/2638)) by @chaliy +* fix(interpreter): process substitution expands to /dev/fd/63 ([#2639](https://github.com/everruns/bashkit/pull/2639)) by @chaliy +* test(oils-spec): measure upstream Oils spec pass rate against real bash ([#2640](https://github.com/everruns/bashkit/pull/2640)) by @chaliy +* fix(interpreter): close Oils word-split, globignore, dbracket, cd and alias gaps ([#2641](https://github.com/everruns/bashkit/pull/2641)) by @chaliy +* feat(eval): repo-shaped workflow tasks with CI-checked reference solutions ([#2642](https://github.com/everruns/bashkit/pull/2642)) by @chaliy +* fix(interpreter): close Oils tilde, patsub, brace, loop and paren-ambiguity gaps ([#2643](https://github.com/everruns/bashkit/pull/2643)) by @chaliy +* fix(interpreter): close Oils type/hash, redirect, here-doc, shell-option and special-variable gaps ([#2644](https://github.com/everruns/bashkit/pull/2644)) by @chaliy +* fix(interpreter): close Oils debug-stack, temp-env, NUL, regex and builtin gaps ([#2645](https://github.com/everruns/bashkit/pull/2645)) by @chaliy +* fix(interpreter): fatal expansion errors, quoted empty words, line shifts and ENOTDIR like bash ([#2646](https://github.com/everruns/bashkit/pull/2646)) by @chaliy +* fix(interpreter): arrays, indirect expansion and DEBUG traps like bash ([#2647](https://github.com/everruns/bashkit/pull/2647)) by @chaliy +* feat(interpreter): history, fc, completion, bind, prompts and bash -i like bash ([#2648](https://github.com/everruns/bashkit/pull/2648)) by @chaliy +* test(interpreter): streaming regression for exec 2>&1 routing per list element ([#2649](https://github.com/everruns/bashkit/pull/2649)) by @chaliy +* fix(interpreter): close Oils long-tail spec gaps ([#2650](https://github.com/everruns/bashkit/pull/2650)) by @chaliy +* fix(parser): expand aliases per line and close final Oils spec gaps ([#2652](https://github.com/everruns/bashkit/pull/2652)) by @chaliy +* fix(hash): budget retained command paths ([#2651](https://github.com/everruns/bashkit/pull/2651)) by @chaliy +* fix(cd): bound CDPATH memory and traversal work ([#2654](https://github.com/everruns/bashkit/pull/2654)) by @chaliy +* fix: close remaining BashBox corpus gaps ([#2657](https://github.com/everruns/bashkit/pull/2657)) by @chaliy +* fix(interpreter): budget function definition metadata ([#2655](https://github.com/everruns/bashkit/pull/2655)) by @chaliy +* feat(eval): refresh LLM evals on 2026-10 lineup, add bashkit_hard eval, wire results into site ([#2658](https://github.com/everruns/bashkit/pull/2658)) by @chaliy +* fix(sed): fall back to direct write when hidden temp file is refused ([#2659](https://github.com/everruns/bashkit/pull/2659)) by @chaliy +* fix(jq): ignore unread invalid stdin under -n ([#2660](https://github.com/everruns/bashkit/pull/2660)) by @chaliy +* feat(eval): one tagged 88-task dataset with runtime tasks, plus bashkit_generate eval ([#2664](https://github.com/everruns/bashkit/pull/2664)) by @chaliy +* fix(interpreter): bound script text echoed into arithmetic diagnostics ([#2665](https://github.com/everruns/bashkit/pull/2665)) by @chaliy +* test(cpython): budget the crash-containment case for its real cost ([#2666](https://github.com/everruns/bashkit/pull/2666)) by @chaliy +* test(fuzz): skip arithmetic inputs the diagnostic would echo back ([#2667](https://github.com/everruns/bashkit/pull/2667)) by @chaliy +* fix(date): accept HH:MM date-times and zone suffixes in -d ([#2662](https://github.com/everruns/bashkit/pull/2662)) by @xmakro +* fix(date): read weekday names in -d instead of returning now ([#2663](https://github.com/everruns/bashkit/pull/2663)) by @xmakro +* fix(arithmetic): bound nested dollar expansion ([#2668](https://github.com/everruns/bashkit/pull/2668)) by @chaliy +* test(security): lock in bounded rejection of bare-negation conditional print ([#2669](https://github.com/everruns/bashkit/pull/2669)) by @chaliy +* fix(interpreter): budget pending array expansions ([#2670](https://github.com/everruns/bashkit/pull/2670)) by @chaliy +* fix(interpreter): bound recursive prompt expansion ([#2671](https://github.com/everruns/bashkit/pull/2671)) by @chaliy +* fix(parser): read a backquoted substitution after a quoted segment ([#2661](https://github.com/everruns/bashkit/pull/2661)) by @xmakro +* fix(indirect): truncate array target at first ] to stop operand recursion ([#2673](https://github.com/everruns/bashkit/pull/2673)) by @chaliy +* fix(indirect): preserve nested subscripts with a complete-reference boundary ([#2674](https://github.com/everruns/bashkit/pull/2674)) by @chaliy +* test(indirect): enforce real bounds in the hostile-cycle harness ([#2676](https://github.com/everruns/bashkit/pull/2676)) by @chaliy +* fix(parser): bound syntax diagnostic construction ([#2675](https://github.com/everruns/bashkit/pull/2675)) by @chaliy +* fix(tests): route argv regression through bounded CLI harness ([#2677](https://github.com/everruns/bashkit/pull/2677)) by @chaliy +* fix(interp): charge tilde expansion to the execution budget ([#2672](https://github.com/everruns/bashkit/pull/2672)) by @chaliy +* test(parser): assert full bounds contract in multiline-ARG0 regression ([#2680](https://github.com/everruns/bashkit/pull/2680)) by @chaliy +* fix(interpreter): bound re-entrant signal-trap delivery depth ([#2679](https://github.com/everruns/bashkit/pull/2679)) by @chaliy +* fix(tests): fail-closed bounded probe launcher with honest lifecycle ([#2681](https://github.com/everruns/bashkit/pull/2681)) by @chaliy +* test(harness): fail-closed lifecycle for bounded probe runner ([#2682](https://github.com/everruns/bashkit/pull/2682)) by @chaliy +* fix(interpreter): bound diagnostic prefix amplification ([#2653](https://github.com/everruns/bashkit/pull/2653)) by @chaliy +* test(harness): bounded join gate, reap seam, launcher-bound selftests ([#2683](https://github.com/everruns/bashkit/pull/2683)) by @chaliy +* test(harness): join completion gate, fault seams, launcher-bound selftests ([#2684](https://github.com/everruns/bashkit/pull/2684)) by @chaliy +* test(harness): parent cleanup, join gate, fault seams ([#2685](https://github.com/everruns/bashkit/pull/2685)) by @chaliy +* test(harness): same-handle gate proof, live fault fixtures, launcher receipts ([#2686](https://github.com/everruns/bashkit/pull/2686)) by @chaliy +* test(harness): single descendant cleanup, strict absence proof ([#2687](https://github.com/everruns/bashkit/pull/2687)) by @chaliy +* fix(security): bound tar lists and nested substitution parsing ([#2678](https://github.com/everruns/bashkit/pull/2678)) by @chaliy +* docs: state dedicated trap-nesting cap alongside shared ceiling ([#2690](https://github.com/everruns/bashkit/pull/2690)) by @chaliy +* fix(tests): resolve the spawned CLI binary without guessing cargo layout ([#2691](https://github.com/everruns/bashkit/pull/2691)) by @chaliy +* fix(arithmetic): budget expanded array keys before allocation ([#2689](https://github.com/everruns/bashkit/pull/2689)) by @chaliy +* fix(awk): bound field operand recursion ([#2693](https://github.com/everruns/bashkit/pull/2693)) by @chaliy +* fix(fmt): budget amplified output before allocation ([#2692](https://github.com/everruns/bashkit/pull/2692)) by @chaliy +* fix(pr): bound pagination allocations before growth ([#2694](https://github.com/everruns/bashkit/pull/2694)) by @chaliy +* test: add test for parameter expansion with command substitution ${VAR:-$(cmd)} ([#2695](https://github.com/everruns/bashkit/pull/2695)) by @chaliy +* test(harness): reaped receipt, readiness-gated faults, ownership proofs ([#2688](https://github.com/everruns/bashkit/pull/2688)) by @chaliy +* fix(jq): bound object-key/index error previews with capped rendering ([#2697](https://github.com/everruns/bashkit/pull/2697)) by @chaliy +* fix(sed): budget pattern/hold growth against live-intermediate limit ([#2698](https://github.com/everruns/bashkit/pull/2698)) by @chaliy +* fix(jq): abort error-preview traversal at budget instead of walking hostile values ([#2699](https://github.com/everruns/bashkit/pull/2699)) by @chaliy +* test(jq): fail-closed bounded fixture for error-preview regressions ([#2700](https://github.com/everruns/bashkit/pull/2700)) by @chaliy +* fix(tests): cap bounded-child RSS instead of address space ([#2701](https://github.com/everruns/bashkit/pull/2701)) by @chaliy +* chore(eval): add GPT-6.1 Sol and GPT-6 Luna to the 88-task and generate results ([#2703](https://github.com/everruns/bashkit/pull/2703)) by @chaliy +* fix(deps): replace archived YAML parser with maintained fork ([#2704](https://github.com/everruns/bashkit/pull/2704)) by @chaliy +* fix(join): budget generated output and synchronous work ([#2713](https://github.com/everruns/bashkit/pull/2713)) by @chaliy +* fix(dd): admit padded buffers before allocation ([#2714](https://github.com/everruns/bashkit/pull/2714)) by @chaliy +* chore(deps): bump the docs-grep-agent-uv group in /examples/docs-grep-agent with 4 updates ([#2712](https://github.com/everruns/bashkit/pull/2712)) by @app/dependabot +* chore(deps): bump the js-npm group in /crates/bashkit-js with 5 updates ([#2709](https://github.com/everruns/bashkit/pull/2709)) by @app/dependabot +* chore(deps): bump the examples-npm group across 2 directories with 6 updates ([#2710](https://github.com/everruns/bashkit/pull/2710)) by @app/dependabot +* chore(deps): bump the site-npm group in /site with 4 updates ([#2711](https://github.com/everruns/bashkit/pull/2711)) by @app/dependabot +* chore(ci): bump the github-actions group with 5 updates ([#2705](https://github.com/everruns/bashkit/pull/2705)) by @app/dependabot +* fix(ci): unbreak main and nightly, and close the dependabot wasmtime gap ([#2715](https://github.com/everruns/bashkit/pull/2715)) by @chaliy +* chore(deps): bump turso_core from 0.8.2 to 0.8.3 ([#2717](https://github.com/everruns/bashkit/pull/2717)) by @chaliy +* fix(ci): pin cargo-geiger where the action actually reads the version ([#2719](https://github.com/everruns/bashkit/pull/2719)) by @chaliy +* chore(deps): bump the rust-dependencies group with 6 updates ([#2720](https://github.com/everruns/bashkit/pull/2720)) by @chaliy +* chore(deps): bump prettier from 3.9.9 to 3.9.10 in /crates/bashkit-js in the js-npm group ([#2716](https://github.com/everruns/bashkit/pull/2716)) by @app/dependabot +* chore(deps): bump zeroize from 1.9.0 to 1.9.1 in /crates/bashkit/fuzz in the fuzz-dependencies group ([#2718](https://github.com/everruns/bashkit/pull/2718)) by @app/dependabot +* fix(make): admit substitution before allocation ([#2721](https://github.com/everruns/bashkit/pull/2721)) by @chaliy +* fix(interpreter): admit concurrent pipeline forks ([#2722](https://github.com/everruns/bashkit/pull/2722)) by @chaliy +* fix(fs): admit standard-stream capture before growth ([#2723](https://github.com/everruns/bashkit/pull/2723)) by @chaliy +* feat(python): enable Python and TypeScript in BashTool ([#2724](https://github.com/everruns/bashkit/pull/2724)) by @chaliy +* fix(deps): clear the 2026-10-14 wasmtime and rustls advisories ([#2725](https://github.com/everruns/bashkit/pull/2725)) by @chaliy +* chore(deps): bump zod from 4.6.5 to 4.6.6 in /crates/bashkit-js in the js-npm group ([#2726](https://github.com/everruns/bashkit/pull/2726)) by @app/dependabot +* chore(deps): bump zod from 4.6.5 to 4.6.6 in /examples in the examples-npm group across 1 directory ([#2728](https://github.com/everruns/bashkit/pull/2728)) by @app/dependabot +* chore(deps): bump serde_json from 1.0.151 to 1.0.154 in /crates/bashkit/fuzz in the fuzz-dependencies group ([#2727](https://github.com/everruns/bashkit/pull/2727)) by @app/dependabot +* chore(ci): bump taiki-e/install-action from 2.87.27 to 2.87.28 in the github-actions group ([#2730](https://github.com/everruns/bashkit/pull/2730)) by @app/dependabot +* chore(deps): bump the rust-dependencies group, with the audits it needs ([#2731](https://github.com/everruns/bashkit/pull/2731)) by @chaliy +* perf(wasm): reduce browser transfer size and verify package identity ([#2732](https://github.com/everruns/bashkit/pull/2732)) by @chaliy +* fix(arrays): release mapfile budgets and surface refusals ([#2733](https://github.com/everruns/bashkit/pull/2733)) by @chaliy +* fix(parser): unify indirect subscript consumption ([#2734](https://github.com/everruns/bashkit/pull/2734)) by @chaliy +* fix(interpreter): charge input budget for numbered input redirects ([#2696](https://github.com/everruns/bashkit/pull/2696)) by @chaliy + +* fix(python): synchronize public package version with native and distribution metadata ([#2735](https://github.com/everruns/bashkit/pull/2735)) by @chaliy + +**Full Changelog**: https://github.com/everruns/bashkit/compare/v0.18.2...v0.19.0 + ## [0.18.2] - 2026-09-22 ### Highlights diff --git a/Cargo.lock b/Cargo.lock index e911dadfd..b7fa1cddb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,7 +357,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bashkit" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "async-trait", @@ -432,7 +432,7 @@ dependencies = [ [[package]] name = "bashkit-bench" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "bashkit", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "bashkit-capi" -version = "0.18.2" +version = "0.19.0" dependencies = [ "bashkit", "serde", @@ -457,7 +457,7 @@ dependencies = [ [[package]] name = "bashkit-cli" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "bashkit", @@ -471,7 +471,7 @@ dependencies = [ [[package]] name = "bashkit-coreutils-port" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "prettyplease", @@ -485,7 +485,7 @@ dependencies = [ [[package]] name = "bashkit-cpython-wasm" -version = "0.18.2" +version = "0.19.0" dependencies = [ "lzma-rs", "wasmtime", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "bashkit-eval" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "async-trait", @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "bashkit-js" -version = "0.18.2" +version = "0.19.0" dependencies = [ "bashkit", "bashkit-scripted-tool", @@ -524,7 +524,7 @@ dependencies = [ [[package]] name = "bashkit-python" -version = "0.18.2" +version = "0.19.0" dependencies = [ "bashkit", "bashkit-scripted-tool", @@ -537,7 +537,7 @@ dependencies = [ [[package]] name = "bashkit-scripted-tool" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "async-trait", @@ -553,7 +553,7 @@ dependencies = [ [[package]] name = "bashkit-wasm" -version = "0.18.2" +version = "0.19.0" dependencies = [ "bashkit", "console_error_panic_hook", diff --git a/Cargo.toml b/Cargo.toml index a93c54942..342941c2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" members = ["crates/*"] [workspace.package] -version = "0.18.2" +version = "0.19.0" edition = "2024" license = "MIT" authors = ["Mykhailo Chalyi ", "Everruns"] diff --git a/README.md b/README.md index d38b4b007..494a0b2c9 100644 --- a/README.md +++ b/README.md @@ -442,7 +442,7 @@ Enable the `sqlite` feature to embed [Turso](https://github.com/tursodatabase/tu ```toml [dependencies] -bashkit = { version = "0.18.2", features = ["sqlite"] } +bashkit = { version = "0.19.0", features = ["sqlite"] } ``` ```rust diff --git a/crates/bashkit-cli/Cargo.toml b/crates/bashkit-cli/Cargo.toml index 9bf09def8..370cde957 100644 --- a/crates/bashkit-cli/Cargo.toml +++ b/crates/bashkit-cli/Cargo.toml @@ -39,7 +39,7 @@ interactive = ["dep:rustyline", "dep:terminal_size", "dep:signal-hook"] # `tzdata` is listed explicitly: an interactive shell must keep honouring # named `TZ=` zones in `date`, which stopped being implied when chrono-tz # moved behind a feature. -bashkit = { path = "../bashkit", version = "0.18.2", default-features = false, features = ["http_client", "ring", "git", "jq", "tzdata"] } +bashkit = { path = "../bashkit", version = "0.19.0", default-features = false, features = ["http_client", "ring", "git", "jq", "tzdata"] } tokio = { workspace = true, features = ["macros", "net", "rt", "rt-multi-thread", "time"] } clap.workspace = true anyhow.workspace = true diff --git a/crates/bashkit-js/package.json b/crates/bashkit-js/package.json index d40f42578..45eca201c 100644 --- a/crates/bashkit-js/package.json +++ b/crates/bashkit-js/package.json @@ -1,6 +1,6 @@ { "name": "@everruns/bashkit", - "version": "0.18.2", + "version": "0.19.0", "description": "Sandboxed bash interpreter for JavaScript/TypeScript", "packageManager": "pnpm@10.33.0", "main": "wrapper.js", diff --git a/crates/bashkit-python/bashkit/__init__.py b/crates/bashkit-python/bashkit/__init__.py index ae4a0fd81..ddf1cb419 100644 --- a/crates/bashkit-python/bashkit/__init__.py +++ b/crates/bashkit-python/bashkit/__init__.py @@ -65,7 +65,7 @@ get_version, ) -__version__ = "0.1.2" +__version__ = get_version() __all__ = [ "AnalyzedCommand", "AnalyzedRedirect", diff --git a/crates/bashkit-python/tests/test_integration.py b/crates/bashkit-python/tests/test_integration.py index 85a80f954..67fc066b4 100644 --- a/crates/bashkit-python/tests/test_integration.py +++ b/crates/bashkit-python/tests/test_integration.py @@ -418,6 +418,15 @@ def test_bashtool_system_prompt_includes_username(): assert "agent007" in sp +def test_package_version_matches_native_and_distribution(): + """Every public version surface identifies the installed release.""" + from importlib.metadata import version + + import bashkit + + assert bashkit.__version__ == bashkit.get_version() == version("bashkit") + + def test_bashtool_version_format(): """Version string looks like a semver.""" tool = BashTool() diff --git a/crates/bashkit-scripted-tool/Cargo.toml b/crates/bashkit-scripted-tool/Cargo.toml index 58b5f19d2..eb1d3adfe 100644 --- a/crates/bashkit-scripted-tool/Cargo.toml +++ b/crates/bashkit-scripted-tool/Cargo.toml @@ -30,7 +30,7 @@ jq = ["bashkit/jq"] tracing = ["dep:tracing"] [dependencies] -bashkit = { path = "../bashkit", version = "0.18.2", default-features = false, features = ["bash_tool"] } +bashkit = { path = "../bashkit", version = "0.19.0", default-features = false, features = ["bash_tool"] } tokio = { workspace = true } async-trait = { workspace = true } tower = { workspace = true } diff --git a/crates/bashkit-scripted-tool/README.md b/crates/bashkit-scripted-tool/README.md index f3f413b5c..d21d1c751 100644 --- a/crates/bashkit-scripted-tool/README.md +++ b/crates/bashkit-scripted-tool/README.md @@ -8,8 +8,8 @@ with pipes, variables, loops and `jq`. ```toml [dependencies] -bashkit = "0.18" -bashkit-scripted-tool = { version = "0.18", features = ["jq"] } +bashkit = "0.19" +bashkit-scripted-tool = { version = "0.19", features = ["jq"] } ``` ```rust,ignore diff --git a/crates/bashkit-wasm/package.json b/crates/bashkit-wasm/package.json index 5cd08712b..92cf145c1 100644 --- a/crates/bashkit-wasm/package.json +++ b/crates/bashkit-wasm/package.json @@ -1,6 +1,6 @@ { "name": "@everruns/bashkit-wasm", - "version": "0.18.2", + "version": "0.19.0", "description": "Sandboxed bash interpreter for the browser (WebAssembly). Single-threaded — no SharedArrayBuffer, no COOP/COEP headers.", "type": "module", "main": "index.js", diff --git a/crates/bashkit/Cargo.toml b/crates/bashkit/Cargo.toml index a3a4b133a..b00b2981e 100644 --- a/crates/bashkit/Cargo.toml +++ b/crates/bashkit/Cargo.toml @@ -143,7 +143,7 @@ monty = { version = "=0.0.19", optional = true } # Real CPython 3.14 as a WASI guest, precompiled to Pulley (cpython feature). # See knowledge/runtimes/cpython-wasm.md. -bashkit-cpython-wasm = { path = "../bashkit-cpython-wasm", version = "0.18.2", optional = true } +bashkit-cpython-wasm = { path = "../bashkit-cpython-wasm", version = "0.19.0", optional = true } wasmtime = { workspace = true, optional = true, features = ["pooling-allocator"] } monty-types = { version = "=0.0.19", optional = true } # Monty's ruff AST uses compact_str 0.9; get-size2 >=0.10.2 only implements diff --git a/crates/bashkit/docs/cpython.md b/crates/bashkit/docs/cpython.md index e575bef14..a9ce9b7eb 100644 --- a/crates/bashkit/docs/cpython.md +++ b/crates/bashkit/docs/cpython.md @@ -19,7 +19,7 @@ Enable the `cpython` cargo feature and register the builtins: ```toml [dependencies] -bashkit = { version = "0.18.2", features = ["cpython"] } +bashkit = { version = "0.19.0", features = ["cpython"] } ``` ```rust @@ -242,7 +242,7 @@ executable memory. Enable `cpython-native` instead of `cpython` to compile it to machine code for your target at build time: ```toml -bashkit = { version = "0.18.2", features = ["cpython-native"] } +bashkit = { version = "0.19.0", features = ["cpython-native"] } ``` Calls get 4-10x faster (`print(1)` ~1 ms instead of ~4 ms; CPU-bound code diff --git a/crates/bashkit/docs/logging.md b/crates/bashkit/docs/logging.md index 7db282260..07e568823 100644 --- a/crates/bashkit/docs/logging.md +++ b/crates/bashkit/docs/logging.md @@ -14,7 +14,7 @@ Add the `logging` feature to your `Cargo.toml`: ```toml [dependencies] -bashkit = { version = "0.18.2", features = ["logging"] } +bashkit = { version = "0.19.0", features = ["logging"] } tracing-subscriber = "0.3" # or your preferred subscriber ``` diff --git a/crates/bashkit/docs/sqlite.md b/crates/bashkit/docs/sqlite.md index b4b31035e..3e195d3c9 100644 --- a/crates/bashkit/docs/sqlite.md +++ b/crates/bashkit/docs/sqlite.md @@ -13,7 +13,7 @@ the `sqlite` feature. The engine is [Turso](https://github.com/tursodatabase/tur ```toml # Cargo.toml -bashkit = { version = "0.18.2", features = ["sqlite"] } +bashkit = { version = "0.19.0", features = ["sqlite"] } ``` ```rust,ignore diff --git a/crates/bashkit/fuzz/Cargo.lock b/crates/bashkit/fuzz/Cargo.lock index af6e58513..d8bb7699a 100644 --- a/crates/bashkit/fuzz/Cargo.lock +++ b/crates/bashkit/fuzz/Cargo.lock @@ -90,7 +90,7 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bashkit" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "async-trait", @@ -140,7 +140,7 @@ dependencies = [ [[package]] name = "bashkit-cpython-wasm" -version = "0.18.2" +version = "0.19.0" dependencies = [ "lzma-rs", "wasmtime", diff --git a/crates/bashkit/src/lib.rs b/crates/bashkit/src/lib.rs index 37bb85093..e7c7cf243 100644 --- a/crates/bashkit/src/lib.rs +++ b/crates/bashkit/src/lib.rs @@ -323,7 +323,7 @@ //! //! ```toml //! [dependencies] -//! bashkit = { version = "0.18.2", features = ["git"] } +//! bashkit = { version = "0.19.0", features = ["git"] } //! ``` //! //! ```rust,ignore @@ -354,7 +354,7 @@ //! //! ```toml //! [dependencies] -//! bashkit = { version = "0.18.2", features = ["python"] } +//! bashkit = { version = "0.19.0", features = ["python"] } //! ``` //! //! ```rust,ignore diff --git a/docs/builtin_typescript.md b/docs/builtin_typescript.md index 8e1669089..e00070909 100644 --- a/docs/builtin_typescript.md +++ b/docs/builtin_typescript.md @@ -12,7 +12,7 @@ Add the `typescript` feature to your `Cargo.toml`: ```toml [dependencies] -bashkit = { version = "0.18.2", features = ["typescript"] } +bashkit = { version = "0.19.0", features = ["typescript"] } ``` Enable TypeScript in the builder: diff --git a/docs/migrating-scripted-tool.md b/docs/migrating-scripted-tool.md index d6d566c20..82fbd16e2 100644 --- a/docs/migrating-scripted-tool.md +++ b/docs/migrating-scripted-tool.md @@ -15,14 +15,16 @@ types, same builders, same logic-only shell, same security defaults. Before: ```toml -bashkit = { version = "0.18.2", features = ["scripted_tool", "python", "typescript", "jq"] } +[dependencies.bashkit] +version = "0.18.2" +features = ["scripted_tool", "python", "typescript", "jq"] ``` After: ```toml -bashkit = { version = "0.18.2", features = ["python", "typescript", "jq"] } -bashkit-scripted-tool = { version = "0.18.2", features = ["python", "typescript", "jq"] } +bashkit = { version = "0.19.0", features = ["python", "typescript", "jq"] } +bashkit-scripted-tool = { version = "0.19.0", features = ["python", "typescript", "jq"] } ``` Keep `bashkit` only if you use it directly (`Bash`, `BashTool`, the `Tool` diff --git a/examples/hyperlight/Cargo.lock b/examples/hyperlight/Cargo.lock index c9e809751..943487c13 100644 --- a/examples/hyperlight/Cargo.lock +++ b/examples/hyperlight/Cargo.lock @@ -69,7 +69,7 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bashkit" -version = "0.18.2" +version = "0.19.0" dependencies = [ "anyhow", "async-trait", diff --git a/justfile b/justfile index 4d1b242f8..5e8ec5e58 100644 --- a/justfile +++ b/justfile @@ -427,65 +427,12 @@ release-check: # Check nightly CI jobs (last 3 runs must be green) just check-nightly - # Monty is a registry dependency, so verify the exact package that CI - # publishes. Rewriting manifests here can hide real packaging failures. + # Cargo stages selected workspace packages in a temporary registry, so + # verification builds the exact new graph before any internal crate is live. echo "" - echo "Dry-run publish bashkit..." - cargo publish -p bashkit --dry-run --allow-dirty - - echo "" - echo "Dry-run publish bashkit-cli..." - # bashkit-cli verifies against the registry package of bashkit. - # Before the matching bashkit release is published, local dry-run cannot - # resolve that package. Package a disposable workspace against the latest - # published core as a structural proxy. The real workflow publishes and - # verifies the matching core before publishing the CLI. - CLI_VERIFY_ROOT=$(mktemp -d) - trap 'rm -rf "$CLI_VERIFY_ROOT"' EXIT - # tar, not rsync: rsync is absent from some cloud release sandboxes. - mkdir -p "$CLI_VERIFY_ROOT/workspace" - tar -c --exclude .git --exclude node_modules --exclude target . \ - | tar -x -C "$CLI_VERIFY_ROOT/workspace" - LATEST_CORE=$(cargo search bashkit --limit 1 | sed -n 's/^bashkit = "\([^"]*\)".*/\1/p') - if [ -z "$LATEST_CORE" ]; then - echo "Error: could not determine latest published bashkit version" - exit 1 - fi - WORKSPACE_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') - CLI_TOML="$CLI_VERIFY_ROOT/workspace/crates/bashkit-cli/Cargo.toml" - sed -i.bak \ - "s/version = \"$WORKSPACE_VERSION\"/version = \"$LATEST_CORE\"/" \ - "$CLI_TOML" - rm "$CLI_TOML.bak" - # The proxy resolves the core from crates.io, so it can only request - # features the published core already has. Features added in this release - # cycle (and Python, which the published core predates) are dropped in the - # disposable copy only; the exact new core package dry-run above validates - # them. Failing here means real packaging drift, not a missing feature. - python3 scripts/cli_publish_proxy.py "$CLI_TOML" "$LATEST_CORE" - cargo publish \ - --manifest-path "$CLI_VERIFY_ROOT/workspace/Cargo.toml" \ - -p bashkit-cli \ - --dry-run \ - --allow-dirty \ - --no-verify - - echo "" - echo "Dry-run publish bashkit-scripted-tool..." - # Same structural proxy as the CLI: point the copy's core pin at the latest - # published bashkit. Every core feature it forwards (bash_tool, jq, python, - # typescript) is already on crates.io. - ST_TOML="$CLI_VERIFY_ROOT/workspace/crates/bashkit-scripted-tool/Cargo.toml" - sed -i.bak \ - "s/version = \"$WORKSPACE_VERSION\"/version = \"$LATEST_CORE\"/" \ - "$ST_TOML" - rm "$ST_TOML.bak" - cargo publish \ - --manifest-path "$CLI_VERIFY_ROOT/workspace/Cargo.toml" \ - -p bashkit-scripted-tool \ - --dry-run \ - --allow-dirty \ - --no-verify + echo "Dry-run publish all release crates..." + cargo publish --dry-run --allow-dirty \ + -p bashkit-cpython-wasm -p bashkit -p bashkit-cli -p bashkit-scripted-tool echo "" echo "All release checks passed!" diff --git a/knowledge/log.md b/knowledge/log.md index 1d7f0f178..a30649fe1 100644 --- a/knowledge/log.md +++ b/knowledge/log.md @@ -2,6 +2,12 @@ ## 2026-10-15 +* **Release metadata**: Homebrew's generated formula uses the workspace MIT license and the required human commit identity. Regression coverage checks both before the automated tap push. See [Release Process](operations/release-process.md). + +* **Python packaging**: `bashkit.__version__` now reads the native `get_version()` instead of a stale literal. An installed-package regression verifies equality with both the native version and distribution metadata. See [Python Package](runtimes/python-package.md). + +* **Release operations**: Preparing 0.19.0 introduces the first published `bashkit-cpython-wasm` and `bashkit-scripted-tool` packages. A standalone core publish dry-run fails because its CPython dependency is not yet on crates.io. The pinned Cargo supports a multi-package dry-run that stages all four exact archives in a temporary registry and verifies their builds together. `just release-check` now uses that graph instead of rewriting disposable manifests against an older core; its regression checks require all four packages and compilation verification. See [Release Process](operations/release-process.md). + * **Operations / Security**: The `rust-dependencies` group bump (proptest 1.11.0 -> 1.12.0, serde_json 1.0.151 -> 1.0.154, syn 3.0.7 -> 3.0.8, and rand_xorshift 0.4.0 -> 0.5.0 behind proptest) was green on every job except Audit, where `cargo vet --locked` reported eleven unvetted crates. proptest 1.12 is the reason: it pulls a borsh serialization chain into the dev tree, adding `bit-set`, `bit-vec`, `borsh`, `borsh-derive`, `core_detect`, `proc-macro-crate` and `toml_edit` where a patch-level dev bump looks like it should add nothing. `cargo tree -e normal -i proptest` finds no path, so the whole chain is dev-only and takes `safe-to-run` exemptions, matching how proptest and rand_xorshift were already carried; the two shipped crates took hand delta audits at `safe-to-deploy`. serde_json's delta is narrowing (`-0` keeps its sign under arbitrary_precision, `as_f32` filters non-finite, invalid UTF-8 is reported at the first bad byte) and syn's only rejects `<-` and stops mis-parsing `->` as subtraction. The one thing worth remembering about the serde_json audit: its new UTF-8 position arithmetic saturates in `as_str`, which this tree compiles, but not in `begin_raw_buffering`, which needs the `raw_value` feature this tree does not enable - so enabling `raw_value` later means re-checking that subtraction. Stale `proptest` 1.11.0 and `rand_xorshift` 0.4.0 exemptions were dropped rather than left behind. The general rule, same as #2706/#2720: a dependabot group bump that adds crates cannot go green on its own, because nothing in the bump records the audits the new crates need. * **Security / Operations**: Nightly run 263 went red on the Advisory Audit with 15 vulnerabilities in the root and fuzz lockfiles and 11 in `examples/hyperlight/host` — none from a tree that changed. Two upstream batches landed on 2026-10-14: eleven `wasmtime` advisories against the pinned 48.0.5 (RUSTSEC-2026-0353 through -0359 and -0361 through -0364: host panics reachable from guest code, an unaligned-host-data bug, and an unmetered component-model handle table) and four `rustls` advisories against 0.23.45 (RUSTSEC-2026-0346 through -0349: `Debug` impls exposing TLS secrets, buffered application data sent to the outer server after ECH rejection, pre-authentication Brotli certificate decompression allocating up to 1 GiB, and an unauthenticated `ServerHello` downgrading later connections to classical key exchange). The wasmtime pin moves `=48.0.5` -> `=48.0.6`, which is the fix release on the 48 line and keeps MSRV at 1.95.0, matching `rust-toolchain.toml`; 49.0.3 also carries the fixes but needs rustc 1.96, so the reason the pin exists is unchanged and the dependabot `ignore` entries stay. `rustls` is a caret dependency and moves 0.23.45 -> 0.23.46. All three cargo lockfiles are updated together, since the fuzz lockfile resolves the root workspace and `examples/hyperlight/host` carries its own. No guest rebuild is needed for the wasmtime bump: `build.rs` recompiles the `.cwasm`, per [cpython-wasm](runtimes/cpython-wasm.md). The general point: an exact pin held for an MSRV reason still has to move on the patch line when that line is the advisory fix — the pin is not a reason to stay vulnerable. diff --git a/knowledge/operations/release-process.md b/knowledge/operations/release-process.md index 75a93e510..81383886e 100644 --- a/knowledge/operations/release-process.md +++ b/knowledge/operations/release-process.md @@ -85,24 +85,18 @@ silently failed. snapshot as xz; ~7.8 MiB with requests/httpx and urllib.request preloaded). `bashkit` depends on the same version of it, so `publish.yml` publishes it first and the `bashkit` dry-run only resolves once that version is live. - - `cargo publish --dry-run -p bashkit` must succeed. Package - `bashkit-cli` and `bashkit-scripted-tool` in a disposable copy against the latest published - registry core version (remove the local path in the copy) as a structural - proxy; Cargo cannot resolve the CLI's new - registry dependency until the core crate is live. The proxy may only ask - the published core for features it already exposes, so - `scripts/cli_publish_proxy.py` reads that crate's feature set from - crates.io and drops the ones added in the current cycle (plus `python`, - which predates Monty's crates.io publication) from the disposable copy - only. Direct CLI-to-core feature aliases are checked independently of the - features requested on the core dependency, because default CLI features - need not appear in that dependency list. A failure there is real packaging - drift, not a missing feature. Normal workspace checks still compile the CLI - against the new local core, and `publish.yml` waits for the core registry - version before publishing the CLI. Packaging caught - the v0.4.0 → v0.4.1 incident: the rustdoc guide lived outside the crate - dir, so `cargo publish` couldn't find it; local `cargo build` did not catch - it. + - Verify all four exact release packages together with the pinned Cargo: + `cargo publish --dry-run -p bashkit-cpython-wasm -p bashkit -p bashkit-cli -p bashkit-scripted-tool` + (`--allow-dirty` during preparation). Cargo stages selected workspace + packages in a temporary registry and builds the packaged dependency graph, + including new internal crates that do not exist on crates.io yet. A lone + core dry-run cannot resolve the unpublished CPython dependency; a proxy + against an older published core would miss the new package graph. No + manifest rewriting or `--no-verify` is needed. `just release-check` runs + this exact preflight. Individual dry-runs in `publish.yml` still follow + dependency order after each prerequisite reaches crates.io. Packaging + caught the v0.4.0 → v0.4.1 incident: a rustdoc guide outside the crate + directory built locally but was absent from the archive. - PyPI / npm: confirm the feeding pipelines still build (`maturin build --release`, `napi build`, browser WASM build smokes) when changes touch packaging. @@ -253,7 +247,9 @@ Dispatched by release.yml after tag verification. Accepts only stable tag, and the tag commit is reachable from `origin/main` before building. Builds prebuilt CLI binaries (macOS aarch64/x86_64, Linux x86_64), uploads to the Release, and pushes a Homebrew formula to `everruns/homebrew-tap` -(`brew install everruns/tap/bashkit`). Secret: `DOPPLER_TOKEN` +(`brew install everruns/tap/bashkit`). Formula license matches the workspace +license; generated tap commits use the repository's configured human release +identity, never a bot identity. Secret: `DOPPLER_TOKEN` (Doppler-managed GitHub PAT for the tap push). ### publish.yml diff --git a/knowledge/runtimes/python-package.md b/knowledge/runtimes/python-package.md index c1d888eb3..9aa44e4a4 100644 --- a/knowledge/runtimes/python-package.md +++ b/knowledge/runtimes/python-package.md @@ -39,6 +39,10 @@ re-exports, `_bashkit.pyi` PEP 561 type stubs + `py.typed`, `langchain.py` / Python package version is read dynamically from workspace `Cargo.toml` via maturin (`dynamic = ["version"]` in pyproject.toml), no manual sync. Chain: workspace `Cargo.toml` → `bashkit-python` `Cargo.toml` (inherits) → maturin → wheel metadata. +`bashkit.__version__` delegates to the extension's `get_version()` so imported +package, native API, and distribution metadata identify the same release. +`tests/test_integration.py::test_package_version_matches_native_and_distribution` +checks all three against an installed wheel. ## Supported Platforms diff --git a/scripts/cli_publish_proxy.py b/scripts/cli_publish_proxy.py deleted file mode 100755 index 52655f428..000000000 --- a/scripts/cli_publish_proxy.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -"""Rewrite a disposable bashkit-cli manifest so it packages against the -published core crate. - -`cargo publish --dry-run -p bashkit-cli` cannot resolve the core version being -released, because it is not on crates.io yet. `just release-check` therefore -packages a throwaway copy of the workspace against the latest *published* core -as a structural proxy. That copy may only request features the published core -already exposes, so this script drops the local path dependency and any feature -the published core lacks (features added in the current cycle, plus `python`, -which predates Monty's crates.io publication). The exact new core package is -validated by its own dry-run, so nothing here weakens the release gate. -""" - -from __future__ import annotations - -import json -import re -import sys -import urllib.request - -API = "https://crates.io/api/v1/crates/bashkit/{version}" -UA = "bashkit-release-check (https://github.com/everruns/bashkit)" -DEPENDENCY = re.compile(r"^bashkit = \{[^}\n]*\}$", re.MULTILINE) -FEATURES = re.compile(r"features = \[([^\]]*)\]") -FORWARDED_FEATURE = re.compile( - r'^([A-Za-z0-9_-]+) = \["bashkit/([A-Za-z0-9_-]+)"\]$', re.MULTILINE -) - - -class ManifestError(Exception): - """The copied manifest does not have the shape the proxy can rewrite.""" - - -def published_features(version: str) -> set[str]: - request = urllib.request.Request(API.format(version=version), headers={"User-Agent": UA}) - with urllib.request.urlopen(request, timeout=30) as response: - payload = json.load(response) - return set(payload["version"]["features"]) - - -def rewrite(manifest: str, available: set[str]) -> tuple[str, list[str], list[str]]: - """Return the rewritten manifest plus the kept and dropped feature names.""" - dependency = DEPENDENCY.search(manifest) - if dependency is None: - raise ManifestError("bashkit dependency line not found") - line = dependency.group(0) - requested = FEATURES.search(line) - if requested is None: - raise ManifestError("bashkit dependency has no feature list") - - names = [name.strip().strip('"') for name in requested.group(1).split(",") if name.strip()] - kept = [name for name in names if name in available] - dropped = [name for name in names if name not in available] - - # CLI features need not also appear on the dependency's feature list. - # Filter direct forwarding aliases independently so defaults cannot retain - # a reference to a feature absent from the published core. - forwarded_dropped = [ - cli_name - for cli_name, core_name in FORWARDED_FEATURE.findall(manifest) - if core_name not in available - ] - dropped.extend(name for name in forwarded_dropped if name not in dropped) - - line = ( - line[: requested.start()] - + "features = [%s]" % ", ".join('"%s"' % name for name in kept) - + line[requested.end() :] - ) - line = line.replace('path = "../bashkit", ', "") - manifest = manifest[: dependency.start()] + line + manifest[dependency.end() :] - - # Drop CLI features that forward to a core feature the published core lacks. - for name in dropped: - manifest = re.sub( - r'^%s = \["bashkit/%s"\]\n' % (re.escape(name), re.escape(name)), - "", - manifest, - flags=re.MULTILINE, - ) - manifest = manifest.replace('"%s", ' % name, "").replace(', "%s"' % name, "") - - return manifest, kept, dropped - - -def main(argv: list[str]) -> int: - manifest_path, core_version = argv[1], argv[2] - with open(manifest_path) as handle: - manifest = handle.read() - try: - rewritten, kept, dropped = rewrite(manifest, published_features(core_version)) - except ManifestError as error: - print("error: %s" % error, file=sys.stderr) - return 1 - with open(manifest_path, "w") as handle: - handle.write(rewritten) - print( - "proxy core %s: kept %s; dropped %s" - % (core_version, ", ".join(kept) or "(none)", ", ".join(dropped) or "(none)") - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main(sys.argv)) diff --git a/scripts/tests/test_cli_publish_proxy.py b/scripts/tests/test_cli_publish_proxy.py deleted file mode 100644 index 78509fe75..000000000 --- a/scripts/tests/test_cli_publish_proxy.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Lock the release-check CLI packaging proxy to the published core's features.""" - -from __future__ import annotations - -import pathlib -import sys -import unittest - -ROOT = pathlib.Path(__file__).resolve().parents[2] -sys.path.insert(0, str(ROOT / "scripts")) - -from cli_publish_proxy import ManifestError, rewrite # noqa: E402 - -MANIFEST = """\ -[package] -name = "bashkit-cli" - -[features] -default = ["http_client", "python", "tzdata"] -python = ["bashkit/python"] -tzdata = ["bashkit/tzdata"] - -[dependencies] -bashkit = { path = "../bashkit", version = "0.17.0", default-features = false, features = ["http_client", "ring", "git", "tzdata"] } -""" - - -class CliPublishProxyTests(unittest.TestCase): - def test_drops_features_the_published_core_lacks(self) -> None: - rewritten, kept, dropped = rewrite(MANIFEST, {"http_client", "git"}) - self.assertEqual(kept, ["http_client", "git"]) - self.assertEqual(dropped, ["ring", "tzdata", "python"]) - self.assertIn('features = ["http_client", "git"]', rewritten) - self.assertNotIn('path = "../bashkit"', rewritten) - self.assertNotIn('python = ["bashkit/python"]', rewritten) - self.assertNotIn('tzdata = ["bashkit/tzdata"]', rewritten) - self.assertIn('default = ["http_client"]', rewritten) - - def test_keeps_everything_when_core_has_all_features(self) -> None: - available = {"http_client", "ring", "git", "python", "tzdata"} - rewritten, kept, dropped = rewrite(MANIFEST, available) - self.assertEqual(dropped, []) - self.assertIn( - 'features = ["http_client", "ring", "git", "tzdata"]', rewritten - ) - self.assertIn('python = ["bashkit/python"]', rewritten) - - def test_rejects_a_manifest_without_the_dependency(self) -> None: - with self.assertRaises(ManifestError): - rewrite("[package]\nname = \"bashkit-cli\"\n", {"git"}) - - def test_rejects_a_dependency_without_features(self) -> None: - manifest = '[dependencies]\nbashkit = { path = "../bashkit", version = "0.17.0" }\n' - with self.assertRaises(ManifestError): - rewrite(manifest, {"git"}) - - -if __name__ == "__main__": - unittest.main() diff --git a/scripts/tests/test_release_workflow.py b/scripts/tests/test_release_workflow.py index c2b80a3dd..97150835a 100644 --- a/scripts/tests/test_release_workflow.py +++ b/scripts/tests/test_release_workflow.py @@ -130,15 +130,28 @@ def test_npm_publish_verification_outlasts_the_registry_cache(self) -> None: self.assertIn("npm view @everruns/bashkit", step) self.assertIn("exit 1", step) - def test_cli_publish_proxy_uses_the_published_core(self) -> None: + def test_release_check_verifies_the_exact_unpublished_package_graph(self) -> None: justfile = (ROOT / "justfile").read_text() - - # The proxy manifest rewrite lives in scripts/cli_publish_proxy.py so it - # can be unit tested; release-check must still call it. - self.assertIn( - 'python3 scripts/cli_publish_proxy.py "$CLI_TOML" "$LATEST_CORE"', justfile - ) - self.assertTrue((ROOT / "scripts/cli_publish_proxy.py").is_file()) + recipe = justfile.split("\nrelease-check:\n", 1)[1].split("\n# Create and push", 1)[0] + + # New internal dependencies are absent from crates.io until publication. + # Cargo stages them together only when every release package is selected. + for package in ("bashkit-cpython-wasm", "bashkit", "bashkit-cli", "bashkit-scripted-tool"): + self.assertRegex(recipe, rf"-p {package}(?:\s|$)") + self.assertIn("--dry-run", recipe) + self.assertNotIn("--no-verify", recipe) + self.assertNotIn("LATEST_CORE", recipe) + self.assertNotIn("cli_publish_proxy", recipe) + + def test_homebrew_release_metadata_matches_repository_policy(self) -> None: + workflow = (ROOT / ".github/workflows/cli-binaries.yml").read_text() + + self.assertIn('git config user.name "Mykhailo Chalyi"', workflow) + self.assertIn('git config user.email "mike@chaliy.name"', workflow) + self.assertNotIn("github-actions[bot]", workflow) + license_match = re.search(r'^license = "([^"]+)"', (ROOT / "Cargo.toml").read_text(), re.MULTILINE) + self.assertIsNotNone(license_match) + self.assertIn(f'license "{license_match.group(1)}"', workflow) def test_web_ci_exercises_release_wasm_optimization(self) -> None: build_script = (ROOT / "crates/bashkit-wasm/scripts/build.sh").read_text() diff --git a/site/src/content/apidocs/typescript.md b/site/src/content/apidocs/typescript.md index 6ea4c140e..91bea5b86 100644 --- a/site/src/content/apidocs/typescript.md +++ b/site/src/content/apidocs/typescript.md @@ -226,6 +226,14 @@ bash.ls(path?: string): string[] List entry names in a directory. Returns empty array if directory does not exist. +### `lstat` + +```typescript +bash.lstat(path: string): { created: number; fileType: string; mode: number; modified: number; size: number } +``` + +Like `stat`, but a symlink reports itself (`fileType: "symlink"`). + ### `mkdir` ```typescript @@ -385,7 +393,7 @@ Use for snapshots crossing trust boundaries. bash.stat(path: string): { created: number; fileType: string; mode: number; modified: number; size: number } ``` -Get metadata for a path (fileType, size, mode, timestamps). +Get metadata for a path (fileType, size, mode, timestamps), following symlinks. ### `symlink` @@ -660,6 +668,14 @@ bashTool.ls(path?: string): string[] List entry names in a directory. Returns empty array if directory does not exist. +### `lstat` + +```typescript +bashTool.lstat(path: string): { created: number; fileType: string; mode: number; modified: number; size: number } +``` + +Like `stat`, but a symlink reports itself (`fileType: "symlink"`). + ### `mkdir` ```typescript @@ -800,7 +816,7 @@ Serialize interpreter state with HMAC-SHA256 using a caller-provided key. bashTool.stat(path: string): { created: number; fileType: string; mode: number; modified: number; size: number } ``` -Get metadata for a path (fileType, size, mode, timestamps). +Get metadata for a path (fileType, size, mode, timestamps), following symlinks. ### `symlink` @@ -894,6 +910,12 @@ fileSystem.copy(fromPath: string, toPath: string): void fileSystem.exists(path: string): boolean ``` +### `lstat` + +```typescript +fileSystem.lstat(path: string): { created: number; fileType: string; mode: number; modified: number; size: number } +``` + ### `mkdir` ```typescript @@ -993,6 +1015,164 @@ new BashError(result: ExecResult): BashError bashError.display(): string ``` +## Terminal + +Interactive bash session on an in-memory terminal: type keys, run until +the shell needs input, read the screen as text. `vi`, `less` and `more` +work. + +```typescript +const t = new Terminal({ rows: 24, cols: 80 }); +await t.call("vi notes.txt"); +const out = await t.call("ihello:wq"); +out.commands[0].exit_code; // 0 +``` + +### Constructor + +```typescript +new Terminal(options?: TerminalOptions): Terminal +``` + +### `exitCode` + +```typescript +exitCode: null | number +``` + +Shell exit code once the session has exited. + +### `activity` + +```typescript +terminal.activity(): TerminalActivityInfo +``` + +What the session is doing. + +### `call` + +```typescript +terminal.call(input: string | TerminalCallArgs, waitMs?: number): Promise +``` + +Agent step: type `input` in Vim key notation (`"ls"`, +`"ihi:wq"`, `""`), wait up to `waitMs` (default 5000), +and report the screen, activity and finished commands. Pass an object +to forward a model's tool call as is (`wait_for`, `screen`, ...). + +### `cursor` + +```typescript +terminal.cursor(): [number, number] +``` + +Cursor position `[row, col]`, zero-based. + +### `historyText` + +```typescript +terminal.historyText(): string +``` + +Screen plus up to 1000 lines of scrollback. + +### `inputPrompt` + +```typescript +terminal.inputPrompt(): null | string +``` + +While `read`/`select` waits for a typed line, the question on the cursor line; else null. + +### `isAlternateScreen` + +```typescript +terminal.isAlternateScreen(): boolean +``` + +True while a full-screen program such as `vi` is open. + +### `readFile` + +```typescript +terminal.readFile(path: string): Promise> +``` + +Read a file from the session's virtual filesystem. + +### `resize` + +```typescript +terminal.resize(rows: number, cols: number): void +``` + +Resize the terminal; a running `vi` redraws. + +### `runUntilIdle` + +```typescript +terminal.runUntilIdle(timeoutMs?: number): Promise<"exited" | "idle" | "timeout"> +``` + +Run until the session needs input or exits, or `timeoutMs` passes. + +### `screenText` + +```typescript +terminal.screenText(): string +``` + +Visible screen as plain text. + +### `send` + +```typescript +terminal.send(data: string | Uint8Array): number +``` + +Queue raw input as if typed (`"\r"` Enter, `"\x1b"` Escape, `"\x03"` Ctrl-C). + +### `size` + +```typescript +terminal.size(): [number, number] +``` + +`[rows, cols]`. + +### `systemPrompt` + +```typescript +terminal.systemPrompt(): string +``` + +Terse usage guide for a system prompt. + +### `takeOutput` + +```typescript +terminal.takeOutput(): Buffer +``` + +Raw output bytes since the last call, for a renderer such as xterm.js. + +### `takeTranscript` + +```typescript +terminal.takeTranscript(): TerminalCommandRecord[] +``` + +Commands that finished since the last call. + +### `toolDefinition` + +```typescript +terminal.toolDefinition(): TerminalToolDefinition +``` + +OpenAI-compatible function definition for `Terminal.call`. + ## getVersion() ```typescript @@ -1536,6 +1716,130 @@ What changed between two commits. or network trust boundaries. Recommended for any snapshot accepted from users, shared storage, or remote callers. +## TerminalActivityInfo + +What the session is doing. + +### Fields + +- **`command?`** — `string` + + The running command line (state `"running"`). +- **`exitCode?`** — `number` + + Shell exit code (state `"exited"`). +- **`state`** — `string` + + `"prompt"`, `"continuation"`, `"running"` or `"exited"`. + +## TerminalCallArgs + +Arguments of `Terminal.call`, named as in `toolDefinition()`. + +### Fields + +- **`close?`** — `boolean` + + Close the named session instead of typing into it. +- **`input?`** — `string` + + Keys in Vim notation (`"ls"`, `""`). +- **`screen?`** — `"full" | "changes" | "none"` + + `full` (default), `changes` (only changed rows) or `none`. +- **`session?`** — `string` + + Terminal tab to use (default `main`); created on first use, files shared. +- **`wait_for?`** — `string` + + Regex: return as soon as command output printed during the call matches. +- **`wait_ms?`** — `number` + + How long to wait for the session to need input (default 5000). + +## TerminalCallResult + +### Fields + +- **`activity`** — `"prompt" | "continuation" | "running" | "input" | "exited"` + + `input`: a running command waits for a typed line (see `input_prompt`). +- **`closed?`** — `boolean` + + The session was closed (`close: true`). +- **`commands`** — `({ command: string; exit_code: number; output: string; output_truncated?: boolean })[]` + + Commands that finished during this call. +- **`cursor?`** — `{ col: number; row: number }` + + Cursor position, 0-based (screen `changes`). +- **`exit_code?`** — `number` + + Shell exit code (activity `exited`). +- **`full_screen`** — `boolean` + + A full-screen program such as vi or less is open. +- **`input_prompt?`** — `string` + + Question on the cursor line while activity is `input`. +- **`matched?`** — `boolean` + + Whether `wait_for` matched (only when it was given). +- **`restarted?`** — `boolean` + + The session had exited and a fresh shell started for this call. +- **`running_command?`** — `string` + + Command line still running (activity `running`). +- **`screen?`** — `string` + + Visible screen as plain text (screen `full`). +- **`screen_changes?`** — `({ row: number; text: string })[]` + + Rows changed since the previous call (screen `changes`). +- **`session?`** — `string` + + Session this result is for, when more than one is open. +- **`sessions?`** — `({ activity: string; name: string })[]` + + Every open session and its activity, when more than one is open. +- **`waiting_for_input`** — `boolean` + + False when `waitMs` ran out while a command was still working. + +## TerminalCommandRecord + +One finished command line. + +### Fields + +- **`command`** — `string` +- **`exitCode`** — `number` +- **`output`** — `string` + + Combined stdout and stderr with `\n` line endings. +- **`outputTruncated`** — `boolean` + +## TerminalOptions + +### Fields + +- **`cols?`** — `number` +- **`cwd?`** — `string` +- **`env?`** — `Record` +- **`hostname?`** — `string` +- **`rows?`** — `number` +- **`username?`** — `string` + +## TerminalToolDefinition + +OpenAI-compatible function definition returned by `Terminal.toolDefinition()`. + +### Fields + +- **`function`** — `{ description: string; name: string; parameters: { properties: Record>; required?: string[]; type: "object" } }` +- **`type`** — `"function"` + ## BuiltinCallback Callback signature for custom builtins. Return the stdout to emit.