evolvingmachines-evolve 0.0.55.dev1355__py3-none-any.whl

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.
evolve/config.py ADDED
@@ -0,0 +1,600 @@
1
+ """Configuration types for Evolve SDK."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, List, Literal, Optional, Protocol, TypedDict, Union, runtime_checkable
5
+
6
+
7
+ AgentType = Literal['codex', 'claude', 'gemini', 'qwen', 'kimi', 'opencode', 'droid']
8
+ WorkspaceMode = Literal['knowledge', 'swe', 'task']
9
+ BrowserProvider = Literal['browser-use', 'actionbook', 'agent-browser']
10
+ BrowserConfig = Union[BrowserProvider, Dict[str, Any]]
11
+ AgentPluginConfig = Dict[str, Any]
12
+ ReasoningEffort = Literal['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'thinking', 'no-thinking']
13
+ #: The platform's named agent-settings presets - exactly two at launch:
14
+ #: 'no-internet' (vendor server-side web tools off) and 'pinned-context'
15
+ #: (one fixed effective context window). See AgentConfig.preset.
16
+ AgentPreset = Literal['no-internet', 'pinned-context']
17
+ ValidationMode = Literal['strict', 'loose']
18
+
19
+
20
+ class SandboxNetworkPolicy(TypedDict, total=False):
21
+ outbound: Literal['open', 'blocked']
22
+ allowedDestinations: List[str]
23
+
24
+
25
+ class SandboxCreateOptions(TypedDict, total=False):
26
+ image: str
27
+ envs: Dict[str, str]
28
+ metadata: Dict[str, str]
29
+ timeoutMs: int
30
+ workingDirectory: str
31
+ network: SandboxNetworkPolicy
32
+ # Run all commands and file operations as this user; providers must reject
33
+ # it if they cannot enforce it, never silently ignore it.
34
+ user: str
35
+ # Home directory for agent config paths. Default: "/root" when user is
36
+ # "root", "/home/<user>" otherwise, "/home/user" when no user is given.
37
+ homeDir: str
38
+
39
+
40
+ @dataclass
41
+ class SchemaOptions:
42
+ """Validation options for schema validation.
43
+
44
+ Args:
45
+ mode: Validation mode - 'strict' (exact types) or 'loose' (coerce types, default)
46
+ """
47
+ mode: ValidationMode = 'loose'
48
+
49
+
50
+ @dataclass
51
+ class BrowserCredentialScopeEntry:
52
+ """Saved browser login selector for a run.
53
+
54
+ Args:
55
+ website: Website/domain, e.g. "github.com"
56
+ account_label: Optional one-word label for a saved credential, such as "qa-admin" or "work"; not the website username or email
57
+ """
58
+ website: str
59
+ account_label: Optional[str] = None
60
+
61
+ def to_dict(self) -> Dict[str, Any]:
62
+ result: Dict[str, Any] = {'website': self.website}
63
+ if self.account_label:
64
+ result['account_label'] = self.account_label
65
+ return result
66
+
67
+
68
+ @dataclass
69
+ class BrowserCredentialsConfig:
70
+ """Browser login MCP configuration for managed remote agent-browser runs.
71
+
72
+ Args:
73
+ allow: Optional list of website/account_label selectors. None or [] exposes all enabled browser logins.
74
+ """
75
+ allow: Optional[List[Union[BrowserCredentialScopeEntry, Dict[str, Any]]]] = None
76
+
77
+ def to_dict(self) -> Dict[str, Any]:
78
+ result: Dict[str, Any] = {}
79
+ if self.allow:
80
+ result['allow'] = [
81
+ entry.to_dict() if isinstance(entry, BrowserCredentialScopeEntry) else dict(entry)
82
+ for entry in self.allow
83
+ ]
84
+ return result
85
+
86
+
87
+ @dataclass
88
+ class HostedClientConfig:
89
+ """Standalone hosted evals client configuration (datasets/jobs).
90
+
91
+ Args:
92
+ api_key: Evolve API key override (default: EVOLVE_API_KEY)
93
+ base_url: API base URL override (default: the Evolve dashboard API)
94
+ """
95
+ api_key: Optional[str] = None
96
+ base_url: Optional[str] = None
97
+
98
+ def to_dict(self) -> Dict[str, Any]:
99
+ result: Dict[str, Any] = {}
100
+ if self.api_key:
101
+ result['api_key'] = self.api_key
102
+ if self.base_url:
103
+ result['base_url'] = self.base_url
104
+ return result
105
+
106
+
107
+ @dataclass
108
+ class BrowserCredentialsClientConfig:
109
+ """Standalone browser credentials client configuration.
110
+
111
+ Args:
112
+ api_key: Evolve API key override
113
+ dashboard_url: Dashboard URL override
114
+ """
115
+ api_key: Optional[str] = None
116
+ dashboard_url: Optional[str] = None
117
+
118
+ def to_dict(self) -> Dict[str, Any]:
119
+ result: Dict[str, Any] = {}
120
+ if self.api_key:
121
+ result['api_key'] = self.api_key
122
+ if self.dashboard_url:
123
+ result['dashboard_url'] = self.dashboard_url
124
+ return result
125
+
126
+
127
+ @dataclass
128
+ class BrowserProfilesClientConfig:
129
+ """Standalone browser profiles client configuration.
130
+
131
+ Args:
132
+ api_key: Evolve API key override
133
+ dashboard_url: Dashboard URL override
134
+ """
135
+ api_key: Optional[str] = None
136
+ dashboard_url: Optional[str] = None
137
+
138
+ def to_dict(self) -> Dict[str, Any]:
139
+ result: Dict[str, Any] = {}
140
+ if self.api_key:
141
+ result['api_key'] = self.api_key
142
+ if self.dashboard_url:
143
+ result['dashboard_url'] = self.dashboard_url
144
+ return result
145
+
146
+
147
+ @dataclass
148
+ class ManagedSecretRef:
149
+ """Dashboard-stored managed secret to attach to a sandbox session.
150
+
151
+ How the value reaches the sandbox is the STORED secret's delivery mode
152
+ (chosen when the secret is saved): 'brokered' keeps the value out of the
153
+ sandbox (opaque placeholder env + egress-proxy swap toward the secret's
154
+ allowed hosts) and 'direct' places the raw value in the sandbox
155
+ environment (URL-parameter keys, gRPC, websockets).
156
+
157
+ Args:
158
+ name: Stable Dashboard secret name, e.g. "GITHUB_TOKEN"
159
+ label: Optional labeled row of that name. Omitted = the server's
160
+ shared resolution law: the 'default'-labeled row when one
161
+ exists, the single row when exactly one exists, and a typed
162
+ ambiguity refusal naming every label otherwise.
163
+ as_name: Optional env var alias in the sandbox. Defaults to name.
164
+ """
165
+ name: str
166
+ label: Optional[str] = None
167
+ as_name: Optional[str] = None
168
+
169
+ def to_dict(self) -> Dict[str, Any]:
170
+ result: Dict[str, Any] = {'name': self.name}
171
+ if self.label:
172
+ result['label'] = self.label
173
+ if self.as_name:
174
+ result['as'] = self.as_name
175
+ return result
176
+
177
+
178
+ @dataclass
179
+ class ManagedSecretsClientConfig:
180
+ """Standalone managed secrets client configuration.
181
+
182
+ Args:
183
+ api_key: Evolve API key override
184
+ dashboard_url: Dashboard URL override
185
+ """
186
+ api_key: Optional[str] = None
187
+ dashboard_url: Optional[str] = None
188
+
189
+
190
+ @dataclass
191
+ class AgentConfig:
192
+ """Agent configuration.
193
+
194
+ Three modes of operation:
195
+ - Gateway mode: Use `api_key` (Evolve key) for dashboard observability
196
+ - Direct mode: Use `provider_api_key` (BYOK) to connect directly to providers
197
+ - OAuth mode: Use `oauth_token` for Claude Max subscription (Claude only)
198
+
199
+ All fields are optional - TS SDK auto-detects from environment variables.
200
+
201
+ Args:
202
+ type: Agent type (codex, claude, gemini, qwen, kimi, opencode, droid) - defaults to 'claude'
203
+ api_key: Evolve API key for gateway mode (defaults to EVOLVE_API_KEY env var)
204
+ provider_api_key: Provider API key for direct mode / BYOK (defaults to provider env var)
205
+ oauth_token: OAuth token for Claude Max subscription (defaults to CLAUDE_CODE_OAUTH_TOKEN env var)
206
+ provider_base_url: Provider base URL for direct mode (auto-detected for Qwen)
207
+ model: Model name (optional - uses agent's default if not specified). Use 'fable' for Claude Fable 5.1 or 'sonnet[1m]' / 'opus[1m]' for 1M context window (Claude only).
208
+ reasoning_effort: Reasoning effort for models that support it (optional)
209
+ max_context_size: Context/completion ceiling for CLIs that must be told one
210
+ (Kimi Code reads it as max_context_size and sends it as the request's
211
+ max_tokens). Set it to the model's real ceiling when driving a harness
212
+ against a model from another family - e.g. Kimi Code against 'gpt-5.5'
213
+ through an OpenAI-compatible gateway, where an oversized max_tokens is
214
+ rejected with a 400. When set it is used verbatim. When omitted, the
215
+ harness's own models keep their registry value and any other model falls
216
+ back to a conservative 128000. Harnesses that never send a ceiling ignore it.
217
+ """
218
+ type: Optional[AgentType] = None
219
+ api_key: Optional[str] = None
220
+ provider_api_key: Optional[str] = None
221
+ oauth_token: Optional[str] = None
222
+ provider_base_url: Optional[str] = None
223
+ model: Optional[str] = None
224
+ reasoning_effort: Optional[ReasoningEffort] = None
225
+ max_context_size: Optional[int] = None
226
+ #: Native agent settings - Harbor's ``config`` agent kwarg. A local file
227
+ #: path or an inline dict, converted into the harness's native settings
228
+ #: document inside the sandbox (Claude: settings JSON via --settings;
229
+ #: Codex: the base ~/.codex/config.toml). The user document is the BASE;
230
+ #: platform routing/MCP/model/effort are stamped on top. Only claude and
231
+ #: codex support it; any other agent type refuses loudly.
232
+ config: Optional[Union[str, Dict[str, Any]]] = None
233
+ #: Named agent-settings preset, delivered through the same channel as
234
+ #: ``config`` and stamped ON TOP of it: ``"no-internet"`` turns off the
235
+ #: vendor's server-side web tools (Claude settings deny WebSearch/WebFetch;
236
+ #: Codex ``-c web_search=disabled``), ``"pinned-context"`` pins one fixed
237
+ #: effective context window. Only claude and codex can guarantee them; any
238
+ #: other combination refuses loudly - never a run silently missing its
239
+ #: guarantee.
240
+ preset: Optional[AgentPreset] = None
241
+
242
+
243
+ @runtime_checkable
244
+ class SandboxProvider(Protocol):
245
+ """Sandbox provider protocol.
246
+
247
+ Any sandbox provider must implement this protocol.
248
+ Currently supported: E2BProvider, DaytonaProvider, ModalProvider
249
+
250
+ To add a new provider:
251
+ 1. Create a class with `type` and `config` properties
252
+ 2. Add handling in bridge/src/adapter.ts
253
+ """
254
+
255
+ @property
256
+ def type(self) -> str:
257
+ """Provider type identifier (e.g., 'e2b', 'daytona')."""
258
+ ...
259
+
260
+ @property
261
+ def config(self) -> dict:
262
+ """Provider configuration dict for the bridge."""
263
+ ...
264
+
265
+
266
+ @dataclass
267
+ class ManagedProvider:
268
+ """A sandbox the Evolve platform runs for you.
269
+
270
+ Managed mode means you hold an Evolve API key and no provider credential at
271
+ all: the Dashboard authenticates the key, records ownership, and makes the
272
+ provider call with platform credentials.
273
+
274
+ Which provider backs the sandbox is an argument here — never an
275
+ environment variable — so a program says what it runs on.
276
+
277
+ Args:
278
+ provider: 'e2b' (default), 'daytona', or 'modal'.
279
+ api_key: Evolve API key (defaults to the EVOLVE_API_KEY env var)
280
+ timeout_ms: Lifetime cap (ms) applied to every sandbox this provider
281
+ creates; per-create options still win.
282
+ resources: Compute sizing applied to every create, as a dict with
283
+ 'cpu' (cores), 'memory' and 'disk' (GiB) keys. Providers and the
284
+ managed doors reject entries they cannot enforce — never
285
+ silently ignored.
286
+
287
+ Example:
288
+ >>> evolve = Evolve(sandbox=ManagedProvider(provider='daytona', timeout_ms=7_200_000))
289
+ """
290
+ provider: Literal['e2b', 'daytona', 'modal'] = 'e2b'
291
+ api_key: Optional[str] = None
292
+ timeout_ms: Optional[int] = None
293
+ resources: Optional[dict] = None
294
+
295
+ @property
296
+ def type(self) -> Literal['managed']:
297
+ """Provider type."""
298
+ return 'managed'
299
+
300
+ @property
301
+ def config(self) -> dict:
302
+ """Provider configuration dict."""
303
+ result: dict = {'provider': self.provider}
304
+ if self.api_key:
305
+ result['apiKey'] = self.api_key
306
+ if self.timeout_ms is not None:
307
+ result['timeoutMs'] = self.timeout_ms
308
+ if self.resources is not None:
309
+ result['resources'] = self.resources
310
+ return result
311
+
312
+
313
+ @dataclass
314
+ class E2BProvider:
315
+ """E2B sandbox provider configuration.
316
+
317
+ Args:
318
+ api_key: E2B API key (defaults to E2B_API_KEY env var)
319
+ timeout_ms: Sandbox timeout in milliseconds (default: 3600000 = 1 hour)
320
+ template_id: E2B template ID (default: 'evolve-all'). Create custom templates at https://e2b.dev/docs/sandbox-template
321
+ """
322
+ api_key: Optional[str] = None
323
+ timeout_ms: int = 3600000
324
+ template_id: Optional[str] = None
325
+
326
+ @property
327
+ def type(self) -> Literal['e2b']:
328
+ """Provider type."""
329
+ return 'e2b'
330
+
331
+ @property
332
+ def config(self) -> dict:
333
+ """Provider configuration dict."""
334
+ result = {}
335
+ if self.api_key:
336
+ result['apiKey'] = self.api_key
337
+ if self.timeout_ms:
338
+ result['defaultTimeoutMs'] = self.timeout_ms
339
+ if self.template_id:
340
+ result['templateId'] = self.template_id
341
+ return result
342
+
343
+
344
+ @dataclass
345
+ class DaytonaProvider:
346
+ """Daytona sandbox provider configuration.
347
+
348
+ Args:
349
+ api_key: Daytona API key (defaults to DAYTONA_API_KEY env var)
350
+ api_url: API URL (defaults to https://app.daytona.io/api)
351
+ target: Target region (defaults to 'us')
352
+ timeout_ms: Sandbox timeout in milliseconds (default: 3600000 = 1 hour)
353
+ snapshot_name: Daytona snapshot name (default: the current release
354
+ snapshot, 'evolve-all-c-<12hex>' with the tag derived from the
355
+ image build inputs; explicit names pass through untouched).
356
+ Custom snapshots via ``cd assets && ./build.sh daytona``
357
+ """
358
+ api_key: Optional[str] = None
359
+ api_url: Optional[str] = None
360
+ target: Optional[str] = None
361
+ timeout_ms: int = 3600000
362
+ snapshot_name: Optional[str] = None
363
+
364
+ @property
365
+ def type(self) -> Literal['daytona']:
366
+ """Provider type."""
367
+ return 'daytona'
368
+
369
+ @property
370
+ def config(self) -> dict:
371
+ """Provider configuration dict."""
372
+ result = {}
373
+ if self.api_key:
374
+ result['apiKey'] = self.api_key
375
+ if self.api_url:
376
+ result['apiUrl'] = self.api_url
377
+ if self.target:
378
+ result['target'] = self.target
379
+ if self.timeout_ms:
380
+ result['defaultTimeoutMs'] = self.timeout_ms
381
+ if self.snapshot_name:
382
+ result['snapshotName'] = self.snapshot_name
383
+ return result
384
+
385
+
386
+ @dataclass
387
+ class ModalProvider:
388
+ """Modal sandbox provider configuration.
389
+
390
+ Args:
391
+ app_name: Modal app namespace (defaults to 'evolve-sandbox')
392
+ timeout_ms: Sandbox timeout in milliseconds (default: 3600000 = 1 hour)
393
+ token_id: Modal token ID (defaults to MODAL_TOKEN_ID env var)
394
+ token_secret: Modal token secret (defaults to MODAL_TOKEN_SECRET env var)
395
+ endpoint: Modal API endpoint (defaults to https://api.modal.com:443)
396
+ image_name: Docker image name (default: 'evolve-all'). Resolved through IMAGE_MAP or used as-is.
397
+ """
398
+ app_name: Optional[str] = None
399
+ timeout_ms: int = 3600000
400
+ token_id: Optional[str] = None
401
+ token_secret: Optional[str] = None
402
+ endpoint: Optional[str] = None
403
+ image_name: Optional[str] = None
404
+
405
+ @property
406
+ def type(self) -> Literal['modal']:
407
+ """Provider type."""
408
+ return 'modal'
409
+
410
+ @property
411
+ def config(self) -> dict:
412
+ """Provider configuration dict."""
413
+ result = {}
414
+ if self.app_name:
415
+ result['appName'] = self.app_name
416
+ if self.timeout_ms:
417
+ result['defaultTimeoutMs'] = self.timeout_ms
418
+ if self.token_id:
419
+ result['tokenId'] = self.token_id
420
+ if self.token_secret:
421
+ result['tokenSecret'] = self.token_secret
422
+ if self.endpoint:
423
+ result['endpoint'] = self.endpoint
424
+ if self.image_name:
425
+ result['imageName'] = self.image_name
426
+ return result
427
+
428
+
429
+ # =============================================================================
430
+ # STORAGE / CHECKPOINTING
431
+ # =============================================================================
432
+
433
+
434
+ @dataclass
435
+ class StorageCredentials:
436
+ """S3 credentials for BYOK storage.
437
+
438
+ Args:
439
+ access_key_id: AWS access key ID
440
+ secret_access_key: AWS secret access key
441
+ """
442
+ access_key_id: str
443
+ secret_access_key: str
444
+
445
+
446
+ @dataclass
447
+ class StorageConfig:
448
+ """Storage configuration for checkpoint persistence.
449
+
450
+ Two modes of operation:
451
+ - BYOK mode: Provide `url` (S3 bucket URL) with optional credentials
452
+ - Gateway mode: Leave empty — Evolve-managed storage via EVOLVE_API_KEY
453
+
454
+ Args:
455
+ url: S3 bucket URL (e.g., 's3://my-bucket/prefix/')
456
+ bucket: S3 bucket name (alternative to url)
457
+ prefix: Key prefix within bucket
458
+ region: AWS region (default: auto-detect)
459
+ endpoint: Custom S3 endpoint (e.g., Cloudflare R2, MinIO)
460
+ credentials: Explicit S3 credentials (defaults to env AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)
461
+ """
462
+ url: Optional[str] = None
463
+ bucket: Optional[str] = None
464
+ prefix: Optional[str] = None
465
+ region: Optional[str] = None
466
+ endpoint: Optional[str] = None
467
+ credentials: Optional[StorageCredentials] = None
468
+
469
+ def to_dict(self) -> Dict[str, Any]:
470
+ """Convert to dict for JSON-RPC transport."""
471
+ result: Dict[str, Any] = {}
472
+ if self.url:
473
+ result['url'] = self.url
474
+ if self.bucket:
475
+ result['bucket'] = self.bucket
476
+ if self.prefix:
477
+ result['prefix'] = self.prefix
478
+ if self.region:
479
+ result['region'] = self.region
480
+ if self.endpoint:
481
+ result['endpoint'] = self.endpoint
482
+ if self.credentials:
483
+ result['credentials'] = {
484
+ 'accessKeyId': self.credentials.access_key_id,
485
+ 'secretAccessKey': self.credentials.secret_access_key,
486
+ }
487
+ return result
488
+
489
+
490
+ @dataclass
491
+ class SessionsConfig:
492
+ """Configuration for the standalone sessions() client.
493
+
494
+ Gateway-only historical trace access. If omitted, the TypeScript sessions()
495
+ client resolves credentials from ``EVOLVE_API_KEY`` and dashboard defaults.
496
+
497
+ Args:
498
+ api_key: Explicit Evolve API key override
499
+ dashboard_url: Dashboard URL override (for staging/self-hosted setups)
500
+ """
501
+ api_key: Optional[str] = None
502
+ dashboard_url: Optional[str] = None
503
+
504
+ def to_dict(self) -> Dict[str, Any]:
505
+ """Convert to dict for JSON-RPC transport."""
506
+ result: Dict[str, Any] = {}
507
+ if self.api_key:
508
+ result['apiKey'] = self.api_key
509
+ if self.dashboard_url:
510
+ result['dashboardUrl'] = self.dashboard_url
511
+ return result
512
+
513
+
514
+ # =============================================================================
515
+ # MANAGED INTEGRATIONS
516
+ # =============================================================================
517
+
518
+
519
+ class EnableFilter(TypedDict):
520
+ """Enable only specific tools."""
521
+ enable: List[str]
522
+
523
+
524
+ class DisableFilter(TypedDict):
525
+ """Disable specific tools."""
526
+ disable: List[str]
527
+
528
+
529
+ class TagsFilter(TypedDict):
530
+ """Filter by behavior tags."""
531
+ tags: List[str]
532
+
533
+
534
+ # Tool filter configuration per app - matches TS SDK IntegrationToolsFilter
535
+ IntegrationToolsFilter = Union[List[str], EnableFilter, DisableFilter, TagsFilter]
536
+
537
+
538
+ @dataclass(kw_only=True)
539
+ class IntegrationsConfig:
540
+ """Managed integrations configuration.
541
+
542
+ Args:
543
+ apps: Apps to expose to the agent (e.g., ["github", "gmail"])
544
+ tools: Per-app tool filtering
545
+ accounts: Pin connected accounts by account ID or account label
546
+ keys: API keys for apps that use API-key auth
547
+ auth_configs: Custom auth config IDs per app
548
+ """
549
+ apps: List[str]
550
+ tools: Optional[Dict[str, IntegrationToolsFilter]] = None
551
+ accounts: Optional[Dict[str, List[str]]] = None
552
+ keys: Optional[Dict[str, str]] = None
553
+ auth_configs: Optional[Dict[str, str]] = None
554
+
555
+ def to_dict(self) -> Dict[str, Any]:
556
+ """Convert to dict for JSON-RPC transport."""
557
+ result: Dict[str, Any] = {'apps': self.apps}
558
+ if self.tools:
559
+ result['tools'] = self.tools
560
+ if self.accounts:
561
+ result['accounts'] = self.accounts
562
+ if self.keys:
563
+ result['keys'] = self.keys
564
+ if self.auth_configs:
565
+ result['auth_configs'] = self.auth_configs
566
+ return result
567
+
568
+
569
+ @dataclass(kw_only=True)
570
+ class IntegrationsSetup:
571
+ """Managed integrations setup.
572
+
573
+ Args:
574
+ user_id: Integration user ID. Use "root" for dashboard-owned/private accounts,
575
+ or your app's stable end-user ID for per-user accounts.
576
+ apps: Apps to expose to the agent
577
+ tools: Per-app tool filtering
578
+ accounts: Pin connected accounts by account ID or account label
579
+ keys: API keys for apps that use API-key auth
580
+ auth_configs: Custom auth config IDs per app
581
+ """
582
+ user_id: str
583
+ apps: List[str]
584
+ tools: Optional[Dict[str, IntegrationToolsFilter]] = None
585
+ accounts: Optional[Dict[str, List[str]]] = None
586
+ keys: Optional[Dict[str, str]] = None
587
+ auth_configs: Optional[Dict[str, str]] = None
588
+
589
+ def to_dict(self) -> Dict[str, Any]:
590
+ """Convert to dict for JSON-RPC transport."""
591
+ result: Dict[str, Any] = {'user_id': self.user_id, 'apps': self.apps}
592
+ if self.tools:
593
+ result['tools'] = self.tools
594
+ if self.accounts:
595
+ result['accounts'] = self.accounts
596
+ if self.keys:
597
+ result['keys'] = self.keys
598
+ if self.auth_configs:
599
+ result['auth_configs'] = self.auth_configs
600
+ return result