Renderers such as errorChain and the activation report expand
AggregateError members but otherwise follow only cause, so a cause of
just the setup failure hid the cleanup failure that may leave
resources alive. The cause is now an AggregateError of both failures.
An upgrade guide records that the class extends TerminalError instead
of AggregateError.
INIT_REJECTED is documented as the code a consumer that drives a shell
uses when the shell rejects its setup input, and the persistent bash
tool README records it.
Refs #7869
TerminalError gains SESSION_CLOSING, SESSION_EXITED, STARTUP_EXITED,
STARTUP_TIMEOUT, NO_SANDBOX, and INIT_REJECTED, following its existing
unprefixed code style. The terminal service and terminal-bash use them
for a send to a closing or exited session, a shell that exits or misses
readiness during startup, and a confined mode without a sandbox
provider; an empty confined argv is SubprocessError
SUBPROCESS_INVALID_ARGV, and a send on an aborted signal throws an
error named AbortError.
The persistent bash and pwsh tools report a missing owner as
ToolContextUnavailableError, a blank command as ToolArgsError, and a
shell that rejects its setup as TerminalError INIT_REJECTED.
Refs #7869
The persistent bash tool types each wrapped command into an interactive
bash, which applies history expansion before parsing. A `!` after a single
quote made bash reject the whole line with `event not found`, so neither
marker printed and the call waited for the full timeout; `!!` silently
substituted the previous history entry.
Disable history expansion when the shell is initialized, as OpenHands does
for its interactive terminal. Raw control characters other than newline and
carriage return still reach readline as typed keys; the README records that
limitation.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
The minimal compositions serve the same models that trained against a
one-shot `bash` tool; three surface differences remained visible to them.
- Every settled command now appends `[Command finished with exit code N]`,
including exit code 0, instead of `[exit code: N]` only on failure. A
timeout appends `[Command timed out or OOM]` after the partial output.
- Trailing newlines are all dropped before the status trailer rather than
only the last one.
- The `bash` description in the `minimal` preset and the `sdk-minimal`
bundle replaces the two fixed network lines with a single environment-
dependent line; the pwsh description is unchanged.
Tests, READMEs, the web minimal-preset schema snapshot, and the packaged
Python minimal model-visible snapshot follow the new wording. The
standalone `persistent-tools` SDK replay fixture keeps its own copy of the
old description since it is a per-launch customization, not the profile.
tool-bash-persistent overwrote the backend's PS1, so terminal-bash prompt
readiness never matched and every send degraded to the 3.5s silence tier
(idleSilenceMs + handoffGraceMs) under production defaults.
The controlled PROMPT_COMMAND now re-asserts PS1 before every prompt, so an
in-shell override never survives to the next prompt. The tool initializes
with stty -echo alone and detects the no-end-marker fallback through the
seam's stdin_read wait reason instead of matching its own prompt text.
Tool calls drop from 7180/3560/3566 ms to 355/88/91 ms (spawn+init+echo,
echo, pwd; darwin, production defaults). The loader composition suite now
pins the fast path by pushing idleSilenceMs beyond the send bound, and a
real-PTY case proves PS1 self-healing.
Fixes#2585