Skip to content

fix(tui): resolve the /speak OpenAI key through the secret chain - #4709

Merged
dgageot merged 3 commits into
feat/docker-agent-env-prefixfrom
fix/tui-transcriber-secret-chain
Oct 23, 2026
Merged

dgageot merged 3 commits into
feat/docker-agent-env-prefixfrom
fix/tui-transcriber-secret-chain

Conversation

@aheritier

@aheritier aheritier commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

/speak read OPENAI_API_KEY with os.Getenv (pkg/tui/tui.go), so it ignored every other secret source: op:// 1Password references, --env-from-file, the docker-agent env file (~/.config/cagent/.env, where docker agent setup stores keys), run secrets and Docker Desktop. Chat worked with a key that voice input could not see.

Changes

  • transcribe.New(env environment.Provider) replaces New(apiKey string). Run reads OPENAI_API_KEY from env when /speak starts.
  • The key is never resolved at TUI construction, so launching the TUI cannot trigger an op unlock prompt or a Docker Desktop query when voice input is not used.
  • tui.WithEnvProvider option; docker agent run passes its runtime env provider (so --env-from-file applies). Other callers default to environment.NewDefaultProvider().

Stacked on #4708, so /speak also honours DOCKER_AGENT_ENV_OPENAI_API_KEY, and the secrets guide caveat that #4708 adds ("voice transcription still reads OPENAI_API_KEY directly") is removed here, landing together with the fix.

Tests

  • TestRun_ResolvesKeyLazily: New makes no lookup; Run makes exactly one.
  • Existing transcriber tests now inject the key through a MapEnvProvider.

References

Topic Link
Secret chain pkg/environment/default.go
Runtime env provider (--env-from-file) pkg/config/runtime.go
Secrets guide docs/guides/secrets/index.md
Related #4702, #4708

This is part 2 of 2 in a stack made with GitButler:

Copilot AI balanced review requested due to automatic review settings October 22, 2026 18:53
@aheritier
aheritier requested a review from a team as a code owner October 22, 2026 18:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Initialization overrides injected transcribers, and secret lookup does not preserve finish and cancellation behavior.

2 open issues
What changed in this PR

Routes /speak authentication through the existing secret-provider chain so voice input can use the same credential sources as chat, without resolving secrets at TUI startup.

Changes:

  • Resolve OPENAI_API_KEY lazily when transcription starts.
  • Pass the run command’s environment provider into the TUI, with a default for other callers.
  • Update transcriber tests for provider injection and verify lazy lookup.
File Description
pkg/​tui/​tui.go Adds environment-provider configuration and initializes the transcriber after options.
pkg/​audio/​transcribe/​transcribe.go Resolves the OpenAI key through a provider on each run.
pkg/​audio/​transcribe/​transcribe_test.go Updates key injection and tests lazy resolution.
pkg/​audio/​transcribe/​finish_test.go Adapts finish tests to provider-based construction.
cmd/​root/​run.go Supplies the runtime environment provider to the TUI.

Review effort: Balanced

Comment thread pkg/audio/transcribe/transcribe.go Outdated
Comment on lines 83 to 85
apiKey, _ := t.env.Get(ctx, "OPENAI_API_KEY")
if apiKey == "" {
return errors.New("/speak sends audio directly to OpenAI; set a separate OPENAI_API_KEY environment variable even when chat uses Docker's models gateway, which does not support /speak")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Right, fixed in 8859213. The lookup now runs after the finish watcher and deferred cleanup are set up, on captureCtx, so finishing during an op unlock prompt stops it (and starts the finish timeout), and an interrupted lookup returns nil instead of the missing-key error. Tests: TestRun_FinishInterruptsKeyLookup and TestRun_CancelDuringKeyLookupIsNotAnError, both with a provider that blocks like op read.

Comment thread pkg/tui/tui.go Outdated
if m.envProvider == nil {
m.envProvider = environment.NewDefaultProvider()
}
m.transcriber = transcribe.New(m.envProvider)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, a regression from moving construction after the options. Fixed in 8859213: the default transcriber is only built when none was injected. Test: TestNew_KeepsInjectedTranscriber.

@aheritier aheritier added area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only. labels Oct 22, 2026
@aheritier
aheritier force-pushed the fix/tui-transcriber-secret-chain branch from 572bd0a to 16f136b Compare October 22, 2026 19:02
@aheritier
aheritier changed the base branch from main to feat/docker-agent-env-prefix October 22, 2026 19:02
/speak read OPENAI_API_KEY with os.Getenv, so it ignored op:// references,
--env-from-file, the docker-agent env file and every other source of the
chain. The transcriber now takes an environment.Provider and reads the key
when /speak starts, never at TUI construction, so launching the TUI cannot
trigger an op unlock prompt. run passes its runtime env provider; other
callers default to environment.NewDefaultProvider().

Signed-off-by: Arnaud Héritier <aheritier@apache.org>
Signed-off-by: Arnaud Héritier <aheritier@apache.org>
@aheritier
aheritier force-pushed the fix/tui-transcriber-secret-chain branch from 16f136b to c7e920c Compare October 22, 2026 19:03
… lookup

- New built the default transcriber after the options, replacing one
  passed with WithTranscriber; it now only fills in a missing one.
- The key lookup ran before the finish watcher and on the parent context,
  so finishing during an op unlock prompt hung, and cancelling reported a
  missing key. It now runs on the capture context after the watcher is set
  up, and an interrupted lookup ends the run quietly.

Signed-off-by: Arnaud Héritier <aheritier@apache.org>
@aheritier
aheritier added this pull request to stack #4711 October 22, 2026 19:29
@dgageot
dgageot added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit 223f940 Oct 23, 2026
22 checks passed
@dgageot
dgageot deleted the fix/tui-transcriber-secret-chain branch October 23, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants