Repository navigation
v0.2.4
Noah Code v0.2.4
This release is a security and reliability hardening pass across the permission engine, checkpoint
capture, workspace edits, budgets, and the terminal interface, plus dependency and CI updates.
Security
- Closed a hard secret-deny bypass through Git object syntax:
git show HEAD:.env(and:path
forms generally) is now classified by the path component and denied likecat .env, in build,
plan, and--automodes alike. - Unscoped patch-output Git commands (
git log -p, baregit show/git diff) no longer ride the
read-only auto-allow bump; they ask, since they can dump committed secrets without naming a path. - Expanded secret classification to common credential stores:
.npmrc,.pypirc,.netrc,
.pgpass,.envrc,.kube/config,.docker/config.json,.aws/credentials, and Java/JCEKS
key stores. - Joined short-flag values are now scanned as paths, so
grep -f/etc/passwd,rg -f.env, and
tail -F~/logcan no longer evade the external-path and secret checks. - Git checkpoint capture no longer stages secret-classified paths into
refs/noah-code/checkpoints/*
and no longer executes repository clean filters — capture uses filter-free plumbing
(hash-object --no-filters+update-index --cacheinfo), preserving executable bits and storing
symlinks as links. - Non-interactive
noah run --autonow rejects elevated-risk commands (file removal, downloads,
find -delete/-exec, and similar) instead of silently approving them; interactive behavior is
unchanged. - Plan mode can no longer be crossed without confirmation:
mode/modelfront matter in
repository custom commands is ignored (user commands keep it), andplan.exit_to_build()is
never auto-approved. find -delete/-exec/-execdirjoined the elevated-risk floor, andrg --hostname-binis no
longer treated as read-only.
Reliability and correctness
- The main edit path (
replace/edit) is now atomic and newline-preserving: byte-level splice,
exactly-one-match enforcement, and temp+fsync+rename writes — CRLF files no longer flip to LF
when one hunk changes, and a crash mid-edit cannot leave a truncated file. - Reads are bounded: whole-file reads above
max_file_bytesrequire an explicit line range, ranged
reads stream instead of loading the file, binary files fail cleanly, and durable pre-images above
undo_blob_limitdegrade to hash-only instead of ballooning the session database. - Background-job durable logs rotate at 4 MB keeping the newest lines, and log paging tail-reads
instead of loading the whole file. - Live model switches (
/model,/reasoning,/providers use) now rebuild the full retry and
fallback pipeline — switches no longer silently drop transient retries and fallback models. - The WAIT path can no longer hang: background-job terminal events always fire even if the runtime
write fails, and the host wait is bounded with state re-checks; a job finishing between WAIT and
wake no longer fails the run spuriously. - Cost budgets work: per-response cost is recovered from provider-reported cost or LiteLLM's
pricing table, somax_cost_usdenforces real charges and/tokensreports real cost instead of
$0.000000. - Ctrl+C under
asyncio.runexits cleanly with the designed double-press flow instead of a
traceback; one press at an idle prompt hints, a second exits, and mid-turn cancellation renders
once. - Configuration is validated strictly: unknown keys and invalid values or TOML fail every command
with a one-line error naming the file and field — no more tracebacks fromnoah doctor. noah runno longer exits 0 without doing anything when an auto-update installs; non-interactive
runs print the update notice and proceed.- Fixed atomic-patch rollback ordering (fsync failure no longer strands a renamed file), new-file
permissions (umask default instead of0600), grep-harvested edit anchors aftercd,
stale-anchor detection without a prior read, double-close()wiping the undo journal, stale
interrupted runs resurrecting, and checkpoint metadata clobbering newer session metadata.
Interface
- Fixed priority keybindings that hijacked basic editing keys: Skills picker moved to
Ctrl+G,
return-to-live moved toCtrl+], andEnd/Ctrl+Kwork normally inside the composer again. Cmd+C/Ctrl+Cin the composer copies the composer's own selection; keyboard (shift-arrow)
selections are copyable.- Transcript selection keeps the original message text instead of copying rendered soft wraps,
padding, or transformed Markdown, with a high-contrast selection highlight across themes. Ctrl+Cnow copies active selections from the transcript, composer, and single-line fields;
Ctrl+V/Cmd+Vreads the native system clipboard asynchronously in every editable field.- Native clipboard helpers are preferred over duplicate OSC 52 writes, and rapid copies are
coalesced to prevent terminal flicker, UI stalls, and stale writes finishing out of order. @-mention suggestions no longer rescan the whole workspace per keystroke: directory pruning
plus a short-lived cache keep the composer responsive in large repositories.- One Ctrl+C now renders a single "turn cancelled" entry, and question-modal "Other" collects a
free-text answer instead of submitting the literal stringother.
Packaging and CI
- LiteLLM re-pinned to
>=1.96.0,<1.99.0(resolved 1.98.0): upstream restored macOS wheels, so
provider and security fixes from 1.92–1.98 now reach installs. - Added upper bounds for end-user installs (
textual,pydantic,click,rich,PyYAML,
packaging), which resolve fresh and ignoreuv.lock. - Unified the duplicated dev-dependency declarations into the single
devextra. - The default test suite is now hermetic by construction (pytest-socket blocks network sockets);
CI runs onv*tags, type-checks in the release pipeline, collects macOS coverage, and runs the
network integration tests as a non-blocking job. - Expanded the hermetic suite to 582 passing tests.
Upgrade
Existing managed installations can run:
noah updateNew installations can use the one-command installer from the README.
Full changelog: v0.2.3...v0.2.4