Skip to content

fix(mlx): self-heal the UseMlx active-provider terminal client - #106

Merged
ThatRendle merged 3 commits into
mainfrom
change/mlx-active-provider-self-heal
Jul 23, 2026
Merged

fix(mlx): self-heal the UseMlx active-provider terminal client#106
ThatRendle merged 3 commits into
mainfrom
change/mlx-active-provider-self-heal

Conversation

@ThatRendle

Copy link
Copy Markdown
Contributor

Why

The UseMlx active-provider composition verb (shipped in #104) registered MLX as the active provider, but the terminal IChatClient it produced never started the mlx_lm runtime. A standalone .UseMlx(model, port) agent threw Connection refused (127.0.0.1:8666) on its first turn, and — because the tool-calling probe never ran — reported SupportsToolCalling == false even if a server were started by hand. The self-heal wrapper (EnsureRunningChatClient) existed but was wired only into the keyed daemon helper MlxClient(key), not the factory that builds the active-provider client.

What changed

Block 1 — factory owns the self-heal (55bd912)

  • MlxProviderFactory ctor(s) gain an MlxProviderExtension parameter; MlxProviderExtension.CreateFactory() passes this.
  • CreateAsync is now genuinely async: it awaits EnsureRunningAsync first (so the tool-calling probe sets ToolCallingVerified before GetCapabilities() snapshots capabilities), then returns the ChatClient → MlxMaxTokensDefaulter → CapabilitiesDecorator stack wrapped outermost in EnsureRunningChatClient.

Block 2 — single-source the keyed path (af5bca4)

  • MlxClientExtensions.MlxClient(key) reduced to resolve the keyed extension, build the ProviderConfig, and return the factory's now-self-healing client — dropping its duplicate up-front EnsureRunningAsync call and its own outer wrapper. Exactly one wrapper, one probe. Keyed ports (firstline :8800 / escalation :8810) and the warm/stop seams (EscalationWarmingService, AddMlxFirstline/AddMlxEscalation, MlxRuntimeKeys) are unchanged.

Scope

providers/Dmon.Providers.Mlx/ + its tests only. No core, ADR, or contract changes — ADR-027 (terminal-client selection) and ADR-034 (MLX runtime lifecycle) are honoured; this restores the intended self-heal. Clean-break internal ctor signature change (no published consumers).

Verification

  • make build — 0 warnings (TreatWarningsAsErrors).
  • env -u MEKO_API_KEY make test — full suite green; Dmon.Providers.Mlx.Tests 88/88 (new coverage: CreateAsync invokes EnsureRunningAsync + returns an EnsureRunningChatClient-wrapped client; advertises SupportsToolCalling == true post-probe; keyed MlxClient(key) returns a single-sourced self-healing client — ensureRunningCalls == 1).
  • openspec validate mlx-active-provider-self-heal --strict — valid.
  • Live (human-in-the-loop): rebuilt sandbox-code and ran a .UseMlx(port 8666) turn — the model responded, created and ran a C hello-world, and prompted for tool permissions. Confirms both runtime self-heal (no Connection refused) and tool-calling.

Change: mlx-active-provider-self-heal

🤖 Generated with Claude Code

ThatRendle and others added 3 commits July 22, 2026 15:06
…t-probe caps)

Move the MLX self-heal into MlxProviderFactory so the UseMlx active-provider
terminal client starts the mlx_lm runtime and advertises the verified
tool-calling capability. CreateAsync now awaits EnsureRunningAsync before
snapshotting capabilities (so the tool-calling probe has set ToolCallingVerified
first) and returns the decorator stack wrapped outermost in
EnsureRunningChatClient. CreateFactory() passes the extension; both factory
ctors take it. Reworked factory tests drive SupportsToolCalling through the
attach-first probe seam and cover the start path + wrapper identity.

Tasks: 1.1, 1.2, 1.3, 3.1, 3.2, 3.3
Change: mlx-active-provider-self-heal

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reduce the keyed daemon helper MlxClient(key) to resolve the keyed
MlxProviderExtension, build the ProviderConfig (BaseUrl null → factory falls
back to the runtime-state URL seeded by EnsureRunningAsync), and return the
factory's CreateAsync result verbatim. Drops the helper's duplicate up-front
EnsureRunningAsync call and its own outer EnsureRunningChatClient wrap — both
now supplied solely by the factory (Block 1), so the returned chain has exactly
one self-heal wrapper and construction probes exactly once. Keyed port routing
(firstline :8800 / escalation :8810) and the warm/stop seams are unchanged. Adds
a regression test asserting the single wrapper and a single EnsureRunningAsync
invocation.

Tasks: 2.1, 2.2, 3.4, 4.1, 4.2
Change: mlx-active-provider-self-heal

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User confirmed a live sandbox-code .UseMlx(port 8666) turn: the model
responded, created and ran a C hello-world, and prompted for tool
permissions — verifying both runtime self-heal and tool-calling. All 12
tasks complete.

Tasks: 4.3
Change: mlx-active-provider-self-heal

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ThatRendle
ThatRendle merged commit c4c3c81 into main Jul 23, 2026
5 checks passed
@ThatRendle
ThatRendle deleted the change/mlx-active-provider-self-heal branch July 23, 2026 13:40
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