refactor(tools): collapse the opaque-tool watchdog to a standard timeout (#1472 step 5)#1487
Merged
Merged
Conversation
…out (#1472 step 5) After #1481 the off-thread StreamingToolWatchdog was used for exactly one thing: bounding opaque tool calls with a flat wall-clock budget. In that mode its two shared Volatile fields were written once and never changed, so the 1s polling timer only ever computed "has the budget elapsed since start?" — which is exactly what a CancellationTokenSource timeout token does. (The "why is this volatile instead of actor state?" smell.) Replace it with a TimeProvider-driven timeout token — new CancellationTokenSource( timeout, timeProvider) linked to the caller token — draining through the same DrainToCompletionAsync helper self-monitoring tools already use. A budget trip is converted to the same TimeoutException message; caller (turn/user) cancellation is rethrown unchanged (preserves #1481). Per call, so a slow tool times out without affecting siblings, and TimeProvider-virtualizable so tests stay deterministic. - Delete StreamingToolWatchdog, ToolWatchdogBudget, ToolWatchdogResetMode — removes the volatile, the polling timer, and the dead ResetOnItem/FirstItem/InterItem machinery (review follow-up #13). - Unify the two drain paths on DrainToCompletionAsync (review follow-up #11). - Point SpawnAgentStreamingTests at the production drain instead of the watchdog (review follow-up #12). - Opaque wall-clock timeout + concurrent independence are already covered at the pipeline level (FakeTimeProvider); add one test for the stream-without-a-completion- item contract; gut the watchdog unit tests, keeping the INetclawTool default-adapter test. No behavior change: opaque tools still time out at the same wall-clock budget with the same message and per-tool isolation; self-monitoring tools are untouched. The spec-of- record ("Per-call liveness by tool class") already says opaque tools get one wall-clock budget — only the mechanism changed. Net: roughly -150 LOC. Part of #1472.
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.
What & why
#1472 step 5 — "addition through subtraction" of the tool-liveness machinery, at the opaque-tool boundary.
After #1481, the off-thread, volatile-polling
StreamingToolWatchdogwas used for exactly one thing: bounding opaque tool calls with a flat wall-clock budget. InWallClockmode its two sharedVolatilefields (lastActivity,budgetTicks) are written once and never change, so the 1-second polling timer only ever computes "hasToolExecutionTimeoutelapsed since start?" — which is exactly what aCancellationTokenSourcetimeout token does. This was the "why is this volatile instead of actor state?" smell.Change
Opaque tools are now bounded by a
TimeProvider-driven timeout token (new CancellationTokenSource(timeout, timeProvider)) linked to the caller token, drained through the sameDrainToCompletionAsynchelper self-monitoring tools already use:TimeoutExceptionmessage; caller (turn/user) cancellation is rethrown unchanged (preserves the refactor(subagents): consolidate sub-agent liveness — guard, delete suspend flag, stop parent over-supervision #1481 cancel-handling).TimeProvider-virtualizable, so tests stay deterministic viaFakeTimeProvider.Deletes
StreamingToolWatchdog,ToolWatchdogBudget,ToolWatchdogResetMode.Folds in three deferred #1481 review follow-ups
DrainToCompletionAsync.SpawnAgentStreamingTestsdrovespawn_agentthrough the watchdog (a path production no longer takes); now drains via the production path.ResetOnItem/FirstItem/InterItemmachinery is gone with the class.No behavior change
Opaque tools still time out at the same wall-clock budget, same message, per-tool isolation. Self-monitoring tools untouched. The spec-of-record (
Per-call liveness by tool classin thestreaming-tool-call-executionchange) already describes opaque tools as "one wall-clock budget" — only the mechanism changed, so no spec edit.Verification
Netclaw.Actors.Testsgreen (2474). Existing pipeline tests already cover opaque wall-clock timeout (FakeTimeProvider) and concurrent independence; added one for the stream-without-a-completion-item contract. Gutted the watchdog unit tests, keeping theINetclawTooldefault-adapter test.dotnet slopwatch analyzeclean; copyright headers present.Part of #1472.