@evolvingmachines/evolve 0.0.55-project-sable.20260916.db0bc0bb → 0.0.56
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.
- package/dist/cli/index.cjs +32 -32
- package/dist/cli/index.js +27 -27
- package/package.json +4 -6
- package/skills/evolve/SKILL.md +4 -3
- package/skills/evolve-evals/SKILL.md +112 -0
- package/{docs-evals → skills/evolve-evals/references}/cli-reference/run.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/cli-reference/skills.mdx +4 -4
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/agents.mdx +2 -2
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/analyze.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/jobs.mdx +3 -3
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/models.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/sandboxes.mdx +2 -2
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/secrets.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/core-concepts/trial-outputs.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/getting-started/quick-start.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/index.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/sdk/python.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/sdk/typescript.mdx +1 -1
- package/{docs-evals → skills/evolve-evals/references}/sdk-reference/errors.mdx +2 -2
- package/{docs-evals → skills/evolve-evals/references}/sdk-reference/jobs.mdx +2 -2
- package/docs-agents/SKILL.md +0 -138
- package/docs-agents/index.md +0 -81
- package/docs-agents/python/01-getting-started.md +0 -511
- package/docs-agents/python/02-configuration.md +0 -855
- package/docs-agents/python/03-runtime.md +0 -899
- package/docs-agents/python/04-streaming.md +0 -451
- package/docs-agents/python/05-swarm-pipeline.md +0 -953
- package/docs-agents/python/index.md +0 -122
- package/docs-agents/typescript/01-getting-started.md +0 -494
- package/docs-agents/typescript/02-configuration.md +0 -841
- package/docs-agents/typescript/03-runtime.md +0 -864
- package/docs-agents/typescript/04-streaming.md +0 -478
- package/docs-agents/typescript/05-swarm-pipeline.md +0 -957
- package/docs-agents/typescript/index.md +0 -121
- package/docs-evals/SKILL.md +0 -112
- package/docs-evals/docs.json +0 -180
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/agent.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/analysis.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/analyze.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/auth.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/check.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/dataset.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/index.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/job.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/secrets.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/skill.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/trial.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/cli-reference/upload.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/check.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/datasets.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/index.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/skills.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/tasks.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/trials.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/core-concepts/upload.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/dashboard/checks.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/dashboard/datasets.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/dashboard/jobs.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/dashboard/traces.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/dashboard/trial-viewer.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/getting-started/installation.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/agents.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/analyses.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/auth.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/checks.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/datasets.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/index.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/meta.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/skills.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/trials.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/types.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/snippets/global-options.mdx +0 -0
- /package/{docs-evals → skills/evolve-evals/references}/snippets/list-flags.mdx +0 -0
|
@@ -1,899 +0,0 @@
|
|
|
1
|
-
# Runtime
|
|
2
|
-
|
|
3
|
-
## Methods
|
|
4
|
-
|
|
5
|
-
`run()` and `execute_command()` are async and return `AgentResponse`. `status()` is async and returns `SessionStatus`. `interrupt()` returns `bool`.
|
|
6
|
-
|
|
7
|
-
```python
|
|
8
|
-
@dataclass
|
|
9
|
-
class AgentResponse:
|
|
10
|
-
sandbox_id: str
|
|
11
|
-
exit_code: int
|
|
12
|
-
stdout: str
|
|
13
|
-
stderr: str
|
|
14
|
-
session_id: str | None # Dashboard session ID for traces/replays, when known
|
|
15
|
-
browser: dict[str, str] | None # live_url, when remote browser is configured
|
|
16
|
-
checkpoint: CheckpointInfo | None # Present when storage= configured and run succeeded
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class SessionStatus:
|
|
20
|
-
sandbox_id: str | None
|
|
21
|
-
sandbox: str
|
|
22
|
-
agent: str
|
|
23
|
-
active_process_id: str | None
|
|
24
|
-
has_run: bool
|
|
25
|
-
timestamp: str
|
|
26
|
-
browser: dict[str, str] | None # live_url/session_id/session_tag, when available
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### run
|
|
30
|
-
|
|
31
|
-
Runs the agent with a given prompt.
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
result = await evolve.run(
|
|
35
|
-
prompt='Analyze the data and create a report',
|
|
36
|
-
timeout_ms=15 * 60 * 1000, # (optional) Default 1 hour
|
|
37
|
-
background=False, # (optional) Run in background
|
|
38
|
-
from_checkpoint='ckpt_abc123', # (optional) Restore from checkpoint ID or 'latest'
|
|
39
|
-
checkpoint_comment='after analysis', # (optional) Label for the auto-checkpoint
|
|
40
|
-
resume=False, # (optional) Force a fresh conversation
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
print(result.exit_code)
|
|
44
|
-
print(result.stdout)
|
|
45
|
-
print(result.checkpoint.id if result.checkpoint else None) # Checkpoint ID (if storage= configured)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
- If `timeout_ms` is omitted the agent uses the default of 3_600_000 ms (1 hour).
|
|
49
|
-
- If `background` is `True`, the call returns immediately with a start handshake (`exit_code=0`), not final completion. Completion is delivered asynchronously via `lifecycle` events (`run_background_complete` or `run_background_failed`) or by polling `status()`.
|
|
50
|
-
- If `from_checkpoint` is set, the SDK restores a checkpoint into a fresh sandbox before running. Pass a checkpoint ID or `'latest'` to restore the most recent. Requires `storage=`. Cannot be used with `sandbox_id=`.
|
|
51
|
-
- If `checkpoint_comment` is set, the auto-checkpoint created after a successful run is labeled with this string. Requires `storage=`.
|
|
52
|
-
- Calling `run()` multiple times maintains the agent context / history.
|
|
53
|
-
- `resume` overrides that. Omitted, the first run in a sandbox starts a fresh conversation and every run after it continues the previous one (an attached `sandbox_id=` sandbox counts as "has already run"). Pass `resume=False` to force a fresh conversation in a sandbox the agent has already worked in — the shape a sequence of INDEPENDENT tasks against one shared sandbox needs, where the environment should persist but the context should not. Pass `resume=True` only when a previous run really happened; asking a CLI to continue a session that does not exist is its own kind of error.
|
|
54
|
-
- Calling `run()` while another run or command is active throws immediately. Call `interrupt()` first or wait for the active operation to finish.
|
|
55
|
-
|
|
56
|
-
### execute_command
|
|
57
|
-
|
|
58
|
-
Runs a direct shell command in the sandbox working directory.
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
# Run shell command directly in sandbox
|
|
62
|
-
result = await evolve.execute_command(
|
|
63
|
-
command='pytest',
|
|
64
|
-
timeout_ms=10 * 60 * 1000, # (optional) Default 1 hour
|
|
65
|
-
background=False, # (optional) Run in background
|
|
66
|
-
)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
- If `background` is `True`, returns a start handshake (`exit_code=0`). Completion arrives via `lifecycle` events (`command_background_complete` or `command_background_failed`).
|
|
70
|
-
|
|
71
|
-
### Streaming Events
|
|
72
|
-
|
|
73
|
-
Subscribe to real-time output from `run()` and `execute_command()`:
|
|
74
|
-
|
|
75
|
-
```python
|
|
76
|
-
evolve.on('content', lambda event: print(event['update']['sessionUpdate']))
|
|
77
|
-
evolve.on('lifecycle', lambda event: print(event['reason'], event['sandbox']))
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
| Event | Type | Description |
|
|
81
|
-
|-------|------|-------------|
|
|
82
|
-
| `content` | `OutputEvent` | Parsed ACP-style events (text, tools, plans) |
|
|
83
|
-
| `lifecycle` | `LifecycleEvent` | Sandbox and agent state transitions |
|
|
84
|
-
| `stdout` | `str` | Raw JSONL output |
|
|
85
|
-
| `stderr` | `str` | Error output |
|
|
86
|
-
|
|
87
|
-
For full type definitions, all event interfaces, browser live view, replay, and UI integration example, see [Streaming Events](./04-streaming.md).
|
|
88
|
-
|
|
89
|
-
### Upload: Local → Sandbox
|
|
90
|
-
|
|
91
|
-
**Format:** `{"destination": content}` — directories created automatically
|
|
92
|
-
|
|
93
|
-
| Method | Destination |
|
|
94
|
-
|--------|-------------|
|
|
95
|
-
| `upload_context()` | `/home/user/workspace/context/{path}` |
|
|
96
|
-
| `upload_files()` | `/home/user/workspace/{path}` |
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
# Single file
|
|
100
|
-
await evolve.upload_context({'spec.json': json.dumps(data)})
|
|
101
|
-
|
|
102
|
-
# Multiple files
|
|
103
|
-
await evolve.upload_files({
|
|
104
|
-
'scripts/setup.sh': '#!/bin/bash\necho hello',
|
|
105
|
-
'data/input.csv': csv_bytes,
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
# From local directory (helper)
|
|
109
|
-
from evolve import read_local_dir
|
|
110
|
-
await evolve.upload_context(read_local_dir('./input', recursive=True))
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
> **Setup alternative:** Constructor parameters `context` and `files` use the same format but upload on first `run()` instead of immediately.
|
|
114
|
-
|
|
115
|
-
### Download: Sandbox → Local
|
|
116
|
-
|
|
117
|
-
**Flow:** `get_output_files()` → `save_local_dir()`
|
|
118
|
-
|
|
119
|
-
```python
|
|
120
|
-
# Return type
|
|
121
|
-
@dataclass
|
|
122
|
-
class OutputResult:
|
|
123
|
-
files: dict # All files from output/ folder
|
|
124
|
-
data: Any | None # Parsed result.json (if schema was set via schema=)
|
|
125
|
-
error: str | None # Validation error message (if schema validation failed)
|
|
126
|
-
raw_data: str | None # Raw result.json content when parse/validation failed (for debugging)
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
from pydantic import BaseModel
|
|
131
|
-
from evolve import Evolve, save_local_dir
|
|
132
|
-
|
|
133
|
-
class ResultSchema(BaseModel):
|
|
134
|
-
summary: str
|
|
135
|
-
score: float
|
|
136
|
-
|
|
137
|
-
evolve = Evolve(
|
|
138
|
-
config=AgentConfig(...),
|
|
139
|
-
schema=ResultSchema, # Agent will be prompted to write result.json
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
await evolve.run(prompt='Analyze and score the document')
|
|
143
|
-
|
|
144
|
-
output = await evolve.get_output_files(recursive=True) # recursive=True for nested dirs
|
|
145
|
-
|
|
146
|
-
# Access all fields
|
|
147
|
-
save_local_dir('./output', output.files) # Save files locally
|
|
148
|
-
print(output.data) # ResultSchema(summary='...', score=85.0)
|
|
149
|
-
print(output.error) # None (or validation error message)
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
- **`files`** — dict of all files from `output/` folder
|
|
153
|
-
- **`data`** — Parsed `result.json` validated against schema (None if no schema or validation failed). For Pydantic schemas, returns a model instance.
|
|
154
|
-
- **`error`** — Validation error message if schema validation failed (None otherwise)
|
|
155
|
-
- **`raw_data`** — Raw result.json content when parse/validation failed (for debugging)
|
|
156
|
-
|
|
157
|
-
Files created before the last `run()` or `execute_command()` are filtered out.
|
|
158
|
-
|
|
159
|
-
`save_local_dir()` confines every entry to the target directory: a name whose resolved path escapes it (`../`, absolute) raises `ValueError` instead of writing outside the directory you chose — file names come from sandbox output and are not trusted.
|
|
160
|
-
|
|
161
|
-
### Session Controls
|
|
162
|
-
|
|
163
|
-
```python
|
|
164
|
-
session_id = await evolve.get_session() # Returns sandbox ID (str) or None
|
|
165
|
-
|
|
166
|
-
status = await evolve.status() # Runtime status snapshot
|
|
167
|
-
# status.sandbox -> "stopped" | "booting" | "ready" | "running" | "paused" | "error"
|
|
168
|
-
# status.agent -> "idle" | "running" | "interrupted" | "error"
|
|
169
|
-
# status.has_run -> bool
|
|
170
|
-
# status.sandbox_id -> str | None
|
|
171
|
-
# status.active_process_id -> str | None
|
|
172
|
-
# status.timestamp -> str (ISO 8601)
|
|
173
|
-
|
|
174
|
-
ok = await evolve.interrupt() # Interrupts active run() or execute_command() process; keeps sandbox alive. Returns bool.
|
|
175
|
-
|
|
176
|
-
# Steer a running task: interrupt, then reprompt in same session.
|
|
177
|
-
# The next run() auto-continues conversation history/context for this sandbox session.
|
|
178
|
-
await evolve.run(prompt='Do a full migration plan', background=True)
|
|
179
|
-
await evolve.interrupt()
|
|
180
|
-
await evolve.run(prompt='Change direction: only auth migration.')
|
|
181
|
-
|
|
182
|
-
await evolve.pause() # Suspends sandbox (stops billing, preserves state)
|
|
183
|
-
await evolve.resume() # Reactivates same sandbox
|
|
184
|
-
|
|
185
|
-
await evolve.kill() # Destroys sandbox; next run() creates a new sandbox
|
|
186
|
-
|
|
187
|
-
await evolve.set_session('existing-sandbox-id') # Sets sandbox ID; reconnection happens on next run()
|
|
188
|
-
|
|
189
|
-
# Checkpointing (requires storage=)
|
|
190
|
-
ckpt = await evolve.checkpoint(comment='before refactor') # Explicit snapshot of current sandbox
|
|
191
|
-
checkpoints = await evolve.list_checkpoints(limit=10) # List checkpoints, newest first
|
|
192
|
-
files = await evolve.storage().download_files('latest', glob=['workspace/**/*.py']) # Download specific files
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
`sandbox_id` is a constructor parameter for initialization—it sets the sandbox ID before the first `run()`. `set_session()` is a runtime method that actively interrupts any running process, flushes the session log, resets checkpoint lineage, and switches to the new sandbox. They are **not** interchangeable: use `sandbox_id=` when constructing, `set_session()` when switching mid-session.
|
|
196
|
-
|
|
197
|
-
**Provider caveats:**
|
|
198
|
-
- **E2B / Daytona** — full support for `pause()`, `resume()`, `interrupt()`.
|
|
199
|
-
- **Modal** — does not support `pause()`. `interrupt()` is effectively unsupported and returns `False` for active processes.
|
|
200
|
-
|
|
201
|
-
### get_host
|
|
202
|
-
|
|
203
|
-
Expose a forwarded port:
|
|
204
|
-
|
|
205
|
-
```python
|
|
206
|
-
url = await evolve.get_host(8000)
|
|
207
|
-
print(f'Workspace service available at {url}')
|
|
208
|
-
```
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## Workspace & Structured Output
|
|
212
|
-
|
|
213
|
-
Calling `run` or `execute_command` for the first time provisions a sandbox with the following filesystem:
|
|
214
|
-
|
|
215
|
-
```
|
|
216
|
-
/home/user/workspace/
|
|
217
|
-
├── context/ # Input files (read-only) provided by the user
|
|
218
|
-
├── scripts/ # Your code goes here
|
|
219
|
-
├── temp/ # Scratch space
|
|
220
|
-
├── output/ # Final deliverables
|
|
221
|
-
└── CLAUDE.md # System prompt (or AGENT.md, GEMINI.md, QWEN.md depending on agent)
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Files passed to `context` are uploaded to `context/`. Files passed to `files` are uploaded relative to the working directory.
|
|
225
|
-
|
|
226
|
-
## Filesystem Instructions
|
|
227
|
-
Evolve writes a default filesystem instructions to the agent's config file in the workspace (`CLAUDE.md`, `AGENT.md`, `GEMINI.md`, or `QWEN.md`):
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
## FILESYSTEM INSTRUCTIONS
|
|
231
|
-
|
|
232
|
-
You are running in a sandbox environment.
|
|
233
|
-
|
|
234
|
-
Present working directory: /home/user/workspace/
|
|
235
|
-
|
|
236
|
-
IMPORTANT - Directory structure:
|
|
237
|
-
/home/user/workspace/
|
|
238
|
-
├── context/ # Input files (read-only) provided by the user
|
|
239
|
-
├── scripts/ # Your code goes here
|
|
240
|
-
├── temp/ # Scratch space
|
|
241
|
-
└── output/ # Final deliverables
|
|
242
|
-
|
|
243
|
-
## OUTPUT RESULTS (DELIVERABLES) MUST BE SAVED to `output/` as files.
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Any string passed to `system_prompt` is automatically appended to the agent's config file in the workspace (`CLAUDE.md`, `AGENT.md`, `GEMINI.md`, or `QWEN.md`) after this default.
|
|
247
|
-
|
|
248
|
-
## Structured Output
|
|
249
|
-
|
|
250
|
-
When you provide a `schema`, Evolve instructs the agent to write structured JSON output.
|
|
251
|
-
|
|
252
|
-
```python
|
|
253
|
-
from pydantic import BaseModel
|
|
254
|
-
|
|
255
|
-
class CREData(BaseModel):
|
|
256
|
-
property_name: str
|
|
257
|
-
units: int
|
|
258
|
-
total_rent: float
|
|
259
|
-
occupancy_rate: float
|
|
260
|
-
|
|
261
|
-
evolve = Evolve(
|
|
262
|
-
schema=CREData,
|
|
263
|
-
context={
|
|
264
|
-
'rent_roll.pdf': open('rent_roll.pdf', 'rb').read(),
|
|
265
|
-
},
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
await evolve.run(prompt='Extract CRE data from the rent roll')
|
|
269
|
-
|
|
270
|
-
output = await evolve.get_output_files()
|
|
271
|
-
print(output.data) # CREData(property_name='...', units=120, ...)
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
When a schema is provided, `get_output_files()` automatically validates `output/result.json` and returns `OutputResult` (see [Download: Sandbox → Local](#download-sandbox--local)).
|
|
275
|
-
|
|
276
|
-
```python
|
|
277
|
-
# Type-safe access to validated data
|
|
278
|
-
if output.data:
|
|
279
|
-
print(output.data.property_name) # Pydantic model instance
|
|
280
|
-
else:
|
|
281
|
-
print(output.error) # "Schema validation failed: ..."
|
|
282
|
-
print(output.raw_data) # Raw JSON for debugging
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
The SDK automatically appends the following to the agent's config file in the workspace (`CLAUDE.md`, `AGENT.md`, `GEMINI.md`, or `QWEN.md`):
|
|
286
|
-
|
|
287
|
-
~~~
|
|
288
|
-
## STRUCTURED OUTPUT
|
|
289
|
-
|
|
290
|
-
Your final result MUST be saved to `output/result.json` following this schema:
|
|
291
|
-
|
|
292
|
-
```json
|
|
293
|
-
{
|
|
294
|
-
"type": "object",
|
|
295
|
-
"properties": {
|
|
296
|
-
"property_name": { "type": "string" },
|
|
297
|
-
"units": { "type": "integer" },
|
|
298
|
-
"total_rent": { "type": "number" },
|
|
299
|
-
"occupancy_rate": { "type": "number" }
|
|
300
|
-
},
|
|
301
|
-
"required": ["property_name", "units", "total_rent", "occupancy_rate"]
|
|
302
|
-
}
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
You are free to:
|
|
306
|
-
- Reason through the problem step by step
|
|
307
|
-
- Read and analyze context files
|
|
308
|
-
- Use any available tools
|
|
309
|
-
- Process incrementally
|
|
310
|
-
- Create intermediate files in `temp/` or `scripts/`
|
|
311
|
-
|
|
312
|
-
But your final `output/result.json` MUST conform to the schema above.
|
|
313
|
-
|
|
314
|
-
### OUTPUT RESULTS (DELIVERABLES) MUST BE WRITTEN to `output/result.json` as files.
|
|
315
|
-
### Never just state results as text.
|
|
316
|
-
~~~
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
## Session Management
|
|
321
|
-
|
|
322
|
-
**Multi-turn conversations** (most common):
|
|
323
|
-
|
|
324
|
-
```python
|
|
325
|
-
evolve = Evolve(
|
|
326
|
-
config=AgentConfig(...),
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
await evolve.run(prompt='Analyze data.csv')
|
|
330
|
-
output = await evolve.get_output_files()
|
|
331
|
-
|
|
332
|
-
# Still same session, automatically maintains context / history
|
|
333
|
-
await evolve.run(prompt='Now create visualization')
|
|
334
|
-
output2 = await evolve.get_output_files()
|
|
335
|
-
|
|
336
|
-
# Still same session, automatically maintains context / history
|
|
337
|
-
await evolve.run(prompt='Export to PDF')
|
|
338
|
-
output3 = await evolve.get_output_files()
|
|
339
|
-
|
|
340
|
-
await evolve.kill() # When done
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
**One-shot tasks** (automatic cleanup):
|
|
344
|
-
|
|
345
|
-
```python
|
|
346
|
-
async with evolve:
|
|
347
|
-
result = await evolve.run(prompt='...')
|
|
348
|
-
output = await evolve.get_output_files()
|
|
349
|
-
# Calls kill() automatically via __aexit__()
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
**Pause and resume** (same instance):
|
|
353
|
-
|
|
354
|
-
```python
|
|
355
|
-
evolve = Evolve(
|
|
356
|
-
config=AgentConfig(...),
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
await evolve.run(prompt='Start analysis')
|
|
360
|
-
await evolve.pause() # Suspend billing, keep state
|
|
361
|
-
# Do other work...
|
|
362
|
-
await evolve.resume() # Reactivate same sandbox
|
|
363
|
-
await evolve.run(prompt='Continue analysis') # Session intact
|
|
364
|
-
|
|
365
|
-
await evolve.kill() # Kill the Sandbox when done
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
**Save and reconnect** (different script/session):
|
|
369
|
-
|
|
370
|
-
```python
|
|
371
|
-
# Script 1: Save session for later
|
|
372
|
-
evolve = Evolve(
|
|
373
|
-
config=AgentConfig(...),
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
await evolve.run(prompt='Start analysis')
|
|
377
|
-
|
|
378
|
-
session_id = await evolve.get_session()
|
|
379
|
-
# Save to file, database, environment variable, etc.
|
|
380
|
-
with open('session.txt', 'w') as session_file:
|
|
381
|
-
session_file.write(session_id)
|
|
382
|
-
|
|
383
|
-
# Script 2: Reconnect to saved session
|
|
384
|
-
with open('session.txt') as session_file:
|
|
385
|
-
saved_id = session_file.read()
|
|
386
|
-
|
|
387
|
-
evolve2 = Evolve(
|
|
388
|
-
config=AgentConfig(...),
|
|
389
|
-
sandbox_id=saved_id # Reconnect
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
await evolve2.run(prompt='Continue analysis') # Session continues from Script 1
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
**Switch between sandboxes** (same instance):
|
|
396
|
-
|
|
397
|
-
```python
|
|
398
|
-
evolve = Evolve(
|
|
399
|
-
config=AgentConfig(...),
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
# Work with first sandbox
|
|
403
|
-
await evolve.run(prompt='Analyze dataset A')
|
|
404
|
-
session_a = await evolve.get_session()
|
|
405
|
-
|
|
406
|
-
# Switch to different sandbox
|
|
407
|
-
await evolve.set_session('existing-sandbox-b-id')
|
|
408
|
-
await evolve.run(prompt='Analyze dataset B') # Now working with sandbox B
|
|
409
|
-
|
|
410
|
-
# Switch back to first sandbox
|
|
411
|
-
await evolve.set_session(session_a)
|
|
412
|
-
await evolve.run(prompt='Compare results') # Back to sandbox A
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
## Storage & Checkpointing
|
|
418
|
-
|
|
419
|
-
> **Gateway feature** — requires `EVOLVE_API_KEY`. Storage is fully managed by Evolve; no S3 buckets or AWS credentials needed.
|
|
420
|
-
|
|
421
|
-
Persist sandbox state beyond sandbox lifetime. Checkpoints archive specific directories under `/home/user/` to Evolve-managed storage and can be restored into a fresh sandbox.
|
|
422
|
-
|
|
423
|
-
**What gets checkpointed:**
|
|
424
|
-
- `/home/user/workspace/` — your project files
|
|
425
|
-
- `/home/user/.<agent>/` — agent settings and session history (e.g. `.claude/`, `.codex/`, `.gemini/`, `.qwen/`, `.kimi-code/`, `.factory/`)
|
|
426
|
-
- For OpenCode: XDG directories (`~/.local/share/opencode/`, `~/.config/opencode/`, `~/.local/state/opencode/`)
|
|
427
|
-
- For Kimi Code: `.kimi-code/config.toml` is excluded because Evolve rewrites gateway credentials before each run; session history and MCP config are still included.
|
|
428
|
-
|
|
429
|
-
**Key properties:**
|
|
430
|
-
- **Auto-checkpoint:** Every successful `run()` with `storage=` creates a checkpoint automatically.
|
|
431
|
-
- **Content-addressed dedup:** Archives are hashed (SHA-256). Same content = skip upload.
|
|
432
|
-
- **Lineage tracking:** Each checkpoint records its `parent_id`, forming a chain across runs and restores.
|
|
433
|
-
|
|
434
|
-
### Configuration
|
|
435
|
-
|
|
436
|
-
```python
|
|
437
|
-
evolve = Evolve(
|
|
438
|
-
config=AgentConfig(type='claude'),
|
|
439
|
-
storage=StorageConfig(), # Uses EVOLVE_API_KEY from env
|
|
440
|
-
)
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
### Auto-Checkpoint (via `run()`)
|
|
444
|
-
|
|
445
|
-
Every successful foreground `run()` auto-creates a checkpoint:
|
|
446
|
-
|
|
447
|
-
```python
|
|
448
|
-
result = await evolve.run(
|
|
449
|
-
prompt='Build the report',
|
|
450
|
-
checkpoint_comment='initial draft',
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
print(result.checkpoint.id) # 'ckpt_m5abc_xyz123'
|
|
454
|
-
print(result.checkpoint.hash) # SHA-256 of archive
|
|
455
|
-
print(result.checkpoint.comment) # 'initial draft'
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
**Behavior notes:**
|
|
459
|
-
|
|
460
|
-
- **Non-fatal:** Auto-checkpoint failures are logged but never cause `run()` to throw. The run result will have `checkpoint` as `None`.
|
|
461
|
-
- **Foreground only:** Background runs (`background=True`) skip auto-checkpointing entirely.
|
|
462
|
-
- **Exclusions:** The archive excludes `node_modules/`, `__pycache__/`, `*.pyc`, `.cache/`, `.npm/`, `.pip/`, `.venv/`, `venv/`, and `{workspace}/temp/` to keep snapshots lean.
|
|
463
|
-
- **Dedup:** Archives are content-addressed by SHA-256 hash. If the hash matches an existing archive in storage, the upload is skipped—only the metadata entry is written.
|
|
464
|
-
- **`from_checkpoint='latest'` edge case:** If no checkpoints exist globally (across all sessions/tags), `from_checkpoint='latest'` throws an error. Note that `'latest'` resolves to the globally newest checkpoint, not scoped to the current session tag. Use `storage().list_checkpoints()` first to check availability.
|
|
465
|
-
|
|
466
|
-
### Explicit Checkpoint
|
|
467
|
-
|
|
468
|
-
Snapshot at any point (between runs, after manual setup, etc.):
|
|
469
|
-
|
|
470
|
-
```python
|
|
471
|
-
ckpt = await evolve.checkpoint(comment='before refactor')
|
|
472
|
-
print(ckpt.id) # 'ckpt_m5def_abc456'
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
Requires an active sandbox (`run()` must have been called first).
|
|
476
|
-
|
|
477
|
-
### Restore from Checkpoint
|
|
478
|
-
|
|
479
|
-
Pass `from_checkpoint` to `run()` to restore a checkpoint into a fresh sandbox before running:
|
|
480
|
-
|
|
481
|
-
```python
|
|
482
|
-
result = await evolve.run(
|
|
483
|
-
prompt='Continue where we left off',
|
|
484
|
-
from_checkpoint='ckpt_m5abc_xyz123',
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
# Or restore the most recent checkpoint
|
|
488
|
-
latest = await evolve.run(
|
|
489
|
-
prompt='Pick up from latest state',
|
|
490
|
-
from_checkpoint='latest',
|
|
491
|
-
)
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
- `from_checkpoint` creates a fresh sandbox, downloads the archive, verifies hash integrity, and extracts it.
|
|
495
|
-
- Cannot be used with `sandbox_id=` (restore requires a fresh sandbox).
|
|
496
|
-
- The restored checkpoint becomes the `parent_id` for the next checkpoint, maintaining lineage.
|
|
497
|
-
- Agent type and workspace mode must match the checkpoint (model changes are fine).
|
|
498
|
-
|
|
499
|
-
### Listing & Browsing Checkpoints
|
|
500
|
-
|
|
501
|
-
**Instance method:**
|
|
502
|
-
|
|
503
|
-
```python
|
|
504
|
-
checkpoints = await evolve.list_checkpoints(
|
|
505
|
-
limit=10, # (optional) default: 100, max: 500
|
|
506
|
-
tag='my-session-tag', # (optional) filter by session tag
|
|
507
|
-
)
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
**Standalone `storage()` client** (no Evolve instance needed):
|
|
511
|
-
|
|
512
|
-
```python
|
|
513
|
-
from evolve import storage
|
|
514
|
-
|
|
515
|
-
async with storage() as store: # Uses EVOLVE_API_KEY from env
|
|
516
|
-
checkpoints = await store.list_checkpoints()
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
The `storage()` factory returns a `StorageClient` with four methods:
|
|
520
|
-
|
|
521
|
-
```python
|
|
522
|
-
# List checkpoints (newest first)
|
|
523
|
-
checkpoints = await store.list_checkpoints(limit=10, tag='my-session')
|
|
524
|
-
|
|
525
|
-
# Get a single checkpoint by ID
|
|
526
|
-
info = await store.get_checkpoint('ckpt_m5abc_xyz123')
|
|
527
|
-
|
|
528
|
-
# Download full checkpoint archive to a local directory
|
|
529
|
-
output_dir = await store.download_checkpoint('ckpt_m5abc_xyz123',
|
|
530
|
-
to='./restored', # (optional) default: cwd
|
|
531
|
-
extract=True, # (optional) default: True — set False to keep raw .tar.gz
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
# Download specific files without extracting the full archive
|
|
535
|
-
files = await store.download_files('ckpt_m5abc_xyz123',
|
|
536
|
-
files=['workspace/output/result.json'], # (optional) exact paths
|
|
537
|
-
glob=['workspace/**/*.py'], # (optional) glob patterns
|
|
538
|
-
to='./output', # (optional) save to disk
|
|
539
|
-
)
|
|
540
|
-
# files is a dict[str, str | bytes] — relative path → file contents
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
Pass `'latest'` instead of a checkpoint ID to any method to resolve the most recent checkpoint.
|
|
544
|
-
|
|
545
|
-
**Downloading folders with glob patterns:**
|
|
546
|
-
|
|
547
|
-
```python
|
|
548
|
-
output = await store.download_files(id, glob=['workspace/output/**'])
|
|
549
|
-
all_files = await store.download_files(id, glob=['workspace/**'])
|
|
550
|
-
|
|
551
|
-
# Save directly to disk
|
|
552
|
-
await store.download_files(id, glob=['workspace/output/**'], to='./local-output')
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
> **Paths are relative to `/home/user/`** — use `workspace/...` not `/home/user/workspace/...`.
|
|
556
|
-
|
|
557
|
-
**Instance-bound `storage()` accessor:**
|
|
558
|
-
|
|
559
|
-
When you already have an Evolve instance, `evolve.storage()` returns a `StorageClient` with credentials automatically bound:
|
|
560
|
-
|
|
561
|
-
```python
|
|
562
|
-
store = evolve.storage()
|
|
563
|
-
files = await store.download_files('latest', glob=['workspace/report.*'])
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
**Standalone `list_checkpoints()`** (convenience shortcut for listing only):
|
|
567
|
-
|
|
568
|
-
```python
|
|
569
|
-
from evolve import list_checkpoints, StorageConfig
|
|
570
|
-
|
|
571
|
-
recent = await list_checkpoints(StorageConfig(), limit=5)
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### Checkpoint Lineage
|
|
575
|
-
|
|
576
|
-
Each checkpoint records `parent_id`. Consecutive runs build a chain:
|
|
577
|
-
|
|
578
|
-
```python
|
|
579
|
-
r1 = await evolve.run(prompt='Step 1')
|
|
580
|
-
# r1.checkpoint.parent_id → None (first)
|
|
581
|
-
|
|
582
|
-
r2 = await evolve.run(prompt='Step 2')
|
|
583
|
-
# r2.checkpoint.parent_id → r1.checkpoint.id
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
Restoring from a checkpoint branches the lineage:
|
|
587
|
-
|
|
588
|
-
```python
|
|
589
|
-
r4 = await evolve.run(prompt='Branch from step 1', from_checkpoint=r1.checkpoint.id)
|
|
590
|
-
# r4.checkpoint.parent_id → r1.checkpoint.id (not r3)
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
### Type Reference
|
|
594
|
-
|
|
595
|
-
```python
|
|
596
|
-
@dataclass
|
|
597
|
-
class CheckpointInfo:
|
|
598
|
-
id: str # Checkpoint ID — pass as from_checkpoint to restore
|
|
599
|
-
hash: str # SHA-256 of tar.gz archive
|
|
600
|
-
tag: str # Session tag at checkpoint time
|
|
601
|
-
timestamp: str # ISO 8601
|
|
602
|
-
size_bytes: int | None # Archive size in bytes
|
|
603
|
-
agent_type: str | None # 'claude' | 'codex' | 'gemini' | 'qwen' | 'kimi' | 'opencode' | 'droid'
|
|
604
|
-
model: str | None # Model used
|
|
605
|
-
workspace_mode: str | None # 'knowledge' | 'swe'
|
|
606
|
-
parent_id: str | None # Parent checkpoint ID (lineage)
|
|
607
|
-
comment: str | None # User-provided label
|
|
608
|
-
|
|
609
|
-
class StorageClient:
|
|
610
|
-
async def list_checkpoints(limit=None, tag=None) -> list[CheckpointInfo]
|
|
611
|
-
async def get_checkpoint(id: str) -> CheckpointInfo
|
|
612
|
-
async def download_checkpoint(id: str, *, to=None, extract=True) -> str
|
|
613
|
-
async def download_files(id: str, *, files=None, glob=None, to=None) -> dict[str, str | bytes]
|
|
614
|
-
|
|
615
|
-
# download_checkpoint options
|
|
616
|
-
to: str | None # Output directory (default: cwd)
|
|
617
|
-
extract: bool # Extract archive (default: True)
|
|
618
|
-
|
|
619
|
-
# download_files options
|
|
620
|
-
files: list[str] | None # Exact file paths to extract
|
|
621
|
-
glob: list[str] | None # Glob patterns to match files
|
|
622
|
-
to: str | None # Save to disk (default: in-memory only)
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
### End-to-End Example
|
|
626
|
-
|
|
627
|
-
```python
|
|
628
|
-
from evolve import Evolve, AgentConfig, StorageConfig, storage
|
|
629
|
-
|
|
630
|
-
# 1. Create and checkpoint
|
|
631
|
-
async with Evolve(
|
|
632
|
-
config=AgentConfig(type='claude'),
|
|
633
|
-
storage=StorageConfig(),
|
|
634
|
-
) as evolve:
|
|
635
|
-
r1 = await evolve.run(
|
|
636
|
-
prompt="Create a file called report.txt with 'Draft v1'",
|
|
637
|
-
checkpoint_comment='initial draft',
|
|
638
|
-
)
|
|
639
|
-
print('Checkpoint 1:', r1.checkpoint.id)
|
|
640
|
-
|
|
641
|
-
# 2. Second run — auto-chains parent_id
|
|
642
|
-
r2 = await evolve.run(
|
|
643
|
-
prompt="Append ' - reviewed' to report.txt",
|
|
644
|
-
checkpoint_comment='reviewed',
|
|
645
|
-
)
|
|
646
|
-
print('Parent:', r2.checkpoint.parent_id) # → r1.checkpoint.id
|
|
647
|
-
|
|
648
|
-
# 3. Restore into fresh sandbox
|
|
649
|
-
async with Evolve(
|
|
650
|
-
config=AgentConfig(type='claude'),
|
|
651
|
-
storage=StorageConfig(),
|
|
652
|
-
) as evolve2:
|
|
653
|
-
r3 = await evolve2.run(
|
|
654
|
-
prompt='Read report.txt — what does it say?',
|
|
655
|
-
from_checkpoint=r1.checkpoint.id,
|
|
656
|
-
)
|
|
657
|
-
# Agent sees 'Draft v1' (not the reviewed version)
|
|
658
|
-
|
|
659
|
-
# 4. Browse checkpoints and download files (no Evolve instance needed)
|
|
660
|
-
async with storage() as store:
|
|
661
|
-
all_checkpoints = await store.list_checkpoints()
|
|
662
|
-
print(f'{len(all_checkpoints)} checkpoints (newest first)')
|
|
663
|
-
|
|
664
|
-
files = await store.download_files('latest', glob=['workspace/report.*'])
|
|
665
|
-
for path, content in files.items():
|
|
666
|
-
print(f'{path}: {content}')
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
---
|
|
670
|
-
|
|
671
|
-
## Observability
|
|
672
|
-
|
|
673
|
-
Full execution traces—including tool calls, file operations (read/write/edit), text responses, and reasoning chunks—are logged to your Evolve dashboard at **https://dashboard.evolvingmachines.ai/traces** for debugging and replay.
|
|
674
|
-
|
|
675
|
-
Additionally, every run and command is logged locally to structured JSON lines under `~/.evolve-sdk/observability/sessions`. File name format:
|
|
676
|
-
|
|
677
|
-
```
|
|
678
|
-
{tag}_{provider}_{sandboxId}_{agent}_{timestamp}.jsonl
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
- `{tag}` – `my-prefix-` + 16 random hex characters (e.g. `my-prefix-a1b2c3d4e5f6g7h8`)
|
|
682
|
-
- `{provider}` – the sandbox provider (e.g. `e2b`)
|
|
683
|
-
- `{sandboxId}` – the active sandbox ID
|
|
684
|
-
- `{agent}` – the agent type (`codex`, `claude`, `gemini`, `qwen`, `kimi`, `opencode`, `droid`)
|
|
685
|
-
- `{timestamp}` – ISO timestamp with `:` and `.` replaced by `-`
|
|
686
|
-
|
|
687
|
-
Each file contains three entry types:
|
|
688
|
-
|
|
689
|
-
```json
|
|
690
|
-
{"_meta":{"tag":"my-prefix-a1b2c3d4","provider":"e2b","agent":"qwen","model":"qwen-coder-plus-latest","sandbox_id":"sbx_123","timestamp":"2025-10-26T20:15:17.984Z"}}
|
|
691
|
-
{"_prompt":{"text":"hello how are you?"}}
|
|
692
|
-
{"jsonrpc":"2.0","method":"session/update", ...}
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
- `_meta` – exactly one line per file (sandbox, agent, timestamp)
|
|
696
|
-
- `_prompt` – one line per `run()` call with the prompt text
|
|
697
|
-
- Raw JSON – every streamed payload (ACP notifications, stdout, etc.)
|
|
698
|
-
|
|
699
|
-
Attach your own prefix to make logs easy to search:
|
|
700
|
-
|
|
701
|
-
```python
|
|
702
|
-
evolve = Evolve(
|
|
703
|
-
config=AgentConfig(...),
|
|
704
|
-
session_tag_prefix='my-project'
|
|
705
|
-
)
|
|
706
|
-
|
|
707
|
-
await evolve.run(prompt='Kick off analysis')
|
|
708
|
-
|
|
709
|
-
print(await evolve.get_session_tag()) # "my-project-ab12cd34"
|
|
710
|
-
print(await evolve.get_session_timestamp()) # Timestamp for first log file
|
|
711
|
-
|
|
712
|
-
await evolve.kill() # Destroys sandbox A
|
|
713
|
-
|
|
714
|
-
await evolve.run(prompt='Start fresh') # New sandbox → new log file
|
|
715
|
-
|
|
716
|
-
print(await evolve.get_session_tag()) # "my-project-f56789cd"
|
|
717
|
-
print(await evolve.get_session_timestamp()) # Timestamp for second log file
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
- `kill()` or `set_session()` flushes the current log; the next `run()` starts a
|
|
721
|
-
fresh file with the new sandbox id.
|
|
722
|
-
- Long-running sessions (pause/resume or ACP auto-resume) keep appending to the
|
|
723
|
-
current file, so you always have the full timeline.
|
|
724
|
-
- Logging is buffered inside the SDK, so it never blocks streaming output.
|
|
725
|
-
|
|
726
|
-
Use the tag together with the sandbox id to correlate logs with files saved in
|
|
727
|
-
`/output/`.
|
|
728
|
-
|
|
729
|
-
### Historical Sessions & Trace Download
|
|
730
|
-
|
|
731
|
-
Gateway mode also exposes a standalone `sessions()` client for querying past
|
|
732
|
-
sessions and downloading full trace files, even after the sandbox has ended.
|
|
733
|
-
|
|
734
|
-
```python
|
|
735
|
-
from evolve import sessions
|
|
736
|
-
|
|
737
|
-
async with sessions() as session:
|
|
738
|
-
page = await session.list(limit=20, state='ended', tag_prefix='my-project')
|
|
739
|
-
|
|
740
|
-
if page.items:
|
|
741
|
-
info = await session.get(page.items[0].id)
|
|
742
|
-
recent_events = await session.events(info.id, since=10)
|
|
743
|
-
transcript = await session.transcript(info.id)
|
|
744
|
-
path = await session.download(info.id, to='./traces')
|
|
745
|
-
replay = await session.browser_replay(info.id)
|
|
746
|
-
|
|
747
|
-
print(info.runtime_status) # 'alive' | 'dead' | 'unknown'
|
|
748
|
-
print(len(recent_events)) # Parsed JSONL objects
|
|
749
|
-
print(path) # ./traces/{tag}.jsonl
|
|
750
|
-
print(replay.replay_url) # Browser replay URL
|
|
751
|
-
```
|
|
752
|
-
|
|
753
|
-
The CLI wraps the same client headless — `evolve session list` (`--state live|ended`, `--agent`, `--tag-prefix`, paged with `--limit`/`--cursor`, `-q` for ids, `--json` for the page) and `evolve session show <id>` — with no Python code involved.
|
|
754
|
-
|
|
755
|
-
The `sessions()` factory returns a `SessionsClient` with six methods:
|
|
756
|
-
|
|
757
|
-
```python
|
|
758
|
-
page = await session.list(
|
|
759
|
-
limit=20,
|
|
760
|
-
cursor=None,
|
|
761
|
-
state='all', # 'live' | 'ended' | 'all'
|
|
762
|
-
agent='claude',
|
|
763
|
-
tag_prefix='my-project',
|
|
764
|
-
sort='newest', # 'newest' | 'oldest' | 'cost'
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
info = await session.get('session-id')
|
|
768
|
-
events = await session.events('session-id', since=50)
|
|
769
|
-
transcript = await session.transcript('session-id', since=50)
|
|
770
|
-
path = await session.download('session-id', to='./traces')
|
|
771
|
-
replay = await session.browser_replay(
|
|
772
|
-
'session-id',
|
|
773
|
-
timeout_ms=600_000, # optional; default 10 minutes
|
|
774
|
-
interval_ms=5_000, # optional; default 5 seconds
|
|
775
|
-
)
|
|
776
|
-
```
|
|
777
|
-
|
|
778
|
-
- `list()` returns `SessionPage(items, next_cursor, has_more)`
|
|
779
|
-
- `get()` returns `SessionInfo` with snake_case fields such as `sandbox_id`,
|
|
780
|
-
`runtime_status`, `created_at`, and `tool_stats` — plus `usage`, the
|
|
781
|
-
one-home reading (spend so far + token breakdown from the same gateway
|
|
782
|
-
records, `provisional` marking numbers that can still grow); it carries the
|
|
783
|
-
same keys a trial's `usage` does, and `None` means the meter never answered
|
|
784
|
-
- `events()` returns parsed JSONL objects for programmatic inspection
|
|
785
|
-
- `transcript()` is the same read whole: `SessionTranscript(session, events, total, gateway_calls, stored_at=None)` — `total` counts every stored event (the next delta's `since`), and `gateway_calls` are the gateway meter's per-call lines (the spec's `GatewayUsageEvent`, its own camelCase keys: `call['update']['usage']` carries `promptTokens`, `completionTokens`, `cachedTokens`, `costUsd`), in time order, the same line a trial's trace carries; they ride beside `events`, never inside them, and are the only per-call tokens and money a client should show. `stored_at` is the server's write instant of each event's row, one per entry of `events` and index-aligned: present on every row-served page (an empty page carries an empty list), `None` when the transcript was served from its file. It places the gateway meter's calls under the harness's steps for harnesses whose lines carry no clock of their own (codex, kimi, qwen); a reader that does not place calls needs nothing from it. The session's total stays on `session.usage` / `session.cost`.
|
|
786
|
-
- `download()` saves the raw `.jsonl` trace file to disk and returns the path
|
|
787
|
-
- `browser_replay()` waits for the managed browser replay and returns
|
|
788
|
-
`replay_url` plus `download_url`
|
|
789
|
-
- Use `replay_url` in your UI for browser playback
|
|
790
|
-
- Use `download_url` when users need the raw `.mp4` file
|
|
791
|
-
- `suggested_start_seconds`, when present, is the recommended replay start time in seconds and is already applied to `replay_url`; keep the raw download unchanged
|
|
792
|
-
- `size_bytes` and `ready_at`, when present, describe the raw recording size and replay readiness time
|
|
793
|
-
- `status` is `'ready'` once the call returns
|
|
794
|
-
|
|
795
|
-
For the full browser setup, live-view, cleanup, and replay flow, see
|
|
796
|
-
[Configuration → Browser Automation](./02-configuration.md#browser-automation).
|
|
797
|
-
|
|
798
|
-
This API is **gateway-only**. In Direct Provider Key Mode, historical traces remain
|
|
799
|
-
available via local JSONL files in `~/.evolve-sdk/observability/sessions/`.
|
|
800
|
-
|
|
801
|
-
---
|
|
802
|
-
|
|
803
|
-
## Cost Tracking
|
|
804
|
-
|
|
805
|
-
Query per-run and per-session LLM spend. Requires gateway mode (`EVOLVE_API_KEY`). Supported for Claude and Codex agents.
|
|
806
|
-
|
|
807
|
-
Cost data may take 5–60s to appear while the gateway finishes metering (typically under 30s).
|
|
808
|
-
|
|
809
|
-
```python
|
|
810
|
-
from evolve import Evolve
|
|
811
|
-
|
|
812
|
-
evolve = Evolve()
|
|
813
|
-
|
|
814
|
-
# Each run() returns a run_id for cost attribution
|
|
815
|
-
r1 = await evolve.run(prompt="Analyze the data")
|
|
816
|
-
r2 = await evolve.run(prompt="Write tests")
|
|
817
|
-
|
|
818
|
-
# Session cost — all runs
|
|
819
|
-
session = await evolve.get_session_cost()
|
|
820
|
-
print(session.total_cost) # 0.42 (USD)
|
|
821
|
-
print(session.total_tokens) # {'prompt': 5000, 'completion': 2000, 'cached': 3200}
|
|
822
|
-
print(len(session.runs)) # 2
|
|
823
|
-
|
|
824
|
-
# Run cost — by ID
|
|
825
|
-
cost = await evolve.get_run_cost(run_id=r1.run_id)
|
|
826
|
-
print(cost.cost, cost.model, cost.requests)
|
|
827
|
-
|
|
828
|
-
# Run cost — by index (1-based, negative = from end)
|
|
829
|
-
first = await evolve.get_run_cost(index=1)
|
|
830
|
-
last = await evolve.get_run_cost(index=-1)
|
|
831
|
-
|
|
832
|
-
await evolve.kill()
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
After `kill()`, the bridge process is stopped. To query costs for a completed session, create a new Evolve instance — the TS bridge handles `previousSessionTag` fallback internally. After another `run()` cycle, the previous session's cost is no longer queryable.
|
|
836
|
-
|
|
837
|
-
### Types
|
|
838
|
-
|
|
839
|
-
```python
|
|
840
|
-
@dataclass
|
|
841
|
-
class RunCost:
|
|
842
|
-
run_id: str # Matches AgentResponse.run_id
|
|
843
|
-
index: int # 1-based chronological position
|
|
844
|
-
cost: float # USD as billed to your Evolve account
|
|
845
|
-
tokens: Dict[str, int] # {'prompt': N, 'completion': N, 'cached': N} — prompt INCLUDES cached
|
|
846
|
-
model: str # Last observed model for this run
|
|
847
|
-
requests: int # Number of LLM API requests
|
|
848
|
-
as_of: str # ISO timestamp of query
|
|
849
|
-
is_complete: bool # False if calls still batching (5–60s)
|
|
850
|
-
truncated: bool # True if spend logs were capped
|
|
851
|
-
|
|
852
|
-
@dataclass
|
|
853
|
-
class SessionCost:
|
|
854
|
-
session_tag: str # Matches get_session_tag()
|
|
855
|
-
total_cost: float # USD across all runs
|
|
856
|
-
total_tokens: Dict[str, int] # {'prompt': N, 'completion': N, 'cached': N}
|
|
857
|
-
runs: List[RunCost] # Chronological order
|
|
858
|
-
as_of: str
|
|
859
|
-
is_complete: bool
|
|
860
|
-
truncated: bool
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
---
|
|
864
|
-
|
|
865
|
-
## Error Handling
|
|
866
|
-
|
|
867
|
-
Common errors and how to handle them:
|
|
868
|
-
|
|
869
|
-
| Error | Cause | Fix |
|
|
870
|
-
|-------|-------|-----|
|
|
871
|
-
| `No API key configured` | No `EVOLVE_API_KEY` or provider key in env | Set `EVOLVE_API_KEY` or pass `api_key`/`provider_api_key` to `AgentConfig` |
|
|
872
|
-
| `No sandbox provider configured` | No sandbox provider key in env | Set `E2B_API_KEY`, `MODAL_TOKEN_ID`+`SECRET`, or `DAYTONA_API_KEY` |
|
|
873
|
-
| `Evolve agent config: "model" is empty` | `AgentConfig(model='')` — an empty string is not a model id | Pass a model id, or drop `model` to take the agent's default |
|
|
874
|
-
| `run() requires a non-empty "prompt" string` | `run()` called with an empty prompt | Pass a non-empty `prompt` |
|
|
875
|
-
| `Operation already active` | Calling `run()` while another run is in progress | `await evolve.interrupt()` first, or wait for the active operation |
|
|
876
|
-
| `Cannot use 'from_checkpoint' with existing session` | `run(from_checkpoint=...)` with `sandbox_id=` | Checkpoint restore requires a fresh sandbox — remove `sandbox_id=` |
|
|
877
|
-
| `No checkpoints found` | `run(from_checkpoint='latest')` with no prior checkpoints | Create a checkpoint first, or use `list_checkpoints()` to verify |
|
|
878
|
-
| `Schema validation failed: ...` | Agent's `result.json` doesn't match schema | Check `output.raw_data` for the actual output; refine your prompt or schema |
|
|
879
|
-
| `Storage requires EVOLVE_API_KEY` | `storage=StorageConfig()` without gateway credentials | Set `EVOLVE_API_KEY` in your environment |
|
|
880
|
-
| Timeout (exit code -1) | Agent exceeded `timeout_ms` | Increase `timeout_ms` or simplify the prompt |
|
|
881
|
-
|
|
882
|
-
```python
|
|
883
|
-
# Handling schema validation errors
|
|
884
|
-
output = await evolve.get_output_files()
|
|
885
|
-
if output.error:
|
|
886
|
-
print(f'Validation failed: {output.error}')
|
|
887
|
-
print(f'Raw output: {output.raw_data}') # Agent's actual JSON for debugging
|
|
888
|
-
|
|
889
|
-
# Handling run errors
|
|
890
|
-
try:
|
|
891
|
-
result = await evolve.run(prompt='...')
|
|
892
|
-
if result.exit_code != 0:
|
|
893
|
-
print(f'Agent failed: {result.stderr}')
|
|
894
|
-
except Exception as err:
|
|
895
|
-
# Raised for: no API key, no sandbox, operation conflict, restore failure
|
|
896
|
-
print(err)
|
|
897
|
-
```
|
|
898
|
-
|
|
899
|
-
---
|