A capability is a declared privilege that a plugin requests in plugin.json.
Chattree enforces capabilities at the Host API layer — if a plugin tries to use an
API without the required capability, the call is rejected with a permissionDenied response.
| Level | Icon | Meaning |
|---|---|---|
normal | 🟢 | Low-risk. Granted automatically or with minimal friction. |
sensitive | 🟡 | Accesses data or outbound communication. Requires user consent. |
critical | 🔴 | Hardware sensors or highly personal data. Requires explicit user authorization. |
Category: Data
Status: ✅ Available
Permission required: None (granted automatically — core sandbox capability)
Host API namespace: storage
Grants access to the plugin's isolated key-value store. Each plugin has its own private keyspace. No other plugin or user can read it.
Operations:
| Operation | Input | Output |
|---|---|---|
storage.set | { key: string, value: string } | { stored: true, key, value } |
storage.get | { key: string } | { value: string|null, exists: boolean } |
storage.delete | { key: string } | { deleted: boolean } |
storage.has | { key: string } | { exists: boolean } |
storage.keys | {} | { keys: string[], count: number } |
Limitations:
262,144 bytes).2,097,152 bytes).Example:
{ "api": "storage", "operation": "set", "payload": { "key": "counter", "value": "42" } }
Category: Network
Status: ✅ Available
Permission required: User consent
Host API namespace: network
Allows the plugin to make outbound HTTP/HTTPS requests.
Operations:
| Operation | Input | Output |
|---|---|---|
network.http_request | { url, method?, headers?, body?, bodyEncoding?, responseType?, timeoutMs? } | { statusCode, headers, body, encoding, isBase64 } |
Input fields:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | ✅ | Full HTTPS/HTTP URL |
method | string | ❌ | GET (default), POST, PUT, DELETE, PATCH, HEAD, OPTIONS |
headers | object | ❌ | Custom headers (hop-by-hop headers are stripped) |
body | string | ❌ | Request body (max 4 MB) |
bodyEncoding | string | ❌ | "utf8" (default) or "base64" for sending binary data |
responseType | string | ❌ | "utf8" or "base64". If omitted, binary MIME types (audio, image, video, pdf, zip) are auto-encoded to "base64" |
timeoutMs | integer | ❌ | Timeout in ms (default 30000, 1000 to 60000) |
Limitations:
http and https schemes allowed.network.local capability.timeoutMs).Possible errors:
Permiso denegado — capability not grantedProtocolo no permitido — non-HTTP schemeAcceso bloqueado — private/local IP blockedMétodo HTTP no permitido — unsupported methodLa respuesta HTTP excedió el tamaño máximo — response > 8 MBTiempo de espera agotado — timeoutCategory: Network
Status: ✅ Available
Permission required: Critical user consent
Host API namespace: network (used together with network.http)
Allows outbound HTTP/HTTPS requests through network.http_request to private LAN IPv4 addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), IPv6 Unique Local Addresses (fc00::/7), loopback (localhost / 127.0.0.1 / ::1), and local mDNS/LAN domains (.local, .lan, .home). Ideal for integrating local services such as Ollama, LM Studio, Home Assistant, or NAS devices.
Security boundaries: Even when network.local is granted, cloud metadata endpoints (169.254.0.0/16, fe80::/10), multicast addresses, and 0.0.0.0 remain strictly blocked.
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: notifications
Allows sending and cancelling local device notifications.
Operations:
| Operation | Input | Output |
|---|---|---|
notifications.send | { title, body? } | { notificationId, sent, remainingQuota } |
notifications.cancel | { notificationId } | { notificationId, cancelled } |
Limitations:
Category: Device
Status: ✅ Available
Permission required: Explicit user authorization + Android Notification Listener Access + Package Whitelist (allowedNotificationPackages)
Host API namespace: notifications, events
Allows plugins to receive real-time streaming events when notifications are posted or updated by other applications on the device (e.g., messaging apps, email, calendar).
[!IMPORTANT] Package Scope Isolation: Access is strictly bounded by
allowedNotificationPackagesin the plugin manifest. A plugin configured with["com.whatsapp"]will only receive notifications originating from WhatsApp. Omission of this field results in a strict deny-all.
Events Emitted on HostEventBus:
| Event Type | Description | Payload Schema |
|---|---|---|
device.notification.received | Fired when a notification arrives or updates | { id, logicalId, packageName, title, text, category, postTime, isClearable, hasReplyAction, isUpdate, actions } |
device.notification.removed | Fired when a notification is dismissed | { id, logicalId, packageName, postTime } |
Event Payload Fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique notification identifier from Android (StatusBarNotification.id + tag) |
logicalId | string | Deterministic key for deduplication: ${packageName}_${id}_${tag} |
packageName | string | Originating app package ID (e.g. com.whatsapp, org.telegram.messenger) |
title | string | Notification title or sender name |
text | string | Message body or summary text |
category | string? | Android category (msg, email, social, alarm, promo, etc.) |
postTime | integer | Timestamp when notification was posted (ms since Unix epoch) |
isClearable | boolean | True if the notification can be swiped away or dismissed |
hasReplyAction | boolean | True if an inline RemoteInput direct reply is available |
isUpdate | boolean | True if this event updates an already active notification session |
actions | array | Available quick actions: [{ title: string, hasRemoteInput: boolean, isQuickReply: boolean, actionHandle: string }] |
Security & Privacy Safeguards:
\b\d{4,8}\b with security keywords like code, OTP, 2FA, verificación), or marked with security flags is immediately dropped in the Android native service. It never enters Dart memory, EventBus, or plugin sandboxes.EventJournal. Notifications are delivered as transient in-memory signals. Plugins requiring historical retention must manage their own storage using their isolated SQLite database (database.sql).Category: Device
Status: ✅ Available
Permission required: User consent + Package Whitelist (allowedNotificationPackages)
Host API namespace: notifications
Allows plugins to inspect the status of the native notification listener service, retrieve currently active status bar notifications, and query the ephemeral in-memory recent buffer (up to 100 entries) filtered strictly by the plugin's package authorization.
Operations:
| Operation | Input | Output |
|---|---|---|
notifications.getStatus | {} | { status: "active" | "disconnected" | "permission_required" | "reconnecting" | "unsupported", isListenerEnabled: boolean, isSupported: boolean } |
notifications.getActive | { packageName?, category?, canReplyOnly?, excludeOngoing?, excludeFromMe?, includeRecent?, limit? } | DeviceNotificationSummary[] |
notifications.query | { packageName?, category?, canReplyOnly?, excludeOngoing?, excludeFromMe?, includeRecent?, limit? } | DeviceNotificationSummary[] |
notifications.getRecent | { limit?, packageName?, category?, canReplyOnly?, excludeOngoing?, excludeFromMe? } | DeviceNotificationSummary[] |
Example Response (notifications.getStatus):
{
"status": "active",
"isListenerEnabled": true,
"isSupported": true
}
Example Response (notifications.getActive / notifications.getRecent):
[
{
"logicalId": "com.whatsapp_1001_chat_42",
"packageName": "com.whatsapp",
"appName": "WhatsApp",
"channelId": "individual_chat_notifications",
"title": "Alice Smith",
"body": "Are we still meeting at 3 PM?",
"category": "msg",
"postTime": 1711234567890,
"isOngoing": false,
"isClearable": true,
"isFromMe": false,
"isGroupSummary": false,
"canReply": true,
"actions": [
{ "title": "Reply", "isReply": true, "semanticAction": "reply", "index": 0, "actionHandle": "act_8f7b2c1409d1e56b..." }
]
}
]
Category: Device
Status: ✅ Available
Permission required: Explicit user authorization + Package Whitelist (allowedNotificationPackages)
Host API namespace: notifications
Allows plugins to execute inline direct replies to active notifications using Android's native RemoteInput API (e.g. auto-replies or AI-drafted responses to WhatsApp, Telegram, or SMS).
Operations:
| Operation | Input | Output |
|---|---|---|
notifications.reply | { logicalId: string, message: string } | { replied: boolean, logicalId: string } |
Input fields:
| Field | Type | Required | Description |
|---|---|---|---|
logicalId | string | ✅ | Identifier of the active notification (as received in DeviceNotificationSummary.logicalId) |
message | string | ✅ | Text content to send (max 4,000 characters) |
Safety & Lifetime Boundaries:
RemoteInput and PendingIntent objects reside exclusively in an Android in-memory session cache. They are never saved to disk and are invalidated immediately when the notification is dismissed.logicalId) in its allowedNotificationPackages.TransactionTooLargeException.NOTIFICATION_EXPIRED or ACTION_UNAVAILABLE if the notification has been dismissed or lacks a reply action.Category: Device
Status: ✅ Available
Permission required: Explicit user authorization + Package Whitelist (allowedNotificationPackages)
Host API namespace: notifications
Allows plugins to programmatically interact with active notifications: dismiss them from the status bar or trigger interactive quick actions (e.g. "Mark as Read", "Archive", "Call Back") using either actionHandle or logicalId + actionIndex, with semanticAction metadata (reply, mark_as_read, archive, delete, etc.) on Android 9+.
Operations:
| Operation | Input | Output |
|---|---|---|
notifications.dismiss | { logicalId: string } | { dismissed: boolean, logicalId: string } |
notifications.invokeAction | { actionHandle: string, logicalId?: string, packageName?: string, actionIndex?: integer } | { invoked: boolean, actionHandle: string } |
Input fields (notifications.dismiss):
| Field | Type | Required | Description |
|---|---|---|---|
logicalId | string | ✅ | Target notification identifier |
Input fields (notifications.invokeAction):
| Field | Type | Required | Description |
|---|---|---|---|
actionHandle | string | ✅ | Cryptographic single-use token (act_<uuid4>) identifying the action |
logicalId | string | ❌ | Expected notification logicalId (used for validation and fallback index resolution) |
packageName | string | ❌ | Expected Android package name (validated against token binding) |
actionIndex | integer | ❌ | Expected action index within the notification |
Security & Lifetime Boundaries for Actions:
actionHandle is an unguessable 128-bit UUID v4 token generated when the notification is parsed.isUsed and immediately purged to prevent replay attacks.(pluginId, notificationKey, packageName). A plugin cannot invoke an action from another plugin or another app.PendingIntent.send(ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(...)). On Android 10–13, background activity launches are restricted by the OS if no foreground service is active.Category: Data
Status: ✅ Available
Permission required: User consent
Host API namespace: files
Opens the native file picker so the user selects a file. Returns an opaque handle — the plugin never receives a real filesystem path.
Operations:
| Operation | Input | Output |
|---|---|---|
files.pick | { allowedExtensions?: string[], title?: string } | { cancelled, handle, name, size, extension, mimeType } |
Example output (success):
{
"cancelled": false,
"handle": "f_a1b2c3d4e5f6...",
"name": "document.pdf",
"size": 45678,
"extension": "pdf",
"mimeType": "application/pdf"
}
Category: Data
Status: ✅ Available
Permission required: User consent
Host API namespace: files
Reads the content of a file previously selected with files.pick.
Operations:
| Operation | Input | Output |
|---|---|---|
files.read | { handle, asText?: boolean } | { handle, name, size, encoding, content } |
Encoding: "utf8" for text files, "base64" for binary files.
Limitations: Max 8 MB per file. Handle must belong to the calling plugin.
Possible errors:
Handle invalido o expirado — call files.pick firstArchivo supera el limite de lectura — file > 8 MBEl archivo ya no existe — file deleted after pickingCategory: Data
Status: ✅ Available
Permission required: User consent
Host API namespace: files
Writes a file to the plugin's private sandbox directory inside Chattree.
The plugin does not control the final path — Chattree determines it.
Operations:
| Operation | Input | Output |
|---|---|---|
files.write | { name, content, encoding? } | { name, size, written, handle } |
Encoding: "utf8" (default) or "base64".
Limitations: Max 8 MB per file. Path traversal (../) blocked.
Category: Device
Status: ✅ Available
Permission required: Explicit user authorization + OS permission
Host API namespace: location
Returns the current device GPS position (one-shot, no streaming).
Operations:
| Operation | Input | Output |
|---|---|---|
location.current | { accuracy?: "high"|"medium"|"low"|"lowest" } | { latitude, longitude, accuracy, timestamp, provider } |
location.permission_status | {} | { serviceEnabled, permission, isGranted, isDeniedForever } |
Limitations:
ACCESS_FINE_LOCATION in Android manifest.Category: Device
Status: ✅ Available
Permission required: Explicit user authorization
Host API namespace: camera
Opens the system camera interface to capture a photo.
Returns an opaque file handle. No direct hardware access.
Operations:
| Operation | Input | Output |
|---|---|---|
camera.capture | { preferredCamera?: "rear"|"front", quality?: 10–100 } | { cancelled, handle, name, size, mimeType } |
Limitations:
files.read with returned handle.Category: Device
Status: ✅ Available
Permission required: Explicit user authorization
Host API namespace: contacts
Searches or retrieves contacts from the device address book.
Operations:
| Operation | Input | Output |
|---|---|---|
contacts.get | { query?: string, limit?: number } | { contacts: [{ id, displayName, phones, emails }] } |
contacts.search | { query: string, limit?: number } | same as above |
Limitations: Max 50 contacts per call. Strips internal metadata.
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: contacts
Creates a new contact in the device address book.
Operations:
| Operation | Input | Output |
|---|---|---|
contacts.create | { displayName, phones?, emails? } | { id, displayName, created } |
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: contacts
Updates an existing contact.
Operations:
| Operation | Input | Output |
|---|---|---|
contacts.update | { id, displayName?, phones?, emails? } | { id, updated } |
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: calendar
Reads events from the device calendar.
Operations:
| Operation | Input | Output |
|---|---|---|
calendar.events | { calendarId, startDate, endDate } | { events: [...] } |
calendar.calendars | {} | { calendars: [{ id, name, isDefault }] } |
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: calendar
Creates, updates or deletes calendar events.
| Operation | Input |
|---|---|
calendar.create | { calendarId, title, startDate, endDate, description? } |
calendar.update | { calendarId, eventId, title?, startDate?, endDate?, description? } |
calendar.delete | { calendarId, eventId } |
Category: Device
Status: ✅ Available
Permission required: None (low-risk)
Host API namespace: share
Opens the native system share sheet to share text or a URL.
Operations:
| Operation | Input | Output |
|---|---|---|
share.text | { text, subject? } | { shared: true } |
Limitations: Max 32,768 characters (32 KB).
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: share
Shares a file (via its handle) through the native share sheet.
Operations:
| Operation | Input | Output |
|---|---|---|
share.file | { handle, text? } | { shared: true, type: "file", handle, fileName } |
Limitations: Handle must belong to the calling plugin.
Category: Device
Status: ✅ Available
Permission required: None (low-risk)
Host API namespace: open
Opens a URL in the device's default browser.
Operations:
| Operation | Input | Output |
|---|---|---|
open.url | { url } | { opened: boolean, url } |
Limitations: Only https and http schemes. Blocks file://, javascript:, intent:, etc.
Category: Device
Status: ✅ Available
Permission required: User consent
Host API namespace: open
Opens an installed app using a URI scheme (e.g. maps://, tel:, mailto:).
Operations:
| Operation | Input | Output |
|---|---|---|
open.app | { uri } | { opened: boolean, target } |
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.notes
Retrieves a specific note from Chattree Notes by ID or title.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.notes.get | { id: string } | { note: { id, title, content, createdAt, updatedAt } } |
Limitations: Max title: 300 chars. Content may be up to 50 000 chars.
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.notes
Searches Chattree Notes by keyword.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.notes.search | { query: string, limit?: number } | { notes: [...], count } |
Limitations: Max 100 results per call. Query truncated at 300 chars.
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.notes
Creates a new note in Chattree Notes (also accepts legacy capability aliases notes.write / writeNotes).
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.notes.create | { title: string, content?: string } | { note: { id, title, content, createdAt, updatedAt } } |
Limitations: Title required, max 300 chars. Content max 50 000 chars.
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.notes
Updates an existing Chattree note.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.notes.update | { id, title?, content? } | { note: { id, title, content, createdAt, updatedAt } } |
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.notes
Permanently deletes a Chattree note.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.notes.delete | { id } | { deleted: true, id } |
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.reminders
Lists and retrieves reminders from Chattree.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.reminders.list | { limit?: number } | { reminders: [...], count } |
chattree.reminders.get | { id } | { reminder: { id, title, scheduledAt, completed, ... } } |
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.reminders
Creates a new reminder with an optional alarm (also accepts legacy capability aliases reminders.write / writeReminders).
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.reminders.create | { title, scheduledAt?: ISO8601 } | { reminder: { id, title, scheduledAt, ... } } |
Limitations: Title max 300 chars.
Category: Data
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.reminders
Updates, deletes or marks reminders as complete.
| Operation | Input |
|---|---|
chattree.reminders.update | { id, title?, scheduledAt? } |
chattree.reminders.delete | { id } |
chattree.reminders.complete | { id } |
Category: AI
Status: ✅ Available (Phase 3G)
Permission required: User consent
Host API namespace: chattree.context
Reads the current message explicitly provided to the plugin's execution scope.
This is strictly limited — it does NOT expose full chat history or all previous messages.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.context.read | {} | { currentMessage, explicitContext, authorized, restricted, timestamp } |
Category: Communication & Personal Data
Status: ✅ Available
Permission required: User consent
Host API namespace: email
Allows the plugin to read email accounts, inspect inbox messages, and fetch message bodies via secure IMAP. App passwords configured via email.config / email.setup are encrypted at rest in FlutterSecureStorage (EncryptedSharedPreferences on Android / Keychain on iOS) and never stored in plaintext in PluginStorageStore.
Operations:
| Operation | Input | Output |
|---|---|---|
email.inbox / email.list / email.events | {} | { status, account, unread_count, total_count, emails: [...] } |
email.read | { id: string } | { id, from, to, subject, date, body } |
email.config / email.setup | { email, password, imapHost?, imapPort? } | { configured: true, email, provider, message } |
email.disconnect / email.logout | {} | { disconnected: true, message } |
Category: Communication
Status: ✅ Available
Permission required: User consent
Host API namespace: email
Allows the plugin to dispatch outgoing emails via secure SMTP. Both email.read and email.send are strictly verified by PermissionGateway before any operation executes.
Operations:
| Operation | Input | Output |
|---|---|---|
email.send / email.create | { to: string, subject: string, body: string } | { sent: true, to, subject, response } |
Category: Network & Automation
Status: ✅ Available
Permission required: User consent
Host API namespace: Inbound Webhook Pipeline (InboundHttpServer / EventRuntime)
Allows the plugin to register HTTP webhook endpoints (127.0.0.1 by default, or LAN 0.0.0.0 when explicitly enabled by the user in Settings) and receive authenticated inbound events (via Bearer token or HMAC-SHA256 signature). Users can inspect the endpoint URL, toggle LAN/Wi-Fi access, copy the active secret, and rotate endpoint secrets at any time from Settings → Plugins → [Plugin] → Webhooks Entrantes.
Category: Network & Connectivity
Status: ✅ Available
Permission required: User consent
Host API namespace: network (WebhookEgressSender)
Allows the plugin to dispatch automated outbound webhook events to external HTTPS services (such as Slack, Notion, Discord, or CI/CD pipelines) when internal Chattree events occur. Outbound webhooks implement SSRF validation, DNS pinning, exponential backoff retries (base 10s + jitter 0..2s), maximum 16 concurrent global dispatches (max 5 per extension), and response body drainage capped at 8 KB to protect mobile resources.
Category: Device
Status: ✅ Available
Permission required: User consent (clipboard.read) / Normal (clipboard.write)
Host API namespace: clipboard
Allows reading text from (clipboard.read, max 100 KB) or copying text to (clipboard.write, max 100 KB) the system clipboard.
| Operation | Input | Output |
|---|---|---|
clipboard.read / clipboard.get | {} | { text: string, hasContent: boolean, length: number } |
clipboard.write / clipboard.set | { text: string } | { copied: true, length: number } |
clipboard.has | {} | { hasContent: boolean } |
Category: AI & Conversation
Status: ✅ Available
Permission required: User consent
Host API namespace: chattree.context (chat / chat.history)
Allows reading a bounded window of recent messages from the active conversation so plugins can summarize threads, extract tasks, or maintain multi-turn context.
Operations:
| Operation | Input | Output |
|---|---|---|
chattree.context.history / chattree.context.messages | { limit?: number } | { messages: [{ role, content, timestamp }], count: number, maxLimit: 20, restricted: boolean, timestamp: string } |
Limitations:
limit clamped between 1 and 20, default 10).content is truncated at 4 000 characters.Category: AI
Status: ✅ Available
Permission required: User consent
Host API namespace: ai (ai.context)
Allows the plugin to request a bounded, single-turn text completion from the active AI model (either a configured remote provider or the loaded on-device GGUF model) without recursive tool execution.
Operations:
| Operation | Input | Output |
|---|---|---|
ai.complete / ai.generate / ai.prompt / ai.context | { prompt: string, systemPrompt?: string, maxTokens?: number, temperature?: number } | { text: string, engine: "remote" | "local", providerId?: string, modelId: string } |
Limitations:
prompt max 16 000 characters; systemPrompt max 4 000 characters.maxTokens clamped between 1 and 2 048 (default 512).Category: AI & Vector RAG
Status: ✅ Available
Permission required: User consent
Host API namespace: ai (ai.embeddings)
Allows the plugin to generate on-device vector embeddings, perform semantic search over the local vector store, and index or remove custom plugin-scoped text documents.
Operations:
| Operation | Input | Output |
|---|---|---|
ai.embed / ai.embeddings | { text: string } | { embedding: number[], dimensions: number, modelId: string } |
ai.search / ai.rag_search | { query: string, topK?: number, minSimilarity?: number } | { results: [{ noteId, noteTitle, chunkText, similarity, chunkIndex }], count: number } |
ai.index_document / ai.index | { id: string, title: string, content: string } | { indexed: true, documentId: string, originalId: string, chunksCount: number } |
ai.remove_document / ai.remove | { id: string } | { removed: true, documentId: string } |
Limitations:
RagService) to be downloaded and initialized.topK clamped between 1 and 20 (default 5).plugin_<pluginId>_<id> so a plugin can only overwrite or remove its own indexed documents.Category: Network
Status: ✅ Available (Phase 1)
Permission required: User consent (network.websocket)
Host API namespace: websocket
Allows plugins to establish and manage bidirectional, full-duplex WebSocket connections for streaming data, live push notifications, real-time messaging, and IoT/telemetry channels.
Operations:
| Operation | Input | Output |
|---|---|---|
websocket.connect | { url: string, headers?: object, protocols?: string[], pingIntervalSeconds?: number } | { connectionId: string, status: "connected" | "connecting", url: string } |
websocket.send | { connectionId: string, message: string, isBinary?: boolean } | { sent: true, bytes: number } |
websocket.receive / websocket.poll | { connectionId: string, maxMessages?: number } | { messages: [{ data: string, isBinary: boolean, timestamp: string }], count: number } |
websocket.status | { connectionId: string } | { connectionId: string, state: "connected" | "connecting" | "closing" | "closed", bufferedAmount: number, queuedMessages: number } |
websocket.close | { connectionId: string, code?: number, reason?: string } | { closed: true, connectionId: string } |
Input fields for websocket.connect:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | ✅ | ws:// or wss:// URI |
headers | object | ❌ | Custom handshake headers (restricted hop-by-hop headers stripped) |
protocols | string[] | ❌ | WebSocket subprotocols requested |
pingIntervalSeconds | number | ❌ | Periodic ping heartbeat interval (default 30s) |
Limitations & Security Guarantees:
16,777,216 bytes) per incoming or outgoing frame. If an inbound frame exceeds 16 MB, the socket is immediately terminated with WebSocket close code 1009 (Message Too Large / RFC 6455) to protect device memory.127.0.0.1, localhost) or private RFC 1918 subnets (192.168.x.x, 10.x.x.x, 172.16.x.x) are rejected unless the plugin also holds the critical network.local capability. Cloud metadata endpoints (169.254.169.254) are permanently blocked.Example:
{
"api": "websocket",
"operation": "connect",
"payload": {
"url": "wss://echo.websocket.events"
}
}
Category: Device / UI
Status: ✅ Available (Phase 2)
Permission required: User consent (ui.components or ui.render)
Host API namespace: ui
Allows plugins to emit rich, interactive declarative UI cards in the chat rather than plain text. Enables progress bars, badges, formatted metric key-value tables, and action buttons.
Operations:
| Operation | Input | Output |
|---|---|---|
ui.render / ui.show_card | { card: UiCard } | { rendered: true, elementCount: number } |
Direct WASM Tool Result: Plugins can also emit declarative UI directly by returning
{ "text": "Summary", "ui": { "type": "card", ... } }in the result of any tool call.
Card Schema (UiCard):
{
"title": "Build Status",
"subtitle": "Production Deployment Pipeline",
"elements": [
{ "type": "badge", "text": "SUCCESS", "variant": "success" },
{ "type": "progress", "value": 100, "label": "Deployment Complete" },
{
"type": "key_value",
"items": [
{ "label": "Commit", "value": "a1b2c3d" },
{ "label": "Environment", "value": "production" }
]
},
{ "type": "divider" },
{
"type": "button_row",
"buttons": [
{ "label": "View Logs", "action": "url", "payload": "https://ci.example.com/build/123" },
{ "label": "Rollback", "action": "command", "payload": "/deploy rollback", "variant": "danger" }
]
}
]
}
Supported Component Elements:
| Component | Properties | Description |
|---|---|---|
text | content (string), style ("body" | "caption" | "headline" | "code"), markdown (boolean) | Text block with optional markdown formatting |
badge | text (string), variant ("info" | "success" | "warning" | "error" | "neutral" | "white" | "mono" | "monochrome" | "primary"), icon (string?, e.g. "note", "alarm") | Status badge with color-coded or monochrome styling |
progress | value (number 0.0 to 1.0 or 0 to 100), label (string?), status (string?) | Progress bar with optional indicator text |
key_value | items: [{ label: string, value: string, copyable?: boolean }] | Structured label-value grid (up to 40 items) |
list | items: [{ title: string, subtitle?: string }] | Compact title-and-preview list (up to 40 items; title max 100 chars, subtitle max 160 chars) |
divider | None | Subtle horizontal separator line |
button_row | buttons: [{ label: string, action: "command" | "url" | "copy", payload: string, variant?: "primary" | "secondary" | "danger" | "white" | "mono" | "monochrome" }] | Interactive action row (up to 8 buttons per row) |
Card-level options: Cards also accept
"hideText": trueto suppress the plain-text intro bubble above the declarative card when the card itself carries the full visual response.
Limitations & Security Guarantees:
UiApiHandler.sanitizeUiCard regardless of source (Host API call or direct WASM return). Invalid nodes, oversized strings, and unknown types are safely stripped.action: "command" and the target payload begins with / (e.g., /deploy, /delete, /reboot) or executes commands, Chattree intercepts the click and displays an explicit user confirmation dialog showing the command before execution. This prevents clickjacking or unauthorized actions from third-party plugins.Category: Automation / Device
Status: ✅ Available (Phase 3)
Permission required: User consent (scheduler.task or scheduler.cron)
Host API namespace: scheduler
Allows plugins to schedule recurring tasks and background automations using standard Cron expressions or periodic intervals, with events delivered through Chattree's central HostEventBus.
Operations:
| Operation | Input | Output |
|---|---|---|
scheduler.schedule_task | { taskId: string, cron?: string, intervalSeconds?: number, payload?: any, description?: string } | { scheduled: true, taskId: string, nextExecution: string } |
scheduler.cancel_task | { taskId: string } | { cancelled: true, taskId: string } |
scheduler.list_tasks | {} | { tasks: [{ taskId, cron, intervalSeconds, nextExecution, payload, createdAt }], count: number } |
scheduler.get_task | { taskId: string } | { task: object | null } |
Cron Syntax & Precision:
calculateNextCronExecution).minute hour day-of-month month day-of-week
*/15 * * * * — Every 15 minutes0 * * * * — Every hour on the hour0 9 * * * — Daily at 9:00 AM0 0 * * 1 — Weekly on Mondays at midnight30 14 1 * * — 1st of every month at 2:30 PMLimitations & Security Guarantees:
minimumIntervalViolation.131,072 bytes) per task payload.scheduler.trigger event on HostEventBus containing { taskId, pluginId, payload, timestamp }.Category: Data
Status: ✅ Available (Phase 4)
Permission required: User consent (database.sql or storage.database)
Host API namespace: database
Provides a fully managed, private, and isolated relational SQLite database for each plugin. Perfect for plugins that need complex queries, indexes, relational filtering, and atomic multi-step transactions.
Operations:
| Operation | Input | Output |
|---|---|---|
database.query | { sql: string, params?: any[] } | { rows: object[], rowCount: number, columns: string[] } |
database.execute | { sql: string, params?: any[] } | { changes: number, lastInsertRowId?: number } |
database.insert | { table: string, values: object } | { insertedId: number } |
database.update | { table: string, values: object, where: string, whereArgs?: any[] } | { rowsAffected: number } |
database.delete | { table: string, where: string, whereArgs?: any[] } | { rowsAffected: number } |
database.transaction | { statements: [{ sql: string, params?: any[] }] } | { success: true, results: object[] } |
database.tables | {} | { tables: string[] } |
Limitations & Security Guarantees:
plugin_<pluginId>.db stored in a protected app sandbox directory. Zero cross-plugin access.209,715,200 bytes) maximum database size. The quota calculates the cumulative size of the .db primary file, .db-wal write-ahead log, .db-shm shared memory file, and .db-journal. If an execution would exceed 200 MB, the operation is blocked with quotaExceeded.1 or 0), and structured objects/arrays are serialized to valid JSON strings.database.transaction runs multiple SQL statements within a single atomic SQLite transaction with immediate automatic rollback on error.ATTACH DATABASE, DETACH DATABASE, VACUUM INTO, direct modifications to sqlite_master, and PRAGMA writable_schema..db, .db-wal, .db-shm, and .db-journal files, leaving zero residual data on disk.Example:
{
"api": "database",
"operation": "execute",
"payload": {
"sql": "CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT, content TEXT, created_at INTEGER)"
}
}