Skip to content

Keep provider diagnostics and model lists in sync#1660

Merged
boudra merged 6 commits into
mainfrom
pi-model-list-empty
Jun 23, 2026
Merged

Keep provider diagnostics and model lists in sync#1660
boudra merged 6 commits into
mainfrom
pi-model-list-empty

Conversation

@boudra

@boudra boudra commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Linked issue

N/A

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Refactor / code improvement
  • Docs

What does this PR do

Provider diagnostics now refresh and report from the same provider snapshot used by the model list. Availability checks stay cheap and only answer whether the provider binary is present; model and mode discovery happen through one catalog refresh path.

This also removes provider-specific diagnostic model probes, lets providers fetch models and modes together where possible, keeps custom added models visible when replacement models are configured, and prevents the model sheet from showing stale discovered models after a terminal refresh error or empty catalog.

How did you verify it

  • npm run format
  • Pre-commit hook: targeted format check, targeted lint, and npm run typecheck
  • npm run typecheck
  • npm run lint
  • Targeted tests:
    • packages/server/src/server/agent/provider-registry.test.ts
    • packages/server/src/server/agent/provider-snapshot-manager.test.ts
    • packages/server/src/server/agent/providers/opencode-agent.test.ts
    • packages/server/src/server/agent/providers/generic-acp-agent.diagnostic.test.ts

No screenshot attached: the app change is state consistency in the existing provider model sheet, not a visual layout change.

Checklist

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes
  • npm run lint passes
  • npm run format ran (Biome)
  • UI changes include screenshots or video for every affected platform
  • Tests added or updated where it made sense

@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR consolidates listModels / listModes into a single fetchCatalog(options, client?) call across every provider client, so provider diagnostics and the model sheet always draw from the same snapshot. It also simplifies isAvailable to a binary-only check (fast) and strips model/mode probing out of individual getDiagnostic implementations, replacing it with a centralized Models: N / Status: suffix appended by ProviderSnapshotManager.getProviderDiagnostic after a forced catalog refresh.

  • Interface unification: AgentClient.fetchCatalog replaces separate listModels / listModes; createRegistryEntry skips runtime discovery for replacement-model profiles, and the snapshot manager passes the live client through so diagnostics use the same authority as background refreshes.
  • Stale model prevention: provider-diagnostic-sheet.tsx switches to useMemo + useEffect so stale discovered models are shown while a refresh is in progress but cleared to [] once a terminal error or empty catalog is confirmed.
  • ACP consolidation: ACPAgentClient now spawns a single process and issues one newSession call for both models and modes, replacing two separate spawns.

Confidence Score: 5/5

Safe to merge. The interface consolidation is internally consistent, all provider implementations updated, tests cover the key catalog-merge and diagnostic paths, and no data-loss or incorrect-behavior paths were found.

The change is a clean rename+unification of a single discovery call across 10+ providers, with careful preservation of model-merge logic and correct client injection for the snapshot manager. The React fix correctly guards the stale-model ref behind a refresh flag so models disappear only on confirmed terminal errors.

No files require special attention. The snapshot manager and registry changes are well-tested by the updated test suites.

Important Files Changed

Filename Overview
packages/server/src/server/agent/agent-sdk-types.ts Replaces ListModelsOptions/ListModesOptions with FetchCatalogOptions and introduces ProviderCatalog, cleanly unifying the discovery interface.
packages/server/src/server/agent/provider-registry.ts createRegistryEntry now skips runtime model discovery when replacement models are configured, calling fetchCatalog only for modes; wrapClientProvider and the existing mergeModels path are preserved correctly.
packages/server/src/server/agent/provider-snapshot-manager.ts getProviderDiagnostic force-refreshes the snapshot via fetchCatalog before appending Models/Status; loadProvider correctly passes the live client to definition.fetchCatalog.
packages/app/src/components/provider-diagnostic-sheet.tsx Switches stableDiscoveredRef management from render-phase assignment to useEffect, returns [] after terminal errors when not refreshing, preventing stale model display.
packages/server/src/server/agent/providers/opencode-agent.ts fetchCatalog acquires the OpenCode server once and runs fetchModelsFromClient/fetchModesFromClient in parallel; getDiagnostic stripped of model probe and server-status row.
packages/server/src/server/agent/providers/acp-agent.ts Consolidates listModels + listModes into a single fetchCatalog that spawns one ACP process and issues one newSession call, returning both models and modes.
packages/server/src/server/agent/providers/pi/agent.ts fetchCatalog passes PI_CATALOG_REQUEST_TIMEOUT_MS to getAvailableModels; isAvailable simplified to binary-only check; getDiagnostic stripped of runtime session probe.
packages/server/src/server/agent/providers/generic-acp-agent.ts getDiagnostic simplified to binary/version checks only; runDiagnosticACPProbe removed in favor of centralized snapshot-based reporting.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as ProviderDiagnosticSheet
    participant PSM as ProviderSnapshotManager
    participant REG as ProviderRegistry
    participant CLI as AgentClient

    Note over UI,CLI: Model Sheet / Background Refresh
    UI->>PSM: "listProviders({wait:true})"
    PSM->>REG: "definition.fetchCatalog({cwd, force}, client)"
    REG->>CLI: client.fetchCatalog(options)
    CLI-->>REG: "{models, modes}"
    REG-->>PSM: "{models: merged, modes: decorated}"
    PSM-->>UI: ProviderSnapshotEntry

    Note over UI,CLI: Provider Diagnostics
    UI->>PSM: getProviderDiagnostic(provider)
    PSM->>PSM: refreshSnapshotForCwd (force)
    PSM->>CLI: client.isAvailable()
    CLI-->>PSM: true/false
    PSM->>REG: "definition.fetchCatalog({cwd, force}, client)"
    REG->>CLI: client.fetchCatalog(options)
    CLI-->>REG: "{models, modes}"
    REG-->>PSM: catalog
    PSM->>CLI: client.getDiagnostic()
    CLI-->>PSM: baseDiagnostic
    PSM-->>UI: baseDiagnostic + Models/Status
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as ProviderDiagnosticSheet
    participant PSM as ProviderSnapshotManager
    participant REG as ProviderRegistry
    participant CLI as AgentClient

    Note over UI,CLI: Model Sheet / Background Refresh
    UI->>PSM: "listProviders({wait:true})"
    PSM->>REG: "definition.fetchCatalog({cwd, force}, client)"
    REG->>CLI: client.fetchCatalog(options)
    CLI-->>REG: "{models, modes}"
    REG-->>PSM: "{models: merged, modes: decorated}"
    PSM-->>UI: ProviderSnapshotEntry

    Note over UI,CLI: Provider Diagnostics
    UI->>PSM: getProviderDiagnostic(provider)
    PSM->>PSM: refreshSnapshotForCwd (force)
    PSM->>CLI: client.isAvailable()
    CLI-->>PSM: true/false
    PSM->>REG: "definition.fetchCatalog({cwd, force}, client)"
    REG->>CLI: client.fetchCatalog(options)
    CLI-->>REG: "{models, modes}"
    REG-->>PSM: catalog
    PSM->>CLI: client.getDiagnostic()
    CLI-->>PSM: baseDiagnostic
    PSM-->>UI: baseDiagnostic + Models/Status
Loading

Reviews (5): Last reviewed commit: "refactor(app): update stable discovered ..." | Re-trigger Greptile

Comment thread packages/server/src/server/agent/provider-registry.ts Outdated
@boudra

boudra commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

On the Pi-specific diagnostic rows: removing those runtime-derived rows is intentional for this PR. Configured providers and Paseo MCP tools both require starting Pi and loading extensions, which is the slow side effect this change is avoiding in diagnostics. The centralized snapshot still appends the authoritative model count and status after one catalog refresh; provider diagnostics stay limited to cheap command/binary/auth facts.

@joyshan1986

Copy link
Copy Markdown

Confirming this resolves a concrete, reproducible case on Windows.

Symptom: Pi showed as not installed / no models in the provider list, while the diagnostic panel simultaneously reported Status: Available with 25 models — the exact diagnostics-vs-model-list mismatch this PR targets.

Root cause (matches this PR): PiAgent.isAvailable() wrapped checkProviderLaunchAvailable + a full pi --mode rpc spawn + getAvailableModels().length > 0 in a hard 2000 ms withTimeout. On this machine pi --mode rpc cold-starts in ~2.1–2.4 s, so isAvailable() always timed out → false → the daemon threw Provider 'pi' is not available and refused to list models. The diagnostic path has no 2000 ms cap, which is why it alone reported "Available".

Environment / measurements:

  • Windows 11, Paseo 0.1.98, pi (@earendil-works/pi-coding-agent) 0.79.10
  • pi --mode rpc + get_available_models → first response at 2135–2227 ms across repeated runs (25 models), never under 2000 ms
  • node dist/cli.js --mode rpc directly ≈ 2140 ms, so it's pi's own cold-start, not the cmd.exe/.cmd shim
  • Even pi --version ≈ 2.4 s

Confirms the fix's direction: as a stopgap I raised only that single literal (20009000 ms) in the installed app.asar, and Pi immediately became available with all 25 models. Since this PR makes isAvailable() answer purely from binary presence (no model fetch under a tight cap), it removes the trigger entirely rather than just widening the window.

Closely related: #1541 (its "Root Cause 1" is this same isAvailable() full-session spawn).

Thanks for the fix!

boudra added 4 commits June 23, 2026 15:40
…alog

Remove listModels/listModes from AgentClient and fetchModels/fetchModes
from ProviderDefinition. All provider runtime discovery now flows through
a single fetchCatalog(options) => ProviderCatalog API.

ProviderSnapshotManager.listModels/listModes remain as cached snapshot
conveniences only. Provider implementations (acp, codex, opencode, pi,
claude, mock) updated accordingly; agent-manager default model resolution
now calls fetchCatalog.

Reshape step toward issue pi-model-list-empty.
…residue

Migrate remaining AgentClient/provider-client implementations and tests to
fetchCatalog. Remove obsolete ListModelsOptions/ListModesOptions interfaces.
Update ProviderSnapshotManager.getProviderDiagnostic to materialize clients
via ensureClient(provider, definition) so diagnostics self-heal the settings
sheet instead of failing when providerClients[provider] is absent.

Allowed to remain: ProviderSnapshotManager.listModels/listModes as cached
snapshot readers; protocol/client legacy list_provider_models names; unrelated
local helper in create-agent-mode.
- Restore TestAgentClient.fetchCatalog with proper model list and resumeSession.
- Restore NativeArchiveRecordingClient and EnvProbeAgentClient removed during refactor.
- Fix ResumeCaptureClient.fetchCatalog and resumeSession.
- Fix stream-coalescing TestAgentClient.fetchCatalog shape and isAvailable.
- Mock accessible OpenCode provider in full-access mode tests so fetchCatalog does not throw.
@boudra boudra merged commit 1970a14 into main Jun 23, 2026
15 checks passed
@boudra boudra deleted the pi-model-list-empty branch June 23, 2026 09:55
sakurayun pushed a commit to sakurayun/paseo-reclaude that referenced this pull request Jun 23, 2026
…tHub 面板工具栏与加载态)

冲突解决(5 文件,均融合双方):
- explorer-sidebar.tsx:保留 fork 的 git tab 与 resolveVisibleExplorerTab,并入上游 PR 加载态 showPrTab(PR 加载中保持 PR tab 可见)
- claude/agent.ts:listModels→fetchCatalog 返回 ProviderCatalog {models,modes},保留 fork 的 SDK effort 装饰与 Ultracode 逻辑
- claude/models.test.ts & agent.test.ts:测试迁移到 fetchCatalog+解构;保留 fork bedrock test,丢弃与 fork 模型表示不兼容的上游 ultracode-thinking-option test(fork 不把 ultracode 作 thinking option)
- pi/agent.ts:采纳上游简化的 isAvailable(fork 未改动该方法),删除随之失效的 withTimeout import

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants