diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index ed14eef8e8a..92ee4e1fbd9 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -2645,7 +2645,7 @@ dependencies = [ [[package]] name = "opencodex-desktop" -version = "2.94.0" +version = "2.95.0" dependencies = [ "base64 0.22.1", "dbus", @@ -3323,9 +3323,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "once_cell", "ring", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 322c1d8f7a4..9407ec769d7 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "opencodex-desktop" -version = "2.94.0" +version = "2.95.0" description = "OpenCodex desktop shell" authors = ["OpenCodex contributors"] license = "MIT" diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index a2dad4dcdde..d5c8dfbe104 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "OpenCodex", - "version": "2.94.0", + "version": "2.95.0", "identifier": "com.opencodex.desktop", "build": { "frontendDist": "../ui", diff --git a/devlog/_fin/260915_workflow_budget_window/030_wfc_diff_plan.md b/devlog/_fin/260915_workflow_budget_window/030_wfc_diff_plan.md index 7b4617b716a..b2d2c7b6905 100644 --- a/devlog/_fin/260915_workflow_budget_window/030_wfc_diff_plan.md +++ b/devlog/_fin/260915_workflow_budget_window/030_wfc_diff_plan.md @@ -96,6 +96,13 @@ declared `deferred-verb` in the route registry against this document, which is what keeps them out of the undeclared-route ratchet without pretending the gap does not exist. +The later configurable-ceilings change also defers the live CLI counterpart of +`PUT /api/workflow-budget/settings` against this document. Its management API +updates the running policy; local `ocx config set/unset workflowBudget...` +edits disk configuration and requires a proxy restart for live convergence. +`GET /api/workflow-budget/settings` is a `local-transport` exemption because +`ocx config get` can read the same configuration block locally. + ## Verification posture Local suite, typecheck, install and build: NOT RUN, by standing instruction. diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index d4fb8509e9f..e81e8b1fb7e 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -154,6 +154,7 @@ export default defineConfig({ items: [ { label: "Overview", translations: { fr: "Vue d’ensemble", ko: "개요", "zh-CN": "概览", "zh-TW": "概覽", ru: "Обзор", ja: "概要", tr: "Genel Bakış" }, slug: "reference/configuration" }, { label: "Providers", translations: { fr: "Fournisseurs", ko: "프로바이더", "zh-CN": "提供商", "zh-TW": "供應商", ru: "Провайдеры", ja: "プロバイダー", tr: "Sağlayıcılar" }, slug: "reference/configuration/providers" }, + { label: "Catalog", translations: { fr: "Catalogue", ko: "카탈로그", "zh-CN": "目录", "zh-TW": "目錄", ru: "Каталог", ja: "カタログ", tr: "Katalog" }, slug: "reference/configuration/catalog" }, { label: "Routing", translations: { fr: "Routage", ko: "라우팅", "zh-CN": "路由", "zh-TW": "路由", ru: "Маршрутизация", ja: "ルーティング", tr: "Yönlendirme" }, slug: "reference/configuration/routing" }, { label: "Agents", translations: { fr: "Agents", ko: "에이전트", "zh-CN": "代理", "zh-TW": "代理", ru: "Агенты", ja: "エージェント", tr: "Ajanlar" }, slug: "reference/configuration/agents" }, { label: "Advisor", translations: { fr: "Conseiller", ko: "어드바이저", "zh-CN": "顾问", "zh-TW": "顧問", ru: "Консультант", ja: "アドバイザー", tr: "Danışman" }, slug: "reference/configuration/advisor" }, diff --git a/docs-site/src/content/docs/fr/guides/claude-code.md b/docs-site/src/content/docs/fr/guides/claude-code.md index 0b167f7c9d7..d26aaa1530f 100644 --- a/docs-site/src/content/docs/fr/guides/claude-code.md +++ b/docs-site/src/content/docs/fr/guides/claude-code.md @@ -663,7 +663,7 @@ Le proxy traduit chaque requête Anthropic Messages API au format Codex Response | Assistant `tool_use` | `function_call` (`input` → JSON-stringifié `arguments`) | | Utilisateur `tool_result` | `function_call_output` (`is_error` → préfixe `[tool error]`) | | Relecture de `thinking` / `redacted_thinking` | Éléments `reasoning` avec enveloppes `ocxr1` bornées pour les signatures et les contenus masqués | -| Outils fonctionnels | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) | +| Outils fonctionnels | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) La recherche hébergée avec `allowed_domains`, `blocked_domains` ou `user_location` est omise sur les routes Responses/Chat traduites ; la forcer renvoie 400. Les définitions de fonctions `defer_loading` valides restent appelables comme fonctions ordinaires. Voir les [limites de traduction](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes). | | `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, fonction nommée→`{type:"function",name}`, hébergée WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/fr/guides/codex-app-models.md b/docs-site/src/content/docs/fr/guides/codex-app-models.md index 8e420079af1..e6a653f337d 100644 --- a/docs-site/src/content/docs/fr/guides/codex-app-models.md +++ b/docs-site/src/content/docs/fr/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Sélecteur de modèles de Codex App description: Comment les modèles opencodex apparaissent dans Codex App, Codex CLI et Codex TUI par l'intermédiaire du catalogue Codex partagé. --- +Par défaut : `false`. Avec `"omitDisabledNativeModels": true` dans `config.json`, puis `ocx sync`, les lignes natives désactivées sont omises du catalogue, du cache et de la découverte HTTP au format Codex. Le modèle d’héritage natif validé avant omission conserve les métadonnées des lignes routées. Les requêtes directes gardent leur routage habituel. Les descriptions de masquage ci-dessous concernent le comportement par défaut. + opencodex ne modifie pas Codex App. Il écrit la même configuration et le même catalogue de modèles Codex que ceux utilisés par Codex CLI et TUI. Le serveur d'application lit cet état partagé, mais certaines versions de Codex Desktop appliquent dans le moteur de rendu une seconde liste d'autorisation distante et peuvent diff --git a/docs-site/src/content/docs/fr/guides/combos.md b/docs-site/src/content/docs/fr/guides/combos.md index ebd6023df2a..350911638a5 100644 --- a/docs-site/src/content/docs/fr/guides/combos.md +++ b/docs-site/src/content/docs/fr/guides/combos.md @@ -200,7 +200,8 @@ Les échecs d’un combo se répartissent entre ceux qui entraînent un **bascul | --- | --- | | HTTP 401, 403, 404, 408, 429, ou n'importe quel 5xx | Refroidissez la cible et passez à la prochaine cible éligible. | | Erreur classée comme erreur d’authentification, d’abonnement, de quota, de limitation de débit, de surcharge ou de serveur en amont | Place la cible en période de refroidissement et bascule, même si le statut seul ne suffit pas. | -| Annulation client (499), `origin_rejected`, refus de cyber-politique, débordement de contexte ou autre demande invalide | Arrêtez et renvoyez l'erreur ; une autre cible ne rendrait pas la demande valide. | +| Annulation client (499), `origin_rejected`, refus de cyber-politique ou autre demande invalide | Arrêtez et renvoyez l'erreur ; une autre cible ne rendrait pas la demande valide. | +| Dépassement de contexte confirmé avant la sortie, y compris HTTP 400 | Essaie la prochaine cible admissible non tentée sans refroidissement ; à épuisement, renvoie la première erreur de contexte HTTP 400 confirmée (un refus de contexte avec un autre statut fait aussi passer à la cible suivante, sans être conservé). | | Rejet structuré de `user`, valeur non prise en charge pour `reasoning.effort`/`reasoning_effort`, rejet d'entrée d'image propre à un modèle (`param: input`), ou refus d'un média d'entrée que la route ne peut pas lire (`unsupported_input_modality`) | Bascule vers la cible admissible suivante avant le début de la sortie, sans délai de refroidissement ; voir Compatibilité des paramètres facultatifs ci-dessous. | | Premier appel d'outil d'un tour Responses exécuté par un adaptateur interne (`runTurn`) que la requête courante n'a pas déclaré, avant toute sortie et tout effet de bord non rejouable | Met la cible en refroidissement et bascule avec le même catalogue d'outils. Après une sortie visible ou un effet de bord non rejouable, le refus est définitif. Les requêtes Chat Completions et Anthropic Messages ne changent pas. | | Toute autre erreur non classifiée | Arrêtez et renvoyez l'erreur. | @@ -210,9 +211,15 @@ valeur `Retry-After` valide, opencodex l’utilise à la place ; les en-têtes d acceptées, et un délai explicite `Retry-After` est plafonné à 24 heures ; les autres temps de recharge restent plafonnés à 10 minutes. La requête actuelle ne réessaye jamais la même cible tentée. Les demandes ultérieures l'ignorent jusqu'à ce qu'il soit -le temps de recharge expire. S’il ne reste aucune cible éligible, le proxy renvoie HTTP 503 avec +le temps de recharge expire. Si aucune cible n’a été tentée et qu’aucune n’est éligible, le proxy renvoie HTTP 503 avec `error.code = "combo_unavailable"`. +Après un refus de contexte HTTP 400 confirmé, l’épuisement des cibles ou du budget d’envoi renvoie +le premier refus sous `context_length_exceeded`, avant toute erreur ultérieure réessayable, même de quota. +Cela inclut les cibles restantes incompatibles avec la relecture du raisonnement ou sans consentement +de dépense. L’annulation, un refus fatal ultérieur ou un refus explicite de la politique de repli reste +prioritaire ; aucune cible facturée sans consentement ne reçoit la requête. + :::note Le basculement est intentionnellement limité. Il facilite la disponibilité, l'authentification et l'authentification spécifiques à la cible. échecs de quota et de surcharge ; il ne cache pas les erreurs des appelants ni les refus de politique. @@ -366,7 +373,7 @@ message de validation. ### Pourquoi le basculement s'est-il arrêté après la première erreur ? -L’erreur était terminale plutôt que spécifique à la cible. Corriger une entrée invalide, réduire un contexte surdimensionné, +L’erreur était terminale plutôt que spécifique à la cible. Corriger une entrée invalide, gérer un refus de politique ou corriger l’origine de la demande rejetée. Les combos ne sautent pas dans ces cas-là. ## Compatibilité des paramètres facultatifs diff --git a/docs-site/src/content/docs/fr/guides/providers.md b/docs-site/src/content/docs/fr/guides/providers.md index 726ae425a87..8d8a85f0a8d 100644 --- a/docs-site/src/content/docs/fr/guides/providers.md +++ b/docs-site/src/content/docs/fr/guides/providers.md @@ -180,8 +180,7 @@ Les fournisseurs OAuth dont les identifiants comportent un identifiant de compte peuvent conserver plusieurs connexions. La page **Fournisseurs** affiche ces comptes dans une liste déroulante, permet d'en ajouter un autre et de changer de compte actif sans déconnecter les autres. Une connexion ordinaire avec un identifiant Kimi qui ne contient aucune information d'identité remplace l'emplacement actif, tandis que l'action explicite **Ajouter un -compte** préserve cet emplacement et en active un nouveau, distinct. Les comptes Kiro sont indexés par ARN de -profil. `chatgpt` n'utilise toujours qu'un seul emplacement, car les comptes du pool Codex sont gérés dans un +compte** préserve cet emplacement et en active un nouveau, distinct. Les comptes Kiro utilisent l’ARN du profil avec l’adresse e-mail de l’utilisateur connecté lorsqu’elle est disponible ; plusieurs utilisateurs peuvent conserver des identifiants distincts pour un même profil d’organisation. `chatgpt` n'utilise toujours qu'un seul emplacement, car les comptes du pool Codex sont gérés dans un registre distinct. Les jetons restent dans `~/.opencodex/auth.json` ; `/api/oauth/accounts` ne renvoie que des métadonnées masquées. @@ -255,6 +254,8 @@ dans le pool de comptes, ce que fait aussi le pool de comptes Codex du tableau d ### Importation des identifiants Kiro +Dans le tableau de bord, choisissez **AWS IAM Identity Center**, puis indiquez l’URL du portail d’accès AWS et la région SSO de votre organisation. Autorisez le code d’appareil dans votre navigateur et choisissez le profil Kiro à enregistrer si plusieurs sont proposés. Aucun CLI ni accès SSH n’est nécessaire. Les instructions CLI ci-dessous concernent le choix **Kiro CLI**. + La connexion Kiro nécessite la CLI Kiro : sous Unix, installez-la avec `curl -fsSL https://cli.kiro.dev/install | bash` ; sous Windows PowerShell, utilisez `irm 'https://cli.kiro.dev/install.ps1' | iex`, puis connectez-vous avec `kiro-cli login`. En l'absence de session `kiro-cli`, `ocx login kiro` se rabat sur un jeton d'accès collé ou sur la variable @@ -359,7 +360,7 @@ promotionnels de Cline ne sont accessibles que dans l'IDE ou la CLI Cline, pas p | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`. L’API `moonshot` utilise `kimi-k3.1-pro` par défaut ; les sélections `kimi-k2.5` enregistrées pour les adresses API officielles migrent vers ce modèle. | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/fr/guides/web-dashboard.md b/docs-site/src/content/docs/fr/guides/web-dashboard.md index 9b7cfe005b3..6b11e7cc80e 100644 --- a/docs-site/src/content/docs/fr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/fr/guides/web-dashboard.md @@ -93,6 +93,8 @@ Les vues Utilisation, Tableau de bord, Fournisseurs, Catalogue des fournisseurs ### Filtrer les requêtes +Le détail d’une requête privilégie la cause d’échec amont enregistrée plutôt qu’un message générique d’erreur du proxy. Le nombre d’envois physiques est toujours affiché lorsqu’il est valide ; une comptabilité absente ou invalide affiche **Unavailable**, et non zéro. **Retries** compte les envois après le premier uniquement lorsqu’une seule tentative entièrement réglée et cohérente est enregistrée. Les tentatives multiples, les comptabilités non résolues ou incohérentes et les normalisations avec perte omettent les retries au lieu d’en inventer. Une cause persistante inconnue revient au titre fondé sur le statut, sans attribution brute. + Les filtres combinent interface, requêtes interceptées, fournisseur, modèle exact, statut, période, vitesse et identifiant de conversation dans le journal chargé. Les choix incluent les tentatives de repli ; les modèles ignorent la casse et les espaces externes, sans correspondance partielle. Un choix disparu revient à Tous. Les périodes de 15 minutes, une heure et un jour évoluent toutes les 30 secondes dans l’onglet Logs, même sans actualisation automatique. La vitesse mesure les jetons de sortie par seconde sur toute la durée : moins de 15, de 15 à moins de 50, ou au moins 50 ; les valeurs indisponibles sont exclues quand ce filtre est actif. Réussite : 2xx ; erreur : 4xx/5xx. diff --git a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md index 25e82d8de21..acdc51645c5 100644 --- a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md @@ -25,6 +25,7 @@ Gestion des fournisseurs non interactive. Les entrées de registre sont classée | `quota` | `--refresh`, `--json` | Lire les rapports sur les quotas des fournisseurs. | | `presets` | `--json` | Répertoriez les préréglages du fournisseur de tableau de bord. | | `account-mode` | `pool`, `direct`, `--json` | Sélectionnez le routage de compte mutualisé ou direct Codex. | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | Prévisualiser, appliquer sélectivement ou restaurer des entrées de fournisseurs sans clé depuis une base SQLite (`cc-switch.db`) ou un fichier JSON CC-Switch. | Par défaut, `add`, `remove` et `set-default` modifient la configuration locale. Ajoutez `--live` pour modifier le proxy actif, et `--yes` pour une suppression en direct. `--sync --json` tente aussi la synchronisation après enregistrement ; en cas d’échec, la sauvegarde reste acquise, avec un code non nul et `needsSync: true`. `--live` et `--sync` sont incompatibles. Voir le [guide anglais](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline) pour pacing et snapshot/apply. @@ -60,6 +61,27 @@ la frontière est à vous de la respecter. Deux raisons pour lesquelles c'est im Utilisez `--api-key` ou un identifiant OAuth pour tout ce qui est secret. ::: +### Import de fournisseurs CC-Switch sans clé + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# Démarrez le proxy dans un autre terminal (ou exécutez ocx ensure), puis ajoutez les clés : +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# Chemin d’annulation distinct (arrêtez d’abord le proxy ; supprime uniquement les entrées sans clé non modifiées) : +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` projette des valeurs **uniquement** depuis une liste blanche explicite de champs de métadonnées non secrets (identifiants de fournisseur, types d’application, URL de base, dialectes et modèles par défaut) à partir d’une base SQLite ou d’un fichier JSON CC-Switch ordinaire. Les octets de la source et de la configuration intégrée sont lus de manière transitoire pour analyser l’entrée, tandis que les champs secrets, noms libres et commentaires TOML sont ignorés au niveau des octets, ne sont jamais décodés en chaînes, écrits ni journalisés, et les tampons d’octets transitoires sont remis à zéro dans `finally`. Les règles strictes de chemin d’URL (`http(s)://host[:port]` sans chemin ou avec 1 à 4 segments d’API en minuscules `[a-z0-9._-]` tels que `/v1`, `/api/v1` ou `/compatible-mode/v1`) et les contrôles syntaxiques d’identifiant/modèle servent de défense en profondeur sans faire écho aux valeurs rejetées. Les fichiers sources sont ouverts une seule fois avec vérification de descripteur `O_NOFOLLOW | O_NONBLOCK` et désérialisés en lecture seule en mémoire pour SQLite sans fichier temporaire ; les fichiers SQLite ayant plusieurs liens physiques (`nlink > 1`) sont refusés afin d’empêcher le contournement des contrôles de fichiers annexes, et si un fichier voisin `-wal` ou `-journal` est non vide, l’import refuse et demande de fermer CC-Switch (ou de consolider la base) avant de réessayer. N’importez que depuis une source locale CC-Switch de confiance et vérifiez chaque ID, modèle et `baseUrl` affichés avant d’ajouter des identifiants : + +- **Aperçu (par défaut) :** exige `--source `, n’écrit rien et indique les entrées découvertes, éligibles, en conflit et invalides. +- **Application (`--apply`) :** exige `--source ` et un proxy arrêté (`ocx stop`), et est refusée sur un Remote Link Child. Répétez `--provider ` pour n’importer que certaines entrées, ou passez `--allow-private-network` pour autoriser les URL de bouclage ou de réseau privé. Même sans aperçu préalable, `--apply` affiche chaque ID de fournisseur, modèle par défaut et URL de destination avant d’écrire les entrées sans clé et le journal d’annulation `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` (`stdout` en mode texte, `stderr` en mode `--json`) ; un second `--apply` est refusé tant qu’un journal actif existe. Vérifiez les entrées affichées, démarrez le proxy (`ocx start` dans un autre terminal ou `ocx ensure`), puis ajoutez les clés avec `printf '%s\n' "$API_KEY" | ocx account add-key ` (entrée standard redirigée) ou `ocx login `. +- **Restauration (`--restore`) :** chemin d’annulation distinct (et non la suite de l’ajout d’identifiants) qui exige un proxy arrêté (`ocx stop`), est refusé sur un Remote Link Child et n’accepte ni `--source`, ni `--provider`, ni `--allow-private-network`. Il inspecte `auth.json` en lecture seule au niveau des octets (échec fermé s’il est illisible ou mal formé) et ne supprime du journal actif que les fournisseurs qui correspondent encore à leur état importé sans clé et qui ne sont ni sélectionnés comme `defaultProvider` ni référencés par le routage, les combos, la politique de repli (fallback policy), les comptes OAuth ou les clés API. + ## Authentification ### `ocx login ` diff --git a/docs-site/src/content/docs/fr/reference/configuration/advisor.md b/docs-site/src/content/docs/fr/reference/configuration/advisor.md index 9c48ca2b7fc..206cbde2454 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/fr/reference/configuration/advisor.md @@ -77,6 +77,10 @@ Une consultation peut envoyer : Le fournisseur conseiller configuré peut différer de celui du worker. +Les consultations respectent à la fois la portée de modèles capturée de la clé d'API de l'appelant, son consentement aux combos payants et ses limites de repli par appareil, et les limites d'hôte de la propre clé d'admission d'Advisor : `OPENCODEX_API_AUTH_TOKEN` ou le fichier de jeton du service s'il est défini et accepté par ce proxy, sinon la première clé d'API configurée. L'ordre des clés ne compte que dans ce dernier cas : placez en premier une clé qui peut atteindre le modèle Advisor, sinon chaque consultation est indisponible. Lorsqu'un jeton d'environnement ou de service est défini, c'est lui qui contrôle la consultation. Ces règles s'appliquent aussi aux choix de combo, aux modèles de décision imbriqués et aux nouvelles tentatives. Une consultation qui ne peut pas les satisfaire renvoie au worker un avis d'indisponibilité. + +Par exemple, une clé `laptop` avec `allowMeteredComboTargets: false` ne peut pas dépenser sur des cibles de combo facturées via Advisor, et `fallback.devices.laptop.provider: false` bloque aussi les sauts de repli de fournisseur d'Advisor. Un appelant loopback sans clé a toujours besoin de l'option `allowMeteredTargets: true` du combo. Lorsque les deux clés définissent `allowMeteredComboTargets: true` et que le combo définit `allowMeteredTargets: true`, Advisor peut utiliser ses cibles facturées, y compris via l'écouteur loopback d'une liaison publique. Le serveur capture la politique de l'appelant lorsqu'il authentifie la requête, avant d'attendre de la capacité pour le corps ou de le lire ; des modifications ultérieures des clés ou du repli ne peuvent pas élargir cette consultation. Cela couvre aussi les assistants de vision routés, les requêtes compact et leurs modèles de décision, ainsi que chaque nouvelle tentative de candidat dans un tour WebSocket. Chaque nouveau tour WebSocket capture sa propre politique. + OpenCodex n'insère pas dans ce prompt de clés d'API de fournisseur, d'en-têtes Authorization, de jetons OAuth, de secrets de configuration réservés au backend, d'environnement de processus, ni de chaîne de pensée cachée. Il ne déchiffre pas et ne transmet pas un raisonnement privé chiffré du fournisseur. **Le contenu de la tâche n'est pas expurgé de secrets.** Une clé collée dans la tâche, un secret dans un fichier lu par les outils, ou un jeton imprimé par un outil ou un journal peut être envoyé. OpenCodex n'exécute pas de DLP général. ## Autorité diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index e63eb4e35a0..6b7439f0b0c 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -44,6 +44,7 @@ Après une inscription ou une connexion OAuth dans l’interface, une boîte de | `providers` | `Record` | — | Mappage du nom du fournisseur avec la configuration du fournisseur. | | `openaiProviderTierVersion?` | `2` | défini par la migration | Marque la projection OpenAI prenant en compte les options uniques comme terminée. | | `disabledModels?` | `string[]` | — | Modèles masqués du catalogue de Codex et de `/v1/models`, mais non bloqués des appels proxy directs. Un identifiant acheminé est supprimé des listes. Un identifiant natif qualifié de compte masque uniquement cette ligne de sélecteur ; un identifiant GPT natif nu masque la ligne nue et chaque ligne de sélecteur de compte pour ce modèle. La page Modèles du tableau de bord expose uniquement les lignes natives routées et nues ; utilisez ce champ de configuration directement pour masquer une ligne qualifiée par le sélecteur. | +| `omitDisabledNativeModels?` | `boolean` | `false` | Par défaut : `false`. Avec `"omitDisabledNativeModels": true` dans `config.json`, puis `ocx sync`, les lignes natives désactivées sont omises du catalogue, du cache et de la découverte HTTP au format Codex. Le modèle d’héritage natif validé avant omission conserve les métadonnées des lignes routées. Les requêtes directes gardent leur routage habituel. | | `providerContextCaps?` | `Record` | `{}` | Limites de contexte actives par fournisseur. Les fenêtres ordinaires sont réduites ; les modèles natifs prenant en charge une fenêtre longue peuvent être étendus uniquement jusqu’à leur propre plafond pris en charge. | | `providerContextCapValues?` | `Record` | `{}` | Dernières limites sélectionnées par fournisseur, conservées après désactivation. Ces valeurs n’activent aucun plafond. Une valeur active est prioritaire sur une valeur mémorisée. | | `contextCapValue?` | `number` | `350000` | Valeur par défaut lors de la première activation. Les activations suivantes restaurent la sélection du fournisseur. Modifier la valeur globale avec `setAll: true` ne modifie que les plafonds actifs ; `setAll: true` sans valeur active tous les fournisseurs configurés à la valeur globale actuelle. | @@ -160,7 +161,7 @@ sauvegarde dont le contenu diffère, puis réécrit en identifiants sans préfix | `noPenaltyModels?` | `string[]` | Modèles qui rejettent les pénalités presence/frequency. Le préréglage `xai` intégré y liste ses modèles de raisonnement (`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`), que xAI documente comme les refusant ; les identifiants sans raisonnement conservent les pénalités de l’appelant. | | `noStructuredOutputModels?` | `string[]` | ID de modèle exact dont le point final `openai-chat` rejette `response_format`. Seule une correspondance exacte du modèle demandé omet le champ ; la traduction à sortie structurée reste activée pour tous les autres modèles `openai-chat`. | | `noJsonSchemaModels?` | `string[]` | ID de modèle exact dont le point final `openai-chat` rejette un `response_format` `json_schema` mais accepte encore `json_object`. Une telle requête est rétrogradée vers `json_object` au lieu d’être supprimée, donc un appelant qui demande du JSON en reçoit toujours. `noStructuredOutputModels` l’emporte quand un modèle figure dans les deux listes. Les préréglages `opencode go` et `opencode zen` l’embarquent pour leurs routes DeepSeek. | -| `foldDeveloperRoleToSystem?` | `boolean` | Indique si une destination `openai-chat` accepte le rôle `developer`. `foldDeveloperRoleToSystem` non défini envoie `system`, `true` envoie `system` et `false` envoie `developer`. Non défini signifie que rien n'a été enregistré pour cette destination ; `true` enregistre un service en amont qui refuse le rôle ; `false` en enregistre un qui l'accepte. Dans tous les cas le message conserve sa position dans la conversation ; seul le rôle change. Une destination qui refuse le rôle répond `400 role 'developer' is not allowed` et le tour ne démarre pas, d'où l'état non enregistré replié par défaut. | +| `foldDeveloperRoleToSystem?` | `boolean` | Indique si une destination `openai-chat` accepte le rôle `developer`. `foldDeveloperRoleToSystem` non défini envoie `system`, `true` envoie `system` et `false` envoie `developer`. Non défini signifie que rien n'a été enregistré pour cette destination ; `true` enregistre un service en amont qui refuse le rôle ; `false` en enregistre un qui l'accepte. Si `singleLeadingSystemMessage` n'est pas activé, le message conserve sa position dans la conversation ; seul le rôle change. Une destination qui refuse le rôle répond `400 role 'developer' is not allowed` et le tour ne démarre pas, d'où l'état non enregistré replié par défaut. | | `parallelToolCalls?` | `boolean` | Contrôler les appels d’outils parallèles. Pour `openai-chat`, ils sont activés par défaut ; `false` envoie explicitement `parallel_tool_calls: false`. Les autres adaptateurs ne les annoncent que lorsque la valeur vaut explicitement `true`. | | `terminalContinuationGuard?` | `boolean` | Active, pour un fournisseur `openai-chat`, une relance interne bornée lorsqu’un tour exploitable annonce une action puis s’arrête proprement sans appel d’outil. La valeur par défaut est `false`, et une valeur explicite `false` équivaut à l’absence du champ. Les tentatives de combinaison et les tours de compactage routés sont exclus ; les autres adaptateurs ignorent cette option. | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` | Réparation SSE en aval désactivée par défaut pour les identifiants d'espace réservé exacts, les identifiants de terminal manquants et (avec `repairInvalidIds`) les identifiants message/reasoning manquant du préfixe canonique `msg_`/`rs_`. Les identifiants d’appel de fonction ne sont jamais réécrits. Le DeepSeek intégré active les deux derniers par défaut. | @@ -187,6 +188,9 @@ sauvegarde dont le contenu diffère, puis réécrit en identifiants sans préfix | `unsafeAllowNativeLocalExec?` | `boolean` | Ancien booléen de Cursor, équivalent à `nativeLocalExec: "on"` uniquement lorsque le champ plus récent n'est pas défini. | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Politique d'exécution locale de Cursor. `off` est la valeur par défaut ; actuellement, `codex-sandbox` échoue de manière sûre comme `off`. | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + La création et le remplacement d’un fournisseur (`POST /api/providers`) valident `responsesPath` et `chatCompletionsPath` avant de modifier la configuration en mémoire ou sur disque. `PATCH /api/providers?name=` fusionne le corps de la requête avec le fournisseur enregistré ; les mises à jour qui touchent des champs autres que `disabled` — à l’exception des mises à jour portant uniquement sur `requestPacing` — valident de la même manière les chemins du fournisseur fusionné avant l’enregistrement, et un chemin conservé invalide renvoie `400` sans modifier la configuration. Les mêmes règles de chemin s’appliquent au chargement d’un fichier de configuration. Les fournisseurs à clé API peuvent détenir une clé littérale ou une référence à une variable d'environnement. Les fournisseurs OAuth utilisent le @@ -195,17 +199,17 @@ configuré sous [`claudeCode.authMode`](/fr/reference/configuration/server/#clau ### Ce qu'une sauvegarde de fournisseur conserve -`POST /api/providers` avec le nom d'un fournisseur existant remplace la ligne enregistrée par une ligne construite à partir de la requête. Le formulaire d'ajout/modification du tableau de bord ne peut pas envoyer tous les champs ; la sauvegarde conserve donc certains champs enregistrés que la requête omet. Huit d'entre eux décrivent le comportement d'un amont précis : `preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. +`POST /api/providers` avec le nom d'un fournisseur existant remplace la ligne enregistrée par une ligne construite à partir de la requête. Le formulaire d'ajout/modification du tableau de bord ne peut pas envoyer tous les champs ; la sauvegarde conserve donc certains champs enregistrés que la requête omet. Onze d'entre eux décrivent le comportement d'un amont précis : `preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. -| Sauvegarde | Les huit réglages | `apiKeyPool` enregistré | +| Sauvegarde | Les onze réglages | `apiKeyPool` enregistré | | --- | --- | --- | | Même destination, champ omis | Valeur enregistrée conservée, y compris un `[]` ou un `false` explicite | Conservé | | Nouvelle destination, champ omis | Non conservée ; les valeurs par défaut du registre pour la nouvelle destination peuvent s'appliquer | Non conservé | | Champ envoyé dans la requête | La valeur de la requête | La valeur de la requête | -La destination est l'adaptateur, l'URL de base (schéma et hôte comparés sans tenir compte de la casse, barres obliques finales ignorées) et, lorsque la requête en indique un, le mode d'authentification. Déplacer un fournisseur vers une autre destination abandonne les huit réglages, qui décrivent l'amont précédent, ainsi que le pool de clés, dont les clés ont été émises pour lui. Une sauvegarde ne fusionne jamais le reste de l'ancienne ligne dans la nouvelle. +La destination est l'adaptateur, l'URL de base (schéma et hôte comparés sans tenir compte de la casse, barres obliques finales ignorées) et, lorsque la requête en indique un, le mode d'authentification. Déplacer un fournisseur vers une autre destination abandonne les dix réglages, qui décrivent l'amont précédent, ainsi que le pool de clés, dont les clés ont été émises pour lui. Une sauvegarde ne fusionne jamais le reste de l'ancienne ligne dans la nouvelle. -`PATCH /api/providers?name=` ne modifie que les champs qu'il nomme et conserve tous les autres champs enregistrés, quelle que soit la destination. Il accepte les huit réglages ; `null` en efface un. Pour les deux listes de raisonnement, un tableau vide est enregistré comme refus explicite au lieu d'être supprimé. +`PATCH /api/providers?name=` ne modifie que les champs qu'il nomme et conserve tous les autres champs enregistrés, quelle que soit la destination. Il accepte les dix réglages ; `null` en efface un. Pour les deux listes de raisonnement, un tableau vide est enregistré comme refus explicite au lieu d'être supprimé. ## Sécurité des connexions sortantes de diagnostic des fournisseurs diff --git a/docs-site/src/content/docs/guides/chatgpt-desktop.md b/docs-site/src/content/docs/guides/chatgpt-desktop.md index 458acdfbedf..8f195bfa829 100644 --- a/docs-site/src/content/docs/guides/chatgpt-desktop.md +++ b/docs-site/src/content/docs/guides/chatgpt-desktop.md @@ -104,10 +104,11 @@ restart transaction. ## Failure behavior and known limits -When the platform is not macOS, the OpenCodex runtime is missing, or the filter -self-test fails, the launcher runs the original binary with untouched stdout. A -missing bundled app-server binary is the exception: there is nothing to fall back -to, so the launcher exits with an error (see below). +The launcher verifies the executable before every start, including Desktop respawns. +If stdout filtering is unavailable or its self-test fails, the verified executable +runs with untouched stdout. If executable verification fails or the OpenCodex +validator is unavailable, the launcher exits with an error without starting the +app-server. A missing bundled app-server binary also prevents startup (see below). A filter that passes the self-test and then dies mid-session closes the pipe. What the bundled app-server does after that has not been verified; it may get SIGPIPE or a write error and be respawned by Desktop through the same launcher. @@ -117,8 +118,9 @@ remaining stream to raw bytes. The experiment depends on the bundled binary path, the app honoring `CODEX_CLI_PATH`, and current RPC field shapes. Updates may change these. A moved -or removed OpenCodex installation fails the launcher preflight and runs the -original binary. Run `ocx chatgpt launch` again after relocating the installation. +or removed OpenCodex installation can leave the launcher unable to verify the +executable, so it refuses startup. Run `ocx chatgpt launch` again after relocating +the installation, or `ocx chatgpt restore` to remove the override. If an app update moves or removes the bundled app-server binary itself, the launcher cannot start it: it prints a message naming `ocx chatgpt launch` and `ocx chatgpt restore` on stderr and exits, and Desktop cannot start its diff --git a/docs-site/src/content/docs/guides/claude-code.md b/docs-site/src/content/docs/guides/claude-code.md index 68d8b86e879..c0b83efa0ce 100644 --- a/docs-site/src/content/docs/guides/claude-code.md +++ b/docs-site/src/content/docs/guides/claude-code.md @@ -286,6 +286,32 @@ backup and replacement; if it detects a concurrent edit, it refuses with `deskto Review the edit and reapply. If restoring the profile also fails after a metadata error, the reported reason includes both failures; reconcile the profile and metadata before retrying. +#### Choose which models Claude Desktop lists + +**Connect → Claude Desktop → Desktop model list** has a checkbox for each model. Search by name or +filter by provider, then use **Select all matching** or **Clear all matching** to change every match, +including rows behind **Show more**. Click +**Save & apply** to write the list to Desktop. Only Claude Desktop's list changes: provider model +settings, Claude Code, Codex and other clients keep every model. + +**Newly discovered models** sets what happens when a provider later adds a model. **List +automatically** adds it to Desktop. **Keep hidden until selected** leaves it out until you check it. +The selection is saved in `claudeCode.desktopModelVisibility` and survives catalog refreshes and +reapplies. Tier families under **Advanced** stay optional and do not affect which models are shown. +A hidden model keeps its Desktop id, so a conversation that is already using it keeps working. + +The card shows the selected, available and applied counts separately. A selected model that is no +longer available is counted on its own and is not written. If the selection leaves no available +models, the writer falls back to every available model, including normally hidden routes. Update +the selection and apply again to narrow it. Before you apply, the card previews the model IDs the +selected profile file will gain and lose. Its applied count reads that file; it does not verify +which models the Desktop process has loaded. The status bar shows **Saved — apply to update Desktop** +until the saved list reaches the profile. Restart Desktop to load profile changes. +Applying replaces the model list in OpenCodex's Desktop profile, +including a list edited there by hand. Discovery-only mode (`--discovery-only`) writes no list, so +it does not use this selection. The dashboard will not save a selection that hides every +available model. + ### First-party (opt-in) :::caution[Account risk] @@ -830,6 +856,11 @@ Proxy startup/ensure, `ocx claude`, and relevant dashboard saves sync your featu overwritten or pruned; your own agents are never touched. - Files are atomically synced per file (write + rename). - `enabled: false` or `injectAgents: false` prunes all verified-owned definitions. +- Turning Claude Code off with the dashboard's integration toggle also prunes them right away + (best effort), so a native `claude` session started afterwards stops seeing proxy-only agents. + A file that cannot be deleted stays, and while a sibling OpenCodex instance owns + `~/.claude/agents` nothing is touched; turning it off again retries and also removes + definitions an older version left behind. - Successful CLI first-party saves and persisted Desktop first-party setup also attempt best-effort roster sync, including repeated setup to repair missing definitions. Refused setup and rollback do not sync agents. Turning first-party off alone does not disable agent registration. @@ -974,7 +1005,7 @@ The proxy translates every Anthropic Messages API request into the Codex Respons | User `tool_result` | `function_call_output` (`is_error` → `[tool error]` prefix) | | `tool_reference` in a tool result | Text `Tool loaded: ` in the paired result by default. The [append-only opt-in](#preserve-cache-prefixes-when-client-tools-load) may place a currently declared deferred function at its successful client discovery result on an eligible final Responses route; a reference alone never grants a tool or enables server-side search | | `thinking` / `redacted_thinking` replay | `reasoning` items with bounded `ocxr1` envelopes for signatures and redacted payloads | -| Function tools | `{type: "function"}` (`web_search*` → `{type: "web_search"}`). On a route to `api.anthropic.com`, the typed `web_search_*` and `web_fetch_*` declarations are also sent to Anthropic as declared and run server-side, unless the web-search sidecar supplies `web_search` itself. A search or fetch with a matching call and complete input comes back to the client as its `server_tool_use` block and its `web_search_tool_result` or `web_fetch_tool_result`, verbatim. Orphan results are dropped without synthesizing a call, while the surrounding text is preserved. When the client sends a complete pair back in its history, the next `api.anthropic.com` request carries it in place only if the following block uniquely identifies its original turn. Repeated anchors, unmatched or duplicate call/result ids, malformed call input, or a result appearing before its call drop the exchange. Call input must be an object with a nonempty search query or fetch URL string; other destinations never see the exchange | +| Function tools | `{type: "function"}` (`web_search*` → `{type: "web_search"}`). On a route to `api.anthropic.com`, the typed `web_search_*` and `web_fetch_*` declarations are also sent to Anthropic as declared and run server-side, unless the web-search sidecar supplies `web_search` itself. A search or fetch with a matching call and complete input comes back to the client as its `server_tool_use` block and its `web_search_tool_result` or `web_fetch_tool_result`, verbatim. Orphan results are dropped without synthesizing a call, while the surrounding text is preserved. When the client sends a complete pair back in its history, the next `api.anthropic.com` request carries it in place only if the following block uniquely identifies its original turn. Repeated anchors, unmatched or duplicate call/result ids, malformed call input, or a result appearing before its call drop the exchange. Call input must be an object with a nonempty search query or fetch URL string; other destinations never see the exchange Hosted search with `allowed_domains`, `blocked_domains` or `user_location` is omitted on translated Responses/Chat routes; forcing omitted search returns 400. Valid `defer_loading` function definitions remain callable as ordinary functions. See [translation limits](#search-restrictions-and-deferred-tools-on-translated-routes). | | `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, named function→`{type:"function",name}`, hosted WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | @@ -987,6 +1018,23 @@ Replay preserves non-hidden signed blocks (including empty thinking) and opaque role; `tool_result` without `tool_use_id`; `tool_use` without id/name; named `tool_choice` without name. +### Search restrictions and deferred tools on translated routes + +When a `web_search*` tool supplies `allowed_domains`, `blocked_domains` or `user_location`, +OpenCodex omits hosted search from the translated Responses/Chat catalog, including combo +fallbacks and search sidecars. Those destinations cannot uniformly preserve the constraints; +search is never silently widened. Duplicate search declarations cannot bypass this rule. +A forced hosted-search choice with no surviving declaration returns HTTP 400. +First-party Anthropic destinations still receive the original typed declarations through the +existing server-tool lane when translation proceeds; native passthrough preserves them directly. +Unrestricted translated search remains available under the existing sidecar and admission rules. + +Valid function definitions with `defer_loading: true` remain callable as ordinary functions on +translated combo routes. This eager-loading degradation does not itself cause compatibility +rejection. Enforce mode still rejects unsupported top-level deferral, schema-less deferred tools, +server-executed discovery and unsupported discovery history. Eligible direct Responses routes +can use the [append-only opt-in](#preserve-cache-prefixes-when-client-tools-load). + ### Unicode-property patterns in tool schemas A JSON Schema `pattern` written for JavaScript may use Unicode property escapes such as diff --git a/docs-site/src/content/docs/guides/codex-app-models.md b/docs-site/src/content/docs/guides/codex-app-models.md index 4da93918113..2f5805d7b49 100644 --- a/docs-site/src/content/docs/guides/codex-app-models.md +++ b/docs-site/src/content/docs/guides/codex-app-models.md @@ -135,7 +135,7 @@ for the command, disable-key semantics, and safety constraints. ### What this means for a disabled native model -Without a native alias configured, disabling a bare native GPT slug does not remove it from the +By default, without a native alias configured, disabling a bare native GPT slug does not remove it from the catalog. The row stays with `visibility: "hide"`, which `/v1/models` and the dashboard both honour — they stop listing the model — while Desktop, under the policy above, can keep showing it. So the model can still be picked in Desktop after you disabled it, and the surfaces disagree about whether @@ -147,9 +147,13 @@ runs on the model you disabled, or fails on whatever path that id resolves to. E outcome is not the one the toggle implies. The row is retained deliberately. It holds the real upstream metadata, so re-enabling the model -restores that metadata instead of a synthesized guess. When you need the row gone outright rather -than hidden, configure a `nativeAlias` combo: while one exists, disabled bare native rows are -omitted from the effective catalog entirely. +restores that metadata instead of a synthesized guess. To omit disabled native rows without a +native alias, set `"omitDisabledNativeModels": true` in `config.json` and run `ocx sync`. +The option defaults off. Sync, the generated cache, and Codex-shaped HTTP discovery omit the +disabled rows while retaining a separately validated native inheritance template, so routed +rows keep their input and tool metadata even when the only template row was disabled. +Direct requests to a disabled native id still route normally. A `nativeAlias` combo also keeps +its existing omission behavior independently of this flag. If Codex's `config.toml` pins a root `model` that this proxy does not expose — a disabled model among them — every new session starts on a model opencodex does not serve. `ocx doctor` reports diff --git a/docs-site/src/content/docs/guides/combos.md b/docs-site/src/content/docs/guides/combos.md index 4651ef4799f..a0b9de7c718 100644 --- a/docs-site/src/content/docs/guides/combos.md +++ b/docs-site/src/content/docs/guides/combos.md @@ -24,7 +24,7 @@ Mark a pay-per-use combo target with `"metered": true` when it follows subscript Request headers cannot grant consent. Model/provider scopes still apply. This covers initial selection, failover and recovery across combo strategies, compaction, Messages, Chat Completions and WebSocket requests, and a subagent model fallback naming a combo alias. A collapsed fallback tries a usable unmetered target before refusing. Unmarked targets retain existing behavior; metering is declared by the operator rather than detected automatically. -For example, a failover chain can target Anthropic OAuth, then Cursor, then `{ "provider": "anthropic-apikey", "model": "claude-opus-5-5", "metered": true }`. Without consent, exhaustion returns the last unmetered error, with one exception: an earlier 429 or 402 quota refusal takes precedence over a later 400, 401 or 403. If the subscription target returns 429 and Cursor then refuses with 401 or 403, the caller receives the subscription's 429, because the spent subscription explains the failure and Cursor's credential refusal does not. Dashboard saves retain omitted target metering and combo consent; explicitly setting either to false clears it. These consent fields are configured through the config file or API; the combo flag is also available through the CLI, while the dashboard does not expose it. +For example, a failover chain can target Anthropic OAuth, then Cursor, then `{ "provider": "anthropic-apikey", "model": "claude-opus-5-5", "metered": true }`. Without consent, exhaustion returns the last unmetered error, with exceptions for [confirmed context overflow](#context-window-refusals) and quota refusals: an earlier 429 or 402 quota refusal takes precedence over a later 400, 401 or 403. If the subscription target returns 429 and Cursor then refuses with 401 or 403, the caller receives the subscription's 429, because the spent subscription explains the failure and Cursor's credential refusal does not. Dashboard saves retain omitted target metering and combo consent; explicitly setting either to false clears it. These consent fields are configured through the config file or API; the combo flag is also available through the CLI, while the dashboard does not expose it. ## 60-second quickstart @@ -152,6 +152,10 @@ native routing and catalog identity on the next sync. ### Failover: ordered primary and backups +For Responses requests that require a tool, Meta Responses targets are skipped before sending +because Meta does not support mandatory `tool_choice`. An eligible later target receives the +original choice. If every target is incompatible for this reason, the combo reports the constraint. + `failover` selects the first eligible target in configuration order. A target is eligible when its provider exists, is enabled, is not cooling down, and can handle any special request constraint. Weights and `stickyLimit` do not affect this strategy. @@ -672,7 +676,8 @@ provider, see [Fallback off](/guides/fallback-off/). | HTTP 401, 403, 404, 408, 429, or any 5xx | Cool the target and hop to the next eligible target. | | HTTP 410 with an explicit model end-of-life, retired, deprecated, sunset, decommissioned, or no-longer-available signal | Cool that target and hop. Unrelated 410 responses remain terminal. | | Classified authentication, subscription, quota, rate-limit, overload, or upstream-server error | Cool the target and hop, even when the status alone is not sufficient. | -| Client cancellation (499), `origin_rejected`, cyber-policy refusal, context overflow, or other invalid request | Stop and return the error; another target would not make the request valid. | +| Confirmed context-window overflow before output, including HTTP 400 | Try the next untried eligible target without cooling this target; another model may have a larger window. Exhaustion returns the first confirmed HTTP 400 context-length error; a context refusal sent with another status still advances but is not retained. | +| Client cancellation (499), `origin_rejected`, cyber-policy refusal, or other invalid request | Stop and return the error; another target would not make the request valid. | | Exact HTTP 400 saying a model is not supported when using Codex with a ChatGPT account | Try the next declared target before output commitment. The message must be the whole refusal in `detail`, `error.message`, or bare text; competing `detail` and `error` envelopes stay terminal. This does not quarantine the account or cool every model on the provider. | | Structured HTTP 400 rejecting optional `user`, an unsupported reasoning effort, model-scoped image input, or input media the route cannot read (`unsupported_input_modality`) | Hop before output commitment without cooling; see request-local target compatibility below. | | First tool call of a Responses turn run by an in-process adapter (`runTurn`) that the current request did not declare, before any output or replay-unsafe side effect | Cool the target and hop with the same tool catalog. After visible output or a replay-unsafe side effect the refusal is final. Chat Completions and Anthropic Messages requests are unchanged. | @@ -762,7 +767,7 @@ cooldown expires; request-local compatibility rejections do not cool the target. immediate upstream directive, just like `Retry-After: 0`. Set `waitForCooldownMs` to allow a later request to wait for the earliest eligible target cooldown, up to that cap on each selection attempt, and then make one fresh selection. A request may therefore wait up to `hops × waitForCooldownMs` -across multiple failover hops. The default is `0`, which fails closed immediately with HTTP 503 when +across multiple failover hops. Before any target is attempted, the default `0` fails closed immediately with HTTP 503 when every eligible target is cooling; that `combo_unavailable` 503 carries a `Retry-After` header equal to the earliest remaining cooldown, rounded up to whole seconds with a minimum of 1. Waits are not jittered, so synchronized wake-ups are possible. An aborted request cancels this wait and returns the normal @@ -770,6 +775,12 @@ synchronized wake-ups are possible. An aborted request cancels this wait and ret cooldown is process-local per-combo state and is separate from the account-level Codex quota cooldown used by native account routing. +After a confirmed HTTP 400 context refusal, exhaustion returns the first such refusal as +`context_length_exceeded`, including when the remaining targets cannot replay reasoning or lack +metered-spend consent, or the send budget is exhausted. It takes precedence over later retryable +errors, including quota errors. Cancellation, later fatal refusals and explicit fallback-policy +refusals still stop immediately; no unconsented metered target is sent the request. + :::note Failover is intentionally bounded. It helps with target-specific availability, authentication, quota, and overload failures; it does not hide caller errors or policy refusals. @@ -1044,9 +1055,13 @@ HTTP 400. A slashed alias whose first segment is a configured Codex account name as HTTP 409; choose a different alias namespace. The CLI and dashboard display the server's exact validation message. +### Context-window refusals + +Combo recovery reuses the bounded context-overflow detector used by Responses recovery. It reads the innermost provider message, including `context_length_exceeded`, `prompt is too long`, `input is too long for requested model`, and `Please reduce the length of the messages or completion`. The last three count only when the provider message starts with them (Anthropic's with its token counts), so a validation error that quotes one as a parameter value, or an SDK error string that wraps one, does not qualify. A code beside an unrelated message, malformed JSON, a parameter validation error, or a generic instruction to shorten a parameter does not qualify either. Already-committed output is never replayed. Fallback policy, spend consent and send budgets still apply; each physical attempt retains its own usage. When eligible targets or the send budget run out, the first confirmed HTTP 400 context refusal takes precedence over later retryable errors, including quota errors. A later fatal refusal still stops immediately. + ### Why did failover stop after the first error? -The error was terminal rather than target-specific. Fix invalid input, reduce an oversized context, +The error was terminal rather than target-specific. Fix invalid input, handle a policy refusal, or correct the rejected request origin. Combos do not hop for those cases. ## Retry all-cooled targets (opt-in) diff --git a/docs-site/src/content/docs/guides/integrations.md b/docs-site/src/content/docs/guides/integrations.md index b478bae8d9b..ae6a4775100 100644 --- a/docs-site/src/content/docs/guides/integrations.md +++ b/docs-site/src/content/docs/guides/integrations.md @@ -69,6 +69,24 @@ This integration retains Pi’s loopback-only export policy and stores no real A [omo (Codex / LazyCodex) role models](#omo-codex--lazycodex-role-models). OpenCode-based omo (oh-my-opencode) keeps its own config under OpenCode; opencodex does not read or write it. +The managed Pi-based omo block uses the Chat dialect (`api: "openai-completions"`) by default. Chat +drops the previous step's reasoning across tool calls, which long omo tool loops notice. To have +enable and every refresh write the Responses dialect instead, set this config-file-only key in +your opencodex config: + +```json +{ "omoClientExport": { "api": "openai-responses" } } +``` + +Restart the proxy to load the edited config (`ocx service restart` for a service install, or `ocx stop`, then `ocx start`), then refresh the omo integration from **Connect → omo** in the dashboard. +Refreshing alone uses the running proxy's config and does not reload the config file. + +The setting applies only to omo; Pi, Gentle Shell and the other Pi-format clients keep +`openai-completions`. To return to Chat, remove the key, restart the proxy in the same way, then +refresh **Connect → omo**. An unknown value is ignored. +opencodex still owns only `providers.opencodex` in `~/.omo/agent/models.json`, so other providers +in that file are left as they are. + ### Qoder CN and WorkBuddy editions In **Connect**, enable **Qoder CN**, **WorkBuddy (CN)** or **WorkBuddy AI**, then choose @@ -969,15 +987,40 @@ exists only while LazyCodex is detected. Without it the dashboard shows neither, Auto-assign proposes a model for every role at once. opencodex asks your default Codex model (the root `model` in Codex `config.toml`) one question: for each role, given its -description and the start of its instructions, which capability tier (fast, standard or frontier) -and how much reasoning (glance, measured, thorough or exhaustive) does it need? That model never -picks a model. opencodex then picks the cheapest model from your picker list that reaches the tier: +description and the start of its instructions, which capability tier (fast, standard or frontier), +how much reasoning (glance, measured, thorough or exhaustive) and which task domain (coding, agentic, +reasoning, writing, knowledge or general) does it need? That model never picks a model. opencodex +then picks the cheapest model from your picker list that reaches the tier: - Models listed under `codexRoleTiers` in the opencodex config (`{ "fast": [...], "standard": [...], "frontier": [...] }`) have that tier. +- When two or more of the other models have a published score in the role's domain, those models are + tiered by distinct score values, without requiring a price; equal scores share a tier, and unscored models are left out. Scores come from + [Artificial Analysis](https://artificialanalysis.ai) when you add your own API key + (below). The response's `capability` block names the source it used. - Other models with a known price are ranked by price and split evenly across the three tiers. With only one or two priced models, the dearest is frontier and the other, if any, is standard. -- Models with no price and no listed tier are never proposed. List them to include them. +- When tiering falls back to price, models with no price and no listed tier are not proposed. List them to include them. + +To use Artificial Analysis, create a free API key at [artificialanalysis.ai](https://artificialanalysis.ai/api-reference) +and set it as a literal, an `${ENV}` reference or a keychain reference: + +```json +{ + "codexRoleCapability": { + "artificialAnalysis": { "apiKey": "${ARTIFICIAL_ANALYSIS_API_KEY}" } + } +} +``` + +opencodex reads it once per day, which stays inside the free tier's daily request limit. Its terms allow +using the data for your own decisions and forbid passing it on, so auto-assign keeps the scores +internal: the response carries the tier each model got and the attribution, without the scores. If the +key is absent or the read fails, auto-assign uses configured tiers and prices; `capability.error` reports +why and the source attribution fields are null. A retired `huggingFace` setting is ignored. + +Artificial Analysis refuses HTTP redirects and limits each response body to 16 MiB with a 30-second +request timeout. Credentials are sent only to its fixed API endpoint. Each proposal shows the model, the tier, the reasoning effort, a one-line reason, and what would move it up or down. A role the model could not size clearly is shown as not sized, with the reason, and diff --git a/docs-site/src/content/docs/guides/lm-studio.md b/docs-site/src/content/docs/guides/lm-studio.md new file mode 100644 index 00000000000..33b43952290 --- /dev/null +++ b/docs-site/src/content/docs/guides/lm-studio.md @@ -0,0 +1,38 @@ +--- +title: LM Studio strict chat templates +description: Configure one leading system message for LM Studio Chat models. +--- + +The **LM Studio (local)** preset enables `singleLeadingSystemMessage: true` for +the `openai-chat` adapter. This combines text-only system/developer instructions +into one leading system message with blank lines between messages. It supports +custom model IDs, including quantization and checkpoint suffixes, without relying +on the model-name compatibility selector. + +For an existing renamed/custom LM Studio provider, add this boolean explicitly +in the provider editor. The stored value is independent of the provider name, +hostname, and port. A same-destination save preserves an omitted setting; a POST +that replaces the destination must resubmit it. PATCH accepts a boolean or `null` +to clear the override. Clearing it on the canonical LM Studio provider restores +the preset default. + +Set `singleLeadingSystemMessage: false` to disable merging for a template that +accepts chronological instruction messages. Other presets keep their defaults. +`foldDeveloperRoleToSystem` continues to control only the developer role; enabling +single-message compatibility takes precedence over that role choice and the +model-name-based late-instruction workaround. + +Hoisting later instructions changes their chronological scope and can invalidate +prompt-cache prefixes. Translated requests preserve IR instruction order: base +system prompts first, followed by timeline developer instructions. The Responses +parser already collects input system items into the base prompt, so their original +positions relative to developer items are not recovered. Direct Chat passthrough +preserves the original relative order of system/developer messages. Both paths +normalize the outbound copy and keep user messages and tool pairs in order. + +Non-text system/developer parts in direct Chat fail explicitly rather than being +silently discarded. Translated developer documents keep their file parts in a +user-compatible message at the existing timeline slot while their text joins the +leading system message. Developer image/audio messages keep their existing +user-compatible mapping. No LM Studio runtime is required to configure this flag; +the served model's template still determines which other wire features it accepts. diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index fd3f1f6bf58..5a43f759fe2 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -69,7 +69,7 @@ is retained once at `~/.opencodex/config.json.pre-openai-tiers-v2.bak`; restore ## Claude Opus 5.5 progress updates When the `anthropic` adapter sends Claude Opus 5.5 to `api.anthropic.com` (OAuth or API key), -OpenCodex requests Anthropic's [progress-update display mode](https://platform.claude.com/docs/en/build-with-claude/thinking#progress-updates). +OpenCodex defaults to Anthropic's [progress-update display mode](https://platform.claude.com/docs/en/build-with-claude/thinking#progress-updates). Progress updates and mid-turn acknowledgements appear as visible assistant commentary in Codex, even when thinking summaries are hidden. Reasoning summaries are omitted in this mode because Anthropic cannot distinguish progress updates from reasoning under `display: "summarized"`. @@ -77,6 +77,14 @@ The original signed thinking blocks are preserved for tool continuation; no sign or rewritten. Updates appear once their signed block completes. Other models and compatible third-party endpoints retain their existing thinking display behavior. +To request summarized thinking instead, explicitly send `reasoning.summary: "concise"` or +`"detailed"`. OpenCodex then sends `thinking: {type: "adaptive", display: "summarized"}` without +the updates beta and returns thinking text as reasoning instead of progress commentary. In this +mode, Anthropic's progress updates may be folded into the reasoning summary. The choice also +works when reasoning effort is omitted or `"none"`; it does not change the requested effort. +Omitting `reasoning.summary` or sending `"auto"` (the Codex default) or `"none"` keeps the updates default. Local summary hiding +still controls reasoning visibility and does not hide progress commentary in updates mode. + ## Anthropic image input The built-in Claude model seeds advertise text and image input for both `anthropic` (OAuth) and @@ -109,6 +117,12 @@ identity for five minutes. Only the refused opaque payloads are suppressed when identified; newly issued thinking remains replayable. Plaintext history remains available; a failed or cancelled retry does not disable thinking replay on later requests. +## xAI Responses includes + +For xAI Responses requests, OpenCodex removes the unsupported `output.encrypted_content` entry +from `include`, including on the [US regional endpoint](https://docs.x.ai/developers/advanced-api-usage/regions). +Other include entries are preserved; an empty result omits the field. Other providers are unaffected. + ## Auth modes Provider configs accept three `authMode` values (`key` is the default). The built-in registry also @@ -204,6 +218,13 @@ Cursor, Kiro, Zed Hosted AI and Nous Portal are login-only and have no API-key e login-only too: `ocx login google-antigravity` signs in with your Google account over the Cloud Code Assist wire, and the `google` preset beside it is the AI Studio Gemini API — a different product reached with its own key, not a key mode for the same login. +If Antigravity login cannot discover a Cloud Code Assist project, the failure includes a sanitized +summary for `loadCodeAssist` and the last `onboardUser` response: HTTP status, response category, +completion/error presence and known project-field types. It distinguishes HTTP refusal, unusable +JSON, invalid roots, unfinished onboarding and completion with an error or without a project. +The summary omits upstream values, arbitrary key names and account/project identifiers. Login +still requires a discovered project and saves no account when discovery fails. + The Antigravity Accounts tab shows an observed membership tier as a badge beside the account label, using the upstream tier ID (for example `free-tier` or `gemini-pro`). No badge appears when the plan is unknown; it does not affect routing or quota. @@ -326,7 +347,7 @@ ocx logout | `kimi` | `openai-chat` | `https://api.kimi.com/coding/v1` | Kimi Code Plan coding models. Defaults to the stable `kimi-for-coding` alias (currently K2.8 Preview): 1M-token context window, adjustable `low`/`high`/`max` thinking (default `max`), text + image input. `k3.1-pro` and `k3.1-pro-highspeed` (K3.1 Pro) are also listed: 1M-token context, thinking-only on the same ladder (default `max`). Retired `kimi-k2.x` selections are migrated to the alias on upgrade. | | `kimi-responses` | `openai-responses` | `https://api.kimi.com/coding/v1` | Same Kimi account login (reuses the `kimi` OAuth credential) over the OpenAI Responses wire. Same model roster and capabilities as `kimi`; thinking content stays encrypted server-side, tool calls and results stay visible. | | `nous` | `openai-chat` | `https://inference-api.nousresearch.com/v1` | Nous Research subscription gateway (same backend Hermes Agent uses). Device-grant login against `portal.nousresearch.com`; the access token is the per-request inference JWT. Mixed paid + `:free` model catalog (`tencent/hy3:free`, `stepfun/step-3.7-flash:free`, ...) discovered live from the signed-in account. Refresh tokens are single-use and rotated on every refresh. | -| `kiro` | `kiro` | `https://runtime.us-east-1.kiro.dev` | The dashboard Login and Add account buttons offer Builder ID, Google, GitHub device login, or Kiro CLI. Native device login adds an account without signing `kiro-cli` out. The Kiro CLI choice imports or starts a CLI session; its Add account workflow temporarily switches the CLI account and restores it on cancellation or failure. | +| `kiro` | `kiro` | `https://runtime.us-east-1.kiro.dev` | The dashboard Login and Add account buttons offer AWS IAM Identity Center, Builder ID, Google, GitHub device login, or Kiro CLI. Native device login adds an account without signing `kiro-cli` out. The Kiro CLI choice imports or starts a CLI session; its Add account workflow temporarily switches the CLI account and restores it on cancellation or failure. | | `google-antigravity` | `google` | `https://daily-cloudcode-pa.googleapis.com` | Google OAuth over the Cloud Code Assist wire. Live discovery uses CCA's authenticated `v1internal:fetchAvailableModels` endpoint and publishes the agent models available to the signed-in account; the maintained catalog remains the fallback. | | `cursor` | `cursor` | `https://api2.cursor.sh` | Experimental PKCE login, live HTTP/2 transport with an opt-in HTTP/1.1 compatibility path, and account-filtered model discovery. | | `zed` | `zed` | `https://cloud.zed.dev` | Experimental Zed Hosted AI bridge. Uses Zed's native-app RSA callback, exchanges the account token for a short-lived hosted-inference token, and discovers the account's live roster. | @@ -419,10 +440,15 @@ think: the picker offers `low` / `high` / `max` (default `max`), Codex `medium` and `xhigh` as `max`, and an explicit `none` leaves `reasoning_effort` out so the upstream default applies. Fixed sampling parameters (`temperature`, `top_p`, penalties) are not sent. A saved `moonshot` model list that is still the previous built-in list gains both ids at startup when it -points at the International or China endpoint; an edited list or a Custom URL is left as it is. -The preset default stays `kimi-k2.7-code`. Moonshot closes the K2 series to new accounts on -2026-10-31 and plans to retire it by the end of December 2026; see the -[Kimi API model list](https://platform.kimi.ai/docs/models). +points at the International or China endpoint; this roster refresh adds no models to an edited +list or a Custom URL. The retired-id migration below applies separately. +The preset defaults to the vendor-recommended `kimi-k3.1-pro`. `kimi-k2.5`, discontinued on +2026-08-31, is removed from the API seeds; saved selections on the official International or China +endpoint migrate to `kimi-k3.1-pro`, refreshing old built-in context and reasoning metadata while +preserving explicit overrides. Custom destinations keep their saved selections. `kimi-k2.6` and +`kimi-k2.7-code` (including highspeed) remain selectable for existing users: Moonshot closes the +K2 series to new accounts on 2026-10-31 and plans to retire it by the end of December 2026; see the +[Kimi API model list](https://platform.kimi.ai/docs/models.md) (checked 2026-10-28). A custom `openai-chat` provider can opt in when its upstream documents support for `prompt_cache_key`: @@ -523,7 +549,7 @@ OAuth providers whose credentials include a stable account id or email can keep login. The Providers page shows those accounts in a dropdown, lets you add another, and switches the active account without logging the others out. A normal login with an identity-less Kimi credential replaces the active slot, while an explicit **Add account** preserves that slot and activates a new, -distinct one. Kiro accounts are keyed by profile ARN. `chatgpt` is always single-slot because Codex +distinct one. Kiro accounts use the profile ARN together with the signed-in user's email when available; different users can keep separate credentials for one organization profile. `chatgpt` is always single-slot because Codex pool accounts have a separate ledger. Tokens stay in `~/.opencodex/auth.json`; `/api/oauth/accounts` returns masked metadata only. @@ -647,7 +673,19 @@ Other messages, statuses, and AWS SSO responses do not trigger this desktop-spec ### Kiro credential import -The dashboard offers native Builder ID, Google, and GitHub device login without Kiro CLI. +The dashboard offers native AWS IAM Identity Center, Builder ID, Google, and GitHub device login without Kiro CLI. + +For an organization account, choose **AWS IAM Identity Center**, enter your AWS access portal URL +(for example `https://example.awsapps.com/start`) and the region where your organization configured +IAM Identity Center. Open the verification link in your own browser, enter the displayed code, +and authorize the account. The hub completes the grant; no SSH session or browser callback to +the hub is required. If AWS lists several Kiro profiles, choose the profile to save. With one +profile the account is saved automatically. The SSO region and the selected profile's API region +are kept separately. Profile discovery checks AWS's US East and Frankfurt service regions. +The signed-in user's email is resolved from AWS for the selected profile before saving; users +sharing one organization profile keep separate account credentials. Cancellation or expiry before +the credential write saves no account. Native login adds a slot and keeps the existing selection. + The Kiro CLI choice and `ocx login kiro` import path use the CLI: on Unix, install it with `curl -fsSL https://cli.kiro.dev/install | bash`; on Windows PowerShell, use `irm 'https://cli.kiro.dev/install.ps1' | iex`; then sign in with `kiro-cli login`. Without a `kiro-cli` session, `ocx login kiro` falls @@ -684,7 +722,7 @@ an ambiguous token selection), when `KIROCLI_DB_PATH` / `KIRO_CLI_DB_FILE` redir from the live CLI store, or when an existing primary CLI database has no recognized token row. Repair or remove the unreadable database under the normal `kiro-cli` data path, unset those import selectors, then retry. Signing in from a machine with no existing `kiro-cli` session is unaffected. -The native dashboard choices are add-only and do not sign out `kiro-cli`. A device dialog shows the code and verification destination. Only recognized Kiro or Builder ID hosts are opened as links; an unexpected destination is shown as copyable text for review. Closing the dialog sends cancellation and leaves a bounded background status check to reconcile a commit already in progress. A stalled status response is retried; exhausting the flow deadline produces the neutral ended outcome rather than claiming success. Google and GitHub approval completes when Kiro reports `authorized` (or the legacy `approved` status) with valid tokens and a profile ARN. +The native dashboard choices are add-only and do not sign out `kiro-cli`. A device dialog shows the code and verification destination. Only recognized Kiro or regional AWS device-SSO hosts are opened as links; an unexpected destination is shown as copyable text for review. Closing the dialog sends cancellation and leaves a bounded background status check to reconcile a commit already in progress. A stalled status response is retried; exhausting the flow deadline produces the neutral ended outcome rather than claiming success. Google and GitHub approval completes when Kiro reports `authorized` (or the legacy `approved` status) with valid tokens and a profile ARN. The account list marks Kiro accounts excluded from automatic selection with a reason, when available. ### DeepSeek API @@ -1044,6 +1082,10 @@ unchanged and were not probed in this update. > models. Coding Plan defaults to `ark-code-latest`, while Agent Plan defaults to > `deepseek-v4-flash`. +On the pay-as-you-go Ark route, Kimi K3.1 Pro (`kimi-k3-1-pro`, including dated versions) +replays tool-only assistant turns with an empty string. Its endpoint rejects the legacy +empty-text array with HTTP 400; other Ark models keep their existing continuation format. + > **Volcengine Plan usage restriction:** Volcengine documents Coding Plan and Agent Plan quota as > valid only inside supported AI coding tools, and warns that using a plan key for general API > calls may suspend the subscription or ban the account. Routing Codex or Claude Code through @@ -1230,6 +1272,14 @@ request translation rather than passthrough reports none. An account that has no served a streaming turn simply shows no quota, which is not an error. Rate limits apply per team, not per key. +When the current Muse account's latest-started retained request explicitly reports subscription exhaustion +with a future reset time, the dashboard quota strip can show that observed limit even if +Meta omitted the normal usage frame. The Muse chip includes a reset countdown. This reads +local request-log evidence and never sends a model request. It does not classify an unnamed +window as five-hour or weekly, and a newer successful request or an elapsed reset removes +this fallback. The fallback selects by request start time across the server-bounded matching +log history, so an older request that finishes later cannot hide a newer success. + For a supported setup, use `meta-model` above with your own key. **Command Code quota.** The dashboard and `ocx account refresh` probe Command Code's diff --git a/docs-site/src/content/docs/guides/sidecars.md b/docs-site/src/content/docs/guides/sidecars.md index d977b2ec025..ed7f778add1 100644 --- a/docs-site/src/content/docs/guides/sidecars.md +++ b/docs-site/src/content/docs/guides/sidecars.md @@ -286,6 +286,8 @@ See the [Configuration reference](/reference/configuration/server/#sidecars) for Search responses with malformed optional fields retain valid answer text and citations. Invalid error-message values use a generic failure message. +The OpenAI search and vision sidecars accept CR, LF and CRLF line endings, including mixed endings and CRLF split across network chunks. They read a final completion or failure event even when the server closes the body without a trailing newline. An unfinished response or one stopped by a size limit still reports an error. + When a search answer ends with a Sources list, an unmatched list item stays in the answer instead of being removed as a citation. A title followed by its URL is still consumed as one citation. Non-HTTP URI-like text inside inline code, such as `mode:fast`, stays in the answer; code-formatted HTTP(S) URLs are still citations. Long search queries and answers are shortened without splitting a UTF-16 surrogate pair, so a single character such as 😀 is never cut in half by the result limits. A sequence of several characters, such as emoji joined with a zero-width joiner, can still be cut between its parts. diff --git a/docs-site/src/content/docs/guides/sub-agent-surface.md b/docs-site/src/content/docs/guides/sub-agent-surface.md index a7bd6c543eb..eab0c0000b8 100644 --- a/docs-site/src/content/docs/guides/sub-agent-surface.md +++ b/docs-site/src/content/docs/guides/sub-agent-surface.md @@ -151,7 +151,8 @@ description of the work Codex usually hands off, sizes it with one call to your and proposes the cheapest sufficient model and an effort from the list the picker offers, with the reasoning and the signals that would move it up or down. Nothing changes until you choose **Use this**, which saves exactly like picking the model and effort by hand. Tiers come from -`codexRoleTiers` or price, as in [role auto-assign](/guides/integrations/#auto-assign). +`codexRoleTiers` or price, as in [role auto-assign](/guides/integrations/#auto-assign), without the +measured scores role auto-assign can read from configured sources. `multiAgentGuidanceEnabled` defaults to on and is the master switch for opencodex-authored guidance on both surfaces. Turning it off suppresses both the v2 designation block and v1 proactive text. diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index 27bc1a1aca1..7426627eeab 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -131,7 +131,7 @@ is visible) and never forces an upstream refresh. | **Providers** | Add, edit, set the default (enabled providers only), enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Removing the current default switches to the first remaining enabled provider when one exists; otherwise deletion is refused and the current default is kept. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. For Claude (Anthropic) OAuth pools, each logged-in account shows its own 5-hour and weekly rate-limit bars (usage is per credential); a failed probe keeps the last-known bars and marks them unavailable until the next successful refresh. Refreshing quota skips paused OAuth accounts and disabled API keys; skipped rows without a cached reading remain unavailable but do not affect the refresh result. Refreshing reports failure when an unpaused OAuth account or enabled API key that supports quota reads has an unavailable reading. The Provider Overview shown when no provider is selected carries a **Refresh all quotas** control that forces one server-side re-read of every configured provider; a provider whose upstream probe fails keeps its last-good row, so the status line reports that the check completed rather than claiming every value is fresh, and each row's own age stays the per-provider freshness signal. | | **Add provider** | One search above the tabs reaches all four at once — Accounts, Free, Local, Paid. While a query is live the results are grouped by tab with a count each, and the selected tab stays put rather than jumping. Press ArrowDown in the search box to focus the first available result action, skipping disabled buttons; if no action is available, focus stays in the search box. Local runtimes (Ollama, vLLM, LM Studio, LiteLLM) have their own tab, and a long provider note clamps to two lines with the full text one click away. Claude Code CLI (subscription) sits under Paid with a Subscription CLI badge, and selecting it warns that it bills the signed-in Claude subscription through `claude -p` and never uses an API key (use `anthropic-apikey` for key billing). | | **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, enable or disable quota auto-switch, set its 1–100% threshold, and configure transient-failure failover. | -| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. | +| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. The **Workflow limits for subagents** panel sets how many turns one task may run in parallel and how many model requests and distinct subagents it may use per 10-minute window. See [workflow limits](/reference/configuration/server/#workflow-limits). | | **Models** | Toggle native GPT and routed models, set provider allowlists and context caps, choose v1/base/v2, and configure the v2 thread limit. The page distinguishes a catalog saved on the hub, a catalog fetched by this client, and activation in a running client. A fetch timestamp does not prove it includes the latest hub save, and runtime activation is shown as unverified. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. | | **Logs** | Auto-refresh recent requests with tokens, requested effort and (when available) effective outbound effort, resolved model, provider, status, request id, duration, and error details. The detail view includes the exact reasoning wire field when the adapter emits one. Filter by opaque conversation/session id (when the client sends one) to total tokens and estimated list-price cost for the currently loaded Logs ring. | | **Usage / Debug** | Inspect token-usage coverage and trends. The Usage page's Models table also breaks each model down into input tokens, output tokens, cache hits, cache writes, and cache hit rate; a dash means cache telemetry for that metric is unavailable. Or enable opt-in provider transport and usage-extraction diagnostics. | @@ -154,6 +154,8 @@ timer. Requests already sent upstream retain their original credentials. ### Filtering request logs +Request details prefer a recognized upstream failure cause over generic proxy-error copy. Physical sends are always shown when valid; missing or invalid accounting shows **Unavailable**, not zero. **Retries** counts sends after the first only when one fully settled, consistent attempt is recorded. Multi-attempt, unresolved, inconsistent, or lossy-normalized records omit retries instead of inventing them. Unknown persisted causes use the status-based headline without raw attribution. + A proxy replay refusal keeps HTTP 429 and error code `upstream_reset_replay_refused`, but its request and attempt failure cause is `transport-ambiguous`, with resend permission `refused-ambiguous`: the upstream may already have processed the turn. A genuine provider @@ -487,7 +489,7 @@ Usage heatmap days have one Tab entry point. Use Up/Down for adjacent days and L shows **No changes** or **Unsaved changes**, with **Revert** to discard edits and **Save** to commit them. Both switches apply immediately; **Save** never changes them. When interception is stopped, the page shows why and offers **Start interception**, which starts it in place without restarting OpenCodex. The former Code, Settings, and Account sub-tabs are gone; everything the read-only Settings view showed except the intercept port number is on this page. -**Claude Desktop** is its own Connect tab at `#claude/desktop`. It contains the Desktop mode picker and model families. One status row shows whether Claude Desktop runs the profile and whether it is saved, with **Save** and **Save & apply** beside it. +**Claude Desktop** is its own Connect tab at `#claude/desktop`. It contains the Desktop mode picker, a per-model list of what Claude Desktop shows (see [Choose which models Claude Desktop lists](/guides/claude-code/#choose-which-models-claude-desktop-lists)), and model families under **Advanced**. One status row shows whether Claude Desktop runs the profile, with a separate chip for whether dashboard edits are saved, and **Save** and **Save & apply** beside it. Claude/Anthropic accounts are managed only on **Providers > Anthropic > Accounts**: login and the browser option, the account roster with switch, pause, remove, and reauthentication, the paste-code field, account pool settings, and quota. Claude no longer has an Account sub-tab. Provider-level controls such as the default provider, removal, and the enabled switch stay on Providers. @@ -519,3 +521,23 @@ test for the same provider returns 409. Completed diagnostics and applicability results return 200, including a structured error when inference fails. Destination policy and provider egress settings apply; redirects, retries and failover are disabled. For the CLI workflow, see [Providers and accounts](/reference/cli/providers-accounts/). + +**Test all models** runs the same completion test for every configured, listed and +default model of the provider, one request at a time, after a single confirmation that +states the maximum number of requests; some models can be skipped before sending. The +single-model **Test model** control and **Test all models** share one guard per provider, so +each stays disabled while the other is confirming or running. **Stop** cancels the in-flight request and sends no +more. Each model is reported as passed, failed (the upstream answered with an error or +timed out) or not tested. The latter covers management API refusals such as a concurrent +test, admission refusals before sending (missing or changed credentials or a refused +destination), and upstream HTTP 401/403/429 authentication, access or quota refusals +returned in a structured result with HTTP 200. None of these categories becomes a disable target. An unsupported +effective adapter marks only that model as not tested and continues, because one provider +can use different wires for different models. Provider-wide applicability refusals, such as +OAuth authentication or unsupported transport settings, stop the run. When any model failed, **Disable failed models** asks a second +confirmation and then removes only those models from the model catalog and picker through the +same visibility setting as the **Models** page, where they can be re-enabled. Like any hidden +model, they stay callable by exact id. Models are never disabled +without that confirmation. Leaving the page or changing the provider connection withdraws +pending disable consent and aborts its request. A new batch waits until disable consent or +the visibility write has settled, so an older response cannot hide the newer batch's action. diff --git a/docs-site/src/content/docs/ja/guides/claude-code.md b/docs-site/src/content/docs/ja/guides/claude-code.md index 68415977f04..f07c99ff38e 100644 --- a/docs-site/src/content/docs/ja/guides/claude-code.md +++ b/docs-site/src/content/docs/ja/guides/claude-code.md @@ -537,7 +537,7 @@ Messages → Responses に変換されるリクエストでは、Claude Code の | Assistant `tool_use` | `function_call`(`input` → JSON 文字列に変換した `arguments`) | | ユーザー `tool_result` | `function_call_output`(`is_error` → `[tool error]` 接頭辞) | | `thinking` / `redacted_thinking` 再生 | シグネチャと秘匿ペイロードを境界付き `ocxr1` エンベロープに保持した `reasoning` 項目 | -| Function ツール | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). `api.anthropic.com` への翻訳ルートでは、型付き `web_search_*` と `web_fetch_*` 宣言も Anthropic に送信され、サーバー側で実行されます。ただし Web 検索サイドカーが `web_search` を提供する場合はそちらが優先されます。検索と取得の結果は `server_tool_use` と対応する `web_search_tool_result` / `web_fetch_tool_result` としてクライアントに返されます | +| Function ツール | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). `api.anthropic.com` への翻訳ルートでは、型付き `web_search_*` と `web_fetch_*` 宣言も Anthropic に送信され、サーバー側で実行されます。ただし Web 検索サイドカーが `web_search` を提供する場合はそちらが優先されます。検索と取得の結果は `server_tool_use` と対応する `web_search_tool_result` / `web_fetch_tool_result` としてクライアントに返されます `allowed_domains`、`blocked_domains`、`user_location` を指定したホスト検索は翻訳された Responses/Chat ルートから除外され、強制すると 400 を返します。有効な `defer_loading` 関数定義は通常の関数として呼び出せます。[翻訳の制限](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes)を参照してください。 | | `tool_choice` | `auto`→`auto`、`none`→`none`、`any`→`required`、名前指定関数→`{type:"function",name}`、ホスト型 WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/ja/guides/codex-app-models.md b/docs-site/src/content/docs/ja/guides/codex-app-models.md index ef99c820f3a..daa8fc7eabf 100644 --- a/docs-site/src/content/docs/ja/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ja/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Codex App モデル ピッカー description: opencodex モデルが、共有 Codex カタログを通じて Codex App、Codex CLI、Codex TUI にどのように表示されるか。 --- +既定値は `false` です。`config.json` に `"omitDisabledNativeModels": true` を設定して `ocx sync` を実行すると、無効な native 行を catalog、cache、Codex 形式の HTTP モデル一覧から除外します。除外前に検証した継承テンプレートを保持するため routed 行のメタデータは変わりません。直接リクエストのルーティングも変わりません。以下の hide の説明は既定の設定に適用されます。 + opencodex は Codex アプリにパッチを適用しません。Codex CLI/TUI と同じ Codex 設定とモデル カタログを書き込みます。app-server はその共有状態を読み取りますが、一部の Codex Desktop リリースは renderer 側で追加の remote allowlist を適用し、routed row を picker から除外する diff --git a/docs-site/src/content/docs/ja/guides/combos.md b/docs-site/src/content/docs/ja/guides/combos.md index 70c327e76d1..943ccef2bc0 100644 --- a/docs-site/src/content/docs/ja/guides/combos.md +++ b/docs-site/src/content/docs/ja/guides/combos.md @@ -124,7 +124,8 @@ ocx combo set balanced \ | HTTP 401、403、404、408、429、または任意の 5xx |ターゲットを冷却し、次の適格なターゲットに移動します。 | |モデルのサポート終了、retired、deprecated、sunset、decommissioned、または利用不可を明示する HTTP 410 |そのターゲットをクールダウンし、次へ進みます。無関係な 410 はターミナル エラーのままです。 | |機密認証、サブスクリプション、クォータ、レート制限、過負荷、またはアップストリーム サーバー エラー |ステータスだけでは物足りない場合でもターゲットを冷やしてホップさせましょう。 | -|クライアントのキャンセル (499)、`origin_rejected`、サイバー ポリシーの拒否、コンテキスト オーバーフロー、またはその他の無効なリクエスト |停止してエラーを返します。別のターゲットではリクエストは有効になりません。 | +|クライアントのキャンセル (499)、`origin_rejected`、サイバー ポリシーの拒否、またはその他の無効なリクエスト |停止してエラーを返します。別のターゲットではリクエストは有効になりません。 | +| 出力前に確認したコンテキスト超過(HTTP 400 を含む) | クールダウンせず次の未試行の適格な対象へ進み、使い切った場合は最初に確認した HTTP 400 のコンテキストエラーを返します(他のステータスのコンテキスト拒否も次へ進みますが、保持されません)。 | | ChatGPT アカウントで Codex を使う際に、そのモデルをサポートしないことを示す正確な HTTP 400 の拒否 | 出力を確定する前に、次の設定済みターゲットを試します。拒否文全体が `detail`、`error.message`、または本文そのものにある場合に限ります。`detail` と `error` が競合する応答は終了扱いです。この拒否だけでアカウントを再認証待ちにしたり、プロバイダーの全モデルをクールダウンしたりしません。 | |`user` の明示的な拒否、`reasoning.effort`/`reasoning_effort` の非対応値、モデル固有の画像入力拒否(`param: input`)、またはルートが読み取れない入力メディアの拒否(`unsupported_input_modality`)を示す構造化 HTTP 400 |出力開始前に次の適格なターゲットへ進み、クールダウンを記録しません。任意パラメーターの互換性を参照してください。 | |インプロセスアダプター(`runTurn`)が実行する Responses ターンで、現在のリクエストが宣言していない最初のツール呼び出し(出力やリプレイ不可の副作用より前) |ターゲットをクールダウンし、同じツールカタログで次へ進みます。可視出力やリプレイ不可の副作用の後は拒否が確定します。Chat Completions と Anthropic Messages のリクエストは変わりません。 | @@ -134,6 +135,8 @@ ocx combo set balanced \ 現在のリクエストは、同じ試行ターゲットを再試行しません。ただし例外が1つあります。`waitForCooldownMs` を設定した単一ターゲット コンボは、フェイルオーバー先の代替がないため、同じリクエスト内でそのターゲットのクールダウンが期限切れになれば、唯一のターゲットを再試行できます。リクエストローカルの互換性拒否は、引き続き再試行せずに返されます。後続のリクエストでは、クールダウンが期限切れになるまでそのターゲットをスキップします。すでに過去の時刻である `Retry-After` の HTTP-date も、`Retry-After: 0` と同様に即時のアップストリーム指示として維持されます。`waitForCooldownMs` を設定すると、後続のリクエストは、最も早く利用可能になるターゲットのクールダウンを、その選択試行ごとにこの上限まで待ってから、新たに 1 回選択できます。したがって、複数のフェイルオーバー ホップをまたぐリクエストは合計で `hops × waitForCooldownMs` まで待つ場合があります。デフォルトは `0` です。適格なターゲットがすべて冷却中の場合、HTTP 503 ですぐにフェイルクローズし、その `combo_unavailable` 503 には最も早く終了する残りのクールダウンと等しい `Retry-After` ヘッダーが含まれ、秒単位に切り上げられ、最小値は 1 秒です。待機にはジッターがないため、同期したウェイクアップが発生する可能性があります。中止されたリクエストはこの待機をキャンセルし、通常の `client_cancelled` 応答を返します。キャンセル後にバックアップ ターゲットをディスパッチすることはありません。コンボ ターゲットのクールダウンはプロセス ローカルなコンボごとの状態であり、ネイティブ アカウント ルーティングで使用されるアカウントレベルの Codex クォータ クールダウンとは別です。 +上記の HTTP 503 は、まだ対象を試行していない場合に適用されます。確認済みの HTTP 400 コンテキスト拒否の後に適格な対象または送信予算を使い切った場合、最初の拒否を `context_length_exceeded` として返し、クォータを含む後続の再試行可能なエラーより優先します。残りの対象が推論を再生できない場合や従量課金への同意がない場合も同様です。キャンセル、後続の致命的な拒否、明示的なフォールバックポリシーの拒否は直ちに停止します。同意のない従量課金対象へは送信しません。 + :::note フェイルオーバーは意図的に制限されています。これは、ターゲット固有の可用性、認証、クォータ、および過負荷の障害に役立ちます。呼び出し元のエラーやポリシーの拒否は隠蔽されません。 コンボではない Responses リクエストでは、allowlist された xAI ポリシー 403 が Codex の転送失敗再試行の前に HTTP 200 `incomplete/content_filter` へ書き換えられます。[xAI policy refusals](/reference/proxy-formats/#xai-policy-refusals) を参照してください。コンボのホップは元の HTTP 403 をホップとして分類します。 @@ -266,7 +269,7 @@ ocx combo remove --yes ### 最初のエラーの後にフェイルオーバーが停止したのはなぜですか? -このエラーはターゲット固有のものではなく、最終的なものでした。無効な入力を修正し、大きすぎるコンテキストを削減し、ポリシーの拒否を処理し、拒否されたリクエストの送信元を修正します。コンボはそのような場合には機能しません。 +このエラーはターゲット固有のものではなく、最終的なものでした。無効な入力を修正し、ポリシーの拒否を処理し、拒否されたリクエストの送信元を修正します。コンボはそのような場合には機能しません。 ## 任意パラメーターの互換性 diff --git a/docs-site/src/content/docs/ja/guides/integrations.md b/docs-site/src/content/docs/ja/guides/integrations.md index 8017713dde2..78a542c29b8 100644 --- a/docs-site/src/content/docs/ja/guides/integrations.md +++ b/docs-site/src/content/docs/ja/guides/integrations.md @@ -223,6 +223,16 @@ Undo は、もともと存在しなかったファイルも含め、**元の両 ダウンロードされる `cline-config-bundle.json` には、`providers.json` 用の `settings` と `models.json` 用の `catalog` という 2 つのネイティブ文書要素が含まれます。それ自体は Cline の設定ファイルではありません。ジャーナル付きのマージとロールバックには統合コマンドを使ってください。生成された統合はリモートの受け入れ認証に対応せず、認証不要のループバックアクセスが必要です。 +## omo (Pi ベース) の Responses 方言 + +管理対象の Pi ベース omo ブロックは、既定で Chat 方言 (`api: "openai-completions"`) を使います。Chat はツール呼び出しをまたいで前のステップの推論を落とすため、長い omo のツールループで影響が出ます。有効化とすべての更新で Responses 方言を書き込むには、opencodex の設定ファイルにだけ設定できる次のキーを追加します。 + +```json +{ "omoClientExport": { "api": "openai-responses" } } +``` + +編集した設定を読み込むためにプロキシを再起動し (サービスとしてインストールした場合は `ocx service restart`、それ以外は `ocx stop` の後に `ocx start`)、ダッシュボードの **Connect → omo** から omo 統合を更新してください。更新だけでは実行中のプロキシの設定を使い、設定ファイルは読み直しません。この設定は omo にだけ適用され、Pi、Gentle Shell などほかの Pi 形式のクライアントは `openai-completions` のままです。Chat に戻すには、キーを削除して同じ手順で再起動し、**Connect → omo** を更新します。不明な値は無視されます。opencodex が所有するのは引き続き `~/.omo/agent/models.json` の `providers.opencodex` だけで、ほかのプロバイダーはそのまま残ります。 + ## omo (Codex / LazyCodex) のロール別推論負荷 LazyCodex が検出されると、omo タブの **omo (Codex / LazyCodex)** セクションで、`$CODEX_HOME/agents/*.toml` にある各ロールのモデルと推論負荷を選んで **保存** できます。モデルが固定されていないロールの行では推論負荷だけを保存でき、そのロールは引き続きモデルを継承します。opencodex はそのロールファイルのルートにある `model` と `model_reasoning_effort` のキーだけを更新し、存在しない固定値はファイルの先頭付近に追加します。ロールの指示、ほかのコメントやキーはそのまま残ります。モデルだけを変えて保存すると `effort` を送らないため、両方のファイルの推論負荷は変わりません。段階を選ぶとロールファイルの `model_reasoning_effort` を追加または置き換え、LazyCodex がその段階に対応していれば `~/.omo/omo.jsonc` の `[codex].agents..reasoning` にも書き込みます(`ultra` はロールファイルにだけ残り、以前の `reasoning` は削除されます)。コマンドラインでは `ocx agent roles set --effort ` で同じ操作ができます。 diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index 8f6283a5c10..e94d4842f7c 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -165,7 +165,7 @@ opt-in した上流がこのフィールドを拒否しても、opencodex はフ 認証情報に固定アカウント ID やメールがある OAuth プロバイダーはログインを複数保持できます。 Providers ページでアカウントを追加し、別アカウントをログアウトせずにアクティブアカウントだけを切り替えられます。 -アカウント識別情報がない Kimi 認証情報は通常のログインではアクティブスロットを差し替えますが、明示的な **アカウントを追加** では既存スロットを保持し、別の新しいスロットをアクティブにします。Kiro アカウントはプロファイル ARN をキーに保存されます。 +アカウント識別情報がない Kimi 認証情報は通常のログインではアクティブスロットを差し替えますが、明示的な **アカウントを追加** では既存スロットを保持し、別の新しいスロットをアクティブにします。Kiro アカウントはプロファイル ARN と、取得できる場合はサインインしたユーザーのメールアドレスを組み合わせて識別されます。同じ組織プロファイルを共有するユーザーも、それぞれ別の認証情報を保持できます。 `chatgpt` は Codex アカウントプールに別の保存場所があり、常に単一スロットのみ書き込みます。トークンは `~/.opencodex/auth.json` に保存され、 `/api/oauth/accounts` はマスク済みメタデータのみを返します。 @@ -184,6 +184,8 @@ cat accounts.json | ocx account import google-antigravity --format cockpit-tools ### Kiro 認証情報の取り込み +ダッシュボードでは **AWS IAM Identity Center** を選び、組織の AWS アクセスポータル URL と SSO リージョンを入力すると、CLI や SSH を使わずにアカウントを追加できます。ブラウザーでデバイスコードを承認し、複数の Kiro プロファイルがある場合は保存するものを選択します。以下の CLI 手順は **Kiro CLI** の選択肢に適用されます。 + Kiro のログインには Kiro CLI が必要です。Unix では `curl -fsSL https://cli.kiro.dev/install | bash`、Windows PowerShell では `irm 'https://cli.kiro.dev/install.ps1' | iex` でインストールしてから、先に `kiro-cli login` でサインインしてください。`kiro-cli` セッションがない場合、`ocx login kiro` は貼り付けたアクセストークンまたは `KIRO_ACCESS_TOKEN` 環境変数にフォールバックします。 通常の `ocx login kiro` 取り込みは CLI の SQLite データベースを読み取り専用で開き、データベース、WAL、SHM を変更しません。 @@ -256,7 +258,7 @@ Cline IDE/CLI のみで API からは使えません。`minimax/minimax-m2.5` | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`。`moonshot` API の既定モデルは `kimi-k3.1-pro` です。公式 API の保存済み `kimi-k2.5` 選択はこのモデルへ移行します。 | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/ja/guides/web-dashboard.md b/docs-site/src/content/docs/ja/guides/web-dashboard.md index b45983256c2..eb8c7522c07 100644 --- a/docs-site/src/content/docs/ja/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ja/guides/web-dashboard.md @@ -70,6 +70,8 @@ bun run dev:gui ### リクエストログの絞り込み +リクエスト詳細では、一般的なプロキシエラーの文言よりも、記録されたアップストリームの失敗原因を優先して表示します。物理的なアップストリーム送信数は有効な場合に常に表示し、会計情報がない、または無効な場合は 0 ではなく **Unavailable** と表示します。**Retries** は、完全に精算され整合する単一の試行が記録されている場合に限り、最初の送信以降の追加送信回数として表示します。複数の試行、未解決・不整合な会計、または損失のある attempt 正規化では、再試行回数を推測せず非表示にします。未知の永続化された原因は、状態に基づく見出しに戻ります。 + Logsではサーフェス、インターセプトされたリクエスト、プロバイダー、完全なモデル名、ステータス、時間、速度、会話IDを組み合わせて、読み込み済みログを絞り込みます。選択肢にはフォールバック試行も含まれます。モデル名は大文字小文字と前後の空白を無視しますが、部分一致ではありません。ログから消えた選択肢は全件に戻ります。 時間は直近15分・1時間・1日で、Logsタブでは自動更新をオフにしても30秒ごとに更新します。速度はリクエスト全体の時間あたりの毎秒出力トークン数で、15未満、15以上50未満、50以上です。速度フィルター中は測定不能な行を除外します。成功は2xx、エラーは4xx/5xxです。 diff --git a/docs-site/src/content/docs/ja/reference/adapters.md b/docs-site/src/content/docs/ja/reference/adapters.md index b817283250d..08cd6d05489 100644 --- a/docs-site/src/content/docs/ja/reference/adapters.md +++ b/docs-site/src/content/docs/ja/reference/adapters.md @@ -111,7 +111,7 @@ HTTP リトライ ループの対象外です。 - メッセージを Anthropic content block(text、base64 image、`tool_use`、`thinking`)に変換します。 - **Extended thinking の計算:** Anthropic は `max_tokens > thinking.budget_tokens` を要求します。 アダプターは reasoning effort を budget にマッピングし(minimal 1024 … max 32000)、出力余裕を取った安全な `max_tokens` を計算します。thinking がオンのときは Anthropic が禁止する **`temperature`/`top_p` を削除**します。 -- **adaptive thinking の表示:** adaptive thinking モデル(Opus 4.7+、Sonnet 5、Fable)には `thinking.display: "summarized"` を送るため、長い思考は数分間の heartbeat ではなく reasoning delta として Chat と Responses のクライアントに届きます。reasoning の要約を隠すリクエスト(`reasoning.summary: "none"`)はプロバイダーの既定値を維持します。 +- **adaptive thinking の表示:** adaptive thinking モデル(Opus 4.7+、Sonnet 5、Fable)には `thinking.display: "summarized"` を送るため、長い思考は数分間の heartbeat ではなく reasoning delta として Chat と Responses のクライアントに届きます。reasoning の要約を隠すリクエスト(`reasoning.summary: "none"`)はプロバイダーの既定値を維持します。ただし、`api.anthropic.com` の Opus 5.5 は `display: "updates"` が既定で、`reasoning.summary` に `"concise"` または `"detailed"` を明示すると updates ベータなしで `"summarized"` を使用します。[Opus 5.5 の進捗更新](/guides/providers/#claude-opus-55-progress-updates)を参照してください。 - 常に `anthropic-version: 2023-06-01` を送ります。`content_block_delta`(`text_delta`、 `thinking_delta`、`input_json_delta`)をストリーミングします。 diff --git a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md index 7e2a2c06150..b199c822ffc 100644 --- a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md @@ -24,6 +24,7 @@ description: プロバイダー構成、資格情報、クォータ、および | `quota` | `--refresh`、`--json` |プロバイダー クォータ レポートを読み取ります。 | | `presets` | `--json` |ダッシュボードプロバイダーのプリセットを一覧表示します。 | | `account-mode` | `pool`、`direct`、`--json` |プールされた Codex アカウント ルーティングまたは直接の Codex アカウント ルーティングを選択します。 | +| `import-cc-switch` | `--source `、`--provider `、`--apply`、`--restore`、`--allow-private-network`、`--json` | CC-Switch の SQLite(`cc-switch.db`)または JSON ファイルからキーなしのプロバイダー項目をプレビュー・選択適用・復元します。 | 既定の `add`、`remove`、`set-default` はローカル設定を変更します。稼働中のプロキシを変更するには `--live`、ライブ削除にはさらに `--yes` が必要です。`--sync --json` も保存後に同期を試み、失敗時は保存を保持して非ゼロ終了と `needsSync: true` を返します。`--live` と `--sync` は併用できません。pacing と snapshot/apply の手順は[英語版](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline)を参照してください。 @@ -57,6 +58,27 @@ ocx models live --provider ark --json 秘密にあたる値は `--api-key` か OAuth ログインを使ってください。 ::: +### キーなしの CC-Switch プロバイダーインポート + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# 別のターミナルでプロキシを起動(または ocx ensure を実行)してからキーを登録: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# ロールバック用の別経路(先にプロキシを停止;未編集のキーなし項目のみ削除): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` は通常の CC-Switch SQLite データベースまたはレガシー JSON ファイルから、非秘密メタデータフィールドの明示的な許可リスト(プロバイダー ID、アプリ種別、ベース URL、アダプター、既定モデル)の値**のみ**を射影します。入力を走査するためソースと埋め込み設定のバイト列を一時的に読み取りますが、秘密フィールド・自由記述の表示名・TOML コメントはバイト単位でスキップされ、文字列へのデコード・書き込み・ログ出力は一切行われず、一時バイトバッファは `finally` でゼロ消去されます。ベース URL パスの厳格な規則(`http(s)://host[:port]` に空パスまたは `/v1`・`/api/v1`・`/compatible-mode/v1` などの小文字 `[a-z0-9._-]` による 1〜4 個の API パスセグメント)と識別子/モデルの構文検査は多層防御として機能し、不適合な値をエコーせずに拒否します。ソースファイルは `O_NOFOLLOW | O_NONBLOCK` の記述子検証付きで一度だけ開かれ、SQLite は一時ファイルを作らずメモリ上で読み取り専用展開されます。複数ハードリンク(`nlink > 1`)を持つ SQLite ファイルは別名パスによるサイドカー検査回避を防ぐため拒否され、隣接する `-wal` または `-journal` ファイルが空でない場合は CC-Switch を終了(またはデータベースをチェックポイント)して再試行するよう求めて拒否します。信頼できるローカルの CC-Switch ソースのみを使用し、出力されたすべての ID・モデル・`baseUrl` を確認してから資格情報を登録してください: + +- **プレビュー(既定):** `--source ` が必要で、何も書き込まず、検出・対象・競合・無効の件数を報告します。 +- **適用(`--apply`):** `--source ` とプロキシの停止(`ocx stop`)が必要で、Remote Link Child では拒否されます。`--provider ` を繰り返して対象を絞り込むか、ループバックやプライベートネットワークのベース URL には `--allow-private-network` を指定します。事前のプレビューなしでも、`--apply` はキーなしのプロバイダー項目とロールバックジャーナル `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` を書き込む前にすべてのプロバイダー ID・既定モデル・接続先 URL を出力します(テキストモードでは `stdout`、`--json` モードでは `stderr`)。アクティブなジャーナルが存在する間、2 回目の `--apply` は拒否されます。出力内容を確認したらプロキシを起動(別ターミナルで `ocx start` または `ocx ensure`)し、`printf '%s\n' "$API_KEY" | ocx account add-key `(パイプ標準入力)または `ocx login ` で資格情報を追加します。 +- **復元(`--restore`):** 資格情報登録の続きではなく独立したロールバック経路であり、プロキシの停止(`ocx stop`)が必要で、Remote Link Child では拒否され、`--source`・`--provider`・`--allow-private-network` は受け付けません。`auth.json` をバイト単位で読み取り専用検査(読み取り不能または不正形式時はフェイルクローズ)し、インポート時のキーなし状態と一致したままで、かつ `defaultProvider` に選択されておらず、ルーティング・コンボ(combos)・フォールバックポリシー(fallback policy)・OAuth アカウント・API キーのいずれからも参照されていないプロバイダーのみをアクティブなジャーナルから削除します。 + ## 認証 ### `ocx login ` diff --git a/docs-site/src/content/docs/ja/reference/configuration/advisor.md b/docs-site/src/content/docs/ja/reference/configuration/advisor.md index d78a179f231..4a5615b8c10 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/ja/reference/configuration/advisor.md @@ -60,6 +60,10 @@ description: manual、preflight、証拠に基づく adaptive ポリシーによ 設定されたアドバイザーのプロバイダーは、ワーカーのプロバイダーと異なる場合があります。 +相談は、呼び出し元の取得済み API キーのモデル範囲・有料 combo への同意・デバイス別 fallback 制限と、Advisor 自身の受付キーのホスト制限の両方に従います。受付キーは、設定されていれば `OPENCODEX_API_AUTH_TOKEN` またはこのプロキシが受け付けるサービストークンファイル、なければ最初に設定された API キーです。キーの順序が意味を持つのはこの最後の場合だけです。Advisor モデルに届くキーを先頭に置かないと、すべての相談が利用できません。環境トークンまたはサービストークンが設定されているときは、そのトークンが相談を決めます。これらの規則は combo の選択、入れ子の判定モデル、再試行にも適用されます。満たせない相談は worker に利用不可の通知を返します。 + +たとえば `allowMeteredComboTargets: false` の `laptop` キーは Advisor 経由で従量課金の combo ターゲットに費用を使えず、`fallback.devices.laptop.provider: false` は Advisor のプロバイダー fallback ホップも止めます。キーのない loopback 呼び出し元には、引き続き combo の `allowMeteredTargets: true` のオプトインが必要です。両方のキーが `allowMeteredComboTargets: true` を設定し、combo が `allowMeteredTargets: true` を設定していれば、公開バインドの loopback リスナー経由でも Advisor は従量課金ターゲットを使えます。サーバーは本文の容量待ちや本文の読み取りより前、リクエストを認証した時点で呼び出し元のポリシーを取得します。その後のキーや fallback の編集でその相談の範囲が広がることはありません。これはルーティングされた画像補助、compact リクエストとその判定モデル、WebSocket ターン内のすべての候補再試行にも適用されます。新しい WebSocket ターンはそれぞれ自分のポリシーを取得します。 + OpenCodex はプロバイダー API キー、Authorization ヘッダー、OAuth トークン、バックエンド専用の設定秘密、プロセス環境、隠された思考連鎖をそのプロンプトへ入れません。暗号化されたプロバイダー私有の推論を復号して転送することもしません。**タスク内容は一般には秘密除去されません。** タスクへ貼られた鍵、ツールが読んだファイル内の秘密、ツールやログが出力したトークンは送られることがあります。OpenCodex は汎用の DLP を実行しません。 ## 権威 diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index 4ed880176bd..7ecffe3764a 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -43,6 +43,7 @@ GUI で登録または OAuth ログインが完了すると、Models ページ | `providers` | `Record` | — |プロバイダー名からプロバイダー設定へのマップ。 | | `openaiProviderTierVersion?` | `2` |移行によって設定される |単一のオプション対応 OpenAI プロジェクションを完了としてマークします。 | | `disabledModels?` | `string[]` | — | Codex catalog と `/v1/models` から非表示にする model。直接の proxy 呼び出しはブロックしません。routed id は一覧から削除されます。account-qualified native id は該当する selector row だけを非表示にし、bare native GPT id は bare row とその model の全 account-selector row を非表示にします。Models ページに表示されるのは bare native 行と routed 行だけです。selector-qualified 行を 1 つだけ非表示にするには、この設定フィールドを直接編集してください。 | +| `omitDisabledNativeModels?` | `boolean` | `false` | 既定値は `false` です。`config.json` に `"omitDisabledNativeModels": true` を設定して `ocx sync` を実行すると、無効な native 行を catalog、cache、Codex 形式の HTTP モデル一覧から除外します。除外前に検証した継承テンプレートを保持するため routed 行のメタデータは変わりません。直接リクエストのルーティングも変わりません。 | | `providerContextCaps?` | `Record` | `{}` | プロバイダーごとの有効なコンテキスト上限。通常のウィンドウは縮小されます。長いウィンドウに対応したネイティブモデルは、そのモデルが対応する上限まで拡張できます。 | | `providerContextCapValues?` | `Record` | `{}` | プロバイダーごとに最後に選択した上限。無効にしても保持され、この値だけで上限が有効になることはありません。有効な値が保存済みの値より優先されます。 | | `contextCapValue?` | `number` | `350000` | 初回の有効化で使う既定値。再び有効にすると、そのプロバイダーの選択値を復元します。`setAll: true` とともにグローバル値を変更すると、有効な上限だけを更新します。値を指定せずに `setAll: true` を送ると、設定済みの全プロバイダーの上限を現在のグローバル値で有効にします。 | @@ -117,7 +118,7 @@ account を削除しても mapping は保持され、同じ id を再追加す | `models?` | `string[]` | 初期/フォールバックモデル一覧。`liveModels: false` で `models` が空でなければ、その後に `retainModels` を追加します。`models` が空または省略されている場合は、設定済みの `defaultModel`、`retainModels` の順に初期一覧を作り、重複 ID は最初の出現だけを残します。 | | `liveModels?` | `boolean` |開始/同期時にライブ カタログをフェッチします (デフォルトは `true`)。カスタムプロバイダーは `${baseUrl}/models` を使用します。組み込みはレジストリ URL とフィルターを使用する場合があります。 | | `selectedModels?` | `string[]` |検出後のカタログ許可リスト。空でない場合は、それらの ID のみが公開されます。空または省略すると、検出されたすべてのモデルが公開されます。 | -| `modelDisplayNames?` | `Record` | このプロバイダーの正確なネイティブモデル ID をキーにした、永続的な表示専用ラベルです。大文字と小文字は区別されます。ラベルはプロバイダーカタログのメタデータより優先され、認証、アダプター、ルーティング、課金、上流リクエストには影響しません。マップは検出上限と同じ 2,000 件までです。 | +| `modelDisplayNames?` | `Record` | このプロバイダーの正確なネイティブモデル ID をキーにした、永続的な表示専用ラベルです。大文字と小文字は区別されます。ラベルはプロバイダーカタログのメタデータより優先され、認証、アダプター、ルーティング、課金、上流リクエストには影響しません。例外として、直接の `provider/model` セレクターが既知の ID、エイリアス、`retainModels` のいずれにも一致しない場合(ライブ一覧がまだ取得されていない間など)、エンコード形が一致するキーをネイティブ ID として送信します。マップは検出上限と同じ 2,000 件までです。 | | `contextWindow?` | `number` | アップストリームのメタデータが無い場合に使うプロバイダー全体のコンテキスト値。メタデータがある場合は上限として働き、より小さいライブ値をそのまま残します。Models ダッシュボードでは `providerContextCaps` とは別に設定します。 | | `modelContextWindows?` | `Record` | モデルごとのコンテキスト値および上限。`contextWindow` より優先され、ウィンドウが不明なら設定値を使い、より小さいライブメタデータがあればそちらが優先されます。 | | `inferContextWindow?` | `boolean` | カスタムプロバイダーのみ、既定は無効。`true` で欠けたウィンドウを同梱モデルメタデータから補完します。推定値であり、中継の容量を検証したものではありません。 | @@ -153,7 +154,7 @@ account を削除しても mapping は保持され、同じ id を再追加す | `noPenaltyModels?` | `string[]` |存在/周波数ペナルティを拒否するモデル。 組み込みの`xai`プリセットは、xAIがこれらを拒否すると文書化している推論モデル(`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`)をここに登録します。非推論モデルは呼び出し元のペナルティをそのまま受け取ります。 | | `noStructuredOutputModels?` | `string[]` | `openai-chat` エンドポイントが `response_format` を拒否する正確なモデル ID。要求モデルが項目と完全一致する場合だけフィールドを省略し、その他の `openai-chat` モデルでは structured-output 変換を維持します。 | | `noJsonSchemaModels?` | `string[]` | `openai-chat` エンドポイントが `json_schema` 形式は拒否しつつ `json_object` は受け入れる正確なモデル ID。この要求はフィールドを削除せず `json_object` に降格して送るため、JSON を求めた呼び出し側は散文ではなく JSON を受け取れます。両方の一覧に載るモデルでは `noStructuredOutputModels` が優先します。`opencode go` / `opencode zen` プリセットが DeepSeek 経路に既定で載せます。 | -| `foldDeveloperRoleToSystem?` | `boolean` | `openai-chat` の宛先が `developer` ロールを受け付けるかを記録します。`foldDeveloperRoleToSystem` が未設定なら `system`、`true` なら `system`、`false` なら `developer` として送ります。未設定はこの宛先について何も記録されていないことを意味し、`true` は上流がロールを拒否する記録、`false` は受け付ける記録です。いずれの場合もメッセージは会話内の位置を保ち、変わるのはロールだけです。ロールを拒否する宛先は `400 role 'developer' is not allowed` を返してターンが始まらないため、未記録の既定は畳む側にしてあります。 | +| `foldDeveloperRoleToSystem?` | `boolean` | `openai-chat` の宛先が `developer` ロールを受け付けるかを記録します。`foldDeveloperRoleToSystem` が未設定なら `system`、`true` なら `system`、`false` なら `developer` として送ります。未設定はこの宛先について何も記録されていないことを意味し、`true` は上流がロールを拒否する記録、`false` は受け付ける記録です。`singleLeadingSystemMessage` が有効でない場合、メッセージは会話内の位置を保ち、変わるのはロールだけです。ロールを拒否する宛先は `400 role 'developer' is not allowed` を返してターンが始まらないため、未記録の既定は畳む側にしてあります。 | | `parallelToolCalls?` | `boolean` |並列ツール呼び出しを切り替えます。 OpenAI Chat はデフォルトでオンになっています。非チャット アダプターは明示的な `true` でのみアドバタイズします。 | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` |正確なプレースホルダー ID、欠落している端末 ID、および(`repairInvalidIds` で)正規の `msg_`/`rs_` 接頭辞を欠く message/reasoning ID に対するダウンストリーム SSE 修復はデフォルトで無効になっています。関数呼び出し ID は決して書き換えられません。組み込み DeepSeek は最後の 2 つをデフォルトで有効にします。 | | `responsesSnapshotRepair?` | `boolean` | デフォルトで無効のクライアント向け修復です。SSE と JSON の Responses ライフサイクルで欠落した status、output、ツールメタデータを補完し、raw 検査と永続化は変更しません。 | @@ -179,23 +180,26 @@ account を削除しても mapping は保持され、同じ id を再追加す | `unsafeAllowNativeLocalExec?` | `boolean` |カーソルのレガシー ブール値。新しいフィールドが設定されていない場合のみ、`nativeLocalExec: "on"` と同等です。 | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` |カーソルのローカル実行ポリシー。 `off` がデフォルトです。 `codex-sandbox` は現在、`off` と同様にフェールクローズされます。 | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + プロバイダーの登録・置換(`POST /api/providers`)では、メモリやファイルの設定を変更する前に `responsesPath` と `chatCompletionsPath` を検証します。`PATCH /api/providers?name=` はリクエスト本文を保存済みのプロバイダーにマージします。`disabled` 以外のフィールドを変更する更新(`requestPacing` のみの更新を除く)では、保存前にマージ後のプロバイダーのパスを同じ方法で検証し、保持されているパスが無効な場合は `400` を返して設定を変更しません。設定ファイルの読み込みにも同じ経路の規則が適用されます。 API キープロバイダーは、リテラルキーまたは環境参照を保持する場合があります。 OAuth プロバイダーは、`ocx login` によって設定された資格情報ストアを使用します。サブスクリプションに基づくクロード コードの起動動作は、[`claudeCode.authMode`](/ja/reference/configuration/server/#claude-code-claudecode) で構成されます。 ### プロバイダーの保存で保持されるもの -既存のプロバイダー名で `POST /api/providers` を送ると、保存済みの行はリクエストから組み立てた行で置き換えられます。ダッシュボードの追加・編集フォームはすべてのフィールドを送れないため、リクエストが省略した保存済みフィールドの一部は保存時に引き継がれます。そのうち次の 8 つは、特定のアップストリームの挙動を記録するものです:`preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 +既存のプロバイダー名で `POST /api/providers` を送ると、保存済みの行はリクエストから組み立てた行で置き換えられます。ダッシュボードの追加・編集フォームはすべてのフィールドを送れないため、リクエストが省略した保存済みフィールドの一部は保存時に引き継がれます。そのうち次の 11 つは、特定のアップストリームの挙動を記録するものです:`preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 -| 保存 | 8 つの設定 | 保存済みの `apiKeyPool` | +| 保存 | 11 つの設定 | 保存済みの `apiKeyPool` | | --- | --- | --- | | 同じ宛先、フィールド省略 | 保存済みの値を保持(明示的な `[]` や `false` を含む) | 保持 | | 新しい宛先、フィールド省略 | 保持しない。新しい宛先のレジストリ既定値が適用されることがあります | 保持しない | | リクエストでフィールドを送信 | リクエストの値 | リクエストの値 | -宛先とは、アダプター、ベース URL(スキームとホストは大文字小文字を区別せず、末尾のスラッシュは無視)、そしてリクエストが指定した場合は認証モードです。別の宛先へ移すと、以前のアップストリームを表す 8 つの設定と、そのために発行されたキーのプールは引き継がれません。保存時に古い行の残りを新しい行へマージすることはありません。 +宛先とは、アダプター、ベース URL(スキームとホストは大文字小文字を区別せず、末尾のスラッシュは無視)、そしてリクエストが指定した場合は認証モードです。別の宛先へ移すと、以前のアップストリームを表す 10 つの設定と、そのために発行されたキーのプールは引き継がれません。保存時に古い行の残りを新しい行へマージすることはありません。 -`PATCH /api/providers?name=` は指定したフィールドだけを変更し、宛先に関係なくほかの保存済みフィールドはすべて保持します。8 つの設定をすべて受け付け、`null` で消去します。2 つの推論リストでは、空の配列は削除されず明示的なオプトアウトとして保存されます。 +`PATCH /api/providers?name=` は指定したフィールドだけを変更し、宛先に関係なくほかの保存済みフィールドはすべて保持します。10 つの設定をすべて受け付け、`null` で消去します。2 つの推論リストでは、空の配列は削除されず明示的なオプトアウトとして保存されます。 ## プロバイダーによるアウトバウンドの安全性診断 diff --git a/docs-site/src/content/docs/ko/guides/claude-code.md b/docs-site/src/content/docs/ko/guides/claude-code.md index aa68c2db6c3..e6f0ebe5d2b 100644 --- a/docs-site/src/content/docs/ko/guides/claude-code.md +++ b/docs-site/src/content/docs/ko/guides/claude-code.md @@ -577,7 +577,7 @@ Messages → Responses로 변환되는 요청에서는 Claude Code의 `/effort` | 사용자 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 접두사) | | 도구 결과의 `tool_reference` | 기본적으로 짝지어진 결과에 `Tool loaded: ` 텍스트를 보존합니다. [append-only 옵션](/guides/claude-code/#preserve-cache-prefixes-when-client-tools-load)을 켠 적격 최종 Responses 경로에서는 성공한 클라이언트 검색 결과 위치에 현재 선언된 지연 함수의 정의를 배치할 수 있습니다. 참조만으로 도구 권한이나 서버 측 검색 기능을 추가하지는 않습니다. | | `thinking` / `redacted_thinking` 재생 | 서명과 비공개 페이로드를 제한된 `ocxr1` 봉투에 담은 `reasoning` 항목 | -| Function 도구 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). `api.anthropic.com`으로 번역하여 보내는 경로에서는 타입이 지정된 `web_search_*`와 `web_fetch_*` 선언도 Anthropic에 전달되어 서버에서 실행됩니다. 웹 검색 사이드카가 `web_search`를 제공하면 사이드카가 우선합니다. 검색과 가져오기 결과는 `server_tool_use` 및 대응하는 `web_search_tool_result` / `web_fetch_tool_result` 블록으로 클라이언트에 반환됩니다 | +| Function 도구 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). `api.anthropic.com`으로 번역하여 보내는 경로에서는 타입이 지정된 `web_search_*`와 `web_fetch_*` 선언도 Anthropic에 전달되어 서버에서 실행됩니다. 웹 검색 사이드카가 `web_search`를 제공하면 사이드카가 우선합니다. 검색과 가져오기 결과는 `server_tool_use` 및 대응하는 `web_search_tool_result` / `web_fetch_tool_result` 블록으로 클라이언트에 반환됩니다 `allowed_domains`, `blocked_domains`, `user_location`이 있으면 번역된 Responses/Chat 경로에서 호스팅 검색을 제외하며, 제외된 검색을 강제하면 400을 반환합니다. 유효한 `defer_loading` 함수 정의는 일반 함수로 호출할 수 있습니다. [번역 제한](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes)을 참고하세요. | | `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, 이름 지정 함수→`{type:"function",name}`, 호스팅 WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/ko/guides/codex-app-models.md b/docs-site/src/content/docs/ko/guides/codex-app-models.md index 40c716687da..f06017e0268 100644 --- a/docs-site/src/content/docs/ko/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ko/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Codex App 모델 선택기 description: 공유 Codex 카탈로그를 통해 opencodex 모델이 Codex App, Codex CLI, Codex TUI에 표시되는 방식. --- +기본값은 `false`입니다. `config.json`에 `"omitDisabledNativeModels": true`를 설정하고 `ocx sync`를 실행하면 비활성화한 native 행을 catalog와 cache, Codex 형식의 HTTP 모델 목록에서 제외합니다. 제외 전 검증한 상속 템플릿을 보존하므로 routed 행의 메타데이터는 유지되며 직접 요청의 라우팅은 바뀌지 않습니다. 아래 hide 설명은 기본 설정에 해당합니다. + opencodex는 Codex App을 직접 고치지 않습니다. Codex CLI/TUI와 같은 Codex 설정과 모델 카탈로그를 씁니다. app-server는 이 공유 상태를 읽지만, 일부 Codex Desktop 릴리스는 renderer에서 추가 remote allowlist를 적용해 routed row를 picker에서 제거할 수 있습니다. 명시적 `nativeAlias: true` combo가 diff --git a/docs-site/src/content/docs/ko/guides/combos.md b/docs-site/src/content/docs/ko/guides/combos.md index 6abe4f0241f..40ad3327e05 100644 --- a/docs-site/src/content/docs/ko/guides/combos.md +++ b/docs-site/src/content/docs/ko/guides/combos.md @@ -130,7 +130,8 @@ ocx combo set balanced \ | HTTP 401, 403, 404, 408, 429, 또는 모든 5xx | 대상을 쿨다운으로 보내고 다음 적합한 대상으로 넘어갑니다. | | 모델 수명 종료, retired, deprecated, sunset, decommissioned, 또는 더 이상 사용할 수 없다는 신호가 명시된 HTTP 410 | 해당 대상만 쿨다운으로 보내고 다음 대상으로 넘어갑니다. 관련 없는 410은 종결 오류로 유지합니다. | | 인증, 구독, 쿼터, 속도 제한, 과부하, 또는 상위 서버 오류로 분류됨 | 상태 코드만으로는 충분하지 않더라도 대상을 쿨다운으로 보내고 넘어갑니다. | -| 클라이언트 취소(499), `origin_rejected`, cyber-policy refusal, context overflow, 또는 기타 invalid request | 멈추고 오류를 반환합니다. 다른 대상을 써도 요청이 유효해지지 않기 때문입니다. | +| 클라이언트 취소(499), `origin_rejected`, cyber-policy refusal, 또는 기타 invalid request | 멈추고 오류를 반환합니다. 다른 대상을 써도 요청이 유효해지지 않기 때문입니다. | +| 출력 전 확인된 컨텍스트 초과 오류(HTTP 400 포함) | 쿨다운 없이 다음 미시도 적격 대상을 시도합니다. 모두 실패하면 처음 확인된 HTTP 400 컨텍스트 오류를 반환합니다(다른 상태 코드의 컨텍스트 거부도 다음 대상으로 넘어가지만 보관되지 않습니다). | | ChatGPT 계정으로 Codex를 사용할 때 해당 모델을 지원하지 않는다는 정확한 HTTP 400 거부 | 출력이 시작되기 전에 다음 설정 대상으로 넘어갑니다. 거부 문장 전체가 `detail`, `error.message` 또는 일반 텍스트에 있어야 하며, `detail`과 `error`가 충돌하면 중단합니다. 이 사유만으로 계정을 재인증 상태로 바꾸거나 공급자의 모든 모델에 쿨다운을 걸지는 않습니다. | | `user`를 명시적으로 거부하거나, `reasoning.effort`/`reasoning_effort`의 지원되지 않는 값, 모델별 이미지 입력 거부(`param: input`) 또는 경로가 읽을 수 없는 입력 미디어 거부(`unsupported_input_modality`)를 나타내는 구조화된 HTTP 400 | 출력 시작 전에 쿨다운 기록 없이 다음 적격 대상으로 넘어갑니다. 선택적 매개변수 호환성을 참조하세요. | | 인프로세스 어댑터(`runTurn`)가 실행하는 Responses 턴에서 현재 요청이 선언하지 않은 첫 도구 호출(출력이나 재전송 불가 부작용 이전) | 대상을 쿨다운하고 같은 도구 카탈로그로 다음 대상으로 넘어갑니다. 출력이 보였거나 재전송 불가 부작용이 생긴 뒤에는 거부가 그대로 확정됩니다. Chat Completions와 Anthropic Messages 요청은 바뀌지 않습니다. | @@ -144,6 +145,8 @@ ocx combo set balanced \ 현재 요청은 이미 시도한 대상을 다시 시도하지 않습니다. 단 한 가지 예외가 있습니다. `waitForCooldownMs`를 설정한 단일 대상 콤보는 대체할 다른 대상이 없으므로, 같은 요청 안에서 그 대상의 쿨다운이 끝나면 유일한 대상을 다시 시도할 수 있습니다. 요청 로컬 호환성 거부는 여전히 재시도 없이 반환됩니다. 이후 요청은 쿨다운이 끝날 때까지 해당 대상을 건너뜁니다. 이미 지난 시각을 가리키는 `Retry-After` HTTP-date도 `Retry-After: 0`과 마찬가지로 업스트림의 즉시 지시로 유지됩니다. `waitForCooldownMs`를 설정하면 이후 요청은 가장 먼저 적합해지는 대상의 쿨다운을 선택 시도마다 이 한도까지 기다린 뒤 새로 한 번 선택합니다. 따라서 여러 failover 홉을 거치는 요청은 총 `hops × waitForCooldownMs`까지 기다릴 수 있습니다. 기본값은 `0`입니다. 모든 적합한 대상이 쿨다운 중이고 대기 한도가 0이거나 가장 이른 만료 시각이 대기 한도를 넘으면 요청은 즉시 HTTP 503으로 종료됩니다. 이 `combo_unavailable` 503에는 가장 이른 잔여 쿨다운과 같은 `Retry-After` 헤더가 포함되며, 값은 올림해 정수 초로 표시되고 최소 1초입니다. 대기에 지터를 적용하지 않으므로 동시에 깨어날 수 있습니다. 요청이 중단되면 이 대기가 취소되고 정상 `client_cancelled` 응답이 반환됩니다. 취소 후 백업 대상을 디스패치하지 않습니다. 콤보 대상 쿨다운은 프로세스 로컬 콤보별 상태입니다. 네이티브 계정 라우팅에서 사용하는 계정 수준 Codex 쿼터 쿨다운과는 별개입니다. +위 HTTP 503 규칙은 아직 대상을 시도하지 않았을 때 적용됩니다. 확인된 HTTP 400 컨텍스트 거부 후 적격 대상이나 전송 예산을 소진하면 첫 거부를 `context_length_exceeded`로 반환하며, 쿼터 오류를 포함한 이후의 재시도 가능한 오류보다 우선합니다. 남은 대상이 추론을 재생할 수 없거나 과금 동의가 없는 경우도 같습니다. 취소, 이후의 치명적 거부, 명시적인 폴백 정책 거부는 즉시 중단합니다. 동의 없는 과금 대상으로 요청을 보내지 않습니다. + :::note 페일오버는 의도적으로 범위를 제한합니다. 대상별 가용성, 인증, 쿼터, 과부하 실패에는 도움이 되지만, 호출자 오류나 정책 거부를 숨기지는 않습니다. 콤보가 아닌 Responses 요청에서는 allowlist에 오른 xAI 정책 403이 Codex가 전송 실패로 재시도하기 전에 HTTP 200 `incomplete/content_filter`로 바뀝니다. [xAI policy refusals](/reference/proxy-formats/#xai-policy-refusals)를 보세요. 콤보 홉은 원래 HTTP 403을 홉으로 분류합니다. @@ -280,7 +283,7 @@ opencodex 인스턴스에 기록했는지 확인하세요. ### 첫 번째 오류 뒤에 failover가 멈춘 이유는 무엇인가요? -대상별 오류가 아니라 종결 오류였기 때문입니다. 잘못된 입력을 수정하고, 너무 큰 context를 줄이고, +대상별 오류가 아니라 종결 오류였기 때문입니다. 잘못된 입력을 수정하고, 정책 거부를 처리하거나, 거부된 요청 origin을 바로잡으세요. combo는 이런 경우 다음 대상으로 넘어가지 않습니다. diff --git a/docs-site/src/content/docs/ko/guides/integrations.md b/docs-site/src/content/docs/ko/guides/integrations.md index d017176dd71..ba1a77a7ea9 100644 --- a/docs-site/src/content/docs/ko/guides/integrations.md +++ b/docs-site/src/content/docs/ko/guides/integrations.md @@ -233,6 +233,16 @@ Undo는 원래 없던 파일까지 포함해 **두 원본 바이트 문자열 다운로드되는 `cline-config-bundle.json`에는 두 네이티브 문서 구성 요소가 있습니다. `providers.json`용 `settings`와 `models.json`용 `catalog`입니다. 번들 자체가 Cline 설정 파일은 아닙니다. 저널을 남기는 병합과 롤백에는 연동 명령을 권장합니다. 생성된 연동은 원격 수용 연결을 지원하지 않으며 인증이 없는 루프백 접근이 필요합니다. +## omo (Pi 기반) Responses 방언 + +관리되는 Pi 기반 omo 블록은 기본으로 Chat 방언(`api: "openai-completions"`)을 씁니다. Chat은 도구 호출 사이에서 이전 단계의 추론을 버리므로 긴 omo 도구 루프에서 차이가 납니다. 연결과 모든 갱신에서 Responses 방언을 쓰게 하려면 opencodex 설정 파일에만 넣을 수 있는 다음 키를 추가하세요. + +```json +{ "omoClientExport": { "api": "openai-responses" } } +``` + +수정한 설정을 읽도록 프록시를 다시 시작한 뒤(서비스로 설치했다면 `ocx service restart`, 아니면 `ocx stop` 다음 `ocx start`), 대시보드의 **Connect → omo**에서 omo 통합을 갱신하세요. 갱신만 하면 실행 중인 프록시의 설정을 쓰므로 설정 파일을 다시 읽지 않습니다. 이 설정은 omo에만 적용되며 Pi, Gentle Shell 등 다른 Pi 형식 클라이언트는 `openai-completions`를 유지합니다. Chat으로 되돌리려면 키를 지우고 같은 방법으로 다시 시작한 뒤 **Connect → omo**를 갱신하세요. 알 수 없는 값은 무시됩니다. opencodex는 여전히 `~/.omo/agent/models.json`의 `providers.opencodex`만 관리하며 다른 프로바이더는 그대로 둡니다. + ## omo (Codex / LazyCodex) 역할별 추론 강도 LazyCodex가 감지되면 omo 탭의 **omo (Codex / LazyCodex)** 섹션에서 `$CODEX_HOME/agents/*.toml`에 있는 역할마다 모델과 추론 강도를 고른 뒤 **저장**을 누를 수 있습니다. 모델 고정이 없는 역할의 행은 강도만 저장할 수 있고, 그 역할은 계속 모델을 상속합니다. opencodex는 그 역할 파일의 루트 `model`과 `model_reasoning_effort` 키만 고치고, 없는 고정값은 파일 위쪽에 추가합니다. 역할의 지침, 다른 주석과 다른 키는 그대로 둡니다. 모델만 바꿔 저장하면 `effort`를 보내지 않으므로 두 파일의 추론 강도가 그대로 유지됩니다. 단계를 고르면 역할 파일의 `model_reasoning_effort`를 추가하거나 바꾸고, LazyCodex가 그 단계를 지원하면 `~/.omo/omo.jsonc`의 `[codex].agents..reasoning`에도 씁니다(`ultra`는 역할 파일에만 남고, 이전 `reasoning`은 제거합니다). 명령줄에서는 `ocx agent roles set --effort `로 같은 작업을 합니다. diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 453aad8d639..aa3b3f3aff1 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -168,7 +168,7 @@ deny-by-default 상태로 유지됩니다. 자격 증명에 고정된 계정 id나 이메일이 있는 OAuth 프로바이더는 로그인을 여러 개 보관할 수 있습니다. Providers 페이지에서 계정을 추가하고, 다른 계정을 로그아웃하지 않은 채 활성 계정만 바꿀 수 있습니다. -계정 식별 정보가 없는 Kimi 자격 증명은 일반 로그인에서는 활성 슬롯을 교체하지만, 명시적인 **계정 추가**에서는 기존 슬롯을 보존하고 별도의 새 슬롯을 활성화합니다. Kiro 계정은 프로필 ARN을 키로 저장됩니다. +계정 식별 정보가 없는 Kimi 자격 증명은 일반 로그인에서는 활성 슬롯을 교체하지만, 명시적인 **계정 추가**에서는 기존 슬롯을 보존하고 별도의 새 슬롯을 활성화합니다. Kiro 계정은 프로필 ARN과 로그인한 사용자의 이메일(제공되는 경우)을 함께 사용해 구분합니다. 같은 조직 프로필을 공유하는 사용자도 각자의 자격 증명을 별도로 유지할 수 있습니다. `chatgpt`는 Codex 계정 풀에 별도 저장소가 있어 항상 단일 슬롯만 씁니다. 토큰은 `~/.opencodex/auth.json`에 저장되고, `/api/oauth/accounts`는 마스킹된 메타데이터만 반환합니다. @@ -187,6 +187,8 @@ cat accounts.json | ocx account import google-antigravity --format cockpit-tools ### Kiro 자격 증명 가져오기 +대시보드에서 **AWS IAM Identity Center**를 선택하고 조직의 AWS 액세스 포털 URL과 SSO 리전을 입력하면 CLI나 SSH 없이 계정을 추가할 수 있습니다. 브라우저에서 기기 코드를 승인하고 Kiro 프로필이 여러 개면 저장할 프로필을 선택하세요. 아래 CLI 설명은 **Kiro CLI** 선택 항목에 해당합니다. + Kiro 로그인에는 Kiro CLI가 필요합니다. Unix에서는 `curl -fsSL https://cli.kiro.dev/install | bash`, Windows PowerShell에서는 `irm 'https://cli.kiro.dev/install.ps1' | iex`로 설치한 뒤 먼저 `kiro-cli login`으로 로그인하세요. `kiro-cli` 세션이 없으면 `ocx login kiro`는 붙여 넣은 액세스 토큰이나 `KIRO_ACCESS_TOKEN` 환경 변수로 폴백합니다. 일반 `ocx login kiro` 가져오기는 CLI SQLite 데이터베이스를 읽기 전용으로 열며 데이터베이스, WAL, SHM을 수정하지 않습니다. @@ -260,7 +262,7 @@ Cline IDE/CLI에서만 제공되며 API로는 사용할 수 없습니다. `minim | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`. `moonshot` API 기본 모델은 `kimi-k3.1-pro`입니다. 공식 API 주소에 저장된 `kimi-k2.5` 선택은 이 모델로 마이그레이션됩니다. | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/ko/guides/sidecars.md b/docs-site/src/content/docs/ko/guides/sidecars.md index eb6ec6a6630..1ad25a749b3 100644 --- a/docs-site/src/content/docs/ko/guides/sidecars.md +++ b/docs-site/src/content/docs/ko/guides/sidecars.md @@ -161,6 +161,8 @@ fingerprint 선례를 따르지만, 실제 계정과 작업량으로 충분히 s 검색 응답의 선택 필드 형식이 잘못되어도 유효한 답변과 출처는 유지합니다. 오류 메시지 값의 형식이 잘못되면 일반 실패 메시지를 표시합니다. +OpenAI 검색·비전 사이드카는 CR, LF, CRLF 줄바꿈과 이들이 섞인 응답을 처리하며, CRLF가 여러 네트워크 청크로 나뉘어도 이벤트를 읽습니다. 서버가 마지막 줄바꿈 없이 응답 본문을 닫아도 마지막 완료 또는 실패 이벤트를 읽습니다. 완료되지 않았거나 크기 제한으로 중단된 응답은 계속 오류로 처리합니다. + 검색 답변의 Sources 목록에서 URL과 짝을 이루지 못한 글머리표는 출처로 삭제하지 않고 답변에 남깁니다. 제목 다음 줄에 해당 URL이 있으면 하나의 출처로 처리합니다. `mode:fast`처럼 인라인 코드 안의 비 HTTP URI 형태 텍스트는 답변에 남기며, 코드로 표시한 HTTP(S) URL은 출처로 처리합니다. 긴 검색어와 답변은 UTF-16 서로게이트 쌍을 나누지 않는 위치에서 줄이므로, 결과 길이 제한 때문에 😀 같은 한 글자가 반으로 잘리지 않습니다. 다만 제로 폭 결합자(ZWJ)로 이어진 이모지처럼 여러 글자로 이루어진 조합은 중간에서 잘릴 수 있습니다. diff --git a/docs-site/src/content/docs/ko/guides/web-dashboard.md b/docs-site/src/content/docs/ko/guides/web-dashboard.md index d97002b856b..c34d9d2a70c 100644 --- a/docs-site/src/content/docs/ko/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ko/guides/web-dashboard.md @@ -70,6 +70,8 @@ Usage, Dashboard, 공급자 작업 화면·카탈로그, API 키 화면은 읽 ### 요청 로그 필터 +요청 상세 화면은 일반 프록시 오류 문구 대신 기록된 업스트림 실패 원인을 우선 표시합니다. 물리적 업스트림 전송 수는 유효할 때 항상 표시하며, 회계 정보가 없거나 유효하지 않으면 0이 아니라 **Unavailable**로 표시합니다. **Retries**는 완전히 정산되고 일관된 단일 시도가 기록된 경우에만 첫 전송 이후 추가 전송 횟수로 표시합니다. 여러 시도, 미해결·불일치 회계 또는 손실 있는 attempt 정규화에서는 재시도 횟수를 만들어 내지 않고 숨깁니다. 알 수 없는 영속 원인은 상태 기반 제목으로 돌아갑니다. + Logs에서는 클라이언트 종류, 가로챈 요청, 공급자, 정확한 모델명, 상태, 시간, 속도, 대화 ID 조건을 함께 적용할 수 있습니다. 현재 불러온 로그만 필터링하며, 공급자·모델 선택지에는 폴백 시도도 포함됩니다. 모델명은 대소문자와 앞뒤 공백을 diff --git a/docs-site/src/content/docs/ko/reference/adapters.md b/docs-site/src/content/docs/ko/reference/adapters.md index cce5366d22c..59466160810 100644 --- a/docs-site/src/content/docs/ko/reference/adapters.md +++ b/docs-site/src/content/docs/ko/reference/adapters.md @@ -127,7 +127,7 @@ Ollama 공급자에 `adapter: "ollama-native"`로 설정할 수도 있습니다. 어댑터는 reasoning effort를 budget으로 매핑하고(minimal 1024 … max 32000), 출력 여유를 둔 안전한 `max_tokens`를 계산합니다. thinking이 켜지면 Anthropic에서 금지한 **`temperature`/`top_p`를 제거**합니다. -- **적응형 thinking 표시:** 적응형 thinking 모델(Opus 4.7+, Sonnet 5, Fable)에는 `thinking.display: "summarized"`를 보내므로, 긴 사고가 몇 분 동안의 heartbeat 대신 reasoning delta로 Chat과 Responses 클라이언트에 전달됩니다. reasoning 요약을 숨기는 요청(`reasoning.summary: "none"`)은 공급자 기본값을 그대로 씁니다. +- **적응형 thinking 표시:** 적응형 thinking 모델(Opus 4.7+, Sonnet 5, Fable)에는 `thinking.display: "summarized"`를 보내므로, 긴 사고가 몇 분 동안의 heartbeat 대신 reasoning delta로 Chat과 Responses 클라이언트에 전달됩니다. reasoning 요약을 숨기는 요청(`reasoning.summary: "none"`)은 공급자 기본값을 그대로 씁니다. 단, `api.anthropic.com`의 Opus 5.5는 `display: "updates"`가 기본이며, `reasoning.summary`를 `"concise"` 또는 `"detailed"`로 명시하면 updates 베타 없이 `"summarized"`를 사용합니다. [Opus 5.5 진행 업데이트](/guides/providers/#claude-opus-55-progress-updates)를 참고하세요. - 항상 `anthropic-version: 2023-06-01`을 보냅니다. `content_block_delta`(`text_delta`, `thinking_delta`, `input_json_delta`)를 스트리밍합니다. diff --git a/docs-site/src/content/docs/ko/reference/cli/agents.md b/docs-site/src/content/docs/ko/reference/cli/agents.md index e6186944fb4..102cff039a0 100644 --- a/docs-site/src/content/docs/ko/reference/cli/agents.md +++ b/docs-site/src/content/docs/ko/reference/cli/agents.md @@ -193,6 +193,8 @@ family는 `opus`, `fable`, `sonnet`, `haiku`이며, 새 route는 `opus`에서 일치가 확인된(`matched`) autostart wrapper만 backing Codex를 직접 실행합니다. 검사 예외를 포함한 알 수 없는 상태는 검증된 bypass가 필요하며, 관리되는 Windows CMD wrapper는 현재 생성 템플릿 전체와 일치해야 `OCX_SHIM_BYPASS=1`로 실행합니다. 일반 npm 런처는 지원합니다. 검증할 수 없거나 오래된 wrapper는 종료 코드 1로 거부하며 Codex를 시작하지 않습니다. `ocx codex-shim install`로 갱신하세요. 이 실행은 shim을 수리하지 않으며, 내용 검사는 Windows backing 파일 출처나 동시 경로 교체를 보장하지 않습니다. +macOS에서는 도움말과 버전을 포함한 매 실행 시, 선택한 실행 파일의 경로나 정규 경로가 `.app` 번들 내부에 있으면 검증합니다. 이름을 바꾼 번들과 번들 내부를 가리키는 심볼릭 링크도 번들 및 실행 파일의 안전한 소유권, 권한, OpenAI 서명을 요구합니다. 선택한 번들 구성원은 실행 파일, 중간 디렉터리 또는 번들 전체의 심볼릭 링크를 거쳐도 해당 번들의 정규 `Contents/` 내부에 있어야 합니다. 검증 실패 시 Codex를 시작하지 않고 종료 코드 1을 반환합니다. npm 설치를 포함한 일반 번들 외부 실행 파일의 동작은 유지됩니다. + 런처 도움말은 `ocx help codex`, Codex 도움말은 `ocx codex --help`입니다. 이후 plain `codex`의 동작은 기존과 같습니다. ### `ocx opencode [opencode args...]` diff --git a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md index a0dae108083..4355de7071b 100644 --- a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md @@ -24,6 +24,7 @@ description: 제공자 설정, 자격 증명, 할당량, 모델 카탈로그 명 | `quota` | `--refresh`, `--json` | 제공자 할당량 보고서를 읽습니다. | | `presets` | `--json` | 대시보드 제공자 프리셋을 나열합니다. | | `account-mode` | `pool`, `direct`, `--json` | Codex 계정 라우팅을 풀 기반으로 할지 직접 연결로 할지 선택합니다. | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | CC-Switch SQLite(`cc-switch.db`) 또는 레거시 JSON 파일에서 키 없는 프로바이더 항목을 미리보기·선택 적용·복원합니다. | 기본 `add`, `remove`, `set-default`는 로컬 설정을 바꿉니다. 실행 중인 프록시를 바꾸려면 `--live`를 지정하세요. 실시간 삭제에는 `--yes`도 필요합니다. `--sync --json`도 저장 후 동기화를 실제로 시도하며, 실패하면 저장은 유지한 채 0이 아닌 코드와 `needsSync: true`를 반환합니다. `--live`와 `--sync`는 함께 쓸 수 없습니다. pacing·snapshot/apply 절차는 [영문 명령 안내](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline)를 참고하세요. @@ -57,6 +58,27 @@ ocx models live --provider ark --json 비밀에 해당하는 값은 `--api-key`나 OAuth 로그인을 쓰세요. ::: +### 키 없는 CC-Switch 프로바이더 가져오기 + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# 다른 터미널에서 프록시를 시작(또는 ocx ensure 실행)한 뒤 키를 등록: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# 별도의 롤백 경로(먼저 프록시를 중지; 수정되지 않은 키 없는 항목만 제거): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch`는 일반 CC-Switch SQLite 데이터베이스나 레거시 JSON 파일에서 명시적으로 허용된 비비밀 메타데이터 필드(프로바이더 ID, 앱 유형, 기본 URL, 프로토콜 어댑터, 기본 모델)의 값**만** 투영합니다. 입력을 스캔하기 위해 소스와 내장 설정 바이트를 일시적으로 읽지만, 비밀 필드·자유 텍스트 표시 이름·TOML 주석은 바이트 수준에서 건너뛰어 문자열로 디코딩하거나 기록·로깅하지 않으며 임시 바이트 버퍼는 `finally`에서 0으로 지워집니다. 좁은 기본 URL 경로 규칙(`http(s)://host[:port]`와 빈 경로 또는 `/v1`, `/api/v1`, `/compatible-mode/v1` 같은 1~4개의 소문자 `[a-z0-9._-]` API 경로 세그먼트)과 식별자/모델 구문 검사는 심층 방어로 동작하며 부적합한 값을 에코하지 않고 거부합니다. 소스 파일은 `O_NOFOLLOW | O_NONBLOCK` 디스크립터 검증과 함께 한 번만 열리고 SQLite는 임시 파일 없이 메모리에서 읽기 전용으로 역직렬화됩니다. 여러 하드 링크(`nlink > 1`)를 가진 SQLite 파일은 별칭 경로를 통한 사이드카 검사 우회를 막기 위해 거부되며, 인접한 `-wal` 또는 `-journal` 파일이 비어 있지 않으면 CC-Switch를 종료(또는 데이터베이스 체크포인트)한 뒤 다시 시도하라고 안내하며 거부합니다. 신뢰할 수 있는 로컬 CC-Switch 소스만 사용하고, 출력된 모든 ID·모델·`baseUrl`을 확인한 뒤 자격 증명을 추가하세요: + +- **미리보기(기본값):** `--source `이 필요하며 아무것도 쓰지 않고 발견·적격·충돌·무효 항목 수를 보고합니다. +- **적용(`--apply`):** `--source `과 중지된 프록시(`ocx stop`)가 필요하며 Remote Link Child에서는 거부됩니다. 선택한 항목만 가져오려면 `--provider `를 반복하고, 루프백이나 사설 네트워크 기본 URL을 허용하려면 `--allow-private-network`를 지정하세요. 사전 미리보기 없이 실행하더라도 `--apply`는 키 없는 프로바이더 항목과 롤백 저널(`$OPENCODEX_HOME/cc-switch-provider-import-journal.json`)을 기록하기 전에 가져올 모든 프로바이더 ID·기본 모델·대상 URL을 먼저 출력합니다(텍스트 모드에서는 `stdout`, `--json` 모드에서는 `stderr`). 활성 저널이 있는 동안 두 번째 `--apply`는 거부됩니다. 출력된 항목을 확인한 뒤 프록시를 시작(다른 터미널에서 `ocx start` 또는 `ocx ensure`)하고 `printf '%s\n' "$API_KEY" | ocx account add-key `(파이프 표준 입력) 또는 `ocx login `로 자격 증명을 등록하세요. +- **복원(`--restore`):** 자격 증명 추가의 연속이 아닌 별도의 롤백 경로로, 중지된 프록시(`ocx stop`)가 필요하며 Remote Link Child에서는 거부되고 `--source`·`--provider`·`--allow-private-network`는 받지 않습니다. `auth.json`을 바이트 수준에서 읽기 전용으로 검사(읽을 수 없거나 형식이 잘못되면 안전하게 보류)하며, 가져온 키 없는 상태와 여전히 일치하고 `defaultProvider`로 선택되지 않았으며 라우팅·콤보(combos)·폴백 정책(fallback policy)·OAuth 계정·API 키 어디에서도 참조되지 않은 프로바이더만 활성 저널에서 제거합니다. + ## 인증 ### `ocx login ` diff --git a/docs-site/src/content/docs/ko/reference/configuration/advisor.md b/docs-site/src/content/docs/ko/reference/configuration/advisor.md index 0e1b4d940fe..c49656a19fb 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/ko/reference/configuration/advisor.md @@ -60,6 +60,10 @@ description: manual, preflight 및 증거 기반 adaptive 정책을 제공하는 설정된 어드바이저 프로바이더는 워커의 프로바이더와 다를 수 있습니다. +상담은 호출자에게서 캡처한 API 키의 모델 범위, 유료 combo 동의, 디바이스 fallback 제한과 Advisor 자체 승인 키의 호스트 제한을 모두 따릅니다. 승인 키는 설정된 경우 `OPENCODEX_API_AUTH_TOKEN` 또는 이 프록시가 받아들이는 서비스 토큰 파일이고, 그렇지 않으면 첫 번째로 구성된 API 키입니다. 키 순서는 이 마지막 경우에만 의미가 있습니다. Advisor 모델에 접근할 수 있는 키를 맨 앞에 두지 않으면 모든 상담을 사용할 수 없습니다. 환경 토큰이나 서비스 토큰이 설정되어 있으면 그 토큰이 상담을 결정합니다. 이 규칙은 combo 선택, 중첩 판정 모델, 재시도에도 적용됩니다. 규칙을 충족하지 못한 상담은 worker에게 사용 불가 안내를 반환합니다. + +예를 들어 `allowMeteredComboTargets: false`인 `laptop` 키는 Advisor를 통해 종량제 combo 대상에 비용을 쓸 수 없고, `fallback.devices.laptop.provider: false`는 Advisor의 프로바이더 fallback 홉도 막습니다. 키가 없는 loopback 호출자는 여전히 combo의 `allowMeteredTargets: true` 옵트인이 필요합니다. 두 키가 모두 `allowMeteredComboTargets: true`를 설정하고 combo가 `allowMeteredTargets: true`를 설정하면, 공개 바인드의 loopback 리스너를 거치더라도 Advisor가 종량제 대상을 사용할 수 있습니다. 서버는 본문 용량을 기다리거나 본문을 읽기 전, 요청을 인증할 때 호출자 정책을 캡처합니다. 이후 키나 fallback을 편집해도 그 상담의 범위는 넓어지지 않습니다. 라우팅된 비전 도우미, compact 요청과 그 판정 모델, WebSocket 턴 안의 모든 후보 재시도에도 똑같이 적용됩니다. 새 WebSocket 턴은 각자 자신의 정책을 캡처합니다. + OpenCodex는 프로바이더 API 키, Authorization 헤더, OAuth 토큰, 백엔드 전용 설정 비밀, 프로세스 환경, 숨겨진 사고 과정을 그 프롬프트에 넣지 않습니다. 암호화된 프로바이더 사유 추론을 복호해 전달하지도 않습니다. **작업 내용의 비밀은 일반적으로 제거되지 않습니다.** 작업에 붙여 넣은 키, 도구가 읽은 파일 속의 비밀, 도구나 로그가 출력한 토큰은 전송될 수 있습니다. OpenCodex는 범용 DLP를 실행하지 않습니다. ## 권한 diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index af503a86dc5..6102a5442e6 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -43,6 +43,7 @@ GUI에서 등록이나 OAuth 로그인을 마치면 Models 페이지로 이동 | `providers` | `Record` | — | 공급자 이름을 공급자 설정에 매핑합니다. | | `openaiProviderTierVersion?` | `2` | 마이그레이션으로 설정됨 | 옵션을 인식하는 단일 OpenAI 투영이 완료되었음을 표시합니다. | | `disabledModels?` | `string[]` | — | Codex catalog와 `/v1/models`에서는 숨기지만 직접 proxy 호출은 차단하지 않습니다. routed id는 목록에서 제거됩니다. account-qualified native id는 해당 selector row만 숨기고, bare native GPT id는 bare row와 그 model의 모든 account-selector row를 숨깁니다. Models 페이지에는 bare native 행과 routed 행만 표시됩니다. selector-qualified 행 하나만 숨기려면 이 설정 필드에 직접 추가하세요. | +| `omitDisabledNativeModels?` | `boolean` | `false` | 기본값은 `false`입니다. `config.json`에 `"omitDisabledNativeModels": true`를 설정하고 `ocx sync`를 실행하면 비활성화한 native 행을 catalog와 cache, Codex 형식의 HTTP 모델 목록에서 제외합니다. 제외 전 검증한 상속 템플릿을 보존하므로 routed 행의 메타데이터는 유지되며 직접 요청의 라우팅은 바뀌지 않습니다. | | `providerContextCaps?` | `Record` | `{}` | 공급자별 활성 컨텍스트 상한입니다. 일반 윈도는 줄어들며, 장문 윈도를 지원하는 네이티브 모델은 해당 모델의 지원 상한까지만 확장할 수 있습니다. | | `providerContextCapValues?` | `Record` | `{}` | 공급자별로 마지막에 선택한 상한입니다. 꺼도 선택값이 남으며, 저장된 값만으로는 상한이 활성화되지 않습니다. 활성 값이 저장된 선택값보다 우선합니다. | | `contextCapValue?` | `number` | `350000` | 처음 켤 때 쓰는 기본값입니다. 다시 켜면 공급자별 선택값을 복원합니다. `setAll: true`와 함께 전역 값을 바꾸면 활성 상한만 갱신합니다. 값 없이 `setAll: true`를 보내면 설정된 모든 공급자의 상한을 현재 전역 값으로 켭니다. | @@ -117,7 +118,7 @@ managed map을 활성화하면 privacy-safe selector를 만들고, 이후 계정 | `models?` | `string[]` | 초기/폴백 모델 목록입니다. `liveModels: false`에서 `models`가 비어 있지 않으면 `models`, `retainModels` 순으로 구성합니다. `models`가 비어 있거나 생략되면 설정된 `defaultModel`, `retainModels` 순으로 구성하고, 중복 ID는 처음 나온 항목만 남깁니다. | | `liveModels?` | `boolean` | 시작 또는 동기화 시 라이브 카탈로그를 가져옵니다. 기본값은 `true`입니다. 사용자 지정 공급자는 `${baseUrl}/models`를 사용하고, 내장은 레지스트리 URL을 사용한 뒤 필터링할 수 있습니다. | | `selectedModels?` | `string[]` | 발견 후 카탈로그 허용 목록입니다. 값이 비어 있지 않으면 그 id만 노출하고, 비어 있거나 생략하면 발견된 모델을 모두 노출합니다. | -| `modelDisplayNames?` | `Record` | 이 공급자의 정확한 네이티브 모델 id를 키로 쓰는 영구 표시 전용 이름입니다. 키는 대소문자를 구분합니다. 이름은 공급자 카탈로그 메타데이터보다 우선하며 인증, 어댑터, 라우팅, 청구 또는 업스트림 요청을 바꾸지 않습니다. 맵은 발견 한도와 같은 최대 2,000개 항목을 가질 수 있습니다. | +| `modelDisplayNames?` | `Record` | 이 공급자의 정확한 네이티브 모델 id를 키로 쓰는 영구 표시 전용 이름입니다. 키는 대소문자를 구분합니다. 이름은 공급자 카탈로그 메타데이터보다 우선하며 인증, 어댑터, 라우팅, 청구 또는 업스트림 요청을 바꾸지 않습니다. 단, 직접 `provider/model` 선택자가 알려진 id, 별칭, `retainModels` 어디에도 맞지 않으면(예: 라이브 목록을 아직 받지 못한 동안) 인코딩 형태가 같은 키를 네이티브 id로 보냅니다. 맵은 발견 한도와 같은 최대 2,000개 항목을 가질 수 있습니다. | | `contextWindow?` | `number` | 업스트림 메타데이터가 없을 때 쓰이는 공급자 전반의 컨텍스트 값입니다. 메타데이터가 있으면 상한으로 동작해 더 작은 라이브 값을 그대로 둡니다. Models 대시보드에서 `providerContextCaps`와 별도로 설정합니다. | | `modelContextWindows?` | `Record` | 모델별 컨텍스트 값이자 상한입니다. `contextWindow`보다 우선하며, 창 크기를 알 수 없으면 설정값을 쓰고 더 작은 라이브 메타데이터가 있으면 그쪽을 따릅니다. | | `inferContextWindow?` | `boolean` | 사용자 지정 공급자 전용이며 기본값은 꺼짐입니다. `true`이면 빠진 창을 번들 모델 메타데이터로 채웁니다. 추정값이며 중계의 실제 용량을 검증하지 않습니다. | @@ -153,7 +154,7 @@ managed map을 활성화하면 privacy-safe selector를 만들고, 이후 계정 | `noPenaltyModels?` | `string[]` | presence/frequency penalty를 허용하지 않는 모델입니다. 기본 `xai` 프리셋은 xAI 문서가 이 값을 거부한다고 밝힌 추론 모델(`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`)을 여기에 넣습니다. 추론이 없는 모델은 호출자가 보낸 penalty를 그대로 받습니다. | | `noStructuredOutputModels?` | `string[]` | `openai-chat` 엔드포인트가 `response_format`을 거부하는 정확한 모델 ID입니다. 요청 모델이 항목과 정확히 일치할 때만 필드를 생략하며, 그 외 `openai-chat` 모델에서는 structured-output 변환을 유지합니다. | | `noJsonSchemaModels?` | `string[]` | `openai-chat` 엔드포인트가 `json_schema` 형식은 거부하지만 `json_object`는 받는 정확한 모델 ID입니다. 이런 요청은 필드를 지우는 대신 `json_object`로 낮춰 보내므로, JSON을 요청한 클라이언트가 산문 대신 JSON을 받습니다. 한 모델이 두 목록에 모두 있으면 `noStructuredOutputModels`가 우선합니다. `opencode go`, `opencode zen` 프리셋이 DeepSeek 경로에 기본으로 싣습니다. | -| `foldDeveloperRoleToSystem?` | `boolean` | `openai-chat` 목적지가 `developer` 역할을 받는지 기록합니다. `foldDeveloperRoleToSystem`이 없으면 `system`, `true`이면 `system`, `false`이면 `developer`로 보냅니다. 값이 없다는 것은 이 목적지에 대해 기록된 것이 없다는 뜻이고, `true`는 상위 서비스가 역할을 거부한다는 기록, `false`는 받아들인다는 기록입니다. 어느 경우에도 메시지는 대화 안의 원래 위치를 유지하며 역할만 바뀝니다. 역할을 거부하는 목적지는 `400 role 'developer' is not allowed`로 응답해 턴이 시작조차 못 하므로, 기록이 없는 상태의 기본값을 접는 쪽으로 둡니다. | +| `foldDeveloperRoleToSystem?` | `boolean` | `openai-chat` 목적지가 `developer` 역할을 받는지 기록합니다. `foldDeveloperRoleToSystem`이 없으면 `system`, `true`이면 `system`, `false`이면 `developer`로 보냅니다. 값이 없다는 것은 이 목적지에 대해 기록된 것이 없다는 뜻이고, `true`는 상위 서비스가 역할을 거부한다는 기록, `false`는 받아들인다는 기록입니다. `singleLeadingSystemMessage`가 활성화되지 않은 경우 메시지는 대화 안의 원래 위치를 유지하며 역할만 바뀝니다. 역할을 거부하는 목적지는 `400 role 'developer' is not allowed`로 응답해 턴이 시작조차 못 하므로, 기록이 없는 상태의 기본값을 접는 쪽으로 둡니다. | | `parallelToolCalls?` | `boolean` | 병렬 도구 호출을 켜거나 끕니다. OpenAI Chat은 기본으로 켜져 있고, 비-chat 어댑터는 명시적으로 `true`일 때만 이를 노출합니다. | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` | 기본값이 꺼진 downstream SSE 복구입니다. 정확한 자리표시자 id, 누락된 종료 id, 그리고(`repairInvalidIds`) 정규 `msg_`/`rs_` 접두사가 없는 message/reasoning id를 복구합니다. function-call id는 다시 쓰지 않습니다. 내장 DeepSeek은 마지막 두 가지를 기본으로 켭니다. | | `responsesSnapshotRepair?` | `boolean` | 기본값이 꺼진 클라이언트용 복구입니다. SSE와 JSON의 Responses 수명 주기에서 누락된 status, output, 도구 메타데이터를 채우며 raw 검사와 영속화는 변경하지 않습니다. | @@ -179,23 +180,26 @@ managed map을 활성화하면 privacy-safe selector를 만들고, 이후 계정 | `unsafeAllowNativeLocalExec?` | `boolean` | Cursor 레거시 불리언입니다. 더 새로운 필드가 설정되지 않았을 때만 `nativeLocalExec: "on"`과 같습니다. | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Cursor 로컬 실행 정책입니다. 기본값은 `off`입니다. `codex-sandbox`는 현재 `off`처럼 실패를 닫습니다. | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + 공급자 등록·교체(`POST /api/providers`)는 `responsesPath`와 `chatCompletionsPath`를 검증한 뒤 메모리와 파일의 설정을 변경합니다. `PATCH /api/providers?name=`는 요청 본문을 저장된 공급자에 병합합니다. `disabled` 외의 필드를 변경하는 업데이트(`requestPacing`만 변경하는 업데이트 제외)는 저장 전에 병합된 공급자의 경로를 같은 방식으로 검증하며, 유지된 경로가 유효하지 않으면 `400`을 반환하고 설정을 변경하지 않습니다. 설정 파일을 읽을 때도 같은 경로 규칙을 적용합니다. API 키 공급자는 리터럴 키나 환경 참조를 둘 수 있습니다. OAuth 공급자는 `ocx login`으로 채워지는 자격 증명 저장소를 사용합니다. 구독 기반 Claude Code 실행 동작은 [`claudeCode.authMode`](/ko/reference/configuration/server/#claude-code-claudecode)에서 설정합니다. ### 프로바이더 저장이 유지하는 것 -기존 프로바이더 이름으로 `POST /api/providers`를 보내면 저장된 행이 요청으로 만든 행으로 바뀝니다. 대시보드의 추가/편집 폼은 모든 필드를 보낼 수 없으므로, 요청이 빠뜨린 저장 필드 일부는 저장할 때 이어서 유지됩니다. 그중 여덟 가지는 특정 업스트림의 동작을 기록한 설정입니다: `preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. +기존 프로바이더 이름으로 `POST /api/providers`를 보내면 저장된 행이 요청으로 만든 행으로 바뀝니다. 대시보드의 추가/편집 폼은 모든 필드를 보낼 수 없으므로, 요청이 빠뜨린 저장 필드 일부는 저장할 때 이어서 유지됩니다. 그중 열한 가지는 특정 업스트림의 동작을 기록한 설정입니다: `preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. -| 저장 | 여덟 가지 설정 | 저장된 `apiKeyPool` | +| 저장 | 열한 가지 설정 | 저장된 `apiKeyPool` | | --- | --- | --- | | 같은 목적지, 필드 생략 | 저장된 값 유지(명시적인 `[]`나 `false` 포함) | 유지 | | 새 목적지, 필드 생략 | 유지하지 않음. 새 목적지의 레지스트리 기본값이 적용될 수 있음 | 유지하지 않음 | | 요청에 필드를 보냄 | 요청의 값 | 요청의 값 | -목적지는 어댑터, 기본 URL(스킴과 호스트는 대소문자를 구분하지 않고, 끝의 슬래시는 무시), 그리고 요청이 지정한 경우 인증 모드입니다. 프로바이더를 다른 목적지로 옮기면 이전 업스트림을 설명하는 여덟 가지 설정과, 그 업스트림용으로 발급된 키 풀을 가져가지 않습니다. 저장은 이전 행의 나머지를 새 행에 병합하지 않습니다. +목적지는 어댑터, 기본 URL(스킴과 호스트는 대소문자를 구분하지 않고, 끝의 슬래시는 무시), 그리고 요청이 지정한 경우 인증 모드입니다. 프로바이더를 다른 목적지로 옮기면 이전 업스트림을 설명하는 열 가지 설정과, 그 업스트림용으로 발급된 키 풀을 가져가지 않습니다. 저장은 이전 행의 나머지를 새 행에 병합하지 않습니다. -`PATCH /api/providers?name=`는 지정한 필드만 바꾸고, 목적지와 상관없이 나머지 저장 필드는 모두 유지합니다. 여덟 가지 설정을 모두 받고, `null`로 지웁니다. 두 추론 목록에서 빈 배열은 삭제되지 않고 명시적인 옵트아웃으로 저장됩니다. +`PATCH /api/providers?name=`는 지정한 필드만 바꾸고, 목적지와 상관없이 나머지 저장 필드는 모두 유지합니다. 열 가지 설정을 모두 받고, `null`로 지웁니다. 두 추론 목록에서 빈 배열은 삭제되지 않고 명시적인 옵트아웃으로 저장됩니다. ## 공급자 진단용 외부 요청 안전성 diff --git a/docs-site/src/content/docs/ko/reference/configuration/routing.md b/docs-site/src/content/docs/ko/reference/configuration/routing.md index d19dce2f092..fed5f09dbfc 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/routing.md +++ b/docs-site/src/content/docs/ko/reference/configuration/routing.md @@ -121,6 +121,27 @@ CLI: `ocx route policy list`, `ocx route policy show `, `ocx route policy dr CLI: `ocx logs explain `, `ocx logs rebuild-index`, `ocx logs index-status`. +### 인덱스 검사와 복구 + +기존 기록 인덱스를 처음 열면 읽기 전용 워커에서 무결성을 검사합니다. +기록 조회는 이 초기 검사를 기다리며, 프록시의 요청 처리 스레드를 막지 않습니다. +동기식 라우팅 조회는 검사 중에 busy를 반환하므로 라우팅 프로필의 +`unknownEvidence.health` 정책이 적용됩니다. 작은 인덱스를 인라인으로 재구축한 경우에는 +성공 직후 사용할 수 있으며, 워커에서 재구축한 인덱스는 게시 전에 검사를 마칩니다. + +검사나 재구축에 성공한 뒤 최소 5분이 지나면 다음 조회가 백그라운드 무결성 검사 하나를 +시작합니다. 이미 검증된 인덱스는 검사 중에도 조회할 수 있습니다. 일반 갱신은 스키마, +원본 파일 식별자와 바이트 오프셋을 확인하지만 전체 동기식 검사를 반복하지 않으며, +조회가 없는 동안에는 타이머를 실행하지 않습니다. 데이터베이스 잠금이나 워커 실행 실패 +같은 일시적인 검사 오류는 인덱스를 삭제하지 않고, 30초가 지난 뒤의 조회에서 다시 시도합니다. +초기 검증을 마치지 못한 인덱스는 그동안 사용할 수 없습니다. + +손상이 탐지되면 `usage.jsonl`에서 자동으로 재구축하며, 영향을 받은 비동기 기록·분석 +조회는 복구 후 한 번 재시도합니다. 인덱스를 닫거나 재구축할 때에는 이전 검사 결과를 +폐기하고 검사 워커가 파일을 해제한 뒤 인덱스를 교체합니다. 검사와 복구는 원본 기록을 +다시 쓰지 않습니다. 초기 검사에는 인덱스 크기에 비례하는 시간이 걸리며, +이후 백그라운드 검사는 인덱스를 사용하는 동안에만 실행됩니다. + ## 마이그레이션 `routingProfiles`는 선택적 추가 설정입니다. 기존 설정 파일과 이전 `usage.jsonl` 행은 그대로 읽힙니다. 인덱스는 일회용이며 삭제 시 다음 쿼리에서 `usage.jsonl`로 자동 재구축됩니다. 자동 튜닝은 없습니다. diff --git a/docs-site/src/content/docs/reference/adapters.md b/docs-site/src/content/docs/reference/adapters.md index 8b9716128bf..8d6ee982c97 100644 --- a/docs-site/src/content/docs/reference/adapters.md +++ b/docs-site/src/content/docs/reference/adapters.md @@ -260,6 +260,27 @@ blank strings and mixed encrypted/unknown parts are not partially converted. See [agent messages](/reference/configuration/providers/#routed-agent-messages) for the separate opt-in encrypted-task recovery behavior. +Meta Responses also receives Codex's current `additional_tools` declarations in the public +`tools` array. The catalog is captured before history normalization: replayed declarations +and historical tool-search discoveries cannot activate tools for the new turn. Current +namespace, custom-tool and long-name aliases retain the existing normalization and response +restoration. Current declarations keep their parameter schemas when search results repeat a tool. +Historical custom calls retain their existing function-call representation without granting +those old tools current authority. Nested function definitions become public declarations. +Malformed or unrepresentable namespace catalogs return HTTP 400 before inference, including +requests with string or omitted input, rather than being partially promoted. + +For Meta Responses at the exact `api.meta.ai` host, OpenCodex omits the unsupported +`output.encrypted_content` include requested by Codex Desktop. Meta otherwise rejects the +request before inference. `reasoning.encrypted_content` and other include entries remain +intact, so stateless reasoning replay is preserved. The same exact-entry normalization +applies to xAI; unrelated Responses destinations keep their original include list. + +These request-shape fixes do not change account entitlements. Meta limits Muse Code +subscription credentials to the Muse Code client; other clients require a separately +billed Meta API key. A subscription key can therefore return HTTP 403 through OpenCodex +even when the request shape is valid. See [Meta's subscription rules](https://dev.meta.ai/docs/muse-code/subscriptions). + For xAI Responses, `auto` or `none` tool selection is omitted when normalization leaves no tools in the request, including when cached-only search is removed. Valid forced function selections remain intact. Replayed custom tool calls with missing or invalid item ids receive stable ids @@ -358,7 +379,9 @@ MiMo model Command Code serves. - **Adaptive thinking display:** adaptive-thinking models (Opus 4.7+, Sonnet 5, Fable) are asked for `thinking.display: "summarized"`, so a long think reaches Chat and Responses clients as reasoning deltas instead of minutes of heartbeats. A request that hides the reasoning summary - (`reasoning.summary: "none"`) keeps the provider default. + (`reasoning.summary: "none"`) keeps the provider default. Native Opus 5.5 on `api.anthropic.com` + defaults to `display: "updates"`; explicit `reasoning.summary: "concise"` or `"detailed"` + selects `"summarized"` without the updates beta. See [Opus 5.5 progress updates](/guides/providers/#claude-opus-55-progress-updates). - **Structured output:** Responses `text.format` and Chat Completions `response_format` requests with `type: "json_schema"` become Anthropic `output_config.format`. The format merges into an existing adaptive-thinking output configuration, preserving a compatible `output_config.effort`. @@ -800,6 +823,11 @@ appended to the id instead. **Targets:** **Azure OpenAI**. Wraps `openai-responses` (so also `passthrough: true`). **Auth:** `key` via the `api-key` header (not Bearer). +Azure requests omit the unsupported output.encrypted_content entry from include, +while retaining reasoning.encrypted_content and all other entries. This also applies +to openai-responses configurations targeting a resource under .openai.azure.com. +When no entries remain, include is omitted. + - Delegates request building to the Responses passthrough, validates that `baseUrl` contains no unresolved template placeholder, and replaces `Authorization` with `api-key`. The configured URL targets Azure's v1 Responses API directly, so the adapter does not append `api-version`. diff --git a/docs-site/src/content/docs/reference/cli/agents.md b/docs-site/src/content/docs/reference/cli/agents.md index fa0798d1e3d..bce042afced 100644 --- a/docs-site/src/content/docs/reference/cli/agents.md +++ b/docs-site/src/content/docs/reference/cli/agents.md @@ -994,6 +994,8 @@ A missing `OPENCODEX_API_AUTH_TOKEN` is read from this installation's `service-a Only a matched autostart wrapper launches its backing Codex directly. Unknown state, including an inspection exception, requires a verified bypass; Windows CMD managed wrappers must match the complete current generated template before the selected absolute command runs with `OCX_SHIM_BYPASS=1`. Ordinary npm launchers remain supported. Unverifiable or obsolete wrappers refuse with exit 1 and start no Codex; run `ocx codex-shim install` to refresh them. This launch does not repair shims. The content check does not establish Windows backing-file provenance or prevent concurrent pathname replacement. +On macOS, every launch, including help/version, verifies the selected executable when its selected path or canonical location is inside any `.app` bundle. Renamed bundles and symlinks into bundles require the same safe ownership, permissions and OpenAI signatures for the bundle and executable. A selected bundle member must stay inside that bundle's canonical `Contents/`, including through executable, directory or whole-bundle symlinks. Failed verification exits 1 without starting Codex. Ordinary non-bundle executables, including npm installations, retain their behavior. + Use `ocx help codex` for launcher help and `ocx codex --help` for Codex help. Launching plain `codex` later retains its existing behavior. ### `ocx opencode [opencode args...]` diff --git a/docs-site/src/content/docs/reference/cli/messaging.md b/docs-site/src/content/docs/reference/cli/messaging.md index b64b25e7255..ef90c846e13 100644 --- a/docs-site/src/content/docs/reference/cli/messaging.md +++ b/docs-site/src/content/docs/reference/cli/messaging.md @@ -246,7 +246,11 @@ hold twelve authenticated connections, four more stay reserved for the owner's o control, outgoing probes and renewal, and at most eight tunnel children and two transient control/inspection helpers, ten total. Setup is bounded by 30 seconds across the whole selected peer batch. A final lease renewal precedes startup -success; final and periodic renewal batches have five-second deadlines, within the setup +success. If SSH reports that a selected forward port was lost, setup closes the owned +pair and selects fresh ports, up to three attempts within that same deadline. SSH forward +confirmation, loopback checks and authenticated peer readiness must all succeed. Other +setup failures stop immediately; published routes do not reconnect automatically. Final +and periodic renewal batches have five-second deadlines, within the setup deadline where applicable, and periodic maintenance runs every ten seconds. Cancellation stops only owned SSH helpers/listeners; it leaves Codex daemons, threads and unrelated proxies alone. diff --git a/docs-site/src/content/docs/reference/cli/providers-accounts.md b/docs-site/src/content/docs/reference/cli/providers-accounts.md index 8c0c237f941..3100c891189 100644 --- a/docs-site/src/content/docs/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/reference/cli/providers-accounts.md @@ -36,6 +36,7 @@ both `--adapter` and `--base-url`. | `resets` | `--limit `, `--json` | List recently detected quota-window resets. | | `presets` | `--json` | List dashboard provider presets. | | `account-mode` | `pool`, `direct`, `--json` | Select pooled or direct Codex account routing. | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | Preview, selectively apply, or restore keyless provider entries from a CC-Switch SQLite database (`cc-switch.db`) or legacy JSON file. | ```bash ocx provider list --json @@ -220,6 +221,27 @@ serialized `{baseline,next}` request must also fit 4 MiB. Interactive stdin, special files, conflicting flags, invalid fields and unconfirmed removals are refused before mutation with exit 2. Input errors never echo the submitted values. +### Keyless CC-Switch provider import + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# Start the proxy in another terminal (or run ocx ensure), then add credentials: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# Separate rollback path (stop the proxy first; removes unedited keyless entries only): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` projects values **only** from an explicit allowlist of non-secret metadata fields (provider IDs, app types, base URLs, wire dialects, and default models) from a plain regular CC-Switch SQLite database or legacy JSON file. Source and embedded-config bytes are read transiently to scan the input, while secret fields, free-text display names, and TOML comments are skipped at the byte level, never decoded into strings, written, or logged, and transient byte buffers are zeroed in `finally`. Narrow base-URL path rules (`http(s)://host[:port]` with an empty path or 1–4 lowercase `[a-z0-9._-]` API path segments such as `/v1`, `/api/v1`, or `/compatible-mode/v1`) and identifier/model syntax checks act as defense in depth and reject non-conforming values without echoing them. Source files are opened once with `O_NOFOLLOW | O_NONBLOCK` descriptor verification and deserialized read-only in memory for SQLite without temporary files; SQLite files with multiple hard links (`nlink > 1`) are refused so sidecar checks cannot be bypassed via an alias path, and if a sibling `-wal` or `-journal` file is non-empty, import refuses and asks you to close CC-Switch (or checkpoint the database) and retry. Only import from a trusted local CC-Switch source, and verify every printed ID, model, and `baseUrl` before adding credentials: + +- **Preview (default):** requires `--source `, writes nothing, and reports discovered, eligible, conflicting, and invalid entries. +- **Apply (`--apply`):** requires `--source ` and a stopped proxy (`ocx stop`), and is refused on a Remote Link Child. Repeat `--provider ` to import only selected entries, or pass `--allow-private-network` to permit loopback or private-network base URLs. Even without a prior preview, `--apply` prints every imported provider ID, default model, and destination URL before writing keyless provider entries and recording a rollback journal at `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` (`stdout` in text mode, `stderr` in `--json` mode); a second apply is refused while an active journal exists. Verify the printed entries, start the proxy (`ocx start` in another terminal or `ocx ensure`), and add credentials with `printf '%s\n' "$API_KEY" | ocx account add-key ` (piped stdin) or `ocx login `. +- **Restore (`--restore`):** separate rollback path (not a continuation of adding credentials) that requires a stopped proxy (`ocx stop`), is refused on a Remote Link Child, and takes no `--source`, `--provider`, or `--allow-private-network`. It inspects `auth.json` read-only at the byte level (failing closed if unreadable or malformed) and removes only providers from the active journal that still match their imported keyless state and are not selected as `defaultProvider` or referenced by routing, combos, fallback policy, OAuth accounts, or API keys. + ## Authentication ### Diagnosing missing main-account quota diff --git a/docs-site/src/content/docs/reference/configuration/advisor.md b/docs-site/src/content/docs/reference/configuration/advisor.md index 036b322d589..aacd2ff6d7e 100644 --- a/docs-site/src/content/docs/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/reference/configuration/advisor.md @@ -118,6 +118,21 @@ A consultation may send: The configured Advisor provider may differ from the worker provider. +Consultations obey both the caller's captured API-key model scope, paid-combo consent and device +fallback limits, and the host limits of Advisor's own admission key: `OPENCODEX_API_AUTH_TOKEN` or the service token file when set and accepted by this proxy, otherwise the first configured API key. Only in that last case does key +order matter: put a key that can reach the Advisor model first, or every consultation is unavailable. +When an environment or service token is set, that token controls the consultation instead. These rules also apply to combo choices, nested +decision models and retries. A consultation that cannot satisfy them returns an unavailable +notice to the worker. + +For example, a `laptop` key with `allowMeteredComboTargets: false` cannot spend on +metered combo targets through Advisor, and `fallback.devices.laptop.provider: false` +also blocks Advisor's provider fallback hops. An unkeyed loopback caller still needs +the combo's `allowMeteredTargets: true` opt-in. When both keys set `allowMeteredComboTargets: true` and the combo sets `allowMeteredTargets: true`, Advisor can use its metered targets, also through the loopback listener of a public bind. The server captures caller policy when it +authenticates the request, before waiting for body capacity or reading the body; later key or fallback edits cannot broaden that consultation. +This also covers routed vision helpers, compact requests and their decision models, and every +candidate retry within a WebSocket turn. Each new WebSocket turn captures its own policy. + OpenCodex does not insert provider API keys, authorization headers, OAuth tokens, backend-only config secrets, process environment, or hidden chain-of-thought into that prompt. It does not decrypt or forward encrypted provider-private reasoning. **Task content is not secret-redacted.** @@ -142,6 +157,12 @@ After consultation exhaustion the `advisor` tool is removed. A repeated call rec paired limit result; if the worker calls it again, typed 502 `advisor_continuation_limit` ends the request without another hidden worker call. The bound is shared with empty-completion retries. +Fetch-based web-search turns support the same manual consultation path. The search loop intercepts +`web_search` and `advisor` calls in worker order, so enabling hosted search does not hide the +advisor tool. Search and Advisor iterations have separate allowances, and a real client tool call +still returns control to the client. Run-turn and image/video sidecar turns remain subject to their existing limitations. +Usage accounting includes each worker turn once, including the leg rejected by the Advisor continuation limit. + Suppression does not read Advisor strings. Automatic dedup is the server-owned ledger. A developer message, including one that copies the transport text, does not suppress preflight. diff --git a/docs-site/src/content/docs/reference/configuration/agents.md b/docs-site/src/content/docs/reference/configuration/agents.md index 1b6791280cd..d2474b94f1c 100644 --- a/docs-site/src/content/docs/reference/configuration/agents.md +++ b/docs-site/src/content/docs/reference/configuration/agents.md @@ -56,7 +56,8 @@ still depends on upstream support for your account. | `subagentModelFallback?` | `string[]` | `[]` | Priority-ordered global fallback models for spawned child turns. | | `subagentModelFallbackByModel?` | `Record` | `{}` | Per-primary-model fallback chains, keyed by the requested primary model id. This is the supported home for per-role fallback metadata; `model_fallback` inside Codex agent TOML makes Codex 0.146+ skip the role (#1190). | | `subagentModelFallbackPollMs?` | `number` | `60000` | Availability-probe cache interval. Values below 1000 ms fall back to the default. | -| `codexRoleTiers?` | `{ fast?, standard?, frontier?: string[] }` | — | Capability tier of named models for [role auto-assign](/guides/integrations/#auto-assign). Listed models take that tier; other models are tiered by price, and unpriced unlisted models are never proposed. | +| `codexRoleTiers?` | `{ fast?, standard?, frontier?: string[] }` | — | Capability tier of named models for [role auto-assign](/guides/integrations/#auto-assign). Listed models take that tier; other models are tiered by measured score in the role's domain when two or more have one, without requiring prices. Equal scores share a tier. Otherwise price tiering excludes unpriced, unlisted models. | +| `codexRoleCapability?` | `{ artificialAnalysis?: { apiKey?: string } }` | No measured source | Measured-score source for [role auto-assign](/guides/integrations/#auto-assign). Artificial Analysis uses your API key (literal, `${ENV}` or keychain reference) once per day; scores stay out of the response. Missing or unavailable data falls back to configured tiers and prices. A retired `huggingFace` key is ignored. | | `effortCap?` | `string` | — | Hard ceiling for qualifying v2 main turns and marked spawned-child turns. Accepts `low` through `ultra`. | | `subagentEffortCap?` | `string` | — | Additional ceiling for spawned-child turns only. When both caps apply, the lower wins. | | `plaintextV2AgentMessages?` | `boolean` | — (unset) | Experimental opt-in. It runs only when explicitly set to `true` and asks eligible native ChatGPT v2 parents to emit `spawn_agent`, `send_message`, and `followup_task` message arguments as plaintext. See [Plaintext v2 agent messages](#plaintext-v2-agent-messages). | diff --git a/docs-site/src/content/docs/reference/configuration/catalog.md b/docs-site/src/content/docs/reference/configuration/catalog.md new file mode 100644 index 00000000000..360cf70e9cf --- /dev/null +++ b/docs-site/src/content/docs/reference/configuration/catalog.md @@ -0,0 +1,32 @@ +--- +title: Catalog options +description: Optional native model omission in the generated Codex catalog. +--- + +## Omit disabled native models + +`omitDisabledNativeModels` is an optional boolean in `config.json`, defaulting to `false`. +Enable it and run `ocx sync` to omit explicitly disabled native rows without requiring a +native-alias combo: + +```json +{ + "omitDisabledNativeModels": true, + "disabledModels": ["gpt-5.5"] +} +``` + +The setting applies to sync, convergence, the generated models cache, and Codex-shaped +`/v1/models?client_version=…` discovery. A bare native disable omits that model and all its +account-selector rows; an account-qualified disable omits only that selector row. +The native inheritance template is validated and captured before omission, so routed +rows keep the same input and tool metadata, including when the only template row is disabled. + +Absent, false, or malformed values preserve the default hidden-row behavior. +Existing native-alias omission remains independent of this flag. This setting controls +discovery only: direct requests to disabled native ids still route normally, and unchanged +sync output keeps its existing no-op write and mtime behavior. + +See [Codex App models](/guides/codex-app-models/#what-this-means-for-a-disabled-native-model) +for Desktop visibility and [provider configuration](/reference/configuration/providers/) +for `disabledModels`. diff --git a/docs-site/src/content/docs/reference/configuration/keychain.md b/docs-site/src/content/docs/reference/configuration/keychain.md new file mode 100644 index 00000000000..c8a4e40d384 --- /dev/null +++ b/docs-site/src/content/docs/reference/configuration/keychain.md @@ -0,0 +1,55 @@ +--- +title: Storing keys in the OS keychain +description: Storing provider API keys and Vertex credentials in macOS Keychain, Windows Credential Manager, or Linux Secret Service. +--- + +For Vertex, the dedicated API-key pool input (`POST /api/providers/keys`) accepts complete GCP +`service_account` or `authorized_user` credential JSON after the provider has been created. +The provider create/edit form and `ocx provider add --api-key` refuse credential JSON before +saving: create the provider without a key, then add the JSON through its key pool. Paste the +JSON contents, not a Windows, Unix, or relative file path. Multiple +JSON objects separated by commas or whitespace are stored together as separate pool entries; +the first becomes active. Configuration contains only `gcp-sa:` references, never the +credential JSON. Mixed JSON/literal-key bulk pastes are rejected as a whole; add literal keys +separately. Storage failures leave the configuration unchanged and report a fixed error without +echoing credential material. Empty marker accounts are invalid and do not fall back to host credentials. +Paste one credential object or a sequence of objects, without an enclosing array or quoted string. +Malformed credential text is rejected. If a selected stored credential is removed or becomes unreadable, +Vertex refuses the request even when it previously cached an access token for that credential. +Removing a key or provider, or replacing a provider with `ocx provider add --force`, deletes its unreferenced GCP credentials after the configuration is +saved; a credential referenced by another provider is retained. If OS-store cleanup fails, a +warning requests manual inspection without undoing the committed configuration removal. +Ordinary keychain store/restore actions leave GCP markers and JSON in the OS store. Restoring +credential JSON through a `keychain:` alias is refused, and cleanup preserves accounts still +referenced through either marker spelling. +Using a `keychain:` alias containing GCP JSON as an API key also refuses the request; select its +`gcp-sa:` reference for Vertex authentication instead. +Literal API keys must be plain token text: JSON objects, arrays and quoted JSON strings are refused +in literal-key resolution and restore, including wrappers containing escaped credential text. +Credential writes and marker persistence share the cleanup transaction lock, so concurrent +removal cannot delete the stored JSON between read-back and the marker commit. + +By default a provider's `apiKey` and `apiKeyPool` sit in `config.json` (mode 0600, atomic writes). +If you would rather keep the key material out of the file, move it into the OS credential store: + +```bash +ocx provider keychain deepseek status # store: file | env | keychain, and whether the keychain answers +ocx provider keychain deepseek store # move active key + pool into the OS keychain +ocx provider keychain deepseek restore # bring the plaintext back and delete the keychain items +``` + +The same operations are `GET`/`POST /api/providers/keychain`. After `store`, `config.json` holds +`"apiKey": "keychain:deepseek"` (pool entries `keychain:deepseek/`) and the secret lives under the +`opencodex.provider-api-key.v1` service in macOS Keychain, Windows Credential Manager, or the Linux +Secret Service. Backups of `config.json` therefore carry references only. Key rotation and failover +keep working: pool entries compare by reference, so a rotation never writes plaintext back. + +Before touching the config, `store` writes and reads back every entry; if the keychain is unavailable +or the read-back does not match, it refuses with 503 and leaves the file as it was. At request time +a reference that cannot be read yields no credential and one warning per key — there is no plaintext +fallback, by design. + +When not to opt in: a proxy running as a headless service (systemd, launchd, Task Scheduler) or in a +container usually has no unlocked keychain session, so requests would fail closed. Use an +`${ENV_VAR}` reference in the service environment there instead. Env references are left untouched +by `store`. diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 64b316c1e0c..7ed86852041 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -107,7 +107,7 @@ separate. Full request URLs such as `/api/v1/responses` are not provider base UR | --- | --- | --- | --- | | `providers` | `Record` | — | Map of provider name to provider config. | | `openaiProviderTierVersion?` | `2` | set by migration | Marks the single option-aware OpenAI projection as complete. | -| `disabledModels?` | `string[]` | — | Models hidden from Codex's catalog and `/v1/models`, but not blocked from direct proxy calls. A routed id is removed from listings. An account-qualified native id hides only that selector row; a bare native GPT id hides the bare row and every account-selector row for that model. The dashboard Models page exposes only routed and bare native rows; use this configuration field directly to hide one selector-qualified row. | +| `disabledModels?` | `string[]` | — | Models hidden from Codex's catalog and `/v1/models`, but not blocked from direct proxy calls. A routed id is removed from listings. An account-qualified native id hides only that selector row; a bare native GPT id hides the bare row and every account-selector row for that model. The dashboard Models page exposes only routed and bare native rows; use this configuration field directly to hide one selector-qualified row. Set [`omitDisabledNativeModels`](/reference/configuration/catalog/) to `true` to omit disabled native rows instead; it defaults off. | | `providerContextCaps?` | `Record` | `{}` | Active provider context limits. Ordinary windows are lowered; native models with a supported long window can expand only up to their own supported ceiling. | | `providerContextCapValues?` | `Record` | `{}` | Last selected provider limits, retained while disabled. These values do not activate a cap. An enabled value takes precedence over a remembered value. | | `contextCapValue?` | `number` | `350000` | Default used on first enable. A later enable restores the selected provider value. Updating the global value with `setAll: true` changes enabled caps only; `setAll: true` without a value enables all configured providers at the current global value. | @@ -326,7 +326,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity | `preserveResponsesMessageMetadata?` | `boolean` | Default off. Preserve private `internal_chat_message_metadata_passthrough` on input items for explicitly trusted launcher relays, including custom native v1 compact requests. This forwards private turn/session metadata to that destination; do not enable for ordinary gateways. | | `dropResponsesReasoningItems?` | `boolean` | Remove replayed Responses `reasoning` items from continuation input entirely, including custom native v1 compact requests, instead of forwarding them with blanked content. This is lossy: summaries, item ids, and `encrypted_content` go with them, so no reasoning item from the previous turn remains in the forwarded input. It does not remove `previous_response_id`, so an upstream holding its own turn state may still reach it. Enable only for an upstream that rejects reasoning replay outright — the built-in Volcengine Ark Coding Plan preset sets it because validated tool continuations answered `400 InvalidParameter` otherwise. Set it to `false` on that provider to forward reasoning items again. | | `disabled?` | `boolean` | Keep the provider on disk but exclude it from routing and model/catalog listings. | -| `apiKey?` | `string` | API key, an `${ENV_VAR}` / `$ENV_VAR` reference, or a `keychain:` reference written by `ocx provider keychain store`. References resolve at request time. See [Storing keys in the OS keychain](#storing-keys-in-the-os-keychain). | +| `apiKey?` | `string` | API key, an `${ENV_VAR}` / `$ENV_VAR` reference, or a `keychain:` reference written by `ocx provider keychain store`. References resolve at request time. See [Storing keys in the OS keychain](/reference/configuration/keychain/). | | `apiKeyTransport?` | `"x-api-key" \| "bearer"` | Anthropic key header style. Defaults to native `x-api-key`; valid only for key-auth `anthropic` providers. | | `apiKeyPool?` | `ApiKeyPoolEntry[]` | Multi-key pool. `apiKey` mirrors the active entry; each item has `id`, `key`, optional `label`, and optional numeric `addedAt`. | | `apiKeyPoolStrategy?` | `"round-robin" \| "fill-first" \| "quota"` | How a warm key is chosen **before** the first attempt when the committed key is already cooling. Omitted keeps rotation reactive-only: the pool moves after a 429 or 401 and not before. `round-robin` takes the next key in the pool, `fill-first` keeps the first eligible one, and `quota` prefers the key with the most remaining headroom, falling back to `fill-first` order for a provider whose per-key quota is unknown. A healthy committed key is never overridden, so a manual key selection stands. | @@ -335,7 +335,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity | `liveModels?` | `boolean` | Fetch the live catalog on start/sync (default `true`). Custom providers use `${baseUrl}/models`; built-ins may use a registry URL and filter. | | `selectedModels?` | `string[]` | Catalog allowlist after discovery. Non-empty exposes only those ids; empty or omitted exposes all discovered models. | | `retainModels?` | `string[]` | Ids kept in the catalog even when live discovery omits them. They need not be repeated in `models`. Empty or omitted keeps today's behavior. | -| `modelDisplayNames?` | `Record` | Durable labels used only for display, keyed by this provider's exact upstream model id. Labels win over provider catalog metadata, survive discovery refreshes and provider edits, and never change authentication, adapter behavior, routing, billing, upstream request construction, the routed `provider/model` selector, or the upstream wire model. Keys are exact and case sensitive. Unknown model ids are kept so a temporarily missing model receives its label when it returns. The map accepts at most 2,000 entries, matching the discovery limit. | +| `modelDisplayNames?` | `Record` | Durable labels used only for display, keyed by this provider's exact upstream model id. Labels win over provider catalog metadata, survive discovery refreshes and provider edits, and never change authentication, adapter behavior, billing, upstream request construction, the routed `provider/model` selector, or the wire model of an id the provider already knows. One routing exception: when a direct `provider/model` selector matches no known id, alias or `retainModels` id (for example while a live roster is still cold), a key whose encoded form matches is sent as the native id. Keys are exact and case sensitive. Unknown model ids are kept so a temporarily missing model receives its label when it returns. The map accepts at most 2,000 entries, matching the discovery limit. | | `contextWindow?` | `number` | Provider-wide context fallback when upstream metadata is absent; otherwise a cap that retains smaller live metadata. Registry defaults cannot override a lower configured limit; explicit `modelContextWindows` entries take priority. The Models dashboard exposes this separately from `providerContextCaps`. | | `modelContextWindows?` | `Record` | Per-model context fallbacks/caps. These override `contextWindow`: an unknown window uses the configured value, while smaller live metadata remains authoritative. | | `inferContextWindow?` | `boolean` | Custom providers only, default off. `true` fills a missing window from bundled model-ID metadata ([details](#context-windows-on-custom-providers)); the value is an estimate, not verified relay capacity. | @@ -370,6 +370,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity | `xaiResponsesXSearch?` | `boolean` | Disabled by default. On an xAI Responses destination, append the provider-hosted `x_search` declaration only when a live `web_search` tool survives final request normalization. Existing declarations are not duplicated, caller `tool_choice`/`allowed_tools` selectors are never widened, and this is separate from the web-search sidecar's `search.xSearch` options. | | `modelPreferHostedTools?` | `Record` | Exact-model opt-in for non-forward Responses gateways that reserve a hosted-tool namespace. Currently accepts only `["image_generation"]`; a matching model must use the `openai-responses` wire and support that hosted tool. It removes colliding client `image_gen` declarations and rewrites their selectors to preserve caller tool choice. For OpenAI API virtual `-pro` models, the selected public ID is matched first and the resolved base wire-model ID is a fallback. `modelAdapters` resolves the public ID first, then the base ID; the second resolution determines the final wire. Other models retain normal alias behavior. | | `annotateEmptyToolOutputs?` | `boolean` | Replace a present-but-empty tool result with a short marker before it reaches the model, so a blank result is not read as a missing one. Applies to blank strings and text-only part arrays; image, file, and encrypted parts are never touched. Defaults to `true` for DeepSeek from the built-in registry and is otherwise unset. Set `false` to opt a provider out — an explicit `false` is preserved across later edits that omit the field. `PATCH /api/providers?name=` accepts `true`, `false`, or `null` to clear the override and return to registry-default behavior. | +| `promoteAdditionalTools?` | `boolean` | Whether to promote Codex Desktop's `additional_tools` input items into top-level `tools` and strip the wrapper from `input`. Enabled automatically for OpenCode Go and xAI (`api.x.ai` and `cli-chat-proxy.grok.com`) unless `authMode` is `"forward"`, where promotion needs an explicit `true`; other OpenAI-strict third-party gateways that return HTTP 422 for unknown item types in `input` can opt in here. `PATCH /api/providers?name=` accepts a boolean or `null` to clear it. | | `unsupportedHostedTools?` | `string[]` | Hosted tool declarations this Responses destination rejects, so they are stripped from `tools`, from client-loaded `additional_tools`, and from `tool_choice` instead of being forwarded and rejected upstream. Use it for an OpenAI-compatible gateway with a narrower capability set — one that accepts plain Responses requests and `function` tools but returns HTTP 400 for hosted `web_search` — so a text-only prompt is not failed by a capability it never needed. Accepts only hosted tool type names (`web_search`, `web_search_preview`, `file_search`, `computer_use_preview`, `computer_use`, `code_interpreter`, `image_generation`, `image_gen`, `mcp`, `tool_search`, `local_shell`, `x_search`); an unrecognized name is rejected rather than silently ignored. Spelling variants of one capability are aliased, so `["web_search"]` also denies `web_search_preview`. A provider cannot both deny a hosted tool here and prefer it in `modelPreferHostedTools`. Xiaomi MiMo Responses destinations (`xiaomimimo.com` and its subdomains, including `api.xiaomimimo.com` and `token-plan-cn.xiaomimimo.com`) automatically strip `web_search` and `web_search_preview` while preserving function tools; no declaration is needed for these hosts. This is independent of `supportsResponsesCustomTools`; set both for a gateway that also rejects native custom tools. `PATCH /api/providers?name=` accepts an array or `null` to clear it. | | `reasoningEffortMap?` | `Record` | Provider-wide wire aliases for reasoning labels. Map a label to `"__omit__"` to drop the reasoning field from the upstream request entirely: `reasoning_effort` on an OpenAI-compatible wire, and Ollama's native `think` field on the Ollama native adapter (#2356). | | `modelReasoningEffortMap?` | `Record>` | Per-model wire aliases for reasoning labels. Map a label to `"__omit__"` to drop the reasoning field from the upstream request entirely. | @@ -381,7 +382,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity | `noPenaltyModels?` | `string[]` | Models that reject presence/frequency penalties. The built-in `xai` preset lists its reasoning models (`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`), which xAI documents as rejecting them; non-reasoning ids keep caller penalties. | | `noStructuredOutputModels?` | `string[]` | Exact model IDs whose `openai-chat` endpoint rejects `response_format`. Only an exact requested-model match omits the field; structured-output translation stays enabled for every other `openai-chat` model. | | `noJsonSchemaModels?` | `string[]` | Exact model IDs whose `openai-chat` endpoint rejects a `json_schema` `response_format` but still accepts `json_object`. Such a request is downgraded to `json_object` instead of being dropped, so a caller asking for JSON still gets JSON. `noStructuredOutputModels` wins when a model is on both lists. The `opencode go` and `opencode zen` presets ship this for their DeepSeek routes. | -| `foldDeveloperRoleToSystem?` | `boolean` | Whether an `openai-chat` destination accepts the `developer` role. `foldDeveloperRoleToSystem` unset sends `system`, `true` sends `system`, and `false` sends `developer`. Unset means nothing has been recorded about this destination; `true` records an upstream that rejects the role; `false` records one that accepts it. The message keeps its position in the conversation in every case — only the role changes. A destination that rejects the role answers `400 role 'developer' is not allowed` and the turn never starts, which is why the unrecorded state is the folded one. A provider save that keeps the destination keeps it; see [What a provider save keeps](#what-a-provider-save-keeps). `PATCH` accepts a boolean or `null` to clear it. | +| `foldDeveloperRoleToSystem?` | `boolean` | Whether an `openai-chat` destination accepts the `developer` role. `foldDeveloperRoleToSystem` unset sends `system`, `true` sends `system`, and `false` sends `developer`. Unset means nothing has been recorded about this destination; `true` records an upstream that rejects the role; `false` records one that accepts it. Unless [single-leading-system compatibility](/guides/lm-studio/) is enabled, the message keeps its position in the conversation in every case — only the role changes. A destination that rejects the role answers `400 role 'developer' is not allowed` and the turn never starts, which is why the unrecorded state is the folded one. A provider save that keeps the destination keeps it; see [What a provider save keeps](#what-a-provider-save-keeps). `PATCH` accepts a boolean or `null` to clear it. | | `omitReasoningEffortWithToolsModels?` | `string[]` | Exact `openai-chat` model IDs that accept a reasoning-effort field on an ordinary turn but reject it once function tools are present. The model keeps its advertised effort ladder; OpenCodex omits the wire field for tool-bearing requests only and the upstream default applies. Narrower than `noReasoningModels`, which strips reasoning from every request and costs the model its picker entirely. A provider save that keeps the destination keeps it; see [What a provider save keeps](#what-a-provider-save-keeps). | | `parallelToolCalls?` | `boolean` | Toggle parallel tool calls. OpenAI Chat defaults on; non-chat adapters advertise only on explicit `true`. | | `terminalContinuationGuard?` | `boolean` | Opt in an `openai-chat` provider to one bounded internal re-ask when an actionable turn announces work, then cleanly stops without a tool call. Defaults to `false`; explicit `false` behaves like omission. Combo attempts and routed compaction turns are excluded, and non-`openai-chat` adapters ignore this option. | @@ -487,15 +488,15 @@ and `modelContextWindows` for that exact id when needed. Unknown ids are not gue ### What a provider save keeps -`POST /api/providers` with the name of an existing provider replaces the stored row with one built from the request. The dashboard's add/edit form cannot send every field, so the save keeps some stored fields the request omits. Eight of them record how one upstream behaves: `preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx` and `retryOnReset`. +`POST /api/providers` with the name of an existing provider replaces the stored row with one built from the request. The dashboard's add/edit form cannot send every field, so the save keeps some stored fields the request omits. Eleven of them record how one upstream behaves: `preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx` and `retryOnReset`. -| Save | The eight settings | Stored `apiKeyPool` | +| Save | The eleven settings | Stored `apiKeyPool` | | --- | --- | --- | | Same destination, field omitted | Stored value kept, including an explicit `[]` or `false` | Kept | | New destination, field omitted | Not kept; registry defaults for the new destination may apply | Not kept | | Field sent in the request | The request's value | The request's value | -The destination is the adapter, the base URL (scheme and host compared without regard to case, trailing slashes ignored) and, when the request names one, the auth mode. Moving a provider to another destination drops the eight settings because they describe the previous upstream, and drops the key pool because its keys were issued for it. A save never merges the rest of the old row into the new one. +The destination is the adapter, the base URL (scheme and host compared without regard to case, trailing slashes ignored) and, when the request names one, the auth mode. Moving a provider to another destination drops the ten settings because they describe the previous upstream, and drops the key pool because its keys were issued for it. A save never merges the rest of the old row into the new one. `hideRawReasoning` is an operator display policy, so a save of the same provider name keeps its stored value whenever the request omits it, including after a destination move. A value sent in the request wins, including `false`. @@ -503,7 +504,7 @@ The optional `inferContextWindow` setting also survives same-name saves that omi destination moves. Its read, update, and clear behavior is described under [Context windows on custom providers](#context-windows-on-custom-providers). -`PATCH /api/providers?name=` changes only the fields it names and keeps every other stored field, whatever the destination. It accepts all eight settings; `null` clears one. It also accepts a boolean `hideRawReasoning` or `null` to clear it. For the two reasoning lists an empty array is stored as an explicit opt-out rather than removed. +`PATCH /api/providers?name=` changes only the fields it names and keeps every other stored field, whatever the destination. It accepts all ten settings; `null` clears one. It also accepts a boolean `hideRawReasoning` or `null` to clear it. For the two reasoning lists an empty array is stored as an explicit opt-out rather than removed. With `webSearchBridge` enabled, a search continuation stays bound to the API-key selection that served the first request. Changing the selected key, its reference or resolved value, authentication @@ -1589,60 +1590,11 @@ same mapping applies to a native `vercel/` selector: use the encoded `vercel-ai-gateway/vercel-` selector in OpenCodex and keep `vercel/` as the model key. -## Static model allowlists - ## Storing keys in the OS keychain -For Vertex, the dedicated API-key pool input (`POST /api/providers/keys`) accepts complete GCP -`service_account` or `authorized_user` credential JSON after the provider has been created. -The provider create/edit form and `ocx provider add --api-key` refuse credential JSON before -saving: create the provider without a key, then add the JSON through its key pool. Paste the -JSON contents, not a Windows, Unix, or relative file path. Multiple -JSON objects separated by commas or whitespace are stored together as separate pool entries; -the first becomes active. Configuration contains only `gcp-sa:` references, never the -credential JSON. Mixed JSON/literal-key bulk pastes are rejected as a whole; add literal keys -separately. Storage failures leave the configuration unchanged and report a fixed error without -echoing credential material. Empty marker accounts are invalid and do not fall back to host credentials. -Paste one credential object or a sequence of objects, without an enclosing array or quoted string. -Malformed credential text is rejected. If a selected stored credential is removed or becomes unreadable, -Vertex refuses the request even when it previously cached an access token for that credential. -Removing a key or provider, or replacing a provider with `ocx provider add --force`, deletes its unreferenced GCP credentials after the configuration is -saved; a credential referenced by another provider is retained. If OS-store cleanup fails, a -warning requests manual inspection without undoing the committed configuration removal. -Ordinary keychain store/restore actions leave GCP markers and JSON in the OS store. Restoring -credential JSON through a `keychain:` alias is refused, and cleanup preserves accounts still -referenced through either marker spelling. -Using a `keychain:` alias containing GCP JSON as an API key also refuses the request; select its -`gcp-sa:` reference for Vertex authentication instead. -Literal API keys must be plain token text: JSON objects, arrays and quoted JSON strings are refused -in literal-key resolution and restore, including wrappers containing escaped credential text. -Credential writes and marker persistence share the cleanup transaction lock, so concurrent -removal cannot delete the stored JSON between read-back and the marker commit. - -By default a provider's `apiKey` and `apiKeyPool` sit in `config.json` (mode 0600, atomic writes). -If you would rather keep the key material out of the file, move it into the OS credential store: - -```bash -ocx provider keychain deepseek status # store: file | env | keychain, and whether the keychain answers -ocx provider keychain deepseek store # move active key + pool into the OS keychain -ocx provider keychain deepseek restore # bring the plaintext back and delete the keychain items -``` +API keys and credential references can be stored in the OS credential store (macOS Keychain, Windows Credential Manager, Linux Secret Service) instead of `config.json`. See [Storing keys in the OS keychain](/reference/configuration/keychain/) for details on migration commands, storage rules, and headless-service caveats. -The same operations are `GET`/`POST /api/providers/keychain`. After `store`, `config.json` holds -`"apiKey": "keychain:deepseek"` (pool entries `keychain:deepseek/`) and the secret lives under the -`opencodex.provider-api-key.v1` service in macOS Keychain, Windows Credential Manager, or the Linux -Secret Service. Backups of `config.json` therefore carry references only. Key rotation and failover -keep working: pool entries compare by reference, so a rotation never writes plaintext back. - -Before touching the config, `store` writes and reads back every entry; if the keychain is unavailable -or the read-back does not match, it refuses with 503 and leaves the file as it was. At request time -a reference that cannot be read yields no credential and one warning per key — there is no plaintext -fallback, by design. - -When not to opt in: a proxy running as a headless service (systemd, launchd, Task Scheduler) or in a -container usually has no unlocked keychain session, so requests would fail closed. Use an -`${ENV_VAR}` reference in the service environment there instead. Env references are left untouched -by `store`. +## Static model allowlists The `zhipu-bigmodel-responses` preset seeds `glm-5.3`, `glm-5.3-flash` and `glm-5-turbo` with `liveModels: false` for `https://open.bigmodel.cn/api/v1`. Its static roster and diff --git a/docs-site/src/content/docs/reference/configuration/routing.md b/docs-site/src/content/docs/reference/configuration/routing.md index e518b009789..63a3a0eae1f 100644 --- a/docs-site/src/content/docs/reference/configuration/routing.md +++ b/docs-site/src/content/docs/reference/configuration/routing.md @@ -401,6 +401,29 @@ CLI: `ocx logs history`, `ocx logs get `, `ocx logs explain `, `ocx logs rebuild-index`, `ocx logs index-status`, `ocx route policy list | show | dry-run | evaluate`. +### Index validation and recovery + +An existing history index is checked in a read-only worker when first opened. +History queries wait for that initial check without blocking the proxy's serving +thread. Synchronous routing reads report busy during validation, so routing +profiles apply their `unknownEvidence.health` policy. A successful small inline +rebuild is usable immediately; a worker rebuild is checked before publication. + +After a successful check or rebuild, the next read at least five minutes later +starts one background integrity check. Reads from the verified index continue +during that check. Normal refreshes retain schema, source-identity and offset +validation without a full synchronous scan, and an idle index starts no timer. +Temporary check failures, such as a busy database or unavailable worker, leave +the index intact and retry on a subsequent read after a 30-second backoff. +An index that has not completed initial validation remains unavailable meanwhile. + +Detected corruption triggers automatic reconstruction from `usage.jsonl`, with +one retry for an affected asynchronous history or analytics read. Closing or +rebuilding the index discards old check results and waits for the scanner to +release the file before replacing it. Validation and recovery never rewrite the +canonical ledger. The initial check still takes time proportional to index size; +later background checks run only when the index is used. + ## Migration `routingProfiles` is optional and additive: existing config files load diff --git a/docs-site/src/content/docs/reference/configuration/server.md b/docs-site/src/content/docs/reference/configuration/server.md index 8c9f113168b..98636bbedba 100644 --- a/docs-site/src/content/docs/reference/configuration/server.md +++ b/docs-site/src/content/docs/reference/configuration/server.md @@ -8,8 +8,18 @@ runs helper features around provider requests. ## Server fields +`completeOnMaxOutputTokens` is an optional boolean, default `false`. Set it to `true` to +report a token-limited translated Responses text answer as `completed` rather than `incomplete`. +The answer remains truncated: OpenCodex preserves its exact text, including unfinished Markdown. +Only nonempty plain-text output qualifies. Tool calls and hosted operations (even if completed), +reasoning, `text.format` with `json_schema` or `json_object`, compaction, explicitly unfinished +turns, errors and missing terminal events remain excluded. Context-window exhaustion and +`pause_turn` remain incomplete. Chat Completions, Anthropic Messages and native Responses +passthrough, including native WebSocket output, retain their existing stop behavior. + | Field | Type | Default | Meaning | | --- | --- | --- | --- | +| `completeOnMaxOutputTokens?` | `boolean` | `false` | Opt in to completing eligible token-limited translated Responses text output. Text stays truncated; see the eligibility and exclusions above. | | `port` | `number` | `10100` | Proxy listen port. | | `hostname?` | `string` | `"127.0.0.1"` | Bind address. A non-loopback bind requires a data-admission token, resolved from `OPENCODEX_API_AUTH_TOKEN`, then `OCX_API_TOKEN_FILE`, then the installed owner-only `service-api-token` — nothing has to be exported by hand. See [Remote access](#remote-access). | | `proxy?` | `string` | — | Outbound HTTP(S) or SOCKS5 proxy URL (`socks5://host:port`), `${ENV_VAR}`, or `"auto"`. HTTP URLs apply to `HTTP_PROXY` / `HTTPS_PROXY` when those are unset. SOCKS5 URLs use OpenCodex's real SOCKS5 transport and are also exposed through `ALL_PROXY` (`ocx start --socks5`); inherited `HTTP(S)_PROXY` is cleared in this process. Loopback stays in `NO_PROXY`. `"auto"` reads Windows WinINET or macOS static HTTP/HTTPS settings once at startup. Inherited HTTP(S) proxy variables skip discovery; on macOS, inherited `ALL_PROXY`/`all_proxy` also skips it. Windows keeps separate `http=` and `https=` entries; a bare `ProxyServer` applies to both. macOS translates IP literals, `*`, and a valid `*.` glob to Bun's `.` bypass. That glob also bypasses the bare apex ``. The exact link-local ranges `169.254/16`, `169.254.0.0/16`, and `fe80::/10` are omitted with a diagnostic: link-local IP literals use the proxy. Other CIDRs, globs, and simple-host exceptions refuse discovery without changing the proxy environment. Disabled, malformed, PAC/WPAD, SOCKS-only, and live changes are not followed. Restart after changing system settings. | @@ -24,6 +34,7 @@ runs helper features around provider requests. | `codexNativeSteering?` | `boolean` | `false` | Experimental, native-only mid-turn steering on the Responses WebSocket endpoint. Requires `websockets: true`, a compatible upstream/client, and a pinned account/model/tool surface. Validated generation settings can change in explicit saved-result continuations. Does not enable translated models or HTTP fallback. See [native steering](/guides/codex-integration/#experimental-native-mid-turn-steering). | | `codexNativeInjection?` | `boolean` | `false` | Experimental saved function-result injection on compatible native multi-agent WebSocket turns. Requires `websockets: true`, explicit `multi_agent.enabled`, and an eligible provider. Separate from steering; no automatic tool rerun or recovery create. See [native injection](/guides/codex-integration/#experimental-native-function-result-injection). | | `nativeReasoningRetention?` | `{ modelSwitch?: boolean; accountSwitch?: boolean }` | both `false` | Permit reasoning ciphertext forwarding across a known model or account change on the same canonical ChatGPT backend. Account retention is experimental. See [Native reasoning retention](#native-reasoning-retention). | +| `workflowBudget?` | `{ maxConcurrentChildren?: number; maxPhysicalSends?: number; maxDistinctChildren?: number }` | `8` / `256` / `64` | Per-task fan-out ceilings: turns running at once, upstream sends per 10-minute window, and distinct subagent threads per window. Omitted fields keep the built-in value. See [Workflow limits](#workflow-limits). | | `corsAllowOrigins?` | `string[]` | `[]` | Additional exact origins allowed by CORS. Loopback origins are always allowed. Authority-based browser extension origins such as `chrome-extension://` are supported; `*` is not a wildcard. Firefox and Safari regenerate the extension UUID (per install / per browser launch), so update the entry when the origin changes. | | `apiKeys?` | `OcxApiKey[]` | `[]` | Generated `ocx_…` data-plane admission credentials on non-loopback binds. They do not authorize management APIs; management access uses the separate credential documented in the [management reference](/reference/management-api/). Dashboard-managed. | | `storageCleanupPolicy?` | `StorageCleanupPolicy` | disabled | Opt-in archived-session cleanup policy. Never enabled implicitly. | @@ -77,6 +88,29 @@ Retention means that OpenCodex may forward the carried ciphertext. It cannot con Use `GET /api/native-reasoning-retention` to read the resolved booleans and `PUT` with a partial boolean object to update them. `PUT` with JSON `null` restores both defaults. Invalid fields or unknown keys are rejected; a malformed file block disables retention with a warning. Dashboard/API changes apply to subsequent requests. Local `ocx config set/unset` edits require a restart to converge the running proxy. +## Workflow limits + +OpenCodex bounds how much one task may fan out. A task is the root conversation, identified by the client's `x-codex-parent-thread-id` header, together with every subagent it starts. Three count ceilings apply to each task, and requests over any of them are refused locally with HTTP 429 and an `x-opencodex-local-refusal` header naming the ceiling (`workflow_concurrency_exhausted`, `workflow_sends_exhausted` or `workflow_children_exhausted`); no provider is contacted. + +| Field | Default | Allowed | Meaning | +| --- | --- | --- | --- | +| `maxConcurrentChildren` | `8` | 2–64 | Turns one task may run at the same time. One slot is always reserved for the task's own conversation, so subagents may use one fewer. | +| `maxPhysicalSends` | `256` | 1–10000 | Upstream sends, retries included, after which the task's new requests are refused for the rest of the 10-minute window. The count is checked when a request is admitted, so retries inside a request that was already admitted, and requests admitted concurrently, can take the total past the ceiling. | +| `maxDistinctChildren` | `64` | 1–1024 | Distinct subagent threads one task may start inside the same window. | + +The defaults suit most installs. Raise them when a workflow that legitimately runs many subagents in parallel keeps hitting a ceiling; lower them to stop a runaway fan-out sooner: + +```jsonc +{ + "workflowBudget": { + "maxConcurrentChildren": 16, + "maxPhysicalSends": 1024 + } +} +``` + +The **Subagents** page of the dashboard shows the effective ceilings and edits them. `GET /api/workflow-budget/settings` returns the effective values with their defaults and allowed ranges; `PUT` with a partial object updates them, and JSON `null` restores the defaults. Dashboard/API changes apply to the next request without a restart and never reset what a task has already used; `POST /api/workflow-budget/clear` remains the way to forgive one task. A malformed file block is ignored with a warning and the built-in ceilings stay in force. Local `ocx config set/unset` edits require a restart to converge the running proxy. The 10-minute window, the reserved interactive slot and the 512 tracked tasks are not configurable. + ## Upstream transport recovery While the canonical ChatGPT upstream WebSocket waits for the first Responses event after @@ -676,6 +710,7 @@ These settings govern `/v1/messages`, `/v1/messages/count_tokens`, the `ocx clau | `claudeCode.authModeMigratedAt?` | `string` | unset | Internal one-time upgrade marker. Do not set manually. | | `claudeCode.classifierModel?` | `string` | unset | Explicit target for Claude Code Auto Mode classifier turns, as a qualified `provider/model` (for example `RelayA/claude-opus-5`). Auto Mode sends bare safety checks such as `claude-opus-5` with no provider, so without this they fall through to `defaultProvider` — which may not speak Anthropic at all. Nothing is inferred automatically: only a target you declare here is used. | | `claudeCode.classifierFallbacks?` | `string[]` | unset | Ordered classifier targets used when `classifierModel` is not set. Same qualified `provider/model` form; the first usable entry wins. An explicit `modelMap` entry for the classifier model still outranks both. | +| `claudeCode.desktopModelVisibility?` | `{ mode: "include" \| "exclude"; routes: string[] }` | unset | Which `provider/model` routes the Claude Desktop gateway list carries. `include` lists only `routes` (new models stay hidden); `exclude` lists all but `routes` (new models appear). Unset lists every available model. If the selection leaves no available models, the writer falls back to every available model, including normally hidden routes; update the selection to narrow it again. Affects only Claude Desktop's static and hybrid lists; set it from **Connect → Claude Desktop**. | | `claudeCode.subagentEffort?` | `"low" \| "medium" \| "high" \| "xhigh" \| "max"` | inherit | Effort written to generated `~/.claude/agents/ocx-*.md`; separate from Codex guidance and proxy caps. Restart through `ocx claude` to regenerate. | The compatibility policy applies to Claude Code, Desktop and other clients using translated @@ -1201,3 +1236,5 @@ Locality is a timeout policy, not network-access authorization. Classification u Disabling the header clock preserves client cancellation and any enclosing overall deadline. An explicitly positive `stallTimeoutSec` still bounds local body silence; unset local stall/header clocks provide no automatic bound before headers. Native Chat/Messages local JSON reads use the local stall budget; public JSON reads retain their previous `max(connectTimeoutMs ?? 200000, 5000)` budget. Compact body reads retain their 300-second default even locally. Image/search sidecar budgets are unchanged. Optional `opencodeClientExport` controls the generated OpenCode provider's `headerTimeoutMs`, `chunkTimeoutMs`, and `timeoutMs` (positive milliseconds or `false` to disable). Unset values preserve OpenCode defaults; malformed fields are omitted. These provider-wide settings are emitted into V1 options and V2 settings. See [slow local models](/guides/opencode/#slow-local-models-prefill-timeouts). + +Optional, config-file-only `omoClientExport.api` selects the dialect of the managed Pi-based omo provider block: `openai-completions` (default) or `openai-responses`. Other values are ignored. Only omo reads it. After adding, changing or removing this key, restart the proxy (`ocx service restart` for a service install, or `ocx stop`, then `ocx start`), then refresh **Connect → omo** in the dashboard. Refresh uses the running proxy's config; it does not reload the file. See [integrations](/guides/integrations/). diff --git a/docs-site/src/content/docs/reference/management-api.md b/docs-site/src/content/docs/reference/management-api.md index 0ad0aff6c21..a85f352c5cb 100644 --- a/docs-site/src/content/docs/reference/management-api.md +++ b/docs-site/src/content/docs/reference/management-api.md @@ -86,12 +86,12 @@ route-specific results rather than repeating this table. | `POST /api/grok/reset-coupons/consume` | Redeem an eligible reset coupon. Body `{ accountId?, tokenId?, operationId? }`. Optional `operationId` (UUIDv4) makes redemption idempotent: repeating the same ID replays the durable result without double-redemption. | 400 invalid JSON/UUID; 401 unauthenticated; 409 `operation_id_owned_by_another_account` / `coupon_unavailable` / `operation_token_mismatch` / `operation_state_changed` / `attempt_in_progress` / `attempt_unresolved`; 502 upstream error; 503 ledger capacity / `ledger_unavailable`; 500 `attempt_mark_failed` | | `GET /api/anthropic/reset-grants?accountId=...` | Read the Claude usage-limit reset grants of one Anthropic OAuth account: eligibility, each grant's resets left, validity window, and the windows it clears, plus any unconfirmed attempt still retryable | 400 no matching account; 401 re-authentication needed; 502 upstream unavailable | | `POST /api/anthropic/reset-grants/consume` | Spend one reset grant. Body `{ accountId, grantId, operationId }`; `operationId` is a UUIDv4 sent upstream as the request ID, so repeating it retries the same claim. Requires a dashboard session. A settled `reset` lifts the account's local cooldown only when it is a quota refusal recorded before the claim was first journaled and the windows the grant reported cleared cover every window that refusal named (both windows when it named none). A refusal recorded while the claim was in flight, an entitlement refusal, or an uncovered window stays, including on a same-id replay. The cached quota is dropped and usage is reread with a fresh request, so the pool can select the account before the old deadline. Model-family exclusions stay until that reread proves a family is no longer rejected. | 400 invalid body; 401 re-authentication needed; 403 `session_required`; 409 `grant_not_usable`, `in_flight`, `unresolved_prior_operation`, `unknown_outcome_expired`, `operation_identity_mismatch`; 500 `journal_write_failed`; 502 `unknown_outcome`; 503 journal busy, unavailable, or full | -| `GET, PUT /api/claude-desktop` | Read or persist the Claude Desktop routed/native profile | 400 invalid or unavailable assignment | +| `GET, PUT /api/claude-desktop` | Read or persist the Claude Desktop routed/native profile and its model visibility. PUT requires a valid `profile`, including when setting or clearing `visibility` (`visibility`: omit to keep, `null` to clear); a visibility-only body returns 400 | 400 missing/invalid profile, invalid or unavailable assignment, invalid visibility, or a visibility that hides every available model; 409 profile or supplied visibility changed during discovery/save | | `POST /api/claude-desktop/apply` | Write the saved profile to Claude Desktop's managed config | 400/500 write failure | | `GET, POST /api/codex/desktop-compatibility/certificate` | Inspect or explicitly prepare Windows compatibility certificate trust | 403 paired local dashboard required for POST; 409 stale, busy, or incomplete state | | `GET, POST /api/codex/desktop-compatibility/runtime` | Inspect or explicitly start, stop, launch, or run a bounded native compatibility trial | 403 paired local dashboard required for POST; 409 unsupported build, untrusted certificate, or incomplete state | | `GET, POST /api/codex/desktop-compatibility/settings` | Read or save the next-start observation preference with its current revision | 403 paired local dashboard required for POST; 409 stale, invalid, or unconfirmed configuration | -| `GET /api/claude-desktop/status` | Inspect saved-versus-applied profile and Desktop health | 400 status read failure | +| `GET /api/claude-desktop/status` | Inspect saved-versus-applied profile, Desktop health, and `appliedModels: { name: string; label: string }[] \| null`. This is the selected profile's on-disk static list, not a verified observation of the running Desktop process; `null` means no applied gateway profile or no readable static list (including discovery-only mode), not an empty list | 400 status read failure | | `GET, PUT /api/claude-code` | Read or update Claude Code gateway, auth-mode, model-map, context, agent, and sidecar settings | 400 invalid field or shape | `tokenId`, when supplied, must be a non-empty string; invalid values return `400 invalid_token_id` before credentials are read or the ledger is opened. Within the 30-day retention window, a same-`operationId` retry that omits `accountId` uses the recorded account, even if the active account changed. An executable `open` retry that omits `tokenId` uses its recorded token. An explicit different account or token is still refused. A definitive failure clears a matching speculative pending hold in the dashboard, allowing a new explicitly confirmed attempt; actual unknown outcomes remain held. @@ -271,13 +271,14 @@ See [Codex prompt layers](/guides/codex-prompt/) for the layer model and the key | `PUT /api/config` | Disabled full-config replacement guard | 405; use focused endpoints instead | | `GET, PUT /api/settings` | Read runtime/startup settings or update auto-start, stream mode, app-owned memory budget, and `codexAccountPickerEnabled` | 400 invalid, non-object, or empty update | | `GET, PUT /api/native-reasoning-retention` | Read resolved model/account reasoning-retention booleans; partial PUT preserves omitted fields, JSON `null` restores defaults. See [native reasoning retention](/reference/configuration/server/#native-reasoning-retention). | 400 unknown field, invalid type or non-object/non-null body | +| `GET, PUT /api/workflow-budget/settings` | Read the effective per-task workflow ceilings with defaults and allowed ranges; partial PUT preserves omitted fields, JSON `null` restores defaults. See [workflow limits](/reference/configuration/server/#workflow-limits). | 400 unknown field, non-integer or out-of-range value, or non-object/non-null body | | `GET /api/startup-health` | Read cached service/shim startup health | — | | `POST /api/startup-action` | Install or repair the service or Codex shim | 400 invalid action; 500 action failure | | `GET, POST /api/windows-tray` | Read Windows tray state or install/start/stop/uninstall it | 400 unsupported platform/action; 500 operation failure | | `GET /api/diagnostics/project-config` | Read cached project configuration warnings | — | | `POST /api/sync` | Sync the current model catalog into Codex | 500 failed sync | | `GET /api/update/check` | Asynchronously check the `latest` or `preview` package channel and refresh the package cache on success | 400 invalid tag | -| `POST /api/update/run` | Asynchronously check a fresh package version, then start an update job, optionally followed by restart | 400 invalid body; job-specific conflict/error status | +| `POST /api/update/run` | Asynchronously check a fresh package version, then start an update job, optionally followed by restart | 400 invalid body; 409 `update_restart_unsupervised` when the proxy runs inside a systemd unit other than OpenCodex's own user service, or the worker cannot leave the service cgroup (with or without restart, since the update stops the proxy first); job-specific conflict/error status | | `GET /api/update/status` | Poll an update job by id | 404 unknown job | | `GET, PUT /api/sidecar-settings` | Read or update web-search and vision sidecar model/backend settings | 400 invalid shape, backend, or limit | | `GET, PUT /api/shadow-call-settings` | Read or update shadow-call interception settings, including `sourceModels`; GET also returns the runtime-owned `sourceModelOptions` inventory | 400 invalid shape or value, including an empty, duplicate, whitespace-bearing, or provider-qualified source prefix, or more than 32 prefixes / 128 characters per prefix; source-list failures carry `code: shadow_call_source_invalid`, target conflicts `code: shadow_call_target_invalid` | diff --git a/docs-site/src/content/docs/ru/guides/claude-code.md b/docs-site/src/content/docs/ru/guides/claude-code.md index 173a0be322c..298ad33242c 100644 --- a/docs-site/src/content/docs/ru/guides/claude-code.md +++ b/docs-site/src/content/docs/ru/guides/claude-code.md @@ -573,7 +573,7 @@ Claude Code — это лишь учётные данные для доступ | `tool_use` ассистента | `function_call` (`input` → `arguments` в виде JSON-строки) | | `tool_result` пользователя | `function_call_output` (`is_error` → префикс `[tool error]`) | | Повтор `thinking` / `redacted_thinking` | Элементы `reasoning` с ограниченными конвертами `ocxr1` для подписей и скрытых данных | -| Function-инструменты | `{type: "function"}` (`web_search*` → `{type: "web_search"}`). На переведённом маршруте к `api.anthropic.com` типизированные объявления `web_search_*` и `web_fetch_*` также передаются Anthropic для выполнения на сервере, если только сайдкар веб-поиска сам не предоставляет `web_search`. Поиск и загрузка возвращаются клиенту как `server_tool_use` и соответствующий `web_search_tool_result` / `web_fetch_tool_result` | +| Function-инструменты | `{type: "function"}` (`web_search*` → `{type: "web_search"}`). На переведённом маршруте к `api.anthropic.com` типизированные объявления `web_search_*` и `web_fetch_*` также передаются Anthropic для выполнения на сервере, если только сайдкар веб-поиска сам не предоставляет `web_search`. Поиск и загрузка возвращаются клиенту как `server_tool_use` и соответствующий `web_search_tool_result` / `web_fetch_tool_result` Поиск с `allowed_domains`, `blocked_domains` или `user_location` исключается из переведённых маршрутов Responses/Chat; принудительный вызов исключённого поиска возвращает 400. Корректные определения функций с `defer_loading` остаются доступными как обычные функции. См. [ограничения перевода](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes). | | `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, именованная функция→`{type:"function",name}`, размещённый WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/ru/guides/codex-app-models.md b/docs-site/src/content/docs/ru/guides/codex-app-models.md index 04a4381f8ee..e1f95d377a7 100644 --- a/docs-site/src/content/docs/ru/guides/codex-app-models.md +++ b/docs-site/src/content/docs/ru/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Селектор моделей Codex App description: Как модели opencodex появляются в Codex App, Codex CLI и Codex TUI через общий каталог Codex. --- +По умолчанию `false`. Установите `"omitDisabledNativeModels": true` в `config.json` и выполните `ocx sync`, чтобы исключить отключённые native-строки из каталога, кеша и HTTP-списка моделей в формате Codex. Проверенный до исключения шаблон сохраняет метаданные routed-строк. Маршрутизация прямых запросов не меняется. Описание скрытия ниже относится к настройкам по умолчанию. + opencodex не патчит Codex App. Он записывает ту же конфигурацию Codex и тот же каталог моделей, которыми пользуются Codex CLI/TUI. App-server читает это общее состояние, но некоторые версии Codex Desktop применяют в renderer дополнительный remote allowlist и могут удалить routed-строки diff --git a/docs-site/src/content/docs/ru/guides/combos.md b/docs-site/src/content/docs/ru/guides/combos.md index b20b666a408..ccb5444da6f 100644 --- a/docs-site/src/content/docs/ru/guides/combos.md +++ b/docs-site/src/content/docs/ru/guides/combos.md @@ -161,7 +161,8 @@ ocx combo set balanced \ | HTTP 401, 403, 404, 408, 429, или любой 5xx | Перевести цель в cooldown и перейти к следующей подходящей цели. | | HTTP 410 с явным признаком окончания срока службы модели, retirement, deprecated, sunset, decommissioned или недоступности | Перевести только эту цель в cooldown и перейти дальше. Несвязанные ответы 410 остаются terminal-ошибками. | | Классифицированная ошибка аутентификации, подписки, квоты, rate-limit, перегрузки или upstream-server | Перевести цель в cooldown и переключиться, даже если одного статуса недостаточно. | -| Отмена клиентом (499), `origin_rejected`, отказ из-за cyber-policy, переполнение контекста или иной некорректный запрос | Остановиться и вернуть ошибку; другая цель не сделает такой запрос корректным. | +| Отмена клиентом (499), `origin_rejected`, отказ из-за cyber-policy или иной некорректный запрос | Остановиться и вернуть ошибку; другая цель не сделает такой запрос корректным. | +| Подтверждённое превышение контекста до вывода, включая HTTP 400 | Попробовать следующую допустимую цель без cooldown; при исчерпании вернуть первую подтверждённую ошибку контекста HTTP 400 (отказ по контексту с другим статусом тоже переводит к следующей цели, но не сохраняется). | | Точный отказ HTTP 400 о том, что модель не поддерживается в Codex с аккаунтом ChatGPT | До начала вывода пробует следующую настроенную цель. Полная фраза отказа должна находиться в `detail`, `error.message` или обычном тексте; смешанные объекты `detail` и `error` завершают запрос. Сам этот отказ не переводит аккаунт в состояние повторного входа и не включает период ожидания для всех моделей провайдера. | | Структурированный HTTP 400, отклоняющий необязательный `user`, неподдерживаемое значение `reasoning.effort`/`reasoning_effort`, специфичный для модели отказ входного изображения (`param: input`) или отказ маршрута, который не может прочитать входные медиаданные (`unsupported_input_modality`) | До начала вывода переходит к следующей допустимой цели без охлаждения; см. «Совместимость необязательных параметров» ниже. | | Первый вызов инструмента в Responses-ходе внутрипроцессного адаптера (`runTurn`), который текущий запрос не объявлял, до любого вывода и до побочного эффекта, небезопасного для повтора | Охлаждает цель и переходит к следующей с тем же каталогом инструментов. После видимого вывода или небезопасного для повтора побочного эффекта отказ окончателен. Запросы Chat Completions и Anthropic Messages не меняются. | @@ -171,6 +172,8 @@ ocx combo set balanced \ Текущий запрос никогда не повторяет уже опробованную цель — с одним исключением: combo с единственной целью и заданным `waitForCooldownMs` может повторить свою единственную цель, когда её cooldown истекает внутри того же запроса, поскольку альтернативы для failover нет. Отказы совместимости, локальные для запроса, по-прежнему возвращаются без повторной попытки. Более поздние запросы пропускают её, пока не истечёт cooldown. HTTP-date в `Retry-After`, указывающий на уже прошедшее время, также сохраняется как немедленная директива upstream, как и `Retry-After: 0`. Задайте `waitForCooldownMs`, чтобы следующий запрос мог подождать cooldown цели, которая станет подходящей раньше всех, до этого ограничения при каждой попытке выбора, а затем выполнить один новый выбор. Поэтому запрос с несколькими hop в failover может ждать в общей сложности до `hops × waitForCooldownMs`. По умолчанию это `0`: если все подходящие цели находятся в cooldown, запрос немедленно завершается HTTP 503; этот ответ `combo_unavailable` содержит заголовок `Retry-After`, равный оставшемуся cooldown цели с самым ранним окончанием, округлённый вверх до целых секунд, минимум до 1 секунды. К ожиданиям не добавляется джиттер, поэтому возможны синхронные пробуждения. Отмена запроса отменяет это ожидание и возвращает обычный ответ `client_cancelled`; после отмены резервная цель не запускается. Cooldown цели combo — состояние процесса, отдельное для каждой combo; он не связан с cooldown квоты Codex на уровне аккаунта, который используется нативной маршрутизацией аккаунта. +Правило HTTP 503 выше относится к выбору до первой попытки. После подтверждённого отказа контекста HTTP 400 исчерпание допустимых целей или бюджета отправок возвращает первый отказ как `context_length_exceeded`, с приоритетом над последующими повторяемыми ошибками, включая квоту. Это относится и к оставшимся целям без совместимого повтора рассуждений или согласия на оплату. Отмена, последующий фатальный отказ и явный запрет политики fallback останавливают запрос немедленно; платная цель без согласия не вызывается. + :::note Failover намеренно ограничен. Он помогает при проблемах доступности конкретной цели, аутентификации, квоты и перегрузки; он не скрывает ошибки вызывающей стороны и отказы политики. @@ -326,8 +329,7 @@ CLI и дашборд показывают точное сообщение ва ### Почему failover остановился после первой ошибки? -Ошибка была terminal, а не специфичной для конкретной цели. Исправьте некорректный ввод, сократите -слишком большой контекст, обработайте отказ политики или исправьте отклонённое происхождение +Ошибка была terminal, а не специфичной для конкретной цели. Исправьте некорректный ввод, обработайте отказ политики или исправьте отклонённое происхождение запроса. В таких случаях combo не выполняют hop. ## Совместимость необязательных параметров diff --git a/docs-site/src/content/docs/ru/guides/integrations.md b/docs-site/src/content/docs/ru/guides/integrations.md index 22a006254e1..47299f9e003 100644 --- a/docs-site/src/content/docs/ru/guides/integrations.md +++ b/docs-site/src/content/docs/ru/guides/integrations.md @@ -557,6 +557,16 @@ OpenCodex требует явного `--overwrite-conflict`. Отключени поддерживает удалённую настройку допуска и требует loopback-доступа без аутентификации. +## Диалект Responses для omo (на базе Pi) + +Управляемый блок omo на базе Pi по умолчанию использует диалект Chat (`api: "openai-completions"`). Chat отбрасывает рассуждения предыдущего шага между вызовами инструментов, и в длинных циклах инструментов omo это заметно. Чтобы подключение и каждое обновление записывали диалект Responses, добавьте в конфигурацию opencodex этот ключ, который задаётся только в файле конфигурации: + +```json +{ "omoClientExport": { "api": "openai-responses" } } +``` + +Перезапустите прокси, чтобы он загрузил изменённую конфигурацию (`ocx service restart` для установки как службы, иначе `ocx stop`, затем `ocx start`), и обновите интеграцию omo в **Connect → omo** на дашборде. Одно обновление использует конфигурацию запущенного прокси и не перечитывает файл. Настройка действует только для omo: Pi, Gentle Shell и другие клиенты формата Pi остаются на `openai-completions`. Чтобы вернуться к Chat, удалите ключ, так же перезапустите прокси и обновите **Connect → omo**. Неизвестное значение игнорируется. opencodex по-прежнему владеет только `providers.opencodex` в `~/.omo/agent/models.json`, остальные провайдеры в этом файле не меняются. + ## Уровень рассуждений ролей omo (Codex / LazyCodex) Когда обнаружен LazyCodex, в разделе **omo (Codex / LazyCodex)** на вкладке omo можно выбрать модель и уровень рассуждений для каждой роли из `$CODEX_HOME/agents/*.toml` и нажать **Сохранить**. Строка роли без закреплённой модели может сохранить только уровень; роль при этом продолжает наследовать модель. opencodex меняет в файле роли только корневые ключи `model` и `model_reasoning_effort`, а отсутствующие значения добавляет ближе к началу файла. Инструкции роли, остальные комментарии и ключи остаются без изменений. Сохранение, которое меняет только модель, не отправляет `effort`, поэтому оба значения уровня сохраняются. Выбор уровня добавляет или заменяет `model_reasoning_effort` в файле роли и, если LazyCodex поддерживает этот уровень, записывает его в `[codex].agents..reasoning` в `~/.omo/omo.jsonc` (`ultra` остаётся только в файле роли, а прежний `reasoning` удаляется). В командной строке то же самое делает `ocx agent roles set --effort `. diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index 05df36d384b..526b10bd03e 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -177,7 +177,7 @@ OAuth-провайдеры, чьи учётные данные содержат несколько входов. Страница Providers показывает эти аккаунты в выпадающем списке, позволяет добавить ещё один и переключает активный аккаунт, не выполняя выход из остальных. При обычном входе учётные данные Kimi без идентификатора заменяют активный слот; явное действие **Добавить аккаунт** -сохраняет прежний слот и активирует отдельный новый. Аккаунты Kiro сохраняются по ARN профиля. +сохраняет прежний слот и активирует отдельный новый. Аккаунты Kiro различаются по ARN профиля и адресу электронной почты вошедшего пользователя, если он доступен; пользователи одного профиля организации могут хранить отдельные учётные данные. `chatgpt` всегда занимает один слот, поскольку у пула аккаунтов Codex отдельный реестр. Токены остаются в `~/.opencodex/auth.json`; `/api/oauth/accounts` возвращает только маскированные метаданные. @@ -196,6 +196,8 @@ Inline JSON и лишние позиционные аргументы откло ### Импорт учётных данных Kiro +В панели выберите **AWS IAM Identity Center**, укажите URL портала доступа AWS и регион SSO организации. Подтвердите код устройства в браузере и выберите профиль Kiro для сохранения, если их несколько. CLI и SSH не требуются. Инструкции CLI ниже относятся к варианту **Kiro CLI**. + Для входа Kiro требуется Kiro CLI: в Unix установите его командой `curl -fsSL https://cli.kiro.dev/install | bash`, в Windows PowerShell — `irm 'https://cli.kiro.dev/install.ps1' | iex`, затем сначала выполните `kiro-cli login`. Если сессии `kiro-cli` нет, `ocx login kiro` использует вставленный токен доступа или переменную окружения `KIRO_ACCESS_TOKEN`. Обычный импорт `ocx login kiro` открывает базу SQLite CLI только для чтения и не изменяет базу, WAL или SHM. @@ -270,7 +272,7 @@ opencodex поставляется с 106 встроенными пресета | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`. Модель API `moonshot` по умолчанию — `kimi-k3.1-pro`; сохранённые варианты `kimi-k2.5` для официальных адресов API переносятся на неё. | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/ru/guides/web-dashboard.md b/docs-site/src/content/docs/ru/guides/web-dashboard.md index d2d9873dbbe..1d912d7f48e 100644 --- a/docs-site/src/content/docs/ru/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ru/guides/web-dashboard.md @@ -73,6 +73,8 @@ bun run dev:gui ### Фильтрация запросов +В деталях запроса распознанная причина сбоя upstream имеет приоритет над общим текстом об ошибке прокси. Число физических отправок всегда показывается, если оно корректно; при отсутствующем или некорректном учёте отображается **Unavailable**, а не ноль. **Retries** считает отправки после первой только при наличии одной попытки с полностью урегулированным и согласованным учётом. При нескольких попытках, незавершённом или противоречивом учёте и при нормализации с потерями retries не выводятся, вместо того чтобы выдумывать их. Неизвестные сохранённые причины возвращают заголовок по статусу без сырой атрибуции. + Фильтры объединяют источник, перехваченные запросы, провайдера, точную модель, статус, время, скорость и ID диалога в загруженном журнале. Варианты включают резервные попытки; модель сравнивается без учёта регистра и крайних пробелов, но не по подстроке. Исчезнувший вариант сбрасывается на все записи. Периоды 15 минут, час и сутки обновляются каждые 30 секунд на вкладке Logs даже при выключенном автообновлении. Скорость — выходные токены в секунду за полную длительность запроса: меньше 15, от 15 до менее 50, не менее 50; недоступные значения исключаются при активном фильтре скорости. Успех — 2xx, ошибки — 4xx/5xx. diff --git a/docs-site/src/content/docs/ru/reference/adapters.md b/docs-site/src/content/docs/ru/reference/adapters.md index 3bad254262f..1a88fa8a30c 100644 --- a/docs-site/src/content/docs/ru/reference/adapters.md +++ b/docs-site/src/content/docs/ru/reference/adapters.md @@ -135,7 +135,7 @@ interface ProviderAdapter { Адаптер отображает уровень рассуждений в бюджет (minimal 1024 … max 32000), затем вычисляет безопасный `max_tokens` с запасом на вывод и **удаляет `temperature`/`top_p`**, когда thinking включён (Anthropic запрещает их в этом режиме). -- **Показ адаптивного thinking:** модели с адаптивным thinking (Opus 4.7+, Sonnet 5, Fable) получают `thinking.display: "summarized"`, поэтому долгое размышление приходит клиентам Chat и Responses как reasoning-дельты, а не как минуты heartbeat. Запрос, скрывающий сводку рассуждений (`reasoning.summary: "none"`), сохраняет значение провайдера по умолчанию. +- **Показ адаптивного thinking:** модели с адаптивным thinking (Opus 4.7+, Sonnet 5, Fable) получают `thinking.display: "summarized"`, поэтому долгое размышление приходит клиентам Chat и Responses как reasoning-дельты, а не как минуты heartbeat. Запрос, скрывающий сводку рассуждений (`reasoning.summary: "none"`), сохраняет значение провайдера по умолчанию. Исключение — Opus 5.5 на `api.anthropic.com`: по умолчанию используется `display: "updates"`, а явное `reasoning.summary` со значением `"concise"` или `"detailed"` выбирает `"summarized"` без updates beta. См. [обновления хода работы Opus 5.5](/guides/providers/#claude-opus-55-progress-updates). - Всегда отправляет `anthropic-version: 2023-06-01`. Стримит `content_block_delta` (`text_delta`, `thinking_delta`, `input_json_delta`). diff --git a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md index c1a53a5103f..80861b69725 100644 --- a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md @@ -26,6 +26,7 @@ pool'ами и контролируют каталог моделей, кото | `quota` | `--refresh`, `--json` | Прочитать отчёты по quota провайдеров. | | `presets` | `--json` | Показать provider preset'ы дашборда. | | `account-mode` | `pool`, `direct`, `--json` | Выбрать pooled или direct routing для аккаунтов Codex. | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | Предпросмотр, выборочное применение или откат провайдеров без ключей из базы SQLite (`cc-switch.db`) или JSON-файла CC-Switch. | По умолчанию `add`, `remove` и `set-default` меняют локальную конфигурацию. Для работающего прокси добавьте `--live`, а для удаления — также `--yes`. `--sync --json` тоже пытается синхронизировать после сохранения; при сбое сохранение остаётся, код выхода ненулевой и `needsSync: true`. Флаги `--live` и `--sync` несовместимы. Pacing и snapshot/apply описаны в [английской инструкции](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline). @@ -61,6 +62,27 @@ ocx models live --provider ark --json Для всего секретного используйте `--api-key` или вход через OAuth. ::: +### Импорт провайдеров CC-Switch без ключей + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# Запустите прокси в другом терминале (или выполните ocx ensure), затем добавьте ключи: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# Отдельный сценарий отката (сначала остановите прокси; удаляет только неизменённые записи без ключей): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` проецирует значения **только** из явного разрешённого списка несекретных полей метаданных (ID провайдеров, типы приложений, базовые URL, адаптеры и модели по умолчанию) из обычной базы SQLite или устаревшего JSON-файла CC-Switch. Байты источника и встроенной конфигурации временно считываются для сканирования входа, при этом секретные поля, произвольные отображаемые имена и комментарии TOML пропускаются на уровне байтов, никогда не декодируются в строки, не записываются и не логируются, а временные байтовые буферы обнуляются в `finally`. Узкие правила пути базового URL (`http(s)://host[:port]` с пустым путём или 1–4 сегментами API-пути в нижнем регистре `[a-z0-9._-]`, например `/v1`, `/api/v1` или `/compatible-mode/v1`) и проверки синтаксиса идентификаторов/моделей служат эшелонированной защитой и отклоняют неподходящие значения без их вывода. Исходные файлы открываются один раз с проверкой дескриптора `O_NOFOLLOW | O_NONBLOCK` и для SQLite десериализуются только для чтения в памяти без временных файлов; файлы SQLite с несколькими жёсткими ссылками (`nlink > 1`) отклоняются, чтобы проверки sidecar-файлов нельзя было обойти через альтернативный путь, а если соседний файл `-wal` или `-journal` не пуст, импорт завершается отказом и просит закрыть CC-Switch (или выполнить checkpoint базы) и повторить попытку. Импортируйте только из доверенного локального источника CC-Switch и проверяйте все выведенные ID, модели и `baseUrl` перед добавлением учётных данных: + +- **Предпросмотр (по умолчанию):** требует `--source `, ничего не записывает и сообщает число найденных, подходящих, конфликтующих и некорректных записей. +- **Применение (`--apply`):** требует `--source ` и остановленного прокси (`ocx stop`) и отклоняется на Remote Link Child. Повторите `--provider ` для выбора отдельных записей или укажите `--allow-private-network` для loopback и частных сетей. Даже без предварительного предпросмотра `--apply` выводит каждый импортируемый ID провайдера, модель по умолчанию и целевой URL до записи провайдеров без ключей и журнала отката `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` (в `stdout` в текстовом режиме, в `stderr` при `--json`); повторный `--apply` отклоняется, пока существует активный журнал. Проверьте выведенные записи, запустите прокси (`ocx start` в другом терминале или `ocx ensure`) и добавьте ключи через `printf '%s\n' "$API_KEY" | ocx account add-key ` (конвейерный stdin) или `ocx login `. +- **Откат (`--restore`):** отдельный сценарий отката (не продолжение добавления ключей), который требует остановленного прокси (`ocx stop`), отклоняется на Remote Link Child и не принимает `--source`, `--provider` или `--allow-private-network`. Команда проверяет `auth.json` только для чтения на уровне байтов (с безопасным отказом, если файл нечитаем или повреждён) и удаляет из активного журнала только те провайдеры, которые по-прежнему совпадают со своим импортированным состоянием без ключей, не выбраны как `defaultProvider` и не используются в маршрутизации, комбо (combos), политике резервирования (fallback policy), аккаунтах OAuth или API-ключах. + ## Аутентификация ### `ocx login ` diff --git a/docs-site/src/content/docs/ru/reference/configuration/advisor.md b/docs-site/src/content/docs/ru/reference/configuration/advisor.md index 7619ab6724c..bc0f09bd583 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/ru/reference/configuration/advisor.md @@ -60,6 +60,10 @@ description: Независимые консультации с политика Настроенный провайдер консультанта может отличаться от провайдера воркера. +Консультации подчиняются одновременно захваченным ограничениям API-ключа вызывающей стороны (область моделей, согласие на платные combo, лимиты резервного перехода для устройства) и ограничениям хоста для собственного ключа допуска Advisor: `OPENCODEX_API_AUTH_TOKEN` или файл токена службы, если они заданы и этот прокси его принимает, иначе первый настроенный API-ключ. Порядок ключей важен только в последнем случае: поставьте первым ключ, которому доступна модель Advisor, иначе все консультации будут недоступны. Если задан токен окружения или службы, консультацию определяет он. Эти правила действуют и для выбора combo, вложенных моделей принятия решений и повторных попыток. Консультация, которая не может им удовлетворить, возвращает worker уведомление о недоступности. + +Например, ключ `laptop` с `allowMeteredComboTargets: false` не может тратить средства на платные цели combo через Advisor, а `fallback.devices.laptop.provider: false` блокирует и переходы Advisor на резервных провайдеров. Вызывающей стороне loopback без ключа по-прежнему нужно согласие combo `allowMeteredTargets: true`. Если оба ключа задают `allowMeteredComboTargets: true`, а combo задаёт `allowMeteredTargets: true`, Advisor может использовать платные цели, в том числе через loopback-слушатель публичной привязки. Сервер захватывает политику вызывающей стороны при аутентификации запроса, до ожидания ёмкости для тела и до его чтения; последующие изменения ключей или резервных переходов не расширяют эту консультацию. Это касается и маршрутизируемых помощников зрения, запросов compact и их моделей решений, а также каждой повторной попытки кандидата в пределах хода WebSocket. Каждый новый ход WebSocket захватывает свою политику. + OpenCodex не вставляет в этот запрос ключи API провайдера, заголовки Authorization, токены OAuth, секреты конфигурации только для бэкенда, окружение процесса или скрытую цепочку рассуждений. Он не расшифровывает и не пересылает зашифрованное закрытое рассуждение провайдера. **Содержимое задачи от секретов не очищается.** Вставленный в задачу ключ, секрет в файле, который прочитали инструменты, или токен, напечатанный инструментом или журналом, может быть отправлен. OpenCodex не выполняет общий DLP. ## Полномочия diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index b3cab619dab..527f8f4761b 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -44,6 +44,7 @@ ocx models provider openrouter on | `providers` | `Record` | — | Map вида provider name → provider config. | | `openaiProviderTierVersion?` | `2` | set by migration | Отмечает, что единая projection OpenAI с учётом режима уже завершена. | | `disabledModels?` | `string[]` | — | Модели, скрытые из каталога Codex и `/v1/models`, но не заблокированные для прямых вызовов прокси. Routed-id удаляются из списков. Account-qualified native-id скрывает только строку этого селектора; bare native GPT-id скрывает bare-строку и строки всех селекторов аккаунтов для этой модели. Страница Models показывает только bare native- и routed-строки; чтобы скрыть одну selector-qualified строку, задайте это поле конфигурации напрямую. | +| `omitDisabledNativeModels?` | `boolean` | `false` | По умолчанию `false`. Установите `"omitDisabledNativeModels": true` в `config.json` и выполните `ocx sync`, чтобы исключить отключённые native-строки из каталога, кеша и HTTP-списка моделей в формате Codex. Проверенный до исключения шаблон сохраняет метаданные routed-строк. Маршрутизация прямых запросов не меняется. | | `providerContextCaps?` | `Record` | `{}` | Активные лимиты контекста по провайдерам. Обычные окна уменьшаются; нативные модели с поддержкой длинного контекста могут расширять окно только до собственного поддерживаемого предела. | | `providerContextCapValues?` | `Record` | `{}` | Последние выбранные лимиты по провайдерам, сохраняемые после отключения. Эти значения сами по себе не включают ограничение. Активное значение имеет приоритет над сохранённым. | | `contextCapValue?` | `number` | `350000` | Значение по умолчанию при первом включении. При повторном включении восстанавливается выбранное значение провайдера. Изменение глобального значения с `setAll: true` обновляет только активные лимиты; `setAll: true` без значения включает лимиты всех настроенных провайдеров с текущим глобальным значением. | @@ -130,7 +131,7 @@ cross-route credential fallback не существует. Строки API GPT- | `models?` | `string[]` | Начальный список моделей или список для резервного режима. При `liveModels: false` за непустым `models` следует `retainModels`; если `models` пуст или отсутствует, сначала берётся настроенный `defaultModel`, затем `retainModels`. Для каждого ID сохраняется только первое вхождение. | | `liveModels?` | `boolean` | Получать live-каталог на start/sync (по умолчанию `true`). Custom-провайдеры используют `${baseUrl}/models`; built-in могут использовать registry URL и дополнительно фильтровать результат. | | `selectedModels?` | `string[]` | Allowlist каталога после discovery. Непустой список показывает только эти id; пустой или отсутствующий показывает всё, что было обнаружено. | -| `modelDisplayNames?` | `Record` | Постоянные display-only имена с точным нативным id модели этого провайдера в качестве ключа. Ключи чувствительны к регистру. Имена имеют приоритет над metadata каталога провайдера и не меняют аутентификацию, adapter, routing, billing или upstream-запросы. Карта содержит не более 2 000 записей, как и discovery. | +| `modelDisplayNames?` | `Record` | Постоянные display-only имена с точным нативным id модели этого провайдера в качестве ключа. Ключи чувствительны к регистру. Имена имеют приоритет над metadata каталога провайдера и не меняют аутентификацию, adapter, routing, billing или upstream-запросы. Исключение: если прямой селектор `provider/model` не совпадает ни с известным id, ни с alias, ни с `retainModels` (например, пока live-каталог ещё не загружен), ключ с совпадающей закодированной формой отправляется как нативный id. Карта содержит не более 2 000 записей, как и discovery. | | `contextWindow?` | `number` | Значение контекста для всего провайдера, применяемое когда upstream не отдаёт metadata; при наличии metadata работает как cap и сохраняет более маленькое live-значение. Панель Models настраивает его отдельно от `providerContextCaps`. | | `modelContextWindows?` | `Record` | Значения и cap'ы контекста по отдельным моделям. Перекрывают `contextWindow`: если окно неизвестно, берётся заданное значение, а более маленькая live-metadata остаётся авторитетной. | | `inferContextWindow?` | `boolean` | Только для пользовательских провайдеров, по умолчанию выключено. `true` заполняет отсутствующее окно из встроенных метаданных модели; это оценка, а не проверенная ёмкость посредника. | @@ -166,7 +167,7 @@ cross-route credential fallback не существует. Строки API GPT- | `noPenaltyModels?` | `string[]` | Модели, отвергающие penalty presence/frequency. Встроенный пресет `xai` перечисляет здесь свои модели рассуждения (`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`), которые, по документации xAI, отвергают эти параметры; модели без рассуждения сохраняют penalty вызывающей стороны. | | `noStructuredOutputModels?` | `string[]` | Точные идентификаторы моделей, чей endpoint `openai-chat` отклоняет `response_format`. Поле опускается только при точном совпадении запрошенной модели; для остальных моделей `openai-chat` преобразование structured output остаётся включённым. | | `noJsonSchemaModels?` | `string[]` | Точные идентификаторы моделей, чей endpoint `openai-chat` отклоняет `response_format` типа `json_schema`, но принимает `json_object`. Такой запрос понижается до `json_object`, а не отбрасывается, поэтому вызывающая сторона всё равно получает JSON. Если модель есть в обоих списках, побеждает `noStructuredOutputModels`. Пресеты `opencode go` и `opencode zen` включают это для своих маршрутов DeepSeek. | -| `foldDeveloperRoleToSystem?` | `boolean` | Принимает ли назначение `openai-chat` роль `developer`. Если `foldDeveloperRoleToSystem` не задан, сообщение уходит как `system`; при `true` — как `system`; при `false` — как `developer`. Не задано означает, что об этом назначении ничего не записано; `true` фиксирует вышестоящий сервис, который роль отклоняет; `false` — тот, который её принимает. В любом случае сообщение сохраняет свою позицию в разговоре, меняется только роль. Назначение, отклоняющее роль, отвечает `400 role 'developer' is not allowed`, и ход не начинается — поэтому незаписанное состояние по умолчанию свёрнуто. | +| `foldDeveloperRoleToSystem?` | `boolean` | Принимает ли назначение `openai-chat` роль `developer`. Если `foldDeveloperRoleToSystem` не задан, сообщение уходит как `system`; при `true` — как `system`; при `false` — как `developer`. Не задано означает, что об этом назначении ничего не записано; `true` фиксирует вышестоящий сервис, который роль отклоняет; `false` — тот, который её принимает. Если `singleLeadingSystemMessage` не включён, сообщение сохраняет свою позицию в разговоре, меняется только роль. Назначение, отклоняющее роль, отвечает `400 role 'developer' is not allowed`, и ход не начинается — поэтому незаписанное состояние по умолчанию свёрнуто. | | `parallelToolCalls?` | `boolean` | Переключатель parallel tool call'ов. Для OpenAI Chat по умолчанию включено; не-chat adapter'ы рекламируют это только при явном `true`. | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` | По умолчанию выключенная downstream SSE-repair для exact placeholder-id, отсутствующих terminal-id и (с `repairInvalidIds`) message/reasoning id без канонического префикса `msg_`/`rs_`. Function-call id никогда не переписываются. Встроенный DeepSeek включает последние два по умолчанию. | | `responsesSnapshotRepair?` | `boolean` | По умолчанию выключенная клиентская repair для неполных lifecycle snapshot'ов Responses в SSE и JSON. Добавляет отсутствующие status, output и tool metadata, не меняя raw inspection и persistence. | @@ -192,6 +193,9 @@ cross-route credential fallback не существует. Строки API GPT- | `unsafeAllowNativeLocalExec?` | `boolean` | Legacy boolean Cursor, эквивалентен `nativeLocalExec: "on"` только если новое поле не задано. | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Политика local-exec для Cursor. `off` — дефолт; `codex-sandbox` сейчас ведёт себя fail-closed как `off`. | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + Регистрация и замена провайдера (`POST /api/providers`) проверяют `responsesPath` и `chatCompletionsPath` до изменения конфигурации в памяти или на диске. `PATCH /api/providers?name=` объединяет тело запроса с сохранённым провайдером; обновления, затрагивающие поля помимо `disabled`, — кроме обновлений только `requestPacing` — так же проверяют пути объединённого провайдера перед сохранением, а недопустимый сохранённый путь возвращает `400` без изменения конфигурации. Те же правила путей применяются при загрузке файла конфигурации. Провайдеры с API-key могут хранить literal key или environment-reference. OAuth-провайдеры @@ -201,17 +205,17 @@ Claude Code настраивается через ### Что сохраняется при сохранении провайдера -`POST /api/providers` с именем существующего провайдера заменяет сохранённую строку строкой, собранной из запроса. Форма добавления и редактирования в панели не может отправить все поля, поэтому сохранение переносит часть сохранённых полей, которые запрос опустил. Восемь из них описывают поведение конкретного апстрима: `preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. +`POST /api/providers` с именем существующего провайдера заменяет сохранённую строку строкой, собранной из запроса. Форма добавления и редактирования в панели не может отправить все поля, поэтому сохранение переносит часть сохранённых полей, которые запрос опустил. Одиннадцать из них описывают поведение конкретного апстрима: `preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. -| Сохранение | Восемь настроек | Сохранённый `apiKeyPool` | +| Сохранение | Одиннадцать настроек | Сохранённый `apiKeyPool` | | --- | --- | --- | | Та же точка назначения, поле опущено | Сохранённое значение остаётся, включая явный `[]` или `false` | Остаётся | | Новая точка назначения, поле опущено | Не переносится; могут примениться значения реестра по умолчанию для новой точки | Не переносится | | Поле передано в запросе | Значение из запроса | Значение из запроса | -Точка назначения — это адаптер, базовый URL (схема и хост сравниваются без учёта регистра, завершающие слеши игнорируются) и, если запрос его указывает, режим аутентификации. При переносе провайдера на другую точку назначения восемь настроек, описывающих прежний апстрим, отбрасываются, как и пул ключей, выпущенных для него. Сохранение никогда не сливает остальную часть старой строки с новой. +Точка назначения — это адаптер, базовый URL (схема и хост сравниваются без учёта регистра, завершающие слеши игнорируются) и, если запрос его указывает, режим аутентификации. При переносе провайдера на другую точку назначения десять настроек, описывающих прежний апстрим, отбрасываются, как и пул ключей, выпущенных для него. Сохранение никогда не сливает остальную часть старой строки с новой. -`PATCH /api/providers?name=` меняет только названные поля и сохраняет все остальные, независимо от точки назначения. Он принимает все восемь настроек; `null` очищает настройку. Для двух списков рассуждений пустой массив сохраняется как явный отказ, а не удаляется. +`PATCH /api/providers?name=` меняет только названные поля и сохраняет все остальные, независимо от точки назначения. Он принимает все десять настроек; `null` очищает настройку. Для двух списков рассуждений пустой массив сохраняется как явный отказ, а не удаляется. ## Безопасность исходящих диагностических запросов diff --git a/docs-site/src/content/docs/tr/guides/chatgpt-desktop.md b/docs-site/src/content/docs/tr/guides/chatgpt-desktop.md index 847b14d27f0..b3dd5372fc3 100644 --- a/docs-site/src/content/docs/tr/guides/chatgpt-desktop.md +++ b/docs-site/src/content/docs/tr/guides/chatgpt-desktop.md @@ -101,10 +101,12 @@ etkisiz bir yeniden başlatma işlemi garanti etmez. ## Hata davranışı ve bilinen sınırlar -Platform macOS değilse, OpenCodex çalışma zamanı eksikse veya filtre öz sınaması -başarısızsa başlatıcı özgün dosyayı stdout'u değiştirmeden çalıştırır. Paketlenmiş -app-server dosyasının eksikliği istisnadır: geri dönülecek dosya olmadığı için -başlatıcı hatayla çıkar. +Başlatıcı, Desktop'ın yeniden başlattığı işlemler dahil her çalıştırmadan önce +yürütülebilir dosyayı doğrular. Stdout filtresi kullanılamıyorsa veya öz sınaması +başarısızsa doğrulanmış dosya stdout değiştirilmeden çalışır. Dosya doğrulaması +başarısızsa ya da OpenCodex doğrulayıcısı kullanılamıyorsa başlatıcı app-server'ı +çalıştırmadan hatayla çıkar. Paketlenmiş app-server dosyasının eksikliği de +başlatmayı engeller. Öz sınamayı geçen filtre oturum ortasında çökerse boru kapanır. Paketlenmiş app-server'ın bundan sonra ne yaptığı doğrulanmamıştır; SIGPIPE/yazma hatası @@ -115,8 +117,9 @@ ham baytlara geçirir. Deney; paketlenmiş dosya yoluna, uygulamanın `CODEX_CLI_PATH` değerini kullanmasına ve güncel RPC alan biçimlerine bağlıdır. Güncellemeler bunları değiştirebilir. -OpenCodex taşınır veya kaldırılırsa başlatıcının ön kontrolü başarısız olur ve -özgün dosya çalışır. Kurulumu taşıdıktan sonra `ocx chatgpt launch` komutunu yeniden çalıştırın. +OpenCodex taşınır veya kaldırılırsa başlatıcı dosyayı doğrulayamayabilir ve +başlatmayı reddeder. Kurulumu taşıdıktan sonra `ocx chatgpt launch` komutunu yeniden +çalıştırın veya geçersiz kılmayı kaldırmak için `ocx chatgpt restore` kullanın. Uygulama güncellemesi paketlenmiş app-server dosyasını taşır veya kaldırırsa başlatıcı onu çalıştıramaz: stderr üzerinde `ocx chatgpt launch` ve diff --git a/docs-site/src/content/docs/tr/guides/claude-code.md b/docs-site/src/content/docs/tr/guides/claude-code.md index bc4d17a2ec3..fd92e204921 100644 --- a/docs-site/src/content/docs/tr/guides/claude-code.md +++ b/docs-site/src/content/docs/tr/guides/claude-code.md @@ -736,7 +736,7 @@ dönüştürür: | Asistan `tool_use` | `function_call` (`input` → JSON dizgeleştirilmiş `arguments`) | | Kullanıcı `tool_result` | `function_call_output` (`is_error` → `[tool error]` öneki) | | `thinking` / `redacted_thinking` tekrarı | İmzaları ve gizli yükleri sınırlı `ocxr1` zarflarında taşıyan `reasoning` öğeleri | -| Fonksiyon araçları | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) | +| Fonksiyon araçları | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) `allowed_domains`, `blocked_domains` veya `user_location` içeren barındırılan arama, çevrilmiş Responses/Chat rotalarından çıkarılır; çıkarılan aramayı zorlamak 400 döndürür. Geçerli `defer_loading` işlev tanımları normal işlevler olarak çağrılabilir. [Çeviri sınırlarına](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes) bakın. | | `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, adlandırılmış fonksiyon→`{type:"function",name}`, barındırılan WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/tr/guides/codex-app-models.md b/docs-site/src/content/docs/tr/guides/codex-app-models.md index 6cf1ba16587..3cb5b1380c7 100644 --- a/docs-site/src/content/docs/tr/guides/codex-app-models.md +++ b/docs-site/src/content/docs/tr/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Codex App model seçicisi description: opencodex modellerinin paylaşılan Codex kataloğu aracılığıyla Codex App, Codex CLI ve Codex TUI içinde nasıl göründüğü. --- +Varsayılan `false`. `config.json` içinde `"omitDisabledNativeModels": true` ayarlayıp `ocx sync` çalıştırarak devre dışı native satırları katalogdan, önbellekten ve Codex biçimli HTTP model listesinden çıkarabilirsiniz. Çıkarmadan önce doğrulanan kalıtım şablonu routed satırların meta verilerini korur. Doğrudan isteklerin yönlendirmesi değişmez. Aşağıdaki gizleme açıklamaları varsayılan ayarlar içindir. + opencodex, Codex App'i yamalamaz. Codex CLI/TUI'nin kullandığı aynı Codex yapılandırmasını ve model kataloğunu yazar. Uygulama sunucusu (app-server) bu paylaşılan durumu okur, ancak bazı Codex Desktop sürümleri oluşturucuda diff --git a/docs-site/src/content/docs/tr/guides/combos.md b/docs-site/src/content/docs/tr/guides/combos.md index 44ab77f40b7..da74bcfd061 100644 --- a/docs-site/src/content/docs/tr/guides/combos.md +++ b/docs-site/src/content/docs/tr/guides/combos.md @@ -229,7 +229,8 @@ ikiye ayrılır. | --- | --- | | HTTP 401, 403, 404, 408, 429 veya herhangi bir 5xx | Hedefi soğutun ve bir sonraki uygun hedefe atlayın. | | Sınıflandırılmış kimlik doğrulama, abonelik, kota, hız sınırı, aşırı yük veya yukarı akış sunucu hatası | Yalnızca durum yeterli olmadığında bile hedefi soğutun ve atlayın. | -| İstemci iptali (499), `origin_rejected`, siber politika reddi, bağlam taşması veya diğer geçersiz istek | Durun ve hatayı döndürün; başka bir hedef isteği geçerli kılmaz. | +| İstemci iptali (499), `origin_rejected`, siber politika reddi veya diğer geçersiz istek | Durun ve hatayı döndürün; başka bir hedef isteği geçerli kılmaz. | +| Çıktıdan önce doğrulanan bağlam aşımı (HTTP 400 dahil) | Soğutma olmadan denenmemiş uygun hedefe geçer; hedefler tükenirse ilk doğrulanan HTTP 400 bağlam hatasını döndürür (başka durum koduyla gelen bağlam reddi de sonraki hedefe geçirir ama saklanmaz). | | `user` alanını açıkça reddeden, `reasoning.effort`/`reasoning_effort` için desteklenmeyen değer bildiren, modele özgü görüntü girdisini reddeden (`param: input`) veya rotanın okuyamadığı girdi medyasını reddeden (`unsupported_input_modality`) yapılandırılmış HTTP 400 | Çıktı başlamadan önce bekleme süresi kaydetmeden sonraki uygun hedefe atlar; aşağıdaki isteğe bağlı parametre uyumluluğuna bakın. | | Süreç içi bir bağdaştırıcının (`runTurn`) yürüttüğü Responses turunda, geçerli isteğin bildirmediği ilk araç çağrısı (herhangi bir çıktıdan ve yeniden oynatılamaz yan etkiden önce) | Hedefi bekleme süresine alır ve aynı araç kataloğuyla sonraki hedefe atlar. Görünür çıktıdan veya yeniden oynatılamaz bir yan etkiden sonra ret kesindir. Chat Completions ve Anthropic Messages istekleri değişmez. | | Diğer sınıflandırılmamış hatalar | Durun ve hatayı döndürün. | @@ -240,9 +241,14 @@ yerine onu kullanır; Codex sıfırlama başlıkları ve yapılandırılmış `c açık `Retry-After` gecikmesi en fazla 24 saat, sıfırlama kaynaklı, yapılandırılmış ve varsayılan soğuma süreleri en fazla 10 dakika ile sınırlandırılır. Geçerli istek denenen aynı hedefi asla yeniden denemez. Daha sonraki istekler -soğuma süresi dolana kadar onu atlar. Uygun hiçbir hedef kalmazsa proxy +soğuma süresi dolana kadar onu atlar. Henüz hiçbir hedef denenmemişse ve uygun hedef yoksa proxy `error.code = "combo_unavailable"` ile HTTP 503 döndürür. +Doğrulanmış bir HTTP 400 bağlam reddinden sonra hedefler veya gönderim bütçesi tükenirse ilk ret +`context_length_exceeded` olarak döner; kota hataları dahil sonraki yeniden denenebilir hatalardan önceliklidir. +Kalan hedeflerin akıl yürütmeyi yeniden oynatamaması veya ücretli kullanım onayının bulunmaması da buna dahildir. +İptal, sonraki ölümcül ret ve açık geri dönüş politikası reddi hemen durdurur; onaysız ücretli hedefe istek gönderilmez. + :::note Yük devretme kasıtlı olarak sınırlandırılmıştır. Hedefe özgü kullanılabilirlik, kimlik doğrulama, kota ve aşırı yük hatalarına yardımcı olur; arayan hatalarını @@ -405,7 +411,7 @@ tam doğrulama mesajını görüntüler. ### Yük devretme ilk hatadan sonra neden durdu? Hata hedefe özgü olmaktan ziyade uç (terminal) bir hataydı. Geçersiz girdiyi -düzeltin, aşırı büyük bir bağlamı azaltın, bir politika reddini işleyin veya +düzeltin, bir politika reddini işleyin veya reddedilen istek kaynağını düzeltin. Kombolar bu durumlar için atlama yapmaz. diff --git a/docs-site/src/content/docs/tr/guides/providers.md b/docs-site/src/content/docs/tr/guides/providers.md index eb6d9d9bc46..c25565978bf 100644 --- a/docs-site/src/content/docs/tr/guides/providers.md +++ b/docs-site/src/content/docs/tr/guides/providers.md @@ -196,8 +196,7 @@ sağlayıcıları birden fazla oturum tutabilir. Sağlayıcılar sayfası bu hes bir açılır menüde gösterir, başka bir tane eklemenize izin verir ve diğerlerinin oturumunu kapatmadan etkin hesabı değiştirir. Normal oturum açmada kimliği olmayan Kimi kimlik bilgileri etkin yuvanın yerini alır; açık **Hesap ekle** akışı mevcut -yuvayı korur ve ayrı yeni yuvayı etkinleştirir. Kiro hesapları profil ARN'sine göre -anahtarlanır. `chatgpt` her zaman tek yuvalıdır çünkü Codex havuz hesaplarının +yuvayı korur ve ayrı yeni yuvayı etkinleştirir. Kiro hesapları, varsa oturum açan kullanıcının e-posta adresi ile profil ARN'si birlikte kullanılarak ayırt edilir; aynı kuruluş profilini paylaşan kullanıcılar ayrı kimlik bilgilerini koruyabilir. `chatgpt` her zaman tek yuvalıdır çünkü Codex havuz hesaplarının ayrı bir defteri vardır. Belirteçler `~/.opencodex/auth.json` içinde kalır; `/api/oauth/accounts` yalnızca maskelenmiş meta verileri döndürür. @@ -280,6 +279,8 @@ referansında [`ocx status` / `ocx doctor`](/tr/reference/cli/) bölümüne bak ### Kiro kimlik bilgisi içe aktarma +Panelde **AWS IAM Identity Center** seçeneğini seçip kuruluşunuzun AWS erişim portalı URL’sini ve SSO bölgesini girin. Tarayıcıda cihaz kodunu onaylayın ve birden fazla Kiro profili varsa kaydedilecek profili seçin. CLI veya SSH gerekmez. Aşağıdaki CLI adımları **Kiro CLI** seçeneği içindir. + Kiro oturum açma Kiro CLI'yı bekler: Unix'te `curl -fsSL https://cli.kiro.dev/install | bash` ile kurun; Windows PowerShell'de `irm 'https://cli.kiro.dev/install.ps1' | iex` kullanın; ardından `kiro-cli login` @@ -397,7 +398,7 @@ yalnızca Cline IDE/CLI içinde mevcuttur; `minimax/minimax-m2.5` belgelenmiş A | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (kodlama) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (kodlama) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`. `moonshot` API varsayılanı `kimi-k3.1-pro` modelidir; resmî API adresleri için kaydedilmiş `kimi-k2.5` seçimleri bu modele taşınır. | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Kodlama) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/tr/guides/web-dashboard.md b/docs-site/src/content/docs/tr/guides/web-dashboard.md index e432e2f8cd6..fee8d7b1d78 100644 --- a/docs-site/src/content/docs/tr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/tr/guides/web-dashboard.md @@ -94,6 +94,8 @@ Kullanım, panel, sağlayıcı çalışma alanı, sağlayıcı kataloğu ve API ### İstek günlüklerini filtreleme +İstek ayrıntıları, genel proxy hatası metni yerine kaydedilmiş upstream hata nedenini öncelikli gösterir. Fiziksel gönderim sayısı geçerliyse her zaman gösterilir; eksik veya geçersiz muhasebe bilgisi sıfır yerine **Unavailable** olarak görüntülenir. **Retries**, yalnızca muhasebesi tamamen kesinleşmiş ve tutarlı tek bir deneme kaydı bulunduğunda ilk gönderimden sonraki ek gönderim sayısını gösterir. Birden fazla deneme, çözümlenmemiş veya tutarsız muhasebe ve kayıplı attempt normalizasyonu, uydurma yeniden deneme sayısı yerine retries alanını gizler. Bilinmeyen kalıcı nedenler, ham atıf olmadan durum tabanlı başlığa döner. + Filtreler yüklü günlükte yüzey, yakalanan istekler, sağlayıcı, tam model adı, durum, zaman, hız ve konuşma kimliğini birleştirir. Seçenekler yedek denemeleri de içerir; model eşleşmesi büyük/küçük harfi ve dış boşlukları yok sayar, kısmi adları eşleştirmez. Kaybolan seçenek tüm kayıtlara döner. Son 15 dakika, saat ve gün pencereleri Logs sekmesinde otomatik yenileme kapalıyken de 30 saniyede bir güncellenir. Hız, tam istek süresindeki saniyelik çıktı jetonudur: 15 altı, 15 dahil 50 altı, en az 50; hız filtresi açıkken ölçülemeyenler dışlanır. Başarı 2xx, hata 4xx/5xx anlamındadır. diff --git a/docs-site/src/content/docs/tr/reference/adapters.md b/docs-site/src/content/docs/tr/reference/adapters.md index 2a31a0cd7f0..5b75e305d39 100644 --- a/docs-site/src/content/docs/tr/reference/adapters.md +++ b/docs-site/src/content/docs/tr/reference/adapters.md @@ -141,7 +141,7 @@ Pro/Max için Bearer + `anthropic-beta`). (en az 1024 … en çok 32000) eşler, ardından çıktı payı ile güvenli bir `max_tokens` hesaplar ve düşünme etkinleştirildiğinde **`temperature`/`top_p`'yi bırakır** (Anthropic orada bunları yasaklar). -- **Uyarlanabilir thinking gösterimi:** uyarlanabilir thinking modellerine (Opus 4.7+, Sonnet 5, Fable) `thinking.display: "summarized"` gönderilir; böylece uzun bir düşünme, Chat ve Responses istemcilerine dakikalarca heartbeat yerine reasoning deltaları olarak ulaşır. Reasoning özetini gizleyen bir istek (`reasoning.summary: "none"`) sağlayıcının varsayılanını korur. +- **Uyarlanabilir thinking gösterimi:** uyarlanabilir thinking modellerine (Opus 4.7+, Sonnet 5, Fable) `thinking.display: "summarized"` gönderilir; böylece uzun bir düşünme, Chat ve Responses istemcilerine dakikalarca heartbeat yerine reasoning deltaları olarak ulaşır. Reasoning özetini gizleyen bir istek (`reasoning.summary: "none"`) sağlayıcının varsayılanını korur. Ancak `api.anthropic.com` üzerindeki Opus 5.5 varsayılan olarak `display: "updates"` kullanır; açıkça `reasoning.summary: "concise"` veya `"detailed"` gönderildiğinde updates betası olmadan `"summarized"` seçilir. Bkz. [Opus 5.5 ilerleme güncellemeleri](/guides/providers/#claude-opus-55-progress-updates). - **Yapılandırılmış çıktı:** `type: "json_schema"` içeren Responses `text.format` ve Chat Completions `response_format` istekleri Anthropic `output_config.format` haline gelir. Format, uyumlu bir diff --git a/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md b/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md index 184e193ca23..eec9aabd8d2 100644 --- a/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md @@ -27,6 +27,7 @@ bir ad hem `--adapter` hem de `--base-url` gerektirir. | `quota` | `--refresh`, `--json` | Sağlayıcı kota raporlarını okuyun. | | `presets` | `--json` | Kontrol paneli sağlayıcı önayarlarını listeleyin. | | `account-mode` | `pool`, `direct`, `--json` | Havuzlanmış veya doğrudan Codex hesap yönlendirmesini seçin. | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | CC-Switch SQLite (`cc-switch.db`) veya JSON dosyasından anahtarsız sağlayıcı girdilerini önizleyin, seçerek uygulayın veya geri yükleyin. | Varsayılan `add`, `remove` ve `set-default` yerel yapılandırmayı değiştirir. Çalışan proxy için `--live`, canlı silme için ayrıca `--yes` kullanın. `--sync --json` da kayıttan sonra eşitlemeyi dener; başarısızlıkta kayıt korunur, sıfır olmayan çıkış kodu ve `needsSync: true` döner. `--live` ile `--sync` birlikte kullanılamaz. Pacing ve snapshot/apply için [İngilizce kılavuza](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline) bakın. @@ -63,6 +64,27 @@ saygı duymanız gereken bir sınırdır. Önemli olmasının iki nedeni: Gizli olan her şey için `--api-key` veya bir OAuth girişi kullanın. ::: +### Anahtarsız CC-Switch sağlayıcı içe aktarma + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# Proxy'yi başka bir terminalde başlatın (veya ocx ensure çalıştırın), ardından anahtar ekleyin: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# Ayrı geri alma yolu (önce proxy'yi durdurun; yalnızca düzenlenmemiş anahtarsız girdileri kaldırır): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch`, düz bir CC-Switch SQLite veritabanından veya eski JSON dosyasından **yalnızca** açıkça izin verilen gizli olmayan meta veri alanlarındaki değerleri (sağlayıcı kimlikleri, uygulama türleri, temel URL'ler, bağdaştırıcılar ve varsayılan modeller) yansıtır. Girdiyi taramak için kaynak ve gömülü yapılandırma baytları geçici olarak okunur; gizli alanlar, serbest metin görünen adları ve TOML yorumları bayt düzeyinde atlanır, hiçbir zaman dizelere çözülmez, yazılmaz veyagünlüklenmez ve geçici bayt arabellekleri `finally` içinde sıfırlanır. Dar temel URL yolu kuralları (boş yola veya `/v1`, `/api/v1`, `/compatible-mode/v1` gibi 1–4 küçük harfli `[a-z0-9._-]` API yolu segmentine sahip `http(s)://host[:port]`) ve tanımlayıcı/model sözdizimi denetimleri derinlemesine savunma sağlar ve uygun olmayan değerleri yansıtmadan reddeder. Kaynak dosyalar `O_NOFOLLOW | O_NONBLOCK` tanımlayıcı doğrulamasıyla bir kez açılır ve SQLite için geçici dosya olmadan bellekte salt okunur olarak açılır; birden fazla sabit bağlantıya (`nlink > 1`) sahip SQLite dosyaları yan dosya denetimlerinin takma ad yoluyla aşılmasını önlemek için reddedilir ve kardeş `-wal` veya `-journal` dosyası boş değilse içe aktarma reddedilerek CC-Switch'i kapatıp (veya veritabanını checkpoint edip) yeniden denemenizi ister. Yalnızca güvendiğiniz yerel bir CC-Switch kaynağından içe aktarın ve kimlik bilgisi eklemeden önce yazdırılan her kimliği, modeli ve `baseUrl` değerini doğrulayın: + +- **Önizleme (varsayılan):** `--source ` gerektirir, hiçbir şey yazmaz ve bulunan, uygun, çakışan ve geçersiz girdileri raporlar. +- **Uygulama (`--apply`):** `--source ` ve durdurulmuş bir proxy (`ocx stop`) gerektirir; Remote Link Child üzerinde reddedilir. Yalnızca seçili girdileri aktarmak için `--provider ` bayrağını tekrarlayın veya geri döngü/özel ağ temel URL'lerine izin vermek için `--allow-private-network` geçirin. Önceden önizleme yapılmasa bile `--apply`, anahtarsız sağlayıcı girdilerini ve `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` geri alma günlüğünü yazmadan önce içe aktarılan her sağlayıcı kimliğini, varsayılan modeli ve hedef URL'yi yazdırır (metin modunda `stdout`, `--json` modunda `stderr`); etkin bir günlük varken ikinci bir `--apply` reddedilir. Yazdırılan girdileri doğrulayın, proxy'yi başlatın (başka bir terminalde `ocx start` veya `ocx ensure`) ve `printf '%s\n' "$API_KEY" | ocx account add-key ` (standart girdi borusu) ya da `ocx login ` ile kimlik bilgilerini ekleyin. +- **Geri yükleme (`--restore`):** kimlik bilgisi eklemenin devamı olmayan ayrı bir geri alma yoludur; durdurulmuş bir proxy (`ocx stop`) gerektirir, Remote Link Child üzerinde reddedilir ve `--source`, `--provider` veya `--allow-private-network` almaz. `auth.json` dosyasını bayt düzeyinde salt okunur denetler (okunamaz veya bozuksa güvenli biçimde korur) ve etkin günlükten yalnızca içe aktarılan anahtarsız durumuyla hâlâ eşleşen, `defaultProvider` olarak seçilmemiş ve yönlendirme, kombolar (combos), yedekleme politikası (fallback policy), OAuth hesapları veya API anahtarları tarafından başvurulmayan sağlayıcıları kaldırır. + ## Kimlik Doğrulama ### `ocx login ` diff --git a/docs-site/src/content/docs/tr/reference/configuration/advisor.md b/docs-site/src/content/docs/tr/reference/configuration/advisor.md index 0a44487f45e..4d20a4c9b6e 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/tr/reference/configuration/advisor.md @@ -60,6 +60,10 @@ Bir danışma şunları gönderebilir: Yapılandırılmış danışman sağlayıcısı, worker sağlayıcısından farklı olabilir. +Danışmalar hem çağıranın yakalanan API anahtarı model kapsamına, ücretli combo onayına ve cihaz yedekleme sınırlarına, hem de Advisor'ın kendi kabul anahtarının ana makine sınırlarına uyar: ayarlıysa `OPENCODEX_API_AUTH_TOKEN` veya bu proxy'nin kabul ettiği hizmet belirteci dosyası, aksi halde yapılandırılmış ilk API anahtarı. Anahtar sırası yalnızca bu son durumda önemlidir: Advisor modeline ulaşabilen bir anahtarı başa koyun, aksi halde her danışma kullanılamaz. Bir ortam veya hizmet belirteci ayarlıysa danışmayı o belirteç belirler. Bu kurallar combo seçimlerine, iç içe karar modellerine ve yeniden denemelere de uygulanır. Bunları karşılayamayan bir danışma, worker'a kullanılamıyor bildirimi döndürür. + +Örneğin `allowMeteredComboTargets: false` olan bir `laptop` anahtarı Advisor üzerinden ücretli combo hedeflerine harcama yapamaz ve `fallback.devices.laptop.provider: false` Advisor'ın sağlayıcı yedekleme adımlarını da engeller. Anahtarsız bir loopback çağıranı yine de combo'nun `allowMeteredTargets: true` onayına ihtiyaç duyar. Her iki anahtar da `allowMeteredComboTargets: true` ayarlarsa ve combo `allowMeteredTargets: true` ayarlarsa, Advisor ücretli hedefleri genel bir bağlamanın loopback dinleyicisi üzerinden de kullanabilir. Sunucu çağıran politikasını isteği doğruladığı anda, gövde kapasitesini beklemeden veya gövdeyi okumadan önce yakalar; sonraki anahtar veya yedekleme düzenlemeleri o danışmayı genişletemez. Bu, yönlendirilmiş görsel yardımcıları, compact isteklerini ve karar modellerini ve bir WebSocket turundaki her aday yeniden denemesini de kapsar. Her yeni WebSocket turu kendi politikasını yakalar. + OpenCodex bu isteme sağlayıcı API anahtarlarını, Authorization başlıklarını, OAuth belirteçlerini, yalnızca arka uca ait yapılandırma sırlarını, süreç ortamını veya gizli düşünce zincirini koymaz. Şifreli sağlayıcıya özel akıl yürütmeyi çözüp iletmez. **Görev içeriği sırlardan arındırılmaz.** Göreve yapıştırılan bir anahtar, araçların okuduğu dosyadaki bir sır veya bir aracın ya da günlüğün yazdırdığı belirteç gönderilebilir. OpenCodex genel bir DLP çalıştırmaz. ## Yetki diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index 91f90523d70..12f2004ed8b 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -44,6 +44,7 @@ Arayüzde kayıt veya OAuth girişi tamamlanınca Models sayfasını açan bir b | `providers` | `Record` | — | Sağlayıcı adından sağlayıcı yapılandırmasına eşleme haritası. | | `openaiProviderTierVersion?` | `2` | geçiş tarafından ayarlanır | Tek seçenek duyarlı OpenAI projeksiyonunu tamamlandı olarak işaretler. | | `disabledModels?` | `string[]` | — | Codex kataloğundan ve `/v1/models` listesinden gizlenen, ancak doğrudan proxy çağrılarından engellenmeyen modeller. Yönlendirilen bir kimlik listelerden kaldırılır. Hesap nitelikli bir yerel kimlik yalnızca o seçici satırını gizler; yalın bir yerel GPT kimliği, yalın satırı ve o model için her hesap seçici satırını gizler. Kontrol paneli Modeller sayfası yalnızca yönlendirilen ve yalın yerel satırları gösterir; seçici nitelikli bir satırı gizlemek için doğrudan bu yapılandırma alanını kullanın. | +| `omitDisabledNativeModels?` | `boolean` | `false` | Varsayılan `false`. `config.json` içinde `"omitDisabledNativeModels": true` ayarlayıp `ocx sync` çalıştırarak devre dışı native satırları katalogdan, önbellekten ve Codex biçimli HTTP model listesinden çıkarabilirsiniz. Çıkarmadan önce doğrulanan kalıtım şablonu routed satırların meta verilerini korur. Doğrudan isteklerin yönlendirmesi değişmez. | | `providerContextCaps?` | `Record` | `{}` | Sağlayıcı başına etkin bağlam sınırları. Normal pencereler küçültülür; uzun pencereyi destekleyen yerel modeller yalnızca kendi desteklenen üst sınırlarına kadar genişletilebilir. | | `providerContextCapValues?` | `Record` | `{}` | Sağlayıcı başına son seçilen sınırlar; devre dışı bırakıldığında da saklanır. Bu değerler tek başına sınırı etkinleştirmez. Etkin değer, saklanan değerden önceliklidir. | | `contextCapValue?` | `number` | `350000` | İlk etkinleştirmede kullanılan varsayılan değer. Sonraki etkinleştirmelerde sağlayıcının seçimi geri yüklenir. Genel değeri `setAll: true` ile güncellemek yalnızca etkin sınırları değiştirir; değer olmadan `setAll: true`, yapılandırılmış tüm sağlayıcıların sınırlarını geçerli genel değerle etkinleştirir. | @@ -165,7 +166,7 @@ alanlı seçilmiş kimlikleri yalın kimliklere yeniden yazar. | `noPenaltyModels?` | `string[]` | Varlık/frekans cezalarını reddeden modeller. Yerleşik `xai` ön ayarı, xAI belgelerine göre bu değerleri reddeden akıl yürütme modellerini (`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`) burada listeler; akıl yürütmesiz modeller çağıranın cezalarını korur. | | `noStructuredOutputModels?` | `string[]` | `openai-chat` uç noktası `response_format`'ı reddeden tam model kimlikleri. Yalnızca tam bir istenen model eşleşmesi alanı atlar; yapılandırılmış çıktı çevirisi diğer her `openai-chat` modeli için etkin kalır. | | `noJsonSchemaModels?` | `string[]` | `openai-chat` uç noktası `json_schema` biçimini reddeden ama `json_object` kabul eden tam model kimlikleri. Böyle bir istek atılmak yerine `json_object` seviyesine düşürülür, böylece JSON isteyen çağıran yine JSON alır. Bir model her iki listede de varsa `noStructuredOutputModels` kazanır. `opencode go` ve `opencode zen` hazır ayarları bunu DeepSeek rotaları için getirir. | -| `foldDeveloperRoleToSystem?` | `boolean` | Bir `openai-chat` hedefinin `developer` rolünü kabul edip etmediğini kaydeder. `foldDeveloperRoleToSystem` ayarlanmamışsa `system`, `true` ise `system`, `false` ise `developer` olarak gönderilir. Ayarlanmamış olması bu hedef için hiçbir şey kaydedilmediği anlamına gelir; `true` rolü reddeden bir üst hizmeti, `false` ise kabul edeni kaydeder. Her durumda mesaj konuşmadaki konumunu korur; yalnızca rol değişir. Rolü reddeden bir hedef `400 role 'developer' is not allowed` yanıtı verir ve tur hiç başlamaz; kaydedilmemiş durumun katlanmış olmasının nedeni budur. | +| `foldDeveloperRoleToSystem?` | `boolean` | Bir `openai-chat` hedefinin `developer` rolünü kabul edip etmediğini kaydeder. `foldDeveloperRoleToSystem` ayarlanmamışsa `system`, `true` ise `system`, `false` ise `developer` olarak gönderilir. Ayarlanmamış olması bu hedef için hiçbir şey kaydedilmediği anlamına gelir; `true` rolü reddeden bir üst hizmeti, `false` ise kabul edeni kaydeder. `singleLeadingSystemMessage` etkin değilse mesaj konuşmadaki konumunu korur; yalnızca rol değişir. Rolü reddeden bir hedef `400 role 'developer' is not allowed` yanıtı verir ve tur hiç başlamaz; kaydedilmemiş durumun katlanmış olmasının nedeni budur. | | `parallelToolCalls?` | `boolean` | Paralel araç çağrılarını açıp kapatın. OpenAI Chat varsayılan olarak açıktır; sohbet harici adaptörler yalnızca açık `true` durumunda bildirir. | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` | Tam yer tutucu kimlikleri, eksik terminal kimlikleri ve (`repairInvalidIds` ile) kurallı `msg_`/`rs_` öneki eksik olan mesaj/akıl yürütme kimlikleri için varsayılan olarak devre dışı bırakılmış aşağı akış SSE onarımı. Fonksiyon çağrısı kimlikleri asla yeniden yazılmaz. Yerleşik DeepSeek son ikisini varsayılan olarak etkinleştirir. | | `responsesSnapshotRepair?` | `boolean` | SSE ve JSON'daki seyrek Responses yaşam döngüsü anlık görüntüleri için varsayılan olarak devre dışı bırakılmış istemciye yönelik onarım. Ham inceleme ve kalıcılık değişmeden kalırken eksik kurallı durumu, çıktıyı ve araç meta verilerini doldurur. | @@ -191,6 +192,9 @@ alanlı seçilmiş kimlikleri yalın kimliklere yeniden yazar. | `unsafeAllowNativeLocalExec?` | `boolean` | Cursor eski boolean değeri, yalnızca daha yeni alan ayarlanmadığında `nativeLocalExec: "on"` değerine eşdeğerdir. | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Cursor yerel yürütme politikası. `off` varsayılandır; `codex-sandbox` şu anda `off` gibi kapalı olarak başarısız olur. | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + Sağlayıcı kaydı ve değiştirme (`POST /api/providers`), bellekteki veya diskteki yapılandırmayı değiştirmeden önce `responsesPath` ve `chatCompletionsPath` değerlerini doğrular. `PATCH /api/providers?name=`, istek gövdesini kayıtlı sağlayıcıyla birleştirir; `disabled` dışındaki alanlara dokunan güncellemeler (yalnızca `requestPacing` içeren güncellemeler hariç), kaydetmeden önce birleştirilmiş sağlayıcının yollarını aynı şekilde doğrular ve geçersiz bir korunmuş yol `400` döndürüp yapılandırmayı değiştirmez. Aynı yol kuralları yapılandırma dosyası yüklenirken de uygulanır. API anahtarı sağlayıcıları değişmez bir anahtar veya bir ortam referansı @@ -201,17 +205,17 @@ yapılandırılır. ### Bir sağlayıcı kaydı neleri korur -Var olan bir sağlayıcının adıyla gönderilen `POST /api/providers`, saklanan satırı istekten oluşturulan bir satırla değiştirir. Panodaki ekleme/düzenleme formu her alanı gönderemez; bu yüzden kayıt, isteğin atladığı bazı saklanan alanları korur. Bunlardan sekizi belirli bir üst kaynağın nasıl davrandığını kaydeder: `preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. +Var olan bir sağlayıcının adıyla gönderilen `POST /api/providers`, saklanan satırı istekten oluşturulan bir satırla değiştirir. Panodaki ekleme/düzenleme formu her alanı gönderemez; bu yüzden kayıt, isteğin atladığı bazı saklanan alanları korur. Bunlardan on biri belirli bir üst kaynağın nasıl davrandığını kaydeder: `preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`. -| Kayıt | Sekiz ayar | Saklanan `apiKeyPool` | +| Kayıt | On bir ayar | Saklanan `apiKeyPool` | | --- | --- | --- | | Aynı hedef, alan atlanmış | Saklanan değer korunur, açık bir `[]` veya `false` dahil | Korunur | | Yeni hedef, alan atlanmış | Korunmaz; yeni hedef için kayıt defteri varsayılanları uygulanabilir | Korunmaz | | Alan istekte gönderilmiş | İsteğin değeri | İsteğin değeri | -Hedef; bağdaştırıcı, temel URL (şema ve ana makine büyük/küçük harf gözetmeden karşılaştırılır, sondaki eğik çizgiler yok sayılır) ve istek bir tane belirtiyorsa kimlik doğrulama modudur. Bir sağlayıcıyı başka bir hedefe taşımak, önceki üst kaynağı tanımlayan sekiz ayarı ve o kaynak için verilmiş anahtar havuzunu bırakır. Bir kayıt, eski satırın geri kalanını yenisiyle asla birleştirmez. +Hedef; bağdaştırıcı, temel URL (şema ve ana makine büyük/küçük harf gözetmeden karşılaştırılır, sondaki eğik çizgiler yok sayılır) ve istek bir tane belirtiyorsa kimlik doğrulama modudur. Bir sağlayıcıyı başka bir hedefe taşımak, önceki üst kaynağı tanımlayan on ayarı ve o kaynak için verilmiş anahtar havuzunu bırakır. Bir kayıt, eski satırın geri kalanını yenisiyle asla birleştirmez. -`PATCH /api/providers?name=` yalnızca adını verdiği alanları değiştirir ve hedef ne olursa olsun diğer tüm saklanan alanları korur. Sekiz ayarın hepsini kabul eder; `null` birini temizler. İki akıl yürütme listesinde boş dizi silinmez, açık bir vazgeçme olarak saklanır. +`PATCH /api/providers?name=` yalnızca adını verdiği alanları değiştirir ve hedef ne olursa olsun diğer tüm saklanan alanları korur. On ayarın hepsini kabul eder; `null` birini temizler. İki akıl yürütme listesinde boş dizi silinmez, açık bir vazgeçme olarak saklanır. ## Sağlayıcı teşhis giden güvenliği diff --git a/docs-site/src/content/docs/troubleshooting/windows-acl-timeouts.md b/docs-site/src/content/docs/troubleshooting/windows-acl-timeouts.md new file mode 100644 index 00000000000..e8d34ca0d17 --- /dev/null +++ b/docs-site/src/content/docs/troubleshooting/windows-acl-timeouts.md @@ -0,0 +1,25 @@ +--- +title: Windows File Permission Timeouts +description: How atomic file writes recover after a temporary icacls stall. +--- + +On Windows, opencodex uses `icacls` to protect temporary files before writing +configuration, credentials, or cached state. If permission hardening fails, the +write fails and the previous destination stays in place. + +A temporary `icacls` timeout does not require a proxy restart to recover. A later +atomic write to the same destination may make one recovery attempt. If that +attempt also times out, further writes to that destination are refused until +five minutes after the recovery attempt started. The next write can then try +hardening again; recovery happens when a write is requested, without a timer. + +If an earlier `icacls` process has outlived its caller's timeout belt and has not +yet been reaped, writes to the same destination are refused, including after +the five-minute window. These refusals return promptly and preserve the next +recovery attempt. Healthy overlapping writes can proceed, including while an +earlier write is hardening its temporary file or waiting to rename it. + +Every recovery attempt must complete permission hardening before publishing the +file. Permission-denied errors remain failures and do not trigger an automatic +retry. Cleanup of a failed write does not consume the next write's recovery +attempt. Other file destinations have independent timeout tracking. diff --git a/docs-site/src/content/docs/zh-cn/guides/claude-code.md b/docs-site/src/content/docs/zh-cn/guides/claude-code.md index 2c5065516d4..d7575506f5a 100644 --- a/docs-site/src/content/docs/zh-cn/guides/claude-code.md +++ b/docs-site/src/content/docs/zh-cn/guides/claude-code.md @@ -499,7 +499,7 @@ Search 和图像描述沿用仓库已有的 Claude Code OAuth fingerprint 先例 | Assistant `tool_use` | `function_call`(`input` → JSON 字符串化的 `arguments`) | | 用户 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 前缀) | | 重放 `thinking` / `redacted_thinking` | `reasoning` 项;签名和脱敏载荷保存在有界 `ocxr1` 信封中 | -| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). 在到 `api.anthropic.com` 的转换路由上,带类型的 `web_search_*` 和 `web_fetch_*` 声明也会发送到 Anthropic 并在服务端执行;如果网页搜索边车提供了 `web_search`,则优先使用边车。搜索和抓取结果以 `server_tool_use` 及对应的 `web_search_tool_result` / `web_fetch_tool_result` 块返回客户端 | +| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`). 在到 `api.anthropic.com` 的转换路由上,带类型的 `web_search_*` 和 `web_fetch_*` 声明也会发送到 Anthropic 并在服务端执行;如果网页搜索边车提供了 `web_search`,则优先使用边车。搜索和抓取结果以 `server_tool_use` 及对应的 `web_search_tool_result` / `web_fetch_tool_result` 块返回客户端 带有 `allowed_domains`、`blocked_domains` 或 `user_location` 的托管搜索会从转换后的 Responses/Chat 路由中移除;强制调用已移除的搜索返回 400。有效的 `defer_loading` 函数定义仍可作为普通函数调用。参见[转换限制](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes)。 | | `tool_choice` | `auto`→`auto`,`none`→`none`,`any`→`required`,指定函数→`{type:"function",name}`,托管 WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md index 3d2f9b0b3ff..fcfa28dffac 100644 --- a/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md +++ b/docs-site/src/content/docs/zh-cn/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Codex App 模型选择器 description: opencodex 中的模型如何通过共享 Codex 目录出现在 Codex App、Codex CLI 和 Codex TUI 中。 --- +默认值为 `false`。在 `config.json` 中设置 `"omitDisabledNativeModels": true` 并运行 `ocx sync`,即可从目录、缓存和 Codex 格式的 HTTP 模型列表中移除已禁用的 native 行。移除前经过验证的继承模板会单独保留,因此 routed 行的元数据不变,直接请求的路由也不变。下文的 hide 说明适用于默认设置。 + opencodex 不会修改 Codex App。它会写入 Codex CLI/TUI 使用的同一套 Codex 配置和模型目录。 app-server 会读取这份共享状态,但部分 Codex Desktop 版本还会在 renderer 中应用第二层远程 allowlist,因此仍可能从选择器里删掉路由模型。 diff --git a/docs-site/src/content/docs/zh-cn/guides/combos.md b/docs-site/src/content/docs/zh-cn/guides/combos.md index 8cb8bbe9e80..5244fe9b646 100644 --- a/docs-site/src/content/docs/zh-cn/guides/combos.md +++ b/docs-site/src/content/docs/zh-cn/guides/combos.md @@ -150,7 +150,8 @@ combo 失败分为 **跳转** 失败和 **终止** 失败。 | HTTP 401、403、404、408、429,或任何 5xx | 使该目标进入冷却,并跳转到下一个合格目标。 | | HTTP 410,并明确表明模型已到生命周期终点、retired、deprecated、sunset、decommissioned 或不再可用 | 仅冷却该目标并继续跳转。无关的 410 仍然是终止错误。 | | 被分类为认证、订阅、配额、速率限制、过载或上游服务器错误 | 即使仅凭状态码不足以判断,也会使该目标进入冷却并跳转。 | -| 客户端取消(499)、`origin_rejected`、cyber-policy 拒绝、上下文溢出,或其他无效请求 | 停止并返回错误;换其他目标也无法让请求变得有效。 | +| 客户端取消(499)、`origin_rejected`、cyber-policy 拒绝、或其他无效请求 | 停止并返回错误;换其他目标也无法让请求变得有效。 | +| 输出前确认的上下文超限(包括 HTTP 400) | 不冷却该目标,尝试下一个尚未尝试的合格目标;耗尽后返回第一个确认的 HTTP 400 上下文错误(其他状态码的上下文拒绝也会切换到下一个目标,但不会被保留)。 | | HTTP 400 明确表示使用 ChatGPT 账户运行 Codex 时不支持该模型 | 在输出提交前尝试下一个已配置目标。完整拒绝句必须位于 `detail`、`error.message` 或纯文本中;同时包含 `detail` 与 `error` 的混合封装仍会终止请求。此原因本身不会将账户标为需要重新登录,也不会让供应商的所有模型进入冷却。 | | 结构化 HTTP 400,明确拒绝 `user`、对 `reasoning.effort`/`reasoning_effort` 返回不支持值、返回模型特定图像输入拒绝(`param: input`),或拒绝该路由无法读取的输入媒体(`unsupported_input_modality`) | 在输出开始前跳转到下一个符合条件的目标,且不记录冷却时间;参见下方可选参数兼容性。 | | 由进程内适配器(`runTurn`)执行的 Responses 回合中,当前请求未声明的第一个工具调用(在任何输出和不可重放的副作用之前) | 让该目标进入冷却,并以相同的工具目录跳转到下一个目标。出现可见输出或不可重放的副作用之后,拒绝即为最终结果。Chat Completions 和 Anthropic Messages 请求不受影响。 | @@ -160,6 +161,8 @@ combo 失败分为 **跳转** 失败和 **终止** 失败。 当前请求不会再次重试同一个已经尝试过的目标——但有一个例外:设置了 `waitForCooldownMs` 的单目标 combo 由于没有可故障转移的替代目标,可以在同一请求内该目标的冷却结束后重试其唯一目标。请求本地的兼容性拒绝仍然直接返回而不重试。后续请求会跳过它,直到冷却结束。已过去的 HTTP-date `Retry-After` 同样会像 `Retry-After: 0` 一样保留为上游即时指令。设置 `waitForCooldownMs` 后,后续请求可以等待最早恢复资格的目标的冷却,单次选择尝试最多等待该上限,然后重新选择一次。因此,多次故障切换跳转的请求总共最多等待 `hops × waitForCooldownMs`。默认值为 `0`;当所有合格目标都处于冷却中时,请求会立即失败并返回 HTTP 503;该 `combo_unavailable` 503 会带有 `Retry-After` 标头,其值等于剩余冷却时间最短的目标,向上取整为整秒,最小值为 1 秒。等待不加入抖动,因此可能同时唤醒。请求中止会取消这次等待并返回正常的 `client_cancelled` 响应;取消后不会调度备用目标。combo 目标冷却是进程本地、按 combo 区分的状态,与原生账户路由使用的账户级 Codex 配额冷却彼此独立。 +上述 HTTP 503 规则适用于尚未尝试任何目标时。确认 HTTP 400 上下文拒绝后,若合格目标或发送预算耗尽,会以 `context_length_exceeded` 返回第一个此类拒绝,优先于后续可重试错误(包括配额错误)。剩余目标无法重放推理或缺少计费使用同意时也适用。取消、后续致命拒绝和明确的回退策略拒绝仍会立即停止;不会向未经同意的计费目标发送请求。 + :::note 故障切换是有边界的。它有助于处理特定目标的可用性、认证、配额和过载失败;它不会掩盖调用方错误或策略拒绝。 在非 combo 的 Responses 请求上,allowlist 中的 xAI 策略 403 会在 Codex 将其当作传输失败重试之前被改写为 HTTP 200 `incomplete/content_filter`;见 [xAI policy refusals](/reference/proxy-formats/#xai-policy-refusals)。combo 跳转仍把原始 HTTP 403 分类为跳转。 @@ -296,7 +299,7 @@ combo id 不存在。响应是 HTTP 404,类型为 `invalid_request_error`。 ### 为什么故障切换在第一次错误后就停止了? -该错误是终止性的,而不是针对目标的。修复无效输入、缩小过大的上下文、处理策略拒绝,或者纠正被拒绝的请求来源。对于这些情况,combo 不会继续跳转。 +该错误是终止性的,而不是针对目标的。修复无效输入、处理策略拒绝,或者纠正被拒绝的请求来源。对于这些情况,combo 不会继续跳转。 ## 可选参数兼容性 diff --git a/docs-site/src/content/docs/zh-cn/guides/integrations.md b/docs-site/src/content/docs/zh-cn/guides/integrations.md index f40a9a00ddd..41c8042427e 100644 --- a/docs-site/src/content/docs/zh-cn/guides/integrations.md +++ b/docs-site/src/content/docs/zh-cn/guides/integrations.md @@ -221,6 +221,16 @@ Undo 会恢复**两个原始字节串**,包括原本不存在的文件。操 下载文件 `cline-config-bundle.json` 包含两个原生文档成员:对应 `providers.json` 的 `settings`,以及对应 `models.json` 的 `catalog`。它本身不是 Cline 设置文件。建议使用集成命令,以获得带日志的合并和回滚。此生成集成不支持远程准入接线,需要免认证的回环访问。 +## omo(基于 Pi)的 Responses 方言 + +托管的基于 Pi 的 omo 配置块默认使用 Chat 方言(`api: "openai-completions"`)。Chat 会在工具调用之间丢弃上一步的推理,较长的 omo 工具循环会受到影响。要让启用和每次刷新都写入 Responses 方言,请在 opencodex 配置中添加以下仅能在配置文件中设置的键: + +```json +{ "omoClientExport": { "api": "openai-responses" } } +``` + +重启代理以加载修改后的配置(服务安装使用 `ocx service restart`,否则先 `ocx stop` 再 `ocx start`),然后在仪表盘的 **Connect → omo** 中刷新 omo 集成。仅刷新会使用运行中代理的配置,不会重新读取配置文件。该设置只对 omo 生效;Pi、Gentle Shell 等其他 Pi 格式客户端仍使用 `openai-completions`。要改回 Chat,请删除该键,按同样方式重启代理,再刷新 **Connect → omo**。未知值会被忽略。opencodex 仍只管理 `~/.omo/agent/models.json` 中的 `providers.opencodex`,该文件中的其他提供商保持不变。 + ## omo (Codex / LazyCodex) 角色推理强度 检测到 LazyCodex 后,可以在 omo 标签页的 **omo (Codex / LazyCodex)** 部分为 `$CODEX_HOME/agents/*.toml` 中的每个角色选择模型和推理强度,然后点击 **保存**。没有固定模型的角色所在行可以只保存推理强度,该角色仍继承其模型。opencodex 只更新该角色文件根级的 `model` 和 `model_reasoning_effort` 键,缺少的固定值会插入到文件开头附近;角色的指令、其他注释和其他键保持不变。只更改模型的保存不会发送 `effort`,两个文件中的推理强度都保持原样。选择某个级别会插入或替换角色文件中的 `model_reasoning_effort`;如果 LazyCodex 支持该级别,还会写入 `~/.omo/omo.jsonc` 的 `[codex].agents..reasoning`(`ultra` 只保留在角色文件中,并移除旧的 `reasoning`)。在命令行中可以用 `ocx agent roles set --effort ` 完成同样的操作。 diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index de310926515..4cbf796c7fb 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -158,7 +158,7 @@ Kimi Code 原生 Responses 路由会省略明确正文为空的 assistant 历史 OAuth 凭据中带有稳定账号 id 或邮箱的提供商可以保存多个登录。Providers 页面会在下拉列表中显示这些 账号,允许继续添加,并在不登出其他账号的情况下切换当前账号。普通登录时,没有身份信息的 Kimi 凭据会替换 -当前 active slot;显式 **添加账号** 会保留原有 slot 并激活一个独立的新 slot。Kiro 账户以配置文件 ARN 为键。 +当前 active slot;显式 **添加账号** 会保留原有 slot 并激活一个独立的新 slot。Kiro 账户结合配置文件 ARN 与已登录用户的电子邮件地址(若可用)来区分身份;共享同一组织配置文件的用户可以各自保留独立的凭据。 `chatgpt` 始终只有一个 slot,因为 Codex 账号池使用独立存储。令牌仍保存在 `~/.opencodex/auth.json` 中;`/api/oauth/accounts` 只返回脱敏后的 metadata。 @@ -177,6 +177,8 @@ cat accounts.json | ocx account import google-antigravity --format cockpit-tools ### Kiro 凭据导入 +在仪表盘选择 **AWS IAM Identity Center**,输入组织的 AWS 访问门户 URL 和 SSO 区域,即可添加账户,无需 CLI 或 SSH。在浏览器批准设备代码后,如有多个 Kiro 配置文件,请选择要保存的配置文件。下面的 CLI 步骤适用于 **Kiro CLI** 选项。 + Kiro 登录需要 Kiro CLI:Unix 使用 `curl -fsSL https://cli.kiro.dev/install | bash` 安装;Windows PowerShell 使用 `irm 'https://cli.kiro.dev/install.ps1' | iex`;然后先运行 `kiro-cli login`。如果没有 `kiro-cli` 会话,`ocx login kiro` 会回退到粘贴的访问令牌或 `KIRO_ACCESS_TOKEN` 环境变量。 普通的 `ocx login kiro` 导入会以只读方式打开 CLI SQLite 数据库,不修改数据库、WAL 或 SHM。 @@ -267,7 +269,7 @@ Cline IDE/CLI 中提供,不能通过 API 使用;`minimax/minimax-m2.5` 是 | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`。`moonshot` API 默认使用 `kimi-k3.1-pro`;官方 API 地址中已保存的 `kimi-k2.5` 选择会迁移到该模型。 | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md b/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md index 4776dd575eb..2c2cde7bd9f 100644 --- a/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md +++ b/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md @@ -70,6 +70,8 @@ bun run dev:gui ### 筛选请求日志 +请求详情优先显示已记录的上游失败原因,而不是通用的代理错误文案。有效的物理上游发送次数始终显示;缺失或无效的会计信息显示为 **Unavailable**,而不是 0。只有在记录到一次已完全结算且一致的尝试时,**Retries** 才显示首次发送后的额外发送次数。多次尝试、未解决或不一致的会计信息,以及有损失的 attempt 归一化,都不会编造重试次数。未知的持久化原因回退为基于状态的标题。 + Logs 可组合界面、被拦截请求、提供商、完整模型名、状态、时间、速度和会话 ID,筛选当前已加载的日志。选项包含回退尝试;模型匹配忽略大小写及首尾空格,但不做部分匹配。日志中消失的选项恢复为全部。 时间范围为最近 15 分钟、1 小时或 1 天;Logs 标签页每 30 秒更新一次,即使关闭自动刷新也会更新。速度按完整请求耗时计算每秒输出 token,分为小于 15、15 至小于 50、至少 50;启用速度筛选时排除无测量值的请求。成功为 2xx,错误为 4xx/5xx。 diff --git a/docs-site/src/content/docs/zh-cn/reference/adapters.md b/docs-site/src/content/docs/zh-cn/reference/adapters.md index 512277d38fa..5af5494c331 100644 --- a/docs-site/src/content/docs/zh-cn/reference/adapters.md +++ b/docs-site/src/content/docs/zh-cn/reference/adapters.md @@ -109,7 +109,7 @@ interface ProviderAdapter { - **Extended thinking 计算:** Anthropic 要求 `max_tokens > thinking.budget_tokens`。adapter 把 reasoning effort 映射成 budget(minimal 1024 … max 32000),再计算留有输出余量的安全 `max_tokens`;启用 thinking 后会**移除 `temperature`/`top_p`**,因为 Anthropic 禁止此组合。 -- **自适应 thinking 显示:** 自适应 thinking 模型(Opus 4.7+、Sonnet 5、Fable)会收到 `thinking.display: "summarized"`,因此长时间思考会以 reasoning 增量送达 Chat 和 Responses 客户端,而不是几分钟的 heartbeat。隐藏推理摘要的请求(`reasoning.summary: "none"`)保持提供方默认值。 +- **自适应 thinking 显示:** 自适应 thinking 模型(Opus 4.7+、Sonnet 5、Fable)会收到 `thinking.display: "summarized"`,因此长时间思考会以 reasoning 增量送达 Chat 和 Responses 客户端,而不是几分钟的 heartbeat。隐藏推理摘要的请求(`reasoning.summary: "none"`)保持提供方默认值。但 `api.anthropic.com` 上的 Opus 5.5 默认使用 `display: "updates"`;显式设置 `reasoning.summary` 为 `"concise"` 或 `"detailed"` 时,使用 `"summarized"`,不添加 updates beta。详见 [Opus 5.5 进度更新](/guides/providers/#claude-opus-55-progress-updates)。 - 始终发送 `anthropic-version: 2023-06-01`。流式输出 `content_block_delta`(`text_delta`、`thinking_delta`、`input_json_delta`)。 diff --git a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md index d30ce4d9ea7..6c7a259ad2c 100644 --- a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md @@ -32,6 +32,7 @@ description: 提供方配置、凭据、配额,以及模型目录命令。 | `quota` | `--refresh`, `--json` | 读取提供方配额报告。 | | `presets` | `--json` | 列出仪表盘提供方预设。 | | `account-mode` | `pool`, `direct`, `--json` | 选择 Codex 账号的池化或直连路由。 | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | 从 CC-Switch SQLite(`cc-switch.db`)或旧版 JSON 文件预览、选择性应用或恢复无密钥提供商条目。 | 默认的 `add`、`remove` 和 `set-default` 修改本地配置。修改运行中的代理需加 `--live`,在线删除还需 `--yes`。`--sync --json` 同样会在保存后尝试同步;失败时保留保存结果,以非零状态退出,并返回 `needsSync: true`。`--live` 不能与 `--sync` 同用。pacing 和 snapshot/apply 操作见[英文指南](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline)。 @@ -62,6 +63,27 @@ ocx models live --provider ark --json 任何机密内容请使用 `--api-key` 或 OAuth 登录。 ::: +### 无密钥导入 CC-Switch 提供商 + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# 在另一个终端启动代理(或运行 ocx ensure),再添加密钥: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# 独立的回滚路径(先停止代理;仅移除未修改的无密钥条目): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` 仅从普通本地 CC-Switch SQLite 数据库或旧版 JSON 文件中投射显式白名单内的非机密元数据字段(提供商 ID、应用类型、base URL、协议适配器与默认模型)。为扫描输入会临时读取源文件与内嵌配置字节,但密钥/凭据字段、自由文本显示名称与 TOML 注释会在字节层跳过,绝不解码为字符串、写入磁盘或记录日志,且临时字节缓冲区会在 `finally` 中清零。窄化 base URL 路径规则(`http(s)://host[:port]` 配空路径或 1–4 个小写 `[a-z0-9._-]` API 路径段,如 `/v1`、`/api/v1` 或 `/compatible-mode/v1`)与标识符/模型语法校验仅作为纵深防御并拒绝不合规值(不回显原值)。源文件通过 `O_NOFOLLOW | O_NONBLOCK` 描述符校验单次打开,SQLite 在内存中只读反序列化且不写入临时文件;具有多个硬链接(`nlink > 1`)的 SQLite 文件会被拒绝以防通过别名路径绕过伴随文件检查,若同级 `-wal` 或 `-journal` 文件非空则会拒绝导入并提示先关闭 CC-Switch(或执行数据库 checkpoint)再重试。请仅从受信任的本地 CC-Switch 源文件导入,并在添加凭据前核对输出的每个 ID、模型与 `baseUrl`: + +- **预览(默认):** 需要 `--source `,不写入任何内容,报告发现、可用、冲突与无效条目数量。 +- **应用(`--apply`):** 需要 `--source ` 且代理已停止(`ocx stop`),在 Remote Link Child 上会被拒绝。重复指定 `--provider ` 可仅导入所选条目,传入 `--allow-private-network` 可允许环回或私有网络 base URL。即使未事先预览,`--apply` 也会在写入无密钥提供商条目及回滚日志 `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` 之前输出每个导入提供商的 ID、默认模型与目标 URL(文本模式输出到 `stdout`,`--json` 模式输出到 `stderr`);存在活动回滚日志时再次运行 `--apply` 会被拒绝。核对输出条目后启动代理(在另一终端运行 `ocx start` 或运行 `ocx ensure`),再通过 `printf '%s\n' "$API_KEY" | ocx account add-key `(管道标准输入)或 `ocx login ` 添加凭据。 +- **恢复(`--restore`):** 独立的回滚路径(而非添加凭据的后续步骤),要求代理已停止(`ocx stop`),在 Remote Link Child 上会被拒绝,且不接受 `--source`、`--provider` 或 `--allow-private-network`。它会以只读字节方式检查 `auth.json`(不可读或格式损坏时安全保留),仅从活动日志中移除仍保持导入时无密钥状态、未被选为 `defaultProvider`,且未被路由、组合(combos)、回退策略(fallback policy)、OAuth 账户或 API 密钥引用的提供商。 + ## 认证 ### `ocx login ` diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/advisor.md b/docs-site/src/content/docs/zh-cn/reference/configuration/advisor.md index 33d2ef714ab..41f38dcafea 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/advisor.md @@ -60,6 +60,10 @@ description: 独立专家咨询,支持 manual、preflight 与基于证据的 a 所配置的顾问 provider 可能与 Worker 的 provider 不同。 +咨询同时遵守调用方捕获的 API key 模型范围、付费 combo 同意和设备 fallback 限制,以及 Advisor 自身准入密钥的主机限制:设置时为 `OPENCODEX_API_AUTH_TOKEN` 或本代理接受的服务 token 文件,否则为第一个配置的 API key。只有在最后这种情况下密钥顺序才有影响:请把能访问 Advisor 模型的密钥放在第一位,否则每次咨询都不可用。设置了环境或服务 token 时,由该 token 决定咨询。这些规则同样适用于 combo 选择、嵌套决策模型和重试。无法满足这些规则的咨询会向 worker 返回不可用通知。 + +例如,`allowMeteredComboTargets: false` 的 `laptop` 密钥无法通过 Advisor 在计费 combo 目标上花费,`fallback.devices.laptop.provider: false` 也会阻止 Advisor 的 provider fallback 跳转。没有密钥的 loopback 调用方仍需要 combo 的 `allowMeteredTargets: true` 选择加入。当两个密钥都设置 `allowMeteredComboTargets: true` 且 combo 设置 `allowMeteredTargets: true` 时,即使经由公共绑定的 loopback 监听器,Advisor 也可以使用计费目标。服务器在验证请求时捕获调用方策略,早于等待正文容量或读取正文;之后的密钥或 fallback 编辑无法扩大该次咨询。这也覆盖路由的视觉辅助、compact 请求及其决策模型,以及 WebSocket 轮次内的每次候选重试。每个新的 WebSocket 轮次会捕获自己的策略。 + OpenCodex 不会把 provider API key、Authorization 头、OAuth token、仅后端使用的配置密钥、进程环境或隐藏的思维链写进该提示,也不会解密或转发加密的 provider 私有推理。**任务内容不做通用脱敏。** 贴进任务的密钥、工具读到的文件里的秘密、工具或日志打印出的 token 都可能被发送。OpenCodex 不运行通用 DLP。 ## 权威 diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index 1cff2b9560f..bc201ce0b73 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -43,6 +43,7 @@ ocx models provider openrouter on | `providers` | `Record` | — | 提供者名称到提供者配置的映射。 | | `openaiProviderTierVersion?` | `2` | 由迁移设置 | 标记单一、可感知选项的 OpenAI 投影已完成。 | | `disabledModels?` | `string[]` | — | 从 Codex catalog 和 `/v1/models` 中隐藏、但不阻止直接 proxy 调用的 model。routed id 会从列表中移除。account-qualified native id 只隐藏对应 selector row;bare native GPT id 会隐藏 bare row 以及该 model 的所有 account-selector row。Models 页面只显示裸原生行和路由行;若只隐藏一个 selector-qualified 行,请直接设置此配置字段。 | +| `omitDisabledNativeModels?` | `boolean` | `false` | 默认值为 `false`。在 `config.json` 中设置 `"omitDisabledNativeModels": true` 并运行 `ocx sync`,即可从目录、缓存和 Codex 格式的 HTTP 模型列表中移除已禁用的 native 行。移除前经过验证的继承模板会单独保留,因此 routed 行的元数据不变,直接请求的路由也不变。 | | `providerContextCaps?` | `Record` | `{}` | 按提供商设置的有效上下文上限。普通窗口只能缩小;支持长窗口的原生模型可以扩展到该模型支持的上限。 | | `providerContextCapValues?` | `Record` | `{}` | 各提供商最后选择的上限,关闭后仍保留。仅保存这些值不会启用上限。有效值优先于保存的选择值。 | | `contextCapValue?` | `number` | `350000` | 首次开启时使用的默认值。再次开启时恢复该提供商的选择值。修改全局值时附带 `setAll: true` 只会更新已开启的上限;不带值的 `setAll: true` 会按当前全局值开启所有已配置提供商的上限。 | @@ -119,7 +120,7 @@ selector,而不是分配一个新名称。 | `models?` | `string[]` | 初始/回退模型列表。`liveModels: false` 时,非空 `models` 后接 `retainModels`;若 `models` 为空或省略,则按已配置的 `defaultModel`、`retainModels` 顺序构建初始列表,重复 ID 仅保留首次出现的位置。 | | `liveModels?` | `boolean` | 启动/同步时获取实时目录(默认 `true`)。自定义提供者使用 `${baseUrl}/models`;内置项可能使用注册表 URL 并进行过滤。 | | `selectedModels?` | `string[]` | 发现之后的目录允许列表。非空时只暴露这些 id;为空或省略时则暴露全部发现到的模型。 | -| `modelDisplayNames?` | `Record` | 持久的仅显示名称,以此提供者的精确原生模型 id 为键。键区分大小写。名称优先于提供者目录元数据,并且不会改变身份验证、适配器、路由、计费或上游请求。该映射最多可包含 2,000 个条目,与发现上限相同。 | +| `modelDisplayNames?` | `Record` | 持久的仅显示名称,以此提供者的精确原生模型 id 为键。键区分大小写。名称优先于提供者目录元数据,并且不会改变身份验证、适配器、路由、计费或上游请求。例外:当直接的 `provider/model` 选择器不匹配任何已知 id、别名或 `retainModels`(例如实时目录尚未获取时),编码形式相同的键会作为原生 id 发送。该映射最多可包含 2,000 个条目,与发现上限相同。 | | `contextWindow?` | `number` | 上游缺少元数据时使用的提供者级上下文数值;有元数据时作为上限,保留更小的实时数值。Models 面板中与 `providerContextCaps` 分开设置。 | | `modelContextWindows?` | `Record` | 按模型设置的上下文数值与上限。优先于 `contextWindow`:窗口未知时采用所配置的数值,而更小的实时元数据仍然优先。 | | `inferContextWindow?` | `boolean` | 仅限自定义渠道,默认关闭。设为 `true` 时用内置模型元数据补全缺失窗口;这是估计值,不代表中转的实测容量。 | @@ -155,7 +156,7 @@ selector,而不是分配一个新名称。 | `noPenaltyModels?` | `string[]` | 会拒绝 presence/frequency penalty 的模型。 内置 `xai` 预设在此列出 xAI 文档说明会拒绝这些参数的推理模型(`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`);非推理模型保留调用方的 penalty。 | | `noStructuredOutputModels?` | `string[]` | `openai-chat` 端点拒绝 `response_format` 的精确模型 ID。仅当请求模型与条目完全匹配时才省略该字段;其他 `openai-chat` 模型仍启用 structured-output 转换。 | | `noJsonSchemaModels?` | `string[]` | `openai-chat` 端点拒绝 `json_schema` 形式但仍接受 `json_object` 的精确模型 ID。这类请求会降级为 `json_object` 而不是被丢弃,因此请求 JSON 的调用方仍能拿到 JSON。同一模型同时出现在两个列表时,以 `noStructuredOutputModels` 为准。`opencode go`、`opencode zen` 预设已为其 DeepSeek 路由内置该项。 | -| `foldDeveloperRoleToSystem?` | `boolean` | 记录某个 `openai-chat` 目的地是否接受 `developer` 角色。`foldDeveloperRoleToSystem` 未设置时按 `system` 发送,`true` 时按 `system` 发送,`false` 时按 `developer` 发送。未设置表示尚未记录该目的地的情况;`true` 记录上游拒绝该角色;`false` 记录其接受该角色。无论哪种情况,消息都保留在对话中的原有位置,只有角色改变。拒绝该角色的目的地会返回 `400 role 'developer' is not allowed`,这一轮根本无法开始,这就是未记录状态默认折叠的原因。 | +| `foldDeveloperRoleToSystem?` | `boolean` | 记录某个 `openai-chat` 目的地是否接受 `developer` 角色。`foldDeveloperRoleToSystem` 未设置时按 `system` 发送,`true` 时按 `system` 发送,`false` 时按 `developer` 发送。未设置表示尚未记录该目的地的情况;`true` 记录上游拒绝该角色;`false` 记录其接受该角色。未启用 `singleLeadingSystemMessage` 时,消息保留在对话中的原有位置,只有角色改变。拒绝该角色的目的地会返回 `400 role 'developer' is not allowed`,这一轮根本无法开始,这就是未记录状态默认折叠的原因。 | | `parallelToolCalls?` | `boolean` | 切换并行工具调用。OpenAI Chat 默认开启;非 chat 适配器只有显式 `true` 时才会声明支持。 | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean; repairInvalidIds?: boolean }` | 默认关闭的下游 SSE 修复,用于精确占位 id、缺失的终止 id,以及(`repairInvalidIds`)缺少规范 `msg_`/`rs_` 前缀的 message/reasoning id。function-call id 永远不会被重写。内置 DeepSeek 默认启用后两项。 | | `responsesSnapshotRepair?` | `boolean` | 默认关闭的客户端修复,用于补全 SSE 与 JSON 中稀疏 Responses 生命周期快照缺失的 status、output 和工具元数据;原始检查与持久化保持不变。 | @@ -181,23 +182,26 @@ selector,而不是分配一个新名称。 | `unsafeAllowNativeLocalExec?` | `boolean` | Cursor 旧布尔值;仅当更新字段未设置时,等同于 `nativeLocalExec: "on"`。 | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Cursor 本地执行策略。`off` 是默认值;`codex-sandbox` 目前会像 `off` 一样失败关闭。 | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + 注册或替换提供商(`POST /api/providers`)时,会先验证 `responsesPath` 和 `chatCompletionsPath`,再修改内存或磁盘中的配置。`PATCH /api/providers?name=` 会将请求体与已保存的提供商合并;除仅更新 `requestPacing` 的请求外,凡是修改 `disabled` 以外字段的更新,都会在保存前以同样方式验证合并后提供商的路径,若保留的既有路径无效则返回 `400`,且不更改配置。加载配置文件时也适用同样的路径规则。 API key 提供者可以持有字面量 key,或环境引用。OAuth 提供者使用由 `ocx login` 填充的凭据存储;基于订阅的 Claude Code 启动行为在 [`claudeCode.authMode`](/zh-cn/reference/configuration/server/#claude-code-claudecode) 下配置。 ### 保存提供方时会保留什么 -用已有提供方的名称调用 `POST /api/providers`,会用根据请求构建的行替换已存储的行。仪表板的添加/编辑表单无法发送所有字段,因此保存时会保留请求省略的部分已存储字段。其中八个记录的是某个上游的行为:`preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 +用已有提供方的名称调用 `POST /api/providers`,会用根据请求构建的行替换已存储的行。仪表板的添加/编辑表单无法发送所有字段,因此保存时会保留请求省略的部分已存储字段。其中十一个记录的是某个上游的行为:`preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 -| 保存 | 八项设置 | 已存储的 `apiKeyPool` | +| 保存 | 十一项设置 | 已存储的 `apiKeyPool` | | --- | --- | --- | | 目的地相同,字段省略 | 保留已存储的值,包括显式的 `[]` 或 `false` | 保留 | | 新目的地,字段省略 | 不保留;可能套用新目的地的注册表默认值 | 不保留 | | 请求中发送了该字段 | 请求中的值 | 请求中的值 | -目的地指适配器、base URL(协议与主机名比较时不区分大小写,忽略末尾斜杠),以及请求中指定了时的认证模式。把提供方移到其他目的地时,描述旧上游的八项设置和为旧上游签发的密钥池都不会带过去。保存从不把旧行的其余部分合并进新行。 +目的地指适配器、base URL(协议与主机名比较时不区分大小写,忽略末尾斜杠),以及请求中指定了时的认证模式。把提供方移到其他目的地时,描述旧上游的十项设置和为旧上游签发的密钥池都不会带过去。保存从不把旧行的其余部分合并进新行。 -`PATCH /api/providers?name=` 只修改它指定的字段,无论目的地如何都保留其他所有已存储字段。它接受全部八项设置,`null` 表示清除。对于两个推理列表,空数组会作为显式退出选项保存,而不会被删除。 +`PATCH /api/providers?name=` 只修改它指定的字段,无论目的地如何都保留其他所有已存储字段。它接受全部十项设置,`null` 表示清除。对于两个推理列表,空数组会作为显式退出选项保存,而不会被删除。 ### 响应服务等级的可信度 diff --git a/docs-site/src/content/docs/zh-tw/guides/claude-code.md b/docs-site/src/content/docs/zh-tw/guides/claude-code.md index 8b4e9ec42fd..5c921ee4a34 100644 --- a/docs-site/src/content/docs/zh-tw/guides/claude-code.md +++ b/docs-site/src/content/docs/zh-tw/guides/claude-code.md @@ -556,7 +556,7 @@ Search 和圖像描述沿用儲存庫已有的 Claude Code OAuth fingerprint 先 | Assistant `tool_use` | `function_call`(`input` → JSON 字串化的 `arguments`) | | 使用者 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 字首) | | 重放 `thinking` / `redacted_thinking` | `reasoning` 項目;簽名與遮蔽載荷保存在有界 `ocxr1` 信封中 | -| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) | +| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) 帶有 `allowed_domains`、`blocked_domains` 或 `user_location` 的託管搜尋會從轉換後的 Responses/Chat 路由中移除;強制呼叫已移除的搜尋會回傳 400。有效的 `defer_loading` 函式定義仍可作為一般函式呼叫。參見[轉換限制](/guides/claude-code/#search-restrictions-and-deferred-tools-on-translated-routes)。 | | `tool_choice` | `auto`→`auto`,`none`→`none`,`any`→`required`,指定名稱 function→`{type:"function",name}`,hosted WebSearch/web_search→`{type:"web_search"}` | | `max_tokens` | `max_output_tokens` | | `stop_sequences` | `stop` | diff --git a/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md b/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md index 2b9eb7c20aa..bccd018f77b 100644 --- a/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md +++ b/docs-site/src/content/docs/zh-tw/guides/codex-app-models.md @@ -3,6 +3,8 @@ title: Codex App 模型選擇器 description: opencodex 模型如何透過共享 Codex 目錄出現在 Codex App、Codex CLI 和 Codex TUI 中。 --- +預設值為 `false`。在 `config.json` 中設定 `"omitDisabledNativeModels": true` 並執行 `ocx sync`,即可從目錄、快取與 Codex 格式的 HTTP 模型清單中移除已停用的 native 列。移除前經過驗證的繼承範本會另外保留,因此 routed 列的中繼資料不變,直接請求的路由也不變。下文的 hide 說明適用於預設設定。 + opencodex 不會修改 Codex App。它會寫入 Codex CLI/TUI 所使用的同一套 Codex 設定和模型目錄。 Codex 的 app-server 會讀取這份共享狀態,但部分 Codex Desktop 版本會在 renderer 套用第二層 遠端模型 allowlist,仍可能把已路由的列從選擇器中移除。 diff --git a/docs-site/src/content/docs/zh-tw/guides/combos.md b/docs-site/src/content/docs/zh-tw/guides/combos.md index 5ff12338332..6d90897006d 100644 --- a/docs-site/src/content/docs/zh-tw/guides/combos.md +++ b/docs-site/src/content/docs/zh-tw/guides/combos.md @@ -164,14 +164,17 @@ Combo 失敗分為**跳轉**失敗與**終端**失敗。 | --- | --- | | HTTP 401、403、404、408、429 或任何 5xx | 冷卻目標並跳到下一個合格目標。 | | 分類為認證、訂閱、配額、限流、過載或上游伺服器錯誤 | 冷卻目標並跳轉,即使單靠狀態碼不足。 | -| 客戶端取消(499)、`origin_rejected`、cyber-policy 拒絕、上下文溢出或其他無效請求 | 停止並回傳錯誤;另一個目標不會讓請求變為有效。 | +| 客戶端取消(499)、`origin_rejected`、cyber-policy 拒絕、其他無效請求 | 停止並回傳錯誤;另一個目標不會讓請求變為有效。 | +| 輸出前確認的上下文超限(包括 HTTP 400) | 不冷卻該目標,嘗試下一個尚未嘗試的合格目標;耗盡後回傳第一個確認的 HTTP 400 上下文錯誤(其他狀態碼的上下文拒絕也會切換到下一個目標,但不會被保留)。 | | 結構化 HTTP 400,明確拒絕 `user`、對 `reasoning.effort`/`reasoning_effort` 回傳不支援值、回傳模型特定影像輸入拒絕(`param: input`),或拒絕該路由無法讀取的輸入媒體(`unsupported_input_modality`) | 在輸出開始前跳轉到下一個符合條件的目標,且不記錄冷卻時間;參見下方選用參數相容性。 | | 由行程內轉接器(`runTurn`)執行的 Responses 回合中,目前請求未宣告的第一個工具呼叫(在任何輸出與不可重播的副作用之前) | 讓該目標進入冷卻,並以相同的工具目錄跳轉到下一個目標。出現可見輸出或不可重播的副作用之後,拒絕即為最終結果。Chat Completions 與 Anthropic Messages 請求不受影響。 | | 任何其他未分類錯誤 | 停止並回傳錯誤。 | 跳轉的目標在沒有設定 `cooldownMs` 時使用上游回退冷卻:請求速率限制代碼 `1302`/`1305` 為 5 秒,用量額度耗盡(不論 HTTP 狀態,包括 502)或憑證/計費失敗為 10 分鐘,其他情況為 60 秒。若上游回應包含有效的 `Retry-After` 值,opencodex 改用它;Codex 重設標頭與設定的 `cooldownMs` 優先於這些回退值。接受數字秒與 HTTP-date 值。明確的上游 `Retry-After` 最長為 24 小時;重設推導、設定與回退冷卻最長為 10 分鐘。 -目前請求永不重試同一已嘗試目標。後續請求會略過它直到冷卻到期。若無合格目標剩餘,代理回傳 HTTP 503 並帶 `error.code = "combo_unavailable"`。 +目前請求永不重試同一已嘗試目標。後續請求會略過它直到冷卻到期。若尚未嘗試任何目標且沒有合格目標,代理回傳 HTTP 503 並帶 `error.code = "combo_unavailable"`。 + +確認 HTTP 400 上下文拒絕後,若合格目標或傳送預算耗盡,會以 `context_length_exceeded` 回傳第一個此類拒絕,優先於後續可重試錯誤(包括配額錯誤)。這也適用於剩餘目標無法重播推理或缺少計費使用同意的情況。取消、後續致命拒絕和明確的備援政策拒絕仍會立即停止;不會向未獲同意的計費目標傳送請求。 :::note Failover 是刻意受限的。它有助於目標特定的可用性、認證、配額與過載失敗;不會隱藏呼叫者錯誤或策略拒絕。 @@ -293,7 +296,7 @@ Combo id 未知。回應為 HTTP 404 並帶 type `invalid_request_error`。執 ### 為什麼 failover 在第一個錯誤後就停止了? -該錯誤是終端的而非目標特定的。修正無效輸入、縮減過大的上下文、處理策略拒絕,或更正被拒的請求來源。Combo 對那些情況不會跳轉。 +該錯誤是終端的而非目標特定的。修正無效輸入、處理策略拒絕,或更正被拒的請求來源。Combo 對那些情況不會跳轉。 ## 選用參數相容性 diff --git a/docs-site/src/content/docs/zh-tw/guides/providers.md b/docs-site/src/content/docs/zh-tw/guides/providers.md index 35cd260dd7c..5f8d0e32108 100644 --- a/docs-site/src/content/docs/zh-tw/guides/providers.md +++ b/docs-site/src/content/docs/zh-tw/guides/providers.md @@ -155,8 +155,7 @@ session/task key 有助提升 Code Plan cache hit rate;沒有 key 的請求 credential 內含穩定 account id 或 email 的 OAuth provider 可以保存多個登入。Providers 頁面會在下拉 選單顯示這些帳號、允許新增帳號,並在不登出其他帳號的情況下切換目前帳號。一般登入時,沒有 identity 的 -Kimi credential 會取代 active slot;明確的 **新增帳號** 會保留原有 slot 並啟用另一個新 slot。Kiro 帳號以 -profile ARN 作為 key。`chatgpt` 始終是 single-slot,因為 +Kimi credential 會取代 active slot;明確的 **新增帳號** 會保留原有 slot 並啟用另一個新 slot。Kiro 帳號結合 profile ARN 與登入使用者的電子郵件地址(若可取得)來識別身分;共用同一組織 profile 的使用者可以各自保留獨立的憑證。`chatgpt` 始終是 single-slot,因為 Codex pool 帳號使用獨立 ledger。Token 仍存放在 `~/.opencodex/auth.json`;`/api/oauth/accounts` 只回傳 遮蔽後的 metadata。 @@ -217,6 +216,8 @@ Reauthenticate。Codex pool 帳號不是那些 provider 之一,但 `ocx login ### Kiro credential 匯入 +在儀表板選擇 **AWS IAM Identity Center**,輸入組織的 AWS 存取入口網站 URL 和 SSO 區域,即可新增帳號,無需 CLI 或 SSH。在瀏覽器核准裝置代碼後,如有多個 Kiro 設定檔,請選擇要儲存的設定檔。以下 CLI 步驟適用於 **Kiro CLI** 選項。 + Kiro 登入預期存在 Kiro CLI。Unix 可用 `curl -fsSL https://cli.kiro.dev/install | bash` 安裝;Windows PowerShell 使用 `irm 'https://cli.kiro.dev/install.ps1' | iex`;接著以 `kiro-cli login` 登入。若沒有 `kiro-cli` session,`ocx login kiro` 會 fallback 到貼上的 access token 或 `KIRO_ACCESS_TOKEN` 環境變數。 @@ -311,7 +312,7 @@ IDE/CLI,不透過 API;`minimax/minimax-m2.5` 是文件列出的 API 免費 | Novita AI | `https://api.novita.ai/openai/v1` | | Together | `https://api.together.xyz/v1` | | Fireworks | `https://api.fireworks.ai/inference/v1` | -| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1` | +| Moonshot (Kimi API) · Kimi (coding) | `https://api.moonshot.ai/v1` · `https://api.kimi.com/coding/v1`。`moonshot` API 預設使用 `kimi-k3.1-pro`;官方 API 位址中已儲存的 `kimi-k2.5` 選擇會遷移到該模型。 | | Hugging Face | `https://router.huggingface.co/v1` | | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | diff --git a/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md b/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md index 3c9340ef572..ea24d88142b 100644 --- a/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md +++ b/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md @@ -79,6 +79,8 @@ GUI session 簽發到服務的頁面中,並在到期或代理重啟時靜默 ### 篩選請求日誌 +請求詳細資訊會優先顯示已記錄的上游失敗原因,而不是通用的代理錯誤文案。有效的上游實際傳送次數一律顯示;缺少或無效的記帳資訊則顯示 **Unavailable**,而非 0。只有在記錄到一次已完全結算且一致的嘗試時,**Retries** 才顯示首次傳送後的額外傳送次數。多次嘗試、未解決或不一致的記帳資訊,以及有損失的 attempt 正規化,都不會憑空產生重試次數。未知的持久化原因會回退為依狀態顯示的標題。 + Logs 可組合介面、被攔截請求、供應商、完整模型名稱、狀態、時間、速度和對話 ID,篩選目前已載入的日誌。選項包含回退嘗試;模型比對忽略大小寫及頭尾空白,但不做部分比對。日誌中消失的選項恢復為全部。 時間範圍為最近 15 分鐘、1 小時或 1 天;Logs 分頁每 30 秒更新一次,即使關閉自動重新整理也會更新。速度按完整請求耗時計算每秒輸出 token,分為小於 15、15 至小於 50、至少 50;啟用速度篩選時排除無測量值的請求。成功為 2xx,錯誤為 4xx/5xx。 diff --git a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md index e5e3c6dd68f..a743fe07a5d 100644 --- a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md @@ -24,6 +24,7 @@ description: 供應商設定、憑證、配額與模型目錄指令。 | `quota` | `--refresh`, `--json` | 讀取供應商配額報告。 | | `presets` | `--json` | 列出儀表板供應商預設。 | | `account-mode` | `pool`, `direct`, `--json` | 選擇池化或直接的 Codex 帳號路由。 | +| `import-cc-switch` | `--source `, `--provider `, `--apply`, `--restore`, `--allow-private-network`, `--json` | 從 CC-Switch SQLite(`cc-switch.db`)或舊版 JSON 檔案預覽、選擇性套用或還原無金鑰供應商項目。 | 預設的 `add`、`remove` 與 `set-default` 修改本機設定。修改執行中的代理需加 `--live`,線上刪除還需 `--yes`。`--sync --json` 同樣會在儲存後嘗試同步;失敗時保留儲存結果,以非零狀態結束,並回傳 `needsSync: true`。`--live` 不能與 `--sync` 同用。pacing 與 snapshot/apply 操作請見[英文指南](/reference/cli/providers-accounts/#snapshot-edit-and-apply-with-a-baseline)。 @@ -40,6 +41,27 @@ ocx models live --provider ark --json `--jsonl` 僅輸出已設定的供應商,每行一個 JSON 物件。每個物件的欄位與 `--json` 輸出中 `configured` 陣列的元素相同,不包含 `registryCount` 摘要。指令碼可以逐行處理這些物件。`--json` 與 `--jsonl` 不能同時使用。 +### 無金鑰匯入 CC-Switch 供應商 + +```bash +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db +ocx stop +ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply +# 在另一個終端機啟動代理(或執行 ocx ensure),再新增金鑰: +ocx start +printf '%s\n' "$API_KEY" | ocx account add-key my-provider + +# 獨立的回滾路徑(先停止代理;僅移除未修改的無金鑰項目): +ocx stop +ocx provider import-cc-switch --restore +``` + +`ocx provider import-cc-switch` 僅從一般本機 CC-Switch SQLite 資料庫或舊版 JSON 檔案投射明確白名單內的非機密中繼資料欄位(供應商 ID、應用類型、base URL、協定轉接器與預設模型)。為掃描輸入會暫時讀取來源與內嵌設定位元組,但金鑰/憑證欄位、自由文字顯示名稱與 TOML 註解會在位元組層級略過,絕不解碼為字串、寫入磁碟或記錄日誌,且暫存位元組緩衝區會在 `finally` 中清零。窄化 base URL 路徑規則(`http(s)://host[:port]` 配空路徑或 1–4 個小寫 `[a-z0-9._-]` API 路徑段,如 `/v1`、`/api/v1` 或 `/compatible-mode/v1`)與識別碼/模型語法檢查僅作為縱深防禦並拒絕不合規值(不回顯原值)。來源檔案透過 `O_NOFOLLOW | O_NONBLOCK` 描述元驗證單次開啟,SQLite 在記憶體中唯讀反序列化且不寫入暫存檔;具有多個硬連結(`nlink > 1`)的 SQLite 檔案會被拒絕以防透過別名路徑繞過附屬檔檢查,若同層 `-wal` 或 `-journal` 檔案非空則會拒絕匯入並提示先關閉 CC-Switch(或執行資料庫 checkpoint)再重試。請僅從受信任的本機 CC-Switch 來源檔案匯入,並在新增憑證前核對輸出的每個 ID、模型與 `baseUrl`: + +- **預覽(預設):** 需要 `--source `,不寫入任何內容,回報發現、可用、衝突與無效項目數量。 +- **套用(`--apply`):** 需要 `--source ` 且代理已停止(`ocx stop`),在 Remote Link Child 上會被拒絕。重複指定 `--provider ` 可僅匯入所選項目,傳入 `--allow-private-network` 可允許迴路或私有網路 base URL。即使未事先預覽,`--apply` 也會在寫入無金鑰供應商項目與回滾日誌 `$OPENCODEX_HOME/cc-switch-provider-import-journal.json` 之前輸出每個匯入供應商的 ID、預設模型與目標 URL(文字模式輸出至 `stdout`,`--json` 模式輸出至 `stderr`);存在使用中回滾日誌時再次執行 `--apply` 會被拒絕。核對輸出項目後啟動代理(在另一終端機執行 `ocx start` 或執行 `ocx ensure`),再透過 `printf '%s\n' "$API_KEY" | ocx account add-key `(管線標準輸入)或 `ocx login ` 新增憑證。 +- **還原(`--restore`):** 獨立的回滾路徑(而非新增憑證的後續步驟),要求代理已停止(`ocx stop`),在 Remote Link Child 上會被拒絕,且不接受 `--source`、`--provider` 或 `--allow-private-network`。它會以唯讀位元組方式檢查 `auth.json`(無法讀取或格式損毀時安全保留),僅從使用中日誌移除仍維持匯入時無金鑰狀態、未被選為 `defaultProvider`,且未被路由、組合(combos)、回退策略(fallback policy)、OAuth 帳號或 API 金鑰參照的供應商。 + ## 認證 ### `ocx login ` diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/advisor.md b/docs-site/src/content/docs/zh-tw/reference/configuration/advisor.md index 05a2936e8f5..2bcaa37e209 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/advisor.md @@ -60,6 +60,10 @@ description: 獨立專家諮詢,支援 manual、preflight 與依據證據的 a 所設定的顧問 provider 可能與 Worker 的 provider 不同。 +諮詢同時遵守呼叫者擷取的 API key 模型範圍、付費 combo 同意與裝置 fallback 限制,以及 Advisor 自身准入金鑰的主機限制:設定時為 `OPENCODEX_API_AUTH_TOKEN` 或本代理接受的服務 token 檔案,否則為第一個設定的 API key。只有在最後這種情況下金鑰順序才有影響:請把能存取 Advisor 模型的金鑰放在第一位,否則每次諮詢都無法使用。設定了環境或服務 token 時,由該 token 決定諮詢。這些規則同樣適用於 combo 選擇、巢狀決策模型與重試。無法滿足這些規則的諮詢會向 worker 回傳不可用通知。 + +例如,`allowMeteredComboTargets: false` 的 `laptop` 金鑰無法透過 Advisor 在計費 combo 目標上花費,`fallback.devices.laptop.provider: false` 也會阻擋 Advisor 的 provider fallback 跳轉。沒有金鑰的 loopback 呼叫者仍需要 combo 的 `allowMeteredTargets: true` 選擇加入。當兩個金鑰都設定 `allowMeteredComboTargets: true` 且 combo 設定 `allowMeteredTargets: true` 時,即使經由公開綁定的 loopback 監聽器,Advisor 也能使用計費目標。伺服器在驗證請求時擷取呼叫者政策,早於等待本文容量或讀取本文;之後的金鑰或 fallback 編輯無法擴大該次諮詢。這也涵蓋路由的視覺輔助、compact 請求及其決策模型,以及 WebSocket 回合內的每次候選重試。每個新的 WebSocket 回合會擷取自己的政策。 + OpenCodex 不會把 provider API key、Authorization 標頭、OAuth token、僅後端使用的設定密鑰、行程環境或隱藏的思維鏈寫進該提示,也不會解密或轉送加密的 provider 私有推理。**任務內容不做通用脫敏。** 貼進任務的金鑰、工具讀到的檔案裡的秘密、工具或日誌印出的 token 都可能被傳送。OpenCodex 不執行通用 DLP。 ## 權威 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 079715c71b3..5a430f66c07 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -43,6 +43,7 @@ ocx models provider openrouter on | `providers` | `Record` | — | 供應商名稱到供應商設定的映射。 | | `openaiProviderTierVersion?` | `2` | 由遷移設定 | 標記單一選項感知的 OpenAI projection 已完成。 | | `disabledModels?` | `string[]` | — | 對 Codex 目錄與 `/v1/models` 隱藏的模型,但不阻擋直接代理呼叫。路由 id 從清單中移除;裸原生 GPT id 取得 `visibility: "hide"`。 | +| `omitDisabledNativeModels?` | `boolean` | `false` | 預設值為 `false`。在 `config.json` 中設定 `"omitDisabledNativeModels": true` 並執行 `ocx sync`,即可從目錄、快取與 Codex 格式的 HTTP 模型清單中移除已停用的 native 列。移除前經過驗證的繼承範本會另外保留,因此 routed 列的中繼資料不變,直接請求的路由也不變。 | | `providerContextCaps?` | `Record` | `{}` | 各供應商目前生效的上下文上限。一般視窗只能縮小;支援長視窗的原生模型可以擴展到該模型支援的上限。 | | `providerContextCapValues?` | `Record` | `{}` | 各供應商最後選擇的上限,停用後仍保留。僅儲存這些值不會啟用上限。生效中的值優先於儲存的選擇值。 | | `contextCapValue?` | `number` | `350000` | 首次啟用時使用的預設值。再次啟用時恢復該供應商的選擇值。修改全域值時附帶 `setAll: true` 只會更新已啟用的上限;不帶值的 `setAll: true` 會以目前全域值啟用所有已設定供應商的上限。 | @@ -128,7 +129,7 @@ ocx models provider openrouter on | `noPenaltyModels?` | `string[]` | 拒絕 presence/frequency penalty 的模型。 內建 `xai` 預設在此列出 xAI 文件說明會拒絕這些參數的推理模型(`grok-4.7`, `grok-4.6`, `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-0309`, `grok-4.20-0309-reasoning`, `grok-build-0.1`);非推理模型保留呼叫者的 penalty。 | | `noStructuredOutputModels?` | `string[]` | 其 `openai-chat` 端點拒絕 `response_format` 的精確模型 ID。僅精確符合的請求模型會省略該欄位;structured-output 轉譯對其他每個 `openai-chat` 模型保持啟用。 | | `noJsonSchemaModels?` | `string[]` | 其 `openai-chat` 端點拒絕 `json_schema` 形式但仍接受 `json_object` 的精確模型 ID。這類請求會降級為 `json_object` 而非被丟棄,因此要求 JSON 的呼叫端仍會拿到 JSON。同一模型同時列在兩份清單時,以 `noStructuredOutputModels` 為準。`opencode go`、`opencode zen` 預設已為其 DeepSeek 路由內建。 | -| `foldDeveloperRoleToSystem?` | `boolean` | 記錄某個 `openai-chat` 目的地是否接受 `developer` 角色。`foldDeveloperRoleToSystem` 未設定時以 `system` 傳送,`true` 時以 `system` 傳送,`false` 時以 `developer` 傳送。未設定表示尚未記錄該目的地的情況;`true` 記錄上游拒絕該角色;`false` 記錄其接受該角色。無論何者,訊息都保留在對話中的原有位置,只有角色改變。拒絕該角色的目的地會回應 `400 role 'developer' is not allowed`,該回合根本無法開始,這就是未記錄狀態預設摺疊的原因。 | +| `foldDeveloperRoleToSystem?` | `boolean` | 記錄某個 `openai-chat` 目的地是否接受 `developer` 角色。`foldDeveloperRoleToSystem` 未設定時以 `system` 傳送,`true` 時以 `system` 傳送,`false` 時以 `developer` 傳送。未設定表示尚未記錄該目的地的情況;`true` 記錄上游拒絕該角色;`false` 記錄其接受該角色。未啟用 `singleLeadingSystemMessage` 時,訊息保留在對話中的原有位置,只有角色改變。拒絕該角色的目的地會回應 `400 role 'developer' is not allowed`,該回合根本無法開始,這就是未記錄狀態預設摺疊的原因。 | | `parallelToolCalls?` | `boolean` | 切換平行工具呼叫。OpenAI Chat 預設開啟;非 chat adapter 僅在明確 `true` 時廣告。 | | `responsesItemIdRepair?` | `{ message?: string[]; reasoning?: string[]; repairMissingTerminalIds?: boolean }` | 預設停用的下游 SSE 修復,用於精確佔位 id 與缺失的終端 id。Function-call id 永不被重寫。 | | `promptCacheKey?` | `boolean` | 為整個 `openai-chat` provider 啟用 `prompt_cache_key` 轉送。adapter 只轉送收到的 key,不自行產生,但 Claude Messages 轉換會從 `metadata.user_id`,或在沒有 metadata 時從模型/系統/工具組合衍生 key。除非 preset 宣告,否則預設關閉;Mistral 依其[提示快取契約](https://docs.mistral.ai/studio/conversations/advanced/prompt-caching)啟用,明確的 `false` 可停用。僅在上游明確支援時啟用,嚴格閘道可能以 HTTP 400 拒絕未知欄位。 | @@ -152,23 +153,26 @@ ocx models provider openrouter on | `unsafeAllowNativeLocalExec?` | `boolean` | Cursor 舊版布林值,僅在較新欄位未設定時等同於 `nativeLocalExec: "on"`。 | | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | Cursor 本機執行政策。`off` 為預設;`codex-sandbox` 目前像 `off` 般 fail closed。 | +LM Studio instruction compatibility: `singleLeadingSystemMessage: true` overrides chronological Chat instruction placement by merging text-only system/developer messages into one leading system message. The LM Studio preset enables it by default; set it to `false` to disable merging. + + 註冊或替換供應商(`POST /api/providers`)時,會先驗證 `responsesPath` 和 `chatCompletionsPath`,再修改記憶體或磁碟中的設定。`PATCH /api/providers?name=` 會將請求內容與已儲存的供應商合併;除僅更新 `requestPacing` 的請求外,凡是修改 `disabled` 以外欄位的更新,都會在儲存前以同樣方式驗證合併後供應商的路徑,若保留的既有路徑無效則回傳 `400`,且不變更設定。載入設定檔時也適用相同的路徑規則。 API-key 供應商可持有字面值金鑰或環境參考。OAuth 供應商使用由 `ocx login` 填入的憑證存放;訂閱支援的 Claude Code 啟動行為在 [`claudeCode.authMode`](/zh-tw/reference/configuration/server/#claude-codeclaudecode) 下設定。 ### 儲存供應商時會保留什麼 -以既有供應商的名稱呼叫 `POST /api/providers`,會以根據請求建立的列取代已儲存的列。儀表板的新增/編輯表單無法傳送所有欄位,因此儲存時會保留請求省略的部分已儲存欄位。其中八個記錄的是某個上游的行為:`preserveReasoningContentModels`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 +以既有供應商的名稱呼叫 `POST /api/providers`,會以根據請求建立的列取代已儲存的列。儀表板的新增/編輯表單無法傳送所有欄位,因此儲存時會保留請求省略的部分已儲存欄位。其中十一個記錄的是某個上游的行為:`preserveReasoningContentModels`, `preserveResponsesReasoningContent`, `requiresReasoningPlaceholderModels`, `foldDeveloperRoleToSystem`, `singleLeadingSystemMessage`, `reasoningWireFormat`, `omitReasoningEffortWithToolsModels`, `promoteAdditionalTools`, `retryOn429`, `transientRetryOn5xx`, `retryOnReset`。 -| 儲存 | 八項設定 | 已儲存的 `apiKeyPool` | +| 儲存 | 十一項設定 | 已儲存的 `apiKeyPool` | | --- | --- | --- | | 目的地相同,欄位省略 | 保留已儲存的值,包括明確的 `[]` 或 `false` | 保留 | | 新目的地,欄位省略 | 不保留;可能套用新目的地的登錄檔預設值 | 不保留 | | 請求中傳送了該欄位 | 請求中的值 | 請求中的值 | -目的地指轉接器、base URL(比較協定與主機時不分大小寫,忽略結尾斜線),以及請求有指定時的驗證模式。把供應商移到其他目的地時,描述舊上游的八項設定和為舊上游核發的金鑰池都不會帶過去。儲存絕不會把舊列的其餘部分合併進新列。 +目的地指轉接器、base URL(比較協定與主機時不分大小寫,忽略結尾斜線),以及請求有指定時的驗證模式。把供應商移到其他目的地時,描述舊上游的十項設定和為舊上游核發的金鑰池都不會帶過去。儲存絕不會把舊列的其餘部分合併進新列。 -`PATCH /api/providers?name=` 只修改它指定的欄位,無論目的地為何都保留其他所有已儲存欄位。它接受全部八項設定,`null` 表示清除。對於兩個推理清單,空陣列會作為明確的退出選項儲存,而不會被刪除。 +`PATCH /api/providers?name=` 只修改它指定的欄位,無論目的地為何都保留其他所有已儲存欄位。它接受全部十項設定,`null` 表示清除。對於兩個推理清單,空陣列會作為明確的退出選項儲存,而不會被刪除。 ## 供應商診斷對外安全 diff --git a/gui/src/codex-quota-utils.ts b/gui/src/codex-quota-utils.ts index 191f61ae866..0dcfcba023c 100644 --- a/gui/src/codex-quota-utils.ts +++ b/gui/src/codex-quota-utils.ts @@ -1,4 +1,4 @@ -import { isTerminalShortWindow } from "../../src/codex/quota-types"; +import { hasElapsedShortWindow, isTerminalShortWindow } from "../../src/codex/quota-types"; export interface AccountQuota { workbuddyCredits?: import("../../src/providers/workbuddy-credit-balance").WorkBuddyCreditBalance; @@ -66,7 +66,7 @@ export function normalizeQuotaForPlan(quota: AccountQuota | null, plan: string | * Evaluates governing quota windows based on the account's plan: * - For 30-day only plans (e.g. Free/Go), only the monthly window governs. * - For standard plans, weekly and monthly windows govern. - * - A known five-hour / short window refines a known governing long-window score. + * - A known five-hour / short window refines a known governing long-window score until its valid reset elapses. * - If no long window has been observed, an active terminal short burst (at 100%) acts as exhausted (100). * - Unknown or unprimed quota returns `null` so callers do not spuriously trigger threshold actions. */ @@ -104,6 +104,7 @@ export function computeCodexUsageScore( ? 100 : null; } - const values = finite(shortPercent) ? [...knownLong, shortPercent] : knownLong; + const values = finite(shortPercent) && !hasElapsedShortWindow(quota.fiveHourResetAt ?? quota.shortResetAt, now) + ? [...knownLong, shortPercent] : knownLong; return values.length ? Math.max(...values) : null; } diff --git a/gui/src/components/ClaudeDesktopVisibility.tsx b/gui/src/components/ClaudeDesktopVisibility.tsx new file mode 100644 index 00000000000..a066a11727d --- /dev/null +++ b/gui/src/components/ClaudeDesktopVisibility.tsx @@ -0,0 +1,172 @@ +import { useMemo, useState, type ReactNode } from "react"; +import { useI18n } from "../i18n/shared"; +import { Notice } from "../ui"; +import { LANE_PAGE } from "../pages/claude-desktop-lane"; +import { + applyPreview, + desiredDesktopList, + filterVisibilityModels, + providerOf, + providersOf, + routeVisible, + setNewModelPolicy, + setRoutesVisible, + visibilityCounts, + type AppliedDesktopModel, + type DesktopVisibility, + type ListedDesktopModel, + type VisibilityModel, +} from "../pages/claude-desktop-visibility"; + +/** How many labels the apply preview names before summarizing the rest as a count. */ +const PREVIEW_NAMES = 6; + +function previewText(labels: string[]): string { + const shown = labels.slice(0, PREVIEW_NAMES).join(", "); + return labels.length > PREVIEW_NAMES ? `${shown} +${labels.length - PREVIEW_NAMES}` : shown; +} + +/** + * Per-model Claude Desktop visibility (#7702): which rows the Desktop 3P list carries. + * Separate from the tier lanes under Advanced; the parent owns the draft and saves it with + * the profile, so Save and Save & apply cover both. + */ +export default function ClaudeDesktopVisibility({ + models, + visibility, + onChange, + rendered, + appliedModels, + disabled, + badges, +}: { + /** Rows in display order (the page puts the role holders first). */ + models: readonly T[]; + visibility: DesktopVisibility | null; + onChange: (next: DesktopVisibility | null) => void; + /** The full list the next apply would write before visibility, in written order. */ + rendered: readonly ListedDesktopModel[]; + /** What Desktop runs now; null when there is no static list to compare against. */ + appliedModels: readonly AppliedDesktopModel[] | null; + disabled: boolean; + badges: (model: T) => ReactNode; +}) { + const { t } = useI18n(); + const [query, setQuery] = useState(""); + const [provider, setProvider] = useState(""); + const [limit, setLimit] = useState(LANE_PAGE); + const providers = useMemo(() => providersOf(models), [models]); + const shown = useMemo(() => filterVisibilityModels(models, query, provider), [models, query, provider]); + const counts = visibilityCounts(models, visibility); + const preview = applyPreview(desiredDesktopList(rendered, visibility), appliedModels); + const shownRoutes = shown.map(model => model.route); + const policy = visibility?.mode ?? "exclude"; + + return ( +
+
+ {t("claudeDesktop.visibility.selected", { n: counts.selected })} + {t("claudeDesktop.visibility.available", { n: counts.available })} + + {appliedModels ? t("claudeDesktop.visibility.applied", { n: appliedModels.length }) : t("claudeDesktop.visibility.appliedUnknown")} + + {counts.selectedUnavailable > 0 && ( + {t("claudeDesktop.visibility.selectedUnavailable", { n: counts.selectedUnavailable })} + )} +
+ +
+ {t("claudeDesktop.visibility.newModels")} + {(["exclude", "include"] as const).map(mode => ( + + ))} +
+ +
+ { setQuery(event.target.value); setLimit(LANE_PAGE); }} + /> + + + +
+ + {counts.selected === 0 && counts.available > 0 && {t("claudeDesktop.visibility.noneSelected")}} + + {shown.length === 0 ? ( +

{t("claudeDesktop.visibility.noMatch")}

+ ) : ( +
    + {shown.slice(0, limit).map(model => { + const visible = routeVisible(visibility, model.route); + return ( +
  • + onChange(setRoutesVisible(visibility, [model.route], event.target.checked))} + /> + + {model.label} + {model.label !== model.route && {model.route}} + + {providerOf(model.route)} + {badges(model)} +
  • + ); + })} +
+ )} + {shown.length > limit && ( + + )} + + {preview && ( +
+ {t("claudeDesktop.visibility.previewTitle")} + {preview.added.length === 0 && preview.removed.length === 0 + ? {t("claudeDesktop.visibility.previewSame")} + : ( + <> + {preview.added.length > 0 && {t("claudeDesktop.visibility.previewAdds", { models: previewText(preview.added) })}} + {preview.removed.length > 0 && {t("claudeDesktop.visibility.previewRemoves", { models: previewText(preview.removed) })}} + + )} +
+ )} +

{t("claudeDesktop.visibility.replaceNote")}

+
+ ); +} diff --git a/gui/src/components/KiroDeviceLoginDialog.tsx b/gui/src/components/KiroDeviceLoginDialog.tsx index aa0c4729be1..65ba58b4a7e 100644 --- a/gui/src/components/KiroDeviceLoginDialog.tsx +++ b/gui/src/components/KiroDeviceLoginDialog.tsx @@ -1,10 +1,11 @@ -import { useId, type RefObject } from "react"; +import { useId, useState, type RefObject } from "react"; import { useT } from "../i18n/shared"; import { useModalDialog } from "../pages/dashboard-shared"; import { kiroVerificationLink } from "../kiro-device-login-helpers"; import { useCopyFeedback } from "./use-copy-feedback"; import { useKiroDeviceLogin } from "./use-kiro-device-login"; import KiroLoginChooser from "./KiroLoginChooser"; +import { KiroIamLoginForm, KiroIamProfilePicker } from "./KiroIamLoginForm"; import type { KiroFinalOutcome } from "../kiro-device-login-finalizer"; import "../styles/kiro-device-login.css"; @@ -23,6 +24,7 @@ export default function KiroDeviceLoginDialog({ apiBase, triggerRef, addAccount, const titleId = useId(); const dialogRef = useModalDialog(true, triggerRef); const login = useKiroDeviceLogin(apiBase, onSettled, pollDelay); + const [iamSetup, setIamSetup] = useState(false); const copy = useCopyFeedback(); const { state } = login; const view = state.view; @@ -36,10 +38,19 @@ export default function KiroDeviceLoginDialog({ apiBase, triggerRef, addAccount, {state.phase === "idle" && ( <>

{t(addAccount ? "kiroLogin.addDescription" : "kiroLogin.loginDescription")}

- { onClose(); onCli(addAccount); }} onMethod={method => { void login.start(method); }} /> + {iamSetup + ? setIamSetup(false)} onStart={options => { void login.start("iam-identity-center", options); }} /> + : { onClose(); onCli(addAccount); }} onMethod={method => { + if (method === "iam-identity-center") setIamSetup(true); + else void login.start(method); + }} />} )} {state.phase === "starting" &&

{t("kiroLogin.starting")}

} + {view?.profiles && (state.phase === "select-profile" || state.phase === "selecting") && <> + { void login.selectProfile(arn); }} /> + {state.error === "profile" &&

{t("kiroLogin.profileFailed")}

} + } {view && (state.phase === "pending" || state.phase === "done" || state.phase === "failed" || state.phase === "expired" || state.phase === "ended") && ( <> {view.userCode && ( @@ -59,6 +70,7 @@ export default function KiroDeviceLoginDialog({ apiBase, triggerRef, addAccount, )}

{state.phase === "pending" && t("kiroLogin.pending")} + {state.phase === "selecting" && t("kiroLogin.selecting")} {state.phase === "done" && t("kiroLogin.done")} {state.phase === "expired" && t("kiroLogin.expired")} {state.phase === "ended" && t("kiroLogin.ended")} diff --git a/gui/src/components/KiroIamLoginForm.tsx b/gui/src/components/KiroIamLoginForm.tsx new file mode 100644 index 00000000000..84c1055d605 --- /dev/null +++ b/gui/src/components/KiroIamLoginForm.tsx @@ -0,0 +1,56 @@ +import { useId, useState } from "react"; +import { useT } from "../i18n/shared"; +import { validKiroIamOptions, type KiroIamOptions, type KiroIamProfile } from "../kiro-device-login-helpers"; + +export function KiroIamLoginForm({ disabled, onStart, onBack }: { + disabled: boolean; onStart: (options: KiroIamOptions) => void; onBack: () => void; +}) { + const t = useT(); + const id = useId(); + const [startUrl, setStartUrl] = useState(""); + const [region, setRegion] = useState("us-east-1"); + const [invalid, setInvalid] = useState(false); + return

{ + event.preventDefault(); + const options = { startUrl: startUrl.trim(), region: region.trim() }; + if (!validKiroIamOptions(options)) { setInvalid(true); return; } + setInvalid(false); onStart(options); + }}> +

{t("kiroLogin.iamDescription")}

+ + { setStartUrl(event.target.value); setInvalid(false); }} /> + + { setRegion(event.target.value); setInvalid(false); }} /> + {invalid && } +
+ + +
+
; +} + +export function KiroIamProfilePicker({ profiles, disabled, onSelect }: { + profiles: KiroIamProfile[]; disabled: boolean; onSelect: (arn: string) => void; +}) { + const t = useT(); + const id = useId(); + const [arn, setArn] = useState(""); + return
{ + event.preventDefault(); + if (profiles.some(profile => profile.arn === arn)) onSelect(arn); + }}> +

{t("kiroLogin.selectProfile")}

+ + + {arn && {arn}} +
+
; +} diff --git a/gui/src/components/KiroLoginChooser.tsx b/gui/src/components/KiroLoginChooser.tsx index 586fe110583..cd9cf6bcada 100644 --- a/gui/src/components/KiroLoginChooser.tsx +++ b/gui/src/components/KiroLoginChooser.tsx @@ -10,6 +10,7 @@ export default function KiroLoginChooser({ disabled, onCli, onMethod }: { return (
+ diff --git a/gui/src/components/ProviderModelsNotice.tsx b/gui/src/components/ProviderModelsNotice.tsx index c19844e84cb..562f227443f 100644 --- a/gui/src/components/ProviderModelsNotice.tsx +++ b/gui/src/components/ProviderModelsNotice.tsx @@ -14,6 +14,43 @@ export interface ProviderModelsNoticeProps { onRetry?: () => void; } +const FOCUS_FALLBACK_REGIONS = [".pws-detail-panel", ".pws-detail", ".pws-main", ".page-head"] as const; +const FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; + +function isUsableFocusTarget(element: Element | null): element is HTMLElement { + if (!element || !element.isConnected || typeof (element as HTMLElement).focus !== "function") return false; + if (element === document.body || element === document.documentElement) return false; + if ((element as HTMLButtonElement).disabled === true) return false; + for (let cur: Element | null = element; cur && cur !== document.documentElement; cur = cur.parentElement) { + const htmlCur = cur as HTMLElement; + if (htmlCur.hidden || cur.hasAttribute("inert") || cur.getAttribute("aria-hidden") === "true") return false; + if (htmlCur.style?.display === "none" || htmlCur.style?.visibility === "hidden" || htmlCur.style?.visibility === "collapse") { + return false; + } + if (typeof window !== "undefined" && typeof window.getComputedStyle === "function") { + const cs = window.getComputedStyle(cur); + if (cs.display === "none" || cs.visibility === "hidden" || cs.visibility === "collapse") return false; + } + } + return true; +} + +function restoreOrFallbackFocus(previous: HTMLElement | null): void { + if (isUsableFocusTarget(previous)) { + previous.focus(); + if (document.activeElement === previous) return; + } + for (const regionSelector of FOCUS_FALLBACK_REGIONS) { + for (const region of document.querySelectorAll(regionSelector)) { + for (const candidate of region.querySelectorAll(FOCUSABLE_SELECTOR)) { + if (!isUsableFocusTarget(candidate)) continue; + candidate.focus(); + if (document.activeElement === candidate) return; + } + } + } +} + export default function ProviderModelsNotice(props: ProviderModelsNoticeProps) { const t = useT(); const titleId = useId(); @@ -22,7 +59,7 @@ export default function ProviderModelsNotice(props: ProviderModelsNoticeProps) { useEffect(() => { const previous = document.activeElement as HTMLElement | null; primary.current?.focus(); - return () => { if (previous?.isConnected && typeof previous.focus === "function") previous.focus(); }; + return () => { restoreOrFallbackFocus(previous); }; }, []); const pending = props.selection?.status === "pending"; const unavailable = props.failed || !props.providerKnown; diff --git a/gui/src/components/WorkflowBudgetPanel.tsx b/gui/src/components/WorkflowBudgetPanel.tsx new file mode 100644 index 00000000000..e8d7cb29e25 --- /dev/null +++ b/gui/src/components/WorkflowBudgetPanel.tsx @@ -0,0 +1,178 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { useT } from "../i18n/shared"; +import { createBoundedFetch } from "../bounded-fetch"; +import { clampNumberDraft } from "../clamp-draft"; +import { requireJson } from "../pages/dashboard-shared"; +import { NumberStepper } from "./NumberStepper"; + +const KEYS = ["maxConcurrentChildren", "maxPhysicalSends", "maxDistinctChildren"] as const; +type Key = typeof KEYS[number]; +type Values = Record; +type Drafts = Record; +interface Settings { + values: Values; + defaults: Values; + bounds: Record; + windowMinutes: number; +} + +const LABEL_KEYS = { + maxConcurrentChildren: ["workflowBudget.maxConcurrentChildren", "workflowBudget.maxConcurrentChildrenHint"], + maxPhysicalSends: ["workflowBudget.maxPhysicalSends", "workflowBudget.maxPhysicalSendsHint"], + maxDistinctChildren: ["workflowBudget.maxDistinctChildren", "workflowBudget.maxDistinctChildrenHint"], +} as const; + +const isInt = (value: unknown): value is number => typeof value === "number" && Number.isInteger(value); + +function readSettings(payload: unknown): Settings { + if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("Invalid workflow budget settings"); + const value = payload as Record; + const defaults = value.defaults as Record | undefined; + const bounds = value.bounds as Record | undefined; + if (!defaults || !bounds || !isInt(value.windowMs)) throw new Error("Missing workflow budget settings"); + const settings = { values: {}, defaults: {}, bounds: {}, windowMinutes: Math.round(value.windowMs / 60_000) } as Settings; + for (const key of KEYS) { + const range = bounds[key]; + if (!isInt(value[key]) || !isInt(defaults[key]) || !range || !isInt(range.min) || !isInt(range.max)) { + throw new Error("Missing workflow budget settings"); + } + settings.values[key] = value[key]; + settings.defaults[key] = defaults[key]; + settings.bounds[key] = { min: range.min, max: range.max }; + } + return settings; +} + +const toDrafts = (values: Values): Drafts => + ({ maxConcurrentChildren: String(values.maxConcurrentChildren), maxPhysicalSends: String(values.maxPhysicalSends), maxDistinctChildren: String(values.maxDistinctChildren) }); + +function parseDraft(raw: string, range: { min: number; max: number }): number | undefined { + if (!/^\d+$/.test(raw.trim())) return undefined; + const value = Number(raw.trim()); + return value >= range.min && value <= range.max ? value : undefined; +} + +export default function WorkflowBudgetPanel({ apiBase }: { apiBase: string }) { + return ; +} + +function WorkflowBudgetControls({ apiBase }: { apiBase: string }) { + const t = useT(); + const [saved, setSaved] = useState(); + const [drafts, setDrafts] = useState({ maxConcurrentChildren: "", maxPhysicalSends: "", maxDistinctChildren: "" }); + const [busy, setBusy] = useState(false); + const [loadError, setLoadError] = useState(false); + const [feedback, setFeedback] = useState<"saved" | "failed" | null>(null); + const active = useRef(false); + const pending = useRef | null>(null); + const accept = useCallback((value: Settings) => { setSaved(value); setDrafts(toDrafts(value.values)); }, []); + const load = useCallback(async () => { + if (pending.current) return; + const request = createBoundedFetch(15_000); + pending.current = request; setLoadError(false); + try { + const response = await fetch(apiBase + "/api/workflow-budget/settings", { signal: request.signal }); + const value = readSettings(await requireJson(response)); + if (active.current && pending.current === request) accept(value); + } catch { + if (active.current && pending.current === request) setLoadError(true); + } finally { + request.clear(); + if (pending.current === request) pending.current = null; + } + }, [apiBase, accept]); + useEffect(() => { + active.current = true; + const timer = window.setTimeout(() => { void load(); }, 0); + return () => { + window.clearTimeout(timer); active.current = false; + pending.current?.controller.abort(); pending.current?.clear(); pending.current = null; + }; + }, [load]); + + const parsed = saved + ? Object.fromEntries(KEYS.map(key => [key, parseDraft(drafts[key], saved.bounds[key])])) as Record + : undefined; + const valid = parsed !== undefined && KEYS.every(key => parsed[key] !== undefined); + const changed = saved !== undefined && parsed !== undefined && KEYS.some(key => parsed[key] !== saved.values[key]); + const atDefaults = saved !== undefined && KEYS.every(key => saved.values[key] === saved.defaults[key]); + + const save = async (reset = false) => { + if (pending.current || saved === undefined || (!reset && (!valid || parsed === undefined))) return; + // Values equal to the defaults are written as a reset, so the install keeps following the + // built-in ceilings instead of pinning today's numbers. + const body = reset || KEYS.every(key => parsed?.[key] === saved.defaults[key]) + ? null + : Object.fromEntries(KEYS.map(key => [key, parsed?.[key]])); + const request = createBoundedFetch(15_000); + pending.current = request; setBusy(true); setFeedback(null); + try { + const response = await fetch(apiBase + "/api/workflow-budget/settings", { + method: "PUT", headers: { "Content-Type": "application/json" }, signal: request.signal, + body: JSON.stringify(body), + }); + const value = readSettings(await requireJson(response)); + if (active.current && pending.current === request) { accept(value); setFeedback("saved"); } + } catch { + if (active.current && pending.current === request) setFeedback("failed"); + } finally { + request.clear(); + if (active.current && pending.current === request) setBusy(false); + if (pending.current === request) pending.current = null; + } + }; + const disabled = busy || saved === undefined || loadError; + const edit = (key: Key, value: string) => { setDrafts(current => ({ ...current, [key]: value })); setFeedback(null); }; + + return
+
{t("workflowBudget.title")}
+

{t("workflowBudget.description", { minutes: saved?.windowMinutes ?? 10 })}

+
+ {KEYS.map(key => { + const [labelKey, hintKey] = LABEL_KEYS[key]; + const label = t(labelKey); + const range = saved?.bounds[key] ?? { min: 0, max: 0 }; + const invalid = saved !== undefined && parsed?.[key] === undefined; + const hintId = "workflow-budget-" + key + "-hint"; + return
+
+ {label} + + {t(hintKey)}{" "} + {saved && t("workflowBudget.range", { default: saved.defaults[key], min: range.min, max: range.max })} + + {invalid && {t("workflowBudget.invalid", { min: range.min, max: range.max })}} +
+ + edit(key, event.target.value)} + onKeyDown={event => { + if (event.key === "Enter" && !event.nativeEvent.isComposing && !disabled && changed && valid) { + event.preventDefault(); void save(); + } + }} + /> + edit(key, clampNumberDraft(drafts[key], 1, range.min, range.max))} + onDecrement={() => edit(key, clampNumberDraft(drafts[key], -1, range.min, range.max))} + /> + +
; + })} +
+ + +
+
+ {loadError &&
{t("workflowBudget.loadFailed")}
} + {feedback === "failed" &&
{t("workflowBudget.saveFailed")}
} + {feedback === "saved" &&
{t("workflowBudget.saved")}
} +
; +} diff --git a/gui/src/components/provider-workspace/ProviderBulkCompletionTest.tsx b/gui/src/components/provider-workspace/ProviderBulkCompletionTest.tsx new file mode 100644 index 00000000000..063a3ee4c48 --- /dev/null +++ b/gui/src/components/provider-workspace/ProviderBulkCompletionTest.tsx @@ -0,0 +1,183 @@ +/** + * Runs the bounded completion probe across every model of one provider, one request at a time, + * and offers to hide the models that failed. Nothing is disabled without a second consent. + */ +import { useEffect, useRef, useState } from "react"; +import { confirmAction } from "../../action-dialogs"; +import { readJsonOrThrow } from "../../fetch-json"; +import { useI18n, useT } from "../../i18n/shared"; +import { putModelVisibility } from "../../model-visibility"; +import type { CompletionTestResult, ProbeGate } from "./ProviderCompletionTest"; + +type RowOutcome = "ok" | "failed" | "skipped"; +type Row = { model: string; outcome: RowOutcome; latencyMs?: number; detail?: string }; +type DisableState = { kind: "idle" } | { kind: "busy" } | { kind: "done"; count: number } | { kind: "error"; message: string }; + +type Props = { apiBase: string; provider: string; models: string[]; gate: ProbeGate; blocked: boolean }; + +/** A failure the upstream actually answered for this model; transport aborts and HTTP refusals are not evidence. */ +function failedModelIds(rows: readonly Row[]): string[] { + return rows.filter(row => row.outcome === "failed").map(row => row.model); +} + +export default function ProviderBulkCompletionTest({ apiBase, provider, models, gate, blocked }: Props) { + const t = useT(); + const { locale } = useI18n(); + const [rows, setRows] = useState([]); + const [running, setRunning] = useState(false); + const [stopped, setStopped] = useState(false); + const [notice, setNotice] = useState(null); + const [disable, setDisable] = useState({ kind: "idle" }); + const flight = useRef(null); + const disableFlight = useRef(null); + useEffect(() => () => { + if (flight.current) { flight.current.abort(); flight.current = null; gate.release(); } + disableFlight.current?.abort(); disableFlight.current = null; + }, [gate]); + + async function testOne(model: string, signal: AbortSignal): Promise { + try { + const response = await fetch(`${apiBase}/api/providers/test-completion?name=${encodeURIComponent(provider)}`, { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ model, confirm: true }), signal, + }); + const value = await readJsonOrThrow(response, t("pws.completion.failed")); + if (!value) return { model, outcome: "skipped", detail: t("pws.completion.failed") }; + if (!value.applicable) { + const detail = `${t("pws.completion.notApplicable")}: ${t(`pws.completion.${value.reason}`)}`; + // Effective wire is resolved per model; a mixed-wire provider can still test later rows. + return value.reason === "unsupported_adapter" ? { model, outcome: "skipped", detail } : { stop: detail }; + } + if (value.ok) return { model, outcome: "ok", latencyMs: value.totalLatencyMs }; + // Admission refusals happen before upstream send and cannot establish a model failure. + if (value.termination === "aborted" || ["credential_missing", "credential_changed", "destination_refused"].includes(value.error?.code ?? "")) { + return { model, outcome: "skipped", detail: value.error?.code ?? "aborted" }; + } + const detail = value.error + ? `${value.error.code}${value.error.status !== undefined ? ` (${value.error.status})` : ""}` + : value.termination; + if (value.error?.code === "http_status" && [401, 403, 429].includes(value.error.status ?? 0)) { + return { model, outcome: "skipped", detail }; + } + return { model, outcome: "failed", latencyMs: value.totalLatencyMs, detail }; + } catch (cause) { + return { model, outcome: "skipped", detail: cause instanceof Error ? cause.message : t("pws.completion.failed") }; + } + } + + async function run() { + if (models.length === 0 || flight.current || disableFlight.current || !gate.claim()) return; + const controller = new AbortController(); + flight.current = controller; + setRunning(true); + setStopped(false); + setRows([]); + setNotice(null); + setDisable({ kind: "idle" }); + try { + if (!await confirmAction({ + message: t("pws.completion.allConfirm", { count: String(models.length), provider }), + confirmLabel: t("pws.completion.runAll"), signal: controller.signal, locale, + }) || controller.signal.aborted) return; + // Sequential on purpose: the server admits one completion test per provider at a time. + for (const model of models) { + if (controller.signal.aborted) break; + const row = await testOne(model, controller.signal); + if (controller.signal.aborted) break; + if ("stop" in row) { setNotice(row.stop); break; } + setRows(previous => [...previous, row]); + } + } finally { + if (flight.current === controller) { + flight.current = null; + gate.release(); + setRunning(false); + } + } + } + + function stop() { + flight.current?.abort(); + flight.current = null; + gate.release(); + setStopped(true); + setRunning(false); + } + + async function disableFailed() { + const failed = failedModelIds(rows); + if (failed.length === 0 || flight.current || disableFlight.current) return; + const controller = new AbortController(); + disableFlight.current = controller; + setDisable({ kind: "busy" }); + let confirmed = false; + try { + confirmed = await confirmAction({ + message: t("pws.completion.disableConfirm", { count: String(failed.length), provider }), + confirmLabel: t("pws.completion.disableFailed", { count: String(failed.length) }), + tone: "danger", locale, signal: controller.signal, + }); + if (!confirmed || controller.signal.aborted || disableFlight.current !== controller) return; + const response = await putModelVisibility(apiBase, "models", provider, failed.map(id => ({ id })), false, fetch, controller.signal); + await readJsonOrThrow(response, t("pws.completion.disableError")); + if (!controller.signal.aborted && disableFlight.current === controller) setDisable({ kind: "done", count: failed.length }); + } catch (cause) { + if (!controller.signal.aborted && disableFlight.current === controller) { + setDisable({ kind: "error", message: cause instanceof Error ? cause.message : t("pws.completion.disableError") }); + } + } finally { + if (disableFlight.current === controller) { + disableFlight.current = null; + if (!confirmed) setDisable({ kind: "idle" }); + } + } + } + + const counts = { ok: 0, failed: 0, skipped: 0 }; + for (const row of rows) counts[row.outcome]++; + const failedCount = counts.failed; + const skipped = counts.skipped + (running ? 0 : models.length - rows.length); + + return ( +
+
+ + {running && } +
+ {notice &&

{notice}

} + {rows.length > 0 && ( +
+ {!running &&

+ {stopped && <>{t("pws.completion.stopped", { done: String(rows.length), total: String(models.length) })} · } + {t("pws.completion.summary", { passed: String(counts.ok), failed: String(failedCount), skipped: String(skipped) })} +

} + + + {rows.map(row => ( + + + + + + + ))} + +
{row.model} + + {t(row.outcome === "ok" ? "pws.completion.passed" : row.outcome === "failed" ? "pws.completion.failedShort" : "pws.completion.notTested")} + + {row.latencyMs !== undefined ? `${row.latencyMs} ms` : ""}{row.detail ?? ""}
+ {!running && failedCount > 0 && disable.kind !== "done" && ( + + )} + {disable.kind === "done" &&

{t("pws.completion.disabledDone", { count: String(disable.count) })}

} + {disable.kind === "error" &&

{disable.message}

} +
+ )} +
+ ); +} diff --git a/gui/src/components/provider-workspace/ProviderCompletionTest.tsx b/gui/src/components/provider-workspace/ProviderCompletionTest.tsx index 63a19421bdb..4e71ec6c21c 100644 --- a/gui/src/components/provider-workspace/ProviderCompletionTest.tsx +++ b/gui/src/components/provider-workspace/ProviderCompletionTest.tsx @@ -4,6 +4,7 @@ import { confirmAction } from "../../action-dialogs"; import { readJsonOrThrow } from "../../fetch-json"; import { useI18n, useT } from "../../i18n/shared"; import { Select } from "../../ui"; +import ProviderBulkCompletionTest from "./ProviderBulkCompletionTest"; import "./provider-completion-test.css"; type InapplicableReason = "disabled" | "forward_auth" | "oauth_auth" | "unsupported_adapter" | "unsupported_transport"; @@ -18,43 +19,144 @@ export type CompletionTestResult = termination: "completed" | "failed" | "timeout" | "aborted" | "too_large"; error: { code: string; status?: number; message: string } | null }; -type Props = { apiBase: string; provider: string; models: string[] }; +export type CompletionProbeStatus = "untested" | "testing" | "ok" | "failed" | "not-applicable"; +export type CompletionFailureRecovery = "aborted" | "concurrency" | "auth" | "rate_limit" | "connection" | "model" | "upstream"; -export default function ProviderCompletionTest({ apiBase, provider, models }: Props) { +/** + * The server admits one completion test per provider at a time and answers an overlap with 409. + * Both probes share this gate so neither control can start while the other is confirming or running. + * Each probe gets a stable gate; whether the other probe holds it arrives as the `blocked` prop. + */ +export type ProbeGate = { + claim(): boolean; + release(): void; +}; +type ProbeOwner = "single" | "bulk"; + +function createProbeGates(onChange: (owner: ProbeOwner | null) => void): Record { + let holder: ProbeOwner | null = null; + const gateFor = (owner: ProbeOwner): ProbeGate => ({ + claim() { + if (holder !== null && holder !== owner) return false; + holder = owner; + onChange(owner); + return true; + }, + release() { + if (holder !== owner) return; + holder = null; + onChange(null); + }, + }); + return { single: gateFor("single"), bulk: gateFor("bulk") }; +} + +type Props = { + apiBase: string; + provider: string; + models: string[]; + probeKey?: string; + selectedModel?: string; + onSelectedModelChange?: (model: string) => void; + initialResult?: CompletionTestResult | null; + initialError?: string | null; + onStatusChange?: (state: { + status: CompletionProbeStatus; + model: string; + result?: CompletionTestResult | null; + error?: string | null; + }) => void; + onFixCredentials?: () => void; + onFixSettings?: () => void; + onFixModels?: () => void; +}; + +export default function ProviderCompletionTest({ + apiBase, provider, models, probeKey, selectedModel: controlledModel, onSelectedModelChange, + initialResult = null, initialError = null, onStatusChange, + onFixCredentials, onFixSettings, onFixModels, +}: Props) { const t = useT(); - const [model, setModel] = useState(""); - const selectedModel = models.includes(model) ? model : ""; + const [internalModel, setInternalModel] = useState(""); + // Permanently clear a selected model that was removed from the list so re-adding it cannot + // resurrect an older selection. + if (controlledModel === undefined && internalModel !== "" && !models.includes(internalModel)) { + setInternalModel(""); + } + const rawModel = controlledModel !== undefined ? controlledModel : internalModel; + const selectedModel = models.includes(rawModel) ? rawModel : ""; + const [active, setActive] = useState(null); + // Created once per mount; the holder lives in the closure, so render never reads it. + const [gates] = useState(() => createProbeGates(setActive)); return (

{t("pws.completion.title")}