Skip to content

feat(core): run tasks from resource events - #1176

Draft
dcramer wants to merge 22 commits into
mainfrom
codex/core-event-tasks
Draft

feat(core): run tasks from resource events#1176
dcramer wants to merge 22 commits into
mainfrom
codex/core-event-tasks

Conversation

@dcramer

@dcramer dcramer commented Jul 31, 2026

Copy link
Copy Markdown
Member

Junior can now persist event tasks that run when provider plugins publish normalized resource events. Tasks use scheduler-aligned destination permissions and credential delegation, support multiple selected event types and multiple tasks for the same event, and dispatch with retry-safe Redis deduplication and rate limits.

The plugin contract binds each event to its plugin manifest namespace and matches both event tasks and resource subscriptions on namespace + identifier + event type; the model-facing tools expose installed plugin namespaces as an enum. GitHub now publishes pull request, deployment, and issue events, including repository-wide or exact-issue selectors and multi-state issue tasks.

Webhook verification remains provider-owned and event payloads remain untrusted at the core dispatch boundary. Existing ephemeral resource-subscription records use a versioned Redis key and expire normally instead of requiring migration; durable event tasks are stored in SQL.

Refs #1174

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview Aug 1, 2026 7:43pm

Request Review

Comment thread packages/junior/src/chat/event-tasks/store.ts Outdated
Comment thread packages/junior/src/chat/tools/event-tasks.ts
Comment thread packages/junior/src/chat/event-tasks/ingest.ts Outdated
Comment thread packages/junior/src/chat/tools/event-tasks.ts
Comment thread packages/junior/migrations/0014_normal_kat_farrell.sql Outdated
Comment thread packages/junior/src/chat/tools/event-tasks.ts Outdated
Comment thread packages/junior/src/chat/event-tasks/ingest.ts Outdated
Comment thread packages/junior/src/chat/tools/event-tasks.ts
Comment thread packages/junior/src/chat/tools/event-tasks.ts
Comment thread packages/junior/src/chat/event-tasks/ingest.ts Outdated
Comment thread packages/junior/src/chat/event-tasks/ingest.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4972148. Configure here.

if (result.status === "already_exists") {
await state.set(key, count, ttlMs);
}
return result;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Retry failures burn dispatch quota

Medium Severity

dispatchWithinRateLimit increments the per-task hourly counter before dispatch() runs, and only decrements on already_exists. Validation, credential binding, and verify in dispatchEventTask happen after that increment. Duplicate provider retries that fail before a dispatch record exists therefore keep consuming quota, which can rate-limit the task and silently drop later matched events for the rest of the window.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4972148. Configure here.

@dcramer
dcramer force-pushed the codex/core-event-tasks branch from 4972148 to b6bb22f Compare August 1, 2026 17:36
@dcramer dcramer changed the title feat(core): add event tasks feat(core): run tasks from resource events Aug 1, 2026
Comment on lines +29 to +32
identifier: z.string().describe("Plugin-defined resource identifier."),
namespace: eventNamespaceSchema(catalog).describe(
"Enabled plugin namespace for this resource.",
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Subscriptions accept arbitrary namespace/identifier with no resource access check

The subscribe tool takes a model-supplied identifier and namespace and only validates them against the enabled-plugin catalog — nothing verifies that this conversation or its users are authorized to see the resource (e.g., a private GitHub repo), so any Slack thread can subscribe to events for resources it cannot access and receive their summaries and untrusted payload content.

Evidence
  • subscribeInputSchema validates namespace only via eventNamespaceSchema(catalog) (membership in enabled plugin namespaces) and events via pluginSupportsEvent; identifier is a bare z.string() with no ownership or access validation.
  • createSubscribeToResourceEventsTool.execute calls createResourceEventSubscription storing namespace/identifier verbatim after only conversation-shape checks (requireConversationContext only requires a Slack thread id).
  • findMatchingResourceEventSubscriptions in chat/resource-events/store.ts matches purely on namespace + identifier + eventType with no per-conversation permission filter, and enqueueResourceEventNotification delivers trustedSummary plus untrustedText (provider payload content, e.g. PR/issue data) into the subscribing thread.
  • No credential delegation or destination-permission check exists on this subscription path (the PR's scheduler-aligned permissions apply to event tasks, not these conversation subscriptions). The subscribe tool also has no approvalMode, so it executes without human review.
Also found at 1 additional location
  • packages/junior/src/chat/tools/index.ts:141

Identified by Warden · security-review · H7P-GW5

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