mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-24 20:25:36 -04:00
feat(tui): add provider auth status indicators to model dialog
Shows ✗ for expired and △ for expiring tokens next to provider names in favorites, recent, and all providers sections. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+11
-10
@@ -126,8 +126,9 @@ Agent-core is a fork of OpenCode with three personas (Zee, Stanley, Johny) shari
|
||||
- [x] MemoryStore high-level API for save/search/list
|
||||
- [x] Collection management with auto-creation
|
||||
- [x] Embedding generation (`src/memory/embedding.ts`)
|
||||
- [x] Local BGE-M3 via vLLM (1024d)
|
||||
- [x] OpenAI fallback
|
||||
- [x] **Qwen3-Embedding-8B via Nebius API** (4096d, #1 on MTEB)
|
||||
- [x] Voyage AI provider support
|
||||
- [x] OpenAI/local fallback options
|
||||
- [x] Conversation continuity across compactions (`src/personas/continuity.ts`)
|
||||
- [x] ContinuityManager with session lifecycle
|
||||
- [x] Key fact extraction (heuristic + LLM ready)
|
||||
@@ -137,7 +138,7 @@ Agent-core is a fork of OpenCode with three personas (Zee, Stanley, Johny) shari
|
||||
- [x] Namespace-based isolation
|
||||
- [x] Shared Qdrant collections
|
||||
- [x] Memory bridge for persona state (`src/personas/memory-bridge.ts`)
|
||||
- [ ] Memory search MCP tool (not yet exposed via MCP)
|
||||
- [x] Memory search MCP tool (`personas-memory` server)
|
||||
|
||||
### 3.2 Orchestration (Tiara) - COMPLETE
|
||||
- [x] Drone spawning via Task tool (`src/personas/tiara.ts`)
|
||||
@@ -158,21 +159,21 @@ Agent-core is a fork of OpenCode with three personas (Zee, Stanley, Johny) shari
|
||||
- [x] Session lifecycle hook for auto-extraction
|
||||
- [x] Heuristic and LLM-based extraction
|
||||
|
||||
### 3.3 MCP Servers
|
||||
### 3.3 MCP Servers - COMPLETE
|
||||
- [x] Context7 integration
|
||||
- [ ] Custom MCP for personas (not yet implemented)
|
||||
- [ ] Memory MCP (expose memory search to external tools)
|
||||
- [ ] Calendar MCP (expose calendar to external tools)
|
||||
- [ ] Portfolio MCP (expose portfolio to external tools)
|
||||
- [x] Custom MCP for personas (`src/mcp/servers/`)
|
||||
- [x] Memory MCP (`personas-memory`) - store, search, list, delete, stats
|
||||
- [x] Calendar MCP (`personas-calendar`) - events, create, update, delete, free-time
|
||||
- [x] Portfolio MCP (`personas-portfolio`) - status, positions, market-data, SEC, backtest
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: TUI Improvements
|
||||
|
||||
### 4.1 Model Selection - MOSTLY COMPLETE
|
||||
### 4.1 Model Selection - COMPLETE
|
||||
- [x] Favorites system (`dialog-model.tsx` favorites support)
|
||||
- [x] Recently used models (`local.model.recent()`)
|
||||
- [ ] Provider status indicators (auth valid/expired)
|
||||
- [x] Provider status indicators (auth valid/expired) - shows ✗/△ in model dialog
|
||||
- [x] Cost display for non-free models (shows "Free" badge)
|
||||
|
||||
### 4.2 Agent Experience
|
||||
|
||||
@@ -8,6 +8,18 @@ import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
|
||||
import { Keybind } from "@/util/keybind"
|
||||
import * as fuzzysort from "fuzzysort"
|
||||
|
||||
/** Get auth status indicator for a provider */
|
||||
function getAuthIndicator(
|
||||
providerID: string,
|
||||
authStatus: Record<string, { valid: boolean; expiringSoon: boolean; expiresIn: number | null }>,
|
||||
): string {
|
||||
const status = authStatus[providerID]
|
||||
if (!status) return ""
|
||||
if (!status.valid) return "✗ "
|
||||
if (status.expiringSoon) return "△ "
|
||||
return ""
|
||||
}
|
||||
|
||||
export function useConnected() {
|
||||
const sync = useSync()
|
||||
return createMemo(() =>
|
||||
@@ -37,6 +49,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
const showSections = showExtra() && needle.length === 0
|
||||
const favorites = connected() ? local.model.favorite() : []
|
||||
const recents = local.model.recent()
|
||||
const authStatus = sync.data.provider_auth_status
|
||||
|
||||
const recentList = showSections
|
||||
? recents.filter(
|
||||
@@ -50,6 +63,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
if (!provider) return []
|
||||
const model = provider.models[item.modelID]
|
||||
if (!model) return []
|
||||
const authIndicator = getAuthIndicator(provider.id, authStatus)
|
||||
return [
|
||||
{
|
||||
key: item,
|
||||
@@ -58,7 +72,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
modelID: model.id,
|
||||
},
|
||||
title: model.name ?? item.modelID,
|
||||
description: provider.name,
|
||||
description: authIndicator + provider.name,
|
||||
category: "Favorites",
|
||||
disabled: provider.id === "opencode" && model.id.includes("-nano"),
|
||||
footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
|
||||
@@ -83,6 +97,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
if (!provider) return []
|
||||
const model = provider.models[item.modelID]
|
||||
if (!model) return []
|
||||
const authIndicator = getAuthIndicator(provider.id, authStatus)
|
||||
return [
|
||||
{
|
||||
key: item,
|
||||
@@ -91,7 +106,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
modelID: model.id,
|
||||
},
|
||||
title: model.name ?? item.modelID,
|
||||
description: provider.name,
|
||||
description: authIndicator + provider.name,
|
||||
category: "Recent",
|
||||
disabled: provider.id === "opencode" && model.id.includes("-nano"),
|
||||
footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
|
||||
@@ -116,8 +131,9 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
(provider) => provider.id !== "opencode",
|
||||
(provider) => provider.name,
|
||||
),
|
||||
flatMap((provider) =>
|
||||
pipe(
|
||||
flatMap((provider) => {
|
||||
const authIndicator = getAuthIndicator(provider.id, authStatus)
|
||||
return pipe(
|
||||
provider.models,
|
||||
entries(),
|
||||
filter(([_, info]) => info.status !== "deprecated"),
|
||||
@@ -130,7 +146,7 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
return {
|
||||
value,
|
||||
title: info.name ?? model,
|
||||
category: connected() ? provider.name : undefined,
|
||||
category: connected() ? authIndicator + provider.name : undefined,
|
||||
disabled: provider.id === "opencode" && model.includes("-nano"),
|
||||
footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
|
||||
onSelect() {
|
||||
@@ -159,8 +175,8 @@ export function DialogModel(props: { providerID?: string }) {
|
||||
(x) => x.footer !== "Free",
|
||||
(x) => x.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const popularProviders = !connected()
|
||||
|
||||
Reference in New Issue
Block a user