Skip to content

feat: add AgentsResource (messaging primitive identity surface)#27

Merged
govindkavaturi-art merged 1 commit intomainfrom
feat/sdk-agents-resource
May 4, 2026
Merged

feat: add AgentsResource (messaging primitive identity surface)#27
govindkavaturi-art merged 1 commit intomainfrom
feat/sdk-agents-resource

Conversation

@mikemolinet
Copy link
Copy Markdown
Collaborator

Summary

Wraps the entire /v1/agents surface from the messaging primitive (Phase 12.1.5). Closes the agents portion of the Messaging primitive endpoints_missing entry in cueapi-python #24's parity manifest.

The companion MessagesResource (send/get/read/ack lifecycle) ships in a follow-up PR for review-burden reasons. Both PRs are independent (different files, different test classes) and can land in either order.

Surface

client.agents exposes:

Method Endpoint
.create(display_name, slug=None, webhook_url=None, metadata=None) POST /v1/agents
.list(status=None, include_deleted=False, limit=50, offset=0) GET /v1/agents
.get(ref, include_deleted=False) GET /v1/agents/{ref}
.update(ref, ...) PATCH /v1/agents/{ref}
.delete(ref) DELETE /v1/agents/{ref}
.webhook_secret_get(ref) GET /v1/agents/{ref}/webhook-secret
.webhook_secret_regenerate(ref) POST /v1/agents/{ref}/webhook-secret/regenerate
.inbox(ref, state=None, ...) GET /v1/agents/{ref}/inbox
.sent(ref, ...) GET /v1/agents/{ref}/sent

Client extension

client._request now accepts an optional headers kwarg. Extends (doesn't replace) the client's default Authorization + Content-Type + User-Agent. Used here for the X-Confirm-Destructive guard; will also be used by the upcoming MessagesResource for X-Cueapi-From-Agent + Idempotency-Key. Backwards-compatible (existing callers don't change).

Design notes pinned by tests

  • include_deleted=True sends \"true\", False (default) omits. Same omit-when-default pattern as the executions-list filters and cueapi-cli's similar flags. Pinned by test_include_deleted_only_sent_when_true.

  • clear_webhook_url=True sends literal JSON null, not field omission. Server uses model_fields_set to disambiguate "omitted = no change" from "explicit null = clear," so the SDK MUST send the key with explicit None. Pinned by test_clear_webhook_url_sends_explicit_null. Mirror of cueapi-cli feat: add MessagesResource (messaging primitive lifecycle) #28's --clear-webhook-url.

  • webhook_url + clear_webhook_url mutex enforced with a clear ValueError before any HTTP call.

  • webhook_secret_regenerate adds X-Confirm-Destructive: true automatically. The server requires it; the SDK handles it so callers don't have to know about the header. Pinned by test_regenerate_sends_destructive_header.

Tests

18 new across 9 test classes. Mock-based, mirrors the existing ExecutionsResource test pattern. All 46 unit tests pass (excluding the pre-existing test_cues.py staging-cred flake which is captured in a separate Backlog item).

No hosted-PR dependency

All 9 endpoints already shipped on prod via Phase 12.1 messaging primitive (per MEMORY.md / CLAUDE.md).

Companion PRs from this session

  • #25mark_verified bug fix + replay()
  • #26WorkersResource + UsageResource
  • (next) MessagesResource — message lifecycle, separate PR

🤖 Generated with Claude Code

Copy link
Copy Markdown
Member

@govindkavaturi-art govindkavaturi-art left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentsResource wraps the messaging-primitive identity surface. The headers param plumbed through _request() is the right place to add support for X-Cueapi-From-Agent / X-Idempotency-Key passthrough. Approve.

Wraps the entire `/v1/agents` surface from the messaging primitive
(Phase 12.1.5). Closes the agents portion of the `Messaging primitive`
endpoints_missing entry in cueapi-python #24's parity manifest. The
companion `MessagesResource` (send/get/read/ack lifecycle) ships in a
follow-up PR.

New resource:

- `cueapi/resources/agents.py`: AgentsResource
  - .create(display_name, slug=None, webhook_url=None, metadata=None)
  - .list(status=None, include_deleted=False, limit=50, offset=0)
  - .get(ref, include_deleted=False)
  - .update(ref, display_name=None, webhook_url=None, clear_webhook_url=False,
           status=None, metadata=None)
  - .delete(ref)
  - .webhook_secret_get(ref)
  - .webhook_secret_regenerate(ref)  # sends X-Confirm-Destructive: true
  - .inbox(ref, state=None, limit=50, offset=0)
  - .sent(ref, limit=50, offset=0)

Client extension:

- `client._request` now accepts an optional `headers` kwarg, which
  extends (does not replace) the client's default Authorization +
  Content-Type + User-Agent headers. Used here for the destructive
  X-Confirm-Destructive guard; will also be used by the upcoming
  MessagesResource for X-Cueapi-From-Agent + Idempotency-Key.

Design notes pinned by tests:

- `--include-deleted` mirror: `include_deleted=True` sends `"true"`,
  `False` (default) omits. Same omit-when-default pattern as PR #26's
  `executions list --has-evidence`.

- `clear_webhook_url=True` sends literal JSON `null` (key present,
  value None), NOT field omission. Server uses `model_fields_set` to
  disambiguate "omitted = no change" from "explicit null = clear", so
  the SDK MUST send the key with explicit None. Pinned by
  test_clear_webhook_url_sends_explicit_null.

- `webhook_url` and `clear_webhook_url` mutex enforced with a clear
  ValueError before any HTTP call.

- `webhook_secret_regenerate` sends X-Confirm-Destructive: true in
  the header. The server requires it; the SDK adds it automatically
  so callers don't have to know about the header. Pinned by
  test_regenerate_sends_destructive_header.

Tests: 18 new across 9 test classes (12 → ~30 unit tests; total 46
passing across all unit-test files).

No hosted-PR dependency. All 9 endpoints already shipped on prod.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@govindkavaturi-art govindkavaturi-art force-pushed the feat/sdk-agents-resource branch from a967e4e to fb4088c Compare May 4, 2026 18:12
@govindkavaturi-art govindkavaturi-art merged commit 7be845b into main May 4, 2026
govindkavaturi-art pushed a commit that referenced this pull request May 4, 2026
Wraps the `/v1/messages` surface (Phase 12.1.5). Closes the messages
portion of the `Messaging primitive` `endpoints_missing` entry in
cueapi-python #24's parity manifest.

New resource:

- `cueapi/resources/messages.py`: MessagesResource
  - .send(from_agent, to, body, subject=, reply_to=, priority=,
          expects_reply=, reply_to_agent=, metadata=, idempotency_key=)
  - .get(msg_id)
  - .mark_read(msg_id)   # idempotent on already-read
  - .ack(msg_id)         # terminal

Client extension:

- Same `_request(headers=...)` extension as PR #27 (AgentsResource).
  Independent commit on this branch since the two resources can land
  in either order; minor merge conflict on client.py is auto-resolvable
  (both PRs add the same kwarg in the same way).

Design notes pinned by tests:

- `from_agent` goes via `X-Cueapi-From-Agent` HEADER, NOT in body.
  The server's MessageCreate schema is extra="forbid" — putting `from`
  in the body would 400, but we want this caught at unit-test time.
  Pinned by test_minimal_body_and_from_header.

- `expects_reply=False` (default) NOT sent in body. Server default
  is False; sending `expects_reply: false` is no-op + adds noise.
  Pinned by test_omits_expects_reply_when_default.

- `idempotency_key` >255 chars raises ValueError client-side BEFORE
  any HTTP call. Matches server's hard limit. Pinned that no HTTP
  request is made when the validation fails.

- `idempotency_key=None` omits the header entirely (no `Idempotency-Key:
  None` leakage). Pinned.

Tests: 9 new (12 → 21 in this resource family; 38 total across all
unit-test files).

Server-side dedup-hit (200 response) and priority-downgrade signals
(`X-CueAPI-Priority-Downgraded` header) are surfaced through the
underlying httpx response — the SDK's `_handle_response` returns the
data dict on 2xx, so callers see status_code 200 vs 201 only via the
underlying client. A future enhancement could expose these signals
explicitly via a richer return type; documented for follow-up.

No hosted-PR dependency. All 4 endpoints already shipped on prod.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
govindkavaturi-art pushed a commit that referenced this pull request May 4, 2026
Wraps the `/v1/messages` surface (Phase 12.1.5). Closes the messages
portion of the `Messaging primitive` `endpoints_missing` entry in
cueapi-python #24's parity manifest.

New resource:

- `cueapi/resources/messages.py`: MessagesResource
  - .send(from_agent, to, body, subject=, reply_to=, priority=,
          expects_reply=, reply_to_agent=, metadata=, idempotency_key=)
  - .get(msg_id)
  - .mark_read(msg_id)   # idempotent on already-read
  - .ack(msg_id)         # terminal

Client extension:

- Same `_request(headers=...)` extension as PR #27 (AgentsResource).
  Independent commit on this branch since the two resources can land
  in either order; minor merge conflict on client.py is auto-resolvable
  (both PRs add the same kwarg in the same way).

Design notes pinned by tests:

- `from_agent` goes via `X-Cueapi-From-Agent` HEADER, NOT in body.
  The server's MessageCreate schema is extra="forbid" — putting `from`
  in the body would 400, but we want this caught at unit-test time.
  Pinned by test_minimal_body_and_from_header.

- `expects_reply=False` (default) NOT sent in body. Server default
  is False; sending `expects_reply: false` is no-op + adds noise.
  Pinned by test_omits_expects_reply_when_default.

- `idempotency_key` >255 chars raises ValueError client-side BEFORE
  any HTTP call. Matches server's hard limit. Pinned that no HTTP
  request is made when the validation fails.

- `idempotency_key=None` omits the header entirely (no `Idempotency-Key:
  None` leakage). Pinned.

Tests: 9 new (12 → 21 in this resource family; 38 total across all
unit-test files).

Server-side dedup-hit (200 response) and priority-downgrade signals
(`X-CueAPI-Priority-Downgraded` header) are surfaced through the
underlying httpx response — the SDK's `_handle_response` returns the
data dict on 2xx, so callers see status_code 200 vs 201 only via the
underlying client. A future enhancement could expose these signals
explicitly via a richer return type; documented for follow-up.

No hosted-PR dependency. All 4 endpoints already shipped on prod.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants