Skip to content

feat(agents): dedicated trigger tools for calendar and task scheduling - #1615

Merged
2witstudios merged 15 commits into
masterfrom
pu/agents-trigger-config
Jun 12, 2026
Merged

2witstudios merged 15 commits into
masterfrom
pu/agents-trigger-config

Conversation

@2witstudios

@2witstudios 2witstudios commented Jun 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

Agents were consistently failing to schedule triggers because the tool surfaces were named and structured for implementation intent, not agent intent:

  • Calendar triggers: buried inside `create_calendar_event` as a nested `agentTrigger` param — nothing in the name indicated it was a scheduling primitive
  • Task triggers: REST-only (`PUT /api/tasks/[taskId]/triggers`) — no AI tool path at all
  • Agents were attempting trigger config via `update_agent_config` and failing silently

Root cause: Three surfaces, three discovery anti-patterns. Only cron (`create_workflow`) was correctly named for its purpose.

Changes

New file: `trigger-tools.ts` — 4 intention-named, flat-schema tools:

Tool What it does
`set_calendar_trigger` Attach/upsert trigger on an existing event, or create a new scheduling event. Two modes.
`delete_calendar_trigger` Remove trigger from event (idempotent, doesn't delete the event).
`set_task_trigger` Expose REST-only task trigger path to agents. Upserts on existing tasks.
`delete_task_trigger` Disable task trigger and recompute task metadata atomically (db.transaction).

All tools reuse existing service layer helpers — no new DB logic.

Permissions — full actor-scoped permission model throughout:

  • Calendar tools: event creator OR drive admin (via `canManageEvent` + `canActorManageDrive`), plus `driveDeniedByAppToken` for MCP token scoping
  • Task tools: `canActorEditPage(ctx, taskListPageId)` respects agent ACL, MCP drive scope, and app-role ceiling; `driveDeniedByAppToken` blocks MCP tokens lacking drive access

Description redirects — 4 existing tools now steer agents to the dedicated trigger tools:

  • `create_calendar_event` / `update_calendar_event` → `set_calendar_trigger`
  • `update_task` (Agent Triggers section) → `set_task_trigger`
  • `create_workflow` → new dedicated tools instead of entity-creation tools
  • `update_agent_config` → explicitly states it does not manage triggers

Registry coverage — 4 new `ActionResultRenderer` entries in `registry.tsx` and corresponding mock/import/spread entries in `ai-tools.test.ts`.

Test plan

  • 23 unit tests in `trigger-tools.test.ts` — all green (covers token denial, actor permissions, parseDateTime error path)
  • `bun run --filter web typecheck` — clean
  • `bun run --filter web lint` — clean
  • CI Unit Tests green (runs 27385798729, 27386095883)
  • CI green on latest push
  • Manual: "schedule yourself to run at 9am tomorrow" → should call `set_calendar_trigger`, not `create_calendar_event`
  • Manual: "run when this task completes" → should call `set_task_trigger`, not modify `update_agent_config`

… task scheduling

Agents were failing at calendar trigger setup because the only path was
a nested agentTrigger param buried inside create_calendar_event — nothing
in the tool name indicated it was the scheduling primitive. Task triggers
had no AI tool at all (REST-only).

Add four explicit, intention-first tools:
- set_calendar_trigger: attach to existing event OR create new backing event
- delete_calendar_trigger: remove trigger without deleting the event
- set_task_trigger: upsert due_date/completion trigger on an existing task
- delete_task_trigger: disable a task trigger

All four reuse the existing service layer (upsertCalendarTriggerWorkflow,
createTaskTriggerWorkflow, removeCalendarTrigger, recomputeTaskTriggerMetadata)
and the shared agentTriggerBaseSchema validation. Update create_workflow
description to point to the new tools instead of the buried agentTrigger params.

20 unit tests, typecheck clean.

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

vercel Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
pagespace-marketing Ready Ready Preview, Comment Jun 12, 2026 1:00am

@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@2witstudios, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 12 minutes and 38 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8059436b-a1f1-4d61-87c3-fed395e1aef5

📥 Commits

Reviewing files that changed from the base of the PR and between e20306c and c75c6dd.

📒 Files selected for processing (6)
  • apps/web/src/components/ai/shared/chat/tool-calls/registry.tsx
  • apps/web/src/lib/ai/core/__tests__/ai-tools.test.ts
  • apps/web/src/lib/ai/core/ai-tools.ts
  • apps/web/src/lib/ai/core/tool-filtering.ts
  • apps/web/src/lib/ai/tools/__tests__/trigger-tools.test.ts
  • apps/web/src/lib/ai/tools/trigger-tools.ts
📝 Walkthrough

Walkthrough

This PR introduces four new AI tools (set_calendar_trigger, delete_calendar_trigger, set_task_trigger, delete_task_trigger) that enable agents to manage scheduled trigger workflows on calendar events and tasks. The tools are integrated into the existing tool registry, classified as write operations for permission filtering, rendered in the chat UI, thoroughly tested, and documented through updated tool descriptions.

Changes

Agent Trigger Tools Feature

Layer / File(s) Summary
Trigger tools implementation: set/delete operations for calendar and task
apps/web/src/lib/ai/tools/trigger-tools.ts
Four new tools with permission-gated calendar and task trigger attachment/removal: set_calendar_trigger supports updating existing events or creating new ones; delete_calendar_trigger removes event triggers; set_task_trigger attaches due-date/completion triggers; delete_task_trigger disables task triggers. Each includes input validation, database transactions, and websocket broadcast notifications.
Tool registration and aggregation
apps/web/src/lib/ai/core/ai-tools.ts, apps/web/src/lib/ai/core/__tests__/ai-tools.test.ts
Trigger tools imported and registered in baseTools registry alongside other core tool groups; test mocks and expectations updated to include triggerTools in aggregation contract and collision-detection logic.
Write-tool classification and read-only filtering
apps/web/src/lib/ai/core/tool-filtering.ts, apps/web/src/lib/ai/core/__tests__/tool-filtering.test.ts
Trigger tools added to WRITE_TOOLS set so isWriteTool() predicate and downstream filtering functions (filterToolsForReadOnly, filterTools) correctly exclude them in read-only mode; test coverage validates classification and filtering behavior.
Tool result rendering in chat interface
apps/web/src/components/ai/shared/chat/tool-calls/registry.tsx
Four new toolRenderers entries for trigger tools that display action results as cards with appropriate action type, fixed title, and message/error fallback from parsed output.
Comprehensive test coverage for trigger tools
apps/web/src/lib/ai/tools/__tests__/trigger-tools.test.ts
Vitest suite with mocked DB/workflow/permissions layers testing calendar trigger attachment/creation/deletion and task trigger creation/deletion across authorization, missing-input, and error scenarios.
Tool documentation updates
apps/web/src/lib/ai/tools/agent-tools.ts, apps/web/src/lib/ai/tools/calendar-write-tools.ts, apps/web/src/lib/ai/tools/task-management-tools.ts, apps/web/src/lib/ai/tools/workflow-tools.ts
Updated tool descriptions clarifying the distinction between using dedicated trigger tools (e.g., set_calendar_trigger) and combined shortcuts (e.g., agentTrigger parameter in create_calendar_event), plus guidance on when cron workflows apply.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • 2witstudios/PageSpace#1177: Adds human-facing REST API routes for task triggers (/api/tasks/[taskId]/triggers) that mirror the AI tool trigger operations introduced here.
  • 2witstudios/PageSpace#1310: Separates core and non-core tools; the new trigger tools feed into that non-core tool dispatch and execution flow.
  • 2witstudios/PageSpace#1444: Similarly extends the tool registry and WRITE_TOOLS filtering logic with new workflow tools, using the same registration and permission-filtering patterns.

🐰 Four new tools to schedule, now the agents play by the rules,
Set calendars, set tasks, delete with permission's cool jewels,
No read-only sneaking through the filtering gate,
Broadcast, test, and document—the triggers aggregate! 🗓️⚙️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing dedicated trigger tools for calendar and task scheduling, which is the core addition across all modified files.
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/agents-trigger-config

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

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

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

@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: 7b6d1889ee

ℹ️ 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 +285 to +287
if (!(await canUserEditPage(userId, taskListPageId))) {
return { success: false, error: 'You do not have edit access to this task list.' };
}

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 Enforce actor-scoped permissions for task triggers

When this tool runs from an agent chat or a drive-scoped MCP token, canUserEditPage(userId, ...) checks only the underlying user's rights and bypasses the agent ACL, MCP drive scope, and app-role ceiling. Consequently, an actor that cannot edit the task list through update_task can still create a deferred workflow there; delete_task_trigger repeats the same bypass at line 352. Use the established canActorEditPage(ctx, taskListPageId) check used by resolveTaskForUpdate for both operations.

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 commit 510f76b. Both set_task_trigger (line 284) and delete_task_trigger (line 351) now use canActorEditPage(ctx, taskListPageId) from actor-permissions, matching the pattern used by all other tools in this codebase. canUserEditPage is no longer imported in trigger-tools.ts.

…ed-vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agents sometimes try to configure triggers via update_agent_config. The
updated description now explicitly directs them to set_calendar_trigger,
set_task_trigger, or create_workflow instead.

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

Updated create_calendar_event and update_calendar_event descriptions to
steer agents toward the new dedicated set_calendar_trigger tool. The
agentTrigger param remains for UI-driven creation workflows.

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

Adds a note to the update_task Agent Triggers section steering agents
toward the dedicated set_task_trigger tool for attaching triggers to
existing tasks, consistent with how calendar-write-tools was updated.

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

- registry.tsx: add ActionResultRenderer entries for set_calendar_trigger,
  delete_calendar_trigger, set_task_trigger, delete_task_trigger — required
  by the registry-coverage guard test
- ai-tools.test.ts: mock + import + spread triggerTools so the aggregation
  test matches the actual pageSpaceTools shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces canUserEditPage(userId, ...) with canActorEditPage(ctx, ...)
in set_task_trigger and delete_task_trigger so that agent ACL, MCP
drive scope, and app-role ceiling are enforced — matching the pattern
used by all other tool permission checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
set_task_trigger and delete_task_trigger now call driveDeniedByAppToken
before canActorEditPage, matching the pattern used by calendar trigger
tools. Also removes dead agent-trigger-shared mock and unused z import
from test file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The four new trigger tools (set/delete_calendar_trigger, set/delete_task_trigger)
were missing from WRITE_TOOLS, so they would have appeared in read-only
agent contexts. Adds them to the set and adds corresponding test coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vi.clearAllMocks() clears call history but not mock implementations.
The new token-denial test at the end of set_task_trigger left
mockDriveDeniedByAppToken returning true, which bled into all
delete_task_trigger tests. Explicitly reset to false in each
task trigger describe block's beforeEach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
task.dueDate!.toISOString() was logically safe (guarded above) but
TypeScript can't narrow through a ternary expression. Use optional
chaining with fallback to eliminate the assertion.

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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/lib/ai/tools/__tests__/trigger-tools.test.ts`:
- Around line 92-95: The test is mocking the wrong module path so
parseDateTime() and normalizeTimezone() used by trigger-tools.ts are not being
controlled; update the vi.mock call in trigger-tools.test.ts to mock the exact
module path that trigger-tools.ts imports (or add an additional vi.mock for that
import) and return mocked implementations for parseDateTime and
normalizeTimezone (the same vi.fn stubs currently used) so that the functions
used by the TriggerTools logic are intercepted during tests; locate usages of
parseDateTime/normalizeTimezone and the import in trigger-tools.ts to verify the
module specifier you need to mock.

In `@apps/web/src/lib/ai/tools/trigger-tools.ts`:
- Around line 364-371: The update to taskTriggers and the subsequent
recomputeTaskTriggerMetadata call must be made atomic so we don't leave rows
disabled while metadata still reflects the old trigger; modify the code around
db.update(taskTriggers)... to perform both the .update(...) and
recomputeTaskTriggerMetadata(...) inside the same transaction (use
db.transaction(...) or pass the transaction/tx object into
recomputeTaskTriggerMetadata so it runs within the same tx), then only call
broadcastTaskEvent after the transaction commits; reference taskTriggers,
recomputeTaskTriggerMetadata, broadcastTaskEvent and ensure
recomputeTaskTriggerMetadata accepts/uses the same tx or is wrapped in the
db.transaction block.
- Around line 145-147: Wrap normalization/parsing of timezone and triggerAt in a
try/catch so malformed inputs return the file’s tool error shape instead of
throwing: call normalizeTimezone(tzInput ?? ctx.timezone) and
parseDateTime(triggerAt, ...) inside a try block (referencing tzInput,
ctx.timezone, triggerAt, parseDateTime, normalizeTimezone, parsedAt, endAt) and
on any exception return { success: false, error: "Invalid trigger time or
timezone" } (or the module's existing validation error helper) rather than
letting the exception escape; only compute endAt = new Date(parsedAt.getTime() +
60*60*1000) after successful parse.
🪄 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: c9f0b4c2-d5fb-4321-b7d3-684cb660112b

📥 Commits

Reviewing files that changed from the base of the PR and between 66a6f2b and e20306c.

📒 Files selected for processing (11)
  • apps/web/src/components/ai/shared/chat/tool-calls/registry.tsx
  • apps/web/src/lib/ai/core/__tests__/ai-tools.test.ts
  • apps/web/src/lib/ai/core/__tests__/tool-filtering.test.ts
  • apps/web/src/lib/ai/core/ai-tools.ts
  • apps/web/src/lib/ai/core/tool-filtering.ts
  • apps/web/src/lib/ai/tools/__tests__/trigger-tools.test.ts
  • apps/web/src/lib/ai/tools/agent-tools.ts
  • apps/web/src/lib/ai/tools/calendar-write-tools.ts
  • apps/web/src/lib/ai/tools/task-management-tools.ts
  • apps/web/src/lib/ai/tools/trigger-tools.ts
  • apps/web/src/lib/ai/tools/workflow-tools.ts

Comment thread apps/web/src/lib/ai/tools/__tests__/trigger-tools.test.ts Outdated
Comment thread apps/web/src/lib/ai/tools/trigger-tools.ts Outdated
Comment thread apps/web/src/lib/ai/tools/trigger-tools.ts Outdated
…uard

- Wrap delete_task_trigger's disable+recompute in db.transaction() so
  the trigger state and task metadata stay consistent on partial failure
- Fix test mock path: '../core/timestamp-utils' → '../../core/timestamp-utils'
- Add mockDbTransaction call-through in delete_task_trigger beforeEach

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit 1640f77 into master Jun 12, 2026
12 checks passed
@2witstudios
2witstudios deleted the pu/agents-trigger-config branch June 12, 2026 02:42

This branch was previously deployed

1 inactive deployment
Preview — c75c6dde Deployed Jun 12, 2026 by vercel[bot]
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