feat(events): recurring expansion, CalDAV pull sync, past search, reminders
- RRULE expansion: list_events now expands recurring events into individual occurrences within the query window using python-dateutil - CalDAV pull sync: new caldav_sync.py + POST /api/events/sync route; imports remote events into the internal store by caldav_uid - Past event search: search_events accepts include_past=true to search historical events; exposed in the LLM tool definition - Internal reminders: migration 0037 adds reminder_minutes + reminder_sent_at columns; event_scheduler.py checks every 5 min and fires push notifications; CalDAV sync job runs hourly - reminder_minutes now stored and returned in create/update routes + tools Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -695,6 +695,10 @@ _CORE_TOOLS = [
|
||||
"type": "string",
|
||||
"description": "Search keyword to match against event titles, locations, and descriptions",
|
||||
},
|
||||
"include_past": {
|
||||
"type": "boolean",
|
||||
"description": "Set to true to include past events in results (default: future events only)",
|
||||
},
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
@@ -744,6 +748,10 @@ _CORE_TOOLS = [
|
||||
"type": "string",
|
||||
"description": "New iCalendar RRULE",
|
||||
},
|
||||
"reminder_minutes": {
|
||||
"type": "integer",
|
||||
"description": "Reminder N minutes before the event. Pass 0 to remove an existing reminder.",
|
||||
},
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
@@ -1471,7 +1479,7 @@ async def execute_tool(
|
||||
"type": "events",
|
||||
"data": {
|
||||
"count": len(events),
|
||||
"events": [e.to_dict() for e in events],
|
||||
"events": events,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1479,6 +1487,7 @@ async def execute_tool(
|
||||
events = await events_search_events(
|
||||
user_id=user_id,
|
||||
query=arguments.get("query", ""),
|
||||
include_past=arguments.get("include_past", False),
|
||||
)
|
||||
return {
|
||||
"success": True,
|
||||
@@ -1502,6 +1511,9 @@ async def execute_tool(
|
||||
fields[str_field] = arguments[str_field]
|
||||
if arguments.get("all_day") is not None:
|
||||
fields["all_day"] = arguments["all_day"]
|
||||
if "reminder_minutes" in arguments:
|
||||
rm = arguments["reminder_minutes"]
|
||||
fields["reminder_minutes"] = None if rm == 0 else rm
|
||||
for dt_field, key in (("start_dt", "start"), ("end_dt", "end")):
|
||||
val = arguments.get(key)
|
||||
if val:
|
||||
|
||||
Reference in New Issue
Block a user