Skip to content

feat(coding-agent): auth gateway forward proxy#195

Open
islee23520 wants to merge 53 commits into
code-yeongyu:mainfrom
islee23520:feat/auth-gateway
Open

feat(coding-agent): auth gateway forward proxy#195
islee23520 wants to merge 53 commits into
code-yeongyu:mainfrom
islee23520:feat/auth-gateway

Conversation

@islee23520

@islee23520 islee23520 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward proxy that injects credentials leased from the auth-broker into upstream provider requests, exposing one authenticated endpoint for Anthropic Messages, OpenAI Chat, and Responses/pi protocols:

  • auth-gateway-transport(-auth/-request/-types): loopback transport, bearer auth, authorized-model restriction.
  • auth-gateway-protocol-adapter + anthropic-messages / openai-chat / responses-pi(-adapter/-events): per-protocol adaptation.
  • auth-gateway-provider-runtime: selects broker credentials per call, reports lease outcomes.
  • auth-gateway-observability: redacted health/check diagnostics.
  • auth-gateway-cli: senpi auth-gateway CLI (serve, token, status, check); main registers it.
  • docs/auth-broker-gateway.md + scripts/verify-auth-broker-gateway.mjs: operator docs and verifier.

Stack

Stacked on the credential-broker PR (uses AuthBrokerRemoteStore). This branch includes the provider-parity and credential-broker commits; merge after them, then rebase to drop them.

Evidence

  • npm run check green.
  • gateway + broker-gateway verifier tests: 25 passing.

Summary by cubic

Adds an auth gateway forward proxy that injects broker‑leased credentials into upstream requests and exposes one authenticated endpoint for Anthropic Messages, OpenAI Chat, and OpenAI Responses/pi. Rebased onto main with full provider/OAuth parity for /login, including cursor-connect and google-gemini-cli transports, expanded provider catalogs, and synced devcontainer/env key wiring.

  • Bug Fixes
    • Normalize model IDs before adapter dispatch; forward selector headers and generation options; preserve tool result names.
    • Map non‑stream error/aborted results to 502/499; emit per‑check verifier receipts; enforce 32‑char gateway bearers; clean stream listeners.
    • /login parity: add provider display names; mark openai-codex-device OAuth‑only; alias device Codex creds to openai-codex; make AuthStorage.list async; restore OAuth‑only exclusions; unify Kimi to a single kimi-coding entry using KIMI_API_KEY.
    • Runtime/dev: parse CRLF‑delimited SSE; drop duplicate Google provider registration; restore xAI device‑code OAuth + registry adapter; add devcontainer/env secrets for new providers; use local fallback keys for lm-studio and ollama.

Written for commit c6813ee. Summary will update on new commits.

Review in cubic

Add 18 API-key providers (alibaba-coding-plan, cursor, deepinfra, firepass,
fugu, gitlab-duo, glm-zcode, kagi, kilo, kimi-code, litellm, lm-studio,
minimax-code(-cn), moonshot, nanogpt, ollama(-cloud), openai-codex-device,
parallel, perplexity, qianfan, qwen-portal, synthetic, tavily, venice, vllm,
zenmux) plus OAuth flows for cursor, gitlab-duo, glm-zcode, google-antigravity,
google-gemini-cli, kilo, kimi-code, openai-codex-device, perplexity, xai, and
the remaining Gajae provider-ID gaps for full /login parity.

The coding-agent provider wiring (model-resolver defaults, provider-display-names,
auth-providers oauth-only set) travels here because model-resolver is exhaustive
over the ai KnownProvider union. Source of truth for keys remains env-api-keys.ts;
parity is enforced by test/api-key-provider-parity.test.ts.
…ackground refresh

Add a loopback credential broker that stores OAuth/API-key credentials in a
SQLite vault and leases them to authorized gateway clients:

- auth-multi-account / credential-selection / in-memory-credential-vault:
  multi-account credential contracts, pooled selection, and an in-memory vault.
- auth-broker + wire-contract: broker service with CAS selection leases,
  redacting wire contract, and a remote store for clients.
- auth-broker-cli + auth-broker-server: `senpi auth-broker` CLI (serve, token,
  login, logout, import, backup, restore, migrate) over a loopback HTTP server.
- auth-broker-refresher: background OAuth refresh loop (5 min skew / 60 s
  cadence) that renews expiring tokens and disables definitive failures.
- model-registry / sdk: wire pooled credential selection and outcome reporting
  into the agent loop; index exports the credential surface; main registers the
  `auth-broker` command.

Stacked on the provider-parity branch (depends on its KnownProvider union).
Add a forward proxy that injects credentials leased from the auth-broker into
upstream provider requests, exposing a single authenticated endpoint for
Anthropic Messages, OpenAI Chat, and Responses/pi protocols:

- auth-gateway-transport(-auth/-request/-types): loopback transport with bearer
  auth, request handling, and authorized-model restriction.
- auth-gateway-protocol-adapter + anthropic-messages / openai-chat /
  responses-pi(-adapter/-events): per-protocol request/response adaptation.
- auth-gateway-provider-runtime: selects broker credentials per call and reports
  lease outcomes.
- auth-gateway-observability: redacted health/check diagnostics.
- auth-gateway-cli: `senpi auth-gateway` CLI (serve, token, status, check);
  main registers the command.
- docs/auth-broker-gateway.md + scripts/verify-auth-broker-gateway.mjs:
  operator docs and a verifier.

Stacked on the credential-broker branch (uses its AuthBrokerRemoteStore).
Declare the API-key providers added for /login parity (alibaba-coding-plan,
deepinfra, firepass, fugu, kagi, litellm, lm-studio, nanogpt, ollama,
ollama-cloud, parallel, qianfan, qwen-portal, synthetic, tavily, venice,
vllm, zenmux) across the three synchronized environment surfaces:
.devcontainer/devcontainer.json secrets, scripts/devenv-setup.mjs PROVIDER_KEYS,
and .agents/skills/senpi-qa/references/env-vars.md. Source of truth remains
packages/ai/src/env-api-keys.ts.
…on-stream)

OpenAI Chat and Anthropic Messages non-stream handlers awaited
result.stream.result() and always returned 200 even when the message
stopReason was error/aborted, masking provider failures as successful
completions. Detect the error/aborted stop reason and return
safeError(502) instead.
…ause, migration writes

- Harden auth-broker-refresher with improved credential refresh logic
- Add restore lease support and remote-store session handling
- Add refresher-start and remote-store-session test coverage
- Update auth-broker-wire-contract with additional contract checks
Resolve changes.md conflict by keeping both the credential broker
section and the upstream compaction/extension sections.
…I output modules

- Split auth-gateway-cli.ts into broker-client, output, parse, and token modules
- Add auth-gateway-request-router.ts for request routing logic
- Add auth-gateway-transport-response.ts for response transport types
- Add command-routes and responses-state test coverage
- Update observability and provider-runtime error handling
Resolve changes.md conflict by keeping both the auth gateway section
and the upstream compaction/extension sections.
- Remove kagi, parallel, and tavily providers (search-only, no chat)
- Fix GLM ZCode OAuth to pin endpoints to z.ai
- Add CRLF-delimited SSE parsing for provider streams
- Add GitLab Duo and Perplexity OAuth test coverage
- Update model-registry and model-resolver for removed providers
Resolve changes.md conflicts in packages/ai and packages/coding-agent
by keeping both the provider parity sections and the upstream sections.
Resolve provider-union conflicts with main Radius support while keeping
parity providers. Drop residual kagi/tavily/parallel env-surface wiring.
Cascade provider-parity + Radius onto the broker branch. Keep getRequestAuth
and pooled credential selection. Drop residual kagi/tavily/parallel providers.
Cascade broker + provider-parity onto the gateway branch. Preserve gateway
docs/commands and drop residual kagi/tavily/parallel providers.
@islee23520

islee23520 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@code-yeongyu This PR's CI is blocked before any jobs start: the fork workflow run concluded action_required, so the required Check and test context remains “Expected — Waiting for status to be reported.”

