Repository navigation
Agents CLI: run against a remote Dex server with TLS and an API key or bearer credential - #1752
Merged
Merged
Conversation
…ormance cases Add dex.auth (none, api_key, bearer) and dex.tls (ca_file, server_name) to the CLI configuration schema, with the file checks insecure_token_url, auth_needs_server_url, and tls_needs_https_server. Add the dex_connection conformance cases that fix how a CLI core turns the file and the DEX_* variables into connection settings, and check them with the reference resolver.
Document dex.auth and dex.tls in the agents CLI reference, with the environment variables, their precedence, and the startup errors, and add the agents and agents-packages change-log fragments.
… dex.tls Resolve dex.auth and dex.tls with the DEX_* variables, and give every Client, the Worker, and the Worker host the same credential and TLS options. Fill the terminal UI launch document's dex.auth and dex.tls and pass it only the one credential variable that the mode needs. A missing setting stops the command with a message that names it, and a rejected credential says whether the token endpoint or the server refused it. Integration tests run against an authenticated dex-server-zd, with a token endpoint and a TLS proxy.
… and dex.tls Resolve dex.auth and dex.tls with the DEX_* variables as the Python core does, and give the Worker, every Client, and the Worker host the same credential and TLS options. Fill the terminal UI launch document's dex.auth and dex.tls and pass it only the one credential variable that the mode needs. Run the dex_connection conformance cases, and add integration tests against an authenticated dex-server-zd with a token endpoint and a TLS proxy.
…r texts Remove DEX_API_KEY, DEX_CREDENTIAL, and DEX_CREDENTIAL_FILE from the environment of shell commands, browsers, downloads, and dexcli dev in both cores, also without dex.auth; only the Worker host and the terminal UI get the one variable that the mode needs. Refusal lines name only the HTTP or gRPC status, never the remote error text, which may echo the credential. The Python connectors subcommand validates the connection settings as the TypeScript one does. Mark the new API as 0.6.2.
…ver-auth # Conflicts: # agents/cli/typescript/core/src/cli-config.ts
The session graph upload log line, the Worker host's local request logs, and the fallback error output name only the status of an UNAUTHENTICATED or PERMISSION_DENIED answer, never the server's details, which may echo the credential. The docs say that messages never show credential values; the token URL they show is a configuration value.
…arnings A logging filter on the Python CLI's log handler, and the TypeScript library console redirection, replace an UNAUTHENTICATED or PERMISSION_DENIED error that the SDK logs, such as a failed Worker Flow definition upload, with its status code and a local hint.
longquanzheng
enabled auto-merge (squash)
November 13, 2026 02:17
longquanzheng
disabled auto-merge
November 13, 2026 03:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The agents CLI commands (Python and TypeScript) can now keep their sessions on a remote Dex server that requires TLS and a credential. Before, a command could name an
httpsserver but sent no credential and could not trust a private CA.Configuration. Two optional sections join the configuration file:
dex.authanddex.tls.dex.authtakes one of three modes:none(the default),api_key(withtoken_urlandserver_id), orbearer(withsource: envorsource: file).dex.tlstakesca_fileandserver_name.DEX_API_KEY,DEX_CREDENTIAL, or a token file. The schema rejectsapi_key,token,credential, andaccess_tokenunderdex.auth.Environment variables. They use dexcli's names:
DEX_TOKEN_URL,DEX_SERVER_ID,DEX_CREDENTIAL_FILE,DEX_TLS_CA_FILE, andDEX_TLS_SERVER_NAME.httpsdex.server.New file checks.
insecure_token_url: the token URL must be https, or http on loopback.auth_needs_server_url:dex.authwithdex.server: autois an error.tls_needs_https_server:dex.tlsneeds an https server.Connections. Every Dex connection of a command uses the resolved credential and TLS options:
sessionslisting Client;Terminal UI. The launch document carries the resolved
dex.authanddex.tls, which were hardcoded tononeand defaults before. The terminal UI environment gets only the one credential variable that the contract allows. Forbearerwithsource: file, that isDEX_CREDENTIAL_FILEset to the resolved path.Errors.
DEX_API_KEY is not set; dex.auth mode api_key needs it.could not exchange DEX_API_KEY for an access token at <token URL> (HTTP status 401)) or the server answeredUNAUTHENTICATEDorPERMISSION_DENIED.Child processes never get the credential. Each command removes
DEX_API_KEY,DEX_CREDENTIAL, andDEX_CREDENTIAL_FILEfrom its own environment right after it reads them. It does so whether or notdex.authis set, because a shell exported for dexcli may hold them. So the shell tool, the browser and its installer, the URL opener, downloads, anddexcli devnever see them, and an agent cannot read them withprintenv.dexcli agents-tuikeeps the contract's one-variable allow-list.CliEnvironment.childProcessVariableslacks the three variables too.connectorsvalidates the settings in both cores. Python now resolves the connection settings before theconnectorssubcommand, as TypeScript does, so a missingDEX_API_KEYstops it with exit code 2 in both.Shared contract.
agents/conformance/cli_config.jsongains:dex_connectionsection, which fixes how the file and the variables become connection settings, with exact error messages.Both cores and the test-only reference resolver in
agents/cli/testsrun these cases.Why
A user wants to run a command such as
dex-agents-claude-sdkwith its sessions on a shared remote Dex server. Such a server requires TLS and an API key or bearer credential. Several pieces already supported this:ApiKeyCredential,FileCredential, and the FlowService TLS options;DEX_*variables;dex.authanddex.tls.The CLI cores never passed a credential, and both launchers hardcoded
{"mode":"none"}. So such a server rejected every call withUNAUTHENTICATED. The Go exampleexamples/go/cmd/ai-agent(#1735) shows the same validation rules for an application.Design decisions
dex.auth.modecomes from the configuration file alone. Without it, the command reads none of the credential variables. A user who exports dexcli's variables for another server keeps a workingautoorhttpconfiguration.DEX_TLSandDEX_FLOW_SERVICE_ADDRESSare never read.httpdex.server, as in dexcli and the SDKs. The docs say it is then readable on the network path. The token URL, in contrast, must be https or on loopback.~and are relative to the configuration file's directory. In a variable, they expand~and are relative to the working directory. Both are normalized lexically.Compatibility
All changes are additive:
dex.auth: {mode: none}and behaves as before.EffectiveConfig.dex.authand.tlsare optional, so code that builds anEffectiveConfigitself keeps compiling.dex.authanddex.tls.dex.authanddex.tlsfields change.Security notes
Secrets come only from the environment or a token file. The schema rejects secret-looking keys in
dex.auth.Startup and runtime messages name settings and variables, never their values. Tests assert that the output does not contain the key or the token.
The terminal UI environment stays an allow-list. It gets only the one auth variable the mode needs, and never provider keys.
The launch document holds no secret.
Child processes other than the Worker host and
dexcli agents-tuinever inheritDEX_API_KEY,DEX_CREDENTIAL, orDEX_CREDENTIAL_FILE. Tests in both languages runenvthrough the shell tool, with and withoutdex.auth, and find none of them.Refusal lines never repeat remote error details. Tests in both languages give the description a status whose details contain a secret.
The CLI cores' own log lines and fallback output name only the status code of an
UNAUTHENTICATEDorPERMISSION_DENIEDanswer. This covers the session graph upload log line, the Worker host's local request andstart_sessionlogs, the TypeScript plain chat, and the last-resort error print. Tests in both languages cover an upload refused with echoed details.Log records and warnings that the Dex SDKs write, such as the Worker's automatic Flow definition upload failure, pass through the same rewrite:
loggingfilter on the command's log handler, so every logger is covered;console.warnandconsole.errorredirection.They cover any SDK log, pull stream reconnects included, that passes the error object itself. Follow-up: an SDK log that has already formatted the error into its message text, or that writes to stderr directly (TypeScript
dex info:lines), is not rewritten. The SDKs should leave refusal details out themselves.The token URL must be https, or http on loopback, as the SDKs require.
Testing
agents/cli/tests/ci.sh: schemas, the new example, and every config anddex_connectioncase against the reference resolver.agents/cli/python/core/ci.shwithDEXCLIset to dexcli 1.46.1 and its dex-server-zd 0.1.26: ruff, format, mypy, and pytest, 714 passed and 2 skipped. New integration tests run against an authenticateddex-server-zd dev(staticTokens), a token endpoint that accepts only the right key, and a TLS proxy with a private CA:dex.tls.ca_file: a chat turn completes, andsessionslists the session;DEX_API_KEY(exit 2, exact message);dex.auth, and with bearer auth;connectorswithoutDEX_API_KEYexits 2;agents/cli/typescript/core/ci.shwith the same dexcli and dex-server-zd: typecheck, conformance, and integration tests, 438 passed and 0 failed. The new tests intest/integ/remote-dex-server.integration.test.tsuse the same kind of fixture:sessionslists the session;DEX_CREDENTIALover plaintext loopback;DEX_API_KEY(exit 2);dex.auth(inlocal-tools.integration.test.ts) and with bearer auth;nothing the Worker host, its children, the launcher's JavaScript output, or its opener prints reaches the terminal UI's screenis flaky on main too, so this PR does not cause it.import noise laterevery 500 ms. After the handoff, the Worker host forwards each line as awarningnotice.open_urlrequest and its response, the fake terminal UI reports a mismatch. A variant also mismatches the launch document's notices.env -i HOME PATH DEXCLI DEX_SERVER_ZD_BINARY), the base commit of this branch failed 9 of 16 runs, and this branch failed 3 of 27. With the usual environment, it passed every run on this branch.ApiKeyExchangeError. A refused exchange surfaces asUNAUTHENTICATEDwith details that start withdex: token exchange, so the TypeScript core recognizes the exchange failure by that prefix.npm run typecheckpasses foragents/cli/typescript/builtinagainst the changed core.agents/cli/typescript/claude-sdkshows 8 type errors in its adapter runner types (ReauthorizingClaudeAgentRunner). None involves the CLI core, and this PR does not touch them.cd docs && npm run checkpasses.Not tested: a real
dexcli agents-tuisession connecting with a credential, which the launch document and environment tests only approach; thePERMISSION_DENIEDbranch; unreadable CA or token files; andDEX_TLS_SERVER_NAMEend to end. bearer file, no auth against an authenticated server, and auth withautohave conformance cases but no separate integration test.