Skip to content

Commit 8bae49d

Browse files
authored
Merge pull request #302 from ailuvu-art/fix/assignee-auto-online-on-mission-start
fix(tasks): auto-wake assignee online on assignment/in_progress
2 parents 1cac7fe + 48bc1ea commit 8bae49d

File tree

2 files changed

+295
-20
lines changed

2 files changed

+295
-20
lines changed

backend/app/api/tasks.py

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from app.services.openclaw.gateway_dispatch import GatewayDispatchService
6161
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
6262
from app.services.openclaw.gateway_rpc import OpenClawGatewayError
63+
from app.services.openclaw.provisioning_db import AgentLifecycleService
6364
from app.services.organizations import require_board_access
6465
from app.services.tags import (
6566
TagState,
@@ -661,15 +662,57 @@ async def _latest_task_comment_by_agent(
661662
return (await session.exec(statement)).first()
662663

663664

665+
async def _wake_agent_online_for_task(
666+
*,
667+
session: AsyncSession,
668+
board: Board,
669+
task: Task,
670+
agent: Agent,
671+
reason: str,
672+
) -> None:
673+
if not agent.openclaw_session_id:
674+
return
675+
service = AgentLifecycleService(session)
676+
try:
677+
await service.commit_heartbeat(agent=agent, status_value="online")
678+
record_activity(
679+
session,
680+
event_type="task.assignee_woken",
681+
message=(f"Assignee heartbeat set online ({reason}): {agent.name}."),
682+
agent_id=agent.id,
683+
task_id=task.id,
684+
board_id=board.id,
685+
)
686+
except Exception as exc: # pragma: no cover - best effort wake path
687+
record_activity(
688+
session,
689+
event_type="task.assignee_wake_failed",
690+
message=(f"Assignee wake failed ({reason}): {agent.name}. Error: {exc!s}"),
691+
agent_id=agent.id,
692+
task_id=task.id,
693+
board_id=board.id,
694+
)
695+
await session.commit()
696+
697+
664698
async def _notify_agent_on_task_assign(
665699
*,
666700
session: AsyncSession,
667701
board: Board,
668702
task: Task,
669703
agent: Agent,
704+
wake_assignee: bool = True,
670705
) -> None:
671706
if not agent.openclaw_session_id:
672707
return
708+
if wake_assignee:
709+
await _wake_agent_online_for_task(
710+
session=session,
711+
board=board,
712+
task=task,
713+
agent=agent,
714+
reason="assignment",
715+
)
673716
dispatch = GatewayDispatchService(session)
674717
config = await dispatch.optional_gateway_config_for_board(board)
675718
if config is None:
@@ -2121,15 +2164,23 @@ async def _lead_apply_status(
21212164
lead_agent = update.actor.agent
21222165
if "status" not in update.updates:
21232166
return
2167+
target_status = _required_status_value(update.updates["status"])
2168+
# Leads may set `in_progress` when simultaneously assigning an agent to an
2169+
# inbox task (assignment-and-start shortcut).
21242170
if update.task.status != "review":
2171+
assigning_agent = "assigned_agent_id" in update.updates and bool(
2172+
_optional_assigned_agent_id(update.updates["assigned_agent_id"])
2173+
)
2174+
if update.task.status == "inbox" and target_status == "in_progress" and assigning_agent:
2175+
update.task.status = target_status
2176+
return
21252177
raise HTTPException(
21262178
status_code=status.HTTP_403_FORBIDDEN,
21272179
detail=(
21282180
"Lead status gate failed: board leads can only change status when the current "
21292181
f"task status is `review` (current: `{update.task.status}`)."
21302182
),
21312183
)
2132-
target_status = _required_status_value(update.updates["status"])
21332184
if target_status not in {"done", "inbox"}:
21342185
raise HTTPException(
21352186
status_code=status.HTTP_403_FORBIDDEN,
@@ -2521,66 +2572,88 @@ async def _notify_task_update_assignment_changes(
25212572
*,
25222573
update: _TaskUpdateInput,
25232574
) -> None:
2575+
board: Board | None = None
2576+
2577+
async def _board() -> Board | None:
2578+
nonlocal board
2579+
if board is None and update.task.board_id:
2580+
board = await Board.objects.by_id(update.task.board_id).first(session)
2581+
return board
2582+
25242583
if (
25252584
update.task.status == "inbox"
25262585
and update.task.assigned_agent_id is None
25272586
and (update.previous_status != "inbox" or update.previous_assigned is not None)
25282587
):
2529-
board = (
2530-
await Board.objects.by_id(update.task.board_id).first(session)
2531-
if update.task.board_id
2532-
else None
2533-
)
2534-
if board:
2588+
current_board = await _board()
2589+
if current_board:
25352590
await _notify_lead_on_task_unassigned(
25362591
session=session,
2537-
board=board,
2592+
board=current_board,
25382593
task=update.task,
25392594
)
25402595

2541-
if (
2542-
not update.task.assigned_agent_id
2543-
or update.task.assigned_agent_id == update.previous_assigned
2544-
):
2596+
if not update.task.assigned_agent_id:
25452597
return
2598+
25462599
assigned_agent = await Agent.objects.by_id(update.task.assigned_agent_id).first(
25472600
session,
25482601
)
25492602
if assigned_agent is None:
25502603
return
2551-
board = (
2552-
await Board.objects.by_id(update.task.board_id).first(session)
2553-
if update.task.board_id
2554-
else None
2604+
2605+
assignment_changed = update.task.assigned_agent_id != update.previous_assigned
2606+
entered_in_progress = (
2607+
update.task.status == "in_progress" and update.previous_status != "in_progress"
25552608
)
2609+
2610+
if entered_in_progress and not assignment_changed:
2611+
current_board = await _board()
2612+
if current_board:
2613+
await _wake_agent_online_for_task(
2614+
session=session,
2615+
board=current_board,
2616+
task=update.task,
2617+
agent=assigned_agent,
2618+
reason="status_in_progress",
2619+
)
2620+
2621+
if not assignment_changed:
2622+
return
2623+
25562624
if (
25572625
update.previous_status == "review"
25582626
and update.task.status == "inbox"
25592627
and update.actor.actor_type == "agent"
25602628
and update.actor.agent
25612629
and update.actor.agent.is_board_lead
25622630
):
2563-
if board:
2631+
current_board = await _board()
2632+
if current_board:
25642633
await _notify_agent_on_task_rework(
25652634
session=session,
2566-
board=board,
2635+
board=current_board,
25672636
task=update.task,
25682637
agent=assigned_agent,
25692638
lead=update.actor.agent,
25702639
)
25712640
return
2641+
25722642
if (
25732643
update.actor.actor_type == "agent"
25742644
and update.actor.agent
25752645
and update.task.assigned_agent_id == update.actor.agent.id
25762646
):
25772647
return
2578-
if board:
2648+
2649+
current_board = await _board()
2650+
if current_board:
25792651
await _notify_agent_on_task_assign(
25802652
session=session,
2581-
board=board,
2653+
board=current_board,
25822654
task=update.task,
25832655
agent=assigned_agent,
2656+
wake_assignee=True,
25842657
)
25852658

25862659

0 commit comments

Comments
 (0)