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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ row records which one was verified and how.
| P1/P2 | Healthy-account spreading | Quota-weighted random race (`kiro/account_manager.py:1162-1208,1288`) | Keeps a healthy active account; deterministic ordering (`src/oauth/account-quota-rank.ts:158-172`, `src/oauth/generic-account-failover.ts:443-484`) | ahead-lb on distribution | **Adopt, better** → 040: deterministic least-loaded choice among quota-healthy accounts, opt-in |
| P3 | Per-account concurrency | Optional semaphore with bounded wait (`kiro/concurrency.py:2,95`, `kiro/http_client.py:554`) | Absent | ahead-lb | **Adopt** → 040 |
| P10 | Affinity | Global last-success cursor (`kiro/account_manager.py:1444`) | None, documented | parity | Reject: a global cursor is not conversation affinity |
| P8/J | Per-request credits | Records upstream credit frames per serving account (`kiro/usage_tracking.py:55,80`, `main.py:594`, `432c9b3`) | Kiro token usage is estimated; no credit field (`src/usage/log.ts:532`) | ahead-lb | **Adopt** → 070 |
| P8/J | Per-request credits | Records upstream credit frames per serving account (`kiro/usage_tracking.py:55,80`, `main.py:594`, `432c9b3`) | At research time: no credit field. Since landed on dev outside this unit as `OcxUsage.providerCredits` from Kiro `meteringEvent` frames (`src/adapters/kiro-events.ts`, `src/adapters/kiro/stream.ts`, `src/usage/log.ts`), summed per physical send | parity | Landed upstream of 070; 070 adds quota gauges and auto-selection only |
| U1 | Multiplier estimates | Coarse per-model estimates (`kiro/model_costs.py:8-22,108-132`) | None | ahead-lb (advisory) | Reject: 070 records measured credits; an estimate beside them would be a second, weaker number |

## Model capability and wire
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,57 @@ At implementation C, run the four new test files above, the four existing focuse
## Round-2 audit fold

- **r3-R2-2 Medium** → Rebased the 070 eligibility hunk on 030's `eligibleIdsIn` filter and existing `cooldownSource: "kiro-suspension"` writes; `projectKiroAccountAutoSelection` maps active suspension to `skipReason: "suspended"` and ordinary rate/default cooldown to `"cooldown"` (`070:263-294,358`). The named candidate/list agreement test covers quarantine, refusal rotation, ordinary cooldown and expiry (`070:387`). Rebase-verify at this layer's P against the implemented 030 head.

## wp8 P re-verification (2026-09-27, branch `codex/kiro-lb2-070-credits-ops` on dev `a91568ec5a`, which contains 010–060)

Executable plan for the 070 build; **overrides** earlier sections where they conflict.

| ID | Disposition |
|---|---|
| **P8/J metering (decision)** | **Already on dev**, outside this stack, as `OcxUsage.providerCredits` (`src/types/request.ts:451`): `meteringEvent` is a known Kiro event parsed from real kiro-cli captures (`src/adapters/kiro-events.ts:24-27,185-200`), the stream keeps the last credit value per attempt (`src/adapters/kiro/stream.ts:609-610`), and `usage/log.ts` persists it (582, 626), tested by `kiro-metering-usage.test.ts` and `kiro-metering-events.test.ts`. 070 therefore drops its own parser, `meteredCredits`, `kiro-credits.ts` and `kiro-metered-credits.test.ts`. The landed code **sums** credits across physical sends (completion fallback, continuations, request-log aggregate); that is kept, because each physical send is billed separately and summing is the correct request spend. The earlier "last value wins, never sum" rule is withdrawn. Docs describe the final request row as request spend and attempt rows (sealed per serving account) as per-account spend. |
| D070-S3 | The metrics projector reads Kiro quota only through `kiroAccountEvidence(account)` (identity-fenced); the `quota.identity` clause is removed. |
| D070-S4 | `parseKiroUsage` adds `kiroCreditsUsed`/`kiroCreditsLimit` to the quota (`src/providers/kiro-usage.ts:131-133,153-158`); `sanitizeKiroQuota` (`src/providers/kiro-account-state-disk.ts:19-30`) keeps both when finite and non-negative, so gauges survive a restart. |
| D070-S5–S7, S9 | Anchors: `eligibleIdsIn` 239-251 (Kiro branch 248-249), `AccountHealth.identity` 84-88, identity-fenced `isCooled` 112-127; `projectAccounts` `oauth-account-routes.ts:392-408` (row literal 405); CLI `account-api.ts:16-34`, `account.ts:100-113` (reuse `not-auto-selected(<reason>)` wording from `selectionExcludedReason` 109-111); metrics owner `request-metrics.ts` (`createRequestMetricsOwner` 191, snapshot 239), `serve-options.ts:295`, `metrics-routes.ts:3-19`. |
| D070-S8 | SD4'-era text and the run-turn reference are removed; the L projection follows SD4'' and the landed 030/040. |
| **autoSelectable (L)** | `kiroAutoSelection(account, now, family?)` returns `{ autoSelectable, skipReason? }` mirroring exactly what `eligibleIdsIn` excludes: `needs_reauth`, `suspended` (`cooldownSource === "kiro-suspension"`, read after `isCooled` prunes and checks identity), `cooled` (any other cooldown), `exhausted` (`kiroAccountEvidence(account).exhausted === true`). It checks both the family key and the family-less key, as `eligibleIdsIn` does. The 040 cap, least-loaded order, 050 membership and 060 `loginOrigin` are preferences or provenance, never skip reasons. A test asserts the projection equals `eligibleIdsIn` membership over a table of states. Recorded limitation: the existing `health` field (`health.ts:180`) does not reflect Kiro suspension/exhaustion, so a row can read `health: ok` next to `autoSelectable: false`; the GUI does not read the new fields, so no GUI change. |
| Metrics (K) | `src/providers/kiro-quota-metrics.ts` projects cached rows only (no scrape-time upstream call): quota percent, credits used, credit limit, seconds to reset; opaque `oauthAccountLogLabel` labels (`o` + 6 hex), at most 32 accounts, rows with `updatedAt > now` dropped. Wired from `serve-options.ts` into `createRequestMetricsOwner` with a type-only import in `request-metrics.ts`. `tests/server/management-metrics-export.test.ts:549-551` (source-oracle composition string) is updated. |
| Residuals | 070:~390 test names become the landed ones (030: `kiro-refusal`, `kiro-refusal-failover`, `server-kiro-refusal-e2e`; 040: `kiro-account-load`, `kiro-leased-responses`, `kiro-pool-load-settings`); the "030/040 must also add" paragraph is deleted. |
| Registry | `kiro-auto-selection.test.ts` (providers/kiro) between `kiro-auth-context-continuation` and `kiro-builder-id-profile` (layout 1079/1080; expected 900/901); `kiro-quota-metrics.test.ts` between `kiro-pool-rank` and `kiro-reasoning-roundtrip` (1091/1092; 912/913); `tests/cli/cli-kiro-auto-selection.test.ts` after `cli-json-contract` (528; 354). |

Verifier set for C: `bun run typecheck`; `bun test tests/providers/kiro/ tests/server/management-metrics-export.test.ts tests/cli/cli-kiro-auto-selection.test.ts tests/oauth/generic-oauth-failover.test.ts` plus `bun test $(rg -l "metrics-routes|request-metrics|projectAccounts|oauth/accounts" tests)` in the clean `/tmp` worktree; layout, ratchet, lab-boundary; privacy; structure.


### wp8 reflection fold (MISALIGNED → folded)

1. **Gauge source:** `kiroAccountEvidence(account, now?)` is extended to also return the identity-fenced
`creditsUsed`/`creditsLimit` from the same quota row (same TTL/reset bound); the metrics projector
reads only that function.
2. **One closed skip-reason set:** `KiroSkipReason = "needs_reauth" | "suspended" | "cooldown" |
"quota_exhausted"`. Every place uses exactly these: the type, `isKiroSkipReason`, the management
DTO, the CLI `AccountRow` and `not-auto-selected(<reason>)` output, docs and test names. The names
`cooled` and `exhausted` in "wp8 P re-verification" are replaced by `cooldown` and
`quota_exhausted`.
3. **One source of truth:** `eligibleIdsIn` calls `kiroAutoSelection` for Kiro accounts (so routing and
the projection cannot drift), and the parity table test stays as a guard. `structure/providers/kiro.md`
and `001_research_gap_inventory.md` are checked for any "never summed" claim; the inventory's P8/J
row is updated to say metering landed outside this stack as `providerCredits` and sums per physical
send.


### wp8 A round 1 fold (reviewer 01a0df8b: GO-WITH-FIXES, 3 Medium → folded)

1. `parseKiroUsage` rejects `used < 0` (and non-finite `used`/`limit`); test: `a negative used reading yields no quota`.
2. The metrics projector iterates the live Kiro roster in stable order and stops after 32 **valid** rows
with distinct labels (stale or unknown rows do not consume the budget); test:
`stale leading accounts do not hide later fresh gauges`.
3. `kiroAutoSelection` has no family parameter: Kiro health keys are family-less (the classifier returns a
family only for `google-antigravity`, `src/oauth/account-quota-rank.ts:24`; Kiro refusal writes use
the family-less key, `generic-account-failover.ts:445`). The family branch is removed; tests cover the
reachable family-less states.

