Skip to content

Add canonical model selection and Gemini provider#444

Merged
BunsDev merged 5 commits intomainfrom
okcode/model-providers-contracts
Apr 13, 2026
Merged

Add canonical model selection and Gemini provider#444
BunsDev merged 5 commits intomainfrom
okcode/model-providers-contracts

Conversation

@BunsDev
Copy link
Copy Markdown
Contributor

@BunsDev BunsDev commented Apr 13, 2026

Summary

  • add a canonical ModelSelection foundation across contracts, persistence, orchestration, and the web app
  • replace static web model inventories with server-driven provider snapshots and model capability metadata
  • add Gemini as a first-class provider with health checks, catalog metadata, and a CLI-backed adapter

Validation

  • bun run --cwd packages/shared test -- src/modelSelection.test.ts
  • bun run --cwd apps/server test -- src/provider/providerCatalog.test.ts
  • bun fmt
  • bun lint
  • bun typecheck

Notes

  • the browser test wrapper in apps/web ignores the individual file filter and starts the broader browser suite; I confirmed the updated picker browser test path passes when that harness starts, then stopped the wrapper instead of treating it as targeted validation

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
okcode-marketing Ready Ready Preview Apr 13, 2026 7:29pm
v0-compute-the-platform-to-build Ready Ready Preview, Open in v0 Apr 13, 2026 7:29pm

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a canonical ModelSelection abstraction across contracts, persistence, orchestration, and the web app, and adds Gemini as a first-class provider backed by CLI health checks and an adapter.

Changes:

  • Add ModelSelection contract + shared normalization utilities, and persist canonical selections for projects/threads.
  • Replace static web model lists with server-driven provider snapshots (including per-model capability metadata).
  • Add Gemini provider integration (health probing + adapter + UI support).

Reviewed changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/shared/src/modelSelection.ts New shared helpers to normalize/construct canonical ModelSelection and prune unsupported options by capabilities.
packages/shared/src/modelSelection.test.ts Unit tests for canonical selection normalization and capability-based pruning.
packages/shared/src/model.ts Extend provider inference and reasoning-effort helpers to include gemini.
packages/shared/package.json Export new @okcode/shared/modelSelection entrypoint.
packages/contracts/src/server.ts Extend server provider snapshot schema to include auth object + model inventories + capabilities.
packages/contracts/src/orchestration.ts Add gemini to ProviderKind; introduce ModelSelection union and wire it through commands/read models.
packages/contracts/src/model.ts Add Gemini model options placeholder + model capability schemas and Gemini model inventory defaults.
apps/web/src/types.ts Carry ModelSelection through web Project/Thread types.
apps/web/src/store.ts Prefer modelSelection for mapping server read model into web state.
apps/web/src/routes/_chat.settings.tsx Add Gemini install/auth guidance and provider UI state wiring.
apps/web/src/routes/_chat.settings.index.tsx Add Gemini install/auth guidance and provider UI state wiring (index route).
apps/web/src/providerModels.ts New mapping layer from server provider snapshots + custom models into picker-ready options (with capabilities).
apps/web/src/modelSelection.ts New helper to resolve a “live” ModelSelection from snapshots, preferences, and draft inputs.
apps/web/src/lib/providerAvailability.ts Include Gemini in provider ordering/labels; support new auth shape during migration.
apps/web/src/components/sme/smeConversationConfig.ts Add Gemini label + SME auth defaults/options.
apps/web/src/components/sme/SmeConversationDialog.tsx Wire custom Gemini models into SME dialog selection flow.
apps/web/src/components/chat/providerStatusPresentation.ts Update provider setup phase logic for new auth shape; add Gemini label.
apps/web/src/components/chat/composerProviderRegistry.tsx Add Gemini provider registry entry for composer state.
apps/web/src/components/chat/ProviderModelPicker.tsx Switch picker to server-driven provider snapshots and add Gemini icon/labeling.
apps/web/src/components/chat/ProviderModelPicker.browser.tsx Update browser tests to mount picker with server provider snapshots (including Gemini).
apps/web/src/components/ChatView.tsx Move sending/persisting to modelSelection (and snapshot-driven models) instead of static inventories.
apps/web/src/appSettings.ts Add Gemini custom-model persistence and normalization.
apps/server/src/sme/authValidation.ts Allow Gemini SME auth methods and defaults.
apps/server/src/serverLayers.ts Register Gemini adapter and add ChildProcessSpawner requirement for provider layer.
apps/server/src/provider/providerCatalog.ts New built-in provider model catalog with capability metadata + custom model merging.
apps/server/src/provider/providerCatalog.test.ts Tests for provider catalog inventories and merging behavior.
apps/server/src/provider/Services/GeminiAdapter.ts New Gemini adapter service contract.
apps/server/src/provider/Layers/ProviderHealth.ts Add Gemini CLI health probe; refactor statuses to new auth shape + attach model inventories.
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts Allow optional Gemini adapter registration.
apps/server/src/provider/Layers/GeminiAdapter.ts New CLI-backed Gemini provider adapter emitting runtime events.
apps/server/src/persistence/Services/ProjectionThreads.ts Add modelSelection field to persisted thread projection schema.
apps/server/src/persistence/Services/ProjectionProjects.ts Add defaultModelSelection field to persisted project projection schema.
apps/server/src/persistence/Migrations/025_CanonicalizeModelSelections.ts Migration adding JSON model-selection columns and backfilling from legacy model strings.
apps/server/src/persistence/Layers/ProjectionThreads.ts Store/read modelSelection as JSON string in projection_threads.
apps/server/src/persistence/Layers/ProjectionProjects.ts Store/read defaultModelSelection as JSON string in projection_projects.
apps/server/src/orchestration/projector.ts Project defaultModelSelection/modelSelection into read model (plus canonicalization on thread create).
apps/server/src/orchestration/decider.ts Emit canonical selections on create commands; pass through selections on updates/turn start.
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts Prefer modelSelection when starting sessions and sending turns.
apps/server/src/orchestration/Layers/ProjectionThreadDetailQuery.ts Include modelSelection in thread detail query results.
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Include model selection fields in snapshot query results.
apps/server/src/orchestration/Layers/ProjectionPipeline.ts Persist model selection fields through projection pipeline updates.
apps/server/src/orchestration/Layers/ProjectionOverviewQuery.ts Include model selection fields in overview query results.
apps/server/src/doctor.ts Display auth status via new auth shape, and include Gemini label.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2267 to +2270
if (
input.modelSelection !== undefined &&
JSON.stringify(input.modelSelection) !== JSON.stringify(serverThread.modelSelection ?? null)
) {
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

Using JSON.stringify to compare ModelSelection objects is brittle (key order / undefined vs missing fields) and can trigger unnecessary thread.meta.update dispatches. Prefer a stable comparison (e.g., compare provider, model, and a normalized options object) or a dedicated deep-equality helper for model selections.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +150 to +163
const nextModel = input.model ?? existing.session.model ?? "auto-gemini-3";
const currentTurnId = turnId();
const prompt = input.input ?? "";
const args = [
"-p",
prompt,
"--output-format",
"stream-json",
"--model",
nextModel,
"--sandbox",
"--approval-mode",
"yolo",
];
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

sendTurn hard-codes --approval-mode yolo, which bypasses the app's runtimeMode (e.g., approval-required) and may allow tool execution without user confirmation. Map Gemini CLI approval behavior to the session/runtimeMode (or omit the flag unless full-access), so approval-required threads can’t run in YOLO mode by default.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +15 to +19
const ProjectionThreadDbRowSchema = ProjectionThread.mapFields(
Struct.assign({
modelSelection: Schema.fromJsonString(ProjectionThread.fields.modelSelection),
}),
);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

modelSelection is mapped with Schema.fromJsonString(...) without allowing DB NULL. Rows where model_selection is NULL (e.g., model is NULL/blank, or pre-migration data) will fail decoding. Wrap this field with Schema.NullOr(...) (and consider normalizing undefined to null on upsert, like ProjectionProjects does) so reads/writes are robust.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

BunsDev and others added 2 commits April 13, 2026 14:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@BunsDev
Copy link
Copy Markdown
Contributor Author

BunsDev commented Apr 13, 2026

@copilot apply changes based on the comments in this thread

…c model/modelSelection fields, map runtimeMode to Gemini approval-mode, fix OpenClaw installed flag, handle NULL modelSelection in DB

Agent-Logs-Url: https://github.com/OpenKnots/okcode/sessions/7c26e215-46c1-4d8f-858d-f66de51bdd35

Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
…Or pattern in ProjectionThreads

Agent-Logs-Url: https://github.com/OpenKnots/okcode/sessions/7c26e215-46c1-4d8f-858d-f66de51bdd35

Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

@copilot apply changes based on the comments in this thread

Applied all changes from the review thread in commits 972b6a1 and 801ae84:

  • ChatView.tsx: replaced JSON.stringify comparison with a stable modelSelectionsAreEqual helper (key-sorted options comparison to avoid false positives from insertion-order differences)
  • packages/shared/src/modelSelection.ts: added the modelSelectionsAreEqual utility
  • decider.ts: when command.modelSelection is absent but command.model is present, derive canonical modelSelection to keep both fields in sync; same pattern for defaultModelSelection/defaultModel
  • projector.ts (project.meta-updated): derive defaultModelSelection from payload.defaultModel when payload.defaultModelSelection is absent, for deterministic replay of older events
  • GeminiAdapter.ts: map runtimeMode to --approval-mode (full-accessyolo, approval-requiredsuggest) instead of hardcoding yolo
  • ProviderHealth.ts: set installed: true when OpenClaw auth/handshake fails so the UI lands on the authenticate phase rather than the install phase
  • ProjectionThreads.ts: wrapped modelSelection with Schema.NullOr(...) to handle NULL DB rows; normalize undefinednull on upsert to match the ProjectionProjects pattern

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants