{"data":{"checksum":"584af6dde90e8bc346b34d67960dd16dc8427b095ab76c3f3139013d16e89a5c","intents":[{"contract_pin":"GOOGLECALENDAR_FIND_EVENT","description":"List upcoming calendar events (also the Today dashboard and shared-calendar reads)","id":"calendar.list","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"end":"time_max","limit":"max_results","start":"time_min"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_FIND_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_FIND_EVENT","description":"Find a calendar event by title, attendee, or id (fuzzy match)","id":"calendar.find_event","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"end":"time_max","query":"q","start":"time_min"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_FIND_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_CREATE_EVENT","description":"Create a calendar event","id":"calendar.create","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"attendees":"attendees","description":"description","end_time":"end_datetime","location":"location","start_time":"start_datetime","title":"summary"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_CREATE_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_CREATE_EVENT","description":"Create a calendar meeting with a Google Meet link attached","id":"calendar.meeting.create_with_meet_link","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"add_meet_link":"create_meeting_room","attendees":"attendees","end_time":"end_datetime","start_time":"start_datetime","title":"summary"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_CREATE_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_PATCH_EVENT","description":"Update a calendar event (patch semantics — only changed fields forwarded)","id":"calendar.update","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"attendees":"attendees","end_time":"end_datetime","event_id":"event_id","start_time":"start_datetime","title":"summary"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_PATCH_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_DELETE_EVENT","description":"Delete a calendar event","id":"calendar.delete","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"event_id":"event_id"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_DELETE_EVENT"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_FIND_FREE_SLOTS","description":"Check attendee availability over a window","id":"calendar.freebusy","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"emails":"items","end":"time_max","start":"time_min"},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_FIND_FREE_SLOTS"}],"sequence":null},{"contract_pin":"GOOGLECALENDAR_GET_CALENDAR","description":"Resolve the shared calendar account identity (the primary calendar's id IS the account email)","id":"calendar.organizer.identity","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"calendar_id":"calendar_id"},"served":false,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_GET_CALENDAR"}],"sequence":null},{"contract_pin":"calendar.organizer.migrate.sequence","description":"Migrate event organizer to the shared service account (recreate-then-cancel per event)","id":"calendar.organizer.migrate","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"calendar.organizer.migrate.sequence"}],"sequence":{"implemented":false,"steps":[]}},{"contract_pin":"calendar.meeting.attach_meet_link.sequence","description":"Attach a Google Meet link to an existing calendar event (S1 meet-attach)","id":"calendar.meeting.attach_meet_link","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"calendar.meeting.attach_meet_link.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_PATCH_EVENT"},{"idempotency":"idempotent","params_mapping":{},"served":false,"toolkit":"googlecalendar","upstream_action":"GOOGLECALENDAR_UPDATE_EVENT"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlecalendar","upstream_action":"GOOGLEMEET_UPDATE_SPACE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"Parent call — calendar.update runs PATCH_EVENT with patch semantics (only changed fields forwarded; start_time/end_time names, not the create action's *_datetime names).","order":1},{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"mapResult — annotateEventLinks hoists meet_link/event_link off the patched event resource; the attach hook later overwrites meet_link on the same key.","order":2},{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"enrichResult hook attachMeetToExistingEvent — wired only on calendar.update; calendarId resolved with parseSourceRef ?? normalizeCalendarEventRef ?? 'primary' precedence.","order":3},{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"No-op gate — if addMeetLink !== true, return the data unchanged.","order":4},{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"Read (free) — the event resource is unwrapped from the PATCH response itself (no separate read call); no recognisable resource ⇒ meet_attach_failed: true + honest error, never throws.","order":5},{"action":"GOOGLECALENDAR_PATCH_EVENT","description":"No-op if already attached — existingMeetLink(event): hangoutLink first, then conferenceData.entryPoints[].uri matching https://meet.google.com/ ⇒ meet_attached: 'already_present', never a second conference.","order":6},{"action":"GOOGLECALENDAR_UPDATE_EVENT","description":"Arg build — event_id + calendar_id; start_datetime echoed VERBATIM from the event's own start.dateTime (offset included, never recomputed, never offset-shifted); duration split into event_duration_hour (floor) + event_duration_minutes (mod 60, Composio caps minutes at 59); create_meeting_room: true; timezone forwarded ONLY when it contains '/' (a real IANA zone — Google's 'UTC' on an offset-bearing dateTime must not be forwarded); summary/description/location echoed so the full PUT cannot clear them; attendees echoed + send_updates: true when guests; org auto-invite bots folded in additively, deduped case-insensitively.","order":7},{"action":"GOOGLECALENDAR_UPDATE_EVENT","description":"Refusal conditions (return { ok: false, reason }, no write) — no event id in the update result; all-day event (start.date without dateTime); unparseable window; non-positive or > 24h window (MAX_EVENT_MINUTES = 24 * 60).","order":8},{"action":"GOOGLECALENDAR_UPDATE_EVENT","description":"Write — GOOGLECALENDAR_UPDATE_EVENT is the ONE Composio action that can create a conference on an existing event; PATCH_EVENT can only PRESERVE one. The raw-HTTP proxy fallback is entitlement-blocked on prod keys and must not be added.","order":9},{"action":"GOOGLECALENDAR_UPDATE_EVENT","description":"Failure semantics — on action failure: meet_attach_failed: true + meet_attach_error (honest, actionable text the engine relays); if the write succeeded but Google returned no conference code, report failure honestly. Never throws — the field patch that already succeeded still stands.","order":10},{"action":"GOOGLEMEET_UPDATE_SPACE","description":"Post-step — ensureMeetSpaceOpen runs on the UPDATE_EVENT result so the minted room gets the same open-access treatment every Bella-created Meet gets. Executes on the SAME googlecalendar account as every other step (the runner's same-identity invariant), not on a googlemeet connection — which is why googlecalendar carries auth/meetings.space.settings (decision GS4, issue #192). Fail-soft: the event is always returned.","order":11}]}},{"contract_pin":"calendar.meeting.open_access.sequence","description":"Ensure a newly created meeting's Meet space is open-access (S2 ensureMeetSpaceOpen)","id":"calendar.meeting.open_access","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"calendar.meeting.open_access.sequence"}],"sequence":{"implemented":false,"steps":[]}},{"contract_pin":"GOOGLEMEET_GET_MEET","description":"Read a Meet space's access configuration","id":"calendar.meeting.get_access","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"meeting_code":"name"},"served":true,"toolkit":"googlemeet","upstream_action":"GOOGLEMEET_GET_MEET"}],"sequence":null},{"contract_pin":"GOOGLEMEET_UPDATE_SPACE","description":"Set a Meet space's access type (read-modify-write through the transport that could read)","id":"calendar.meeting.set_access","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"access_type":"access_type","meeting_code":"space_id"},"served":true,"toolkit":"googlemeet","upstream_action":"GOOGLEMEET_UPDATE_SPACE"}],"sequence":null},{"contract_pin":"calendar.create.duplicate_guard.sequence","description":"Block a calendar.create that duplicates an existing event (time ∧ audience ∧ title, or time ∧ title for solo)","id":"calendar.create.duplicate_guard","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"calendar.create.duplicate_guard.sequence"}],"sequence":{"implemented":false,"steps":[]}},{"contract_pin":"calendar.attendee.add.merge","description":"Merge an attendee into an event's attendee list (pure — already-present is a no-op, never an LLM call)","id":"calendar.attendee.add","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"add":"add","event":"attendees","self_email":"selfEmail"},"served":true,"toolkit":"platform","upstream_action":"calendar.attendee.add.merge"}],"sequence":null},{"contract_pin":"calendar.attendee.drop.merge","description":"Remove an attendee from an event's attendee list (pure — ambiguous matches remove nobody)","id":"calendar.attendee.drop","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"event":"attendees","remove":"remove","self_email":"selfEmail"},"served":true,"toolkit":"platform","upstream_action":"calendar.attendee.drop.merge"}],"sequence":null},{"contract_pin":"GOOGLEMEET_GET_MEET","description":"Get a Google Meet space by meeting code","id":"meet.space.get","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"meeting_code":"name"},"served":true,"toolkit":"googlemeet","upstream_action":"GOOGLEMEET_GET_MEET"}],"sequence":null},{"contract_pin":"GOOGLEMEET_UPDATE_SPACE","description":"Update a Google Meet space (write addressed by resource name from the read, never by meeting code)","id":"meet.space.update","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"config":"access_type","name":"space_id"},"served":true,"toolkit":"googlemeet","upstream_action":"GOOGLEMEET_UPDATE_SPACE"}],"sequence":null},{"contract_pin":"GMAIL_FETCH_EMAILS","description":"Search Gmail messages by query","id":"gmail.search","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"limit":"max_results","query":"query"},"served":true,"toolkit":"gmail","upstream_action":"GMAIL_FETCH_EMAILS"}],"sequence":null},{"contract_pin":"GMAIL_FETCH_EMAILS","description":"List recent Gmail messages","id":"gmail.recent","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"limit":"max_results"},"served":true,"toolkit":"gmail","upstream_action":"GMAIL_FETCH_EMAILS"}],"sequence":null},{"contract_pin":"GMAIL_FETCH_EMAILS","description":"List unread Gmail messages","id":"gmail.unread","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"limit":"max_results"},"served":true,"toolkit":"gmail","upstream_action":"GMAIL_FETCH_EMAILS"}],"sequence":null},{"contract_pin":"GMAIL_FETCH_MESSAGE_BY_MESSAGE_ID","description":"Fetch a single Gmail message by id (optionally with parsed attachments)","id":"gmail.message","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"message_id":"message_id"},"served":true,"toolkit":"gmail","upstream_action":"GMAIL_FETCH_MESSAGE_BY_MESSAGE_ID"}],"sequence":null},{"contract_pin":"GMAIL_SEND_EMAIL","description":"Send a Gmail message","id":"gmail.send","identity_scope":"user","implementations":[{"idempotency":"non-idempotent","params_mapping":{"body":"body","subject":"subject","to":"recipient_email"},"served":true,"toolkit":"gmail","upstream_action":"GMAIL_SEND_EMAIL"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read a sheet range (headers + data rows)","id":"sheets.read","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"range":"ranges","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_VALUES_UPDATE","description":"Write values to a sheet range","id":"sheets.write","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"range":"range","spreadsheet_id":"spreadsheet_id","values":"values"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND","description":"Append a row to a sheet (atomic values.append)","id":"sheets.append","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"range":"range","spreadsheet_id":"spreadsheetId","values":"values"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_SORT_RANGE","description":"Sort a sheet range (spanning the full width so ledger keys travel with their rows)","id":"sheets.sort","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"range":"range","sort_column":"sortSpec","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_SORT_RANGE"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_CREATE_GOOGLE_SHEET1","description":"Create a Google Sheet","id":"sheets.create","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"title":"title"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_CREATE_GOOGLE_SHEET1"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_ADD_SHEET","description":"Add a worksheet to a spreadsheet","id":"sheets.add_sheet","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"sheet_name":"title","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_ADD_SHEET"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_GET_SHEET_NAMES","description":"List the worksheets of a spreadsheet","id":"sheets.list_sheets","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_SHEET_NAMES"}],"sequence":null},{"contract_pin":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read raw cell values from a sheet","id":"sheets.get_values","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"range":"ranges","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"}],"sequence":null},{"contract_pin":"sheet.update_row_by_key.sequence","description":"Update a sheet row located by an exact key (S12 — fail before any write on unknown columns / ambiguous keys)","id":"sheet.update_row_by_key","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"sheet.update_row_by_key.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read header + data (bounded wide).","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Resolve key column and update columns up front — fail before any write on an unknown column.","order":2},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Locate the row by exact (trimmed) key match; 0 matches ⇒ row_not_found; >1 ⇒ ambiguous_key — a human must disambiguate.","order":3},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Write single-cell VALUES_UPDATE per column (USER_ENTERED so dates/formulas/links parse); never a span that could blank a neighbour.","order":4}]}},{"contract_pin":"tasks.create.sequence","description":"Create a task row in the org task grid (S11 — grid IO with read-after-write by id)","id":"tasks.create","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"tasks.create.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Load the grid (headers + data rows).","order":1},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"ensureIdColumn — append the Bella-managed id column header and backfill ids for existing rows if missing; backfill blank id cells if present.","order":2},{"action":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND","description":"Append the header-aligned row (atomic values.append on Google Sheets; Excel uses a caller-computed next-row UPDATE_RANGE with a documented race).","order":3},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read-after-write — getTaskById confirms the row actually landed; an unverified write returns { ok: false, reason: 'write_unverified' }, never a false success.","order":4}]}},{"contract_pin":"tasks.update.sequence","description":"Update a task row in the org task grid (S11 — single-cell writes, never a span)","id":"tasks.update","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"tasks.update.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Load the grid and locate the row by exact id.","order":1},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Write single-cell VALUES_UPDATE per changed field (never a span) so a neighbouring cell can never be blanked.","order":2}]}},{"contract_pin":"GOOGLESHEETS_GET_BATCH_VALUES","description":"List task rows from the org task grid","id":"tasks.list","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"range":"ranges","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"}],"sequence":null},{"contract_pin":"expense.record.sequence","description":"Record an expense claim row (S6 — content-addressed dedup, confirmation gate, pinned-range write, read-after-write verify)","id":"expense.record","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"expense.record.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_BATCH_GET"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_FIND_FILE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_BATCH_UPDATE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_SORT_RANGE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Ledger resolution — sheetSlot/evidenceSlot are a PAIR; passing one alone is refused; absent ⇒ server resolves from the filer's team-member row.","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Filer + date — identity from the turn, never the engine; date normalized to DD/MM/YYYY server-side, fail before any external call.","order":2},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Amount + currency — explicit currency authoritative; non-finite/non-positive refused; a rupiah figure below the implausible-IDR floor is refused as a foreign-currency figure; USD writes a live GOOGLEFINANCE formula cell.","order":3},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Dedup ledger key — receipt-backed = the receipt's sha256; text-only = synthetic txt:<sha256> key, prefixed so it can never collide with a real receipt hash.","order":4},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Resource Map + creds resolution.","order":5},{"action":"GOOGLESHEETS_BATCH_GET","description":"READ — ONE BATCH_GET carrying the column-Z dedup ledger, the header row, AND the occupancy grid — zero extra round-trips.","order":6},{"action":"GOOGLESHEETS_BATCH_GET","description":"Layout guard — fail-closed BEFORE any write if the sheet's real header doesn't match the expected schema.","order":7},{"action":"GOOGLESHEETS_BATCH_GET","description":"Dedup — key already in the ledger ⇒ { recorded: false, deduped: true, matchedRow } — idempotent across any engine drift.","order":8},{"action":"GOOGLESHEETS_BATCH_GET","description":"Large-amount confirmation gate — above the slot's confirmThresholdIdr without confirmed: true short-circuits with NO side effects; runs AFTER dedup, BEFORE evidence/append.","order":9},{"action":"GOOGLEDRIVE_FIND_FILE","description":"Evidence (receipt-backed only) — FIND_FILE by content-addressed name → reuse; else upload. Drive is a best-effort MIRROR, never a gate — on any Drive failure the row STILL records with EVIDENCE_UNMIRRORED_MARKER.","order":10},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"WRITE — target row computed server-side from the occupancy grid, written with an EXPLICIT row- and column-pinned range <tab>!A<row>:Z<row> — NEVER values.append. The dedup key rides in column Z of the SAME write.","order":11},{"action":"GOOGLESHEETS_BATCH_GET","description":"READ-AFTER-WRITE — the row is read back and verified cell-for-cell; a mismatch FAILS the verb loudly — recorded: true is only ever returned alongside alignmentVerified: true.","order":12},{"action":"GOOGLESHEETS_BATCH_UPDATE","description":"Amount number format — repeatCell for \"Rp\"#,##0 on column E; a KNOWN-FAILING vendor seam; the outcome is reported as numberFormatApplied, never silent.","order":13},{"action":"GOOGLESHEETS_SORT_RANGE","description":"Date-order sort spanning A..Z (so the ledger column travels WITH its row), starting at the first DATA row. Best-effort ONLY — a sort failure is logged and the successful result still returned.","order":14}]}},{"contract_pin":"expense.provision_sheet.sequence","description":"Provision the expense sheet + evidence folder + persist slots (S7 — idempotent, never double-create)","id":"expense.provision_sheet","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"expense.provision_sheet.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_CREATE_FOLDER"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_CREATE_GOOGLE_SHEET1"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Idempotency — if the Resource Map already holds both slots, return created: false with the existing ids; a retried YES or a concurrent provision must not create a second sheet/folder.","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Creds fail-closed with an actionable message.","order":2},{"action":"GOOGLEDRIVE_CREATE_FOLDER","description":"Evidence folder — CREATE_FOLDER (VERIFY slug) if the slot is empty.","order":3},{"action":"GOOGLESHEETS_CREATE_GOOGLE_SHEET1","description":"Spreadsheet — CREATE_GOOGLE_SHEET1 if the slot is empty, then VALUES_UPDATE of the header row + USD→IDR rate cell (USER_ENTERED so the GOOGLEFINANCE formula is live). Header write failure is NON-FATAL.","order":4},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Merge-write-back — setResourceMap replaces the whole resources blob, so re-read + spread first so a concurrent provision of a DIFFERENT slot isn't clobbered; if both slots now present, adopt the winner's ids.","order":5}]}},{"contract_pin":"reservation.book.sequence","description":"Book a reservation row (S13 — occupancy re-checked immediately before the write)","id":"reservation.book","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"reservation.book.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read reservations grid → mint id + dedup; a duplicate (same patient/date/time) returns { duplicate: true, id }, no write.","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Cross-date guard — the same patient already has active reservation(s) for the same service on OTHER dates ⇒ surface similar_pending for re-confirmation; retried with acknowledgeExisting: true to proceed.","order":2},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read the schedule grid → validateSlot.","order":3},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"R3 re-check occupancy IMMEDIATELY BEFORE THE WRITE — the read at the top of the call is as fresh as the append that follows.","order":4},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Intake gate (R6) — refuse to write an INCOMPLETE intake record unless the row can identify itself (has name + WhatsApp); then HOLD the slot with status needs-intake instead of refusing.","order":5},{"action":"GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND","description":"Append (A:P, USER_ENTERED).","order":6},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"F3 deterministic confirmation composed server-side.","order":7}]}},{"contract_pin":"reservation.reschedule.sequence","description":"Reschedule a reservation (S13 — R9 occupancy re-check excluding the reservation's own row)","id":"reservation.reschedule","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"reservation.reschedule.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read + locate the row by exact col-A id; 0 ⇒ reservation_not_found; >1 ⇒ ambiguous_id.","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Requester-may-mutate check.","order":2},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Validate the NEW slot against the grid.","order":3},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"R9 occupancy re-check excluding this reservation's own row — a same-slot no-op never falsely reports 'taken'.","order":4},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Write only B, C, L, N, O — each contiguous group its own VALUES_UPDATE so column M (Sumber) + D–K are never touched.","order":5},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"F3 deterministic reschedule notice.","order":6}]}},{"contract_pin":"reservation.update.sequence","description":"Update a reservation (S13 — only supplied fields, as contiguous runs)","id":"reservation.update","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"reservation.update.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read + locate the row.","order":1},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Write only the fields SUPPLIED, as CONTIGUOUS runs (contiguousRuns) so an unsupplied column between two supplied ones is never overwritten.","order":2},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Values normalized by the SAME functions book uses — an update can never introduce a value book would have refused; refuses to blank a required intake field.","order":3}]}},{"contract_pin":"reservation.set_status.sequence","description":"Set a reservation's status (S13 — contiguous-run writes)","id":"reservation.set_status","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"reservation.set_status.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read + locate the row by exact col-A id.","order":1},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Write the status cell as a contiguous run; never a span that could blank a neighbour.","order":2}]}},{"contract_pin":"reservation.send_due_reminders.sequence","description":"Send due reservation reminders and stamp the sent marker (S14 — in-flight claim, stamp-after-send)","id":"reservation.send_due_reminders","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"reservation.send_due_reminders.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"kirimdev","upstream_action":"whatsapp.send_template"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_VALUES_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Config gate — reminders disabled ⇒ visible no-op with enabled: false.","order":1},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read the reservations grid (A2:R1000).","order":2},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Plan due reminders (pure; drops anything <= now with the SAME now).","order":3},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"T5 in-flight claim — remindersInFlight set keyed orgId:reservationId:sentCol, claimed SYNCHRONOUSLY so two overlapping runs can never both send the SAME reminder; released in a finally.","order":4},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Mode decision (free_form / template / skipped_past / skipped_no_template) using lastPatientInboundAt.","order":5},{"action":"whatsapp.send_template","description":"Send — free_form ⇒ kirimdevSendText (markdown rendered through the ChannelRenderer seam); template ⇒ kirimdevSendTemplate with reminderTemplateParams.","order":6},{"action":"GOOGLESHEETS_VALUES_UPDATE","description":"Stamp the sent-marker column (USER_ENTERED) so this lead never fires again — idempotent. Sent-but-unstamped is logged as an ERROR.","order":7},{"action":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Honest accounting — skipped absorbs only routine cases; a missing WABA template (noTemplate) or missing WhatsApp transport (noTransport) is undelivered, never skipped.","order":8}]}},{"contract_pin":"GOOGLESHEETS_GET_BATCH_VALUES","description":"Read the project tracker sheet","id":"project_tracker.read","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"range":"ranges","spreadsheet_id":"spreadsheet_id"},"served":true,"toolkit":"googlesheets","upstream_action":"GOOGLESHEETS_GET_BATCH_VALUES"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_FIND_FILE","description":"List files in a Drive folder","id":"drive.list","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"folder_id":"folder_id","limit":"pageSize"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_FIND_FILE"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_GET_FILE_METADATA","description":"Get a Drive file's metadata","id":"drive.get","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"file_id":"fileId"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_GET_FILE_METADATA"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_MOVE_FILE","description":"Move a Drive file between folders","id":"drive.move","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"add_parents":"add_parents","file_id":"file_id","remove_parents":"remove_parents"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_MOVE_FILE"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_DELETE_FILE","description":"Delete a Drive file","id":"drive.delete","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"file_id":"fileId"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_DELETE_FILE"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_COPY_FILE","description":"Copy a Drive file","id":"drive.copy","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"file_id":"file_id"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_COPY_FILE"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE","description":"Share a Drive file (result always states the link's EFFECTIVE access)","id":"drive.share","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"file_id":"file_id","role":"role","type":"type"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_CREATE_FOLDER","description":"Create a Drive folder","id":"drive.create_folder","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"name":"folder_name","parent_id":"parent_id"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_CREATE_FOLDER"}],"sequence":null},{"contract_pin":"drive.upload.sequence","description":"Upload a file to Drive (S8 — stage → create → move → share)","id":"drive.upload","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"drive.upload.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_UPLOAD_FILE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_MOVE_FILE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLEDRIVE_UPLOAD_FILE","description":"Resolve bytes from the durable object store (sourceUrl/objectKey) or inline content.","order":1},{"action":"GOOGLEDRIVE_UPLOAD_FILE","description":"Stage — mint a presigned S3 URL (dedup by md5 — an identical prior upload returns the same key), PUT the bytes.","order":2},{"action":"GOOGLEDRIVE_UPLOAD_FILE","description":"Create — UPLOAD_FILE with the staged FileUploadData.","order":3},{"action":"GOOGLEDRIVE_MOVE_FILE","description":"Move — UPLOAD_FILE has NO parent parameter (passing parent_id is silently ignored, file lands in root), so MOVE_FILE afterwards with add_parents as a STRING and remove_parents of the root placement. Non-fatal: a failed move still leaves a working evidence link.","order":4},{"action":"GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE","description":"Share (optional) — linkAccess: 'anyone-with-link' ⇒ ADD_FILE_SHARING_PREFERENCE { file_id, role: 'reader', type: 'anyone' }; on failure the result states the link's EFFECTIVE access (private) with a note.","order":5}]}},{"contract_pin":"drive.export_pdf.sequence","description":"Export a Google Workspace file to PDF and store the bytes (S9 — bytes never pass through the model)","id":"drive.export_pdf","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"drive.export_pdf.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE","description":"Export — EXPORT_GOOGLE_WORKSPACE_FILE { fileId, mimeType } under the org service entity (camelCase args per the action schema).","order":1},{"action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE","description":"Download — PRIMARY: the bytes ride file.s3url (an R2 presigned URL), fetched server-side bounded by size (25MB cap) + timeout (60s). FALLBACK: inline base64 content. Neither ⇒ clean fail, never guess.","order":2},{"action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE","description":"Store — bytes go to the durable object store; the result returns ONLY { objectKey, url, mimeType, byteSize } — the file bytes NEVER pass through the reasoning engine.","order":3}]}},{"contract_pin":"drive.kb_sync.sequence","description":"Sync a Drive folder into the knowledge base (S10 — content-hash idempotent, bounded walk)","id":"drive.kb_sync","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"drive.kb_sync.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_FIND_FILE"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE"},{"idempotency":"idempotent","params_mapping":{},"served":false,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_DOWNLOAD_FILE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLEDRIVE_FIND_FILE","description":"List — FIND_FILE with query '<folderId>' in parents and trashed = false under the org service entity.","order":1},{"action":"GOOGLEDRIVE_FIND_FILE","description":"Filter — only ingestible mimeTypes (Google Doc → export; pdf/docx/txt/md/csv/octet-stream → download); a Sheet/Slide/folder/image is skipped with a reason.","order":2},{"action":"GOOGLEDRIVE_EXPORT_GOOGLE_WORKSPACE_FILE","description":"Per-file fetch — Google Doc ⇒ EXPORT_GOOGLE_WORKSPACE_FILE (docx export feeds mammoth); else DOWNLOAD_FILE; bytes resolved via presigned URL first (SSRF-guarded, size-capped 25MB, timeout 60s) then inline base64.","order":3},{"action":"GOOGLEDRIVE_DOWNLOAD_FILE","description":"Ingest — content-hash idempotent upsert → recompile the knowledge corpus ONLY when created/updated > 0.","order":4},{"action":"GOOGLEDRIVE_FIND_FILE","description":"Failure semantics — a single bad file is recorded in skipped and the rest proceed; never throws for one file.","order":5}]}},{"contract_pin":"GOOGLEDRIVE_GET_FILE_METADATA","description":"Validate a resource-map entry against Drive (GET_FILE_METADATA)","id":"drive.resource_map.validate","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"file_id":"fileId"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_GET_FILE_METADATA"}],"sequence":null},{"contract_pin":"GOOGLEDRIVE_FIND_FILE","description":"Browse Drive for a resource-map entry (FIND_FILE)","id":"drive.resource_map.browse","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"query":"q"},"served":true,"toolkit":"googledrive","upstream_action":"GOOGLEDRIVE_FIND_FILE"}],"sequence":null},{"contract_pin":"GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT","description":"Read a Google Doc","id":"docs.read","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"document_id":"document_id"},"served":true,"toolkit":"googledocs","upstream_action":"GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT"}],"sequence":null},{"contract_pin":"GOOGLEDOCS_INSERT_TEXT_ACTION","description":"Append text to a Google Doc","id":"docs.append_text","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"document_id":"document_id","text":"text_to_insert"},"served":true,"toolkit":"googledocs","upstream_action":"GOOGLEDOCS_INSERT_TEXT_ACTION"}],"sequence":null},{"contract_pin":"GOOGLEDOCS_REPLACE_ALL_TEXT","description":"Replace text in a Google Doc","id":"docs.replace_text","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"document_id":"document_id","new_text":"replace_text","old_text":"find_text"},"served":true,"toolkit":"googledocs","upstream_action":"GOOGLEDOCS_REPLACE_ALL_TEXT"}],"sequence":null},{"contract_pin":"GOOGLEDOCS_CREATE_DOCUMENT","description":"Create a Google Doc","id":"docs.create","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"title":"title"},"served":true,"toolkit":"googledocs","upstream_action":"GOOGLEDOCS_CREATE_DOCUMENT"}],"sequence":null},{"contract_pin":"GOOGLESLIDES_CREATE_PRESENTATION","description":"Create a Google Slides presentation","id":"slides.create","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"title":"title"},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_CREATE_PRESENTATION"}],"sequence":null},{"contract_pin":"GOOGLESLIDES_PRESENTATIONS_GET","description":"Get a Google Slides presentation","id":"slides.get","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"presentation_id":"presentationId"},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_PRESENTATIONS_GET"}],"sequence":null},{"contract_pin":"slides.append_slide.sequence","description":"Append a slide to a presentation (S15 — one batchUpdate request)","id":"slides.append_slide","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"slides.append_slide.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE","description":"One batchUpdate request under the org service entity — appendSlide ⇒ one createSlide request (optional predefinedLayout).","order":1},{"action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE","description":"Result shaped to the verbs' historical contract ({ presentationId, slideObjectId? }) so existing skill bodies keep working unchanged.","order":2}]}},{"contract_pin":"slides.replace_text.sequence","description":"Replace text across a presentation (S15 — one deck-wide, case-sensitive replaceAllText request)","id":"slides.replace_text","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"slides.replace_text.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE"}],"sequence":{"implemented":true,"steps":[{"action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE","description":"One batchUpdate request under the org service entity — replaceText ⇒ one deck-wide, case-sensitive replaceAllText request.","order":1},{"action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE","description":"Result shaped to the verb's historical contract ({ presentationId, occurrencesChanged }) so existing skill bodies keep working unchanged.","order":2}]}},{"contract_pin":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE","description":"Apply a raw batchUpdate request to a presentation","id":"slides.batch_update","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"presentation_id":"presentationId","requests":"requests"},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_PRESENTATIONS_BATCH_UPDATE"}],"sequence":null},{"contract_pin":"GOOGLESLIDES_PRESENTATIONS_COPY_FROM_TEMPLATE","description":"Copy a presentation from a template (single action — program-level chaining only)","id":"slides.copy_from_template","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"new_title":"new_title","parent_folder_id":"parent_folder_id","template_presentation_id":"template_presentation_id"},"served":true,"toolkit":"googleslides","upstream_action":"GOOGLESLIDES_PRESENTATIONS_COPY_FROM_TEMPLATE"}],"sequence":null},{"contract_pin":"ONE_DRIVE_ONEDRIVE_LIST_FILES","description":"List files in a OneDrive folder","id":"onedrive.list","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"folder_id":"folder_id"},"served":false,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_ONEDRIVE_LIST_FILES"}],"sequence":null},{"contract_pin":"ONE_DRIVE_GET_ITEM","description":"Get a OneDrive file","id":"onedrive.get","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"item_id":"item_id"},"served":true,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_GET_ITEM"}],"sequence":null},{"contract_pin":"ONE_DRIVE_ONEDRIVE_CREATE_TEXT_FILE","description":"Upload a file to OneDrive","id":"onedrive.upload","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"file":"text_content","folder_id":"parent_id"},"served":true,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_ONEDRIVE_CREATE_TEXT_FILE"}],"sequence":null},{"contract_pin":"ONE_DRIVE_MOVE_ITEM","description":"Move a OneDrive file","id":"onedrive.move","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"folder_id":"parent_id","item_id":"item_id"},"served":true,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_MOVE_ITEM"}],"sequence":null},{"contract_pin":"ONE_DRIVE_DELETE_ITEM","description":"Delete a OneDrive file","id":"onedrive.delete","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"item_id":"item_id"},"served":true,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_DELETE_ITEM"}],"sequence":null},{"contract_pin":"ONE_DRIVE_ONEDRIVE_CREATE_FOLDER","description":"Create a OneDrive folder","id":"onedrive.create_folder","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"name":"folder_name","parent_id":"parent_id"},"served":true,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_ONEDRIVE_CREATE_FOLDER"}],"sequence":null},{"contract_pin":"onedrive.kb_sync.sequence","description":"Sync a OneDrive folder into the knowledge base (S10 — OneDrive source)","id":"onedrive.kb_sync","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":true,"toolkit":"platform","upstream_action":"onedrive.kb_sync.sequence"},{"idempotency":"idempotent","params_mapping":{},"served":false,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_ONEDRIVE_LIST_FILES"},{"idempotency":"idempotent","params_mapping":{},"served":false,"toolkit":"one_drive","upstream_action":"ONE_DRIVE_ONEDRIVE_DOWNLOAD_FILE"}],"sequence":{"implemented":true,"steps":[{"action":"ONE_DRIVE_ONEDRIVE_LIST_FILES","description":"List — ONEDRIVE_LIST_FILES { folder_id }.","order":1},{"action":"ONE_DRIVE_ONEDRIVE_DOWNLOAD_FILE","description":"Fetch — ONEDRIVE_DOWNLOAD_FILE { item_id } → the shared byte resolver (presigned URL first, then inline base64); content-hash idempotent ingest.","order":2}]}},{"contract_pin":"EXCEL_GET_WORKSHEET_USED_RANGE","description":"Read a worksheet's used range (Excel Online grid IO)","id":"excel.get_used_range","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"workbook_id":"workbook_id","worksheet":"worksheet"},"served":false,"toolkit":"excel","upstream_action":"EXCEL_GET_WORKSHEET_USED_RANGE"}],"sequence":null},{"contract_pin":"EXCEL_UPDATE_RANGE","description":"Write a range in an Excel Online worksheet (read-then-write; documented race on concurrent creates)","id":"excel.update_range","identity_scope":"service","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"range":"range","values":"values","workbook_id":"workbook_id","worksheet":"worksheet"},"served":false,"toolkit":"excel","upstream_action":"EXCEL_UPDATE_RANGE"}],"sequence":null},{"contract_pin":"GITHUB_CREATE_AN_ISSUE_COMMENT","description":"Add a comment to an existing GitHub issue or pull request","id":"github.create_comment","identity_scope":"org","implementations":[{"idempotency":"non-idempotent","params_mapping":{"body":"body","issue_number":"issue_number","owner":"owner","repo":"repo"},"served":true,"toolkit":"github","upstream_action":"GITHUB_CREATE_AN_ISSUE_COMMENT"}],"sequence":null},{"contract_pin":"GITHUB_CREATE_AN_ISSUE","description":"Open a new GitHub issue","id":"github.create_issue","identity_scope":"org","implementations":[{"idempotency":"non-idempotent","params_mapping":{"body":"body","owner":"owner","repo":"repo","title":"title"},"served":true,"toolkit":"github","upstream_action":"GITHUB_CREATE_AN_ISSUE"}],"sequence":null},{"contract_pin":"GITHUB_LIST_REPOSITORY_ISSUES","description":"List GitHub issues for a repository","id":"github.list_issues","identity_scope":"org","implementations":[{"idempotency":"idempotent","params_mapping":{"owner":"owner","repo":"repo"},"served":false,"toolkit":"github","upstream_action":"GITHUB_LIST_REPOSITORY_ISSUES"}],"sequence":null},{"contract_pin":"LINEAR_CREATE_LINEAR_ISSUE","description":"Create a Linear issue","id":"linear.create_issue","identity_scope":"user","implementations":[{"idempotency":"non-idempotent","params_mapping":{"description":"description","team_id":"team_id","title":"title"},"served":false,"toolkit":"linear","upstream_action":"LINEAR_CREATE_LINEAR_ISSUE"}],"sequence":null},{"contract_pin":"LINEAR_LIST_LINEAR_ISSUES","description":"List Linear issues","id":"linear.list_issues","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"team_id":"team_id"},"served":false,"toolkit":"linear","upstream_action":"LINEAR_LIST_LINEAR_ISSUES"}],"sequence":null},{"contract_pin":"LINEAR_UPDATE_ISSUE","description":"Update a Linear issue","id":"linear.update_issue","identity_scope":"user","implementations":[{"idempotency":"idempotent-with-key","params_mapping":{"description":"description","issue_id":"issue_id","title":"title"},"served":true,"toolkit":"linear","upstream_action":"LINEAR_UPDATE_ISSUE"}],"sequence":null},{"contract_pin":"NOTION_APPEND_BLOCK_CHILDREN","description":"Append a block to a Notion page","id":"notion.append_block","identity_scope":"user","implementations":[{"idempotency":"non-idempotent","params_mapping":{"block":"children","page_id":"block_id"},"served":false,"toolkit":"notion","upstream_action":"NOTION_APPEND_BLOCK_CHILDREN"}],"sequence":null},{"contract_pin":"NOTION_CREATE_NOTION_PAGE","description":"Create a Notion page","id":"notion.create_page","identity_scope":"user","implementations":[{"idempotency":"non-idempotent","params_mapping":{"content":"content","parent_id":"parent_id","title":"title"},"served":false,"toolkit":"notion","upstream_action":"NOTION_CREATE_NOTION_PAGE"}],"sequence":null},{"contract_pin":"NOTION_SEARCH_NOTION_PAGE","description":"Search Notion pages","id":"notion.search","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"query":"query"},"served":false,"toolkit":"notion","upstream_action":"NOTION_SEARCH_NOTION_PAGE"}],"sequence":null},{"contract_pin":"SLACK_FETCH_CONVERSATION_HISTORY","description":"Fetch a Slack channel's message history","id":"slack.fetch_history","identity_scope":"org","implementations":[{"idempotency":"idempotent","params_mapping":{"channel_id":"channel","limit":"limit"},"served":false,"toolkit":"slack","upstream_action":"SLACK_FETCH_CONVERSATION_HISTORY"}],"sequence":null},{"contract_pin":"SLACK_LIST_ALL_CHANNELS","description":"List Slack channels","id":"slack.list_channels","identity_scope":"org","implementations":[{"idempotency":"idempotent","params_mapping":{},"served":false,"toolkit":"slack","upstream_action":"SLACK_LIST_ALL_CHANNELS"}],"sequence":null},{"contract_pin":"SLACK_SEND_MESSAGE","description":"Send a Slack message to a channel","id":"slack.send_message","identity_scope":"org","implementations":[{"idempotency":"non-idempotent","params_mapping":{"channel_id":"channel","text":"text"},"served":true,"toolkit":"slack","upstream_action":"SLACK_SEND_MESSAGE"}],"sequence":null},{"contract_pin":"composio.execute","description":"Execute a dynamic Composio action (escape hatch — schema-validated, audited; typed intents always win)","id":"composio.execute","identity_scope":"user","implementations":[{"idempotency":"non-idempotent","params_mapping":{"action":"action","args":"args","toolkit":"toolkit"},"served":true,"toolkit":"composio","upstream_action":"composio.execute"}],"sequence":null},{"contract_pin":"composio.search","description":"Search the upstream Composio catalog (discovery)","id":"composio.search","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"query":"query"},"served":true,"toolkit":"composio","upstream_action":"composio.search"}],"sequence":null},{"contract_pin":"web.search","description":"Search the web (TinyFish lane — declared data-only; wiring is a later effort)","id":"web.search","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"query":"query"},"served":true,"toolkit":"tinyfish","upstream_action":"web.search"}],"sequence":null},{"contract_pin":"web.fetch","description":"Fetch a web page (TinyFish lane — declared data-only; wiring is a later effort)","id":"web.fetch","identity_scope":"user","implementations":[{"idempotency":"idempotent","params_mapping":{"url":"url"},"served":true,"toolkit":"tinyfish","upstream_action":"web.fetch"}],"sequence":null},{"contract_pin":"render.html_to_pdf","description":"Render HTML to PDF (local headless Chromium — same 'produce a PDF' intent as drive.export_pdf)","id":"render.html_to_pdf","identity_scope":"service","implementations":[{"idempotency":"idempotent","params_mapping":{"html":"html"},"served":true,"toolkit":"render","upstream_action":"render.html_to_pdf"}],"sequence":null},{"contract_pin":"whatsapp.send_template","description":"Send a WhatsApp template message (kirimdev lane — the reminder-send half of sendDueReminders)","id":"whatsapp.send_template","identity_scope":"service","implementations":[{"idempotency":"non-idempotent","params_mapping":{"params":"params","phone":"phone","template":"template"},"served":true,"toolkit":"kirimdev","upstream_action":"whatsapp.send_template"}],"sequence":null}],"surface":"vocabulary","version":"584af6dde90e"},"meta":{"request_id":"req_MAiPR3kebVXO4UQD","vocabulary_version":"584af6dde90e"}}