Skip to content

fix: provider usage accounting, retry/overflow classification, and output clamping#109796

Merged
steipete merged 5 commits into
mainfrom
fix/provider-retry-overflow-hardening
Jul 17, 2026
Merged

fix: provider usage accounting, retry/overflow classification, and output clamping#109796
steipete merged 5 commits into
mainfrom
fix/provider-retry-overflow-hardening

Conversation

@steipete

@steipete steipete commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Provider-boundary correctness for Anthropic and Mistral plus shared retry/overflow hardening:

  • Anthropic streams crashed with a TypeError when a proxy omitted usage from message_delta events.
  • One-hour prompt-cache writes were billed internally at the five-minute rate (1.25x input) even though the API charges 2x — cost reporting under-counted whenever long cache retention was active. Usage now carries the 1h bucket separately and prices it correctly, keeping the aggregate cacheWrite field intact.
  • Anthropic-compatible backends that emit and accept empty thinking signatures lost their thinking blocks on replay; an opt-in allowEmptySignature compat flag preserves them (default unchanged).
  • Mistral prompt caching was never engaged or accounted: requests now send the prompt-cache key and cached-token usage is parsed into usage.cacheRead.
  • The Anthropic SDK's hidden default retries (2) stacked under the app's own retry policy; the client now defaults to 0 when unset.
  • Retry classification missed HTTP 524 (Cloudflare origin timeout), abrupt socket-close failures, and camel-case ResourceExhausted — all transient, all previously fatal.
  • Context-overflow classification missed parenthesized token-count messages ("maximum context length (262144)") and "maximum allowed input length" phrasings — those errors skipped compaction recovery and stuck sessions.
  • Simple-stream requests with a max-token budget near a full context drew provider validation 400s that the overflow classifier cannot recognize; output budgets are now clamped against a conservative input estimate so requests survive and real overflows stay classifiable.

Why This Change Was Made

These are correctness gaps users hit as crashed streams, silently wrong cost numbers, stuck sessions after context overflow, and hung retries. The classifiers stay purely classificatory; policy remains with the caller.

User Impact

Anthropic streams tolerate lean proxies; cost reporting is accurate under 1h cache retention; Mistral costs drop via real prompt caching; transient CF/socket/gRPC failures retry instead of failing hard; overflow recovery triggers reliably; near-full-context requests degrade gracefully instead of erroring.

Evidence

  • 163 focused unit tests across 7 files (usage buckets, cost math, compat replay, clamp math, table-driven retry/overflow classifiers).
  • Live verification (real API keys): node scripts/test-live.mjs -- packages/ai/src/providers/anthropic.live.test.ts packages/ai/src/providers/mistral.live.test.ts — 4/4 passing: Anthropic stream with usage, 1h cache-retention request parsing cache_creation buckets with correct cost accounting, thinking-signature survival, and a Mistral stream with cache-field parsing.
  • Full changed-file gate green on hosted runner (baselines, three typecheck lanes, lint, boundaries): run 29563721450.
  • Plugin-SDK API baseline regenerated via the sanctioned workflow for the additive Usage.cacheWrite1h / allowEmptySignature contracts; pnpm plugin-sdk:api:check green post-rebase.

Additional live probes (real API keys): overflow classification proven against providers' current error strings — a deliberately context-exceeding request to Anthropic and to OpenAI Responses each classify as overflow via isContextOverflow (guarding compaction recovery against provider error-string drift), and the new output-clamp path completes successfully on a near-full context instead of a validation 400. Anthropic+Mistral 6/6, OpenAI Responses 4/4 keyed runs.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: L maintainer Maintainer-authored PR labels Jul 17, 2026

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d814961c61

ℹ️ 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".

Comment thread packages/ai/src/providers/anthropic.ts Outdated
Comment on lines +966 to +967
const maxTokens = clampMaxTokensToContext(model, context, adjusted.maxTokens);
const thinkingBudget = clampThinkingBudgetToMaxTokens(maxTokens, adjusted.thinkingBudget);

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.

P2 Badge Preserve answer budget after context clamping

When a budget-based Anthropic model is near the context limit, clampMaxTokensToContext can reduce max_tokens below the previously adjusted thinking budget plus the helper's 1024-token answer reserve; this line then clamps the thinking budget to max_tokens - 1 (for example 1500/1499), leaving only one token for the actual reply. The Anthropic SDK contract for thinking.budget_tokens says it counts toward max_tokens, so these requests can spend the entire output budget on thinking and return a max_tokens stop instead of a usable answer; reapply the existing min-output-budget logic after the context clamp or disable/reduce thinking in this case.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the fix/provider-retry-overflow-hardening branch from d814961 to 8e955eb Compare July 17, 2026 08:23

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e955ebf59

ℹ️ 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".

Comment on lines +518 to +519
/** Whether empty thinking signatures can be replayed as native thinking blocks. Default: false. */
allowEmptySignature?: boolean;

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.

P2 Badge Propagate allowEmptySignature through model config

When this new compat knob is needed by a configured or catalog Anthropic-compatible model, it cannot actually be set: the config type allowlist only picks supportsEagerToolInputStreaming/supportsLongCacheRetention, the runtime TypeBox compat schema omits it, and the model-catalog normalizer drops unknown boolean fields. As a result normal model loading rejects or strips compat.allowEmptySignature, so getAnthropicCompat() always falls back to false outside tests that construct Model objects directly, and empty-signature replay still gets converted to text for those providers.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the fix/provider-retry-overflow-hardening branch from 8e955eb to 43d701f Compare July 17, 2026 08:52

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 43d701faa8

ℹ️ 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".

textTokens +=
textTokenUpperBound(tool.name) +
textTokenUpperBound(tool.description) +
serializedTokenUpperBound(tool.parameters);

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.

P2 Badge Guard raw tool schema reads during context estimates

When streamSimpleAnthropic/streamSimpleMistral pass the context into buildBaseOptions, this estimator now runs before the provider's existing tool projection/quarantine. If a direct/custom tool has an unreadable parameters getter (a case the provider tests already preserve by skipping the bad tool and keeping healthy siblings), this property read throws synchronously, so simple streams fail before streamAnthropic/streamMistral can apply that safe fallback. Snapshot descriptor fields under the same guarded path, or reuse the provider projection rather than reading raw tool descriptors here.

Useful? React with 👍 / 👎.

}

function textTokenUpperBound(value: string): number {
return UTF8_ENCODER.encode(value).byteLength;

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.

P2 Badge Avoid clamping token budgets from byte counts

This byte length is subtracted from model.contextWindow, which is measured in tokens. For large ASCII/code prompts the byte count can be several times the real tokenizer count, so a prompt like ~130k ASCII characters on a 128k-token model can still be well under the model window but gets clamped to the 1-token floor, yielding effectively empty replies instead of preventing only true provider validation overflows. Use a less destructive estimate/fallback, or reserve hard clamping for actual token counts.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 17, 2026, 7:21 AM ET / 11:21 UTC.

Summary
The PR changes Anthropic and Mistral cache and usage accounting, Anthropic replay and retry behavior, shared retry and overflow classification, and simple-stream output-budget clamping.

PR surface: Source +166, Tests +610, Generated 0. Total +776 across 18 files.

Reproducibility: yes. at source level: deterministic fixtures exercise omitted Anthropic usage, retry and overflow strings, and cache accounting. The claimed real-provider runs are not yet inspectable enough to verify the complete current-head runtime path.

Review metrics: 2 noteworthy metrics.

  • Public contract additions: 2 added. Usage.cacheWrite1h and AnthropicMessagesCompat.allowEmptySignature must remain aligned across exported types, loaders, transports, protocols, and plugin SDK baselines.
  • Retry default behavior: 1 changed. An omitted Anthropic request retry value now becomes zero instead of retaining the SDK's documented default of two attempts.

Stored data model
Persistent data-model change detected: persistent cache schema: packages/ai/src/providers/anthropic-usage.test.ts, persistent cache schema: packages/ai/src/providers/anthropic.live.test.ts, persistent cache schema: packages/ai/src/providers/mistral.live.test.ts, persistent cache schema: packages/ai/src/providers/mistral.test.ts, serialized state: packages/ai/src/providers/simple-options.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Resolve all six findings across provider, model-loading, transport, and output-estimation paths.
  • Rebase onto current main and refresh the review against the resulting mergeable head.
  • Attach redacted live terminal output or logs that demonstrate the changed runtime behavior.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The body claims successful live-key runs but provides no inspectable redacted terminal output, logs, recording, or linked artifact; attach after-fix evidence covering retries, transport accounting, and near-context output, redact secrets and private endpoints, update the PR body, and ask a maintainer to comment @clawsweeper re-review if automation does not rerun.

Risk before merge

  • [P1] The branch is conflict-dirty against current main, whose provider architecture has continued moving, so the eventual merge result needs a fresh whole-boundary review after rebase.
  • [P1] Changing an omitted Anthropic retry value from the SDK default to zero can alter behavior for existing direct callers unless maintainers explicitly accept or relocate that policy.
  • [P1] The claimed live validation is not independently inspectable, especially for outer retries, boundary-aware transport behavior, and near-context output quality.

Maintainer options:

  1. Repair and rebase the complete boundary (recommended)
    Resolve the six findings across provider, loader, transport, and estimation paths, rebase onto current main, and rerun review with inspectable live proof.
  2. Accept the retry API change
    Maintainers may intentionally choose zero as the new unset retry behavior, but must explicitly own the compatibility change and require direct-caller upgrade proof.
  3. Split the patch
    Pause this broad branch and resubmit narrow provider-accounting, classifier, and output-budget changes independently if rebasing cannot preserve a reviewable boundary.

Next step before merge

  • [P1] Contributor repair is required, but the protected maintainer label, dirty merge state, and unresolved public retry-contract choice require human handling rather than automated fix dispatch.

Maintainer decision needed

  • Question: Should omitting maxRetries from the public Anthropic stream API preserve the SDK's existing default, or should OpenClaw redefine omission to mean zero retries?
  • Rationale: Both behaviors are technically possible, but choosing between public-call compatibility and centralized outer-retry policy changes existing caller semantics and requires maintainer intent.
  • Likely owner: steipete — The LLM package-boundary history and current provider work make this the strongest available owner for the public retry-contract choice.
  • Options:
    • Preserve omission semantics (recommended): Leave the provider API's unset value untouched and pass zero explicitly only from OpenClaw call sites that already own an outer retry loop.
    • Redefine the provider default: Keep unconditional zero retries, but treat it as an intentional compatibility change with direct-caller tests and upgrade documentation.

Security
Cleared: The provider, shared type, generated baseline, and test changes introduce no concrete credential, permission, dependency-source, workflow-execution, or supply-chain regression.

Review findings

  • [P1] Preserve the unset Anthropic retry contract — packages/ai/src/providers/anthropic.ts:458
  • [P1] Do not clamp token budgets from UTF-8 byte counts — packages/ai/src/providers/simple-options.ts:52
  • [P2] Preserve tool quarantine before estimating context — packages/ai/src/providers/simple-options.ts:71
Review details

Best possible solution:

Rebase onto current main, keep retry policy at the app-owned outer-retry boundary unless maintainers explicitly approve a public default change, replace byte-based hard clamping with a token-aware or non-destructive guard, and propagate replay and usage invariants through model loading and every Anthropic transport before attaching redacted live output.

Do we have a high-confidence way to reproduce the issue?

Yes at source level: deterministic fixtures exercise omitted Anthropic usage, retry and overflow strings, and cache accounting. The claimed real-provider runs are not yet inspectable enough to verify the complete current-head runtime path.

Is this the best way to solve the issue?

No. The classifier and accounting changes are plausible, but the patch is currently too one-sided across public API, model loading, boundary-aware transport, and output-budget estimation to be the best maintainable fix.

Full review comments:

  • [P1] Preserve the unset Anthropic retry contract — packages/ai/src/providers/anthropic.ts:458
    Setting maxRetries to zero when the option is omitted changes the public stream behavior from the SDK's documented two-retry default. Keep omission intact and pass zero from callers that own an outer retry loop, unless maintainers explicitly approve the compatibility change. (raw.githubusercontent.com)
    Confidence: 0.97
  • [P1] Do not clamp token budgets from UTF-8 byte counts — packages/ai/src/providers/simple-options.ts:52
    This subtracts byte length from a context window measured in tokens, so large ASCII or code prompts that still fit can be forced to the one-token output floor. Use actual token counts, a calibrated estimate, or a fallback that does not destructively clamp uncertain inputs.
    Confidence: 0.99
  • [P2] Preserve tool quarantine before estimating context — packages/ai/src/providers/simple-options.ts:71
    The new estimator reads raw tool descriptors before Anthropic and Mistral apply their guarded tool projection. A throwing parameters getter now aborts the stream instead of quarantining only the malformed tool; estimate from the safe projection or guard descriptor reads identically.
    Confidence: 0.96
  • [P2] Expose allowEmptySignature through model loading — packages/llm-core/src/types.ts:516
    Adding the compatibility field only to the shared model type does not make it usable by configured or catalog models when the config schema and normalizer still reject or strip it. Thread the field through every supported model-loading surface and add a configured-model regression test.
    Confidence: 0.97
  • [P2] Mirror the omitted-usage guard in the transport stream — packages/ai/src/providers/anthropic.ts:540
    The boundary-aware Anthropic transport still treats a message_delta without usage as unavailable and discards the valid message_start context snapshot. Apply the same omission guard there so embedded and proxied agent runs retain context accounting.
    Confidence: 0.96
  • [P2] Populate one-hour cache writes on the transport path — packages/ai/src/providers/anthropic.ts:505
    The sibling Anthropic transport still records only aggregate cache-creation tokens, so long-retention requests do not populate cacheWrite1h and remain priced at the five-minute rate. Share the cache-bucket parser with that runtime path and cover its cost projection.
    Confidence: 0.97

Overall correctness: patch is incorrect
Overall confidence: 0.97

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ecfcaa07e6d4.

Label changes

Label justifications:

  • P1: The PR addresses provider crashes and stuck overflow recovery, but its current head can also break existing retry behavior or reduce valid responses to effectively empty output.
  • merge-risk: 🚨 compatibility: The patch changes an established retry default and adds public usage and model-compatibility fields without complete loader and sibling-boundary propagation.
  • merge-risk: 🚨 availability: The byte-based context clamp and incomplete sibling transport handling can cause provider requests to fail or return unusably small responses.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The body claims successful live-key runs but provides no inspectable redacted terminal output, logs, recording, or linked artifact; attach after-fix evidence covering retries, transport accounting, and near-context output, redact secrets and private endpoints, update the PR body, and ask a maintainer to comment @clawsweeper re-review if automation does not rerun.
Evidence reviewed

PR surface:

Source +166, Tests +610, Generated 0. Total +776 across 18 files.

View PR surface stats
Area Files Added Removed Net
Source 8 246 80 +166
Tests 9 613 3 +610
Docs 0 0 0 0
Config 0 0 0 0
Generated 1 2 2 0
Other 0 0 0 0
Total 18 861 85 +776

What I checked:

  • Re-review continuity: The current head is identical to the latest completed ClawSweeper review head, so its six unresolved findings remain applicable and are repeated rather than silently dropped. (60405efce9bf)
  • Anthropic retry contract: The SDK documents two retries as the default when maxRetries is omitted, while this patch changes omission to an explicit zero; that is a compatibility decision rather than a transparent implementation detail. (raw.githubusercontent.com) (packages/ai/src/providers/anthropic.ts:458, 60405efce9bf)
  • Sibling transport path: Current main maintains a separate native Anthropic transport with its own message conversion and usage shape, confirming that provider-only usage and replay changes require explicit sibling-path handling. (raw.githubusercontent.com) (src/agents/anthropic-transport-stream.ts:3, ecfcaa07e6d4)
  • Mistral dependency check: Mistral documents cached tokens at usage.prompt_tokens_details.cached_tokens; the generated SDK keeps unknown response keys through its catch-all object mapping, so the patch's direct snake-case read is compatible and the latest SDK-extras bot comment is not promoted to a finding. (raw.githubusercontent.com) (packages/ai/src/providers/mistral.ts:390, 60405efce9bf)
  • Feature history: The relevant history includes the LLM-core extraction in aa0d6e1bca55a662450ee69beefc0523d296349f, Mistral prompt-cache work in 1824aa07a0d59f63b42a8b7519b2c1cbd74ac348, and structured provider-error work in f80f47219091633656e5b54663d5ebd0332ef18f. (github.com) (packages/llm-core/src/types.ts:268, aa0d6e1bca55)
  • Real behavior proof: The PR body reports live-key test counts but does not include redacted terminal output, logs, a recording, or a linked artifact that reviewers can inspect against the changed runtime paths. (60405efce9bf)

Likely related people:

  • steipete: Authored the LLM-core extraction and has continuing current-main history around provider and shared type boundaries. (github.com) (role: major LLM package refactor author; confidence: high; commits: aa0d6e1bca55, 4c33aaa; files: packages/llm-core/src/types.ts, packages/ai/src/providers/anthropic.ts)
  • Alix-007: Authored the existing Mistral prompt-cache-key implementation, making them directly relevant to the caching and accounting changes. (github.com) (role: Mistral caching contributor; confidence: high; commits: 1824aa07a0d5; files: packages/ai/src/providers/mistral.ts, packages/llm-core/src/types.ts)
  • joshavant: Recently changed structured provider-error classification on the shared LLM contract surface. (github.com) (role: recent provider error-classification contributor; confidence: medium; commits: f80f47219091; files: packages/llm-core/src/types.ts, src/llm/utils/retry.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-17T09:13:23.872Z sha 274425b :: needs real behavior proof before merge. :: [P2] Expose allowEmptySignature through model loading | [P1] Do not clamp token budgets from UTF-8 byte counts | [P2] Preserve tool quarantine before estimating context
  • reviewed 2026-07-17T09:27:02.144Z sha 8723179 :: needs real behavior proof before merge. :: [P1] Preserve the unset Anthropic retry contract | [P1] Do not clamp token budgets from UTF-8 byte counts | [P2] Preserve tool quarantine before estimating context | [P2] Expose allowEmptySignature through model loading
  • reviewed 2026-07-17T10:17:03.155Z sha 5b917ff :: needs real behavior proof before merge. :: [P1] Preserve the unset Anthropic retry contract | [P1] Do not clamp token budgets from UTF-8 byte counts | [P2] Preserve tool quarantine before estimating context | [P2] Expose allowEmptySignature through model loading | [P2] Mirror the omitted-usage guard in the transport stream | [P2] Populate one-hour cache writes on the transport path
  • reviewed 2026-07-17T10:38:50.805Z sha 60405ef :: needs real behavior proof before merge. :: [P1] Preserve the unset Anthropic retry contract | [P1] Do not clamp token budgets from UTF-8 byte counts | [P2] Preserve tool quarantine before estimating context | [P2] Expose allowEmptySignature through model loading | [P2] Mirror the omitted-usage guard in the transport stream | [P2] Populate one-hour cache writes on the transport path

@steipete
steipete force-pushed the fix/provider-retry-overflow-hardening branch from 274425b to 8723179 Compare July 17, 2026 09:21
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 17, 2026

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06d49522d9

ℹ️ 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".

};
} else {
output.usage.contextUsage = { state: "unavailable" };
if (event.usage) {

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.

P2 Badge Mirror usage-delta guard in transport stream

When an Anthropic model runs through the boundary-aware transport (src/agents/provider-transport-stream.ts selects src/agents/anthropic-transport-stream.ts for embedded/proxied/transported anthropic-messages), that sibling stream still handles message_delta without this event.usage guard: an omitted usage object falls through to contextUsage = { state: "unavailable" }, discarding the valid message_start prompt snapshot. The package provider test added here passes, but production agent runs on that transport path can still lose context accounting and overflow-compaction signals; mirror the guard there too.

Useful? React with 👍 / 👎.

Comment on lines +505 to +507
const cacheWriteUsage = readAnthropicCacheWriteUsage(event.message.usage);
if (cacheWriteUsage.cacheWrite1h !== undefined) {
output.usage.cacheWrite1h = cacheWriteUsage.cacheWrite1h;

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.

P2 Badge Populate 1-hour cache writes on the transport path

With long Anthropic prompt caching on the boundary-aware transport path, the sibling stream still only copies cache_creation_input_tokens and never calls readAnthropicCacheWriteUsage, so usage.cacheWrite1h stays undefined. Because the new cost calculation relies on that field to price 1-hour writes at input * 2, embedded/proxied Anthropic runs continue to bill all cache writes at the 5-minute rate while the package provider reports a different total.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the fix/provider-retry-overflow-hardening branch from 06d4952 to 5b917ff Compare July 17, 2026 10:13

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b917ff5a8

ℹ️ 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".

Comment on lines +271 to +272
/** Subset of `cacheWrite` written with 1-hour retention when reported. */
cacheWrite1h?: number;

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.

P2 Badge Carry cacheWrite1h through worker protocol

When an Anthropic response with 1-hour cache writes is replayed or returned over worker inference, this new Usage field is not accepted by the closed worker usage schemas in packages/gateway-protocol/src/schema/worker-inference.ts and worker-admission.ts, and the terminal projection in src/gateway/worker-environments/inference-terminal-message.ts also drops it. That means a transcript containing usage.cacheWrite1h can be rejected as an invalid worker context/terminal outcome, or the new billing bucket disappears on the worker path even though the package provider emits it.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the fix/provider-retry-overflow-hardening branch from 5b917ff to 60405ef Compare July 17, 2026 10:34
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 17, 2026

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60405efce9

ℹ️ 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".

Comment on lines +397 to +401
const rawCachedTokens =
record.promptTokensDetails?.cachedTokens ??
record.prompt_tokens_details?.cached_tokens ??
record.cachedTokens ??
record.cached_tokens;

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.

P2 Badge Read Mistral cached tokens from SDK extras

When a real Mistral stream returns cache usage, the SDK has already parsed usage into UsageInfo; Mistral documents cached tokens at usage.prompt_tokens_details.cached_tokens, but the current TS SDK keeps unmodeled wire fields under additionalProperties in UsageInfo. These reads only check top-level camel/snake fields, so real cache hits still report cacheRead: 0 and bill all prompt tokens as uncached; include record.additionalProperties?.prompt_tokens_details?.cached_tokens or otherwise preserve that SDK extra field.

Useful? React with 👍 / 👎.

# Conflicts:
#	docs/.generated/plugin-sdk-api-baseline.sha256

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cfe3a8041f

ℹ️ 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".

cacheRead: number;
cacheWrite: number;
/** Subset of `cacheWrite` written with 1-hour retention when reported. */
cacheWrite1h?: number;

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.

P2 Badge Preserve the 1h cache-write bucket in normalized usage

When Anthropic long-retention responses reach normal agent/session accounting, this new field is dropped by normalizeUsage (src/agents/usage.ts) and the run/session cost estimator still prices cacheWrite only as cacheWrite * cost.cacheWrite (src/utils/usage-format.ts). That means embedded/gateway usage summaries and session cost totals continue billing 1-hour cache writes at the 5-minute cache-write rate even though the provider stream now records the correct bucket; carry cacheWrite1h through normalized usage/accumulation and apply the same 2x-input pricing there.

Useful? React with 👍 / 👎.

# Conflicts:
#	docs/.generated/plugin-sdk-api-baseline.sha256
@steipete
steipete merged commit 6745f9a into main Jul 17, 2026
112 checks passed
@steipete
steipete deleted the fix/provider-retry-overflow-hardening branch July 17, 2026 11:40
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 18, 2026
…tput clamping (openclaw#109796)

* fix(ai): provider usage accounting, retry and overflow classification, output clamping

* ci: refresh pull-request validation

* test(ai): live-probe overflow classification and output clamping
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation maintainer Maintainer-authored PR merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant