Skip to content

feat(calendar): schedule agent on events + event type filter - #1147

Merged
2witstudios merged 5 commits into
masterfrom
pu/schedule-an-agent
Apr 29, 2026
Merged

2witstudios merged 5 commits into
masterfrom
pu/schedule-an-agent

Conversation

@2witstudios

@2witstudios 2witstudios commented Apr 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Schedule agent UI: New "Run agent" section in the event creation modal (drive context, new events only). A toggle reveals an agent picker (populated from the drive's AI agents) and a prompt field. On save, creates a `calendarTriggers` row so the cron poller fires the agent at event start time. The switch is disabled (with an explanatory note) when the drive has no AI agents.
  • API: accept agent trigger on POST: `POST /api/calendar/events` now accepts an optional `agentTrigger: { agentPageId, prompt }`. Agent is validated (must be `AI_CHAT`, not trashed, in same drive, user must have view permission via `canUserViewPage`) before any DB writes. The event, attendees, and trigger rows are created in a single `db.transaction()` so a partial failure leaves no orphaned data.
  • API: sync trigger on PATCH: `PATCH /api/calendar/events/[eventId]` now updates `calendar_triggers.triggerAt` atomically (same `db.transaction()`) when `startAt` changes. Only `pending` triggers are synced; completed/failed/cancelled rows are left untouched.
  • API: annotate events on GET: Both GET branches annotate each event with `hasAgentTrigger: boolean` via a secondary batch query on `calendarTriggers`. Only active (non-cancelled, non-failed) triggers count.
  • Event type filter: New `hiddenEventTypes` state in `useCalendarFilterStore` (persisted, typed as `'agent' | 'user'` literal union). Drive calendar header gets "Events" / "Agents" Toggle chips; user calendar sidebar gets an "Event types" section with User events / Agent events checkboxes. Wired into both desktop (`CalendarView`) and mobile (`MobileCalendarView`) paths.
  • Prompt validation: `agentTrigger.prompt` is validated with `.trim().min(1)` so whitespace-only strings are rejected server-side.

Test plan

  • Open a drive calendar → "New Event" → confirm "Run agent" section appears with toggle
  • Open personal calendar → "New Event" → confirm section is absent
  • Toggle "Run agent" on → agent dropdown populates with drive's AI agents
  • Select agent + enter prompt → save → event appears on calendar
  • Confirm `calendar_triggers` row exists with `status='pending'` and correct `triggerAt`
  • Reschedule event via PATCH → confirm `calendar_triggers.triggerAt` updates to match new `startAt`
  • Cancel/fail a trigger, then GET events → `hasAgentTrigger` is `false` for that event
  • Toggle "Agents" filter off in drive header → agent-triggered events disappear
  • Toggle "Events" filter off → regular events disappear, agent events remain
  • User calendar sidebar: both filter checkboxes work correctly
  • Mobile sidebar sheet: same filter toggles appear and work
  • Drive with no agents: "Run agent" switch is disabled with explanatory message
  • Filter state persists across page reload (localStorage)
  • POST with whitespace-only prompt → 400 error

🤖 Generated with Claude Code

- Add "Run agent" section to EventModal (drive context, new events only)
- Fetches drive agents from /api/drives/[driveId]/agents for picker
- POST /api/calendar/events now accepts agentTrigger (agentPageId + prompt)
- Validates agent exists in drive before insert, then creates calendarTriggers row
- GET /api/calendar/events annotates each event with hasAgentTrigger boolean
- Extend useCalendarFilterStore with hiddenEventTypes (persisted)
- Drive calendar header: Events/Agents toggle chips
- User calendar sidebar: Event types section with User/Agent checkboxes
- filteredEvents respects hiddenEventTypes in CalendarView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@2witstudios has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 37 minutes and 37 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b0dbf47-0839-4bf1-b000-436497a5226b

📥 Commits

Reviewing files that changed from the base of the PR and between e29a432 and 6b60619.

📒 Files selected for processing (6)
  • apps/web/src/app/api/calendar/events/[eventId]/__tests__/can-edit-event.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/route.ts
  • apps/web/src/app/api/calendar/events/route.ts
  • apps/web/src/components/calendar/EventModal.tsx
  • apps/web/src/components/calendar/MobileCalendarView.tsx
  • apps/web/src/components/calendar/__tests__/CalendarSidebar.test.tsx
📝 Walkthrough

Walkthrough

Adds agent-trigger scheduling for calendar events: POST accepts optional agentTrigger (agentPageId, prompt); server validates drive-scoped, non-recurring events and agent page access, inserts calendarTriggers; GET annotates events with hasAgentTrigger. UI supports creating agent-scheduled events and filtering user vs agent events.

Changes

Cohort / File(s) Summary
API Route Handlers
apps/web/src/app/api/calendar/events/route.ts, apps/web/src/app/api/calendar/events/[eventId]/route.ts
POST handler accepts agentTrigger, enforces drive-scoped & non-recurring constraints, validates agent page visibility, inserts calendarTriggers; PATCH updates calendarTriggers.triggerAt when start time changes; GET annotates events with hasAgentTrigger.
Calendar Types & Data Layer
apps/web/src/components/calendar/calendar-types.ts, apps/web/src/components/calendar/useCalendarData.ts
Added optional hasAgentTrigger?: boolean to CalendarEvent. createEvent input type accepts optional agentTrigger: { agentPageId: string; prompt: string }.
Calendar UI Components
apps/web/src/components/calendar/EventModal.tsx, apps/web/src/components/calendar/CalendarView.tsx, apps/web/src/components/calendar/CalendarSidebar.tsx
EventModal: agent-scheduling UI, fetch drive agents, validate selection/prompt, include agentTrigger on save. CalendarView & Sidebar: event-type visibility state passed/consumed and UI toggles for user vs agent events; filtering applied to displayed events.
Calendar Filter Store
apps/web/src/stores/useCalendarFilterStore.ts
Adds hiddenEventTypes, toggleEventType, isEventTypeVisible, and persistence for event-type visibility (types: `'agent'

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Client
    participant Modal as EventModal
    participant Agents as Agents Service
    participant API as Calendar API
    participant DB as Database

    User->>Modal: Open new event modal (drive context)
    Modal->>Agents: GET /api/drives/{driveId}/agents
    Agents-->>Modal: agent list
    User->>Modal: Enable agent scheduling, select agent, enter prompt, Save
    Modal->>Modal: Validate agent selection & prompt
    Modal->>API: POST /api/calendar/events { eventData, agentTrigger }
    API->>API: Validate drive-scoped & non-recurring
    API->>DB: INSERT calendar_event (+ attendees)
    API->>DB: INSERT calendarTriggers {calendarEventId, agentPageId, prompt, triggerAt, ...}
    DB-->>API: OK
    API-->>Modal: 201 Created (hasAgentTrigger: true)
Loading
sequenceDiagram
    participant View as CalendarView
    participant API as Calendar API
    participant Store as Calendar Filter Store
    participant Sidebar as CalendarSidebar
    participant User as User

    View->>API: GET /api/calendar/events
    API-->>View: events[] with hasAgentTrigger flags
    View->>Store: read hiddenEventTypes
    View->>View: filter events by hiddenEventTypes (agent/user)
    View->>Sidebar: pass visibility booleans & toggle handlers
    User->>Sidebar: toggle agent/user visibility
    Sidebar->>Store: toggleEventType(type)
    Store-->>View: updated hiddenEventTypes (subscriber)
    View->>User: render filtered events
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hop and plan with prompt in paw,

I schedule thoughts without a flaw,
Agent and user, side by side,
Filters set—let calendars bide,
A gentle nudge, and tasks take flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 PR title accurately summarizes the two main features: agent scheduling on calendar events and event type filtering. It is concise, specific, and clearly reflects the core changes.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pu/schedule-an-agent

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 37 minutes and 37 seconds.

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a55a1ab6f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +573 to +576
// Create agent trigger if requested (validation already done above)
if (data.agentTrigger && agentPageId && data.driveId) {
await db.insert(calendarTriggers).values({
calendarEventId: event.id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep trigger schedule in sync with event time changes

This POST path creates a calendar_triggers row tied to the event start time, but no linkage/update is added for later edits, so moving the event in /api/calendar/events/[eventId] will update calendar_events.startAt without updating calendar_triggers.triggerAt. In practice, a user can create a “Run agent” event, reschedule it in the calendar UI, and the agent will still fire at the original time. Since this commit introduces trigger creation from the main calendar flow, it also needs a corresponding trigger-resync path (or equivalent metadata+transaction strategy) to avoid stale executions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in e29a432: the PATCH handler in /api/calendar/events/[eventId]/route.ts now updates calendar_triggers.triggerAt to match the new startAt when the event is rescheduled (only for status = 'pending' rows).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in d41ed21: the PATCH handler in /api/calendar/events/[eventId]/route.ts now wraps both the event-time update and the calendar_triggers.triggerAt sync in a single db.transaction(). When startAt changes, the transaction updates calendarEvents and immediately updates any pending trigger row to the new time — only pending rows are touched so completed, failed, and cancelled triggers are not affected.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/api/calendar/events/route.ts`:
- Around line 573-584: The calendar trigger insert must be made atomic with the
event and attendee inserts: wrap the event creation, attendee inserts, and the
calendarTriggers insert in a single database transaction so all three commit or
rollback together; specifically, update the handler to run db.transaction (or
your ORM's transaction API) around the code that creates the event (where
event.id is produced), inserts attendees, and the
db.insert(calendarTriggers).values(...) call (referenced as calendarTriggers and
the db.insert(...).values(...) call) and ensure you use the event id returned
from the transaction-scoped insert when creating the trigger so any failure
rolls back the event and attendees to avoid partial duplicates.
- Around line 54-57: The agentTrigger.prompt Zod schema currently allows strings
of only whitespace; update the schema for agentTrigger (the agentTrigger object
in route.ts) to validate trimmed non-empty input, e.g. use
z.string().trim().min(1).max(10000) or add a .transform(s => s.trim()) followed
by .refine(s => s.length > 0) so blank prompts like "   " are rejected.

In `@apps/web/src/components/calendar/CalendarSidebar.tsx`:
- Around line 110-186: The interactive filter containers currently make the
outer div focusable and handle clicks via onToggleUserEvents/onToggleAgentEvents
while the checkbox semantics (role="checkbox" and aria-checked) are on the
nested span, so screen readers won't announce the checked state; move the
accessible state to the actionable element by applying role="checkbox",
aria-checked={userEventsVisible|agentEventsVisible} and aria-label to the outer
focusable element (the divs handling onClick/onKeyDown) or replace the outer div
with a native button/input element, ensuring
onToggleUserEvents/onToggleAgentEvents remain the handlers and that the inner
span no longer duplicates role/aria attributes (keep visual checkbox markup
only).

In `@apps/web/src/components/calendar/CalendarView.tsx`:
- Around line 422-448: The new event-type filter UI added in CalendarView is
only rendered for non-user desktop layouts and isn't exposed to mobile because
MobileCalendarView is returned earlier without receiving the event-filter state
or handlers; update CalendarView to pass the isEventTypeVisible and
toggleEventType props (or a consolidated eventFilter props object) into
MobileCalendarView and implement corresponding props in MobileCalendarView to
render the same toggles (or a mobile-friendly UI) so mobile users can toggle
'user' and 'agent' filters; search for CalendarView, MobileCalendarView,
isEventTypeVisible, and toggleEventType to locate the affected code and wire the
props through.

In `@apps/web/src/components/calendar/EventModal.tsx`:
- Around line 407-468: The Run agent flow currently allows enabling scheduling
even when there are no agents; update the UI logic around canScheduleAgent /
Switch (id="schedule-agent") and the state used by selectedAgentId so the switch
is disabled (or the section replaced by an empty state) when agentsLoading is
true or agents.length === 0; ensure the Switch's disabled prop is tied to that
condition and the Select is not selectable, and when agents become unavailable
clear selectedAgentId/agentPrompt to a safe default to avoid save-time
validation loops in the save handler (e.g., when scheduleAgent is true but no
valid selectedAgentId).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1202d965-9218-4f81-a05d-9ced227587b2

📥 Commits

Reviewing files that changed from the base of the PR and between 90d2f6c and a55a1ab.

📒 Files selected for processing (7)
  • apps/web/src/app/api/calendar/events/route.ts
  • apps/web/src/components/calendar/CalendarSidebar.tsx
  • apps/web/src/components/calendar/CalendarView.tsx
  • apps/web/src/components/calendar/EventModal.tsx
  • apps/web/src/components/calendar/calendar-types.ts
  • apps/web/src/components/calendar/useCalendarData.ts
  • apps/web/src/stores/useCalendarFilterStore.ts

Comment thread apps/web/src/app/api/calendar/events/route.ts
Comment thread apps/web/src/app/api/calendar/events/route.ts Outdated
Comment thread apps/web/src/components/calendar/CalendarSidebar.tsx
Comment thread apps/web/src/components/calendar/CalendarView.tsx
Comment thread apps/web/src/components/calendar/EventModal.tsx
- enforce canUserViewPage on agent trigger creation (P1)
- sync trigger triggerAt when event is moved (P2)
- filter cancelled/failed triggers from hasAgentTrigger
- narrow event type store to 'agent'|'user' literal union
- fix ARIA nesting in sidebar event type filters
- guard __none sentinel in EventModal save path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/src/app/api/calendar/events/route.ts (2)

581-592: ⚠️ Potential issue | 🟠 Major

Make event + attendee + trigger writes atomic.

If Line 583 insert fails, the event is already created and this returns 500 with partial state.

💡 Suggested refactor pattern
-    // Create the event
-    const [event] = await db
-      .insert(calendarEvents)
-      .values({ ... })
-      .returning();
-
-    await db.insert(eventAttendees).values({ ...creator... });
-    // ...insert other attendees...
-
-    if (data.agentTrigger && agentPageId && data.driveId) {
-      await db.insert(calendarTriggers).values({ ... });
-    }
+    const event = await db.transaction(async (tx) => {
+      const [createdEvent] = await tx
+        .insert(calendarEvents)
+        .values({ ... })
+        .returning();
+
+      await tx.insert(eventAttendees).values({ ...creator... });
+      // ...insert other attendees via tx...
+
+      if (data.agentTrigger && agentPageId && data.driveId) {
+        await tx.insert(calendarTriggers).values({ ... });
+      }
+
+      return createdEvent;
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/api/calendar/events/route.ts` around lines 581 - 592, The
event, attendee and trigger inserts must be executed inside a single DB
transaction so a failure (e.g., the calendarTriggers insert) won't leave a
partially-created event; wrap the creation of the event (calendarEvents), the
attendee insert (calendarAttendees) and the trigger insert (calendarTriggers) in
one transaction using db.transaction and perform the inserts via the transaction
object (e.g., tx.insert(...).values(...)), capture the created event id from the
tx result and use it for subsequent inserts, and rollback automatically on
error.

54-57: ⚠️ Potential issue | 🟡 Minor

Reject whitespace-only agent prompts at API boundary.

Line 56 still allows " " because .min(1) checks raw length, not trimmed content.

💡 Suggested fix
   agentTrigger: z.object({
     agentPageId: z.string(),
-    prompt: z.string().min(1).max(10000),
+    prompt: z.string().trim().min(1).max(10000),
   }).optional(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/api/calendar/events/route.ts` around lines 54 - 57, The
prompt field in the agentTrigger schema currently uses z.string().min(1) which
permits whitespace-only strings; update the agentTrigger: z.object({
agentPageId: z.string(), prompt: z.string().min(1).max(10000), }).optional() by
tightening the prompt validator to reject whitespace-only input—either call
.trim().min(1) if your Zod version supports trim, or add .refine(p =>
p.trim().length > 0, { message: "Prompt cannot be empty or whitespace only" })
(and keep the .max(10000)); modify the prompt schema in route.ts accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/api/calendar/events/`[eventId]/route.ts:
- Around line 244-255: The event time update and the calendarTriggers update
must be performed in a single DB transaction to avoid leaving triggers stale if
the second update fails; wrap the existing event update and the conditional
block that updates calendarTriggers (the code that calls db.update(...) on
calendar events and the block that calls db.update(calendarTriggers).set({
triggerAt: adjustedStartAt }) where eq(calendarTriggers.calendarEventId,
eventId) and status is 'pending') inside a single db.transaction (use the same
transaction handle, e.g. tx) so both updates use tx.update(...) and the
transaction is committed/rolled back atomically.

---

Duplicate comments:
In `@apps/web/src/app/api/calendar/events/route.ts`:
- Around line 581-592: The event, attendee and trigger inserts must be executed
inside a single DB transaction so a failure (e.g., the calendarTriggers insert)
won't leave a partially-created event; wrap the creation of the event
(calendarEvents), the attendee insert (calendarAttendees) and the trigger insert
(calendarTriggers) in one transaction using db.transaction and perform the
inserts via the transaction object (e.g., tx.insert(...).values(...)), capture
the created event id from the tx result and use it for subsequent inserts, and
rollback automatically on error.
- Around line 54-57: The prompt field in the agentTrigger schema currently uses
z.string().min(1) which permits whitespace-only strings; update the
agentTrigger: z.object({ agentPageId: z.string(), prompt:
z.string().min(1).max(10000), }).optional() by tightening the prompt validator
to reject whitespace-only input—either call .trim().min(1) if your Zod version
supports trim, or add .refine(p => p.trim().length > 0, { message: "Prompt
cannot be empty or whitespace only" }) (and keep the .max(10000)); modify the
prompt schema in route.ts accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c66f810-b402-4ac1-921b-557fe0c8f006

📥 Commits

Reviewing files that changed from the base of the PR and between a55a1ab and e29a432.

📒 Files selected for processing (5)
  • apps/web/src/app/api/calendar/events/[eventId]/route.ts
  • apps/web/src/app/api/calendar/events/route.ts
  • apps/web/src/components/calendar/CalendarSidebar.tsx
  • apps/web/src/components/calendar/EventModal.tsx
  • apps/web/src/stores/useCalendarFilterStore.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/stores/useCalendarFilterStore.ts
  • apps/web/src/components/calendar/EventModal.tsx

Comment thread apps/web/src/app/api/calendar/events/[eventId]/route.ts Outdated
2witstudios and others added 3 commits April 28, 2026 21:13
CalendarSidebar gained 4 required props in the new feature
commit but MobileCalendarView.tsx and its test file were not
updated, causing TS build failures in CI. Wire useCalendarFilterStore
into MobileCalendarView and supply the new props to CalendarSidebar.
Update all CalendarSidebar test renders with default values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- wrap POST event+attendees+trigger in a single db.transaction so
  a trigger insert failure cannot leave a partial event in the DB
- wrap PATCH event+trigger-sync in a single db.transaction so
  a trigger update failure cannot leave stale triggerAt
- add .trim() to agentTrigger.prompt schema so whitespace-only
  strings are rejected server-side
- disable the 'Run agent' switch and show a message when the drive
  has no AI agent pages, preventing a dead-end save loop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit 9c38a26 into master Apr 29, 2026
10 checks passed
@2witstudios
2witstudios deleted the pu/schedule-an-agent branch April 29, 2026 22:09
2witstudios added a commit that referenced this pull request May 15, 2026
* feat(calendar): schedule agent on events + type filter

- Add "Run agent" section to EventModal (drive context, new events only)
- Fetches drive agents from /api/drives/[driveId]/agents for picker
- POST /api/calendar/events now accepts agentTrigger (agentPageId + prompt)
- Validates agent exists in drive before insert, then creates calendarTriggers row
- GET /api/calendar/events annotates each event with hasAgentTrigger boolean
- Extend useCalendarFilterStore with hiddenEventTypes (persisted)
- Drive calendar header: Events/Agents toggle chips
- User calendar sidebar: Event types section with User/Agent checkboxes
- filteredEvents respects hiddenEventTypes in CalendarView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(calendar): review fixes for agent scheduling

- enforce canUserViewPage on agent trigger creation (P1)
- sync trigger triggerAt when event is moved (P2)
- filter cancelled/failed triggers from hasAgentTrigger
- narrow event type store to 'agent'|'user' literal union
- fix ARIA nesting in sidebar event type filters
- guard __none sentinel in EventModal save path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(calendar): wire event type filter props to MobileCalendarView

CalendarSidebar gained 4 required props in the new feature
commit but MobileCalendarView.tsx and its test file were not
updated, causing TS build failures in CI. Wire useCalendarFilterStore
into MobileCalendarView and supply the new props to CalendarSidebar.
Update all CalendarSidebar test renders with default values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(calendar): atomicity, prompt trimming, and empty-agent UX

- wrap POST event+attendees+trigger in a single db.transaction so
  a trigger insert failure cannot leave a partial event in the DB
- wrap PATCH event+trigger-sync in a single db.transaction so
  a trigger update failure cannot leave stale triggerAt
- add .trim() to agentTrigger.prompt schema so whitespace-only
  strings are rejected server-side
- disable the 'Run agent' switch and show a message when the drive
  has no AI agent pages, preventing a dead-end save loop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(calendar): update PATCH test mock for db.transaction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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