Skip to content

fix(sdk-rust): tolerate public agent payloads#127

Merged
khaliqgant merged 1 commit intomainfrom
codex/fix-rust-agent-reclaim-sdk
May 10, 2026
Merged

fix(sdk-rust): tolerate public agent payloads#127
khaliqgant merged 1 commit intomainfrom
codex/fix-rust-agent-reclaim-sdk

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented May 10, 2026

Summary

Fix the upstream Relaycast SDK/API mismatch that forced AgentWorkforce/relay#830 to work around RelayCast::register_or_get_agent() with raw JSON.

The Rust SDK Agent type previously modeled internal database fields (workspace_id, token_hash, required created_at) that are not part of the public GET /v1/agents/{name} response. When registration hit a 409 and register_or_get_agent() tried to reclaim the existing agent, get_agent() failed to deserialize the public payload before token rotation could complete.

This PR updates the Rust SDK to model the public agent payload, makes optional response fields tolerant during deserialization, and keeps the server/OpenAPI/shared type contract aligned by returning/documenting created_at, last_seen, and detail-response channel memberships.

Changes

  • Remove internal-only workspace_id and token_hash requirements from the Rust SDK Agent type.
  • Make Rust SDK agent metadata, created_at, last_seen, and channels tolerant of public/deployed payload variance.
  • Add AgentChannelMembership and export it from the Rust SDK.
  • Make register_or_get_agent() reclaim on either agent_already_exists or HTTP 409.
  • Make reclaimed CreateAgentResponse.created_at fall back to last_seen when the server payload omits created_at.
  • Add a regression test for the 409 reclaim path using the public GET /v1/agents/{name} payload shape.
  • Include created_at in public server agent list/get/update responses.
  • Update openapi.yaml for agent last_seen, detail-response channels, and channel membership role values.
  • Update shared type tests so @relaycast/types asserts the public agent shape instead of internal fields.

Validation

  • cargo test
  • cargo clippy --all-targets
  • npm test -w @relaycast/types
  • npm run build -w @relaycast/types
  • npm test -w @relaycast/sdk -- src/__tests__/relay.test.ts src/__tests__/strict-identity.test.ts
  • npm run build -w @relaycast/server
  • npm test -w @relaycast/server -- src/routes/__tests__/agent.test.ts
  • npm run lint -w @relaycast/server (passes with existing warnings)
  • git diff --check

Note: cargo fmt --check still reports pre-existing formatting drift in packages/sdk-rust/src/agent.rs, which this PR intentionally leaves untouched.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8735a371-09d4-4b0e-841c-4a358c770293

📥 Commits

Reviewing files that changed from the base of the PR and between 0372c29 and 0198e9d.

📒 Files selected for processing (7)
  • openapi.yaml
  • packages/sdk-rust/src/lib.rs
  • packages/sdk-rust/src/relay.rs
  • packages/sdk-rust/src/types.rs
  • packages/sdk-rust/tests/parity.rs
  • packages/server/src/engine/agent.ts
  • packages/types/src/__tests__/types.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/server/src/engine/agent.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/sdk-rust/src/lib.rs
  • packages/sdk-rust/src/types.rs
  • openapi.yaml
  • packages/sdk-rust/src/relay.rs
  • packages/sdk-rust/tests/parity.rs

📝 Walkthrough

Walkthrough

Agent schema extended with created_at, last_seen, and channel membership; Rust types, SDK exports, SDK conflict handling, server agent responses, and parity tests updated to match.

Changes

Agent Presence and Channel Membership

Layer / File(s) Summary
API Contract
openapi.yaml
Agent schema gains explicit created_at description, adds last_seen (date-time), and a channels array. New AgentChannelMembership schema defines id, name, role, joined_at.
Rust Type Definition
packages/sdk-rust/src/types.rs
Agent struct: created_at/last_seen -> Option<String>, metadata has serde default, channels: Vec<AgentChannelMembership> added; workspace_id and token_hash removed. New AgentChannelMembership struct added.
SDK Export
packages/sdk-rust/src/lib.rs
AgentChannelMembership added to public re-exports.
SDK Request Handling
packages/sdk-rust/src/relay.rs
register_or_get_agent treats HTTP 409 as agent-already-exists and constructs created_at from agent.created_at.or(agent.last_seen).unwrap_or_default().
Server Endpoint Payloads
packages/server/src/engine/agent.ts
listAgents, getAgentByName, and updateAgent now include created_at (ISO string from createdAt) in returned agent objects.
Tests and Infrastructure
packages/sdk-rust/tests/parity.rs, packages/types/src/__tests__/types.test.ts
Added api_error helper and a test confirming 409 conflict handling with token rotation; imports and some assertions reformatted; TS tests updated to expect metadata, created_at, and channels.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SDK as RelayCast::register_or_get_agent
  participant Server
  Client->>SDK: request create agent
  SDK->>Server: POST /agents
  Server-->>SDK: 409 / agent_already_exists
  SDK->>Server: GET /agents/{id}
  Server-->>SDK: Agent payload
  SDK->>Server: POST /agents/{id}/rotate-token
  Server-->>SDK: rotated token
  SDK-->>Client: CreateAgentResponse (created_at from created_at || last_seen || default)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through agent fields with cheer, 🐰
Timestamps arrive and channels appear,
Created_at and last_seen side by side,
Tokens rotate as tests verify the tide,
Quietly, the API grows more clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(sdk-rust): tolerate public agent payloads' directly addresses the main change - fixing the Rust SDK to accept public agent payloads instead of requiring internal-only fields.
Description check ✅ Passed The description comprehensively explains the SDK/API mismatch issue, the specific changes made across multiple packages, and includes validation results, all clearly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-rust-agent-reclaim-sdk

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/sdk-rust/src/relay.rs (1)

545-551: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid using empty-string sentinel for created_at in reclaim flow.

On Line 550, defaulting to "" produces a non-date value in a timestamp field and forces downstream special-casing. This should be represented as optional instead of fabricated.

Proposed fix
# packages/sdk-rust/src/relay.rs
-                    created_at: agent.created_at.unwrap_or_default(),
+                    created_at: agent.created_at,
# packages/sdk-rust/src/types.rs
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct CreateAgentResponse {
     pub id: String,
     pub name: String,
     pub token: String,
     pub status: String,
-    pub created_at: String,
+    #[serde(default)]
+    pub created_at: Option<String>,
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk-rust/src/relay.rs` around lines 545 - 551, The
CreateAgentResponse currently uses agent.created_at.unwrap_or_default() which
yields an empty-string sentinel for created_at; instead make created_at an
Option/nullable in CreateAgentResponse and propagate the optional value from
agent.created_at (do not call unwrap_or_default). Update the CreateAgentResponse
struct/type to accept an Option<String>/Option<DateTime> (matching the domain
type) and set created_at: agent.created_at so downstream code can handle absence
without special-casing an empty string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/sdk-rust/src/relay.rs`:
- Around line 545-551: The CreateAgentResponse currently uses
agent.created_at.unwrap_or_default() which yields an empty-string sentinel for
created_at; instead make created_at an Option/nullable in CreateAgentResponse
and propagate the optional value from agent.created_at (do not call
unwrap_or_default). Update the CreateAgentResponse struct/type to accept an
Option<String>/Option<DateTime> (matching the domain type) and set created_at:
agent.created_at so downstream code can handle absence without special-casing an
empty string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 05e43b61-b947-408c-91d1-6dad4bfaf355

📥 Commits

Reviewing files that changed from the base of the PR and between c040c1a and a9df731.

📒 Files selected for processing (6)
  • openapi.yaml
  • packages/sdk-rust/src/lib.rs
  • packages/sdk-rust/src/relay.rs
  • packages/sdk-rust/src/types.rs
  • packages/sdk-rust/tests/parity.rs
  • packages/server/src/engine/agent.ts

@github-actions
Copy link
Copy Markdown

Preview deployed!

Environment URL
API https://pr127-api.relaycast.dev
Health https://pr127-api.relaycast.dev/health
Observer https://pr127-observer.relaycast.dev/observer

This preview shares the staging database and will be cleaned up when the PR is merged or closed.

Run E2E tests

npm run e2e -- https://pr127-api.relaycast.dev --ci

Open observer dashboard

https://pr127-observer.relaycast.dev/observer

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@khaliqgant khaliqgant force-pushed the codex/fix-rust-agent-reclaim-sdk branch from a9df731 to 0372c29 Compare May 10, 2026 18:22
@khaliqgant khaliqgant force-pushed the codex/fix-rust-agent-reclaim-sdk branch from 0372c29 to 0198e9d Compare May 10, 2026 18:25
@khaliqgant khaliqgant merged commit 0622a2f into main May 10, 2026
5 checks passed
@khaliqgant khaliqgant deleted the codex/fix-rust-agent-reclaim-sdk branch May 10, 2026 18:30
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.

1 participant