{"ok":true,"site":{"host":"gabrielcatdeveloper.github.io","url":"https://gabrielcatdeveloper.github.io/portami/","type":"live","desc":"portami","tools":[{"name":"get_identity","kind":"answer","impl":"imperative","description":"Return the current anonymous identity (pubKey, anonId, createdAt). Read-only — never returns the private key.","title":"Get identity","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"export_identity_jwk","kind":"act","impl":"imperative","description":"Return the private key as a JSON Web Key. ⚠️ Anyone with this file can sign as you — handle with care. Destructive to leak.","title":"Export identity (raw JWK)","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"import_identity_jwk","kind":"transact","impl":"imperative","description":"Replace the local identity with the one in the provided JWK. ⚠️ Destructive: your previous anonId and any votes tied to it are gone.","title":"Import identity (raw JWK)","inputSchema":{"type":"object","properties":{"privKeyJwk":{"type":"string","description":"The JSON Web Key containing the private key (must include `d`)."}},"required":["privKeyJwk"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"regenerate_identity","kind":"transact","impl":"imperative","description":"Generate a fresh Ed25519 keypair. ⚠️ Destructive: previous anonId, votes, and trust relationships are lost.","title":"Regenerate identity","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"reset_identity","kind":"transact","impl":"imperative","description":"Wipe the identity AND all local data (routes, proposals, recordings, drafts, paired devices, trips, sync metadata). ⚠️⚠️ Very destructive — cannot be undone.","title":"Reset everything","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"export_identity_backup_file","kind":"act","impl":"imperative","description":"Return a passphrase-protected backup file (.portami-backup shape). Pass the same passphrase to `import_identity_backup_file` to restore. PBKDF2-SHA256 600k iters + AES-GCM.","title":"Export identity backup (passphrase-protected)","inputSchema":{"type":"object","properties":{"passphrase":{"type":"string","description":"Passphrase (≥8 chars) used to encrypt the private key."}},"required":["passphrase"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"import_identity_backup_file","kind":"transact","impl":"imperative","description":"Restore an identity from a passphrase-protected backup. ⚠️ Destructive: replaces the current identity.","title":"Import identity backup (passphrase-protected)","inputSchema":{"type":"object","properties":{"backupJson":{"type":"string","description":"The full backup object as a JSON string (the contents of the .portami-backup file)."},"passphrase":{"type":"string","description":"The passphrase used at export time."}},"required":["backupJson","passphrase"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_routes","kind":"answer","impl":"imperative","description":"List routes from the local cache. Optional filters: only mine, only favourites, vehicle kind, name substring.","title":"List routes","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"onlyMine":{"type":"string","description":"Filter to routes I created.","enum":["true","false"]},"onlyFavorites":{"type":"string","description":"Filter to favourite routes.","enum":["true","false"]},"vehicleKind":{"type":"string","description":"Filter by vehicle kind.","enum":["bus","train","tram","metro","other"]},"query":{"type":"string","description":"Case-insensitive substring match on the route name."},"limit":{"type":"number","description":"Max routes to return (default 100, max 500).","minimum":1,"maximum":500}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_route","kind":"answer","impl":"imperative","description":"Return a single route by id (from the local cache).","title":"Get route by id","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"The route id."}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"find_routes_nearby","kind":"answer","impl":"imperative","description":"Query the server for routes near a coordinate. Results are merged into the local cache and returned.","title":"Find routes nearby","inputSchema":{"type":"object","properties":{"lat":{"type":"number","description":"Latitude (WGS84).","minimum":-90,"maximum":90},"lng":{"type":"number","description":"Longitude (WGS84).","minimum":-180,"maximum":180},"radiusKm":{"type":"number","description":"Search radius in km (default 5).","minimum":0.1,"maximum":50}},"required":["lat","lng"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"mark_route_favorite","kind":"act","impl":"imperative","description":"Toggle a route's favourite flag in the local cache.","title":"Mark route as favourite","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"favorite":{"type":"string","description":"\"true\" to mark, \"false\" to unmark.","enum":["true","false"]}},"required":["routeId","favorite"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_active_buses","kind":"answer","impl":"imperative","description":"List buses currently on trips. Optionally filter by routeId. Returns trip id, route, anonId, position, speed, vehicle kind.","title":"Get active buses","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Optional. If set, return only buses on this route."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_stop_request","kind":"answer","impl":"imperative","description":"Return how to ask the driver to stop on a given route (button, shout, app, other/unknown) and the confirmation count.","title":"Get stop-request info for a route","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"set_stop_request","kind":"act","impl":"imperative","description":"Propose new stop-request info for a route. The server increments the confirmation count if it already exists; otherwise it seeds a new entry.","title":"Set stop-request info for a route","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"type":{"type":"string","description":"Mechanism to request a stop.","enum":["button","shout","app","other","unknown"]},"notes":{"type":"string","description":"Free-text instructions (optional)."},"buttonPhotoUrl":{"type":"string","description":"Data URL of a photo of the button (optional)."}},"required":["routeId","type"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_proposals","kind":"answer","impl":"imperative","description":"List proposals for a route. Optional status filter. Pulls from the server (and merges into local cache).","title":"List proposals","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"status":{"type":"string","description":"Filter by status.","enum":["pending","approved","rejected","expired"]},"limit":{"type":"number","description":"Max proposals to return (default 50, max 200).","minimum":1,"maximum":200}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_proposal","kind":"answer","impl":"imperative","description":"Read a single proposal by id (from the local cache).","title":"Get proposal","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"proposalId":{"type":"string","description":"Proposal id."}},"required":["proposalId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"create_proposal","kind":"act","impl":"imperative","description":"Propose changes to a route. Pass `diff` as a JSON string with one or more RouteDiff entries. Each entry shape: stop-added {stop}, stop-removed {stopId}, stop-moved {stopId, fromLat, fromLng, toLat, toLng}, stop-renamed {stopId, fromName, toName}, polyline-partial-replaced {fromIdx, toIdx, newSegment}, meta-changed {field:\"name\", from, to}.","title":"Create proposal","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"title":{"type":"string","description":"Short title for the proposal."},"rationale":{"type":"string","description":"Why this change is needed (optional)."},"diff":{"type":"string","description":"JSON-encoded array of RouteDiff entries. Example: [{\"kind\":\"stop-renamed\",\"stopId\":\"s1\",\"fromName\":\"A\",\"toName\":\"A (norte)\"}]"}},"required":["routeId","title","diff"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"vote_proposal","kind":"act","impl":"imperative","description":"Cast your vote (approve or reject) on a proposal. You can only vote once per proposal; revote is not allowed.","title":"Vote on a proposal","inputSchema":{"type":"object","properties":{"proposalId":{"type":"string","description":"Proposal id."},"vote":{"type":"string","description":"Your vote.","enum":["approve","reject"]}},"required":["proposalId","vote"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_proposal_diff_kinds","kind":"answer","impl":"imperative","description":"Return the RouteDiff kinds accepted by `create_proposal`.","title":"List supported proposal diff kinds","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_active_trip","kind":"answer","impl":"imperative","description":"Return the currently active trip (or null). Includes route, startedAt, last sample.","title":"Get active trip","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"start_trip","kind":"act","impl":"imperative","description":"Start a new trip on a route. Requires the route to be in the local cache. Calls POST /trips/start (signed).","title":"Start a trip","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id to start the trip on."}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"end_trip","kind":"act","impl":"imperative","description":"End the currently active trip. Also stops any in-flight trip-share. Reason is one of \"manual\" (user), \"heuristic\" (auto-end from trip detector), or \"arrival\" (got off at destination).","title":"End active trip","inputSchema":{"type":"object","properties":{"reason":{"type":"string","description":"Why the trip ended.","enum":["manual","heuristic","arrival"]}},"required":["reason"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_recent_trips","kind":"answer","impl":"imperative","description":"Return trips stored locally, newest first.","title":"List recent trips","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Optional routeId filter."},"limit":{"type":"number","description":"Max trips to return (default 20, max 100).","minimum":1,"maximum":100}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"push_gps_sample","kind":"act","impl":"imperative","description":"Push a single GPS sample for the active trip to the server. Requires the \"Modo colaborador\" flag enabled in Settings and an active trip. Signed POST /trips/:id/samples.","title":"Push GPS sample to server","inputSchema":{"type":"object","properties":{"ts":{"type":"number","description":"Sample timestamp (ms since epoch)."},"lat":{"type":"number","description":"Latitude.","minimum":-90,"maximum":90},"lng":{"type":"number","description":"Longitude.","minimum":-180,"maximum":180},"acc":{"type":"number","description":"Accuracy in meters."},"speed":{"type":"number","description":"Speed in m/s (optional)."}},"required":["ts","lat","lng","acc"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"start_trip_share","kind":"act","impl":"imperative","description":"Begin broadcasting the active trip to all connected paired peers. Requires an active trip (call start_trip first). Returns the share id and per-recipient status.","title":"Start trip sharing","inputSchema":{"type":"object","properties":{"plannedRouteJson":{"type":"string","description":"Optional JSON-encoded Journey plan (pass to override the trip's attached plan)."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"stop_trip_share","kind":"act","impl":"imperative","description":"Stop broadcasting the active trip and notify recipients.","title":"Stop trip sharing","inputSchema":{"type":"object","properties":{"reason":{"type":"string","description":"Why sharing stopped.","enum":["manual","heuristic","arrival","trip-ended"]}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_outgoing_share","kind":"answer","impl":"imperative","description":"Return the currently active outgoing share (or null if not sharing). Includes per-recipient delivery status.","title":"Get active outgoing share","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_incoming_shares","kind":"answer","impl":"imperative","description":"List trips friends are currently sharing with you (Following). Includes last known location, planned route, ETA to next stop.","title":"List incoming shared trips","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_outgoing_share_history","kind":"answer","impl":"imperative","description":"Return the last 20 outgoing trip shares (newest first).","title":"List outgoing share history","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"retry_trip_share_recipient","kind":"act","impl":"imperative","description":"Manually retry sending the trip-share-start message to a specific paired device (by deviceId). No-op if the peer is disconnected.","title":"Retry a trip-share recipient","inputSchema":{"type":"object","properties":{"deviceId":{"type":"string","description":"Paired device id (== pubKey)."}},"required":["deviceId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_friend_location","kind":"answer","impl":"imperative","description":"Return the most recent location received from a specific friend (by their anonId).","title":"Get a friend's last location","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"fromAnonId":{"type":"string","description":"Friend anonId."}},"required":["fromAnonId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"remove_incoming_share","kind":"act","impl":"imperative","description":"Delete the local record of an incoming shared trip. The friend keeps sharing — you just stop following.","title":"Remove an incoming shared trip","inputSchema":{"type":"object","properties":{"fromAnonId":{"type":"string","description":"Friend anonId."}},"required":["fromAnonId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"is_sharing","kind":"answer","impl":"imperative","description":"Return true iff the user is currently broadcasting a trip.","title":"Is sharing active?","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_peer_statuses","kind":"answer","impl":"imperative","description":"Return the current WebRTC connection status of every paired device: disconnected, connecting, connected, reconnecting, unreachable, revoked, error.","title":"List WebRTC peer statuses","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_paired_devices","kind":"answer","impl":"imperative","description":"List every WebRTC-paired device the user has, with last-seen timestamps.","title":"List paired devices","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"revoke_paired_device","kind":"act","impl":"imperative","description":"Remove a paired device from the local store and close its WebRTC connection. ⚠️ Destructive: you'll need to re-pair to share trips with them again.","title":"Revoke paired device","inputSchema":{"type":"object","properties":{"deviceId":{"type":"string","description":"Device id (== pubKey)."}},"required":["deviceId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"create_pairing_offer","kind":"act","impl":"imperative","description":"Initiate a pairing session: generate an SDP offer. The returned `offer` is an opaque JSON string — paste it into the friend's portami (or hand it to `join_with_offer`).","title":"Create pairing offer","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"join_with_pairing_offer","kind":"act","impl":"imperative","description":"Joiner side: take an SDP offer from the initiator and produce an SDP answer to send back.","title":"Join with pairing offer","inputSchema":{"type":"object","properties":{"offer":{"type":"string","description":"The SDP offer JSON string."}},"required":["offer"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"finish_pairing_as_initiator","kind":"act","impl":"imperative","description":"Initiator side: paste the joiner's SDP answer here to complete the handshake.","title":"Finish pairing as initiator","inputSchema":{"type":"object","properties":{"answer":{"type":"string","description":"The SDP answer JSON string."}},"required":["answer"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"reset_pairing","kind":"act","impl":"imperative","description":"Abort the current pairing flow and clear all transient state. Useful when a flow stalls.","title":"Reset pairing flow","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_pairing_status","kind":"answer","impl":"imperative","description":"Return the current pairing phase (idle, awaiting-peer, verifying, transferring, syncing, success, error) and the pair-code for verification.","title":"Get pairing status","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"create_invite_link","kind":"act","impl":"imperative","description":"Create a /connect deeplink that bundles an SDP offer with the emitter's anonId + alias + optional tripShareId. The friend opens it in portami to pair + accept the trip share.","title":"Create invite link","inputSchema":{"type":"object","properties":{"tripShareId":{"type":"string","description":"Optional tripShareId to auto-attach on accept."},"baseUrl":{"type":"string","description":"Override the origin (default: current location)."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"build_answer_back_url","kind":"act","impl":"imperative","description":"Joiner side: produce the /connect-back URL containing the SDP answer, to be opened by the initiator.","title":"Build answer-back URL","inputSchema":{"type":"object","properties":{"emitterAnonId":{"type":"string","description":"Initiator's anonId (the `for` parameter)."},"answer":{"type":"string","description":"The SDP answer JSON string."}},"required":["emitterAnonId","answer"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"plan_journey","kind":"act","impl":"imperative","description":"Plan a multi-leg journey from a coordinate A to coordinate B. Optional filters: departAfter (ms epoch), maxWalkSpeedMs (default 1.4), maxBoardings (default 3), walkRadiusM (default 600), excludeRouteIds (JSON array of strings), vehicleKinds (JSON array).","title":"Plan a journey (A → B)","inputSchema":{"type":"object","properties":{"fromLat":{"type":"number","description":"Origin latitude.","minimum":-90,"maximum":90},"fromLng":{"type":"number","description":"Origin longitude.","minimum":-180,"maximum":180},"toLat":{"type":"number","description":"Destination latitude.","minimum":-90,"maximum":90},"toLng":{"type":"number","description":"Destination longitude.","minimum":-180,"maximum":180},"departAfterUtc":{"type":"number","description":"Depart after this timestamp (ms epoch). Defaults to now."},"maxWalkSpeedMs":{"type":"number","description":"Max walking speed in m/s (default 1.4)."},"maxBoardings":{"type":"number","description":"Max boardings including the first (default 3)."},"walkRadiusM":{"type":"number","description":"Search radius around from/to in meters (default 600)."},"excludeRouteIdsJson":{"type":"string","description":"JSON array of route ids to exclude, e.g. [\"r1\",\"r2\"]."},"vehicleKindsJson":{"type":"string","description":"JSON array of vehicle kinds to include, e.g. [\"bus\",\"train\"]. Allowed: bus, train, tram, metro, other."}},"required":["fromLat","fromLng","toLat","toLng"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_incidents","kind":"answer","impl":"imperative","description":"List active service incidents. Optional routeId filter. Auto-hides resolved + auto-expired.","title":"List incidents","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Optional route id filter."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"report_incident","kind":"act","impl":"imperative","description":"Report a service incident (cancellation, delay, diversion, other) on a route. Signed.","title":"Report incident","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"kind":{"type":"string","description":"Incident kind.","enum":["cancellation","delay","diversion","other"]},"reason":{"type":"string","description":"Free text explaining the incident (e.g. \"Manifestación en Sol\")."},"endsAt":{"type":"number","description":"Optional auto-expiry timestamp (ms epoch)."}},"required":["routeId","kind","reason"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"resolve_incident","kind":"act","impl":"imperative","description":"Mark an incident as resolved. Signed.","title":"Resolve incident","inputSchema":{"type":"object","properties":{"incidentId":{"type":"string","description":"Incident id."}},"required":["incidentId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_stop_alerts","kind":"answer","impl":"imperative","description":"List local stop alerts. Either pass a routeId to filter, or omit to list every alert on the device.","title":"List stop alerts","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id filter (omit for all)."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"add_stop_alert","kind":"act","impl":"imperative","description":"Add a stop alert. Provide either `triggerMinutes` (ETA-based) or `triggerDistanceM` (distance-based) or both.","title":"Add stop alert","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id this alert belongs to."},"stopId":{"type":"string","description":"Stop id within the route."},"stopName":{"type":"string","description":"Stop name (human-readable, used in the notification)."},"triggerMinutes":{"type":"number","description":"Fire when ETA ≤ this many minutes."},"triggerDistanceM":{"type":"number","description":"Fire when distance ≤ this many meters."}},"required":["routeId","stopId","stopName"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"remove_stop_alert","kind":"transact","impl":"imperative","description":"Delete a stop alert by id.","title":"Remove stop alert","inputSchema":{"type":"object","properties":{"id":{"type":"number","description":"Alert id (autoincrement)."}},"required":["id"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"reset_stop_alerts_for_route","kind":"act","impl":"imperative","description":"Reset the \"triggered\" flag on every alert for a route so they'll fire again on the next trip.","title":"Reset stop alerts for a route","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"clear_stop_alerts","kind":"transact","impl":"imperative","description":"Delete every stop alert on the device. ⚠️ Destructive.","title":"Clear all stop alerts","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_settings","kind":"answer","impl":"imperative","description":"Return current settings: language, collaborate flag, testing mode + GPS source, and server health snapshot.","title":"Get settings","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"set_collaborate_enabled","kind":"act","impl":"imperative","description":"Enable or disable the \"Modo colaborador\" flag. When ON, every GPS sample of an active trip is POSTed (signed) to the server. OFF by default — the user must opt in.","title":"Toggle \"GPS to server\"","inputSchema":{"type":"object","properties":{"enabled":{"type":"string","description":"\"true\" to enable, \"false\" to disable.","enum":["true","false"]}},"required":["enabled"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"set_testing_mode","kind":"act","impl":"imperative","description":"Enable or disable testing mode. When ON, MSW intercepts every API call and the GPS source becomes synthetic (unless you also pass gpsMode=\"real\"). Requires page reload for the change to fully take effect.","title":"Toggle testing mode","inputSchema":{"type":"object","properties":{"enabled":{"type":"string","description":"\"true\" to enable testing mode.","enum":["true","false"]},"gpsMode":{"type":"string","description":"GPS source when testing mode is on.","enum":["simulated","real"]}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"set_language","kind":"act","impl":"imperative","description":"Change the UI language (es/ca/en). Reload the page for the change to apply everywhere.","title":"Set UI language","inputSchema":{"type":"object","properties":{"language":{"type":"string","description":"Language code.","enum":["es","ca","en"]}},"required":["language"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_current_position","kind":"answer","impl":"imperative","description":"Return the most recent GPS sample (or wait for one if none has arrived). May trigger the browser permission prompt on first call.","title":"Get current GPS position","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_geolocation_permission","kind":"answer","impl":"imperative","description":"Return the current geolocation permission state without requesting it. One of: unknown, granted, denied, prompt, error.","title":"Get geolocation permission","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"export_my_routes_geojson","kind":"act","impl":"imperative","description":"Build a signed GeoJSON FeatureCollection containing all routes the user created + their proposals. Returns the full JSON so the agent can write it to disk or hand it off.","title":"Export my routes as GeoJSON","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"import_geojson","kind":"act","impl":"imperative","description":"Import a previously-exported FeatureCollection. Verifies the signature; if invalid, marks the import as read-only (no edits applied). Use `resolutionsJson` to override the per-route resolution (default: keep if exists, replace otherwise).","title":"Import a GeoJSON export","inputSchema":{"type":"object","properties":{"geojson":{"type":"string","description":"The GeoJSON object as a JSON string."},"resolutionsJson":{"type":"string","description":"Optional JSON map of routeId → mode. Mode is one of: replace, keep, merge."}},"required":["geojson"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_bus_reports","kind":"answer","impl":"imperative","description":"List observations about specific buses on a route, newest first.","title":"List bus reports","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"limit":{"type":"number","description":"Max reports to return (default 20, max 100).","minimum":1,"maximum":100}},"required":["routeId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"report_bus","kind":"act","impl":"imperative","description":"Record an observation about a specific bus on a route (license plate, stop button presence, photo, notes). Signed.","title":"Report a bus","inputSchema":{"type":"object","properties":{"routeId":{"type":"string","description":"Route id."},"plate":{"type":"string","description":"License plate / fleet number."},"hasStopButton":{"type":"string","description":"\"true\" if the bus has a stop button.","enum":["true","false"]},"notes":{"type":"string","description":"Free notes (optional)."},"buttonPhotoUrl":{"type":"string","description":"Data URL of a photo of the button (optional)."}},"required":["routeId","plate"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"send_rescue_me","kind":"transact","impl":"imperative","description":"Broadcast a panic alert to every connected paired peer. Use ONLY when the user is in trouble. The receivers see a top-of-screen banner with the user's last location.","title":"Send a rescue-me alert","inputSchema":{"type":"object","properties":{"message":{"type":"string","description":"Short message included in the alert (optional)."},"lat":{"type":"number","description":"Optional current latitude to attach."},"lng":{"type":"number","description":"Optional current longitude to attach."},"accuracyM":{"type":"number","description":"Optional accuracy in meters."}},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_pending_rescues","kind":"answer","impl":"imperative","description":"List rescue alerts received from paired friends that have not yet been acknowledged.","title":"List pending rescue alerts","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"list_all_rescues","kind":"answer","impl":"imperative","description":"List every rescue alert currently in memory (acknowledged + pending).","title":"List all rescue alerts","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"acknowledge_rescue","kind":"act","impl":"imperative","description":"Locally mark a rescue alert as acknowledged (no effect on the sender). Use `remove_rescue` to also drop it from memory.","title":"Acknowledge a rescue alert","inputSchema":{"type":"object","properties":{"rescueId":{"type":"string","description":"Rescue id."}},"required":["rescueId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"remove_rescue","kind":"act","impl":"imperative","description":"Drop a rescue alert from the in-memory store.","title":"Remove a rescue alert","inputSchema":{"type":"object","properties":{"rescueId":{"type":"string","description":"Rescue id."}},"required":["rescueId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_rescue_ttl_ms","kind":"answer","impl":"imperative","description":"Return the in-memory TTL (ms) applied to rescue alerts after acknowledgement. Default 5 minutes.","title":"Get rescue TTL","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"required":[],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_server_health","kind":"answer","impl":"imperative","description":"Return the current health snapshot: status (unknown | healthy | degraded | offline), lastCheckedAt, consecutiveFailures, apiBase.","title":"Get server health","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"},{"name":"get_api_base","kind":"answer","impl":"imperative","description":"Return the configured API base URL. Empty string means the app is using MSW mocks (dev mode, or testing enabled).","title":"Get API base URL","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/portami/"}],"category":"Travel & Events","favicon":"/favicons/gabrielcatdeveloper.github.io.svg","apiSurface":"spec","toolCount":70}}