Leak-free ClaudeWrapper.DuplexSession transport backed by
Forcola.Duplex.
Holds the long-lived claude subprocess in its own process group; on
session close, owner death, or BEAM death the whole group is killed
(SIGTERM then SIGKILL), reaping claude and every stdio MCP server it
spawned. The default ClaudeWrapper.DuplexSession.Adapter.Port closes
the port instead, which sends no signal (see #185).
This module compiles only when forcola is a dependency. Select it
per session with adapter: ClaudeWrapper.DuplexSession.Adapter.Forcola,
or globally with
config :claude_wrapper, duplex_adapter: ClaudeWrapper.DuplexSession.Adapter.Forcola.
forcola is POSIX-only.
How it fits the adapter seam
A small translator GenServer owns the Forcola.Duplex session and
forwards its owner messages into the three shapes
ClaudeWrapper.DuplexSession.Adapter delivers. The translator's pid is
the transport handle, so the session's %{port: handle} match works
unchanged:
{:forcola_line, _, line}->{handle, {:data, line <> "\n"}}(forcola strips the trailing newline; the session's buffer splits on it, so it is re-added){:forcola_exit, _, status}->{handle, {:exit_status, code}}, then the translator stops and its linked{:EXIT, handle, reason}reaches the session{:forcola_stderr, _, _}is dropped, matching thePortadapter which does not fold stderr into the NDJSON stream
Summary
Functions
Returns a specification to start this module under a supervisor.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.