From 877d30cdd7b5fb30dfa01f5e8fa8b7a81c97732e Mon Sep 17 00:00:00 2001 From: James Rich Date: Fri, 22 May 2026 18:48:50 -0500 Subject: [PATCH] feat(ai): Add app_metadata.xml and fix KDoc for KSP compliance Audit findings addressed: - Add res/xml/app_metadata.xml with LLM-facing operational patterns, workflow dependencies, and constraints for the AppFunctions suite - Register app_metadata in Google flavor AndroidManifest.xml - Convert all @AppFunctionSerializable class-level @property tags to inline KDoc per property (required by KSP for doc extraction) - Add app_description string resource for displayDescription Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- androidApp/src/google/AndroidManifest.xml | 3 + .../app/ai/appfunctions/AppFunctionModels.kt | 213 ++++++++---------- .../src/google/res/xml/app_metadata.xml | 26 +++ androidApp/src/main/res/values/strings.xml | 1 + 4 files changed, 123 insertions(+), 120 deletions(-) create mode 100644 androidApp/src/google/res/xml/app_metadata.xml diff --git a/androidApp/src/google/AndroidManifest.xml b/androidApp/src/google/AndroidManifest.xml index a8ac5d49e..234f47eba 100644 --- a/androidApp/src/google/AndroidManifest.xml +++ b/androidApp/src/google/AndroidManifest.xml @@ -25,6 +25,9 @@ + diff --git a/androidApp/src/google/kotlin/org/meshtastic/app/ai/appfunctions/AppFunctionModels.kt b/androidApp/src/google/kotlin/org/meshtastic/app/ai/appfunctions/AppFunctionModels.kt index fc479dcf6..82bb324a4 100644 --- a/androidApp/src/google/kotlin/org/meshtastic/app/ai/appfunctions/AppFunctionModels.kt +++ b/androidApp/src/google/kotlin/org/meshtastic/app/ai/appfunctions/AppFunctionModels.kt @@ -18,218 +18,191 @@ package org.meshtastic.app.ai.appfunctions import androidx.appfunctions.AppFunctionSerializable -/** - * Response returned when a message is successfully sent via the mesh network. - * - * @property messageId The identifier assigned to the outgoing message. - * @property channel The channel or destination the message was sent to. - * @property timestamp The time the message was sent (epoch milliseconds). - */ +/** Response returned when a message is successfully sent via the mesh network. */ @AppFunctionSerializable(isDescribedByKDoc = true) -data class SendMessageResponse(val messageId: Int, val channel: String, val timestamp: Long) +data class SendMessageResponse( + /** The identifier assigned to the outgoing message. */ + val messageId: Int, + /** The channel or destination the message was sent to. */ + val channel: String, + /** The time the message was sent (epoch milliseconds). */ + val timestamp: Long, +) -/** - * Response containing the current status of the Meshtastic mesh network. - * - * @property connectionState The current radio connection state (e.g., CONNECTED, DISCONNECTED). - * @property onlineNodeCount The number of nodes currently online (heard within the last 15 minutes). - * @property totalNodeCount The total number of nodes known to the network. - * @property localBatteryLevel The battery percentage of the connected Meshtastic device (1-100), or null if - * unavailable. - * @property localNodeName The display name of the local node, or null if not set. - */ +/** Response containing the current status of the Meshtastic mesh network. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class MeshStatusResponse( + /** The current radio connection state (e.g., CONNECTED, DISCONNECTED). */ val connectionState: String, + /** The number of nodes currently online (heard within the last 2 hours). */ val onlineNodeCount: Int, + /** The total number of nodes known to the network. */ val totalNodeCount: Int, + /** The battery percentage of the connected Meshtastic device (1-100), or null if unavailable. */ val localBatteryLevel: Int?, + /** The display name of the local node, or null if not set. */ val localNodeName: String?, ) -/** - * Response containing information about a single mesh node. - * - * @property id The unique node identifier in Meshtastic hex format (e.g., !abc12345). - * @property name The human-readable name of the node. - * @property batteryLevel The node's battery percentage (0-100), or null if unavailable. - * @property lastHeard The time this node was last heard from (epoch milliseconds). - * @property isOnline Whether this node is currently considered online. - */ +/** Information about a single mesh node. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class NodeInfo( + /** The unique node identifier in Meshtastic hex format (e.g., !abc12345). */ val id: String, + /** The human-readable name of the node. */ val name: String, + /** The node's battery percentage (0-100), or null if unavailable. */ val batteryLevel: Int?, + /** The time this node was last heard from (epoch milliseconds). */ val lastHeard: Long, + /** Whether this node is currently considered online. */ val isOnline: Boolean, ) -/** - * Response containing a list of nodes visible on the mesh network. - * - * @property nodes List of nodes sorted by most recently heard first. - */ +/** Response containing a list of nodes visible on the mesh network. */ @AppFunctionSerializable(isDescribedByKDoc = true) -data class GetNodeListResponse(val nodes: List) +data class GetNodeListResponse( + /** List of nodes sorted by most recently heard first. */ + val nodes: List, +) -/** - * Response containing information about a single mesh channel. - * - * @property index The channel index (0-7). - * @property name The human-readable name of the channel. - * @property isPrimary Whether this is the primary/default channel. - * @property uplinkEnabled Whether uplink is enabled for this channel. - * @property downlinkEnabled Whether downlink is enabled for this channel. - */ +/** Information about a single mesh channel. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class ChannelInfo( + /** The channel index (0-7). */ val index: Int, + /** The human-readable name of the channel. */ val name: String, + /** Whether this is the primary/default channel. */ val isPrimary: Boolean, + /** Whether uplink is enabled for this channel. */ val uplinkEnabled: Boolean, + /** Whether downlink is enabled for this channel. */ val downlinkEnabled: Boolean, ) -/** - * Response containing the list of available mesh channels. - * - * @property channels List of all configured channels. - */ +/** Response containing the list of available mesh channels. */ @AppFunctionSerializable(isDescribedByKDoc = true) -data class GetChannelInfoResponse(val channels: List) +data class GetChannelInfoResponse( + /** List of all configured channels. */ + val channels: List, +) -/** - * Response containing the status of the local Meshtastic device. - * - * @property model The hardware model of the device (e.g., "Meshtastic nRF52840"). - * @property firmwareVersion The firmware version string. - * @property batteryLevel The device battery percentage (0-100), or null if not battery-powered. - * @property chargingStatus The charging state (CHARGING, NOT_CHARGING, or UNKNOWN). - * @property deviceName The display name of the device, or null if not set. - * @property isActive Whether the radio is currently active and connected. - */ +/** Response containing the status of the local Meshtastic device. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class GetDeviceStatusResponse( + /** The hardware model of the device (e.g., "Meshtastic nRF52840"). */ val model: String, + /** The firmware version string. */ val firmwareVersion: String, + /** The device battery percentage (0-100), or null if not battery-powered. */ val batteryLevel: Int?, + /** The charging state (CHARGING, NOT_CHARGING, or UNKNOWN). */ val chargingStatus: String, + /** The display name of the device, or null if not set. */ val deviceName: String?, + /** Whether the radio is currently active and connected. */ val isActive: Boolean, ) -/** - * Response containing detailed telemetry for a specific mesh node. - * - * @property id Node ID in hex format (e.g., "!abc12345"). - * @property userId User ID string for this node. - * @property name Display name of the node. - * @property batteryLevel Battery percentage (0-100), or null if unavailable. - * @property voltage Supply voltage in volts, or null if unavailable. - * @property hardwareModel Hardware model string. - * @property firmwareVersion Firmware version string. - * @property snr Signal-to-noise ratio of strongest signal. - * @property rssi Received signal strength indicator in dB. - * @property hopsAway Number of hops away from local node (-1 if unknown). - * @property channel Channel index this node is on. - * @property lastHeard Last heard timestamp (milliseconds since epoch). - * @property userRole User role or device type. - * @property isLicensed Whether the user is licensed. - * @property latitude Latitude in degrees, or null if unknown. - * @property longitude Longitude in degrees, or null if unknown. - */ +/** Response containing detailed telemetry for a specific mesh node. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class GetNodeDetailsResponse( + /** Node ID in hex format (e.g., "!abc12345"). */ val id: String, + /** User ID string for this node. */ val userId: String, + /** Display name of the node. */ val name: String, + /** Battery percentage (0-100), or null if unavailable. */ val batteryLevel: Int?, + /** Supply voltage in volts, or null if unavailable. */ val voltage: Float?, + /** Hardware model string. */ val hardwareModel: String, + /** Firmware version string. */ val firmwareVersion: String, + /** Signal-to-noise ratio of strongest signal. */ val snr: Float, + /** Received signal strength indicator in dB. */ val rssi: Int, + /** Number of hops away from local node (-1 if unknown). */ val hopsAway: Int, + /** Channel index this node is on. */ val channel: Int, + /** Last heard timestamp (milliseconds since epoch). */ val lastHeard: Long, + /** User role or device type. */ val userRole: String, + /** Whether the user is licensed. */ val isLicensed: Boolean, + /** Latitude in degrees, or null if unknown. */ val latitude: Double?, + /** Longitude in degrees, or null if unknown. */ val longitude: Double?, ) -/** - * Response containing aggregate mesh network metrics. - * - * @property totalNodeCount Total number of known nodes. - * @property onlineNodeCount Number of nodes currently online. - * @property averageBatteryLevel Average battery level across mesh, or null. - * @property meshHealthScore Estimated health score (0-100). - * @property mostRecentPacketTime Timestamp of most recent packet (ms since epoch). - * @property meshUptimeSeconds Mesh uptime in seconds. - * @property channelUtilizationPercent Channel utilization percentage, or null if unavailable. - */ +/** Response containing aggregate mesh network metrics. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class GetMeshMetricsResponse( + /** Total number of known nodes. */ val totalNodeCount: Int, + /** Number of nodes currently online. */ val onlineNodeCount: Int, + /** Average battery level across mesh, or null if no data. */ val averageBatteryLevel: Int?, + /** Estimated health score (0-100). */ val meshHealthScore: Int, + /** Timestamp of most recent packet (ms since epoch). */ val mostRecentPacketTime: Long, + /** Mesh uptime in seconds. */ val meshUptimeSeconds: Long, + /** Channel utilization percentage, or null if unavailable. */ val channelUtilizationPercent: Int?, ) -/** - * Response containing recent messages from the mesh network. - * - * @property messages List of recent messages ordered by most recent first. - */ +/** Response containing recent messages from the mesh network. */ @AppFunctionSerializable(isDescribedByKDoc = true) -data class GetRecentMessagesResponse(val messages: List) +data class GetRecentMessagesResponse( + /** List of recent messages ordered by most recent first. */ + val messages: List, +) -/** - * Information about a single mesh message. - * - * @property senderName Display name of the message sender. - * @property text The message text content. - * @property contactName Name of the channel or contact the message belongs to. - * @property receivedTime Timestamp when the message was received (ms since epoch). - * @property fromLocal True if this message was sent by the local user. - * @property read True if this message has been read by the user. - */ +/** Information about a single mesh message. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class MessageInfo( + /** Display name of the message sender. */ val senderName: String, + /** The message text content. */ val text: String, + /** Name of the channel or contact the message belongs to. */ val contactName: String, + /** Timestamp when the message was received (ms since epoch). */ val receivedTime: Long, + /** True if this message was sent by the local user. */ val fromLocal: Boolean, + /** True if this message has been read by the user. */ val read: Boolean, ) -/** - * Response containing a summary of unread messages across all contacts. - * - * @property totalUnreadCount Total number of unread messages across all non-muted contacts. - * @property contacts Per-contact breakdown of unread messages, sorted by most recent. - */ +/** Response containing a summary of unread messages across all contacts. */ @AppFunctionSerializable(isDescribedByKDoc = true) -data class GetUnreadSummaryResponse(val totalUnreadCount: Int, val contacts: List) +data class GetUnreadSummaryResponse( + /** Total number of unread messages across all non-muted contacts. */ + val totalUnreadCount: Int, + /** Per-contact breakdown of unread messages, sorted by most recent. */ + val contacts: List, +) -/** - * Unread message details for a single contact or channel. - * - * @property name Display name of the contact or channel. - * @property unreadCount Number of unread messages from this contact. - * @property lastMessagePreview Preview text of the most recent message (up to 100 chars), or null if unavailable. - * @property lastMessageTime Timestamp of the most recent message (ms since epoch), or null if unavailable. - */ +/** Unread message details for a single contact or channel. */ @AppFunctionSerializable(isDescribedByKDoc = true) data class ContactUnreadInfo( + /** Display name of the contact or channel. */ val name: String, + /** Number of unread messages from this contact. */ val unreadCount: Int, + /** Preview text of the most recent message (up to 100 chars), or null if unavailable. */ val lastMessagePreview: String?, + /** Timestamp of the most recent message (ms since epoch), or null if unavailable. */ val lastMessageTime: Long?, ) diff --git a/androidApp/src/google/res/xml/app_metadata.xml b/androidApp/src/google/res/xml/app_metadata.xml new file mode 100644 index 000000000..1ef23a2b2 --- /dev/null +++ b/androidApp/src/google/res/xml/app_metadata.xml @@ -0,0 +1,26 @@ + + + + diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index ba56e1790..ef6e3c9f7 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -17,4 +17,5 @@ --> Meshtastic + Off-grid mesh networking for secure, long-range communications via LoRa radio.