feat(agent): add ironrdp-agent CLI + daemon for agentic / LLM control#1339
Draft
Benoît Cortier (CBenoit) wants to merge 1 commit into
Draft
feat(agent): add ironrdp-agent CLI + daemon for agentic / LLM control#1339Benoît Cortier (CBenoit) wants to merge 1 commit into
Benoît Cortier (CBenoit) wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new ironrdp-agent crate (CLI + long-running daemon) intended for programmatic/LLM-driven control of IronRDP sessions via a custom binary IPC protocol, and adds a Windows GitHub Actions workflow + PowerShell harness to exercise an end-to-end “agentic RDP” scenario.
Changes:
- Add
ironrdp-agentdaemon/client implementation with custom length-prefixed IPC framing and request/response types. - Add credential redaction utilities and property descriptions for
dump-properties. - Add GitHub Actions workflow plus PowerShell scripts to automate a localhost RDP scenario and collect artifacts.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/ironrdp-agent/tests/ipc.rs | Integration test for IPC framing round-trips over an in-memory duplex stream. |
| crates/ironrdp-agent/src/redact.rs | Sensitive-key detection + value redaction and unit tests. |
| crates/ironrdp-agent/src/main.rs | Main agent implementation: CLI client mode, daemon, session management, screenshot encoding, IPC server/client plumbing. |
| crates/ironrdp-agent/src/lib.rs | Library crate surface for the agent modules. |
| crates/ironrdp-agent/src/ipc.rs | Binary IPC wire format, framing helpers, and encode/decode implementations + unit tests. |
| crates/ironrdp-agent/src/help.rs | Long-form --help-agent text describing usage and IPC model. |
| crates/ironrdp-agent/src/descriptions.rs | Human-readable .rdp/agent:* property descriptions for dump-properties. |
| crates/ironrdp-agent/src/cli.rs | Clap CLI argument model and .rdp payload generation for connect. |
| crates/ironrdp-agent/Cargo.toml | New crate manifest (features, deps, bin/lib split). |
| Cargo.lock | Lockfile update to include the new crate and dependencies. |
| .github/workflows/agentic-rdp.yml | CI workflow to build ironrdp-agent and run the agentic RDP scenario on Windows runners. |
| .github/agentic-rdp/Start-InteractivePSHostServer.ps1 | Registers/starts/waits for an interactive PSHostServer for GUI/interactive validation. |
| .github/agentic-rdp/Start-AgentDaemon.ps1 | Starts the ironrdp-agent daemon for the CI scenario and writes state/log paths. |
| .github/agentic-rdp/Invoke-InteractiveCommand.ps1 | Verifies interactive remoting by launching/closing a GUI process (Notepad). |
| .github/agentic-rdp/Invoke-AgenticRdpTest.ps1 | Orchestrates the full scenario stages, artifacts, and cleanup. |
| .github/agentic-rdp/Invoke-AgenticDesktopScenario.ps1 | Runs a desktop interaction scenario and validates screenshot properties. |
| .github/agentic-rdp/Enable-LocalRdp.ps1 | Enables local RDP on the runner, creates temporary password, and restores settings on cleanup. |
| .github/agentic-rdp/Connect-AgentSession.ps1 | Connects an RDP session via ironrdp-agent and validates negotiated framebuffer size. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+5
to
+6
| //! the variant, followed by a `u32` request id (for correlation), followed by | ||
| //! the variant payload. |
Comment on lines
+905
to
+909
| let mut body = vec![0u8; usize::try_from(len).context("frame length exceeds usize")?]; | ||
| reader.read_exact(&mut body).await.context("read frame body")?; | ||
| let mut cursor = ReadCursor::new(&body); | ||
| let pdu = P::decode(&mut cursor).context("decode PDU")?; | ||
| Ok(pdu) |
Comment on lines
+924
to
+931
| async fn print_response(response: Response, screenshot_output: Option<PathBuf>) -> anyhow::Result<()> { | ||
| match response { | ||
| Response::Ok | Response::Health => Ok(()), | ||
| Response::Error { message } => anyhow::bail!("{message}"), | ||
| Response::Connect { session_id } => { | ||
| println!("{session_id}"); | ||
| Ok(()) | ||
| } |
Comment on lines
+754
to
+760
| Command::Mouse(cmd) => ( | ||
| Request::Mouse { | ||
| session_id: cmd.session, | ||
| action: cli_mouse_to_ipc(cmd.action), | ||
| }, | ||
| None, | ||
| ), |
| })]) | ||
| .await | ||
| } | ||
| MouseAction::Position => Ok(()), |
Comment on lines
+443
to
+448
| let server = ServerOptions::new() | ||
| .access_inbound(true) | ||
| .access_outbound(true) | ||
| .pipe_mode(PipeMode::Byte) | ||
| .create(&path) | ||
| .with_context(|| format!("failed to create named pipe {path}"))?; |
Comment on lines
+80
to
+85
| --desktop-size $DesktopSize ` | ||
| --no-credssp ` | ||
| --autologon ` | ||
| --compression-enabled false ` | ||
| --color-depth 16 ` | ||
| --no-server-pointer |
Comment on lines
+86
to
+90
| $connectJson | Set-Content -Path (Join-Path $ArtifactsDir 'agent-connect.json') -Encoding utf8NoBOM | ||
|
|
||
| $connect = $connectJson | ConvertFrom-Json | ||
| $sessionId = [string] $connect.session_id | ||
|
|
Comment on lines
+28
to
+29
| $statusJson = Invoke-Agent status --session $SessionId | ||
| return ($statusJson | ConvertFrom-Json) |
Comment on lines
+36
to
+44
| function Get-SessionStatus { | ||
| param( | ||
| [Parameter(Mandatory)] | ||
| [string] $SessionId | ||
| ) | ||
|
|
||
| $statusJson = Invoke-Agent status --session $SessionId | ||
| $statusJson | Set-Content -Path (Join-Path $ArtifactsDir 'agent-session-status.json') -Encoding utf8NoBOM | ||
| return ($statusJson | ConvertFrom-Json) |
`ironrdp-agent` is a new headless client built on top of `ironrdp-client`. It's designed to be driven by an LLM or a test harness rather than a human, and is provably free of the viewer's heavy deps (cpal, native cliprdr, ironrdp-mstsgu, reqwest, winit, softbuffer).
42344cf to
34c94bd
Compare
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.
ironrdp-agentis a new headless client built on top ofironrdp-client. It's designed to be driven by an LLM or a test harness rather than a human, and is provably free of the viewer's heavy deps (cpal, native cliprdr, ironrdp-mstsgu, reqwest, winit, softbuffer).