| when_to_use | Trigger: "trag X ein", "block out", "schedule X", "add X to the calendar". Acknowledge AFTER the skill returns, never before. Reply ONE short line ("Termin eingetragen").
If date or hour is missing, ask before calling — never default to 10:00 or pick a day yourself.
DATE-ANCHOR DISCIPLINE: when the user named a relative date (weekday name, "morgen", "in 3 Tagen"), FIRST write the resolved date + weekday inline as a one-liner BEFORE calling the skill — e.g. "User said Friday → looking up table → 2026-06-05 (Freitag)". This catches the off-by-one weekday bug that hit T4. The lookup table in the NOW section is authoritative; do not compute day offsets yourself.
Then after the skill returns, check that the echoed `verified_weekday` matches. If it doesn't, call update_calendar_event with the correct date BEFORE telling the user the event was created.
For named household members: `find_person(query=..., source='household')` first; pass the returned id via `attendee_user_ids`. Free-text `person=` only for non-Yorik people (kids, guests). A `contact_id` from `source='contacts'` is NOT a valid attendee_user_id.
Pass `location` whenever the user mentions WHERE — the backend geocodes it and the UI renders a travel-time badge ("leave at HH:MM"). Without it, the badge doesn't show.
Provider categories (Zahnarzt, Hausarzt, Werkstatt, Friseur, Apotheke, Tierarzt, Steuerberater, Anwalt, Optiker): call `find_known_provider(category=...)` first; if 0 matches, `find_provider_nearby(poi=..., near=...)` next. Never skip to web search for these.
If the user names their own provider not in the picker: `find_person`, then `add_contact` if missing, then this skill.
If the returned event has `travel_time_s > 0`, offer `block_travel_time(event_id=<returned>)` — never auto-block. Skip the offer when travel_time_s is 0/null.
Examples:
- User accepts a meeting in WhatsApp ("yes, Thursday 2pm works") → add it.
- "block out Friday 10–12 for dentist" → add it; find_known_provider for location.
- Another skill records a commitment the user just made.
|
| inputs | {"title":{"type":"string","required":true,"description":"Short event title (e.g. \"Dentist appointment\", \"Coffee with Anna\")"},"starts_at":{"type":"string","required":true,"description":"ISO 8601 start (e.g. \"2026-05-22T14:00:00\"). Local time, no timezone suffix needed."},"ends_at":{"type":"string","required":false,"description":"ISO 8601 end. Defaults to starts_at + 60 minutes."},"all_day":{"type":"boolean","required":false,"default":false},"person":{"type":"string","required":false,"description":"Who it's for/with — \"admin\" | \"member\" | \"child\" | \"all\" | a name string"},"notes":{"type":"string","required":false},"calendar_id":{"type":"integer","required":false,"description":"Target calendar. Omit to default to the creator's personal calendar\n— multi-attendee events still appear on every invited attendee's\nview via the attendee/RSVP join, so they don't need to live on the\nHousehold calendar. Pass an explicit calendar_id only when the user\nnamed one (\"trag das in den Haushaltskalender ein\") or when the item\nis genuinely person-agnostic (trash day, mortgage, holidays).\n"},"attendee_user_ids":{"type":"array","required":false,"description":"User IDs to invite. Each gets a notification + RSVP buttons on the\nevent. Use find_person / SELECT FROM user_profiles to resolve names\nto IDs first. NEVER guess an id.\n"},"attendee_names":{"type":"array","required":false,"description":"Free-text attendee names for people without Yorik logins (kids,\nexternal guests). They appear on the event chip but no RSVP is\nrequested. Prefer attendee_user_ids when the person has an account.\n"},"visibility":{"type":"string","required":false,"default":"default","description":"'default' (uses calendar ACL) or 'private' (even on shared calendars,\nhide title+notes from anyone except the owner — they see 'Busy').\n"},"location":{"type":"string","required":false,"description":"Where the appointment is — free-text address, business name +\naddress, or city. Examples:\n - \"Zahnarztpraxis Dr. Müller, Hauptstr. 7, 30159 Hannover\"\n - \"TU Berlin\"\n - \"Café Goldmund, Kreuzberg\"\nWhen set, Yorik geocodes it and computes driving time from the\nuser's home address (cached on the event row, shown as a travel-\ntime badge in the calendar). Leave empty for at-home events.\n\nPass this whenever the appointment is physically somewhere — even\nif the user didn't explicitly say \"create event at X\", infer from\ncontext: \"Zahnarzttermin\" → location = the dentist's address (call\nfind_known_provider first to resolve \"der Zahnarzt\"); \"treffen mit\nAnna im Café Goldmund\" → location = \"Café Goldmund\".\n"},"category":{"type":"string","required":false,"description":"Closed enum — drives the event's colour on the calendar. Pick the\nsingle best match from the user's phrasing; omit if genuinely\nambiguous (no colour > wrong colour).\n - \"family\" — Familie, Haushalt, Kinder, Eltern, Geburtstage\n - \"business\" — Arbeit, Meetings, Kundentermine, Liefertermine\n - \"drive\" — Anfahrt, Rückfahrt, Reisezeit (you usually don't\n set this manually — block_travel_time does it)\n - \"health\" — Arzt, Zahnarzt, Therapie, Untersuchung, Apotheke\n - \"personal\" — Sport, Gym, Hobby, „Zeit für mich\"\n - \"social\" — Freunde, Essen, Feiern, Party\nExamples:\n \"Zahnarzttermin\" → category=\"health\"\n \"Kundentermin SAP\" → category=\"business\"\n \"Schule abholen\" → category=\"family\"\n \"Sport im Studio\" → category=\"personal\"\n \"Essen mit Tom\" → category=\"social\"\n"}} |