# dsh-agy-ui

<p align="center">
  <strong>Non-invasive UI & UX Companion Enhancement Plugin for <code>dsh-agy</code> in DeepSeek Harness (DSH)</strong><br/>
  Model List Purification · Header Quota Badge · Glassmorphic Dual-Bucket Popover · Mobile Drawer Adaptation
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/dsh-agy-ui"><img src="https://img.shields.io/npm/v/dsh-agy-ui?color=blue&label=npm" alt="npm version"/></a>
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License"/></a>
  <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DeepSeek%20Harness-Plugin-orange" alt="DSH Plugin"/></a>
  <a href="https://github.com/Erick0412-dev/dsh-agy-ui/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/></a>
</p>

<p align="center">
  <b>🌐 Language / 语言:</b>
  <a href="./README.md">简体中文</a> ·
  <b>English</b>
</p>

---

## 💡 Why dsh-agy-ui?

In the [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) ecosystem, [dsh-agy](https://github.com/chaos-03x/dsh-agy) serves as the lightning-fast, rock-solid provider directly talking to Google's internal API (`POST /v1internal:streamGenerateContent`) with native DSH Tool execution.

However, out-of-the-box it has several noticeable usability pain points:

| Dimension | Native `dsh-agy` Experience | Enhanced with `dsh-agy-ui` |
| :--- | :--- | :--- |
| **Model Selection** | Cluttered with internal ghost models (`chat_23310`, `tab_flash_lite_preview`) | 🧹 **Auto Regex Purification**, completely filters out non-chat models |
| **Reasoning Efforts** | Low / Medium / High efforts expanded into separate dropdown rows | 🎛️ **Smart Variant Folding**, seamlessly triggers DSH native reasoning picker |
| **Tiered Model Names** | Raw IDs displayed crudely (e.g. `gemini-3.8-flash-tiered`) | ✨ **Standardized Brand Typography**, shown cleanly as `Gemini 3.8 Flash` |
| **Model Priority** | Disordered sequence, hard to spot primary models quickly | 🏆 **Strict Priority Ranking**: 3.8 Flash > 3.7 > 3.6 > Pro > Claude > GPT-OSS |
| **Quota Visibility** | Quotas hidden away on separate `/agy` dashboard | ⚡ **Persistent Header Badge**, directly showing the tightest quota window (5-hour / weekly) |
| **Quota Inspection** | Must navigate away from current chat to check details | 🪟 **Hover/Click Glassmorphic Popover**, inspecting every 5-hour / daily / weekly / monthly window |
| **Mobile UX** | Desktop popovers overflow or get cut off on narrow screens | 📱 **Responsive Drawer**, automatically slides up an iOS/Android bottom sheet |

`dsh-agy-ui` is strictly architected as a **pure companion enhancement plugin**: network transport, request streaming, and tool execution remain 100% managed by `dsh-agy`.

---

## 🏛️ Architecture & Interception Flow

`dsh-agy-ui` builds on Cordis microkernel plugin architecture. It dynamically wraps the LLM adapter in-memory and injects UI components into DSH Web slots:

```text
                    ┌────────────────────────────────────────────────────────┐
                    │               DeepSeek Harness (DSH Web)               │
                    └───────────┬────────────────────────────────┬───────────┘
                                │                                │
                        (Web Slots Injection)            (Cordis Plugin Hook)
                                │                                │
                                ▼                                ▼
                    ┌────────────────────────┐      ┌─────────────────────────┐
                    │      dsh-agy-ui        │◄─────┤ Adapter Dynamic Wrapper │
                    │ (Header Badge + Popover)│      │(Purify/Fold/Brand/Sort) │
                    └───────────┬────────────┘      └────────────┬────────────┘
                                │ HTTP Status Read                │ Native Delegation
                                ▼                                ▼
                    ┌────────────────────────┐      ┌─────────────────────────┐
                    │    dsh-agy Backend     │      │   dsh-agy Core Layer    │
                    │  (/agy/api/accounts)   │      │(Direct Google API Stream│
                    └────────────────────────┘      └─────────────────────────┘
                                ▲
                                │ Read Compatibility (Optional)
                    ┌───────────┴────────────┐
                    │      dsh-agy-link      │
                    │ (/plugins/agy-link/..) │
                    └────────────────────────┘
```

---

## ✨ Features Breakdown

### 1. 🧼 Model Purification & Standardized Presentation

<p align="center">
  <img src="./docs/images/clean-models.png" width="300" alt="Purified Models List Preview" />
  <br />
  <em>▲ Purified Antigravity (agy) native model selector dropdown preview</em>
</p>

- **Ghost Model Exclusion**: Automatically excludes internal non-chat endpoints matching `^chat_\d+$` and `^tab_`.
- **Reasoning Variant Folding**: Collapses fragmented `-low`, `-medium`, `-high` models into their root model, injecting standard `thinkingEfforts` metadata so DSH's native reasoning dropdown activates.
- **Brand Typography**: Formats Tiered models (e.g. `gemini-3.8-flash-tiered`) into canonical titles (`Gemini 3.8 Flash`).
- **Priority Ranking**: Gemini Flash (3.8 > 3.7 > 3.6 > 3.5 > 3) → Gemini Pro (3.1 Pro) → 2.5 Series → Claude 4.6 → GPT-OSS.

### 2. ⚡ Persistent Header Quota Badge

<p align="center">
  <img src="./docs/images/badge.png" width="200" alt="Header Quota Badge Preview" />
  <br />
  <em>▲ Real-time quota capsule badge mounted on the session header (breathing indicator + dynamic percentage)</em>
</p>

- **Non-Invasive Slot Injection**: Mounted at `conversation.session.header.actions` with isolated ID `agy-ui-quota-badge`.
- **Direct Quota Glance**: Directly shows the **tightest** window of the active account (e.g. `AGY · 55%`), read from dsh-agy’s **grouped windows** (`account.limits`) rather than the per-model quota upstream removed.
- **Weighed by runway left, not by raw minimum**: the 5-hour and weekly windows are each measured against their own drain threshold (15% / 1%, matching dsh-agy's rotation test), so a nearly spent week takes the badge over and is named (`AGY · 周额度 1%`), while a half-spent week does not drag the 5-hour figure down. `daily` / `monthly` windows are displayed but never drive it — the pool does not block on them.
- **Subtle Breathing Animation**: Steady indicator dot in normal operation (green = active, yellow = cooling/rate-limited, gray = unavailable); smoothly pulses for 1.2s only during background sync or manual refresh.
- **120s Global Frequency Lock**: 2-minute polling interval combined with a 120-second window-focus cooldown lock, preventing high-frequency Google 429 rate limits caused by tab switches.

### 3. 🪟 Glassmorphic Quota Inspector Popover

<p align="center">
  <img src="./docs/images/popover.png" width="380" alt="Quota Inspector Popover Preview" />
  <br />
  <em>▲ Grouped multi-window (5-hour / daily / weekly / monthly) quota inspection panel</em>
</p>

- **Hover & Pin Support**: Hover to display instantly, centered right below the badge. Click the badge to pin it open.
- **Every window of every upstream group**: dsh-agy's quota groups pass through verbatim (currently `Gemini Models` and `Claude and GPT models`), one row per window, labelled from upstream's own `window` field:
  - **5-hour (5h)**, **daily**, **weekly**, **monthly** — and any window token upstream adds later is carried through verbatim rather than dropped.
  - **A window upstream did not report renders as `—` over a grey empty bar**, never 0%: "unknown" and "exhausted" are opposite facts.
- **Account Health & Shortcuts**: Displays masked account identifier and real-time health status (including the `verification-required` park and its appeal link), a one-click refresh button, and — on `dsh-agy ≥ 0.3.0` — a pointer to **Settings → Antigravity**.

### 4. 📱 Mobile Bottom Sheet Drawer
- Responsive adaptation: On screens $\le$ 640px, the popover transforms into an iOS/Android style bottom drawer with touch-friendly dismiss and backdrop.

### 5. 🛡️ Namespace Isolation & Coexistence
- All CSS classes use the `agy-ui-*` namespace prefix, completely eliminating style bleed.
- Fully compatible with `dsh-agy-link`: quota data follows the fallback order **dsh-agy grouped windows → dsh-agy-link family quotas → legacy per-model rows**.

### 6. 🔌 Data Channel (dsh-agy ≥ 0.3.0)

dsh-agy 0.3.0 moved its management surface from the standalone `/agy` dashboard onto **DSH's own RPC channel**: the host registers `/api/agy` and the browser calls it through `connection.rpc.call('/api', 'agy', { method, payload })`. Only `/agy/oauth-callback` is still a plain HTTP route (Google redirects a browser there with a GET); the old `/agy/api/accounts` is **gone**. The 5-hour / weekly source also changed: `fetchAvailableModels`' per-model `quotaInfo` has **no window field** and therefore can never distinguish 5h from weekly, so the windows now come from `v1internal:retrieveUserQuotaSummary`, cached per account as `cachedLimits`.

This plugin therefore reads, in order (see `src/quota-source.ts`):

| Priority | Channel | Notes |
| :--- | :--- | :--- |
| 1 | `POST /api/agy` → `account.list` | Account rows plus `limits` / `limitsUpdatedAt` (a TTL cache — **no upstream request**) |
| 1 | `POST /api/agy` → `account.limits` | The grouped windows; inside the 10-minute TTL the server skips the probe entirely, so a real measurement happens only when stale or on an explicit refresh (`force: true`) |
| 2 | `GET /agy/api/accounts` | Legacy dsh-agy (< 0.3.0) fallback |
| 3 | `GET /plugins/agy-link/status` | An independent quota pool, always fetched in parallel |

- **Automatic polling never `force`s**: no upstream call inside the TTL. If a probe run fails entirely (`measured: 0` with `failed > 0`) the client backs off for a full TTL, so a broken probe cannot turn every poll into an upstream request.
- **Degrades instead of blanking**: a missing `connection` service or a failing RPC falls back to the legacy HTTP channel, and when neither answers the last good data is kept on screen rather than cleared.

---

## 🚀 Quick Start & Installation

> 💡 **Prerequisite**: Ensure that the upstream [dsh-agy](https://github.com/chaos-03x/dsh-agy) provider is installed and authenticated.

This project is officially published on **[npm (dsh-agy-ui)](https://www.npmjs.com/package/dsh-agy-ui)**. Run one command in your terminal to automatically install dependencies and register the bundle into your DSH profile:

```bash
# Official CLI one-click install & registration
dsh plugin --profile web add dsh-agy-ui

# 💡 If dsh is not in global PATH, run via npx:
npx @deepseek-ai/dsh plugin --profile web add dsh-agy-ui
```

Start or restart DSH Web:
```bash
dsh web
# Or: dsh restart
```

Refresh your DSH Web interface (`http://127.0.0.1:3080`) to enjoy purified models and the header quota badge!

<details>
<summary><b>🛠️ Developer / Git Installation Options</b></summary>

- **Direct Install from GitHub**:
  ```bash
  dsh plugin --profile web add github:Erick0412-dev/dsh-agy-ui
  ```
- **Local Source Development**:
  ```bash
  dsh plugin --profile web add /path/to/dsh-agy-ui
  ```
</details>

---

## ❓ Frequently Asked Questions (FAQ)

<details>
<summary><b>Q1: Why does the badge indicator show a gray dot?</b></summary>
A gray dot indicates that no active Antigravity accounts were detected. On dsh-agy 0.3.0 and later, add an account and complete Google authentication under DSH's <b>Settings → Antigravity</b> (upstream removed the old <code>/agy</code> dashboard).
</details>

<details>
<summary><b>Q2: Why doesn't the quota refresh immediately when switching tabs?</b></summary>
To protect your accounts against Google 429 Rate Limit penalties, the plugin implements a <b>120-second Global Frequency Lock</b>. Window focus and tab switches within 120 seconds are debounced. You can click the manual refresh icon in the popover at any time to force an immediate update.
</details>

<details>
<summary><b>Q3: Can I run both dsh-agy-ui and dsh-agy-link together?</b></summary>
Yes! <code>dsh-agy-ui</code> features isolated slot IDs and distinct CSS namespaces. It seamlessly coexists with <code>dsh-agy-link</code> and uses it as a fallback: dsh-agy's grouped windows (5h / daily / weekly / monthly) come first, and link's family quotas are read when no grouped data exists yet.
</details>

---

## 🙏 Acknowledgments

This project stands on the shoulders of giants. Sincere thanks to:

- 🌟 **[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)** (`deepseek-ai/deepseek-harness`)  
  **The Core Foundation of Everything**. DSH's elegant Cordis microkernel, extensible Web UI slots, and LLM adapter abstractions make non-invasive companion plugins possible.
- ⚡ **[dsh-agy](https://github.com/chaos-03x/dsh-agy)** by [@chaos-03x](https://github.com/chaos-03x)  
  **The Core Upstream Provider**. Connecting directly to Google's internal APIs with native tool calling and multi-account rotation, providing the reliable backend engine.
- 🎨 **[dsh-agy-link](https://github.com/amlyczz/dsh-agy-link)** by [@amlyczz](https://github.com/amlyczz)  
  **Invaluable Inspiration & Aesthetic Masterpiece**. Its gorgeous UI layout, dual-bucket quota monitoring concept (5h sprint + 7d weekly), and refined visual craft served as tremendous inspiration for this companion plugin!

---

## ⚖️ License & Compliance

- This project is licensed under the [MIT License](./LICENSE).
- All referenced upstream projects (`deepseek-harness`, `dsh-agy`, `dsh-agy-link`) are licensed under the permissive **MIT License**. This project complies with all applicable open-source license terms by integrating through public hooks, runtime wrappers, and HTTP APIs without incorporating proprietary or infringing derivative code.
- **Disclaimer**: This is an independent community open-source project and is not affiliated with, endorsed by, or sponsored by Google LLC or DeepSeek. Please use responsibly according to each service provider's terms of service.
