diff --git a/src/roe/api/agents.py b/src/roe/api/agents.py index aa377c4..3c1cfcd 100644 --- a/src/roe/api/agents.py +++ b/src/roe/api/agents.py @@ -25,6 +25,7 @@ agents_jobs_artifacts_result_retrieve, agents_jobs_cancel_all_create, agents_jobs_cancel_create, + agents_jobs_webhook_resend_create, agents_jobs_delete_data_create, agents_jobs_list, agents_jobs_references_retrieve, @@ -58,9 +59,15 @@ from roe._generated.models.agent_job_cancel_all_response import ( AgentJobCancelAllResponse, ) +from roe._generated.models.resend_agent_job_webhook_request import ( + ResendAgentJobWebhookRequest, +) from roe._generated.models.agent_job_delete_data_response import ( AgentJobDeleteDataResponse, ) +from roe._generated.models.agent_job_webhook_resend_response import ( + AgentJobWebhookResendResponse, +) from roe._generated.models.agent_job_result_many_request import ( AgentJobResultManyRequest, ) @@ -424,6 +431,28 @@ def cancel(self, job_id: str) -> None: organization_id=self._org_id, ) + def resend_webhook( + self, job_id: str, webhook_id: str | None = None + ) -> AgentJobWebhookResendResponse: + """Re-send a finished job's completion webhook. + + Sends to every webhook attached to the job's agent, or only to + ``webhook_id`` when given. The job is not re-run and its status is + unchanged. ``queued`` reports how many deliveries were queued; zero + means nothing was eligible. + """ + body = ResendAgentJobWebhookRequest( + webhook_id=UUID(str(webhook_id)) if webhook_id is not None else UNSET, + ) + response = request_raw( + self._raw, + agents_jobs_webhook_resend_create, + UUID(str(job_id)), + body=body, + organization_id=self._org_id, + ) + return AgentJobWebhookResendResponse.from_dict(response.json()) + def cancel_all(self, agent_id: str) -> AgentJobCancelAllResponse: response = request_raw( self._raw, diff --git a/tests/unit/test_agents_wrapper_transport.py b/tests/unit/test_agents_wrapper_transport.py index 7bb1088..438e9cd 100644 --- a/tests/unit/test_agents_wrapper_transport.py +++ b/tests/unit/test_agents_wrapper_transport.py @@ -275,6 +275,18 @@ def test_cancel_all_returns_structured_response_body(): assert result.note == "cancelling" +def test_resend_webhook_posts_to_the_job_and_returns_queued_count(): + api, request = _api(httpx.Response(200, json={"status": "queued", "queued": 2})) + + result = api.jobs.resend_webhook(JOB_ID) + + kwargs = request.call_args.kwargs + assert kwargs["method"] == "post" + assert kwargs["url"].endswith(f"/v1/agents/jobs/{JOB_ID}/webhook/resend/") + assert kwargs["params"] == {"organization_id": ORG_ID} + assert result.queued == 2 + + def test_retrieve_status_parses_single_status_shape_without_id(): # GET /v1/agents/jobs/{job_id}/status/ returns AgentJobSingleStatus # ({status, timestamp, error_message?}) with no "id" field — parsing it