gravity-cli 0.7.3__tar.gz → 0.7.5__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 (135) hide show
  1. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/PKG-INFO +2 -2
  2. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/catalog.yaml +81 -0
  3. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/contract.py +4 -0
  4. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/errors.py +1 -0
  5. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/manifest.py +63 -0
  6. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/pyproject.toml +1 -1
  7. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/schema/manifest.schema.json +46 -0
  8. gravity_cli-0.7.5/packages/gravity-schema/tests/test_accounts.py +42 -0
  9. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/pyproject.toml +2 -2
  10. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/CHANGELOG.md +9 -0
  11. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/docs/Gravity.agent.md +15 -0
  12. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/uv.lock +2 -2
  13. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/.env.example +0 -0
  14. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/.github/workflows/ci.yml +0 -0
  15. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/.github/workflows/release.yml +0 -0
  16. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/.gitignore +0 -0
  17. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/AGENT_STANDARD.md +0 -0
  18. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/LICENSE +0 -0
  19. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/NOTICE +0 -0
  20. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/README.md +0 -0
  21. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/RELEASING.md +0 -0
  22. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/TEMPLATES.md +0 -0
  23. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/LICENSE +0 -0
  24. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/NOTICE +0 -0
  25. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/README.md +0 -0
  26. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
  27. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
  28. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
  29. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +0 -0
  30. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/cron.py +0 -0
  31. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/export.py +0 -0
  32. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
  33. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/rules.py +0 -0
  34. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/rules_cases.json +0 -0
  35. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
  36. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/gravity_schema/validation.py +0 -0
  37. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/conftest.py +0 -0
  38. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_actions_memory.py +0 -0
  39. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
  40. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_cron.py +0 -0
  41. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_errors.py +0 -0
  42. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_json_schema.py +0 -0
  43. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
  44. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_manifest.py +0 -0
  45. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_rules.py +0 -0
  46. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_slots.py +0 -0
  47. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_source_scan.py +0 -0
  48. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/packages/gravity-schema/tests/test_validation.py +0 -0
  49. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/BUILDER_GUIDE.md +0 -0
  50. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/__init__.py +0 -0
  51. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/__main__.py +0 -0
  52. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/_runner_script.py +0 -0
  53. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/api.py +0 -0
  54. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/changelog.py +0 -0
  55. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/cli.py +0 -0
  56. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/__init__.py +0 -0
  57. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/adapt.py +0 -0
  58. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/build.py +0 -0
  59. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/changelog.py +0 -0
  60. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/connect.py +0 -0
  61. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/dev.py +0 -0
  62. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/docs.py +0 -0
  63. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/guide.py +0 -0
  64. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/init.py +0 -0
  65. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/login.py +0 -0
  66. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/memory.py +0 -0
  67. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/push.py +0 -0
  68. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/run.py +0 -0
  69. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/runs.py +0 -0
  70. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/schedule.py +0 -0
  71. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/tools.py +0 -0
  72. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/update.py +0 -0
  73. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/commands/validate.py +0 -0
  74. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/config.py +0 -0
  75. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/dev.py +0 -0
  76. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/dev_page/index.html +0 -0
  77. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/docs/problem-to-agent.md +0 -0
  78. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/inputs.py +0 -0
  79. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/localrun.py +0 -0
  80. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/manifest.py +0 -0
  81. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/project.py +0 -0
  82. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/chat/.gitignore +0 -0
  83. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
  84. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/chat/requirements.txt +0 -0
  85. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/chat/src/agent.py +0 -0
  86. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
  87. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
  88. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
  89. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
  90. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/minimal/.gitignore +0 -0
  91. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
  92. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
  93. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
  94. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/.gitignore +0 -0
  95. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
  96. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/requirements.txt +0 -0
  97. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/src/agent.py +0 -0
  98. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
  99. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
  100. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
  101. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/templates/structured/src/state.py +0 -0
  102. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/timeline.py +0 -0
  103. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/ui.py +0 -0
  104. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/updater.py +0 -0
  105. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/src/gravity_cli/workspace.py +0 -0
  106. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/starter/manifest.yaml +0 -0
  107. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/starter/requirements.txt +0 -0
  108. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/starter/src/agent.py +0 -0
  109. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/starter/src/summary.py +0 -0
  110. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/starter/tests/test_summary.py +0 -0
  111. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/conftest.py +0 -0
  112. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_adapt.py +0 -0
  113. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_agent_guide.py +0 -0
  114. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_agents.py +0 -0
  115. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_api.py +0 -0
  116. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_build_push.py +0 -0
  117. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_changelog.py +0 -0
  118. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_connect.py +0 -0
  119. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_dev.py +0 -0
  120. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_gateway_token.py +0 -0
  121. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_inputs_memory.py +0 -0
  122. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_localrun.py +0 -0
  123. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_manifest_integration.py +0 -0
  124. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_run.py +0 -0
  125. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_runs.py +0 -0
  126. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_schedule.py +0 -0
  127. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_session.py +0 -0
  128. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_templates.py +0 -0
  129. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_timeline.py +0 -0
  130. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_tools.py +0 -0
  131. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_ui.py +0 -0
  132. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_updater.py +0 -0
  133. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_validate.py +0 -0
  134. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_welcome.py +0 -0
  135. {gravity_cli-0.7.3 → gravity_cli-0.7.5}/tests/test_workspace.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gravity-cli
3
- Version: 0.7.3
3
+ Version: 0.7.5
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.7.3
9
+ Requires-Dist: gravity-schema==0.7.5
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
@@ -65,6 +65,24 @@
65
65
  guarantee: certified
66
66
  side_effect: read
67
67
 
68
+ - name: browser.read
69
+ slug: null
70
+ toolkit: null
71
+ description: read a page of a site the person signed in to once (an account the manifest declares) through Gravity's browser worker, as that person; returns the final url, the page text and its links. Only pages of the account's own site. The agent never sees the login. Charged to the run's budget
72
+ scopes: []
73
+ breadth: generic
74
+ guarantee: certified
75
+ side_effect: read
76
+
77
+ - name: browser.act
78
+ slug: null
79
+ toolkit: null
80
+ description: do one typed action as the person on a site account they signed in to (linkedin.com send_dm, connect, comment; x.com send_dm, reply), with the text given; paced and capped per account per day by the gateway. Must be under approvals.required_for. Returns what was done, or a refusal code (NOT_SIGNED_IN, NOT_CONNECTED, DAILY_CAP, BLOCKED_BY_SITE)
81
+ scopes: []
82
+ breadth: generic
83
+ guarantee: certified
84
+ side_effect: write
85
+
68
86
  - name: supadata.get_transcript
69
87
  slug: null
70
88
  toolkit: null
@@ -354,6 +372,15 @@
354
372
  guarantee: certified
355
373
  side_effect: read
356
374
 
375
+ - name: apollo.enrich_person
376
+ slug: APOLLO_PEOPLE_ENRICHMENT
377
+ toolkit: apollo
378
+ description: match one person in Apollo from their name and company domain (or LinkedIn URL) and return their work email, title and profile links; uses the person's own Apollo account and its credits
379
+ scopes: []
380
+ breadth: generic
381
+ guarantee: community
382
+ side_effect: read
383
+
357
384
  - name: hunter.find_email
358
385
  slug: HUNTER_EMAIL_FINDER
359
386
  toolkit: hunter
@@ -372,6 +399,60 @@
372
399
  guarantee: community
373
400
  side_effect: read
374
401
 
402
+ - name: scrapecreators.linkedin_profile
403
+ slug: SCRAPECREATORS_GET_LINKEDIN_PROFILE
404
+ toolkit: scrapecreators
405
+ description: read one public LinkedIn person profile by URL (experience with descriptions, education, about and recent activity) without the person's or the user's login; on Gravity's account through Composio Instant, 1 credit a profile
406
+ scopes: []
407
+ breadth: generic
408
+ guarantee: community
409
+ side_effect: read
410
+
411
+ - name: scrapecreators.linkedin_post
412
+ slug: SCRAPECREATORS_GET_LINKEDIN_POST
413
+ toolkit: scrapecreators
414
+ description: read one public LinkedIn post by URL (text, author, date, engagement and comments); on Gravity's account through Composio Instant
415
+ scopes: []
416
+ breadth: generic
417
+ guarantee: community
418
+ side_effect: read
419
+
420
+ - name: scrapecreators.twitter_profile
421
+ slug: SCRAPECREATORS_GET_TWITTER_PROFILE
422
+ toolkit: scrapecreators
423
+ description: read one public X/Twitter profile by handle (bio, links, verification, audience); on Gravity's account through Composio Instant
424
+ scopes: []
425
+ breadth: generic
426
+ guarantee: community
427
+ side_effect: read
428
+
429
+ - name: scrapecreators.twitter_tweets
430
+ slug: SCRAPECREATORS_LIST_TWITTER_USER_TWEETS
431
+ toolkit: scrapecreators
432
+ description: read the public tweets X shows for one handle (about 100 popular ones, not a full history); on Gravity's account through Composio Instant
433
+ scopes: []
434
+ breadth: generic
435
+ guarantee: community
436
+ side_effect: read
437
+
438
+ - name: scrapecreators.reddit_search
439
+ slug: SCRAPECREATORS_SEARCH_REDDIT
440
+ toolkit: scrapecreators
441
+ description: search Reddit posts or comments by keyword with sort and timeframe, one page per call with a cursor; on Gravity's account through Composio Instant, no Reddit API key needed
442
+ scopes: []
443
+ breadth: generic
444
+ guarantee: community
445
+ side_effect: read
446
+
447
+ - name: scrapecreators.social_profiles
448
+ slug: SCRAPECREATORS_FIND_CREATOR_SOCIAL_PROFILES
449
+ toolkit: scrapecreators
450
+ description: find a person's linked profiles on other social platforms from one known profile; on Gravity's account through Composio Instant
451
+ scopes: []
452
+ breadth: generic
453
+ guarantee: community
454
+ side_effect: read
455
+
375
456
  - name: exa.search
376
457
  slug: EXA_SEARCH
377
458
  toolkit: exa
@@ -82,6 +82,10 @@ MAX_PREVIOUS_KB: Final[int] = 256
82
82
 
83
83
  # Declared actions (manifest `actions`).
84
84
  MAX_ACTIONS: Final[int] = 20
85
+ # Sites the gateway's browser worker has sign-in and recipes for (accounts, browser.read, browser.act).
86
+ ACCOUNT_SITES: Final[frozenset[str]] = frozenset({"linkedin.com", "x.com"})
87
+ MAX_ACCOUNTS: Final[int] = 5
88
+ BROWSER_TOOLS: Final[frozenset[str]] = frozenset({"browser.read", "browser.act"})
85
89
 
86
90
 
87
91
  # Generous bounds, so a pathological manifest can't tie up the validator.
@@ -39,6 +39,7 @@ class ErrorCode(str, Enum):
39
39
  INVALID_CONDITION = "INVALID_CONDITION"
40
40
  INVALID_ACTION = "INVALID_ACTION"
41
41
  INVALID_MEMORY = "INVALID_MEMORY"
42
+ INVALID_ACCOUNT = "INVALID_ACCOUNT"
42
43
  LOCKFILE_MISMATCH = "LOCKFILE_MISMATCH"
43
44
  MISSING_FIELD = "MISSING_FIELD"
44
45
  UNKNOWN_FIELD = "UNKNOWN_FIELD"
@@ -726,6 +726,42 @@ class ActionSpec(BaseModel):
726
726
  return self
727
727
 
728
728
 
729
+ class AccountSpec(BaseModel):
730
+ """A site account the person signs in to once on a Gravity page (Accounts vault). The agent never sees the
731
+ login: the gateway's browser worker opens the session for browser.read and browser.act calls only."""
732
+
733
+ model_config = _STRICT
734
+
735
+ name: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)]
736
+ site: str
737
+ label: Annotated[str, Field(max_length=C.MAX_LABEL_LENGTH)] = ""
738
+ purpose: Annotated[str, Field(max_length=C.MAX_LABEL_LENGTH)] = ""
739
+ required: bool = False
740
+
741
+ @field_validator("name")
742
+ @classmethod
743
+ def _check_name(cls, v: str) -> str:
744
+ if not C.INPUT_NAME_RE.match(v):
745
+ raise PydanticCustomError(
746
+ ErrorCode.INVALID_ACCOUNT.value,
747
+ "Account name '{value}' is not a valid identifier.",
748
+ {"value": v, "remediation": "Use lowercase letters, digits and underscores, e.g. linkedin."},
749
+ )
750
+ return v
751
+
752
+ @field_validator("site")
753
+ @classmethod
754
+ def _check_site(cls, v: str) -> str:
755
+ site = v.strip().lower().removeprefix("www.")
756
+ if site not in C.ACCOUNT_SITES:
757
+ raise PydanticCustomError(
758
+ ErrorCode.INVALID_ACCOUNT.value,
759
+ "Accounts on '{value}' are not supported.",
760
+ {"value": v, "remediation": f"Use one of: {', '.join(sorted(C.ACCOUNT_SITES))}."},
761
+ )
762
+ return site
763
+
764
+
729
765
  class OfferAs(str, Enum):
730
766
  """How the actions under a result are shown."""
731
767
 
@@ -767,6 +803,8 @@ class Manifest(BaseModel):
767
803
  actions: Annotated[list[ActionSpec], Field(max_length=C.MAX_ACTIONS)] = Field(default_factory=list)
768
804
  offer_as: OfferAs = OfferAs.BUTTONS
769
805
  offer_label: Annotated[str, Field(max_length=C.MAX_LABEL_LENGTH)] | None = None
806
+ # Site accounts the person signs in to once (Accounts vault); browser.* tools act through them.
807
+ accounts: Annotated[list[AccountSpec], Field(max_length=C.MAX_ACCOUNTS)] = Field(default_factory=list)
770
808
 
771
809
  @field_validator("contract")
772
810
  @classmethod
@@ -897,6 +935,31 @@ class Manifest(BaseModel):
897
935
  )
898
936
  return self
899
937
 
938
+ @model_validator(mode="after")
939
+ def _check_accounts(self) -> Manifest:
940
+ names = [a.name for a in self.accounts]
941
+ dupes = sorted({n for n in names if names.count(n) > 1})
942
+ if dupes:
943
+ raise PydanticCustomError(
944
+ ErrorCode.INVALID_ACCOUNT.value,
945
+ "Account name(s) declared twice: {value}.",
946
+ {"value": ", ".join(dupes), "remediation": "Give each account its own name."},
947
+ )
948
+ browser = sorted(self.declared_tools & C.BROWSER_TOOLS)
949
+ if browser and not self.accounts:
950
+ raise PydanticCustomError(
951
+ ErrorCode.INVALID_ACCOUNT.value,
952
+ "{value} acts through a person's own site account, and none is declared.",
953
+ {"value": ", ".join(browser), "remediation": "Declare the site under `accounts` (e.g. name: linkedin, site: linkedin.com)."},
954
+ )
955
+ if "browser.act" in self.declared_tools and "browser.act" not in self.approvals.required_for:
956
+ raise PydanticCustomError(
957
+ ErrorCode.APPROVAL_REQUIRED.value,
958
+ "browser.act sends messages as the person, so each call needs their approval.",
959
+ {"remediation": "Add browser.act under approvals.required_for, with a fallback for when it isn't approved."},
960
+ )
961
+ return self
962
+
900
963
  @model_validator(mode="after")
901
964
  def _check_memory(self) -> Manifest:
902
965
  if self.state is not None and self.memory is not None:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gravity-schema"
3
- version = "0.7.3"
3
+ version = "0.7.5"
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,5 +1,43 @@
1
1
  {
2
2
  "$defs": {
3
+ "AccountSpec": {
4
+ "additionalProperties": false,
5
+ "description": "A site account the person signs in to once on a Gravity page (Accounts vault). The agent never sees the\nlogin: the gateway's browser worker opens the session for browser.read and browser.act calls only.",
6
+ "properties": {
7
+ "label": {
8
+ "default": "",
9
+ "maxLength": 200,
10
+ "title": "Label",
11
+ "type": "string"
12
+ },
13
+ "name": {
14
+ "maxLength": 64,
15
+ "title": "Name",
16
+ "type": "string"
17
+ },
18
+ "purpose": {
19
+ "default": "",
20
+ "maxLength": 200,
21
+ "title": "Purpose",
22
+ "type": "string"
23
+ },
24
+ "required": {
25
+ "default": false,
26
+ "title": "Required",
27
+ "type": "boolean"
28
+ },
29
+ "site": {
30
+ "title": "Site",
31
+ "type": "string"
32
+ }
33
+ },
34
+ "required": [
35
+ "name",
36
+ "site"
37
+ ],
38
+ "title": "AccountSpec",
39
+ "type": "object"
40
+ },
3
41
  "ActionSpec": {
4
42
  "additionalProperties": false,
5
43
  "description": "One thing the agent can do. The runner passes the chosen ones in state[\"actions\"].",
@@ -736,6 +774,14 @@
736
774
  "additionalProperties": false,
737
775
  "description": "What a builder declares about an agent: identity, entrypoint, consumer inputs, and the tools it is permitted to call. Contract version v1.",
738
776
  "properties": {
777
+ "accounts": {
778
+ "items": {
779
+ "$ref": "#/$defs/AccountSpec"
780
+ },
781
+ "maxItems": 5,
782
+ "title": "Accounts",
783
+ "type": "array"
784
+ },
739
785
  "actions": {
740
786
  "items": {
741
787
  "$ref": "#/$defs/ActionSpec"
@@ -0,0 +1,42 @@
1
+ """Site accounts (the Accounts vault) and the browser tools that act through them."""
2
+
3
+ import pytest
4
+ from gravity_schema import dev_catalog, load_manifest_text, validate_manifest
5
+ from gravity_schema.errors import ManifestValidationError
6
+
7
+ BASE = {"name": "leads", "runtime": {"entrypoint": "src/agent.py:graph"}}
8
+ LINKEDIN = {"name": "linkedin", "site": "www.LinkedIn.com", "label": "Your LinkedIn", "purpose": "send messages you approve"}
9
+
10
+
11
+ def codes(manifest):
12
+ with pytest.raises(ManifestValidationError) as e:
13
+ validate_manifest(manifest)
14
+ return {i.code.value for i in e.value.issues}
15
+
16
+
17
+ def test_accounts_with_browser_tools_validate():
18
+ m = validate_manifest({**BASE, "accounts": [LINKEDIN, {"name": "x", "site": "x.com"}],
19
+ "permissions": {"tools": ["browser.read", "browser.act"]},
20
+ "approvals": {"required_for": ["browser.act"]}})
21
+ assert [a.site for a in m.accounts] == ["linkedin.com", "x.com"] and not m.accounts[0].required
22
+
23
+
24
+ def test_account_rules():
25
+ assert codes({**BASE, "accounts": [{"name": "fb", "site": "facebook.com"}]}) == {"INVALID_ACCOUNT"}
26
+ assert codes({**BASE, "accounts": [LINKEDIN, LINKEDIN]}) == {"INVALID_ACCOUNT"}
27
+ assert codes({**BASE, "accounts": [{"name": "Linked In", "site": "linkedin.com"}]}) == {"INVALID_ACCOUNT"}
28
+ assert codes({**BASE, "permissions": {"tools": ["browser.read"]}}) == {"INVALID_ACCOUNT"} # no account to act through
29
+ assert codes({**BASE, "accounts": [LINKEDIN], "permissions": {"tools": ["browser.act"]}}) == {"APPROVAL_REQUIRED"}
30
+
31
+
32
+ def test_browser_and_apollo_tools_are_in_the_catalog():
33
+ names = dev_catalog().names()
34
+ assert {"browser.read", "browser.act", "apollo.enrich_person"} <= names
35
+ text = """
36
+ name: leads
37
+ runtime: {entrypoint: src/agent.py:graph}
38
+ accounts: [{name: linkedin, site: linkedin.com}]
39
+ permissions: {tools: [browser.read, browser.act, apollo.enrich_person]}
40
+ approvals: {required_for: [browser.act]}
41
+ """
42
+ assert load_manifest_text(text, catalog=dev_catalog()).accounts[0].name == "linkedin"
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "gravity-cli"
3
- version = "0.7.3"
3
+ version = "0.7.5"
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.7.3", # released in lockstep: always the same version as this package
10
+ "gravity-schema==0.7.5", # 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",
@@ -4,6 +4,15 @@ Shown by `gravity update` and `gravity changelog`, and after an update. Each rel
4
4
  then one bullet per change: `- new|improved|fixed: A one-line headline. The detail behind it.`
5
5
  Write it for a builder, not for a maintainer. `tests/test_changelog.py` fails a release that has no entry.
6
6
 
7
+ ## 0.7.5 - 2026-10-24
8
+
9
+ - new: ScrapeCreators tools on Gravity's account. scrapecreators.linkedin_profile reads a public LinkedIn profile in full (experience with descriptions, education, recent activity) where LinkedIn refuses a plain read, plus linkedin_post, twitter_profile, twitter_tweets, reddit_search (no Reddit API key) and social_profiles. They run through Composio Instant, charged to the run's budget.
10
+
11
+ ## 0.7.4 - 2026-10-24
12
+
13
+ - new: Site accounts. Declare `accounts` (linkedin.com, x.com) and the person signs in once on Gravity; browser.read reads their pages and browser.act sends a DM, a connection note, a comment or a reply they approve, paced and capped per day. Agent code never sees the login.
14
+ - new: apollo.enrich_person matches a person in Apollo from their name and company domain and returns their work email and profile links, on the person's own Apollo account.
15
+
7
16
  ## 0.7.3 - 2026-10-23
8
17
 
9
18
  - fixed: gravity push fits bigger agents. The manifest travels in a 12 KB request header and every unset field went up as a null, about 100 bytes per input and action; they are now left out, so an agent with a full form and many actions uploads.
@@ -307,6 +307,9 @@ actions: # what the agent can do (section 10); leave o
307
307
  offer_as: buttons # buttons (default) | question: how the actions show under the result
308
308
  offer_label: "What next?"
309
309
 
310
+ # accounts: # a site account the person signs in to once on Gravity (section 6, browser.*)
311
+ # - { name: linkedin, site: linkedin.com, label: "Your LinkedIn", purpose: "send messages you approve" }
312
+
310
313
  outputs: # what a person may ask the result to become (section 9), at most 8
311
314
  summary: {renderer: markdown, label: "Summary", destinations: [download, gmail.draft, gmail.send]}
312
315
  report: {renderer: docx, label: "Report (Word)"}
@@ -349,6 +352,7 @@ Rules the validator enforces beyond field types:
349
352
  - **Actions** (`INVALID_ACTION`): names unique; `requires` and `then` name other declared actions and never loop;
350
353
  at least one is `default: true`, and a default action's `requires` are defaults declared before it; a
351
354
  `run_once` action has no `schedule` and is not a `then` target; an action's own inputs have no action-choice.
355
+ - **Accounts** (`INVALID_ACCOUNT`): sites are linkedin.com or x.com; names unique identifiers; `browser.read` or `browser.act` needs at least one account, and `browser.act` must be under `approvals.required_for` (`APPROVAL_REQUIRED`).
352
356
  - **Memory** (`INVALID_MEMORY`): `memory` and `state` are not both declared; collection names are identifiers;
353
357
  there is no shared-across-people collection.
354
358
  - **Quote** `python: "3.12"` and write `version: 1.0.0` as a plain semver (`1.0` is a YAML float and is refused).
@@ -517,6 +521,16 @@ Catalog of this release: {{tool_counts}}.
517
521
  an error, so check `status`. Each call costs about $0.0002 from the run's budget, and a run gets at most 200.
518
522
  Private, internal and metadata addresses, ports other than 80/443/8080/8443, and non-http(s) URLs answer
519
523
  `URL_REFUSED`. Prefer it over a `proxy_url` secret input: the person supplies nothing.
524
+ - `browser.read` and `browser.act` work as the person on a site they signed in to once on Gravity (My agents,
525
+ Accounts). Declare the site under `accounts` (`linkedin.com` or `x.com`); the person's login and session stay in
526
+ Gravity's vault and browser worker, and agent code never sees them. `browser.read {account, url}` returns `url`,
527
+ `text` and `links` for a page of that account's own site only. `browser.act {account, action, target, text}` does one
528
+ typed action: `send_dm` (a profile URL or handle; LinkedIn only to first-degree connections), `connect` (a LinkedIn
529
+ profile URL, note at most 300 characters), `comment` (a LinkedIn post URL), `reply` (an X post URL). It must be under
530
+ `approvals.required_for`, so the person sees and approves each message; the gateway paces it and holds daily caps per
531
+ account (LinkedIn: 25 DMs, 15 notes, 10 comments; X: 15 DMs, 10 replies). Refusals come back as text codes:
532
+ `NOT_SIGNED_IN` (ask the person to sign in again, under "could not do"), `NOT_CONNECTED`, `DAILY_CAP`,
533
+ `BLOCKED_BY_SITE`. Browser time is charged to the run's budget.
520
534
  - Outbound HTTPS is open, so code may read public pages and APIs directly (`httpx`, `curl_cffi`). Anything that acts
521
535
  on an account still goes through a tool, so the person's connection and approvals apply.
522
536
  - Locally, `gravity connect <app>` attaches **your** account to your local runs (`gravity connect` alone lists what
@@ -818,6 +832,7 @@ checkpointing, durable approvals, `gravity.ask()`, action buttons, chains and ou
818
832
  | `APPROVAL_REQUIRED` (inside a hosted run) | A gated tool was called through the plain MCP client | Load tools with `await gravity.tools()` (section 5) |
819
833
  | `INVALID_APPROVAL`, `INVALID_TRIGGER`, `INVALID_KIND`, `INVALID_OUTPUT` | `required_for` names an undeclared tool; nothing can start the agent, a bad cron, both day fields, faster than 5 minutes; a monitor without a trigger; a bad output name or destination | Section 4 |
820
834
  | `INVALID_CONDITION`, `INVALID_ACTION`, `INVALID_MEMORY` | a `show_if` on a later input or a value it cannot take, a `choose_one` member required alone; an action that requires or chains to an undeclared one or loops, no default action; `memory` beside `state`, a bad collection | Section 4 |
835
+ | `INVALID_ACCOUNT` | an account on a site other than linkedin.com or x.com, a name used twice or not an identifier; `browser.*` declared with no account to act through | Section 6 |
821
836
  | `UNDECLARED_TOOL_CALL` | Source names a catalog write tool the manifest does not grant | Declare and gate it, remove the call, or make a deliver-only tool an output `destination` |
822
837
  | `FILE_INPUT_UNSUPPORTED` (hosted build only) | A required `file` input | Make it optional, add a text fallback |
823
838
  | `SECRET_FOUND` (hosted build only) | The secret scanner matched something in the upload | Remove it, fixtures included; rotate it if it was real |
@@ -164,7 +164,7 @@ wheels = [
164
164
 
165
165
  [[package]]
166
166
  name = "gravity-cli"
167
- version = "0.7.3"
167
+ version = "0.7.5"
168
168
  source = { editable = "." }
169
169
  dependencies = [
170
170
  { name = "gravity-schema" },
@@ -201,7 +201,7 @@ dev = [
201
201
 
202
202
  [[package]]
203
203
  name = "gravity-schema"
204
- version = "0.7.3"
204
+ version = "0.7.5"
205
205
  source = { editable = "packages/gravity-schema" }
206
206
  dependencies = [
207
207
  { name = "pydantic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes