-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix missing sidebar ports for agent-run dev servers #2562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -514,6 +514,13 @@ private final class ClaudeHookSessionStore { | |
| } | ||
| } | ||
|
|
||
| private let codexHookWrapperProcessNames: Set<String> = [ | ||
| "sh", | ||
| "bash", | ||
| "zsh", | ||
| "env" | ||
| ] | ||
|
|
||
| enum CLIIDFormat: String { | ||
| case refs | ||
| case uuids | ||
|
|
@@ -12763,12 +12770,21 @@ struct CMUXCLI { | |
| workspaceId: workspaceId, | ||
| client: client | ||
| ) | ||
| let agentPIDKey = codexAgentPIDKey(sessionId: parsedInput.sessionId) | ||
| let codexPid = inferredCodexAgentPID() | ||
| if let sessionId = parsedInput.sessionId { | ||
| try? sessionStore.upsert( | ||
| sessionId: sessionId, | ||
| workspaceId: workspaceId, | ||
| surfaceId: surfaceId, | ||
| cwd: parsedInput.cwd | ||
| cwd: parsedInput.cwd, | ||
| pid: codexPid | ||
| ) | ||
| } | ||
| if let codexPid { | ||
| _ = try? sendV1Command( | ||
| "set_agent_pid \(agentPIDKey) \(codexPid) --tab=\(workspaceId)", | ||
| client: client | ||
| ) | ||
| } | ||
| print("{}") | ||
|
|
@@ -12781,6 +12797,23 @@ struct CMUXCLI { | |
| fallback: workspaceArg, | ||
| client: client | ||
| ) | ||
| let agentPIDKey = codexAgentPIDKey(sessionId: parsedInput.sessionId ?? mappedSession?.sessionId) | ||
| let codexPid = mappedSession?.pid ?? inferredCodexAgentPID() | ||
| if let sessionId = parsedInput.sessionId, let mappedSession { | ||
| try? sessionStore.upsert( | ||
| sessionId: sessionId, | ||
| workspaceId: workspaceId, | ||
| surfaceId: mappedSession.surfaceId, | ||
| cwd: parsedInput.cwd ?? mappedSession.cwd, | ||
| pid: codexPid | ||
| ) | ||
| } | ||
| if let codexPid { | ||
| _ = try? sendV1Command( | ||
| "set_agent_pid \(agentPIDKey) \(codexPid) --tab=\(workspaceId)", | ||
| client: client | ||
| ) | ||
| } | ||
| _ = try? sendV1Command("clear_notifications --tab=\(workspaceId)", client: client) | ||
| try setCodexStatus( | ||
| client: client, | ||
|
|
@@ -12806,11 +12839,13 @@ struct CMUXCLI { | |
| workspaceId: workspaceId, | ||
| client: client | ||
| ) | ||
| let agentPIDKey = codexAgentPIDKey(sessionId: parsedInput.sessionId ?? mappedSession?.sessionId) | ||
|
|
||
| // Build completion notification from Codex stop payload | ||
| let lastMessage = parsedInput.object?["last_assistant_message"] as? String | ||
| ?? parsedInput.object?["lastAssistantMessage"] as? String | ||
| let cwd = parsedInput.cwd ?? mappedSession?.cwd | ||
| let codexPid = mappedSession?.pid ?? inferredCodexAgentPID() | ||
| let projectName: String? = { | ||
| guard let cwd, !cwd.isEmpty else { return nil } | ||
| return URL(fileURLWithPath: NSString(string: cwd).expandingTildeInPath).lastPathComponent | ||
|
|
@@ -12822,10 +12857,17 @@ struct CMUXCLI { | |
| workspaceId: workspaceId, | ||
| surfaceId: surfaceId, | ||
| cwd: cwd, | ||
| pid: codexPid, | ||
| lastSubtitle: "Completed", | ||
| lastBody: lastMessage.map { truncate($0, maxLength: 200) } | ||
| ) | ||
| } | ||
| if let codexPid { | ||
| _ = try? sendV1Command( | ||
| "set_agent_pid \(agentPIDKey) \(codexPid) --tab=\(workspaceId)", | ||
| client: client | ||
| ) | ||
| } | ||
|
|
||
| // Send completion notification | ||
| var subtitle = "Completed" | ||
|
|
@@ -12875,6 +12917,67 @@ struct CMUXCLI { | |
| _ = try client.send(command: cmd) | ||
| } | ||
|
|
||
| private func codexAgentPIDKey(sessionId: String?) -> String { | ||
| guard let sessionId = sessionId?.trimmingCharacters(in: .whitespacesAndNewlines), | ||
| !sessionId.isEmpty else { | ||
| return "codex" | ||
| } | ||
| return "codex.\(sessionId)" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Sanitize Prompt for AI agents |
||
| } | ||
|
|
||
| private func inferredCodexAgentPID() -> Int? { | ||
| var candidate = getppid() | ||
| var remainingWrapperSkips = 8 | ||
|
|
||
| while candidate > 1, remainingWrapperSkips > 0 { | ||
| guard let processName = processName(for: candidate) else { break } | ||
| if !codexHookWrapperProcessNames.contains(processName) { | ||
| break | ||
| } | ||
| let next = parentPID(of: candidate) | ||
| guard next > 1, next != candidate else { break } | ||
| candidate = next | ||
|
Comment on lines
+13433
to
+13440
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, let's check if the file exists and read the specific lines
wc -l CLI/cmux.swiftRepository: manaflow-ai/cmux Length of output: 80 🏁 Script executed: #!/bin/bash
# Read lines 12932-12939 and surrounding context (12920-12950) for better understanding
sed -n '12920,12950p' CLI/cmux.swiftRepository: manaflow-ai/cmux Length of output: 1214 🏁 Script executed: #!/bin/bash
# Search for the definition of codexHookWrapperProcessNames
rg "codexHookWrapperProcessNames" CLI/cmux.swift -B 2 -A 2Repository: manaflow-ai/cmux Length of output: 386 🏁 Script executed: #!/bin/bash
# Search for how the wrapper detection logic is used and the rescan mechanism
rg "parentPID\|remainingWrapperSkips" CLI/cmux.swift -B 3 -A 3Repository: manaflow-ai/cmux Length of output: 42 🏁 Script executed: #!/bin/bash
# Get the full definition of codexHookWrapperProcessNames
rg "private let codexHookWrapperProcessNames" CLI/cmux.swift -A 10Repository: manaflow-ai/cmux Length of output: 233 🏁 Script executed: #!/bin/bash
# Search for hook payload or environment variable usage for PID
rg "CODEX_HOOK|hook.*PID|payload.*PID" CLI/cmux.swift -iRepository: manaflow-ai/cmux Length of output: 781 🏁 Script executed: #!/bin/bash
# Search for how inferredCodexAgentPID is used and whether there are other PID detection mechanisms
rg "inferredCodexAgentPID\|codexAgentPID" CLI/cmux.swift -B 2 -A 2 | head -60Repository: manaflow-ai/cmux Length of output: 42 🏁 Script executed: #!/bin/bash
# Check for any normalization or variant handling of process names
rg "processName|sh|bash|zsh|env" CLI/cmux.swift | grep -E "(normalize|variant|strip|replace)" | head -20Repository: manaflow-ai/cmux Length of output: 1491 Wrapper detection is too narrow and needs broadening or alternative strategy. The hard-coded Either:
🤖 Prompt for AI Agents |
||
| remainingWrapperSkips -= 1 | ||
| } | ||
|
|
||
| return candidate > 1 ? Int(candidate) : nil | ||
| } | ||
|
|
||
| private func parentPID(of pid: pid_t) -> pid_t { | ||
| var info = kinfo_proc() | ||
| var size = MemoryLayout<kinfo_proc>.size | ||
| var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] | ||
| guard sysctl(&mib, 4, &info, &size, nil, 0) == 0 else { | ||
| return -1 | ||
| } | ||
| return info.kp_eproc.e_ppid | ||
| } | ||
|
|
||
| private func processName(for pid: pid_t) -> String? { | ||
| let process = Process() | ||
| let stdout = Pipe() | ||
| process.executableURL = URL(fileURLWithPath: "/bin/ps") | ||
| process.arguments = ["-p", String(pid), "-o", "comm="] | ||
| process.standardOutput = stdout | ||
| process.standardError = FileHandle.nullDevice | ||
|
|
||
| do { | ||
| try process.run() | ||
| } catch { | ||
| return nil | ||
| } | ||
| process.waitUntilExit() | ||
| guard process.terminationStatus == 0 else { return nil } | ||
|
|
||
| let data = stdout.fileHandleForReading.readDataToEndOfFile() | ||
| guard let output = String(data: data, encoding: .utf8)? | ||
| .trimmingCharacters(in: .whitespacesAndNewlines), | ||
| !output.isEmpty else { | ||
| return nil | ||
| } | ||
| return URL(fileURLWithPath: output).lastPathComponent.lowercased() | ||
| } | ||
|
|
||
| private func versionSummary() -> String { | ||
| let info = resolvedVersionInfo() | ||
| let commit = info["CMUXCommit"].flatMap { normalizedCommitHash($0) } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Persist the recovered session on
prompt-submit.Line 12802 only upserts when a prior session record already exists. If
SessionStartwas skipped or the store entry was lost, this path still recovers/registers a PID, but none of that recovered state is saved, sostophas to rediscover everything from ambient state again.🤖 Prompt for AI Agents