cloudflare_access_apps_list
ChatGPTList the Cloudflare Access applications on an account. Read-only.
cloudflare_access_policies_list
ChatGPTList the policies on one Cloudflare Access application. Read-only.
cloudflare_dns_create_record
ChatGPTCreate a new DNS record in a Cloudflare zone. Common types: A (IPv4), AAAA (IPv6), CNAME (alias), TXT (text), MX (mail). The proxied flag only applies to A/AAAA/CNAME and routes traffic through Cloudflare's edge. NOTE on TTL and proxied: if proxied=True, Cloudflare overrides the ttl to 1 ("auto") regardless of what you pass. This is documented Cloudflare behavior, not a bug. For non-proxied records, ttl can be 1 (auto) or 60..86400 seconds.
cloudflare_dns_delete_record
ChatGPTDelete a DNS record from a Cloudflare zone. Destructive: the record is gone immediately and the deletion propagates through Cloudflare's network in seconds. Use cloudflare_dns_list_records first to confirm you have the right record_id.
cloudflare_dns_list_records
ChatGPTList DNS records in a Cloudflare zone, optionally filtered. Use this to inspect existing records and to get the record id values needed by update_record and delete_record. Returns up to 100 records per call; if a zone has more, narrow down with the type or name filters.
cloudflare_dns_list_zones
ChatGPTList every Cloudflare zone the saved token has access to. Use this first to discover zone IDs before listing or modifying records. The result includes each zone's id, name, status, and whether it's paused.
cloudflare_dns_update_record
ChatGPTPartially update an existing DNS record. Change only the fields you pass; everything else stays as it was. This is a PATCH operation: omitted fields are left untouched. This is the safe semantic — "change proxied to true, leave the rest alone" — that matches how users think about edits. Use cloudflare_dns_list_records first to get the record_id. NOTE on TTL: if you set proxied=True (or the record is already proxied), Cloudflare will force ttl=1 even if you pass another value. To change ttl freely, the record must be non-proxied. To change the record's type or name (e.g. A -> CNAME), delete the record and create a new one — there's no in-place type change.
cloudflare_ruleset_get
ChatGPTRead one Cloudflare ruleset with its rules. Read-only. Includes each rule's expression, which is where a match such as ip.geoip.country ne "FR" becomes visible.
cloudflare_rulesets_list
ChatGPTList the rulesets on a Cloudflare zone: WAF, rate limiting, redirects. Read-only. Names the rulesets; the rules inside one come from cloudflare_ruleset_get.
cloudflare_security_events
ChatGPTList recent Cloudflare firewall events on a zone, newest first. Read-only. Answers questions like "what was blocked for visitors from BE yesterday" without turning any protection off.
cloudflare_zone_settings
ChatGPTRead the security-relevant settings of a Cloudflare zone. Read-only. Security level, challenge TTL, browser check, WAF and TLS.
compute
ChatGPTManage the lifecycle of cloud VMs through a saved compute integration. A single tool with an operation selector. Which cloud is driven comes from the integration's saved metadata.driver (currently 'digitalocean').
compute_destroy
ChatGPTDestroy a VM and its disk. This cannot be undone. Without confirm=True it changes nothing and returns a summary: show it to the user, get their agreement, then call again with confirm=True.
compute_images
ChatGPTList the operating system images a VM can be created from (slug).
compute_list
ChatGPTList the VMs (droplets) in a saved compute integration's cloud account.
compute_power_off
ChatGPTPower off a VM. Everything running on it stops until it is powered on again.
compute_power_on
ChatGPTPower on a VM.
compute_regions
ChatGPTList the regions a VM can be created in (slug and name).
compute_sizes
ChatGPTList the VM sizes available, with monthly and hourly prices.
compute_snapshot
ChatGPTTake a snapshot of a VM.
list_integrations
ChatGPTList the saved integrations for the calling user. Use this when you need to know which integrations are available before calling a provider tool (telegram_send_message, resend_send_email, cloudflare_dns_*). All those tools take an integration_name; this is how to discover the valid names. Returns: { "ok": true, "items": [ { "name": "<user-chosen alias>", "provider": "cloudflare" | "telegram" | "resend", "created_at": "<ISO8601>" }, ... ], "count": <int> } The returned data deliberately does NOT include each integration's metadata (e.g. Telegram default_chat_id, Resend default_from) nor the encrypted credentials. Those are managed exclusively from the dashboard UI by the authenticated user, and the LLM cannot read or modify them via tools. If the user has no integrations, returns an empty list with count zero — not an error.
notifications_ack
ChatGPTClear an item from the notifications inbox once you have handled it. Clear only items you have read: clearing an unfamiliar job_id, or "all", could remove a result another conversation is still waiting for.
notifications_check
ChatGPTList what you haven't seen yet: completed background jobs and announcements. Each completed job comes with a short summary, plus a count of jobs still running. The completed jobs it returns are marked as read, so a second call returns only what arrived since.
notifications_get
ChatGPTRead one notification in full: a background job's result or an announcement.
resend_send_email
ChatGPTSend a transactional or operational email via Resend. Use this for emails that need to land prolijo: deploy reports, backup summaries, certificate-expiry notices, weekly digests, invoices. For quick alerts where formality doesn't matter, telegram_send_message is faster to consume. The sending address (from_address) defaults to the integration's configured metadata.default_from. You can override it if the integration's API key has access to multiple verified domains and a particular send needs a specific one — but the domain MUST be verified in the Resend account this API key belongs to. The recipient list (to) is always passed in by the caller because email is, by nature, "send to whoever needs to read this." Unlike Telegram (where the bot has a small fixed set of reachable chats), email targets are open-world. At least one of html and text must be provided. If both are, Resend sends a multipart email and the client picks. Plain-text fallback is always good practice.
sentinel_agent_update
ChatGPTUpdate a Linux host's SentinelX agent to the latest version, remotely. It runs the official installer with sudo and restarts the agent, which cuts calls in flight: run it when nothing important is in progress, and only with confirm=True. Needs passwordless sudo for the agent's user and script_run enabled; otherwise it stops before changing anything and says why. Linux only. On a failure it reports the stage and how to recover.
sentinel_capabilities
ChatGPTGet SentinelX capabilities, including allowed commands and rich service definitions. Defaults to detail="summary" for a bounded discovery view (host/version, ops, limits, policy counts) without command/service values or playbook bodies. Pass detail="full" for the legacy full response. Requires agent >= 0.10.0; older agents ignore the selector and return the full response.
sentinel_chmod
ChatGPTChange the mode bits of a path on the target host. The path must resolve under an rw file_ops entry.
sentinel_chown
ChatGPTChange the owner and/or group of a path on the target host. The path must resolve under an rw file_ops entry. At least one of owner/group is required. Note: chown to a different owner normally requires root. The agent typically runs as a non-root service user, so this will usually fail with permission_denied — the agent reports that explicitly rather than pretending it worked. It is included for completeness and for the cases where the agent IS privileged.
sentinel_clear_default_host
ChatGPTRemove the user's default host preference. After this, tool calls without an explicit host_id will fall back to the standard behavior: auto-route if only one host is connected, or ambiguous_host error if multiple are connected. Returns: {"ok": True, "removed": true|false}. removed=false means no default was set in the first place (no-op, not an error — the tool is idempotent).
sentinel_context_archive
ChatGPTArchive a context: it stays searchable but leaves the active listings. Clears the conversations bound to it. Reversible with sentinel_context_reactivate.
sentinel_context_delete
ChatGPTPermanently delete a context and all its revisions. This cannot be undone. Prefer sentinel_context_archive for normal cleanup.
sentinel_context_export_md
ChatGPTReturn a saved context as Markdown. It never writes files.
sentinel_context_list
ChatGPTList saved contexts with compact metadata.
sentinel_context_reactivate
ChatGPTBring an archived context back to active and return its state.
sentinel_context_resume
ChatGPTRestore a saved context's state to continue the work. It restores knowledge, not authorization: revalidate live host, service, Git and job state, and any human approval, before acting on it. Archived contexts are returned read-only; sentinel_context_reactivate brings one back.
sentinel_context_save
ChatGPTSave operational state so the work can continue later, in this or another chat. Without context_id it creates a new context, and data needs title, objective and summary. With context_id it adds a new revision; pass base_revision (the revision you last read) so a concurrent change is not overwritten. Save concise operational state, never transcripts or secrets. To make a subcontext, set data.parent_context_id; data.related_context_ids links peers.
sentinel_context_search
ChatGPTSearch saved contexts by keyword and tag.
sentinel_context_settings_get
ChatGPTRead your continuity preferences: when to save automatically, and Markdown mirroring.
sentinel_context_settings_set
ChatGPTChange your continuity preferences. Pass only what you want to change.
sentinel_context_status
ChatGPTShow which context, if any, is bound to this conversation.
sentinel_copy
ChatGPTCopy a file or directory on the target host. BOTH src and dst must resolve under an rw file_ops entry. The destination is gated exactly like the source — otherwise copy would be an exfiltration primitive (read from an rw path, write the bytes anywhere). Directories are copied recursively.
sentinel_delete
ChatGPTDelete a file or (with recursive=true) a directory. The path must resolve under an rw file_ops entry. The agent ALWAYS takes a backup before destroying anything: a file is copied to a sibling .bak.<timestamp>, a directory is archived to a sibling .bak.<timestamp>.tar.gz. If the backup cannot be made the delete is refused (backup_failed) — nothing is destroyed without a recovery path. Deleting a directory requires recursive=true explicitly. Without it, a directory is rejected with is_directory. This makes accidental rm -rf-shaped mistakes impossible to trigger by a single confused call.
sentinel_diagnose
ChatGPTCheck SentinelX's own records for the cause of a problem. Run it before filing a report. For a stall, a disconnect loop, a blocked call or an operation that keeps failing. It names the cause when the evidence is conclusive, with the actions to take, and often ends the matter without a report. A result of conclusion "inconclusive" with should_report true is a real answer: file the report rather than picking the nearest-looking cause. The evidence block is always returned.
sentinel_edit
ChatGPTEdit a file on the connected host using safe, structured operations. Modes: - replace literal old -> new_text - regex pattern -> new_text - replace-block start_marker..end_marker -> new_text - append append new_text to file - prepend prepend new_text to file - write overwrite file with new_text For large content use the chunked variant: sentinel_edit_upload_init, sentinel_edit_upload_file, sentinel_edit_upload_complete. Security: path must resolve under a file_ops entry whose access is rw in the agent's config (the unified r/rw path model). A path that is only readable (access: r), or outside the allowlist, or that escapes it via ../symlink, is rejected with path_not_allowed — edit is a mutation and is gated exactly like move/copy/delete. This is enforced agent-side, after canonicalizing symlinks.
sentinel_edit_upload_complete
ChatGPTRun the edit using files previously staged via sentinel_edit_upload_file.
sentinel_edit_upload_file
ChatGPTStage one role file for an edit upload session. Provide exactly one of content (utf-8 text) or content_base64 (binary). role must be 'old' or 'new'. Defaults filename to <role>.txt.
sentinel_edit_upload_init
ChatGPTInitialize an edit upload session. Returns an upload_id to use in the follow-up calls (sentinel_edit_upload_file, sentinel_edit_upload_complete).
sentinel_exec
ChatGPTExecute a command through SentinelX (must be in the allowed commands list). Set background=true for a long-running command: it returns a job_id immediately instead of blocking, and the result lands in the notifications pool — read it later with notifications_check. Under background the wall-clock ceiling is raised to 3600s. notify_telegram / notify_resend request a push when the job finishes (on success AND failure). Each accepts: - false (default): no push. - true: use your default integration of that type (errors if you have none, or several with no clear default — name one instead). - "<integration_name>": use that specific saved integration. Any notify_* implies background=true.
sentinel_file_download_link
ChatGPTCreate a temporary link that downloads one file from a host. Use it to hand the user a file of up to 512 MiB (the hub's transfer limit) instead of reading it into the conversation. Give them the link to open in a browser; a client with a shell can fetch it with: curl -X POST -OJ <url>. The link works until one download completes and expires after expires_minutes (default 15, at most 60). The file is read under the host's file_ops policy, now and again when the link is opened. Returns the link, the file name and size, and when it expires.
sentinel_file_upload_attachment
ChatGPTSave one file the user attached in this conversation into a host, at target_path. For clients that pass attached files to tools (ChatGPT). In any other client, use sentinel_file_upload_link instead. target_path must be an absolute path to the file to create, inside a writable (rw) file_ops path of the host, and the agent's OS user must be able to write to that directory. An existing file is not replaced unless overwrite=true. Files of up to 512 MiB. SentinelX fetches the attachment from the client's file storage and streams it to the host; the bytes never go through the conversation. Returns the path, size and sha256. A large file may still be in transfer: then the result says in_progress and notifications_check shows the job when it is done.
sentinel_file_upload_link
ChatGPTCreate a temporary link where the user uploads one file into a host, at a path fixed now. target_path must be an absolute path to the file to create, inside a writable (rw) file_ops path of the host. It is checked now and cannot change: the file lands there or nowhere. The agent's OS user must also be able to write to that directory (Unix permissions), or the upload fails at the end. Files of up to 512 MiB. Give the user the link: they choose the file in a browser (any size up to the limit), or from a shell run: curl -T <file> <url>, which sends the file in one request and so only works up to about 100 MB; larger files go through the browser page. An existing file is not replaced unless overwrite=true. The link accepts one file and expires after expires_minutes (default 30, at most 120). Returns the link and a job_id: once the file has arrived, notifications_check lists that job with its size and sha256.
sentinel_get_default_host
ChatGPTReturn the user's currently-configured default host (if any). The response carries both the stored host_id AND a flag indicating whether that host is currently connected, so the caller knows whether tool calls without explicit host_id will succeed. Returns: {"ok": True, "default_host_id": "<id> or null", "is_connected": true|false|null} is_connected is null when no default is set.
sentinel_git_apply_patch
ChatGPTApply one unified diff to a repository, all or nothing. The repository must be under a file_ops entry with access: rw. The agent checks the patch first; if it does not apply cleanly, nothing changes.
sentinel_git_clone
ChatGPTClone a Git repository into a new directory on the host. Refuses a destination that exists and is not empty, so it never writes into another tree. The destination must be under a file_ops entry with access: rw.
sentinel_git_diff
ChatGPTShow a repository's current changes in one bounded, structured view. Read-only. Per-file status, insertion/deletion counts and the unified patch (within server caps), plus a summary. When a cap applies, truncated.files or truncated.patch is set.
sentinel_git_fetch
ChatGPTFetch from a Git remote: updates remote-tracking refs, never the working tree.
sentinel_git_ls_remote
ChatGPTRead the refs a Git remote has right now, without a working tree. Read-only. Use it to learn the commit a branch is on before a forced push.
sentinel_git_push
ChatGPTPush a branch to a Git remote. A forced push must pass expected_remote_sha, the commit you believe the remote branch is on: if someone pushed since you looked, the push is refused and nothing is lost. An unconditional force is not available on purpose.
sentinel_help
ChatGPTGet the help section exposed by SentinelX capabilities. Defaults to the bounded topic="index" view. Selectors can drill into topic (e.g. "playbooks" or a help section), path (one exact leaf), or playbook (one playbook), with offset/limit paging where supported. Pass topic="all" for the legacy full response. Requires agent >= 0.10.0; older agents ignore the selectors and return the full response.
sentinel_list
ChatGPTList entries in a directory on the target host. Output is structured (each entry has name/type/size/mtime), unlike sentinel_exec("ls -la") which returns opaque text. Always skips common noise directories (.git, __pycache__, node_modules, .venv, .pytest_cache, dist, build, etc.) regardless of show_hidden.
sentinel_list_hosts
ChatGPTList the authenticated user's hosts: connected, offline and disabled. Each host entry includes: - host_id: stable unique id, generated at install time - hostname: system hostname reported by the agent at handshake - label: user-defined alias (null if none set; see sentinel_set_host_label) - session_id: internal session identifier (null for a host currently served by another hub worker) - connected_at: ISO timestamp of when the agent connected - agent_version: version reported by the agent - capabilities: ops the agent supports - operational: whether this host may currently RUN operations. A host that is connected but beyond the plan's active-host limit is listed with operational=false plus a "status" and "note" explaining why; tool calls routed to it return upgrade_required until a slot frees (disconnect/disable another host) or the plan is upgraded. The top-level "slots" block summarises the plan: limit, how many hosts hold an active slot, how many are parked, and how many slots are available. Other tools' host_id parameter accepts any of host_id, label, or hostname. Resolution priority is exact host_id -> label -> hostname. Enrolled hosts that are NOT connected are not part of hosts/count; they are listed apart, so a missing host is never mistaken for a deleted one: - offline: enrolled, currently disconnected, with last_connected_at. It moves back into hosts on its own when its agent reconnects. - disabled: refused at connect, with disabled_at and disabled_by ("user": disabled from the dashboard, re-enable it there; "admin": disabled by SentinelX support). There is no tool to re-enable a host. Operations cannot target offline or disabled hosts. Each group holds at most 20 entries, most recent first; offline_total / disabled_total appear when there are more. Returns {"ok": true, "hosts": [...], "count": N, "slots": {...}, "offline": [...], "disabled": [...]}; offline and disabled only when non-empty.
sentinel_local_api
ChatGPTTalk to a tool on the host that already speaks a structured protocol. Use this instead of sentinel_exec when the target exposes a socket or JSON-RPC endpoint, because the answer comes back as data rather than as text meant for a terminal. Docker is the common case: State is the machine value "running" rather than something to infer from "Up 7 hours (healthy)", and ports are a structured list rather than a comma-laden string. Only endpoints and actions the host has declared are reachable. The host's config is the allowlist and neither the model nor the hub can widen it.
sentinel_mint_enrollment
ChatGPTConnect a new host to this account: returns the install command with a one-time token. Use it whenever the user wants to add a server, including their first one when the account has no hosts yet, and for unattended provisioning. The returned install_hint has one command per OS (linux, macos, windows): give the user the one for their server's OS to run on that machine; the host appears once the agent connects. It creates nothing by itself and never touches an existing host. Do not use it to re-enroll an existing host that lost its credential. The token works once and expires; minting is rate-limited.
sentinel_move
ChatGPTMove or rename a file or directory on the target host. BOTH src and dst must resolve under a file_ops entry whose access is rw in the agent's config. Resolving happens after canonicalizing symlinks, so .. traversal and symlink-escape are rejected. You cannot move data out of a writable subtree into a location the operator didn't declare.
sentinel_ping
ChatGPTSimple health check (does not call SentinelX core internals).
sentinel_project_snapshot
ChatGPTOne-call deterministic project/repository orientation. Aggregates git + filesystem facts into a single bounded response so you don't need several read/list/search/exec(git ...) round-trips to orient in a repository: git state (branch, head, detached, dirty, ahead/behind), change stats (staged/unstaged/untracked, insertions/deletions), tracked-file count, top-level directories, and file-extension counts. Read-only. For a non-git but readable directory it returns a plain filesystem summary (kind="directory") instead of failing. The extension counts let you infer the project type yourself (e.g. many .kt + build.gradle -> Android/Kotlin) without a curated manifest list.
sentinel_read
ChatGPTRead the contents of a file on the target host. The path must fall under one of the agent's configured file_ops.allowed_read_paths — directories like /etc/nginx, /var/log, /opt, etc. that the host's operator has chosen to expose. Paths outside that list, or path-traversal attempts via .., are rejected with path_not_allowed.
sentinel_read_media
ChatGPTRead a media or binary file from a host as native MCP content. Use this instead of sentinel_read when the file is not text: images, PDFs and audio come back as content the model can actually look at or listen to, rather than as bytes or an unreadable transcription.
sentinel_remove_host_label
ChatGPTRemove the label attached to a host. The host_id is unaffected; only the alias is forgotten. Returns {"ok": true, "removed": bool}.
sentinel_report
ChatGPTSend SentinelX a bug report, a malfunction, a feature request or a suggestion. For problems that may be SentinelX's own; ordinary host or application failures are not, unless evidence points to SentinelX (use "malfunction" when ownership is unclear). Run sentinel_diagnose first. The host, agent version and timestamps are recorded from the session: do not send them. Keep observed facts separate from hypotheses, and never include secrets. The whole report must stay under 48 KB. To add to a report already filed, use sentinel_report_followup instead of filing it again.
sentinel_report_followup
ChatGPTAdd to a report already filed: answer a question, say whether a fix worked, add evidence. It reopens a closed report and puts it back in front of the SentinelX team. Do not file the same report again instead: a matching new report counts as another occurrence and its text is not kept.
sentinel_report_status
ChatGPTRead a report's current state and the full exchange about it, oldest first.
sentinel_request_api_key
ChatGPTMint a short-lived REST API key and return it with a ready-to-use contract. Use this when a task is better done as a batch/aggregation loop than as many separate MCP calls -- e.g. grep a huge log and return only a count, check a metric across N hosts and report only the outliers, or a fetch->filter-> summarize pipeline. Mint a key, then curl `POST {base_url}/op` from your sandbox; only the final summary needs to re-enter your context. The key carries YOUR access, scoped down and short-lived. It is never more than you already have over MCP: every op still passes the target agent's allowlist, and every call is audited exactly like an MCP call. Treat it as a secret -- anyone holding it has that access until it expires.
sentinel_restart
ChatGPTRestart an allowed service through SentinelX.
sentinel_script_run
ChatGPTRun a one-off bash or python3 script on the connected host. The script content is written to a temporary workdir, executed, and (by default) cleaned up afterward. Set cleanup=False to keep the workdir for debugging — its path is returned in the response.
sentinel_search
ChatGPTRecursive content search across a directory subtree. Searches file contents under the file_ops allowlist and returns structured matches. Always skips binary files (heuristic: first 8KB contains NUL) and noise directories.
sentinel_service_reload
ChatGPTReload a service's configuration without a full restart, where the service supports it.
sentinel_service_start
ChatGPTStart a service on a host.
sentinel_service_status
ChatGPTRead a service's status: its full status, whether it is running, and whether it starts at boot. Read-only. Each part is returned as the agent answered it; a part this host's allowlist does not permit comes back as its own error, and the rest are still returned.
sentinel_service_stop
ChatGPTStop a service on a host. Whatever it serves is down until it is started again.
sentinel_set_default_host
ChatGPTDesignate host_id as the default target when host_id is omitted. After calling this, any tool that takes a host_id parameter will, if not given one explicitly, route to this host. Useful when you have multiple hosts connected and almost always work with one of them. The host_id must be the EXACT identifier (not a label or hostname). Use sentinel_list_hosts to discover available host_ids. We require the exact id deliberately — labels can be changed or removed, and hostnames can collide between hosts. The default should pin to a specific, stable identity. A default that points to a host which later disconnects is NOT an error at set-time; resolve_session will just fall back to ambiguous_host with a clear "your default is offline" message when the next tool call happens. To switch defaults, just call this again with the new host_id. To remove the default entirely, use sentinel_clear_default_host. Returns: On success: {"ok": True, "host_id": "<id>", "is_connected": bool}. The is_connected flag tells you whether your new default is actually online right now — useful feedback if you pinned a host that's currently offline.
sentinel_set_host_label
ChatGPTAttach a human-friendly label (alias) to one of the user's hosts. The label can then be passed in any tool's host_id parameter as a convenient alternative to the long host_id. Labels are also surfaced in sentinel_list_hosts output.
sentinel_state
ChatGPTGet SentinelX internal state.
sentinel_transfer_file
ChatGPTMove a file directly between two hosts you've enrolled, without routing the bytes through this chat. Files up to 512 MiB are supported. The file streams source -> SentinelX hub -> destination one chunk at a time (the hub never holds the whole file, and it never goes peer-to-peer, so neither host needs an inbound port). The destination is verified by sha256 against the source before the transfer is reported complete.
sentinel_upload_chunk
ChatGPTSend one chunk of a chunked upload. index is 0-based; chunks can be sent out of order — the agent reassembles by index when complete is called.
sentinel_upload_complete
ChatGPTReassemble all chunks into the target file. If sha256 is provided, the agent verifies the assembled file matches it. Cleans up temp data on success.
sentinel_upload_file
ChatGPTUpload a file to the connected host's upload base directory. Provide exactly one of: - content_base64: file content inlined as base64 - file_url: an http(s) URL the agent will fetch target_path is relative to the configured upload base on the host (path traversal is rejected). Set overwrite=True to replace existing files.
sentinel_upload_init
ChatGPTBegin a chunked upload. Returns an upload_id to use in upload_chunk and upload_complete. total_size is optional; if provided, the agent verifies the assembled file matches.
telegram_send_message
ChatGPTSend a text message to a Telegram chat using a saved bot. The target chat is taken from the integration's stored configuration (metadata.default_chat_id), set by the authenticated user via the dashboard. This tool deliberately does NOT accept a chat_id parameter — the LLM cannot redirect messages to chats it picks. If you need to reach different chats with the same bot token, create one integration per target chat in the dashboard. Use this for operational notifications: "deploy finished", "backup failed", "container unhealthy", "user requested this summary". It is NOT a chat interface — the bot does not receive replies, and there is no inbound message handling.