Repository navigation
fix(sdk): name failing tools in limits-guard; host-provided image resizer - #50
Merged
Merged
Conversation
The limits-guard reducer runs after the core reducer, which has already cleared the executing tool call and dropped the call from pendingToolCalls. Every tool_completed/tool_failed therefore resolved to the tool name "unknown": failures of different tools shared one counter, any successful tool reset it, and the warning to the model never named the tool. In a host that retried a failing tool many times, maxConsecutiveToolFailures never fired. Take the name from the pendingToolResults entry that the core reducer has just recorded for the same tool call id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bootstrap() always built a VipsImageResizer, for both the LLM provider's image processor and the upload image classifier. A host without OS processes, such as one that runs the agent inside a Cloudflare Durable Object, cannot run vipsheader/vipsthumbnail, so large screenshots never shrink and do not reach the model. Platform gets an optional imageResizer, like the other host capabilities (shell, git, sessionLog, llmCallLog). Both call sites use it; a host that omits it keeps the vips resizer. The existing ImageResizer interface is what both call sites need, so it is documented and exported from the package root and @roj-ai/sdk/platform instead of adding a new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matej21
marked this pull request as ready for review
October 15, 2026 15:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent fixes in
@roj-ai/sdk.1. limits-guard counted every tool failure under
unknownA host saw its coding agent retry a failing tool 13 times, although
maxConsecutiveToolFailures: 5andmaxRepeatedToolCalls: 4were configured.Cause: plugin reducers run after the core reducer (
createApplyEventcomposesbaseReducerfirst). By the time limits-guard handlestool_completed/tool_failed, the core reducer has clearedexecutingToolCalland removed the call frompendingToolCalls.resolveToolNametherefore always returnedunknown, so:Reproduced against the reducer: fail, success, fail, fail gave
{"unknown":{"count":2}}.Fix: read the name from the
pendingToolResultsentry with the same tool call id. The core reducer has just added that entry, with the name it derived from the same sources (executingToolCall, thenpendingToolCalls). The limits counters now use the same name as the tool result that the model sees.The repeated identical call limit (
maxRepeatedToolCalls) was not affected. It counts ontool_started, which carriestoolName. A regression test now covers it.2. Host-provided image resizer
bootstrap()always built aVipsImageResizerfor the LLM provider's image processor and for the upload image classifier. A host without OS processes cannot runvipsheader/vipsthumbnail, for example a host that runs the agent inside a Cloudflare Durable Object. On such a host, large screenshots are never shrunk and do not reach the model.New public API:
Platform, like the other optional host capabilities (shell,git,createServiceExecutor,sessionLog,llmCallLog).ImageResizerinterface already covers what both call sites need, so this PR documents and exports it instead of adding a new interface.FileSystem: callers read the result and unlinktempFilethroughplatform.fs.bootstrap()resolves one resizer,platform.imageResizer ?? new VipsImageResizer(...), and passes it to both call sites. The two vips instances it replaces had identical configuration, and the classifier still passes its own 1024px limit on each call.Tests
plugins/limits-guard/tool-failures.test.ts(reducer): a failure is counted under the tool's name. A failing tool that alternates with successes of another tool still reaches the limit. A success of the same tool resets the count. Two of these tests fail onmain.limits-guard.integration.test.ts: a failing tool that alternates with a successful one gets a status warning that names it (Tool "flaky_tool" has failed 2 times), then the agent is paused withTool 'flaky_tool' failed 3 consecutive times. Onmain, the agent runs untilmaxTurns. A second test checks that the repeated identical call limit pauses the agent first (4 repeats withmaxConsecutiveToolFailures: 5).bootstrap-image-resizer.test.ts: a host resizer is called by the Anthropic provider's image processor and by the image classifier, with no process calls. Without a host resizer, both paths runvipsheader. The first test fails onmain.bun run lint,bun run ts:build,tsc -p packages/sdk/tsconfig.test.json, andbun run test(1828 pass, 0 fail).🤖 Generated with Claude Code