@evolvingmachines/evolve 0.0.55-project-sable.20260916.db0bc0bb

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