Skip to content

Commit 76ea3f8

Browse files
committed
fix: treat timer triggers as external resume triggers
1 parent b5d481d commit 76ea3f8

5 files changed

Lines changed: 60 additions & 13 deletions

File tree

‎pyproject.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[project]
22
name = "uipath-runtime"
3-
version = "0.11.4"
3+
version = "0.11.5"
44
description = "Runtime abstractions and interfaces for building agents and automation scripts in the UiPath ecosystem"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
77
dependencies = [
8-
"uipath-core>=0.5.22,<0.6.0",
8+
"uipath-core>=0.5.23,<0.6.0",
99
]
1010
classifiers = [
1111
"Intended Audience :: Developers",

‎src/uipath/runtime/debug/runtime.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,10 @@ async def _stream_and_debug(
194194
resume_data: dict[str, Any] | None = None
195195
try:
196196
trigger_data: dict[str, Any] | None = None
197-
if (
198-
final_result.trigger.trigger_type
199-
== UiPathResumeTriggerType.API
197+
if final_result.trigger.trigger_type in (
198+
UiPathResumeTriggerType.API,
199+
UiPathResumeTriggerType.INBOX,
200+
UiPathResumeTriggerType.TIMER,
200201
):
201202
trigger_data = (
202203
await self.debug_bridge.wait_for_resume()

‎src/uipath/runtime/resumable/runtime.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ async def stream(
147147
async def _get_fired_triggers(self) -> dict[str, Any] | None:
148148
"""Check stored triggers for any that have already fired.
149149
150-
Skips async-external triggers (API, Inbox) whose payloads only arrive
151-
asynchronously and cannot be polled at suspend time.
150+
Skips external triggers (API, Inbox, Timer) whose payloads only arrive
151+
asynchronously or through Orchestrator resume and cannot be polled at
152+
suspend time.
152153
153154
Returns:
154155
A resume map of {interrupt_id: resume_data} for fired triggers, or None.
@@ -161,7 +162,11 @@ async def _get_fired_triggers(self) -> dict[str, Any] | None:
161162
t
162163
for t in triggers
163164
if t.trigger_type
164-
not in (UiPathResumeTriggerType.API, UiPathResumeTriggerType.INBOX)
165+
not in (
166+
UiPathResumeTriggerType.API,
167+
UiPathResumeTriggerType.INBOX,
168+
UiPathResumeTriggerType.TIMER,
169+
)
165170
]
166171
return await self._build_resume_map(pollable_triggers)
167172

‎tests/test_resumable.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,47 @@ def create_inbox_trigger(data: dict[str, Any]) -> UiPathResumeTrigger:
548548
trigger_manager.read_trigger.assert_not_called()
549549
assert runtime_impl.execution_count == 1
550550

551+
@pytest.mark.asyncio
552+
async def test_resumable_skips_timer_triggers_on_auto_resume_check(self) -> None:
553+
"""Timer triggers should be skipped when checking for auto-resume."""
554+
555+
runtime_impl = MultiTriggerMockRuntime()
556+
storage = StatefulStorageMock()
557+
trigger_manager = make_trigger_manager_mock()
558+
559+
def create_timer_trigger(data: dict[str, Any]) -> UiPathResumeTrigger:
560+
return UiPathResumeTrigger(
561+
interrupt_id="", # Will be set by resumable runtime
562+
trigger_type=UiPathResumeTriggerType.TIMER,
563+
payload=data,
564+
)
565+
566+
trigger_manager.create_trigger = AsyncMock(side_effect=create_timer_trigger) # type: ignore[method-assign]
567+
read_trigger_guard = AsyncMock(
568+
side_effect=AssertionError(
569+
"read_trigger must not be called for Timer triggers pre-resume"
570+
)
571+
)
572+
trigger_manager.read_trigger = read_trigger_guard # type: ignore[method-assign]
573+
574+
resumable = UiPathResumableRuntime(
575+
delegate=runtime_impl,
576+
storage=storage,
577+
trigger_manager=trigger_manager,
578+
runtime_id="runtime-1",
579+
)
580+
581+
result = await resumable.execute({})
582+
583+
assert result.status == UiPathRuntimeStatus.SUSPENDED
584+
assert result.triggers is not None
585+
assert len(result.triggers) == 2
586+
assert all(
587+
t.trigger_type == UiPathResumeTriggerType.TIMER for t in result.triggers
588+
)
589+
trigger_manager.read_trigger.assert_not_called()
590+
assert runtime_impl.execution_count == 1
591+
551592
@pytest.mark.asyncio
552593
async def test_resumable_auto_resumes_task_triggers_but_not_api_triggers(
553594
self,

‎uv.lock‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)