@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,731 @@
1
+ ---
2
+ title: "jobs"
3
+ description: "Start, follow, and derive jobs."
4
+ ---
5
+
6
+ `jobs()` returns the jobs client. Every job method takes a job id, and every derived run returns a new `Job`.
7
+
8
+ ## start
9
+
10
+ <Tabs>
11
+ <Tab title="TypeScript">
12
+ ```ts
13
+ start(
14
+ input: JobCreate,
15
+ options?: StartJobOptions,
16
+ ): Promise<Job>
17
+ ```
18
+
19
+ `JobCreate`: `datasets` (selectors: `name`, optional `version`, `task_names`, `exclude_task_names`, `n_tasks`), `agents` (arms: `name`, `model_name`, optional `version`, `reasoning_effort`, `kwargs`, `preset`, `skills`), and optional `job_name`, `n_attempts`, `n_concurrent_trials` (1 to 150), `max_trial_spend_usd`, `sandbox_provider`, `retry`, `analyze`, the five `*_timeout_multiplier` fields, `verifier_env` (`REWARDKIT_JUDGE` and `REWARDKIT_MODEL` only) and `secrets`. `agent_env` is in the shape but refused by the server.
20
+
21
+ The response is the [Job](/sdk-reference/types#job), with the resolved `max_trial_spend_usd`, `worst_case_spend_usd`, `retry`, `analyze` and multipliers. `options.idempotencyKey` makes a repeated call return the same job, with `idempotent_replay` true; a different request under a used key is refused with `idempotency_key_reused`.
22
+
23
+ ```ts
24
+ const job = await jobs().start({
25
+ datasets: [{ name: "terminal-bench-4", version: "4.0", n_tasks: 5 }],
26
+ agents: [{ name: "codex", model_name: "gpt-5.5" }],
27
+ });
28
+ ```
29
+ </Tab>
30
+ <Tab title="Python">
31
+ ```python
32
+ async def start(
33
+ *,
34
+ datasets: List[DatasetSelector | dict],
35
+ agents: List[AgentArm | dict],
36
+ job_name=None,
37
+ n_attempts=None,
38
+ n_concurrent_trials=None,
39
+ max_trial_spend_usd=None,
40
+ sandbox_provider=None,
41
+ retry=None,
42
+ analyze=None,
43
+ timeout_multiplier=None,
44
+ agent_timeout_multiplier=None,
45
+ verifier_timeout_multiplier=None,
46
+ agent_setup_timeout_multiplier=None,
47
+ environment_build_timeout_multiplier=None,
48
+ agent_env=None,
49
+ verifier_env=None,
50
+ secrets=None,
51
+ idempotency_key=None,
52
+ ) -> Job
53
+ ```
54
+
55
+ The same fields as TypeScript, as keyword arguments; `datasets` and `agents` take dicts or the `DatasetSelector` and `AgentArm` dataclasses, and `retry` is a `JobRetryConfigInput`. `idempotency_key` makes a repeated call return the same job, with `idempotent_replay` true; a different request under a used key is refused with `idempotency_key_reused`.
56
+
57
+ ```python
58
+ job = await jobs().start(
59
+ datasets=[{"name": "terminal-bench-4", "version": "4.0", "n_tasks": 5}],
60
+ agents=[{"name": "codex", "model_name": "gpt-5.5"}],
61
+ )
62
+ ```
63
+ </Tab>
64
+ </Tabs>
65
+
66
+ ## get
67
+
68
+ <Tabs>
69
+ <Tab title="TypeScript">
70
+ ```ts
71
+ get(id: string): Promise<Job>
72
+ ```
73
+
74
+ One job in full.
75
+
76
+ ```ts
77
+ const job = await jobs().get("3e1f9a2c-…");
78
+ ```
79
+ </Tab>
80
+ <Tab title="Python">
81
+ ```python
82
+ async def get(id: str) -> Job
83
+ ```
84
+
85
+ One job in full.
86
+
87
+ ```python
88
+ job = await jobs().get("3e1f9a2c-…")
89
+ ```
90
+ </Tab>
91
+ </Tabs>
92
+
93
+ ## list
94
+
95
+ <Tabs>
96
+ <Tab title="TypeScript">
97
+ ```ts
98
+ list(options?: { search?: string; scope?: "my" | "shared"; limit?: number; cursor?: string }): JobList
99
+ ```
100
+
101
+ Your jobs, newest first. Await one page, or iterate every page.
102
+
103
+ ```ts
104
+ for await (const job of jobs().list({ search: "nightly" })) {
105
+ console.log(job.id, job.status);
106
+ }
107
+ ```
108
+ </Tab>
109
+ <Tab title="Python">
110
+ ```python
111
+ def list(
112
+ *,
113
+ search: Optional[str] = None,
114
+ scope: Optional[JobListScope] = None,
115
+ limit: Optional[int] = None,
116
+ cursor: Optional[str] = None,
117
+ )
118
+ ```
119
+
120
+ Your jobs, newest first. Await one page, or iterate every page.
121
+
122
+ ```python
123
+ async for job in jobs().list(search="nightly"): print(job.id, job.status)
124
+ ```
125
+ </Tab>
126
+ </Tabs>
127
+
128
+ ## trials
129
+
130
+ <Tabs>
131
+ <Tab title="TypeScript">
132
+ ```ts
133
+ trials(
134
+ id: string,
135
+ options?: { status?: TrialStatus[]; dataset?: string; limit?: number; cursor?: string },
136
+ ): TrialList
137
+ ```
138
+
139
+ A job's trials. `status` and `dataset` filter.
140
+
141
+ ```ts
142
+ const failed = await jobs().trials(
143
+ job.id,
144
+ { status: ["INFRASTRUCTURE_ERROR", "SCORING_ERROR"] },
145
+ );
146
+ ```
147
+ </Tab>
148
+ <Tab title="Python">
149
+ ```python
150
+ def trials(
151
+ id: str,
152
+ *,
153
+ status: Optional[List[str]] = None,
154
+ dataset: Optional[str] = None,
155
+ limit: Optional[int] = None,
156
+ cursor: Optional[str] = None,
157
+ )
158
+ ```
159
+
160
+ A job's trials. `status` and `dataset` filter.
161
+
162
+ ```python
163
+ failed = await jobs().trials(
164
+ job.id,
165
+ status=["INFRASTRUCTURE_ERROR", "SCORING_ERROR"],
166
+ )
167
+ ```
168
+ </Tab>
169
+ </Tabs>
170
+
171
+ ## tasks
172
+
173
+ <Tabs>
174
+ <Tab title="TypeScript">
175
+ ```ts
176
+ tasks(
177
+ id: string,
178
+ options?: { limit?: number; cursor?: string },
179
+ ): JobTaskRollupList
180
+ ```
181
+
182
+ One row per task: its trial tally, mean reward and cost, the dataset it came from (`source`), and its latest quality check (`check`, null when never checked).
183
+
184
+ ```ts
185
+ for await (const row of jobs().tasks(job.id)) {
186
+ console.log(row.task_name, row.mean_reward);
187
+ }
188
+ ```
189
+ </Tab>
190
+ <Tab title="Python">
191
+ ```python
192
+ def tasks(
193
+ id: str,
194
+ *,
195
+ limit: Optional[int] = None,
196
+ cursor: Optional[str] = None,
197
+ )
198
+ ```
199
+
200
+ One row per task: its trial tally, mean reward and cost, the dataset it came from (`source`), and its latest quality check (`check`, None when never checked).
201
+
202
+ ```python
203
+ async for row in jobs().tasks(job.id): print(row.task_name, row.mean_reward)
204
+ ```
205
+ </Tab>
206
+ </Tabs>
207
+
208
+ ## watch
209
+
210
+ <Tabs>
211
+ <Tab title="TypeScript">
212
+ ```ts
213
+ watch(id: string, options?: { onEvent?: (event: JobEvent) => void; signal?: AbortSignal; reconnectDelayMs?: number; maxReconnectDelayMs?: number }): JobWatch
214
+ ```
215
+
216
+ The job's event stream. Await the handle for the final job, or iterate it for each event; one form per call, and `onEvent` fires in both. The stream replays from the beginning and reconnects on its own, backing off from `reconnectDelayMs` (default 1000) to `maxReconnectDelayMs` (default 30000). Every event type and payload is on [types](/sdk-reference/types#events); a `trial.settled` is not final while a `trial.retrying` can follow it.
217
+
218
+ ```ts
219
+ const final = await jobs().watch(job.id);
220
+ ```
221
+ </Tab>
222
+ <Tab title="Python">
223
+ ```python
224
+ def watch(
225
+ id: str,
226
+ *,
227
+ on_event: Optional[Callable[[JobEvent], None]] = None,
228
+ timeout_s: Optional[float] = None,
229
+ reconnect_delay_s: float = 1.0,
230
+ max_reconnect_delay_s: float = MAX_WATCH_DELAY_SEC,
231
+ )
232
+ ```
233
+
234
+ The job's event stream. Await the handle for the final job, or iterate it for each event; one form per call. The stream replays from the beginning and reconnects on its own, backing off from `reconnect_delay_s` to `max_reconnect_delay_s` (30 s). Every event type and payload is on [types](/sdk-reference/types#events); a `trial.settled` is not final while a `trial.retrying` can follow it.
235
+
236
+ ```python
237
+ final = await jobs().watch(job.id)
238
+ ```
239
+ </Tab>
240
+ </Tabs>
241
+
242
+ ## cancel
243
+
244
+ <Tabs>
245
+ <Tab title="TypeScript">
246
+ ```ts
247
+ cancel(id: string): Promise<Job>
248
+ ```
249
+
250
+ Request cancellation. A finished job is a no-op.
251
+
252
+ ```ts
253
+ await jobs().cancel(job.id);
254
+ ```
255
+ </Tab>
256
+ <Tab title="Python">
257
+ ```python
258
+ async def cancel(id: str) -> Job
259
+ ```
260
+
261
+ Request cancellation. A finished job is a no-op.
262
+
263
+ ```python
264
+ await jobs().cancel(job.id)
265
+ ```
266
+ </Tab>
267
+ </Tabs>
268
+
269
+ ## resume
270
+
271
+ <Tabs>
272
+ <Tab title="TypeScript">
273
+ ```ts
274
+ resume(
275
+ id: string,
276
+ request?: { filter_error_types?: string[] },
277
+ options?: StartJobOptions,
278
+ ): Promise<Job>
279
+ ```
280
+
281
+ A new job over a finished job's failed or stopped trials; `source_jobs` records `action: "resume"`. The default set and the refusals are on [jobs](/core-concepts/jobs#derive-a-new-job). Supports `idempotencyKey`.
282
+
283
+ ```ts
284
+ const next = await jobs().resume(
285
+ job.id,
286
+ { filter_error_types: ["InfrastructureError"] },
287
+ );
288
+ ```
289
+ </Tab>
290
+ <Tab title="Python">
291
+ ```python
292
+ async def resume(
293
+ id: str,
294
+ *,
295
+ filter_error_types: Optional[List[str]] = None,
296
+ idempotency_key: Optional[str] = None,
297
+ ) -> Job
298
+ ```
299
+
300
+ A new job over a finished job's failed or stopped trials.
301
+
302
+ ```python
303
+ next_job = await jobs().resume(
304
+ job.id,
305
+ filter_error_types=["InfrastructureError"],
306
+ )
307
+ ```
308
+ </Tab>
309
+ </Tabs>
310
+
311
+ ## retry
312
+
313
+ <Tabs>
314
+ <Tab title="TypeScript">
315
+ ```ts
316
+ retry(
317
+ id: string,
318
+ request?: { trial_ids?: string[]; failed_only?: boolean },
319
+ options?: StartJobOptions,
320
+ ): Promise<Job>
321
+ ```
322
+
323
+ A new job re-running selected trials: named ids, the failed ones, or all of them; `trial_ids` and `failed_only` together is refused. The named form works on a running job once every named trial has settled; the other two need a finished source. The rules are on [jobs](/core-concepts/jobs#derive-a-new-job).
324
+
325
+ ```ts
326
+ const again = await jobs().retry(
327
+ job.id,
328
+ { failed_only: true },
329
+ );
330
+ ```
331
+ </Tab>
332
+ <Tab title="Python">
333
+ ```python
334
+ async def retry(
335
+ id: str,
336
+ *,
337
+ trial_ids: Optional[List[str]] = None,
338
+ failed_only: Optional[bool] = None,
339
+ idempotency_key: Optional[str] = None,
340
+ ) -> Job
341
+ ```
342
+
343
+ A new job re-running selected trials: named ids, the failed ones, or all of them.
344
+
345
+ ```python
346
+ again = await jobs().retry(
347
+ job.id,
348
+ failed_only=True,
349
+ )
350
+ ```
351
+ </Tab>
352
+ </Tabs>
353
+
354
+ ## regrade
355
+
356
+ <Tabs>
357
+ <Tab title="TypeScript">
358
+ ```ts
359
+ regrade(
360
+ id: string,
361
+ request?: { statuses?: TrialStatus[]; task_name?: string },
362
+ ): Promise<Job>
363
+ ```
364
+
365
+ Verifier-only re-run of a finished job, in fresh separate verifier boxes under the recorded network policy. The result is a job with `is_regrade` true. Only settled separate-mode trials are eligible; `no_regradable_trials` when none are. See [jobs](/core-concepts/jobs#derive-a-new-job).
366
+
367
+ ```ts
368
+ const regraded = await jobs().regrade(
369
+ job.id,
370
+ { task_name: "tricky-task" },
371
+ );
372
+ ```
373
+ </Tab>
374
+ <Tab title="Python">
375
+ ```python
376
+ async def regrade(
377
+ id: str,
378
+ *,
379
+ statuses: Optional[List[str]] = None,
380
+ task_name: Optional[str] = None,
381
+ ) -> Job
382
+ ```
383
+
384
+ Verifier-only re-run of a finished job. The result is a job.
385
+
386
+ ```python
387
+ regraded = await jobs().regrade(
388
+ job.id,
389
+ task_name="tricky-task",
390
+ )
391
+ ```
392
+ </Tab>
393
+ </Tabs>
394
+
395
+ ## analyze
396
+
397
+ <Tabs>
398
+ <Tab title="TypeScript">
399
+ ```ts
400
+ analyze(
401
+ id: string,
402
+ request?: AnalyzeConfigInput,
403
+ ): Promise<Job>
404
+ ```
405
+
406
+ Enqueue one trace analysis per trial and return at once with the job, its `stats.analysis.n_pending` counting the batch. `AnalyzeConfigInput`: `model_name`, `rubric`, `prompt`, `sandbox_provider`, `reasoning_effort`, `n_concurrent`, `passing`, `failing`, `n_trials`, all optional; `{}` is every default. Calling it again is the re-analysis path, one wave at a time. The same object on `start()` as `analyze` arms the embedded trigger.
407
+
408
+ ```ts
409
+ await jobs().analyze(
410
+ job.id,
411
+ {
412
+ failing: true,
413
+ n_trials: 20,
414
+ },
415
+ );
416
+ ```
417
+ </Tab>
418
+ <Tab title="Python">
419
+ ```python
420
+ async def analyze(
421
+ id: str,
422
+ *,
423
+ model_name=None,
424
+ rubric: Optional[Rubric] = None,
425
+ prompt=None,
426
+ sandbox_provider=None,
427
+ reasoning_effort=None,
428
+ n_concurrent=None,
429
+ passing=None,
430
+ failing=None,
431
+ n_trials=None,
432
+ ) -> Job
433
+ ```
434
+
435
+ Enqueue one trace analysis per trial and return at once.
436
+
437
+ ```python
438
+ await jobs().analyze(
439
+ job.id,
440
+ failing=True,
441
+ n_trials=20,
442
+ )
443
+ ```
444
+ </Tab>
445
+ </Tabs>
446
+
447
+ ## watchAnalysis
448
+
449
+ <Tabs>
450
+ <Tab title="TypeScript">
451
+ ```ts
452
+ watchAnalysis(id: string, options?: { onStats?: (job: Job) => void; signal?: AbortSignal; pollIntervalMs?: number }): Promise<Job>
453
+ ```
454
+
455
+ Poll until no analysis is pending, from `pollIntervalMs` (default 2000) doubling to 30 s while the tally stands still, `onStats` firing on every change. Resolves with the final job. A job that was never analyzed polls forever, so call it after `analyze()`. On a still-running job created with `analyze`, `n_pending` can touch 0 between trial settles, so the watch can return early.
456
+
457
+ ```ts
458
+ const analyzed = await jobs().watchAnalysis(job.id);
459
+ ```
460
+ </Tab>
461
+ <Tab title="Python">
462
+ ```python
463
+ async def watch_analysis(
464
+ id: str,
465
+ *,
466
+ on_stats: Optional[Callable[[Job], None]] = None,
467
+ poll_interval_s: float = 2.0,
468
+ timeout_s: Optional[float] = None,
469
+ ) -> Job
470
+ ```
471
+
472
+ Poll until no analysis is pending. Resolves with the final job.
473
+
474
+ ```python
475
+ analyzed = await jobs().watch_analysis(job.id)
476
+ ```
477
+ </Tab>
478
+ </Tabs>
479
+
480
+ ## compare
481
+
482
+ <Tabs>
483
+ <Tab title="TypeScript">
484
+ ```ts
485
+ compare(ids: string[]): Promise<CompareResponse>
486
+ ```
487
+
488
+ Two to ten jobs side by side: per-job aggregates and a per-task matrix, disagreement rows first. Means cover SCORED trials only, with `coverage` beside them. The shape is on [types](/sdk-reference/types#smaller-shapes).
489
+
490
+ ```ts
491
+ const cmp = await jobs().compare([jobA.id, jobB.id]);
492
+ ```
493
+ </Tab>
494
+ <Tab title="Python">
495
+ ```python
496
+ async def compare(ids: List[str]) -> CompareResponse
497
+ ```
498
+
499
+ Two to ten jobs side by side: per-job aggregates and a per-task matrix.
500
+
501
+ ```python
502
+ cmp = await jobs().compare([job_a.id, job_b.id])
503
+ ```
504
+ </Tab>
505
+ </Tabs>
506
+
507
+ ## download
508
+
509
+ <Tabs>
510
+ <Tab title="TypeScript">
511
+ ```ts
512
+ download(id: string): Promise<Buffer>
513
+ download(
514
+ id: string,
515
+ options: { to: string },
516
+ ): Promise<string>
517
+ download(
518
+ id: string,
519
+ options: { stream: true },
520
+ ): Promise<ReadableStream<Uint8Array>>
521
+ ```
522
+
523
+ A finished job's results as one `.tar.gz` in the standard job layout: the bytes, the saved file's path, or a stream. The first two shapes are verified against the response's `Content-Length` and, when the server states one, its digest; `{ stream: true }` hands you the raw bytes to verify yourself.
524
+
525
+ ```ts
526
+ const path = await jobs().download(
527
+ job.id,
528
+ { to: "./results" },
529
+ );
530
+ ```
531
+ </Tab>
532
+ <Tab title="Python">
533
+ ```python
534
+ async def download(
535
+ id: str,
536
+ *,
537
+ to: Optional[str] = None,
538
+ )
539
+ ```
540
+
541
+ A finished job's results as one `.tar.gz` in the standard job layout: the bytes, or with `to` the saved file's path.
542
+
543
+ ```python
544
+ path = await jobs().download(
545
+ job.id,
546
+ to="./results",
547
+ )
548
+ ```
549
+ </Tab>
550
+ </Tabs>
551
+
552
+ ## upload
553
+
554
+ <Tabs>
555
+ <Tab title="TypeScript">
556
+ ```ts
557
+ upload(source: string | { archive_url: string }, options?: { dataset?: string; onUploadProgress?: (sentBytes: number, totalBytes: number) => void; onRegistered?: (importId: string) => void }): Promise<JobImport>
558
+ ```
559
+
560
+ Upload a job directory, its `.tar.gz`, or a public archive URL as a finished job. Returns the import record; follow it with `watchImport`. A large upload resumes after a dropped connection, and `onRegistered` hands you the import id as soon as it starts. A directory without `result.json` and `config.json` at its root is refused client-side.
561
+
562
+ ```ts
563
+ const imp = await jobs().upload(
564
+ "./job-2026-08-27__12-00-00",
565
+ { dataset: "terminal-bench-4@4.0" },
566
+ );
567
+ ```
568
+ </Tab>
569
+ <Tab title="Python">
570
+ ```python
571
+ async def upload(
572
+ dir_or_archive: Optional[str] = None,
573
+ *,
574
+ archive_url: Optional[str] = None,
575
+ dataset: Optional[str] = None,
576
+ on_upload_progress=None,
577
+ on_registered=None,
578
+ ) -> JobImport
579
+ ```
580
+
581
+ Upload a job directory, its `.tar.gz`, or a public archive URL as a finished job. Returns the import record.
582
+
583
+ ```python
584
+ imp = await jobs().upload(
585
+ "./job-2026-08-27__12-00-00",
586
+ dataset="terminal-bench-4@4.0",
587
+ )
588
+ ```
589
+ </Tab>
590
+ </Tabs>
591
+
592
+ ## getImport, watchImport, listImports
593
+
594
+ <Tabs>
595
+ <Tab title="TypeScript">
596
+ ```ts
597
+ getImport(id: string): Promise<JobImport>
598
+ watchImport(id: string, options?: { onStatus?: (jobImport: JobImport) => void; onProgress?: (progress: JobImportProgress, jobImport: JobImport) => void; pollIntervalMs?: number; signal?: AbortSignal }): Promise<JobImport>
599
+ listImports(options?: { status?: DatasetImportStatus; limit?: number; cursor?: string }): JobImportList
600
+ ```
601
+
602
+ Read one upload, follow it until it settles, or list your uploads.
603
+
604
+ ```ts
605
+ const done = await jobs().watchImport(imp.id);
606
+ ```
607
+ </Tab>
608
+ <Tab title="Python">
609
+ ```python
610
+ async def get_import(id: str) -> JobImport
611
+ async def watch_import(
612
+ id: str,
613
+ *,
614
+ on_status=None,
615
+ on_progress=None,
616
+ poll_interval_s: float = 2.0,
617
+ timeout_s: Optional[float] = None,
618
+ ) -> JobImport
619
+ def list_imports(
620
+ *,
621
+ status: Optional[str] = None,
622
+ limit: Optional[int] = None,
623
+ cursor: Optional[str] = None,
624
+ )
625
+ ```
626
+
627
+ Read one upload, follow it until it settles, or list your uploads.
628
+
629
+ ```python
630
+ done = await jobs().watch_import(imp.id)
631
+ ```
632
+ </Tab>
633
+ </Tabs>
634
+
635
+ ## delete
636
+
637
+ <Tabs>
638
+ <Tab title="TypeScript">
639
+ ```ts
640
+ delete(id: string): Promise<JobDeleteResult>
641
+ ```
642
+
643
+ Permanently delete a job you created: its trials, traces, analyses and stored files. The receipt is `{ job_id, trials_deleted, analyses_deleted }`. The refusals are on [jobs](/core-concepts/jobs#stop-cancel-delete).
644
+
645
+ ```ts
646
+ await jobs().delete(job.id);
647
+ ```
648
+ </Tab>
649
+ <Tab title="Python">
650
+ ```python
651
+ async def delete(id: str) -> JobDeleteResult
652
+ ```
653
+
654
+ Permanently delete a job you created: its trials, traces, analyses and stored files.
655
+
656
+ ```python
657
+ await jobs().delete(job.id)
658
+ ```
659
+ </Tab>
660
+ </Tabs>
661
+
662
+ ## grep
663
+
664
+ Search every trial's parsed trace in one pass. `q` is a case-insensitive regex over each event's type and content; a plain string is a plain substring. Items are per-trial groups with the exact `match_count` and the first five matching events, ordered by trial id; `limit` defaults to 50, at most 200. Keep paging while `hasMore` (`has_more` in Python) is true; a pattern too expensive to evaluate is refused with `invalid_input`.
665
+
666
+ <Tabs>
667
+ <Tab title="TypeScript">
668
+ ```ts
669
+ grep(
670
+ id: string,
671
+ q: string,
672
+ options?: { type?: string; limit?: number; cursor?: string },
673
+ ): Promise<JobGrepPage>
674
+ ```
675
+
676
+ ```ts
677
+ const hits = await jobs().grep(job.id, "permission denied");
678
+ ```
679
+ </Tab>
680
+ <Tab title="Python">
681
+ ```python
682
+ async def grep(
683
+ id: str,
684
+ q: str,
685
+ *,
686
+ type: Optional[str] = None,
687
+ cursor: Optional[str] = None,
688
+ limit: Optional[int] = None,
689
+ ) -> JobGrepPage
690
+ ```
691
+
692
+ ```python
693
+ hits = await jobs().grep(job.id, "permission denied")
694
+ ```
695
+ </Tab>
696
+ </Tabs>
697
+
698
+ ## passAtK
699
+
700
+ <Tabs>
701
+ <Tab title="TypeScript">
702
+ ```ts
703
+ import { passAtK } from "@evolvingmachines/evolve";
704
+
705
+ function passAtK(job: Job): { evals_key: string; points: { k: number; value: number }[] }[]
706
+ ```
707
+
708
+ Reads `stats.evals[key].pass_at_k` off a job you already hold, as sorted numbers; no request is made. Groups that cannot answer are left out, so an empty array means the job has no pass@k to show.
709
+
710
+ ```ts
711
+ for (const group of passAtK(job)) {
712
+ for (const point of group.points) console.log(group.evals_key, `pass@${point.k}`, point.value.toFixed(3));
713
+ }
714
+ ```
715
+ </Tab>
716
+ <Tab title="Python">
717
+ ```python
718
+ from evolve import pass_at_k
719
+
720
+ def pass_at_k(job: Job) -> List[PassAtKGroup]
721
+ ```
722
+
723
+ Reads `stats["evals"][key]["pass_at_k"]` off a job you already hold, as sorted `PassAtKGroup(evals_key, points)` with `PassAtKPoint(k, value)`; no request is made. Groups that cannot answer are left out, so an empty list means the job has no pass@k to show.
724
+
725
+ ```python
726
+ for group in pass_at_k(job):
727
+ for point in group.points:
728
+ print(group.evals_key, f"pass@{point.k}", f"{point.value:.3f}")
729
+ ```
730
+ </Tab>
731
+ </Tabs>