Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 134 additions & 62 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,89 +1,161 @@
# Copy this file to .env. Sign-in is done by hand in a browser window
# (run `python -m deepseek.auth` or `python -m qwen.auth`), so no credentials are stored here.
# All providers are opt-in. DeepSeek web automation may restrict your account.
DEEPSEEK_ENABLED=0

# --- Common ---
# Default listen address is 127.0.0.1 (local services only).
# Use 0.0.0.0 for access from the network or Docker containers.
# HOST=0.0.0.0
# PORT=8000

# Transports (how a provider answers requests):
# browser — signed-in site tab + userscripts; model id is <provider>-web; the
# model is chosen in the site UI. Needs BROWSER_BRIDGE_ENABLED=1, a
# paired tab, and browser/README.md. Used by Grok, Mistral, GLM,
# ChatGPT/Claude (browser only); Qwen/Gemini/Kimi when *_TRANSPORT=browser.
# http — direct website API after a manual auth CLI (no tab during requests).
# Qwen default; optional for Kimi. DeepSeek has no *_TRANSPORT flag
# and always uses this style after `python -m deepseek.auth`.
# cli — Gemini default: official Antigravity CLI (`agy`) + GEMINI_MODELS.
# Enable a provider with <PROVIDER>_ENABLED=1. Dual-transport providers set
# <PROVIDER>_TRANSPORT (qwen: http|browser, gemini: cli|browser, kimi: browser|http).

# Shared signed-in browser tab bridge via a userscript browser extension:
# Tampermonkey for Chrome, Violentmonkey for Chrome, or Userscripts for Safari.
# Read browser/README.md before enabling. Required by browser transports
# (Qwen, Gemini, Grok, Mistral, GLM, ChatGPT, Claude, Kimi).
BROWSER_BRIDGE_ENABLED=0

# How long to wait for one provider answer, in seconds (1–18000). Default 600.
# Used by browser tabs, Kimi HTTP and Gemini CLI (after a browser stream starts,
# the same budget applies).
# PROVIDER_RESPONSE_TIMEOUT=18000

# Minimum seconds between starts of attempts/continuations (durable across processes).
# DeepSeek also spaces HTTP dispatches by up to 3 seconds and enforces durable
# default rolling budgets: 12 generations/hour, 60/day; 3 new chats/hour, 6/day;
# 40 HTTP requests/hour, 200/day. These are local ceilings, not safe quotas.
# This is pacing, not a guarantee of acceptable use or website quota.
PROVIDER_MIN_INTERVAL=30
# Configurable DeepSeek ceilings. Integers 1..2000; day >= hour for each kind.
# Restart to apply. Existing usage is preserved; website quotas are independent.
DEEPSEEK_MAX_GENERATIONS_PER_HOUR=12
DEEPSEEK_MAX_GENERATIONS_PER_DAY=60
DEEPSEEK_MAX_CHATS_PER_HOUR=3
DEEPSEEK_MAX_CHATS_PER_DAY=6
DEEPSEEK_MAX_HTTP_PER_HOUR=40
DEEPSEEK_MAX_HTTP_PER_DAY=200
# Accepted replies per saved stateless conversation; integer 1..1000.
# Raising preserves existing history and one-use continuation protection.
DEEPSEEK_MAX_CONVERSATION_REPLIES=20
# Optional Qwen Chat models: run `python -m qwen.auth`, then set QWEN_ENABLED=1.
# Qwen uses a separate profile and session under session/qwen/.
QWEN_ENABLED=0
# Optional: before a new request, renew a locally expired Qwen access token
# once using its existing website refresh cookie. No browser/background work,
# no replay after refusal; failures persist a pause. See docs/HARNESS.en.md.
QWEN_RENEW_SESSION=0
# QWEN_PROFILE_DIR=/absolute/path/to/a/dedicated/qwen/profile

# Experimental signed-in browser tabs (Safari Userscripts or a compatible manager).
# Read browser/README.md before enabling. Each uses the web UI's selected model.
BROWSER_BRIDGE_ENABLED=0
GROK_ENABLED=0
MISTRAL_ENABLED=0
KIMI_ENABLED=0
# Optional direct Kimi Instant transport; browser remains necessary for manual
# initial session capture only. See docs/KIMI.md for qualification and setup.
# Session renewal is a separate opt-in and never follows a provider refusal.
KIMI_TRANSPORT=browser
KIMI_RENEW_SESSION=0
GLM_ENABLED=0
# WEB_PROVIDER_TIMEOUT=180
# Accepted replies per saved stateless conversation (DeepSeek + web providers,
# including Qwen browser); integer 1..1000. Raising preserves existing history
# and one-use continuation protection.
CONVERSATION_MAX_REPLIES=20

# Gemini uses the official Antigravity CLI, with tools disabled.
# Sign in with `agy`; obtain real account model slugs with `agy models`.
GEMINI_ENABLED=0
GEMINI_MODELS={}
# ANTIGRAVITY_BIN=agy
# For a broken IPv6 socket only, providers.antigravity_ipv4 wraps the official
# CLI using IPv4 on your existing route, without changing system/VPN settings.

# --- Optional ---
# DEEPSEEK_PROFILE_DIR=session/profile # reuse an existing signed-in Chrome profile
# HOST=127.0.0.1
# PORT=8000
# RATE_LIMIT_PER_MINUTE=30 # per-client-IP limit on /v1 endpoints

# Sessions are loaded from cache by default. The server never logs in, refreshes a
# browser session in the background, or replays a rejected request.
# Missing/expired sessions return 401 login_required, or a terminal SSE error
# after streaming headers are sent. Run the auth CLI manually.
# Restrictions, quota and uncertain upstream failures pause that provider in
# session/provider-pauses.json (HTTP 403; SSE error if headers are already sent).
# Inspect: python -m providers.access status
# After confirming normal website access manually:
# python -m providers.access resume qwen
# Existing SERVER_INTERACTIVE_LOGIN / SESSION_REFRESH_ENABLED settings no longer
# enable automatic browser capture in the server.

# --- Tool-call truncation handling ---
# --- TOOL / TOOLCALL ---
# How OpenAI `tools` are flattened into the provider prompt.
# full — full JSON Schema + tool_calls rules on every turn.
# light — recommended; full block on the first turn of a provider chat; short
# name/format reminder on continuations (smaller follow-ups).
# none — like light on the first turn; no tools block on continuations.
# HTTP transports (DeepSeek, Qwen API, Kimi direct, Gemini CLI): default full.
# Browser tab bridge (grok/mistral/glm/chatgpt/claude/kimi/qwen/gemini): default light.
TOOL_INSTRUCTIONS_HTTP=full
TOOL_INSTRUCTIONS_BROWSER=light

# DeepSeek's web output limit can cut a large tool call off mid-JSON. When that
# happens it stops without returning partial calls. Continuations are off by
# default. An explicit opt-in (>0) only continues a provider-confirmed output
# limit, never an otherwise finished reply with a missing Markdown fence.
TOOLCALL_MAX_CONTINUATIONS=0

# Optional: one format correction after a normally completed invalid DeepSeek
# tool reply, in the same conversation. Adds at most one paced request. Never
# retries provider refusals, output limits, or uncertain dispatches. Default off.
TOOLCALL_FORMAT_REPAIR=0

# Dump the raw model reply to the log whenever a tool call can't be parsed.
# DEBUG_TOOLCALLS=1
# TOOLCALL_DEBUG=1

# Save the last invalid tool reply privately under session/, without a history request.
# May contain private source or secrets; disabled by default. Do not publish the file.
# TOOLCALL_DIAGNOSTICS=1

# --- DeepSeek ---
DEEPSEEK_ENABLED=0
# TRANSPORT
# http only after `python -m deepseek.auth` (no DEEPSEEK_TRANSPORT).
# DeepSeek also spaces HTTP dispatches by up to 3 seconds and enforces durable
# default rolling budgets: 12 generations/hour, 60/day; 3 new chats/hour, 6/day;
# 40 HTTP requests/hour, 200/day. These are local ceilings, not safe quotas.
# Configurable DeepSeek ceilings. Integers 1..2000; day >= hour for each kind.
# Restart to apply. Existing usage is preserved; website quotas are independent.
DEEPSEEK_MAX_GENERATIONS_PER_HOUR=12
DEEPSEEK_MAX_GENERATIONS_PER_DAY=60
DEEPSEEK_MAX_CHATS_PER_HOUR=3
DEEPSEEK_MAX_CHATS_PER_DAY=6
DEEPSEEK_MAX_HTTP_PER_HOUR=40
DEEPSEEK_MAX_HTTP_PER_DAY=200
# DEEPSEEK_PROFILE_DIR=session/profile # reuse an existing signed-in Chrome profile
# Private failed SSE bytes (may contain private source/response text):
# up to 4 files, 8 MiB decoded bytes each; no automatic deletion or replay.
# DEEPSEEK_STREAM_DIAGNOSTICS=1

# --- Qwen ---
QWEN_ENABLED=0
# TRANSPORT
# http — `python -m qwen.auth` + QWEN_MODELS (default).
# browser — model qwen-web; needs BROWSER_BRIDGE_ENABLED=1 and a paired tab.
QWEN_TRANSPORT=http
# Public OpenAI id → website wire model (HTTP transport). Default if unset:
# qwen3.8-omni-flash, qwen3.8-max, qwen3.7-plus.
QWEN_MODELS={"qwen3.8-omni-flash":"qwen3.8-omni-flash","qwen3.8-max":"qwen3.8-max","qwen3.7-plus":"qwen3.7-plus"}
# Optional HTTP-only: renew a locally expired access token once via the website
# refresh cookie. No browser/background work, no replay after refusal; failures
# persist a pause. Ignored for browser transport. See docs/HARNESS.en.md.
QWEN_RENEW_SESSION=0
# QWEN_PROFILE_DIR=/absolute/path/to/a/dedicated/qwen/profile

# --- Gemini ---
GEMINI_ENABLED=0
# TRANSPORT
# cli — sign in with `agy`; put slugs from `agy models` into GEMINI_MODELS (default).
# browser — gemini-web on AI Mode (google.com/search?udm=50) or gemini.google.com/app.
GEMINI_TRANSPORT=cli
# Example catalog current as of 2026-10-11, from `agy models` (account/region may differ).
# GEMINI_MODELS={"gemini-3.8-flash-high":"gemini-3.8-flash-high","gemini-3.8-flash-medium":"gemini-3.8-flash-medium","gemini-3.8-flash-low":"gemini-3.8-flash-low","gemini-3.7-flash-high":"gemini-3.7-flash-high","gemini-3.7-flash-medium":"gemini-3.7-flash-medium","gemini-3.7-flash-low":"gemini-3.7-flash-low","gemini-3.6-flash-high":"gemini-3.6-flash-high","gemini-3.6-flash-medium":"gemini-3.6-flash-medium","gemini-3.6-flash-low":"gemini-3.6-flash-low","gemini-3.1-pro-high":"gemini-3.1-pro-high","gemini-3.1-pro-low":"gemini-3.1-pro-low","gemini-claude-sonnet-4-6":"claude-sonnet-4-6","gemini-claude-opus-4-6-thinking":"claude-opus-4-6-thinking","gemini-gpt-oss-120b-medium":"gpt-oss-120b-medium"}
# ANTIGRAVITY_BIN=agy
# For a broken IPv6 socket only, providers.antigravity_ipv4 wraps the official
# CLI using IPv4 on your existing route, without changing system/VPN settings.

# --- Grok ---
GROK_ENABLED=0
# TRANSPORT
# browser only (no GROK_TRANSPORT). Model: grok-web.
# Needs BROWSER_BRIDGE_ENABLED=1 and a paired tab on grok.com.

# --- Mistral ---
MISTRAL_ENABLED=0
# TRANSPORT
# browser only (no MISTRAL_TRANSPORT). Model: mistral-web.
# Needs BROWSER_BRIDGE_ENABLED=1 and a paired tab on chat.mistral.ai.

# --- GLM ---
GLM_ENABLED=0
# TRANSPORT
# browser only (no GLM_TRANSPORT). Model: glm-web.
# Needs BROWSER_BRIDGE_ENABLED=1 and a paired tab on chat.z.ai.

# --- ChatGPT ---
CHATGPT_ENABLED=0
# TRANSPORT
# browser only (no CHATGPT_TRANSPORT). Model: chatgpt-web.
# Needs BROWSER_BRIDGE_ENABLED=1 and a paired tab on chatgpt.com.

# --- Claude ---
CLAUDE_ENABLED=0
# TRANSPORT
# browser only (no CLAUDE_TRANSPORT). Model: claude-web.
# Needs BROWSER_BRIDGE_ENABLED=1 and a paired signed-in tab on https://claude.ai/;
# select the model in the site UI.

# --- Kimi ---
KIMI_ENABLED=0
# TRANSPORT
# browser — kimi-web (default); large pastes ~4KB+ may become a TXT attachment —
# keep prompts shorter, or use http. Needs BROWSER_BRIDGE_ENABLED=1.
# http — Instant after manual session capture; browser still needed once for
# capture. See docs/KIMI.md. Renewal never follows a refusal.
KIMI_TRANSPORT=browser
KIMI_RENEW_SESSION=0

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ __pycache__/
*.pyc
.env
logs/
.tmp/

# Session artifacts — contain session tokens + cookies. NEVER commit.
session.json
session
*.env.*
!.env.example
CLAUDE.md
CLAUDE.md

45 changes: 35 additions & 10 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,9 @@ not resume a stopped goal, replay an old reply, or reset history or counters.
| Missing group or model | Check `.env`, the running server and `/v1/models`, then reinstall the catalog |

DeepSeek defaults are **12/60 generations, 3/6 new chats and 40/200 HTTP requests
per hour/day**, with 20 accepted replies per conversation and a 30-second
interval. `DEEPSEEK_MAX_*` settings belong to the bridge process environment.
per hour/day**, with a shared 20 accepted replies per conversation
(`CONVERSATION_MAX_REPLIES`) and a 30-second interval. `DEEPSEEK_MAX_*` budget
ceilings and `CONVERSATION_MAX_REPLIES` belong to the bridge process environment.
Raising a local ceiling does not raise the website's allowance.
[Configuration and error guide](docs/HARNESS.en.md#local-limits-and-errors).

Expand Down Expand Up @@ -388,14 +389,17 @@ Provider documentation retains detailed qualification results.

| Provider / model ID | Browser during requests | Status |
| --- | --- | --- |
| DeepSeek · `deepseek-chat`, `deepseek-expert` | Not needed after manual sign-in | Small Harness tasks passed; repeated account restrictions occurred. No automatic renewal |
| Qwen · `qwen3.8-omni-flash`, `qwen3.8-max` | Not needed after sign-in | Tested in Harness; Max reached a daily limit. [Session](docs/HARNESS.en.md#qwen-session) |
| DeepSeek · `deepseek-chat`, `deepseek-expert` | Not needed after manual sign-in | Browserless HTTP; small Harness tasks passed; repeated account restrictions occurred. No automatic renewal |
| Qwen · HTTP via `QWEN_MODELS` / `qwen-web` | HTTP: not needed after sign-in; `qwen-web`: connected tab | HTTP Flash/Max and the browser adapter verified; Max hit a daily limit. `qwen3.7-plus` is catalogued without a separate live check. [Session](docs/HARNESS.en.md#qwen-session) |
| Kimi · `kimi-instant` | Not needed after capture | Experimental HTTP: reply, renewal and Harness `read` passed |
| Kimi · `kimi-k3`, `kimi-k2.8` | Not needed after capture | Implemented, not qualified end to end: K3 refused free-user capacity; K2.8 was not attempted afterward. [Details](docs/KIMI.md) |
| GLM · `glm-web` | Connected tab required | Previously tested with GLM-5.3-Flash; browserless access remains incomplete. [Blockers](docs/GLM.md) |
| Kimi · `kimi-web` | Connected tab required | Older tab adapter; model selected on the website |
| Mistral / Grok | Tab required | Experimental: Mistral continuation returned `429`; Grok capture did not complete |
| Gemini / Antigravity | Official CLI | Request rejected by region in our test; working access not established |
| Kimi · `kimi-k3`, `kimi-k2.8` | Not needed after capture | HTTP implemented, not qualified end to end: K3 refused free-user capacity; K2.8 was not attempted afterward. [Details](docs/KIMI.md) |
| Kimi · `kimi-web` | Connected tab required | Browser adapter verified; large composer pastes (~4KB+, system+tools) become a TXT attachment on the site — confirmed. See `browser/README.md` and `.env.example` |
| GLM · `glm-web` | Connected tab required | Browser adapter verified; browserless access remains incomplete. [Blockers](docs/GLM.md) |
| Mistral · `mistral-web` | Connected tab required | Browser adapter verified — see `browser/README.md` |
| Grok · `grok-web` | Connected tab required | Browser adapter verified — see `browser/README.md` |
| ChatGPT · `chatgpt-web` | Connected tab required | Browser adapter verified — see `browser/README.md` |
| Claude · `claude-web` | Connected tab required | Browser adapter verified (ProseMirror + `/api/*`) — see `browser/README.md` |
| Gemini · `gemini-web` / Antigravity CLI | `gemini-web`: connected tab; CLI: `agy` | Browser adapter verified. CLI was rejected by region in our test |

All connections start disabled. Website versions, quotas and availability may
change. Chinese providers also have limits; the exact Qwen Max daily allowance
Expand All @@ -417,7 +421,8 @@ not clear account restrictions or quotas. Inspect pauses with
`python -m providers.access status`.
[Resume rules](docs/ACCOUNT_SAFETY.md).

The API binds to `127.0.0.1`; `session/` and `.env` are excluded from Git.
The API binds to `127.0.0.1` by default (local only). Set `HOST=0.0.0.0` for
network or Docker access. `session/` and `.env` are excluded from Git.
Do not publish them, personalized Userscripts or budget credentials.
Supported: text and emulated tool calls. Images, audio and video are not uploaded,
even for models named Omni.
Expand Down Expand Up @@ -445,6 +450,21 @@ CI covers Python 3.9/3.12, PoW WASM, shell/JavaScript and isolated browser fixtu
Locally: `python -m unittest discover -s tests -t . -v`.
[Real Harness CLI fixtures with mocked providers](docs/HARNESS.en.md#offline-verification).

### Browser exchange safety

DOM fallback is disabled: an old visible answer cannot complete a new request.
Claude requires a network terminal marker; truncated, partial or malformed answers are
not reported as complete. Uncertain dispatched outcomes retain pending state
and a safety pause across restart. There is no automatic retry.
Verified offline without live accounts. Re-export and update the userscripts
to apply the browser changes (controller 0.3.34).

Mistral accepts only a request-bound stream with a terminal trailer.
Harness `--without-browser` excludes browser models and their aliases.
The browser controller waits for text insertion, preserves user edits and resumes
only explicitly bound conversations. These cases passed 28 isolated browser
fixtures without accounts.

### Model identifiers

`GET /v1/models` lists bare IDs and `provider/model` aliases, for example
Expand Down Expand Up @@ -480,3 +500,8 @@ will complete.
[Tsuev/opencode-deepseek](https://github.com/Tsuev/opencode-deepseek) and
[sums001/Deepseek-API](https://github.com/sums001/Deepseek-API).
Use accounts under each service's terms. [License](LICENSE).

Browser adapters (userscripts, tab protocol, ChatGPT / Claude / Gemini / Qwen /
Kimi / GLM / Mistral / Grok providers and related checks) are reworked in the
[SimolSem/opencode-deepseek](https://github.com/SimolSem/opencode-deepseek) fork
and contributed here via pull requests.
Loading
Loading