Skip to content

Hold update relaunches on the shared agent classifier - #17824

Closed
azooz2003-bit wants to merge 26 commits into
mainfrom
feat/update-gate-agent-activity
Closed

azooz2003-bit wants to merge 26 commits into
mainfrom
feat/update-gate-agent-activity

Conversation

@azooz2003-bit

Copy link
Copy Markdown
Collaborator

Summary

Stacks on #15296 and #15276, and must merge after both. The branch contains a merge of both.

The update relaunch gate from #15296 classified agents from hook lifecycle state alone, which cannot tell a model request from a foreground build, so any mid-turn local agent counted as risky and held automatic installs. This switches the gate to the shared classifier from #15276 (AgentActivityIndex), which also reads the pane's process tree.

Mapping, per AgentActivitySnapshot (a pure static AppDelegate.updateRelaunchBlockers(agents:workspaceTitles:shellPanels:)):

  • id is the panel id; location is the workspace title looked up by workspaceID; name is the agent's display name from its kind (the snapshot's name is the pane title, so it is not used).
  • A pane that survives the relaunch (cmux ssh or cloud) is safe, with "Keeps running on the remote host".
  • Any other agent takes the classifier's safety as is: safe, care or risky. Only a running command, an open question or permission, or a draft is risky; a model request in flight is care and resumes.
  • The activity line is the tool command (whitespace collapsed, cut at 60 characters), else the tool name, else a label for the activity kind. A question or permission prompt always says it is waiting on you.
  • A local panel with no live agent that is running a foreground command still counts as a running command. Agents whose activity is ended count as plain panels.

The classifier reads the process census asynchronously, so UpdateActionDelegate.updaterRelaunchBlockers() is now async. UpdateRelaunchGate awaits its readiness closure on the first publish, every re-check and after prepare, and re-checks that the hold is still current after each await. An install the user asks for reads the blockers in a main-actor task before deciding to relaunch or ask. The interim hook-lifecycle classifier is removed; midTaskPanelIds and UpdateRelaunchContinuationNudges stay.

New activity labels are localized in all nine app locales.

Testing

  • swift test in Packages/macOS/CmuxUpdater: 146 tests passed, three runs. The gate tests now wait for the hold's first async publish before asserting on it; the behaviors they check are unchanged.
  • cmuxTests/UpdateRelaunchBlockersTests.swift is rewritten for the mapping. Not run locally: it needs the app test target, so CI runs it.
  • python3 scripts/verify-local.py (16/16 passed, including Swift syntax, test wiring and localization parity) and python3 scripts/localization_catalog.py check (0 parity errors).
  • The app was not compiled or run locally.

Changelog

Changed: Automatic updates no longer wait on an agent that only looks busy; they wait only for agents running a command or waiting on you

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI, settings, menu, schema, help-text or user-facing docs change: localization audited, six new update.agentActivity.* keys with all nine locales
  • Reviewed with a subagent before merge, and all bot and human review comments resolved

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Switches the update relaunch gate from hook lifecycle state to the shared AgentActivityIndex classifier, so an automatic install only waits on agents running a command, blocked on a prompt, or sitting on a draft — a mid-turn agent that is only thinking resumes as care and no longer holds the install. Adds an Install Updates Automatically setting (on for nightly builds) where updates download in the background and install at a quiet moment. The interim hook-lifecycle classifier is removed.

Agent classification

  • The gate now reads AgentActivityIndex snapshots; the blockers read is async and re-awaited on every check.
  • Only a running command, an open question or permission, or a draft is risky; a model request in flight resumes as care.
  • A cmux ssh or cloud pane counts as safe because it keeps running on the remote host.
  • A local pane with no live agent but a foreground command still counts as a running command.
  • The classifier also powers a new local-only agent.list worker surface.

Automatic installs

  • New "Install Updates Automatically" setting, on by default for nightly, drives Sparkle's background downloads.
  • An explicit install relaunches as soon as session capture finishes; automatic installs wait for quiet.
  • Every relaunch first captures fresh resume indexes, so sessions started since the last scan still resume.
  • Interrupted agents resume with a continue-nudge prompt; unused nudges expire.

Written for commit 0596e2e. Summary will update on new commits.

Review in cubic


Migrated from #15417 after correcting the PR author identity. The original head commit 0596e2e is preserved.

teamleaderleo and others added 26 commits September 28, 2026 04:58
…launch right away

An explicit Install and Relaunch, Restart Now or Install Now relaunches as soon
as the app has captured its sessions (#15084). With the new Install Updates
Automatically setting (on by default for nightly), Sparkle downloads updates in
the background and the relaunch waits for no busy agent, no running command and
a minute without input. Every update relaunch first takes a fresh process scan,
so agents started since the last scan are saved as running and resume.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Safe and care agents resume after the relaunch, so only risky agents (a
foreground command, an unanswered prompt) and other running commands hold it.
An install the user asks for relaunches right away unless something is risky;
then the popover lists every agent with a safety chip and offers Wait, Update
When These Finish and Update Anyway. Remote cmux ssh agents keep running on
their host and count as safe.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AgentActivity splits the coarse working/needs-input lifecycle into what an
agent is doing now (thinking, a tool with its command and start time,
subagents, background work, a question, a permission) and ResumeSafety
classifies whether a restart or update can interrupt it (safe, care, risky,
with reasons). Pure package code with tests; the app wiring follows.

Refs #15266

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AgentHookActivityState folds one session's queued hooks (prompt submit,
pre and post tool use by tool_use_id, stop with background work, idle
notification, session start and end) into turn facts. AgentActivityEvidence
combines them with the session registry state, the Feed decision overlay
and a foreground command into AgentActivitySignals. AgentForegroundCommand
picks the command an agent runs through a foreground shell child, so MCP
servers and background shells do not count.

Refs #15266

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The current-work reducer built a Cloud or SSH agent row with the badge's
report provenance (hook, plugin, detected) as its kind and the raw daemon
state, so a blocked remote agent never raised needs_input attention.
SurfaceAgentBadge now exposes agentIdentity (the adapter, shared with the
sidebar slot key) and sessionState (blocked is needs_input, done is ended),
and carries extra.agent_session_id from the cmux-tui catalog so the row
gets its session id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AgentActivityIndex joins the agent session registry to live workspace and
dock panels in one main-actor turn, adds each session's hook turn facts,
the Feed decision overlay and, for local panes, a foreground command from
one cached process census, then classifies. Each entry carries workspace,
panel, surface and pane ids, name, agent, session, pid, placement (local,
ssh with its host, cloud), survives_app_relaunch, activity and
resume_safety.

Claude now installs an unmatched queued PostToolUse hook. agent.hook.enqueue
records every admitted hook in AgentHookActivityTracker before queueing,
and a Claude post-tool-use only closes the open call: no hook process runs
for it.

agent.list is a worker-lane read advertised in capabilities. It has no
relay contract because it returns local argv, and a test pins that.

Refs #15266

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tic update

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n nudges

A menu install while risky agents hold an automatic update switches the
popover to asking instead of stopping them. A continuation nudge the
restore never used expires after ten minutes, and a failed relaunch stops
marking panels after a minute.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review fixes for the activity index:
- A compaction, resume or unknown SessionStart keeps the running turn;
  only startup and clear reset it.
- Placement comes from the surface (cloud attachment, owning machine,
  remote terminal context), so a local pane in a remote workspace no
  longer reports survives_app_relaunch.
- An unavailable or partial process census, or a local agent without a
  pid, sets foregroundCommandUnknown; the classifier never calls that
  safe (reason process_unknown).
- Failed, denied and interrupted calls close: Claude also sends
  PostToolUseFailure to post-tool-use, an idle prompt ends the turn, and
  a later call of another tool closes a pending question. Both
  post-tool-use hooks are async.
- Compaction keeps tool_use_id and agent_id; a post without an id closes
  the newest same-named call.
- started_at is the PreToolUse time; since moves only on a change.
- Subagent hooks and late posts never reopen a finished turn; hooks
  decide the turn only after they have seen a boundary.
- Process filtering starts at the turn start, else at the last idle
  point, so older shells (such as shell-wrapped MCP servers) are ignored.
- Relayed question PreToolUse is ignored: remote daemons send no
  PostToolUse, and the Feed overlay covers remote questions.
- agent.list errors when the session owners are unavailable instead of
  returning an empty list, and redacts tool commands and argv.

Refs #15266

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…types

package-conventions-lint flags caseless all-static enums. The classifier
becomes AgentActivitySignals.classify() (plus AgentActivityEvidence.classify()),
with the read-only and subagent-launcher tool sets on AgentActivity.Tool.
AgentForegroundCommand becomes AgentProcessTree, an instantiated census with
foregroundCommandPID(agentPID:notBefore:) and a static describe(arguments:).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The CLI's redaction policy is not compiled into the app target. The
socket is same-user and never relayed, and the updater shows these
commands to the user, so report them as recorded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A CMUX_UI_TEST_* variable marks the process as a test host, which never
starts the updater, so the DEV-build dogfood opt-in needs its own feed
variable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	cmux.xcodeproj/project.pbxproj
…o-update

# Conflicts:
#	cmux.xcodeproj/project.pbxproj
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	tests/test_claude_wrapper_hooks.py
The update relaunch gate now reads AgentActivityIndex snapshots instead of
hook lifecycle state, so a mid-turn agent that is only thinking resumes as
care, and only a running command, an open question or permission, or a
draft holds the relaunch. The updater's blockers read is now async and the
gate awaits it on each check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ent-activity

# Conflicts:
#	cmux.xcodeproj/project.pbxproj
@coderabbitai

coderabbitai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Note

Pull Request opener @azooz2003-bit is not an author or co-author of any commit in this PR (commit identities: teamleaderleo, claude). The CLA check will still proceed and requires every listed identity plus @azooz2003-bit to have signed.

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants