Skip to content

Conversation

@threepointone
Copy link
Contributor

fixes #255

@changeset-bot
Copy link

changeset-bot bot commented Oct 28, 2025

🦋 Changeset detected

Latest commit: 66d1916

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 28, 2025

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/agents@610

commit: 66d1916

@claude
Copy link

claude bot commented Nov 25, 2025

Claude Code Review

PR #610: Readonly State

This PR implements readonly connections, allowing WebSocket clients to receive state updates and call RPC methods without permission to modify state. The implementation is solid with comprehensive test coverage.

Issues Found

1. SQL Query Performance Issue (packages/agents/src/index.ts:775-795)

isConnectionReadonly() queries the database on every check when the connection is not in the in-memory cache. This could cause performance problems during high-frequency state updates.

isConnectionReadonly(connection: Connection): boolean {
  if (this._readonlyConnections.has(connection.id)) {
    return true;
  }
  
  // This SQL query runs every time for non-readonly connections
  const result = this.sql<{ connection_id: string }>`
    SELECT connection_id FROM cf_agents_readonly_connections
    WHERE connection_id = ${connection.id}
  `;
  // ...
}

Recommendation: Cache negative results (non-readonly connections) as well, or at minimum document the performance characteristics. Consider:

  • Track non-readonly connections in a separate Set
  • Add a flag to track if DB has been checked for a given connection
  • Document that this is optimized for the readonly case

2. Race Condition on Cleanup (packages/agents/src/index.ts:648-675)

The onClose wrapper performs SQL DELETE without waiting for completion. If the agent hibernates immediately after onClose returns, the deletion might not persist.

this.onClose = (connection: Connection) => {
  this._readonlyConnections.delete(connection.id);
  this.sql`DELETE FROM cf_agents_readonly_connections WHERE connection_id = ${connection.id}`;
  // No await - might not complete before hibernation
};

Impact: Low probability but could leave orphaned entries in the database that accumulate over time.

Recommendation: If SQL operations should complete before hibernation, consider awaiting or document this tradeoff.

Architecture & Testing

✅ Proper separation of concerns with server-side and client-side APIs
✅ Comprehensive test coverage (425 lines covering all major scenarios)
✅ Hibernation persistence correctly implemented with dual in-memory/SQL storage
✅ Client error handling with onStateUpdateError callback
✅ Documentation is thorough with practical examples

Minor Observations

  • Test at line 263: Comment says "reconnect with same connection" but creates a new connection with same room name (expected behavior, just misleading comment)
  • The TestReadonlyAgent.stateUpdateAttempts array tracks attempts but is never cleared - acceptable for test agent

Overall: Good implementation. Address the SQL performance pattern before merging.

agents-git-bot bot pushed a commit to cloudflare/cloudflare-docs that referenced this pull request Nov 25, 2025
Adds comprehensive documentation for the new readonly connections feature introduced in PR #610.

Key additions:
- Server-side methods: shouldConnectionBeReadonly(), setConnectionReadonly(), isConnectionReadonly()
- Client-side API: onStateUpdateError callback for error handling
- Multiple usage examples covering common scenarios (query params, RBAC, admin dashboards, dynamic permissions)
- Implementation details including SQL persistence and hibernation support
- Best practices for authentication, user feedback, and access control

This feature allows restricting certain WebSocket connections from modifying agent state while still allowing them to receive state updates and call RPC methods.

Related PR: cloudflare/agents#610

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@threepointone threepointone marked this pull request as ready for review November 25, 2025 16:48
@threepointone threepointone changed the title wip: Readonly state Readonly state Nov 25, 2025
Introduces readonly connections to restrict certain WebSocket clients from modifying agent state while allowing state updates and RPC calls. Adds server-side methods for managing readonly status, persists status in SQL for hibernation, and client-side error handling via onStateUpdateError. Updates documentation and relevant types, client, and React hook implementations.
Introduces a new TestReadonlyAgent Durable Object and comprehensive tests for readonly connection behavior, including state update restrictions, RPC permissions, persistence, and cleanup. Updates wrangler config to register the new agent for testing.
Improved type safety in readonly-connections.test.ts by introducing explicit message interfaces and type guards, replacing 'any' with specific types in test helpers and assertions. Also updated TestReadonlyAgent to ignore unused connection parameter in shouldConnectionBeReadonly. These changes enhance code reliability and maintainability in the test suite.
agents-git-bot bot pushed a commit to cloudflare/cloudflare-docs that referenced this pull request Nov 27, 2025
Synced from cloudflare/agents PR #610 (cloudflare/agents#610)

Introduces documentation for the readonly connections feature which allows restricting certain WebSocket connections from modifying Agent state while still allowing them to receive state updates and call RPC methods.

Key features documented:
- Server-side methods: shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly
- Client-side API: onStateUpdateError callback
- Usage examples for query parameter based access, role-based access control, admin dashboards, and dynamic permission changes
- Behavior details, best practices, and migration guide
- Implementation details including persistence across hibernation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@whoiskatrin whoiskatrin self-requested a review November 27, 2025 16:56
@threepointone
Copy link
Contributor Author

moving to draft, might have a better implementation here

@threepointone threepointone marked this pull request as draft December 4, 2025 15:22
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.

Make client-initiated direct state change opt-in

2 participants