Skip to content

feat(tools): add WaitTool for pausing on long-running jobs - #6690

Merged
joaomdmoura merged 5 commits into
mainfrom
feat/wait-tool
Jul 28, 2026
Merged

feat(tools): add WaitTool for pausing on long-running jobs#6690
joaomdmoura merged 5 commits into
mainfrom
feat/wait-tool

Conversation

@joaomdmoura

@joaomdmoura joaomdmoura commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Why

Agents that kick off out-of-band work — a sandbox build, a deployment, an async API job — have no way to let clock time pass. Today they either poll in a tight loop or give up before the work finishes. LLMs are bad at waiting because nothing in the toolset lets them.

What

WaitTool in crewai-tools. Stdlib only, no API key, no new dependencies.

from crewai_tools import WaitTool

wait_tool = WaitTool()

wait_tool.run(seconds=30, reason="sandbox build running")
# 'Waited 30 seconds. Reason: sandbox build running'

wait_tool.run(seconds=3600)
# 'Waited 300 seconds. Requested 3600 seconds, capped at 300 seconds per call -
#  call this tool again if more waiting is needed.'

WaitTool(max_seconds=1800)              # raise the per-call cap
await wait_tool.arun(seconds=30)        # asyncio.sleep, doesn't block the loop
  • Args: seconds (float, >= 0) and an optional reason, echoed back so traces show what was being waited on instead of a bare sleep.
  • Init param: max_seconds, default 300.
  • Sync _run (time.sleep) and async _arun (asyncio.sleep).

Design notes

The cap clamps instead of erroring. A model asking for an hour gets 5 minutes plus an instruction to call again. A hard failure seemed likely to train worse behavior. Trade-off: an agent stuck polling burns a call every 5 minutes rather than getting a clear stop signal. There is no cumulative wait budget across calls — that would need per-instance state and felt out of scope here.

The description is written for tool selection. It lists when to reach for the tool (builds, deploys, batch jobs, async polling, rate-limit backoff) and explicitly when not to (pacing a conversation, faking progress, when the answer is already in hand).

Also included

  • Exports from crewai_tools and crewai_tools.tools
  • Regenerated tool.specs.json (+88 lines, WaitTool entry only) so the platform sync picks it up
  • 10 tests in lib/crewai-tools/tests/tools/wait_tool_test.py
  • Tool README and docs at docs/edge/en/tools/automation/waittool.mdx, plus nav entry and overview card

Not included

  • No version bump / release cut
  • Docs are English + edge only. The ar, ko, and pt-BR locales and versioned snapshots are untouched, assuming translations sync separately. Happy to add them if this PR should carry all four.

Testing

10 passed for the new suite; test_generate_tool_specs and test_import_without_warnings also pass. ruff check, ruff format, and mypy clean on the new files.

🤖 Generated with Claude Code


Note

Low Risk
Additive tool with no auth or data changes; main runtime effect is blocking sleep up to the configured cap per invocation.

Overview
Adds WaitTool to crewai-tools so agents can pause between status checks on out-of-band work (builds, deploys, async jobs) instead of tight polling or giving up early.

The tool accepts seconds (≥ 0) and an optional reason echoed in the result, uses max_seconds (default 300) to clamp longer waits and tell the model to call again, implements sync time.sleep and async asyncio.sleep, disables tool result caching so waits cannot be skipped, and keeps the LLM-facing description in sync with the cap. It is exported from the package, covered by tests, documented under edge automation, and registered in tool.specs.json.

Reviewed by Cursor Bugbot for commit ff94734. Bugbot is set up for automated code reviews on this repo. Configure here.

Agents that kick off out-of-band work (a sandbox build, a deployment, an
async API job) have no way to let clock time pass: they either poll in a
tight loop or give up before the work finishes.

WaitTool pauses for a given number of seconds, with an optional reason
echoed back for traces. A single call waits at most max_seconds (default
300, configurable). Longer requests are clamped to the cap and the result
says so, so the model calls again rather than failing. Sync and async
execution are both implemented; stdlib only, no new dependencies.

