Skip to content

Conversation

@lewisjcs
Copy link
Contributor

@lewisjcs lewisjcs commented Nov 4, 2025

Purpose of PR

  • Introduced LOCATION_AGENT in locations for agent-specific functionality.
  • Added createAgent import and integrated makeAgentAPI for handling agent data in the API.
  • Updated types to include AgentAppSDK and related agent context types.

PR Checklist

  • Tests are added/updated/not required
  • Tests are passing
  • Typescript typings are added/updated/not required

Summary by CodeRabbit

  • New Features

    • Agent integration with context tracking (view + metadata: entry ID, content type ID, focused field ID).
    • Event listener APIs for agent context changes and toolbar actions.
    • SDK expanded with agent support and a new agent location; agent API exported for apps.
  • Tests

    • Added unit tests and fixtures covering agent context emission, updates, multiple handlers, handler unregistration, toolbar actions, and error cases.

@lewisjcs lewisjcs requested review from a team as code owners November 4, 2025 22:39
@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Walkthrough

Adds an Agent runtime and types: introduces createAgent to bind an AgentContext to a Channel (exposes onContextChange and onToolbarAction), registers an Agent API producer, adds LOCATION_AGENT, updates types/exports, and adds fixtures and unit tests for agent behavior.

Changes

Cohort / File(s) Summary
Agent runtime
lib/agent.ts
New default-exported createAgent(channel, contextData): AgentAPI factory. Validates contextData, initializes a memoized signal, subscribes to channel contextChanged and toolbarAction events, updates/dispatches context, and exposes onContextChange and onToolbarAction registration/unsubscribe.
API integration
lib/api.ts
Import createAgent; add makeAgentAPI(channel, { agent }) producer; register [locations.LOCATION_AGENT]: [makeSharedAPI, makeAgentAPI] in LOCATION_TO_API_PRODUCERS.
Locations
lib/locations.ts
Added LOCATION_AGENT: 'agent' to exported Locations.
Type definitions
lib/types/agent.types.ts, lib/types/api.types.ts, lib/types/index.ts
New AgentContext, ToolbarActionName, ToolbarAction; extend AgentAPI with onToolbarAction; add AgentAppSDK type; include agent in KnownAppSDK; extend ConnectMessage with optional agent?; export AgentAPI, AgentContext, AgentAppSDK.
Tests & Mocks
test/mocks/agent.ts, test/mocks/connectMessage.ts, test/unit/agent.spec.ts, test/unit/api.spec.ts
Add agent fixtures (mockAgentContext*, connectMessageWithAgent) and tests covering createAgent validation, initial emission, metadata permutations, handler registration/unregistration, multiple handlers, toolbar action handling, and SDK integration for agent location.

Sequence Diagram(s)

sequenceDiagram
    participant App as Client/App
    participant AgentFactory as createAgent
    participant Channel
    participant Signal as MemoizedSignal
    participant AgentAPI as AgentAPI (handler store)

    App->>AgentFactory: createAgent(channel, contextData)
    AgentFactory->>Signal: init(contextData)
    AgentFactory->>Channel: subscribe(contextChanged, toolbarAction)
    AgentFactory-->>App: return AgentAPI { onContextChange, onToolbarAction }

    App->>AgentAPI: onContextChange(handler)
    AgentAPI->>AgentAPI: register handler
    AgentAPI-->>App: return unsubscribe

    Channel->>AgentFactory: contextChanged(newContext)
    AgentFactory->>Signal: update(newContext)
    AgentFactory->>AgentAPI: dispatch(updatedContext)
    AgentAPI->>App: invoke registered handlers

    Channel->>AgentFactory: toolbarAction(action)
    AgentFactory->>AgentAPI: dispatch(action)
    AgentAPI->>App: invoke toolbar handlers
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to:
    • Validation and error handling for missing/null contextData in lib/agent.ts.
    • MemoizedSignal initialization/update ordering and emission semantics.
    • Channel subscription/unsubscribe lifecycle to prevent leaks.
    • Correct unsubscribe behavior for onContextChange and onToolbarAction.
    • Tests covering minimal/partial metadata and handler detach behavior.

Poem

