Skip to content

Refactor agent resume specs#3122

Open
lawrencecchen wants to merge 3 commits intomainfrom
task-dry-restorable-agent-resume-spec
Open

Refactor agent resume specs#3122
lawrencecchen wants to merge 3 commits intomainfrom
task-dry-restorable-agent-resume-spec

Conversation

@lawrencecchen
Copy link
Copy Markdown
Contributor

@lawrencecchen lawrencecchen commented Apr 22, 2026

Summary

  • refactor Claude, Codex, and OpenCode resume handling into declarative provider specs instead of hard-coded builder branches
  • keep provider-specific launcher, flag, and environment policies explicit while making new CLI support easier to add in one place
  • add unit coverage for provider-specific environment allowlists on resumed commands

Testing

  • xcodebuild test -project GhosttyTabs.xcodeproj -scheme cmux-unit -destination 'platform=macOS' -derivedDataPath /tmp/cmux-task-dry-resume-spec-tests -only-testing:cmuxTests/SessionPersistenceTests
  • ./scripts/reload.sh --tag dry-resume-spec

Issues

Summary by CodeRabbit

  • Refactor

    • Session resume logic moved to a policy-driven pipeline for consistent option preservation and launcher-specific invocation behavior.
  • Bug Fixes

    • Resume command generation now sanitizes and normalizes forwarded arguments; only --image/-i is preserved for Codex, while --add-dir is no longer forwarded.
  • Tests

    • Added/updated tests validating provider-specific environment allowlists (e.g., CODEX_HOME, OPENCODE_CONFIG_DIR) and resume-script content.

Summary by cubic

Refactored agent resume logic to use declarative provider specs and tightened option handling. Codex now preserves repeatable --add-dir while only --image/-i consume multiple values; tests cover this and per‑provider environment allowlists.

  • Refactors
    • Introduced AgentResumeProviderSpec and AgentResumeOptionPolicy with declarative flag handling, dropped/reject lists, and session placement; unified resume command building and moved safe env keys to per‑provider allowlists (with tests).
    • Provider-specific arg handling: strips internal OpenCode TUI worker paths, preserves first positional for opencode, skips Claude hook settings, respects Codex resume positional rules.
    • Codex: narrowed greediness to --image/-i; --add-dir is preserved as repeatable (not greedy). Updated CLI to stop treating --add-dir as variadic.
    • Launcher overrides: claudeTeams runs as cmux claude-teams, omo as cmux omo; omx and omc resume is not supported.

Written for commit 3998818. Summary will update on new commits.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 22, 2026 11:43pm

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

Resume-command generation in RestorableAgentSession is restructured from imperative per-kind branching to a policy-driven resumeProviderSpec pipeline that centralizes option preservation, launcher overrides, session-placement, argument sanitization, and provider-specific safe environment key allowlists.

Changes

Cohort / File(s) Summary
Resume Command Refactoring
Sources/RestorableAgentSession.swift
Replaces hardcoded per-kind resume logic with resumeProviderSpec that provides option-preservation policy, launcher override mapping (including unsupported markers), session-argv placement strategies, argument sanitization hooks, and per-kind safe-environment allowlists. resumeArguments now uses the spec pipeline and removes former per-kind helpers and global allowlist.
Tests: Session Persistence & Env Allowlist
cmuxTests/SessionPersistenceTests.swift
Updates tests to expect additional preserved hook flags in Codex resume invocations, ensures startup script text no longer contains a removed literal, and adds assertions verifying provider-specific environment allowlisting for Codex (CODEX_HOME) and OpenCode (OPENCODE_CONFIG_DIR).
CLI Argument Forwarding
CLI/cmux.swift
Adjusts Codex CLI argument forwarding sanitization to preserve only --image/-i as variadic options; previously-preserved --add-dir variadic options are no longer retained when constructing forwarded Codex invocations.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant RestorableAgentSession
    participant ResumeProviderSpec
    participant PreserveOptions
    participant SessionPlacement
    participant Launcher
    participant EnvironmentFilter

    Client->>RestorableAgentSession: request resumeArguments(snapshot, launcher)
    RestorableAgentSession->>ResumeProviderSpec: select spec (launcherOverrides/defaultInvocation)
    ResumeProviderSpec->>PreserveOptions: apply option-preservation policy
    PreserveOptions-->>ResumeProviderSpec: sanitized args
    ResumeProviderSpec->>SessionPlacement: build argv with sessionId placement
    SessionPlacement-->>RestorableAgentSession: final argv
    RestorableAgentSession->>EnvironmentFilter: filter env via safeEnvironmentKeys
    RestorableAgentSession->>Launcher: produce resumeCommand (argv + filtered env)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰
I nibbled switches, tangled and long,
Now specs hum a tidy song.
Options kept where rules decree,
Env keys guarded just for me,
Hopping forward—resume, set free!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Refactor agent resume specs' accurately reflects the main change: converting resume command generation from hardcoded branching to declarative provider specs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description comprehensively covers what changed (refactored resume handling to use declarative specs), why (make provider policies explicit, easier to add CLI support), testing approach (provided xcodebuild and script commands with specific scheme), and includes related issue reference.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-dry-restorable-agent-resume-spec

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 22, 2026

Greptile Summary

This PR replaces the monolithic AgentResumeCommandBuilder helper statics (three separate preserved*Arguments methods and a single isSafeEnvironmentKey switch covering all providers) with a declarative AgentResumeProviderSpec struct attached to each RestorableAgentKind. Session placement, option policies, launcher overrides, and per-provider safe-environment allowlists are now co-located in one place per provider, making future CLI additions a single-struct change. The refactoring is behaviorally faithful: all prior dispatch paths are reproduced through the spec, and a new unit test validates the key observable difference (env-key allowlists are now provider-scoped rather than shared).

Confidence Score: 5/5

Safe to merge — all findings are minor style/clarity suggestions with no correctness impact.

The refactoring preserves all prior dispatch paths (launcher overrides, default invocations, option policies), and the behavioral change (env-key allowlists split per provider) is intentional, tested, and consistent with the PR goal. The three P2 comments concern function visibility, enum-case naming clarity, and a missing comment on silent fallback — none affect runtime correctness.

No files require special attention.

Important Files Changed

Filename Overview
Sources/RestorableAgentSession.swift Replaces three monolithic per-provider static-method blocks with a declarative AgentResumeProviderSpec struct; behavior is preserved and the new dispatch is correct. Minor style concerns: isOpenCodeInternalWorkerArgument visibility widened to fileprivate, and AgentResumeSessionPlacement associated-value semantics are slightly ambiguous.
cmuxTests/SessionPersistenceTests.swift Adds testResumeCommandUsesProviderSpecificEnvironmentAllowlists covering the new per-provider safeEnvironmentKeys split for Codex and OpenCode; asserts cross-provider env keys are filtered and the correct session flags are emitted.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[resumeShellCommand] --> B[kind.resumeProviderSpec]
    B --> C{launchCommand?.launcher}
    C -- "nil or missing" --> D["?? .use(defaultInvocation)"]
    C -- "in launcherOverrides" --> E{launcherBehavior}
    E -- ".unsupported" --> F[return nil]
    E -- ".use(invocation)" --> G[commandParts]
    D --> G
    G --> H[strip leadingSubcommand from tail]
    H --> I[sanitizeArguments]
    I --> J[preserveOptions via policy]
    J -- "rejectOption found" --> F
    J -- "nonRestorableCommand found" --> F
    J -- success --> K[sessionPlacement.build]
    K -- ".afterPrefix" --> L["exec + subcommand + prefix + sessionId + preserved"]
    K -- ".afterPreserved" --> M["exec + subcommand + prefix + preserved + sessionId"]
    L --> N[wrap env keys via safeEnvironmentKeys]
    M --> N
    N --> O[shellSingleQuoted join]
Loading

Reviews (1): Last reviewed commit: "refactor: declarative agent resume specs" | Re-trigger Greptile

Comment thread Sources/RestorableAgentSession.swift Outdated
Comment thread Sources/RestorableAgentSession.swift
Comment thread Sources/RestorableAgentSession.swift
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Sources/RestorableAgentSession.swift (1)

57-57: Use private here.

SwiftLint flagged this as private_over_fileprivate; the helper is only used in this file, so private keeps the narrower access level.

♻️ Proposed fix
-fileprivate func isOpenCodeInternalWorkerArgument(_ value: String) -> Bool {
+private func isOpenCodeInternalWorkerArgument(_ value: String) -> Bool {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/RestorableAgentSession.swift` at line 57, Change the access level of
the helper function isOpenCodeInternalWorkerArgument from fileprivate to
private; locate the function declaration for isOpenCodeInternalWorkerArgument(_
value: String) and replace fileprivate with private to narrow its visibility to
this file's scope and satisfy the SwiftLint private_over_fileprivate rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/RestorableAgentSession.swift`:
- Around line 191-201: The variadicOptions array in RestorableAgentSession.swift
is incorrectly treating repeatable single-value flags (e.g., "--add-dir") as
variadic; because optionWidth consumes all following non-dash tokens this can
swallow the original prompt into the flag value. Remove these repeatable
single-value flags (like "--add-dir", "--file", "--mcp-config", etc.) from
variadicOptions and treat them as single-value repeatable options in the
resume-generation logic (the code paths using optionWidth and the resume command
builder), ensuring each occurrence only consumes one following token; then add a
regression assertion in the existing long "--add-dir" test case to assert that
the original prompt text does not appear as a value after "--add-dir" in the
generated resume command.

---

Nitpick comments:
In `@Sources/RestorableAgentSession.swift`:
- Line 57: Change the access level of the helper function
isOpenCodeInternalWorkerArgument from fileprivate to private; locate the
function declaration for isOpenCodeInternalWorkerArgument(_ value: String) and
replace fileprivate with private to narrow its visibility to this file's scope
and satisfy the SwiftLint private_over_fileprivate rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8c821e7-211d-43c0-b347-572ac4a2b8c2

📥 Commits

Reviewing files that changed from the base of the PR and between 7102fdf and e2bcf02.

📒 Files selected for processing (2)
  • Sources/RestorableAgentSession.swift
  • cmuxTests/SessionPersistenceTests.swift

Comment thread Sources/RestorableAgentSession.swift Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CLI/cmux.swift (1)

14069-14080: ⚠️ Potential issue | 🟠 Major

Restore --add-dir to Codex variadic options for correct multi-directory preservation.

Line 14073 sets variadicOptions to only ["--image", "-i"], omitting --add-dir. With this configuration, codex --add-dir dirA dirB --model x sanitizes to consume only dirA; dirB becomes a positional and stops parsing, dropping --model x. Tests in cmuxTests/SessionPersistenceTests.swift (lines 1712–1728) confirm that multiple --add-dir entries must survive sanitization and resume generation. RestorableAgentSession.swift marks --add-dir as greedyValueOptions (line 200), and claudeLaunchVariadicOptions (line 14275) already includes it. Apply the fix:

Fix
         case "codex":
             return sanitizedAgentOptions(
                 tail,
                 valueOptions: Self.codexLaunchValueOptions,
                 optionalValueOptions: [],
-                variadicOptions: ["--image", "-i"],
+                variadicOptions: ["--add-dir", "--image", "-i"],
                 nonRestorableCommands: Self.codexLaunchNonRestorableCommands,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 14069 - 14080, The variadicOptions passed into
sanitizedAgentOptions for the codex launch is missing "--add-dir", causing
multiple --add-dir arguments to be mis-parsed; update the variadicOptions array
in the codex branch where sanitizedAgentOptions is called (the call that
currently uses Self.codexLaunchValueOptions and variadicOptions:
["--image","-i"]) to include "--add-dir" (and its short form if applicable) so
that RestorableAgentSession's greedyValueOptions and claudeLaunchVariadicOptions
behavior are respected and multiple --add-dir entries survive sanitization and
resume generation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/RestorableAgentSession.swift`:
- Around line 57-59: sanitizeArguments currently only inspects original.tail
while stripInternalWorkerArguments
(OpenCodeResumeArgumentFilter.stripInternalWorkerArguments) can remove the
actual worker path from argv[0], so the resumed command can still launch
original.executable (e.g., bun) and ignore provider overrides; update the
sanitization to also set/overwrite the executable when internal launcher details
are stripped: in OpenCodeResumeArgumentFilter.stripInternalWorkerArguments and
wherever sanitizeArguments is used, when you detect and remove an internal
worker argv[0] or a bun+script pattern, force the launch executable to the
provider's resolved executable (apply provider spec overrides such as cmux for
claudeTeams/omo) so both argv[0] and the executable are consistent with the
sanitized tail.

---

Outside diff comments:
In `@CLI/cmux.swift`:
- Around line 14069-14080: The variadicOptions passed into sanitizedAgentOptions
for the codex launch is missing "--add-dir", causing multiple --add-dir
arguments to be mis-parsed; update the variadicOptions array in the codex branch
where sanitizedAgentOptions is called (the call that currently uses
Self.codexLaunchValueOptions and variadicOptions: ["--image","-i"]) to include
"--add-dir" (and its short form if applicable) so that RestorableAgentSession's
greedyValueOptions and claudeLaunchVariadicOptions behavior are respected and
multiple --add-dir entries survive sanitization and resume generation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06b3f4c2-2d6f-4aaa-b9a6-abc91d7afc66

📥 Commits

Reviewing files that changed from the base of the PR and between e2bcf02 and 3998818.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • Sources/RestorableAgentSession.swift
  • cmuxTests/SessionPersistenceTests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmuxTests/SessionPersistenceTests.swift

Comment thread Sources/RestorableAgentSession.swift
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.

1 participant