diff --git a/cueapi/resources/agents.py b/cueapi/resources/agents.py index c6c65ef..c5c6d36 100644 --- a/cueapi/resources/agents.py +++ b/cueapi/resources/agents.py @@ -252,6 +252,7 @@ def subscriptions_create( event_type: str, delivery_target: str, webhook_url: Optional[str] = None, + inline_body: Optional[bool] = None, ) -> Dict[str, Any]: """Create a subscription for an agent (PR-1b event-emit primitive). @@ -266,11 +267,22 @@ def subscriptions_create( ``"webhook"`` (server POSTs to ``webhook_url`` with HMAC). webhook_url: Required when ``delivery_target="webhook"``; HTTPS only. Ignored for pull subscriptions. + inline_body: Opt into body embedding on emitted events + (server Item 1, hosted PR #791). When ``True``, the source + message body is embedded in the event payload as + ``payload.body`` — eliminates the extra + ``GET /v1/messages/{id}`` round-trip on the consumer side. + Bodies > 32KB are NOT embedded; instead the payload carries + ``payload.body_omitted = "size_too_large"`` and + ``payload.body_size_bytes = N`` so consumers can fall back + to the fetch. Default ``None`` means "don't send the field" + — server default is ``False``. Returns: Subscription dict. For webhook subscriptions, the response includes ``webhook_secret`` ONE-TIME — save it now; the - server never re-exposes it. + server never re-exposes it. Both pull and webhook responses + surface ``inline_body`` so list-callers can observe own state. Errors: 400 ``unknown_event_type`` / ``invalid_delivery_target`` / @@ -282,6 +294,8 @@ def subscriptions_create( } if webhook_url is not None: body["webhook_url"] = webhook_url + if inline_body is not None: + body["inline_body"] = inline_body return self._client._post(f"/v1/agents/{ref}/subscriptions", json=body) def subscriptions_list(self, ref: str) -> Dict[str, Any]: diff --git a/tests/test_agents_resource.py b/tests/test_agents_resource.py index 61e086a..e8770d0 100644 --- a/tests/test_agents_resource.py +++ b/tests/test_agents_resource.py @@ -346,6 +346,67 @@ def test_webhook_url_omitted_when_none(self): body = mock_client._post.call_args.kwargs["json"] assert "webhook_url" not in body + def test_inline_body_omitted_when_none(self): + # Server-side default is False; sending an unset kwarg should + # NOT put the field on the wire (avoid payload noise for + # callers who don't care about body embedding). + mock_client = MagicMock() + mock_client._post.return_value = {"id": "sub_uuid"} + r = AgentsResource(mock_client) + + r.subscriptions_create( + "agt_x", + event_type="message.received", + delivery_target="pull", + ) + + body = mock_client._post.call_args.kwargs["json"] + assert "inline_body" not in body + + def test_inline_body_true_passes_through(self): + # Opt-in: inline_body=True embeds message.body on emitted events + # (server Item 1, hosted PR #791). + mock_client = MagicMock() + mock_client._post.return_value = { + "id": "sub_uuid", + "delivery_target": "pull", + "inline_body": True, + } + r = AgentsResource(mock_client) + + r.subscriptions_create( + "agt_x", + event_type="message.received", + delivery_target="pull", + inline_body=True, + ) + + body = mock_client._post.call_args.kwargs["json"] + assert body["inline_body"] is True + + def test_inline_body_false_explicit_passes_through(self): + # Explicit False (distinct from None / unset) — caller may want + # to force the no-embed contract on a per-subscription basis. + # We pass it through verbatim; server treats False the same as + # the schema default but explicit is non-noisy. + mock_client = MagicMock() + mock_client._post.return_value = { + "id": "sub_uuid", + "delivery_target": "pull", + "inline_body": False, + } + r = AgentsResource(mock_client) + + r.subscriptions_create( + "agt_x", + event_type="message.received", + delivery_target="pull", + inline_body=False, + ) + + body = mock_client._post.call_args.kwargs["json"] + assert body["inline_body"] is False + class TestSubscriptionsList: def test_get_path(self):