🐇 I tuned the signals, nudged context with a hop,
Channels whispered updates, the handlers did not stop,
Metadata snug in pockets, some small and some grand,
Toolbar taps chimed through the meadow on demand,
Hooray — the agent listens, and I thump on the land.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding agent location and API integration, with a reference to the ticket.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/agent-location-EXT-6966

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • EXT-6966: Request failed with status code 404

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

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/agent.ts (1)

13-19: Optional: Local context variable is updated but not read.

The context variable on line 13 is initialized and updated (line 18) but never read after being passed to MemoizedSignal on line 15. The MemoizedSignal maintains the actual state, so this local variable is redundant.

Consider simplifying by removing the local variable:

-  let context: AgentContext = contextData
-
-  const contextChanged = new MemoizedSignal<[AgentContext]>(context)
+  const contextChanged = new MemoizedSignal<[AgentContext]>(contextData)
 
   channel.addHandler('contextChanged', (newContext: AgentContext) => {
-    context = newContext
     contextChanged.dispatch(newContext)
   })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f0f0237 and c1ac5ad.

📒 Files selected for processing (6)
  • lib/agent.ts (1 hunks)
  • lib/api.ts (3 hunks)
  • lib/locations.ts (1 hunks)
  • lib/types/agent.types.ts (1 hunks)
  • lib/types/api.types.ts (5 hunks)
  • lib/types/index.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-24T16:31:58.950Z
Learnt from: tylerwashington888
Repo: contentful/ui-extensions-sdk PR: 2338
File: lib/asset.ts:49-51
Timestamp: 2025-09-24T16:31:58.950Z
Learning: Both asset API and entry API implementations use VoidFunction for onMetadataChanged handler parameter, maintaining consistency across the codebase despite type definitions specifying (metadata?: Metadata) => void.

Applied to files:

  • lib/types/agent.types.ts
🧬 Code graph analysis (4)
lib/agent.ts (4)
lib/channel.ts (1)
  • Channel (26-86)
lib/types/api.types.ts (1)
  • ConnectMessage (408-436)
lib/types/agent.types.ts (2)
  • AgentAPI (10-12)
  • AgentContext (1-8)
lib/signal.ts (1)
  • MemoizedSignal (24-57)
lib/api.ts (3)
lib/channel.ts (1)
  • Channel (26-86)
lib/types/api.types.ts (1)
  • ConnectMessage (408-436)
lib/agent.ts (1)
  • createAgent (5-25)
lib/types/api.types.ts (1)
lib/types/agent.types.ts (2)
  • AgentAPI (10-12)
  • AgentContext (1-8)
lib/types/agent.types.ts (1)
lib/types/index.ts (2)
  • AgentContext (40-40)
  • AgentAPI (40-40)
🔇 Additional comments (12)
lib/types/index.ts (1)

37-41: LGTM! Clean type exports for agent integration.

The new exports for AgentAppSDK, AgentAPI, and AgentContext are correctly structured and follow the existing export patterns in this file.

lib/locations.ts (1)

13-13: LGTM! Location constant added correctly.

The LOCATION_AGENT constant follows the established pattern and correctly integrates with the Locations type definition in lib/types/api.types.ts.

lib/agent.ts (1)

5-24: Solid implementation! Verify test coverage.

The createAgent function correctly:

  • Validates required context data
  • Uses MemoizedSignal to ensure handlers are immediately invoked with current context
  • Subscribes to context change events via the channel
  • Returns a properly typed AgentAPI

The implementation follows existing SDK patterns and integrates cleanly with the channel-based event system.

The PR checklist shows tests are not marked as complete. Please ensure this new functionality is covered by tests, particularly:

  • Context validation (line 9-11)
  • Handler invocation on registration
  • Context updates via channel events
  • Handler cleanup (unsubscribe function)
lib/api.ts (3)

11-11: LGTM! Clean import integration.

The createAgent import is correctly added alongside other API factory functions.


49-49: LGTM! Proper API producer mapping.

The LOCATION_AGENT is correctly mapped to include both makeSharedAPI (for common SDK functionality) and makeAgentAPI (for agent-specific features), following the established pattern for other locations.


172-176: LGTM! API producer follows established patterns.

The makeAgentAPI function correctly:

  • Follows the same signature as other API producers
  • Destructures the agent field from ConnectMessage
  • Delegates to createAgent for initialization
  • Returns an object exposing the agent API

The validation of the agent field is appropriately handled within createAgent.

lib/types/agent.types.ts (1)

1-12: LGTM! Clean and consistent type definitions.

The types are well-structured:

  • AgentContext clearly defines the agent context shape
  • AgentAPI follows the SDK's established pattern of returning VoidFunction for cleanup, consistent with other APIs like onMetadataChanged in asset/entry APIs

Based on learnings

Minor: Verify the metadata field requirement.

The metadata field on AgentContext (line 3) is required (not optional), though all its properties are optional. This means every AgentContext must have a metadata object, even if all fields are undefined.

If agent contexts might legitimately not have any metadata, consider making the field optional:

 export interface AgentContext {
   view: string
-  metadata: {
+  metadata?: {
     entryId?: string
     contentTypeId?: string
     lastFocusedFieldId?: string
   }
 }

If the current design is intentional (always require the object for simpler property access), this is fine as-is.

lib/types/api.types.ts (5)

25-25: LGTM! Required import for agent types.

The import correctly brings in the new agent-related types from the dedicated types file.


346-352: LGTM! AgentAppSDK type is well-defined.

The AgentAppSDK type correctly:

  • Extends BaseAppSDK with support for InstallationParameters
  • Sets InstanceParameters and InvocationParameters to never (appropriate for agent location, consistent with HomeAppSDK and ConfigAppSDK)
  • Adds the agent: AgentAPI property for agent-specific functionality

366-366: LGTM! Union type properly extended.

AgentAppSDK is correctly added to the KnownAppSDK union, maintaining type safety for all SDK locations.


405-405: LGTM! Location constant matches runtime definition.

The LOCATION_AGENT entry in the Locations interface correctly matches the runtime constant added in lib/locations.ts.


435-435: LGTM! Optional agent context in ConnectMessage.

Making the agent field optional is correct—it will only be present when the SDK is initialized at the LOCATION_AGENT location. The validation in createAgent ensures it's defined when needed.

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/types/agent.types.ts (1)

12-15: Consider whether the action field is necessary.

The action field is currently hardcoded to the literal 'click'. If all toolbar actions are clicks, this field adds no information and could be removed. Alternatively, if you anticipate other action types in the future (e.g., 'hover', 'focus'), consider making it a union type now.

Current structure:

export interface ToolbarAction {
  name: ToolbarActionName
  action: 'click'
}

Option 1 - Remove redundant field:

 export interface ToolbarAction {
   name: ToolbarActionName
-  action: 'click'
 }

Option 2 - Make it extensible:

+export type ActionType = 'click' | 'hover' | 'focus'
+
 export interface ToolbarAction {
   name: ToolbarActionName
-  action: 'click'
+  action: ActionType
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0e077af and a48a7c9.

📒 Files selected for processing (2)
  • lib/agent.ts (1 hunks)
  • lib/types/agent.types.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
lib/agent.ts (4)
lib/channel.ts (1)
  • Channel (26-86)
lib/types/api.types.ts (1)
  • ConnectMessage (408-436)
lib/types/agent.types.ts (3)
  • AgentAPI (17-20)
  • AgentContext (1-8)
  • ToolbarAction (12-15)
lib/signal.ts (2)
  • MemoizedSignal (24-57)
  • Signal (3-22)
lib/types/agent.types.ts (1)
lib/types/index.ts (2)
  • AgentContext (40-40)
  • AgentAPI (40-40)
🔇 Additional comments (8)
lib/types/agent.types.ts (2)

1-8: LGTM! Clean type definition for agent context.

The AgentContext structure is well-designed with appropriate optional fields for flexibility.


17-20: LGTM! Well-designed event handler API.

The API follows the standard pattern of returning an unsubscribe function, enabling proper cleanup.

lib/agent.ts (6)

1-4: LGTM! Imports are correct and minimal.

All imported types and classes are used appropriately in the implementation.


10-12: Approve the null check; consider deeper validation.

The null check is essential and correctly prevents invalid initialization. However, consider whether you should also validate that contextData contains the required fields (view, metadata) to catch malformed data early.

If runtime validation is desired:

  if (!contextData) {
    throw new Error('Context data is required')
  }
+ if (!contextData.view || !contextData.metadata) {
+   throw new Error('Context data must contain view and metadata')
+ }

14-16: LGTM! Appropriate signal types for state vs. events.

Using MemoizedSignal for context ensures new handlers immediately receive the current state, while using Signal for toolbar actions correctly treats them as ephemeral events.


18-24: LGTM! Channel handlers correctly wire events to signals.

The implementation properly bridges channel events to the signal dispatchers. The handlers are not explicitly unregistered, which is appropriate if createAgent is called only once per agent instance lifecycle.


26-29: LGTM! Clean API implementation with proper cleanup.

The returned object correctly implements the AgentAPI interface, and both methods return unsubscribe functions for proper listener cleanup.


18-24: Note: Past review comment about chat input handling.

A previous reviewer (tylerwashington888) mentioned that the agent should also handle consumption of editor/chat input (user typing messages). This functionality doesn't appear to be implemented in the current PR. Please confirm whether chat input handling is:

  • Out of scope for this PR and tracked separately
  • Already handled elsewhere in the codebase
  • Still needs to be added

Based on learnings

- Introduced LOCATION_AGENT in locations for agent-specific functionality.
- Added createAgent import and integrated makeAgentAPI for handling agent data in the API.
- Updated types to include AgentAppSDK and related agent context types.
- Updated context handling in createAgent to directly dispatch new context without intermediate variable.
- Added new connectMessageWithAgent mock for testing agent-specific functionality.
- Expanded unit tests to verify agent API integration, including context handling and error scenarios for missing agent data.
- Introduced a new Signal for toolbar actions in createAgent.
- Updated the AgentAPI to include onToolbarAction for handling toolbar events.
- Defined ToolbarAction type to standardize action structure.
- Updated agent tests to verify the presence of onToolbarAction method.
- Expanded context change handling tests to include toolbarAction event registration.
- Added new tests for toolbar action handling, including multiple handlers and action type verification.
@lewisjcs lewisjcs force-pushed the feat/agent-location-EXT-6966 branch from f146777 to 953cfcd Compare November 7, 2025 18:47
Copy link

@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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 953cfcd and e7d799d.

📒 Files selected for processing (2)
  • lib/agent.ts (1 hunks)
  • lib/types/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
lib/agent.ts (4)
lib/channel.ts (1)
  • Channel (26-86)
lib/types/api.types.ts (1)
  • ConnectMessage (408-436)
lib/types/agent.types.ts (3)
  • AgentAPI (17-20)
  • AgentContext (1-8)
  • ToolbarAction (12-15)
lib/signal.ts (2)
  • MemoizedSignal (24-57)
  • Signal (3-22)
🔇 Additional comments (6)
lib/types/index.ts (1)

37-40: LGTM! Clean integration of agent types into public API.

The new exports properly expose the agent-related types to SDK consumers, following the established pattern.

lib/agent.ts (5)

1-3: LGTM! Imports are clean and appropriate.

All required dependencies are properly imported.


5-11: LGTM! Good defensive validation.

The function correctly validates that contextData is provided before proceeding, preventing runtime errors downstream.


13-15: LGTM! Appropriate signal type choices.

Using MemoizedSignal for context ensures new subscribers immediately receive the current state, while regular Signal for toolbar actions correctly treats them as event-driven with no initial state.


17-23: Verify: Is chat input handling required?

A previous review comment (line 20) mentions: "i think we need to also handle the consumption of editor input right? The user types 'analyze my entries into the chat window and presses submit' out widget render pipeline needs to be able to consume this chat and pass it to the custom app"

The current implementation only handles contextChanged and toolbarAction events. Should there be a handler for user chat input, or is that functionality planned for a separate PR?


25-28: LGTM! Clean API surface following observer pattern.

The returned API correctly provides subscription methods that return detach functions, allowing consumers to clean up their own handlers.

@lewisjcs lewisjcs merged commit 2febd54 into main Nov 7, 2025
10 checks passed
@lewisjcs lewisjcs deleted the feat/agent-location-EXT-6966 branch November 7, 2025 21:01
contentful-automation bot added a commit that referenced this pull request Nov 11, 2025
# [4.46.0](v4.45.0...v4.46.0) (2025-11-11)

### Bug Fixes

* bump CircleCI Node version to 24.11.0 [] ([#2365](#2365)) ([e054b06](e054b06))
* **deps:** downgrade semantic-release and related dependencies ([#2366](#2366)) ([d27fde3](d27fde3))

### Features

* **api:** add agent location and API integration [EXT-6966] ([#2362](#2362)) ([2febd54](2febd54))
@contentful-automation
Copy link
Contributor

🎉 This PR is included in version 4.46.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants