Skip to content

fix: support runtime-managed OAuth providers (fix OAuth Codex due to Anthropic no longer supporting usage plans)#273

Merged
jalehman merged 9 commits into
Martian-Engineering:mainfrom
electricsheephq:fix/runtime-oauth-summary-expansion
Apr 5, 2026
Merged

fix: support runtime-managed OAuth providers (fix OAuth Codex due to Anthropic no longer supporting usage plans)#273
jalehman merged 9 commits into
Martian-Engineering:mainfrom
electricsheephq:fix/runtime-oauth-summary-expansion

Conversation

@100yenadmin

Copy link
Copy Markdown
Collaborator

Summary

This patch improves Lossless Claw's model-backed summarizer path so runtime-managed OAuth providers (especially openai-codex) can be used more cleanly for LCM summarization without assuming a direct static API key.

It also adds a configurable summaryTimeoutMs for slower/larger summarization loads.

What changed

  • add summaryTimeoutMs to LCM config + plugin schema
  • add isRuntimeManagedAuthProvider to injected LCM dependencies
  • teach the summarizer path to avoid direct API-key prefetch for runtime-managed OAuth providers
  • skip the direct-credential retry path for runtime-managed OAuth providers
  • keep provider/model resolution on the existing deps.complete(...) runtime path

Why

Lossless Claw already uses OpenClaw's runtime completion path (deps.complete) rather than manual gateway HTTP calls. The gap is in auth/credential handling for providers whose auth is runtime-managed OAuth rather than a normal static key.

This especially matters for:

  • summaryModel / summaryProvider
  • future/runtime use with openai-codex/gpt-5.4

Notes

This PR is intentionally surgical and focused on the summarizer path first.
Expansion-model support likely deserves the same treatment next / in follow-up if maintainers want that included here.

Validation

  • npm test
  • Result in local clone: 532 passed, 1 failed
  • Remaining failure was a timing-sensitive existing test timeout in test/plugin-config-registration.test.ts:
    • inherits OpenClaw's default model for summarization when no LCM model override is set
  • The failure was a timeout, not an assertion mismatch or compile/runtime break from the patch.

Related

Copilot AI review requested due to automatic review settings April 5, 2026 11:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates Lossless Claw’s model-backed summarizer flow to better support providers whose credentials are resolved at runtime via OAuth/auth-profiles (not static API keys), and adds a configurable per-call summarizer timeout.

Changes:

  • Add summaryTimeoutMs to LCM config resolution and expose it in openclaw.plugin.json.
  • Introduce deps.isRuntimeManagedAuthProvider and use it in the summarizer path to skip API-key prefetch + skip direct-credential retry for runtime-managed providers.
  • Wire summarizer logging to report the configured timeout value.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/types.ts Extends LcmDependencies with an optional runtime-managed-auth predicate.
src/summarize.ts Makes summarizer timeout configurable and adapts auth handling for runtime-managed providers.
src/plugin/index.ts Provides isRuntimeManagedAuthProvider implementation in plugin dependencies.
src/db/config.ts Adds summaryTimeoutMs to resolved LCM config (env + plugin config).
openclaw.plugin.json Exposes summaryTimeoutMs in UI hints + config schema.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/db/config.ts Outdated
delegationTimeoutMs: envDelegationTimeoutMs ?? toNumber(pc.delegationTimeoutMs) ?? 120000,
summaryTimeoutMs:
parseFiniteInt(env.LCM_SUMMARY_TIMEOUT_MS)
?? toNumber(pc.summaryTimeoutMs) ?? 120000,

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

summaryTimeoutMs now defaults to 120000 in resolveLcmConfig, but the summarizer’s documented default is 60_000 (DEFAULT_SUMMARIZER_TIMEOUT_MS). Because deps.config.summaryTimeoutMs will always be set, this silently changes the default summarizer timeout from 60s to 120s for users who don’t configure it, which can increase gateway stall risk. Consider defaulting this to 60_000 (or leaving it undefined unless configured) to preserve previous behavior.

Suggested change
?? toNumber(pc.summaryTimeoutMs) ?? 120000,
?? toNumber(pc.summaryTimeoutMs) ?? 60_000,

Copilot uses AI. Check for mistakes.
Comment thread src/summarize.ts Outdated
Comment on lines +1291 to +1294
const runtimeManagedAuth = params.deps.isRuntimeManagedAuthProvider?.(provider, providerApi) === true;
const apiKey = runtimeManagedAuth
? undefined
: await params.deps.getApiKey(provider, model, lookupOptions);

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

This new runtimeManagedAuth branch changes behavior by skipping getApiKey and passing apiKey: undefined into deps.complete. There are existing summarizer tests, but none cover the isRuntimeManagedAuthProvider === true path (e.g., openai-codex). Add a focused test asserting deps.getApiKey is not called and that the direct-credentials retry path is skipped for runtime-managed providers.

Copilot uses AI. Check for mistakes.
Comment thread openclaw.plugin.json
Comment on lines +87 to +90
"summaryTimeoutMs": {
"label": "Summary Timeout (ms)",
"help": "Maximum time to wait for a single model-backed LCM summarizer call before timing out"
},

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

New config/env surface area was added (summaryTimeoutMs / LCM_SUMMARY_TIMEOUT_MS). Repo convention appears to be to document user-facing config options in README alongside the plugin schema; please update the README configuration section (plugin config example + env var table) to include this new timeout so users can discover it.

Copilot uses AI. Check for mistakes.
Comment thread src/plugin/index.ts Outdated
Comment on lines +1176 to +1179
if (normalizedProvider === "openai-codex" || normalizedProvider === "github-copilot") {
return true;
}
return isCodexResponsesApi(providerApi);

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

isCodexResponsesApi is called here but doesn't appear to be defined or imported anywhere in the repo, which will break builds. Either implement this helper locally (e.g., normalize/compare providerApi against the codex responses API id) or replace it with an existing predicate (such as shouldOmitTemperatureForApi(providerApi) if that’s the intended check).

Copilot uses AI. Check for mistakes.
@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Correction / scope-restoration update:

I restored the actual runtime-managed OAuth behavior work so this PR is again about Codex OAuth support in the summarizer path, not just the timeout/docs add-on.

What's now in the branch again

  • runtime-managed auth providers can skip direct API-key prefetch in the summarizer path
  • runtime-managed auth providers skip the direct-credential retry branch
  • focused test coverage added for that behavior (openai-codex-style path)
  • summaryTimeoutMs remains as a secondary improvement
  • preserved the old default timeout behavior (60000ms) unless configured
  • README/schema/docs updated for the timeout setting

Additional fix included

The previously flaky registration test was updated with an explicit timeout so the suite no longer fails on the same 5s timing issue.

Validation

Re-ran the test suite after restoring the OAuth behavior patch and the focused tests.
The run output indicated the suite was back in a healthy state with the restored behavior patch plus the timeout-fix to the flaky registration test.

This should now better reflect the real goal of the PR: making runtime-managed OAuth-backed summarizer providers (especially openai-codex) actually work through Lossless Claw's existing deps.complete(...) path.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/plugin/index.ts
Comment on lines +1174 to +1180
isRuntimeManagedAuthProvider: (provider: string, providerApi?: string) => {
const normalizedProvider = normalizeProviderId(provider);
if (normalizedProvider === "openai-codex" || normalizedProvider === "github-copilot") {
return true;
}
return isCodexResponsesApi(providerApi);
},

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

isRuntimeManagedAuthProvider references isCodexResponsesApi(providerApi), but isCodexResponsesApi is not defined or imported in this module (search shows only this callsite). This will fail to compile at runtime/tsc. Define this helper (e.g., based on known Codex Responses API ids) or replace the call with an existing predicate in this file.

Copilot uses AI. Check for mistakes.
@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Fresh-pass root-cause update after adversarial testing:

What we now know with high confidence

The remaining issue is not that Lossless Claw needs to bypass the plugin bridge or call the gateway manually.

The plugin bridge in src/plugin/index.ts already has explicit openai-codex OAuth handling via:

  • resolveApiKeyFromAuthProfiles(...)
  • piAiModule.getOAuthApiKey(...)
  • existing tests in test/index-secret-ref-auth-profiles.test.ts

So the bridge contract for Codex OAuth is:

resolve OAuth-backed credentials into the effective token/key that completeSimple(...) expects

—not—

call completeSimple(...) with apiKey: undefined

Why this matters for the PR

The restored summarizer patch was directionally correct in trying to avoid the wrong fallback path for runtime-managed providers, but the first bridge-level interpretation was too blunt.

Adversarial bridge testing showed that a naive “skip credential resolution for runtime-managed providers” approach breaks existing modelAuth behavior and is not the right fix.

Correct patch direction from here

Keep

  • the existing plugin bridge OAuth resolution path
  • bridge/provider API preservation (openai-codex-responses)
  • timeout configurability (summaryTimeoutMs) as a secondary improvement

Change

  • the summarizer-layer auth retry / fallback behavior in src/summarize.ts
  • specifically, keep credential resolution intact, but avoid the wrong direct-fallback semantics when the provider is runtime-managed / OAuth-backed

Important scope clarification

Our earlier live logs proved that LCM/provider routing is path-sensitive and that OAuth/API paths behave very differently (MiniMax portal 404s vs API-key timeouts/successes), but those logs did not directly prove the Codex OAuth seam by themselves.

The Codex-specific diagnosis here comes from:

  • code inspection
  • existing bridge/auth tests
  • new adversarial tests added against this PR

Patch plan

  1. Do not bypass bridge credential resolution for openai-codex.
  2. Keep getApiKey(...) / bridge auth resolution available, because that is how the plugin bridge reaches the Codex OAuth helper path.
  3. Adjust src/summarize.ts retry semantics only so runtime-managed/OAuth-backed providers do not incorrectly take the “direct credential retry” path that assumes a separate static key fallback.
  4. Add/keep focused tests for:
    • summarizer runtime-managed auth behavior
    • existing modelAuth lookup-order behavior
    • bridge/provider config behavior
  5. Keep summaryTimeoutMs / LCM_SUMMARY_TIMEOUT_MS as the secondary improvement in this PR.

Practical implication

This PR should still be about Codex OAuth support, but the fix needs to be centered on the summarizer retry policy, not a broad plugin-bridge bypass.

@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Fresh update after the focused auth-seam debugging pass:

Root cause resolved more precisely

The correct contract is:

  • keep the plugin bridge's existing Codex OAuth credential resolution path intact
  • do not bypass bridge credential resolution for openai-codex
  • only suppress the wrong direct retry path in src/summarize.ts for runtime-managed/OAuth-backed providers

In other words:

  • first-pass credential resolution stays
  • second-stage skipModelAuth: true direct retry is what gets suppressed for runtime-managed providers

Why this is the right fix

The plugin bridge already has explicit openai-codex OAuth handling in resolveApiKeyFromAuthProfiles(...) / getOAuthApiKey(...).
Trying to skip credential resolution wholesale was the wrong move and broke existing modelAuth expectations.

The updated patch now preserves the bridge/auth resolution path and narrows the behavior change to the summarizer retry policy.

Focused validation

Ran the relevant focused suite:

npm test -- test/summarize.test.ts test/index-complete-model-auth.test.ts test/index-complete-provider-config.test.ts test/lcm-expand-query-tool.test.ts

Result:

  • 4 test files passed
  • 69 tests passed

This includes:

  • summarizer behavior tests
  • bridge/model-auth lookup-order tests
  • provider config bridge tests
  • expansion override/fallback tests

Practical status

This PR now much more accurately reflects the intended Codex OAuth support work:

  • preserve Codex OAuth credential resolution
  • avoid the wrong direct-retry fallback semantics in the summarizer path
  • keep timeout configurability as the secondary add-on

@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Additional follow-up: expansion override coverage has now been strengthened as well.

What was added

  • a new happy-path test for split Codex expansion overrides:
    • expansionProvider = openai-codex
    • expansionModel = gpt-5.4
  • this complements the existing coverage for:
    • canonical provider/model override forms
    • override auth-scope fallback
    • unauthorized override fallback

Focused validation

Ran:

npm test -- test/lcm-expand-query-tool.test.ts test/summarize.test.ts test/index-complete-model-auth.test.ts test/index-complete-provider-config.test.ts

Result:

  • 4 test files passed
  • 70 tests passed

So the focused matrix now covers:

  • summarizer auth behavior
  • bridge/model-auth lookup-order behavior
  • provider config bridge behavior
  • expansion override behavior including split openai-codex happy path

This makes the PR much closer to a full OAuth-support update for both:

  • compaction summarization
  • LCM_EXPANSION_MODEL / LCM_EXPANSION_PROVIDER delegated expansion overrides

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/summarize.ts
Comment on lines 1224 to 1229
const initialAuthError = new LcmProviderAuthError({ provider, model, failure });
const runtimeManagedAuth = params.deps.isRuntimeManagedAuthProvider?.(provider, providerApi) === true;
if (runtimeManagedAuth) {
throw initialAuthError;
}
console.warn(initialAuthError.message);

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

The new runtimeManagedAuth handling only skips the direct-credential retry path, but the initial call still performs deps.getApiKey(...) and passes that value through to deps.complete(...). If the intent is to avoid direct API-key prefetch for runtime-managed OAuth providers, consider bypassing the initial getApiKey lookup (and always passing apiKey: undefined) when isRuntimeManagedAuthProvider(provider, providerApi) is true.

Copilot uses AI. Check for mistakes.
Comment thread test/summarize.test.ts Outdated
Comment on lines +779 to +813
it("skips direct api-key lookup and direct-credential retry for runtime-managed auth providers", async () => {
const consoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
try {
const deps = makeDeps({
config: { summaryTimeoutMs: 60_000 },
resolveModel: vi.fn(() => ({
provider: "openai-codex",
model: "gpt-5.4",
})),
getApiKey: vi.fn(async () => "should-not-be-used"),
isRuntimeManagedAuthProvider: vi.fn(() => true),
complete: vi.fn(async () => ({
content: [],
error: {
kind: "provider_auth",
statusCode: 401,
message: "Missing required scope: model.request",
},
})),
});

const result = await createLcmSummarizeFromLegacyParams({
deps,
legacyParams: { provider: "openai-codex", model: "gpt-5.4" },
});

await expect(result!.fn("R".repeat(8_000), false)).rejects.toBeInstanceOf(
LcmProviderAuthError,
);
expect(vi.mocked(deps.getApiKey)).toHaveBeenCalledTimes(1);
expect(vi.mocked(deps.getApiKey)).toHaveBeenCalledWith("openai-codex", "gpt-5.4", {
profileId: undefined,
agentDir: undefined,
runtimeConfig: undefined,
});

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

This test name says it "skips direct api-key lookup", but it asserts deps.getApiKey is called once and the mock even returns a sentinel value. Either update the behavior under test to truly bypass API-key lookup for runtime-managed providers, or rename/adjust the assertions so the test matches the actual intended behavior (e.g. only verifying the direct-credential retry is skipped).

Copilot uses AI. Check for mistakes.
@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Final coverage pass update:

I re-read the configuration/docs and scanned the remaining model-calling surfaces to make sure we hadn't missed anything beyond:

  • compaction summarization (summaryModel / summaryProvider)
  • delegated expansion overrides (expansionModel / expansionProvider)

The one additional model-backed surface worth explicitly covering was:

  • large-file summarization
    • largeFileSummaryModel
    • largeFileSummaryProvider

That path reuses the same summarizer creation logic, but it did not have explicit Codex/OAuth-oriented coverage before.

Added

  • a focused engine test covering the Codex large-file summarization path

Focused validation rerun

npm test -- test/engine.test.ts test/summarize.test.ts test/lcm-expand-query-tool.test.ts test/index-complete-model-auth.test.ts test/index-complete-provider-config.test.ts

Result:

  • 5 test files passed
  • 185 tests passed

Net effect

The focused OAuth-support matrix now explicitly covers all of the meaningful model-calling surfaces I could find in the plugin:

  1. Compaction summarization
  2. Delegated expansion overrides
  3. Large-file summarization

That makes this PR much stronger as a full OAuth-support update rather than a narrow fix.

@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Follow-up on the review points around runtime-managed auth behavior:

Test wording cleanup

One review point correctly noted that the earlier test name implied “skip all API-key lookup,” while the actual implementation preserves first-pass credential resolution.

I pushed a follow-up rename so the test now reflects the intended contract more precisely:

  • preserve first-pass credential resolution
  • skip the direct-credential retry path for runtime-managed / OAuth-backed providers

Why we are not implementing “always pass apiKey: undefined”

After a focused debugging pass, I’m now highly confident the bridge contract here is:

  • the plugin bridge already resolves openai-codex OAuth via its auth-profile / OAuth-helper path
  • therefore the first-pass credential resolution must remain intact
  • the thing that needed fixing was the summarizer’s wrong direct-retry fallback semantics, not wholesale bypass of credential resolution

This conclusion is grounded in:

  • the existing resolveApiKeyFromAuthProfiles(...) + getOAuthApiKey(...) path in src/plugin/index.ts
  • the existing Codex OAuth auth-profile test coverage in test/index-secret-ref-auth-profiles.test.ts
  • the focused bridge/auth verification run below

Focused verification

npm test -- test/summarize.test.ts test/index-complete-model-auth.test.ts test/index-secret-ref-auth-profiles.test.ts test/index-complete-provider-config.test.ts

Result:

  • 4 files passed
  • 54 tests passed

So the current branch keeps the bridge’s Codex OAuth credential resolution intact while narrowing the behavior change to the summarizer retry policy.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/plugin/index.ts Outdated
Comment on lines +1174 to +1179
isRuntimeManagedAuthProvider: (provider: string, providerApi?: string) => {
const normalizedProvider = normalizeProviderId(provider);
if (normalizedProvider === "openai-codex" || normalizedProvider === "github-copilot") {
return true;
}
return isCodexResponsesApi(providerApi);

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

isRuntimeManagedAuthProvider calls isCodexResponsesApi(providerApi), but isCodexResponsesApi is not defined or imported anywhere in this module. If this function is invoked for providers other than the explicit openai-codex/github-copilot fast path, it will throw a ReferenceError at runtime.

Define this helper locally (or reuse an existing API-normalization predicate, e.g. comparing providerApi to openai-codex-responses) and/or import it from the appropriate module so the predicate is always safe to call.

Copilot uses AI. Check for mistakes.
@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Responding to the remaining review concern around provider scope:

This is a fair observation: the current special-case treatment is intentionally scoped to the runtime-managed provider family we can identify confidently in the current codebase (openai-codex, github-copilot, and the Codex-responses API family).

That scoping is deliberate.

Why the PR is targeted instead of fully generic

This PR is fixing a concrete compatibility gap that showed up when switching LCM summarization onto Codex OAuth. Anthropic already worked through the existing summarizer path because it follows a simpler/normal provider auth flow in this stack.

Codex is different in this codebase:

  • the plugin bridge already has explicit openai-codex OAuth handling
  • it resolves credentials via auth profiles / getOAuthApiKey(...)
  • that bridge contract is more specific than “OAuth provider” in the abstract

So the purpose of this PR is to make the summarizer logic cooperate correctly with that existing Codex OAuth bridge path, not to redefine all OAuth-provider semantics across the plugin in one shot.

Why we are not implementing “always pass apiKey: undefined”

I tested that direction during the debugging pass and it turned out to be the wrong abstraction for this codebase.

The bridge already expects to resolve Codex OAuth into the effective credential/token used by completeSimple(...). Bypassing first-pass credential resolution broke existing modelAuth expectations. The refined patch therefore keeps the bridge resolution path intact and only suppresses the wrong direct retry path in src/summarize.ts.

Verification behind this conclusion

Focused verification runs covered:

  • summarizer behavior
  • bridge/model-auth lookup-order behavior
  • provider config bridge behavior
  • Codex OAuth auth-profile helper behavior
  • expansion override behavior
  • large-file summarization path

Most relevant focused auth/bridge verification:

npm test -- test/summarize.test.ts test/index-complete-model-auth.test.ts test/index-secret-ref-auth-profiles.test.ts test/index-complete-provider-config.test.ts

Result:

  • 4 files passed
  • 54 tests passed

Expanded focused matrix:

npm test -- test/engine.test.ts test/summarize.test.ts test/lcm-expand-query-tool.test.ts test/index-complete-model-auth.test.ts test/index-complete-provider-config.test.ts

Result:

  • 5 files passed
  • 185 tests passed

Future direction

If maintainers want a generic abstraction for “runtime-managed OAuth providers”, I agree that would be useful — but it likely belongs in a follow-up change with a runtime/provider capability flag rather than more hardcoded provider-family heuristics.

For this PR, the narrower scope is intentional: fix the concrete Codex OAuth compatibility gap without broadening auth behavior in ways that could regress other providers.

@100yenadmin

Copy link
Copy Markdown
Collaborator Author

Final cleanup note:

I found and fixed one remaining legitimate review item in the branch before considering it ready for internal application:

  • isRuntimeManagedAuthProvider(...) was still referencing isCodexResponsesApi(...)
  • that helper was not defined in this module
  • the branch now uses the existing predicate helper already present in the file instead

Verification rerun

npm test -- test/summarize.test.ts test/index-complete-model-auth.test.ts test/index-secret-ref-auth-profiles.test.ts test/index-complete-provider-config.test.ts test/lcm-expand-query-tool.test.ts test/engine.test.ts

Result:

  • 6 test files passed
  • 188 tests passed

So at this point the legitimate review comments have been addressed, and the focused model/auth/expansion/large-file matrix is green.

@100yenadmin 100yenadmin changed the title fix: support runtime-managed OAuth summarizer providers fix: support runtime-managed OAuth providers (fix OAuth Codex due to Anthropic no longer supporting usage plans) Apr 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jalehman jalehman merged commit 40c90b1 into Martian-Engineering:main Apr 5, 2026
1 check passed
@jalehman

jalehman commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Thank you!

@github-actions github-actions Bot mentioned this pull request Apr 5, 2026
GodsBoy added a commit to GodsBoy/lossless-claw that referenced this pull request Apr 19, 2026
When the user has a ChatGPT Plus/Pro subscription (no raw OPENAI_API_KEY
but a populated ~/.codex/auth.json), route openai-codex summarization
through the local `codex` CLI so it can use its stored OAuth
credentials. Mirrors the existing Anthropic OAuth delegate pattern (PR
Martian-Engineering#58).

Changes in tui/repair.go:
- Hoist cliSummarizationSystemPrompt constant, reused by both CLI
  delegates.
- Add hasCodexOAuth() that probes ~/.codex/auth.json presence.
- Add summarizeViaCodexCLI() that runs `codex exec` with a restrictive
  sandbox (--sandbox read-only), --ephemeral, --output-last-message, and
  streams the prompt on stdin. The system directive is prepended to the
  stdin payload because codex has no --system-prompt flag.
- Add filteredOpenAIChildEnv() that strips every OPENAI_* env var from
  the child environment, preventing OPENAI_BASE_URL or proxy vars from
  re-routing OAuth traffic to an unintended endpoint.
- Gate in summarize(): allow an empty apiKey when provider is
  openai-codex and hasCodexOAuth() returns true.
- Branch in summarizeOpenAI(): delegate to summarizeViaCodexCLI() when
  the Codex OAuth path applies.
- resolveProviderAPIKey(): when Codex OAuth is available, return an
  empty string with nil error so the caller can route to the CLI
  delegate instead of failing hard. When OAuth is absent, the error now
  hints at `codex login`.

Tests in tui/codex_oauth_test.go cover:
- hasCodexOAuth: file absent, empty, present, directory.
- OAuth path delegates to CLI (HTTP transport never called).
- API-key path still uses direct HTTP (regression).
- Empty key without OAuth still errors.
- resolveProviderAPIKey returns empty sentinel under OAuth; hints at
  `codex login` without OAuth.
- CLI missing from PATH yields an actionable error.
- Non-zero exit surfaces stderr.
- Empty and oversized CLI output are rejected.
- filteredOpenAIChildEnv removes every OPENAI_* var and preserves
  unrelated env.

Closes Martian-Engineering#469

Refs Martian-Engineering#58 (Anthropic delegate pattern), Martian-Engineering#273 (plugin-side Codex OAuth).
jalehman added a commit that referenced this pull request Apr 23, 2026
* feat(tui): support Codex OAuth via codex CLI delegate

When the user has a ChatGPT Plus/Pro subscription (no raw OPENAI_API_KEY
but a populated ~/.codex/auth.json), route openai-codex summarization
through the local `codex` CLI so it can use its stored OAuth
credentials. Mirrors the existing Anthropic OAuth delegate pattern (PR
#58).

Changes in tui/repair.go:
- Hoist cliSummarizationSystemPrompt constant, reused by both CLI
  delegates.
- Add hasCodexOAuth() that probes ~/.codex/auth.json presence.
- Add summarizeViaCodexCLI() that runs `codex exec` with a restrictive
  sandbox (--sandbox read-only), --ephemeral, --output-last-message, and
  streams the prompt on stdin. The system directive is prepended to the
  stdin payload because codex has no --system-prompt flag.
- Add filteredOpenAIChildEnv() that strips every OPENAI_* env var from
  the child environment, preventing OPENAI_BASE_URL or proxy vars from
  re-routing OAuth traffic to an unintended endpoint.
- Gate in summarize(): allow an empty apiKey when provider is
  openai-codex and hasCodexOAuth() returns true.
- Branch in summarizeOpenAI(): delegate to summarizeViaCodexCLI() when
  the Codex OAuth path applies.
- resolveProviderAPIKey(): when Codex OAuth is available, return an
  empty string with nil error so the caller can route to the CLI
  delegate instead of failing hard. When OAuth is absent, the error now
  hints at `codex login`.

Tests in tui/codex_oauth_test.go cover:
- hasCodexOAuth: file absent, empty, present, directory.
- OAuth path delegates to CLI (HTTP transport never called).
- API-key path still uses direct HTTP (regression).
- Empty key without OAuth still errors.
- resolveProviderAPIKey returns empty sentinel under OAuth; hints at
  `codex login` without OAuth.
- CLI missing from PATH yields an actionable error.
- Non-zero exit surfaces stderr.
- Empty and oversized CLI output are rejected.
- filteredOpenAIChildEnv removes every OPENAI_* var and preserves
  unrelated env.

Closes #469

Refs #58 (Anthropic delegate pattern), #273 (plugin-side Codex OAuth).

* docs: clarify tui codex oauth usage

---------

Co-authored-by: Josh Lehman <josh@martian.engineering>
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.

3 participants