Could you open the run below and choose Approve and run workflows?

https://github.com/code-yeongyu/senpi/actions/runs/29494708619

No branch-protection or workflow change is requested; this only approves the existing run for this PR head SHA.

@code-yeongyu code-yeongyu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review verdict: NEEDS-CHANGES

Good security posture on the transport (loopback default, 256-bit generated tokens, constant-time bearer compare, redacted diagnostics, no upstream credential echo), but there's a broker-token disclosure path, two advertised request paths that 404/succeed-on-failure, and a verifier that can approve without evidence. Stacked on #193/#194, so their re-port lands first.

Findings

Pri Location Finding
P1 packages/coding-agent/src/cli/auth-gateway-broker-client.ts:61 Validate the broker URL before sending its bearer token — brokerStore sends broker.token to whatever URL came from SENPI_AUTH_BROKER_URL or brokerUrl without applying the transport's loopback/HTTPS policy. serve performs metadataSnapshot() before startAuthGatewayTransport gets a chance to validate brokerUrl, and status/check never call that validator, so http://broker.example receives the broker bearer over plaintext before any rejection.
P1 scripts/verify-auth-broker-gateway.mjs:176 Require real evidence for every verifier check — writeManifest marks all 17 provider/routing/gateway checks as passed and points every one at the same generic tests receipt. Because receipt() only rejects a few failure-looking strings, an arbitrary fresh text file with no failure marker yields a full score and APPROVE even when none of the named checks ran; the added verifier test demonstrates this with fabricated tests clean receipts.
P1 packages/coding-agent/src/core/auth-gateway-request-router.ts:61 Normalize qualified model IDs before Chat and Messages dispatch — modelForRequest accepts the documented provider/model form, but these branches pass the original body unchanged to the adapter. The adapter then sends the full provider/model string as modelId, while the runtime allowlist expects only modelId, so an authorized qualified request returns 404; model IDs that themselves contain / are also unusable.
P1 packages/coding-agent/src/core/auth-gateway-responses-pi-adapter.ts:100 Return a failure status for non-stream provider errors — safeGatewayResult converts provider error and aborted results into a sanitized error message, but the Responses branch always stores that message as chained context and returns HTTP 200. The Pi non-stream branch has the same behavior, so callers treat failed upstream requests as successful responses instead of the 502 mapping used by Chat and Messages.
P2 packages/coding-agent/src/core/auth-gateway-transport-request.ts:51 Handle CORS preflight before bearer authentication — Bearer authentication runs before the OPTIONS branch. Real browsers do not attach the requested Authorization value to a preflight request, so every browser preflight receives 401 and the advertised exact-origin CORS mode cannot be used; the server test masks this by manually adding Authorization to OPTIONS.
P2 packages/coding-agent/src/core/auth-gateway-transport-request.ts:85 Forward selector headers through the transport boundary — The HTTP dispatch object carries only body, method, path, peer address, and signal, so the router can invoke the adapters only with body and signal. Consequently selectorFromHeaders() always receives undefined on real gateway requests and explicit credential/identity selectors silently fall back to automatic selection; arbitrary-header rejection is likewise exercised only in direct adapter tests.
P2 packages/coding-agent/src/core/auth-gateway-openai-chat.ts:72 Forward accepted generation options to the provider runtime — The OpenAI adapter accepts and validates max_completion_tokens, max_tokens, and temperature, then drops them because the parsed result contains no stream options and the runtime call supplies none. The Anthropic adapter similarly discards its required max_tokens and optional temperature, so client limits and sampling settings are silently ignored.
P2 packages/coding-agent/src/core/auth-gateway-openai-chat.ts:106 Preserve tool names when rebuilding tool results — Every OpenAI tool-result message is assigned the literal name tool rather than the name of the preceding assistant tool call with the matching ID; the Anthropic adapter does the same. Provider adapters such as Google and OpenAI-compatible endpoints that require result names consume ToolResultMessage.toolName, so follow-up turns can send the wrong tool identity.
P2 packages/coding-agent/src/core/auth-gateway-transport-auth.ts:66 Enforce minimum bearer-token strength — Both caller-supplied token values and existing token files are accepted when merely non-empty. A library user can therefore enable a TLS remote bind with a one-character bearer token, making the gateway trivially brute-forceable despite the strong token generated by the CLI.
P2 packages/coding-agent/src/core/auth-gateway-transport-response.ts:46 Remove losing listeners from the backpressure wait — Whenever drain wins this Promise.race, the independently registered one-shot close listener remains attached until the response closes. A long stream to a slow client can experience many drain cycles, accumulating listeners and triggering MaxListenersExceededWarning plus memory growth.
P2 packages/coding-agent/src/core/auth-gateway-observability.ts:92 Publish routable qualified model IDs — The documentation defines gateway IDs as provider/model, but /v1/models publishes only modelId. If two authorized providers share an ID, discovery returns duplicate indistinguishable IDs while modelForRequest rejects that unqualified ID as ambiguous, so neither advertised entry can be invoked.

Merge path

  1. Apply the loopback/HTTPS URL policy in brokerConfig/brokerStore before any fetch — serve/status/check currently send the broker bearer to an unvalidated SENPI_AUTH_BROKER_URL.
  2. Make scripts/verify-auth-broker-gateway.mjs record per-check structured evidence instead of synthesizing passed: true from one generic receipt.
  3. Fix qualified provider/model dispatch for Chat/Messages and 502-map non-stream error/aborted results in the Responses/Pi adapter.
  4. Re-port transport/adapters/redaction onto main's auth layout + provider factories; avoid new reliance on the temporary compat registry.

(Reviewed with the PR refs fetched locally against origin/main; line numbers refer to the PR head.)

… P1s, drop GLM ZCode

Re-port OAuth flows from utils/oauth to auth/oauth with bundledLoaders,
route Cursor through Connect/protobuf ChatService, fix GitLab PAT exchange
and gpt-5.1 chat-completions routing, delist Perplexity session OAuth from
/login, exclude Google CCA providers from API-key login, remove unofficial
GLM ZCode login, keep browser smoke Node-free, and update stale kimi-coding
live-test model ids.
…ndings

CAS-guard OAuth disable against updatedAt snapshots, preserve OAuth extras
(projectId) through vault round-trips, resolve pooled OAuth via provider
getRequestAuth/getApiKey, count pools in hasAuth, redact disabled.cause on
upsert/import, prune expired leases, restore models.json oauth:"radius"
registration, and normalize broker login provider aliases.
…/P2 findings

Validate broker URLs before sending bearer tokens, move CORS preflight
before auth, enforce 32-char gateway bearer minimum, map non-stream
error/aborted results to 502/499, publish qualified provider/model IDs,
and refuse verifier APPROVE when checks are failed or receipts are empty.
@islee23520

Copy link
Copy Markdown
Contributor Author

Review follow-up (P1/P2)

Addressed CHANGES_REQUESTED on feat/auth-gateway (8d44e7d95), after merging feat/auth-credential-broker (b20e61ed8).

Fixes

Pri Finding Fix
P1 broker URL not validated before bearer assertBrokerUrlAllowed() runs in brokerConfig() before token is returned/sent; http requires loopback
P1 verifier APPROVE without evidence check refuses any failed checks + total under 90; receipts must be non-empty/non-placeholder
P1 provider/model dispatch shared modelForRequest for chat/messages/responses/pi; extracted + tested
P1 non-stream error returns 200 Responses/Pi non-stream error→502, aborted→499
P2 CORS preflight order OPTIONS handled before bearer auth
P2 weak bearer gateway bearer must be at least 32 chars
P2 /v1/models unqualified IDs publishes provider/modelId

Evidence

vitest packages/coding-agent:
  test/suite/auth-gateway-review-p1.test.ts
  test/suite/auth-gateway-server.test.ts
→ 2 files / 9 tests PASS

Residual

Bring PR code-yeongyu#193 onto current main auth/ModelRuntime layout while preserving
provider OAuth parity review fixes (Cursor Connect, GitLab, Perplexity
delist, Google OAuth-only, GLM ZCode removed) and regenerating model catalogs.
… main rebase

Rebase the broker branch onto the conflict-resolved code-yeongyu#193/main auth stack,
re-port multi-account vault hooks onto CredentialStore AuthStorage, and keep
broker review P1/P2 fixes (CAS disable, extras, lease prune, getRequestAuth).
…in rebase

Bring the gateway branch onto the conflict-resolved code-yeongyu#193/code-yeongyu#194 stack and keep
auth-gateway review P1/P2 fixes (broker URL validation, CORS preflight order,
verifier evidence, 502 mapping, qualified models).
@gitguardian

gitguardian Bot commented Jul 18, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
34886147 Triggered Generic High Entropy Secret a7648c9 packages/ai/scripts/generate-models.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Keep main's video-input and truncation-recovery changelog sections and
retain auth-gateway/broker/provider-parity fork notes.
Drop the PR's xAI browser OAuth in favor of main's device-code flow, restore
OAuth-only API-key login exclusions for Google CCA providers, and keep the
provider/OAuth parity work on current main.
Keep main's device-code xAI flow (drop PR browser discovery), add a thin
legacy OAuthProviderInterface adapter for the compat registry, and exclude
google-gemini-cli / google-antigravity from API-key /login.
…view fixes

Normalize Chat/Messages model IDs before adapter dispatch, emit distinct
per-check verifier receipts, forward selector headers, pass generation
options, preserve tool result names, enforce 32-char bearer min at resolve,
clean drain listeners, and fix biome unused isRecord/optional chain.
@islee23520

Copy link
Copy Markdown
Contributor Author

Review follow-up (P1 + P2)

Rebased via merge of origin/main (linear rebase of the stacked auth history collides on the OAuth path rename utils/oauthauth/oauth; merge keeps the stack mergeable). Head is now d1a2a4df2 on feat/auth-gateway.

P1 addressed

  1. Broker URL before bearer (auth-gateway-broker-client.ts)
    assertBrokerUrlAllowed() runs in brokerConfig() before any token is read or sent. Non-loopback http:// broker URLs fail closed for serve / status / check paths that go through brokerConfig / brokerStore.

  2. Verifier per-check evidence (scripts/verify-auth-broker-gateway.mjs)
    writeManifest no longer stamps all 17 checks with one shared tests receipt. Each check writes checks/<id>.txt with structured metadata (check, objective, points, source) plus the mapped evidence source body and exitCode: 0. Validation still rejects failure markers / empty / placeholder receipts. Verifier test asserts 17 distinct receipt paths.

  3. Qualified model dispatch for Chat / Messages (auth-gateway-request-router.ts + auth-gateway-model-select.ts)
    modelForRequest still resolves provider/model. Chat and Messages adapters receive bareModel(...) so the body field is the bare modelId while the adapter's separate provider option is the authorized provider (Responses/Pi keep qualifyModel because their runtime splits provider/model itself).

  4. Non-stream provider error/aborted → failure status (auth-gateway-responses-pi-adapter.ts)
    Already mapped: non-stream Responses and Pi paths return 502 for error and 499 for aborted instead of 200 + chained context.

Biome CI

  • Removed unused isRecord from auth-gateway-request-router.ts.
  • auth-storage.ts hasAuth uses optional chaining (credentialVault?.metadataSnapshot()... === true).

P2 also landed (within fix budget)

Item Change
CORS OPTIONS before bearer Already ordered: OPTIONS answered before isAuthorized
Forward selector headers Transport request now carries headers; router passes them into Chat/Messages adapters; CORS allow-list includes x-auth-broker-credential-id / x-auth-broker-identity-key / x-senpi-session-id
Generation options OpenAI/Anthropic adapters forward maxTokens / temperature via streamOptions on the runtime call
Tool result names Tool results resolve names from prior assistant tool_call / tool_use IDs instead of the literal "tool"
Min bearer strength resolveGatewayAuth rejects tokens shorter than 32 characters (caller-supplied and existing files)
Drain listener cleanup waitForDrainOrClose removes the losing drain/close listener
Qualified /v1/models IDs Already publishes provider/model; tests updated to match

Verification

  • biome check on touched gateway/verifier paths: clean
  • vitest test/suite/auth-gateway*: 31/31 passed

Remaining / not in this push

Integrate main's ModelRuntime/video/bash abort changes with the credential
broker branch. Keep both change logs (main video/truncation + broker/OAuth).
CAS-disable now matches credential_id + updatedAt + material so a concurrent
re-login cannot be disabled by a stale refresh. Preserve CLIProxy/Gajae
projectId extras through import, count pools in hasAuth/getAuthStatus, and
use optional chaining for credentialVault (biome useOptionalChain).
builtinProviders listed googleProvider() twice, so Models.setProvider
collapsed to 63 unique providers while the array length was 64 and
providers.test failed. Matches main's single registration.
- Remove double googleProvider() so builtinModels length matches unique IDs
- Replace browser/discovery xAI OAuth with main's device-code flow plus
  compat xaiOAuthProvider adapter for the legacy registry
- Remove duplicate googleProvider() (providers.test 63 vs 64)
- Restore main device-code xAI OAuth + registry adapter (fixes xai-oauth.test)
- Lengthen real-surface gateway bearer fixture to satisfy min-32 strength check
@islee23520

Copy link
Copy Markdown
Contributor Author

Evidence-based follow-up (CI green path)

Review P1 verification

Finding Verdict
Broker URL before bearer FixedassertBrokerUrlAllowed in brokerConfig before token use
Verifier real per-check receipts Fixed — distinct check receipts, not synthetic all-pass
Chat/Messages bare model IDs FixedbareModel() after allowlist
Non-stream 502/499 Fixed — Responses/Pi adapter
Min bearer ≥32 Fixed — transport assert + real-surface fixture lengthened

CI root causes this push

  1. Duplicate googleProvider() → providers.test 63≠64
  2. Stale browser xAI OAuth after merge → xai-oauth discovery failures
  3. Real-surface test used a short gateway bearer after min-length enforcement

Tests run locally

  • packages/ai providers + xai: 31/31
  • Gateway suite (11 files including review-p1, server, responses, real-surface, verifier): 35/35
  • biome on gateway/auth files: clean

Not merging until Check and test is green and review is re-approved.

Expose minimax-code, minimax-code-cn, moonshot, and opencode-zen as
API-key login targets via display-name eligibility.
- Add display names so minimax-code/moonshot/opencode-zen appear in /login
- Exclude oauth-only providers from API-key login (codex-device, google CCA)
- Store openai-codex-device credentials under canonical openai-codex key
- Align gajae-login-provider-parity tests with async list() API
Root causes verified against review + failing CI:
1. Missing display names hid minimax-code/moonshot/opencode-zen from /login
2. openai-codex-device was also API-key eligible (duplicate login entry)
3. AuthStorage.set did not alias device codex credentials to openai-codex
4. Test expected sync list() but AuthStorage.list is async

Also restores OAUTH_ONLY exclusions and lengthens real-surface bearer.
@islee23520

Copy link
Copy Markdown
Contributor Author

Evidence cycle 2 — CI failure fixed

Failed CI (run 29686961660)

  1. gajae-login-provider-parity — missing login IDs minimax-code, minimax-code-cn, moonshot, opencode-zen
  2. device-code storage test — list() returned a Promise (async API) and set() did not alias openai-codex-deviceopenai-codex
  3. app-server thread handler tests timed out (30s) — treated as likely flaky/environment; not reproduced in the focused suite this cycle

Fixes

Change Why
Display names for the four providers isApiKeyLoginProvider requires display name for built-in API-key eligibility
OAUTH_ONLY_MODEL_PROVIDERS Prevents double listing of oauth-only providers as api_key
AuthStorage.storageKey() Canonical storage for openai-codex-device
Gajae tests set membership + await list()

Local verification

  • test/gajae-login-provider-parity.test.ts2/2 pass
  • gateway suite (11 files) → 35/35 (previous cycle)
  • senpi-qa cli-smoke / mock-loop / rpc self-tests → pass (evidence under worktree local-ignore/qa-evidence/20260719-auth-pr195-evidence)

Not merging until Check and test is green and review is re-approved.

…render

Long absolute worktree paths wrap at render width 120, so compact
collapsed text is no longer a single line. Flatten whitespace before
asserting the compact resource label.
- Flatten whitespace in tool-execution compact path assertions so deep
  worktree absolute paths that wrap still match
- Apply biome formatting on auth-broker and related suite tests
…render

Long absolute worktree paths wrap at render width 120, so compact
collapsed text is no longer a single line. Flatten whitespace before
asserting the compact resource label. Also normalize multi-account
suite formatting for biome.
Build harness under real timers and enable fake timers only for the
idle-unload countdown so Vitest no longer flakes with 30s hangs on
advanceTimersByTimeAsync/runOnlyPendingTimersAsync in CI.
Build harness under real timers and enable fake timers only for the
idle-unload countdown so Vitest no longer flakes with 30s hangs on
advanceTimersByTimeAsync/runOnlyPendingTimersAsync in CI.
Build harness under real timers and enable fake timers only for the
idle-unload countdown so Vitest no longer flakes with 30s hangs on
advanceTimersByTimeAsync/runOnlyPendingTimersAsync in CI.
@islee23520

Copy link
Copy Markdown
Contributor Author

CI fix: app-server idle-unload timer flake

Root cause

Check and test was flaking on:

  • app-server-thread-handlers-catch.test.ts — idle unload registry error
  • app-server-thread-handlers-cold.test.ts — idle unload / transport-close / dispose timer cases

Those tests called vi.useFakeTimers() before async harness setup (createHarness / thread/start). Under load, Vitest fake timers could hang advanceTimersByTimeAsync / runOnlyPendingTimersAsync until the 30s suite timeout.

Fix (head 7525f8a67a)

  • Build harness under real timers
  • Enable fake timers only for the idle-unload countdown (toFake: ["setTimeout","clearTimeout"])
  • Prefer advanceTimersByTimeAsync(0) for the zero-delay unload case

Local evidence

  • Targeted suite: 25/25 stress passes on main deps; 5/5 per PR worktree
  • Full packages/coding-agent vitest with the fix: 3781 passed | 31 skipped (432 files)
  • Pre-commit npm run check green on commit (PR feat(ai): provider and OAuth parity for /login #193 worktree)

Still open / not claimed closed

Please re-run review after CI is green on this head.

kimi-code and kimi-coding are the same product at api.kimi.com/coding with
the same KIMI_API_KEY. Upstream kimi-coding is the maintained entry
(k3 video input, adaptive thinking), so drop the PR-only kimi-code OAuth
provider and its catalog so /login shows exactly one Kimi entry.
Adds a regression asserting a single kimi-* provider and KIMI_API_KEY
resolution.
…parity

# Conflicts:
#	packages/ai/src/providers/data/openrouter.json
…feat/auth-credential-broker

# Conflicts:
#	package-lock.json
#	packages/ai/src/changes.md
#	packages/coding-agent/src/core/auth-providers.ts
#	packages/coding-agent/src/core/changes.md
…o feat/auth-gateway

# Conflicts:
#	packages/ai/src/changes.md
#	packages/coding-agent/src/core/auth-broker.ts
#	packages/coding-agent/src/core/auth-providers.ts
#	packages/coding-agent/src/core/changes.md
…parity

# Conflicts:
#	packages/ai/test/abort.test.ts
#	packages/ai/test/context-overflow.test.ts
#	packages/ai/test/cross-provider-handoff.test.ts
#	packages/ai/test/empty.test.ts
#	packages/ai/test/image-tool-result.test.ts
#	packages/ai/test/stream.test.ts
#	packages/ai/test/tokens.test.ts
#	packages/ai/test/tool-call-without-result.test.ts
#	packages/ai/test/total-tokens.test.ts
#	packages/ai/test/unicode-surrogate.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants