gravity-cli 0.10.0__tar.gz → 0.11.1__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 (285) hide show
  1. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/AGENT_STANDARD.md +1 -1
  2. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/AGENT_STANDARD_V2.md +1 -1
  3. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/PKG-INFO +4 -4
  4. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/README.md +2 -2
  5. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/catalog.yaml +45 -0
  6. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/contract.py +2 -0
  7. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/manifest.py +15 -3
  8. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/manifest_v2.py +2 -0
  9. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/rules.py +25 -0
  10. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/rules_cases.json +19 -2
  11. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/specs.py +52 -8
  12. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/pyproject.toml +1 -1
  13. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/schema/manifest.schema.json +76 -3
  14. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_manifest.py +8 -0
  15. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_rules.py +39 -0
  16. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/pyproject.toml +2 -2
  17. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/BUILDER_GUIDE.md +2 -0
  18. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/BUILDER_GUIDE_V2.md +1 -1
  19. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/CHANGELOG.md +18 -0
  20. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/_runner_script.py +26 -1
  21. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/connect.py +5 -3
  22. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/run.py +13 -7
  23. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/dev.py +2 -2
  24. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/dev_page/index.html +11 -5
  25. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/docs/Gravity.agent.md +49 -9
  26. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/inputs.py +13 -2
  27. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/timeline.py +17 -1
  28. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_connect.py +1 -1
  29. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_connect_v2.py +25 -1
  30. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_dev_v2.py +9 -0
  31. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_inputs_memory.py +18 -0
  32. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_templates.py +27 -0
  33. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_timeline.py +17 -0
  34. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/uv.lock +6 -6
  35. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/.env.example +0 -0
  36. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/.github/workflows/ci.yml +0 -0
  37. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/.github/workflows/composio-watch.yml +0 -0
  38. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/.github/workflows/release.yml +0 -0
  39. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/.gitignore +0 -0
  40. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/LICENSE +0 -0
  41. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/NOTICE +0 -0
  42. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/RELEASING.md +0 -0
  43. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/TEMPLATES.md +0 -0
  44. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/LICENSE +0 -0
  45. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/NOTICE +0 -0
  46. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/README.md +0 -0
  47. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
  48. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
  49. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/built_ins.py +0 -0
  50. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
  51. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +0 -0
  52. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/cron.py +0 -0
  53. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/describe.py +0 -0
  54. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/errors.py +0 -0
  55. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/export.py +0 -0
  56. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
  57. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/gen_vocabulary.py +0 -0
  58. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/instance.py +0 -0
  59. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
  60. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/validation.py +0 -0
  61. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/__init__.py +0 -0
  62. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/__main__.py +0 -0
  63. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/capabilities.yaml +0 -0
  64. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/airtable.yaml +0 -0
  65. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/asana.yaml +0 -0
  66. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/browser_tool.yaml +0 -0
  67. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/composio_search.yaml +0 -0
  68. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/dropbox.yaml +0 -0
  69. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/exa.yaml +0 -0
  70. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/excel.yaml +0 -0
  71. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/firecrawl.yaml +0 -0
  72. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/github.yaml +0 -0
  73. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/gmail.yaml +0 -0
  74. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googlecalendar.yaml +0 -0
  75. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googledocs.yaml +0 -0
  76. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googledrive.yaml +0 -0
  77. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googlesheets.yaml +0 -0
  78. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googleslides.yaml +0 -0
  79. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/googletasks.yaml +0 -0
  80. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/hackernews.yaml +0 -0
  81. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/hubspot.yaml +0 -0
  82. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/instagram.yaml +0 -0
  83. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/jira.yaml +0 -0
  84. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/linear.yaml +0 -0
  85. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/linkedin.yaml +0 -0
  86. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/microsoft_teams.yaml +0 -0
  87. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/notion.yaml +0 -0
  88. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/outlook.yaml +0 -0
  89. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/perplexityai.yaml +0 -0
  90. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/pinterest.yaml +0 -0
  91. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/reddit.yaml +0 -0
  92. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/slack.yaml +0 -0
  93. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/tavily.yaml +0 -0
  94. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/connectors/youtube.yaml +0 -0
  95. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/core_shapes.yaml +0 -0
  96. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/generated_shapes.yaml +0 -0
  97. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/policy.yaml +0 -0
  98. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/airtable.json +0 -0
  99. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/asana.json +0 -0
  100. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/browser_tool.json +0 -0
  101. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/composio_search.json +0 -0
  102. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/dropbox.json +0 -0
  103. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/exa.json +0 -0
  104. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/excel.json +0 -0
  105. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/firecrawl.json +0 -0
  106. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/github.json +0 -0
  107. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/gmail.json +0 -0
  108. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googlecalendar.json +0 -0
  109. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googledocs.json +0 -0
  110. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googledrive.json +0 -0
  111. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googlesheets.json +0 -0
  112. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googleslides.json +0 -0
  113. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/googletasks.json +0 -0
  114. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/hackernews.json +0 -0
  115. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/hubspot.json +0 -0
  116. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/instagram.json +0 -0
  117. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/jira.json +0 -0
  118. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/linear.json +0 -0
  119. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/linkedin.json +0 -0
  120. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/microsoft_teams.json +0 -0
  121. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/notion.json +0 -0
  122. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/outlook.json +0 -0
  123. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/perplexityai.json +0 -0
  124. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/pinterest.json +0 -0
  125. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/reddit.json +0 -0
  126. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/slack.json +0 -0
  127. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/tavily.json +0 -0
  128. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas/youtube.json +0 -0
  129. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/schemas.py +0 -0
  130. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/ochat.yaml +0 -0
  131. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/ocrm.yaml +0 -0
  132. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/ogh.yaml +0 -0
  133. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/oms.yaml +0 -0
  134. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/oweb.yaml +0 -0
  135. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/owork.yaml +0 -0
  136. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/shapes.d/shared.yaml +0 -0
  137. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/sync.py +0 -0
  138. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/ochat.yaml +0 -0
  139. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/ocrm.yaml +0 -0
  140. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/ogh.yaml +0 -0
  141. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/oms.yaml +0 -0
  142. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/oweb.yaml +0 -0
  143. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/owork.yaml +0 -0
  144. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/rchat.yaml +0 -0
  145. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/rfiles.yaml +0 -0
  146. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/rgh.yaml +0 -0
  147. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/rtrack.yaml +0 -0
  148. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.d/rwork.yaml +0 -0
  149. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/gravity_schema/vocabulary/verbs.yaml +0 -0
  150. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/conftest.py +0 -0
  151. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/fixtures/demo_manifests.yaml +0 -0
  152. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_accounts.py +0 -0
  153. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_actions_memory.py +0 -0
  154. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
  155. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_cron.py +0 -0
  156. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_describe.py +0 -0
  157. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_errors.py +0 -0
  158. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_generated_tables.py +0 -0
  159. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_instance.py +0 -0
  160. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_json_schema.py +0 -0
  161. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
  162. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_manifest_v2.py +0 -0
  163. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_place_maker.py +0 -0
  164. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_slots.py +0 -0
  165. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_source_scan.py +0 -0
  166. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_sync.py +0 -0
  167. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_validation.py +0 -0
  168. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/packages/gravity-schema/tests/test_vocabulary.py +0 -0
  169. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/scripts/composio-watch.sh +0 -0
  170. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/__init__.py +0 -0
  171. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/__main__.py +0 -0
  172. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/api.py +0 -0
  173. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/changelog.py +0 -0
  174. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/cli.py +0 -0
  175. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/__init__.py +0 -0
  176. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/adapt.py +0 -0
  177. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/build.py +0 -0
  178. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/capabilities.py +0 -0
  179. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/changelog.py +0 -0
  180. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/check.py +0 -0
  181. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/dev.py +0 -0
  182. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/docs.py +0 -0
  183. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/guide.py +0 -0
  184. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/help.py +0 -0
  185. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/init.py +0 -0
  186. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/login.py +0 -0
  187. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/memory.py +0 -0
  188. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/push.py +0 -0
  189. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/runs.py +0 -0
  190. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/schedule.py +0 -0
  191. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/tools.py +0 -0
  192. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/update.py +0 -0
  193. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/commands/validate.py +0 -0
  194. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/config.py +0 -0
  195. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/docs/Gravity.getting-started.md +0 -0
  196. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/docs/problem-to-agent.md +0 -0
  197. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/localrun.py +0 -0
  198. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/manifest.py +0 -0
  199. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/project.py +0 -0
  200. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/chat/.gitignore +0 -0
  201. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
  202. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/chat/requirements.txt +0 -0
  203. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/chat/src/agent.py +0 -0
  204. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
  205. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
  206. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
  207. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
  208. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/minimal/.gitignore +0 -0
  209. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
  210. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
  211. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
  212. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/.gitignore +0 -0
  213. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
  214. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/requirements.txt +0 -0
  215. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/src/agent.py +0 -0
  216. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
  217. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
  218. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
  219. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/structured/src/state.py +0 -0
  220. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/chat/.gitignore +0 -0
  221. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/chat/manifest.yaml +0 -0
  222. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/chat/requirements.txt +0 -0
  223. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/chat/src/agent.py +0 -0
  224. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/chat/tests/test_agent.py +0 -0
  225. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/deepagent/.gitignore +0 -0
  226. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/deepagent/manifest.yaml +0 -0
  227. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/deepagent/requirements.txt +0 -0
  228. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/deepagent/src/agent.py +0 -0
  229. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/deepagent/tests/test_agent.py +0 -0
  230. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/minimal/.gitignore +0 -0
  231. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/minimal/manifest.yaml +0 -0
  232. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/minimal/requirements.txt +0 -0
  233. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/minimal/src/agent.py +0 -0
  234. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/minimal/tests/test_agent.py +0 -0
  235. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/.gitignore +0 -0
  236. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/manifest.yaml +0 -0
  237. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/requirements.txt +0 -0
  238. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/__init__.py +0 -0
  239. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/agent.py +0 -0
  240. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/gateway.py +0 -0
  241. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/nodes/__init__.py +0 -0
  242. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/nodes/agent.py +0 -0
  243. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/prompts.py +0 -0
  244. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/src/state.py +0 -0
  245. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/templates/v2/structured/tests/test_agent.py +0 -0
  246. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/ui.py +0 -0
  247. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/updater.py +0 -0
  248. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/src/gravity_cli/workspace.py +0 -0
  249. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter/manifest.yaml +0 -0
  250. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter/requirements.txt +0 -0
  251. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter/src/agent.py +0 -0
  252. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter/src/summary.py +0 -0
  253. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter/tests/test_summary.py +0 -0
  254. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter-v2/manifest.yaml +0 -0
  255. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter-v2/requirements.txt +0 -0
  256. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter-v2/src/agent.py +0 -0
  257. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter-v2/src/summary.py +0 -0
  258. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/starter-v2/tests/test_summary.py +0 -0
  259. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/conftest.py +0 -0
  260. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_adapt.py +0 -0
  261. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_agent_guide.py +0 -0
  262. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_agents.py +0 -0
  263. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_api.py +0 -0
  264. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_build_push.py +0 -0
  265. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_changelog.py +0 -0
  266. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_check.py +0 -0
  267. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_dev.py +0 -0
  268. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_gateway_token.py +0 -0
  269. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_localrun.py +0 -0
  270. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_manifest_integration.py +0 -0
  271. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_run.py +0 -0
  272. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_run_v2.py +0 -0
  273. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_runs.py +0 -0
  274. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_schedule.py +0 -0
  275. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_schedule_v2.py +0 -0
  276. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_session.py +0 -0
  277. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_templates_v2.py +0 -0
  278. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_tools.py +0 -0
  279. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_ui.py +0 -0
  280. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_updater.py +0 -0
  281. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_v2_cli.py +0 -0
  282. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_validate.py +0 -0
  283. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_validate_v2.py +0 -0
  284. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_welcome.py +0 -0
  285. {gravity_cli-0.10.0 → gravity_cli-0.11.1}/tests/test_workspace.py +0 -0
@@ -348,7 +348,7 @@ to one address, with the output's content in the message body and no
348
348
  attachment; a thread sends at most 200 a day. The platform sends it, not the
349
349
  agent, so the agent needs no Gmail tool and no approval for it.
350
350
 
351
- Renderers: `markdown`, `docx`, `pptx`, `rows` (the first table, as CSV). Each
351
+ Renderers: `markdown`, `docx`, `pptx`, `pdf`, `rows` (the first table, as CSV), `xlsx` (every table, a sheet each). Each
352
352
  consumer picks outputs and destinations per run. The agent writes one
353
353
  **corpus** into its final state, and every output is rendered from it:
354
354
 
@@ -384,7 +384,7 @@ record tagged with its app in `extra.app`. The platform delivers it after the
384
384
  run, not the agent, so the agent declares no write for it; an email goes at
385
385
  most once per run and a thread sends at most 200 a day, extra places included.
386
386
 
387
- Renderers: `markdown`, `docx`, `pptx`, `rows` (the first table, as CSV). Each
387
+ Renderers: `markdown`, `docx`, `pptx`, `pdf`, `rows` (the first table, as CSV), `xlsx` (every table, a sheet each). Each
388
388
  consumer picks outputs and destinations per run. The agent writes one
389
389
  **corpus** into its final state, and every output is rendered from it:
390
390
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gravity-cli
3
- Version: 0.10.0
3
+ Version: 0.11.1
4
4
  Summary: gravity cli — init, run, and push python agents to the Gravity marketplace
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
7
7
  License-File: NOTICE
8
8
  Requires-Python: >=3.12
9
- Requires-Dist: gravity-schema==0.10.0
9
+ Requires-Dist: gravity-schema==0.11.1
10
10
  Requires-Dist: httpx>=0.27.0
11
11
  Requires-Dist: keyring>=25.0.0
12
12
  Requires-Dist: python-dotenv>=1.0.0
@@ -720,7 +720,7 @@ slots: # a part the consumer chooses; each adapter ge
720
720
 
721
721
  outputs: # extra formats the platform renders and delivers itself
722
722
  report:
723
- renderer: markdown # markdown | docx | pptx | rows
723
+ renderer: markdown # markdown | docx | pptx | pdf | rows | xlsx
724
724
  label: "Digest as a document"
725
725
  destinations: [download]
726
726
  ```
@@ -752,7 +752,7 @@ outputs: # extra formats the platform renders and deliv
752
752
  | `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
753
753
  | `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
754
754
  | `slots.<name>` | no | none | A swappable part: `label`, `default` adapter, and `adapters` mapping each adapter name to the tools it may call. Code lives in `src/adapters/<slot>/<adapter>.py`. Slot names cannot repeat an input's name. |
755
- | `outputs.<name>` | no | none | Up to 8 extra formats rendered from the result: `renderer` (`markdown`, `docx`, `pptx` or `rows`), `label` and `destinations` (`download`, or a catalog tool such as `gmail.send`). |
755
+ | `outputs.<name>` | no | none | Up to 8 extra formats rendered from the result: `renderer` (`markdown`, `docx`, `pptx`, `pdf`, `rows` or `xlsx`), `label` and `destinations` (`download`, or a catalog tool such as `gmail.send`). |
756
756
 
757
757
  Unknown fields are rejected rather than ignored, so a typo is reported instead of silently dropped.
758
758
 
@@ -703,7 +703,7 @@ slots: # a part the consumer chooses; each adapter ge
703
703
 
704
704
  outputs: # extra formats the platform renders and delivers itself
705
705
  report:
706
- renderer: markdown # markdown | docx | pptx | rows
706
+ renderer: markdown # markdown | docx | pptx | pdf | rows | xlsx
707
707
  label: "Digest as a document"
708
708
  destinations: [download]
709
709
  ```
@@ -735,7 +735,7 @@ outputs: # extra formats the platform renders and deliv
735
735
  | `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
736
736
  | `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
737
737
  | `slots.<name>` | no | none | A swappable part: `label`, `default` adapter, and `adapters` mapping each adapter name to the tools it may call. Code lives in `src/adapters/<slot>/<adapter>.py`. Slot names cannot repeat an input's name. |
738
- | `outputs.<name>` | no | none | Up to 8 extra formats rendered from the result: `renderer` (`markdown`, `docx`, `pptx` or `rows`), `label` and `destinations` (`download`, or a catalog tool such as `gmail.send`). |
738
+ | `outputs.<name>` | no | none | Up to 8 extra formats rendered from the result: `renderer` (`markdown`, `docx`, `pptx`, `pdf`, `rows` or `xlsx`), `label` and `destinations` (`download`, or a catalog tool such as `gmail.send`). |
739
739
 
740
740
  Unknown fields are rejected rather than ignored, so a typo is reported instead of silently dropped.
741
741
 
@@ -74,6 +74,15 @@
74
74
  guarantee: certified
75
75
  side_effect: read
76
76
 
77
+ - name: treg.email_find
78
+ slug: null
79
+ toolkit: null
80
+ description: find and verify a person's work email from their LinkedIn URL, or a full name plus the company's email domain; served by the gateway through the team's lookup account (a fraction of a cent a call), capped per call and verified in the same call
81
+ scopes: []
82
+ breadth: generic
83
+ guarantee: certified
84
+ side_effect: read
85
+
77
86
  - name: browser.act
78
87
  slug: null
79
88
  toolkit: null
@@ -155,6 +164,42 @@
155
164
  guarantee: certified
156
165
  side_effect: write
157
166
 
167
+ - name: sheets.create
168
+ slug: GOOGLESHEETS_CREATE_GOOGLE_SHEET1
169
+ toolkit: googlesheets
170
+ description: create a new Google Sheet in the connected Google account
171
+ scopes: [sheets]
172
+ breadth: generic
173
+ guarantee: certified
174
+ side_effect: write
175
+
176
+ - name: sheets.add_tab
177
+ slug: GOOGLESHEETS_ADD_SHEET
178
+ toolkit: googlesheets
179
+ description: add a tab to a Google Sheet in the connected Google account
180
+ scopes: [sheets]
181
+ breadth: generic
182
+ guarantee: certified
183
+ side_effect: write
184
+
185
+ - name: docs.read
186
+ slug: GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT
187
+ toolkit: googledocs
188
+ description: read a Google Doc in the connected Google account as plain text (document_id, from a google-doc input)
189
+ scopes: [docs]
190
+ breadth: generic
191
+ guarantee: certified
192
+ side_effect: read
193
+
194
+ - name: docs.create
195
+ slug: GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN
196
+ toolkit: googledocs
197
+ description: create a Google Doc from markdown in the connected Google account
198
+ scopes: [docs]
199
+ breadth: generic
200
+ guarantee: certified
201
+ side_effect: write
202
+
158
203
  - name: slack.send
159
204
  slug: SLACK_SEND_MESSAGE
160
205
  toolkit: slack
@@ -106,6 +106,8 @@ MAX_INPUTS: Final[int] = 50
106
106
  MAX_TOOLS: Final[int] = 50
107
107
  MAX_OPTIONS: Final[int] = 100
108
108
  MAX_FILE_EXTENSIONS: Final[int] = 20
109
+ # A file input with `max` (several files): the most it may ask for. Each upload is up to 25 MB.
110
+ MAX_FILES: Final[int] = 20
109
111
  MAX_NAME_LENGTH: Final[int] = 64
110
112
  MAX_ENTRYPOINT_LENGTH: Final[int] = 256
111
113
  MAX_LABEL_LENGTH: Final[int] = 200
@@ -319,11 +319,23 @@ class Manifest(BaseModel):
319
319
 
320
320
  @model_validator(mode="after")
321
321
  def _check_monitor_has_a_trigger(self) -> Manifest:
322
- if self.kind is AgentKind.MONITOR and self.triggers.schedule is None and not self.triggers.webhook:
322
+ if (self.kind is AgentKind.MONITOR and self.triggers.schedule is None and not self.triggers.webhook
323
+ and self.triggers.email is None):
323
324
  raise PydanticCustomError(
324
325
  ErrorCode.INVALID_KIND.value,
325
- "A monitor needs a schedule or a webhook trigger.",
326
- {"remediation": "Add triggers.schedule or triggers.webhook, or use kind: interactive."},
326
+ "A monitor needs a schedule, an email or a webhook trigger.",
327
+ {"remediation": "Add triggers.schedule, triggers.email or triggers.webhook, or use kind: interactive."},
328
+ )
329
+ return self
330
+
331
+ @model_validator(mode="after")
332
+ def _check_email_trigger_reads_gmail(self) -> Manifest:
333
+ # Gravity reads the new mail with the person's Gmail, under the agent's own permission.
334
+ if self.triggers.email is not None and "gmail.read" not in self.permissions.tools:
335
+ raise PydanticCustomError(
336
+ ErrorCode.INVALID_TRIGGER.value,
337
+ "triggers.email watches Gmail, but the agent does not declare gmail.read.",
338
+ {"remediation": "Add gmail.read to permissions.tools."},
327
339
  )
328
340
  return self
329
341
 
@@ -53,7 +53,9 @@ RENDERER_DESTINATIONS: dict[Renderer, tuple[str, ...]] = {
53
53
  "post.create", "file.create"),
54
54
  Renderer.DOCX: ("file.create", "document.create", "message.send"),
55
55
  Renderer.PPTX: ("file.create", "presentation.create", "message.send"),
56
+ Renderer.PDF: ("file.create", "message.send"),
56
57
  Renderer.ROWS: ("row.append", "file.create"),
58
+ Renderer.XLSX: ("file.create",),
57
59
  }
58
60
 
59
61
  # A filter on one of these fields takes one of the shape's listed values.
@@ -130,6 +130,21 @@ def _is_day(value: Any) -> bool:
130
130
  return True
131
131
 
132
132
 
133
+ # The file id in a Google Doc or Sheet link, or a bare id (Google's are 25 or more of these characters).
134
+ _GOOGLE_PATH = {"google-doc": "document", "google-sheet": "spreadsheets"}
135
+ _GOOGLE_ID = re.compile(r"^[A-Za-z0-9_-]{25,}$")
136
+
137
+
138
+ def google_file_id(kind: str, value: Any) -> str | None:
139
+ """'https://docs.google.com/spreadsheets/d/<id>/edit#gid=0' -> '<id>' for a google-sheet input (document for a
140
+ google-doc one); a bare id as it is; None for anything else, a Sheet's link in a Doc input included."""
141
+ text = value.strip() if isinstance(value, str) else ""
142
+ if _GOOGLE_ID.match(text):
143
+ return text
144
+ m = re.search(rf"docs\.google\.com/{_GOOGLE_PATH.get(kind, '-')}/d/([A-Za-z0-9_-]{{20,}})", text)
145
+ return m.group(1) if m else None
146
+
147
+
133
148
  def _bounds(spec: InputSpec, value: Any) -> str | None:
134
149
  label = spec.label or spec.name
135
150
  if spec.many_days and not _blank(value):
@@ -140,6 +155,14 @@ def _bounds(spec: InputSpec, value: Any) -> str | None:
140
155
  if spec.max is not None and len(value) > spec.max:
141
156
  return f'"{label}": pick at most {spec.max} days.'
142
157
  return None
