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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/AGENT_STANDARD.md +50 -19
  2. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/PKG-INFO +53 -14
  3. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/README.md +51 -12
  4. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +478 -0
  5. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/cron.py +26 -1
  6. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/manifest.py +56 -0
  7. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/validation.py +11 -1
  8. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/pyproject.toml +1 -1
  9. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_cron.py +16 -1
  10. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_manifest.py +106 -0
  11. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/pyproject.toml +2 -2
  12. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/BUILDER_GUIDE.md +38 -26
  13. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/_runner_script.py +10 -3
  14. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/api.py +57 -15
  15. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/cli.py +15 -0
  16. gravity_cli-0.5.4/src/gravity_cli/commands/adapt.py +182 -0
  17. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/build.py +13 -2
  18. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/connect.py +21 -4
  19. gravity_cli-0.5.4/src/gravity_cli/commands/dev.py +40 -0
  20. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/login.py +19 -3
  21. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/push.py +16 -2
  22. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/run.py +182 -62
  23. gravity_cli-0.5.4/src/gravity_cli/commands/runs.py +103 -0
  24. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/schedule.py +23 -6
  25. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/validate.py +95 -19
  26. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/config.py +27 -8
  27. gravity_cli-0.5.4/src/gravity_cli/dev.py +315 -0
  28. gravity_cli-0.5.4/src/gravity_cli/dev_page/index.html +406 -0
  29. gravity_cli-0.5.4/src/gravity_cli/localrun.py +228 -0
  30. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/project.py +7 -2
  31. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/timeline.py +3 -2
  32. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/ui.py +49 -6
  33. gravity_cli-0.5.4/tests/test_adapt.py +134 -0
  34. gravity_cli-0.5.4/tests/test_agents.py +91 -0
  35. gravity_cli-0.5.4/tests/test_api.py +218 -0
  36. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_build_push.py +70 -3
  37. gravity_cli-0.5.4/tests/test_connect.py +146 -0
  38. gravity_cli-0.5.4/tests/test_dev.py +291 -0
  39. gravity_cli-0.5.4/tests/test_localrun.py +83 -0
  40. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_run.py +95 -7
  41. gravity_cli-0.5.4/tests/test_runs.py +91 -0
  42. gravity_cli-0.5.4/tests/test_schedule.py +63 -0
  43. gravity_cli-0.5.4/tests/test_session.py +146 -0
  44. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_templates.py +21 -0
  45. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_ui.py +14 -0
  46. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_validate.py +69 -2
  47. gravity_cli-0.5.4/tests/test_welcome.py +98 -0
  48. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/uv.lock +2 -2
  49. gravity_cli-0.5.2/src/gravity_cli/commands/adapt.py +0 -154
  50. gravity_cli-0.5.2/src/gravity_cli/commands/runs.py +0 -49
  51. gravity_cli-0.5.2/tests/test_adapt.py +0 -71
  52. gravity_cli-0.5.2/tests/test_api.py +0 -101
  53. gravity_cli-0.5.2/tests/test_connect.py +0 -78
  54. gravity_cli-0.5.2/tests/test_session.py +0 -80
  55. gravity_cli-0.5.2/tests/test_welcome.py +0 -43
  56. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/.env.example +0 -0
  57. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/.github/workflows/ci.yml +0 -0
  58. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/.github/workflows/release.yml +0 -0
  59. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/.gitignore +0 -0
  60. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/LICENSE +0 -0
  61. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/NOTICE +0 -0
  62. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/RELEASING.md +0 -0
  63. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/TEMPLATES.md +0 -0
  64. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/LICENSE +0 -0
  65. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/NOTICE +0 -0
  66. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/README.md +0 -0
  67. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
  68. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
  69. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
  70. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/catalog.yaml +0 -0
  71. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/contract.py +0 -0
  72. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/errors.py +0 -0
  73. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/export.py +0 -0
  74. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
  75. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
  76. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/schema/manifest.schema.json +0 -0
  77. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/conftest.py +0 -0
  78. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
  79. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_errors.py +0 -0
  80. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_json_schema.py +0 -0
  81. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
  82. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_slots.py +0 -0
  83. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_source_scan.py +0 -0
  84. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/packages/gravity-schema/tests/test_validation.py +0 -0
  85. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/__init__.py +0 -0
  86. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/__main__.py +0 -0
  87. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/__init__.py +0 -0
  88. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/guide.py +0 -0
  89. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/init.py +0 -0
  90. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/commands/tools.py +0 -0
  91. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/manifest.py +0 -0
  92. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/chat/.gitignore +0 -0
  93. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
  94. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/chat/requirements.txt +0 -0
  95. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/chat/src/agent.py +0 -0
  96. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
  97. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
  98. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
  99. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
  100. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/minimal/.gitignore +0 -0
  101. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
  102. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
  103. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
  104. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/.gitignore +0 -0
  105. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
  106. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/requirements.txt +0 -0
  107. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/src/agent.py +0 -0
  108. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
  109. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
  110. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
  111. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/src/gravity_cli/templates/structured/src/state.py +0 -0
  112. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/starter/manifest.yaml +0 -0
  113. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/starter/requirements.txt +0 -0
  114. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/starter/src/agent.py +0 -0
  115. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/starter/src/summary.py +0 -0
  116. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/starter/tests/test_summary.py +0 -0
  117. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/conftest.py +0 -0
  118. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_gateway_token.py +0 -0
  119. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_manifest_integration.py +0 -0
  120. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_timeline.py +0 -0
  121. {gravity_cli-0.5.2 → gravity_cli-0.5.4}/tests/test_tools.py +0 -0
@@ -39,7 +39,7 @@ description: One line. # ≤500 chars
39
39
 
40
40
  runtime:
41
41
  framework: langgraph # only value accepted
42
- python: "3.13" # 3.10–3.13; the lockfile is compiled for it
42
+ python: "3.12" # the platform runs 3.12; validate refuses any other
43
43
  entrypoint: src/agent.py:graph # file:object — see §3
44
44
  timeout_seconds: 300 # 10–900
45
45
 
@@ -67,7 +67,7 @@ resources:
67
67
  triggers: # optional — what starts a run
68
68
  manual: true # default
69
69
  # schedule:
70
- # cron: "0 9 * * 1" # 5 fields: digits, * / , - only (no @daily, no MON)
70
+ # cron: "0 9 * * 1" # 5 fields: digits, * / , - only (no @daily, no MON); every 5 minutes at the fastest; one day field at most; Asia/Kolkata
71
71
  # consumer_can_change: true
72
72
 
73
73
  approvals: # optional — tools that pause the run for the consumer's yes
@@ -78,8 +78,9 @@ approvals: # optional — tools that pause the run for th
78
78
  ```
79
79
 
80
80
  **`approvals` is enforced by the gateway.** A listed tool is held until a
81
- human answers: in `gravity run` you're asked (`Approve? [y/N]`, default no;
82
- no answer is a no); in the product it's the consumer's approval inbox. Not
81
+ human answers: in `gravity run` you're asked (`y approve · n reject · enter reject`,
82
+ default no; no answer is a no) and `gravity dev` shows Approve/Reject buttons; in the
83
+ product it's the consumer's approval inbox. Not
83
84
  approved, the call returns `APPROVAL_REJECTED` (or `APPROVAL_TIMEOUT` after 4
84
85
  minutes) and never runs — handle that and fall back, as `meeting-notes`
85
86
  keeps the recap as a draft. Your code can't skip the question: the gateway
@@ -109,7 +110,7 @@ async def graph(): # ✓ recommended — an async factory
109
110
  ...
110
111
  return workflow.compile()
111
112
 
112
- graph = asyncio.run(_build()) # ✓ a graph compiled at import
113
+ graph = asyncio.run(_build()) # ✗ gravity validate refuses asyncio.run at import
113
114
  graph = _build() # ✓ also fine — a coroutine is awaited
114
115
  ```
115
116
 
@@ -179,9 +180,10 @@ tools = await client.get_tools() # exactly permissions.tools, nothing else
179
180
  Tool names are `<app>.<action>`, lowercase, from `gravity tools` — Gravity's
180
181
  names, never a vendor's (`gmail.read`, not `GMAIL_FETCH_EMAILS`). The gateway
181
182
  hands them to your model with `_` for `.` (`gmail_read`), because
182
- OpenAI-family models reject dots in function names. The sandbox allows no
183
- other egress — `requests.get(...)` to anywhere times out. Declare what you
184
- need; don't reach around it.
183
+ OpenAI-family models reject dots in function names. Outbound network from the
184
+ sandbox is open today (it may be locked down later), but anything that acts on a
185
+ user's account must go through a tool: that is where the credentials are, and a
186
+ direct call would have none. Declare what you need; don't reach around it.
185
187
 
186
188
  **Models** go through the LLM gateway with the same token:
187
189
 
@@ -202,6 +204,26 @@ emb = OpenAIEmbeddings(model="voyageai/voyage-4-lite", # or google/gemini-embe
202
204
  vectors = await emb.aembed_documents(["first text", "second text"])
203
205
  ```
204
206
 
207
+ **Images** come from an image model through the same URL and token: no tool to
208
+ declare. Ask for image output; the picture comes back as a data URL, which is
209
+ exactly what the corpus takes (§8):
210
+
211
+ ```python
212
+ async with httpx.AsyncClient(timeout=120) as http:
213
+ resp = await http.post(f"{os.environ['GRAVITY_LLM_URL']}/chat/completions",
214
+ headers={"Authorization": f"Bearer {os.environ['GRAVITY_RUN_TOKEN']}"},
215
+ json={"model": "google/gemini-2.5-flash-image", # or openai/gpt-5-image-mini
216
+ "modalities": ["image", "text"],
217
+ "messages": [{"role": "user", "content": "A flat illustration of ..., no text"}]})
218
+ resp.raise_for_status()
219
+ src = resp.json()["choices"][0]["message"]["images"][0]["image_url"]["url"] # data:image/png;base64,...
220
+ ```
221
+
222
+ An image is a few MB and the whole state is checkpointed after every step, so
223
+ make pictures in the **last** node (or the one that builds the corpus) and keep
224
+ them out of state until then. Each one is metered and counts toward the run's
225
+ budget like any model call.
226
+
205
227
  Those three env vars — `GRAVITY_GATEWAY_URL`, `GRAVITY_LLM_URL`,
206
228
  `GRAVITY_RUN_TOKEN` — are the entire Gravity surface, plus the `gravity`
207
229
  package for tools on the platform (§8). No `OPENAI_API_KEY`: locally and in
@@ -221,12 +243,17 @@ Windows or macOS pins the wrong wheels and only fails at deploy.
221
243
 
222
244
  `gravity run --input k=v` runs your graph in your own venv against a real
223
245
  gateway, exactly as production does: tools over `GRAVITY_GATEWAY_URL`,
224
- models over `GRAVITY_LLM_URL`, both with `GRAVITY_RUN_TOKEN`. You set a
225
- different token once, `GRAVITY_DEV_TOKEN`, in `~/.gravity/.env`: each run
226
- trades it for a run token scoped to your `permissions.tools`, so an
227
- undeclared tool is refused locally exactly as it will be when hosted. Your
228
- agent only ever sees the run token. `GRAVITY_GATEWAY_URL` defaults to
229
- `http://127.0.0.1:8000/mcp` and `GRAVITY_LLM_URL` to the same host's `/v1`.
246
+ models over `GRAVITY_LLM_URL`, both with `GRAVITY_RUN_TOKEN`. `gravity login`
247
+ gives you a gateway token; each run trades it for a run token scoped to your
248
+ `permissions.tools`, so an undeclared tool is refused locally exactly as it
249
+ will be when hosted. Your agent only ever sees the run token. The gateway is
250
+ the one Gravity names (`GET /api/cli/config`); set `GRAVITY_GATEWAY_URL` (and
251
+ `GRAVITY_DEV_TOKEN`, in `~/.gravity/.env`) only for a gateway you run yourself.
252
+
253
+ `gravity dev` opens a local dashboard over the same runs: a form from your
254
+ manifest, a live timeline, approvals, the result and a history. A run from
255
+ `gravity run` in the same folder shows up in it, and the other way round (both
256
+ leave their record in `.gravity/runs/`; one run is live per folder).
230
257
 
231
258
  ## 7. What's checked where
232
259
 
@@ -250,10 +277,10 @@ graph. `gravity` is preinstalled in the image; don't put it in
250
277
 
251
278
  **Tools: `await gravity.tools()`** instead of your own `MultiServerMCPClient`.
252
279
  The same LangChain tools under the same names (`gmail_read`), from the same env
253
- vars. Off the platform (`gravity run`, tests) it is exactly the client in §4,
254
- but only if your venv has it: it stays out of `requirements.txt` and `gravity run`
255
- doesn't add it, so install it yourself (`uv pip install -e <repo>/code-first/python/gravity-sdk`)
256
- or `gravity run` fails with `No module named 'gravity'`.
280
+ vars. Off the platform (`gravity run`, `gravity dev`, tests) the package is not
281
+ installed and is not on PyPI: use the client in §4 directly, as the templates do
282
+ (it is the same tools under the same names), or guard the import
283
+ (`try: import gravity except ImportError: ...`). A plain dict works for the corpus.
257
284
  On the platform, each tool in `approvals.required_for` is wrapped: calling it
258
285
  checkpoints the run and stops it until the consumer decides, with no
259
286
  connection held open. Approved, the call runs with the arguments the consumer
@@ -328,6 +355,7 @@ corpus = gravity.Corpus(
328
355
  facts=[gravity.Fact(label="TAM", value="$18B", source="s1")], # value: text or a number
329
356
  table=gravity.Table(columns=["Segment", "Teams"], rows=[["Founders", "1.2M"]]),
330
357
  notes="speaker notes / read-out text",
358
+ images=[gravity.Image(src=src, alt="server rack", caption="Option A")], # data URL, see §4
331
359
  sections=[gravity.Section(heading="one level of nesting, same shape")],
332
360
  )],
333
361
  sources=[gravity.Source(id="s1", title="Report", url="https://example.com")],
@@ -336,7 +364,10 @@ return {**corpus.to_state(), "result": {"title": corpus.title}}
336
364
  ```
337
365
 
338
366
  A plain dict of the same shape works too. The models catch a typo when the
339
- corpus is built rather than when it's rendered. Set `result` as well: it's
367
+ corpus is built rather than when it's rendered. Images (`png`, `jpeg`, `webp`
368
+ or `gif` data URLs, at most 8 MB each and 12 per corpus) are embedded in the
369
+ Word, PowerPoint and Markdown downloads and shown on `/app` and in `gravity
370
+ dev`; an emailed output names each one as `[image: alt — caption]` instead. Set `result` as well: it's
340
371
  what the run returns.
341
372
 
342
373
  ## 9. Checklist
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gravity-cli
3
- Version: 0.5.2
3
+ Version: 0.5.4
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.5.2
9
+ Requires-Dist: gravity-schema==0.5.4
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
@@ -108,10 +108,10 @@ The same rules apply on your laptop and when the agent runs hosted, because both
108
108
 
109
109
  | | |
110
110
  |---|---|
111
- | Commands | 16, grouped into build, publish, inspect, account and help |
111
+ | Commands | 18, grouped into build an agent, publish, your agents, account and help |
112
112
  | Starter templates | 4: `minimal`, `structured`, `deepagent`, `chat` |
113
- | Tool catalog | 1,139 tools across 8 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn), 25 of them curated |
114
- | Test suite | 285 tests, run on Ubuntu and Windows with Python 3.12 and 3.13 |
113
+ | Tool catalog | 1,402 tools across 15 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn, Jira, Confluence, Notion, WhatsApp, web search, and two built-ins), 28 of them curated |
114
+ | Test suite | 466 tests (214 CLI, 252 schema), run on Ubuntu and Windows with Python 3.12 and 3.13 |
115
115
  | Runtime contract | 3 environment variables, no Gravity import in your code |
116
116
 
117
117
  ## Five things to know before you start
@@ -196,6 +196,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
196
196
  | **Build an agent** | [`gravity init`](#gravity-init) | Create a new agent from a template |
197
197
  | | [`gravity validate`](#gravity-validate) | Check your agent for mistakes before it runs |
198
198
  | | [`gravity run`](#gravity-run) | Run your agent locally against real tools |
199
+ | | [`gravity dev`](#gravity-dev) | Open a local dashboard to run and watch your agent |
199
200
  | | [`gravity schedule`](#gravity-schedule) | Run your agent on its schedule, locally |
200
201
  | | [`gravity adapt`](#gravity-adapt) | Write a new slot adapter with AI, kept only if its tests pass |
201
202
  | **Publish** | [`gravity build`](#gravity-build) | Package your agent into `build/agent.zip` |
@@ -211,7 +212,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
211
212
  | **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
212
213
  | | [`gravity help`](#gravity-help) | Show the list of commands |
213
214
 
214
- `gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
215
+ `gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`; it must start with `http://` or `https://`. The flag applies to the command it is given with; on later commands a `GRAVITY_API_URL` in `~/.gravity/.env` or the shell still wins over `config.json`, and the CLI says so when that is the case. `gravity --version` (or `-V`) prints the installed version. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
215
216
 
216
217
  ### `gravity init`
217
218
 
@@ -260,6 +261,9 @@ What it checks:
260
261
  - every imported write tool is under `approvals.required_for`
261
262
  - your code never names a write tool the manifest does not grant
262
263
  - a warning, never a failure, for read tools your code names but the manifest does not declare
264
+ - `graph` is an `async def` (or a compiled graph), not a plain `def` or a bare value; a name bound by an import counts
265
+ - nothing under `src/` reads `os.environ` or calls `asyncio.run` at module level, and no import is relative
266
+ - a schedule runs at most every 5 minutes and restricts the day of the month or the weekday, not both
263
267
 
264
268
  ```bash
265
269
  gravity validate
@@ -291,10 +295,33 @@ gravity run --reply "make it shorter"
291
295
  - Requires a gateway token: the one `gravity login` stores, or `GRAVITY_DEV_TOKEN`.
292
296
  - A call to a tool in `approvals.required_for` pauses, shows what it would do, and waits for `y` or `n`. With no terminal to ask in, it is rejected.
293
297
  - Exits with code 0 when the run finishes and 1 when it fails.
298
+ - Leaves a record in `.gravity/runs/<id>/` (`run.json` and `events.jsonl`), which is what `gravity dev` reads. One run is live per folder at a time, because runs share the folder's memory, store and thread files.
299
+
300
+ ### `gravity dev`
301
+
302
+ Open a local dashboard for the agent in this folder: a form built from the manifest's inputs, a live timeline of each step, model call and tool call, approval buttons for held calls, the result, and a history of runs.
303
+
304
+ ```
305
+ gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
306
+ ```
307
+
308
+ | Option | Description |
309
+ |---|---|
310
+ | `--port N` | Port to listen on (default 4747). If it is taken, a free one is used and printed. |
311
+ | `--no-open` | Print the address without opening a browser (SSH, WSL, CI) |
312
+ | `--llm-url URL` | Model endpoint to use instead of `GRAVITY_LLM_URL` |
313
+
314
+ - A run from `gravity run` in the same folder shows up in the dashboard live, and a run started in the dashboard is the same run (same engine, same memory, store and thread files), so `gravity run --reply` continues it.
315
+ - It listens on `127.0.0.1` only, behind a random token in the printed address, for as long as the command runs. Ctrl+C stops it and any run it started. Nothing keeps running in the background.
316
+ - Connected accounts: it shows the accounts your tools need and a Connect button for each one that is missing.
317
+ - Approvals: a call held for approval can be decided in the terminal or in the dashboard; whichever answers first wins.
318
+ - Cancel a run from the dashboard only if the dashboard started it; stop a terminal run with Ctrl+C in that terminal.
319
+ - Results show as text. Word, PowerPoint and CSV files are produced by Gravity after you push. A file input is read as text here.
320
+ - Same requirements as `gravity run`: a gateway token from `gravity login`, or `GRAVITY_DEV_TOKEN`.
294
321
 
295
322
  ### `gravity schedule`
296
323
 
297
- Run the agent on its `triggers.schedule`, the way the platform will, until you press Ctrl+C. Each tick is an ordinary run with the same inputs.
324
+ Run the agent on its `triggers.schedule`, the way the platform will, until you press Ctrl+C. Ticks are timed in Asia/Kolkata, as the hosted schedules are. Each tick is an ordinary run with the same inputs; a missing required input is refused before the first tick.
298
325
 
299
326
  ```
300
327
  gravity schedule [path] [--input KEY=VALUE]... [--cron EXPR] [--now]
@@ -334,7 +361,7 @@ gravity adapt <slot> <name> --tools TOOLS [--hint TEXT] [--model MODEL] [--path
334
361
  gravity adapt deliver slack --tools slack.send,slack.list_channels --hint "post to #leads"
335
362
  ```
336
363
 
337
- Requires `tests/test_<slot>_adapters.py` in the project, `GRAVITY_DEV_TOKEN` and a reachable gateway. It makes up to 3 attempts, feeding each test failure back to the model.
364
+ Requires `tests/test_<slot>_adapters.py` in the project and a gateway token (`gravity login`, or `GRAVITY_DEV_TOKEN`). It makes up to 3 attempts, feeding each test failure back to the model.
338
365
 
339
366
  ### `gravity build`
340
367
 
@@ -344,7 +371,7 @@ Validate the agent, then bundle it into `build/agent.zip` with its hashes in `bu
344
371
  gravity build [path]
345
372
  ```
346
373
 
347
- The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/` and `tests/`. The same source always produces the same bytes.
374
+ The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/`, `tests/` and `tools_needed.yaml` when present; dotfiles under `src/` and `tests/` are left out. It refuses to build without `requirements.lock` (run `gravity validate` first). The same source always produces the same bytes.
348
375
 
349
376
  ```bash
350
377
  gravity build
@@ -362,6 +389,9 @@ gravity push [path]
362
389
  - Refuses if there is no build, or if the source changed since the build. Run `gravity build` again.
363
390
  - A published version can never be replaced. Raise `version` in the manifest to publish again.
364
391
  - Press Ctrl+C to stop waiting; the build carries on, and `gravity agents` shows its status.
392
+ - The bundle may be at most 30 MB; a larger `agent.zip` is refused before upload. Remove large files from `src/` or `tests/` and build again.
393
+ - The manifest travels in a request header, so `manifest.yaml` must stay under 12 KB once encoded; a larger one is refused before upload. Keep descriptions and help text short.
394
+ - Re-pushing a version whose build failed, without changing the source, is refused with the failure reason: fix it and raise `version` in the manifest.
365
395
 
366
396
  ```bash
367
397
  gravity build && gravity push
@@ -369,7 +399,7 @@ gravity build && gravity push
369
399
 
370
400
  ### `gravity agents`
371
401
 
372
- List your published agents with their version, status and last update.
402
+ List your published agents with their version, status, review and last update. Under the table it prints what the Gravity team told you: the reviewer's notes on an approved or rejected version, and the reason if an agent was turned off. A rejected version needs a fix and a new `version:` in `manifest.yaml` before you push again. A failed build prints its reason under the table.
373
403
 
374
404
  ```
375
405
  gravity agents
@@ -428,7 +458,7 @@ gravity logout
428
458
 
429
459
  ### `gravity whoami`
430
460
 
431
- Show the account you are signed in as, and whether you hold a gateway token and until when.
461
+ Show the account you are signed in as, and whether you hold a gateway token and until when (an expired one is a warning).
432
462
 
433
463
  ```
434
464
  gravity whoami
@@ -628,7 +658,7 @@ resources:
628
658
  triggers:
629
659
  manual: true # the consumer can start a run
630
660
  schedule:
631
- cron: "0 8 * * 1-5" # weekdays at 08:00, local time
661
+ cron: "0 8 * * 1-5" # weekdays at 08:00, Asia/Kolkata
632
662
  consumer_can_change: true
633
663
 
634
664
  state:
@@ -672,7 +702,7 @@ outputs: # extra formats the platform renders and deliv
672
702
  | `approvals.required_for` | no | none | Tools that pause until a person approves. Each must also be declared under `permissions.tools` or a slot adapter. |
673
703
  | `resources.memory_mb` | no | `512` | 128 to 4096. A request; the platform may lower it. |
674
704
  | `triggers.manual` | no | `true` | Whether the consumer can start a run. Something must start the agent: `manual`, a `schedule` or a `webhook`. |
675
- | `triggers.schedule` | no | none | `cron` (5 fields, local time, such as `"0 9 * * 1"`) and `consumer_can_change` (default `true`). |
705
+ | `triggers.schedule` | no | none | `cron` (5 fields, Asia/Kolkata, such as `"0 9 * * 1"`; every 5 minutes at the fastest; day of month or weekday, not both) and `consumer_can_change` (default `true`). |
676
706
  | `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
677
707
  | `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
678
708
  | `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. |
@@ -700,6 +730,8 @@ Files the CLI writes:
700
730
  | `.gravity/payload.json` | `run` | The inputs passed to the agent |
701
731
  | `.gravity/thread.json` | `run` | The last conversation of a chat agent, for `--reply` |
702
732
  | `.gravity/memory.json` | `run` | Memory kept between runs, when the manifest declares `state` |
733
+ | `.gravity/payload.json` | `run`, `dev` | The run's inputs for the runner; deleted when the run ends (`run.json` keeps them with secret inputs redacted) |
734
+ | `.gravity/tool_schemas.json` | `adapt` | Argument schemas of every tool the slot uses, read from the gateway |
703
735
 
704
736
  The template `.gitignore` excludes `build/`, `.gravity/` and `.env`.
705
737
 
@@ -747,7 +779,12 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
747
779
  | `Can't reach the gateway at …` | No gateway at that address. Set `GRAVITY_GATEWAY_URL` to your gateway's `/mcp` address. |
748
780
  | `The gateway refused this run` | The gateway rejected the token or a declared tool. Run `gravity whoami` to check the token, or `gravity login` for a fresh one. |
749
781
  | `You are not signed in` | A control-plane command without credentials. Run `gravity login`. |
750
- | `Can't reach Gravity at …` | The control plane did not answer. Check `GRAVITY_API_URL` in `~/.gravity/.env`. |
782
+ | `Can't reach Gravity at …` | The control plane did not answer. Check `GRAVITY_API_URL` in `~/.gravity/.env`, or `api_url` in `~/.gravity/config.json` (set by `gravity --api-url`). |
783
+ | `Gravity refused GRAVITY_API_TOKEN` | The dev token in `~/.gravity/.env` is wrong or revoked. Fix it there, or remove it and run `gravity login`. |
784
+ | `… did not answer with JSON` or `Gravity answered 5xx at …` | A proxy, VPN sign-in page or outage is answering instead of Gravity. Check `GRAVITY_API_URL`; for a 5xx wait a minute and retry, then tell Gravity if it persists. |
785
+ | `Another run is live` | Runs in one folder take turns. Wait for it, or stop it; if none is live, delete `.gravity/run.lock`. |
786
+ | `Manifest is not UTF-8 text` | Notepad saved it as "Unicode" (UTF-16). Re-save it with Encoding: UTF-8. |
787
+ | `could not load the agent: No module named …` only on Windows | Windows limits paths to 260 characters and some packages in `.venv` exceed it from a deep folder. Move the agent to a shorter path or enable long paths. |
751
788
  | `Missing required input: …` | Pass each required input with `--input name=value`, or give it a `default` in the manifest. |
752
789
  | `No build to push` or `The build is stale` | Run `gravity build` again after any change to `src/`, the manifest or the requirements. |
753
790
  | `✗ requirements.lock` in `gravity validate` | A dependency has no prebuilt package for the platform image (ARM64 Linux). Pick a version that publishes one, or a different package. |
@@ -755,6 +792,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
755
792
 
756
793
  ## What's new
757
794
 
795
+ - **0.5.4** `gravity agents` shows each agent's review: a REVIEW column (not submitted, submitted, approved, rejected), and under "From the Gravity team" the reviewer's notes and what to do next. An agent the Gravity team turned off shows as `off` with the reason. UPDATED reads "3 min ago" instead of a timestamp, and status and review are coloured. Also, from a full audit of every command: `gravity --version`. Clear messages instead of tracebacks for: a proxy or sign-in page answering instead of Gravity, a refused `GRAVITY_API_TOKEN` or `GRAVITY_DEV_TOKEN`, an expired sign-in code, a non-UTF-8 manifest, an out-of-range `--port`, a missing agent folder, a malformed session file. `gravity validate` now refuses what the build would refuse: a `graph` that is not an async factory, `os.environ` or `asyncio.run` at import, relative imports, unquoted `python: 3.12`, schedules faster than every 5 minutes or with both day fields, out-of-range cron fields; it accepts a re-exported `graph`. `gravity agents` shows the review status and failure reasons; `gravity runs` never shortens an id; `gravity logs` never wraps. `gravity push` refuses a bundle over 30 MB or a manifest over 12 KB before uploading, and says why when a failed version is re-pushed. `gravity build` ships `tools_needed.yaml`, leaves dotfiles out, and refuses to build without `requirements.lock`. `gravity adapt` leaves the folder untouched when a model call fails. `gravity schedule` checks inputs before the first tick and runs on Asia/Kolkata like the hosted schedules. `gravity run` deletes `.gravity/payload.json` after the run, warns when no `.venv` is present, and keeps a plain agent's `result`. `gravity connect <app>` refuses on the shared test account instead of attaching your account to it. The builder guide and agent standard describe the real flow (init, validate, run or dev, build, push, review on /app).
796
+ - **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,402 across 15 apps including the curated and built-in ones). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
758
797
  - **0.5.2** Jira, Confluence and Notion join the catalog: 1,351 tools across 11 apps. `get_store()` works in local runs, kept in `.gravity/store.json`. Renewing an expired sign-in keeps your gateway token, so `gravity run` keeps working after an hour. Commands start faster: the catalog loads in a quarter of the time.
759
798
  - **0.5.0** `gravity connect` links your own accounts. `web.search` and `web.fetch` join the catalog. `gravity validate` now refuses a tool that isn't in the catalog, an imported write tool without an approval, and code that names a write tool the manifest doesn't grant. `gravity tools` reads the gateway's list and needs no sign-in. On Windows the session stays in the keychain instead of a file.
760
799
  - **0.4.4** Same code as 0.4.3, released from this repository so the GitHub release, the tag and PyPI line up again. Adds `RELEASING.md`.
@@ -91,10 +91,10 @@ The same rules apply on your laptop and when the agent runs hosted, because both
91
91
 
92
92
  | | |
93
93
  |---|---|
94
- | Commands | 16, grouped into build, publish, inspect, account and help |
94
+ | Commands | 18, grouped into build an agent, publish, your agents, account and help |
95
95
  | Starter templates | 4: `minimal`, `structured`, `deepagent`, `chat` |
96
- | Tool catalog | 1,139 tools across 8 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn), 25 of them curated |
97
- | Test suite | 285 tests, run on Ubuntu and Windows with Python 3.12 and 3.13 |
96
+ | Tool catalog | 1,402 tools across 15 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn, Jira, Confluence, Notion, WhatsApp, web search, and two built-ins), 28 of them curated |
97
+ | Test suite | 466 tests (214 CLI, 252 schema), run on Ubuntu and Windows with Python 3.12 and 3.13 |
98
98
  | Runtime contract | 3 environment variables, no Gravity import in your code |
99
99
 
100
100
  ## Five things to know before you start
@@ -179,6 +179,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
179
179
  | **Build an agent** | [`gravity init`](#gravity-init) | Create a new agent from a template |
180
180
  | | [`gravity validate`](#gravity-validate) | Check your agent for mistakes before it runs |
181
181
  | | [`gravity run`](#gravity-run) | Run your agent locally against real tools |
182
+ | | [`gravity dev`](#gravity-dev) | Open a local dashboard to run and watch your agent |
182
183
  | | [`gravity schedule`](#gravity-schedule) | Run your agent on its schedule, locally |
183
184
  | | [`gravity adapt`](#gravity-adapt) | Write a new slot adapter with AI, kept only if its tests pass |
184
185
  | **Publish** | [`gravity build`](#gravity-build) | Package your agent into `build/agent.zip` |
@@ -194,7 +195,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
194
195
  | **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
195
196
  | | [`gravity help`](#gravity-help) | Show the list of commands |
196
197
 
197
- `gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
198
+ `gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`; it must start with `http://` or `https://`. The flag applies to the command it is given with; on later commands a `GRAVITY_API_URL` in `~/.gravity/.env` or the shell still wins over `config.json`, and the CLI says so when that is the case. `gravity --version` (or `-V`) prints the installed version. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
198
199
 
199
200
  ### `gravity init`
200
201
 
@@ -243,6 +244,9 @@ What it checks:
243
244
  - every imported write tool is under `approvals.required_for`
244
245
  - your code never names a write tool the manifest does not grant
245
246
  - a warning, never a failure, for read tools your code names but the manifest does not declare
247
+ - `graph` is an `async def` (or a compiled graph), not a plain `def` or a bare value; a name bound by an import counts
248
+ - nothing under `src/` reads `os.environ` or calls `asyncio.run` at module level, and no import is relative
249
+ - a schedule runs at most every 5 minutes and restricts the day of the month or the weekday, not both
246
250
 
247
251
  ```bash
248
252
  gravity validate
@@ -274,10 +278,33 @@ gravity run --reply "make it shorter"
274
278
  - Requires a gateway token: the one `gravity login` stores, or `GRAVITY_DEV_TOKEN`.
275
279
  - A call to a tool in `approvals.required_for` pauses, shows what it would do, and waits for `y` or `n`. With no terminal to ask in, it is rejected.
276
280
  - Exits with code 0 when the run finishes and 1 when it fails.
281
+ - Leaves a record in `.gravity/runs/<id>/` (`run.json` and `events.jsonl`), which is what `gravity dev` reads. One run is live per folder at a time, because runs share the folder's memory, store and thread files.
282
+
283
+ ### `gravity dev`
284
+
285
+ Open a local dashboard for the agent in this folder: a form built from the manifest's inputs, a live timeline of each step, model call and tool call, approval buttons for held calls, the result, and a history of runs.
286
+
287
+ ```
288
+ gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
289
+ ```
290
+
291
+ | Option | Description |
292
+ |---|---|
293
+ | `--port N` | Port to listen on (default 4747). If it is taken, a free one is used and printed. |
294
+ | `--no-open` | Print the address without opening a browser (SSH, WSL, CI) |
295
+ | `--llm-url URL` | Model endpoint to use instead of `GRAVITY_LLM_URL` |
296
+
297
+ - A run from `gravity run` in the same folder shows up in the dashboard live, and a run started in the dashboard is the same run (same engine, same memory, store and thread files), so `gravity run --reply` continues it.
298
+ - It listens on `127.0.0.1` only, behind a random token in the printed address, for as long as the command runs. Ctrl+C stops it and any run it started. Nothing keeps running in the background.
299
+ - Connected accounts: it shows the accounts your tools need and a Connect button for each one that is missing.
300
+ - Approvals: a call held for approval can be decided in the terminal or in the dashboard; whichever answers first wins.
301
+ - Cancel a run from the dashboard only if the dashboard started it; stop a terminal run with Ctrl+C in that terminal.
302
+ - Results show as text. Word, PowerPoint and CSV files are produced by Gravity after you push. A file input is read as text here.
303
+ - Same requirements as `gravity run`: a gateway token from `gravity login`, or `GRAVITY_DEV_TOKEN`.
277
304
 
278
305
  ### `gravity schedule`
279
306
 
280
- Run the agent on its `triggers.schedule`, the way the platform will, until you press Ctrl+C. Each tick is an ordinary run with the same inputs.
307
+ Run the agent on its `triggers.schedule`, the way the platform will, until you press Ctrl+C. Ticks are timed in Asia/Kolkata, as the hosted schedules are. Each tick is an ordinary run with the same inputs; a missing required input is refused before the first tick.
281
308
 
282
309
  ```
283
310
  gravity schedule [path] [--input KEY=VALUE]... [--cron EXPR] [--now]
@@ -317,7 +344,7 @@ gravity adapt <slot> <name> --tools TOOLS [--hint TEXT] [--model MODEL] [--path
317
344
  gravity adapt deliver slack --tools slack.send,slack.list_channels --hint "post to #leads"
318
345
  ```
319
346
 
320
- Requires `tests/test_<slot>_adapters.py` in the project, `GRAVITY_DEV_TOKEN` and a reachable gateway. It makes up to 3 attempts, feeding each test failure back to the model.
347
+ Requires `tests/test_<slot>_adapters.py` in the project and a gateway token (`gravity login`, or `GRAVITY_DEV_TOKEN`). It makes up to 3 attempts, feeding each test failure back to the model.
321
348
 
322
349
  ### `gravity build`
323
350
 
@@ -327,7 +354,7 @@ Validate the agent, then bundle it into `build/agent.zip` with its hashes in `bu
327
354
  gravity build [path]
328
355
  ```
329
356
 
330
- The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/` and `tests/`. The same source always produces the same bytes.
357
+ The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/`, `tests/` and `tools_needed.yaml` when present; dotfiles under `src/` and `tests/` are left out. It refuses to build without `requirements.lock` (run `gravity validate` first). The same source always produces the same bytes.
331
358
 
332
359
  ```bash
333
360
  gravity build
@@ -345,6 +372,9 @@ gravity push [path]
345
372
  - Refuses if there is no build, or if the source changed since the build. Run `gravity build` again.
346
373
  - A published version can never be replaced. Raise `version` in the manifest to publish again.
347
374
  - Press Ctrl+C to stop waiting; the build carries on, and `gravity agents` shows its status.
375
+ - The bundle may be at most 30 MB; a larger `agent.zip` is refused before upload. Remove large files from `src/` or `tests/` and build again.
376
+ - The manifest travels in a request header, so `manifest.yaml` must stay under 12 KB once encoded; a larger one is refused before upload. Keep descriptions and help text short.
377
+ - Re-pushing a version whose build failed, without changing the source, is refused with the failure reason: fix it and raise `version` in the manifest.
348
378
 
349
379
  ```bash
350
380
  gravity build && gravity push
@@ -352,7 +382,7 @@ gravity build && gravity push
352
382
 
353
383
  ### `gravity agents`
354
384
 
355
- List your published agents with their version, status and last update.
385
+ List your published agents with their version, status, review and last update. Under the table it prints what the Gravity team told you: the reviewer's notes on an approved or rejected version, and the reason if an agent was turned off. A rejected version needs a fix and a new `version:` in `manifest.yaml` before you push again. A failed build prints its reason under the table.
356
386
 
357
387
  ```
358
388
  gravity agents
@@ -411,7 +441,7 @@ gravity logout
411
441
 
412
442
  ### `gravity whoami`
413
443
 
414
- Show the account you are signed in as, and whether you hold a gateway token and until when.
444
+ Show the account you are signed in as, and whether you hold a gateway token and until when (an expired one is a warning).
415
445
 
416
446
  ```
417
447
  gravity whoami
@@ -611,7 +641,7 @@ resources:
611
641
  triggers:
612
642
  manual: true # the consumer can start a run
613
643
  schedule:
614
- cron: "0 8 * * 1-5" # weekdays at 08:00, local time
644
+ cron: "0 8 * * 1-5" # weekdays at 08:00, Asia/Kolkata
615
645
  consumer_can_change: true
616
646
 
617
647
  state:
@@ -655,7 +685,7 @@ outputs: # extra formats the platform renders and deliv
655
685
  | `approvals.required_for` | no | none | Tools that pause until a person approves. Each must also be declared under `permissions.tools` or a slot adapter. |
656
686
  | `resources.memory_mb` | no | `512` | 128 to 4096. A request; the platform may lower it. |
657
687
  | `triggers.manual` | no | `true` | Whether the consumer can start a run. Something must start the agent: `manual`, a `schedule` or a `webhook`. |
658
- | `triggers.schedule` | no | none | `cron` (5 fields, local time, such as `"0 9 * * 1"`) and `consumer_can_change` (default `true`). |
688
+ | `triggers.schedule` | no | none | `cron` (5 fields, Asia/Kolkata, such as `"0 9 * * 1"`; every 5 minutes at the fastest; day of month or weekday, not both) and `consumer_can_change` (default `true`). |
659
689
  | `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
660
690
  | `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
661
691
  | `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. |
@@ -683,6 +713,8 @@ Files the CLI writes:
683
713
  | `.gravity/payload.json` | `run` | The inputs passed to the agent |
684
714
  | `.gravity/thread.json` | `run` | The last conversation of a chat agent, for `--reply` |
685
715
  | `.gravity/memory.json` | `run` | Memory kept between runs, when the manifest declares `state` |
716
+ | `.gravity/payload.json` | `run`, `dev` | The run's inputs for the runner; deleted when the run ends (`run.json` keeps them with secret inputs redacted) |
717
+ | `.gravity/tool_schemas.json` | `adapt` | Argument schemas of every tool the slot uses, read from the gateway |
686
718
 
687
719
  The template `.gitignore` excludes `build/`, `.gravity/` and `.env`.
688
720
 
@@ -730,7 +762,12 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
730
762
  | `Can't reach the gateway at …` | No gateway at that address. Set `GRAVITY_GATEWAY_URL` to your gateway's `/mcp` address. |
731
763
  | `The gateway refused this run` | The gateway rejected the token or a declared tool. Run `gravity whoami` to check the token, or `gravity login` for a fresh one. |
732
764
  | `You are not signed in` | A control-plane command without credentials. Run `gravity login`. |
733
- | `Can't reach Gravity at …` | The control plane did not answer. Check `GRAVITY_API_URL` in `~/.gravity/.env`. |
765
+ | `Can't reach Gravity at …` | The control plane did not answer. Check `GRAVITY_API_URL` in `~/.gravity/.env`, or `api_url` in `~/.gravity/config.json` (set by `gravity --api-url`). |
766
+ | `Gravity refused GRAVITY_API_TOKEN` | The dev token in `~/.gravity/.env` is wrong or revoked. Fix it there, or remove it and run `gravity login`. |
767
+ | `… did not answer with JSON` or `Gravity answered 5xx at …` | A proxy, VPN sign-in page or outage is answering instead of Gravity. Check `GRAVITY_API_URL`; for a 5xx wait a minute and retry, then tell Gravity if it persists. |
768
+ | `Another run is live` | Runs in one folder take turns. Wait for it, or stop it; if none is live, delete `.gravity/run.lock`. |
769
+ | `Manifest is not UTF-8 text` | Notepad saved it as "Unicode" (UTF-16). Re-save it with Encoding: UTF-8. |
770
+ | `could not load the agent: No module named …` only on Windows | Windows limits paths to 260 characters and some packages in `.venv` exceed it from a deep folder. Move the agent to a shorter path or enable long paths. |
734
771
  | `Missing required input: …` | Pass each required input with `--input name=value`, or give it a `default` in the manifest. |
735
772
  | `No build to push` or `The build is stale` | Run `gravity build` again after any change to `src/`, the manifest or the requirements. |
736
773
  | `✗ requirements.lock` in `gravity validate` | A dependency has no prebuilt package for the platform image (ARM64 Linux). Pick a version that publishes one, or a different package. |
@@ -738,6 +775,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
738
775
 
739
776
  ## What's new
740
777
 
778
+ - **0.5.4** `gravity agents` shows each agent's review: a REVIEW column (not submitted, submitted, approved, rejected), and under "From the Gravity team" the reviewer's notes and what to do next. An agent the Gravity team turned off shows as `off` with the reason. UPDATED reads "3 min ago" instead of a timestamp, and status and review are coloured. Also, from a full audit of every command: `gravity --version`. Clear messages instead of tracebacks for: a proxy or sign-in page answering instead of Gravity, a refused `GRAVITY_API_TOKEN` or `GRAVITY_DEV_TOKEN`, an expired sign-in code, a non-UTF-8 manifest, an out-of-range `--port`, a missing agent folder, a malformed session file. `gravity validate` now refuses what the build would refuse: a `graph` that is not an async factory, `os.environ` or `asyncio.run` at import, relative imports, unquoted `python: 3.12`, schedules faster than every 5 minutes or with both day fields, out-of-range cron fields; it accepts a re-exported `graph`. `gravity agents` shows the review status and failure reasons; `gravity runs` never shortens an id; `gravity logs` never wraps. `gravity push` refuses a bundle over 30 MB or a manifest over 12 KB before uploading, and says why when a failed version is re-pushed. `gravity build` ships `tools_needed.yaml`, leaves dotfiles out, and refuses to build without `requirements.lock`. `gravity adapt` leaves the folder untouched when a model call fails. `gravity schedule` checks inputs before the first tick and runs on Asia/Kolkata like the hosted schedules. `gravity run` deletes `.gravity/payload.json` after the run, warns when no `.venv` is present, and keeps a plain agent's `result`. `gravity connect <app>` refuses on the shared test account instead of attaching your account to it. The builder guide and agent standard describe the real flow (init, validate, run or dev, build, push, review on /app).
779
+ - **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,402 across 15 apps including the curated and built-in ones). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
741
780
  - **0.5.2** Jira, Confluence and Notion join the catalog: 1,351 tools across 11 apps. `get_store()` works in local runs, kept in `.gravity/store.json`. Renewing an expired sign-in keeps your gateway token, so `gravity run` keeps working after an hour. Commands start faster: the catalog loads in a quarter of the time.
742
781
  - **0.5.0** `gravity connect` links your own accounts. `web.search` and `web.fetch` join the catalog. `gravity validate` now refuses a tool that isn't in the catalog, an imported write tool without an approval, and code that names a write tool the manifest doesn't grant. `gravity tools` reads the gateway's list and needs no sign-in. On Windows the session stays in the keychain instead of a file.
743
782
  - **0.4.4** Same code as 0.4.3, released from this repository so the GitHub release, the tag and PyPI line up again. Adds `RELEASING.md`.