feat(desktop): let the agent reap background processes + neutral signal-kill rendering - #49809
Conversation
…ling signal-kills as failures
Two gaps in the background-process status stack:
1. REAP CAPABILITY — finished/killed background entries piled up with no way
for the agent (or a re-poll-safe UI action) to evict them. The agent could
`kill` a LIVE process but had no primitive to drop a FINISHED one from the
registry, so the desktop "Background" group accumulated dead rows that only
cleared on a 30-min TTL or app restart.
- ProcessRegistry.remove(session_id): evict one finished entry; refuses a
running process (returns status 'running' — kill it first); idempotent.
- ProcessRegistry.clear_finished(task_id?, session_key?): evict all finished
entries, optionally scoped.
- Gateway: process.remove (session-scoped, 4044 if not owned, 4090 if still
running) and process.clear_finished (caller-session-scoped), mirroring the
existing process.kill scoping.
- Agent `process` tool: new `remove` and `clear` actions so the agent can
up/down background sessions during its own iteration.
- Desktop: dismissBackgroundProcess now fires process.remove so the eviction
is registry-authoritative (other windows + re-polls won't resurrect it),
plus a "Clear finished" accessory on the Background group header that
bulk-reaps via process.clear_finished.
2. SIGNAL-EXIT RENDERING — a negative exit code is a signal termination
(kill_process sets -15 = SIGTERM), i.e. a DELIBERATE teardown, not a crash.
It was painted the same scary red "failed" with a raw "exit -15" badge as a
genuine exit-1 failure. Now exitCode < 0 maps to a new neutral 'stopped'
state (muted dot + muted badge) and the badge renders the signal NAME
(SIGTERM/SIGINT/SIGKILL) instead of a raw negative number. A real non-zero
exit still renders red "failed" with "exit N".
i18n: statusStack gains `stopped` and `clearFinished` (en/ja/zh/zh-hant +
types.ts).
Tests: process_registry remove/clear_finished (evict, refuse-running,
idempotent, session-key scoping — 80 pass); composer-status (negative exit ->
'stopped', exit 1 -> 'failed', clearFinished keeps running rows). tsc clean.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing two real current gaps: main still has no finished-process reap operation, and tools/process_registry.py:1503-1505 deliberately assigns killed processes exit code -15 while the desktop currently renders it failed.
Problems
apps/desktop/src/store/composer-status.ts:138treats every negative exit as a deliberate stop. That also catches the real backend-loss path, which setsexit_code = -1,completion_reason = "lost", andtermination_source = "backend_lost"intools/process_registry.py:1025-1029.- The new desktop RPC writes discard all failures at
composer-status.ts:240and:251; the local dismissal can therefore hide a row even though the registry did not reap it.
Suggested changes
- Carry an explicit termination reason to the desktop and classify only confirmed kills as stopped; test backend loss separately.
- Await reap RPCs and reconcile/report an error on failure. Add gateway tests for owner scoping and the running-process refusal.
Salvage will require conflict-aware reapplication because the current process RPCs and tool session scoping now live at tui_gateway/server.py:11481-11513 and tools/process_registry.py:2217-2248.
Automated hermes-sweeper review.
|
|
||
| const state: StatusItemState = !exited ? 'running' | ||
| : exitCode === undefined || exitCode === 0 ? 'done' | ||
| : exitCode < 0 ? 'stopped' |
There was a problem hiding this comment.
exitCode < 0 is not synonymous with a deliberate signal kill: current ProcessRegistry uses exit_code = -1, completion_reason = "lost", and termination_source = "backend_lost" when the environment disappears (tools/process_registry.py:1025-1029). Please classify from an explicit termination reason, or at minimum keep that loss path failed.
feat(desktop): let the agent reap background processes + stop mislabeling signal-kills as failures
Two gaps in the background-process status stack:
REAP CAPABILITY — finished/killed background entries piled up with no way
for the agent (or a re-poll-safe UI action) to evict them. The agent could
killa LIVE process but had no primitive to drop a FINISHED one from theregistry, so the desktop "Background" group accumulated dead rows that only
cleared on a 30-min TTL or app restart.
running process (returns status 'running' — kill it first); idempotent.
entries, optionally scoped.
running) and process.clear_finished (caller-session-scoped), mirroring the
existing process.kill scoping.
processtool: newremoveandclearactions so the agent canup/down background sessions during its own iteration.
is registry-authoritative (other windows + re-polls won't resurrect it),
plus a "Clear finished" accessory on the Background group header that
bulk-reaps via process.clear_finished.
SIGNAL-EXIT RENDERING — a negative exit code is a signal termination
(kill_process sets -15 = SIGTERM), i.e. a DELIBERATE teardown, not a crash.
It was painted the same scary red "failed" with a raw "exit -15" badge as a
genuine exit-1 failure. Now exitCode < 0 maps to a new neutral 'stopped'
state (muted dot + muted badge) and the badge renders the signal NAME
(SIGTERM/SIGINT/SIGKILL) instead of a raw negative number. A real non-zero
exit still renders red "failed" with "exit N".
i18n: statusStack gains
stoppedandclearFinished(en/ja/zh/zh-hant +types.ts).
Tests: process_registry remove/clear_finished (evict, refuse-running,
idempotent, session-key scoping — 80 pass); composer-status (negative exit ->
'stopped', exit 1 -> 'failed', clearFinished keeps running rows). tsc clean.