The tool description spells out when to reach for it (builds, deploys,
batch jobs, async polling, backoff) and when not to, so models pick it up
for the right reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 20:59
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds WaitTool with validated synchronous and asynchronous waits, configurable per-call caps, public exports, tool metadata, tests, and documentation integrated into the automation tools catalog.

Changes

WaitTool feature

Layer / File(s) Summary
WaitTool implementation
lib/crewai-tools/src/crewai_tools/tools/wait_tool/...
Defines validated inputs, configurable maximum duration, capped wait resolution, formatted results, disabled caching, and synchronous/asynchronous execution.
Public integration and validation
lib/crewai-tools/src/crewai_tools/__init__.py, lib/crewai-tools/src/crewai_tools/tools/__init__.py, lib/crewai-tools/tool.specs.json, lib/crewai-tools/tests/tools/wait_tool_test.py
Exports WaitTool, registers initialization and runtime schemas, and tests waiting, capping, validation, async execution, descriptions, caching, and imports.
WaitTool documentation
lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md, docs/edge/en/tools/automation/..., docs/docs.json
Documents installation, usage, arguments, maximum wait behavior, async execution, and adds the tool to the automation catalog.

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant WaitTool
  participant Sleep as time.sleep_or_asyncio.sleep
  Agent->>WaitTool: invoke with seconds and optional reason
  WaitTool->>WaitTool: validate and clamp seconds
  WaitTool->>Sleep: wait for effective duration
  Sleep-->>WaitTool: wait completed
  WaitTool-->>Agent: return formatted result
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the addition of WaitTool for pausing on long-running jobs.
Description check ✅ Passed The description is clearly related to the WaitTool addition and its behavior, exports, and docs.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wait-tool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
lib/crewai-tools/tests/tools/wait_tool_test.py (1)

85-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Also test the crewai_tools.tools export.

This test verifies only from crewai_tools import WaitTool; add the corresponding crewai_tools.tools import assertion so that both public export paths are protected.

Suggested test extension
 def test_exported_from_package():
     from crewai_tools import WaitTool as ExportedWaitTool
+    from crewai_tools.tools import WaitTool as ToolsExportedWaitTool

     assert ExportedWaitTool is WaitTool
+    assert ToolsExportedWaitTool is WaitTool

Based on the PR objective, both package exports are part of the public contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/tests/tools/wait_tool_test.py` around lines 85 - 89, Extend
test_exported_from_package to also import WaitTool from crewai_tools.tools and
assert it is the same object as WaitTool, preserving the existing top-level
export assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/edge/en/tools/automation/waittool.mdx`:
- Around line 43-53: Update the example around the Agent tools list to define or
import check_build_status_tool before it is passed to Agent, or explicitly mark
it as an application-provided placeholder so the snippet is copyable without an
undefined symbol.
- Around line 100-107: Update the Async Support example around wait_tool.arun to
show await inside an async context: wrap the call in an async function and
invoke it with an async runner, or explicitly state that the snippet requires an
async notebook/runtime.

In `@lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md`:
- Around line 30-35: Update the Agent usage example to eliminate the undefined
check_build_status_tool reference: either add its valid definition/import within
the example or remove it from the tools list and use only the defined wait_tool,
keeping the snippet directly runnable.

---

Nitpick comments:
In `@lib/crewai-tools/tests/tools/wait_tool_test.py`:
- Around line 85-89: Extend test_exported_from_package to also import WaitTool
from crewai_tools.tools and assert it is the same object as WaitTool, preserving
the existing top-level export assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8882d403-1006-450a-bf47-d3f3e1b24959

📥 Commits

Reviewing files that changed from the base of the PR and between e2c8d7c and 122debd.

📒 Files selected for processing (10)
  • docs/docs.json
  • docs/edge/en/tools/automation/overview.mdx
  • docs/edge/en/tools/automation/waittool.mdx
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md
  • lib/crewai-tools/src/crewai_tools/tools/wait_tool/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py
  • lib/crewai-tools/tests/tools/wait_tool_test.py
  • lib/crewai-tools/tool.specs.json

Comment thread docs/edge/en/tools/automation/waittool.mdx
Comment thread docs/edge/en/tools/automation/waittool.mdx
Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md

Copilot AI 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.

Pull request overview

Adds a new WaitTool to crewai-tools to let agents pause for real clock time between checks of long-running out-of-band work (builds, deploys, async jobs, backoff), with a per-call cap and both sync/async implementations.

Changes:

  • Introduces WaitTool (sync time.sleep / async asyncio.sleep) with seconds + optional reason, clamping waits to max_seconds.
  • Exposes the tool via package exports and registers it in tool.specs.json.
  • Adds unit tests and documentation (tool page, automation overview card, docs nav entry).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
lib/crewai-tools/tool.specs.json Registers WaitTool in generated tool specs for platform sync.
lib/crewai-tools/tests/tools/wait_tool_test.py Adds coverage for clamping, reason echo, async behavior, and exports.
lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py Implements WaitTool, schemas, clamping, and result formatting.
lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md Documents WaitTool usage and parameters in tool README.
lib/crewai-tools/src/crewai_tools/tools/wait_tool/init.py Exports WaitTool / WaitToolSchema from the tool package.
lib/crewai-tools/src/crewai_tools/tools/init.py Adds WaitTool import and export in tools module.
lib/crewai-tools/src/crewai_tools/init.py Re-exports WaitTool at package top-level.
docs/edge/en/tools/automation/waittool.mdx Adds end-user documentation page for WaitTool.
docs/edge/en/tools/automation/overview.mdx Adds WaitTool card to Automation overview page.
docs/docs.json Adds WaitTool to docs navigation under Automation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py
@mintlify

mintlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
crewai 🟢 Ready View Preview Jul 27, 2026, 9:15 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

…ippets

BaseTool.run() skips args_schema validation when called with positional
arguments, so tool.run(-5) reached time.sleep(-5) and failed with an
unrelated error. _resolve_duration now enforces the seconds >= 0 contract
itself, covered for both run() and arun().

Docs and README examples are now self-contained: check_build_status_tool
is defined with the @tool decorator instead of referenced out of nowhere,
and the async example awaits inside asyncio.run() rather than at top level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 22:30
@joaomdmoura

Copy link
Copy Markdown
Collaborator Author

Review comments addressed in 438a89c.

Copilot — negative seconds via positional call: real bug, fixed. BaseTool.run() skips args_schema validation when positional args are present, so tool.run(-5) reached time.sleep(-5). _resolve_duration now enforces seconds >= 0 itself with a clear message, tested for both run() and arun().

CodeRabbit — undefined check_build_status_tool (docs page + README): both examples now define it with the @tool decorator, body commented as the place for your own build system call.

CodeRabbit — top-level await in the async example (docs page + README): both now await inside async def main() run via asyncio.run(main()).

CodeRabbit nitpick — also assert the crewai_tools.tools export: added, both export paths are now covered.

CodeRabbit pre-merge — docstring coverage 29.41%: not addressed deliberately. The shortfall is test functions; neighbouring suites in lib/crewai-tools/tests/tools/ mostly do not docstring their tests, and the test names already say what they assert. All non-test functions in the diff have docstrings. Happy to add them if the threshold should be honoured repo-wide.

Suite is 12 passing; ruff check, ruff format, and mypy clean. tool.specs.json needed no regeneration, the tool description did not change.

Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py:139

  • BaseTool.run() skips args_schema validation when called with positional args, so _run() can be invoked with non-float values (e.g. tool.run("5") or tool.run("abc")). Right now that can lead to TypeError inside _resolve_duration/_format_result rather than a consistent ValueError, and it also bypasses the coercion you get with keyword args.

Coerce seconds to float inside _run() and pass the coerced value through to _resolve_duration and _format_result so positional calls behave consistently with keyword calls.

    def _run(self, seconds: float, reason: str | None = None) -> str:
        """Block for ``seconds``, capped at ``max_seconds``.

        Args:
            seconds: How many seconds to wait.
            reason: Optional note on what is being waited for.

        Returns:
            A summary of how long was waited and whether the request was capped.
        """
        waited, _ = self._resolve_duration(seconds)
        time.sleep(waited)
        return self._format_result(waited, seconds, reason)

lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py:153

  • Same positional-args issue exists for async execution: BaseTool.arun() skips args_schema validation when positional args are used, so _arun() should coerce/validate seconds before calling _resolve_duration and formatting the result. Otherwise positional calls like await tool.arun("5") can raise TypeError unexpectedly.
    async def _arun(self, seconds: float, reason: str | None = None) -> str:
        """Await for ``seconds``, capped at ``max_seconds``, without blocking the loop.

        Args:
            seconds: How many seconds to wait.
            reason: Optional note on what is being waited for.

        Returns:
            A summary of how long was waited and whether the request was capped.
        """
        waited, _ = self._resolve_duration(seconds)
        await asyncio.sleep(waited)
        return self._format_result(waited, seconds, reason)

Unprefixed links resolve against the default docs version (v1.15.7),
where the wait tool page does not exist, so the card 404'd in the broken
link check. Prefixing with /edge matches how other edge pages link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 22:50

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6205516. Configure here.

Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comment thread docs/edge/en/tools/automation/overview.mdx
Two issues from review, both confirmed against the code.

Waits inherited the default cache_function, which always allows caching.
With crew cache enabled, a repeat call with the same arguments returned
"Waited N seconds." straight from the cache without sleeping, turning a
poll-wait-check loop into a busy loop. WaitTool now declares a
cache_function that always refuses.

The description advertising the cap was only rebuilt when max_seconds
reached __init__ without an explicit description. Passing both (as a
platform building from tool.specs.json init params would), calling
model_validate, or assigning max_seconds left the text claiming 300
seconds while clamping to something else. A model_validator now derives
the description from max_seconds on construction, validation, and
assignment, and leaves a caller-supplied description untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 23:00
@github-actions github-actions Bot added size/XL and removed size/L labels Jul 27, 2026

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py
Comment thread lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py Outdated

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
lib/crewai-tools/tests/tools/wait_tool_test.py (1)

4-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep the test on the public contract.

DEFAULT_MAX_SECONDS and _build_description are implementation details. Obtain the baseline description through WaitTool().description instead. As per coding guidelines, “Write unit tests for new functionality that focus on behavior rather than implementation details.”

Proposed change
-from crewai_tools.tools.wait_tool.wait_tool import (
-    DEFAULT_MAX_SECONDS,
-    _build_description,
-)
-
 ...
-    tool = build(_build_description(DEFAULT_MAX_SECONDS))
+    tool = build(WaitTool().description)

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/tests/tools/wait_tool_test.py` around lines 4 - 7, Update
the wait tool test imports and setup to stop importing the implementation
details DEFAULT_MAX_SECONDS and _build_description; obtain the baseline
description from WaitTool().description instead, while keeping assertions
focused on the public behavior of WaitTool.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/crewai-tools/tests/tools/wait_tool_test.py`:
- Around line 4-7: Update the wait tool test imports and setup to stop importing
the implementation details DEFAULT_MAX_SECONDS and _build_description; obtain
the baseline description from WaitTool().description instead, while keeping
assertions focused on the public behavior of WaitTool.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4bc4a60b-c68a-479a-b640-daaac52e877b

📥 Commits

Reviewing files that changed from the base of the PR and between 6205516 and f343936.

📒 Files selected for processing (3)
  • lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py
  • lib/crewai-tools/tests/tools/wait_tool_test.py
  • lib/crewai-tools/tool.specs.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/crewai-tools/tool.specs.json
  • lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py

_resolve_duration now rejects NaN with its own message instead of letting
time.sleep raise "Invalid value NaN (not a number)" from a positional
call. Infinity keeps clamping to the cap like any other oversized wait.

Result and description text no longer says "1 seconds". Tests use the
public WaitTool().description as the baseline rather than reaching for
module-private helpers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 23:41
@joaomdmoura

Copy link
Copy Markdown
Collaborator Author

Latest round addressed in ff94734.

Copilot — NaN reaching time.sleep: valid, fixed. Positional calls skip schema validation, so run(float("nan")) hit time.sleep(nan) and raised ValueError: Invalid value NaN (not a number). _resolve_duration now rejects it with its own message. Infinity intentionally still clamps to the cap rather than erroring — inf reads as "wait as long as you can", which is what clamping does.

Copilot — "Waited 1 seconds": valid, fixed. A _format_seconds helper pluralizes waited, requested, and max_seconds in both the result message and the generated description.

CodeRabbit — tests importing _build_description / DEFAULT_MAX_SECONDS: fair, fixed. The parametrized test now takes its baseline from WaitTool().description, so nothing module-private is imported.

Copilot — /edge/ href inconsistent with sibling cards: declined, this one is wrong. The unprefixed form is exactly what CI flagged as broken (run 30310976168): unprefixed links resolve against the default version (v1.15.7), where this page does not exist. Siblings work because their pages are in the released snapshot. Reproduced locally with mint@4.2.741 broken-links on a pruned tree — clean with the prefix, broken without.

23 tests passing; ruff, mypy, and tool.specs.json all clean.

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py:232

  • BaseTool.run()/arun() skip args_schema validation whenever positional args are used (see lib/crewai/src/crewai/tools/base_tool.py:318-320). If seconds is passed positionally as a non-numeric value (e.g. a string), _resolve_duration() (via math.isnan) and _format_result() comparisons can raise TypeError instead of a clear ValueError. Coercing seconds to float in _run/_arun keeps positional calls aligned with Pydantic’s coercion behavior for kwargs and ensures consistent error messages.
        waited, _ = self._resolve_duration(seconds)
        time.sleep(waited)
        return self._format_result(waited, seconds, reason)

    async def _arun(self, seconds: float, reason: str | None = None) -> str:
        """Await for ``seconds``, capped at ``max_seconds``, without blocking the loop.

        Args:
            seconds: How many seconds to wait.
            reason: Optional note on what is being waited for.

        Returns:
            A summary of how long was waited and whether the request was capped.
        """
        waited, _ = self._resolve_duration(seconds)
        await asyncio.sleep(waited)
        return self._format_result(waited, seconds, reason)

Comment thread lib/crewai-tools/tests/tools/wait_tool_test.py
@joaomdmoura
joaomdmoura enabled auto-merge (squash) July 28, 2026 00:10

@alex-clawd alex-clawd 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.

Approved.

Genuinely useful gap to close — an agent that starts out-of-band work currently has no way to let clock time pass, so it either busy-polls or gives up early.

Three things I'd have worried about, all handled:

cache_function returns False. This is the one that would have quietly ruined the tool. A cached hit hands back "Waited 300 seconds." without waiting, turning the poll-wait-check loop it exists to support into a busy loop that looks like it's working. Easy to miss and miserable to debug.

_resolve_duration doesn't trust the schema. BaseTool.run skips args_schema validation for positional args, so bounds are enforced in the method too — with tests that specifically call positionally. NaN and infinity are handled explicitly rather than left to time.sleep to reject in its own way.

The advertised cap can't drift from the enforced one. _sync_description_with_cap runs on construction and assignment, and _is_generated_description means a caller-authored description is left alone rather than clobbered. Telling the model a cap that isn't the real one is the sort of thing that surfaces as a confusing loop much later.

On clamping rather than erroring: agree with the call. A model asking for an hour and getting a hard failure learns to avoid the tool; getting 5 minutes plus "call again if more waiting is needed" teaches the right loop. The result string carries that instruction, which is what makes it work.

Also good that reason is echoed back — a trace showing "Reason: sandbox build running" beats a bare sleep when someone is working out where 5 minutes went.

Ran the tests locally: 23 passed. CI 27 pass / 1 skipping.

@joaomdmoura
joaomdmoura merged commit 97981ed into main Jul 28, 2026
58 checks passed
@joaomdmoura
joaomdmoura deleted the feat/wait-tool branch July 28, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants