Repository navigation
Remove the Rust agent runtime from the TUI; keep /model via ACP config options - #7
Merged
Merged
Conversation
…tions Publish a select config option (id "model", category "model") in the session/new, session/load and session/resume responses and route session/set_config_option to switch the active model. Choices come from the optional [[models]] tables in agent.toml (see agent.example.toml); the restored choice is derived from the session's recorded model. This is the standard ACP counterpart of the TUI /model picker, replacing the removed Rust runtime's model switching. Tests: +3 in test_acp_agent.py (advertise, switch, restore on load), +2 in test_config.py (parsing/defaults). Co-authored-by: Cursor <[email protected]>
…CP config options zypi is now purely an ACP client. The agent (Python pi_agent_cli today, a future pi-rust) runs as a separate process behind standard ACP (stdio JSON-RPC); nothing in the TUI process can create an agent or call a model provider any more. Removed: MvpAgent and the ACP server implementation in pi-shell, the pi-sampler crate (and 25 third-party crates only it used), headless mode, the leader process/protocol/cluster, the mcp/memory/plugin/sessions/share/trace/worktree/models subcommands, and the tests and helpers that only exercised them. pi-shell shrinks from ~404k to ~66k lines; the workspace loses ~366k lines of Rust (Cargo.lock 1311 -> 1285 packages). Kept: pi-tools, pi-workspace, pi-agent and the rest of the non-runtime crates. pi_shell::sampling stays as a thin re-export of pi-sampling-types. /model is restored through ACP Session Config Options: pi-acp-lib gains a typed SetSessionConfigOption request, and the pager builds the model catalogue from the agent's category=model config option and switches with session/set_config_option (session/set_model remains only as a fallback when the agent does not advertise the option). Verified on macOS: cargo check (pager-bin, pager-minimal, update; workspace --tests), pi-shell lib 1136 passed, pi-pager lib 8606 passed, and a real zypi <-> pi_agent_cli <-> OpenRouter session in a PTY including a /model switch. Not verified: Linux/Windows cfg code. Known failures unrelated to this change are listed in docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md section 10.4. Co-authored-by: Cursor <[email protected]>
Revised plan for removing the Rust agent runtime from the TUI (target architecture, ADRs, staged plan) plus section 10: what was executed, how /model was restored through ACP Session Config Options, verification results, known unrelated failures and remaining work. Also annotates the Phase 4 spec: /model has a standard ACP counterpart and is kept. Co-authored-by: Cursor <[email protected]>
Plan stage 0 (0.1 / 0.2 / 0.4) of PLAN-RUST-AGENT-RUNTIME-REMOVAL.md. - scripts/tui_baseline.py + scripts/tui_baseline.toml: one executor (gates | check | test | release) driven by a manifest. Blocking gates: toolchain pin == tui/rust-toolchain.toml, pi-sampler absent from Cargo.lock and the graph, no MvpAgent / acp_session_impl / SamplerActor under tui/crates, unique-package ceiling for pi-pager-bin on linux-gnu, consumer `cargo check --locked`, release binary not above the v0.4.0 size. Report-only until the first Linux run is triaged: 8 test suites, `cargo check --workspace --tests`, warning count. Ratchet semantics (ceilings only go down, floors only go up). - .github/workflows/tui-ci.yml: `check` and `test` on ubuntu-24.04 (pinned toolchain, rust-cache, protoc), plus a manual `release-baseline` job; results go to the job summary and to artifacts. - docs/baselines/tui.md: pre-removal (v0.4.0) vs post-removal (macOS) numbers, test table with the reason for every known failure / skip, deny-list with keep-or-shrink reasons, and the steps to finish once the Linux run exists. - scripts/tests/test_tui_baseline.py (18 tests) is collected by pytest via `testpaths`. The Linux job has not been run yet; the numbers in the report are macOS measurements. Co-authored-by: Cursor <[email protected]>
Plan A.2 tail. The leader process, protocol and CLI flags were removed with the Rust runtime;
this removes what the pager still carried for them. Production behaviour is unchanged:
`leader_mode` and `reconnect_pending` were constant false, and the follow-up routing is now
exactly `[ui].follow_up_behavior = "steer"`.
pi-pager
- AppView: drop `leader_mode`, `leader_roster`, `reconnect_pending`, the roster upsert/remove
helpers, `Effect::FetchRoster` and `TaskResult::Roster{Loaded,Failed}`. The dashboard roster is
`dashboard_local_sessions` only (polled while open); `roster.rs` keeps just the row types.
- `immediate_server_send_eligible(agent)` loses its `leader_mode` argument; `handle_sessions_changed`,
the reconnect guards in the drain paths and the `RestartSharedLeader` hint are gone.
- Startup failure screen is single-agent: no attempt counter or "longest step", no leader row, no
`zypi leader kill` advice. `bounded_connect` and `ConnectFailure` shrink accordingly.
- Dashboard / queue / prompt doc comments and the embedded user guide no longer describe leader mode.
- Tests: fixtures use the production default (Queue); the 15 tests that cover the server-
authoritative immediate send opt in with `SteerFollowUp` (agent_view::test_fixtures).
Removed tests that exercised the reconnect guard; the announcements watermark test no longer
simulates a reconnect reset that no longer exists.
other crates
- pi-telemetry: remove `StartupPhase::LeaderConnect` and `AgentKind::Leader`; `DashboardOpened`
reports `{ agents, subagents }`. The process-identity vocabulary (`Entrypoint::Leader`,
`LeaderMode`, `is_leader_mode`) is kept: it is a telemetry schema, not UI state.
- pi-config-types / pi-shell: remove `RemoteSettings.leader_mode`, `[cli].use_leader` and
`AgentMode::Leader`. Old config files and server payloads that still carry the keys are
ignored (no `deny_unknown_fields`; `merge_section` keeps unmodelled keys; covered by tests).
- pi-http: drop the unused `MIN_CLIENT_CONNECT_TIMEOUT`.
- pi-test-support: delete the leader harness and the UDS fault proxy (no users left) together
with their dependencies (clap, tokio-tungstenite, tracing-subscriber).
Verification (macOS): `cargo check --workspace --tests` (0 errors), consumer build (20 warnings,
unchanged), pi-pager lib + 3 integration targets 8,870 passed / 2 known failures, pi-shell 1,140,
pi-telemetry 244, pi-http 13, pi-acp-lib 21. Totals are 18 below the previous baseline because
tests of the deleted code went with it; scripts/tui_baseline.toml `reference_passed` is updated.
`rg -i leader --glob '*.rs' tui/crates` drops from 684 lines / 121 files to 279 lines / 84 files;
what is left is process-group "leader", update convergence, telemetry identity, config watcher
and the multi-client shared-session protocol (ADR4 / A.3).
Co-authored-by: Cursor <[email protected]>
…onstant Follow-up to the leader UI residue cleanup. These still described the removed leader: - pi-acp-lib/normalize.rs claimed a leader socket and a leader-bridge replay sniff in pi-pager-bin that no longer exist. Nothing in the workspace calls spawn_stdin_line_reader any more, so the module doc now says so and leaves the decision to plan A.3. - pi-status-line: the doc of CLIENT_STATUS_LINE_META said a leader injects it. Nothing reads or writes it now; doc only, the public constants stay. - pi-http: delete STARTUP_AUTH_TIMEOUT, the boot-auth cap of the leader process. It has no references anywhere in the repository. No behaviour change. `rg -i leader --glob '*.rs' tui/crates`: 279 lines / 84 files -> 273 / 80. Co-authored-by: Cursor <[email protected]>
Plan 1.R3, naming and wording only. The pager's agent lifecycle still used the vocabulary of the
removed in-process runtime ("session actors flush SessionEnd teardown"); the agent is a child
process that the stdio bridge kills and reaps.
Renames (pi-pager)
- SpawnedAgent -> AgentProcess (thread_handle -> bridge_thread)
- AgentShutdownGuard -> AgentProcessGuard
- spawn_grok_shell -> spawn_agent_process (drops the unused _memory_config parameter)
- AcpConnection.agent_thread -> bridge_thread
- SESSION_FLUSH_GRACE / AGENT_JOIN_SLACK -> AGENT_EXIT_GRACE / BRIDGE_JOIN_SLACK
(still 10 s + 2 s; the 20 s budget in app::exit_timeout is unchanged)
- join_agent_thread -> join_bridge_thread
Wording
- The slow-exit notice reads "Stopping agent…" instead of "Finishing session…", and the timeout
warning no longer claims SessionEnd teardown may be incomplete; it says the agent process may
still be running.
- Docs describe what the code does: cancel -> SIGKILL -> reap, the guard is held by app::run
only, the child inherits stderr.
Behaviour is unchanged: same timeouts, same kill/reap path. Not done, and recorded in the plan
under 1.R3: where the child's stderr goes, a graceful exit on stdin EOF (1.P2), and an experiment
on leftover bash/MCP grandchildren after SIGKILL / Ctrl-C / kill -9.
Verification (macOS): tui_baseline.py check (consumer build, 20 warnings as before,
`cargo check --workspace --tests` 0 errors) and tui_baseline.py test (8 suites, 10,839 passed /
2 known failures / 80 ignored).
Co-authored-by: Cursor <[email protected]>
…r the leader cleanup Plan (docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md) - Status r4 with the list of local commits; nothing is pushed. - Stage 0: 0.4 (deny-list) done; 0.1/0.2 stay open until the workflow has run on Linux. - A.2 tail done. 1.R3: naming and wording done; the open items are spelled out (where the child's stderr goes, graceful exit 1.P2, leftover bash/MCP grandchildren after a kill). - Section 10.4: leader residue rewritten. 273 lines / 80 files still say "leader"; none of it is UI state (shared-session protocol vocabulary awaiting ADR4/A.3, process-group leaders, update convergence, telemetry identity). Suggested order of next steps updated. - New section 10.6: what r4 changed and how it was verified (macOS only); revision log r4. Baseline report (docs/baselines/tui.md) - Test counts after the leader cleanup: pi-shell 1,140, pi-pager 8,870, total 10,839. That is 18 fewer than first measured because the tests of the deleted code went with it. - Line count after the cleanup (1,252,374); dependency graph (995), warnings (20) and `cargo check --workspace --tests` (0 errors) re-measured and unchanged. Co-authored-by: Cursor <[email protected]>
An earlier exploratory `cargo tree --no-dedupe` for native -sys crates never finished, so the question "does the Linux job need apt packages?" was left implicit. Answer it with the plain (deduplicated) tree, which has the same 995 packages the gates count: the -sys crates are bundled or fall back to a source build, and there is no alsa/openssl/udev/dbus -sys crate, so the workflow installing only protoc (like release.yml, which built v0.4.0 on ubuntu-24.04) is expected to be enough. Noted that this covers the pi-pager-bin graph only, and what to do if the test job fails to link. Co-authored-by: Cursor <[email protected]>
… the default text encoding On Windows (and with LC_ALL=C elsewhere) Python's default text encoding is not UTF-8. The tool wrote its Markdown report, which contains an em dash and status emoji, with the default encoding and crashed at the end of every run with UnicodeEncodeError. The tests read the vendored Cargo.toml files (one has a non-ASCII description) the same way and failed on the first of them. That would break the cross-platform pytest run the repository requires before a push. - Read and write every file with encoding="utf-8"; decode cargo/rustc child output as UTF-8. - Reconfigure stdout/stderr to UTF-8 (errors=replace) at startup. - New test: publish() in a child interpreter with PYTHONUTF8=0, LC_ALL=C and PYTHONCOERCECLOCALE=0, which reproduces the legacy-codec behaviour on POSIX. Reproduced before the fix with those settings (`tui_baseline.py gates` crashed in publish(), the manifest test failed on a Cargo.toml). After it, 19 tests pass in both modes and `gates` exits 0. ruff check and ruff format --check pass for the whole repository. Not run on a real Windows host. The baseline report no longer claims the script was verified on every platform. Co-authored-by: Cursor <[email protected]>
… it drops `zypi -p` is handed to the Python agent in main(), before async_main(), which is where the OS sandbox is applied. A sandbox that was asked for (--sandbox, GROK_SANDBOX, [sandbox].profile in config.toml, or a deployment requirement) was therefore silently skipped, and every flag the agent does not receive was silently ignored. ADR7 of the plan does not allow running unsandboxed without saying so. - pi-shell: requested_sandbox_profile() resolves the profile exactly like apply_sandbox() (requirement > --sandbox > GROK_SANDBOX > [sandbox].profile) without applying anything. - pi-pager-bin: -p exits with status 2 and an explanation when that profile is not `off`. Passing --sandbox off is the way out, unless a deployment requirement fixes the profile. - pi-pager-bin: the 24 user-facing flags that are not forwarded to the agent (--model, --output-format, --always-approve, --allow, --resume, ...) each print one warning on stderr. The full flag contract stays plan item 1.P6. Tests: 3 in pi-shell (which layer wins, explicit off, requirement cannot be lifted), 6 in pi-pager-bin. Also run against a debug zypi with PI_AGENT_COMMAND=/bin/echo: --sandbox workspace, GROK_SANDBOX=strict and a config.toml profile exit 2 without starting the agent; --sandbox off starts it and prints the warnings. Co-authored-by: Cursor <[email protected]>
….ai fetch The welcome screen fetched https://x.ai/cli/changelogs/<version>.external.{md,json} at every startup (cached under $GROK_HOME, offline switch GROK_CHANGELOG_OFFLINE) to draw a "Changelog" block, a "Changelog" menu row and the /release-notes command. zypi is not that CLI, and a phone-home to a third-party host at startup is not something a local coding agent should do. A PTY probe with a logging proxy used to show `CONNECT x.ai:443` right after launch; after this change nothing reaches the proxy in 45 seconds. Removed: - pi-shell-base `util::changelog` (ChangelogManager, cache, parsing) and the `Effect::FetchChangelog` / `TaskResult::ChangelogFetched` plumbing, - the welcome "Changelog" block (hero box and stacked layout), the menu row and its hit-testing/hover state, `/release-notes` (+ `/changelog` alias), - the unreferenced upstream release-note data under pi-shell (CHANGELOG.md and changelogs/*.{md,json}, 1 MB, versions 0.2.0 - 1.0.8) and the offline-only scenario release_notes_scroll.yaml. `Action::ShowReleaseNotes` doubled as the generic markdown viewer for /docs, so it is kept as `Action::ShowDoc`. The welcome layout's "changelog" slot was shared with remote announcements; it is now the announcement slot (`WelcomeLayout::info`, `effective_info`), and the layout tests that used a fixed changelog height now use an announcement (stacked-slot boundary, tip squeeze, compact, hero/stacked exclusivity). Verified: cargo check --workspace --tests (minus the already-broken pi-fast-worktree), pi-pager 8,581 lib tests (-8 net), pi-shell, pi-pager-bin; PTY probe shows the welcome screen without the Changelog row and zero outbound requests.
TUI CI run 37263077643 (ubuntu-24.04, rust 1.94.0, PR #7 at e20af45): gates pass, dependency graph 995 packages, consumer build 20 dead_code warnings, `cargo check --workspace --tests` 0 errors, 8 suites 10,851 passed / 0 failed / 75 ignored. Following docs/baselines/tui.md section 5: - [test] enforce = true and [check] enforce_workspace_tests = true, - max_warnings = 20, graph ceiling 995 (was the 1000 placeholder), - reference_passed refreshed to the macOS numbers at this tip, - unset_env also clears PI_HOME / GROK_HOME / PI_AGENT_COMMAND / PI_PYTHON / GROK_SANDBOX: a leftover PI_HOME (it outranks the GROK_HOME the config-isolation tests set) made test_config_update_isolation fail on a developer machine only, - the report gets a Linux column, per-suite timings and the corrected status. min_passed floors stay unset until the entry-audit deletions (plan A.3 / A.4) have landed, because deleting dead code legitimately deletes its tests. The release baseline (size, cold build time, --version latency) is still to be run, after the last push.
…reachable The registry filtered `builtin_commands()` at runtime down to nine names (`PI_STANDARD_SLASH_NAMES`); the other ~60 command modules compiled but could not be listed, completed or dispatched. This turns the runtime filter into a deletion (plan A.3, first slice). Kept: new, resume, quit (exit), help, theme, settings, multiline, model, home, plus `effort_levels` (helper of /model). Deleted: 57 modules and `workflow_tests.rs` (9.1k lines): always-approve, announcements, auto, btw, cd, compact, compact-mode, config-agents, context, copy, dashboard, debug, delete, docs, doctor, edit-prompt, effort, expand, export, feedback, find, fork, gboom, history, imagine, imagine-video, import-claude, jump, login, logout, loop, mcps, personas, plan, plugin (hooks / plugins / marketplace / skills), privacy, queue, recap, remember, rename, rewind, minimal/fullscreen switchers, scroll-debug, session-info, share, tasks, timeline, timestamps, mouse reporting, transcript, tutorial, usage, view-plan, vim-mode, voice, workflow(s). Registry: the `pi_standard_slash` switch and its three callers are gone, because the builtin set is the whitelist now; a test pins `builtin_commands()` to `PI_STANDARD_SLASH_NAMES` (test-only constant) and another asserts the removed names do not resolve. The command palette is rebuilt from the rows that survived the old runtime filter (New Session, Resume, Model, Multiline, Theme, Settings, Shortcuts, Quit); seven dead `PaletteCommand` variants and their handlers are removed. Helpers that other code still needed move to their callers instead of keeping a command module alive: `ForkArgs` (app::actions, until the fork flow itself goes), the doctor report builder (dispatch::prompt) and `detect_external_auth_provider` (event_loop). Those three are the entry points the next slices (fork, doctor, account/tier) will remove. Tests: tests that exercised deleted commands are deleted or pointed at a kept command (/model, /theme, /settings); the pinned mode-refusal list now has only /theme; the dashboard-surface slash tests go with the dashboard in the next commit. Verified (no NO_COLOR/FORCE_COLOR): pi-pager lib 8383 passed / 2 failed (the two known Alt/Opt footer tests), settings_e2e 276 passed, other integration targets ok.
The multi-session dashboard was the leader/roster front end: with a single
agent process per `zypi` there is nothing to list, peek into or dispatch to.
Entry audit (docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md, Appendix A):
delete.
- views/dashboard/, dispatch/dashboard.rs, dashboard_telemetry.rs, roster.rs
and the dashboard dispatch tests are gone.
- `ActiveView` is now `{ Welcome, Agent(AgentId) }`; `VoiceTarget` has a
single variant; `AgentView::draw` loses the overlay parameters;
`SlashCommand::{session_scoped, offered_when_session_less, dashboard_only}`
and `SlashController::hide_session_scoped` are removed;
`command_offered` takes two arguments.
- Actions, keymap entries, shortcut-help rows, startup flags
(`deferred_startup.open_dashboard`), telemetry events and config keys that
only served the dashboard are removed. The queue-drain helper is renamed
`drain_queue_for_agent`.
- 112 dashboard/overlay-only tests deleted; the rest were rewritten to use
`Welcome` / `Agent` views. Two scenario YAMLs and the `23-dashboard.md`
embedded guide are removed.
pi-pager lib tests: 7601 passed / 0 failed / 56 ignored (macOS, 2 threads).
…llow Restore point for the dead-code cascade (A.4). 62 modules stay `pub` (the ones pi-pager-minimal, pi-pager-bin, the integration tests, benches, examples and playground bins import); the other 151 are `pub(crate)`. The lib now compiles with warnings visible (to be cleared by the following commits).
…vealed With the crate-root `#![allow(dead_code, unused_*)]` gone and modules narrowed to `pub(crate)`, rustc now reports what nothing reaches. This commit deletes it, driven by the compiler's diagnostics and repeated to a fixed point: - legacy ext-notification / ext-method handlers (follow_ups, settings, queue, MCP, interjection, plan mode, ...) that only a `#[cfg(test)]` dispatcher still routed, together with that dispatcher and the tests that exercised it - unused slash plumbing left behind by the deleted commands (`CommandResult` variants, `usage()`, `ModeSupport::MinimalOnly`, `SlashController`'s cwd, ...) - empty / vestigial modules: `tool_usage`, `diagnostics::doctor_format`, four `acp_handler` submodules - unused parameters and variables Vendor `x.ai/` ext methods are now ignored unconditionally (no `cfg!(test)` bypass). pi-pager lib tests: 7069 passed, 0 failed, 51 ignored.
…askResult Temporarily narrowing the three action enums to pub(crate) let rustc report every never-constructed variant and never-read field once the slash modules, the dashboard and the legacy ext dispatchers were gone. The cascade removed ~11.8k lines: dead variants and their handlers, now-unused imports and helpers, and the tests that only exercised those paths. Also deletes 32 tests that were already #[ignore]d as "grok-specific feature not supported" and target removed features (plan, compact, queue-edit-into-builtin, hooks, voice, remote restore, plugin CTA), plus 7 tests (plugin CTA, coding-data-sharing) whose effect handlers the cascade removed. Visibility of app::actions is restored to pub because tests/settings_e2e.rs consumes Action.
…xtensions/rewind (A.3-3 part 1) Remove the keyboard entry points whose backing features are being stripped from the Rust client (ToggleTasks, SendToBackground, InterjectPrompt, OpenExtensions, Rewind, KillBgTask, ExitSession, NewSessionInWorktree): ActionId variants, default ActionDefs, input routing, hints, test-only registries, and the tests that exercised the deleted chords. Ctrl+G stays the external-editor chord in minimal mode only; restored/rewritten the tests that covered still-live behaviour (minimal registry surfaces, Ctrl+G mode split, shortcuts-help Ctrl+G rows, minimal palette registry). pi-pager lib tests: 6814 passed / 0 failed / 13 ignored.
…3 part 2) Remove the hooks / plugins / marketplace / skills / workflows / MCP-servers extensions modal, the /config-agents agents modal and the persona detail view, together with everything that only existed to drive them: - views: extensions_modal (+ workflows_picker_rows), agents_modal, persona_detail; agent_view/modals.rs (key/mouse/paste handlers) - Action/Effect/TaskResult variants for hooks/plugins/marketplace/MCP/skills management and their stub effect handlers, router arms and task-result handlers; ConfigFile editor requests; OpenManagedConnectors - ext-notification arms for HooksChanged / PluginsChanged, the workflow catalog refresh plumbing and the deferred extensions-fetch flag - pi-pager-minimal: the /mcps below-prompt panel and the extensions-modal live render (the /resume panel stays); minimal_api accessors for the modal Tests that only exercised those surfaces are deleted with them; the 247 removed tests all belong to the removed modules (checked name by name). `Action`/`Effect`/`TaskResult` stay `pub` because tests/settings_e2e.rs uses them. pi-pager lib tests: 6557 passed / 0 failed / 13 ignored.
…(A.3-3 3b-1) Remove the tasks pane, bg-task and scheduled-task state, the kill/demote/ cancel actions and effects, the bg-task block, viewer and status chips, the background and scheduled-task ACP handlers, and the subagent lifecycle arms that depended on them. Keep the bash highlighter (views/bash_highlight.rs) that lived in the tasks pane. Adapt pi-pager-minimal's commit frontier and drop tests that only exercised deleted features.
…e (A.3-3 3b-2) Remove the subagent transcript views, workflow runs overlay, goal detail/status UI, subagent catalog pane and child-session routing. Notifications now route to the root agent only (find_session_match returns an AgentId); the turn-status, timeline rail, send-now and cancel paths lose their subagent/goal conditions. Restored live-feature tests that the cascade dropped with the subagent fixtures (bind_session_id cursor reset, resize tip gate, reseed round trip, resume focus).
…bagent wait plumbing (A.3-3 3b-3) - remove the cancel-turn subagent choice panel (BlockingCard::CancelTurn, EscStep::KeepRunning, Action::CancelTurnChoice, minimal Modal::Cancel), the cancel_subagents preference/wire meta and its setting handling - remove turn-status Watchers / bg button / still-running watcher cue, has_running_execute, and the related hit areas and hover plumbing - drop VerbGroupKind::Subagent, BgTaskState/BgTaskStatus/ScheduledTaskInfo and other dead bg-task state, plus the vestigial wire-compat tests - tests: 6071 pi-pager lib / 80 pi-pager-minimal pass; 39 tests removed, all tied to the deleted behaviour
…d steer (A.3-3 3c) The Python agent keeps no server-side prompt queue, never adopts a running prompt from another client and never emits interjection notifications, so the server-authoritative half of the queue machinery was dead by data. Removed: - shared_prompt_queues, optimistic echoes, the merged queue view, queue mutation RPCs (remove / edit / reorder), send-now / interject / force-interject actions, effects and key chords, and the parked-wait look in the turn status; - the [ui].follow_up_behavior (queue | steer) setting and the contextual_hints.send_now tip, including their cross-crate plumbing (pi-shared, pi-config-types, pi-shell, pi-telemetry, pi-pager-render). Old config files that still contain these keys keep parsing: the config structs do not deny unknown fields. Kept: the local drip-feed queue (pending_prompts, QueueEntryKind, drain, queue pane delete / reorder / edit, combined bubbles), which is what the Python agent actually relies on while a turn is running. Tests: removed the tests of the deleted behaviour, ported the ones that describe retained local-queue behaviour (drain, FIFO, combined bubbles, notification suppression, stale prompt-RPC error gate, queue pane keys and mouse) and added bash_while_running_is_queued_locally. Verified locally under low resources (jobs=2, test-threads=2): pi-pager lib 5960 passed / 0 failed / 13 ignored; pi-pager-minimal 80; pi-shell lib 1138; pi-shared 101; pi-telemetry 244; pi-config-types 94; pi-pager-render 1084 (+ tmux_probe timing test passes in isolation). cargo check --all-targets for pi-pager and pi-pager-minimal is clean.
…metry stubs (A.3-3 3d-1) The inline plugin-install CTA (keyword-matched marketplace upsell above the prompt) and its marketplace-update check had no producer after the Rust agent runtime removal; every Effect was executed by a stub. Remove: - pi-pager: dispatch/cta.rs, tests/cta_e2e.rs, the CTA Effect/TaskResult variants, CtaPhase/PluginCtaState and layout row, event-loop marketplace override readers, their tests - pi-shell: dismissed-plugin-CTA config helpers, MarketplaceConfig.plugin_cta_marketplace - pi-config-types: RemoteSettings.plugin_cta - pi-telemetry: PluginCta* events - pi-pager: drop the unused pi-plugin-marketplace dependency Lib tests: pi-pager 5882 passed / 0 failed; settings_e2e 266 passed.
…and Claude-import flow (A.3-3 3d-2)
All four had no producer after the Rust agent runtime removal:
- views/mcps_modal.rs: unreferenced outside its own module
- McpInitProgress: seeded on every new session and only cleared by an
mcp_initialized notification the Python agent never sends, so the idle row
showed 'Starting session...' for 30 s and kept the tick loop on the fast path
- MCP elicitation card (views/elicitation_view, agent_view/elicitation.rs,
BlockingCard::McpElicitation): no code ever set elicitation_view /
pending_elicitation outside tests
- Claude-settings import (import_claude_modal, dispatch/import_claude.rs,
welcome menu row, Action::{ImportClaudeSettings,ImportClaudeConfirm,
ImportClaudeCancel,DismissClaudeImport}): has_claude_import was never set
Also restores the AgentView doc comment that had been glued to McpInitProgress.
Tests: pi-pager lib 5809 passed / 0 failed; settings_e2e 266; pi-pager-minimal 80.
Lost tests (73) all belong to the deleted features.
…ng-data-sharing (A.3-3 3d-3a) Compiler-driven removal of ACP ext calls the Python agent never serves: info/compact/history/agent-name/bundle/usage-modal effects, shell and prompt suggestions, feedback pane and trace offer, remember note, recap, doctor-fix, consent notice, privacy/announcement plumbing in the effect layer, and the coding_data_sharing setting (Privacy category, lock plumbing, SetCodingDataSharing). Tests: lib 5621 passed, settings_e2e 253 passed (low-resource run).
…ssion-picker source machinery (A.3-3 3d-3b/c)
The Python agent advertises no rewind, fork, jump or foreign-session
capability and every session it lists has source "local", so all of the
following were dead by data (not by rustc): rewind/jump/memory modals and
their dispatch, fork and foreign-session resume (with the pi-foreign-sessions
scan, compat and launch plumbing), inline prompt edit, content / deep search,
the source filter, lanes and card-detail fetches of the session picker, and
the restore-progress channel.
- Session picker: one FetchSessionList { seq } -> SessionListLoaded /
SessionListFailed round trip, single-variant PickerItem, stale-seq drop,
"No sessions found for this directory" / "Couldn't load sessions: ..."
toasts; filter indicator and header notes removed from the shared picker.
- Settings: ui.confirm_before_rewind removed end to end.
- Billing: Effect::FetchBilling / TaskResult::BillingFetched lose the nonce;
the handler stays a stub until the xAI auth layer goes (Stage D).
- pi-pager-minimal: follows the deleted Remember mode and /feedback card
(placeholder override, feedback editor, question-view feedback_input).
- pi-pager-bin: print mode no longer lists the removed --fork-session flag.
- Tests: recovered the live-feature tests (picker delete, navigation,
stale seq, list toasts, tick demand, expand toggle) in adapted form and
rewrote the Esc tests for the removed rewind arm; dead-feature tests are
deleted.
Verified under low resources (CARGO_BUILD_JOBS=2, CARGO_INCREMENTAL=0,
--test-threads=2): pi-pager --lib 5393 passed / 0 failed / 13 ignored,
settings_e2e 251 passed; cargo check --all-targets is clean for pi-pager,
pi-pager-bin and pi-pager-minimal (pi-shell keeps its 20 dead_code warnings).
pi-foreign-sessions (foreign coding-agent session scan; its last user went with the foreign-resume flow), pi-agent-lifecycle, pi-plugin-marketplace, pi-session-search and pi-shell-session-support have no dependents in the workspace and are not reachable from pi-pager-bin. pi-file-utils is dropped from pi-pager's direct dependencies (unreferenced there). The workspace goes from 90 to 85 members; Cargo.lock shrinks accordingly.
…h and the pi-hooks-plugins-types crate (A.3-3 3d-5)
The Python agent emits no hook/plugin notifications and the /hooks and
/plugins commands are already gone, so everything that rendered or
routed them was dead-by-data:
- pi-shell: SessionUpdate::{HookAnnotation, HookExecution, HooksChanged,
PluginsChanged, PluginUpdatesInstalled} + their DTOs, the
session::commands husk (HOOK_DENIED_CATEGORY)
- pi-pager scrollback: ScrollbackEntry.hook_data, tool/hook.rs,
tool/lifecycle.rs (ToolCallBlock::Lifecycle), verb-group hook counts,
hook hover popup, stop-hook folding into turn markers
(SessionEventBlock.stop_hooks/prompt_id, TurnBlockedByHook,
HookAnnotation), PendingStopHooks stash, the cancellationCategory
plumbing (TerminalSignal / PendingTurnEnd / cancelled_turn_event),
push_turn_terminal_marker's ending-prompt-id argument
- ToolCallBlock::label_kind() is now total (VerbGroupKind) and
verb_group_kind() derives from it
- CommandRegistry::set_plugins_visible + appearance.disable_plugins
(the commands it toggled are not registered)
- docs: hooks/plugins guides (09-plugins, 10-hooks, hooks-and-plugins,
custom-hooks) and their docs.rs / README entries
- workspace: pi-hooks-plugins-types is now orphaned and deleted; pi-pager
drops the dependency (85 -> 84 members)
40 tests removed with the code they covered (stop-hook stash/merge,
HookDenied marker, hook aggregation in verb-group headers); one rewritten
(estimate_uses_foldability_for_collapsed_shortcut) and one added
(no_event_pushes_no_marker).
Verified (low-resource): pi-pager --lib 5353 passed / 0 failed / 13
ignored, --test settings_e2e 251 passed; cargo check --all-targets clean
for pi-pager, pi-pager-bin and pi-pager-minimal.
… and the dead code they hid (A.4-1) Nine crates carried #![allow(unused_imports, unused_variables, unused_mut, unreachable_code, dead_code)] at the crate root. Removing them shows 9 dead functions in pi-workspace (fs_notify adapters, hub_transfer_client) and nothing elsewhere; the functions and the tests that only exercised them are deleted. File-level allow(dead_code) in pi-workspace is gone as well.
…behind them (A.4-2) The crate-root allow(dead_code) attributes are gone (4eb9f11), but rustc still cannot see dead items that are reachable through a `pub mod`. Lower pi-shell's module tree to `pub(crate)` (compile-driven: modules the consumers need are restored on E0603), then let the warnings drive the deletion of what is left: - 12 husk modules (session_registry_client, extensions/{billing,mcp, session_search,task}, session/{mcp_dispatcher,mcp_dispatcher_e2e_tests, mcp_restart,persistence_tests,restore_stub,memory}, resolve/system_prompt), config/watcher.rs and claude_import_state.rs - BackendClient husk and the remote SessionUpdate DTO, ConsumerKind and RequirementSource variants nobody builds, SessionDirMode (only one mode is ever constructed), the SyncOutcome/GoalPauseReason leftovers, the replay-preparation passes whose outputs nobody reads - the tests that only exercised the deleted code (checked with vac2/empties: no vacuous or empty test is left behind) `cargo check` of pi-pager, pi-pager-bin, pi-pager-minimal, pi-update and pi-shell with --all-targets reports 0 errors and 0 warnings. pi-shell lib tests: 1052 passed, 0 failed, 1 ignored; sandbox_requirements_pin and test_config_update_isolation pass.
Found with `-W unused-crate-dependencies` over `cargo check --workspace --tests` (separate target dir, JSON diagnostics attributed per unit), then cross-checked against the sources for cfg-gated uses. A dependency was only removed when it is unused in every unit of its package and its name does not appear in any source file; optional dependencies that no feature enables went the same way. Kept on purpose: the `cargo-shear` ignores (`pi-ratatui-inline` in pi-pager-minimal, `tokio-tungstenite` in pi-workspace, `serde` / `tonic` in pi-tools-api), `zstd` (behind pi-workspace's `compression` feature) and the Linux / macOS-x86_64 / Windows-only uses (`nix`, `libc`, `pi-sandbox`, `pi-tty-utils`, `image`). - pi-env: tracing, url - pi-file-utils: dunce, indexmap - pi-pager: obfstr, together with the dead `obf` module (its only macro had no caller left) - pi-shell: notify, notify-debouncer-mini, pi-mcp, pi-memory (also dev), sha2, plus the never-enabled optional tonic / tonic-prost / prost and the windows-only siphasher - pi-shell-base: anyhow, pi-extra-ca, pi-version - pi-telemetry: pi-env, obfstr (optional, never enabled) - pi-tools: pi-env, pi-workspace-types, tonic, and the never-enabled optional encoding_rs, kamadak-exif, unicode-normalization - pi-workspace: humantime-serde, pi-env, pi-fsnotify, tonic, the optional sha1 / zip, and the flate2 dev-dependency Cargo.lock loses 16 packages (aes, bzip2, bzip2-sys, cipher, deflate64, humantime, humantime-serde, inout, kamadak-exif, lzma-rs, lzma-sys, mutate_once, notify-debouncer-mini, pbkdf2, xz2, zopfli); the dependency graph of pi-pager-bin drops from 992 to 980 packages on x86_64-linux and pi-memory is no longer reachable from it. The consumer build and `cargo check --workspace --tests` (minus pi-fast-worktree) stay clean.
…dencies nobody uses (A.4-3) The dependency sweep left two library crates without a single dependent: pi-memory (10.7k lines, was only used by pi-shell) and pi-fsnotify (7.0k lines, was only used by pi-workspace). Like the crates removed in 86ea4c9, they go. Cargo.lock also loses file-id, notify-debouncer-full and sqlite-vec. Twenty `[workspace.dependencies]` entries were not referenced by any member any more (eight of them because of the previous commit: encoding_rs, humantime-serde, kamadak-exif, notify-debouncer-mini, sha1, siphasher, unicode-normalization, zip; the rest were already orphaned: alacritty_terminal, cryptify, env_logger, eventsource-stream, gethostname, glob, jsonschema, md5, moka, rsa, terminput, tokio-retry). They had no effect on the build; they are removed so that the next sweep does not trip over them.
…tures (A.4-4) The `local-workspace` feature was never enabled by any consumer build, so everything behind `#[cfg(feature = "local-workspace")]` was code rustc never compiled and no lint could flag. It is gone together with the feature: - `rsrm cfgfeat` deleted the 197 nodes whose cfg is false with the feature off (3.1k lines across 23 pi-pager files) and stripped the redundant `#[cfg(not(feature = "local-workspace"))]` attributes; - `views/welcome/workspace_mode.rs`, the `--local-workspace*` CLI flags, the per-session "on your machine" permission-title qualifier and the `session_flags` parameter of `finalize_chat_session_meta` / `session_flags_for_effects` went with it; - the feature lines and `default-bazel` entries are removed from the pi-shell-base, pi-shell, pi-pager and pi-pager-bin manifests. `cargo check --all-targets` on the consumer set is clean (0 warnings) and the touched test filters pass (358 passed).
`PagerArgs::chat()` and `process_chat_mode_enabled()` can never be true in
this fork (the gateway "chat" product is not reachable through standard
ACP), so everything keyed on them was dead by data, not by reachability, and
rustc could not see it:
- the `--chat` flag, `AppView.chat_mode`, `AgentView.chat_kind` /
`conversation_entry`, `Effect::{CreateSession,CreateWorktreeSession,
LoadSession}.chat_kind`, `DeferredStartupActions.pending_chat`,
`MaterializeCtx.chat_mode` and the `_meta.kind = "chat"` stamping /
scrubbing helpers
- the chat branches of the session picker, context bar, credit bar, welcome
screen and model setter; `source == "conversation"` picker rows and the
conversation delete path
- `pi-shell::agent::chat_modes`
- 20-odd tests that only asserted the chat behaviour
Folded with a syn-based constant folder, then pruned by the compiler: the
whole pager + shell set builds without warnings for all targets;
pi-pager lib 5327 passed, settings_e2e 251, grok_home_paths 2,
selection_model_public_api 2.
TUI CI run 37379203134 (Linux x86_64, rustc 1.94.0): graph 980 packages, 0 warnings, `--workspace --tests` clean, 8 suites 7478 passed / 0 failed. - graph ceiling 995 -> 980, warning ceiling 20 -> 0 - `reference_passed` is now the Linux result (it used to be the macOS one) and every suite gets a `min_passed` floor a few percent below it, so a drop in the test count has to be a deliberate edit of the manifest
Mechanical: `rustfmt --edition 2024 --config-path tui/rustfmt.toml` on the 216
files that this branch changed and that were not formatted. It repairs the
indentation fragments that the dead-code removal left behind (`use a::{b, };`,
blocks that lost their `if`) and normalises a few lines that were already
unformatted. Files the branch never touched are left alone, so the diff stays
reviewable on its own; skip this commit when reading the removal commits.
The agents modal was the last non-test user of pi_agent in pi-pager; keep the dev-dependency and drop the normal one.
- plan: status r5; 0.1 / 0.6 / A.3 / A.4 ticked with what was done and what was left on purpose; new section 10.7 (commits, audit outcome, scale, validation, leftovers); appendix A results; revision log - docs/baselines/tui.md: Linux numbers of TUI CI run 37379203134 (graph 980, 0 warnings, 7478 tests), five-triple graph sizes, min_passed floors, dependents of the tracked crates - baseline manifest: drop the two known_failures that died with the dashboard
- Welcome screen: drop the "New worktree" menu item, the ctrl+w shortcut and the New Worktree dialog (it could only fail with "Git worktree sessions are not supported in standard ACP mode"); menu is now Resume session / Quit. - Session picker: drop ctrl+w "resume in worktree" (PickSessionInWorktree, session_picker_worktree_selection, dispatch_pick_session_in_worktree) and its "worktree" shortcut hint. - Hero subtitle no longer advertises the deleted /feedback command. - Remove cloud_modal_open (const false) from is_scroll_blocking_modal_open and the now-unused LineEditor::insert_paste_with_byte_limit. Verified: pi-pager lib 5307 passed (was 5327; 20 worktree-dialog tests removed), settings_e2e 251, grok_home_paths 2, selection_model_public_api 2, 0 errors / 0 warnings on --all-targets; PTY smoke (boot/worktree/ui/resume) OK against the debug zypi.
…, part) The resume picker showed "<ISO timestamp> (<short id>)" for every session because the agent had no title to offer. session/list now returns: - title: the first user message of the session (whitespace collapsed, 100 characters at most, like pi's session selector), or "(no messages)"; - updated_at: the session file's modification time (every entry is an append), falling back to the header's created-at stamp. Only a bounded prefix (200 lines) of each file is read, off the event loop. No cursor pagination: the pager does not follow nextCursor, so everything stays in one page. Tests: 11 new (helper unit tests + an agent-level one); 609 passed in the mock suite. Verified in the TUI with a PTY run of /resume.
…stable methods The new tests/test_acp_stdio_contract.py spawns the agent process and talks JSON-RPC over its stdio like the TUI does: initialize, session/new, streamed prompt, the model config option, session/list titles, session/load replay and session/resume across a restart, session/close, unknown methods, exit 0 on stdin EOF. PI_ACP_CONTRACT_COMMAND points the same suite at another stdio ACP agent (for example a future pi-rust). It found a real bug: initialize advertises session/close and session/resume, but run_agent() ran without use_unstable_protocol, so the SDK answered both with "Method not found" (plus a UserWarning on stderr). __main__ now passes use_unstable_protocol=True. Plan items 0.3 / 1.P7 (partly). 617 passed in the mock suite.
…Stage 1 start - docs/baselines/tui.md: Linux column for ac1d3ff (TUI CI runs 37389447358 and 37389455793): 980 packages, 0 warnings, 7,437 passed; release-dist zypi 422,034,120 B (-10.3 %), cold build 1,155 s (-29.9 %), --version 23.1 ms. Step 5 notes that a manual run is not cancelled by PR pushes. - plan: 0.2 done; r5 verification rows, PTY findings, the leftover list (worktree flags, privacy banner, branding, graceful exit) and the Stage 1 items that started (1.P1 half, 1.P5 route bug, 0.3 / 1.P7 Python). - scripts/tui_baseline.toml: pi-pager reference_passed 5593 -> 5552.
…ing tools are reaped (1.P2) Measured with a scripted bash call: closing stdin already ended the agent with exit 0 and no tool left behind (asyncio.run cancels the turns, the bash tool kills its process group), but SIGTERM killed the process at once and the tool survived. - __main__.serve(): SIGTERM cancels the main task, the same path as EOF (POSIX only; Windows has no add_signal_handler and ends on EOF). - tests/_tool_agent.py + tests/test_acp_shutdown.py: the real serve() with a scripted long bash call; EOF and SIGTERM must exit 0 and reap the tool. Mutation-checked: without the handler the SIGTERM test fails (-15). - AGENTS.md: a 'Stopping the agent' section.
…a grace (1.P2, 1.R3) Quitting the TUI while the agent ran a bash tool used to SIGKILL the agent at once; the tool's process group then outlived it (PTY check: double Ctrl+Q, the 'sleep' kept running). The bridge now closes the agent's stdin, which the Python agent treats as 'the client is gone', waits up to AGENT_EOF_GRACE (3 s) for it to exit and reaps its tools, and kills it only if it does not. Stdout is drained while the agent stops so it never meets a closed pipe. - spawn.rs: stop_child() + StopOutcome, AGENT_EOF_GRACE (< AGENT_EXIT_GRACE, checked at compile time); two unit tests (exits on EOF / ignores EOF). - PTY check with the scripted agent: tool reaped after quit, quit still 0.3 s, mock /exit leaves no process and no stderr noise.
Found by killing the TUI in a PTY (kill -9 while a bash tool ran): the TUI was the session leader, so the kernel sent SIGHUP to its foreground process group, the agent died at once on the default action, and the tool's process group was left running. A closing terminal does the same. - __main__.serve(): SIGHUP takes the same path as SIGTERM and EOF (the signal set is looked up by name; Windows has neither handlers nor SIGHUP). - test_acp_shutdown.py: the signal test is parametrised over SIGTERM and SIGHUP. - PTY check, scripted agent running 'sleep 300': kill -9 zypi leaves the tool running with a SIGTERM-only handler and reaps it with SIGHUP too; closing the PTY master and SIGTERM to zypi reap it either way.
The stop-signal handlers made SIGTERM/SIGHUP harder to act on: if the graceful stop hangs (asyncio.run waits for a thread that cannot be cancelled) the agent could only be killed with SIGKILL. The handlers now remove themselves on the first signal, so a second one terminates the process as before. test_acp_shutdown.py: a stuck-thread mode of the scripted agent and a test that the second SIGTERM kills it (-15). Mutation-checked: without the self-removal the test times out.
zy1233
marked this pull request as ready for review
October 6, 2026 01:56
main gained the Phase 6/7 audit work (project trust, provider-scoped API keys, Model.reasoning, the workflow sandbox, prompts queued behind background turns) while this branch removed the Rust runtime and restored /model. Six files conflicted (17 hunks), all in packages/pi-agent-cli and pyproject.toml. Both sides' features are kept: - agent.py: main's deferred session setup (the trust question, advertising commands, the notices) replaces the plain deferred advertise. Session responses still carry configOptions and the per-session pi/* hints. _bind_session decides about trust first, then restores the persisted model choice and hands both to create_session_harness. - config.py: the trust fields and model_reasoning sit next to models, default_choice and model_choices. api_key_getter(env_name, provider) is main's provider-scoped hook, now for any model; make_get_api_key(config) is the [model] default. - factory.py: create_session_harness takes both trust and model_choice. model_for_choice carries Model.reasoning (CliConfig.model_reasoning, the same for every choice), and api_key_for_choice scopes a choice's key to its provider. - __main__.py, pyproject.toml, AGENTS.md: both sides' additions. New test: a chosen model keeps Model.reasoning and gets only its own provider's key.
zy1233
added a commit
that referenced
this pull request
Oct 10, 2026
…ds the merge order The ADR1 PR 1 branch and the mode_a branch were local when the plan and the baseline report were written, and said "not pushed" in eight places. They are pushed now: #11 (codex/adr1-pr1-python) on top of #10, #12 (codex/phase1-mode-a) on top of #11. Those places name the PRs instead; the r9 statements that were true when they were written stay, as the r10 entry already says. The r10 entry in the revision log gets the last step. The user asked to sync main to origin and merge the processed PRs. The stack has four (#9, #10, #11, #12), and the upper ones have nothing to merge into until the lower ones are in, so all four are merged, bottom-up, one squash each (the convention of #7 and #8), with the base moved to main before each merge and the tree of main compared with the branch's afterwards. Local main is 9 commits ahead of origin/main; all of them except one older docs commit (superseded by the revised one in #9) are already in #9's branch, so main is not pushed: after the merges it is reset to origin/main, and the old main stays on a backup branch. Docs only: the plan, docs/baselines/tui.md.
zy1233
added a commit
that referenced
this pull request
Oct 20, 2026
…used in pi-shell (plan decision 10) (#27) * refactor(tui): delete the goal prompt templates and the research script that nothing loads Nothing in the tree names these 15 files any more (git grep by file name: no hit). The last code that loaded them was removed with the Rust agent runtime in 455527c (#7); the files stayed, and rustc cannot see a file that no `include_str!` names, so no warning reported them. pi-shell/src/session/templates/ (14 Markdown prompt templates for the goal mode) and pi-shell/src/session/workflows/deep_research.rhai (584 lines). 15 files, 1,033 lines. No Rust code changes. * refactor(tui): delete the dead units session::worktree and session::helpers of pi-shell The call graph (scripts/tui_callgraph.py units) reports both as dead: nothing in the consumer build (pi-pager-bin, pi-pager-minimal, pi-update) reaches them. rustc does not warn, because what they hold is `pub(crate)` or a re-export. session::worktree (359 lines): a glob re-export of pi_workspace::worktree, two `From` impls between the shell's WorktreeType and the workspace's (nothing calls them), and 24 tests of types and functions that live in pi-workspace (serde shapes, build_candidate_list, BackgroundCopyContext, resolve_worktree_by_id_or_path). session::helpers (3 files, 61 lines): comments, and two tests of string shapes. The two `resolve_worktree_by_id_or_path` tests were the ones that opened worktrees.db in whatever GROK_HOME held; they are gone with the unit. pi-shell tests: 864 -> 838. pi_workspace::worktree has no user left in pi-shell after this; that is left for plan D.8. * refactor(tui): remove the [storage] section from the pi-shell config `[storage] cleanup_ttl_days` set how long old session files were kept. The code that read it went with the session store (#26), and the pager keeps no session file now. `StorageConfig` and the `storage` field of `Config` are deleted. A config file that still has the section loads. The section is reported as unrecognized, like any other unknown section: a new test pins that, and the "valid config" test no longer lists the section. `storage_mode` (the `--storage-mode` flag and `Config.storage_mode`) is a different setting. This change does not touch it. * refactor(tui): delete util::config::worktree of pi-shell `util::config::worktree` holds a `WorktreeType` enum (with `FromStr` and a `From` impl for `CreationMode`). Its one user was `session::worktree`, which the previous commit deletes. The baseline `check` shows it: the glob re-export of the module is an unused import (1 warning; the ceiling is 0). `pi-workspace` and `pi-workspace-types` have their own `WorktreeType` and do not use this one. The module has one test, `test_worktree_type_fromstr`; it goes with it. `pi-shell` has no other reference to `pi_fast_worktree` now, so that dependency goes in the next commit. * refactor(tui): remove the dependencies of pi-shell that no source file names Ten dependencies of `pi-shell` are not named in any source file of the crate outside comments (a text scan of `src/`, `tests/` and `build.rs`; the compiler has the last word): - `[dependencies]`: `urlencoding`, `base64`, `pi-fast-worktree`, `pi-compaction-transcript`, `pi-workflow`, `tdigests`, and `nix` (unix only). - `[dev-dependencies]`: `pretty_assertions`, `pi-test-utils`, `tokio-util`. Eight lost their last user in #26 (the session store). `pi-fast-worktree` lost its last users in `session::worktree` and `util::config::worktree` (this branch), and `tokio-util` in the tests of `session::worktree`. The dependency graph of `pi-pager-bin` goes from 980 to 970 packages on Linux: `pi-workflow` (a crate of this workspace; no other crate depends on it now) and nine packages from the registry (`tdigests`, `rhai`, `rhai_codegen`, `smartstring`, `thin-vec`, `const-random`, `const-random-macro`, `crunchy`, `tiny-keccak`). `Cargo.lock` loses `tdigests` and ten dependency lines. The other packages stay in the lock file, because `pi-workflow` is still a member of the workspace. `THIRD-PARTY-NOTICES` loses 5 entries (it lists the packages that ship on any platform; `tui_notices.py check`: 984 of 984). * chore(tui): lower the ceilings of the baseline after the cleanup of pi-shell - `[gates.residue]`: `leader` 245 -> 243 and `x.ai` 304 -> 297. The deletions of #26 removed those lines; `check` has said "lower the ceiling" since then. - `[graph.max_nodes]`: 980 -> 970 packages on Linux (the dependency commit of this branch). - `pi-shell` `reference_passed`: 864 -> 838. The tests of `session::worktree` (24, Unix only), `session::helpers` (2) and `util::config::worktree` (1) are gone, and one test is new (a `[storage]` section is reported as unrecognized). The ten references add up to 8,488 (8,514 before). A local run on Linux (WSL2) gives: `check` with 0 warnings, 970 packages, `leader` 243 and `x.ai` 297; the ten test suites 8,487 passed and 1 failed (the known `tmux_probe` test of `pi-pager-render`); `tui_notices.py check` 984 of 984. * docs: record plan r16 (the merge of #25 and #26, and what the session store left in pi-shell) - CHANGELOG.md: the entry for the third cleanup of pi-shell (15 files, three modules, the [storage] setting, 10 dependencies). It also says that the earlier entry left `[storage] cleanup_ttl_days` for this one. - docs/baselines/tui.md: current baseline r16 (tests 8,488, graph 970, `leader` 243, `x.ai` 297) and a column r16 in the table of sizes. The numbers are from local runs (WSL2); the CI numbers of PR #27 follow in a later commit. - docs/baselines/archive/tui-history.md: the r16 paragraph, with the CI numbers of `7d82c27`. - docs/baselines/tui-support.md: made again (`pi-shell` has 7 dead units, 1,008 lines, not 9 and 1,388). - Plan: status r16, next steps, decision 10, D.2, D.8 (new candidates: 7 dead units, the crate `pi-workflow`, `Config.storage_mode`, about 88 files that no code loads), D.9, a new open item "races between tests", the index and the revision list. - Execution log: section 10.19, B.11 (how to repeat the checks) and the r16 revision entry. * docs: record the CI result of PR 27 in the plan, the log and the baselines `CI` (run 39193752450) and `TUI CI` (run 39193752451) pass on 6bd7db5. `TUI CI`: 8,488 passed, 0 failed, 21 ignored; 970 packages in the graph; `leader` 243, `x.ai` 297; 0 warnings. These are the numbers that section 10.19 of the execution log expected, so only the "to be added" line and the timing bullet change, plus one sentence in each r16 entry. The cargo test job took 17 m 34 s and the gates job 4 m 24 s. The tests themselves took about 54 s of that time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Removes the in-process Rust agent runtime from the vendored TUI (
tui/, binaryzypi). The TUI is now a pure ACP client; the Pythonpi_agent_cliis the only agent. This clears the way for a futurepi-rustagent, which only has to be a stdio ACP executable (PI_AGENT_COMMAND).pi-sampler,MvpAgent, the sampler/provider stack, the headless runner and the whole leader mode are gone (682 files in one compiler-verified commit)./modelis kept, through ACP Session Config Options: the Python agent advertises acategory: "model"select option and routessession/set_config_option; the pager builds its model picker from it (legacysession/set_modelstays as a fallback).tui-ci.yml+scripts/tui_baseline.py+ manifest; enforced on Linux (toolchain pin, deny-list, dependency-graph ceiling, zero warnings,cargo check --workspace --tests, 8 test suites with floors). The report isdocs/baselines/tui.md, now including the release-profile numbers.settings,new,model,resume,theme,multiline,home,help,exit). The crate-rootallow(unused, dead_code)attributes are gone and the consumer build has 0 warnings; unused dependencies and crates are deleted; two always-false worlds (chat_mode,cfg(feature = "local-workspace")) are removed; the welcome screen no longer offers the dead "New worktree" entry or advertises/feedback.session/listreturns real titles (the first user message) andupdated_at(file mtime); the resume picker used to show ISO timestamps. Nocursorpaging yet: the pager ignoresnextCursor.packages/pi-agent-cli/tests/test_acp_stdio_contract.py, runnable against another agent withPI_ACP_CONTRACT_COMMAND). It found thatinitializeadvertisedsession/closeandsession/resumewhile the SDK answered "Method not found";run_agentnow runs withuse_unstable_protocol=True.zypiwhile the agent ran abashcall SIGKILLed the agent and the tool's process group kept running. stdin EOF already made the Python agent exit cleanly (exit 0, tools reaped) and SIGTERM did not (−15, tool left behind). Now SIGTERM and SIGHUP take the EOF path (SIGHUP is what the kernel sends to the foreground process group when the TUI, as session leader, is killed or the terminal closes), and the pager closes the agent's stdin, waits up toAGENT_EOF_GRACE(3 s), and only then kills it.-pprint mode refuses--sandbox(it used to run unsandboxed silently) and reports the flags it drops on stderr; the full flag contract is Stage 1 (1.P6).main. Another machine pushed 7 commits tomainwhile this PR was open (the Phase 6/7 audit work: project trust, provider-scoped API keys,Model.reasoning, the workflow sandbox, queued prompts, 0.5.0). None of them touchestui/. Six files conflicted (17 hunks:agent.py,config.py,factory.py,__main__.py,pyproject.toml,packages/pi-agent-cli/AGENTS.md), all on the Python side, and both sides are kept. The seam: anapi_key_envis the key of its own provider only, now for every/modelchoice too (api_key_getter(env_name, provider));model_for_choicecarriesModel.reasoning, so switching model keeps it;_bind_sessiondecides project trust first and then restores the saved model. One new test covers the seam.git show --remerge-diff 9b71a57shows only what was resolved by hand.Size: 1,485 files changed, +11,177 / −625,252 against
mainat7e7d4d4. Tracked.rslines undertui/: 1,650,814 → 1,049,333;.rsfiles 2,577 → 1,832;tui/Cargo.lockpackages 1,311 → 1,257; codegen crates 74 → 65. Release-profilezypi: 422,034,120 B (−10.3 %), cold CI build 1,155 s (−29.9 %),--versionmedian 23.1 ms (numbers atac1d3ff).Plan and execution record:
docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md(r5, section 10.7 for this round). The merge ofmainis recorded in section 10.9, which lands with #8.Review guide
54 commits (53 plus the merge of
main). Most of the diff is deletion; the Rust commits are meant to be read one at a time and are mechanical (a compiler-driven cascade starting from one removed entry), so review them as "does it build, do the remaining tests pass, is what stays on the whitelist".9d3b4b39add266/modelon the pager side.bf4b79f,0a081f4,61126ed,cca48b0,d775a54,259b056,52a787f,34fbd48,ea5a60d3117858,e20af45,56550ac,27df705a648e92,aeaf9b6,0703d6a0a58b81,57c0d80-pfails closed on--sandbox; changelog / release-notes removal6983c99...98e29c9,7ff908b5fcec14,ac1d3ffchat_modeworld; dead worktree entries and the/feedbackhint4eb9f11,ab77740,fbcaf14,886d5af,86ea4c9,0e71894,dbd4de6pubtopub(crate), unused dependencies and crates,local-workspacecfg50ccfb1,5163592,f14e321,421d497,b617575session/listtitles, stdio contract suite +use_unstable_protocol, SIGTERM / SIGHUP stop the agent like EOF, a repeated signal still kills (with the shutdown tests)e8557e5acp/spawn.rs)fe1fc65git diff -wshows nothing).9b71a57main(Phase 6/7 audit work): the 6 conflicted files resolved by keeping both sides, plus one test for the/modelseam.git show --remerge-diff 9b71a57shows the hand-resolved part only.Verification
TUI CIonb617575(the last commit with code changes): PR run 37394965723 is green: dependency graph 980 packages (ceiling 980), 0 warnings, consumer build andcargo check --workspace --testspass, 8 suites 7,439 passed / 0 failed / 20 ignored (enforced; the 2 more than atac1d3ffare the unit tests ofacp/spawn.rs). The manualrelease_baselinerun 37389455793 onac1d3ffis the source of the release-profile numbers. The docs-only headea5a60dis green too:TUI CIrun 37397452910,CIrun 37397453697 (Ruff, gates + cargo check, cargo test, pytest on 3.11 / 3.12 / 3.13).main(head9b71a57), Linux CI is green:TUI CIrun 37436336738 (gates pass, 8 suites 7,439 passed / 0 failed / 20 ignored, the same as before the merge, asmaintouches nothing undertui/) andCIrun 37436336636 (Ruff, pytest on Python 3.11 / 3.12 / 3.13). On macOS the fullpytestrun gives 1,999 passed and 6 failed; the same 6 fail onorigin/mainitself (7e7d4d4) and none fails on Linux CI. They are macOS-only:AGENTS.mdandAGENTS.MDname the same file on a case-insensitive file system, socontext_files.pyloads it twice (a real bug since Phase 5, newly exposed by a test inmain); macOS refuses a non-UTF-8 file name; and 4 workflow-sandbox tests fail because on macOS the sandbox reports norlimit-aslayer, a child process and a 1 GiB allocation are not stopped without the audit hook, and the child sees macOS's own__CF_USER_TEXT_ENCODING. I did not touch them. PTY smoke with the merged agent (mock LLM): boot, a turn,/modelwith two models, switching and back,/exitreturning 0 with no leftover agent.pi-pagerlib 5,307 passed / 0 failed / 13 ignored atac1d3ff;settings_e2e251;grok_home_paths2;selection_model_public_api2;--all-targetshas 0 errors and 0 warnings. Ate8557e5:cargo test -p pi-pager --lib acp::spawn10 passed (2 new); the debug build has 0 warnings.zypi+ the Python agent): boot (about 3 s to a ready session), command palette,/settings,/theme,/model, a turn,/exitand relaunch +/resumewith history replay all work;/exitreturns 0 and leaves nopi_agent_cliprocess behind. The later runs used the agent's mock LLM (PI_USE_MOCK=1): the free OpenRouter slug used earlier now answers 404. With a scripted agent runningexec sleep 300: double Ctrl+Q exitszypiwith 0 in 0.4 s and the old binary left thesleeprunning, the new one does not;kill -9 zypi(the agent then gets SIGHUP),kill -TERM zypi, closing the terminal and a plain Ctrl+C leave nosleepbehind either. Full table in the plan, section 10.7.pytest -m "not real_llm"621 passed (was 598 at r4: +11session/list, +8 stdio contract, +4 shutdown);ruff checkandruff format --checkpass. LinuxCI(Python 3.11 to 3.13) onb617575: green, run 37394965713; it includes the stdio contract suite and all four shutdown tests. The shutdown tests were mutation-checked: without the SIGTERM handler that test fails with −15, without SIGHUPkill -9 zypileaves the tool running, and without the handlers' self-removal the repeated-signal test times out.Not verified
stop_child, not a PTY smoke.kill -9 zypiwhenzypiis not the session leader (the agent then only sees stdin EOF; covered by a test of the EOF path, not end to end), and MCP child processes.-w/--worktree,--worktree-ref,--restore-code, the/newworktree modes, the "remote restore" path and the welcome privacy banner (plan section 10.7, item 3). No UI entry reaches the worktree ones any more.Next (follow-up work, not part of this PR)
cursorpaging forsession/list(the pager has to follownextCursorfirst), the-pflag contract (1.P6), ADR1 (the pager stops reading session files from disk),stderrdestination of the agent./themenames, desktop notification title,zypi doctor, the disk-usage hint) are being renamed in a separate PR stacked on this one.tui/NOTICEand third-party notices.async-openaiout of the tree, session data layer, xAI auth layer).