Skip to content

feat: expose safe model run failures#243

Merged
roackb2 merged 1 commit into
mainfrom
codex/model-failure-classification
Jul 12, 2026
Merged

feat: expose safe model run failures#243
roackb2 merged 1 commit into
mainfrom
codex/model-failure-classification

Conversation

@roackb2

@roackb2 roackb2 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

What changed

  • classify model authentication, permission, rate-limit, request, transport, empty-response, and unknown failures in the existing model-turn retry service
  • propagate the optional safe RunFailure through RunResult, terminal traces, loop state/activity/checkpoints, and programmatic conversation turn results
  • replace raw provider diagnostics with stable safe messages before logs, retry traces, or final summaries
  • teach the hosted-agent example and programmatic docs to project result.failure instead of parsing provider text

Why

A hosted BYOK product needs to distinguish a rejected user credential from other model failures without duplicating provider HTTP/status logic. The existing Heddle model-turn boundary already owns retry classification, so this extends that service rather than adding a parallel classifier.

This also closes a secret-safety gap: arbitrary provider error text no longer flows into durable traces, logs, or product-facing summaries.

Impact

The new failure field is optional and backward-compatible. Existing hosts keep their current result flow; hosts that need stable product errors can branch on result.failure?.code. Human-readable model error summaries are intentionally safer and less provider-specific.

Verification

  • yarn test: 114 unit files / 674 tests; 32 integration files / 293 tests
  • yarn eslint
  • npm pack --silent --pack-destination /private/tmp (full build, remote-package compile, web build, hosted React example build)
  • unpublished tarball dogfooded by the SlideX server: 37 tests, both typechecks, and production build

@roackb2 roackb2 marked this pull request as ready for review July 12, 2026 12:14

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5116cc3f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const status = AgentModelTurnRetryService.readStatusCode(error);
const failureCode = status === undefined ? 'unknown' : MODEL_FAILURE_BY_STATUS.get(status) ?? 'unknown';
const failure = AgentModelTurnRetryService.modelFailure(failureCode);
const message = AgentModelTurnRetryService.safeMessage(failureCode);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve context-overflow signals for compaction

When a provider rejects an oversized prompt/context with HTTP 400/422, this replaces the provider error text with Model request was rejected before chat persistence sees the result. I checked ConversationTurnArtifacts.build: it drives shouldForceCompactionAfterFailure(args.result.summary) and failure hint formatting from args.result.summary, looking for context-window/prompt-too-long phrases. After this change those phrases are gone, so long sessions no longer force compaction or show the recovery hint, and retrying the same conversation can keep failing.

Useful? React with 👍 / 👎.

const message = AgentModelTurnRetryService.formatErrorMessage(error);
const providerMessage = AgentModelTurnRetryService.formatErrorMessage(error);
const status = AgentModelTurnRetryService.readStatusCode(error);
const failureCode = status === undefined ? 'unknown' : MODEL_FAILURE_BY_STATUS.get(status) ?? 'unknown';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Carry HTTP status from OpenAI-compatible failures

When using the built-in OpenAI-compatible providers such as Hugging Face, OpenRouter, Together, or Groq, OpenAiCompatibleAdapter.readJsonResponse throws a plain Error whose message contains response.status but has no status/statusCode property. This line therefore classifies those 401/403/429 responses as unknown (and some 429s may not retry unless the body happens to say rate limit), so hosted BYOK callers relying on result.failure.code cannot distinguish rejected credentials or rate limits for these supported providers.

Useful? React with 👍 / 👎.

@roackb2 roackb2 merged commit acd3f2d into main Jul 12, 2026
5 checks passed
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.

1 participant