## wp8 build notes

- Added identity-fenced precise Kiro plan credits, cache-only bounded quota gauges, and a single `kiroAutoSelection` projection shared by candidate routing and account-list status. CLI text and JSON expose the same closed reason set. Existing `providerCredits` metering remains the request-spend source; no second parser or token-derived credit estimate was added.
- Updated public English and directly affected translations, plus owning structure contracts. No GUI or scrape-time network path changed. The three `layout.json` entries share lines with their preceding alphabetical entries to stay below the 2,000-line file-size ratchet.
- Verification: `bun run typecheck` passed; focused Kiro/metrics/CLI/failover/refusal suite passed (696 tests); layout, file-size, and Lab guards passed (52 tests); `bun run privacy:scan` and `bun run structure:check` passed; docs-site frozen install and build passed. A later focused test addition for disk sanitization is rerun in the final gate below. No full suite or live Kiro call was run.
- Final focused rerun after the disk-sanitizer test: 697 pass, 0 fail across 39 files. The translated management rows were then corrected to place the new Kiro facts in the description column; the docs-site build completed (529 pages, 72,011 links checked). `git diff --check` reported no whitespace errors.
1 change: 1 addition & 0 deletions docs-site/src/content/docs/fr/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Les comptes inactifs n’ont pas encore nécessairement de liste observée.

- Construit le `conversationState` de Kiro, mappe les outils Codex et leurs résultats, puis envoie les blocs d’image pris en charge par le protocole Kiro.
- Décode `application/vnd.amazon.eventstream`, reconstruit les événements de texte, de raisonnement et d’outil, détecte les données JSON d’outil tronquées et estime l’utilisation, car le service en amont ne renvoie aucun nombre de jetons.
Les jetons restent estimés, mais les crédits de `meteringEvent` sont mesurés dans `providerCredits` : la dernière valeur d'une réponse est retenue, puis les envois physiques facturés séparément sont additionnés. Aucun crédit n'est déduit des jetons.
- Utilise à l’identique le `baseUrl` configuré lorsqu’il est personnalisé. Une URL canonique `runtime.{region}.kiro.dev` suit la région d’API de l’identifiant importé ; seule cette forme canonique peut faire l’objet d’un unique repli borné vers `q.{region}.amazonaws.com` après un échec de point de terminaison, de signature, de DNS ou de connexion, ou une réponse HTTP 502/503/504 reçue avant toute sortie.
- Gère la récupération après réinitialisation de connexion lorsqu’un rejeu est sûr, cet unique repli de point de terminaison admissible, une actualisation OAuth suivie d’un rejeu après une réponse HTTP 401, ainsi qu’une récupération bornée pour les réponses Kiro 429 transitoires. Un délai de récupération partagé et une seule sonde après ce délai empêchent les requêtes concurrentes d’épuiser des budgets de nouvelle tentative indépendants ; un quota épuisé n’est pas réessayé sur le même compte, et les autres erreurs de service ne sont pas rejouées. Tous les envois Kiro utilisent la sortie réseau configurée ; un délai d’en-tête dépassé renvoie 504, l’annulation du client arrête la requête et les erreurs HTTP 5xx finales affichent un texte public fixe.
- Avec deux comptes enregistrés, un refus de débit refroidit brièvement le compte concerné. Un refus mensuel confirmé (HTTP 400 ou 429) l’exclut jusqu’à la réinitialisation observée ou l’expiration des données ; une suspension confirmée (HTTP 403) le met temporairement en quarantaine, mais un 403 ordinaire ne déclenche pas de rotation. La rotation après refus reste active lorsque la préférence proactive est désactivée. Le choix d’un autre compte avant le premier envoi exige une préférence proactive, où le réglage du fournisseur prime sur le réglage global. Un tour terminé par le même compte efface un ancien verdict d’épuisement.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ en privilégiant celui dont l'allocation restante connue est la plus élevée ;
Pour Kiro, les refus de débit, de quota mensuel confirmé et de suspension confirmée peuvent changer de compte avant toute sortie. Le quota mensuel exclut seulement ce compte jusqu’à la réinitialisation ou l’expiration des données ; une réponse terminée du même compte efface un ancien verdict. Le réglage du fournisseur prime sur le réglage global pour la préférence proactive, sans désactiver la rotation réactive.
Kiro peut choisir `least-loaded` pour placer les requêtes de façon proactive lorsque `pool.kernel` et la préférence proactive sont activés. `maxConcurrentPerAccount` (1–100) crée une file bornée par compte et par processus : un compte sélectionné saturé attend au plus 250 ms, puis renvoie 503 `account_capacity` avec `Retry-After: 1`. Cette limite ne déplace pas la requête vers un autre compte.

`ocx account list kiro` affiche `not-auto-selected(<raison>)` pour un compte exclu de la sélection automatique. Le JSON contient `autoSelectable` et, si la valeur est fausse, un `skipReason` fermé (`needs_reauth`, `suspended`, `cooldown` ou `quota_exhausted`). Un compte actif unique peut encore servir. Les crédits `providerCredits` sont mesurés par `meteringEvent` : la dernière valeur d'une réponse est retenue et les envois facturés séparément sont additionnés, sans estimation à partir des jetons.

`--json` renvoie :

```text
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/fr/reference/management-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ par le fournisseur en amont, cette information reste absente : elle n'est pas d
| `GET /api/debug/injection-logs` | Lire un nombre limité d'entrées de débogage de l'injection du guidage | — |
| `GET /api/claude/inbound-debug` | Lire l'état et les entrées du débogage entrant | — |
| `GET /api/usage` | Résumer l'utilisation par période et par interface cliente ; les réponses Codex comprennent aussi une ventilation `accounts` indexée par des libellés de journalisation stables ne contenant aucune donnée personnelle | Renvoie 500 `{ "error": "read_failed" }` si le stockage ne peut pas être lu |
| `GET /api/metrics` | Renvoyer les métriques texte Prometheus locales au processus : requêtes logiques, envois physiques, types de récupération, durée et TTFT. Les libellés sont limités au protocole, au résultat et à la classe de récupération ; aucun identifiant de requête ou d'identifiant secret n'est exporté. | 404 si `metricsExport.enabled` n'était pas vrai au démarrage ; l'authentification de gestion est obligatoire et les identifiants du plan de données ne donnent aucun accès |
| `GET /api/metrics` | Renvoyer les métriques texte Prometheus locales au processus : requêtes logiques, envois physiques, types de récupération, durée et TTFT. Les métriques de requêtes utilisent des libellés fermés ; les jauges Kiro ajoutent uniquement un libellé de compte opaque et borné ; aucun identifiant de requête ou d'identifiant secret n'est exporté. Les quatre jauges `opencodex_kiro_quota_{used_credits,limit_credits,used_percent,seconds_to_reset}` lisent uniquement le cache et utilisent au plus 32 étiquettes de compte opaques. Aucune sonde réseau lors de la collecte. | 404 si `metricsExport.enabled` n'était pas vrai au démarrage ; l'authentification de gestion est obligatoire et les identifiants du plan de données ne donnent aucun accès |
| `GET /api/storage` | Analyser l'utilisation du stockage Codex par catégorie | Renvoie une charge utile `error: "scan_failed"` en cas d'échec de l'analyse |
| `POST /api/storage/cleanup/preview` | Prévisualiser le nettoyage des sessions archivées et renvoyer une empreinte contraignante | 400 `invalid_json` ou `invalid_percent` |
| `POST /api/storage/cleanup` | Mettre en quarantaine ou supprimer définitivement l'ensemble archivé prévisualisé | 400 saisie invalide ; 409 état obsolète, occupé ou référencé ; 500 échec du système de fichiers ou de la base de données |
Expand Down Expand Up @@ -278,7 +278,7 @@ Tant qu’une liste initiale fiable n’est pas disponible, les requêtes PUT va
| `POST /api/oauth/login/cancel` | Annuler un flux OAuth public en cours | 400 fournisseur inconnu |
| `GET /api/oauth/status` | Sonder le flux OAuth d'un fournisseur | 400 fournisseur inconnu |
| `POST /api/oauth/logout` | Supprimer les informations d'identification du fournisseur sélectionné | 400 fournisseur inconnu ; `oauth_mutation_busy` |
| `GET, DELETE /api/oauth/accounts` | Répertorier les comptes masqués ou supprimer un compte | 400 invalide provider/id ; 404 compte manquant ; `oauth_mutation_busy` |
| `GET, DELETE /api/oauth/accounts` | Répertorier les comptes masqués ou supprimer un compte Les lignes Kiro ajoutent `autoSelectable` et un `skipReason` fermé en cas d’exclusion de la sélection automatique ; un compte actif unique peut encore servir. Le quota reste facultatif. | 400 invalide provider/id ; 404 compte manquant ; `oauth_mutation_busy` |
| `PUT /api/oauth/accounts/active` | Sélectionnez le compte OAuth actif | 400 invalide provider/account ; `oauth_mutation_busy` |
| `GET, PUT, PATCH /api/oauth/accounts/pool` | Lire ou mettre à jour la stratégie du pool OAuth Anthropic | 400 fournisseur non Anthropic ou stratégie invalide |
| `POST /api/oauth/accounts/clear-cooldown` | Effacer le temps de recharge d'un compte OAuth | 400 invalide provider/account |
Expand Down
Loading
Loading