Cross-platform shared artifacts that back the Convos iOS and Android clients.
metrics/— Kotlin Gradle project. Declares metrics descriptors (events, user properties, navigation graph) and runs KSP codegen against them.ConvosMetrics/— generated Swift Package, mirrored out of the KSP build so iOS can consume it as an SPM dependency. Do not hand-edit. Re-run./gradlew buildinsidemetrics/to regenerate.docs/— protocol notes..githooks/— shared git hooks. Install once with./scripts/install-hooks.sh.
cd metrics
./gradlew buildThis runs KSP, regenerates the Swift package into <repo>/ConvosMetrics/, and splices the metrics catalog below into this README.
A pre-commit hook runs ./gradlew build and aborts the commit if the regenerated Swift package or this README differ from what's staged. Activate it once per clone with:
./scripts/install-hooks.shThat sets core.hooksPath=.githooks for the local repo.
The section between the AUTOGEN markers below is regenerated by the build from the descriptors under metrics/descriptors/src/main/kotlin/org/convos/metrics/descriptors/.
| Event | Function | Parameters |
|---|---|---|
started_conversation |
startedConversation |
none |
joined_conversation |
joinedConversation |
verification_duration: Floatmember_count: Inthas_assistant: Booleansource: ConversationSource { URL, Scan, Paste, Message } |
invited_to_conversation |
invitedToConversation |
member_count: Inthas_assistant: Boolean |
added_assistant |
addedAssistant |
member_count: Int |
sent_message |
sentMessage |
sending_time: Floatmember_count: Intattachment_types: Listhas_text: Booleanhas_assistant: Booleanis_success: Boolean |
shared_conversation |
sharedConversation |
member_count: Inthas_assistant: Booleanshare_target: ShareTarget { MESSAGES, MAIL, COPY, QR_CODE, AIRDROP, OTHER, CANCELLED }has_expiration: Booleanexpires_after_use: Booleanis_success: Boolean |
built_agent |
builtAgent |
build_duration: Floatinstruction_char_count: Intinstruction_word_count: Intattachment_types: Listhas_voice_memo: Booleanvoice_memo_duration: Floatconnection_types: Listentry_mode: AgentBuilderEntryMode { COMPOSER, VOICE_MEMO }is_success: Boolean |
purchase_initiated |
purchaseInitiated |
product_id: Stringtier: SubscriptionTier { BUILDER, PRO }period: SubscriptionPeriod { MONTHLY, ANNUAL }source: PaywallSource { SETTINGS, LOW_BALANCE_BANNER, ONBOARDING, MEMBER_CARD, DEBUG } |
purchase_succeeded |
purchaseSucceeded |
product_id: Stringtier: SubscriptionTier { BUILDER, PRO }period: SubscriptionPeriod { MONTHLY, ANNUAL }source: PaywallSource { SETTINGS, LOW_BALANCE_BANNER, ONBOARDING, MEMBER_CARD, DEBUG }duration_secs: Float |
purchase_cancelled |
purchaseCancelled |
product_id: Stringsource: PaywallSource { SETTINGS, LOW_BALANCE_BANNER, ONBOARDING, MEMBER_CARD, DEBUG } |
purchase_failed |
purchaseFailed |
product_id: Stringsource: PaywallSource { SETTINGS, LOW_BALANCE_BANNER, ONBOARDING, MEMBER_CARD, DEBUG }reason: PurchaseFailureReason { PRODUCT_NOT_FOUND, PURCHASE_PENDING, PURCHASE_UNVERIFIED, BACKEND_VERIFY_UNAVAILABLE, BILLING_CLIENT_UNAVAILABLE, UNKNOWN } |
purchases_restored |
purchasesRestored |
restored_count: Int |
| Key | Field | Type | Nullable |
|---|---|---|---|
has_messaged_assistant |
hasMessagedAssistant |
Boolean | no |
last_assistant_message_timestamp |
lastAssistantMessageTimestamp |
String | yes |
contact_count |
contactCount |
Int | no |
conversation_count |
conversationCount |
Int | no |
assistant_conversation_count |
assistantConversationCount |
Int | no |
conversation_count24_hours |
conversationCount24Hours |
Int | no |
conversation_count7_days |
conversationCount7Days |
Int | no |
max_active_convo_age |
maxActiveConvoAge |
Float | no |
Graph source: navigators.dot. navigators.png is re-rendered by the build when Graphviz is installed.
| Screen | Args | Outgoing |
|---|---|---|
tab_root |
none | navigateTo → ConversationsnavigateTo → StuffOverviewnavigateTo → Contacts |
conversations |
none | navigateTo → Conversationpresent → AppSettingspresent → NewConversationpresent → ExplodeConfirmationpresent → ConnectionGrantpresent → ExplodeInfopresent → PinLimitInfopresent → ContactCardpresent → AgentBuilder |
conversation |
conversationId: String |
present → Paywallpresent → ConversationInfopresent → MyInfopresent → MemberProfilepresent → ShareInvitepresent → NewConversationpresent → Reactionspresent → ExplodeInfopresent → LockedConvoInfopresent → FullConvoInfopresent → ConversationForkedInfopresent → RevealMediaInfopresent → PhotosInfopresent → AssistantConfirmationpresent → AgentInfopresent → AgentPowerInfopresent → ExplodedInviteInfopresent → SetupProfilepresent → InviteAcceptedpresent → RequestPushNotificationspresent → BackwardsSecrecyInfopresent → AddMemberspresent → ContactCardpresent → AgentTemplateContactCardpresent → AgentBuilderpresent → ThinkingDetailpresent → AttachmentPreview |
stuff_overview |
none | navigateTo → StuffDetailpresent → AppSettingspresent → NewConversationpresent → AgentBuilder |
stuff_detail |
itemId: StringconversationId: String? |
leaf |
app_settings |
none | navigateTo → MyInfonavigateTo → CustomizeSettingsnavigateTo → AssistantSettingsnavigateTo → ConnectionsnavigateTo → BackupRestorenavigateTo → DeleteAllDatanavigateTo → Devicespresent → Paywall |
new_conversation |
mode: NewConversationMode { CREATE, SCANNER, JOIN_INVITE }inviteCode: String? |
navigateTo → Conversation |
explode_confirmation |
conversationId: String |
leaf |
conversation_info |
conversationId: String |
navigateTo → ConversationInfoEditnavigateTo → MembersListnavigateTo → AgentFilesLinksnavigateTo → AgentTemplateContactCard |
conversation_info_edit |
conversationId: String |
leaf |
members_list |
conversationId: String |
navigateTo → MemberProfilenavigateTo → AgentTemplateContactCard |
member_profile |
conversationId: StringmemberId: String |
leaf |
share_invite |
conversationId: String |
leaf |
reactions |
conversationId: StringmessageId: String |
leaf |
agent_files_links |
conversationId: String |
present → AttachmentPreview |
setup_profile |
none | leaf |
invite_accepted |
none | leaf |
request_push_notifications |
none | leaf |
my_info |
none | navigateTo → QuicknameRandomizer |
customize_settings |
none | leaf |
assistant_settings |
none | present → InviteCodeEntry |
connections |
none | present → ConnectionGrant |
backup_restore |
none | leaf |
delete_all_data |
none | leaf |
quickname_randomizer |
none | leaf |
invite_code_entry |
none | leaf |
connection_grant |
serviceId: StringconversationId: String |
leaf |
devices |
none | present → PairDevicepresent → RemoveDevice |
pair_device |
pairingId: String?initiatorName: String?expiresAt: Long? |
leaf |
remove_device |
deviceId: String |
leaf |
explode_info |
none | leaf |
pin_limit_info |
none | leaf |
locked_convo_info |
conversationId: String |
present → LockConvoConfirmation |
full_convo_info |
none | leaf |
conversation_forked_info |
conversationId: String |
leaf |
reveal_media_info |
none | leaf |
photos_info |
none | leaf |
assistant_confirmation |
conversationId: String |
leaf |
agent_info |
none | leaf |
agent_power_info |
none | leaf |
exploded_invite_info |
none | leaf |
backwards_secrecy_info |
none | leaf |
lock_convo_confirmation |
conversationId: String |
leaf |
contacts |
none | navigateTo → ContactCardpresent → NewConversationpresent → AppSettingspresent → AgentBuilder |
contact_card |
inboxId: StringconversationId: String? |
navigateTo → Contacts |
agent_template_contact_card |
templateId: StringinboxId: StringconversationId: String? |
leaf |
add_members |
conversationId: StringconversationTitle: String? |
leaf |
agent_builder |
conversationId: StringentryMode: AgentBuilderEntryPoint { INLINE, SHEET } |
navigateTo → Conversation |
thinking_detail |
conversationId: StringsenderInboxId: StringmessageId: String |
leaf |
attachment_preview |
conversationId: String?senderInboxId: String? |
navigateTo → ContactCardnavigateTo → AgentTemplateContactCard |
paywall |
source: PaywallSource { SETTINGS, LOW_BALANCE_BANNER, ONBOARDING, MEMBER_CARD, DEBUG } |
leaf |
subscription_settings |
none | present → Paywall |
billing_debug |
none | present → PaywallnavigateTo → SubscriptionSettings |
