LB_INTERNAL_CREATE_ROUTINE
ChatGPTCreate a routine or one-off reminder that runs a prompt on the user's behalf and delivers the result as a report (optionally with a push/email notification). NOT available from inside a running routine. Before creating, agree on the essentials with the user: what the routine should do (the prompt), when it should run (the schedule), and a short clear title. Write the prompt as self-contained instructions addressed to the assistant that will run them unattended — specific about what to look at (integrations, meetings, context), what to produce, and the output format. Name which integrations and context sources to read, state the output format explicitly, and never end the prompt with follow-up questions — nobody is there to answer them. Avoid vague prompts like "summarize my day". Pick a schedule whose cadence matches the cadence of the underlying work — don't default to daily at 09:00 when the work is weekly or monthly. If the user only wants to hear about some runs ("ping me when someone requests my review", "only tell me if the error rate spiked"), set kind: "watcher" and put the condition in the prompt as its own sentence: "Only notify me when [condition]. If it is not met, write the report as usual but do not notify me about this run. If you could not check the condition, notify me." A content filter is NOT enough. A prompt that only says what to report, or what to say when there is nothing, still notifies on every run. A watcher whose condition can only happen once ("tell me once the invoice is paid", "let me know when the order arrives", "ping me when a specific person replies") says so in the prompt as its own sentence: "Tell me once, then stop." Judge by what the user named, not by whether that kind of event could happen again: "my package", "the invoice", "her reply" each name one occurrence and take the sentence. Give it the cadence its condition deserves. A reminder for a moment ('at 7pm tomorrow') is kind: "reminder" with date and time in schedule and nothing else: no check runs, and the prompt is the message itself, sent once as written by push and email and relayed faithfully by a messaging channel or agent, then the reminder retires. Write it as the message the user should read at that moment: address them directly, one or two short sentences, every detail they gave, no greeting and no mention of the time, since it arrives at the time. For recurring requests, use kind: "digest" when every run should notify, or kind: "watcher" when only a matching run should notify. For relative times such as "in an hour", calculate the date and time from the current timestamp in the user's timezone, including any date rollover. If the user states what to remember and when, choose a short title and create the reminder without another confirmation. Creation immediately generates a first report (visible in the Routines tab shortly after) and then runs on the schedule; a reminder skips that first report and waits for its moment instead. Users have a plan-based limit on how many routines they can have; if it's reached, this returns a message to relay instead of creating.
LB_INTERNAL_GET_MEETING
ChatGPTGet the details of a single meeting by its id: its name, TL;DR, full summary, and user-added manual notes, plus the associated calendar event (time, description, and that event's attendees) when the meeting is linked to one. Attendees are only shown via that linked calendar event — a transcript-only meeting with no calendar event won't list attendees here. This does NOT return the transcript; use LB_INTERNAL_GET_MEETING_TRANSCRIPT for the raw word-for-word transcript. Use this after LB_INTERNAL_LIST_MEETINGS or LB_INTERNAL_SEARCH_MEETINGS surfaces a meeting you want to know more about. Works on RECORDED meetings only — the id must come from a recorded result; a scheduled/unrecorded calendar event has no id and can't be fetched here. If the meeting recurs on a schedule, its linked event is tagged "(recurring)".
LB_INTERNAL_GET_MEETING_TRANSCRIPT
ChatGPTGet the verbatim transcript of a single meeting by its id, with speaker attribution as recorded. Use this when you need the exact words spoken (quotes, precise wording, who said what) rather than a summary. Get the meeting id from LB_INTERNAL_LIST_MEETINGS or LB_INTERNAL_SEARCH_MEETINGS. Transcripts can be long; prefer LB_INTERNAL_SEARCH_MEETINGS when you only need the relevant portions. Works on RECORDED meetings only — a scheduled/unrecorded calendar event has no id and no transcript to fetch here.
LB_INTERNAL_GET_ROUTINE_CONFIG
ChatGPTGet a routine's full configuration: its prompt (the exact instructions it runs), schedule and/or triggers, paused state, auto-pause setting, notification settings (push and email), and agent mode. Pass the routine_id from LB_INTERNAL_LIST_ROUTINES. Call this BEFORE updating a routine with LB_INTERNAL_UPDATE_ROUTINE — especially before editing its prompt, which must be replaced in full — and whenever the user asks what a routine does, why its reports look the way they do, or how to improve it. To read the reports themselves, use LB_INTERNAL_GET_ROUTINE_REPORTS.
LB_INTERNAL_GET_ROUTINE_REPORTS
ChatGPTFetch a routine's past reports, most recent first, each with its date, title, text, and actions the user already completed outside Littlebird. Treat those actions as resolved and do not propose them again. Pass the routine_id from LB_INTERNAL_LIST_ROUTINES. limit sets how many to return (default 5, max 25); long histories are truncated to fit.
LB_INTERNAL_GET_SUBSCRIPTION_STATUS
ChatGPTCall this whenever the user asks about their subscription state like what plan they're on, what they're paying, whether their subscription is active, when it renews, or which provider (Stripe, iOS App Store, team) bills them. Returns provider, plan, renewal/cycle-end date, active state, and team info.
LB_INTERNAL_LIST_MEETINGS
ChatGPTList the user's meetings in reverse-chronological order — BOTH recorded meetings (ones Littlebird captured, with notes/transcript) and scheduled calendar events that weren't recorded — each with its name, time, and attendees. Use it to browse "what did I have last week" / "my recent calls", or, with a future end_date, "what's coming up". A meeting/event that repeats on a schedule is tagged "(recurring)" next to its name. Telling them apart: only RECORDED meetings get an id in the trailing id list — drill into those with LB_INTERNAL_GET_MEETING or LB_INTERNAL_GET_MEETING_TRANSCRIPT. A listed calendar event with no id was not (or has not yet been) recorded, so it has no summary/transcript to open; that's also how you spot which upcoming events are on the calendar but not yet captured. NOTE: future/upcoming events are never recorded (they haven't happened yet), so they never have a summary or transcript and can't be opened (GET_MEETING) or searched (SEARCH_MEETINGS) — they only ever show up here as bare calendar entries. Pass name to find a meeting BY TITLE — the right tool for recurring meetings and their instances (e.g. "my last standup", "the previous 1:1 with Priya"). It matches the title against BOTH recorded meetings (by their own name or their linked calendar-event title) AND scheduled calendar events, returning matches most-recent-first. By default it returns PAST instances up to now (reaching back as far as needed) and excludes upcoming ones, since those are never recorded and a name lookup usually wants prior instances to compare against; to include upcoming instances, pass a future end_date. As with any list, only RECORDED matches carry an id — drill into those with LB_INTERNAL_GET_MEETING; a title-matching calendar event with no id wasn't (or hasn't yet been) recorded, so there's nothing to open. Use name (not LB_INTERNAL_SEARCH_MEETINGS) whenever you're looking a meeting up by what it's CALLED rather than what was discussed in it. Optional start_date/end_date are ISO date strings (e.g. "2026-06-01") bounding the window; set a future end_date to include upcoming calendar events. Without name: omit both to default to a recent window (about the last 90 days), and a very wide window is narrowed to its most recent portion (pass an explicit range to browse older meetings). With name: past reach-back is uncapped, but upcoming events are excluded unless you pass a future end_date. limit caps how many meetings/events are returned. This reads straight from the database (not the meeting search index), so it is unaffected by search-index availability. To find a meeting by TOPIC (what was discussed) rather than by title, use LB_INTERNAL_SEARCH_MEETINGS instead.
LB_INTERNAL_LIST_RECENT_CONVERSATIONS
ChatGPTList your conversations with the user by TIME, most recently active first. Use it for "what did we talk about yesterday", "show me my last few chats", or "what were we working on this week". Each entry has the conversation's id, title, source, start and last-active times, user-message count, and tags (favorite, project, origin; the conversation you are in is tagged "this conversation"). limit caps the list (default 10, max 20). start_date/end_date are ISO date strings bounding when a conversation was LAST ACTIVE; to page further back, pass the cursor from the result's paging hint. To find a conversation by what was discussed, use LB_INTERNAL_SEARCH_CONVERSATIONS.
LB_INTERNAL_LIST_ROUTINES
ChatGPTList the user's routines and reminders with their kind, title, schedule and/or trigger, report count, latest report date, status, and id. Pass kind: "reminder" to list reminders, or omit kind for all kinds. Completed ones are included and labeled. An optional limit returns the newest matches. Use the id with LB_INTERNAL_GET_ROUTINE_REPORTS to fetch that routine's reports.
LB_INTERNAL_READ_CONVERSATION
ChatGPTRead one conversation with the user in order, a page of turns at a time (a turn is one user message and your final reply to it). Use it after LB_INTERNAL_SEARCH_CONVERSATIONS or LB_INTERNAL_LIST_RECENT_CONVERSATIONS points you at a chat and you need more than the excerpt. chat_id is the conversation id from those tools, or 'current' for the conversation you are in (useful when earlier turns have been compacted away). By default it reads from turn 1; pass start_turn (1-based) to start elsewhere — e.g. the turn number shown next to a search excerpt — and num_turns for the page size (default 10, max 30). Alternatively pass query (a keyword or phrase) to open the page centered on the first turn that mentions it. Returns the conversation's title, source, dates, and tags, then the requested turns with timestamps; long turns are trimmed.
LB_INTERNAL_SEARCH_CONVERSATIONS
ChatGPTSearch your past conversations with the user (earlier Littlebird chats) by TOPIC. Use it whenever the answer may live in something you and the user discussed before: "what did we decide about the pricing page", "that thing you suggested last week", "where were we with the migration", or any shorthand reference that could point at an earlier chat. query is a few distinctive words — a project name, a topic, a proper noun ("pricing page", not "what did we decide about the pricing page"); hybrid semantic + keyword search runs over the messages of every conversation. Results are grouped by conversation, most relevant first, each with its title, source, dates, tags, and an excerpt of the matched turns with nearby context. Follow up with LB_INTERNAL_READ_CONVERSATION to read more of a conversation, or pass chat_id here to search inside one. By default the search covers all past conversations EXCEPT the one you are in. Pass chat_id='current' to search earlier turns of THIS conversation (e.g. when it has been compacted and you need something said earlier), or a conversation id from an earlier result to search inside that chat. start_date/end_date bound the window by when the messages were sent. limit caps the number of matched conversations (default 5, max 10). Results are ordered by relevance, NOT by date — for "what did we talk about yesterday" or "my last few chats", use LB_INTERNAL_LIST_RECENT_CONVERSATIONS instead. This is backed by the chat search index and can be briefly unavailable; you'll get a clear "temporarily unavailable" message — retry shortly.
LB_INTERNAL_SEARCH_MEETINGS
ChatGPTSemantic + keyword (hybrid) search over the user's meeting transcripts and summaries by TOPIC. This is the tool for "find the meeting where we discussed X" or "the call with the GCP team about billing" — use it when you're looking for a meeting by what was said in it, NOT by its title. To find a meeting by its name (including recurring meetings like a weekly standup and their previous instances), use LB_INTERNAL_LIST_MEETINGS with name instead. Always pass query (what was discussed) — it drives the search and should describe the topic as fully as you can. When you know them, ALSO pass attendees (a list of participant names or emails — e.g. ["Priya", "sam@acme.com"]) to narrow and re-rank the most relevant results toward meetings that involved those people. IMPORTANT: attendees is an ANY-match (OR) filter — a meeting matches if it involved AT LEAST ONE of the listed people, NOT all of them. It does NOT mean "the meeting with Priya AND Sam"; to confirm everyone attended, check a result's attendees with LB_INTERNAL_GET_MEETING. This is also best-effort: it only applies over the top candidates the hybrid search surfaces, so a matching meeting that falls outside that candidate pool may not appear. If the meeting you expect isn't returned, broaden or reword query and try again rather than relying on the attendee filter alone. start_date/end_date are ISO date strings that bound the search window, and limit caps the number of matched meetings returned. Results are ordered by relevance, NOT by date — to get the most recent instance of something, use LB_INTERNAL_LIST_MEETINGS instead. Returns the matching meetings with their summaries and the most relevant transcript chunks. To read the full verbatim transcript of a result, follow up with LB_INTERNAL_GET_MEETING_TRANSCRIPT using its id. This tool is backed by the meeting search index, so it only surfaces RECORDED meetings that have been indexed — upcoming or never-recorded calendar events aren't searchable here (browse them with LB_INTERNAL_LIST_MEETINGS). It can be briefly unavailable if that index times out — you'll get a clear "temporarily unavailable" message; retry shortly. If you ALREADY have a meeting id, call LB_INTERNAL_GET_MEETING (or LB_INTERNAL_GET_MEETING_TRANSCRIPT) directly instead of searching.
LB_INTERNAL_UPDATE_ROUTINE
ChatGPTUpdate an existing routine. Pass the routine_id plus ONLY the fields to change; omitted fields are left untouched. NOT available from inside a running routine. - prompt REPLACES the routine's entire prompt. Always fetch the current prompt first with LB_INTERNAL_GET_ROUTINE_CONFIG, apply the user's edits to it, and pass the complete new text — never a fragment or a diff. - schedule REPLACES the whole schedule (frequency + time + days). - is_paused pauses (true) or resumes (false) the routine. Pausing is for a routine the user wants back later; a routine they have called off takes completed instead. - completed permanently retires a reminder or watcher before it fires again. Pass true by itself only after the user confirms. - Adding a notify-only-when condition follows the LB_INTERNAL_CREATE_ROUTINE guidance: set kind: "watcher" and put the condition into prompt as its own sentence. Routines listed as "set up by Littlebird" keep the agent mode Littlebird set, but their title, prompt, and schedule can change. Ones listed as read-only cannot be changed. Show the user what you're about to change and get their confirmation before calling this, unless they already stated the exact change. Updates do NOT immediately re-run the routine; changes take effect from its next scheduled run.
dismiss_action_card
ChatGPTDismiss a pending action card. Call this when the user asks to dismiss, discard, or remove a card, or when you judge that a card should go, such as when it is stale, superseded, or no longer relevant. Read the card first when which card is meant is ambiguous. Optional feedback records the reason. This changes only the card's lifecycle in Littlebird. It never runs, sends, creates, or updates anything in an external service. Only draft and failed cards can be dismissed, and repeating a dismissal is safe: it does not replace the original feedback.
get_action_cards
ChatGPTRead the user's Littlebird action cards, newest first, across all of their conversations. An action card is an action Littlebird drafted for the user - send this email, create this calendar event, file this issue - that is waiting on their approval. Nothing has been sent or created until the user approves the card in Littlebird, and no tool here can approve one. By default this lists active cards: draft, failed, or currently running. Use status to list completed cards, cards the user marked already done or started a chat from (completed_externally), dismissed cards, or all non-withdrawn cards. Withdrawn cards (status revoked) are left out of every listing because they were replaced, but can still be fetched by exact card_ids. Neither this tool nor update_action_card sends anything. Never describe a card as sent because you read it. Pass card_ids to fetch specific cards, including a withdrawn one; exact-id lookup ignores status and paging. Omit card_ids to browse, then page further back with limit and the next_cursor this tool returns, keeping the same status across the pages. Each card carries its content, the editable_fields that update_action_card accepts for that concrete card, whether the user has edited it since Littlebird drafted it, a revision token to pass to update_action_card, the chat_id of the conversation it came from, the context note it was created with, and - once it has run - its external_ref (for a sent email, the Gmail message id). agent_action records approved_by_agent or dismissed_by_agent; when it is absent the actor is unknown, so do not infer that the user acted. created_at and updated_at are ISO 8601 times in UTC: when the card was created, and when anything on it last changed, such as its content or its status. A card can be rewritten in place, and its context note keeps the wording it was created with.
ingest
ChatGPTSave text to Littlebird so it becomes available as user context. That means it will potentially be incorporated into Littlebird's replies, routine runs, and so on.
search_user_context
ChatGPTLook up data from the user's internal knowledge base (notes, messages, code, documents, personal info). - Provide 5-7 concise search queries in total - Use any split between search_queries and search_queries_messages EXCEPT 0 regular search_queries - must be at least 1 - If the question is exclusively about messages use specifically 1 search_queries and the rest search_queries_messages - For messaging apps like Slack, WhatsApp, Messages, Signal, etc. use primarily search_queries_messages, for non-messaging apps like Notion, Google Chrome etc. use search_queries (though some other messaging apps are currently not supported by search_queries_messages so if you don't see any results, try search_queries) - For user requests that need thorough search ("Summarize my day") or if previous search didn't surface enough info, you need more than 7 queries - then use MULTIPLE calls to this tool with 7 queries each - date_range is optional, start and end are both INCLUSIVE & ALWAYS <= current time (use "now" for "end" to search latest records) - When a date, e.g. 2026-01-07, is used as "start" it means the start of that day and when used in "end" it means up till the end of that day. - "My messages on Jan 7th 2026" → "2026-01-07" to "2026-01-07" (ideally use 3-5 calls to this tool with different time ranges e.g. 0-8 am etc. since finding _all_ messages requires thoroughness) - "Messages about ponies in Feb 2026" → "2026-02-01" to "2026-02-28" (just 1 call to the tool, with search_queries_messages, enough since it's a narrow topic) - "What do I have scheduled for tomorrow" → "<date about 3 days ago>" to "now" (even though user asks about future, any records with info about tomorrow could have only been captured BEFORE current time) - "last X days" → means up till NOW - Date searches find inputs when observed; messages when sent or observed - Semantic queries must make sense for RAG, so they must be: - varied in their semantic meaning - not using terms that don't translate well to embeddings, e.g. "here" (that's relative) or "Apr 6" (use date_range instead) or "I" (use name instead) - AVOID: NO "Messages on Jan 7", NO "Messages today", NO "Emails on Feb 14" (date is NOT for semantic query, but for date_range) - GOOD: "Work message", "Personal message" - AVOID: "Emails I received" (semantic search doesn't know who "I" is) - GOOD: "Work email <user name> received", "Personal email for <user name>" - Filters are optional - "app": "Messages" | "WhatsApp" | "Slack" | "Microsoft Word" | "Finder" | "Notes" | "Littlebird Chat" | "Mobile Screenshots" | <any standard app name on a Mac> | <any app name that appears in previously retrieved messages in the "sent via Some App" annotation> - Messages refers to iMessages - app names must be exact matches (though case-insensitive) so if you're not finding any results when using an app name, one possibility is that the app name is slightly different in the records, in which case you can (a) remove filters, even date filters, and just try using the app name in your semantic queries (which you normally SHOULDN'T do, normally put the app name in the filter), then if you find even one result from the app, you will see the correct app name OR (b) try different possible variations of the app name (use multiple calls to this tool) - You don't need to use filters every time but if e.g. user asks specifically about iMessages then you can use "app": "Messages" - Use the app name “Littlebird Chat” to search for previous conversations (from different threads) you’ve had with the user - When using this filter, use ONLY search_queries - Use the app name "Mobile Screenshots" to search screenshots and photos captured on the user's phone (e.g. "what do you know from yesterday's screenshots") - When using this filter, use ONLY search_queries Use this tool when the needed information is more private than public. If the user asks about something related to their work or personal life, e.g. some person or project that you don't have context for, use this tool (never say "it's not in my current context"). Definitely use this …
update_action_card
ChatGPTPatch fields on a pending action card, leaving it in place under the same card_id. Read the card with get_action_cards first, update only its editable_fields, and pass the revision it returned. The user may have edited the card since Littlebird drafted it, so an update with a stale revision is rejected, and the rejection hands you the current content to reapply your change to. This tool only revises the draft the user sees in Littlebird, where they approve it themselves. It does not execute the card's action or change anything in an external service. Report that the card was updated; do not claim its action completed. Only pending cards (draft or failed) can be edited. A completed card has already run - a completed gmail card has already been sent - and cannot be changed.