gravity-cli 0.5.3__tar.gz → 0.5.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.
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/AGENT_STANDARD.md +20 -18
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/PKG-INFO +30 -15
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/README.md +28 -13
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/cron.py +26 -1
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/manifest.py +91 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/validation.py +11 -1
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/pyproject.toml +1 -1
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/schema/manifest.schema.json +97 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_cron.py +16 -1
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_manifest.py +127 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/pyproject.toml +2 -2
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/BUILDER_GUIDE.md +37 -26
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/_runner_script.py +9 -3
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/api.py +57 -15
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/cli.py +13 -0
- gravity_cli-0.5.5/src/gravity_cli/commands/adapt.py +182 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/build.py +13 -2
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/connect.py +21 -4
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/dev.py +1 -1
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/login.py +19 -3
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/push.py +16 -2
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/run.py +30 -4
- gravity_cli-0.5.5/src/gravity_cli/commands/runs.py +103 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/schedule.py +23 -6
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/validate.py +95 -19
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/config.py +27 -8
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/dev.py +9 -3
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/project.py +7 -2
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/timeline.py +3 -2
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/ui.py +49 -6
- gravity_cli-0.5.5/tests/test_adapt.py +134 -0
- gravity_cli-0.5.5/tests/test_agents.py +91 -0
- gravity_cli-0.5.5/tests/test_api.py +218 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_build_push.py +70 -3
- gravity_cli-0.5.5/tests/test_connect.py +146 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_dev.py +27 -3
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_run.py +71 -7
- gravity_cli-0.5.5/tests/test_runs.py +91 -0
- gravity_cli-0.5.5/tests/test_schedule.py +63 -0
- gravity_cli-0.5.5/tests/test_session.py +146 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_templates.py +21 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_ui.py +14 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_validate.py +69 -2
- gravity_cli-0.5.5/tests/test_welcome.py +98 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/uv.lock +2 -2
- gravity_cli-0.5.3/src/gravity_cli/commands/adapt.py +0 -154
- gravity_cli-0.5.3/src/gravity_cli/commands/runs.py +0 -49
- gravity_cli-0.5.3/tests/test_adapt.py +0 -71
- gravity_cli-0.5.3/tests/test_api.py +0 -101
- gravity_cli-0.5.3/tests/test_connect.py +0 -78
- gravity_cli-0.5.3/tests/test_session.py +0 -80
- gravity_cli-0.5.3/tests/test_welcome.py +0 -43
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/.env.example +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/.github/workflows/ci.yml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/.github/workflows/release.yml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/.gitignore +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/LICENSE +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/NOTICE +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/RELEASING.md +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/TEMPLATES.md +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/LICENSE +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/NOTICE +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/README.md +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/catalog.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/contract.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/errors.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/export.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/conftest.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_errors.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_json_schema.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_slots.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_source_scan.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/tests/test_validation.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/__init__.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/__main__.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/__init__.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/guide.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/init.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/commands/tools.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/dev_page/index.html +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/localrun.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/manifest.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/chat/.gitignore +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/chat/requirements.txt +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/chat/src/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/minimal/.gitignore +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/.gitignore +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/requirements.txt +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/src/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/src/gravity_cli/templates/structured/src/state.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/starter/manifest.yaml +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/starter/requirements.txt +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/starter/src/agent.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/starter/src/summary.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/starter/tests/test_summary.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/conftest.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_gateway_token.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_localrun.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_manifest_integration.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/tests/test_timeline.py +0 -0
- {gravity_cli-0.5.3 → gravity_cli-0.5.5}/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.
|
|
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 (`
|
|
82
|
-
no answer is a no)
|
|
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()) #
|
|
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.
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
|
@@ -241,12 +243,12 @@ Windows or macOS pins the wrong wheels and only fails at deploy.
|
|
|
241
243
|
|
|
242
244
|
`gravity run --input k=v` runs your graph in your own venv against a real
|
|
243
245
|
gateway, exactly as production does: tools over `GRAVITY_GATEWAY_URL`,
|
|
244
|
-
models over `GRAVITY_LLM_URL`, both with `GRAVITY_RUN_TOKEN`.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
`
|
|
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.
|
|
250
252
|
|
|
251
253
|
`gravity dev` opens a local dashboard over the same runs: a form from your
|
|
252
254
|
manifest, a live timeline, approvals, the result and a history. A run from
|
|
@@ -275,10 +277,10 @@ graph. `gravity` is preinstalled in the image; don't put it in
|
|
|
275
277
|
|
|
276
278
|
**Tools: `await gravity.tools()`** instead of your own `MultiServerMCPClient`.
|
|
277
279
|
The same LangChain tools under the same names (`gmail_read`), from the same env
|
|
278
|
-
vars. Off the platform (`gravity run`, tests)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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.
|
|
282
284
|
On the platform, each tool in `approvals.required_for` is wrapped: calling it
|
|
283
285
|
checkpoints the run and stops it until the consumer decides, with no
|
|
284
286
|
connection held open. Approved, the call runs with the arguments the consumer
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gravity-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.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.5.
|
|
9
|
+
Requires-Dist: gravity-schema==0.5.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
|
|
@@ -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 |
|
|
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,
|
|
114
|
-
| Test suite |
|
|
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
|
|
@@ -212,7 +212,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
212
212
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
213
213
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
214
214
|
|
|
215
|
-
`gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json
|
|
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.
|
|
216
216
|
|
|
217
217
|
### `gravity init`
|
|
218
218
|
|
|
@@ -261,6 +261,9 @@ What it checks:
|
|
|
261
261
|
- every imported write tool is under `approvals.required_for`
|
|
262
262
|
- your code never names a write tool the manifest does not grant
|
|
263
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
|
|
264
267
|
|
|
265
268
|
```bash
|
|
266
269
|
gravity validate
|
|
@@ -318,7 +321,7 @@ gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
|
|
|
318
321
|
|
|
319
322
|
### `gravity schedule`
|
|
320
323
|
|
|
321
|
-
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.
|
|
322
325
|
|
|
323
326
|
```
|
|
324
327
|
gravity schedule [path] [--input KEY=VALUE]... [--cron EXPR] [--now]
|
|
@@ -358,7 +361,7 @@ gravity adapt <slot> <name> --tools TOOLS [--hint TEXT] [--model MODEL] [--path
|
|
|
358
361
|
gravity adapt deliver slack --tools slack.send,slack.list_channels --hint "post to #leads"
|
|
359
362
|
```
|
|
360
363
|
|
|
361
|
-
Requires `tests/test_<slot>_adapters.py` in the project
|
|
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.
|
|
362
365
|
|
|
363
366
|
### `gravity build`
|
|
364
367
|
|
|
@@ -368,7 +371,7 @@ Validate the agent, then bundle it into `build/agent.zip` with its hashes in `bu
|
|
|
368
371
|
gravity build [path]
|
|
369
372
|
```
|
|
370
373
|
|
|
371
|
-
The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/` and `tests
|
|
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.
|
|
372
375
|
|
|
373
376
|
```bash
|
|
374
377
|
gravity build
|
|
@@ -386,6 +389,9 @@ gravity push [path]
|
|
|
386
389
|
- Refuses if there is no build, or if the source changed since the build. Run `gravity build` again.
|
|
387
390
|
- A published version can never be replaced. Raise `version` in the manifest to publish again.
|
|
388
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.
|
|
389
395
|
|
|
390
396
|
```bash
|
|
391
397
|
gravity build && gravity push
|
|
@@ -393,7 +399,7 @@ gravity build && gravity push
|
|
|
393
399
|
|
|
394
400
|
### `gravity agents`
|
|
395
401
|
|
|
396
|
-
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.
|
|
397
403
|
|
|
398
404
|
```
|
|
399
405
|
gravity agents
|
|
@@ -452,7 +458,7 @@ gravity logout
|
|
|
452
458
|
|
|
453
459
|
### `gravity whoami`
|
|
454
460
|
|
|
455
|
-
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).
|
|
456
462
|
|
|
457
463
|
```
|
|
458
464
|
gravity whoami
|
|
@@ -652,7 +658,7 @@ resources:
|
|
|
652
658
|
triggers:
|
|
653
659
|
manual: true # the consumer can start a run
|
|
654
660
|
schedule:
|
|
655
|
-
cron: "0 8 * * 1-5" # weekdays at 08:00,
|
|
661
|
+
cron: "0 8 * * 1-5" # weekdays at 08:00, Asia/Kolkata
|
|
656
662
|
consumer_can_change: true
|
|
657
663
|
|
|
658
664
|
state:
|
|
@@ -696,7 +702,7 @@ outputs: # extra formats the platform renders and deliv
|
|
|
696
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. |
|
|
697
703
|
| `resources.memory_mb` | no | `512` | 128 to 4096. A request; the platform may lower it. |
|
|
698
704
|
| `triggers.manual` | no | `true` | Whether the consumer can start a run. Something must start the agent: `manual`, a `schedule` or a `webhook`. |
|
|
699
|
-
| `triggers.schedule` | no | none | `cron` (5 fields,
|
|
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`). |
|
|
700
706
|
| `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
|
|
701
707
|
| `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
|
|
702
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. |
|
|
@@ -724,6 +730,8 @@ Files the CLI writes:
|
|
|
724
730
|
| `.gravity/payload.json` | `run` | The inputs passed to the agent |
|
|
725
731
|
| `.gravity/thread.json` | `run` | The last conversation of a chat agent, for `--reply` |
|
|
726
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 |
|
|
727
735
|
|
|
728
736
|
The template `.gitignore` excludes `build/`, `.gravity/` and `.env`.
|
|
729
737
|
|
|
@@ -771,7 +779,12 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
771
779
|
| `Can't reach the gateway at …` | No gateway at that address. Set `GRAVITY_GATEWAY_URL` to your gateway's `/mcp` address. |
|
|
772
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. |
|
|
773
781
|
| `You are not signed in` | A control-plane command without credentials. Run `gravity login`. |
|
|
774
|
-
| `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. |
|
|
775
788
|
| `Missing required input: …` | Pass each required input with `--input name=value`, or give it a `default` in the manifest. |
|
|
776
789
|
| `No build to push` or `The build is stale` | Run `gravity build` again after any change to `src/`, the manifest or the requirements. |
|
|
777
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. |
|
|
@@ -779,7 +792,9 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
779
792
|
|
|
780
793
|
## What's new
|
|
781
794
|
|
|
782
|
-
- **0.5.
|
|
795
|
+
- **0.5.5** `manifest.yaml` takes an optional `display:` hint for how a run's result shows: `view` (document, report, table, chart, code or files), `field` (the result key to show) and `chart` (`type: line` or `bar`, `x`, and up to three `y` fields). It is a hint, never a rule: an unknown view or key is ignored, so it never fails a push. 0.5.4 and earlier refuse a manifest that has it.
|
|
796
|
+
- **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).
|
|
797
|
+
- **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.
|
|
783
798
|
- **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.
|
|
784
799
|
- **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.
|
|
785
800
|
- **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 |
|
|
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,
|
|
97
|
-
| Test suite |
|
|
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
|
|
@@ -195,7 +195,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
195
195
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
196
196
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
197
197
|
|
|
198
|
-
`gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json
|
|
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.
|
|
199
199
|
|
|
200
200
|
### `gravity init`
|
|
201
201
|
|
|
@@ -244,6 +244,9 @@ What it checks:
|
|
|
244
244
|
- every imported write tool is under `approvals.required_for`
|
|
245
245
|
- your code never names a write tool the manifest does not grant
|
|
246
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
|
|
247
250
|
|
|
248
251
|
```bash
|
|
249
252
|
gravity validate
|
|
@@ -301,7 +304,7 @@ gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
|
|
|
301
304
|
|
|
302
305
|
### `gravity schedule`
|
|
303
306
|
|
|
304
|
-
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.
|
|
305
308
|
|
|
306
309
|
```
|
|
307
310
|
gravity schedule [path] [--input KEY=VALUE]... [--cron EXPR] [--now]
|
|
@@ -341,7 +344,7 @@ gravity adapt <slot> <name> --tools TOOLS [--hint TEXT] [--model MODEL] [--path
|
|
|
341
344
|
gravity adapt deliver slack --tools slack.send,slack.list_channels --hint "post to #leads"
|
|
342
345
|
```
|
|
343
346
|
|
|
344
|
-
Requires `tests/test_<slot>_adapters.py` in the project
|
|
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.
|
|
345
348
|
|
|
346
349
|
### `gravity build`
|
|
347
350
|
|
|
@@ -351,7 +354,7 @@ Validate the agent, then bundle it into `build/agent.zip` with its hashes in `bu
|
|
|
351
354
|
gravity build [path]
|
|
352
355
|
```
|
|
353
356
|
|
|
354
|
-
The bundle holds `manifest.yaml`, `requirements.txt`, `requirements.lock`, `src/` and `tests
|
|
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.
|
|
355
358
|
|
|
356
359
|
```bash
|
|
357
360
|
gravity build
|
|
@@ -369,6 +372,9 @@ gravity push [path]
|
|
|
369
372
|
- Refuses if there is no build, or if the source changed since the build. Run `gravity build` again.
|
|
370
373
|
- A published version can never be replaced. Raise `version` in the manifest to publish again.
|
|
371
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.
|
|
372
378
|
|
|
373
379
|
```bash
|
|
374
380
|
gravity build && gravity push
|
|
@@ -376,7 +382,7 @@ gravity build && gravity push
|
|
|
376
382
|
|
|
377
383
|
### `gravity agents`
|
|
378
384
|
|
|
379
|
-
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.
|
|
380
386
|
|
|
381
387
|
```
|
|
382
388
|
gravity agents
|
|
@@ -435,7 +441,7 @@ gravity logout
|
|
|
435
441
|
|
|
436
442
|
### `gravity whoami`
|
|
437
443
|
|
|
438
|
-
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).
|
|
439
445
|
|
|
440
446
|
```
|
|
441
447
|
gravity whoami
|
|
@@ -635,7 +641,7 @@ resources:
|
|
|
635
641
|
triggers:
|
|
636
642
|
manual: true # the consumer can start a run
|
|
637
643
|
schedule:
|
|
638
|
-
cron: "0 8 * * 1-5" # weekdays at 08:00,
|
|
644
|
+
cron: "0 8 * * 1-5" # weekdays at 08:00, Asia/Kolkata
|
|
639
645
|
consumer_can_change: true
|
|
640
646
|
|
|
641
647
|
state:
|
|
@@ -679,7 +685,7 @@ outputs: # extra formats the platform renders and deliv
|
|
|
679
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. |
|
|
680
686
|
| `resources.memory_mb` | no | `512` | 128 to 4096. A request; the platform may lower it. |
|
|
681
687
|
| `triggers.manual` | no | `true` | Whether the consumer can start a run. Something must start the agent: `manual`, a `schedule` or a `webhook`. |
|
|
682
|
-
| `triggers.schedule` | no | none | `cron` (5 fields,
|
|
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`). |
|
|
683
689
|
| `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
|
|
684
690
|
| `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
|
|
685
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. |
|
|
@@ -707,6 +713,8 @@ Files the CLI writes:
|
|
|
707
713
|
| `.gravity/payload.json` | `run` | The inputs passed to the agent |
|
|
708
714
|
| `.gravity/thread.json` | `run` | The last conversation of a chat agent, for `--reply` |
|
|
709
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 |
|
|
710
718
|
|
|
711
719
|
The template `.gitignore` excludes `build/`, `.gravity/` and `.env`.
|
|
712
720
|
|
|
@@ -754,7 +762,12 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
754
762
|
| `Can't reach the gateway at …` | No gateway at that address. Set `GRAVITY_GATEWAY_URL` to your gateway's `/mcp` address. |
|
|
755
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. |
|
|
756
764
|
| `You are not signed in` | A control-plane command without credentials. Run `gravity login`. |
|
|
757
|
-
| `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. |
|
|
758
771
|
| `Missing required input: …` | Pass each required input with `--input name=value`, or give it a `default` in the manifest. |
|
|
759
772
|
| `No build to push` or `The build is stale` | Run `gravity build` again after any change to `src/`, the manifest or the requirements. |
|
|
760
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. |
|
|
@@ -762,7 +775,9 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
762
775
|
|
|
763
776
|
## What's new
|
|
764
777
|
|
|
765
|
-
- **0.5.
|
|
778
|
+
- **0.5.5** `manifest.yaml` takes an optional `display:` hint for how a run's result shows: `view` (document, report, table, chart, code or files), `field` (the result key to show) and `chart` (`type: line` or `bar`, `x`, and up to three `y` fields). It is a hint, never a rule: an unknown view or key is ignored, so it never fails a push. 0.5.4 and earlier refuse a manifest that has it.
|
|
779
|
+
- **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).
|
|
780
|
+
- **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.
|
|
766
781
|
- **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.
|
|
767
782
|
- **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.
|
|
768
783
|
- **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`.
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
"""Standard 5-field cron,
|
|
1
|
+
"""Standard 5-field cron, in whatever timezone the `after` datetime carries (gravity
|
|
2
|
+
schedule passes Asia/Kolkata, like the hosted schedules). Day-of-week 0 or 7 is Sunday; when both day
|
|
2
3
|
fields are restricted, either matching is enough (as in cron itself)."""
|
|
3
4
|
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
7
|
+
import math
|
|
6
8
|
from datetime import datetime, timedelta
|
|
9
|
+
from itertools import pairwise
|
|
7
10
|
|
|
8
11
|
_BOUNDS = ((0, 59), (0, 23), (1, 31), (1, 12), (0, 7))
|
|
12
|
+
# Every tick is a paid run; the control plane's MIN_SCHEDULE_MINUTES.
|
|
13
|
+
MIN_GAP_MINUTES = 5
|
|
9
14
|
|
|
10
15
|
|
|
11
16
|
def _values(field: str, low: int, high: int) -> set[int]:
|
|
@@ -22,6 +27,26 @@ def _values(field: str, low: int, high: int) -> set[int]:
|
|
|
22
27
|
return out
|
|
23
28
|
|
|
24
29
|
|
|
30
|
+
def field_values(expr: str) -> list[set[int]]:
|
|
31
|
+
"""Each field's values. ValueError when a field is malformed, empty or out of its range."""
|
|
32
|
+
out = [_values(f, lo, hi) for f, (lo, hi) in zip(expr.split(), _BOUNDS)]
|
|
33
|
+
for vals, (lo, hi) in zip(out, _BOUNDS):
|
|
34
|
+
if not vals or min(vals) < lo or max(vals) > hi:
|
|
35
|
+
raise ValueError(f"'{expr}' has a value out of range")
|
|
36
|
+
return out
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def min_gap(expr: str) -> float:
|
|
40
|
+
"""Minutes between the closest two fires, from the minute and hour fields alone (the day
|
|
41
|
+
fields can only widen it). Same arithmetic as the control plane's checkScheduleFloor."""
|
|
42
|
+
minutes, hours = field_values(expr)[:2]
|
|
43
|
+
mins = sorted(minutes)
|
|
44
|
+
gap = min((b - a for a, b in pairwise(mins)), default=math.inf)
|
|
45
|
+
if any((h + 1) % 24 in hours for h in hours):
|
|
46
|
+
gap = min(gap, 60 - mins[-1] + mins[0])
|
|
47
|
+
return gap
|
|
48
|
+
|
|
49
|
+
|
|
25
50
|
def matches(expr: str, at: datetime) -> bool:
|
|
26
51
|
fields = expr.split()
|
|
27
52
|
minute, hour, dom, month, dow = (_values(f, *b) for f, b in zip(fields, _BOUNDS))
|
|
@@ -9,10 +9,13 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
|
|
|
9
9
|
from pydantic_core import PydanticCustomError
|
|
10
10
|
|
|
11
11
|
from . import contract as C
|
|
12
|
+
from . import cron
|
|
12
13
|
from .errors import ErrorCode
|
|
13
14
|
|
|
14
15
|
# Unknown fields are rejected, so a newer-contract field is reported, not dropped.
|
|
15
16
|
_STRICT = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
17
|
+
# Display hints only: an unknown key is dropped rather than refused (DisplaySpec).
|
|
18
|
+
_HINT = ConfigDict(extra="ignore", str_strip_whitespace=True)
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
class InputType(str, Enum):
|
|
@@ -99,6 +102,18 @@ class RuntimeSpec(BaseModel):
|
|
|
99
102
|
)
|
|
100
103
|
return v
|
|
101
104
|
|
|
105
|
+
@field_validator("python", mode="before")
|
|
106
|
+
@classmethod
|
|
107
|
+
def _check_python_is_text(cls, v: Any) -> Any:
|
|
108
|
+
# YAML reads an unquoted 3.12 as a float (and 3.10 as 3.1).
|
|
109
|
+
if isinstance(v, (int, float)):
|
|
110
|
+
raise PydanticCustomError(
|
|
111
|
+
ErrorCode.INVALID_PYTHON_VERSION.value,
|
|
112
|
+
"Python version {value} is not quoted text.",
|
|
113
|
+
{"value": v, "remediation": 'Put it in quotes: python: "3.12".'},
|
|
114
|
+
)
|
|
115
|
+
return v
|
|
116
|
+
|
|
102
117
|
@field_validator("python")
|
|
103
118
|
@classmethod
|
|
104
119
|
def _check_python(cls, v: str) -> str:
|
|
@@ -375,6 +390,37 @@ class ScheduleSpec(BaseModel):
|
|
|
375
390
|
),
|
|
376
391
|
},
|
|
377
392
|
)
|
|
393
|
+
try:
|
|
394
|
+
cron.field_values(v)
|
|
395
|
+
except ValueError:
|
|
396
|
+
raise PydanticCustomError(
|
|
397
|
+
ErrorCode.INVALID_TRIGGER.value,
|
|
398
|
+
"Schedule '{value}' has a value out of range.",
|
|
399
|
+
{
|
|
400
|
+
"value": v,
|
|
401
|
+
"remediation": (
|
|
402
|
+
"Minutes are 0-59, hours 0-23, days 1-31, months 1-12 and "
|
|
403
|
+
"weekdays 0-7 (0 and 7 are Sunday)."
|
|
404
|
+
),
|
|
405
|
+
},
|
|
406
|
+
) from None
|
|
407
|
+
# The platform's scheduler cannot express cron's "either day matches".
|
|
408
|
+
if fields[2] != "*" and fields[4] != "*":
|
|
409
|
+
raise PydanticCustomError(
|
|
410
|
+
ErrorCode.INVALID_TRIGGER.value,
|
|
411
|
+
"Schedule '{value}' restricts both the day of the month and the weekday.",
|
|
412
|
+
{"value": v, "remediation": "Set one of them to *."},
|
|
413
|
+
)
|
|
414
|
+
if cron.min_gap(v) < cron.MIN_GAP_MINUTES:
|
|
415
|
+
raise PydanticCustomError(
|
|
416
|
+
ErrorCode.INVALID_TRIGGER.value,
|
|
417
|
+
"Schedules can run at most every {limit} minutes.",
|
|
418
|
+
{
|
|
419
|
+
"value": v,
|
|
420
|
+
"limit": cron.MIN_GAP_MINUTES,
|
|
421
|
+
"remediation": f"Use '*/{cron.MIN_GAP_MINUTES} * * * *' or something slower.",
|
|
422
|
+
},
|
|
423
|
+
)
|
|
378
424
|
return v
|
|
379
425
|
|
|
380
426
|
|
|
@@ -473,6 +519,37 @@ class SlotSpec(BaseModel):
|
|
|
473
519
|
return self
|
|
474
520
|
|
|
475
521
|
|
|
522
|
+
class ChartHint(BaseModel):
|
|
523
|
+
"""Which of the records' fields to plot: `x` along the bottom, `y` as the lines or bars."""
|
|
524
|
+
|
|
525
|
+
model_config = _HINT
|
|
526
|
+
|
|
527
|
+
type: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None # line | bar
|
|
528
|
+
x: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None
|
|
529
|
+
y: Annotated[list[Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)]], Field(max_length=3)] = Field(
|
|
530
|
+
default_factory=list
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
class DisplaySpec(BaseModel):
|
|
535
|
+
"""How the run's result shows on the page, when the builder wants to say so.
|
|
536
|
+
|
|
537
|
+
A hint, not a rule: without it the page reads the result's own shape, and a
|
|
538
|
+
value the page doesn't know (a typo, a newer view) is ignored the same way,
|
|
539
|
+
so it never fails a push or a run.
|
|
540
|
+
|
|
541
|
+
view: document | report | table | chart | code | files
|
|
542
|
+
field: the result key that is the main thing to show
|
|
543
|
+
chart: {type: line | bar, x: date, y: [reach, likes]}
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
model_config = _HINT
|
|
547
|
+
|
|
548
|
+
view: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None
|
|
549
|
+
field: Annotated[str, Field(max_length=C.MAX_NAME_LENGTH)] | None = None
|
|
550
|
+
chart: ChartHint | None = None
|
|
551
|
+
|
|
552
|
+
|
|
476
553
|
class Manifest(BaseModel):
|
|
477
554
|
"""A complete agent declaration. Structural only: tool existence is checked in validation.py."""
|
|
478
555
|
|
|
@@ -497,6 +574,8 @@ class Manifest(BaseModel):
|
|
|
497
574
|
kind: AgentKind = AgentKind.INTERACTIVE
|
|
498
575
|
# Empty: the agent returns `result` only.
|
|
499
576
|
outputs: dict[str, OutputSpec] = Field(default_factory=dict)
|
|
577
|
+
# How the result shows on screen; absent, the page decides from the result itself.
|
|
578
|
+
display: DisplaySpec | None = None
|
|
500
579
|
|
|
501
580
|
@field_validator("contract")
|
|
502
581
|
@classmethod
|
|
@@ -527,6 +606,18 @@ class Manifest(BaseModel):
|
|
|
527
606
|
)
|
|
528
607
|
return v
|
|
529
608
|
|
|
609
|
+
@field_validator("version", mode="before")
|
|
610
|
+
@classmethod
|
|
611
|
+
def _check_version_is_text(cls, v: Any) -> Any:
|
|
612
|
+
# YAML reads an unquoted 1.0 as a float.
|
|
613
|
+
if isinstance(v, (int, float)):
|
|
614
|
+
raise PydanticCustomError(
|
|
615
|
+
ErrorCode.INVALID_AGENT_VERSION.value,
|
|
616
|
+
"Agent version {value} is not quoted text.",
|
|
617
|
+
{"value": v, "remediation": 'Put it in quotes: version: "1.0.0".'},
|
|
618
|
+
)
|
|
619
|
+
return v
|
|
620
|
+
|
|
530
621
|
@field_validator("version")
|
|
531
622
|
@classmethod
|
|
532
623
|
def _check_version(cls, v: str) -> str:
|
{gravity_cli-0.5.3 → gravity_cli-0.5.5}/packages/gravity-schema/gravity_schema/validation.py
RENAMED
|
@@ -156,7 +156,17 @@ def load_manifest_file(
|
|
|
156
156
|
"Run `gravity init` to create one, or check the path.",
|
|
157
157
|
str(p),
|
|
158
158
|
)
|
|
159
|
-
|
|
159
|
+
try:
|
|
160
|
+
text = p.read_text(encoding="utf-8")
|
|
161
|
+
except UnicodeDecodeError:
|
|
162
|
+
# Notepad's "Unicode" is UTF-16; a BOM-only UTF-8 file decodes fine and YAML skips the BOM.
|
|
163
|
+
raise _fail(
|
|
164
|
+
ErrorCode.MALFORMED_YAML,
|
|
165
|
+
"Manifest is not UTF-8 text.",
|
|
166
|
+
"Save the file as UTF-8 (in Notepad: Encoding, then UTF-8).",
|
|
167
|
+
str(p),
|
|
168
|
+
) from None
|
|
169
|
+
return load_manifest_text(text, catalog=catalog)
|
|
160
170
|
|
|
161
171
|
|
|
162
172
|
def _fail(code: ErrorCode, message: str, remediation: str, location: str = "") -> ManifestValidationError:
|