Releases: ScottRBK/agent-shell
Release list
v0.4.0
AgentShell can now open the agents' native interactive UIs in tmux, alongside its existing
headless execution API. This release also adds persistent Pi package and local extension management.
Interactive tmux sessions
AgentShell.open_interactive()opens a native agent UI throughTmuxExecutionHostfor all
seven supported agents. Native permission prompts remain available.- Sessions expose terminal controls and structured events where the harness supports them;
session.capabilitiesreports the available features. TmuxPlacement.split_pane(direction="right" | "down")adds pane placement alongside new
sessions and windows. Existing session names are matched exactly.- Cleanup removes only the resources owned by the run, preserving neighbouring panes and sessions.
Packages and local extensions
- Adds the shared
PackageSpec,add_package,list_packages, andremove_packageAPI.
Pi is the first implementation; other adapters report unsupported operations clearly. - Supports exact npm versions, Git sources with explicit refs, local extension files, and package
directories. Registrations persist for later AgentShell instances through Pi's existing settings. - Local files remain in place, including symbolic-link identities. Removing a local registration
preserves the source files. - Source validation, persistence checks, timeouts, and cancellation handling surface installation
failures, including cases where Pi exits successfully without saving its settings.
Compatibility
- No intended breaking changes to existing headless APIs or execution defaults.
- Interactive sessions remain experimental and opt-in. Feature support varies by harness;
tmux is required, and this host currently supportsNoIsolationonly. - Package management uses the caller's existing Pi configuration. Callers own container isolation;
per-run resource selection and individual resource toggles are outside this initial API. - No new Python runtime dependencies.
See usage examples and package implementation details.
Validation
- Unit/integration CI and the tag build passed: 1,102 tests passed and 18 were skipped on the
hosted runner. The wheel and source archive report version 0.4.0 and contain the runtime files. - Five real Pi package smoke tests passed without downloads or model requests.
- Independent Astra x-high review completed; its symbolic-link finding was fixed and verified.
Full changelog: v0.3.2...v0.4.0
agent-shell-py 0.3.2
Adds three experimental ways to watch agent runs and an explicit PID isolation option for
restricted Linux containers.
New features
HerdrExecutionHost: run each command in an owned Herdr pane.TmuxExecutionHost: run in a new session or a new window in an existing session, with
explicit placement throughTmuxPlacement.TerminalWindowExecutionHost: mirror a headless run into a graphical terminal window.LinuxPidNamespaceIsolation(mount_proc=False): retain the PID signal boundary, PID 1
reaper, and descendant cleanup when a private/procmount is unavailable. The selected
mode is probed explicitly and never silently falls back.
Compatibility and limitations
- Native execution and existing isolation defaults remain unchanged. The existing
AGENTSHELL_ISOLATION_POLICY=linux-pid-namespacevalue still mounts a private/proc. - The three visible execution hosts are experimental, opt-in, and currently support
NoIsolationonly. Their APIs may change in a later minor release. mount_proc=Falseinherits outer/procvisibility and does not create a private mount
namespace. Process tools may report PIDs that differ from the child's namespace PIDs.
This policy is not a filesystem, network, or credential sandbox.- No new Python runtime dependencies. Visible hosts require their external executables.
- Added lifecycle, compatibility, public-export, and E2E tests, plus updated API documentation.
- Stabilized the Herdr cleanup test by allowing subprocess startup time on shared CI runners;
the simulated hung command remains bounded.
Validation
- 979 unit and integration tests passed locally. CI and the tag build passed; the tag build
ran 961 tests and skipped 18 namespace tests unavailable on the hosted runner. - Both live Codex PID isolation E2E cases passed: private and inherited
/proc. - Downstream checks passed: 44 Pi extension tests and 128 relevant eval-harness tests.
Full changelog: v0.3.1...v0.3.2
agent-shell-py 0.3.1
Isolation policy environment configuration
AgentShell can now select its isolation policy through the process environment:
export AGENTSHELL_ISOLATION_POLICY=linux-pid-namespaceAccepted values are none and linux-pid-namespace. Resolution is deliberately predictable:
- An explicit
isolation_policy=object AGENTSHELL_ISOLATION_POLICYNoIsolation()
Empty or unknown values raise ValueError instead of silently disabling isolation. When the
variable is unset, existing callers retain the NativeExecutionHost() plus NoIsolation()
default.
Verification
- Added public-constructor coverage for both values, invalid configuration, the unchanged default,
and explicit-policy precedence. - Added a real-process integration test proving the environment-selected CLI runs as PID 2 inside
the Linux PID namespace. - Updated the README, architecture documentation, and invoking-cli-agents skill/API reference.
- All 860 unit and integration tests passed locally and in GitHub Actions.
- Independent Claude Code Opus review at high effort returned
READY TO COMMITwith no actionable
warnings.
Full changelog: v0.3.0...v0.3.1
v0.3.0
What’s new
Composable execution hosts and isolation policies
AgentShell now separates where a CLI process runs from how that process is isolated:
ExecutionHostowns process launch and returns a per-runRunHandle.IsolationPolicycan independently prepare a launch.NativeExecutionHostwithNoIsolationremains the default, preserving existing callers.- All seven adapters use the new execution seam for
execute(),stream(), andhealth_check().
This provides the foundation for future execution hosts such as tmux and Herder without requiring new agent adapters.
Opt-in Linux PID namespace isolation
LinuxPidNamespaceIsolation places the CLI and its descendants inside a rootless user/PID namespace. This prevents child-namespace processes from seeing or directly signalling AgentShell’s ancestor processes.
Isolation requests fail closed when the required Linux kernel features or unshare support are unavailable. There is no silent fallback to native execution.
This is a process/signal boundary, not a general sandbox: it does not restrict filesystem access, credentials, networking, tools, resources, or every possible host-side signalling path.
Process lifecycle improvements
- Process execution and cancellation now flow through
RunHandleacross every adapter. - Non-zero exits expose normalized
returncodeandsignalmetadata in stream events andAgentExecutionError. - Process guardians remain attached to the stable run handle throughout normal completion, cancellation, abandoned streams, and interpreter shutdown.
Documentation and test reliability
- Documented Cursor’s user-scope MCP configuration behavior.
- Cursor E2E and health checks now explicitly select
auto, avoiding mutable named-model defaults that free Cursor plans cannot execute. - Added real-kernel and integration coverage for execution-host wiring, PID isolation, cancellation, status propagation, and fail-closed behavior.
Compatibility
There are no intentional breaking defaults in this release. Existing AgentShell(agent_type=...) callers continue to use native execution without isolation unless they explicitly select a different host or policy.
Full Changelog: v0.2.5...v0.3.0
v0.2.5
v0.2.4
What's new
Grok Build CLI adapter
- New
AgentType.GROKbacked by thegrokCLI - Headless via
--output-format streaming-messages-json(full assistant blocks) execute/stream/health_check/list_models- Session resume via
--resume - Canonical tool denies (
bash→run_terminal_cmd, verified live) - User-scoped MCP add/remove/list (
~/.grok/config.toml) - Cost, duration, and raw
output_tokensfrom the terminalresultevent
Docs
- README, AGENTS.md, agent parameter comparison, and installable skills updated for seven agents
Install
uv add agent-shell-py==0.2.4
# or
pip install agent-shell-py==0.2.4v0.2.3
Fixes
- Normalise Copilot CLI reasoning-effort values case-insensitively.
- Pass supported effort values to Copilot in the required lowercase form.
- Reject unsupported effort values before starting the Copilot CLI process.
- Update the Copilot CLI parameter documentation for the current effort choices and flags.
Tests
- 735 unit and integration tests passed.
- Added regression coverage for every supported effort value, case normalisation, and invalid-value rejection.
This release includes the unreleased changes merged in PR #16 and addresses issues #14 and #15.
Full changelog: v0.2.2...v0.2.3
v0.2.2
Closes #13.
Process cleanup safety
AgentShell no longer stores numeric process-group IDs and later tries to infer whether they still
belong to an AgentShell child. A reaped PID can be reused by Linux, so that design could send
SIGKILL to an unrelated process group.
Each agent execution now uses a dedicated process-group guardian:
- AgentShell owns the guardian through a private pipe, an exact kernel object that cannot be
confused with a recycled PID. - Cancellation, abandoned streams, model-discovery cleanup, and interpreter exit ask the guardian
to kill its own group. - Normal stream completion releases the guardian without killing processes intentionally left by
the CLI. - If the guardian has already died, cleanup fails safe and accepts a possible leak rather than
signalling a numeric process-group ID. - Guardians close inherited file descriptors and redirect their own output to
/dev/null.
All six adapters and every model-discovery path now use the same ownership-safe mechanism.
Tests and review
- Added real-process lifecycle coverage for all six adapters through normal completion,
cancellation, and stream abandonment. - Added kernel-level coverage for child/grandchild cleanup, dead-guardian failure, interpreter
disappearance, already-reaped CLIs, model-discovery cleanup, and file-descriptor isolation. - Full local suite: 784 passed, 2 skipped.
- Process-lifecycle suite passed 10 consecutive stress runs.
- Wheel and source distribution builds verified.
- Reviewed twice by Claude Code Opus at maximum effort with no blocking findings.
v0.2.1
Model discovery
AgentShell can now ask every supported CLI which models it currently advertises:
models = await shell.list_models(cwd="/path/to/project")
response = await shell.execute(
cwd="/path/to/project",
prompt="Review this project",
model=models[0],
)The returned strings are passed unchanged to execute(model=...) and stream(model=...).
Discovery is account/workspace-aware, sends no inference prompt, imports no harness SDK, and
uses no static fallback catalog.
Supported discovery paths:
- Claude Code stream-JSON initialization
opencode models- Copilot CLI headless JSON-RPC
codex debug modelspi --no-approve --list-modelscursor-agent models
Reliability
- Added timeout, cancellation, malformed-output, and UTF-8 error handling.
- Discovery subprocesses use an isolated live process-group sentinel, preventing both leaked
descendants and signalling through a reaped/reused CLI PID. - Pi selectors are provider-qualified so duplicate model IDs remain unambiguous.
- Harness ordering and aliases such as
autoanddefaultare preserved.
Documentation and tests
- Updated the README, AgentShell skill, architecture notes, and agent parameter comparison.
- Added parser, integration, process-lifecycle, selector pass-through, and real-CLI E2E tests.
- 726 unit/integration tests pass, all six model-discovery E2Es pass, and the full local E2E
suite was verified before release.
v0.2.0
Breaking changes
execute() now raises AgentExecutionError instead of returning on failure.
Previously a crashed, truncated or agent-level-failed run was indistinguishable from an
empty answer. Callers that relied on inspecting an empty response must now catch:
from agent_shell.models.agent import AgentExecutionError
try:
result = await shell.execute(cwd=..., prompt=...)
except AgentExecutionError as e:
print(f"failed: {e}") # str(e) is the bare reasonAgentExecutionError carries whatever partial data the run produced (response, cost,
session_id, duration, output_tokens).
AgentType.GEMINI_CLI removed. Gemini is deprecated and never had an adapter, so
constructing it always raised ValueError. shell.py now has a module-level _ADAPTERS
registry, pinned to the enum by a test so a future AgentType cannot ship without one.
Fixes
- #7 Process and stderr teardown was duplicated and subtly wrong across all six adapters.
Now a sharedrelease_process()called from afinallyin everystream().
kill_process_group()signals only while the pid still leads its own group — asyncio's
ThreadedChildWatcherreaps the child and frees its pid before settingreturncode, so
returncodeis not a sound liveness test and a recycled pid could be signalled. - #10 The Pi adapter reported success for failed runs. It now recognises failing stop
reasons, prefers Pi's structurederrorMessage, and judges only the last assistant message,
since Pi emits multipleagent_endevents when it auto-retries. - #11 A shared
outcome.pyholds one success predicate and reason precedence used by both
execute()andhealth_check(), so the two cannot drift;response.pyholds the shared
aggregation. cleanup_process_groups()bypassed the new pid-leader guard and calledos.killpgdirectly.- Orphaned grandchildren leaked when the child was reaped while its process group still had
live members. Reclaimed on the abandoned path. - A failing unit test could leave an invented pid in the module-global registry, and
atexit
would then SIGKILL whatever real process group held that number.
Documentation
- Codex and Cursor cannot enforce
disallowed_tools; the skill docs told readers to rely on it
for read-only reviewers. - Stderr in
errorevents is head+tail (500 characters each side), not the documented last 500. - Added a Pi section and documented Cursor as a supported agent.
- Corrected process-cleanup docstrings that asserted measurably false claims.
Tests
Session-resumption e2e tests asserted that a model would recall a planted word, measuring model
compliance rather than library behaviour — one failed because the model declined to repeat the
token. All six adapters now assert session-id identity across a resume plus a distinct id for a
fresh session. Copilot e2e is pinned to the free auto model, with the two reasoning-effort
tests gated behind COPILOT_PAID_TIER.
682 unit and integration tests passing; 57 e2e passing, 2 skipped.