gravity-schema 0.5.2__tar.gz → 0.5.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/PKG-INFO +1 -1
  2. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/catalog_imported.yaml +478 -0
  3. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/cron.py +26 -1
  4. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/manifest.py +56 -0
  5. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/validation.py +11 -1
  6. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/pyproject.toml +1 -1
  7. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_cron.py +16 -1
  8. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_manifest.py +106 -0
  9. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/.gitignore +0 -0
  10. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/LICENSE +0 -0
  11. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/NOTICE +0 -0
  12. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/README.md +0 -0
  13. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/__init__.py +0 -0
  14. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/__main__.py +0 -0
  15. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/catalog.py +0 -0
  16. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/catalog.yaml +0 -0
  17. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/contract.py +0 -0
  18. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/errors.py +0 -0
  19. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/export.py +0 -0
  20. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/gen_catalog.py +0 -0
  21. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/gravity_schema/source_scan.py +0 -0
  22. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/schema/manifest.schema.json +0 -0
  23. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/conftest.py +0 -0
  24. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_catalog_parity.py +0 -0
  25. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_errors.py +0 -0
  26. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_json_schema.py +0 -0
  27. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_kind_outputs.py +0 -0
  28. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_slots.py +0 -0
  29. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_source_scan.py +0 -0
  30. {gravity_schema-0.5.2 → gravity_schema-0.5.4}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gravity-schema
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Gravity agent manifest contract — models, validation, and JSON Schema export
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -13156,3 +13156,481 @@
13156
13156
  breadth: generic
13157
13157
  guarantee: experimental
13158
13158
  side_effect: read