158
+ if spec.many_files and not _blank(value):
159
+ if not isinstance(value, list) or not all(isinstance(v, str) and v.strip() for v in value):
160
+ return f'"{label}": upload the files again.'
161
+ if spec.min is not None and len(value) < spec.min:
162
+ return f'"{label}": upload at least {spec.min} files.'
163
+ if spec.max is not None and len(value) > spec.max:
164
+ return f'"{label}": upload at most {spec.max} files.'
165
+ return None
143
166
  if spec.type is InputType.MULTI_SELECT and not _blank(value):
144
167
  if not isinstance(value, list) or any(v not in (spec.options or []) for v in value):
145
168
  return f'"{label}": pick from {", ".join(spec.options or [])}.'
@@ -166,6 +189,8 @@ def _check(inputs: list[InputSpec], shown: list[str], answers: dict[str, Any]) -
166
189
  out.append((spec.name, f'"{spec.label or spec.name}": pick at least {spec.min}.'))
167
190
  elif spec.many_days and spec.min and _blank(value):
168
191
  out.append((spec.name, f'"{spec.label or spec.name}": pick at least {spec.min} days.'))
192
+ elif spec.many_files and spec.min and _blank(value):
193
+ out.append((spec.name, f'"{spec.label or spec.name}": upload at least {spec.min} files.'))
169
194
  elif (problem := _bounds(spec, value)) is not None:
170
195
  out.append((spec.name, problem))
171
196
  return out
@@ -12,7 +12,8 @@
12
12
  {"name": "contact_source", "type": "static-dropdown", "label": "Contacts from", "options": ["gravity", "hunter", "both"], "default": "gravity"},
13
13
  {"name": "hunter_key", "type": "secret", "label": "Your Hunter API key", "required": true, "show_if": {"contact_source": ["hunter", "both"]}},
14
14
  {"name": "goal", "type": "action-choice", "label": "What do you want to do first?", "actions": ["find_companies", "write_emails"]},
15
- {"name": "call_days", "type": "date", "label": "Days for a call", "min": 1, "max": 3, "show_if": {"goal": "write_emails"}}
15
+ {"name": "call_days", "type": "date", "label": "Days for a call", "min": 1, "max": 3, "show_if": {"goal": "write_emails"}},
16
+ {"name": "past_pitches", "type": "file", "label": "Past pitch decks", "accept": ["pdf"], "min": 1, "max": 2, "show_if": {"goal": "find_companies"}}
16
17
  ],
17
18
  "slots": {
18
19
  "deliver": {"label": "Deliver to", "default": "gravity", "adapters": {"gravity": [], "gmail": ["gmail.draft"]}},
@@ -79,6 +80,22 @@
79
80
  {"name": "min days asks for at least one",
80
81
  "answers": {"resume_text": "cv", "platforms": ["x"], "goal": "write_emails"},
81
82
  "visible": ["resume_text", "platforms", "contact_source", "goal", "call_days"],
82
- "problems": ["call_days"]}
83
+ "problems": ["call_days"]},
84
+ {"name": "a file with max takes a list of links",
85
+ "answers": {"resume_text": "cv", "platforms": ["x"], "goal": "find_companies", "past_pitches": ["https://x.test/uploads/a.pdf", "https://x.test/uploads/b.pdf"]},
86
+ "visible": ["resume_text", "platforms", "contact_source", "goal", "past_pitches"],
87
+ "problems": []},
88
+ {"name": "no more files than max",
89
+ "answers": {"resume_text": "cv", "platforms": ["x"], "goal": "find_companies", "past_pitches": ["https://x.test/a.pdf", "https://x.test/b.pdf", "https://x.test/c.pdf"]},
90
+ "visible": ["resume_text", "platforms", "contact_source", "goal", "past_pitches"],
91
+ "problems": ["past_pitches"]},
92
+ {"name": "several files are a list, not one link",
93
+ "answers": {"resume_text": "cv", "platforms": ["x"], "goal": "find_companies", "past_pitches": "https://x.test/a.pdf"},
94
+ "visible": ["resume_text", "platforms", "contact_source", "goal", "past_pitches"],
95
+ "problems": ["past_pitches"]},
96
+ {"name": "min files asks for at least one",
97
+ "answers": {"resume_text": "cv", "platforms": ["x"], "goal": "find_companies"},
98
+ "visible": ["resume_text", "platforms", "contact_source", "goal", "past_pitches"],
99
+ "problems": ["past_pitches"]}
83
100
  ]
84
101
  }
@@ -35,12 +35,16 @@ class InputType(str, Enum):
35
35
  # An API key or password. The platform seals it with KMS when it is submitted, keeps it
36
36
  # out of every API response and log, and opens it only to hand it to the running agent.
37
37
  SECRET = "secret"
38
- # The agent gets a short-lived URL it fetches through the gateway.
38
+ # The agent gets a short-lived URL it fetches through the gateway; with `max`, a list of them (several files).
39
39
  FILE = "file"
40
40
  # Several of `options`; the value is a list.
41
41
  MULTI_SELECT = "multi-select"
42
42
  # One of this agent's declared `actions`; the chosen action runs (rules.py).
43
43
  ACTION_CHOICE = "action-choice"
44
+ # A Google Doc or Sheet of the person's: they paste its link, the agent gets the file id (rules.google_file_id),
45
+ # which is what docs.read and sheets.read take.
46
+ GOOGLE_DOC = "google-doc"
47
+ GOOGLE_SHEET = "google-sheet"
44
48
 
45
49
 
46
50
  # Which Python types each input's `default` may be.
@@ -57,6 +61,8 @@ _PYTHON_TYPES: dict[InputType, tuple[type, ...]] = {
57
61
  InputType.FILE: (), # never has a default
58
62
  InputType.MULTI_SELECT: (list,),
59
63
  InputType.ACTION_CHOICE: (str,),
64
+ InputType.GOOGLE_DOC: (str,),
65
+ InputType.GOOGLE_SHEET: (str,),
60
66
  }
61
67
 
62
68
  # A `show_if` value: what the named input or slot must be for this question to be asked.
@@ -86,7 +92,9 @@ class Renderer(str, Enum):
86
92
  MARKDOWN = "markdown"
87
93
  DOCX = "docx"
88
94
  PPTX = "pptx"
89
- ROWS = "rows"
95
+ PDF = "pdf"
96
+ ROWS = "rows" # CSV of the first table
97
+ XLSX = "xlsx" # every table a sheet
90
98
 
91
99
 
92
100
  class WebhookSource(str, Enum):
@@ -205,7 +213,7 @@ class InputSpec(BaseModel):
205
213
  # Asked only when every named earlier input or slot has one of the given values (rules.py).
206
214
  show_if: Annotated[ShowIf | None, Field(max_length=C.MAX_INPUTS)] = None
207
215
  # multi-select: how many options may be picked. date: how many days; with `max` the answer is a list of
208
- # "YYYY-MM-DD" days, picked on a calendar.
216
+ # "YYYY-MM-DD" days, picked on a calendar. file: how many files; with `max` the answer is a list of links.
209
217
  min: Annotated[int | None, Field(ge=0, le=C.MAX_OPTIONS)] = None
210
218
  max: Annotated[int | None, Field(ge=1, le=C.MAX_OPTIONS)] = None
211
219
  # action-choice only: the declared actions offered, in order.
@@ -259,14 +267,27 @@ class InputSpec(BaseModel):
259
267
  """A date input that takes several days: its answer is a list of "YYYY-MM-DD"."""
260
268
  return self.type is InputType.DATE and self.max is not None
261
269
 
270
+ @property
271
+ def many_files(self) -> bool:
272
+ """A file input that takes several files: its answer is a list of links, one per file."""
273
+ return self.type is InputType.FILE and self.max is not None
274
+
262
275
  @model_validator(mode="after")
263
276
  def _check_choice_fields(self) -> InputSpec:
264
277
  multi = self.type is InputType.MULTI_SELECT
265
- if (self.min is not None or self.max is not None) and not (multi or self.type is InputType.DATE):
278
+ if (self.min is not None or self.max is not None) and not (multi or self.type in (InputType.DATE, InputType.FILE)):
266
279
  raise PydanticCustomError(
267
280
  ErrorCode.INVALID_INPUT.value,
268
- "Input '{value}' sets min or max but is not a multi-select or a date.",
269
- {"value": self.name, "remediation": "Set type to 'multi-select' or 'date', or remove min and max."},
281
+ "Input '{value}' sets min or max but is not a multi-select, a date or a file.",
282
+ {"value": self.name, "remediation": "Set type to 'multi-select', 'date' or 'file', or remove min and max."},
283
+ )
284
+ file_bounds = self.type is InputType.FILE and (self.min is not None or self.max is not None)
285
+ if file_bounds and (self.max is None or (self.min or 0) > self.max or self.max > C.MAX_FILES):
286
+ raise PydanticCustomError(
287
+ ErrorCode.INVALID_INPUT.value,
288
+ "File input '{value}' asks for between {lo} and {hi} files.",
289
+ {"value": self.name, "lo": self.min or 0, "hi": self.max,
290
+ "remediation": f"Set `max` (how many files may be uploaded, at most {C.MAX_FILES}), and keep min <= max."},
270
291
  )
271
292
  days_bounds = self.type is InputType.DATE and (self.min is not None or self.max is not None)
272
293
  if days_bounds and (self.max is None or (self.min or 0) > self.max):
@@ -465,6 +486,16 @@ class ScheduleSpec(BaseModel):
465
486
  return v
466
487
 
467
488
 
489
+ class EmailTriggerSpec(BaseModel):
490
+ """Run when new mail matching a Gmail search arrives in the person's inbox. Gravity checks every 5 minutes and
491
+ starts a run only when there is new mail, with the messages in inputs["event"]["emails"]."""
492
+
493
+ model_config = _STRICT
494
+
495
+ query: Annotated[str, Field(min_length=1, max_length=500)] # a Gmail search: "from:recruiting@acme.com is:unread"
496
+ consumer_can_change: bool = True
497
+
498
+
468
499
  class TriggersSpec(BaseModel):
469
500
  """What starts a run. Declared so the consumer sees it before installing."""
470
501
 
@@ -473,14 +504,15 @@ class TriggersSpec(BaseModel):
473
504
  manual: bool = True
474
505
  schedule: ScheduleSpec | None = None
475
506
  webhook: list[WebhookSource] = Field(default_factory=list)
507
+ email: EmailTriggerSpec | None = None
476
508
 
477
509
  @model_validator(mode="after")
478
510
  def _check_something_starts_it(self) -> TriggersSpec:
479
- if not self.manual and self.schedule is None and not self.webhook:
511
+ if not self.manual and self.schedule is None and not self.webhook and self.email is None:
480
512
  raise PydanticCustomError(
481
513
  ErrorCode.INVALID_TRIGGER.value,
482
514
  "Nothing can start this agent.",
483
- {"remediation": "Set manual: true, or add a schedule or a webhook."},
515
+ {"remediation": "Set manual: true, or add a schedule, an email trigger or a webhook."},
484
516
  )
485
517
  return self
486
518
 
@@ -550,6 +582,15 @@ class ChartHint(BaseModel):
550
582
  )
551
583
 
552
584
 
585
+ class TabHint(BaseModel):
586
+ """One tab of the result, as the agent's page in Explore lists it: its name and what is on it."""
587
+
588
+ model_config = _HINT
589
+
590
+ name: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)]
591
+ about: Annotated[str, Field(max_length=C.MAX_LABEL_LENGTH)] | None = None
592
+
593
+
553
594
  class DisplaySpec(BaseModel):
554
595
  """How the run's result shows on the page, when the builder wants to say so.
555
596
 
@@ -560,6 +601,8 @@ class DisplaySpec(BaseModel):
560
601
  view: document | report | table | chart | code | files
561
602
  field: the result key that is the main thing to show
562
603
  chart: {type: line | bar, x: date, y: [reach, likes]}
604
+ tabs: [{name: Overview, about: "Your best options and what they cost"}], the
605
+ result's tabs in order, listed on the agent's page before anyone runs it
563
606
  """
564
607
 
565
608
  model_config = _HINT
@@ -567,6 +610,7 @@ class DisplaySpec(BaseModel):
567
610
  view: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None
568
611
  field: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None
569
612
  chart: ChartHint | None = None
613
+ tabs: Annotated[list[TabHint], Field(max_length=12)] = Field(default_factory=list)
570
614
 
571
615
 
572
616
  class ChooseOneSpec(BaseModel):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gravity-schema"
3
- version = "0.10.0"
3
+ version = "0.11.1"
4
4
  description = "Gravity agent manifest contract — models, validation, and JSON Schema export"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -301,7 +301,7 @@
301
301
  "type": "object"
302
302
  },
303
303
  "DisplaySpec": {
304
- "description": "How the run's result shows on the page, when the builder wants to say so.\n\nA hint, not a rule: without it the page reads the result's own shape, and a\nvalue the page doesn't know (a typo, a newer view) is ignored the same way,\nso it never fails a push or a run.\n\n view: document | report | table | chart | code | files\n field: the result key that is the main thing to show\n chart: {type: line | bar, x: date, y: [reach, likes]}",
304
+ "description": "How the run's result shows on the page, when the builder wants to say so.\n\nA hint, not a rule: without it the page reads the result's own shape, and a\nvalue the page doesn't know (a typo, a newer view) is ignored the same way,\nso it never fails a push or a run.\n\n view: document | report | table | chart | code | files\n field: the result key that is the main thing to show\n chart: {type: line | bar, x: date, y: [reach, likes]}\n tabs: [{name: Overview, about: \"Your best options and what they cost\"}], the\n result's tabs in order, listed on the agent's page before anyone runs it",
305
305
  "properties": {
306
306
  "chart": {
307
307
  "anyOf": [
@@ -327,6 +327,14 @@
327
327
  "default": null,
328
328
  "title": "Field"
329
329
  },
330
+ "tabs": {
331
+ "items": {
332
+ "$ref": "#/$defs/TabHint"
333
+ },
334
+ "maxItems": 12,
335
+ "title": "Tabs",
336
+ "type": "array"
337
+ },
330
338
  "view": {
331
339
  "anyOf": [
332
340
  {
@@ -344,6 +352,28 @@
344
352
  "title": "DisplaySpec",
345
353
  "type": "object"
346
354
  },
355
+ "EmailTriggerSpec": {
356
+ "additionalProperties": false,
357
+ "description": "Run when new mail matching a Gmail search arrives in the person's inbox. Gravity checks every 5 minutes and\nstarts a run only when there is new mail, with the messages in inputs[\"event\"][\"emails\"].",
358
+ "properties": {
359
+ "consumer_can_change": {
360
+ "default": true,
361
+ "title": "Consumer Can Change",
362
+ "type": "boolean"
363
+ },
364
+ "query": {
365
+ "maxLength": 500,
366
+ "minLength": 1,
367
+ "title": "Query",
368
+ "type": "string"
369
+ }
370
+ },
371
+ "required": [
372
+ "query"
373
+ ],
374
+ "title": "EmailTriggerSpec",
375
+ "type": "object"
376
+ },
347
377
  "InputSpec": {
348
378
  "additionalProperties": false,
349
379
  "description": "One value the consumer supplies. Renders as one form field.",
@@ -523,7 +553,9 @@
523
553
  "secret",
524
554
  "file",
525
555
  "multi-select",
526
- "action-choice"
556
+ "action-choice",
557
+ "google-doc",
558
+ "google-sheet"
527
559
  ],
528
560
  "title": "InputType",
529
561
  "type": "string"
@@ -1034,7 +1066,9 @@
1034
1066
  "markdown",
1035
1067
  "docx",
1036
1068
  "pptx",
1037
- "rows"
1069
+ "pdf",
1070
+ "rows",
1071
+ "xlsx"
1038
1072
  ],
1039
1073
  "title": "Renderer",
1040
1074
  "type": "string"
@@ -1339,10 +1373,49 @@
1339
1373
  "title": "StateSpec",
1340
1374
  "type": "object"
1341
1375
  },
1376
+ "TabHint": {
1377
+ "description": "One tab of the result, as the agent's page in Explore lists it: its name and what is on it.",
1378
+ "properties": {
1379
+ "about": {
1380
+ "anyOf": [
1381
+ {
1382
+ "maxLength": 200,
1383
+ "type": "string"
1384
+ },
1385
+ {
1386
+ "type": "null"
1387
+ }
1388
+ ],
1389
+ "default": null,
1390
+ "title": "About"
1391
+ },
1392
+ "name": {
1393
+ "maxLength": 64,
1394
+ "title": "Name",
1395
+ "type": "string"
1396
+ }
1397
+ },
1398
+ "required": [
1399
+ "name"
1400
+ ],
1401
+ "title": "TabHint",
1402
+ "type": "object"
1403
+ },
1342
1404
  "TriggersSpec": {
1343
1405
  "additionalProperties": false,
1344
1406
  "description": "What starts a run. Declared so the consumer sees it before installing.",
1345
1407
  "properties": {
1408
+ "email": {
1409
+ "anyOf": [
1410
+ {
1411
+ "$ref": "#/$defs/EmailTriggerSpec"
1412
+ },
1413
+ {
1414
+ "type": "null"
1415
+ }
1416
+ ],
1417
+ "default": null
1418
+ },
1346
1419
  "manual": {
1347
1420
  "default": true,
1348
1421
  "title": "Manual",
@@ -765,6 +765,14 @@ def test_display_hint_parses(manifest_data):
765
765
  assert (d.view, d.field, d.chart.x, d.chart.y) == ("chart", "daily", "date", ["reach"])
766
766
 
767
767
 
768
+ def test_display_tabs_parse_and_survive_the_dump(manifest_data):
769
+ # The push sends model_dump, so the tabs must be in it for the catalog to list them.
770
+ manifest_data["display"] = {"tabs": [{"name": "Overview", "about": "Your best options"}, {"name": "Money"}]}
771
+ d = validate_manifest(manifest_data).display
772
+ assert [t.name for t in d.tabs] == ["Overview", "Money"]
773
+ assert validate_manifest(manifest_data).model_dump(mode="json", exclude_none=True)["display"]["tabs"][0] == {"name": "Overview", "about": "Your best options"}
774
+
775
+
768
776
  def test_display_never_fails_on_what_the_page_does_not_know(manifest_data):
769
777
  # An unknown view is kept for the page to ignore; an unknown key is dropped.
770
778
  manifest_data["display"] = {"view": "carousel", "colour": "blue"}
@@ -97,3 +97,42 @@ def test_a_date_with_max_takes_several_days():
97
97
  manifest = validate_manifest(_with(inputs=inputs, choose_one=[]))
98
98
  days, deadline = manifest.inputs
99
99
  assert days.many_days and not deadline.many_days
100
+
101
+
102
+ def test_a_file_with_max_takes_several_files():
103
+ inputs = [{"name": "resumes", "label": "Resumes", "type": "file", "accept": ["pdf"], "max": 20},
104
+ {"name": "jd", "label": "Job description", "type": "file"}]
105
+ resumes, jd = validate_manifest(_with(inputs=inputs, choose_one=[])).inputs
106
+ assert resumes.many_files and not jd.many_files
107
+
108
+
109
+ @pytest.mark.parametrize("bad", [{"min": 2}, {"min": 3, "max": 2}, {"max": 21}])
110
+ def test_a_file_asks_for_a_sensible_number_of_files(bad):
111
+ with pytest.raises(ManifestValidationError):
112
+ validate_manifest(_with(inputs=[{"name": "f", "label": "F", "type": "file", **bad}], choose_one=[]))
113
+
114
+
115
+ def test_a_google_doc_or_sheet_link_becomes_the_file_id():
116
+ from gravity_schema.rules import google_file_id
117
+
118
+ fid = "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"
119
+ assert google_file_id("google-sheet", f" https://docs.google.com/spreadsheets/d/{fid}/edit#gid=0 ") == fid
120
+ assert google_file_id("google-doc", f"https://docs.google.com/document/d/{fid}/edit?usp=sharing") == fid
121
+ assert google_file_id("google-doc", fid) == fid # a bare id is taken as it is
122
+ assert google_file_id("google-doc", f"https://docs.google.com/spreadsheets/d/{fid}/edit") is None # a Sheet is not a Doc
123
+ for bad in ("my budget sheet", "https://example.com/d/x", None, 7):
124
+ assert google_file_id("google-sheet", bad) is None
125
+ inputs = [{"name": "sheet", "label": "Your sheet", "type": "google-sheet", "required": True}]
126
+ assert validate_manifest(_with(inputs=inputs, choose_one=[])).inputs[0].type.value == "google-sheet"
127
+
128
+
129
+ def test_an_email_trigger_watches_gmail_with_the_agents_own_permission():
130
+ m = validate_manifest(_with(inputs=[], choose_one=[], permissions={"tools": ["gmail.read"]},
131
+ triggers={"manual": True, "email": {"query": "from:recruiting@acme.com"}}))
132
+ assert m.triggers.email.query == "from:recruiting@acme.com" and m.triggers.email.consumer_can_change
133
+ with pytest.raises(ManifestValidationError, match="gmail.read"):
134
+ validate_manifest(_with(inputs=[], choose_one=[], permissions={"tools": []},
135
+ triggers={"manual": True, "email": {"query": "is:unread"}}))
136
+ with pytest.raises(ManifestValidationError):
137
+ validate_manifest(_with(inputs=[], choose_one=[], permissions={"tools": ["gmail.read"]},
138
+ triggers={"manual": True, "email": {"query": ""}}))
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "gravity-cli"
3
- version = "0.10.0"
3
+ version = "0.11.1"
4
4
  description = "gravity cli — init, run, and push python agents to the Gravity marketplace"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
7
7
  license-files = ["LICENSE", "NOTICE"]
8
8
  requires-python = ">=3.12"
9
9
  dependencies = [
10
- "gravity-schema==0.10.0", # released in lockstep: always the same version as this package
10
+ "gravity-schema==0.11.1", # released in lockstep: always the same version as this package
11
11
  "typer>=0.15.0",
12
12
  "httpx>=0.27.0",
13
13
  "rich>=13.9.0",
@@ -352,7 +352,9 @@ outputs:
352
352
  | `markdown` | A formatted page; also what's emailed | Everything in the corpus |
353
353
  | `docx` | A Word document | Title, headings, prose and bullets, facts and tables as tables, notes as an italic read-out, a Sources list |
354
354
  | `pptx` | A slide deck | A title slide, a Summary slide, then each section as slides (about 10 lines per slide, continued as "(cont.)"), each table on its own slide, `notes` as speaker notes, a Sources slide |
355
+ | `pdf` | A PDF | The same as `docx`, laid out for print; text outside Latin-1 (other scripts, emoji) shows as `?` for now |
355
356
  | `rows` | A CSV file | **The first table** in the corpus. Without a table there's nothing to export |
357
+ | `xlsx` | An Excel workbook | **Every table**, one sheet each named after its section. Without a table there's nothing to export |
356
358
 
357
359
  **`kind`** tells Gravity how the agent behaves:
358
360
  - `transform`: all input at the start, a result at the end (a report from a file). Gravity
@@ -127,7 +127,7 @@ resources:
127
127
  ```yaml
128
128
  outputs:
129
129
  digest:
130
- renderer: markdown # markdown | docx | pptx | rows
130
+ renderer: markdown # markdown | docx | pptx | pdf | rows | xlsx
131
131
  label: "The digest"
132
132
  destinations: [download, message.send]
133
133
  default: {destination: message.send, app: gmail}