feat(frontend): triggers & crons in the agent playground#4883
Conversation
Adds triggers/subscriptions/test (testTriggerSubscription) and tools resolveTools, plus is_test flag and related additive type drift.
Add a Triggers accordion section to the agent config panel that lists the current agent's persisted schedules and app subscriptions as rows, reusing the existing atom-driven trigger drawers, hooks, ActiveToggle and per-row actions. Triggers are scoped to the agent by matching any data.references id against the agent's app/variant/revision ids (resolved from the workflow molecule). Creating a schedule from the section default-binds it to the current agent via a new optional defaultReferences on the subscription and schedule drawer atoms.
Picking an event in the trigger catalog now opens the subscription config drawer prefilled with the chosen connection + event and pre-bound to the current agent via defaultReferences, completing the App trigger flow (catalog -> connect -> event -> config -> Test/Create). Also improves the workspace catalog (events were previously read-only).
The default-bind seed showed a raw id in the Bound workflow field (twice - the picker placeholder and a redundant secondary line). Thread a human- readable defaultBoundLabel (the agent's name) from the section through the catalog/schedule drawers, and drop the duplicate secondary text.
The trigger drawers/section used Tailwind `dark:` variants (a no-op in this app — dark mode is a CSS-variable token layer) and fixed hex tokens, so chips/borders/text rendered light-on-light in dark mode. Switch the inputs-mapping selector chips, editor/captured-event borders, schedule next-run text, and the Triggers row to antd semantic tokens (colorFillSecondary/colorText/colorBorder/colorTextSecondary/colorSuccess).
…gger drawers
Per the designs, replace the single Bound workflow picker with a By revision /
By environment toggle in both the subscription and schedule drawers. By
environment binds {environment(slug), application(slug)} so the trigger always
runs whatever revision is deployed (backend resolves via app slug + env);
By revision keeps the existing picker. Thread the agent's app slug through
defaultReferences for default-bind, and detect environment refs in edit mode.
…rigger-cron-playground
The backend test endpoint 500s when an event already has a subscription on
the connection (Composio upserts one trigger instance per (connection,event)
and trigger_id is unique). Detect an existing subscription for the chosen
connection+event and disable Test with a tooltip ('Already subscribed —
revoke it to test') instead of letting it 500. Band-aid; the real fix is
backend (tolerate an existing provider trigger instance).
…g title - Disable Create (not just Test) when the event already has a subscription on the connection, with a tooltip. - Abort the in-flight test long-poll when the drawer closes (form unmounts), so a pending wait clears instead of lingering. - Title the captured-event section 'Waiting for an event' while polling and 'Captured event' only after one arrives.
Give users time to trigger a real provider event during a subscription test (was 60s).
…iptions Add a right panel listing existing connections, each expandable to its subscriptions (event + pause/delete). Revoking a subscription here frees its event and clears the already-subscribed guard on the form. Widen the drawer to fit the form + panel.
…event A trigger fires server-side and never reaches the playground chat (dispatcher invoke_workflow → delivery, no session/stream). Add a 'Run in playground' action on a captured test event: it channels the event's resolved inputs into the current agent's active chat session via simulatedAgentRunAtomFamily (keyed by entityId), so the draft agent runs and streams there. Works for new (create drawer) and existing (row → edit drawer) triggers; the playground entityId is threaded through the drawer state and the catalog flow.
- Add a 'Run in playground' item to each trigger row's ⋯ menu: it replays the
trigger's latest captured delivery (a real event) into the agent's active
chat session; if none captured yet, prompts to Test first.
- Fix the catalog for the trigger context (was reusing the shared tools
catalog): retitle to 'Add an app trigger'/'Choose an event', drop the
misleading tool 'X actions' badge, and clarify the empty state ('This app
has no triggers'). Trigger-capable filtering of the 1047-integration list
still needs a backend change (catalog_service is shared with tools).
…fix connection filter - The app-trigger catalog now shows a 'Your connections' section (existing connections) above 'Or connect a new app', so users reuse a connection instead of connecting anew. Picking one jumps to that app's events. - Root-cause fix: queryTriggerConnections sent provider_key/integration_key in the request BODY, but the backend reads them as QUERY params — so the filter was dropped and ALL connections came back (a Gmail event prefilled a Slack connection). Send them as query params via projectScopedParams.
Restructure the app-trigger catalog into two panels: - Left 'Your connections': each existing connection is an accordion row that lazily loads its integration's trigger events on expand (ConnectionEventsList, mounted only when open). Picking an event creates the trigger directly — connection + event chosen in one place, skipping the separate events screen. 'View all events' falls back to the full events view when there are more. - Right 'Or connect a new app': the search + full integrations list (unchanged). When there are no connections the left panel is omitted and the browse panel fills the drawer.
…n drawer
The connection is now chosen upstream in the catalog ('Your connections' →
event), and in edit mode it is fixed — so the drawer's right-hand connections
panel (browse connections + their subscriptions, revoke to free an event) was
redundant clutter. Collapse to a single-panel form and narrow the drawer.
Managing/revoking existing subscriptions lives in the Triggers list.
… edit mode Two changes to the subscription drawer: 1. In the playground (playgroundEntityId set), Test is the primary action — it's the only one whose effect is visible here (capture a live event → Run in playground) and it persists nothing (the test subscription is always torn down server-side). Saving persists a server-side trigger whose effect surfaces in observability, so it's demoted to a secondary 'Save trigger'. In settings/edit contexts, persisting stays primary. 2. Fix Test failing with a 500 in edit mode. Editing a live subscription and hitting Test spun up a transient is_test sub for the SAME (connection, event) the live sub already occupies — the provider keeps one trigger instance per pair, so it collided (UniqueViolation). Edit-mode Test now polls the live subscription's own deliveries for a fresh event instead of creating a colliding test sub. Also retire the stale 'revoke it' tooltip wording (the connections panel it referenced is gone).
Edit-mode Test baselined against only the single newest delivery, so deliveries.find(d => d.id !== baselineId) matched the next-oldest delivery on the first poll and returned a previous event immediately instead of waiting. Remember the full set of pre-existing delivery ids and only surface one whose id was not seen before.
…yground Split the subscription drawer: left = config (persist-only footer, Save/Create), right = a TestPlaygroundPanel that captures events and runs them in the playground. - Edit mode lists the last 3 real deliveries (queryTriggerDeliveries) as cards for one-click Run in playground, and 'Wait for a new event' polls the live subscription for a fresh delivery (highlighted when captured). - New mode shows an empty state; 'Wait for an event' captures via the throwaway is_test subscription, then shows it as a card. - Run in playground closes the drawer so the chat is visible; it is hidden when there is no playground (workspace settings) per the settings-mode rule. - Moves all test/run state out of SubscriptionForm into the panel; the config footer now only persists.
- queryTriggerDeliveries isn't guaranteed newest-first, so a freshly captured event could land in the middle of the recent list. Sort by created_at desc before slicing to the last 3. - Each delivery card gets a 'View payload' action that opens a modal with the full captured payload (available even in settings mode, where Run in playground is hidden).
The Save footer was nested inside the left config panel, leaving the right test panel with a ragged bottom edge. Restructure into a column: the two panels share a flex-1 row, and a single full-width footer (Save/Create) sits beneath both.
…on drawer The footer now has two actions: Cancel (close without saving) and Save/Create. Save is enabled only when the config differs from its starting point — the loaded subscription in edit mode, or the empty/prefilled defaults in new mode — compared as normalized JSON so formatting changes don't count as edits.
A cron has no external event to wait for — its payload is the static inputs you configure. So the schedule drawer, when opened from a playground, gains a right panel whose 'Run in playground' simulates a scheduled tick: it channels the current static inputs straight into the agent's chat session (no save, no waiting, works on an unsaved schedule). Outside a playground (settings) it stays single-panel. Also gate Save on draft changes (normalized-JSON dirty check) and thread playgroundEntityId through ScheduleDrawerState + the open sites.
…tions
Replace the plain antd Table in the deliveries drawer with the InfiniteVirtualTable
(via useTableManager + InfiniteVirtualTableFeatureShell), backed by a proper
paginated data source.
- Scroll fix: the IVT owns a height-bounded, virtualized vertical scroll
container (autoHeight + flex-1 parent), so the body scrolls instead of the
drawer overflowing horizontally.
- New data source: triggerDeliveriesPaginatedStore (createPaginatedEntityStore)
in @agenta/entities/gatewayTrigger, driven by triggerDeliveriesOwnerAtom; the
drawer sets the owner on open and the store fetches per {subscription_id |
schedule_id}. (The deliveries endpoint isn't windowed yet, so it fetches once
and the IVT virtualizes; hasMore=false.)
- Row actions: per-delivery 'Run in playground' (replays the delivery's inputs
into the agent chat; threaded playgroundEntityId through DeliveriesDrawerState),
'View payload' (EnhancedModal with the full payload), and 'Copy event ID'.
The deliveries drawer is a read-only audit log with no table-level multi-row
actions, so the selection checkboxes/select-all add no value. Pass
rowSelection={undefined} to the feature shell.
The FE writes custom tools in the OpenAI shape {type:"function", function:{name,
description, parameters}}. coerce_tool_config only handled a gateway slug nested in
function.name and otherwise raised "Unsupported tool configuration shape" — which
500s server-side trigger dispatch and leaves the tool nameless ("undefined") in
the runner, where the agent loops calling a no-op tool.
Add a branch: a non-gateway function tool coerces to a ClientToolConfig, reading
the name from function.name (not top-level) and input_schema from
function.parameters, carrying needs_approval/render/permission through. Gateway
slugs are still handled above; client tools remain browser-fulfilled (this stops
the crash/undefined-name, it doesn't make them server-executable). Adds two
coercion tests.
…rigger-cron-playground
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds agent-scoped trigger management (subscriptions and schedules) to the agent drill-in view, with revision/environment dual-binding in trigger drawers, a virtualized deliveries drawer with per-row "Run in playground" replay via a new shared Jotai atom handoff consumed by the agent chat panel, plus minor fixes (query-param correction for connection filtering, test subscription timeout raised to 300 s). ChangesAgent Trigger Management & Run-in-Playground
Sequence Diagram(s)sequenceDiagram
actor User
participant TriggerManagementSection
participant TriggerDeliveriesDrawer
participant simulatedAgentRunAtomFamily
participant AgentChatPanel
User->>TriggerManagementSection: clicks "Run in playground" on a subscription/schedule row
TriggerManagementSection->>TriggerManagementSection: queryTriggerDeliveries → extract inputs
TriggerManagementSection->>simulatedAgentRunAtomFamily: set({text, nonce}) for entityId
AgentChatPanel->>simulatedAgentRunAtomFamily: read pendingRun for active entityId
AgentChatPanel->>AgentChatPanel: submit pendingRun.text via useAgentChatQueue
AgentChatPanel->>simulatedAgentRunAtomFamily: clear (set null)
User->>TriggerDeliveriesDrawer: clicks "Run in playground" on a delivery row
TriggerDeliveriesDrawer->>simulatedAgentRunAtomFamily: set({text, nonce}) for entityId
AgentChatPanel->>simulatedAgentRunAtomFamily: read and consume pendingRun
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
web/packages/agenta-entities/src/gatewayTrigger/state/deliveriesPaginatedStore.ts (1)
5-8: 🚀 Performance & Scalability | 🔵 TrivialVirtualization doesn’t cap the fetch size.
This still loads the entire delivery history before the drawer can render, so busy triggers can turn open into a large payload + parse cost even though the table itself is virtualized. If this view needs to handle long-lived owners, add backend pagination or at least a server-side recency cap/filter before relying on IVT here.
Also applies to: 52-74
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1c461203-08e1-4c1b-95f2-dc5fdfae04e6
📒 Files selected for processing (17)
api/oss/src/core/triggers/service.pysdks/python/agenta/sdk/agents/tools/compat.pysdks/python/oss/tests/pytest/unit/agents/tools/test_parsing.pyweb/oss/src/components/AgentChatSlice/AgentChatPanel.tsxweb/packages/agenta-entities/src/gatewayTrigger/api/api.tsweb/packages/agenta-entities/src/gatewayTrigger/index.tsweb/packages/agenta-entities/src/gatewayTrigger/state/atoms.tsweb/packages/agenta-entities/src/gatewayTrigger/state/deliveriesPaginatedStore.tsweb/packages/agenta-entities/src/gatewayTrigger/state/index.tsweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsxweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerCatalogDrawer.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerScheduleDrawer.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsxweb/packages/agenta-shared/src/state/index.tsweb/packages/agenta-shared/src/state/simulatedAgentRun.ts
- compat.py: keep ClientToolConfig's default object schema for no-arg
function tools instead of overriding it with a bare {}
- AgentChatPanel: route Run-in-playground through the chat queue (submit)
so it respects HITL/queued ordering instead of a raw sendMessage
- AgentTemplateControl: render section extra in the tabs layout so the
add actions (tools/MCP/skills/triggers) work there too
- TriggerManagementSection: target-guard row keyboard activation and
respect read-only mode for row-open + edit/refresh/revoke/delete
- TriggerDeliveriesDrawer: clear payload-modal state on drawer close
- TriggerSubscriptionDrawer: read the bound revision id from all ref keys
via a shared extractBoundRevId (kills application_variant read/write drift)
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsx (2)
101-107: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPass the same fallback value into
statusColor.The label already falls back to
record.status?.code, but the color still reads onlyrecord.status?.type. Rows that only havecodewill show the right text with the wrong default color.Suggested fix
const type = record.status?.type ?? record.status?.code return ( <Tooltip title={record.status?.message ?? undefined}> - <Tag color={statusColor(record.status?.type)}> + <Tag color={statusColor(type)}> {type ?? "unknown"} </Tag> </Tooltip> )
145-147: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDon't collapse primitive results into
-.
!resulttreats0,false, and""as empty, so successful deliveries with primitive outputs render as missing data.Suggested fix
const result = record.data?.result - if (!result || Object.keys(result).length === 0) { + if ( + result == null || + (typeof result === "object" && Object.keys(result).length === 0) + ) { return <Typography.Text type="secondary">-</Typography.Text> }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a0560b96-118f-40a0-b179-c75463e5af1f
📒 Files selected for processing (7)
sdks/python/agenta/sdk/agents/tools/compat.pysdks/python/oss/tests/pytest/unit/agents/tools/test_parsing.pyweb/oss/src/components/AgentChatSlice/AgentChatPanel.tsxweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsxweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsxweb/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- sdks/python/oss/tests/pytest/unit/agents/tools/test_parsing.py
- web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
- web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsx
- web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx
Address CodeRabbit follow-up: only the explicit OpenAI `type:"function"` shape coerces to a client tool. An unknown/typo'd config that merely carries a stray `function.name` now fails loud (ToolConfigurationError) instead of silently becoming a client tool. Adds a test for the rejection.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…rigger-cron-playground # Conflicts: # web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
The OpenAI function-shape -> client tool coercion (compat.py + tests) is an agent-runtime fix unrelated to triggers/cron; removing it from this branch so the PR is just the FE feature + the trigger-related test long-poll timeout.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…ilter contract The backend reads provider_key/integration_key as Query params, so the FE sends them in the query string with an empty POST body. Update the unit test to assert the filters land in opts.params (not the body), matching the implementation.
What
Adds the triggers & crons surface to the agent playground. Authors can attach event subscriptions (Composio) and scheduled (cron) triggers to an agent, browse a catalog of their existing connections, test/replay deliveries, and pipe a captured event — or a simulated scheduled tick — straight into a playground chat session via Run in playground.
Stacks on
big-agents(the base of this PR). All FE work lives in the@agenta/*packages.Highlights
Config panel
App-trigger catalog
Subscription drawer
Schedule (cron) drawer
Deliveries drawer
InfiniteVirtualTable(fixes the broken scroll) with a proper paginated entity store and per-row actions (Run in playground / View payload / Copy event ID). No row selection (read-only audit log).SDK
coerce_tool_confignow maps the OpenAI{type:"function", function:{…}}shape to a client tool (reading the name fromfunction.name) instead of rejecting it with "Unsupported tool configuration shape". Adds unit tests.Notes
big-agentsmerge is clean; the only shared file is the generated Fern barrel, where both sides only addexport *lines — git produces the same additive union codegen would, so no regeneration is needed.Testing
uv run python -m pytest sdks/python/oss/tests/pytest/unit/agents/tools/test_parsing.py(passing).