13159
+ - name: whatsapp.configure_conversational_automation
13160
+ slug: WHATSAPP_CONFIGURE_CONVERSATIONAL_AUTOMATION
13161
+ toolkit: whatsapp
13162
+ description: Configure conversational automation settings for a WhatsApp Business phone number. Sets up away messages, greeting messages, custom commands, and ice breaker prompts to automate customer interactions....
13163
+ scopes:
13164
+ - whatsapp_business_messaging
13165
+ - whatsapp_business_management
13166
+ breadth: generic
13167
+ guarantee: experimental
13168
+ side_effect: write
13169
+ - name: whatsapp.create_flow
13170
+ slug: WHATSAPP_CREATE_FLOW
13171
+ toolkit: whatsapp
13172
+ description: Create a new WhatsApp Flow for the WhatsApp Business Account. Use this action when you want to create an interactive flow that can collect user data, guide users through a process, or provide structur...
13173
+ scopes:
13174
+ - whatsapp_business_messaging
13175
+ - whatsapp_business_management
13176
+ breadth: generic
13177
+ guarantee: experimental
13178
+ side_effect: write
13179
+ - name: whatsapp.create_max_price_agreements
13180
+ slug: WHATSAPP_CREATE_MAX_PRICE_AGREEMENTS
13181
+ toolkit: whatsapp
13182
+ description: Create a max price agreement for WhatsApp marketing messages pricing. Max price agreements set a ceiling on the per-conversation price you'll pay for WhatsApp messaging across your WhatsApp Business A...
13183
+ scopes:
13184
+ - business_management
13185
+ - whatsapp_business_management
13186
+ - whatsapp_business_messaging
13187
+ breadth: generic
13188
+ guarantee: experimental
13189
+ side_effect: write
13190
+ - name: whatsapp.create_message_template
13191
+ slug: WHATSAPP_CREATE_MESSAGE_TEMPLATE
13192
+ toolkit: whatsapp
13193
+ description: Create a new message template for the WhatsApp Business Account. Templates must be approved by WhatsApp before they can be used. Templates are required for marketing messages and messages sent outside...
13194
+ scopes:
13195
+ - whatsapp_business_management
13196
+ breadth: generic
13197
+ guarantee: experimental
13198
+ side_effect: write
13199
+ - name: whatsapp.create_qr_code
13200
+ slug: WHATSAPP_CREATE_QR_CODE
13201
+ toolkit: whatsapp
13202
+ description: Create a new QR code with a prefilled message for a WhatsApp Business phone number. Use this action when you need to generate a shareable QR code that customers can scan to start a conversation with y...
13203
+ scopes:
13204
+ - whatsapp_business_management
13205
+ breadth: generic
13206
+ guarantee: experimental
13207
+ side_effect: write
13208
+ - name: whatsapp.create_upload_session
13209
+ slug: WHATSAPP_CREATE_UPLOAD_SESSION
13210
+ toolkit: whatsapp
13211
+ description: Create a resumable upload session for large file uploads to WhatsApp. This action initiates an upload session that allows you to upload large files in chunks. First create a session, then use the sess...
13212
+ scopes:
13213
+ - whatsapp_business_messaging
13214
+ breadth: generic
13215
+ guarantee: experimental
13216
+ side_effect: write
13217
+ - name: whatsapp.get_activities
13218
+ slug: WHATSAPP_GET_ACTIVITIES
13219
+ toolkit: whatsapp
13220
+ description: Retrieve activity history for a WhatsApp Business Account. This action returns a paginated list of activities such as phone number verifications, user additions, and other administrative actions perfo...
13221
+ scopes:
13222
+ - whatsapp_business_management
13223
+ breadth: generic
13224
+ guarantee: experimental
13225
+ side_effect: read
13226
+ - name: whatsapp.get_business_account_details
13227
+ slug: WHATSAPP_GET_BUSINESS_ACCOUNT_DETAILS
13228
+ toolkit: whatsapp
13229
+ description: Retrieve comprehensive details about a WhatsApp Business Account (WABA). Returns configuration, status, and settings information including account review status, business verification status, timezone...
13230
+ scopes:
13231
+ - whatsapp_business_messaging
13232
+ breadth: generic
13233
+ guarantee: experimental
13234
+ side_effect: read
13235
+ - name: whatsapp.get_business_compliance_info
13236
+ slug: WHATSAPP_GET_BUSINESS_COMPLIANCE_INFO
13237
+ toolkit: whatsapp
13238
+ description: Retrieve comprehensive business compliance information for a WhatsApp Business phone number. Returns regulatory and compliance details including entity registration status, grievance officer contact i...
13239
+ scopes:
13240
+ - whatsapp_business_management
13241
+ breadth: generic
13242
+ guarantee: experimental
13243
+ side_effect: read
13244
+ - name: whatsapp.get_business_encryption
13245
+ slug: WHATSAPP_GET_BUSINESS_ENCRYPTION
13246
+ toolkit: whatsapp
13247
+ description: Retrieve the WhatsApp Business public key and its signature verification status. Use this action when you need to obtain the end-to-end encryption public key for a WhatsApp Business phone number. This...
13248
+ scopes:
13249
+ - whatsapp_business_messaging
13250
+ breadth: generic
13251
+ guarantee: experimental
13252
+ side_effect: read
13253
+ - name: whatsapp.get_business_profile
13254
+ slug: WHATSAPP_GET_BUSINESS_PROFILE
13255
+ toolkit: whatsapp
13256
+ description: Get the business profile information for a WhatsApp Business phone number. This includes business details like description, address, website, and contact info.
13257
+ scopes:
13258
+ - whatsapp_business_messaging
13259
+ breadth: generic
13260
+ guarantee: experimental
13261
+ side_effect: read
13262
+ - name: whatsapp.get_client_business_accounts
13263
+ slug: WHATSAPP_GET_CLIENT_BUSINESS_ACCOUNTS
13264
+ toolkit: whatsapp
13265
+ description: Retrieve WhatsApp Business Accounts shared with or managed by a business portfolio. Use this action when you need to get WhatsApp Business Accounts (WABAs) that are client accounts shared with or mana...
13266
+ scopes:
13267
+ - whatsapp_business_management
13268
+ breadth: generic
13269
+ guarantee: experimental
13270
+ side_effect: read
13271
+ - name: whatsapp.get_commerce_settings
13272
+ slug: WHATSAPP_GET_COMMERCE_SETTINGS
13273
+ toolkit: whatsapp
13274
+ description: Get commerce settings for a WhatsApp Business phone number. Retrieves cart and catalog visibility settings for a specific WhatsApp Business phone number. Use this action to check whether shopping cart...
13275
+ scopes:
13276
+ - whatsapp_business_messaging
13277
+ breadth: generic
13278
+ guarantee: experimental
13279
+ side_effect: read
13280
+ - name: whatsapp.get_flow_assets
13281
+ slug: WHATSAPP_GET_FLOW_ASSETS
13282
+ toolkit: whatsapp
13283
+ description: List assets associated with a WhatsApp Flow. Use this action when you need to retrieve the assets (such as flow.json) that are associated with a WhatsApp Flow. The response includes download URLs for...
13284
+ scopes:
13285
+ - whatsapp_business_messaging
13286
+ breadth: generic
13287
+ guarantee: experimental
13288
+ side_effect: read
13289
+ - name: whatsapp.get_join_requests
13290
+ slug: WHATSAPP_GET_JOIN_REQUESTS
13291
+ toolkit: whatsapp
13292
+ description: Get all pending join requests for a WhatsApp group. Use this action when you need to retrieve pending requests from users who want to join a WhatsApp group. This is useful for group administration, mo...
13293
+ scopes:
13294
+ - whatsapp_business_management
13295
+ - whatsapp_business_messaging
13296
+ breadth: generic
13297
+ guarantee: experimental
13298
+ side_effect: read
13299
+ - name: whatsapp.get_media_info
13300
+ slug: WHATSAPP_GET_MEDIA_INFO
13301
+ toolkit: whatsapp
13302
+ description: Get metadata and download URL for uploaded WhatsApp media. Returns media ID, download URL (valid for 5 minutes), MIME type, SHA256 hash, and file size. The download URL can be used to retrieve the act...
13303
+ scopes:
13304
+ - whatsapp_business_messaging
13305
+ breadth: generic
13306
+ guarantee: experimental
13307
+ side_effect: read
13308
+ - name: whatsapp.get_message_history
13309
+ slug: WHATSAPP_GET_MESSAGE_HISTORY
13310
+ toolkit: whatsapp
13311
+ description: Retrieve message history for a WhatsApp Business phone number. Returns a paginated list of messages with their delivery status information, including events like delivery status updates, timestamps, a...
13312
+ scopes:
13313
+ - whatsapp_business_messaging
13314
+ - whatsapp_business_management
13315
+ breadth: generic
13316
+ guarantee: experimental
13317
+ side_effect: read
13318
+ - name: whatsapp.get_message_templates
13319
+ slug: WHATSAPP_GET_MESSAGE_TEMPLATES
13320
+ toolkit: whatsapp
13321
+ description: Get all message templates for the WhatsApp Business Account. Templates are required for sending messages outside the 24-hour window and for marketing/utility messages.
13322
+ scopes:
13323
+ - whatsapp_business_management
13324
+ breadth: generic
13325
+ guarantee: experimental
13326
+ side_effect: read
13327
+ - name: whatsapp.get_owned_business_accounts
13328
+ slug: WHATSAPP_GET_OWNED_BUSINESS_ACCOUNTS
13329
+ toolkit: whatsapp
13330
+ description: Retrieve WhatsApp Business Accounts owned by a business portfolio. Use this action when you need to get all WhatsApp Business Accounts (WABAs) that are directly owned by a specific business. This diff...
13331
+ scopes:
13332
+ - whatsapp_business_messaging
13333
+ breadth: generic
13334
+ guarantee: experimental
13335
+ side_effect: read
13336
+ - name: whatsapp.get_phone_number
13337
+ slug: WHATSAPP_GET_PHONE_NUMBER
13338
+ toolkit: whatsapp
13339
+ description: Retrieve detailed information about a specific WhatsApp Business phone number. Returns phone number details including verification status, quality rating, display number, verified business name, throu...
13340
+ scopes:
13341
+ - whatsapp_business_management
13342
+ breadth: generic
13343
+ guarantee: experimental
13344
+ side_effect: read
13345
+ - name: whatsapp.get_phone_numbers
13346
+ slug: WHATSAPP_GET_PHONE_NUMBERS
13347
+ toolkit: whatsapp
13348
+ description: Retrieve all phone numbers registered to your WhatsApp Business Account. Returns phone number IDs, display numbers, verification status, quality ratings, and messaging throughput limits. Use the phone...
13349
+ scopes:
13350
+ - whatsapp_business_management
13351
+ breadth: generic
13352
+ guarantee: experimental
13353
+ side_effect: read
13354
+ - name: whatsapp.get_schedules
13355
+ slug: WHATSAPP_GET_SCHEDULES
13356
+ toolkit: whatsapp
13357
+ description: Retrieve campaign schedules associated with a WhatsApp Business Account. Use this action when you need to list all scheduled campaigns for a WABA, including their status, timing, and recurrence patter...
13358
+ scopes:
13359
+ - whatsapp_business_management
13360
+ breadth: generic
13361
+ guarantee: experimental
13362
+ side_effect: read
13363
+ - name: whatsapp.get_settings
13364
+ slug: WHATSAPP_GET_SETTINGS
13365
+ toolkit: whatsapp
13366
+ description: Get Cloud API settings for a WhatsApp Business phone number. Retrieves calling, storage, and payload encryption settings for a specific WhatsApp Business phone number. This includes information about...
13367
+ scopes:
13368
+ - whatsapp_business_management
13369
+ - whatsapp_business_messaging
13370
+ breadth: generic
13371
+ guarantee: experimental
13372
+ side_effect: read
13373
+ - name: whatsapp.get_subscribed_apps
13374
+ slug: WHATSAPP_GET_SUBSCRIBED_APPS
13375
+ toolkit: whatsapp
13376
+ description: Get all applications currently subscribed to webhooks for a WhatsApp Business Account. Use this action when you need to retrieve the list of third-party applications or integrations that are subscribe...
13377
+ scopes:
13378
+ - whatsapp_business_management
13379
+ - whatsapp_business_messaging
13380
+ breadth: generic
13381
+ guarantee: experimental
13382
+ side_effect: read
13383
+ - name: whatsapp.get_template_library
13384
+ slug: WHATSAPP_GET_TEMPLATE_LIBRARY
13385
+ toolkit: whatsapp
13386
+ description: Browse the pre-built message template library for WhatsApp Business Account. This retrieves a list of available templates that can be used for messaging. Use when you need to discover available messag...
13387
+ scopes:
13388
+ - whatsapp_business_messaging
13389
+ - whatsapp_business_management
13390
+ breadth: generic
13391
+ guarantee: experimental
13392
+ side_effect: read
13393
+ - name: whatsapp.get_template_status
13394
+ slug: WHATSAPP_GET_TEMPLATE_STATUS
13395
+ toolkit: whatsapp
13396
+ description: Get the status and details of a specific message template. This is useful for checking if a template has been approved, rejected, or is still pending review.
13397
+ scopes:
13398
+ - whatsapp_business_management
13399
+ breadth: generic
13400
+ guarantee: experimental
13401
+ side_effect: read
13402
+ - name: whatsapp.list_flows
13403
+ slug: WHATSAPP_LIST_FLOWS
13404
+ toolkit: whatsapp
13405
+ description: List all WhatsApp Flows associated with the WhatsApp Business Account. Returns flow details including name, status, categories, and any validation errors. Use when you need to retrieve all available f...
13406
+ scopes:
13407
+ - whatsapp_business_management
13408
+ breadth: generic
13409
+ guarantee: experimental
13410
+ side_effect: read
13411
+ - name: whatsapp.list_groups
13412
+ slug: WHATSAPP_LIST_GROUPS
13413
+ toolkit: whatsapp
13414
+ description: List all WhatsApp groups associated with a business phone number. Use this action when you need to retrieve the list of groups that a business phone number is a member of. This is useful for managing...
13415
+ scopes:
13416
+ - whatsapp_business_management
13417
+ - whatsapp_business_messaging
13418
+ breadth: generic
13419
+ guarantee: experimental
13420
+ side_effect: read
13421
+ - name: whatsapp.list_qr_codes
13422
+ slug: WHATSAPP_LIST_QR_CODES
13423
+ toolkit: whatsapp
13424
+ description: Get a list of all QR codes for a WhatsApp Business phone number. Use this action when you need to retrieve all message QR codes associated with a specific WhatsApp Business phone number. This is usefu...
13425
+ scopes:
13426
+ - whatsapp_business_management
13427
+ breadth: generic
13428
+ guarantee: experimental
13429
+ side_effect: read
13430
+ - name: whatsapp.list_solutions
13431
+ slug: WHATSAPP_LIST_SOLUTIONS
13432
+ toolkit: whatsapp
13433
+ description: Retrieve a paginated list of Multi-Partner Solutions for a WhatsApp Business Account. Use when you need to view all multi-partner solutions (such as SMB solutions) associated with a WABA, including th...
13434
+ scopes:
13435
+ - whatsapp_business_management
13436
+ breadth: generic
13437
+ guarantee: experimental
13438
+ side_effect: read
13439
+ - name: whatsapp.publish_flow
13440
+ slug: WHATSAPP_PUBLISH_FLOW
13441
+ toolkit: whatsapp
13442
+ description: Publish a WhatsApp Flow to make it available for use. Use this action when you have finished designing and testing a WhatsApp Flow and are ready to make it available for users. The flow must be in DRA...
13443
+ scopes:
13444
+ - whatsapp_business_management
13445
+ - whatsapp_business_messaging
13446
+ breadth: generic
13447
+ guarantee: experimental
13448
+ side_effect: write
13449
+ - name: whatsapp.register_phone
13450
+ slug: WHATSAPP_REGISTER_PHONE
13451
+ toolkit: whatsapp
13452
+ description: Register a WhatsApp Business phone number for use with WhatsApp Cloud API. Use when you need to register a phone number that has been added to your WhatsApp Business Account. This action requires sett...
13453
+ scopes:
13454
+ - whatsapp_business_management
13455
+ - whatsapp_business_messaging
13456
+ breadth: generic
13457
+ guarantee: experimental
13458
+ side_effect: write
13459
+ - name: whatsapp.send_contacts
13460
+ slug: WHATSAPP_SEND_CONTACTS
13461
+ toolkit: whatsapp
13462
+ description: 'Send contacts WhatsApp number. Note: The message will be delivered to the recipient only if they have initiated a conversation first.'
13463
+ scopes:
13464
+ - whatsapp_business_messaging
13465
+ breadth: generic
13466
+ guarantee: experimental
13467
+ side_effect: write
13468
+ - name: whatsapp.send_interactive_buttons
13469
+ slug: WHATSAPP_SEND_INTERACTIVE_BUTTONS
13470
+ toolkit: whatsapp
13471
+ description: Send an interactive button message with up to 3 reply buttons to a WhatsApp user. Interactive button messages allow recipients to quickly respond by tapping predefined buttons. Perfect for yes/no ques...
13472
+ scopes:
13473
+ - whatsapp_business_messaging
13474
+ breadth: generic
13475
+ guarantee: experimental
13476
+ side_effect: write
13477
+ - name: whatsapp.send_interactive_list
13478
+ slug: WHATSAPP_SEND_INTERACTIVE_LIST
13479
+ toolkit: whatsapp
13480
+ description: Send an interactive list message to a WhatsApp number. List messages display a menu of options organized into sections. Users tap a button to view the list and select one option. Perfect for product c...
13481
+ scopes:
13482
+ - whatsapp_business_messaging
13483
+ breadth: generic
13484
+ guarantee: experimental
13485
+ side_effect: write
13486
+ - name: whatsapp.send_location
13487
+ slug: WHATSAPP_SEND_LOCATION
13488
+ toolkit: whatsapp
13489
+ description: Send a location message with coordinates, name, and address to a WhatsApp user. This action allows you to share location information through WhatsApp Business API. The location message includes latitu...
13490
+ scopes:
13491
+ - whatsapp_business_messaging
13492
+ breadth: generic
13493
+ guarantee: experimental
13494
+ side_effect: write
13495
+ - name: whatsapp.send_media
13496
+ slug: WHATSAPP_SEND_MEDIA
13497
+ toolkit: whatsapp
13498
+ description: 'Send a media message to a WhatsApp number. Note: The media will be delivered to the recipient only if they have texted first.'
13499
+ scopes:
13500
+ - whatsapp_business_messaging
13501
+ breadth: generic
13502
+ guarantee: experimental
13503
+ side_effect: write
13504
+ - name: whatsapp.send_media_by_id
13505
+ slug: WHATSAPP_SEND_MEDIA_BY_ID
13506
+ toolkit: whatsapp
13507
+ description: Send media using a media ID from previously uploaded media. This is more efficient than sending media by URL as the media is already on WhatsApp servers. Use upload_media action first to get the media...
13508
+ scopes:
13509
+ - whatsapp_business_messaging
13510
+ breadth: generic
13511
+ guarantee: experimental
13512
+ side_effect: write
13513
+ - name: whatsapp.send_message
13514
+ slug: WHATSAPP_SEND_MESSAGE
13515
+ toolkit: whatsapp
13516
+ description: 'Send a text message to a WhatsApp user. Important: The recipient phone number must be registered on WhatsApp and must have initiated a conversation with your business within the last 24 hours, OR you...'
13517
+ scopes:
13518
+ - whatsapp_business_messaging
13519
+ breadth: generic
13520
+ guarantee: experimental
13521
+ side_effect: write
13522
+ - name: whatsapp.send_template_message
13523
+ slug: WHATSAPP_SEND_TEMPLATE_MESSAGE
13524
+ toolkit: whatsapp
13525
+ description: Send a template message to a WhatsApp number.
13526
+ scopes:
13527
+ - whatsapp_business_messaging
13528
+ breadth: generic
13529
+ guarantee: experimental
13530
+ side_effect: write
13531
+ - name: whatsapp.set_two_step_verification
13532
+ slug: WHATSAPP_SET_TWO_STEP_VERIFICATION
13533
+ toolkit: whatsapp
13534
+ description: Set or reset the two-step verification PIN for a WhatsApp Business phone number. Use when you need to change the 6-digit PIN that protects a registered phone number — for example after onboarding a ne...
13535
+ scopes:
13536
+ - whatsapp_business_management
13537
+ - whatsapp_business_messaging
13538
+ breadth: generic
13539
+ guarantee: experimental
13540
+ side_effect: write
13541
+ - name: whatsapp.subscribe_app
13542
+ slug: WHATSAPP_SUBSCRIBE_APP
13543
+ toolkit: whatsapp
13544
+ description: Subscribe your application to receive webhook notifications for a WhatsApp Business Account (WABA). This action registers the application to receive webhook events from the specified WABA. Use this ac...
13545
+ scopes:
13546
+ - whatsapp_business_management
13547
+ - whatsapp_business_messaging
13548
+ breadth: generic
13549
+ guarantee: experimental
13550
+ side_effect: write
13551
+ - name: whatsapp.update_business_account
13552
+ slug: WHATSAPP_UPDATE_BUSINESS_ACCOUNT
13553
+ toolkit: whatsapp
13554
+ description: Update metadata for a WhatsApp Flow including its name, categories, and endpoint URI. Use when you need to modify the configuration or settings of an existing WhatsApp Flow.
13555
+ scopes:
13556
+ - whatsapp_business_management
13557
+ breadth: generic
13558
+ guarantee: experimental
13559
+ side_effect: write
13560
+ - name: whatsapp.update_business_profile
13561
+ slug: WHATSAPP_UPDATE_BUSINESS_PROFILE
13562
+ toolkit: whatsapp
13563
+ description: Update WhatsApp Business Profile information including business details, contact information, and profile settings. Use when you need to modify the business profile settings such as the about text, bu...
13564
+ scopes:
13565
+ - whatsapp_business_management
13566
+ - whatsapp_business_messaging
13567
+ breadth: generic
13568
+ guarantee: experimental
13569
+ side_effect: write
13570
+ - name: whatsapp.update_commerce_settings
13571
+ slug: WHATSAPP_UPDATE_COMMERCE_SETTINGS
13572
+ toolkit: whatsapp
13573
+ description: Set or update commerce settings for a WhatsApp Business phone number. Use this action to enable or disable shopping cart functionality and product catalog visibility for a specific WhatsApp Business p...
13574
+ scopes:
13575
+ - whatsapp_business_management
13576
+ - whatsapp_business_messaging
13577
+ breadth: generic
13578
+ guarantee: experimental
13579
+ side_effect: write
13580
+ - name: whatsapp.update_flow_json
13581
+ slug: WHATSAPP_UPDATE_FLOW_JSON
13582
+ toolkit: whatsapp
13583
+ description: Update the Flow JSON configuration for a WhatsApp Flow. Use this action when you need to update or upload a new flow.json file for an existing WhatsApp Flow. The Flow JSON defines the structure, scree...
13584
+ scopes:
13585
+ - whatsapp_business_management
13586
+ - whatsapp_business_messaging
13587
+ breadth: generic
13588
+ guarantee: experimental
13589
+ side_effect: write
13590
+ - name: whatsapp.update_settings
13591
+ slug: WHATSAPP_UPDATE_SETTINGS
13592
+ toolkit: whatsapp
13593
+ description: Update Cloud API settings for a WhatsApp Business phone number. Use this action to modify calling features, user identity verification settings, payload encryption configuration, and data storage pref...
13594
+ scopes:
13595
+ - whatsapp_business_management
13596
+ - whatsapp_business_messaging
13597
+ breadth: generic
13598
+ guarantee: experimental
13599
+ side_effect: write
13600
+ - name: whatsapp.upload_media
13601
+ slug: WHATSAPP_UPLOAD_MEDIA
13602
+ toolkit: whatsapp
13603
+ description: 'Upload media files (images, videos, audio, documents, stickers) to WhatsApp servers. The uploaded media gets a media ID that can be used in send_media or other messaging actions. Supported formats: -...'
13604
+ scopes:
13605
+ - whatsapp_business_messaging
13606
+ breadth: generic
13607
+ guarantee: experimental
13608
+ side_effect: write
13609
+ - name: whatsapp.upsert_message_template
13610
+ slug: WHATSAPP_UPSERT_MESSAGE_TEMPLATE
13611
+ toolkit: whatsapp
13612
+ description: Create or update a message template for the WhatsApp Business Account. Use this action when you need to create a new message template or update an existing one with the same name. If a template with t...
13613
+ scopes:
13614
+ - whatsapp_business_management
13615
+ - whatsapp_business_messaging
13616
+ breadth: generic
13617
+ guarantee: experimental
13618
+ side_effect: write
13619
+ - name: whatsapp.verify_code
13620
+ slug: WHATSAPP_VERIFY_CODE
13621
+ toolkit: whatsapp
13622
+ description: Verify the OTP code for a pre-verified phone number. Use when you need to complete phone number verification by submitting the received OTP code. The phone number must first be added to your WhatsApp...
13623
+ scopes:
13624
+ - whatsapp_business_management
13625
+ - whatsapp_business_messaging
13626
+ breadth: generic
13627
+ guarantee: experimental
13628
+ side_effect: write
13629
+ - name: whatsapp.who_am_i
13630
+ slug: WHATSAPP_WHO_AM_I
13631
+ toolkit: whatsapp
13632
+ description: Return the identity (name) of the connected WhatsApp (Meta) account.
13633
+ scopes: []
13634
+ breadth: generic
13635
+ guarantee: experimental
13636
+ side_effect: read
@@ -1,11 +1,16 @@
1
- """Standard 5-field cron, local time. Day-of-week 0 or 7 is Sunday; when both day
1
+ """Standard 5-field cron, in whatever timezone the `after` datetime carries (gravity
2
+ schedule passes Asia/Kolkata, like the hosted schedules). Day-of-week 0 or 7 is Sunday; when both day
2
3
  fields are restricted, either matching is enough (as in cron itself)."""
3
4
 
4
5
  from __future__ import annotations
5
6
 
7
+ import math
6
8
  from datetime import datetime, timedelta
9
+ from itertools import pairwise
7
10
 
8
11
  _BOUNDS = ((0, 59), (0, 23), (1, 31), (1, 12), (0, 7))
12
+ # Every tick is a paid run; the control plane's MIN_SCHEDULE_MINUTES.
13
+ MIN_GAP_MINUTES = 5
9
14
 
10
15
 
11
16
  def _values(field: str, low: int, high: int) -> set[int]:
@@ -22,6 +27,26 @@ def _values(field: str, low: int, high: int) -> set[int]:
22
27
  return out
23
28
 
24
29
 
30
+ def field_values(expr: str) -> list[set[int]]:
31
+ """Each field's values. ValueError when a field is malformed, empty or out of its range."""
32
+ out = [_values(f, lo, hi) for f, (lo, hi) in zip(expr.split(), _BOUNDS)]
33
+ for vals, (lo, hi) in zip(out, _BOUNDS):
34
+ if not vals or min(vals) < lo or max(vals) > hi:
35
+ raise ValueError(f"'{expr}' has a value out of range")
36
+ return out
37
+
38
+
39
+ def min_gap(expr: str) -> float:
40
+ """Minutes between the closest two fires, from the minute and hour fields alone (the day
41
+ fields can only widen it). Same arithmetic as the control plane's checkScheduleFloor."""
42
+ minutes, hours = field_values(expr)[:2]
43
+ mins = sorted(minutes)
44
+ gap = min((b - a for a, b in pairwise(mins)), default=math.inf)
45
+ if any((h + 1) % 24 in hours for h in hours):
46
+ gap = min(gap, 60 - mins[-1] + mins[0])
47
+ return gap
48
+
49
+
25
50
  def matches(expr: str, at: datetime) -> bool:
26
51
  fields = expr.split()
27
52
  minute, hour, dom, month, dow = (_values(f, *b) for f, b in zip(fields, _BOUNDS))
@@ -9,6 +9,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
9
9
  from pydantic_core import PydanticCustomError
10
10
 
11
11
  from . import contract as C
12
+ from . import cron
12
13
  from .errors import ErrorCode
13
14
 
14
15
  # Unknown fields are rejected, so a newer-contract field is reported, not dropped.
@@ -99,6 +100,18 @@ class RuntimeSpec(BaseModel):
99
100
  )
100
101
  return v
101
102
 
103
+ @field_validator("python", mode="before")
104
+ @classmethod
105
+ def _check_python_is_text(cls, v: Any) -> Any:
106
+ # YAML reads an unquoted 3.12 as a float (and 3.10 as 3.1).
107
+ if isinstance(v, (int, float)):
108
+ raise PydanticCustomError(
109
+ ErrorCode.INVALID_PYTHON_VERSION.value,
110
+ "Python version {value} is not quoted text.",
111
+ {"value": v, "remediation": 'Put it in quotes: python: "3.12".'},
112
+ )
113
+ return v
114
+
102
115
  @field_validator("python")
103
116
  @classmethod
104
117
  def _check_python(cls, v: str) -> str:
@@ -375,6 +388,37 @@ class ScheduleSpec(BaseModel):
375
388
  ),
376
389
  },
377
390
  )
391
+ try:
392
+ cron.field_values(v)
393
+ except ValueError:
394
+ raise PydanticCustomError(
395
+ ErrorCode.INVALID_TRIGGER.value,
396
+ "Schedule '{value}' has a value out of range.",
397
+ {
398
+ "value": v,
399
+ "remediation": (
400
+ "Minutes are 0-59, hours 0-23, days 1-31, months 1-12 and "
401
+ "weekdays 0-7 (0 and 7 are Sunday)."
402
+ ),
403
+ },
404
+ ) from None
405
+ # The platform's scheduler cannot express cron's "either day matches".
406
+ if fields[2] != "*" and fields[4] != "*":
407
+ raise PydanticCustomError(
408
+ ErrorCode.INVALID_TRIGGER.value,
409
+ "Schedule '{value}' restricts both the day of the month and the weekday.",
410
+ {"value": v, "remediation": "Set one of them to *."},
411
+ )
412
+ if cron.min_gap(v) < cron.MIN_GAP_MINUTES:
413
+ raise PydanticCustomError(
414
+ ErrorCode.INVALID_TRIGGER.value,
415
+ "Schedules can run at most every {limit} minutes.",
416
+ {
417
+ "value": v,
418
+ "limit": cron.MIN_GAP_MINUTES,
419
+ "remediation": f"Use '*/{cron.MIN_GAP_MINUTES} * * * *' or something slower.",
420
+ },
421
+ )
378
422
  return v
379
423
 
380
424
 
@@ -527,6 +571,18 @@ class Manifest(BaseModel):
527
571
  )
528
572
  return v
529
573
 
574
+ @field_validator("version", mode="before")
575
+ @classmethod
576
+ def _check_version_is_text(cls, v: Any) -> Any:
577
+ # YAML reads an unquoted 1.0 as a float.
578
+ if isinstance(v, (int, float)):
579
+ raise PydanticCustomError(
580
+ ErrorCode.INVALID_AGENT_VERSION.value,
581
+ "Agent version {value} is not quoted text.",
582
+ {"value": v, "remediation": 'Put it in quotes: version: "1.0.0".'},
583
+ )
584
+ return v
585
+
530
586
  @field_validator("version")
531
587
  @classmethod
532
588
  def _check_version(cls, v: str) -> str:
@@ -156,7 +156,17 @@ def load_manifest_file(
156
156
  "Run `gravity init` to create one, or check the path.",
157
157
  str(p),
158
158
  )
159
- return load_manifest_text(p.read_text(encoding="utf-8"), catalog=catalog)
159
+ try:
160
+ text = p.read_text(encoding="utf-8")
161
+ except UnicodeDecodeError:
162
+ # Notepad's "Unicode" is UTF-16; a BOM-only UTF-8 file decodes fine and YAML skips the BOM.
163
+ raise _fail(
164
+ ErrorCode.MALFORMED_YAML,
165
+ "Manifest is not UTF-8 text.",
166
+ "Save the file as UTF-8 (in Notepad: Encoding, then UTF-8).",
167
+ str(p),
168
+ ) from None
169
+ return load_manifest_text(text, catalog=catalog)
160
170
 
161
171
 
162
172
  def _fail(code: ErrorCode, message: str, remediation: str, location: str = "") -> ManifestValidationError:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gravity-schema"
3
- version = "0.5.2"
3
+ version = "0.5.4"
4
4
  description = "Gravity agent manifest contract — models, validation, and JSON Schema export"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime
2
2
 
3
3
  import pytest
4
- from gravity_schema.cron import matches, next_run
4
+ from gravity_schema.cron import field_values, matches, min_gap, next_run
5
5
 
6
6
  MON_0900 = datetime(2026, 9, 28, 9, 0) # a Monday
7
7
 
@@ -29,3 +29,18 @@ def test_next_run_is_strictly_after():
29
29
  assert next_run("0 9 * * *", MON_0900) == datetime(2026, 9, 29, 9, 0)
30
30
  with pytest.raises(ValueError):
31
31
  next_run("0 0 31 2 *", MON_0900) # 31 February
32
+
33
+
34
+ def test_field_values_checks_every_range():
35
+ assert field_values("0 9 1 * 7")[4] == {7}
36
+ for bad in ("60 * * * *", "0 24 * * *", "0 0 0 * *", "0 0 32 * *", "0 0 * 13 *", "0 0 * * 8", "5-3 * * * *", "*/0 * * * *"):
37
+ with pytest.raises(ValueError):
38
+ field_values(bad)
39
+
40
+
41
+ def test_min_gap_is_the_closest_two_fires():
42
+ assert min_gap("*/5 * * * *") == 5
43
+ assert min_gap("0,3 * * * *") == 3
44
+ assert min_gap("0,59 * * * *") == 1 # 10:59 then 11:00
45
+ assert min_gap("59 9 * * *") == float("inf") # once a day; the day fields don't matter
46
+ assert min_gap("58 9,10 * * *") == 60
@@ -8,9 +8,12 @@ from gravity_schema import (
8
8
  InputType,
9
9
  ManifestValidationError,
10
10
  ModelTier,
11
+ load_manifest_file,
11
12
  validate_manifest,
12
13
  )
13
14
  from gravity_schema.contract import MAX_INPUTS, MAX_TOOLS
15
+ from gravity_schema.manifest import ScheduleSpec
16
+ from pydantic import ValidationError
14
17
 
15
18
 
16
19
  def codes(exc: ManifestValidationError) -> set[ErrorCode]:
@@ -122,6 +125,32 @@ def test_valid_semver_accepted(manifest_data, good):
122
125
  assert validate_manifest(manifest_data).version == good
123
126
 
124
127
 
128
+ @pytest.mark.parametrize("bad", [1.0, 1])
129
+ def test_unquoted_version_says_to_quote_it(manifest_data, bad):
130
+ """`version: 1.0` is a YAML float; pydantic's bare 'Input should be a valid string' has no fix."""
131
+ manifest_data["version"] = bad
132
+
133
+ with pytest.raises(ManifestValidationError) as ei:
134
+ validate_manifest(manifest_data)
135
+
136
+ issue = only_issue(ei.value)
137
+ assert issue.code is ErrorCode.INVALID_AGENT_VERSION
138
+ assert issue.location == "version"
139
+ assert 'version: "1.0.0"' in (issue.remediation or "")
140
+
141
+
142
+ def test_unquoted_python_says_to_quote_it(manifest_data):
143
+ manifest_data["runtime"]["python"] = 3.12
144
+
145
+ with pytest.raises(ManifestValidationError) as ei:
146
+ validate_manifest(manifest_data)
147
+
148
+ issue = only_issue(ei.value)
149
+ assert issue.code is ErrorCode.INVALID_PYTHON_VERSION
150
+ assert issue.location == "runtime.python"
151
+ assert 'python: "3.12"' in (issue.remediation or "")
152
+
153
+
125
154
  # Runtime
126
155
 
127
156
 
@@ -575,6 +604,59 @@ def test_invalid_cron_rejected(manifest_data, bad):
575
604
  assert ErrorCode.INVALID_TRIGGER in rejected_with(manifest_data)
576
605
 
577
606
 
607
+ @pytest.mark.parametrize("bad", ["60 * * * *", "0 25 * * *", "0 8 32 * *", "0 8 1 13 *", "0 8 * * 8", "*/0 * * * *", "5-3 * * * *"])
608
+ def test_cron_value_out_of_range_rejected(manifest_data, bad):
609
+ """These passed as '5 fields of digits' but never fire, or the platform refuses them."""
610
+ manifest_data["triggers"] = {"schedule": {"cron": bad}}
611
+
612
+ with pytest.raises(ManifestValidationError) as ei:
613
+ validate_manifest(manifest_data)
614
+
615
+ issue = only_issue(ei.value)
616
+ assert issue.code is ErrorCode.INVALID_TRIGGER
617
+ assert "0-59" in (issue.remediation or "")
618
+
619
+
620
+ def test_cron_restricting_both_day_fields_rejected(manifest_data):
621
+ """Unix cron ORs them; the platform's scheduler cannot."""
622
+ manifest_data["triggers"] = {"schedule": {"cron": "0 9 1 * 1"}}
623
+
624
+ with pytest.raises(ManifestValidationError) as ei:
625
+ validate_manifest(manifest_data)
626
+
627
+ issue = only_issue(ei.value)
628
+ assert issue.code is ErrorCode.INVALID_TRIGGER
629
+ assert "Set one of them to *" in (issue.remediation or "")
630
+
631
+
632
+ @pytest.mark.parametrize("bad", ["* * * * *", "*/2 * * * *", "*/4 * * * *", "0,3 * * * *", "0,59 * * * *", "0,2 9 * * *"])
633
+ def test_cron_faster_than_every_5_minutes_rejected(manifest_data, bad):
634
+ """The control plane's floor (checkScheduleFloor), with its wording, caught before push."""
635
+ manifest_data["triggers"] = {"schedule": {"cron": bad}}
636
+
637
+ with pytest.raises(ManifestValidationError) as ei:
638
+ validate_manifest(manifest_data)
639
+
640
+ issue = only_issue(ei.value)
641
+ assert issue.code is ErrorCode.INVALID_TRIGGER
642
+ assert issue.message == "Schedules can run at most every 5 minutes."
643
+ assert issue.remediation
644
+
645
+
646
+ @pytest.mark.parametrize("good", ["*/5 * * * *", "0,5 9 * * *", "0,30 * * * *", "59 23 * * *", "58 9,10 * * *", "0 9 1 * *", "0 9 * * 7"])
647
+ def test_cron_at_or_above_the_floor_accepted(manifest_data, good):
648
+ manifest_data["triggers"] = {"schedule": {"cron": good}}
649
+ assert validate_manifest(manifest_data).triggers.schedule.cron == good
650
+
651
+
652
+ def test_schedule_spec_alone_applies_the_same_rules():
653
+ """`gravity schedule --cron` validates through ScheduleSpec directly."""
654
+ with pytest.raises(ValidationError) as ei:
655
+ ScheduleSpec(cron="*/2 * * * *")
656
+ assert "Schedules can run at most every 5 minutes." in str(ei.value)
657
+ assert ScheduleSpec(cron="*/5 * * * *").cron == "*/5 * * * *"
658
+
659
+
578
660
  def test_an_agent_nothing_can_start_is_rejected(manifest_data):
579
661
  manifest_data["triggers"] = {"manual": False}
580
662
  assert ErrorCode.INVALID_TRIGGER in rejected_with(manifest_data)
@@ -626,6 +708,30 @@ def test_runtime_python_defaults_to_313(manifest_data):
626
708
  assert validate_manifest(manifest_data).runtime.python == "3.12"
627
709
 
628
710
 
711
+ # Loading from disk (the path every gravity command takes)
712
+
713
+
714
+ def test_utf16_manifest_file_reports_the_encoding(tmp_path):
715
+ """Notepad's 'Unicode' is UTF-16; it must be a problem, not a UnicodeDecodeError traceback."""
716
+ p = tmp_path / "manifest.yaml"
717
+ p.write_bytes("contract: v1\nname: x\n".encode("utf-16"))
718
+
719
+ with pytest.raises(ManifestValidationError) as ei:
720
+ load_manifest_file(p)
721
+
722
+ issue = only_issue(ei.value)
723
+ assert issue.code is ErrorCode.MALFORMED_YAML
724
+ assert issue.location == str(p)
725
+ assert "UTF-8" in (issue.remediation or "")
726
+
727
+
728
+ def test_utf8_bom_manifest_file_still_loads(tmp_path):
729
+ """Notepad's default 'UTF-8 with BOM' is fine: YAML skips the BOM."""
730
+ p = tmp_path / "manifest.yaml"
731
+ p.write_bytes(b"\xef\xbb\xbf" + b"name: x\nruntime:\n entrypoint: src/agent.py:graph\n")
732
+ assert load_manifest_file(p).name == "x"
733
+
734
+
629
735
  def test_a_secret_input_cannot_have_a_default(manifest_data):
630
736
  manifest_data["inputs"] = [{"name": "api_key", "type": "secret", "label": "Key", "default": "sk-x"}]
631
737
  with pytest.raises(ManifestValidationError) as e:
File without changes
File without changes
File without changes