Skip to content

feat: recover from typed context-length rejections - #42

Merged
danielkov merged 3 commits into
mainfrom
feat/context-rejection-recovery
Oct 19, 2026
Merged

danielkov merged 3 commits into
mainfrom
feat/context-rejection-recovery

Conversation

@danielkov

Copy link
Copy Markdown
Owner

Summary

Preserve typed context-length rejections and add opt-in, bounded transcript recovery at the model-request boundary. Hosts can compact canonical history and retry the rejected inference without replaying completed tools.

Motivation

Usage reports can lag unexpectedly large requests. Recovering after a provider rejects the context requires both reliable error classification and a mutation point before the loop terminates the logical turn.

Impact

Recovery is disabled by default. Hosts opt in with ContextRecoveryPolicy and handle MutationPoint::ContextLengthRejected; AgentKit does not choose what history to remove or estimate whether a request fits. Hosts retaining transcript state must implement TranscriptObserver::on_transcript_replaced to persist committed recovery replacements rather than staged mutator edits.

OpenAI Responses HTTP, SSE, and WebSocket paths preserve exact context-length codes, including subscription requests. Confirmed context rejections bypass unchanged-request provider retries. OpenAI chat-completions HTTP and Anthropic HTTP/SSE prompt-length rejections also receive typed classification; generic compatible-provider and completions SSE coverage is unchanged.

Technical details

Transactional and bounded recovery

Recovery mutators edit a staged transcript. The driver validates it and checks cancellation and the shared deadline before committing and notifying replacement observers. Unchanged candidates stop recovery. The retry preserves session/turn identity, tools, and cache settings; any exposed response delta or tool call prevents recovery, including output later superseded by the provider.

Attempt limits and one absolute deadline cover recovery mutations and replacement generation from the first rejection. Production deadline handling remains runtime-agnostic, using the already-locked futures-timer dependency without introducing package versions or features.

Provider-confirmed classification

LoopError::is_context_length_exceeded() uses a sanitized typed code, not rendered error text or generic HTTP 400 status. OpenAI error-body inspection is byte/time bounded. Anthropic classification requires its structured invalid-request envelope and specific prompt-length message prefix. Confirmed errors retain no prompt or error-body text.

Integration compatibility

The replacement observer method has a default no-op implementation. Existing mutation-point encodings are unchanged; closed downstream decoders need to recognize ContextLengthRejected when opting into recovery. The new upstream error kind retains the existing unknown-value fallback.

@kit-code-agent kit-code-agent 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.

Recovery can commit unsendable history, miss replayed Anthropic context rejections, and leave cancellation/usage observers inconsistent. Replacement requests also retain stale trace inputs and introduce redundant full-transcript copies.

Took 14 min

Comment thread crates/agentkit-loop/src/lib.rs
Comment thread crates/agentkit-provider-anthropic/src/lib.rs
Comment thread crates/agentkit-loop/src/lib.rs
Comment thread crates/agentkit-loop/src/lib.rs Outdated
Comment thread crates/agentkit-loop/src/lib.rs Outdated
Comment thread crates/agentkit-loop/src/lib.rs Outdated
@danielkov
danielkov enabled auto-merge (squash) October 19, 2026 14:13

@kit-code-agent kit-code-agent 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.

Recovered turns append duplicate input-message attributes to a shared OTel span when input capture is enabled.

Took 20 min

Comment thread crates/agentkit-loop/src/lib.rs

@kit-code-agent kit-code-agent 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.

No issues found. The changes look good to merge.

Took 13 min

@danielkov
danielkov disabled auto-merge October 19, 2026 14:49
@danielkov
danielkov merged commit 9870415 into main Oct 19, 2026
3 of 4 checks passed
danielkov added a commit that referenced this pull request Oct 19, 2026
## Summary
Prepare releases for all unpublished package changes and the libraries
that must move together with the new loop API. Update 17 package
versions, their internal dependency requirements, and the workspace
lockfile without changing external dependencies.

## Impact
| Packages | Release |
| --- | --- |
| `agentkit-loop`, `agentkit-adapter-completions`, `agentkit-acp`,
`agentkit-compaction`, `agentkit-reporting` | `0.11.0` |
| All nine `agentkit-provider-*` crates | `0.11.0` |
| `agentkit` facade | `0.12.0` |
| `agentkit-mcp` | `0.11.3` |
| `how-cli` | `0.10.7` |

## Technical details
### Changes included since published releases
Four packages have unpublished source changes relative to their latest
non-yanked crates.io archives: loop, OpenAI, and Anthropic include
context-rejection recovery and classification from #42; MCP includes
advertised catalog subscription capability handling from #41.
Anthropic's cached-token usage fix from #38 is already included in
published `0.10.11`.

The other 12 publishable packages have no unpublished package changes
and retain their versions. The existing publish script covers all 29
publishable workspace members in dependency order; no publish-script
change is needed.

### Coordinated minor versions
The new `ContextRecoveryDeadlineExceeded` variant extends the public,
exhaustive `LoopError` enum, requiring a minor release before 1.0.
Providers, the completions adapter, compaction, ACP, and reporting
expose loop traits or types and therefore move to `0.11.0` together. The
facade moves to `0.12.0`; binary-only `how-cli` takes a patch bump.

All affected version-bearing internal dependency edges are aligned,
including optional facade dependencies and integration-test
requirements, so registry builds do not mix incompatible loop type
identities. Compaction and reporting receive explicit versions rather
than raising the inherited workspace version for unchanged crates. The
lockfile changes only the 17 workspace package versions.
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