Skip to content

feat(ffi): redirect smartcards through WinSCard on Windows - #2175

Merged
Marc-André Moreau (mamoreau-devolutions) merged 7 commits into
masterfrom
claude/ffi-smartcard-noop-763d89
Oct 22, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 7 commits into
masterfrom
claude/ffi-smartcard-noop-763d89

Conversation

@mamoreau-devolutions

@mamoreau-devolutions Marc-André Moreau (mamoreau-devolutions) commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Problem

ClientConnector.WithStaticChannelRdpdr in the .NET FFI announces a smartcard device backed by NoopRdpdrBackend. That backend's handle_scard_call returned no messages, so the client never completed any MS-RDPESC IOCTL the server sent (EstablishContext, ListReaders, GetStatusChange, ...). Server-side WinSCard calls in the session hung, which can stall logon and UAC prompts that check for readers. ironrdp-client attaches the same backend when RDPDR is enabled with no native backend.

Fix

The noop backend answers every smartcard call. It replies with the return structure the server expects for that IOCTL, with ReturnCode = SCARD_E_NO_SERVICE and IoStatus = STATUS_SUCCESS, as MS-RDPESC 3.1.4 requires for smart card errors. Shared helpers in ironrdp-rdpdr build these replies, and the Windows backend now uses them too:

  • ScardCall::error_response builds the completion for a failed call. An IOCTL with no known return structure gets STATUS_UNSUCCESSFUL and no output, as in FreeRDP; this path is unreachable today.
  • esc::complete_call replies STATUS_BUFFER_TOO_SMALL when the structure does not fit the server's output buffer, so the server retries with a larger one (3.2.5.2).
  • Error returns zero every field besides ReturnCode (2.2.3): empty context and handle (ScardContext::empty, ScardHandle::empty), NULL buffers, and no reader states (kept on SCARD_E_TIMEOUT, as FreeRDP does). This also fixes failure replies in the Windows backend (Connect, Transmit, Reconnect, GetTransmitCount, GetDeviceTypeId).

Real WinSCard redirection in the FFI on Windows. WithStaticChannelRdpdr now backs the smartcard with the WinSCard passthrough from ironrdp-rdpdr-native, the same backend the viewer and ActiveX use. Other platforms keep the noop backend.

The native backend runs blocking calls (GetStatusChange, Connect, Transmit, ...) on worker threads, and their completions only reach the server through Rdpdr::poll_deferred_messages. So:

  • ActiveStage.Process sends deferred RDPDR completions after each frame, as ironrdp-client does, except after DeactivateAll or Terminate.
  • New ActiveStage.PollRdpdrDeferred() returns them while the server is idle. Hosts should call it about every 50 ms, serialized with their other ActiveStage calls.
  • The Avalonia example polls every 50 ms from its read loop, and starts the next read only after the current frame's outputs are handled, so reactivation reads are not raced.

The existing .NET API is unchanged; PollRdpdrDeferred is an addition. The regenerated bindings only add the new method and doc comments.

Related PRs

This PR builds on #2161 and #2162, both now merged into master:

Testing

  • Testsuite tests send an EstablishContext IOCTL through Rdpdr with the noop backend, cover the STATUS_BUFFER_TOO_SMALL and no-structure paths, and check that error replies for eight call types are all zero after ReturnCode.
  • cargo test passes for ironrdp-rdpdr, ironrdp-rdpdr-native and the testsuite's rdpdr tests.
  • cargo xtask check fmt, cargo xtask check locks, cargo xtask ffi build and cargo xtask ffi bindings (which builds the .NET library) pass, and the Avalonia example builds.
  • Clippy with -D warnings is clean on ironrdp-rdpdr, ironrdp-rdpdr-native, ironrdp-testsuite-core and ffi. The full cargo xtask check lints could not run locally because the libopus_sys CMake build fails on that machine.

Not tested: a live session with a reader and card, and building the FFI for non-Windows targets (left to CI).

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 20, 2026 23:07
@github-actions github-actions Bot added kind/protocol Affects RDP or related protocol behavior risk/medium Behavioral change that does not substantially alter a core public API scope/core Touches the core architectural tier scope/cross-cutting Spans multiple architectural boundaries scope/ffi Affects native or .NET bindings size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure labels Oct 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The Avalonia example introduces competing reactivation reads and unsynchronized polling of mutable native session state.

2 open issues
What changed in this PR

Adds Windows smartcard redirection to the .NET FFI and completes unsupported smartcard requests instead of leaving server calls waiting.

Changes:

  • Uses the native WinSCard backend on Windows.
  • Adds deferred-completion polling and updates the Avalonia example.
  • Adds IOCTL-specific error responses and regression tests.
File Description
ffi/​src/​session/​mod.rs Exposes and integrates deferred-completion polling.
ffi/​src/​connector/​mod.rs Selects the Windows native backend.
ffi/​dotnet/​Devolutions.IronRdp/​Generated/​RawClientConnector.cs Documents smartcard behavior.
ffi/​dotnet/​Devolutions.IronRdp/​Generated/​RawActiveStage.cs Adds native polling declaration.
ffi/​dotnet/​Devolutions.IronRdp/​Generated/​ClientConnector.cs Documents backend selection and polling.
ffi/​dotnet/​Devolutions.IronRdp/​Generated/​ActiveStage.cs Adds managed polling wrapper.
ffi/​dotnet/​Devolutions.IronRdp.AvaloniaExample/​MainWindow.axaml.cs Polls completions during idle reads.
ffi/​Cargo.toml Adds Windows backend dependency.
crates/​ironrdp-testsuite-core/​tests/​rdpdr/​mod.rs Tests noop responses and buffer limits.
crates/​ironrdp-rdpdr/​src/​pdu/​esc/​mod.rs Builds call-specific error returns.
crates/​ironrdp-rdpdr/​src/​backend/​noop.rs Completes smartcard calls with no-service errors.
Cargo.lock Records the FFI backend dependency.

Review effort: Balanced

Comment thread ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs Outdated
Comment thread ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs Outdated
@github-actions github-actions Bot added risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny triage/overlap Possible overlap with another pull request; advisory only and removed risk/medium Behavioral change that does not substantially alter a core public API labels Oct 20, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The PR fixes a real hang: NoopRdpdrBackend previously returned no completion for smartcard IOCTLs, stalling server-side WinSCard. The new ScardCall::error_return plus the noop handle_scard_call completion follow MS-RDPESC 3.1.4/3.2.5.2 (ReturnCode in the IOCTL-specific return, IoStatus SUCCESS, BUFFER_TOO_SMALL retry path) and are covered by new testsuite tests. The Windows WinSCard passthrough, FFI deferred-poll API, and Avalonia lock/poll rework are coherent. Two low-severity wire deviations remain in the new error path: error returns carry nonzero cbContext/cbHandle from ScardContext::from_native(0) where MS-RDPESC 2.2.3 requires all non-ReturnCode fields to be zero, and unrecognized IOCTLs are completed with IoStatus SUCCESS plus a Long_Return body instead of an NTSTATUS error with no output. Maintainability findings are confirmed against pr-head: error_return duplicates ironrdp-rdpdr-native's private unsupported() mapping variant-for-variant, and the noop completion body re-imple…

Push a commit after addressing these findings. If no code change is needed, you may resolve inline threads and comment @github-actions review-ready to request human review.

Comment thread crates/ironrdp-rdpdr/src/pdu/esc/mod.rs Outdated
Comment thread crates/ironrdp-rdpdr/src/pdu/esc/mod.rs Outdated
Comment thread crates/ironrdp-rdpdr/src/pdu/esc/mod.rs Outdated
Comment thread ffi/src/session/mod.rs
Comment thread crates/ironrdp-rdpdr/src/pdu/esc/mod.rs Outdated
Comment thread crates/ironrdp-rdpdr/src/backend/noop.rs Outdated
@github-actions github-actions Bot added ai-reviewed/1 One automated review completed needs-author-action The pull request author is the current next actor and removed needs-author-action The pull request author is the current next actor labels Oct 20, 2026
@github-actions github-actions Bot added risk/medium Behavioral change that does not substantially alter a core public API and removed risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny labels Oct 21, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The PR correctly fixes the noop-backend smartcard hang by completing every MS-RDPESC IOCTL via ScardCall::error_response, centralizes error/buffer-too-small completion in esc::complete_call, and adds Windows WinSCard passthrough to the .NET FFI plus an additive PollRdpdrDeferred API. Protocol behavior matches MS-RDPESC 3.1.4/2.2.3/3.2.5.2. Six valid candidates remain: an open question on opt-in deferred polling (medium), an unverified STATUS_UNSUCCESSFUL fallback, a dead/discarding factory error arm, an abandoned pending ReadPdu in the example loop, and two low-severity duplication issues. All six are published; the factory-error finding is refined with a corrected line range; the rest are accepted substantially unchanged.

  1. [skeptical] Hang fix relies on hosts voluntarily adopting the new PollRdpdrDeferred API — medium 🟠 ❓ — ffi/src/session/mod.rs
    Deferred RDPDR completions only reach the server via the per-frame flush in ActiveStage::process (lines 130-134) or the new opt-in poll_rdpdr_deferred (lines 137-145). A host that never polls on a timer gets completions only when the server sends traffic, but a server blocked in a WinSCard call awaiting the pending completion may send nothing, reproducing the hang. MAX_DEFERRED back-pressure (SCARD_E_NO_MEMORY) does not unblock it either. Nothing in the FFI enforces the polling contract; whether a Windows server always sends frames during an outstanding IOCTL, and whether all .NET consumers adopted the new API, could not be verified.
  2. [skeptical] Unreachable factory error arm discards the underlying cause — low 🟡 — ffi/src/connector/mod.rs
    with_static_channel_rdpdr builds the backend via WindowsRdpdrBackendFactory::from_drives(Vec::new()). Per factory.rs (from_drive_configuration, lines 139-157), every error variant (DuplicateDeviceId/RepeatedInitialSelection/UnconfiguredInitialSelection) requires at least one drive, so this call cannot fail and the '?' arm is dead code today. Its map_err also replaces any future, more specific factory error with the generic 'invalid RDPDR backend configuration', misdiagnosing a real failure if validation is added. Propagate the actual error or use a non-fallible construction for the smartcard-only configuration.
  3. [skeptical] Read loop can exit while readPduTask is still pending and unobserved — low 🟡 — ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs
    In ReadPduAndProcessActiveStage, the deferred-poll branch assigns keepLooping from HandleActiveStageOutput(deferred); if it returns false (e.g. Terminate), the while loop exits without awaiting the pending readPduTask started earlier in the iteration. If the abandoned ReadPdu subsequently faults (connection reset, stream error), the exception is unobserved and surfaces as UnobservedTaskException rather than the loop's Trace error path, hiding the disconnect cause. Await or observe the task before returning.

Push a commit after addressing these findings. If no code change is needed, you may resolve inline threads and comment @github-actions review-ready to request human review.

Comment thread crates/ironrdp-rdpdr/src/pdu/esc/mod.rs Outdated
Comment thread crates/ironrdp-rdpdr-native/src/windows/scard/mod.rs Outdated
Comment thread crates/ironrdp-testsuite-core/tests/rdpdr/mod.rs
@github-actions github-actions Bot added ai-reviewed/2 Two automated reviews completed needs-author-action The pull request author is the current next actor and removed ai-reviewed/1 One automated review completed labels Oct 21, 2026
@github-actions github-actions Bot added risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny and removed needs-author-action The pull request author is the current next actor risk/medium Behavioral change that does not substantially alter a core public API triage/overlap Possible overlap with another pull request; advisory only labels Oct 21, 2026
@github-actions github-actions Bot added triage/overlap Possible overlap with another pull request; advisory only needs-review A human reviewer is the current next actor and removed needs-author-action The pull request author is the current next actor labels Oct 21, 2026
@github-actions github-actions Bot added needs-review A human reviewer is the current next actor and removed needs-review A human reviewer is the current next actor triage/overlap Possible overlap with another pull request; advisory only labels Oct 21, 2026
@github-actions github-actions Bot removed the needs-review A human reviewer is the current next actor label Oct 22, 2026
`NoopRdpdrBackend::handle_scard_call` returned no messages, so every
MS-RDPESC IOCTL sent to an announced smartcard (EstablishContext,
ListReaders, GetStatusChange, ...) was never completed and server-side
WinSCard callers hung. The .NET FFI (`with_static_channel_rdpdr`) and
`ironrdp-client` announce a smartcard on top of this backend.

The noop backend now answers each call with the IOCTL-specific return
structure carrying SCARD_E_NO_SERVICE and IoStatus STATUS_SUCCESS, as
MS-RDPESC 3.1.4 requires for smart card errors, or STATUS_BUFFER_TOO_SMALL
when the server's output buffer cannot hold it.

The new `ScardCall::error_return` helper builds the matching return
structure for any call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`ClientConnector.WithStaticChannelRdpdr` now backs the announced
smartcard with the native WinSCard passthrough from
`ironrdp-rdpdr-native` on Windows, the same backend the viewer and
ActiveX use. Other platforms keep the noop backend, which answers every
call with SCARD_E_NO_SERVICE.

The native backend completes blocking calls (GetStatusChange, Connect,
Transmit, ...) on worker threads. `ActiveStage.Process` now forwards
those completions after each frame, and the new
`ActiveStage.PollRdpdrDeferred` lets hosts flush them while the server
is idle. The Avalonia example polls every 50 ms from its read loop, like
`ironrdp-client` does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Error returns built for failed smartcard calls carried a nonzero
REDIR_SCARDCONTEXT/REDIR_SCARDHANDLE, while MS-RDPESC 2.2.3 requires all
fields besides ReturnCode to be zero. Add `ScardContext::empty` and
`ScardHandle::empty`, and use them in the shared error path and in the
native backend's EstablishContext/Connect failures.

An IOCTL with no known return structure is now completed with
STATUS_UNSUCCESSFUL and no output (MS-RDPESC 3.1.4, FreeRDP) instead of
a Long_Return the server cannot decode. ReleaseTartedEvent keeps its
Long_Return.

`ScardCall::error_response` replaces `error_return` and builds the whole
completion, and `esc::complete_call` holds the STATUS_BUFFER_TOO_SMALL
retry rule. The native backend now uses both instead of its private
copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The native backend's Connect failure after a WinSCard error still sent
the negotiated protocol, which MS-RDPESC 2.2.3 requires to be zero.
Replace the success guards in `complete_establish`/`complete_connect`
with `establish_failed`/`connect_failed`, which every failure site now
calls instead of building placeholder handles.

Also:
- ffi: keep the factory error as the cause instead of replacing it.
- Avalonia example: report a read that fails after the session ended
  instead of leaving its exception unobserved.
- testsuite: build smartcard requests with the shared device-control
  framing helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MS-RDPESC 2.2.3 zeroes every field besides ReturnCode on failure, but
several error returns still carried non-NULL NDR pointers:

- The shared error path built Transmit, Control, GetAttrib and ReadCache
  returns with an empty buffer, which encodes a referent ID and a
  conformant count. It now uses the existing NULL-buffer probe
  constructors.
- GetStatusChange_Return/LocateCards_Return and GetReaderIcon_Return
  always wrote a pointer. Like FreeRDP, they now write a zero count and a
  NULL pointer when empty, and drop their payload on failure (reader
  states are kept on SCARD_E_TIMEOUT, as FreeRDP does).
- The native backend sent pioRecvPci on a failed Transmit, and could
  pass WinSCard out-parameters through on failed Reconnect,
  GetTransmitCount and GetDeviceTypeId.

Also:
- ffi: `ActiveStage.Process` no longer flushes deferred RDPDR frames
  after DeactivateAll (stale channel IDs) or Terminate.
- .NET: `Framed.Write` used a thread-affine Mutex across an await, which
  throws when the continuation resumes on another thread; it now uses
  SemaphoreSlim.
- Avalonia example: guard pointer press/release before the session is
  active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the `Framed.Write` lock fix, which #2161 makes with a test, and
take the Avalonia example's ActiveStage lock as #2162 writes it
(`_stageLock`, `WithStageLock`, `SendFastpath`). On top of #2162 this
PR only adds the RDPDR poll in the read loop, the read-ordering fix and
a guard in `SendFastpath`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fold the private `error_return` into `ScardCall::error_response`,
inline the single-use `establish_failed`, and have the FFI's deferred
RDPDR poll helper return a `Vec` instead of an `Option` used as a
collection. No behavior change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs-review A human reviewer is the current next actor label Oct 22, 2026
@github-actions github-actions Bot added risk/medium Behavioral change that does not substantially alter a core public API needs-review A human reviewer is the current next actor and removed needs-review A human reviewer is the current next actor risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny labels Oct 22, 2026
@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) merged commit b735d19 into master Oct 22, 2026
43 checks passed
@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) deleted the claude/ffi-smartcard-noop-763d89 branch October 22, 2026 02:49
@github-actions github-actions Bot removed the needs-review A human reviewer is the current next actor label Oct 22, 2026
Marc-André Moreau (mamoreau-devolutions) added a commit that referenced this pull request Oct 22, 2026
Adds clipboard file transfer, in both directions, to the Windows
clipboard backend (`WinClipboard`), and stops it advertising formats it
can't deliver. ActiveX and `ironrdp-client` get the feature as is; the
.NET FFI gets it through new bindings, opt-in.

## Backend (`feat(cliprdr)`)

**Remote to local.** When the remote format list has
`FileGroupDescriptorW`, an OLE data object goes on the clipboard
(`OleSetClipboard`). It offers `FileGroupDescriptorW` and one
`FileContents` `IStream` per file (`lindex`), plus the other remote
formats.
- Streams read through `FileContents` RANGE requests carrying the
`clipDataId` of the lock `Cliprdr` took for that list, so a paste keeps
working after the remote clipboard changes, until the lock is released.
Files over 4 GiB work.
- OLE needs an STA: this is only enabled when `WinClipboard` is created
on one. COM is never initialized on a thread that has none.
- The objects are not agile and run on the clipboard thread. A stream
read blocks it like `WM_RENDERFORMAT` handling already does (30 s
timeout, chunks up to 1 MiB with read-ahead). Backend events arriving
during a wait are now queued instead of dropped.

**Local to remote.** `CF_HDROP` paths (directories walked into relative
paths), or another application's virtual files (`FileGroupDescriptorW` +
`FileContents`), are offered with `SendInitiateFileCopy` once the
channel is ready. The remote's SIZE/RANGE requests are served, from the
lock snapshot when a `clipDataId` is given.

**Capabilities.** The backend now negotiates `STREAM_FILECLIP_ENABLED`,
`FILECLIP_NO_FILE_PATHS`, `CAN_LOCK_CLIPDATA` and
`HUGE_FILE_SUPPORT_ENABLED`.

**Format filtering.** Handle-based formats (`CF_BITMAP`,
`CF_ENHMETAFILE`, `CF_METAFILEPICT`, `CF_PALETTE`), private ranges,
local paths and shell items (`CF_HDROP`, `FileNameW`, `Shell IDList
Array`) and OLE storage formats are no longer advertised in either
direction. Images still go through as `CF_DIB`, which Windows
synthesizes.

**Request validation.** `ironrdp-client` treats a rejected clipboard
request as session-fatal, so the backend checks what it sends against
the rules `Cliprdr` enforces: file list indexes, range bounds, huge-file
positions, and released locks.

**Also fixed.** The window subclass state leaked on drop, and
`WM_DESTROY` freed it while `WM_NCDESTROY` could still use it.

## Viewer (`fix(viewer)`)

Without a backend factory, `ironrdp-client` created `WinClipboard` on
the RDP thread, which runs a tokio runtime and no Windows message loop,
so clipboard redirection never worked in the viewer (no clipboard
events, no backend wake-ups, no STA for OLE). The viewer now initializes
OLE and creates the clipboard, with file transfer, on its winit thread
and hands the factory to `RdpClient`.

## .NET FFI (`feat(ffi)`)

- `ActiveStage.SubmitClipboardMessage` hands any backend message to the
clipboard channel, file transfer included, and returns the frame to
send.
- `ActiveStage.DriveClipboardTimeouts` expires clipboard locks and file
transfers.
- `WinCliprdr.NewWithFileTransfer(bool)` opts in. `WinCliprdr.New()`
keeps file transfer off, so existing consumers that only route the three
original message types don't receive new ones.
- `WinCliprdr.NextClipboardMessageTimeout(ms)` lets a loop wake up to
drive the timeouts.
- The Avalonia example opts in, submits every message through
`SubmitClipboardMessage`, drives the timeouts, and logs a failed
clipboard operation instead of stopping clipboard handling.

Only the four affected generated files are included.

## Testing

- `cargo xtask check fmt`, `check lints` and `check locks` pass.
- 12 unit tests in `cliprdr_native::windows`: format filtering, file
list building, file contents serving, range sizing, descriptor
round-trips.
- 6 tests in `cliprdr_native::windows_live` run a `CliprdrClient` over
`WinClipboard` against an in-memory `CliprdrServer` on the real Windows
clipboard. They are opt-in with `IRONRDP_WIN_CLIPBOARD_LIVE=1`, because
they replace the clipboard and briefly open an Explorer window. All pass
locally:
- Explorer pastes remote folders and files, contents checked byte for
byte.
  - A 5 GiB remote file is read through OLE past 4 GiB.
  - Remote handle and path formats are not offered locally.
- Local folders reach the remote, both at connect time and when copied
mid-session.
  - Local image formats are filtered.
  - Virtual files from another session reach the remote.
- .NET: the solution builds and the smoke tests pass (9/9, one new).

- End to end with `ironrdp-viewer` against a Windows 11 server
(rdpclip): capabilities negotiated as `STREAM_FILECLIP_ENABLED |
FILECLIP_NO_FILE_PATHS | CAN_LOCK_CLIPDATA | HUGE_FILE_SUPPORT_ENABLED`.
A local folder (nested directories, an empty directory, an empty file, a
5 MiB random file) copied in Explorer was pasted into Explorer in the
session, served under rdpclip's lock. Copying it back in the session and
pasting it into a local Explorer window gave byte-identical files
(SHA-256).

Not verified by hand: ActiveX and the Avalonia example against a real
server.

## Notes for review

- This conflicts with #2175 in `MainWindow.axaml.cs` and the generated
`ActiveStage` files. The clipboard loop body here is a single
`SubmitClipboardMessage` call, so the merge only needs #2175's
`WithActiveStage(...)` wrapper around it; the generated files can be
regenerated.
- Running the extra test suite locally may need `CMAKE` pointed at
Visual Studio 2026's bundled CMake: `libopus_sys` fails with older CMake
versions that lack the "Visual Studio 18 2026" generator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
llm-providers — 8786bd9a Deployed Oct 22, 2026 by mamoreau-devolutions via Classify pull request #5891
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed/3 Final automated review completed kind/protocol Affects RDP or related protocol behavior risk/medium Behavioral change that does not substantially alter a core public API scope/core Touches the core architectural tier scope/cross-cutting Spans multiple architectural boundaries scope/ffi Affects native or .NET bindings size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure

Development

Successfully merging this pull request may close these issues.

2 participants