Repository navigation
feat(cli): add the OrcaRouter Cyber Inference model provider - #3739
beilsteinernest215-cmd wants to merge 1 commit into
Conversation
Signed-off-by: beilsteinernest215-cmd <beilsteinernest215-cmd@users.noreply.github.com>
PR validation —
|
| Status | Policy | Material | Messages |
|---|---|---|---|
pr-min-approvals |
pr-info |
PR/MR #3739 has 0 approving reviews, 1 required. | |
| ✅ Passed | pr-description-required |
pr-info |
- |
| ✅ Passed | pr-user-story-linked |
pr-info |
- |
AI Session Checks — ⚠️ no AI session found
Missing AI Coding Sessions
This organization requires every PR to be backed by a Chainloop Trace AI coding session, and none was found for this one.
Please make sure the AI coding session evidence has been sent by the Chainloop CLI, or add the skip-ai-session label to this PR to bypass this check.
Learn more about Chainloop Trace.
Security Checks — ✅ 5 passing
✅ secret-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
Scans not applied (3)
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
iac-scan |
no IaC files changed |
Security context
✅ Nothing this change touches has a recorded security-fix history.
View in Chainloop ↗ · How this works ↗
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
12 issues found across 20 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/cli/README.md">
<violation number="1" location="app/cli/README.md:20">
P2: This setup implies that `login` or `connect` configures `trace run --codex`, but Trace never reads the stored OrcaRouter credential; Codex still requires `ORCAROUTER_API_KEY` in its environment and a provider/model selection. Clarify the required environment/configuration here, or inject the stored credential into the wrapped Codex process.</violation>
</file>
<file name="internal/orcarouter/pkce.go">
<violation number="1" location="internal/orcarouter/pkce.go:243">
P2: The printed `--no-browser` URL cannot complete when opened on another machine because the redirect targets that machine's `127.0.0.1`, not the CLI host. Keep this mode explicitly local-only or provide a reachable/manual callback flow instead of advertising that it works elsewhere.</violation>
<violation number="2" location="internal/orcarouter/pkce.go:401">
P2: `Exchange` treats every scope string that differs byte-for-byte from the request as a downgrade. OAuth scopes are order-insensitive token sets, so multi-scope grants such as `"profile api"` are rejected when `"api profile"` was requested; compare scope tokens and reject only when a requested token is absent.</violation>
</file>
<file name="internal/orcarouter/inference.go">
<violation number="1" location="internal/orcarouter/inference.go:87">
P2: Trim the model before marshaling it. Currently surrounding whitespace passes validation but is sent to OrcaRouter, so a quoted `--model` value fails even when it names a valid catalog model.</violation>
</file>
<file name="app/cli/cmd/orcarouter.go">
<violation number="1" location="app/cli/cmd/orcarouter.go:292">
P2: `models` treats a 401 as degraded discovery and never marks the requesting credential generation for reauthentication, then lists the seed models as usable. Mark the generation and return an actionable reauthentication error for `ErrNeedsReauth` instead of falling back.</violation>
<violation number="2" location="app/cli/cmd/orcarouter.go:355">
P2: The chat command sends `orcarouter/auto` even when the current credential's live catalog does not offer that model, and it accepts arbitrary IDs despite saying models come from the catalog. Select or revalidate the default against live discovery, or require an explicitly listed `--model` instead of defaulting to an inaccessible recommendation.</violation>
<violation number="3" location="app/cli/cmd/orcarouter.go:367">
P2: The 401 reauthentication marker is lost when the `chat` process exits, so a subsequent `orcarouter status` cannot tell the user that the stored key was rejected. Persist the rejected generation (or another reauthentication marker) alongside the credential and reload it when constructing the manager.</violation>
</file>
<file name="internal/orcarouter/provider.go">
<violation number="1" location="internal/orcarouter/provider.go:57">
P2: `RecommendedModels` claims these IDs are included in cold-start seeding, but the degraded catalog omits both cyber models. Add verified seed entries for them, or update this comment and API so the recommendation list is explicitly documentation-only.</violation>
</file>
<file name="internal/orcarouter/catalog.go">
<violation number="1" location="internal/orcarouter/catalog.go:166">
P1: `fetchCatalog` sends the credential before validating the configured origin. A remote `http://` `ORCA_API_BASE_URL` can therefore receive the API key in cleartext; call `ValidateOrigin` before creating the request, as `Client.Chat` does.</violation>
<violation number="2" location="internal/orcarouter/catalog.go:230">
P2: `rawModel.model` drops the catalog’s declared output modalities, so the output-modality guard in `IsTextChat` never runs. Carry that metadata into `Model` and exclude records whose declared outputs omit text; otherwise non-chat models can appear in the text selector and fail when used.</violation>
</file>
<file name="app/cli/internal/trace/README.md">
<violation number="1" location="app/cli/internal/trace/README.md:220">
P2: The checked-in E2E suite does not run any OrcaRouter model: `config.yaml` lists only the OpenRouter Codex model, and the runner filters configured models rather than adding new ones. Add an OrcaRouter model to the suite configuration or describe this as harness support only.</violation>
</file>
<file name="app/cli/documentation/cli-reference.md">
<violation number="1" location="app/cli/documentation/cli-reference.md:2068">
P2: The documented browser-failure fallback is not implemented: a failed default browser opener returns an error without printing the URL. Say that users must pass `--no-browser` to print the authorization URL.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
View guided diff | Re-trigger cubic
| if cred.Key == "" { | ||
| return nil, errors.New("no OrcaRouter credential") | ||
| } | ||
| baseURL = NormalizeBase(baseURL) |
There was a problem hiding this comment.
P1: fetchCatalog sends the credential before validating the configured origin. A remote http:// ORCA_API_BASE_URL can therefore receive the API key in cleartext; call ValidateOrigin before creating the request, as Client.Chat does.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/orcarouter/catalog.go, line 166:
<comment>`fetchCatalog` sends the credential before validating the configured origin. A remote `http://` `ORCA_API_BASE_URL` can therefore receive the API key in cleartext; call `ValidateOrigin` before creating the request, as `Client.Chat` does.</comment>
<file context>
@@ -0,0 +1,365 @@
+ if cred.Key == "" {
+ return nil, errors.New("no OrcaRouter credential")
+ }
+ baseURL = NormalizeBase(baseURL)
+ if baseURL == "" {
+ baseURL = BaseURL
</file context>
|
|
||
| ## OrcaRouter Cyber Inference | ||
|
|
||
| `chainloop orcarouter` configures [OrcaRouter](https://www.orcarouter.ai), an OpenAI-compatible inference gateway for security teams, so `chainloop trace run --codex` and the Trace end-to-end suite can route a Codex session through `https://api.orcarouter.ai/v1` with `model_providers.orcarouter`. The provider is `OrcaRouter Cyber Inference`: cyber models for an authorized red team, gateway controls for a blue team. |
There was a problem hiding this comment.
P2: This setup implies that login or connect configures trace run --codex, but Trace never reads the stored OrcaRouter credential; Codex still requires ORCAROUTER_API_KEY in its environment and a provider/model selection. Clarify the required environment/configuration here, or inject the stored credential into the wrapped Codex process.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/README.md, line 20:
<comment>This setup implies that `login` or `connect` configures `trace run --codex`, but Trace never reads the stored OrcaRouter credential; Codex still requires `ORCAROUTER_API_KEY` in its environment and a provider/model selection. Clarify the required environment/configuration here, or inject the stored credential into the wrapped Codex process.</comment>
<file context>
@@ -15,6 +15,22 @@ b) [Attestation Crafting Process](https://docs.chainloop.dev/getting-started/att
+## OrcaRouter Cyber Inference
+
+`chainloop orcarouter` configures [OrcaRouter](https://www.orcarouter.ai), an OpenAI-compatible inference gateway for security teams, so `chainloop trace run --codex` and the Trace end-to-end suite can route a Codex session through `https://api.orcarouter.ai/v1` with `model_providers.orcarouter`. The provider is `OrcaRouter Cyber Inference`: cyber models for an authorized red team, gateway controls for a blue team.
+
+Two credential paths end at the same durable `sk-orca-…` API key, and either works alone:
</file context>
| `chainloop orcarouter` configures [OrcaRouter](https://www.orcarouter.ai), an OpenAI-compatible inference gateway for security teams, so `chainloop trace run --codex` and the Trace end-to-end suite can route a Codex session through `https://api.orcarouter.ai/v1` with `model_providers.orcarouter`. The provider is `OrcaRouter Cyber Inference`: cyber models for an authorized red team, gateway controls for a blue team. | |
| `chainloop orcarouter` manages [OrcaRouter](https://www.orcarouter.ai) credentials, model discovery, and direct chat. To route Codex through `https://api.orcarouter.ai/v1`, set `ORCAROUTER_API_KEY` and select a model as `orcarouter:<model id>`; `chainloop trace run --codex` wraps the supplied Codex command and does not read the stored key. The provider is `OrcaRouter Cyber Inference`: cyber models for an authorized red team, gateway controls for a blue team. |
|
|
||
| // Listen first, so the callback URL is known before the browser opens and | ||
| // no approval can race the listener. | ||
| listener, err := net.Listen("tcp", "127.0.0.1:0") |
There was a problem hiding this comment.
P2: The printed --no-browser URL cannot complete when opened on another machine because the redirect targets that machine's 127.0.0.1, not the CLI host. Keep this mode explicitly local-only or provide a reachable/manual callback flow instead of advertising that it works elsewhere.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/orcarouter/pkce.go, line 243:
<comment>The printed `--no-browser` URL cannot complete when opened on another machine because the redirect targets that machine's `127.0.0.1`, not the CLI host. Keep this mode explicitly local-only or provide a reachable/manual callback flow instead of advertising that it works elsewhere.</comment>
<file context>
@@ -0,0 +1,422 @@
+
+ // Listen first, so the callback URL is known before the browser opens and
+ // no approval can race the listener.
+ listener, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return Credential{}, fmt.Errorf("listen on loopback: %w", err)
</file context>
| if cred.Key == "" { | ||
| return "", ErrNoCredential | ||
| } | ||
| if strings.TrimSpace(req.Model) == "" { |
There was a problem hiding this comment.
P2: Trim the model before marshaling it. Currently surrounding whitespace passes validation but is sent to OrcaRouter, so a quoted --model value fails even when it names a valid catalog model.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/orcarouter/inference.go, line 87:
<comment>Trim the model before marshaling it. Currently surrounding whitespace passes validation but is sent to OrcaRouter, so a quoted `--model` value fails even when it names a valid catalog model.</comment>
<file context>
@@ -0,0 +1,158 @@
+ if cred.Key == "" {
+ return "", ErrNoCredential
+ }
+ if strings.TrimSpace(req.Model) == "" {
+ return "", errors.New("no model selected")
+ }
</file context>
| return fmt.Errorf("%w; run `chainloop orcarouter login` or `chainloop orcarouter connect`", orcarouter.ErrNoCredential) | ||
| } | ||
| if model == "" { | ||
| model = orcarouter.DefaultModel |
There was a problem hiding this comment.
P2: The chat command sends orcarouter/auto even when the current credential's live catalog does not offer that model, and it accepts arbitrary IDs despite saying models come from the catalog. Select or revalidate the default against live discovery, or require an explicitly listed --model instead of defaulting to an inaccessible recommendation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/cmd/orcarouter.go, line 355:
<comment>The chat command sends `orcarouter/auto` even when the current credential's live catalog does not offer that model, and it accepts arbitrary IDs despite saying models come from the catalog. Select or revalidate the default against live discovery, or require an explicitly listed `--model` instead of defaulting to an inaccessible recommendation.</comment>
<file context>
@@ -0,0 +1,374 @@
+ return fmt.Errorf("%w; run `chainloop orcarouter login` or `chainloop orcarouter connect`", orcarouter.ErrNoCredential)
+ }
+ if model == "" {
+ model = orcarouter.DefaultModel
+ }
+
</file context>
| if id == "" { | ||
| return Model{}, false | ||
| } | ||
| out := Model{ |
There was a problem hiding this comment.
P2: rawModel.model drops the catalog’s declared output modalities, so the output-modality guard in IsTextChat never runs. Carry that metadata into Model and exclude records whose declared outputs omit text; otherwise non-chat models can appear in the text selector and fail when used.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/orcarouter/catalog.go, line 230:
<comment>`rawModel.model` drops the catalog’s declared output modalities, so the output-modality guard in `IsTextChat` never runs. Carry that metadata into `Model` and exclude records whose declared outputs omit text; otherwise non-chat models can appear in the text selector and fail when used.</comment>
<file context>
@@ -0,0 +1,365 @@
+ if id == "" {
+ return Model{}, false
+ }
+ out := Model{
+ ID: id,
+ Name: m.Name,
</file context>
|
|
||
| The configured OpenCode 2, Pi, and Codex models use OpenRouter and need `OPENROUTER_API_KEY`. A Codex model without the `openrouter/` prefix uses the default Codex provider and needs `CODEX_API_KEY`. The pinned Codex model is `openrouter/openai/gpt-6-luna`. GPT-5 Mini frequently ended the scripted task before committing and pushing, so it is not the default test model. | ||
|
|
||
| A Codex model can also run on [OrcaRouter](https://www.orcarouter.ai), the OpenAI-compatible gateway for security teams: write it as `orcarouter:<model id>`, for example `orcarouter:orcarouter/auto`, and set `ORCAROUTER_API_KEY`. The suite passes `model_providers.orcarouter` to Codex with `base_url = https://api.orcarouter.ai/v1`, the same definition `chainloop orcarouter` uses. `chainloop orcarouter login` stores a pasted key and `chainloop orcarouter connect` obtains one with OAuth 2.0 + PKCE; either credential works, and `chainloop orcarouter models` lists the live catalog. See the [Cyber Inference models](https://www.orcarouter.ai/models?tier=cyber). |
There was a problem hiding this comment.
P2: The checked-in E2E suite does not run any OrcaRouter model: config.yaml lists only the OpenRouter Codex model, and the runner filters configured models rather than adding new ones. Add an OrcaRouter model to the suite configuration or describe this as harness support only.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/internal/trace/README.md, line 220:
<comment>The checked-in E2E suite does not run any OrcaRouter model: `config.yaml` lists only the OpenRouter Codex model, and the runner filters configured models rather than adding new ones. Add an OrcaRouter model to the suite configuration or describe this as harness support only.</comment>
<file context>
@@ -217,6 +217,8 @@ The harnesses run as follows:
The configured OpenCode 2, Pi, and Codex models use OpenRouter and need `OPENROUTER_API_KEY`. A Codex model without the `openrouter/` prefix uses the default Codex provider and needs `CODEX_API_KEY`. The pinned Codex model is `openrouter/openai/gpt-6-luna`. GPT-5 Mini frequently ended the scripted task before committing and pushing, so it is not the default test model.
+A Codex model can also run on [OrcaRouter](https://www.orcarouter.ai), the OpenAI-compatible gateway for security teams: write it as `orcarouter:<model id>`, for example `orcarouter:orcarouter/auto`, and set `ORCAROUTER_API_KEY`. The suite passes `model_providers.orcarouter` to Codex with `base_url = https://api.orcarouter.ai/v1`, the same definition `chainloop orcarouter` uses. `chainloop orcarouter login` stores a pasted key and `chainloop orcarouter connect` obtains one with OAuth 2.0 + PKCE; either credential works, and `chainloop orcarouter models` lists the live catalog. See the [Cyber Inference models](https://www.orcarouter.ai/models?tier=cyber).
+
Some scenarios add the git commands of a human after the session, for example a commit that replaces an AI line, an amend, or a rename, and then push again. They work on a feature branch, because the push reads only the commits that are not on the default branch of the remote.
</file context>
| return fmt.Errorf("%w; run `chainloop orcarouter login` or `chainloop orcarouter connect`", orcarouter.ErrNoCredential) | ||
| } | ||
|
|
||
| cat := orcarouter.DiscoverCatalog(cmd.Context(), nil, orcarouter.APIOrigin(), cred) |
There was a problem hiding this comment.
P2: models treats a 401 as degraded discovery and never marks the requesting credential generation for reauthentication, then lists the seed models as usable. Mark the generation and return an actionable reauthentication error for ErrNeedsReauth instead of falling back.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/cmd/orcarouter.go, line 292:
<comment>`models` treats a 401 as degraded discovery and never marks the requesting credential generation for reauthentication, then lists the seed models as usable. Mark the generation and return an actionable reauthentication error for `ErrNeedsReauth` instead of falling back.</comment>
<file context>
@@ -0,0 +1,374 @@
+ return fmt.Errorf("%w; run `chainloop orcarouter login` or `chainloop orcarouter connect`", orcarouter.ErrNoCredential)
+ }
+
+ cat := orcarouter.DiscoverCatalog(cmd.Context(), nil, orcarouter.APIOrigin(), cred)
+ if cat.Degraded {
+ fmt.Fprintf(cmd.OutOrStderr(), "Live discovery failed (%v); showing the verified fallback catalog.\n", cat.Err)
</file context>
| When no browser can be opened, the authorization URL is printed so it can be | ||
| visited elsewhere; the loopback callback still receives the result. |
There was a problem hiding this comment.
P2: The documented browser-failure fallback is not implemented: a failed default browser opener returns an error without printing the URL. Say that users must pass --no-browser to print the authorization URL.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At app/cli/documentation/cli-reference.md, line 2068:
<comment>The documented browser-failure fallback is not implemented: a failed default browser opener returns an error without printing the URL. Say that users must pass `--no-browser` to print the authorization URL.</comment>
<file context>
@@ -1964,6 +1964,318 @@ Options inherited from parent commands
+The key is durable and is reused until it is revoked. Re-authorizing on every
+launch would exhaust the per-user issuance limit.
+
+When no browser can be opened, the authorization URL is printed so it can be
+visited elsewhere; the loopback callback still receives the result.
+
</file context>
| When no browser can be opened, the authorization URL is printed so it can be | |
| visited elsewhere; the loopback callback still receives the result. | |
| Use `--no-browser` to print the authorization URL when a browser is unavailable; the loopback callback still receives the result. |
| } | ||
|
|
||
| cred := Credential{Key: decoded.Key, Source: SourcePKCE, UserID: decoded.UserID, Scope: decoded.Scope} | ||
| if requestedScope != "" && decoded.Scope != "" && decoded.Scope != requestedScope { |
There was a problem hiding this comment.
P2: Exchange treats every scope string that differs byte-for-byte from the request as a downgrade. OAuth scopes are order-insensitive token sets, so multi-scope grants such as "profile api" are rejected when "api profile" was requested; compare scope tokens and reject only when a requested token is absent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/orcarouter/pkce.go, line 401:
<comment>`Exchange` treats every scope string that differs byte-for-byte from the request as a downgrade. OAuth scopes are order-insensitive token sets, so multi-scope grants such as `"profile api"` are rejected when `"api profile"` was requested; compare scope tokens and reject only when a requested token is absent.</comment>
<file context>
@@ -0,0 +1,422 @@
+ }
+
+ cred := Credential{Key: decoded.Key, Source: SourcePKCE, UserID: decoded.UserID, Scope: decoded.Scope}
+ if requestedScope != "" && decoded.Scope != "" && decoded.Scope != requestedScope {
+ return Credential{}, fmt.Errorf("%w: granted %q, requested %q", ErrScopeDowngrade, decoded.Scope, requestedScope)
+ }
</file context>
What this adds
Chainloop Trace records AI coding sessions, and its Codex harness is the one place in the tree that names a model provider (
app/cli/internal/trace/e2e/codex.gobuildsmodel_providers.openroutertoday). This adds OrcaRouter Cyber Inference as a named, first-class provider next to it, with both credential paths in one place. OrcaRouter is an OpenAI-compatible AI gateway that routes many providers behind one endpoint. OrcaRouter's cyber tier is aimed at security teams doing authorized, scoped work: https://www.orcarouter.ai/cyber and https://www.orcarouter.ai/models?tier=cyber.Provider and credentials
internal/orcarouterholds the provider definition (BaseURL = "https://api.orcarouter.ai/v1",EnvKey = "ORCAROUTER_API_KEY"), the credential seam, its lifetime rules, the model catalog with capability filters, and the chat client.sk-orca-…key, stored next toauth.tokenin the CLI's existing devel config profile and cleared bylogout.127.0.0.1and receives the code without copy/paste. S256 challenge from a per-attemptcrypto/randverifier, freshstateper attempt, and a constant-timestatecomparison before the code is used. The minted key is durable, not a refresh token, so a 401 marks exactly the account/generation that made the request as needing reauthentication; there is no fake refresh grant.https://www.orcarouter.ai(/auth,/api/v1/auth/keys), inference and model discovery onhttps://api.orcarouter.ai/v1.chainloop orcarouter login|connect|status|models|chat|logoutexposes both paths;connect --no-browserprints the authorization URL for headless machines.Models
GET /v1/models, never free text.chatfilters to chat-capable models and--image-inputnarrows to models whosearchitecture.input_modalitiesexplicitly declare image; undeclared models fail closed.orcarouter/autois the default; OrcaCyber Ultra (orcacyber-ultra-1.0, security code scanning and multi-step reasoning over large repositories) and OrcaCyber Zero (orcacyber-zero-1.5, OrcaCyber-harness driven) are the dedicated cyber models for teams with business verification;orcarouter/fusionis the general fallback. The small cold-start seed exists only for outage; a successful live fetch is authoritative.Trace harness
The Codex harness accepts
orcarouter:<model id>and passes a namedmodel_providers.orcarouter={name=…,base_url=…,env_key=…}, so a Trace session can run onorcarouter/autoor a cyber model. No other provider's behavior changes.Verification
All checks run through the provider code in this patch in a scrubbed environment:
go test ./internal/orcarouter/— provider, both credential adapters, PKCE (verifier/challenge/state, authorize URL, exchange path and body, rejection, state mismatch, code reuse, scope read back from the response, 401 generation handling, 429, network failure), catalog and capability filters, error classification. Tests assert the key and verifier never appear in errors or logs.go test ./app/cli/cmd/ -run 'TestOrcaRouter|TestDescribeConnectError'— command wiring for both paths.go test ./app/cli/internal/trace/e2e/ -run 'TestCodex…'— the harness still emits the OpenRouter definition unchanged and now emits the OrcaRouter one.GET /v1/modelsreturned 21 models, an--image-inputrun narrowed to 3, and a chat completion returned a non-empty answer. The verification key is not entitled toorcarouter/autoororcarouter/fusionyet (HTTP 403model_access_denied), so the live chat useddeepseek/deepseek-v4.1-flash— a connectivity probe only, not a user-facing recommendation. That 403 is handled explicitly as a model-permission error rather than a rejected credential, since a new login would not fix it;TestChatModelNotAllowedcovers it.Limits
chatcommand are model entry points in this tree, and both are text-only, so no multimodal selector is wired to a UI.I'm an engineer on the OrcaRouter team.
It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.