Skip to content

feat: add Synthetic provider plugin#329

Open
ben-vargas wants to merge 1 commit intorobinebers:mainfrom
ben-vargas:synthetic-provider-plugin
Open

feat: add Synthetic provider plugin#329
ben-vargas wants to merge 1 commit intorobinebers:mainfrom
ben-vargas:synthetic-provider-plugin

Conversation

@ben-vargas
Copy link
Copy Markdown

@ben-vargas ben-vargas commented Mar 30, 2026

Description

Adds a new provider plugin for Synthetic that displays quota usage from their /v2/quotas API endpoint.

Synthetic is currently rolling out a new rate limit system (v3) alongside the legacy subscription model. This plugin supports both:

  • v3 users see a rolling 5h request limit, weekly mana bar, and hourly search quota
  • Legacy users see subscription request count, free tool calls, and hourly search quota

The plugin auto-detects which system the user is on based on the presence of rollingFiveHourLimit and weeklyTokenLimit in the API response.

API key discovery checks multiple sources in order:

  1. Pi auth.json
  2. Pi models.json
  3. Factory/Droid settings.json (matches customModels entries with synthetic.new in the baseUrl)
  4. OpenCode auth.json
  5. SYNTHETIC_API_KEY env var

Related Issue

N/A — no existing issue for this provider.

Type of Change

  • Bug fix
  • New feature
  • New provider plugin
  • Documentation
  • Performance improvement
  • Other (describe below)

Testing

  • I ran bun run build and it succeeded
  • I ran bun run test and all tests pass (991 tests, 59 files, including 52 new tests for this plugin)
  • I tested the change locally with bun tauri dev

Screenshots

Overview — Synthetic in the main plugin list

1-overview

Detail view — 5h Rate Limit, Mana Bar, and Search

2-detail-view

Settings — Synthetic as a toggleable plugin

3-config

API response — raw /v2/quotas output matching the displayed values

4-direct-quota-endpoint-call

Synthetic billing page — their own dashboard for comparison

5-synthetic-billing-use-page

Files

Added:

  • plugins/synthetic/plugin.json — manifest with 6 line declarations
  • plugins/synthetic/plugin.js — probe implementation with multi-source auth discovery
  • plugins/synthetic/plugin.test.js — 52 tests covering auth, errors, all line types, v3/legacy detection
  • plugins/synthetic/icon.svg — sourced from synthetic.new/favicon.svg
  • docs/providers/synthetic.md — provider documentation

Modified:

  • src-tauri/src/plugin_engine/host_api.rs — added SYNTHETIC_API_KEY and PI_CODING_AGENT_DIR to env var whitelist

Checklist

  • I read CONTRIBUTING.md
  • My PR targets the main branch
  • I did not introduce new dependencies without justification

Add a new provider plugin for Synthetic (synthetic.new) that displays
quota usage from their /v2/quotas API endpoint.

Supports both the new v3 rate limit system (rolling 5h request limit
and weekly mana bar) and the legacy subscription based quota, showing
whichever is relevant to the user's account. Search quota is shown
for all users.

API key discovery checks multiple sources in order: Pi auth.json,
Pi models.json, Factory/Droid settings.json, OpenCode auth.json,
and the SYNTHETIC_API_KEY env var. Two new env vars are whitelisted
in the Rust host (SYNTHETIC_API_KEY and PI_CODING_AGENT_DIR).

Icon sourced from synthetic.new/favicon.svg. Brand color is black
to match their branding.

Files added:
- plugins/synthetic/plugin.json
- plugins/synthetic/plugin.js
- plugins/synthetic/plugin.test.js (52 tests)
- plugins/synthetic/icon.svg
- docs/providers/synthetic.md

Files modified:
- src-tauri/src/plugin_engine/host_api.rs (env var whitelist)
Copilot AI review requested due to automatic review settings March 30, 2026 18:28
@github-actions github-actions bot added rust Pull requests that update rust code plugin docs labels Mar 30, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 6 files

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54e15dd895

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +180 to +181
var onV3 = !!json.rollingFiveHourLimit || !!json.weeklyTokenLimit;
if (!onV3 && json.subscription && typeof json.subscription.limit === "number") {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require usable v3 fields before hiding legacy lines

onV3 is set from object truthiness alone, so any truthy placeholder (for example rollingFiveHourLimit: {} from a partial/transition payload) suppresses both Subscription and Free Tool Calls even when the v3 lines are not renderable. In that case users lose their only meaningful quota lines and may see incomplete usage data. Gate this on the expected numeric fields (or both v3 blocks) instead of plain object presence.

Useful? React with 👍 / 👎.

Comment on lines +124 to +126
var msg =
json && json.error ? json.error : "Request failed (HTTP " + resp.status + ")";
throw msg;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize API error payload to a string before throwing

For non-2xx responses this throws json.error directly, but many APIs return structured objects there. When a non-string is thrown, runtime error extraction falls back to a generic failure message, so users lose the actionable server error text. Convert object-shaped errors to a string (for example error.message) before throwing.

Useful? React with 👍 / 👎.

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

Adds a new “Synthetic” provider plugin that fetches and displays quota usage from Synthetic’s /v2/quotas endpoint, including support for both the new v3 rate-limit system and the legacy subscription model.

Changes:

  • Added synthetic provider plugin (manifest, implementation, icon) and comprehensive Vitest coverage.
  • Added provider documentation for Synthetic (auth discovery, endpoint details, displayed metrics).
  • Whitelisted SYNTHETIC_API_KEY and PI_CODING_AGENT_DIR for plugin env access in the Tauri host API.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src-tauri/src/plugin_engine/host_api.rs Extends env-var allowlist to support Synthetic plugin auth/discovery.
plugins/synthetic/plugin.json Declares Synthetic plugin metadata and line definitions (scopes/orders).
plugins/synthetic/plugin.js Implements API key discovery and /v2/quotas probing + line rendering.
plugins/synthetic/plugin.test.js Adds extensive tests covering auth resolution, errors, and v3/legacy outputs.
plugins/synthetic/icon.svg Adds Synthetic icon used in UI.
docs/providers/synthetic.md Documents auth sources, API shape, and displayed lines for Synthetic.

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

Comment on lines +179 to +181
// Subscription — legacy request count, only shown if NOT on v3 rate limits
var onV3 = !!json.rollingFiveHourLimit || !!json.weeklyTokenLimit;
if (!onV3 && json.subscription && typeof json.subscription.limit === "number") {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

onV3 detection uses rollingFiveHourLimit || weeklyTokenLimit, but the PR description says the plugin should auto-detect v3 based on the presence of both fields. If the API ever returns only one of these fields, this will treat the user as v3 and hide legacy lines unexpectedly. Either update the detection logic to match the documented behavior, or update the PR description/docs to reflect the OR-based detection.

Copilot uses AI. Check for mistakes.
Comment on lines +731 to +748
it("returns 5 lines when rate limited", () => {
var ctx = makeCtx();
setPiAuth(ctx, "syn_testkey");
mockHttp(
ctx,
successPayload({
rollingFiveHourLimit: {
nextTickAt: "2026-03-30T15:30:29.000Z",
tickPercent: 0.05,
remaining: 0,
max: 600,
limited: true,
},
})
);
var result = plugin.probe(ctx);
expect(result.lines.length).toBe(4); // v3 user rate limited: no subscription
expect(result.lines[2].label).toBe("Rate Limited");
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The test name says "returns 5 lines when rate limited" but the assertion expects 4 lines (and the listed labels also add up to 4). This mismatch makes the test misleading—rename the test or update the expectation to match the intended behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +148
| Line | Scope | Condition | Description |
|----------------|----------|----------------------------------|------------------------------------------------|
| 5h Rate Limit | overview | `rollingFiveHourLimit` present | Requests remaining in 5-hour rolling window |
| Mana Bar | overview | `weeklyTokenLimit` present | Weekly token budget as percentage |
| Rate Limited | detail | `rollingFiveHourLimit.limited` | Red badge shown only when actively rate limited|
| Subscription | detail | `subscription` present | Legacy request count for billing period |
| Search | detail | `search.hourly` present | Hourly search request quota |
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The "Displayed Lines" table doesn’t match the implementation/manifest: (1) the plugin can emit a "Free Tool Calls" progress line for legacy users but it’s missing from the table, (2) "Subscription" is listed as scope=detail but the manifest declares it as scope=overview, and (3) both legacy lines are only shown when NOT on v3 (when v3 fields are absent). Consider updating the table (and the 5h Rate Limit description if you intend to describe used vs remaining) to reflect what the plugin actually returns/displays.

Suggested change
| Line | Scope | Condition | Description |
|----------------|----------|----------------------------------|------------------------------------------------|
| 5h Rate Limit | overview | `rollingFiveHourLimit` present | Requests remaining in 5-hour rolling window |
| Mana Bar | overview | `weeklyTokenLimit` present | Weekly token budget as percentage |
| Rate Limited | detail | `rollingFiveHourLimit.limited` | Red badge shown only when actively rate limited|
| Subscription | detail | `subscription` present | Legacy request count for billing period |
| Search | detail | `search.hourly` present | Hourly search request quota |
| Line | Scope | Condition | Description |
|----------------|----------|---------------------------------------------------|-----------------------------------------------------|
| 5h Rate Limit | overview | `rollingFiveHourLimit` present | Usage in 5-hour rolling window (used / limit) |
| Mana Bar | overview | `weeklyTokenLimit` present | Weekly token budget as percentage |
| Rate Limited | detail | `rollingFiveHourLimit.limited` | Red badge shown only when actively rate limited |
| Subscription | overview | `subscription` present and v3 fields absent | Legacy request count for billing period |
| Free Tool Calls| overview | `freeToolCalls` present and v3 fields absent | Legacy free tool-call quota |
| Search | detail | `search.hourly` present | Hourly search request quota |

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +154
Progress lines include:
- `resetsAt` — ISO timestamp of next restoration tick (5h, mana) or renewal (subscription, search)
- `periodDurationMs` — 5 hours (rate limit), 1 week (mana), or 1 hour (search)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Docs state that progress lines include resetsAt and periodDurationMs for 5h rate limit and mana (restoration ticks), but the plugin implementation/tests intentionally omit these fields for those lines (only Search/legacy lines include reset info). Please update this section to match the actual output (or update the plugin if the intent is to expose those fields).

Suggested change
Progress lines include:
- `resetsAt` — ISO timestamp of next restoration tick (5h, mana) or renewal (subscription, search)
- `periodDurationMs` — 5 hours (rate limit), 1 week (mana), or 1 hour (search)
Progress lines expose the following fields:
- 5h Rate Limit / Mana Bar: remaining quota or percentage only (no reset metadata fields)
- Subscription / Search: include `resetsAt` (ISO timestamp of next renewal) and `periodDurationMs` (billing-period duration for subscription, 1 hour for search)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs plugin rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants