diff --git a/CHANGELOG.md b/CHANGELOG.md index c4855f71..63d82d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to the `synth-ai` package are documented here. +## Unreleased + +### Removed + +- **Grok / xAI** (breaking enum change). `SmrAgentModel.X_AI_GROK_BUILD`, + `SmrAgentModel.CURSOR_GROK_4_5`, `ActorModel.GROK_BUILD` / + `DeprecatedActorModel.GROK_BUILD`, and the `xai` member of + `ResourceProvider`, `SmrCredentialProvider`, `SmrInferenceProvider`, + `CredentialProvider`, and `InferenceProvider` are gone, as is `xai` from the + public provider selection. The backend no longer serves `x-ai/grok-build` + or `cursor/grok-4.5`; use `openrouter/openai/gpt-5.6-luna`. + ## 0.19.0 — 2026-09-14 ### Added diff --git a/README.md b/README.md index a2f20e5c..266aa2c8 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,32 @@ CLI discovery: synth-ai research --help ``` +Project creation also accepts the backend-owned `ProjectSpec.policy` mapping. +For example, a server-enabled fresh project can request +`policy={"host_resource_custody_mode": "horizons_docker_sessions_only"}`. +The backend validates this restricted mode and owns its immutable resource +binding; SDK serialization does not grant additional authority. + +## Container pools + +The optional `synth-ai[pools]` extra exposes the canonical `synth-containers` +client through `AsyncSynthClient.pools`. It uses the same configured backend +credential and keeps hosted admission, resource ownership, and recovery in the +backend. The enclosing async client closes the pool transport. + +```python +from synth_ai import AsyncSynthClient + +async def inspect_lease(lease_id: str, task_id: str): + async with AsyncSynthClient() as client: + return await client.pools.get_lease_interactive(lease_id, task_id=task_id) +``` + +For explicit lifetime management, `from synth_ai.pools import PoolClient` +re-exports the same implementation. Research-only installations do not import +this optional dependency. Development candidates must install the exact pinned +containers wheel; an unpublished candidate extra is not a release claim. + ## CLI ```bash diff --git a/openapi/research-v1.json b/openapi/research-v1.json index 6e88d853..b4f13645 100644 --- a/openapi/research-v1.json +++ b/openapi/research-v1.json @@ -405,6 +405,258 @@ "title": "AgentConfigV1", "type": "object" }, + "AsyncBlockerHandoffContextV1": { + "additionalProperties": false, + "properties": { + "action_digest": { + "pattern": "^[0-9a-f]{64}$", + "title": "Action Digest", + "type": "string" + }, + "action_kind": { + "title": "Action Kind", + "type": "string" + }, + "action_schema_version": { + "title": "Action Schema Version", + "type": "string" + }, + "async_assignment_id": { + "title": "Async Assignment Id", + "type": "string" + }, + "binding": { + "$ref": "#/components/schemas/RuntimeBinding" + }, + "blocker_id": { + "title": "Blocker Id", + "type": "string" + }, + "evidence_resources": { + "default": [], + "items": { + "$ref": "#/components/schemas/ProducedResourceReferenceV1" + }, + "title": "Evidence Resources", + "type": "array" + }, + "preauthorization_rule": { + "title": "Preauthorization Rule", + "type": "string" + }, + "rationale": { + "maxLength": 4000, + "minLength": 1, + "title": "Rationale", + "type": "string" + }, + "required_operator_capability": { + "title": "Required Operator Capability", + "type": "string" + }, + "schema_version": { + "const": "smr.intern-async-blocker-handoff-context.v1", + "default": "smr.intern-async-blocker-handoff-context.v1", + "title": "Schema Version", + "type": "string" + }, + "summary": { + "maxLength": 2000, + "minLength": 1, + "title": "Summary", + "type": "string" + } + }, + "required": [ + "blocker_id", + "async_assignment_id", + "binding", + "action_schema_version", + "action_kind", + "action_digest", + "summary", + "rationale", + "preauthorization_rule", + "required_operator_capability" + ], + "title": "AsyncBlockerHandoffContextV1", + "type": "object" + }, + "AsyncBlockerHandoffReceiptV1": { + "additionalProperties": false, + "properties": { + "blocker_id": { + "title": "Blocker Id", + "type": "string" + }, + "context": { + "$ref": "#/components/schemas/AsyncBlockerHandoffContextV1" + }, + "context_digest": { + "pattern": "^[0-9a-f]{64}$", + "title": "Context Digest", + "type": "string" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "handoff_receipt_id": { + "title": "Handoff Receipt Id", + "type": "string" + }, + "idempotency_key": { + "title": "Idempotency Key", + "type": "string" + }, + "opened_by_user_id": { + "title": "Opened By User Id", + "type": "string" + }, + "org_id": { + "title": "Org Id", + "type": "string" + }, + "schema_version": { + "const": "smr.intern-async-blocker-handoff-receipt.v1", + "default": "smr.intern-async-blocker-handoff-receipt.v1", + "title": "Schema Version", + "type": "string" + }, + "sync_session_id": { + "title": "Sync Session Id", + "type": "string" + } + }, + "required": [ + "handoff_receipt_id", + "blocker_id", + "org_id", + "sync_session_id", + "opened_by_user_id", + "idempotency_key", + "context", + "context_digest", + "created_at" + ], + "title": "AsyncBlockerHandoffReceiptV1", + "type": "object" + }, + "AsyncBlockerOpenSyncRequest": { + "additionalProperties": false, + "properties": { + "idempotency_key": { + "maxLength": 512, + "minLength": 1, + "title": "Idempotency Key", + "type": "string" + } + }, + "required": [ + "idempotency_key" + ], + "title": "AsyncBlockerOpenSyncRequest", + "type": "object" + }, + "AsyncBlockerOpenSyncResponse": { + "additionalProperties": false, + "properties": { + "blocker": { + "$ref": "#/components/schemas/AsyncBlockerResponse" + }, + "handoff_receipt": { + "$ref": "#/components/schemas/AsyncBlockerHandoffReceiptV1" + }, + "schema_version": { + "const": "smr.intern-async-blocker-open-sync.v1", + "default": "smr.intern-async-blocker-open-sync.v1", + "title": "Schema Version", + "type": "string" + }, + "sync_session": { + "$ref": "#/components/schemas/SyncSessionResponse" + } + }, + "required": [ + "blocker", + "sync_session", + "handoff_receipt" + ], + "title": "AsyncBlockerOpenSyncResponse", + "type": "object" + }, + "AsyncBlockerResolveRequest": { + "additionalProperties": false, + "description": "Operator disposition for one exact Async-to-Sync handoff.", + "properties": { + "comment": { + "anyOf": [ + { + "maxLength": 4000, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Comment" + }, + "idempotency_key": { + "maxLength": 512, + "minLength": 1, + "title": "Idempotency Key", + "type": "string" + }, + "outcome": { + "enum": [ + "completed", + "denied", + "superseded" + ], + "title": "Outcome", + "type": "string" + }, + "supporting_receipt_ids": { + "default": [], + "items": { + "type": "string" + }, + "maxItems": 128, + "title": "Supporting Receipt Ids", + "type": "array" + } + }, + "required": [ + "idempotency_key", + "outcome" + ], + "title": "AsyncBlockerResolveRequest", + "type": "object" + }, + "AsyncBlockerResolveResponse": { + "additionalProperties": false, + "properties": { + "blocker": { + "$ref": "#/components/schemas/AsyncBlockerResponse" + }, + "continuation_command": { + "$ref": "#/components/schemas/InternRuntimeCommandReceipt" + }, + "schema_version": { + "const": "smr.intern-async-blocker-resolution.v1", + "default": "smr.intern-async-blocker-resolution.v1", + "title": "Schema Version", + "type": "string" + } + }, + "required": [ + "blocker", + "continuation_command" + ], + "title": "AsyncBlockerResolveResponse", + "type": "object" + }, "AsyncBlockerResponse": { "additionalProperties": false, "properties": { @@ -442,6 +694,17 @@ ], "title": "Action Schema Version" }, + "async_assignment_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Async Assignment Id" + }, "binding": { "anyOf": [ { @@ -8529,6 +8792,230 @@ "title": "InternProgressClaimStatus", "type": "string" }, + "InternResourceDisposition": { + "additionalProperties": false, + "description": "One enumerated resource. ``retained`` always names its owner and reason.", + "properties": { + "cleanup_owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cleanup Owner" + }, + "cleanup_owner_run_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cleanup Owner Run Id" + }, + "disposition": { + "enum": [ + "settled", + "pending", + "unknown", + "excluded", + "retained" + ], + "title": "Disposition", + "type": "string" + }, + "epoch": { + "anyOf": [ + { + "minimum": 0.0, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Epoch" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "relation": { + "enum": [ + "self", + "owned", + "borrowed", + "shared", + "retained" + ], + "title": "Relation", + "type": "string" + }, + "resource_id": { + "title": "Resource Id", + "type": "string" + }, + "resource_kind": { + "title": "Resource Kind", + "type": "string" + } + }, + "required": [ + "resource_kind", + "resource_id", + "relation", + "disposition", + "reason" + ], + "title": "InternResourceDisposition", + "type": "object" + }, + "InternResourceInventory": { + "additionalProperties": false, + "description": "``coverage_complete`` is creator coverage; ``disposition_complete`` is cleanup.\n\nPending and unknown entries may exist in a complete inventory.", + "properties": { + "coverage": { + "const": "registered-runtime-resources-v1", + "default": "registered-runtime-resources-v1", + "title": "Coverage", + "type": "string" + }, + "coverage_complete": { + "default": false, + "title": "Coverage Complete", + "type": "boolean" + }, + "creator_set": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Creator Set" + }, + "disposition_complete": { + "default": false, + "title": "Disposition Complete", + "type": "boolean" + }, + "epochs": { + "items": { + "$ref": "#/components/schemas/InternRuntimeResourceEpoch" + }, + "title": "Epochs", + "type": "array" + }, + "incomplete_reasons": { + "items": { + "type": "string" + }, + "title": "Incomplete Reasons", + "type": "array" + }, + "observed_at": { + "format": "date-time", + "title": "Observed At", + "type": "string" + }, + "profile": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Profile" + }, + "resources": { + "items": { + "$ref": "#/components/schemas/InternResourceDisposition" + }, + "title": "Resources", + "type": "array" + }, + "runtime_id": { + "title": "Runtime Id", + "type": "string" + }, + "runtime_kind": { + "enum": [ + "sync", + "async" + ], + "title": "Runtime Kind", + "type": "string" + } + }, + "required": [ + "runtime_kind", + "runtime_id", + "observed_at", + "resources" + ], + "title": "InternResourceInventory", + "type": "object" + }, + "InternResourceStopEffect": { + "additionalProperties": false, + "description": "An effect actually performed when a runtime epoch closed.", + "properties": { + "effect": { + "enum": [ + "stop_requested", + "already_settled", + "retained", + "admission_fenced", + "stopped", + "already_terminal", + "stop_accepted", + "stop_failed" + ], + "title": "Effect", + "type": "string" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "resource_id": { + "title": "Resource Id", + "type": "string" + }, + "resource_kind": { + "title": "Resource Kind", + "type": "string" + } + }, + "required": [ + "resource_kind", + "resource_id", + "effect", + "reason" + ], + "title": "InternResourceStopEffect", + "type": "object" + }, "InternRuntimeCommandReceipt": { "additionalProperties": false, "properties": { @@ -8751,6 +9238,203 @@ "title": "InternRuntimeEventStreamEnvelope", "type": "object" }, + "InternRuntimeResourceEpoch": { + "additionalProperties": false, + "description": "One admission epoch; closed epochs remain enumerated for recovery.", + "properties": { + "admission": { + "enum": [ + "open", + "closed" + ], + "title": "Admission", + "type": "string" + }, + "cleanup_settled_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cleanup Settled At" + }, + "closed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Closed At" + }, + "closure_kind": { + "anyOf": [ + { + "enum": [ + "conversation_closed", + "work_cancelled", + "work_completed", + "work_failed", + "archived", + "superseded_by_reopen" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Closure Kind" + }, + "effects": { + "items": { + "$ref": "#/components/schemas/InternResourceStopEffect" + }, + "title": "Effects", + "type": "array" + }, + "epoch": { + "minimum": 0.0, + "title": "Epoch", + "type": "integer" + }, + "opened_at": { + "format": "date-time", + "title": "Opened At", + "type": "string" + } + }, + "required": [ + "epoch", + "admission", + "opened_at" + ], + "title": "InternRuntimeResourceEpoch", + "type": "object" + }, + "InternSessionUsageResponse": { + "additionalProperties": false, + "description": "Usage receipt for one Intern runtime: its direct inference plus the runs it launched.", + "properties": { + "billing": { + "$ref": "#/components/schemas/InternUsageBillingState" + }, + "bindings": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Bindings", + "type": "array" + }, + "metadata": { + "additionalProperties": true, + "title": "Metadata", + "type": "object" + }, + "org_id": { + "title": "Org Id", + "type": "string" + }, + "origin_runtime_id": { + "title": "Origin Runtime Id", + "type": "string" + }, + "origin_runtime_kind": { + "enum": [ + "sync", + "async" + ], + "title": "Origin Runtime Kind", + "type": "string" + }, + "run_count": { + "title": "Run Count", + "type": "integer" + }, + "spend_cents": { + "title": "Spend Cents", + "type": "integer" + }, + "token_count": { + "title": "Token Count", + "type": "integer" + } + }, + "required": [ + "origin_runtime_kind", + "origin_runtime_id", + "org_id", + "spend_cents", + "token_count", + "run_count", + "billing", + "bindings", + "metadata" + ], + "title": "InternSessionUsageResponse", + "type": "object" + }, + "InternUsageBillingState": { + "additionalProperties": false, + "properties": { + "billing_failed_row_count": { + "title": "Billing Failed Row Count", + "type": "integer" + }, + "billing_state": { + "enum": [ + "settled", + "pending", + "stalled", + "failed", + "observability_only", + "not_applicable" + ], + "title": "Billing State", + "type": "string" + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Message" + }, + "show_stalled_banner": { + "title": "Show Stalled Banner", + "type": "boolean" + }, + "stalled_row_count": { + "title": "Stalled Row Count", + "type": "integer" + }, + "stalled_spend_cents": { + "title": "Stalled Spend Cents", + "type": "integer" + } + }, + "required": [ + "billing_state", + "stalled_row_count", + "stalled_spend_cents", + "billing_failed_row_count", + "show_stalled_banner" + ], + "title": "InternUsageBillingState", + "type": "object" + }, "JsonValue": {}, "MagiActuationStatus": { "enum": [ @@ -10206,6 +10890,14 @@ "title": "OeqResolutionResponse", "type": "object" }, + "OriginRuntimeKind": { + "enum": [ + "sync", + "async" + ], + "title": "OriginRuntimeKind", + "type": "string" + }, "ParentObjectiveKind": { "enum": [ "open_ended_question", @@ -12816,7 +13508,6 @@ "synth_ai", "cursor", "deepseek", - "xai", "modal", "openai_chatgpt", "baseten" @@ -13082,6 +13773,146 @@ "title": "RunLifecycleEnqueueStatus", "type": "string" }, + "RunResourceSettlementResponse": { + "additionalProperties": false, + "properties": { + "confirmed": { + "anyOf": [ + { + "minimum": 0.0, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Confirmed" + }, + "coverage": { + "enum": [ + "untracked", + "explicit-v1" + ], + "title": "Coverage", + "type": "string" + }, + "coverage_complete": { + "default": false, + "title": "Coverage Complete", + "type": "boolean" + }, + "edge_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Edge Id" + }, + "excluded": { + "anyOf": [ + { + "minimum": 0.0, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Excluded" + }, + "observed_at": { + "format": "date-time", + "title": "Observed At", + "type": "string" + }, + "pending": { + "anyOf": [ + { + "minimum": 0.0, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Pending" + }, + "registered_tree_settled": { + "default": false, + "title": "Registered Tree Settled", + "type": "boolean" + }, + "root_confirmed": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Root Confirmed" + }, + "root_run_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Root Run Id" + }, + "run_id": { + "title": "Run Id", + "type": "string" + }, + "scope_kind": { + "anyOf": [ + { + "enum": [ + "root_tree", + "owned_subtree" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Scope Kind" + }, + "settled": { + "title": "Settled", + "type": "boolean" + }, + "unknown": { + "anyOf": [ + { + "minimum": 0.0, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Unknown" + } + }, + "required": [ + "run_id", + "observed_at", + "coverage", + "settled" + ], + "title": "RunResourceSettlementResponse", + "type": "object" + }, "RunbookKind": { "enum": [ "lite", @@ -13552,7 +14383,6 @@ "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra", - "cursor/grok-4.5", "gpt-5.4", "gpt-5.4-mini", "nemotron-super", @@ -13561,8 +14391,6 @@ "cursor/composer-2.5", "cursor/gpt-5", "cursor/sonnet-4", - "x-ai/grok-4.3", - "x-ai/grok-build", "moonshotai/kimi-k2.6", "baseten/moonshotai/Kimi-K3", "modal/moonshotai/Kimi-K3", @@ -14798,7 +15626,6 @@ "openrouter", "poolside", "synth_internal", - "xai", "tinker" ], "title": "SmrCredentialProvider", @@ -20598,8 +21425,7 @@ "openrouter", "poolside", "synth", - "synth_internal", - "xai" + "synth_internal" ], "title": "SmrInferenceProvider", "type": "string" @@ -22245,7 +23071,7 @@ "type": "null" } ], - "description": "Override one shared agent model for this run. Shared top-level values are 'baseten/moonshotai/Kimi-K3', 'baseten/zai-org/GLM-5.2', 'cursor/composer-2.5', 'deepseek/deepseek-v4-flash', 'deepseek/deepseek-v4-flash-direct', 'deepseek/deepseek-v4-pro', 'deepseek/deepseek-v4-pro-direct', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.5', 'gpt-5.6-luna', 'modal/moonshotai/Kimi-K3', 'openrouter/openai/gpt-5.6-luna', 'openrouter/poolside/laguna-s-2.1', 'poolside/laguna-s-2.1', 'synth_internal/laguna-s-2.1-nvfp4', 'x-ai/grok-4.3', 'x-ai/grok-build'. Use actor_model_overrides for actor-specific model selection." + "description": "Override one shared agent model for this run. Shared top-level values are 'baseten/moonshotai/Kimi-K3', 'baseten/zai-org/GLM-5.2', 'cursor/composer-2.5', 'deepseek/deepseek-v4-flash', 'deepseek/deepseek-v4-flash-direct', 'deepseek/deepseek-v4-pro', 'deepseek/deepseek-v4-pro-direct', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.5', 'gpt-5.6-luna', 'modal/moonshotai/Kimi-K3', 'openrouter/openai/gpt-5.6-luna', 'openrouter/poolside/laguna-s-2.1', 'poolside/laguna-s-2.1', 'synth_internal/laguna-s-2.1-nvfp4'. Use actor_model_overrides for actor-specific model selection." }, "agent_model_params": { "anyOf": [ @@ -22597,7 +23423,7 @@ "type": "null" } ], - "description": "Declarative provider routing preferences. Use agent_inference for agent actor traffic and experiment_inference for benchmark/eval model traffic. Defaults require ZDR and US domicile/region for OpenAI, Baseten, Gemini, and Grok-family providers." + "description": "Declarative provider routing preferences. Use agent_inference for agent actor traffic and experiment_inference for benchmark/eval model traffic. Defaults require ZDR and US domicile/region for OpenAI, Baseten, and Gemini providers." }, "providers": { "description": "Run-scoped resource bindings. This is independent from the inference provider selector.", @@ -24498,6 +25324,18 @@ ], "title": "Requested State" }, + "resource_settlement": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Resource Settlement" + }, "run_id": { "title": "Run Id", "type": "string" @@ -27031,6 +27869,28 @@ "title": "Org Id", "type": "string" }, + "origin_runtime_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Origin Runtime Id" + }, + "origin_runtime_kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Origin Runtime Kind" + }, "output_obligations": { "items": { "$ref": "#/components/schemas/SmrRunOutputObligationResponse" @@ -30588,6 +31448,7 @@ "title": "Event Id" }, "kind": { + "description": "Transcript event kind, e.g. message.completed, reasoning.summary, turn.completed, tool.call.completed, or task.completion_claimed (an accepted worker completion claim projected from its durable claim row; payload schema smr.task-completion-claimed.v1 with claim_id, task_key, participant_id, text and summary).", "minLength": 1, "title": "Kind", "type": "string" @@ -33095,6 +33956,601 @@ "title": "SwarmNotebookResponse", "type": "object" }, + "SwarmRolloutArtifact": { + "additionalProperties": false, + "description": "A rollout-owned retained artifact; storage location stays private.", + "properties": { + "artifact_id": { + "title": "Artifact Id", + "type": "string" + }, + "artifact_type": { + "title": "Artifact Type", + "type": "string" + }, + "content_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Type" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "size_bytes": { + "title": "Size Bytes", + "type": "integer" + } + }, + "required": [ + "artifact_id", + "artifact_type", + "size_bytes" + ], + "title": "SwarmRolloutArtifact", + "type": "object" + }, + "SwarmRolloutExecution": { + "additionalProperties": false, + "description": "What the Rhodes executor actually launched for this rollout.\n\nWritten by the worker from the resolved execution target and provider\nlaunch facts; never from request or result payloads. ``executed_image_digest``\nis set only when the launch reference was digest-pinned or the provider\nresolved the exact image ID; otherwise ``image_attestation`` says why not.", + "properties": { + "deployment_binding": { + "title": "Deployment Binding", + "type": "string" + }, + "executed_image_digest": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Executed Image Digest" + }, + "executed_source_digest": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Executed Source Digest" + }, + "image_attestation": { + "title": "Image Attestation", + "type": "string" + }, + "interface": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Interface" + }, + "interface_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Interface Mode" + }, + "launch_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Launch Id" + }, + "launches": { + "items": { + "$ref": "#/components/schemas/SwarmRolloutExecutionLaunch" + }, + "title": "Launches", + "type": "array" + }, + "lease": { + "anyOf": [ + { + "$ref": "#/components/schemas/SwarmRolloutLease" + }, + { + "type": "null" + } + ] + }, + "lease_disposition": { + "$ref": "#/components/schemas/SwarmRolloutLeaseDisposition" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Provider" + }, + "recorded_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recorded At" + }, + "recorded_by": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recorded By" + } + }, + "required": [ + "lease_disposition", + "deployment_binding", + "image_attestation" + ], + "title": "SwarmRolloutExecution", + "type": "object" + }, + "SwarmRolloutExecutionLaunch": { + "additionalProperties": false, + "description": "One provider sandbox launch (Harbor: agent, then optional verifier).", + "properties": { + "executed_image_digest": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Executed Image Digest" + }, + "image_attestation": { + "title": "Image Attestation", + "type": "string" + }, + "launch_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Launch Id" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Provider" + }, + "recorded_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Recorded At" + }, + "role": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Role" + }, + "sequence": { + "title": "Sequence", + "type": "integer" + } + }, + "required": [ + "sequence", + "image_attestation" + ], + "title": "SwarmRolloutExecutionLaunch", + "type": "object" + }, + "SwarmRolloutLease": { + "additionalProperties": false, + "properties": { + "lease_id": { + "title": "Lease Id", + "type": "string" + } + }, + "required": [ + "lease_id" + ], + "title": "SwarmRolloutLease", + "type": "object" + }, + "SwarmRolloutLeaseDisposition": { + "additionalProperties": false, + "description": "Why ``lease`` is or is not set; never inferred from a nearby lease.", + "properties": { + "reason": { + "title": "Reason", + "type": "string" + }, + "status": { + "enum": [ + "recorded", + "not_applicable" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "reason" + ], + "title": "SwarmRolloutLeaseDisposition", + "type": "object" + }, + "SwarmRolloutLogicalIntent": { + "additionalProperties": false, + "description": "The one intended evaluation this rollout executes (server-bound scope).\n\n``accepted_submissions`` counts distinct submissions (request keys) that\nattached to this rollout instead of creating another paid execution.\n``intent_id`` is set only for an explicit caller intent.", + "properties": { + "accepted_submissions": { + "minimum": 1.0, + "title": "Accepted Submissions", + "type": "integer" + }, + "intent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Intent Id" + }, + "intent_ref": { + "title": "Intent Ref", + "type": "string" + }, + "scope_kind": { + "enum": [ + "smr_run", + "intern_runtime", + "organization", + "intern_delegation" + ], + "title": "Scope Kind", + "type": "string" + }, + "source": { + "enum": [ + "explicit", + "derived" + ], + "title": "Source", + "type": "string" + } + }, + "required": [ + "intent_ref", + "source", + "scope_kind", + "accepted_submissions" + ], + "title": "SwarmRolloutLogicalIntent", + "type": "object" + }, + "SwarmRolloutPage": { + "additionalProperties": false, + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/SwarmRolloutSummary" + }, + "title": "Items", + "type": "array" + }, + "limit": { + "title": "Limit", + "type": "integer" + }, + "run_id": { + "title": "Run Id", + "type": "string" + } + }, + "required": [ + "run_id", + "items", + "limit" + ], + "title": "SwarmRolloutPage", + "type": "object" + }, + "SwarmRolloutReleaseCoordinates": { + "additionalProperties": false, + "description": "Release identities frozen at acceptance, not a native execution attestation.", + "properties": { + "resolved_image_digest": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Resolved Image Digest" + }, + "runtime_image_release_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Runtime Image Release Id" + }, + "shared_bundle_release_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Shared Bundle Release Id" + }, + "task_bundle_release_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Task Bundle Release Id" + } + }, + "title": "SwarmRolloutReleaseCoordinates", + "type": "object" + }, + "SwarmRolloutSummary": { + "additionalProperties": false, + "description": "One pool rollout whose verified budget parent is the swarm.\n\nBuilt field by field from the rollout row, never from the stored request,\nso task-provided environment and transport credentials cannot leak.", + "properties": { + "adapter": { + "title": "Adapter", + "type": "string" + }, + "artifacts": { + "items": { + "$ref": "#/components/schemas/SwarmRolloutArtifact" + }, + "title": "Artifacts", + "type": "array" + }, + "budget_parent_run_id": { + "title": "Budget Parent Run Id", + "type": "string" + }, + "cancelled_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cancelled At" + }, + "completed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Completed At" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + }, + "execution": { + "anyOf": [ + { + "$ref": "#/components/schemas/SwarmRolloutExecution" + }, + { + "type": "null" + } + ] + }, + "logical_intent": { + "anyOf": [ + { + "$ref": "#/components/schemas/SwarmRolloutLogicalIntent" + }, + { + "type": "null" + } + ] + }, + "pool_id": { + "title": "Pool Id", + "type": "string" + }, + "release_coordinates": { + "anyOf": [ + { + "$ref": "#/components/schemas/SwarmRolloutReleaseCoordinates" + }, + { + "type": "null" + } + ] + }, + "rollout_id": { + "title": "Rollout Id", + "type": "string" + }, + "score": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Score" + }, + "seed": { + "title": "Seed", + "type": "integer" + }, + "started_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Started At" + }, + "status": { + "title": "Status", + "type": "string" + }, + "success": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Success" + }, + "task_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Task Id" + }, + "trace_correlation_id": { + "title": "Trace Correlation Id", + "type": "string" + } + }, + "required": [ + "rollout_id", + "pool_id", + "adapter", + "status", + "seed", + "trace_correlation_id", + "budget_parent_run_id" + ], + "title": "SwarmRolloutSummary", + "type": "object" + }, "SyncApprovalActionV1": { "additionalProperties": false, "properties": { @@ -36183,16 +37639,18 @@ "description": "Backend authority used to serve one transcript page.", "enum": [ "smr_control_plane.redis.live_transcript.v1", - "smr_transcript_events.terminal_archive.v1" + "smr_transcript_events.terminal_archive.v1", + "smr_transcript_events.durable_replay.v1" ], "title": "TranscriptProjectionAuthority", "type": "string" }, "TranscriptReplayMode": { - "description": "Ordering domain used by transcript page cursors.", + "description": "Ordering domain used by transcript page cursors.\n\n``live_tail`` pages come from the Redis live stream while the run is live.\n``terminal_archive`` pages replay durable transcript rows after the run is\nterminal. ``durable_replay`` pages replay durable rows for a live run whose\nlive stream no longer holds the requested range (trimmed or drained) or\nwhen the caller resumes with a durable cursor.", "enum": [ "live_tail", - "terminal_archive" + "terminal_archive", + "durable_replay" ], "title": "TranscriptReplayMode", "type": "string" @@ -42859,6 +44317,42 @@ "description": "Opaque pagination cursor. Accepted forms: '', '|', or ''.", "title": "Cursor" } + }, + { + "description": "Intern runtime kind that launched the run; requires origin_runtime_id.", + "in": "query", + "name": "origin_runtime_kind", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/OriginRuntimeKind" + }, + { + "type": "null" + } + ], + "description": "Intern runtime kind that launched the run; requires origin_runtime_id.", + "title": "Origin Runtime Kind" + } + }, + { + "description": "Intern Sync session or Async assignment id that launched the run.", + "in": "query", + "name": "origin_runtime_id", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Intern Sync session or Async assignment id that launched the run.", + "title": "Origin Runtime Id" + } } ], "responses": { @@ -44419,28 +45913,118 @@ "description": "Validation Error" } }, - "summary": "Ensure Intern Async Runtime", + "summary": "Ensure Intern Async Runtime", + "tags": [ + "smr", + "research-intern" + ] + } + }, + "/smr/research-intern/async-assignments": { + "get": { + "deprecated": true, + "operationId": "list_intern_async_assignments", + "parameters": [ + { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 100, + "maximum": 500, + "minimum": 1, + "title": "Limit", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AsyncRuntimeResponse" + }, + "title": "Response List Intern Async Assignments Smr Research Intern Async Assignments Get", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "List Intern Async Assignments", + "tags": [ + "smr", + "research-intern" + ] + }, + "post": { + "deprecated": true, + "description": "Create leave-safe Async work before starting durable orchestration.", + "operationId": "create_intern_async_assignment", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncRuntimeEnsureRequest" + } + } + }, + "required": true + }, + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncRuntimeResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Create Intern Async Assignment", "tags": [ "smr", "research-intern" ] } }, - "/smr/research-intern/async-assignments": { + "/smr/research-intern/async-assignments/{assignment_id}": { "get": { "deprecated": true, - "operationId": "list_intern_async_assignments", + "operationId": "get_intern_async_assignment", "parameters": [ { - "in": "query", - "name": "limit", - "required": false, + "in": "path", + "name": "assignment_id", + "required": true, "schema": { - "default": 100, - "maximum": 500, - "minimum": 1, - "title": "Limit", - "type": "integer" + "title": "Assignment Id", + "type": "string" } } ], @@ -44449,11 +46033,7 @@ "content": { "application/json": { "schema": { - "items": { - "$ref": "#/components/schemas/AsyncRuntimeResponse" - }, - "title": "Response List Intern Async Assignments Smr Research Intern Async Assignments Get", - "type": "array" + "$ref": "#/components/schemas/AsyncRuntimeResponse" } } }, @@ -44470,21 +46050,33 @@ "description": "Validation Error" } }, - "summary": "List Intern Async Assignments", + "summary": "Get Intern Async Assignment", "tags": [ "smr", "research-intern" ] - }, + } + }, + "/smr/research-intern/async-assignments/{assignment_id}/commands": { "post": { "deprecated": true, - "description": "Create leave-safe Async work before starting durable orchestration.", - "operationId": "create_intern_async_assignment", + "operationId": "command_intern_async_assignment", + "parameters": [ + { + "in": "path", + "name": "assignment_id", + "required": true, + "schema": { + "title": "Assignment Id", + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AsyncRuntimeEnsureRequest" + "$ref": "#/components/schemas/InternRuntimeCommandRequest" } } }, @@ -44495,7 +46087,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AsyncRuntimeResponse" + "$ref": "#/components/schemas/InternRuntimeCommandReceipt" } } }, @@ -44512,17 +46104,17 @@ "description": "Validation Error" } }, - "summary": "Create Intern Async Assignment", + "summary": "Command Intern Async Assignment", "tags": [ "smr", "research-intern" ] } }, - "/smr/research-intern/async-assignments/{assignment_id}": { + "/smr/research-intern/async-assignments/{assignment_id}/resources": { "get": { - "deprecated": true, - "operationId": "get_intern_async_assignment", + "description": "Read the exact recorded assignment, never silently substitute today's singleton.", + "operationId": "get_intern_async_assignment_resources", "parameters": [ { "in": "path", @@ -44539,7 +46131,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AsyncRuntimeResponse" + "$ref": "#/components/schemas/InternResourceInventory" } } }, @@ -44556,17 +46148,16 @@ "description": "Validation Error" } }, - "summary": "Get Intern Async Assignment", + "summary": "Get Intern Async Assignment Resources", "tags": [ "smr", "research-intern" ] } }, - "/smr/research-intern/async-assignments/{assignment_id}/commands": { - "post": { - "deprecated": true, - "operationId": "command_intern_async_assignment", + "/smr/research-intern/async-assignments/{assignment_id}/usage": { + "get": { + "operationId": "get_intern_async_assignment_usage", "parameters": [ { "in": "path", @@ -44578,11 +46169,99 @@ } } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternSessionUsageResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Get Intern Async Assignment Usage", + "tags": [ + "smr", + "research-intern" + ] + } + }, + "/smr/research-intern/async/blockers/{blocker_id}": { + "get": { + "description": "Read one exact blocker and its durable terminal resolution receipt.", + "operationId": "get_intern_async_blocker", + "parameters": [ + { + "in": "path", + "name": "blocker_id", + "required": true, + "schema": { + "title": "Blocker Id", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncBlockerResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Get Intern Async Blocker", + "tags": [ + "smr", + "research-intern" + ] + } + }, + "/smr/research-intern/async/blockers/{blocker_id}/open-sync": { + "post": { + "description": "Open one exact-context Sync handoff without creating an approval.", + "operationId": "open_intern_async_blocker_sync", + "parameters": [ + { + "in": "path", + "name": "blocker_id", + "required": true, + "schema": { + "title": "Blocker Id", + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InternRuntimeCommandRequest" + "$ref": "#/components/schemas/AsyncBlockerOpenSyncRequest" } } }, @@ -44593,7 +46272,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InternRuntimeCommandReceipt" + "$ref": "#/components/schemas/AsyncBlockerOpenSyncResponse" } } }, @@ -44610,7 +46289,61 @@ "description": "Validation Error" } }, - "summary": "Command Intern Async Assignment", + "summary": "Open Intern Async Blocker Sync", + "tags": [ + "smr", + "research-intern" + ] + } + }, + "/smr/research-intern/async/blockers/{blocker_id}/resolve": { + "post": { + "description": "Record explicit Sync disposition and durably continue Async work.", + "operationId": "resolve_intern_async_blocker", + "parameters": [ + { + "in": "path", + "name": "blocker_id", + "required": true, + "schema": { + "title": "Blocker Id", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncBlockerResolveRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncBlockerResolveResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Resolve Intern Async Blocker", "tags": [ "smr", "research-intern" @@ -47737,6 +49470,94 @@ ] } }, + "/smr/research-intern/sync-sessions/{sync_session_id}/resources": { + "get": { + "description": "See notes/specifications/tanha/current/systems/platform/intern_resource_inventory.md.", + "operationId": "get_intern_sync_session_resources", + "parameters": [ + { + "in": "path", + "name": "sync_session_id", + "required": true, + "schema": { + "title": "Sync Session Id", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternResourceInventory" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Get Intern Sync Session Resources", + "tags": [ + "smr", + "research-intern" + ] + } + }, + "/smr/research-intern/sync-sessions/{sync_session_id}/usage": { + "get": { + "description": "Session resource/usage panel projection (receipts only).", + "operationId": "get_intern_sync_session_usage", + "parameters": [ + { + "in": "path", + "name": "sync_session_id", + "required": true, + "schema": { + "title": "Sync Session Id", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternSessionUsageResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Get Intern Sync Session Usage", + "tags": [ + "smr", + "research-intern" + ] + } + }, "/smr/research-intern/tasks/{intern_task_id}": { "patch": { "operationId": "patch_intern_effort_task", @@ -47912,6 +49733,42 @@ "description": "Opaque pagination cursor. Accepted forms: '', '|', or ''.", "title": "Cursor" } + }, + { + "description": "Intern runtime kind that launched the run; requires origin_runtime_id.", + "in": "query", + "name": "origin_runtime_kind", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/OriginRuntimeKind" + }, + { + "type": "null" + } + ], + "description": "Intern runtime kind that launched the run; requires origin_runtime_id.", + "title": "Origin Runtime Kind" + } + }, + { + "description": "Intern Sync session or Async assignment id that launched the run.", + "in": "query", + "name": "origin_runtime_id", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Intern Sync session or Async assignment id that launched the run.", + "title": "Origin Runtime Id" + } } ], "responses": { @@ -48417,6 +50274,48 @@ ] } }, + "/smr/runs/{run_id}/resource-settlement": { + "get": { + "operationId": "get_run_resource_settlement", + "parameters": [ + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "title": "Run Id", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunResourceSettlementResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Get Run Resource Settlement", + "tags": [ + "smr" + ] + } + }, "/smr/runs/{run_id}/resume": { "post": { "operationId": "resume_run", @@ -48459,6 +50358,60 @@ ] } }, + "/smr/runs/{run_id}/rollouts": { + "get": { + "operationId": "list_swarm_rollouts", + "parameters": [ + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "title": "Run Id", + "type": "string" + } + }, + { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 100, + "maximum": 500, + "minimum": 1, + "title": "Limit", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SwarmRolloutPage" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "List the container-pool rollouts a swarm launched", + "tags": [ + "smr" + ] + } + }, "/smr/runs/{run_id}/runtime/stream": { "get": { "operationId": "stream_run_events", @@ -49004,6 +50957,7 @@ "title": "Event Id" }, "kind": { + "description": "Transcript event kind, e.g. message.completed, reasoning.summary, turn.completed, tool.call.completed, or task.completion_claimed (an accepted worker completion claim projected from its durable claim row; payload schema smr.task-completion-claimed.v1 with claim_id, task_key, participant_id, text and summary).", "minLength": 1, "title": "Kind", "type": "string" diff --git a/pyproject.toml b/pyproject.toml index 5096f553..ba2ab7b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,9 @@ synth-ai = "synth_ai.cli:cli" synth-ai-research-mcp = "synth_ai.mcp.research.server:main" [project.optional-dependencies] +pools = [ + "synth-containers>=0.4.3.dev2026091126,<0.5", +] dev = [ "build>=1.2.2.post1", "twine>=4.0.0", diff --git a/schemas/public_models.json b/schemas/public_models.json index 66b7191d..9d5a788e 100644 --- a/schemas/public_models.json +++ b/schemas/public_models.json @@ -1,6 +1,51 @@ { "version": 1, "models": [ + { + "id": "openrouter/openai/gpt-5.6-luna", + "display_name": "GPT-5.6 Luna (OpenRouter)", + "public": true, + "provider": "openrouter", + "provider_model_id": "openai/gpt-5.6-luna", + "compute_pool_kind": "api_proxy_pool", + "compute_pool_id": "openrouter_direct_api", + "route_kind": "direct_api", + "provider_domicile": "us", + "route_regions": [ + "us" + ], + "zdr_supported": true, + "harnesses": [ + "codex" + ], + "aliases": [ + "openrouter-gpt-5.6-luna", + "gpt-5.6-luna-openrouter" + ], + "supported_reasoning_efforts": [ + "low", + "medium", + "high", + "xhigh" + ], + "default_reasoning_effort": "high", + "supported_service_tiers": [], + "context_window_tokens": 1050000, + "max_output_tokens": 128000, + "usage_required": false, + "pricing_present": true, + "pricing": { + "currency": "USD", + "unit": "token", + "input_usd": "2E-7", + "cached_input_usd": "2E-8", + "output_usd": "0.0000012", + "reasoning_output_usd": "0.0000012", + "source": "openai_api_pricing_via_openrouter", + "observed_at": "2026-07-30" + }, + "pricing_by_service_tier": {} + }, { "id": "gpt-5.6-luna", "display_name": "GPT-5.6 Luna", @@ -174,46 +219,6 @@ }, "pricing_by_service_tier": {} }, - { - "id": "cursor/grok-4.5", - "display_name": "Cursor Grok 4.5", - "public": true, - "provider": "cursor", - "provider_model_id": "grok-4.5", - "compute_pool_kind": "api_proxy_pool", - "compute_pool_id": "cursor_pool", - "route_kind": "api_proxy_pool", - "provider_domicile": "us", - "route_regions": [ - "us" - ], - "zdr_supported": true, - "harnesses": [ - "cursor" - ], - "aliases": [ - "cursor-grok-4.5", - "grok-4.5" - ], - "supported_reasoning_efforts": [], - "default_reasoning_effort": null, - "supported_service_tiers": [], - "context_window_tokens": null, - "max_output_tokens": null, - "usage_required": true, - "pricing_present": true, - "pricing": { - "currency": "USD", - "unit": "token", - "input_usd": "0.000002", - "cached_input_usd": "2E-7", - "output_usd": "0.000006", - "reasoning_output_usd": "0.000006", - "source": "cursor_grok_4_5_launch", - "observed_at": "2026-07-09" - }, - "pricing_by_service_tier": {} - }, { "id": "gpt-5.4-mini", "display_name": "GPT-5.4 Mini", @@ -317,8 +322,7 @@ ], "aliases": [ "kimi-k3", - "modal-kimi-k3", - "moonshotai/Kimi-K3" + "modal-kimi-k3" ], "supported_reasoning_efforts": [ "low" @@ -346,6 +350,50 @@ "provider_catalog_entry_sha256": "a22f5b6d1a743123c2f49191dc9517abe2e265c60f4ae1b619b1a320e48595b0", "provider_revision_attested": false }, + { + "id": "meta/muse-spark-1.2", + "display_name": "Meta Muse Spark 1.2", + "public": true, + "provider": "openrouter", + "provider_model_id": "meta/muse-spark-1.2", + "compute_pool_kind": "api_proxy_pool", + "compute_pool_id": "meta_direct_api", + "route_kind": "direct_api", + "provider_domicile": "us", + "route_regions": [ + "us" + ], + "zdr_supported": true, + "harnesses": [ + "codex" + ], + "aliases": [ + "muse-spark-1.2", + "meta-muse-spark-1.2" + ], + "supported_reasoning_efforts": [ + "low", + "medium", + "high" + ], + "default_reasoning_effort": "high", + "supported_service_tiers": [], + "context_window_tokens": 1048576, + "max_output_tokens": 131072, + "usage_required": false, + "pricing_present": true, + "pricing": { + "currency": "USD", + "unit": "token", + "input_usd": "0.00000125", + "cached_input_usd": "1.5E-7", + "output_usd": "0.00000425", + "reasoning_output_usd": "0.00000425", + "source": "https://developer.meta.com/ai/resources/blog/build-with-muse-code/", + "observed_at": "2026-08-11" + }, + "pricing_by_service_tier": {} + }, { "id": "synth_internal/laguna-s-2.1-nvfp4", "display_name": "Laguna S 2.1 NVFP4 (Synth Internal \u00b7 Modal)", diff --git a/schemas/smr_openapi.yaml b/schemas/smr_openapi.yaml index 776eda96..97e2291b 100644 --- a/schemas/smr_openapi.yaml +++ b/schemas/smr_openapi.yaml @@ -672,6 +672,32 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/billing/credits-unknown: + post: + tags: + - billing + summary: Report Credit Unknown + description: Record that a client refused to invent a credit amount. + operationId: report_credit_unknown_api_v1_billing_credits_unknown_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreditUnknownEvent' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/CreditUnknownAccepted' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/billing/entitlements: get: tags: @@ -685,6 +711,49 @@ paths: application/json: schema: $ref: '#/components/schemas/BillingEntitlementSnapshot' + /api/v1/billing/checkout-session: + post: + tags: + - billing + summary: Create Billing Checkout Session + description: 'Hosted checkout URL for a Desktop tier. Desktop opens it and never + sees + + card details.' + operationId: create_billing_checkout_session_api_v1_billing_checkout_session_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CheckoutSessionBody' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HostedBillingSession' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/billing/portal-session: + post: + tags: + - billing + summary: Create Billing Portal Session + description: Hosted billing-management URL for the current org. + operationId: create_billing_portal_session_api_v1_billing_portal_session_post + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HostedBillingSession' /api/v1/balance/current: get: tags: @@ -930,177 +999,179 @@ paths: application/json: schema: $ref: '#/components/schemas/MeResponse' - /api/v1/free-tier/usage: + /api/v1/desktop/account-snapshot: get: tags: - - free-tier - summary: Get Free Tier Usage - description: 'Get free tier usage and limits for the authenticated organization. - - - Returns current usage against daily and monthly limits, plus timestamps - - for when limits reset.' - operationId: get_free_tier_usage_api_v1_free_tier_usage_get + - desktop + summary: Get Desktop Account Snapshot + description: "One document for the Desktop account menu, usage sheet, and first\ + \ run.\n\nAn unauthenticated caller never reaches here \u2014 the key dependency\ + \ answers\n401 and Desktop renders its signed-out state." + operationId: get_desktop_account_snapshot_api_v1_desktop_account_snapshot_get responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/FreeTierUsageResponse' - /api/v1/synth/models: + $ref: '#/components/schemas/DesktopAccountSnapshot' + /api/v1/desktop/plan-catalog: get: tags: - - synth - summary: List Synth Models - operationId: list_synth_models_api_v1_synth_models_get + - desktop + summary: Get Desktop Plan Catalog + description: 'Tier dollars, so the shell can render an upgrade sheet before + a snapshot + + is available.' + operationId: get_desktop_plan_catalog_api_v1_desktop_plan_catalog_get responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: true - type: object - title: Response List Synth Models Api V1 Synth Models Get - /api/v1/stack-aux/usage: - get: - tags: - - stack-aux - summary: Get Stack Aux Usage - operationId: get_stack_aux_usage_api_v1_stack_aux_usage_get - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - /api/v1/stack-aux/openai/v1/responses: - post: - tags: - - stack-aux - summary: Stack Aux Create Response - operationId: stack_aux_create_response_api_v1_stack_aux_openai_v1_responses_post - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - /api/v1/stack-inference/usage: + items: + $ref: '#/components/schemas/DesktopPlanOption' + type: array + title: Response Get Desktop Plan Catalog Api V1 Desktop Plan Catalog + Get + /api/v1/desktop/cloud-identity: get: tags: - - stack-inference - summary: Get Stack Inference Usage - operationId: get_stack_inference_usage_api_v1_stack_inference_usage_get - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - /api/v1/stack-inference/openai/v1/responses: - post: - tags: - - stack-inference - summary: Stack Inference Create Response - operationId: stack_inference_create_response_api_v1_stack_inference_openai_v1_responses_post + - desktop + summary: Get Desktop Cloud Identity + operationId: get_desktop_cloud_identity_api_v1_desktop_cloud_identity_get responses: '200': description: Successful Response content: application/json: - schema: {} - /api/v1/growth/cta-events: + schema: + $ref: '#/components/schemas/DesktopCloudIdentity' + /api/v1/mq/threads: post: tags: - - growth - summary: Record Growth Cta Event - operationId: record_growth_cta_event_api_v1_growth_cta_events_post + - mq-grants + summary: Create Thread + operationId: messaging.threads.create requestBody: content: application/json: schema: - $ref: '#/components/schemas/GrowthCtaEventRequest' + $ref: '#/components/schemas/CreateThreadRequest' required: true responses: - '201': + '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/GrowthCtaEventResponse' + additionalProperties: true + type: object + title: Response Messaging.Threads.Create '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/growth/funnel-events/ready: + /api/v1/mq/threads/{thread_id}: get: tags: - - growth - summary: Growth Funnel Events Ready - operationId: growth_funnel_events_ready_api_v1_growth_funnel_events_ready_get + - mq-grants + summary: Get Thread + operationId: messaging.threads.get + parameters: + - name: thread_id + in: path + required: true + schema: + type: string + format: uuid + title: Thread Id responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: - anyOf: - - type: string - - type: boolean type: object - title: Response Growth Funnel Events Ready Api V1 Growth Funnel Events - Ready Get - /api/v1/growth/funnel-events: + additionalProperties: true + title: Response Messaging.Threads.Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/mq/threads/{thread_id}/messages: post: tags: - - growth - summary: Record Growth Funnel Event - operationId: record_growth_funnel_event_api_v1_growth_funnel_events_post + - mq-grants + summary: Publish Thread Message + operationId: messaging.threads.publish + parameters: + - name: thread_id + in: path + required: true + schema: + type: string + format: uuid + title: Thread Id requestBody: + required: true content: application/json: schema: - $ref: '#/components/schemas/GrowthFunnelEventRequest' - required: true + $ref: '#/components/schemas/PublishMessageRequest' responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/GrowthFunnelEventResponse' + type: object + additionalProperties: true + title: Response Messaging.Threads.Publish '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/growth/funnel/{product}: + /api/v1/mq/threads/{thread_id}/history: get: tags: - - growth - summary: Growth Funnel Product Rollup - operationId: growth_funnel_product_rollup_api_v1_growth_funnel__product__get + - mq-grants + summary: Thread History + operationId: messaging.threads.history parameters: - - name: product + - name: thread_id in: path required: true schema: type: string - title: Product - - name: window_days + format: uuid + title: Thread Id + - name: after_seq in: query required: false schema: type: integer - default: 30 - title: Window Days + maximum: 18446744073709551615 + minimum: 0 + default: 0 + title: After Seq + - name: limit + in: query + required: false + schema: + type: integer + maximum: 200 + minimum: 1 + default: 50 + title: Limit responses: '200': description: Successful Response @@ -1109,402 +1180,1074 @@ paths: schema: type: object additionalProperties: true - title: Response Growth Funnel Product Rollup Api V1 Growth Funnel Product Get + title: Response Messaging.Threads.History '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/product/stack-crashes/ready: + /api/v1/mq/jwks.json: get: tags: - - product - summary: Stack Crash Reports Ready - operationId: stack_crash_reports_ready_api_v1_product_stack_crashes_ready_get + - mq-grants + summary: Get Mq Jwks + description: Public verification keys for MQ. Contains no private material. + operationId: get_mq_jwks_api_v1_mq_jwks_json_get responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: - anyOf: - - type: string - - type: boolean + additionalProperties: true type: object - title: Response Stack Crash Reports Ready Api V1 Product Stack Crashes - Ready Get - /api/v1/product/stack-usage-events/ready: + title: Response Get Mq Jwks Api V1 Mq Jwks Json Get + /api/v1/mq/enrollments: get: tags: - - product - summary: Stack Usage Events Ready - operationId: stack_usage_events_ready_api_v1_product_stack_usage_events_ready_get + - mq-grants + summary: List Enrollments + operationId: list_enrollments_api_v1_mq_enrollments_get responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: - anyOf: - - type: string - - type: boolean + additionalProperties: true type: object - title: Response Stack Usage Events Ready Api V1 Product Stack Usage - Events Ready Get - /api/v1/product/stack-usage-events: + title: Response List Enrollments Api V1 Mq Enrollments Get post: tags: - - product - summary: Record Stack Usage Events - operationId: record_stack_usage_events_api_v1_product_stack_usage_events_post + - mq-grants + summary: Enroll Device + operationId: enroll_device_api_v1_mq_enrollments_post requestBody: content: application/json: schema: - $ref: '#/components/schemas/StackUsageEventsRequest' + $ref: '#/components/schemas/EnrollRequest' required: true responses: - '200': + '201': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/StackUsageEventsResponse' + additionalProperties: true + type: object + title: Response Enroll Device Api V1 Mq Enrollments Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/product/stack-crashes: - post: + /api/v1/mq/enrollments/{enrollment_id}: + get: tags: - - product - summary: Record Stack Client Crash - operationId: record_stack_client_crash_api_v1_product_stack_crashes_post - requestBody: + - mq-grants + summary: Get Enrollment + operationId: get_enrollment_api_v1_mq_enrollments__enrollment_id__get + parameters: + - name: enrollment_id + in: path required: true - content: - application/json: - schema: - $ref: '#/components/schemas/StackCrashReportRequest' + schema: + type: string + format: uuid + title: Enrollment Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/StackCrashReportResponse' + type: object + additionalProperties: true + title: Response Get Enrollment Api V1 Mq Enrollments Enrollment Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - get: + /api/v1/mq/enrollments/{enrollment_id}/revoke: + post: tags: - - product - summary: List Stack Crash Reports - operationId: list_stack_crash_reports_api_v1_product_stack_crashes_get + - mq-grants + summary: Revoke Enrollment + description: Device sign-out. Idempotent; the enrollment cannot be re-enrolled. + operationId: revoke_enrollment_api_v1_mq_enrollments__enrollment_id__revoke_post parameters: - - name: crash_class - in: query - required: false - schema: - anyOf: - - type: string - maxLength: 80 - - type: 'null' - title: Crash Class - - name: surface - in: query - required: false - schema: - anyOf: - - type: string - maxLength: 80 - - type: 'null' - title: Surface - - name: version - in: query - required: false - schema: - anyOf: - - type: string - maxLength: 80 - - type: 'null' - title: Version - - name: window_days - in: query - required: false - schema: - type: integer - maximum: 365 - minimum: 1 - default: 30 - title: Window Days - - name: limit - in: query - required: false - schema: - type: integer - maximum: 200 - minimum: 1 - default: 50 - title: Limit - - name: offset - in: query - required: false + - name: enrollment_id + in: path + required: true schema: - type: integer - minimum: 0 - default: 0 - title: Offset + type: string + format: uuid + title: Enrollment Id + requestBody: + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/EmptyRequest' + - type: 'null' + title: Body responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/StackCrashReportListResponse' + type: object + additionalProperties: true + title: Response Revoke Enrollment Api V1 Mq Enrollments Enrollment + Id Revoke Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/product/stack-crashes/summary: - get: + /api/v1/mq/grants: + post: tags: - - product - summary: Stack Crash Reports Summary - operationId: stack_crash_reports_summary_api_v1_product_stack_crashes_summary_get - parameters: - - name: window_days - in: query - required: false - schema: - type: integer - maximum: 365 - minimum: 1 - default: 7 - title: Window Days - - name: recent_limit - in: query - required: false - schema: - type: integer - maximum: 50 - minimum: 1 - default: 10 - title: Recent Limit + - mq-grants + summary: Create Grant + operationId: create_grant_api_v1_mq_grants_post + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateGrantRequest' responses: - '200': + '201': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/StackCrashReportSummaryResponse' + type: object + additionalProperties: true + title: Response Create Grant Api V1 Mq Grants Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/usage: - get: - tags: - - usage - summary: Get Usage - description: 'Get usage and limits for the authenticated organization. - - - Returns current usage against daily and monthly limits for all API surfaces, - - organized by API type (inference, verifiers, rl, sft, research). - - - Response includes: - - - org_id: Organization identifier - - - tier: Current tier (free, free_beta, standard, standard_beta, max, max_beta) - - - period: Start dates for daily and monthly periods - - - apis: Usage metrics per API surface - - - totals: Aggregate usage (total spend)' - operationId: get_usage_api_v1_usage_get - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/UsageResponse' - /api/v1/usage/user-limits: get: tags: - - usage - summary: Get User Limits - description: 'Get effective limits for the authenticated user. - - - Combines org rate limits (tier/overrides) with access-tier permissions - - (scopes, allowed models, allowed workflows).' - operationId: get_user_limits_api_v1_usage_user_limits_get + - mq-grants + summary: List Grants + operationId: list_grants_api_v1_mq_grants_get + parameters: + - name: enrollment_id + in: query + required: false + schema: + anyOf: + - type: string + format: uuid + - type: 'null' + title: Enrollment Id + - name: thread_id + in: query + required: false + schema: + anyOf: + - type: string + format: uuid + - type: 'null' + title: Thread Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/UserLimitsResponse' - /api/v1/usage/tiers: - get: - tags: - - usage - summary: Get Tier Limits - description: 'Get default rate limits for all tiers. - - - Returns the default limits for each tier - - (free, free_beta, standard, standard_beta, max, max_beta). - - These are the base limits before any per-org overrides are applied. - - - Use this endpoint to display pricing/limits information in your application.' - operationId: get_tier_limits_api_v1_usage_tiers_get - responses: - '200': - description: Successful Response + type: object + additionalProperties: true + title: Response List Grants Api V1 Mq Grants Get + '422': + description: Validation Error content: application/json: schema: - items: - $ref: '#/components/schemas/TierLimitsResponse' - type: array - title: Response Get Tier Limits Api V1 Usage Tiers Get - /api/v1/usage/tiers/{tier}: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/mq/grants/{grant_id}: get: tags: - - usage - summary: Get Tier Limit - description: "Get default rate limits for a specific tier.\n\nArgs:\n tier:\ - \ Tier name (free, free_beta, standard, standard_beta, max, max_beta)\n\n\ - Returns default limits for the specified tier." - operationId: get_tier_limit_api_v1_usage_tiers__tier__get + - mq-grants + summary: Get Grant + operationId: get_grant_api_v1_mq_grants__grant_id__get parameters: - - name: tier + - name: grant_id in: path required: true schema: type: string - title: Tier + format: uuid + title: Grant Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/TierLimitsResponse' + type: object + additionalProperties: true + title: Response Get Grant Api V1 Mq Grants Grant Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/usage/record-items: + /api/v1/mq/grants/{grant_id}/revoke: post: tags: - - usage - summary: Record Usage Items - operationId: record_usage_items_api_v1_usage_record_items_post + - mq-grants + summary: Revoke Grant + operationId: revoke_grant_api_v1_mq_grants__grant_id__revoke_post + parameters: + - name: grant_id + in: path + required: true + schema: + type: string + format: uuid + title: Grant Id requestBody: content: application/json: schema: - $ref: '#/components/schemas/UsageRecordItemsIn' - required: true + anyOf: + - $ref: '#/components/schemas/EmptyRequest' + - type: 'null' + title: Body responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/UsageRecordItemsOut' + type: object + additionalProperties: true + title: Response Revoke Grant Api V1 Mq Grants Grant Id Revoke Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/usage/record: + /api/v1/mq/grants/{grant_id}/restore: post: tags: - - usage - summary: Record Usage - operationId: record_usage_api_v1_usage_record_post + - mq-grants + summary: Restore Grant + operationId: restore_grant_api_v1_mq_grants__grant_id__restore_post + parameters: + - name: grant_id + in: path + required: true + schema: + type: string + format: uuid + title: Grant Id requestBody: content: application/json: schema: - $ref: '#/components/schemas/UsageIn' - required: true + anyOf: + - $ref: '#/components/schemas/EmptyRequest' + - type: 'null' + title: Body responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: true type: object - title: Response Record Usage Api V1 Usage Record Post + additionalProperties: true + title: Response Restore Grant Api V1 Mq Grants Grant Id Restore + Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/usage/preflight: + /api/v1/mq/grants/{grant_id}/renew: post: tags: - - usage - summary: Preflight Usage - operationId: preflight_usage_api_v1_usage_preflight_post + - mq-grants + summary: Renew Grant + operationId: renew_grant_api_v1_mq_grants__grant_id__renew_post + parameters: + - name: grant_id + in: path + required: true + schema: + type: string + format: uuid + title: Grant Id requestBody: + required: true content: application/json: schema: - $ref: '#/components/schemas/PreflightIn' - required: true + $ref: '#/components/schemas/RenewRequest' responses: '200': description: Successful Response content: application/json: schema: - additionalProperties: true type: object - title: Response Preflight Usage Api V1 Usage Preflight Post + additionalProperties: true + title: Response Renew Grant Api V1 Mq Grants Grant Id Renew Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /api/v1/internal/usage/record: + /api/v1/mq/grants/{grant_id}/credential: post: tags: - - usage - summary: Record Internal Usage + - mq-grants + summary: Issue Grant Credential + description: Short-lived (60-300 s) grant credential plus the verified MQ endpoint. + operationId: issue_grant_credential_api_v1_mq_grants__grant_id__credential_post + parameters: + - name: grant_id + in: path + required: true + schema: + type: string + format: uuid + title: Grant Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Issue Grant Credential Api V1 Mq Grants Grant Id Credential + Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/free-tier/usage: + get: + tags: + - free-tier + summary: Get Free Tier Usage + description: 'Get free tier usage and limits for the authenticated organization. + + + Returns current usage against daily and monthly limits, plus timestamps + + for when limits reset.' + operationId: get_free_tier_usage_api_v1_free_tier_usage_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/FreeTierUsageResponse' + /api/v1/models/adapters: + get: + tags: + - adapters + summary: List Adapters + operationId: list_adapters_api_v1_models_adapters_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response List Adapters Api V1 Models Adapters Get + /api/v1/models/adapters/{digest}/manifest: + get: + tags: + - adapters + summary: Adapter Manifest + operationId: adapter_manifest_api_v1_models_adapters__digest__manifest_get + parameters: + - name: digest + in: path + required: true + schema: + type: string + title: Digest + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Adapter Manifest Api V1 Models Adapters Digest Manifest + Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/models/adapters/{digest}/files/{name}: + get: + tags: + - adapters + summary: Adapter File + operationId: adapter_file_api_v1_models_adapters__digest__files__name__get + parameters: + - name: digest + in: path + required: true + schema: + type: string + title: Digest + - name: name + in: path + required: true + schema: + type: string + title: Name + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/synth/models: + get: + tags: + - synth + summary: List Synth Models + operationId: list_synth_models_api_v1_synth_models_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response List Synth Models Api V1 Synth Models Get + /api/v1/stack-aux/usage: + get: + tags: + - stack-aux + summary: Get Stack Aux Usage + operationId: get_stack_aux_usage_api_v1_stack_aux_usage_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/stack-aux/openai/v1/responses: + post: + tags: + - stack-aux + summary: Stack Aux Create Response + operationId: stack_aux_create_response_api_v1_stack_aux_openai_v1_responses_post + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/stack-inference/usage: + get: + tags: + - stack-inference + summary: Get Stack Inference Usage + operationId: get_stack_inference_usage_api_v1_stack_inference_usage_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/stack-inference/openai/v1/responses: + post: + tags: + - stack-inference + summary: Stack Inference Create Response + operationId: stack_inference_create_response_api_v1_stack_inference_openai_v1_responses_post + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + /api/v1/growth/cta-events: + post: + tags: + - growth + summary: Record Growth Cta Event + operationId: record_growth_cta_event_api_v1_growth_cta_events_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GrowthCtaEventRequest' + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GrowthCtaEventResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/growth/funnel-events/ready: + get: + tags: + - growth + summary: Growth Funnel Events Ready + operationId: growth_funnel_events_ready_api_v1_growth_funnel_events_ready_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + anyOf: + - type: string + - type: boolean + type: object + title: Response Growth Funnel Events Ready Api V1 Growth Funnel Events + Ready Get + /api/v1/growth/funnel-events: + post: + tags: + - growth + summary: Record Growth Funnel Event + operationId: record_growth_funnel_event_api_v1_growth_funnel_events_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GrowthFunnelEventRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GrowthFunnelEventResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/growth/funnel/{product}: + get: + tags: + - growth + summary: Growth Funnel Product Rollup + operationId: growth_funnel_product_rollup_api_v1_growth_funnel__product__get + parameters: + - name: product + in: path + required: true + schema: + type: string + title: Product + - name: window_days + in: query + required: false + schema: + type: integer + default: 30 + title: Window Days + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Growth Funnel Product Rollup Api V1 Growth Funnel Product Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/product/stack-crashes/ready: + get: + tags: + - product + summary: Stack Crash Reports Ready + operationId: stack_crash_reports_ready_api_v1_product_stack_crashes_ready_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + anyOf: + - type: string + - type: boolean + type: object + title: Response Stack Crash Reports Ready Api V1 Product Stack Crashes + Ready Get + /api/v1/product/stack-usage-events/ready: + get: + tags: + - product + summary: Stack Usage Events Ready + operationId: stack_usage_events_ready_api_v1_product_stack_usage_events_ready_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + anyOf: + - type: string + - type: boolean + type: object + title: Response Stack Usage Events Ready Api V1 Product Stack Usage + Events Ready Get + /api/v1/product/stack-usage-events: + post: + tags: + - product + summary: Record Stack Usage Events + operationId: record_stack_usage_events_api_v1_product_stack_usage_events_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StackUsageEventsRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/StackUsageEventsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/product/usage-events/ready: + get: + tags: + - product + summary: Product Usage Events Ready + operationId: product_usage_events_ready_api_v1_product_usage_events_ready_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: + anyOf: + - type: string + - type: boolean + type: object + title: Response Product Usage Events Ready Api V1 Product Usage Events + Ready Get + /api/v1/product/usage-events: + post: + tags: + - product + summary: Record Product Usage Events + operationId: record_product_usage_events_api_v1_product_usage_events_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ProductUsageEventsRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/StackUsageEventsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/product/stack-crashes: + post: + tags: + - product + summary: Record Stack Client Crash + operationId: record_stack_client_crash_api_v1_product_stack_crashes_post + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/StackCrashReportRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/StackCrashReportResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + get: + tags: + - product + summary: List Stack Crash Reports + operationId: list_stack_crash_reports_api_v1_product_stack_crashes_get + parameters: + - name: crash_class + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 80 + - type: 'null' + title: Crash Class + - name: surface + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 80 + - type: 'null' + title: Surface + - name: version + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 80 + - type: 'null' + title: Version + - name: window_days + in: query + required: false + schema: + type: integer + maximum: 365 + minimum: 1 + default: 30 + title: Window Days + - name: limit + in: query + required: false + schema: + type: integer + maximum: 200 + minimum: 1 + default: 50 + title: Limit + - name: offset + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: Offset + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/StackCrashReportListResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/product/stack-crashes/summary: + get: + tags: + - product + summary: Stack Crash Reports Summary + operationId: stack_crash_reports_summary_api_v1_product_stack_crashes_summary_get + parameters: + - name: window_days + in: query + required: false + schema: + type: integer + maximum: 365 + minimum: 1 + default: 7 + title: Window Days + - name: recent_limit + in: query + required: false + schema: + type: integer + maximum: 50 + minimum: 1 + default: 10 + title: Recent Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/StackCrashReportSummaryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/usage: + get: + tags: + - usage + summary: Get Usage + description: 'Get usage and limits for the authenticated organization. + + + Returns current usage against daily and monthly limits for all API surfaces, + + organized by API type (inference, verifiers, rl, sft, research). + + + Response includes: + + - org_id: Organization identifier + + - tier: Current tier (free, free_beta, standard, standard_beta, max, max_beta) + + - period: Start dates for daily and monthly periods + + - apis: Usage metrics per API surface + + - totals: Aggregate usage (total spend)' + operationId: get_usage_api_v1_usage_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UsageResponse' + /api/v1/usage/user-limits: + get: + tags: + - usage + summary: Get User Limits + description: 'Get effective limits for the authenticated user. + + + Combines org rate limits (tier/overrides) with access-tier permissions + + (scopes, allowed models, allowed workflows).' + operationId: get_user_limits_api_v1_usage_user_limits_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UserLimitsResponse' + /api/v1/usage/tiers: + get: + tags: + - usage + summary: Get Tier Limits + description: 'Get default rate limits for all tiers. + + + Returns the default limits for each tier + + (free, free_beta, standard, standard_beta, max, max_beta). + + These are the base limits before any per-org overrides are applied. + + + Use this endpoint to display pricing/limits information in your application.' + operationId: get_tier_limits_api_v1_usage_tiers_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/TierLimitsResponse' + type: array + title: Response Get Tier Limits Api V1 Usage Tiers Get + /api/v1/usage/tiers/{tier}: + get: + tags: + - usage + summary: Get Tier Limit + description: "Get default rate limits for a specific tier.\n\nArgs:\n tier:\ + \ Tier name (free, free_beta, standard, standard_beta, max, max_beta)\n\n\ + Returns default limits for the specified tier." + operationId: get_tier_limit_api_v1_usage_tiers__tier__get + parameters: + - name: tier + in: path + required: true + schema: + type: string + title: Tier + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TierLimitsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/usage/record-items: + post: + tags: + - usage + summary: Record Usage Items + operationId: record_usage_items_api_v1_usage_record_items_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UsageRecordItemsIn' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UsageRecordItemsOut' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/usage/record: + post: + tags: + - usage + summary: Record Usage + operationId: record_usage_api_v1_usage_record_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UsageIn' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response Record Usage Api V1 Usage Record Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/usage/preflight: + post: + tags: + - usage + summary: Preflight Usage + operationId: preflight_usage_api_v1_usage_preflight_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreflightIn' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response Preflight Usage Api V1 Usage Preflight Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/internal/usage/record: + post: + tags: + - usage + summary: Record Internal Usage operationId: record_internal_usage_api_v1_internal_usage_record_post requestBody: content: @@ -1785,6 +2528,75 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/workshop/v1/usage/records: + post: + tags: + - workshop + - usage + summary: Ingest Workshop Usage + operationId: ingest_workshop_usage_api_v1_workshop_v1_usage_records_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UsageRecordIn' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UsageRecordOut' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/workshop/v1/usage/aggregate: + get: + tags: + - workshop + - usage + summary: Get Workshop Usage Aggregate + operationId: get_workshop_usage_aggregate_api_v1_workshop_v1_usage_aggregate_get + parameters: + - name: start + in: query + required: true + schema: + type: string + format: date-time + title: Start + - name: end + in: query + required: true + schema: + type: string + format: date-time + title: End + - name: workspaceId + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Workspaceid + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/UsageAggregate' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/vllm/loras/upload-url: post: tags: @@ -2647,6 +3459,97 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/capabilities/training: + get: + tags: + - optimizers + summary: Get Training Provider Capabilities + description: Spend-free Tinker capability discovery through Optimizers Beta. + operationId: get_training_provider_capabilities_api_v1_optimizers_capabilities_training_get + parameters: + - name: model + in: query + required: true + schema: + type: string + minLength: 1 + maxLength: 240 + title: Model + - name: algorithm + in: query + required: true + schema: + enum: + - sft + - cispo + type: string + title: Algorithm + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/models/training: + get: + tags: + - optimizers + summary: Get Hosted Training Model Catalog + description: List policy-supported models; live capability preflight still gates + launch. + operationId: get_hosted_training_model_catalog_api_v1_optimizers_models_training_get + parameters: + - name: algorithm + in: query + required: false + schema: + anyOf: + - enum: + - sft + - cispo + type: string + - type: 'null' + title: Algorithm + - name: status + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 40 + - type: 'null' + title: Status + - name: q + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Q + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Hosted Training Model Catalog Api V1 Optimizers + Models Training Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/optimizers/usage/registrations: post: tags: @@ -2867,6 +3770,137 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/internal/runs/{run_id}/saved-checkpoints/uploads: + post: + tags: + - optimizers + summary: Create Internal Saved Lora Upload + description: Issue the object-scoped upload defined by ``hosted_training.md``. + operationId: create_internal_saved_lora_upload_api_v1_optimizers_internal_runs__run_id__saved_checkpoints_uploads_post + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: authorization + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Authorization + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OptimizerInternalSavedLoraUploadRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Create Internal Saved Lora Upload Api V1 Optimizers + Internal Runs Run Id Saved Checkpoints Uploads Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/internal/runs/{run_id}/saved-checkpoints/complete: + post: + tags: + - optimizers + summary: Complete Internal Saved Lora Upload + operationId: complete_internal_saved_lora_upload_api_v1_optimizers_internal_runs__run_id__saved_checkpoints_complete_post + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: authorization + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Authorization + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OptimizerInternalSavedLoraCompleteRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Complete Internal Saved Lora Upload Api V1 Optimizers + Internal Runs Run Id Saved Checkpoints Complete Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/internal/runs/{run_id}/training-admission: + post: + tags: + - optimizers + summary: Admit Internal Optimizer Training Operation + description: Backend-owned hard-cap gate called before every paid RL operation. + operationId: admit_internal_optimizer_training_operation_api_v1_optimizers_internal_runs__run_id__training_admission_post + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: authorization + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Authorization + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OptimizerInternalTrainingAdmissionRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Admit Internal Optimizer Training Operation Api V1 + Optimizers Internal Runs Run Id Training Admission Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/optimizers/internal/runs/{run_id}/events: post: tags: @@ -3174,6 +4208,15 @@ paths: - optimizers summary: Submit Optimizer Run operationId: submit_optimizer_run_api_v1_optimizers_runs_post + parameters: + - name: X-Synth-Training-Validation-Grant + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Synth-Training-Validation-Grant requestBody: required: true content: @@ -3207,7 +4250,8 @@ paths: - gepa - go-ex - mapo - - online-reflexion + - sft + - cispo type: string - type: 'null' title: Algorithm @@ -3259,6 +4303,45 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/resume: + post: + tags: + - optimizers + summary: Resume Optimizer Training Run + operationId: resume_optimizer_training_run_api_v1_optimizers_runs__run_id__resume_post + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: X-Synth-Training-Validation-Grant + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: X-Synth-Training-Validation-Grant + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OptimizerTrainingResumeRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/optimizers/runs/{run_id}: get: tags: @@ -3284,6 +4367,108 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/saved-checkpoints: + get: + tags: + - optimizers + summary: Get Optimizer Run Saved Checkpoints + description: Return visible saved models linked to one optimizer run. + operationId: get_optimizer_run_saved_checkpoints_api_v1_optimizers_runs__run_id__saved_checkpoints_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: checkpoint_kind + in: query + required: false + schema: + anyOf: + - enum: + - inference + - training + type: string + - type: 'null' + title: Checkpoint Kind + - name: status + in: query + required: false + schema: + anyOf: + - enum: + - uploading + - ready + - failed + - archived + type: string + - type: 'null' + default: ready + title: Status + - name: limit + in: query + required: false + schema: + type: integer + maximum: 100 + minimum: 1 + default: 100 + title: Limit + - name: offset + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: Offset + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Optimizer Run Saved Checkpoints Api V1 Optimizers + Runs Run Id Saved Checkpoints Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/outputs: + get: + tags: + - optimizers + summary: Get Optimizer Run Outputs + description: List automatically persisted result, artifacts, and model checkpoints. + operationId: get_optimizer_run_outputs_api_v1_optimizers_runs__run_id__outputs_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Optimizer Run Outputs Api V1 Optimizers Runs Run + Id Outputs Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/optimizers/runs/{run_id}/events: get: tags: @@ -3333,6 +4518,166 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/training-events: + get: + tags: + - optimizers + summary: Get Optimizer Training Events + operationId: get_optimizer_training_events_api_v1_optimizers_runs__run_id__training_events_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: after_sequence + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: limit + in: query + required: false + schema: + type: integer + maximum: 5000 + minimum: 1 + default: 500 + title: Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/training-attempts: + get: + tags: + - optimizers + summary: Get Optimizer Training Attempts + operationId: get_optimizer_training_attempts_api_v1_optimizers_runs__run_id__training_attempts_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Optimizer Training Attempts Api V1 Optimizers + Runs Run Id Training Attempts Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/training-receipt: + get: + tags: + - optimizers + summary: Get Optimizer Training Receipt + operationId: get_optimizer_training_receipt_api_v1_optimizers_runs__run_id__training_receipt_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: attempt_id + in: query + required: false + schema: + anyOf: + - type: string + minLength: 1 + maxLength: 240 + - type: 'null' + title: Attempt Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Optimizer Training Receipt Api V1 Optimizers Runs Run + Id Training Receipt Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/runs/{run_id}/training-events/stream: + get: + tags: + - optimizers + summary: Stream Optimizer Training Events + operationId: stream_optimizer_training_events_api_v1_optimizers_runs__run_id__training_events_stream_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: after_sequence + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: limit + in: query + required: false + schema: + type: integer + maximum: 5000 + minimum: 1 + default: 500 + title: Limit + - name: Last-Event-ID + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Last-Event-Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/optimizers/runs/{run_id}/state: get: tags: @@ -3720,6 +5065,410 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/uploads: + post: + tags: + - optimizer-checkpoints + summary: Create Saved Lora Upload + operationId: create_saved_lora_upload_api_v1_optimizers_checkpoints_uploads_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SavedLoraUploadRequest' + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + additionalProperties: true + type: object + title: Response Create Saved Lora Upload Api V1 Optimizers Checkpoints + Uploads Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/{checkpoint_id}/complete: + post: + tags: + - optimizer-checkpoints + summary: Complete Saved Lora Upload + operationId: complete_saved_lora_upload_api_v1_optimizers_checkpoints__checkpoint_id__complete_post + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SavedLoraCompleteRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Complete Saved Lora Upload Api V1 Optimizers Checkpoints Checkpoint + Id Complete Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints: + get: + tags: + - optimizer-checkpoints + summary: Search Saved Loras + operationId: search_saved_loras_api_v1_optimizers_checkpoints_get + parameters: + - name: q + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 300 + - type: 'null' + title: Q + - name: scope + in: query + required: false + schema: + enum: + - all + - mine + - org + type: string + default: all + title: Scope + - name: provider + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Provider + - name: checkpoint_kind + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Checkpoint Kind + - name: base_model + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 300 + - type: 'null' + title: Base Model + - name: run_id + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Run Id + - name: attempt_id + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Attempt Id + - name: source_checkpoint_id + in: query + required: false + schema: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Source Checkpoint Id + - name: optimizer_algorithm + in: query + required: false + schema: + anyOf: + - enum: + - sft + - cispo + type: string + - type: 'null' + title: Optimizer Algorithm + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + default: ready + title: Status + - name: tags + in: query + required: false + schema: + anyOf: + - type: array + items: + type: string + - type: 'null' + title: Tags + - name: limit + in: query + required: false + schema: + type: integer + maximum: 100 + minimum: 1 + default: 50 + title: Limit + - name: offset + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: Offset + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Search Saved Loras Api V1 Optimizers Checkpoints Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/{checkpoint_id}: + get: + tags: + - optimizer-checkpoints + summary: Get Saved Lora Checkpoint + operationId: get_saved_lora_checkpoint_api_v1_optimizers_checkpoints__checkpoint_id__get + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Saved Lora Checkpoint Api V1 Optimizers Checkpoints Checkpoint + Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - optimizer-checkpoints + summary: Patch Saved Lora Checkpoint + operationId: patch_saved_lora_checkpoint_api_v1_optimizers_checkpoints__checkpoint_id__patch + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SavedLoraPatchRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Patch Saved Lora Checkpoint Api V1 Optimizers Checkpoints Checkpoint + Id Patch + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - optimizer-checkpoints + summary: Delete Saved Lora Checkpoint + operationId: delete_saved_lora_checkpoint_api_v1_optimizers_checkpoints__checkpoint_id__delete + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + - name: purge + in: query + required: false + schema: + type: boolean + default: false + title: Purge + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Delete Saved Lora Checkpoint Api V1 Optimizers Checkpoints Checkpoint + Id Delete + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/{checkpoint_id}/inference-target: + get: + tags: + - optimizer-checkpoints + summary: Saved Lora Inference Target + operationId: saved_lora_inference_target_api_v1_optimizers_checkpoints__checkpoint_id__inference_target_get + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Saved Lora Inference Target Api V1 Optimizers Checkpoints Checkpoint + Id Inference Target Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/{checkpoint_id}/chat: + post: + tags: + - optimizer-checkpoints + summary: Chat Saved Lora Checkpoint + operationId: chat_saved_lora_checkpoint_api_v1_optimizers_checkpoints__checkpoint_id__chat_post + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CheckpointChatRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Chat Saved Lora Checkpoint Api V1 Optimizers Checkpoints Checkpoint + Id Chat Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /api/v1/optimizers/checkpoints/{checkpoint_id}/download: + get: + tags: + - optimizer-checkpoints + summary: Download Saved Lora Checkpoint + operationId: download_saved_lora_checkpoint_api_v1_optimizers_checkpoints__checkpoint_id__download_get + parameters: + - name: checkpoint_id + in: path + required: true + schema: + type: string + title: Checkpoint Id + - name: expires_in + in: query + required: false + schema: + type: integer + maximum: 3600 + minimum: 60 + default: 900 + title: Expires In + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Download Saved Lora Checkpoint Api V1 Optimizers Checkpoints Checkpoint + Id Download Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v1/exe-credentials/issue: post: tags: @@ -9146,6 +10895,34 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - smr + - research-intern + summary: Detach Research Intern Factory + operationId: detach_research_intern_factory + parameters: + - name: factory_id + in: path + required: true + schema: + type: string + title: Factory Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Detach Research Intern Factory + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /smr/research-intern/factories: get: tags: @@ -9350,61 +11127,185 @@ paths: tags: - smr - research-intern - summary: Close Intern Sync Branch - description: Admit a typed close whose runtime transition seals and merges the - branch. - operationId: close_intern_sync_branch_smr_research_intern_sync_sessions__sync_session_id__close_post + summary: Close Intern Sync Branch + description: Admit a typed close whose runtime transition seals and merges the + branch. + operationId: close_intern_sync_branch_smr_research_intern_sync_sessions__sync_session_id__close_post + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SyncBranchCloseRequestV1' + responses: + '202': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/InternRuntimeCommandReceipt' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/meta-threads: + get: + tags: + - smr + - research-intern + summary: List Intern Meta Threads + operationId: list_intern_meta_threads + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/MetaThreadResponseV1' + type: array + title: Response List Intern Meta Threads Smr Research Intern Meta + Threads Get + /smr/research-intern/meta-threads/{meta_thread_id}: + get: + tags: + - smr + - research-intern + summary: Get Intern Meta Thread + operationId: get_intern_meta_thread + parameters: + - name: meta_thread_id + in: path + required: true + schema: + type: string + title: Meta Thread Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/MetaThreadResponseV1' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/meta-threads/{meta_thread_id}/segments: + get: + tags: + - smr + - research-intern + summary: List Intern Meta Thread Segments + operationId: list_intern_meta_thread_segments + parameters: + - name: meta_thread_id + in: path + required: true + schema: + type: string + title: Meta Thread Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MetaThreadSegmentResponseV1' + title: Response List Intern Meta Thread Segments Smr Research Intern + Meta Threads Meta Thread Id Segments Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/meta-threads/{meta_thread_id}/handoffs: + get: + tags: + - smr + - research-intern + summary: List Intern Meta Thread Handoffs + operationId: list_intern_meta_thread_handoffs + parameters: + - name: meta_thread_id + in: path + required: true + schema: + type: string + title: Meta Thread Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MetaHandoffResponseV1' + title: Response List Intern Meta Thread Handoffs Smr Research Intern + Meta Threads Meta Thread Id Handoffs Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/meta-threads/{meta_thread_id}/handoffs/{handoff_id}/approve: + post: + tags: + - smr + - research-intern + summary: Approve Intern Meta Thread Handoff + description: "Attended approve: needs_review \u2192 approved (head unchanged)." + operationId: approve_intern_meta_thread_handoff_smr_research_intern_meta_threads__meta_thread_id__handoffs__handoff_id__approve_post parameters: - - name: sync_session_id + - name: meta_thread_id in: path required: true schema: type: string - title: Sync Session Id - requestBody: + title: Meta Thread Id + - name: handoff_id + in: path required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SyncBranchCloseRequestV1' + schema: + type: string + title: Handoff Id responses: - '202': + '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/InternRuntimeCommandReceipt' + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/meta-threads: - get: - tags: - - smr - - research-intern - summary: List Intern Meta Threads - operationId: list_intern_meta_threads_smr_research_intern_meta_threads_get - responses: - '200': - description: Successful Response - content: - application/json: - schema: - items: - $ref: '#/components/schemas/MetaThreadResponseV1' - type: array - title: Response List Intern Meta Threads Smr Research Intern Meta - Threads Get - /smr/research-intern/meta-threads/{meta_thread_id}: - get: + /smr/research-intern/meta-threads/{meta_thread_id}/handoffs/{handoff_id}/reject: + post: tags: - smr - research-intern - summary: Get Intern Meta Thread - operationId: get_intern_meta_thread_smr_research_intern_meta_threads__meta_thread_id__get + summary: Reject Intern Meta Thread Handoff + description: "Attended reject: needs_review \u2192 rejected (head unchanged)." + operationId: reject_intern_meta_thread_handoff_smr_research_intern_meta_threads__meta_thread_id__handoffs__handoff_id__reject_post parameters: - name: meta_thread_id in: path @@ -9412,26 +11313,33 @@ paths: schema: type: string title: Meta Thread Id + - name: handoff_id + in: path + required: true + schema: + type: string + title: Handoff Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/MetaThreadResponseV1' + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/meta-threads/{meta_thread_id}/segments: - get: + /smr/research-intern/meta-threads/{meta_thread_id}/handoffs/{handoff_id}/continue: + post: tags: - smr - research-intern - summary: List Intern Meta Thread Segments - operationId: list_intern_meta_thread_segments_smr_research_intern_meta_threads__meta_thread_id__segments_get + summary: Continue Intern Meta Thread Handoff + description: 'Attended continue: approve if needed, then advance Async spine.' + operationId: continue_intern_meta_thread_handoff_smr_research_intern_meta_threads__meta_thread_id__handoffs__handoff_id__continue_post parameters: - name: meta_thread_id in: path @@ -9439,30 +11347,38 @@ paths: schema: type: string title: Meta Thread Id + - name: handoff_id + in: path + required: true + schema: + type: string + title: Handoff Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MetaHandoffContinueRequestV1' responses: '200': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/MetaThreadSegmentResponseV1' - title: Response List Intern Meta Thread Segments Smr Research Intern - Meta Threads Meta Thread Id Segments Get + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/meta-threads/{meta_thread_id}/handoffs: + /smr/research-intern/meta-threads/{meta_thread_id}/deliveries: get: tags: - smr - research-intern - summary: List Intern Meta Thread Handoffs - operationId: list_intern_meta_thread_handoffs_smr_research_intern_meta_threads__meta_thread_id__handoffs_get + summary: List Intern Meta Thread Deliveries + operationId: list_intern_meta_thread_deliveries_smr_research_intern_meta_threads__meta_thread_id__deliveries_get parameters: - name: meta_thread_id in: path @@ -9478,9 +11394,9 @@ paths: schema: type: array items: - $ref: '#/components/schemas/MetaHandoffResponseV1' - title: Response List Intern Meta Thread Handoffs Smr Research Intern - Meta Threads Meta Thread Id Handoffs Get + $ref: '#/components/schemas/MetaThreadDeliveryResponseV1' + title: Response List Intern Meta Thread Deliveries Smr Research Intern + Meta Threads Meta Thread Id Deliveries Get '422': description: Validation Error content: @@ -9493,7 +11409,7 @@ paths: - smr - research-intern summary: List Intern Meta Thread Messages - operationId: list_intern_meta_thread_messages_smr_research_intern_meta_threads__meta_thread_id__messages_get + operationId: list_intern_meta_thread_messages parameters: - name: meta_thread_id in: path @@ -9533,7 +11449,7 @@ paths: - smr - research-intern summary: Create Intern Meta Thread Message - operationId: create_intern_meta_thread_message_smr_research_intern_meta_threads_messages_post + operationId: create_intern_meta_thread_message requestBody: content: application/json: @@ -9587,7 +11503,7 @@ paths: - research-intern summary: Get Intern Sync Deploy Packet description: Return a secret-free paste-ready Mode-B coding-agent handoff. - operationId: get_intern_sync_deploy_packet_smr_research_intern_sync_sessions__sync_session_id__deploy_packet_get + operationId: get_intern_sync_deploy_packet parameters: - name: sync_session_id in: path @@ -9615,7 +11531,7 @@ paths: - research-intern summary: Get Intern Sync Session Usage description: Session resource/usage panel projection (receipts only). - operationId: get_intern_sync_session_usage_smr_research_intern_sync_sessions__sync_session_id__usage_get + operationId: get_intern_sync_session_usage parameters: - name: sync_session_id in: path @@ -9629,10 +11545,7 @@ paths: content: application/json: schema: - type: object - additionalProperties: true - title: Response Get Intern Sync Session Usage Smr Research Intern - Sync Sessions Sync Session Id Usage Get + $ref: '#/components/schemas/InternSessionUsageResponse' '422': description: Validation Error content: @@ -9645,7 +11558,7 @@ paths: - smr - research-intern summary: Get Intern Async Assignment Usage - operationId: get_intern_async_assignment_usage_smr_research_intern_async_assignments__assignment_id__usage_get + operationId: get_intern_async_assignment_usage parameters: - name: assignment_id in: path @@ -9659,10 +11572,64 @@ paths: content: application/json: schema: - type: object - additionalProperties: true - title: Response Get Intern Async Assignment Usage Smr Research Intern - Async Assignments Assignment Id Usage Get + $ref: '#/components/schemas/InternSessionUsageResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-sessions/{sync_session_id}/resources: + get: + tags: + - smr + - research-intern + summary: Get Intern Sync Session Resources + description: See notes/specifications/tanha/current/systems/platform/intern_resource_inventory.md. + operationId: get_intern_sync_session_resources + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/InternResourceInventory' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/async-assignments/{assignment_id}/resources: + get: + tags: + - smr + - research-intern + summary: Get Intern Async Assignment Resources + description: Read the exact recorded assignment, never silently substitute today's + singleton. + operationId: get_intern_async_assignment_resources + parameters: + - name: assignment_id + in: path + required: true + schema: + type: string + title: Assignment Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/InternResourceInventory' '422': description: Validation Error content: @@ -9741,7 +11708,7 @@ paths: - smr - research-intern summary: Acquire Or Renew Intern Sync Presence - operationId: acquire_or_renew_intern_sync_presence_smr_research_intern_sync_sessions__sync_session_id__presence_put + operationId: acquire_intern_sync_presence parameters: - name: sync_session_id in: path @@ -9774,7 +11741,7 @@ paths: - smr - research-intern summary: Release Intern Sync Presence - operationId: release_intern_sync_presence_smr_research_intern_sync_sessions__sync_session_id__presence_release_post + operationId: release_intern_sync_presence parameters: - name: sync_session_id in: path @@ -10109,443 +12076,31 @@ paths: required: true responses: '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncRuntimeResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/ensure: - post: - tags: - - smr - - research-intern - summary: Ensure Intern Async Runtime Alias - description: Named ensure alias over the same organization-singleton operation. - operationId: ensure_intern_async_runtime_alias - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncRuntimeEnsureRequest' - required: true - responses: - '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncRuntimeResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/fixtures: - post: - tags: - - smr - - research-intern - summary: Create Research Intern Acceptance Fixture - description: 'Provision a disposable, ready Factory/Project/Effort/Run fixture. - - - No tribal ids: one command yields a bound, Factory-ready chain with the - - organization Intern attached, idempotent per idempotency_key within one - - test attempt. The receipt is the durable evidence of what exists.' - operationId: create_research_intern_acceptance_fixture_smr_research_intern_fixtures_post - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InternAcceptanceFixtureRequest' - required: true - responses: - '201': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/fixtures/{fixture_id}: - get: - tags: - - smr - - research-intern - summary: Get Research Intern Acceptance Fixture - description: Read one fixture receipt (evidence stays readable through retention). - operationId: get_research_intern_acceptance_fixture_smr_research_intern_fixtures__fixture_id__get - parameters: - - name: fixture_id - in: path - required: true - schema: - type: string - title: Fixture Id - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/fixtures/{fixture_id}:teardown: - post: - tags: - - smr - - research-intern - summary: Teardown Research Intern Acceptance Fixture - description: Explicitly tear down a fixture while preserving its evidence window. - operationId: teardown_research_intern_acceptance_fixture_smr_research_intern_fixtures__fixture_id__teardown_post - parameters: - - name: fixture_id - in: path - required: true - schema: - type: string - title: Fixture Id - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/blockers/{blocker_id}/open-sync: - post: - tags: - - smr - - research-intern - summary: Open Intern Async Blocker Sync - description: Open one exact-context Sync handoff without creating an approval. - operationId: open_intern_async_blocker_sync_smr_research_intern_async_blockers__blocker_id__open_sync_post - parameters: - - name: blocker_id - in: path - required: true - schema: - type: string - title: Blocker Id - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncBlockerOpenSyncRequest' - responses: - '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncBlockerOpenSyncResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/blockers/{blocker_id}: - get: - tags: - - smr - - research-intern - summary: Get Intern Async Blocker - description: Read one exact blocker and its durable terminal resolution receipt. - operationId: get_intern_async_blocker_smr_research_intern_async_blockers__blocker_id__get - parameters: - - name: blocker_id - in: path - required: true - schema: - type: string - title: Blocker Id - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncBlockerResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/blockers/{blocker_id}/resolve: - post: - tags: - - smr - - research-intern - summary: Resolve Intern Async Blocker - description: Record explicit Sync disposition and durably continue Async work. - operationId: resolve_intern_async_blocker_smr_research_intern_async_blockers__blocker_id__resolve_post - parameters: - - name: blocker_id - in: path - required: true - schema: - type: string - title: Blocker Id - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncBlockerResolveRequest' - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncBlockerResolveResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/commands: - post: - tags: - - smr - - research-intern - summary: Command Intern Async Runtime - description: Admit a control command to the organization's singleton Async inbox. - operationId: command_intern_async_runtime - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InternRuntimeCommandRequest' - required: true - responses: - '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternRuntimeCommandReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/messages: - post: - tags: - - smr - - research-intern - summary: Message Intern Async Runtime - description: Typed instruction alias over the singleton PostgreSQL command inbox. - operationId: message_intern_async_runtime - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AsyncInstructionCommandRequest' - required: true - responses: - '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternRuntimeCommandReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/craftax-campaigns/instructions: - post: - tags: - - smr - - research-intern - summary: Instruct Intern Craftax Campaign - description: 'Admit one exact Luna/Craftax campaign instruction to Async Intern. - - - The alias deliberately reuses the singleton command ledger. It provides a - - strict campaign boundary without creating a second runtime or experiment - - authority beside Factory.' - operationId: instruct_intern_craftax_campaign - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CraftaxCampaignInstructionRequest' - required: true - responses: - '202': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/InternRuntimeCommandReceipt' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/craftax-campaigns/evidence: - post: - tags: - - smr - - research-intern - summary: Publish Intern Craftax Evidence - description: Publish one exact-bound typed record to the Factory experiment - log. - operationId: publish_intern_craftax_evidence - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CraftaxEvidencePublicationRequest' - required: true - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/CraftaxEvidencePublicationResponse' - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/events: - get: - tags: - - smr - - research-intern - summary: List Intern Async Runtime Events - description: Replay the singleton Async event ledger without a runtime path - id. - operationId: list_intern_async_runtime_events - parameters: - - name: after_sequence - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - title: After Sequence - - name: limit - in: query - required: false - schema: - type: integer - maximum: 500 - minimum: 1 - default: 100 - title: Limit - responses: - '200': - description: Successful Response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/InternRuntimeEventResponse' - title: Response List Intern Async Runtime Events Smr Research Intern - Async Events Get - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async/events/stream: - get: - tags: - - smr - - research-intern - summary: Stream Intern Async Runtime Events - description: Tail the singleton Async event ledger without a runtime path id. - operationId: stream_intern_async_runtime_events - parameters: - - name: after_sequence - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - title: After Sequence - - name: Last-Event-ID - in: header - required: false - schema: - anyOf: - - type: string - - type: 'null' - title: Last-Event-Id - responses: - '200': - description: Replay-then-tail SSE over the singleton Async ledger. + description: Successful Response content: - text/event-stream: + application/json: schema: - $ref: '#/components/schemas/InternRuntimeEventStreamEnvelope' + $ref: '#/components/schemas/AsyncRuntimeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async-assignments: + /smr/research-intern/async/ensure: post: tags: - smr - research-intern - summary: Create Intern Async Assignment - description: Create leave-safe Async work before starting durable orchestration. - operationId: create_intern_async_assignment - deprecated: true + summary: Ensure Intern Async Runtime Alias + description: Named ensure alias over the same organization-singleton operation. + operationId: ensure_intern_async_runtime_alias requestBody: - required: true content: application/json: schema: $ref: '#/components/schemas/AsyncRuntimeEnsureRequest' + required: true responses: '202': description: Successful Response @@ -10559,567 +12114,515 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/fixtures: + post: + tags: + - smr + - research-intern + summary: Create Research Intern Acceptance Fixture + description: 'Provision a disposable, ready Factory/Project/Effort/Run fixture. + + + No tribal ids: one command yields a bound, Factory-ready chain with the + + organization Intern attached, idempotent per idempotency_key within one + + test attempt. The receipt is the durable evidence of what exists.' + operationId: create_intern_acceptance_fixture + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InternAcceptanceFixtureRequest' + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/fixtures/{fixture_id}: get: tags: - smr - research-intern - summary: List Intern Async Assignments - operationId: list_intern_async_assignments - deprecated: true + summary: Get Research Intern Acceptance Fixture + description: Read one fixture receipt (evidence stays readable through retention). + operationId: get_intern_acceptance_fixture parameters: - - name: limit - in: query - required: false + - name: fixture_id + in: path + required: true schema: - type: integer - maximum: 500 - minimum: 1 - default: 100 - title: Limit + type: string + title: Fixture Id responses: '200': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/AsyncRuntimeResponse' - title: Response List Intern Async Assignments Smr Research Intern - Async Assignments Get + $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async-assignments/{assignment_id}: - get: + /smr/research-intern/fixtures/{fixture_id}:teardown: + post: tags: - smr - research-intern - summary: Get Intern Async Assignment - operationId: get_intern_async_assignment - deprecated: true + summary: Teardown Research Intern Acceptance Fixture + description: Explicitly tear down a fixture while preserving its evidence window. + operationId: teardown_intern_acceptance_fixture parameters: - - name: assignment_id + - name: fixture_id in: path required: true schema: type: string - title: Assignment Id + title: Fixture Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/AsyncRuntimeResponse' + $ref: '#/components/schemas/InternAcceptanceFixtureReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/async-assignments/{assignment_id}/commands: + /smr/research-intern/async/blockers/{blocker_id}/open-sync: post: tags: - smr - research-intern - summary: Command Intern Async Assignment - operationId: command_intern_async_assignment - deprecated: true + summary: Open Intern Async Blocker Sync + description: Open one exact-context Sync handoff without creating an approval. + operationId: open_intern_async_blocker_sync parameters: - - name: assignment_id + - name: blocker_id in: path required: true schema: type: string - title: Assignment Id + title: Blocker Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/InternRuntimeCommandRequest' + $ref: '#/components/schemas/AsyncBlockerOpenSyncRequest' responses: '202': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/InternRuntimeCommandReceipt' + $ref: '#/components/schemas/AsyncBlockerOpenSyncResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/events: + /smr/research-intern/async/blockers/{blocker_id}: get: tags: - smr - research-intern - summary: List Intern Runtime Events - operationId: list_intern_runtime_events + summary: Get Intern Async Blocker + description: Read one exact blocker and its durable terminal resolution receipt. + operationId: get_intern_async_blocker parameters: - - name: runtime_kind - in: path - required: true - schema: - type: string - title: Runtime Kind - - name: runtime_id + - name: blocker_id in: path required: true schema: type: string - title: Runtime Id - - name: after_sequence - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - title: After Sequence - - name: limit - in: query - required: false - schema: - type: integer - maximum: 500 - minimum: 1 - default: 100 - title: Limit + title: Blocker Id responses: '200': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/InternRuntimeEventResponse' - title: Response List Intern Runtime Events Smr Research Intern Runtimes Runtime - Kind Runtime Id Events Get + $ref: '#/components/schemas/AsyncBlockerResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/events/stream: - get: + /smr/research-intern/async/blockers/{blocker_id}/resolve: + post: tags: - smr - research-intern - summary: Stream Intern Runtime Events - operationId: stream_intern_runtime_events + summary: Resolve Intern Async Blocker + description: Record explicit Sync disposition and durably continue Async work. + operationId: resolve_intern_async_blocker parameters: - - name: runtime_kind + - name: blocker_id in: path required: true schema: type: string - title: Runtime Kind - - name: runtime_id - in: path + title: Blocker Id + requestBody: required: true - schema: - type: string - title: Runtime Id - - name: after_sequence - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - title: After Sequence - - name: Last-Event-ID - in: header - required: false - schema: - anyOf: - - type: string - - type: 'null' - title: Last-Event-Id + content: + application/json: + schema: + $ref: '#/components/schemas/AsyncBlockerResolveRequest' responses: '200': - description: Replay-then-tail SSE over durable Intern events. + description: Successful Response content: - text/event-stream: + application/json: schema: - $ref: '#/components/schemas/InternRuntimeEventStreamEnvelope' + $ref: '#/components/schemas/AsyncBlockerResolveResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/mcp-actions: - get: + /smr/research-intern/async/commands: + post: tags: - smr - research-intern - summary: List Intern Runtime Mcp Actions - operationId: list_intern_runtime_mcp_actions - parameters: - - name: runtime_kind - in: path - required: true - schema: - type: string - title: Runtime Kind - - name: runtime_id - in: path + summary: Command Intern Async Runtime + description: Admit a control command to the organization's singleton Async inbox. + operationId: command_intern_async_runtime + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InternRuntimeCommandRequest' required: true - schema: - type: string - title: Runtime Id - - name: limit - in: query - required: false - schema: - type: integer - maximum: 500 - minimum: 1 - default: 100 - title: Limit responses: - '200': + '202': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/InternMcpActionResponse' - title: Response List Intern Runtime Mcp Actions Smr Research Intern - Runtimes Runtime Kind Runtime Id Mcp Actions Get + $ref: '#/components/schemas/InternRuntimeCommandReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/mcp-actions/{action_id}/approval: + /smr/research-intern/async/handoff-model: post: tags: - smr - research-intern - summary: Decide Intern Runtime Mcp Action Approval - operationId: decide_intern_runtime_mcp_action_approval - parameters: - - name: runtime_kind - in: path - required: true - schema: - type: string - title: Runtime Kind - - name: runtime_id - in: path - required: true - schema: - type: string - title: Runtime Id - - name: action_id - in: path - required: true - schema: - type: string - title: Action Id + summary: Handoff Intern Async Model + description: 'Product verb: change Async model/effort via spine handoff (no + MT id). + + + See: ``notes/plans/smr/intern_metaharness_control_plane.md``.' + operationId: handoff_intern_async_model requestBody: - required: true content: application/json: schema: - $ref: '#/components/schemas/InternMcpActionApprovalRequest' + $ref: '#/components/schemas/AsyncHandoffModelRequestV1' + required: true responses: - '200': + '202': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/InternMcpActionResponse' + $ref: '#/components/schemas/InternRuntimeCommandReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-sessions/{sync_session_id}/async-blocker-handoff: - get: + /smr/research-intern/async/handoffs/review: + post: tags: - smr - research-intern - summary: Get Sync Async Blocker Handoff - operationId: get_sync_async_blocker_handoff_smr_research_intern_sync_sessions__sync_session_id__async_blocker_handoff_get - parameters: - - name: sync_session_id - in: path + summary: Seal Intern Async Handoff For Review + description: 'Attended product verb: seal Async model switch at needs_review + (no MT id).' + operationId: seal_intern_async_handoff_for_review + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AsyncHandoffReviewRequestV1' required: true - schema: - type: string - title: Sync Session Id responses: - '200': + '201': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/AsyncBlockerHandoffReceiptV1' + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-sessions/{sync_session_id}/approvals: + /smr/research-intern/async/handoffs: get: tags: - smr - research-intern - summary: List Sync Approval Cards - operationId: list_sync_approval_cards_smr_research_intern_sync_sessions__sync_session_id__approvals_get + summary: List Intern Async Handoffs + description: List Async spine handoffs for the org singleton (no MT id). + operationId: list_intern_async_handoffs + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/MetaHandoffResponseV1' + type: array + title: Response List Intern Async Handoffs Smr Research Intern Async + Handoffs Get + /smr/research-intern/async/handoffs/{handoff_id}/approve: + post: + tags: + - smr + - research-intern + summary: Approve Intern Async Handoff + operationId: approve_intern_async_handoff parameters: - - name: sync_session_id + - name: handoff_id in: path required: true schema: type: string - title: Sync Session Id + title: Handoff Id responses: '200': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/SyncApprovalCardV1' - title: Response List Sync Approval Cards Smr Research Intern Sync - Sessions Sync Session Id Approvals Get + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-approvals/{approval_id}: - get: + /smr/research-intern/async/handoffs/{handoff_id}/reject: + post: tags: - smr - research-intern - summary: Get Sync Approval Card - operationId: get_sync_approval_card_smr_research_intern_sync_approvals__approval_id__get + summary: Reject Intern Async Handoff + operationId: reject_intern_async_handoff parameters: - - name: approval_id + - name: handoff_id in: path required: true schema: type: string - title: Approval Id + title: Handoff Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/SyncApprovalCardV1' + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-approvals/{approval_id}/decision: + /smr/research-intern/async/handoffs/{handoff_id}/continue: post: tags: - smr - research-intern - summary: Decide Sync Approval Card - operationId: decide_sync_approval_card_smr_research_intern_sync_approvals__approval_id__decision_post + summary: Continue Intern Async Handoff + operationId: continue_intern_async_handoff parameters: - - name: approval_id + - name: handoff_id in: path required: true schema: type: string - title: Approval Id + title: Handoff Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/SyncApprovalDecisionRequestV1' + $ref: '#/components/schemas/MetaHandoffContinueRequestV1' responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/SyncApprovalCardV1' + $ref: '#/components/schemas/MetaHandoffResponseV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-sessions/{sync_session_id}/experiments: - get: + /smr/research-intern/async/messages: + post: tags: - smr - research-intern - summary: List Sync Session Experiments - description: List the experiment ledger scoped to one Sync session. - operationId: list_sync_session_experiments_smr_research_intern_sync_sessions__sync_session_id__experiments_get - parameters: - - name: sync_session_id - in: path + summary: Message Intern Async Runtime + description: Typed instruction alias over the singleton PostgreSQL command inbox. + operationId: message_intern_async_runtime + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AsyncInstructionCommandRequest' required: true - schema: - type: string - title: Sync Session Id - - name: limit - in: query - required: false - schema: - type: integer - maximum: 256 - minimum: 1 - default: 100 - title: Limit responses: - '200': + '202': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/SyncExperimentV1' - title: Response List Sync Session Experiments Smr Research Intern - Sync Sessions Sync Session Id Experiments Get + $ref: '#/components/schemas/InternRuntimeCommandReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-sessions/{sync_session_id}/experiments/{experiment_id}/lifecycle: - post: + /smr/research-intern/async/events: + get: tags: - smr - research-intern - summary: Transition Sync Session Experiment - description: 'Operator promote/stop decision on one session-scoped experiment. - - - Presence-enforced like sync approvals. ``promote`` keeps exactly one - - promoted experiment per session (the previous one is marked - - ``superseded``); ``stop`` records the honest no-lift terminal. Typed - - conditions: ``intern_experiment_not_found``, - - ``intern_experiment_wrong_session``, ``intern_experiment_not_scored`` - - (promote without ``allow_unscored``), ``intern_experiment_not_promotable`` - - and ``intern_experiment_not_stoppable``.' - operationId: transition_sync_session_experiment_smr_research_intern_sync_sessions__sync_session_id__experiments__experiment_id__lifecycle_post + summary: List Intern Async Runtime Events + description: Replay the singleton Async event ledger without a runtime path + id. + operationId: list_intern_async_runtime_events parameters: - - name: sync_session_id - in: path - required: true + - name: after_sequence + in: query + required: false schema: - type: string - title: Sync Session Id - - name: experiment_id - in: path - required: true + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: limit + in: query + required: false schema: - type: string - title: Experiment Id - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SyncExperimentLifecycleRequestV1' + type: integer + maximum: 500 + minimum: 1 + default: 100 + title: Limit responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/SyncExperimentV1' + type: array + items: + $ref: '#/components/schemas/InternRuntimeEventResponse' + title: Response List Intern Async Runtime Events Smr Research Intern + Async Events Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sync-sessions/{sync_session_id}/harness-bundle: + /smr/research-intern/async/events/stream: get: tags: - smr - research-intern - summary: Download Sync Session Harness Bundle - description: 'Download the take-home harness bundle zip for one Sync session - (F6.1). - - - Streams a byte-stable zip (workspace tree at the pinned snapshot, winning - - candidate, experiment ledger, result summary, README, MANIFEST) whose - - sha256 digest is served in the ``X-Harness-Bundle-Digest`` header. - - Bundles are S3-backed and require no open session, so closed sessions - - read back identically (F6.4).' - operationId: download_sync_session_harness_bundle_smr_research_intern_sync_sessions__sync_session_id__harness_bundle_get + summary: Stream Intern Async Runtime Events + description: Tail the singleton Async event ledger without a runtime path id. + operationId: stream_intern_async_runtime_events parameters: - - name: sync_session_id - in: path - required: true + - name: after_sequence + in: query + required: false schema: - type: string - title: Sync Session Id + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: Last-Event-ID + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Last-Event-Id responses: '200': - description: Successful Response + description: Replay-then-tail SSE over the singleton Async ledger. + content: + text/event-stream: + schema: + $ref: '#/components/schemas/InternRuntimeEventStreamEnvelope' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions: + /smr/research-intern/async-assignments: post: tags: - smr - research-intern - summary: Create Research Intern Session - operationId: create_research_intern_session + summary: Create Intern Async Assignment + description: Create leave-safe Async work before starting durable orchestration. + operationId: create_intern_async_assignment deprecated: true requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionCreateRequest' + $ref: '#/components/schemas/AsyncRuntimeEnsureRequest' responses: - '200': + '202': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionResponse' + $ref: '#/components/schemas/AsyncRuntimeResponse' '422': description: Validation Error content: @@ -11130,8 +12633,8 @@ paths: tags: - smr - research-intern - summary: List Research Intern Sessions - operationId: list_research_intern_sessions + summary: List Intern Async Assignments + operationId: list_intern_async_assignments deprecated: true parameters: - name: limit @@ -11151,97 +12654,102 @@ paths: schema: type: array items: - $ref: '#/components/schemas/ResearchInternSessionResponse' - title: Response List Research Intern Sessions Smr Research Intern - Sessions Get + $ref: '#/components/schemas/AsyncRuntimeResponse' + title: Response List Intern Async Assignments Smr Research Intern + Async Assignments Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}: + /smr/research-intern/async-assignments/{assignment_id}: get: tags: - smr - research-intern - summary: Get Research Intern Session - operationId: get_research_intern_session + summary: Get Intern Async Assignment + operationId: get_intern_async_assignment deprecated: true parameters: - - name: session_id + - name: assignment_id in: path required: true schema: type: string - title: Session Id + title: Assignment Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionResponse' + $ref: '#/components/schemas/AsyncRuntimeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/events: + /smr/research-intern/async-assignments/{assignment_id}/commands: post: tags: - smr - research-intern - summary: Append Research Intern Session Event - operationId: append_research_intern_session_event + summary: Command Intern Async Assignment + operationId: command_intern_async_assignment deprecated: true parameters: - - name: session_id + - name: assignment_id in: path required: true schema: type: string - title: Session Id + title: Assignment Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResearchInternEventAppendRequest' + $ref: '#/components/schemas/InternRuntimeCommandRequest' responses: - '200': + '202': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternEventResponse' + $ref: '#/components/schemas/InternRuntimeCommandReceipt' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/events: get: tags: - smr - research-intern - summary: List Research Intern Session Events - operationId: list_research_intern_session_events - deprecated: true + summary: List Intern Runtime Events + operationId: list_intern_runtime_events parameters: - - name: session_id + - name: runtime_kind in: path required: true schema: type: string - title: Session Id + title: Runtime Kind + - name: runtime_id + in: path + required: true + schema: + type: string + title: Runtime Id - name: after_sequence in: query required: false schema: type: integer - maximum: 2147483647 minimum: 0 default: 0 title: After Sequence @@ -11262,38 +12770,40 @@ paths: schema: type: array items: - $ref: '#/components/schemas/ResearchInternEventResponse' - title: Response List Research Intern Session Events Smr Research Intern - Sessions Session Id Events Get + $ref: '#/components/schemas/InternRuntimeEventResponse' + title: Response List Intern Runtime Events Smr Research Intern Runtimes Runtime + Kind Runtime Id Events Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/events/stream: + /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/events/stream: get: tags: - smr - research-intern - summary: Stream Research Intern Session Events - description: Read-only stream; the durable writer pushes projection and `/sync` - reconciles. - operationId: stream_research_intern_session_events - deprecated: true + summary: Stream Intern Runtime Events + operationId: stream_intern_runtime_events parameters: - - name: session_id + - name: runtime_kind in: path required: true schema: type: string - title: Session Id + title: Runtime Kind + - name: runtime_id + in: path + required: true + schema: + type: string + title: Runtime Id - name: after_sequence in: query required: false schema: type: integer - maximum: 2147483647 minimum: 0 default: 0 title: After Sequence @@ -11307,32 +12817,37 @@ paths: title: Last-Event-Id responses: '200': - description: Replay-then-tail SSE stream over durable Research Intern events. + description: Replay-then-tail SSE over durable Intern events. content: text/event-stream: schema: - $ref: '#/components/schemas/ResearchInternEventStreamEnvelope' + $ref: '#/components/schemas/InternRuntimeEventStreamEnvelope' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/sync: - post: + /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/mcp-actions: + get: tags: - smr - research-intern - summary: Sync Research Intern Session - operationId: sync_research_intern_session - deprecated: true + summary: List Intern Runtime Mcp Actions + operationId: list_intern_runtime_mcp_actions parameters: - - name: session_id + - name: runtime_kind in: path required: true schema: type: string - title: Session Id + title: Runtime Kind + - name: runtime_id + in: path + required: true + schema: + type: string + title: Runtime Id - name: limit in: query required: false @@ -11340,7 +12855,7 @@ paths: type: integer maximum: 500 minimum: 1 - default: 200 + default: 100 title: Limit responses: '200': @@ -11348,111 +12863,306 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionSyncResponse' + type: array + items: + $ref: '#/components/schemas/InternMcpActionResponse' + title: Response List Intern Runtime Mcp Actions Smr Research Intern + Runtimes Runtime Kind Runtime Id Mcp Actions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/turns: + /smr/research-intern/runtimes/{runtime_kind}/{runtime_id}/mcp-actions/{action_id}/approval: post: tags: - smr - research-intern - summary: Create Research Intern Session Turn - operationId: create_research_intern_session_turn - deprecated: true + summary: Decide Intern Runtime Mcp Action Approval + operationId: decide_intern_runtime_mcp_action_approval parameters: - - name: session_id + - name: runtime_kind in: path required: true schema: type: string - title: Session Id + title: Runtime Kind + - name: runtime_id + in: path + required: true + schema: + type: string + title: Runtime Id + - name: action_id + in: path + required: true + schema: + type: string + title: Action Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResearchInternTurnRequest' + $ref: '#/components/schemas/InternMcpActionApprovalRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/InternMcpActionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-sessions/{sync_session_id}/async-blocker-handoff: + get: + tags: + - smr + - research-intern + summary: Get Sync Async Blocker Handoff + operationId: get_sync_async_blocker_handoff_smr_research_intern_sync_sessions__sync_session_id__async_blocker_handoff_get + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AsyncBlockerHandoffReceiptV1' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-sessions/{sync_session_id}/approvals: + get: + tags: + - smr + - research-intern + summary: List Sync Approval Cards + operationId: list_intern_sync_approvals + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SyncApprovalCardV1' + title: Response List Sync Approval Cards Smr Research Intern Sync + Sessions Sync Session Id Approvals Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-approvals/{approval_id}: + get: + tags: + - smr + - research-intern + summary: Get Sync Approval Card + operationId: get_sync_approval_card_smr_research_intern_sync_approvals__approval_id__get + parameters: + - name: approval_id + in: path + required: true + schema: + type: string + title: Approval Id responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternTurnResponse' + $ref: '#/components/schemas/SyncApprovalCardV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/close: + /smr/research-intern/sync-approvals/{approval_id}/decision: post: tags: - smr - research-intern - summary: Close Research Intern Session - operationId: close_research_intern_session - deprecated: true + summary: Decide Sync Approval Card + operationId: decide_intern_sync_approval parameters: - - name: session_id + - name: approval_id in: path required: true schema: type: string - title: Session Id + title: Approval Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionCloseRequest' + $ref: '#/components/schemas/SyncApprovalDecisionRequestV1' responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternSessionResponse' + $ref: '#/components/schemas/SyncApprovalCardV1' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/research-intern/sessions/{session_id}/trace:publish: + /smr/research-intern/sync-sessions/{sync_session_id}/experiments: + get: + tags: + - smr + - research-intern + summary: List Sync Session Experiments + description: List the experiment ledger scoped to one Sync session. + operationId: list_sync_session_experiments_smr_research_intern_sync_sessions__sync_session_id__experiments_get + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + - name: limit + in: query + required: false + schema: + type: integer + maximum: 256 + minimum: 1 + default: 100 + title: Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SyncExperimentV1' + title: Response List Sync Session Experiments Smr Research Intern + Sync Sessions Sync Session Id Experiments Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-sessions/{sync_session_id}/experiments/{experiment_id}/lifecycle: post: tags: - smr - research-intern - summary: Publish Research Intern Session Trace - description: Promote a terminal Intern event chain through Factory Trace Store - authority. - operationId: publish_research_intern_session_trace - deprecated: true + summary: Transition Sync Session Experiment + description: 'Operator promote/stop decision on one session-scoped experiment. + + + Presence-enforced like sync approvals. ``promote`` keeps exactly one + + promoted experiment per session (the previous one is marked + + ``superseded``); ``stop`` records the honest no-lift terminal. Typed + + conditions: ``intern_experiment_not_found``, + + ``intern_experiment_wrong_session``, ``intern_experiment_not_scored`` + + (promote without ``allow_unscored``), ``intern_experiment_not_promotable`` + + and ``intern_experiment_not_stoppable``.' + operationId: transition_sync_session_experiment_smr_research_intern_sync_sessions__sync_session_id__experiments__experiment_id__lifecycle_post parameters: - - name: session_id + - name: sync_session_id in: path required: true schema: type: string - title: Session Id + title: Sync Session Id + - name: experiment_id + in: path + required: true + schema: + type: string + title: Experiment Id requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResearchInternTracePublicationRequest' + $ref: '#/components/schemas/SyncExperimentLifecycleRequestV1' responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/ResearchInternTracePublicationResponse' + $ref: '#/components/schemas/SyncExperimentV1' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/research-intern/sync-sessions/{sync_session_id}/harness-bundle: + get: + tags: + - smr + - research-intern + summary: Download Sync Session Harness Bundle + description: 'Download the take-home harness bundle zip for one Sync session + (F6.1). + + + Streams a byte-stable zip (workspace tree at the pinned snapshot, winning + + candidate, experiment ledger, result summary, README, MANIFEST) whose + + sha256 digest is served in the ``X-Harness-Bundle-Digest`` header. + + Bundles are S3-backed and require no open session, so closed sessions + + read back identically (F6.4).' + operationId: download_sync_session_harness_bundle_smr_research_intern_sync_sessions__sync_session_id__harness_bundle_get + parameters: + - name: sync_session_id + in: path + required: true + schema: + type: string + title: Sync Session Id + responses: + '200': + description: Successful Response '422': description: Validation Error content: @@ -15362,6 +17072,13 @@ paths: - type: string - type: 'null' title: Project Id + - name: unbound_only + in: query + required: false + schema: + type: boolean + default: false + title: Unbound Only - name: limit in: query required: false @@ -17083,6 +18800,152 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /smr/projects/{project_id}/wiki/reviews: + post: + tags: + - smr + - wiki + summary: Review Project Wiki Proposal + description: "Librarian accept/reject/merge \u2014 operator REST parity with\ + \ Intern MCP." + operationId: review_project_wiki_proposal_smr_projects__project_id__wiki_reviews_post + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmrWikiReviewProposalRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Review Project Wiki Proposal Smr Projects Project + Id Wiki Reviews Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/projects/{project_id}/wiki/sections/supersede: + post: + tags: + - smr + - wiki + summary: Supersede Project Wiki Section + operationId: supersede_project_wiki_section_smr_projects__project_id__wiki_sections_supersede_post + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmrWikiSupersedeSectionRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Supersede Project Wiki Section Smr Projects Project + Id Wiki Sections Supersede Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/projects/{project_id}/wiki/sections/invalidate: + post: + tags: + - smr + - wiki + summary: Invalidate Project Wiki Section + operationId: invalidate_project_wiki_section_smr_projects__project_id__wiki_sections_invalidate_post + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmrWikiInvalidateSectionRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Invalidate Project Wiki Section Smr Projects Project + Id Wiki Sections Invalidate Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/projects/{project_id}/wiki/pages/compact: + post: + tags: + - smr + - wiki + summary: Compact Project Wiki Page + operationId: compact_project_wiki_page_smr_projects__project_id__wiki_pages_compact_post + parameters: + - name: project_id + in: path + required: true + schema: + type: string + title: Project Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmrWikiCompactPageRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Compact Project Wiki Page Smr Projects Project Id Wiki + Pages Compact Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /smr/projects/{project_id}/outputs: get: tags: @@ -18912,6 +20775,123 @@ paths: title: Cursor description: 'Opaque pagination cursor. Accepted forms: '''', ''|'', or ''''.' + - name: origin_runtime_kind + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/OriginRuntimeKind' + - type: 'null' + description: Intern runtime kind that launched the run; requires origin_runtime_id. + title: Origin Runtime Kind + description: Intern runtime kind that launched the run; requires origin_runtime_id. + - name: origin_runtime_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Intern Sync session or Async assignment id that launched the + run. + title: Origin Runtime Id + description: Intern Sync session or Async assignment id that launched the + run. + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SmrRunResponse' + title: Response List Runs Smr Runs Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/jobs: + get: + tags: + - smr + summary: List Jobs + operationId: list_jobs + parameters: + - name: project_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Optional project_id filter. Omit for org-wide jobs feed. + title: Project Id + description: Optional project_id filter. Omit for org-wide jobs feed. + - name: public_state + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Public run-state filter (single value or comma-separated). + title: Public State + description: Public run-state filter (single value or comma-separated). + - name: active_only + in: query + required: false + schema: + type: boolean + description: Only include active public states. + default: false + title: Active Only + description: Only include active public states. + - name: created_after + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: Inclusive lower bound on run.created_at (ISO-8601). + title: Created After + description: Inclusive lower bound on run.created_at (ISO-8601). + - name: created_before + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: Inclusive upper bound on run.created_at (ISO-8601). + title: Created Before + description: Inclusive upper bound on run.created_at (ISO-8601). + - name: limit + in: query + required: false + schema: + type: integer + maximum: 200 + minimum: 1 + default: 50 + title: Limit + - name: cursor + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: 'Opaque pagination cursor. Accepted forms: '''', + ''|'', or ''''.' + title: Cursor + description: 'Opaque pagination cursor. Accepted forms: '''', + ''|'', or ''''.' responses: '200': description: Successful Response @@ -18921,114 +20901,71 @@ paths: type: array items: $ref: '#/components/schemas/SmrRunResponse' - title: Response List Runs Smr Runs Get + title: Response List Jobs Smr Jobs Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/jobs: + /smr/runs/{run_id}: get: tags: - smr - summary: List Jobs - operationId: list_jobs + summary: Get Run + operationId: retrieve_run parameters: - - name: project_id - in: query - required: false - schema: - anyOf: - - type: string - - type: 'null' - description: Optional project_id filter. Omit for org-wide jobs feed. - title: Project Id - description: Optional project_id filter. Omit for org-wide jobs feed. - - name: public_state - in: query - required: false - schema: - anyOf: - - type: string - - type: 'null' - description: Public run-state filter (single value or comma-separated). - title: Public State - description: Public run-state filter (single value or comma-separated). - - name: active_only - in: query - required: false - schema: - type: boolean - description: Only include active public states. - default: false - title: Active Only - description: Only include active public states. - - name: created_after - in: query - required: false - schema: - anyOf: - - type: string - format: date-time - - type: 'null' - description: Inclusive lower bound on run.created_at (ISO-8601). - title: Created After - description: Inclusive lower bound on run.created_at (ISO-8601). - - name: created_before - in: query - required: false - schema: - anyOf: - - type: string - format: date-time - - type: 'null' - description: Inclusive upper bound on run.created_at (ISO-8601). - title: Created Before - description: Inclusive upper bound on run.created_at (ISO-8601). - - name: limit - in: query - required: false + - name: run_id + in: path + required: true schema: - type: integer - maximum: 200 - minimum: 1 - default: 50 - title: Limit - - name: cursor - in: query - required: false + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/SmrRunResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/runs/{run_id}/resource-settlement: + get: + tags: + - smr + summary: Get Run Resource Settlement + operationId: get_run_resource_settlement + parameters: + - name: run_id + in: path + required: true schema: - anyOf: - - type: string - - type: 'null' - description: 'Opaque pagination cursor. Accepted forms: '''', - ''|'', or ''''.' - title: Cursor - description: 'Opaque pagination cursor. Accepted forms: '''', - ''|'', or ''''.' + type: string + title: Run Id responses: '200': description: Successful Response content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/SmrRunResponse' - title: Response List Jobs Smr Jobs Get + $ref: '#/components/schemas/RunResourceSettlementResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /smr/runs/{run_id}: + /smr/runs/{run_id}/rollouts: get: tags: - smr - summary: Get Run - operationId: retrieve_run + summary: List the container-pool rollouts a swarm launched + operationId: list_swarm_rollouts parameters: - name: run_id in: path @@ -19036,13 +20973,22 @@ paths: schema: type: string title: Run Id + - name: limit + in: query + required: false + schema: + type: integer + maximum: 500 + minimum: 1 + default: 100 + title: Limit responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/SmrRunResponse' + $ref: '#/components/schemas/SwarmRolloutPage' '422': description: Validation Error content: @@ -20075,6 +22021,28 @@ paths: title: Cursor description: 'Opaque pagination cursor. Accepted forms: '''', ''|'', or ''''.' + - name: origin_runtime_kind + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/OriginRuntimeKind' + - type: 'null' + description: Intern runtime kind that launched the run; requires origin_runtime_id. + title: Origin Runtime Kind + description: Intern runtime kind that launched the run; requires origin_runtime_id. + - name: origin_runtime_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Intern Sync session or Async assignment id that launched the + run. + title: Origin Runtime Id + description: Intern Sync session or Async assignment id that launched the + run. responses: '200': description: Successful Response @@ -27040,6 +29008,7 @@ paths: - snapshot - transcript - heartbeat + - terminal title: RuntimeStreamEventKind type: string SmrRuntimeStreamHeartbeatPayload: @@ -27137,6 +29106,66 @@ paths: - timeline title: SmrRuntimeStreamSnapshotPayload type: object + SmrRuntimeStreamTerminalPayload: + additionalProperties: false + description: 'Final frame emitted when a run is terminal by durable + contract. + + + The runtime stream reads Redis control-plane state only. Once + that snapshot + + is gone there will never be another runtime event for the run, + and the + + durable terminal receipt (``SmrRunTerminalOutcome`` / ``smr_run_terminations``) + + is the sole remaining authority. This payload carries the terminal + fact + + in-band so an SSE consumer terminalises on the stream it is + already parsing + + instead of reconnecting against a refusal.' + properties: + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + run_id: + minLength: 1 + title: Run Id + type: string + public_state: + minLength: 1 + title: Public State + type: string + terminal_outcome: + anyOf: + - type: string + - type: 'null' + title: Terminal Outcome + stop_reason: + anyOf: + - type: string + - type: 'null' + title: Stop Reason + snapshot_missing_reason: + minLength: 1 + title: Snapshot Missing Reason + type: string + source: + const: smr_run_terminal_outcome.tombstone.runtime_stream.v1 + default: smr_run_terminal_outcome.tombstone.runtime_stream.v1 + title: Source + type: string + required: + - run_id + - public_state + - snapshot_missing_reason + title: SmrRuntimeStreamTerminalPayload + type: object SmrRuntimeStreamTimelineEventDetailPayload: additionalProperties: false properties: @@ -27387,6 +29416,12 @@ paths: title: Occurred At type: string kind: + description: Transcript event kind, e.g. message.completed, + reasoning.summary, turn.completed, tool.call.completed, + or task.completion_claimed (an accepted worker completion + claim projected from its durable claim row; payload schema + smr.task-completion-claimed.v1 with claim_id, task_key, + participant_id, text and summary). minLength: 1 title: Kind type: string @@ -27467,6 +29502,7 @@ paths: - $ref: '#/$defs/SmrRuntimeStreamSnapshotPayload' - $ref: '#/$defs/SmrTranscriptEventResponse' - $ref: '#/$defs/SmrRuntimeStreamHeartbeatPayload' + - $ref: '#/$defs/SmrRuntimeStreamTerminalPayload' title: Payload state_version: minLength: 1 @@ -30799,6 +32835,28 @@ paths: title: Cursor description: 'Opaque pagination cursor. Accepted forms: '''', ''|'', or ''''.' + - name: origin_runtime_kind + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/OriginRuntimeKind' + - type: 'null' + description: Intern runtime kind that launched the run; requires origin_runtime_id. + title: Origin Runtime Kind + description: Intern runtime kind that launched the run; requires origin_runtime_id. + - name: origin_runtime_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Intern Sync session or Async assignment id that launched the + run. + title: Origin Runtime Id + description: Intern Sync session or Async assignment id that launched the + run. responses: '200': description: Successful Response @@ -30842,6 +32900,69 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /smr/swarms/{run_id}/resource-settlement: + get: + tags: + - smr + - swarm-alias + summary: Get Run Resource Settlement + operationId: get_run_resource_settlement_get_swarm_alias + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/RunResourceSettlementResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/swarms/{run_id}/rollouts: + get: + tags: + - smr + - swarm-alias + summary: List the container-pool rollouts a swarm launched + operationId: list_swarm_rollouts_get_swarm_alias + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: limit + in: query + required: false + schema: + type: integer + maximum: 500 + minimum: 1 + default: 100 + title: Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/SwarmRolloutPage' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /smr/swarms/{run_id}/configuration: get: tags: @@ -31841,6 +33962,28 @@ paths: title: Cursor description: 'Opaque pagination cursor. Accepted forms: '''', ''|'', or ''''.' + - name: origin_runtime_kind + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/OriginRuntimeKind' + - type: 'null' + description: Intern runtime kind that launched the run; requires origin_runtime_id. + title: Origin Runtime Kind + description: Intern runtime kind that launched the run; requires origin_runtime_id. + - name: origin_runtime_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Intern Sync session or Async assignment id that launched the + run. + title: Origin Runtime Id + description: Intern Sync session or Async assignment id that launched the + run. responses: '200': description: Successful Response @@ -35257,6 +37400,7 @@ paths: - snapshot - transcript - heartbeat + - terminal title: RuntimeStreamEventKind type: string SmrRuntimeStreamHeartbeatPayload: @@ -35354,6 +37498,66 @@ paths: - timeline title: SmrRuntimeStreamSnapshotPayload type: object + SmrRuntimeStreamTerminalPayload: + additionalProperties: false + description: 'Final frame emitted when a run is terminal by durable + contract. + + + The runtime stream reads Redis control-plane state only. Once + that snapshot + + is gone there will never be another runtime event for the run, + and the + + durable terminal receipt (``SmrRunTerminalOutcome`` / ``smr_run_terminations``) + + is the sole remaining authority. This payload carries the terminal + fact + + in-band so an SSE consumer terminalises on the stream it is + already parsing + + instead of reconnecting against a refusal.' + properties: + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + run_id: + minLength: 1 + title: Run Id + type: string + public_state: + minLength: 1 + title: Public State + type: string + terminal_outcome: + anyOf: + - type: string + - type: 'null' + title: Terminal Outcome + stop_reason: + anyOf: + - type: string + - type: 'null' + title: Stop Reason + snapshot_missing_reason: + minLength: 1 + title: Snapshot Missing Reason + type: string + source: + const: smr_run_terminal_outcome.tombstone.runtime_stream.v1 + default: smr_run_terminal_outcome.tombstone.runtime_stream.v1 + title: Source + type: string + required: + - run_id + - public_state + - snapshot_missing_reason + title: SmrRuntimeStreamTerminalPayload + type: object SmrRuntimeStreamTimelineEventDetailPayload: additionalProperties: false properties: @@ -35604,6 +37808,12 @@ paths: title: Occurred At type: string kind: + description: Transcript event kind, e.g. message.completed, + reasoning.summary, turn.completed, tool.call.completed, + or task.completion_claimed (an accepted worker completion + claim projected from its durable claim row; payload schema + smr.task-completion-claimed.v1 with claim_id, task_key, + participant_id, text and summary). minLength: 1 title: Kind type: string @@ -35684,6 +37894,7 @@ paths: - $ref: '#/$defs/SmrRuntimeStreamSnapshotPayload' - $ref: '#/$defs/SmrTranscriptEventResponse' - $ref: '#/$defs/SmrRuntimeStreamHeartbeatPayload' + - $ref: '#/$defs/SmrRuntimeStreamTerminalPayload' title: Payload state_version: minLength: 1 @@ -37100,27 +39311,605 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /v1/delivery: - post: + /smr/inference/v1/budgets/{scope_kind}/{scope_id}: + get: tags: - - manderqueue-bridge - summary: Mq Worker Delivery - operationId: mq_worker_delivery_v1_delivery_post + - inference-budgets + summary: Get Budget + operationId: get_budget_smr_inference_v1_budgets__scope_kind___scope_id__get + parameters: + - name: scope_kind + in: path + required: true + schema: + enum: + - organization + - project + type: string + title: Scope Kind + - name: scope_id + in: path + required: true + schema: + type: string + format: uuid + title: Scope Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Budget Smr Inference V1 Budgets Scope Kind Scope + Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + put: + tags: + - inference-budgets + summary: Put Budget + operationId: put_budget_smr_inference_v1_budgets__scope_kind___scope_id__put + parameters: + - name: scope_kind + in: path + required: true + schema: + enum: + - organization + - project + type: string + title: Scope Kind + - name: scope_id + in: path + required: true + schema: + type: string + format: uuid + title: Scope Id requestBody: + required: true content: application/json: schema: - $ref: '#/components/schemas/MqDeliveryEnvelope' + $ref: '#/components/schemas/BudgetUpdateRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Put Budget Smr Inference V1 Budgets Scope Kind Scope + Id Put + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/inference/v1/budgets/execution_scopes/{kind}/{identifier}: + put: + tags: + - inference-budgets + summary: Admit Execution Budget Scope + operationId: admit_execution_budget_scope_smr_inference_v1_budgets_execution_scopes__kind___identifier__put + parameters: + - name: kind + in: path + required: true + schema: + enum: + - experiment + - deployment + type: string + title: Kind + - name: identifier + in: path + required: true + schema: + type: string + title: Identifier + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BudgetScopeAdmission' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Admit Execution Budget Scope Smr Inference V1 Budgets + Execution Scopes Kind Identifier Put + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + get: + tags: + - inference-budgets + summary: Get Execution Budget Scope + operationId: get_execution_budget_scope_smr_inference_v1_budgets_execution_scopes__kind___identifier__get + parameters: + - name: kind + in: path + required: true + schema: + type: string + title: Kind + - name: identifier + in: path + required: true + schema: + type: string + title: Identifier + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Execution Budget Scope Smr Inference V1 Budgets + Execution Scopes Kind Identifier Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/inference/v1/budgets/execution_scopes/{kind}/{identifier}/settle: + post: + tags: + - inference-budgets + summary: Settle Execution Budget Scope + operationId: settle_execution_budget_scope_smr_inference_v1_budgets_execution_scopes__kind___identifier__settle_post + parameters: + - name: kind + in: path required: true + schema: + enum: + - pool_rollout + - experiment + - deployment + - smr_run + - intern_runtime + type: string + title: Kind + - name: identifier + in: path + required: true + schema: + type: string + title: Identifier responses: '200': description: Successful Response content: application/json: schema: + type: object additionalProperties: true + title: Response Settle Execution Budget Scope Smr Inference V1 Budgets + Execution Scopes Kind Identifier Settle Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/inference/v1/budgets/provider_captures/{invocation_id}/receipt: + get: + tags: + - inference-budgets + summary: Provider Capture Receipt + operationId: provider_capture_receipt_smr_inference_v1_budgets_provider_captures__invocation_id__receipt_get + parameters: + - name: invocation_id + in: path + required: true + schema: + type: string + title: Invocation Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: type: object - title: Response Mq Worker Delivery V1 Delivery Post + additionalProperties: true + title: Response Provider Capture Receipt Smr Inference V1 Budgets + Provider Captures Invocation Id Receipt Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/inference/v1/budgets/provider_captures/{invocation_id}/publish: + post: + tags: + - inference-budgets + summary: Publish Provider Capture Receipt + operationId: publish_provider_capture_receipt_smr_inference_v1_budgets_provider_captures__invocation_id__publish_post + parameters: + - name: invocation_id + in: path + required: true + schema: + type: string + title: Invocation Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Publish Provider Capture Receipt Smr Inference V1 + Budgets Provider Captures Invocation Id Publish Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /smr/inference/v1/budgets/execution_scopes/{kind}/{identifier}/host_inventory/close: + post: + tags: + - inference-budgets + summary: Close Execution Host Inventory + operationId: close_execution_host_inventory_smr_inference_v1_budgets_execution_scopes__kind___identifier__host_inventory_close_post + parameters: + - name: kind + in: path + required: true + schema: + enum: + - smr_run + - intern_runtime + type: string + title: Kind + - name: identifier + in: path + required: true + schema: + type: string + title: Identifier + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Close Execution Host Inventory Smr Inference V1 Budgets + Execution Scopes Kind Identifier Host Inventory Close Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/prepare: + post: + tags: + - artifacts + - workshop + summary: Prepare Workshop Artifact + operationId: prepare_workshop_artifact_artifacts_v1_workshop_prepare_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopArtifactPrepareRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopArtifactPrepareResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/{publication_id}/finalize: + post: + tags: + - artifacts + - workshop + summary: Finalize Workshop Artifact + operationId: finalize_workshop_artifact_artifacts_v1_workshop__publication_id__finalize_post + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopArtifactFinalizeRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopArtifactCommittedResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/reports/prepare: + post: + tags: + - artifacts + - workshop + - reports + summary: Prepare Workshop Report + operationId: prepare_workshop_report_artifacts_v1_workshop_reports_prepare_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportPrepareRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportPrepareResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/reports/{publication_id}/finalize: + post: + tags: + - artifacts + - workshop + - reports + summary: Finalize Workshop Report + operationId: finalize_workshop_report_artifacts_v1_workshop_reports__publication_id__finalize_post + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportFinalizeRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportCommittedResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/reports/{publication_id}/audience: + put: + tags: + - artifacts + - workshop + - reports + summary: Set Workshop Report Audience + operationId: set_workshop_report_audience_artifacts_v1_workshop_reports__publication_id__audience_put + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportShareRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportShareResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - artifacts + - workshop + - reports + summary: Revoke Workshop Report Audience + operationId: revoke_workshop_report_audience_artifacts_v1_workshop_reports__publication_id__audience_delete + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + - name: receipt_digest + in: query + required: true + schema: + type: string + title: Receipt Digest + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportShareResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /artifacts/v1/workshop/reports/{publication_id}/promote: + post: + tags: + - artifacts + - workshop + - reports + summary: Promote Workshop Report + operationId: promote_workshop_report_artifacts_v1_workshop_reports__publication_id__promote_post + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportPromoteRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportPromotionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - artifacts + - workshop + - reports + summary: Unpublish Workshop Report + operationId: unpublish_workshop_report_artifacts_v1_workshop_reports__publication_id__promote_delete + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + - name: reason + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Reason + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkshopReportPromotionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /reports/v1/publications/{publication_id}: + get: + tags: + - reports + summary: Get Workshop Report Publication + operationId: get_workshop_report_publication_reports_v1_publications__publication_id__get + parameters: + - name: publication_id + in: path + required: true + schema: + type: string + title: Publication Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /reports/{slug}: + get: + tags: + - reports + - public + summary: Get Public Workshop Report + operationId: get_public_workshop_report_reports__slug__get + parameters: + - name: slug + in: path + required: true + schema: + type: string + title: Slug + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} '422': description: Validation Error content: @@ -40732,187 +43521,6 @@ paths: application/json: schema: $ref: '#/components/schemas/GeloLaunchPromoStatusResponse' - /s/{route_token}/{path}: - get: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - post: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get__post__1 - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - put: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get__put__2 - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - patch: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get__patch__3 - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - delete: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get__delete__4 - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - options: - tags: - - synthtunnel-public - summary: Synthtunnel Gateway - operationId: synthtunnel_gateway_s__route_token___path__get__options__5 - parameters: - - name: route_token - in: path - required: true - schema: - type: string - title: Route Token - - name: path - in: path - required: true - schema: - type: string - title: Path - responses: - '200': - description: Successful Response - content: - application/json: - schema: {} - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' /api/v1/sessions: get: tags: @@ -41883,6 +44491,47 @@ paths: application/json: schema: $ref: '#/components/schemas/FlushSSEStreamsResponse' + /api/v1/admin/qa/cleanup-org: + post: + tags: + - admin + summary: Qa Cleanup Org + description: "Terminally clean up a disposable QA fixture org.\n\nAdmin-key\ + \ gated (``Authorization: Bearer ``). Given a QA org\nid this\ + \ revokes all of the org's API keys (``is_active=false``), suspends\nthe org\ + \ (``org_access_control.access_tier='suspended'`` \u2014 the terminal org\n\ + state), and releases every unreleased promotion grant so it stops counting\n\ + against capped campaigns (e.g. workshop_starter_launch_202608 with 100\nslots).\n\ + \nSafety: refuses to touch orgs that do not look like QA fixtures. An org\n\ + qualifies when its (Clerk display) name matches the QA fixture shapes\n``Workshop\ + \ E2E `` / ``Workshop August Promo `` (or the\nslug form ``workshop-august-promo-*``),\ + \ or when every member email is a\nClerk test address (contains ``+clerk_test``).\ + \ Anything\nelse requires ``force=true`` plus ``confirm_org_name`` equal to\ + \ the org's\nexact name. ``dry_run`` defaults to true and reports the plan\ + \ without\nwriting. Every run (dry or live) is logged with the full action\ + \ list." + operationId: qa_cleanup_org_api_v1_admin_qa_cleanup_org_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QaCleanupOrgRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/QaCleanupOrgResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + security: + - HTTPBearer: [] /api/v1/admin/feature-flags/{org_id}/{feature}: get: tags: @@ -42784,6 +45433,186 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/deployments/{task_id}: + get: + tags: + - container-pools + - container-pools + summary: Get Deployment + operationId: get_deployment_v1_pools__pool_id__deployments__task_id__get + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: task_id + in: path + required: true + schema: + type: string + title: Task Id + - name: project_id + in: query + required: true + schema: + type: string + format: uuid + title: Project Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Deployment V1 Pools Pool Id Deployments Task + Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/deployments/{task_id}/operations: + post: + tags: + - container-pools + - container-pools + summary: Mutate Deployment + operationId: mutate_deployment_v1_pools__pool_id__deployments__task_id__operations_post + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: task_id + in: path + required: true + schema: + type: string + title: Task Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DeploymentOperationRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Mutate Deployment V1 Pools Pool Id Deployments Task + Id Operations Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + get: + tags: + - container-pools + - container-pools + summary: Find Deployment Operation + description: Reconcile an uncertain mutation without repeating its provider + action. + operationId: find_deployment_operation_v1_pools__pool_id__deployments__task_id__operations_get + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: task_id + in: path + required: true + schema: + type: string + title: Task Id + - name: project_id + in: query + required: true + schema: + type: string + format: uuid + title: Project Id + - name: idempotency_key + in: query + required: true + schema: + type: string + minLength: 1 + maxLength: 256 + title: Idempotency Key + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Find Deployment Operation V1 Pools Pool Id Deployments Task + Id Operations Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/deployment-operations/{operation_id}: + get: + tags: + - container-pools + - container-pools + summary: Get Deployment Operation + operationId: get_deployment_operation_v1_pools__pool_id__deployment_operations__operation_id__get + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: operation_id + in: path + required: true + schema: + type: string + title: Operation Id + - name: project_id + in: query + required: true + schema: + type: string + format: uuid + title: Project Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: object + additionalProperties: true + title: Response Get Deployment Operation V1 Pools Pool Id Deployment + Operations Operation Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /v1/rollouts: get: tags: @@ -42919,6 +45748,31 @@ paths: schema: type: string title: Rollout Id + - name: after_sequence + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: limit + in: query + required: false + schema: + type: integer + maximum: 1000 + minimum: 1 + default: 200 + title: Limit + - name: format + in: query + required: false + schema: + type: string + pattern: ^(sse|json)$ + default: sse + title: Format responses: '200': description: Successful Response @@ -43860,6 +46714,31 @@ paths: schema: type: string title: Rollout Id + - name: after_sequence + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + title: After Sequence + - name: limit + in: query + required: false + schema: + type: integer + maximum: 1000 + minimum: 1 + default: 200 + title: Limit + - name: format + in: query + required: false + schema: + type: string + pattern: ^(sse|json)$ + default: sse + title: Format responses: '200': description: Successful Response @@ -44259,6 +47138,14 @@ paths: schema: type: string title: Release Id + - name: expected_release_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Expected Release Id responses: '200': description: Successful Response @@ -44296,6 +47183,14 @@ paths: schema: type: string title: Release Id + - name: expected_release_id + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Expected Release Id responses: '200': description: Successful Response @@ -44466,6 +47361,235 @@ paths: content: application/json: schema: {} + /v1/pools/{pool_id}/rollouts/{rollout_id}/native_events: + post: + tags: + - container-pools + summary: Ingest Rollout Native Events V1 + operationId: ingest_rollout_native_events_v1_v1_pools__pool_id__rollouts__rollout_id__native_events_post + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: rollout_id + in: path + required: true + schema: + type: string + title: Rollout Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/container_leases/{lease_id}/sessions: + post: + tags: + - container-pools + summary: Bind Interactive Lease Session + operationId: bind_interactive_lease_session_v1_container_leases__lease_id__sessions_post + parameters: + - name: lease_id + in: path + required: true + schema: + type: string + title: Lease Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/container_leases/{lease_id}/sessions/{session_id}/{operation}: + post: + tags: + - container-pools + summary: Execute Interactive Lease Action + operationId: execute_interactive_lease_action_v1_container_leases__lease_id__sessions__session_id___operation__post + parameters: + - name: lease_id + in: path + required: true + schema: + type: string + title: Lease Id + - name: session_id + in: path + required: true + schema: + type: string + title: Session Id + - name: operation + in: path + required: true + schema: + type: string + title: Operation + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/container_leases/{lease_id}/interactive: + get: + tags: + - container-pools + summary: Get Interactive Lease Binding + operationId: get_interactive_lease_binding_v1_container_leases__lease_id__interactive_get + parameters: + - name: lease_id + in: path + required: true + schema: + type: string + title: Lease Id + - name: task_id + in: query + required: true + schema: + type: string + title: Task Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/rollouts/{rollout_id}/trace_archives: + post: + tags: + - container-pools + summary: Attach Rollout Trace Archive + operationId: attach_rollout_trace_archive_v1_pools__pool_id__rollouts__rollout_id__trace_archives_post + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: rollout_id + in: path + required: true + schema: + type: string + title: Rollout Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/rollouts/{rollout_id}/native_agent_captures/{capture_id}: + get: + tags: + - container-pools + summary: Read Native Agent Capture V1 + operationId: read_native_agent_capture_v1_v1_pools__pool_id__rollouts__rollout_id__native_agent_captures__capture_id__get + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: rollout_id + in: path + required: true + schema: + type: string + title: Rollout Id + - name: capture_id + in: path + required: true + schema: + type: string + title: Capture Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/pools/{pool_id}/rollouts/{rollout_id}/native_agent_captures/{capture_id}/publish: + post: + tags: + - container-pools + summary: Publish Native Agent Capture V1 + operationId: publish_native_agent_capture_v1_v1_pools__pool_id__rollouts__rollout_id__native_agent_captures__capture_id__publish_post + parameters: + - name: pool_id + in: path + required: true + schema: + type: string + title: Pool Id + - name: rollout_id + in: path + required: true + schema: + type: string + title: Rollout Id + - name: capture_id + in: path + required: true + schema: + type: string + title: Capture Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /v1/pools/{pool_id}/rollouts/{rollout_id}/inference/v1/chat/completions: post: tags: @@ -45512,6 +48636,68 @@ components: - is_active title: ApiKeyRedacted description: A key row with the secret redacted. Safe to list. + ArtifactObjectDeclaration: + properties: + logical_path: + type: string + maxLength: 1024 + minLength: 1 + title: Logical Path + digest_sha256: + type: string + pattern: ^[0-9a-f]{64}$ + title: Digest Sha256 + size_bytes: + type: integer + maximum: 9.223372036854776e+18 + minimum: 0.0 + title: Size Bytes + media_type: + type: string + maxLength: 255 + minLength: 1 + title: Media Type + additionalProperties: false + type: object + required: + - logical_path + - digest_sha256 + - size_bytes + - media_type + title: ArtifactObjectDeclaration + description: One immutable manifest object addressed only by logical path and + digest. + ArtifactUploadTarget: + properties: + logical_path: + type: string + maxLength: 1024 + minLength: 1 + title: Logical Path + digest_sha256: + type: string + pattern: ^[0-9a-f]{64}$ + title: Digest Sha256 + upload_url: + type: string + maxLength: 16384 + minLength: 1 + title: Upload Url + required_headers: + additionalProperties: + type: string + type: object + maxProperties: 16 + title: Required Headers + additionalProperties: false + type: object + required: + - logical_path + - digest_sha256 + - upload_url + - required_headers + title: ArtifactUploadTarget + description: Create-only upload instructions without a provider storage locator. AsyncBlockerHandoffContextV1: properties: schema_version: @@ -45706,6 +48892,11 @@ components: title: AsyncBlockerResolveResponse AsyncBlockerResponse: properties: + async_assignment_id: + anyOf: + - type: string + - type: 'null' + title: Async Assignment Id schema_version: type: string const: smr.intern-async-blocker.v1 @@ -45898,6 +49089,81 @@ components: - effort_id - status title: AsyncEffortWorkSummary + AsyncHandoffModelRequestV1: + properties: + command_id: + type: string + maxLength: 512 + minLength: 1 + title: Command Id + idempotency_key: + type: string + maxLength: 512 + minLength: 1 + title: Idempotency Key + expected_generation: + type: integer + minimum: 0.0 + title: Expected Generation + summary: + type: string + maxLength: 4000 + minLength: 1 + title: Summary + agent_config: + $ref: '#/components/schemas/AgentConfigV1' + evidence_references: + items: + $ref: '#/components/schemas/ProducedResourceReferenceV1' + type: array + maxItems: 128 + title: Evidence References + default: [] + require_review: + type: boolean + title: Require Review + default: false + additionalProperties: false + type: object + required: + - command_id + - idempotency_key + - expected_generation + - summary + - agent_config + title: AsyncHandoffModelRequestV1 + description: 'Product verb: change Async model/effort via spine handoff (no + MT id).' + AsyncHandoffReviewRequestV1: + properties: + idempotency_key: + type: string + maxLength: 512 + minLength: 1 + title: Idempotency Key + summary: + type: string + maxLength: 4000 + minLength: 1 + title: Summary + agent_config: + $ref: '#/components/schemas/AgentConfigV1' + evidence_references: + items: + $ref: '#/components/schemas/ProducedResourceReferenceV1' + type: array + maxItems: 128 + title: Evidence References + default: [] + additionalProperties: false + type: object + required: + - idempotency_key + - summary + - agent_config + title: AsyncHandoffReviewRequestV1 + description: 'Attended seal: park a model/effort switch at needs_review (no + MT id).' AsyncInstructionCommandRequest: properties: command_id: @@ -46024,8 +49290,8 @@ components: objective: type: string maxLength: 20000 - minLength: 1 title: Objective + default: '' idempotency_key: type: string maxLength: 512 @@ -46045,10 +49311,17 @@ components: minimum: 0.0 title: Factory Ready Wait Seconds default: 0 + index_enabled: + type: boolean + title: Index Enabled + default: false + index_draft_enabled: + type: boolean + title: Index Draft Enabled + default: false additionalProperties: false type: object required: - - objective - idempotency_key title: AsyncRuntimeEnsureRequest AsyncRuntimeHostLease: @@ -46062,6 +49335,12 @@ components: - type: string - type: 'null' title: Lease Id + generation: + anyOf: + - type: integer + minimum: 1.0 + - type: 'null' + title: Generation sticky_key: anyOf: - type: string @@ -46254,11 +49533,30 @@ components: temporal_workflow_id: type: string title: Temporal Workflow Id + phase: + $ref: '#/components/schemas/RuntimePhaseWire' + default: lifecycle:not_started + stop_reason: + anyOf: + - $ref: '#/components/schemas/StopReason' + - type: 'null' + resume: + anyOf: + - $ref: '#/components/schemas/ResumeConditionResponse' + - type: 'null' + active_effort_id: + anyOf: + - type: string + - type: 'null' + title: Active Effort Id + outcome: + anyOf: + - $ref: '#/components/schemas/RuntimeOutcome' + - type: 'null' leave_safe: type: boolean - const: true title: Leave Safe - default: true + default: false created_at: type: string format: date-time @@ -46380,6 +49678,18 @@ components: - completed - failed title: AsyncStatus + description: 'Org-runtime lane status (legacy flat enum; prefer derived ``phase``). + + + DEPRECATED / never assigned by the reducer (kept for DB CHECK + OpenAPI + + compat until a deliberate drop): ``CHECKPOINTING``, ``AWAITING_INPUT`` + + (Effort-level only now), ``CANCELLING``, ``FAILED``. Prefer + + ``packages.intern.async_.phase.derive_phase`` for new readers. + + # See: INTERN_ASYNC_RUNTIME_PHASE_REFACTOR_HANDOFF_2026-08-08.md' AutumnBalanceProxyResponse: properties: org_id: @@ -46606,12 +49916,12 @@ components: title: Summary html: type: string - format: binary + contentMediaType: application/octet-stream title: Html preview_png: anyOf: - type: string - format: binary + contentMediaType: application/octet-stream - type: 'null' title: Preview Png visual_kind: @@ -46652,12 +49962,12 @@ components: title: Summary html: type: string - format: binary + contentMediaType: application/octet-stream title: Html preview_png: anyOf: - type: string - format: binary + contentMediaType: application/octet-stream - type: 'null' title: Preview Png visual_kind: @@ -46703,12 +50013,12 @@ components: title: Summary html: type: string - format: binary + contentMediaType: application/octet-stream title: Html preview_png: anyOf: - type: string - format: binary + contentMediaType: application/octet-stream - type: 'null' title: Preview Png visual_kind: @@ -46743,7 +50053,7 @@ components: properties: file: type: string - format: binary + contentMediaType: application/octet-stream title: File artifact_type: type: string @@ -46794,6 +50104,49 @@ components: required: - token title: BrokeredRepositoryToken + BudgetScopeAdmission: + properties: + project_id: + type: string + maxLength: 128 + minLength: 1 + title: Project Id + limits: + additionalProperties: true + type: object + maxProperties: 16 + title: Limits + parent: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Parent + additionalProperties: false + type: object + required: + - project_id + - limits + title: BudgetScopeAdmission + BudgetUpdateRequest: + properties: + expected_revision: + type: integer + maximum: 2147483646.0 + minimum: 0.0 + title: Expected Revision + limits: + additionalProperties: true + type: object + maxProperties: 16 + title: Limits + additionalProperties: false + type: object + required: + - expected_revision + - limits + title: BudgetUpdateRequest CancelSubscriptionResponse: properties: cancelled_at: @@ -46943,6 +50296,12 @@ components: - type: 'null' title: N default: 1 + provider: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Provider stop: anyOf: - items: @@ -46991,6 +50350,20 @@ components: - messages title: ChatCompletionRequest description: OpenAI Chat Completion request format. + CheckoutSessionBody: + properties: + plan: + type: string + enum: + - starter + - pro + title: Plan + description: Desktop tier to purchase. + type: object + required: + - plan + title: CheckoutSessionBody + description: Desktop speaks tiers, not provider product ids. CheckoutSessionRequest: properties: plan_type: @@ -47026,6 +50399,42 @@ components: - session_id title: CheckoutSessionResponse description: Response with checkout session URL. + CheckpointChatRequest: + properties: + messages: + items: + $ref: '#/components/schemas/SavedLoraSampleMessage' + type: array + maxItems: 64 + minItems: 1 + title: Messages + max_tokens: + type: integer + maximum: 2048.0 + minimum: 1.0 + title: Max Tokens + default: 64 + temperature: + anyOf: + - type: number + maximum: 2.0 + minimum: 0.0 + - type: 'null' + title: Temperature + execute: + type: boolean + title: Execute + default: false + type: object + required: + - messages + title: CheckpointChatRequest + description: 'One bounded chat completion from a saved sampler checkpoint. + + + ``execute=false`` (default) only binds the target and spends nothing. + + ``execute=true`` is a paid provider sample from the exact checkpoint.' ClaimEvidenceInput: properties: relation: @@ -47975,845 +51384,280 @@ components: title: Deployment Id vm_name: type: string - title: Vm Name - repository: - type: string - title: Repository - path: - type: string - title: Path - branch: - type: string - title: Branch - source_commit_sha: - type: string - title: Source Commit Sha - detached: - type: boolean - title: Detached - clean: - type: boolean - title: Clean - type: object - required: - - schema_version - - deployment_id - - vm_name - - repository - - path - - branch - - source_commit_sha - - detached - - clean - title: CloudDeploymentWorkspaceMaterializeResponse - CloudDeploymentWorkspaceRepository: - properties: - repository: - type: string - title: Repository - path: - type: string - title: Path - remote_repo: - type: string - title: Remote Repo - declared_branch: - type: string - title: Declared Branch - declared_source_commit_sha: - type: string - title: Declared Source Commit Sha - authority: - type: string - title: Authority - live: - $ref: '#/components/schemas/CloudDeploymentWorkspaceLiveState' - type: object - required: - - repository - - path - - remote_repo - - declared_branch - - declared_source_commit_sha - - authority - - live - title: CloudDeploymentWorkspaceRepository - CloudDeploymentWorkspaceResponse: - properties: - schema_version: - type: string - const: cloud-deployment-workspace-v1 - title: Schema Version - deployment_id: - type: string - title: Deployment Id - vm_name: - type: string - title: Vm Name - workspace_root: - type: string - title: Workspace Root - repositories: - items: - $ref: '#/components/schemas/CloudDeploymentWorkspaceRepository' - type: array - title: Repositories - type: object - required: - - schema_version - - deployment_id - - vm_name - - workspace_root - - repositories - title: CloudDeploymentWorkspaceResponse - CompleteTrainedModelAdapterUploadBody: - properties: - bucket: - type: string - title: Bucket - key: - type: string - title: Key - adapter_size_bytes: - type: integer - exclusiveMinimum: 0.0 - title: Adapter Size Bytes - metadata_patch: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Metadata Patch - type: object - required: - - bucket - - key - - adapter_size_bytes - title: CompleteTrainedModelAdapterUploadBody - ContainerEvalPackageBody: - properties: - kind: - type: string - title: Kind - name: - type: string - title: Name - version: - anyOf: - - type: string - - type: 'null' - title: Version - artifact_id: - anyOf: - - type: string - - type: 'null' - title: Artifact Id - storage_uri: - anyOf: - - type: string - - type: 'null' - title: Storage Uri - archive_size_bytes: - anyOf: - - type: integer - - type: 'null' - title: Archive Size Bytes - manifest: - additionalProperties: true - type: object - title: Manifest - metadata: - additionalProperties: true - type: object - title: Metadata - type: object - required: - - kind - - name - title: ContainerEvalPackageBody - ContainerEvalPackageResponse: - properties: - package_id: - type: string - title: Package Id - work_product_id: - anyOf: - - type: string - - type: 'null' - title: Work Product Id - org_id: - type: string - title: Org Id - project_id: - type: string - title: Project Id - run_id: - type: string - title: Run Id - kind: - type: string - title: Kind - name: - type: string - title: Name - version: - anyOf: - - type: string - - type: 'null' - title: Version - status: - type: string - title: Status - validation_status: - type: string - title: Validation Status - artifact_id: - anyOf: - - type: string - - type: 'null' - title: Artifact Id - storage_uri: - anyOf: - - type: string - - type: 'null' - title: Storage Uri - archive_size_bytes: - anyOf: - - type: integer - - type: 'null' - title: Archive Size Bytes - manifest: - additionalProperties: true - type: object - title: Manifest - validation: - additionalProperties: true - type: object - title: Validation - metadata: - additionalProperties: true - type: object - title: Metadata - created_at: - anyOf: - - type: string - - type: 'null' - title: Created At - updated_at: - anyOf: - - type: string - - type: 'null' - title: Updated At - deleted_at: - anyOf: - - type: string - - type: 'null' - title: Deleted At - type: object - required: - - package_id - - org_id - - project_id - - run_id - - kind - - name - - status - - validation_status - title: ContainerEvalPackageResponse - ContentDeclaration: - properties: - logical_path: - type: string - maxLength: 1024 - minLength: 1 - title: Logical Path - content_digest: - type: string - maxLength: 71 - minLength: 64 - pattern: ^(?:sha256:)?[0-9a-f]{64}$ - title: Content Digest - size_bytes: - type: integer - minimum: 0.0 - title: Size Bytes - media_type: - type: string - maxLength: 255 - minLength: 1 - title: Media Type - type: object - required: - - logical_path - - content_digest - - size_bytes - - media_type - title: ContentDeclaration - CraftaxArtifactRef: - properties: - uri: - type: string - maxLength: 2048 - minLength: 1 - title: Uri - sha256: - type: string - pattern: ^[0-9a-f]{64}$ - title: Sha256 - mime_type: - anyOf: - - type: string - maxLength: 200 - - type: 'null' - title: Mime Type - additionalProperties: false - type: object - required: - - uri - - sha256 - title: CraftaxArtifactRef - description: Immutable artifact identity; URIs never contain inline credentials. - CraftaxCampaignBinding: - properties: - schema_version: - type: string - const: intern.craftax-campaign-binding.v1 - title: Schema Version - default: intern.craftax-campaign-binding.v1 - campaign_id: - type: string - maxLength: 512 - minLength: 1 - title: Campaign Id - factory_id: - type: string - maxLength: 512 - minLength: 1 - title: Factory Id - project_id: - type: string - maxLength: 512 - minLength: 1 - title: Project Id - effort_id: - type: string - maxLength: 512 - minLength: 1 - title: Effort Id - run_id: - anyOf: - - type: string - maxLength: 512 - - type: 'null' - title: Run Id - environment_version: - type: string - maxLength: 512 - minLength: 1 - title: Environment Version - harness_version: - type: string - maxLength: 512 - minLength: 1 - title: Harness Version - baseline_policy: - $ref: '#/components/schemas/CraftaxArtifactRef' - actor_model: - type: string - const: luna - title: Actor Model - default: luna - actor_auth: - type: string - const: chatgpt - title: Actor Auth - default: chatgpt - policy_model: - type: string - const: luna - title: Policy Model - default: luna - policy_auth: - type: string - enum: - - openai_api - - openrouter - title: Policy Auth - default: openai_api - evaluation_plan: - $ref: '#/components/schemas/CraftaxEvaluationPlan' - additionalProperties: false - type: object - required: - - campaign_id - - factory_id - - project_id - - effort_id - - environment_version - - harness_version - - baseline_policy - - evaluation_plan - title: CraftaxCampaignBinding - description: Exact authority and artifact pins for one Craftax research campaign. - CraftaxCampaignInstructionRequest: - properties: - command_id: - type: string - maxLength: 512 - minLength: 1 - title: Command Id - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - expected_generation: - type: integer - minimum: 0.0 - title: Expected Generation - objective: - type: string - maxLength: 20000 - minLength: 1 - title: Objective - stage: - type: string - enum: - - P0_baseline - - P1_early_survival - - P2_crafting_progression - - P3_exploration - title: Stage - binding: - $ref: '#/components/schemas/CraftaxCampaignBinding' - additionalProperties: false - type: object - required: - - command_id - - idempotency_key - - expected_generation - - objective - - stage - - binding - title: CraftaxCampaignInstructionRequest - description: Typed alias that admits one campaign instruction to Async Intern. - CraftaxEvaluationPlan: - properties: - schema_version: - type: string - const: intern.craftax-evaluation-plan.v1 - title: Schema Version - default: intern.craftax-evaluation-plan.v1 - evaluation_id: - type: string - maxLength: 512 - minLength: 1 - title: Evaluation Id - baseline_candidate_id: - type: string - maxLength: 512 - minLength: 1 - title: Baseline Candidate Id - treatment_candidate_id: - anyOf: - - type: string - maxLength: 512 - - type: 'null' - title: Treatment Candidate Id - development_seeds: - items: - type: integer - type: array - maxItems: 10000 - minItems: 1 - title: Development Seeds - held_out_seeds: - items: - type: integer - type: array - maxItems: 10000 - minItems: 1 - title: Held Out Seeds - rollouts_per_seed: - type: integer - maximum: 10000.0 - minimum: 1.0 - title: Rollouts Per Seed - maximum_policy_tokens: - type: integer - minimum: 1.0 - title: Maximum Policy Tokens - maximum_cost_cents: - type: integer - minimum: 0.0 - title: Maximum Cost Cents - primary_gate: - $ref: '#/components/schemas/CraftaxMetricGate' - regression_gates: - items: - $ref: '#/components/schemas/CraftaxRegressionGate' - type: array - maxItems: 64 - title: Regression Gates - early_stop_conditions: - items: - type: string - type: array - maxItems: 64 - title: Early Stop Conditions - additionalProperties: false - type: object - required: - - evaluation_id - - baseline_candidate_id - - development_seeds - - held_out_seeds - - rollouts_per_seed - - maximum_policy_tokens - - maximum_cost_cents - - primary_gate - title: CraftaxEvaluationPlan - description: Preregistered comparison; seed membership is fixed before treatment. - CraftaxEvaluationResult: - properties: - schema_version: - type: string - const: intern.craftax-evaluation-result.v1 - title: Schema Version - evaluation_id: - type: string - maxLength: 512 - minLength: 1 - title: Evaluation Id - baseline_candidate_id: - type: string - maxLength: 512 - minLength: 1 - title: Baseline Candidate Id - treatment_candidate_id: - type: string - maxLength: 512 - minLength: 1 - title: Treatment Candidate Id - held_out: - type: boolean - title: Held Out - primary_metric: - type: string - maxLength: 200 - minLength: 1 - title: Primary Metric - baseline_value: - type: number - title: Baseline Value - treatment_value: - type: number - title: Treatment Value - absolute_uplift: - type: number - title: Absolute Uplift - relative_uplift: - anyOf: - - type: number - - type: 'null' - title: Relative Uplift - per_seed: - items: - $ref: '#/components/schemas/CraftaxSeedObservation' - type: array - maxItems: 10000 - minItems: 1 - title: Per Seed - regressions: - items: - type: string - type: array - maxItems: 64 - title: Regressions - actor_usage: - $ref: '#/components/schemas/CraftaxUsage' - policy_usage: - $ref: '#/components/schemas/CraftaxUsage' - verdict: - type: string - enum: - - pass - - fail - - indeterminate - title: Verdict - evidence_refs: - items: - $ref: '#/components/schemas/CraftaxArtifactRef' - type: array - maxItems: 128 - title: Evidence Refs - additionalProperties: false + title: Vm Name + repository: + type: string + title: Repository + path: + type: string + title: Path + branch: + type: string + title: Branch + source_commit_sha: + type: string + title: Source Commit Sha + detached: + type: boolean + title: Detached + clean: + type: boolean + title: Clean type: object required: - schema_version - - evaluation_id - - baseline_candidate_id - - treatment_candidate_id - - held_out - - primary_metric - - baseline_value - - treatment_value - - absolute_uplift - - per_seed - - actor_usage - - policy_usage - - verdict - title: CraftaxEvaluationResult - CraftaxEvidencePublicationRequest: + - deployment_id + - vm_name + - repository + - path + - branch + - source_commit_sha + - detached + - clean + title: CloudDeploymentWorkspaceMaterializeResponse + CloudDeploymentWorkspaceRepository: properties: - idempotency_key: + repository: type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - factory_id: + title: Repository + path: type: string - maxLength: 512 - minLength: 1 - title: Factory Id - project_id: + title: Path + remote_repo: type: string - maxLength: 512 - minLength: 1 - title: Project Id - effort_id: + title: Remote Repo + declared_branch: type: string - maxLength: 512 - minLength: 1 - title: Effort Id - run_id: + title: Declared Branch + declared_source_commit_sha: type: string - maxLength: 512 - minLength: 1 - title: Run Id - record: - oneOf: - - $ref: '#/components/schemas/CraftaxPolicyCandidate' - - $ref: '#/components/schemas/CraftaxEvaluationPlan' - - $ref: '#/components/schemas/CraftaxEvaluationResult' - - $ref: '#/components/schemas/CraftaxVisualArtifact' - - $ref: '#/components/schemas/CraftaxPolicyDecision' - title: Record - discriminator: - propertyName: schema_version - mapping: - intern.craftax-evaluation-plan.v1: '#/components/schemas/CraftaxEvaluationPlan' - intern.craftax-evaluation-result.v1: '#/components/schemas/CraftaxEvaluationResult' - intern.craftax-policy-candidate.v1: '#/components/schemas/CraftaxPolicyCandidate' - intern.craftax-policy-decision.v1: '#/components/schemas/CraftaxPolicyDecision' - intern.craftax-visual-artifact.v1: '#/components/schemas/CraftaxVisualArtifact' - additionalProperties: false + title: Declared Source Commit Sha + authority: + type: string + title: Authority + live: + $ref: '#/components/schemas/CloudDeploymentWorkspaceLiveState' type: object required: - - idempotency_key - - factory_id - - project_id - - effort_id - - run_id - - record - title: CraftaxEvidencePublicationRequest - CraftaxEvidencePublicationResponse: + - repository + - path + - remote_repo + - declared_branch + - declared_source_commit_sha + - authority + - live + title: CloudDeploymentWorkspaceRepository + CloudDeploymentWorkspaceResponse: properties: schema_version: type: string - const: smr.intern-craftax-evidence-publication.v1 + const: cloud-deployment-workspace-v1 title: Schema Version - default: smr.intern-craftax-evidence-publication.v1 - entry_id: + deployment_id: type: string - title: Entry Id - record_schema_version: + title: Deployment Id + vm_name: type: string - title: Record Schema Version - record_identity: + title: Vm Name + workspace_root: type: string - title: Record Identity - link_ids: + title: Workspace Root + repositories: items: - type: string + $ref: '#/components/schemas/CloudDeploymentWorkspaceRepository' type: array - title: Link Ids - replayed: - type: boolean - title: Replayed - additionalProperties: false + title: Repositories type: object required: - - entry_id - - record_schema_version - - record_identity - - replayed - title: CraftaxEvidencePublicationResponse - CraftaxMetricGate: + - schema_version + - deployment_id + - vm_name + - workspace_root + - repositories + title: CloudDeploymentWorkspaceResponse + CompleteTrainedModelAdapterUploadBody: properties: - metric: + bucket: type: string - maxLength: 200 - minLength: 1 - title: Metric - direction: + title: Bucket + key: type: string - enum: - - maximize - - minimize - title: Direction - minimum_absolute_uplift: - type: number - title: Minimum Absolute Uplift - default: 0.0 - minimum_relative_uplift: - type: number - title: Minimum Relative Uplift - default: 0.0 - additionalProperties: false + title: Key + adapter_size_bytes: + type: integer + exclusiveMinimum: 0.0 + title: Adapter Size Bytes + metadata_patch: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata Patch type: object required: - - metric - - direction - title: CraftaxMetricGate - CraftaxPolicyCandidate: + - bucket + - key + - adapter_size_bytes + title: CompleteTrainedModelAdapterUploadBody + ContainerEvalPackageBody: properties: - schema_version: - type: string - const: intern.craftax-policy-candidate.v1 - title: Schema Version - candidate_id: + kind: type: string - maxLength: 512 - minLength: 1 - title: Candidate Id - campaign_id: + title: Kind + name: type: string - maxLength: 512 - minLength: 1 - title: Campaign Id - parent_candidate_id: + title: Name + version: anyOf: - type: string - maxLength: 512 - type: 'null' - title: Parent Candidate Id - stage: - type: string - maxLength: 200 - minLength: 1 - title: Stage - hypothesis: - type: string - maxLength: 20000 - minLength: 1 - title: Hypothesis - failure_mode: - type: string - maxLength: 500 - minLength: 1 - title: Failure Mode - change_summary: - type: string - maxLength: 20000 - minLength: 1 - title: Change Summary - policy_model: - type: string - const: luna - title: Policy Model - policy_artifact_ref: - $ref: '#/components/schemas/CraftaxArtifactRef' - prompt_or_program_ref: - $ref: '#/components/schemas/CraftaxArtifactRef' - config_ref: - $ref: '#/components/schemas/CraftaxArtifactRef' - additionalProperties: false + title: Version + artifact_id: + anyOf: + - type: string + - type: 'null' + title: Artifact Id + storage_uri: + anyOf: + - type: string + - type: 'null' + title: Storage Uri + archive_size_bytes: + anyOf: + - type: integer + - type: 'null' + title: Archive Size Bytes + manifest: + additionalProperties: true + type: object + title: Manifest + metadata: + additionalProperties: true + type: object + title: Metadata type: object required: - - schema_version - - candidate_id - - campaign_id - - stage - - hypothesis - - failure_mode - - change_summary - - policy_model - - policy_artifact_ref - - prompt_or_program_ref - - config_ref - title: CraftaxPolicyCandidate - CraftaxPolicyDecision: + - kind + - name + title: ContainerEvalPackageBody + ContainerEvalPackageResponse: properties: - schema_version: + package_id: type: string - const: intern.craftax-policy-decision.v1 - title: Schema Version - decision_id: + title: Package Id + work_product_id: + anyOf: + - type: string + - type: 'null' + title: Work Product Id + org_id: type: string - maxLength: 512 - minLength: 1 - title: Decision Id - campaign_id: + title: Org Id + project_id: type: string - maxLength: 512 - minLength: 1 - title: Campaign Id - candidate_id: + title: Project Id + run_id: type: string - maxLength: 512 - minLength: 1 - title: Candidate Id - evaluation_id: + title: Run Id + kind: type: string - maxLength: 512 - minLength: 1 - title: Evaluation Id - decision: + title: Kind + name: type: string - enum: - - promote - - reject - - revise - - indeterminate - title: Decision - rationale: + title: Name + version: + anyOf: + - type: string + - type: 'null' + title: Version + status: type: string - maxLength: 20000 - minLength: 1 - title: Rationale - factory_selection_required: - type: boolean - title: Factory Selection Required - default: true - additionalProperties: false + title: Status + validation_status: + type: string + title: Validation Status + artifact_id: + anyOf: + - type: string + - type: 'null' + title: Artifact Id + storage_uri: + anyOf: + - type: string + - type: 'null' + title: Storage Uri + archive_size_bytes: + anyOf: + - type: integer + - type: 'null' + title: Archive Size Bytes + manifest: + additionalProperties: true + type: object + title: Manifest + validation: + additionalProperties: true + type: object + title: Validation + metadata: + additionalProperties: true + type: object + title: Metadata + created_at: + anyOf: + - type: string + - type: 'null' + title: Created At + updated_at: + anyOf: + - type: string + - type: 'null' + title: Updated At + deleted_at: + anyOf: + - type: string + - type: 'null' + title: Deleted At type: object required: - - schema_version - - decision_id - - campaign_id - - candidate_id - - evaluation_id - - decision - - rationale - title: CraftaxPolicyDecision - CraftaxRegressionGate: + - package_id + - org_id + - project_id + - run_id + - kind + - name + - status + - validation_status + title: ContainerEvalPackageResponse + ContentDeclaration: properties: - metric: + logical_path: type: string - maxLength: 200 + maxLength: 1024 minLength: 1 - title: Metric - maximum_regression: - type: number + title: Logical Path + content_digest: + type: string + maxLength: 71 + minLength: 64 + pattern: ^(?:sha256:)?[0-9a-f]{64}$ + title: Content Digest + size_bytes: + type: integer minimum: 0.0 - title: Maximum Regression - additionalProperties: false + title: Size Bytes + media_type: + type: string + maxLength: 255 + minLength: 1 + title: Media Type type: object required: - - metric - - maximum_regression - title: CraftaxRegressionGate + - logical_path + - content_digest + - size_bytes + - media_type + title: ContentDeclaration CraftaxScorerImageReleaseDeclaration: properties: archive_sha256: @@ -48920,110 +51764,6 @@ components: - inspection - declaration title: CraftaxScorerImageReleaseResponse - CraftaxSeedObservation: - properties: - seed: - type: integer - title: Seed - baseline: - additionalProperties: - type: number - type: object - title: Baseline - treatment: - additionalProperties: - type: number - type: object - title: Treatment - additionalProperties: false - type: object - required: - - seed - - baseline - - treatment - title: CraftaxSeedObservation - CraftaxUsage: - properties: - input_tokens: - type: integer - minimum: 0.0 - title: Input Tokens - default: 0 - output_tokens: - type: integer - minimum: 0.0 - title: Output Tokens - default: 0 - cost_cents: - type: number - minimum: 0.0 - title: Cost Cents - default: 0 - latency_milliseconds: - type: integer - minimum: 0.0 - title: Latency Milliseconds - default: 0 - additionalProperties: false - type: object - title: CraftaxUsage - CraftaxVisualArtifact: - properties: - schema_version: - type: string - const: intern.craftax-visual-artifact.v1 - title: Schema Version - artifact_id: - type: string - maxLength: 512 - minLength: 1 - title: Artifact Id - evaluation_id: - type: string - maxLength: 512 - minLength: 1 - title: Evaluation Id - candidate_id: - type: string - maxLength: 512 - minLength: 1 - title: Candidate Id - visual_kind: - type: string - enum: - - reward_distribution - - survival_curve - - death_causes - - progression - - inventory_trajectory - - action_distribution - - visitation_heatmap - - candidate_card - title: Visual Kind - seed_scope: - type: string - enum: - - development - - held_out - title: Seed Scope - artifact_ref: - $ref: '#/components/schemas/CraftaxArtifactRef' - source_result_sha256: - type: string - pattern: ^[0-9a-f]{64}$ - title: Source Result Sha256 - additionalProperties: false - type: object - required: - - schema_version - - artifact_id - - evaluation_id - - candidate_id - - visual_kind - - seed_scope - - artifact_ref - - source_result_sha256 - title: CraftaxVisualArtifact CreateCredentialRequest: properties: credential_name: @@ -49049,6 +51789,46 @@ components: - credential_type - credential_value title: CreateCredentialRequest + CreateGrantRequest: + properties: + thread_id: + type: string + format: uuid + title: Thread Id + enrollment_id: + type: string + format: uuid + title: Enrollment Id + operations: + items: + type: string + enum: + - read + - publish + type: array + maxItems: 2 + minItems: 1 + title: Operations + ttl_seconds: + type: integer + maximum: 2592000.0 + minimum: 60.0 + title: Ttl Seconds + history_after_seq: + anyOf: + - type: integer + maximum: 1.8446744073709552e+19 + minimum: 0.0 + - type: 'null' + title: History After Seq + additionalProperties: false + type: object + required: + - thread_id + - enrollment_id + - operations + - ttl_seconds + title: CreateGrantRequest CreateLeaseRequest: properties: client_instance_id: @@ -49187,6 +51967,25 @@ components: - snapshot_id title: CreateSnapshotResponse description: Response payload for snapshot creation. + CreateThreadRequest: + properties: + title: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Title + idempotency_key: + type: string + maxLength: 128 + minLength: 1 + pattern: ^[A-Za-z0-9._:-]{1,128}$ + title: Idempotency Key + additionalProperties: false + type: object + required: + - idempotency_key + title: CreateThreadRequest CreateTunnelRequest: properties: subdomain: @@ -49262,6 +52061,28 @@ components: - repository_credential - expires_at title: CredentialBrokerIssueResponse + CredentialRequest: + properties: + enrollment_id: + type: string + format: uuid + title: Enrollment Id + incarnation: + type: integer + minimum: 1.0 + title: Incarnation + ttl_seconds: + type: integer + maximum: 300.0 + minimum: 60.0 + title: Ttl Seconds + default: 300 + additionalProperties: false + type: object + required: + - enrollment_id + - incarnation + title: CredentialRequest CredentialResponse: properties: id: @@ -49314,6 +52135,39 @@ components: - OTHER title: CredentialType description: Credential type enum. + CreditUnknownAccepted: + properties: + accepted: + type: boolean + const: true + title: Accepted + default: true + type: object + title: CreditUnknownAccepted + CreditUnknownEvent: + properties: + surface: + type: string + enum: + - web_header + - desktop_account + title: Surface + reason: + type: string + enum: + - snapshot_missing + - workshop_spend_missing + - remaining_cents_missing + - remaining_cents_invalid + - request_failed + - malformed_snapshot + title: Reason + type: object + required: + - surface + - reason + title: CreditUnknownEvent + description: Bounded, secret-free client report for an UNKNOWN credit display. CreditsBalanceResponse: properties: org_id: @@ -50716,6 +53570,409 @@ components: - failed - canceled title: DeliveryState + DeploymentOperationRequest: + properties: + project_id: + type: string + format: uuid + title: Project Id + operation: + type: string + enum: + - create + - update + - delete + title: Operation + idempotency_key: + type: string + maxLength: 256 + minLength: 1 + title: Idempotency Key + expected_revision: + anyOf: + - type: string + - type: 'null' + title: Expected Revision + payload: + additionalProperties: true + type: object + title: Payload + additionalProperties: false + type: object + required: + - project_id + - operation + - idempotency_key + title: DeploymentOperationRequest + DesktopAccount: + properties: + id: + type: string + title: Id + display_name: + anyOf: + - type: string + - type: 'null' + title: Display Name + email: + anyOf: + - type: string + - type: 'null' + title: Email + avatar_url: + anyOf: + - type: string + - type: 'null' + title: Avatar Url + type: object + required: + - id + title: DesktopAccount + DesktopAccountSnapshot: + properties: + schema_version: + type: string + title: Schema Version + default: synth.desktop-account.v1 + status: + type: string + enum: + - active + - limited + - past_due + - canceled + - unknown + title: Status + account: + $ref: '#/components/schemas/DesktopAccount' + organization: + anyOf: + - $ref: '#/components/schemas/DesktopOrganization' + - type: 'null' + plan: + $ref: '#/components/schemas/DesktopPlan' + allowance: + $ref: '#/components/schemas/DesktopAllowance' + usage: + $ref: '#/components/schemas/DesktopUsage' + billing_actions: + $ref: '#/components/schemas/DesktopBillingActions' + catalog: + items: + $ref: '#/components/schemas/DesktopPlanOption' + type: array + title: Catalog + generated_at: + type: string + title: Generated At + degraded: + items: + type: string + type: array + title: Degraded + description: Sections that fell back to a default because a dependency failed. + type: object + required: + - status + - account + - plan + - allowance + - usage + - billing_actions + - catalog + - generated_at + title: DesktopAccountSnapshot + DesktopAllowance: + properties: + limit_cents: + anyOf: + - type: integer + - type: 'null' + title: Limit Cents + used_cents: + anyOf: + - type: integer + - type: 'null' + title: Used Cents + remaining_cents: + anyOf: + - type: integer + - type: 'null' + title: Remaining Cents + resets_at: + anyOf: + - type: string + - type: 'null' + title: Resets At + source: + type: string + enum: + - entitlement + - catalog_minus_usage + - none + title: Source + default: none + type: object + title: DesktopAllowance + description: 'Cloud dollars for the current period. `limit_cents=None` means + the + + backend does not meter this account in dollars; Desktop must then show no + + dollar figure rather than inventing one.' + DesktopBillingActions: + properties: + checkout_url: + anyOf: + - type: string + - type: 'null' + title: Checkout Url + portal_url: + anyOf: + - type: string + - type: 'null' + title: Portal Url + upgrade_tier: + anyOf: + - type: string + enum: + - free + - starter + - pro + - type: 'null' + title: Upgrade Tier + type: object + title: DesktopBillingActions + description: Hosted URLs only. Desktop opens them in the system browser. + DesktopCloudIdentity: + properties: + schema_version: + type: string + title: Schema Version + default: synth.desktop-cloud-identity.v1 + backend_origin: + type: string + title: Backend Origin + backend_id: + type: string + title: Backend Id + account_id: + type: string + title: Account Id + org_id: + type: string + title: Org Id + profile_id: + type: string + title: Profile Id + verified_at: + type: string + format: date-time + title: Verified At + valid_until: + type: string + format: date-time + title: Valid Until + revalidate_before_remote_operation: + type: boolean + title: Revalidate Before Remote Operation + default: true + credential_expiry: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Credential Expiry + revocation_contract: + type: string + title: Revocation Contract + default: fresh_database_key_and_membership_check + type: object + required: + - backend_origin + - backend_id + - account_id + - org_id + - profile_id + - verified_at + - valid_until + title: DesktopCloudIdentity + DesktopOrganization: + properties: + id: + type: string + title: Id + display_name: + anyOf: + - type: string + - type: 'null' + title: Display Name + role: + anyOf: + - type: string + - type: 'null' + title: Role + type: object + required: + - id + title: DesktopOrganization + DesktopPlan: + properties: + tier: + type: string + enum: + - free + - starter + - pro + title: Tier + display_name: + type: string + title: Display Name + state: + type: string + title: State + price_cents: + type: integer + title: Price Cents + effective_price_cents: + type: integer + title: Effective Price Cents + billing_interval: + type: string + title: Billing Interval + default: month + grant_kind: + type: string + enum: + - subscription + - trial + - promotion + - admin + - none + title: Grant Kind + default: none + entitlement_state: + type: string + enum: + - pending + - active + - exhausted + - expired + - revoked + title: Entitlement State + entitlement_starts_at: + anyOf: + - type: string + - type: 'null' + title: Entitlement Starts At + entitlement_expires_at: + anyOf: + - type: string + - type: 'null' + title: Entitlement Expires At + campaign_id: + anyOf: + - type: string + - type: 'null' + title: Campaign Id + claim_state: + anyOf: + - type: string + enum: + - eligible + - claimed + - unavailable + - type: 'null' + title: Claim State + renews_at: + anyOf: + - type: string + - type: 'null' + title: Renews At + cancel_at_period_end: + type: boolean + title: Cancel At Period End + default: false + is_paid: + type: boolean + title: Is Paid + default: false + type: object + required: + - tier + - display_name + - state + - price_cents + - effective_price_cents + - entitlement_state + title: DesktopPlan + DesktopPlanOption: + properties: + tier: + type: string + enum: + - free + - starter + - pro + title: Tier + display_name: + type: string + title: Display Name + price_cents: + type: integer + title: Price Cents + monthly_allowance_cents: + type: integer + title: Monthly Allowance Cents + interval: + type: string + title: Interval + default: month + type: object + required: + - tier + - display_name + - price_cents + - monthly_allowance_cents + title: DesktopPlanOption + description: 'One purchasable Desktop tier. The catalog travels with the snapshot + so + + the renderer never hardcodes a dollar figure for production.' + DesktopUsage: + properties: + today: + $ref: '#/components/schemas/DesktopUsageWindow' + seven_days: + $ref: '#/components/schemas/DesktopUsageWindow' + thirty_days: + $ref: '#/components/schemas/DesktopUsageWindow' + type: object + title: DesktopUsage + DesktopUsageWindow: + properties: + events: + type: integer + title: Events + default: 0 + billed_cents: + type: integer + title: Billed Cents + default: 0 + nominal_cents: + type: integer + title: Nominal Cents + default: 0 + tokens: + type: integer + title: Tokens + default: 0 + runtime_seconds: + type: integer + title: Runtime Seconds + default: 0 + type: object + title: DesktopUsageWindow DevEnvironmentActionRequest: properties: metadata: @@ -51279,6 +54536,33 @@ components: - authority_receipt title: DraftDeliveryAuthorityResponse description: Accepted-decision-bound open-draft Delivery and its authority proof. + EmptyRequest: + properties: {} + additionalProperties: false + type: object + title: EmptyRequest + EnrollRequest: + properties: + device_id: + type: string + pattern: ^[A-Za-z0-9._:-]{1,128}$ + title: Device Id + session_id: + type: string + pattern: ^[A-Za-z0-9._:-]{1,128}$ + title: Session Id + label: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Label + additionalProperties: false + type: object + required: + - device_id + - session_id + title: EnrollRequest EnsureCustomerRequest: properties: org_id: @@ -51335,6 +54619,7 @@ components: - managed_research - managed_research_private_alpha - smr_spend + - workshop_spend - smr_credit_wallet - smr_active_projects - smr_open_ended_discovery @@ -53040,6 +56325,33 @@ components: type: object title: HeartbeatResponse description: Response for lease heartbeat. + HostedBillingSession: + properties: + url: + type: string + title: Url + mode: + type: string + enum: + - provider + - hosted_web + title: Mode + session_id: + anyOf: + - type: string + - type: 'null' + title: Session Id + type: object + required: + - url + - mode + title: HostedBillingSession + description: 'A URL for the system browser. `mode` says who issued it: `provider` + for a + + real provider-hosted session, `hosted_web` when the web app completes the + + purchase (it holds the signed-in billing session).' ImageReleaseArtifact: properties: artifact_id: @@ -53062,96 +56374,6 @@ components: - archive_sha256 - archive_size_bytes title: ImageReleaseArtifact - ImageReleaseDeclarationRequest: - properties: - kind: - type: string - enum: - - craftax_scorer - - actor_runtime - title: Kind - archive_sha256: - type: string - pattern: ^[0-9a-f]{64}$ - title: Archive Sha256 - archive_size_bytes: - type: integer - maximum: 17179869184.0 - exclusiveMinimum: 0.0 - title: Archive Size Bytes - image_manifest_digest: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Image Manifest Digest - image_ref: - type: string - maxLength: 255 - minLength: 3 - title: Image Ref - platform_os: - type: string - const: linux - title: Platform Os - platform_architecture: - type: string - enum: - - amd64 - - arm64 - title: Platform Architecture - source_repository: - type: string - maxLength: 512 - minLength: 20 - title: Source Repository - source_commit_sha: - type: string - pattern: ^[0-9a-f]{40}$ - title: Source Commit Sha - fixture_manifest_sha256: - anyOf: - - type: string - pattern: ^[0-9a-f]{64}$ - - type: 'null' - title: Fixture Manifest Sha256 - fixture_binary_sha256: - anyOf: - - type: string - pattern: ^[0-9a-f]{64}$ - - type: 'null' - title: Fixture Binary Sha256 - actor_role: - anyOf: - - type: string - maxLength: 64 - - type: 'null' - title: Actor Role - interface_mode: - anyOf: - - type: string - maxLength: 64 - - type: 'null' - title: Interface Mode - capabilities: - anyOf: - - items: - type: string - type: array - maxItems: 32 - - type: 'null' - title: Capabilities - additionalProperties: false - type: object - required: - - kind - - archive_sha256 - - archive_size_bytes - - image_manifest_digest - - image_ref - - platform_os - - platform_architecture - - source_repository - - source_commit_sha - title: ImageReleaseDeclarationRequest ImageReleaseFinalizeRequest: properties: upload_id: @@ -53241,63 +56463,6 @@ components: - platform_os - platform_architecture title: ImageReleaseInspection - ImageReleaseResponse: - properties: - schema_version: - type: string - const: smr-image-release-v1 - title: Schema Version - release_id: - type: string - title: Release Id - org_id: - type: string - title: Org Id - artifact: - additionalProperties: true - type: object - title: Artifact - declaration: - additionalProperties: true - type: object - title: Declaration - inspection: - additionalProperties: true - type: object - title: Inspection - runtime_image_release: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Runtime Image Release - additionalProperties: false - type: object - required: - - schema_version - - release_id - - org_id - - artifact - - declaration - - inspection - title: ImageReleaseResponse - ImageReleaseStagingCleanupResponse: - properties: - upload_id: - type: string - title: Upload Id - status: - type: string - enum: - - deleted - - absent - title: Status - additionalProperties: false - type: object - required: - - upload_id - - status - title: ImageReleaseStagingCleanupResponse ImageReleaseStorageIdentity: properties: object_key: @@ -53555,6 +56720,67 @@ components: - increase_by title: IncreaseLimitRequest description: Request to increase a limit. + IndexAccessPolicy: + properties: + enabled: + type: boolean + title: Enabled + default: false + allow_draft_preparation: + type: boolean + title: Allow Draft Preparation + default: false + visibility: + type: string + enum: + - public + - private + title: Visibility + default: public + collection_ids: + items: + type: string + pattern: ^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$ + type: array + maxItems: 16 + title: Collection Ids + default: [] + max_searches: + type: integer + maximum: 100.0 + minimum: 0.0 + title: Max Searches + default: 10 + max_contents: + type: integer + maximum: 200.0 + minimum: 0.0 + title: Max Contents + default: 20 + max_context_tokens: + type: integer + maximum: 64000.0 + minimum: 0.0 + title: Max Context Tokens + default: 8000 + private_spend_cap_cents: + anyOf: + - type: integer + maximum: 100000.0 + minimum: 1.0 + - type: 'null' + title: Private Spend Cap Cents + additionalProperties: false + type: object + title: IndexAccessPolicy + description: 'Shared across every agent of one Swarm run or one Intern turn/cycle. + + + Defaults follow the frozen fast-MVP integration plan: public-only, 10 searches, + + 20 contents reads and 8,000 Index context tokens. Private access needs selected + + collections and an explicit spend cap; enabling Index never enables charges.' InferenceLifecycleAction: type: string enum: @@ -53764,6 +56990,8 @@ components: enum: - viewable - downloadable + - blocked + - unavailable title: Readiness default: viewable metadata: @@ -54914,6 +58142,8 @@ components: - call_tool - read_resource - create_factory + - attach_factory + - detach_factory - create_project - create_effort - start_factory_run @@ -55848,6 +59078,165 @@ components: - accepted - rejected title: InternProgressClaimStatus + InternResourceDisposition: + properties: + resource_kind: + type: string + title: Resource Kind + resource_id: + type: string + title: Resource Id + cleanup_owner_run_id: + anyOf: + - type: string + - type: 'null' + title: Cleanup Owner Run Id + relation: + type: string + enum: + - self + - owned + - borrowed + - shared + - retained + title: Relation + disposition: + type: string + enum: + - settled + - pending + - unknown + - excluded + - retained + title: Disposition + reason: + type: string + title: Reason + cleanup_owner: + anyOf: + - type: string + - type: 'null' + title: Cleanup Owner + epoch: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Epoch + additionalProperties: false + type: object + required: + - resource_kind + - resource_id + - relation + - disposition + - reason + title: InternResourceDisposition + description: One enumerated resource. ``retained`` always names its owner and + reason. + InternResourceInventory: + properties: + runtime_kind: + type: string + enum: + - sync + - async + title: Runtime Kind + runtime_id: + type: string + title: Runtime Id + observed_at: + type: string + format: date-time + title: Observed At + coverage: + type: string + const: registered-runtime-resources-v1 + title: Coverage + default: registered-runtime-resources-v1 + coverage_complete: + type: boolean + title: Coverage Complete + default: false + incomplete_reasons: + items: + type: string + type: array + title: Incomplete Reasons + resources: + items: + $ref: '#/components/schemas/InternResourceDisposition' + type: array + title: Resources + creator_set: + anyOf: + - type: string + - type: 'null' + title: Creator Set + profile: + anyOf: + - type: string + - type: 'null' + title: Profile + disposition_complete: + type: boolean + title: Disposition Complete + default: false + epochs: + items: + $ref: '#/components/schemas/InternRuntimeResourceEpoch' + type: array + title: Epochs + additionalProperties: false + type: object + required: + - runtime_kind + - runtime_id + - observed_at + - resources + title: InternResourceInventory + description: '``coverage_complete`` is creator coverage; ``disposition_complete`` + is cleanup. + + + Pending and unknown entries may exist in a complete inventory.' + InternResourceStopEffect: + properties: + resource_kind: + type: string + title: Resource Kind + resource_id: + type: string + title: Resource Id + effect: + type: string + enum: + - stop_requested + - already_settled + - retained + - admission_fenced + - stopped + - already_terminal + - stop_accepted + - stop_failed + title: Effect + owner: + anyOf: + - type: string + - type: 'null' + title: Owner + reason: + type: string + title: Reason + additionalProperties: false + type: object + required: + - resource_kind + - resource_id + - effect + - reason + title: InternResourceStopEffect + description: An effect actually performed when a runtime epoch closed. InternRuntimeCommandReceipt: properties: schema_version: @@ -56021,6 +59410,109 @@ components: required: - event title: InternRuntimeEventStreamEnvelope + InternRuntimeResourceEpoch: + properties: + epoch: + type: integer + minimum: 0.0 + title: Epoch + admission: + type: string + enum: + - open + - closed + title: Admission + closure_kind: + anyOf: + - type: string + enum: + - conversation_closed + - work_cancelled + - work_completed + - work_failed + - archived + - superseded_by_reopen + - type: 'null' + title: Closure Kind + opened_at: + type: string + format: date-time + title: Opened At + closed_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Closed At + effects: + items: + $ref: '#/components/schemas/InternResourceStopEffect' + type: array + title: Effects + cleanup_settled_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Cleanup Settled At + additionalProperties: false + type: object + required: + - epoch + - admission + - opened_at + title: InternRuntimeResourceEpoch + description: One admission epoch; closed epochs remain enumerated for recovery. + InternSessionUsageResponse: + properties: + origin_runtime_kind: + type: string + enum: + - sync + - async + title: Origin Runtime Kind + origin_runtime_id: + type: string + title: Origin Runtime Id + org_id: + type: string + title: Org Id + spend_cents: + type: integer + title: Spend Cents + token_count: + type: integer + title: Token Count + run_count: + type: integer + title: Run Count + billing: + $ref: '#/components/schemas/InternUsageBillingState' + bindings: + items: + additionalProperties: true + type: object + type: array + title: Bindings + metadata: + additionalProperties: true + type: object + title: Metadata + additionalProperties: false + type: object + required: + - origin_runtime_kind + - origin_runtime_id + - org_id + - spend_cents + - token_count + - run_count + - billing + - bindings + - metadata + title: InternSessionUsageResponse + description: 'Usage receipt for one Intern runtime: its direct inference plus + the runs it launched.' InternToolCallGroupV1: properties: group_id: @@ -56152,6 +59644,44 @@ components: - lifecycle - idempotency_identity title: InternToolCallReceiptV1 + InternUsageBillingState: + properties: + billing_state: + type: string + enum: + - settled + - pending + - stalled + - failed + - observability_only + - not_applicable + title: Billing State + stalled_row_count: + type: integer + title: Stalled Row Count + stalled_spend_cents: + type: integer + title: Stalled Spend Cents + billing_failed_row_count: + type: integer + title: Billing Failed Row Count + message: + anyOf: + - type: string + - type: 'null' + title: Message + show_stalled_banner: + type: boolean + title: Show Stalled Banner + additionalProperties: false + type: object + required: + - billing_state + - stalled_row_count + - stalled_spend_cents + - billing_failed_row_count + - show_stalled_banner + title: InternUsageBillingState InternWorkSummaryV1: properties: approach_summary: @@ -57367,6 +60897,23 @@ components: - review - system title: MessageKind + MetaHandoffContinueRequestV1: + properties: + child_agent_config: + anyOf: + - $ref: '#/components/schemas/AgentConfigV1' + - type: 'null' + summary: + anyOf: + - type: string + maxLength: 4000 + - type: 'null' + title: Summary + additionalProperties: false + type: object + title: MetaHandoffContinueRequestV1 + description: "Attended continue after approve \u2014 optional child AgentConfig\ + \ override." MetaHandoffResponseV1: properties: schema_version: @@ -57382,6 +60929,11 @@ components: source_segment_id: type: string title: Source Segment Id + destination_segment_id: + anyOf: + - type: string + - type: 'null' + title: Destination Segment Id summary: type: string title: Summary @@ -57391,6 +60943,14 @@ components: type: array title: Evidence References default: [] + parent_agent_config: + anyOf: + - $ref: '#/components/schemas/AgentConfigV1' + - type: 'null' + child_agent_config: + anyOf: + - $ref: '#/components/schemas/AgentConfigV1' + - type: 'null' agent_config: anyOf: - $ref: '#/components/schemas/AgentConfigV1' @@ -57405,6 +60965,18 @@ components: type: string format: date-time title: Sealed At + approved_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Approved At + continued_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Continued At merged_at: anyOf: - type: string @@ -57425,9 +60997,112 @@ components: MetaHandoffStatus: type: string enum: - - sealed + - needs_review + - approved + - continued + - rejected + - superseded - merged title: MetaHandoffStatus + description: Handoff lifecycle. Sync close uses MERGED; Async spine uses CONTINUED. + MetaThreadDeliveryResponseV1: + properties: + schema_version: + type: string + title: Schema Version + default: smr.meta-thread-delivery.v1 + delivery_id: + type: string + title: Delivery Id + organization_id: + type: string + title: Organization Id + research_intern_id: + type: string + title: Research Intern Id + meta_thread_id: + type: string + title: Meta Thread Id + kind: + type: string + title: Kind + target_mode: + type: string + title: Target Mode + segment_id: + anyOf: + - type: string + - type: 'null' + title: Segment Id + body: + anyOf: + - type: string + - type: 'null' + title: Body + summary: + anyOf: + - type: string + - type: 'null' + title: Summary + child_agent_config: + anyOf: + - $ref: '#/components/schemas/AgentConfigV1' + - type: 'null' + status: + type: string + title: Status + targets: + items: + $ref: '#/components/schemas/MetaThreadDeliveryTargetResponseV1' + type: array + title: Targets + default: [] + handoff_id: + anyOf: + - type: string + - type: 'null' + title: Handoff Id + created_at: + type: string + format: date-time + title: Created At + completed_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Completed At + additionalProperties: false + type: object + required: + - delivery_id + - organization_id + - research_intern_id + - meta_thread_id + - kind + - target_mode + - status + - created_at + title: MetaThreadDeliveryResponseV1 + MetaThreadDeliveryTargetResponseV1: + properties: + segment_id: + type: string + title: Segment Id + lane_runtime_id: + type: string + title: Lane Runtime Id + runtime_command_id: + anyOf: + - type: string + - type: 'null' + title: Runtime Command Id + additionalProperties: false + type: object + required: + - segment_id + - lane_runtime_id + title: MetaThreadDeliveryTargetResponseV1 MetaThreadKind: type: string enum: @@ -57657,101 +61332,6 @@ components: It is never retrievable from this API again (list is redacted).' - MqDeliveryEnvelope: - properties: - job_id: - type: string - title: Job Id - message_id: - type: string - title: Message Id - thread_id: - type: string - title: Thread Id - recipient: - $ref: '#/components/schemas/MqDeliveryRecipient' - attempts: - type: integer - title: Attempts - default: 0 - message: - $ref: '#/components/schemas/MqDeliveryMessage' - type: object - required: - - job_id - - message_id - - thread_id - - recipient - title: MqDeliveryEnvelope - MqDeliveryMessage: - properties: - seq: - anyOf: - - type: integer - - type: 'null' - title: Seq - kind: - anyOf: - - type: string - - type: 'null' - title: Kind - body: - anyOf: - - type: string - - type: 'null' - title: Body - payload: - additionalProperties: true - type: object - title: Payload - sender: - additionalProperties: true - type: object - title: Sender - idempotency_key: - anyOf: - - type: string - - type: 'null' - title: Idempotency Key - correlation_id: - anyOf: - - type: string - - type: 'null' - title: Correlation Id - parent_message_id: - anyOf: - - type: string - - type: 'null' - title: Parent Message Id - causation_id: - anyOf: - - type: string - - type: 'null' - title: Causation Id - created_at: - anyOf: - - type: string - - type: 'null' - title: Created At - type: object - title: MqDeliveryMessage - MqDeliveryRecipient: - properties: - kind: - type: string - title: Kind - id: - type: string - title: Id - org_id: - type: string - title: Org Id - type: object - required: - - kind - - id - - org_id - title: MqDeliveryRecipient NotebookEconomics: properties: nominal_cost_cents: @@ -58549,20 +62129,59 @@ components: OptimizerInternalRunEventBatchItem: properties: seq: - type: integer - minimum: 0.0 + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' title: Seq event_type: - type: string + anyOf: + - type: string + - type: 'null' title: Event Type payload: additionalProperties: true type: object title: Payload + schema_version: + anyOf: + - type: string + - type: 'null' + title: Schema Version + event_id: + anyOf: + - type: string + - type: 'null' + title: Event Id + attempt_id: + anyOf: + - type: string + - type: 'null' + title: Attempt Id + occurred_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Occurred At + kind: + anyOf: + - type: string + - type: 'null' + title: Kind + phase: + anyOf: + - type: string + - type: 'null' + title: Phase + producer: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Producer type: object - required: - - seq - - event_type title: OptimizerInternalRunEventBatchItem OptimizerInternalRunEventBatchRequest: properties: @@ -58587,21 +62206,61 @@ components: type: string title: Org Id seq: - type: integer - minimum: 0.0 + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' title: Seq event_type: - type: string + anyOf: + - type: string + - type: 'null' title: Event Type payload: additionalProperties: true type: object title: Payload + schema_version: + anyOf: + - type: string + - type: 'null' + title: Schema Version + event_id: + anyOf: + - type: string + - type: 'null' + title: Event Id + attempt_id: + anyOf: + - type: string + - type: 'null' + title: Attempt Id + occurred_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Occurred At + kind: + anyOf: + - type: string + - type: 'null' + title: Kind + phase: + anyOf: + - type: string + - type: 'null' + title: Phase + producer: + anyOf: + - additionalProperties: + type: string + type: object + - type: 'null' + title: Producer type: object required: - org_id - - seq - - event_type title: OptimizerInternalRunEventRequest OptimizerInternalRunSnapshotResponse: properties: @@ -58624,6 +62283,21 @@ components: algorithm: type: string title: Algorithm + implementation: + anyOf: + - type: string + - type: 'null' + title: Implementation + implementation_version: + anyOf: + - type: string + - type: 'null' + title: Implementation Version + attempt_id: + anyOf: + - type: string + - type: 'null' + title: Attempt Id status: type: string title: Status @@ -58646,6 +62320,110 @@ components: - cancellation_requested - storage_mode title: OptimizerInternalRunSnapshotResponse + OptimizerInternalSavedLoraCompleteRequest: + properties: + checkpoint_id: + type: string + title: Checkpoint Id + sha256: + anyOf: + - type: string + maxLength: 71 + - type: 'null' + title: Sha256 + storage_version: + anyOf: + - type: string + maxLength: 400 + - type: 'null' + title: Storage Version + type: object + required: + - checkpoint_id + title: OptimizerInternalSavedLoraCompleteRequest + OptimizerInternalSavedLoraUploadRequest: + properties: + attempt_id: + type: string + maxLength: 240 + minLength: 1 + title: Attempt Id + name: + type: string + maxLength: 160 + minLength: 1 + title: Name + provider: + type: string + enum: + - tinker + - river + title: Provider + checkpoint_kind: + type: string + enum: + - inference + - training + title: Checkpoint Kind + provider_checkpoint_reference: + type: string + maxLength: 2000 + minLength: 1 + title: Provider Checkpoint Reference + base_model: + type: string + maxLength: 300 + minLength: 1 + title: Base Model + lora_rank: + anyOf: + - type: integer + maximum: 4096.0 + minimum: 1.0 + - type: 'null' + title: Lora Rank + step: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Step + source_checkpoint_id: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Source Checkpoint Id + tags: + items: + type: string + type: array + maxItems: 32 + title: Tags + metadata: + additionalProperties: true + type: object + title: Metadata + content_type: + type: string + maxLength: 200 + title: Content Type + default: application/x-tar + expires_in: + type: integer + maximum: 86400.0 + minimum: 60.0 + title: Expires In + default: 3600 + type: object + required: + - attempt_id + - name + - provider + - checkpoint_kind + - provider_checkpoint_reference + - base_model + title: OptimizerInternalSavedLoraUploadRequest OptimizerInternalSynthTunnelRefreshRequest: properties: org_id: @@ -58655,6 +62433,44 @@ components: required: - org_id title: OptimizerInternalSynthTunnelRefreshRequest + OptimizerInternalTrainingAdmissionRequest: + properties: + org_id: + type: string + title: Org Id + attempt_id: + type: string + maxLength: 240 + minLength: 1 + title: Attempt Id + operation: + type: string + enum: + - rollout_sample + - training_step + - checkpoint + title: Operation + step: + type: integer + minimum: 1.0 + title: Step + current_estimated_cost_usd: + type: number + minimum: 0.0 + title: Current Estimated Cost Usd + reserved_operation_cost_usd: + type: number + minimum: 0.0 + title: Reserved Operation Cost Usd + type: object + required: + - org_id + - attempt_id + - operation + - step + - current_estimated_cost_usd + - reserved_operation_cost_usd + title: OptimizerInternalTrainingAdmissionRequest OptimizerRunListItemResponse: properties: run_id: @@ -58676,6 +62492,26 @@ components: algorithm: type: string title: Algorithm + implementation: + anyOf: + - type: string + - type: 'null' + title: Implementation + implementation_version: + anyOf: + - type: string + - type: 'null' + title: Implementation Version + attempt_id: + anyOf: + - type: string + - type: 'null' + title: Attempt Id + current_phase: + anyOf: + - type: string + - type: 'null' + title: Current Phase status: type: string title: Status @@ -58740,9 +62576,25 @@ components: - gepa - go-ex - mapo - - online-reflexion + - sft + - cispo title: Algorithm default: gepa + implementation: + anyOf: + - type: string + - type: 'null' + title: Implementation + implementation_version: + anyOf: + - type: string + - type: 'null' + title: Implementation Version + attempt_id: + anyOf: + - type: string + - type: 'null' + title: Attempt Id run_id: anyOf: - type: string @@ -58773,6 +62625,19 @@ components: anyOf: - $ref: '#/components/schemas/OptimizerContainerPoolTarget' - type: 'null' + resume_checkpoint_id: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Resume Checkpoint Id + admission: + type: string + enum: + - standard + - validation_only + title: Admission + default: standard type: object title: OptimizerRunSubmitRequest OptimizerStartupRequest: @@ -58803,6 +62668,56 @@ components: title: Metadata type: object title: OptimizerStartupRequest + OptimizerTrainingResumeRequest: + properties: + algorithm: + type: string + const: cispo + title: Algorithm + checkpoint_id: + type: string + maxLength: 500 + minLength: 1 + title: Checkpoint Id + idempotency_key: + type: string + maxLength: 500 + minLength: 1 + title: Idempotency Key + attempt_id: + anyOf: + - type: string + maxLength: 240 + minLength: 1 + - type: 'null' + title: Attempt Id + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + config_json: + additionalProperties: true + type: object + title: Config Json + container_pool: + anyOf: + - $ref: '#/components/schemas/OptimizerContainerPoolTarget' + - type: 'null' + admission: + type: string + enum: + - standard + - validation_only + title: Admission + default: standard + type: object + required: + - algorithm + - checkpoint_id + - idempotency_key + - config_json + title: OptimizerTrainingResumeRequest OptimizerUsageRegistrationRequest: properties: algorithm: @@ -58811,7 +62726,8 @@ components: - gepa - go-ex - mapo - - online-reflexion + - sft + - cispo title: Algorithm event_name: type: string @@ -58922,6 +62838,12 @@ components: - last_updated - created_at title: OrganizationBalanceResponse + OriginRuntimeKind: + type: string + enum: + - sync + - async + title: OriginRuntimeKind ParentObjectiveEvaluationState: type: string enum: @@ -59198,6 +63120,7 @@ components: - artifact_publication - trace_publication - workspace_archive + - manderqueue_message title: ProducedResourceKind ProducedResourceReferenceV1: properties: @@ -59241,6 +63164,28 @@ components: - resource_kind - resource_id title: ProducedResourceReferenceV1 + ProductUsageEventsRequest: + properties: + schema_version: + anyOf: + - type: integer + - type: 'null' + title: Schema Version + product: + type: string + maxLength: 40 + minLength: 1 + title: Product + events: + items: + additionalProperties: true + type: object + type: array + title: Events + type: object + required: + - product + title: ProductUsageEventsRequest ProjectChangeSetCreateRequest: properties: title: @@ -61820,6 +65765,52 @@ components: title: Client Metadata type: object title: PublishManderqueueMessageRequest + PublishMessageRequest: + properties: + kind: + type: string + enum: + - ask + - answer + - steer + - notice + - actor_runtime + - blocker + - handoff_ping + title: Kind + body: + type: string + maxLength: 65536 + title: Body + payload: + additionalProperties: true + type: object + title: Payload + idempotency_key: + type: string + maxLength: 128 + minLength: 1 + pattern: ^[A-Za-z0-9._:-]{1,128}$ + title: Idempotency Key + parent_message_id: + anyOf: + - type: string + format: uuid + - type: 'null' + title: Parent Message Id + correlation_id: + anyOf: + - type: string + maxLength: 128 + - type: 'null' + title: Correlation Id + additionalProperties: false + type: object + required: + - kind + - body + - idempotency_key + title: PublishMessageRequest PullRequestBinding: properties: contract_version: @@ -61933,6 +65924,116 @@ components: - merged - closed title: PullRequestState + QaCleanupOrgRequest: + properties: + org_id: + type: string + title: Org Id + dry_run: + type: boolean + title: Dry Run + default: true + reason: + type: string + maxLength: 200 + title: Reason + default: qa_cleanup + force: + type: boolean + title: Force + default: false + confirm_org_name: + anyOf: + - type: string + - type: 'null' + title: Confirm Org Name + type: object + required: + - org_id + title: QaCleanupOrgRequest + QaCleanupOrgResponse: + properties: + org_id: + type: string + title: Org Id + org_name: + type: string + title: Org Name + clerk_org_id: + anyOf: + - type: string + - type: 'null' + title: Clerk Org Id + dry_run: + type: boolean + title: Dry Run + qa_name_match: + type: boolean + title: Qa Name Match + qa_member_emails_match: + type: boolean + title: Qa Member Emails Match + forced: + type: boolean + title: Forced + api_keys_revoked: + type: integer + title: Api Keys Revoked + api_key_ids: + items: + type: string + type: array + title: Api Key Ids + access_tier_before: + anyOf: + - type: string + - type: 'null' + title: Access Tier Before + access_tier_after: + type: string + title: Access Tier After + promotion_grants_released: + items: + $ref: '#/components/schemas/QaCleanupReleasedGrant' + type: array + title: Promotion Grants Released + member_emails: + items: + type: string + type: array + title: Member Emails + type: object + required: + - org_id + - org_name + - dry_run + - qa_name_match + - qa_member_emails_match + - forced + - api_keys_revoked + - api_key_ids + - access_tier_after + - promotion_grants_released + - member_emails + title: QaCleanupOrgResponse + QaCleanupReleasedGrant: + properties: + grant_id: + type: string + title: Grant Id + campaign_id: + type: string + title: Campaign Id + released_slot_number: + anyOf: + - type: string + - type: 'null' + title: Released Slot Number + type: object + required: + - grant_id + - campaign_id + title: QaCleanupReleasedGrant RecordUsageRequest: properties: metric_type: @@ -62124,6 +66225,129 @@ components: - released_at title: ReleaseResponse description: Response for lease release. + RenewRequest: + properties: + ttl_seconds: + type: integer + maximum: 2592000.0 + minimum: 60.0 + title: Ttl Seconds + additionalProperties: false + type: object + required: + - ttl_seconds + title: RenewRequest + ReportBlockV1: + properties: + schema_version: + type: string + const: synth.report-revision.v1 + title: Schema Version + default: synth.report-revision.v1 + block_id: + type: string + maxLength: 128 + minLength: 1 + title: Block Id + kind: + type: string + enum: + - report.outline.v1 + - report.prose.v1 + - report.result.v1 + - report.visual.v1 + - report.diagram.v1 + - report.trace-v5.v1 + - report.claim.v1 + - report.attachment.v1 + - report.experiment-records.v1 + - report.research-log.v1 + title: Kind + anchor: + type: string + maxLength: 128 + minLength: 1 + title: Anchor + title: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Title + payload: + additionalProperties: true + type: object + title: Payload + source_revision: + anyOf: + - type: string + maxLength: 255 + - type: 'null' + title: Source Revision + source_digest: + anyOf: + - type: string + pattern: ^(sha256:)?[0-9a-f]{64}$ + - type: 'null' + title: Source Digest + access_state: + type: string + enum: + - accessible + - inaccessible + - missing + title: Access State + default: accessible + integrity_state: + type: string + enum: + - verified + - failed + - unknown + title: Integrity State + default: unknown + additionalProperties: false + type: object + required: + - block_id + - kind + - anchor + title: ReportBlockV1 + ReportClaimV1: + properties: + claim_id: + type: string + maxLength: 128 + minLength: 1 + title: Claim Id + statement: + type: string + maxLength: 8000 + minLength: 1 + title: Statement + status: + type: string + enum: + - draft + - supported + - qualified + - contradicted + - unresolved + title: Status + default: draft + evidence_refs: + items: + type: string + type: array + maxItems: 64 + title: Evidence Refs + default: [] + additionalProperties: false + type: object + required: + - claim_id + - statement + title: ReportClaimV1 ReportCreateRequestV1: properties: title: @@ -62136,6 +66360,19 @@ components: maxLength: 200000 minLength: 1 title: Body + summary: + anyOf: + - type: string + maxLength: 8000 + - type: 'null' + title: Summary + authors: + items: + type: string + type: array + maxItems: 32 + title: Authors + default: [] structured_sections: items: additionalProperties: true @@ -62151,8 +66388,31 @@ components: maxItems: 256 title: Sources default: [] + blocks: + items: + $ref: '#/components/schemas/ReportBlockV1' + type: array + maxItems: 256 + title: Blocks + default: [] + claims: + items: + $ref: '#/components/schemas/ReportClaimV1' + type: array + maxItems: 128 + title: Claims + default: [] + limitations: + items: + type: string + type: array + maxItems: 128 + title: Limitations + default: [] visual: - $ref: '#/components/schemas/ReportVisualBindingV1' + anyOf: + - $ref: '#/components/schemas/ReportVisualBindingV1' + - type: 'null' idempotency_key: type: string maxLength: 512 @@ -62176,7 +66436,6 @@ components: required: - title - body - - visual - idempotency_key - project_id title: ReportCreateRequestV1 @@ -62246,6 +66505,19 @@ components: maxLength: 200000 minLength: 1 title: Body + summary: + anyOf: + - type: string + maxLength: 8000 + - type: 'null' + title: Summary + authors: + items: + type: string + type: array + maxItems: 32 + title: Authors + default: [] structured_sections: items: additionalProperties: true @@ -62261,8 +66533,31 @@ components: maxItems: 256 title: Sources default: [] + blocks: + items: + $ref: '#/components/schemas/ReportBlockV1' + type: array + maxItems: 256 + title: Blocks + default: [] + claims: + items: + $ref: '#/components/schemas/ReportClaimV1' + type: array + maxItems: 128 + title: Claims + default: [] + limitations: + items: + type: string + type: array + maxItems: 128 + title: Limitations + default: [] visual: - $ref: '#/components/schemas/ReportVisualBindingV1' + anyOf: + - $ref: '#/components/schemas/ReportVisualBindingV1' + - type: 'null' idempotency_key: type: string maxLength: 512 @@ -62278,7 +66573,6 @@ components: required: - title - body - - visual - idempotency_key title: ReportRevisionCreateRequestV1 ReportRevisionResponseV1: @@ -62353,6 +66647,35 @@ components: $ref: '#/components/schemas/ReportSourceResponseV1' type: array title: Sources + blocks: + items: + $ref: '#/components/schemas/ReportBlockV1' + type: array + title: Blocks + default: [] + claims: + items: + $ref: '#/components/schemas/ReportClaimV1' + type: array + title: Claims + default: [] + limitations: + items: + type: string + type: array + title: Limitations + default: [] + summary: + anyOf: + - type: string + - type: 'null' + title: Summary + authors: + items: + type: string + type: array + title: Authors + default: [] created_at: type: string format: date-time @@ -62781,286 +67104,36 @@ components: org_id: type: string title: Org Id - lane: - type: string - title: Lane - candidate_id: - type: string - title: Candidate Id - receipt: - additionalProperties: true - type: object - title: Receipt - replayed: - type: boolean - title: Replayed - created_at: - type: string - format: date-time - title: Created At - additionalProperties: false - type: object - required: - - receipt_id - - receipt_url - - content_digest - - org_id - - lane - - candidate_id - - receipt - - replayed - - created_at - title: ResearchInternAcceptanceReceiptPublicationResponse - ResearchInternEventActorKind: - type: string - enum: - - operator - - research_intern - - magi - - system - title: ResearchInternEventActorKind - ResearchInternEventAppendRequest: - properties: - event_kind: - $ref: '#/components/schemas/ResearchInternEventKind' - mode: - anyOf: - - $ref: '#/components/schemas/MagiMode' - - type: 'null' - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - expected_state_generation: - type: integer - minimum: 0.0 - title: Expected State Generation - body: - anyOf: - - type: string - maxLength: 20000 - - type: 'null' - title: Body - payload: - additionalProperties: true - type: object - title: Payload - evidence_refs: - items: - type: string - type: array - title: Evidence Refs - additionalProperties: false - type: object - required: - - event_kind - - idempotency_key - - expected_state_generation - title: ResearchInternEventAppendRequest - ResearchInternEventKind: - type: string - enum: - - objective - - operator_message - - agent_message - - progress - - magi_decision - - state_snapshot - - error - - closed - title: ResearchInternEventKind - ResearchInternEventResponse: - properties: - schema_version: - type: string - const: smr.research-intern-event.v1 - title: Schema Version - default: smr.research-intern-event.v1 - event_id: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Event Id - session_id: - type: string - title: Session Id - research_intern_id: - type: string - title: Research Intern Id - org_id: - type: string - title: Org Id - sequence: - type: integer - title: Sequence - previous_state_generation: - type: integer - title: Previous State Generation - state_generation: - type: integer - title: State Generation - event_kind: - $ref: '#/components/schemas/ResearchInternEventKind' - actor_kind: - $ref: '#/components/schemas/ResearchInternEventActorKind' - actor_id: - anyOf: - - type: string - - type: 'null' - title: Actor Id - mode: - anyOf: - - $ref: '#/components/schemas/MagiMode' - - type: 'null' - canonical_user: - anyOf: - - $ref: '#/components/schemas/MagiCanonicalUser' - - type: 'null' - receipt_id: - anyOf: - - type: string - - type: 'null' - title: Receipt Id - idempotency_key: - type: string - title: Idempotency Key - body: - anyOf: - - type: string - - type: 'null' - title: Body - payload: - additionalProperties: true - type: object - title: Payload - evidence_refs: - items: - type: string - type: array - title: Evidence Refs - created_at: - type: string - format: date-time - title: Created At - additionalProperties: false - type: object - required: - - event_id - - session_id - - research_intern_id - - org_id - - sequence - - previous_state_generation - - state_generation - - event_kind - - actor_kind - - idempotency_key - - payload - - evidence_refs - - created_at - title: ResearchInternEventResponse - ResearchInternEventStreamCursor: - properties: - schema_version: - type: string - const: smr.research-intern-event-stream-cursor.v1 - title: Schema Version - default: smr.research-intern-event-stream-cursor.v1 - after_sequence: - type: integer - minimum: 1.0 - title: After Sequence - event_id: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Event Id - state_generation: - type: integer - minimum: 1.0 - title: State Generation - additionalProperties: false - type: object - required: - - after_sequence - - event_id - - state_generation - title: ResearchInternEventStreamCursor - description: Exact durable event-log position used by Intern SSE consumers. - ResearchInternEventStreamEnvelope: - oneOf: - - $ref: '#/components/schemas/ResearchInternEventStreamEvent' - - $ref: '#/components/schemas/ResearchInternEventStreamHeartbeat' - title: ResearchInternEventStreamEnvelope - description: OpenAPI-visible discriminated union for Intern SSE data payloads. - discriminator: - propertyName: kind - mapping: - event: '#/components/schemas/ResearchInternEventStreamEvent' - heartbeat: '#/components/schemas/ResearchInternEventStreamHeartbeat' - ResearchInternEventStreamEvent: - properties: - schema_version: - type: string - const: smr.research-intern-event-stream.v1 - title: Schema Version - default: smr.research-intern-event-stream.v1 - kind: - type: string - const: event - title: Kind - default: event - session_id: - type: string - title: Session Id - cursor: - $ref: '#/components/schemas/ResearchInternEventStreamCursor' - event: - $ref: '#/components/schemas/ResearchInternEventResponse' - additionalProperties: false - type: object - required: - - session_id - - cursor - - event - title: ResearchInternEventStreamEvent - description: One durable ResearchInternEventResponse framed for SSE transport. - ResearchInternEventStreamHeartbeat: - properties: - schema_version: - type: string - const: smr.research-intern-event-stream.v1 - title: Schema Version - default: smr.research-intern-event-stream.v1 - kind: + lane: type: string - const: heartbeat - title: Kind - default: heartbeat - session_id: + title: Lane + candidate_id: type: string - title: Session Id - cursor: - anyOf: - - $ref: '#/components/schemas/ResearchInternEventStreamCursor' - - type: 'null' - reconnect_after_ms: - type: integer - maximum: 30000.0 - minimum: 1000.0 - title: Reconnect After Ms - emitted_at: + title: Candidate Id + receipt: + additionalProperties: true + type: object + title: Receipt + replayed: + type: boolean + title: Replayed + created_at: type: string format: date-time - title: Emitted At + title: Created At additionalProperties: false type: object required: - - session_id - - reconnect_after_ms - - emitted_at - title: ResearchInternEventStreamHeartbeat - description: Non-durable liveness frame; cursor is the last durable emitted - event. + - receipt_id + - receipt_url + - content_digest + - org_id + - lane + - candidate_id + - receipt + - replayed + - created_at + title: ResearchInternAcceptanceReceiptPublicationResponse ResearchInternFactoryMembershipResponse: properties: research_intern_id: @@ -63239,219 +67312,6 @@ components: - created_at - updated_at title: ResearchInternResponse - ResearchInternSessionCloseRequest: - properties: - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - expected_state_generation: - type: integer - minimum: 0.0 - title: Expected State Generation - status: - type: string - enum: - - completed - - partial - - failed - - stopped - - canceled - - archived - title: Status - rationale: - type: string - maxLength: 20000 - minLength: 1 - title: Rationale - evidence_refs: - items: - type: string - type: array - title: Evidence Refs - additionalProperties: false - type: object - required: - - idempotency_key - - expected_state_generation - - status - - rationale - title: ResearchInternSessionCloseRequest - ResearchInternSessionCreateRequest: - properties: - factory_id: - type: string - maxLength: 255 - minLength: 1 - title: Factory Id - project_id: - type: string - maxLength: 255 - minLength: 1 - title: Project Id - effort_id: - type: string - maxLength: 255 - minLength: 1 - title: Effort Id - run_id: - anyOf: - - type: string - maxLength: 255 - - type: 'null' - title: Run Id - objective: - type: string - maxLength: 20000 - minLength: 1 - title: Objective - objective_bounds: - additionalProperties: true - type: object - title: Objective Bounds - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - metadata: - additionalProperties: true - type: object - title: Metadata - additionalProperties: false - type: object - required: - - factory_id - - project_id - - effort_id - - objective - - idempotency_key - title: ResearchInternSessionCreateRequest - ResearchInternSessionResponse: - properties: - session_id: - type: string - title: Session Id - research_intern_id: - type: string - title: Research Intern Id - org_id: - type: string - title: Org Id - factory_id: - type: string - title: Factory Id - project_id: - type: string - title: Project Id - effort_id: - type: string - title: Effort Id - run_id: - anyOf: - - type: string - - type: 'null' - title: Run Id - objective: - type: string - title: Objective - objective_bounds: - additionalProperties: true - type: object - title: Objective Bounds - status: - $ref: '#/components/schemas/ResearchInternSessionStatus' - state_generation: - type: integer - title: State Generation - last_event_sequence: - type: integer - title: Last Event Sequence - metadata: - additionalProperties: true - type: object - title: Metadata - created_by_user_id: - anyOf: - - type: string - - type: 'null' - title: Created By User Id - created_at: - type: string - format: date-time - title: Created At - updated_at: - type: string - format: date-time - title: Updated At - closed_at: - anyOf: - - type: string - format: date-time - - type: 'null' - title: Closed At - additionalProperties: false - type: object - required: - - session_id - - research_intern_id - - org_id - - factory_id - - project_id - - effort_id - - objective - - objective_bounds - - status - - state_generation - - last_event_sequence - - metadata - - created_at - - updated_at - title: ResearchInternSessionResponse - ResearchInternSessionStatus: - type: string - enum: - - active - - completed - - partial - - failed - - stopped - - canceled - - archived - title: ResearchInternSessionStatus - ResearchInternSessionSyncResponse: - properties: - schema_version: - type: string - const: smr.research-intern-session-sync.v1 - title: Schema Version - default: smr.research-intern-session-sync.v1 - session: - $ref: '#/components/schemas/ResearchInternSessionResponse' - events: - items: - $ref: '#/components/schemas/ResearchInternEventResponse' - type: array - title: Events - source_run_id: - type: string - title: Source Run Id - projected_count: - type: integer - title: Projected Count - has_more: - type: boolean - title: Has More - additionalProperties: false - type: object - required: - - session - - events - - source_run_id - - projected_count - - has_more - title: ResearchInternSessionSyncResponse ResearchInternStatus: type: string enum: @@ -63459,271 +67319,6 @@ components: - paused - archived title: ResearchInternStatus - ResearchInternTracePublicationRequest: - properties: - schema_version: - type: string - const: smr.research-intern-trace-publication-request.v1 - title: Schema Version - default: smr.research-intern-trace-publication-request.v1 - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - expected_state_generation: - type: integer - minimum: 1.0 - title: Expected State Generation - additionalProperties: false - type: object - required: - - idempotency_key - - expected_state_generation - title: ResearchInternTracePublicationRequest - description: Optimistic fence for publishing one terminal Intern event chain. - ResearchInternTracePublicationResponse: - properties: - schema_version: - type: string - const: smr.research-intern-trace-publication.v1 - title: Schema Version - default: smr.research-intern-trace-publication.v1 - session_id: - type: string - title: Session Id - research_intern_id: - type: string - title: Research Intern Id - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - org_id: - type: string - title: Org Id - factory_id: - type: string - title: Factory Id - project_id: - type: string - title: Project Id - effort_id: - type: string - title: Effort Id - run_id: - type: string - title: Run Id - state_generation: - type: integer - minimum: 1.0 - title: State Generation - event_count: - type: integer - minimum: 1.0 - title: Event Count - trace_id: - type: string - title: Trace Id - trace_digest: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Trace Digest - capture_id: - type: string - title: Capture Id - bundle_id: - type: string - title: Bundle Id - manifest_digest: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Manifest Digest - promotion: - $ref: '#/components/schemas/TracePromotionReceipt' - additionalProperties: false - type: object - required: - - session_id - - research_intern_id - - idempotency_key - - org_id - - factory_id - - project_id - - effort_id - - run_id - - state_generation - - event_count - - trace_id - - trace_digest - - capture_id - - bundle_id - - manifest_digest - - promotion - title: ResearchInternTracePublicationResponse - description: Factory Trace Store receipt for one genuine terminal Intern Trace - V5. - ResearchInternTurnControl: - type: string - enum: - - pause - - intervene - - resume - title: ResearchInternTurnControl - ResearchInternTurnError: - properties: - error_code: - type: string - title: Error Code - message: - type: string - title: Message - retryable: - type: boolean - title: Retryable - detail: - additionalProperties: true - type: object - title: Detail - additionalProperties: false - type: object - required: - - error_code - - message - - retryable - title: ResearchInternTurnError - ResearchInternTurnRequest: - properties: - body: - type: string - maxLength: 20000 - minLength: 1 - title: Body - mode: - $ref: '#/components/schemas/MagiMode' - default: sync - idempotency_key: - type: string - maxLength: 512 - minLength: 1 - title: Idempotency Key - expected_session_state_generation: - type: integer - minimum: 0.0 - title: Expected Session State Generation - expected_intern_state_generation: - type: integer - minimum: 0.0 - title: Expected Intern State Generation - control: - anyOf: - - $ref: '#/components/schemas/ResearchInternTurnControl' - - type: 'null' - rationale: - anyOf: - - type: string - maxLength: 20000 - - type: 'null' - title: Rationale - state_patch: - additionalProperties: true - type: object - title: State Patch - evidence_refs: - items: - type: string - type: array - title: Evidence Refs - wait_timeout_seconds: - type: number - maximum: 30.0 - minimum: 0.0 - title: Wait Timeout Seconds - default: 15.0 - poll_interval_ms: - type: integer - maximum: 2000.0 - minimum: 100.0 - title: Poll Interval Ms - default: 250 - additionalProperties: false - type: object - required: - - body - - idempotency_key - - expected_session_state_generation - - expected_intern_state_generation - title: ResearchInternTurnRequest - description: One browser-callable operator turn against the bound real runtime. - ResearchInternTurnResponse: - properties: - schema_version: - type: string - const: smr.research-intern-turn.v1 - title: Schema Version - default: smr.research-intern-turn.v1 - turn_id: - type: string - pattern: ^sha256:[0-9a-f]{64}$ - title: Turn Id - status: - $ref: '#/components/schemas/ResearchInternTurnStatus' - session: - $ref: '#/components/schemas/ResearchInternSessionResponse' - run_id: - type: string - title: Run Id - operator_event: - $ref: '#/components/schemas/ResearchInternEventResponse' - decision_receipt: - anyOf: - - $ref: '#/components/schemas/MagiDecisionReceiptResponse' - - type: 'null' - agent_event: - anyOf: - - $ref: '#/components/schemas/ResearchInternEventResponse' - - type: 'null' - projected_events: - items: - $ref: '#/components/schemas/ResearchInternEventResponse' - type: array - title: Projected Events - reconnect_after_sequence: - type: integer - minimum: 0.0 - title: Reconnect After Sequence - waited_seconds: - type: number - minimum: 0.0 - title: Waited Seconds - replayed: - type: boolean - title: Replayed - error: - anyOf: - - $ref: '#/components/schemas/ResearchInternTurnError' - - type: 'null' - additionalProperties: false - type: object - required: - - turn_id - - status - - session - - run_id - - operator_event - - reconnect_after_sequence - - waited_seconds - - replayed - title: ResearchInternTurnResponse - ResearchInternTurnStatus: - type: string - enum: - - accepted - - completed - - timed_out - - failed - title: ResearchInternTurnStatus ResolutionDecision: type: string enum: @@ -63779,7 +67374,6 @@ components: - synth_ai - cursor - deepseek - - xai - modal - openai_chatgpt - baseten @@ -63794,7 +67388,7 @@ components: title: Config limit: anyOf: - - $ref: '#/components/schemas/UsageLimit' + - $ref: '#/components/schemas/UsageLimit-Input' - type: 'null' additionalProperties: false type: object @@ -63879,6 +67473,59 @@ components: additionalProperties: false type: object title: ResourceRoutingPolicy + ResumeConditionResponse: + properties: + kind: + $ref: '#/components/schemas/ResumeKind' + wake_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Wake At + watchdog_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Watchdog At + interaction_ids: + items: + type: string + type: array + title: Interaction Ids + default: [] + effort_ids: + items: + type: string + type: array + title: Effort Ids + default: [] + external_ref: + anyOf: + - type: string + - type: 'null' + title: External Ref + producer: + anyOf: + - $ref: '#/components/schemas/WaitProducer' + - type: 'null' + additionalProperties: false + type: object + required: + - kind + title: ResumeConditionResponse + description: "Wire form of packages.intern.async_.phase.ResumeCondition.\n\n\ + # See: INTERN_ASYNC_RUNTIME_PHASE_REFACTOR_HANDOFF_2026-08-08.md \xA76.3" + ResumeKind: + type: string + enum: + - timer + - human_answer + - operator_command + - external_event + - never + title: ResumeKind RotateTunnelRequest: properties: local_port: @@ -64025,6 +67672,87 @@ components: - evaluation_iteration - max_evaluation_iterations title: RunObjectiveResponse + RunResourceSettlementResponse: + properties: + run_id: + type: string + title: Run Id + observed_at: + type: string + format: date-time + title: Observed At + coverage: + type: string + enum: + - untracked + - explicit-v1 + title: Coverage + settled: + type: boolean + title: Settled + registered_tree_settled: + type: boolean + title: Registered Tree Settled + default: false + coverage_complete: + type: boolean + title: Coverage Complete + default: false + scope_kind: + anyOf: + - type: string + enum: + - root_tree + - owned_subtree + - type: 'null' + title: Scope Kind + root_run_id: + anyOf: + - type: string + - type: 'null' + title: Root Run Id + edge_id: + anyOf: + - type: string + - type: 'null' + title: Edge Id + pending: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Pending + unknown: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Unknown + confirmed: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Confirmed + excluded: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Excluded + root_confirmed: + anyOf: + - type: boolean + - type: 'null' + title: Root Confirmed + additionalProperties: false + type: object + required: + - run_id + - observed_at + - coverage + - settled + title: RunResourceSettlementResponse RunWorkProductResponse: properties: work_product_id: @@ -64432,6 +68160,209 @@ components: - archived - failed title: RuntimeOutcome + RuntimePhaseWire: + type: string + enum: + - agent:bootstrapping + - agent:cancelled + - agent:failed + - agent:finished + - agent:gated + - agent:held + - agent:publishing + - agent:running + - agent:tool + - agent:waiting + - agent:waiting_evidence + - lifecycle:not_started + - world:syncing + - world:waiting + title: RuntimePhaseWire + SavedLoraCompleteRequest: + properties: + sha256: + anyOf: + - type: string + maxLength: 71 + - type: 'null' + title: Sha256 + storage_version: + anyOf: + - type: string + maxLength: 400 + - type: 'null' + title: Storage Version + type: object + title: SavedLoraCompleteRequest + SavedLoraPatchRequest: + properties: + name: + anyOf: + - type: string + maxLength: 160 + minLength: 1 + - type: 'null' + title: Name + description: + anyOf: + - type: string + maxLength: 4000 + - type: 'null' + title: Description + visibility: + anyOf: + - type: string + enum: + - private + - org + - type: 'null' + title: Visibility + tags: + anyOf: + - items: + type: string + type: array + maxItems: 32 + - type: 'null' + title: Tags + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Metadata + status: + anyOf: + - type: string + enum: + - ready + - archived + - type: 'null' + title: Status + type: object + title: SavedLoraPatchRequest + SavedLoraSampleMessage: + properties: + role: + type: string + enum: + - system + - user + - assistant + title: Role + content: + type: string + maxLength: 131072 + minLength: 1 + title: Content + type: object + required: + - role + - content + title: SavedLoraSampleMessage + SavedLoraUploadRequest: + properties: + name: + type: string + maxLength: 160 + minLength: 1 + title: Name + description: + type: string + maxLength: 4000 + title: Description + default: '' + provider: + type: string + enum: + - tinker + - river + - synth + - imported + title: Provider + checkpoint_kind: + type: string + enum: + - inference + - training + title: Checkpoint Kind + visibility: + type: string + enum: + - private + - org + title: Visibility + default: private + base_model: + type: string + maxLength: 300 + minLength: 1 + title: Base Model + lora_rank: + anyOf: + - type: integer + maximum: 4096.0 + minimum: 1.0 + - type: 'null' + title: Lora Rank + step: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Step + provider_checkpoint_reference: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Provider Checkpoint Reference + run_id: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Run Id + attempt_id: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Attempt Id + source_checkpoint_id: + anyOf: + - type: string + maxLength: 240 + - type: 'null' + title: Source Checkpoint Id + tags: + items: + type: string + type: array + maxItems: 32 + title: Tags + metadata: + additionalProperties: true + type: object + title: Metadata + content_type: + type: string + maxLength: 200 + title: Content Type + default: application/x-tar + expires_in: + type: integer + maximum: 86400.0 + minimum: 60.0 + title: Expires In + default: 3600 + type: object + required: + - name + - provider + - checkpoint_kind + - base_model + title: SavedLoraUploadRequest SealedDatasetRevision: properties: schema_version: @@ -65453,6 +69384,7 @@ components: SmrAgentModel: type: string enum: + - openrouter/openai/gpt-5.6-luna - gpt-5-codex - gpt-5.3-codex - gpt-5.3-codex-spark @@ -65460,7 +69392,6 @@ components: - gpt-5.6-luna - gpt-5.6-sol - gpt-5.6-terra - - cursor/grok-4.5 - gpt-5.4 - gpt-5.4-mini - nemotron-super @@ -65469,8 +69400,6 @@ components: - cursor/composer-2.5 - cursor/gpt-5 - cursor/sonnet-4 - - x-ai/grok-4.3 - - x-ai/grok-build - moonshotai/kimi-k2.6 - baseten/moonshotai/Kimi-K3 - modal/moonshotai/Kimi-K3 @@ -65481,6 +69410,8 @@ components: - deepseek/deepseek-chat - deepseek/deepseek-reasoner - poolside/laguna-s-2.1 + - openrouter/poolside/laguna-s-2.1 + - meta/muse-spark-1.2 - synth_internal/laguna-s-2.1-nvfp4 title: SmrAgentModel SmrAgentModelCatalogEntry: @@ -66995,7 +70926,6 @@ components: - openrouter - poolside - synth_internal - - xai - tinker title: SmrCredentialProvider SmrCredentialRefCreateRequest: @@ -74302,7 +78232,6 @@ components: - poolside - synth - synth_internal - - xai title: SmrInferenceProvider SmrInferenceProviderCatalogEntry: properties: @@ -78114,9 +82043,9 @@ components: values are 'baseten/moonshotai/Kimi-K3', 'baseten/zai-org/GLM-5.2', 'cursor/composer-2.5', 'deepseek/deepseek-v4-flash', 'deepseek/deepseek-v4-flash-direct', 'deepseek/deepseek-v4-pro', 'deepseek/deepseek-v4-pro-direct', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.5', - 'gpt-5.6-luna', 'modal/moonshotai/Kimi-K3', 'poolside/laguna-s-2.1', 'synth_internal/laguna-s-2.1-nvfp4', - 'x-ai/grok-4.3', 'x-ai/grok-build'. Use actor_model_overrides for actor-specific - model selection. + 'gpt-5.6-luna', 'modal/moonshotai/Kimi-K3', 'openrouter/openai/gpt-5.6-luna', + 'openrouter/poolside/laguna-s-2.1', 'poolside/laguna-s-2.1', 'synth_internal/laguna-s-2.1-nvfp4'. + Use actor_model_overrides for actor-specific model selection. agent_harness: anyOf: - type: string @@ -78201,10 +82130,10 @@ components: description: Declarative provider routing preferences. Use agent_inference for agent actor traffic and experiment_inference for benchmark/eval model traffic. Defaults require ZDR and US domicile/region for OpenAI, Baseten, - Gemini, and Grok-family providers. + and Gemini providers. limit: anyOf: - - $ref: '#/components/schemas/UsageLimit' + - $ref: '#/components/schemas/UsageLimit-Input' - type: 'null' description: Optional run-scoped usage limit. required_capabilities: @@ -81088,6 +85017,12 @@ components: status_url: type: string title: Status Url + resource_settlement: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Resource Settlement type: object required: - run_id @@ -82424,7 +86359,9 @@ components: type: string title: Pool Id host_kind: - type: string + anyOf: + - type: string + - type: 'null' title: Host Kind local_execution: anyOf: @@ -82434,7 +86371,6 @@ components: required: - owner - pool_id - - host_kind title: SmrRunLifecycleDispatchResponse SmrRunLifecycleFailureResponse: properties: @@ -83759,6 +87695,10 @@ components: anyOf: - $ref: '#/components/schemas/SmrRunPolicyLimits' - type: 'null' + index: + anyOf: + - $ref: '#/components/schemas/IndexAccessPolicy' + - type: 'null' additionalProperties: false type: object title: SmrRunPolicySpec @@ -83983,6 +87923,16 @@ components: - type: string - type: 'null' title: Effort Id + origin_runtime_kind: + anyOf: + - type: string + - type: 'null' + title: Origin Runtime Kind + origin_runtime_id: + anyOf: + - type: string + - type: 'null' + title: Origin Runtime Id run_kind: type: string enum: @@ -88816,6 +92766,11 @@ components: type: string minLength: 1 title: Kind + description: Transcript event kind, e.g. message.completed, reasoning.summary, + turn.completed, tool.call.completed, or task.completion_claimed (an accepted + worker completion claim projected from its durable claim row; payload + schema smr.task-completion-claimed.v1 with claim_id, task_key, participant_id, + text and summary). payload: additionalProperties: $ref: '#/components/schemas/JsonValue' @@ -89079,7 +93034,6 @@ components: - groq - google - baseten - - xai - daytona - exe_dev - docker @@ -89820,6 +93774,41 @@ components: title: Metadata type: object title: SmrWikiChangeSetCreateRequest + SmrWikiCompactPageRequest: + properties: + page_id: + type: string + title: Page Id + keep_section_id: + type: string + title: Keep Section Id + merged_section_ids: + items: + type: string + type: array + title: Merged Section Ids + merged_body_markdown: + type: string + title: Merged Body Markdown + reason: + type: string + title: Reason + actor_role: + type: string + title: Actor Role + default: operator + source_run_id: + anyOf: + - type: string + - type: 'null' + title: Source Run Id + type: object + required: + - page_id + - keep_section_id + - merged_body_markdown + - reason + title: SmrWikiCompactPageRequest SmrWikiEvidenceLinkCreateRequest: properties: target_kind: @@ -89870,6 +93859,33 @@ components: - target_kind - target_id title: SmrWikiEvidenceLinkCreateRequest + SmrWikiInvalidateSectionRequest: + properties: + section_id: + type: string + title: Section Id + reason: + type: string + title: Reason + falsifier_evidence_link_id: + anyOf: + - type: string + - type: 'null' + title: Falsifier Evidence Link Id + actor_role: + type: string + title: Actor Role + default: operator + source_run_id: + anyOf: + - type: string + - type: 'null' + title: Source Run Id + type: object + required: + - section_id + - reason + title: SmrWikiInvalidateSectionRequest SmrWikiProposalCreateRequest: properties: changeset_id: @@ -89922,6 +93938,37 @@ components: title: Metadata type: object title: SmrWikiProposalCreateRequest + SmrWikiReviewProposalRequest: + properties: + proposal_id: + type: string + title: Proposal Id + verdict: + type: string + title: Verdict + reason: + type: string + title: Reason + merge_into_section_id: + anyOf: + - type: string + - type: 'null' + title: Merge Into Section Id + actor_role: + type: string + title: Actor Role + default: operator + reviewed_in_run_id: + anyOf: + - type: string + - type: 'null' + title: Reviewed In Run Id + type: object + required: + - proposal_id + - verdict + - reason + title: SmrWikiReviewProposalRequest SmrWikiStalenessSignalCreateRequest: properties: target_kind: @@ -89956,6 +94003,32 @@ components: - target_id - reason title: SmrWikiStalenessSignalCreateRequest + SmrWikiSupersedeSectionRequest: + properties: + section_id: + type: string + title: Section Id + by_section_id: + type: string + title: By Section Id + reason: + type: string + title: Reason + actor_role: + type: string + title: Actor Role + default: operator + source_run_id: + anyOf: + - type: string + - type: 'null' + title: Source Run Id + type: object + required: + - section_id + - by_section_id + - reason + title: SmrWikiSupersedeSectionRequest SmrWorkerAppendExperimentLogEntryRequest: properties: project_id: @@ -92075,6 +96148,24 @@ components: - uploads title: StartingDataUploadUrlsResponse description: Presigned PUT URLs for each requested file. + StopReason: + type: string + enum: + - none + - cadence_timer + - judgment_parked + - no_runnable_effort + - tool_in_flight + - publish_in_flight + - actor_reply_pending + - external_execution_active + - evidence_finalizing + - operator_hold + - budget_exhausted + - capability_gate + - infrastructure_failure + - terminal + title: StopReason StoreKeyRequest: properties: provider: @@ -92335,6 +96426,373 @@ components: - generated_at - bounded title: SwarmNotebookResponse + SwarmRolloutArtifact: + properties: + artifact_id: + type: string + title: Artifact Id + artifact_type: + type: string + title: Artifact Type + content_type: + anyOf: + - type: string + - type: 'null' + title: Content Type + size_bytes: + type: integer + title: Size Bytes + created_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Created At + additionalProperties: false + type: object + required: + - artifact_id + - artifact_type + - size_bytes + title: SwarmRolloutArtifact + description: A rollout-owned retained artifact; storage location stays private. + SwarmRolloutExecution: + properties: + recorded_by: + anyOf: + - type: string + - type: 'null' + title: Recorded By + interface: + anyOf: + - type: string + - type: 'null' + title: Interface + interface_mode: + anyOf: + - type: string + - type: 'null' + title: Interface Mode + provider: + anyOf: + - type: string + - type: 'null' + title: Provider + launch_id: + anyOf: + - type: string + - type: 'null' + title: Launch Id + lease: + anyOf: + - $ref: '#/components/schemas/SwarmRolloutLease' + - type: 'null' + lease_disposition: + $ref: '#/components/schemas/SwarmRolloutLeaseDisposition' + deployment_binding: + type: string + title: Deployment Binding + executed_image_digest: + anyOf: + - type: string + - type: 'null' + title: Executed Image Digest + image_attestation: + type: string + title: Image Attestation + executed_source_digest: + anyOf: + - type: string + - type: 'null' + title: Executed Source Digest + launches: + items: + $ref: '#/components/schemas/SwarmRolloutExecutionLaunch' + type: array + title: Launches + recorded_at: + anyOf: + - type: string + - type: 'null' + title: Recorded At + additionalProperties: false + type: object + required: + - lease_disposition + - deployment_binding + - image_attestation + title: SwarmRolloutExecution + description: 'What the Rhodes executor actually launched for this rollout. + + + Written by the worker from the resolved execution target and provider + + launch facts; never from request or result payloads. ``executed_image_digest`` + + is set only when the launch reference was digest-pinned or the provider + + resolved the exact image ID; otherwise ``image_attestation`` says why not.' + SwarmRolloutExecutionLaunch: + properties: + sequence: + type: integer + title: Sequence + role: + anyOf: + - type: string + - type: 'null' + title: Role + provider: + anyOf: + - type: string + - type: 'null' + title: Provider + launch_id: + anyOf: + - type: string + - type: 'null' + title: Launch Id + executed_image_digest: + anyOf: + - type: string + - type: 'null' + title: Executed Image Digest + image_attestation: + type: string + title: Image Attestation + recorded_at: + anyOf: + - type: string + - type: 'null' + title: Recorded At + additionalProperties: false + type: object + required: + - sequence + - image_attestation + title: SwarmRolloutExecutionLaunch + description: 'One provider sandbox launch (Harbor: agent, then optional verifier).' + SwarmRolloutLease: + properties: + lease_id: + type: string + title: Lease Id + additionalProperties: false + type: object + required: + - lease_id + title: SwarmRolloutLease + SwarmRolloutLeaseDisposition: + properties: + status: + type: string + enum: + - recorded + - not_applicable + title: Status + reason: + type: string + title: Reason + additionalProperties: false + type: object + required: + - status + - reason + title: SwarmRolloutLeaseDisposition + description: Why ``lease`` is or is not set; never inferred from a nearby lease. + SwarmRolloutLogicalIntent: + properties: + intent_ref: + type: string + title: Intent Ref + source: + type: string + enum: + - explicit + - derived + title: Source + intent_id: + anyOf: + - type: string + - type: 'null' + title: Intent Id + scope_kind: + type: string + enum: + - smr_run + - intern_runtime + - organization + - intern_delegation + title: Scope Kind + accepted_submissions: + type: integer + minimum: 1.0 + title: Accepted Submissions + additionalProperties: false + type: object + required: + - intent_ref + - source + - scope_kind + - accepted_submissions + title: SwarmRolloutLogicalIntent + description: 'The one intended evaluation this rollout executes (server-bound + scope). + + + ``accepted_submissions`` counts distinct submissions (request keys) that + + attached to this rollout instead of creating another paid execution. + + ``intent_id`` is set only for an explicit caller intent.' + SwarmRolloutPage: + properties: + run_id: + type: string + title: Run Id + items: + items: + $ref: '#/components/schemas/SwarmRolloutSummary' + type: array + title: Items + limit: + type: integer + title: Limit + additionalProperties: false + type: object + required: + - run_id + - items + - limit + title: SwarmRolloutPage + SwarmRolloutReleaseCoordinates: + properties: + runtime_image_release_id: + anyOf: + - type: string + - type: 'null' + title: Runtime Image Release Id + resolved_image_digest: + anyOf: + - type: string + - type: 'null' + title: Resolved Image Digest + shared_bundle_release_id: + anyOf: + - type: string + - type: 'null' + title: Shared Bundle Release Id + task_bundle_release_id: + anyOf: + - type: string + - type: 'null' + title: Task Bundle Release Id + additionalProperties: false + type: object + title: SwarmRolloutReleaseCoordinates + description: Release identities frozen at acceptance, not a native execution + attestation. + SwarmRolloutSummary: + properties: + rollout_id: + type: string + title: Rollout Id + pool_id: + type: string + title: Pool Id + task_id: + anyOf: + - type: string + - type: 'null' + title: Task Id + adapter: + type: string + title: Adapter + status: + type: string + title: Status + seed: + type: integer + title: Seed + trace_correlation_id: + type: string + title: Trace Correlation Id + budget_parent_run_id: + type: string + title: Budget Parent Run Id + release_coordinates: + anyOf: + - $ref: '#/components/schemas/SwarmRolloutReleaseCoordinates' + - type: 'null' + logical_intent: + anyOf: + - $ref: '#/components/schemas/SwarmRolloutLogicalIntent' + - type: 'null' + execution: + anyOf: + - $ref: '#/components/schemas/SwarmRolloutExecution' + - type: 'null' + artifacts: + items: + $ref: '#/components/schemas/SwarmRolloutArtifact' + type: array + title: Artifacts + success: + anyOf: + - type: boolean + - type: 'null' + title: Success + score: + anyOf: + - type: number + - type: 'null' + title: Score + error: + anyOf: + - type: string + - type: 'null' + title: Error + created_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Created At + started_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Started At + completed_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Completed At + cancelled_at: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Cancelled At + additionalProperties: false + type: object + required: + - rollout_id + - pool_id + - adapter + - status + - seed + - trace_correlation_id + - budget_parent_run_id + title: SwarmRolloutSummary + description: 'One pool rollout whose verified budget parent is the swarm. + + + Built field by field from the rollout row, never from the stored request, + + so task-provided environment and transport credentials cannot leak.' SyncApprovalActionV1: properties: schema_version: @@ -93295,6 +97753,30 @@ components: - expires_at - interactive_approval_available title: SyncPresenceLeaseResponse + SyncRuntimeBudget: + properties: + maximum_cost_cents: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Maximum Cost Cents + maximum_daily_cost_cents: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Maximum Daily Cost Cents + maximum_monthly_cost_cents: + anyOf: + - type: integer + minimum: 0.0 + - type: 'null' + title: Maximum Monthly Cost Cents + additionalProperties: false + type: object + title: SyncRuntimeBudget + description: Spend admission ceilings shared by all effects in one Sync session. SyncRuntimeCommandRequest: properties: command_id: @@ -93355,8 +97837,8 @@ components: objective: type: string maxLength: 20000 - minLength: 1 title: Objective + default: '' idempotency_key: type: string maxLength: 512 @@ -93375,14 +97857,23 @@ components: anyOf: - $ref: '#/components/schemas/SyncObjectiveBoundsV1' - type: 'null' + budget: + $ref: '#/components/schemas/SyncRuntimeBudget' require_operator_approval: type: boolean title: Require Operator Approval default: true + index_enabled: + type: boolean + title: Index Enabled + default: false + index_draft_enabled: + type: boolean + title: Index Draft Enabled + default: false additionalProperties: false type: object required: - - objective - idempotency_key title: SyncSessionCreateRequest SyncSessionResponse: @@ -93424,6 +97915,8 @@ components: anyOf: - $ref: '#/components/schemas/SyncObjectiveBoundsV1' - type: 'null' + budget: + $ref: '#/components/schemas/SyncRuntimeBudget' pending_turn_id: anyOf: - type: string @@ -95674,6 +100167,7 @@ components: enum: - smr_control_plane.redis.live_transcript.v1 - smr_transcript_events.terminal_archive.v1 + - smr_transcript_events.durable_replay.v1 title: TranscriptProjectionAuthority description: Backend authority used to serve one transcript page. TranscriptReplayMode: @@ -95681,8 +100175,20 @@ components: enum: - live_tail - terminal_archive + - durable_replay title: TranscriptReplayMode - description: Ordering domain used by transcript page cursors. + description: 'Ordering domain used by transcript page cursors. + + + ``live_tail`` pages come from the Redis live stream while the run is live. + + ``terminal_archive`` pages replay durable transcript rows after the run is + + terminal. ``durable_replay`` pages replay durable rows for a live run whose + + live stream no longer holds the requested range (trimmed or drained) or + + when the caller resumes with a durable cursor.' TransferCreditsRequest: properties: from_user_id: @@ -95991,6 +100497,58 @@ components: - org_id - key_prefix title: UpsertEnvKeyResponse + UsageAggregate: + properties: + schemaVersion: + type: integer + const: 1 + title: Schemaversion + default: 1 + accountId: + type: string + title: Accountid + workspaceId: + anyOf: + - type: string + - type: 'null' + title: Workspaceid + period: + $ref: '#/components/schemas/UsagePeriod' + generatedAt: + type: string + format: date-time + title: Generatedat + freshness: + type: string + enum: + - fresh + - delayed + - stale + title: Freshness + pendingRecords: + type: integer + title: Pendingrecords + totals: + items: + $ref: '#/components/schemas/UsageTotal' + type: array + title: Totals + limits: + items: + $ref: '#/components/schemas/UsageLimit-Output' + type: array + title: Limits + additionalProperties: false + type: object + required: + - accountId + - period + - generatedAt + - freshness + - pendingRecords + - totals + - limits + title: UsageAggregate UsageBreakdownValueResponse: properties: nominal_usage_usd: @@ -96329,7 +100887,7 @@ components: - usage_item_id - quantity title: UsageItemIn - UsageLimit: + UsageLimit-Input: properties: max_spend_usd: anyOf: @@ -96364,6 +100922,40 @@ components: additionalProperties: false type: object title: UsageLimit + UsageLimit-Output: + properties: + service: + type: string + title: Service + unit: + type: string + enum: + - request + - token + - compute_second + - job + - usd + title: Unit + limit: + type: string + title: Limit + remaining: + type: string + title: Remaining + resetAt: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Resetat + additionalProperties: false + type: object + required: + - service + - unit + - limit + - remaining + title: UsageLimit UsageMetricResponse: properties: used: @@ -96406,6 +100998,150 @@ components: - spend_summary - sources title: UsageOverviewResponse + UsagePeriod: + properties: + start: + type: string + format: date-time + title: Start + end: + type: string + format: date-time + title: End + timezone: + type: string + title: Timezone + default: UTC + additionalProperties: false + type: object + required: + - start + - end + title: UsagePeriod + UsageRecordIn: + properties: + schemaVersion: + type: integer + const: 1 + title: Schemaversion + default: 1 + recordId: + type: string + maxLength: 255 + minLength: 1 + title: Recordid + idempotencyKey: + type: string + maxLength: 255 + minLength: 1 + title: Idempotencykey + accountId: + type: string + maxLength: 255 + minLength: 1 + title: Accountid + workspaceId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Workspaceid + projectId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Projectid + runId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Runid + service: + type: string + maxLength: 100 + minLength: 1 + title: Service + provider: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Provider + model: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Model + workflowFamily: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Workflowfamily + occurredAt: + type: string + format: date-time + title: Occurredat + quantity: + type: string + title: Quantity + unit: + type: string + enum: + - request + - token + - compute_second + - job + - usd + title: Unit + priceVersion: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Priceversion + costUsd: + anyOf: + - type: string + - type: 'null' + title: Costusd + state: + type: string + enum: + - pending + - finalized + - corrected + title: State + correctsRecordId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Correctsrecordid + additionalProperties: false + type: object + required: + - recordId + - idempotencyKey + - accountId + - service + - occurredAt + - quantity + - unit + - state + title: UsageRecordIn + description: Runtime-supplied immutable usage fact or explicit correction. UsageRecordItemsIn: properties: job_id: @@ -96491,6 +101227,135 @@ components: - line_items - cost_cents title: UsageRecordItemsOut + UsageRecordOut: + properties: + schemaVersion: + type: integer + const: 1 + title: Schemaversion + default: 1 + recordId: + type: string + maxLength: 255 + minLength: 1 + title: Recordid + idempotencyKey: + type: string + maxLength: 255 + minLength: 1 + title: Idempotencykey + accountId: + type: string + maxLength: 255 + minLength: 1 + title: Accountid + workspaceId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Workspaceid + projectId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Projectid + runId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Runid + service: + type: string + maxLength: 100 + minLength: 1 + title: Service + provider: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Provider + model: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Model + workflowFamily: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Workflowfamily + occurredAt: + type: string + format: date-time + title: Occurredat + quantity: + type: string + title: Quantity + unit: + type: string + enum: + - request + - token + - compute_second + - job + - usd + title: Unit + priceVersion: + anyOf: + - type: string + maxLength: 100 + minLength: 1 + - type: 'null' + title: Priceversion + costUsd: + anyOf: + - type: string + - type: 'null' + title: Costusd + state: + type: string + enum: + - pending + - finalized + - corrected + title: State + correctsRecordId: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Correctsrecordid + receivedAt: + type: string + format: date-time + title: Receivedat + additionalProperties: false + type: object + required: + - recordId + - idempotencyKey + - accountId + - service + - occurredAt + - quantity + - unit + - state + - receivedAt + title: UsageRecordOut + description: Server-accepted record with authoritative receipt time. UsageRecordResponse: properties: id: @@ -96684,6 +101549,61 @@ components: - total_charged_usd - total_internal_cost_usd title: UsageSummaryResponse + UsageTotal: + properties: + service: + type: string + title: Service + workflowFamily: + anyOf: + - type: string + - type: 'null' + title: Workflowfamily + provider: + anyOf: + - type: string + - type: 'null' + title: Provider + model: + anyOf: + - type: string + - type: 'null' + title: Model + unit: + type: string + enum: + - request + - token + - compute_second + - job + - usd + title: Unit + pendingQuantity: + type: string + title: Pendingquantity + finalizedQuantity: + type: string + title: Finalizedquantity + pendingCostUsd: + type: string + title: Pendingcostusd + finalizedCostUsd: + type: string + title: Finalizedcostusd + recordCount: + type: integer + title: Recordcount + additionalProperties: false + type: object + required: + - service + - unit + - pendingQuantity + - finalizedQuantity + - pendingCostUsd + - finalizedCostUsd + - recordCount + title: UsageTotal UsageType: type: string enum: @@ -96808,6 +101728,11 @@ components: type: type: string title: Error Type + input: + title: Input + ctx: + type: object + title: Context type: object required: - loc @@ -97452,6 +102377,19 @@ components: - lora_id - base_model_alias title: VllmLoraUploadUrlRequest + WaitProducer: + type: string + enum: + - run + - swarm + - effort + - actor_reply + - operator_answer + - evidence + - world_sync + title: WaitProducer + description: "Closed vocabulary for a named ambient wait (L1).\n\n# See: INTERN_ASYNC_RUNTIME_PHASE_REFACTOR_HANDOFF_2026-08-08.md\ + \ \xA75 L1" WebhookAddCreditsRequest: properties: org_id: @@ -97644,6 +102582,427 @@ components: - failed - deleted title: WorkProductStatus + WorkshopArtifactCommittedResponse: + properties: + schema_version: + type: string + const: synth.workshop-artifact-upload.v1 + title: Schema Version + default: synth.workshop-artifact-upload.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + collection_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Collection Id + revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Revision + manifest_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Manifest Digest + status: + type: string + const: committed + title: Status + committed_url: + type: string + title: Committed Url + additionalProperties: false + type: object + required: + - publication_id + - collection_id + - revision + - manifest_digest + - status + - committed_url + title: WorkshopArtifactCommittedResponse + WorkshopArtifactFinalizeRequest: + properties: + schema_version: + type: string + const: synth.workshop-artifact-upload.v1 + title: Schema Version + default: synth.workshop-artifact-upload.v1 + visual_id: + type: string + maxLength: 255 + minLength: 1 + title: Visual Id + receipt_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Receipt Digest + additionalProperties: false + type: object + required: + - visual_id + - receipt_digest + title: WorkshopArtifactFinalizeRequest + WorkshopArtifactPrepareRequest: + properties: + schema_version: + type: string + const: synth.workshop-artifact-upload.v1 + title: Schema Version + default: synth.workshop-artifact-upload.v1 + visual_id: + type: string + maxLength: 255 + minLength: 1 + title: Visual Id + visual_revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Visual Revision + receipt_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Receipt Digest + bundle_schema_version: + type: string + const: synth.artifact-bundle.v1 + title: Bundle Schema Version + default: synth.artifact-bundle.v1 + objects: + items: + $ref: '#/components/schemas/ArtifactObjectDeclaration' + type: array + title: Objects + additionalProperties: false + type: object + required: + - visual_id + - visual_revision + - receipt_digest + - objects + title: WorkshopArtifactPrepareRequest + WorkshopArtifactPrepareResponse: + properties: + schema_version: + type: string + const: synth.workshop-artifact-upload.v1 + title: Schema Version + default: synth.workshop-artifact-upload.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + collection_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Collection Id + revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Revision + manifest_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Manifest Digest + upload_targets: + items: + $ref: '#/components/schemas/ArtifactUploadTarget' + type: array + title: Upload Targets + additionalProperties: false + type: object + required: + - publication_id + - collection_id + - revision + - manifest_digest + - upload_targets + title: WorkshopArtifactPrepareResponse + WorkshopReportAudience: + properties: + kind: + type: string + enum: + - private + - workspace + - members + title: Kind + workspace_id: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Workspace Id + member_ids: + items: + type: string + maxLength: 255 + minLength: 1 + type: array + title: Member Ids + default: [] + additionalProperties: false + type: object + required: + - kind + title: WorkshopReportAudience + WorkshopReportCommittedResponse: + properties: + schema_version: + type: string + const: synth.workshop-report-upload.v1 + title: Schema Version + default: synth.workshop-report-upload.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + collection_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Collection Id + revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Revision + manifest_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Manifest Digest + status: + type: string + const: committed + title: Status + committed_url: + type: string + title: Committed Url + additionalProperties: false + type: object + required: + - publication_id + - collection_id + - revision + - manifest_digest + - status + - committed_url + title: WorkshopReportCommittedResponse + WorkshopReportFinalizeRequest: + properties: + schema_version: + type: string + const: synth.workshop-report-upload.v1 + title: Schema Version + default: synth.workshop-report-upload.v1 + report_id: + type: string + maxLength: 255 + minLength: 1 + title: Report Id + receipt_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Receipt Digest + additionalProperties: false + type: object + required: + - report_id + - receipt_digest + title: WorkshopReportFinalizeRequest + WorkshopReportPrepareRequest: + properties: + schema_version: + type: string + const: synth.workshop-report-upload.v1 + title: Schema Version + default: synth.workshop-report-upload.v1 + report_id: + type: string + maxLength: 255 + minLength: 1 + title: Report Id + report_revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Report Revision + receipt_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Receipt Digest + bundle_schema_version: + type: string + const: synth.report-bundle.v1 + title: Bundle Schema Version + default: synth.report-bundle.v1 + objects: + items: + $ref: '#/components/schemas/ArtifactObjectDeclaration' + type: array + title: Objects + additionalProperties: false + type: object + required: + - report_id + - report_revision + - receipt_digest + - objects + title: WorkshopReportPrepareRequest + WorkshopReportPrepareResponse: + properties: + schema_version: + type: string + const: synth.workshop-report-upload.v1 + title: Schema Version + default: synth.workshop-report-upload.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + collection_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Collection Id + revision: + type: integer + maximum: 2147483647.0 + minimum: 1.0 + title: Revision + manifest_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Manifest Digest + upload_targets: + items: + $ref: '#/components/schemas/ArtifactUploadTarget' + type: array + title: Upload Targets + additionalProperties: false + type: object + required: + - publication_id + - collection_id + - revision + - manifest_digest + - upload_targets + title: WorkshopReportPrepareResponse + WorkshopReportPromoteRequest: + properties: + schema_version: + type: string + const: synth.workshop-report-promotion.v1 + title: Schema Version + default: synth.workshop-report-promotion.v1 + slug: + type: string + maxLength: 96 + pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$ + title: Slug + approval_request_id: + anyOf: + - type: string + maxLength: 255 + minLength: 1 + - type: 'null' + title: Approval Request Id + reason: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Reason + additionalProperties: false + type: object + required: + - slug + title: WorkshopReportPromoteRequest + WorkshopReportPromotionResponse: + properties: + schema_version: + type: string + const: synth.workshop-report-promotion.v1 + title: Schema Version + default: synth.workshop-report-promotion.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + slug: + type: string + title: Slug + status: + type: string + enum: + - published + - unpublished + title: Status + public_url: + type: string + title: Public Url + additionalProperties: false + type: object + required: + - publication_id + - slug + - status + - public_url + title: WorkshopReportPromotionResponse + WorkshopReportShareRequest: + properties: + schema_version: + type: string + const: synth.workshop-report-sharing.v1 + title: Schema Version + default: synth.workshop-report-sharing.v1 + receipt_digest: + type: string + pattern: ^[0-9a-f]{64}$ + title: Receipt Digest + audience: + $ref: '#/components/schemas/WorkshopReportAudience' + redaction_policy_version: + type: string + maxLength: 100 + minLength: 1 + title: Redaction Policy Version + additionalProperties: false + type: object + required: + - receipt_digest + - audience + - redaction_policy_version + title: WorkshopReportShareRequest + WorkshopReportShareResponse: + properties: + schema_version: + type: string + const: synth.workshop-report-sharing.v1 + title: Schema Version + default: synth.workshop-report-sharing.v1 + publication_id: + type: string + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + title: Publication Id + audience: + $ref: '#/components/schemas/WorkshopReportAudience' + status: + type: string + enum: + - active + - revoked + title: Status + additionalProperties: false + type: object + required: + - publication_id + - audience + - status + title: WorkshopReportShareResponse WorkspaceConfirmPushRequest: properties: commit_sha: diff --git a/synth_ai/client.py b/synth_ai/client.py index caa0102b..29312a12 100644 --- a/synth_ai/client.py +++ b/synth_ai/client.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: from synth_ai.core.http.async_transport import AsyncHttpTransport from synth_ai.core.http.transport import HttpTransport + from synth_ai.pools import PoolClient from synth_ai.sdk.index.client import AsyncIndexAPI, IndexAPI from synth_ai.sdk.messaging import AsyncMessagingClient, MessagingClient from synth_ai.sdk.optimizers import AsyncOptimizersClient, OptimizersClient @@ -143,6 +144,7 @@ def __init__( self._async_optimizers_client: AsyncOptimizersClient | None = None self._index_api: AsyncIndexAPI | None = None self._index_transport: AsyncHttpTransport | None = None + self._pool_client: PoolClient | None = None self._async_messaging_client: AsyncMessagingClient | None = None @property @@ -186,6 +188,25 @@ def research(self) -> AsyncResearchClient: ) return self._async_research_client + @property + def pools(self) -> PoolClient: + """Backend-owned pool, deployment and interactive lease operations. + + See: evals/docs/handoffs/EVAL_EXECUTION_STREAMING_DELIVERY_PLAN_2026-09-10.md §3. + Uses the canonical synth-containers client and this client's existing + backend credential. Requires the optional ``synth-ai[pools]`` extra. + Requests always pass through hosted admission and resource ownership. + """ + if self._pool_client is None: + from synth_ai.pools import PoolClient + + self._pool_client = PoolClient( + api_key=self.api_key, + backend_url=self.base_url, + timeout_seconds=self.timeout_seconds, + ) + return self._pool_client + @property def async_research(self) -> AsyncResearchClient: """Deprecated alias for :attr:`research`.""" @@ -198,19 +219,30 @@ def async_research(self) -> AsyncResearchClient: async def close(self) -> None: """Close all asynchronous Research transports.""" - if self._index_transport is not None: - await self._index_transport.close() - self._index_transport = None - self._index_api = None - if self._async_messaging_client is not None: - await self._async_messaging_client.close() - self._async_messaging_client = None - if self._async_research_client is not None: - await self._async_research_client.close() - self._async_research_client = None - if self._async_optimizers_client is not None: - await self._async_optimizers_client.close() - self._async_optimizers_client = None + try: + if self._index_transport is not None: + await self._index_transport.close() + self._index_transport = None + self._index_api = None + finally: + try: + if self._async_messaging_client is not None: + await self._async_messaging_client.close() + self._async_messaging_client = None + finally: + try: + if self._async_research_client is not None: + await self._async_research_client.close() + self._async_research_client = None + finally: + try: + if self._async_optimizers_client is not None: + await self._async_optimizers_client.close() + self._async_optimizers_client = None + finally: + if self._pool_client is not None: + await self._pool_client.aclose() + self._pool_client = None @property def optimizers(self) -> AsyncOptimizersClient: diff --git a/synth_ai/mcp/research/tools/progress.py b/synth_ai/mcp/research/tools/progress.py index efd5f318..589f618c 100644 --- a/synth_ai/mcp/research/tools/progress.py +++ b/synth_ai/mcp/research/tools/progress.py @@ -90,7 +90,7 @@ def _provider_bindings_schema() -> dict[str, Any]: def _provider_selection_schema() -> dict[str, Any]: - public = ["auto", "openai", "synth", "xai", "cursor"] + public = ["auto", "openai", "synth", "cursor"] return { "oneOf": [ {"type": "string", "enum": public}, diff --git a/synth_ai/mcp/research/tools/runs.py b/synth_ai/mcp/research/tools/runs.py index 44477db4..b38a3ffd 100644 --- a/synth_ai/mcp/research/tools/runs.py +++ b/synth_ai/mcp/research/tools/runs.py @@ -168,7 +168,7 @@ def _provider_bindings_schema() -> dict[str, Any]: def _provider_selection_schema() -> dict[str, Any]: - public = ["auto", "openai", "synth", "xai", "cursor"] + public = ["auto", "openai", "synth", "cursor"] return { "oneOf": [ {"type": "string", "enum": public}, @@ -568,7 +568,7 @@ def build_run_tools(server: Any) -> list[ToolDefinition]: "agent_model": { "type": "string", "enum": list(SMR_AGENT_MODEL_VALUES), - "description": "Optional run-level agent model override using a backend catalog model id such as gpt-5.4-mini, x-ai/grok-4.3, or x-ai/grok-build." + "description": "Optional run-level agent model override using a backend catalog model id such as gpt-5.4-mini or openrouter/openai/gpt-5.6-luna." + _HOSTED_LOCAL_ONLY_NOTE, }, "agent_harness": { diff --git a/synth_ai/pools.py b/synth_ai/pools.py new file mode 100644 index 00000000..30904110 --- /dev/null +++ b/synth_ai/pools.py @@ -0,0 +1,24 @@ +"""Canonical async container-pool client, re-exported without a second transport. + +See: evals/docs/handoffs/EVAL_EXECUTION_STREAMING_DELIVERY_PLAN_2026-09-10.md §3. +Prefer ``AsyncSynthClient().pools`` to inherit the configured backend identity; +``PoolClient`` is available for callers managing its asynchronous lifetime. +""" + +from __future__ import annotations + +try: + from synth_containers.pools import ( + PoolClient, + PoolClientError, + PoolRolloutTimeout, + PoolStateError, + ) +except ModuleNotFoundError as error: + if error.name not in {"synth_containers", "synth_containers.pools"}: + raise + raise ImportError( + "Container pools require synth-ai[pools] and its matching synth-containers artifact" + ) from error + +__all__ = ["PoolClient", "PoolClientError", "PoolRolloutTimeout", "PoolStateError"] diff --git a/synth_ai/sdk/research/client.py b/synth_ai/sdk/research/client.py index 96e1661f..7187fb8f 100644 --- a/synth_ai/sdk/research/client.py +++ b/synth_ai/sdk/research/client.py @@ -8,6 +8,7 @@ from synth_ai.core.http.async_transport import AsyncHttpTransport from synth_ai.core.http.transport import HttpTransport from synth_ai.core.utils.urls import BACKEND_URL_BASE, normalize_backend_base +from synth_ai.sdk.research.container_pools.api import ContainerPoolsAPI from synth_ai.sdk.research.environments import AsyncEnvironmentsAPI, EnvironmentsAPI from synth_ai.sdk.research.factories import AsyncFactoriesAPI, FactoriesAPI from synth_ai.sdk.research.image_releases import ( @@ -58,6 +59,7 @@ def __init__( self.factories = FactoriesAPI(self._transport) self.environments = EnvironmentsAPI(self._transport) self.image_releases = ImageReleasesAPI(self._transport) + self.container_pools = ContainerPoolsAPI(self._transport) self.traces = ResearchTracesAPI(self._transport) self.visuals = VisualsAPI(self._transport) self._economics: EconomicsAPI | None = None diff --git a/synth_ai/sdk/research/container_pools/__init__.py b/synth_ai/sdk/research/container_pools/__init__.py new file mode 100644 index 00000000..0dc1cffb --- /dev/null +++ b/synth_ai/sdk/research/container_pools/__init__.py @@ -0,0 +1,55 @@ +"""Container pools: deploy a container to a pool and run graded rollouts. + +Reached as ``SynthClient().research.container_pools``. Note the neighbouring +``client.image_releases`` is a different object — that one is the SMR actor +runtime surface at ``/smr/v1/image-releases``; a pool's runtime image release +lives under ``container_pools.create_release``. +""" + +from synth_ai.sdk.research.container_pools.api import ( + DEFAULT_POLL_INTERVAL_SECONDS, + ContainerPoolsAPI, + RolloutTimeoutError, +) +from synth_ai.sdk.research.container_pools.contracts import ( + HARBOR_CONTAINER_SUBTYPE, + TERMINAL_ROLLOUT_STATUSES, + Pool, + PoolId, + PoolTask, + PoolTaskId, + Rollout, + RolloutArtifact, + RolloutId, + RuntimeImageRelease, + RuntimeImageReleaseId, +) +from synth_ai.sdk.research.container_pools.packaging import ( + DockerContextArchive, + HarborBundle, + HarborBundleError, + build_docker_context_archive, + build_harbor_bundle_archive, +) + +__all__ = [ + "DEFAULT_POLL_INTERVAL_SECONDS", + "HARBOR_CONTAINER_SUBTYPE", + "TERMINAL_ROLLOUT_STATUSES", + "ContainerPoolsAPI", + "DockerContextArchive", + "HarborBundle", + "HarborBundleError", + "Pool", + "PoolId", + "PoolTask", + "PoolTaskId", + "Rollout", + "RolloutArtifact", + "RolloutId", + "RolloutTimeoutError", + "RuntimeImageRelease", + "RuntimeImageReleaseId", + "build_docker_context_archive", + "build_harbor_bundle_archive", +] diff --git a/synth_ai/sdk/research/container_pools/api.py b/synth_ai/sdk/research/container_pools/api.py new file mode 100644 index 00000000..9ce8bd5e --- /dev/null +++ b/synth_ai/sdk/research/container_pools/api.py @@ -0,0 +1,538 @@ +"""Container pools: create, deploy a release, bind it, run rollouts. + +The deployment primitive is the **runtime image release**, not the pool. A pool +holds tasks; a release holds the image (and, for the Harbor subtype, the task +bundle); binding a release to a task is what builds and attaches a snapshot. +Several tasks in one pool may bind the same release, which is what makes a +taskset cost one build. +""" + +from __future__ import annotations + +import time +from collections.abc import Mapping +from pathlib import Path +from typing import Any +from urllib.parse import quote + +from synth_ai.core.contracts.json_value import JsonObject, JsonValue +from synth_ai.core.http.request import HttpRequest +from synth_ai.core.http.transport import HttpTransport +from synth_ai.sdk.research.container_pools.contracts import ( + HARBOR_CONTAINER_SUBTYPE, + Pool, + PoolId, + PoolTask, + PoolTaskId, + Rollout, + RolloutArtifact, + RolloutId, + RuntimeImageRelease, + RuntimeImageReleaseId, +) +from synth_ai.sdk.research.container_pools.operations import container_pool_operation +from synth_ai.sdk.research.container_pools.packaging import build_harbor_bundle_archive + +#: Poll interval while waiting on a rollout. Harbor rollouts run for minutes to +#: hours, so a tight loop buys nothing and costs request quota. +DEFAULT_POLL_INTERVAL_SECONDS = 5.0 + + +class RolloutTimeoutError(TimeoutError): + """A rollout did not reach a terminal status inside the wait budget.""" + + +def _request( + operation_id: str, + path: str, + *, + query: JsonObject | None = None, + body: JsonObject | None = None, + timeout_seconds: float | None = None, +) -> HttpRequest: + return HttpRequest( + container_pool_operation(operation_id), + path, + query=query or {}, + body=body, + timeout_seconds=timeout_seconds, + ) + + +def _seg(value: str, *, field_name: str) -> str: + text = str(value or "").strip() + if not text: + raise ValueError(f"{field_name} must be a non-empty string") + return quote(text, safe="") + + +def _items(value: JsonValue, *keys: str) -> list[JsonValue]: + """Unwrap a list response that may be bare or wrapped under a key.""" + if isinstance(value, list): + return value + if isinstance(value, dict): + for key in keys: + nested = value.get(key) + if isinstance(nested, list): + return nested + return [] + + +def _json_object(value: Mapping[str, Any] | None) -> JsonObject: + return {str(key): item for key, item in dict(value or {}).items()} + + +class ContainerPoolsAPI: + """Pools, their tasks, their releases, and their rollouts.""" + + def __init__(self, transport: HttpTransport) -> None: + self._transport = transport + + # -- pools ---------------------------------------------------------- + + def list(self) -> tuple[Pool, ...]: + value = self._transport.execute(_request("list_container_pools", "/v1/pools")) + return tuple(Pool.from_wire(item) for item in _items(value, "pools", "data")) + + def create( + self, + *, + pool_id: str, + backend: str = "arbitrary", + name: str | None = None, + metadata: Mapping[str, Any] | None = None, + **extra: Any, + ) -> Pool: + """Create a pool. + + ``backend`` defaults to ``arbitrary``: runtime image releases are only + accepted on ``arbitrary`` and ``smr_session`` pools, so a Harbor task + belongs on an arbitrary pool with ``container_subtype="harbor"`` on its + release — not on a ``backend="harbor"`` pool. + """ + body: JsonObject = {"pool_id": pool_id, "backend": backend} + if name: + body["name"] = name + if metadata: + body["metadata"] = _json_object(metadata) + body.update(_json_object(extra)) + return Pool.from_wire( + self._transport.execute(_request("create_container_pool", "/v1/pools", body=body)) + ) + + def get(self, pool_id: PoolId | str) -> Pool: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + return Pool.from_wire(self._transport.execute(_request("get_container_pool", path))) + + def delete(self, pool_id: PoolId | str) -> None: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + self._transport.execute(_request("delete_container_pool", path)) + + # -- tasks ---------------------------------------------------------- + + def list_tasks(self, pool_id: PoolId | str) -> tuple[PoolTask, ...]: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/tasks" + value = self._transport.execute(_request("list_container_pool_tasks", path)) + return tuple(PoolTask.from_wire(item) for item in _items(value, "tasks", "data")) + + def create_task( + self, + pool_id: PoolId | str, + *, + task_id: str, + backend: str = "arbitrary", + task_config: Mapping[str, Any] | None = None, + task_metadata: Mapping[str, Any] | None = None, + ) -> PoolTask: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/tasks" + body: JsonObject = {"task_id": task_id, "backend": backend} + if task_config: + body["task_config"] = _json_object(task_config) + if task_metadata: + body["task_metadata"] = _json_object(task_metadata) + return PoolTask.from_wire( + self._transport.execute(_request("create_container_pool_task", path, body=body)) + ) + + def update_task( + self, + pool_id: PoolId | str, + task_id: PoolTaskId | str, + request: Mapping[str, Any], + ) -> PoolTask: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/tasks/{_seg(task_id, field_name='task_id')}" + ) + return PoolTask.from_wire( + self._transport.execute( + _request("update_container_pool_task", path, body=_json_object(request)) + ) + ) + + def delete_task(self, pool_id: PoolId | str, task_id: PoolTaskId | str) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/tasks/{_seg(task_id, field_name='task_id')}" + ) + return self._transport.execute(_request("delete_container_pool_task", path)) + + # -- releases ------------------------------------------------------- + + def list_releases(self, pool_id: PoolId | str) -> tuple[RuntimeImageRelease, ...]: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/runtime_image_releases" + value = self._transport.execute( + _request("list_container_pool_runtime_image_releases", path) + ) + return tuple( + RuntimeImageRelease.from_wire(item) + for item in _items(value, "runtime_image_releases", "releases", "data") + ) + + def create_release( + self, + pool_id: PoolId | str, + *, + source_kind: str, + archive_base64: str | None = None, + image_ref: str | None = None, + compute_provider: str | None = None, + name: str | None = None, + dockerfile_path: str | None = None, + metadata: Mapping[str, Any] | None = None, + upload_timeout_seconds: float = 600.0, + ) -> RuntimeImageRelease: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/runtime_image_releases" + body: JsonObject = {"source_kind": source_kind} + if archive_base64: + body["archive_base64"] = archive_base64 + if image_ref: + body["image_ref"] = image_ref + if compute_provider: + body["compute_provider"] = compute_provider + if name: + body["name"] = name + if dockerfile_path: + body["dockerfile_path"] = dockerfile_path + if metadata: + body["metadata"] = _json_object(metadata) + return RuntimeImageRelease.from_wire( + self._transport.execute( + _request( + "create_container_pool_runtime_image_release", + path, + body=body, + # A bundle upload is a single large request; the default + # transport timeout is sized for control-plane calls. + timeout_seconds=upload_timeout_seconds, + ) + ) + ) + + def create_harbor_release( + self, + pool_id: PoolId | str, + *, + bundle_dir: str | Path, + model_name: str, + reasoning_effort: str = "medium", + harness_subtype: str = "codex", + compute_provider: str | None = None, + name: str | None = None, + dockerfile_path: str | None = None, + metadata: Mapping[str, Any] | None = None, + allow_credential_files: bool = False, + collect_phase_logs: bool | None = None, + ) -> RuntimeImageRelease: + """Package a Harbor task bundle and publish it as a release. + + Rejects a bundle that cannot deploy before uploading it — see + :mod:`.packaging` for the build-context and credential rules. + """ + bundle = build_harbor_bundle_archive( + bundle_dir, + dockerfile_path=dockerfile_path or "environment/Dockerfile", + allow_credential_files=allow_credential_files, + ) + release_metadata: JsonObject = { + "container_subtype": HARBOR_CONTAINER_SUBTYPE, + "container_harness_subtype": harness_subtype, + "model_name": model_name, + "harness_kwargs": {"reasoning_effort": reasoning_effort}, + } + if dockerfile_path: + release_metadata["harbor_dockerfile_path"] = dockerfile_path + if collect_phase_logs is not None: + # Whether the backend collects /logs from the sandbox into rollout + # artifacts. On by default; a failed rollout otherwise discards the + # only evidence of why the agent phase failed, and every distinct + # failure reports the same exit code. + release_metadata["collect_phase_logs"] = bool(collect_phase_logs) + release_metadata.update(_json_object(metadata)) + return self.create_release( + pool_id, + source_kind="docker_context", + archive_base64=bundle.archive_base64, + compute_provider=compute_provider, + name=name or bundle.task_name, + dockerfile_path=dockerfile_path, + metadata=release_metadata, + ) + + def get_release( + self, pool_id: PoolId | str, release_id: RuntimeImageReleaseId | str + ) -> RuntimeImageRelease: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/runtime_image_releases/{_seg(release_id, field_name='release_id')}" + ) + return RuntimeImageRelease.from_wire( + self._transport.execute(_request("get_container_pool_runtime_image_release", path)) + ) + + def delete_release( + self, + pool_id: PoolId | str, + release_id: RuntimeImageReleaseId | str, + ) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/runtime_image_releases/{_seg(release_id, field_name='release_id')}" + ) + return self._transport.execute( + _request("delete_container_pool_runtime_image_release", path) + ) + + def bind_release( + self, + pool_id: PoolId | str, + release_id: RuntimeImageReleaseId | str, + *, + bind_timeout_seconds: float = 1800.0, + ) -> JsonValue: + """Bind a release to the pool's tasks, building the snapshot. + + This is the slow call: it builds an image. Pool-level bind currently + requires the pool to hold exactly one active task. + """ + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/runtime_image_releases/{_seg(release_id, field_name='release_id')}/bind" + ) + return self._transport.execute( + _request( + "bind_container_pool_runtime_image_release", + path, + timeout_seconds=bind_timeout_seconds, + ) + ) + + def bind_task_release( + self, + pool_id: PoolId | str, + task_id: PoolTaskId | str, + release_id: RuntimeImageReleaseId | str, + *, + expected_release_id: RuntimeImageReleaseId | str | None = None, + bind_timeout_seconds: float = 1800.0, + ) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/tasks/{_seg(task_id, field_name='task_id')}" + f"/runtime_image_releases/{_seg(release_id, field_name='release_id')}/bind" + ) + query: JsonObject = {} + if expected_release_id is not None: + query["expected_release_id"] = str(expected_release_id) + return self._transport.execute( + _request( + "bind_container_pool_task_runtime_image_release", + path, + query=query, + timeout_seconds=bind_timeout_seconds, + ) + ) + + # -- project-bound deployments ------------------------------------ + + def get_deployment( + self, + pool_id: PoolId | str, + task_id: PoolTaskId | str, + *, + project_id: str, + ) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/deployments/{_seg(task_id, field_name='task_id')}" + ) + return self._transport.execute( + _request("get_container_pool_deployment", path, query={"project_id": project_id}) + ) + + def mutate_deployment( + self, + pool_id: PoolId | str, + task_id: PoolTaskId | str, + *, + project_id: str, + operation: str, + idempotency_key: str, + payload: Mapping[str, Any] | None = None, + expected_revision: str | None = None, + ) -> JsonValue: + if operation not in {"create", "update", "delete"}: + raise ValueError("operation must be create, update, or delete") + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/deployments/{_seg(task_id, field_name='task_id')}/operations" + ) + body: JsonObject = { + "project_id": project_id, + "operation": operation, + "idempotency_key": idempotency_key, + "payload": _json_object(payload), + } + if expected_revision is not None: + body["expected_revision"] = expected_revision + return self._transport.execute( + _request("mutate_container_pool_deployment", path, body=body) + ) + + def get_deployment_operation( + self, + pool_id: PoolId | str, + operation_id: str, + *, + project_id: str, + ) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/deployment-operations/{_seg(operation_id, field_name='operation_id')}" + ) + return self._transport.execute( + _request( + "get_container_pool_deployment_operation", + path, + query={"project_id": project_id}, + ) + ) + + def find_deployment_operation( + self, + pool_id: PoolId | str, + task_id: PoolTaskId | str, + *, + project_id: str, + idempotency_key: str, + ) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/deployments/{_seg(task_id, field_name='task_id')}/operations" + ) + return self._transport.execute( + _request( + "find_container_pool_deployment_operation", + path, + query={"project_id": project_id, "idempotency_key": idempotency_key}, + ) + ) + + # -- rollouts ------------------------------------------------------- + + def create_rollout( + self, + pool_id: PoolId | str, + *, + task_id: str | None = None, + seed: int | None = None, + policy: Mapping[str, Any] | None = None, + metadata: Mapping[str, Any] | None = None, + env: Mapping[str, Any] | None = None, + **extra: Any, + ) -> Rollout: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/rollouts" + body: JsonObject = {} + if task_id: + body["task_id"] = task_id + if seed is not None: + body["seed"] = int(seed) + if policy: + # The backend 422s when policy.model disagrees with the task-pinned + # harbor_agent.model_name, so leaving this unset is usually right. + body["policy"] = _json_object(policy) + if metadata: + body["metadata"] = _json_object(metadata) + if env: + body["env"] = _json_object(env) + body.update(_json_object(extra)) + return Rollout.from_wire( + self._transport.execute(_request("create_container_pool_rollout", path, body=body)) + ) + + def get_rollout(self, pool_id: PoolId | str, rollout_id: RolloutId | str) -> Rollout: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/rollouts/{_seg(rollout_id, field_name='rollout_id')}" + ) + return Rollout.from_wire( + self._transport.execute(_request("get_container_pool_rollout", path)) + ) + + def list_rollouts(self, pool_id: PoolId | str) -> tuple[Rollout, ...]: + path = f"/v1/pools/{_seg(pool_id, field_name='pool_id')}/rollouts" + value = self._transport.execute(_request("list_container_pool_rollouts", path)) + return tuple(Rollout.from_wire(item) for item in _items(value, "rollouts", "data")) + + def cancel_rollout(self, pool_id: PoolId | str, rollout_id: RolloutId | str) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/rollouts/{_seg(rollout_id, field_name='rollout_id')}/cancel" + ) + return self._transport.execute(_request("cancel_container_pool_rollout", path)) + + def list_rollout_artifacts( + self, pool_id: PoolId | str, rollout_id: RolloutId | str + ) -> tuple[RolloutArtifact, ...]: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/rollouts/{_seg(rollout_id, field_name='rollout_id')}/artifacts" + ) + value = self._transport.execute(_request("list_container_pool_rollout_artifacts", path)) + return tuple(RolloutArtifact.from_wire(item) for item in _items(value, "artifacts", "data")) + + def get_rollout_usage(self, pool_id: PoolId | str, rollout_id: RolloutId | str) -> JsonValue: + path = ( + f"/v1/pools/{_seg(pool_id, field_name='pool_id')}" + f"/rollouts/{_seg(rollout_id, field_name='rollout_id')}/usage" + ) + return self._transport.execute(_request("get_container_pool_rollout_usage", path)) + + def wait_for_rollout( + self, + pool_id: PoolId | str, + rollout_id: RolloutId | str, + *, + timeout_seconds: float, + poll_interval_seconds: float = DEFAULT_POLL_INTERVAL_SECONDS, + ) -> Rollout: + """Poll until the rollout is terminal, or raise :class:`RolloutTimeoutError`. + + Raising rather than returning a non-terminal rollout keeps a timeout + from being mistaken for a failed run: the rollout is still executing, + and its result is still retrievable later. + """ + deadline = time.monotonic() + float(timeout_seconds) + while True: + rollout = self.get_rollout(pool_id, rollout_id) + if rollout.is_terminal: + return rollout + if time.monotonic() >= deadline: + raise RolloutTimeoutError( + f"rollout {rollout.rollout_id} still {rollout.status!r} after " + f"{timeout_seconds}s; it is still running and can be polled again" + ) + time.sleep(poll_interval_seconds) + + +__all__ = ["ContainerPoolsAPI", "DEFAULT_POLL_INTERVAL_SECONDS", "RolloutTimeoutError"] diff --git a/synth_ai/sdk/research/container_pools/contracts.py b/synth_ai/sdk/research/container_pools/contracts.py new file mode 100644 index 00000000..fa40b86e --- /dev/null +++ b/synth_ai/sdk/research/container_pools/contracts.py @@ -0,0 +1,205 @@ +"""Typed wire contracts for container pools. + +Only the fields the SDK depends on are decoded strictly; the untouched wire +payload is kept on ``raw`` so callers can reach backend fields this package has +not modelled yet without waiting for an SDK release. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from synth_ai.core.contracts.json_value import JsonObject, JsonValue +from synth_ai.sdk.research.contracts._wire import ( + object_value, + optional_text, + required_text, +) + + +class PoolId(str): + """Backend-owned container pool identifier.""" + + +class PoolTaskId(str): + """Caller-chosen task identifier, unique within a pool.""" + + +class RuntimeImageReleaseId(str): + """Backend-owned runtime image release identifier.""" + + +class RolloutId(str): + """Backend-owned rollout identifier.""" + + +#: Rollout states that will not change again. +TERMINAL_ROLLOUT_STATUSES = frozenset({"completed", "failed", "cancelled"}) + +HARBOR_CONTAINER_SUBTYPE = "harbor" + + +def _optional_object(payload: JsonObject, name: str) -> JsonObject: + value = payload.get(name) + return value if isinstance(value, dict) else {} + + +def _optional_float(payload: JsonObject, name: str) -> float | None: + value = payload.get(name) + if isinstance(value, bool) or value is None: + return None + if isinstance(value, (int, float)): + return float(value) + return None + + +@dataclass(frozen=True, slots=True) +class Pool: + pool_id: PoolId + backend: str + status: str + name: str | None + raw: JsonObject = field(repr=False) + + @classmethod + def from_wire(cls, value: JsonValue) -> Pool: + payload = object_value(value, operation_id="container_pool") + return cls( + pool_id=PoolId(required_text(payload, "pool_id")), + backend=required_text(payload, "backend"), + status=str(payload.get("status") or ""), + name=optional_text(payload, "name"), + raw=payload, + ) + + +@dataclass(frozen=True, slots=True) +class PoolTask: + task_id: PoolTaskId + backend: str | None + status: str | None + raw: JsonObject = field(repr=False) + + @classmethod + def from_wire(cls, value: JsonValue) -> PoolTask: + payload = object_value(value, operation_id="container_pool_task") + return cls( + task_id=PoolTaskId(required_text(payload, "task_id")), + backend=optional_text(payload, "backend"), + status=optional_text(payload, "status"), + raw=payload, + ) + + +@dataclass(frozen=True, slots=True) +class RuntimeImageRelease: + """A deployable image plus, for the Harbor subtype, its task bundle. + + This is the object many tasks share: binding the same release to several + tasks in a pool is what makes a taskset cost one build rather than N. + """ + + release_id: RuntimeImageReleaseId + source_kind: str + compute_provider: str | None + container_subtype: str | None + resolved_digest: str | None + raw: JsonObject = field(repr=False) + + @property + def is_harbor(self) -> bool: + return self.container_subtype == HARBOR_CONTAINER_SUBTYPE + + @classmethod + def from_wire(cls, value: JsonValue) -> RuntimeImageRelease: + payload = object_value(value, operation_id="container_pool_runtime_image_release") + identifier = payload.get("release_id") or payload.get("id") + if not isinstance(identifier, str) or not identifier.strip(): + raise ValueError("runtime image release must carry release_id or id") + metadata = _optional_object(payload, "release_metadata") or _optional_object( + payload, "metadata" + ) + subtype = metadata.get("container_subtype") + return cls( + release_id=RuntimeImageReleaseId(identifier.strip()), + source_kind=str(payload.get("source_kind") or ""), + compute_provider=optional_text(payload, "compute_provider"), + container_subtype=subtype.strip().lower() + if isinstance(subtype, str) and subtype.strip() + else None, + resolved_digest=optional_text(payload, "resolved_digest"), + raw=payload, + ) + + +@dataclass(frozen=True, slots=True) +class Rollout: + rollout_id: RolloutId + status: str + #: Present only once the rollout reaches a terminal status. + outcome_reward: float | None + error: str | None + metrics: JsonObject = field(repr=False) + raw: JsonObject = field(repr=False) + + @property + def is_terminal(self) -> bool: + return self.status in TERMINAL_ROLLOUT_STATUSES + + @property + def succeeded(self) -> bool: + return self.status == "completed" and not self.error + + @classmethod + def from_wire(cls, value: JsonValue) -> Rollout: + payload = object_value(value, operation_id="container_pool_rollout") + identifier = payload.get("rollout_id") or payload.get("id") + if not isinstance(identifier, str) or not identifier.strip(): + raise ValueError("rollout must carry rollout_id or id") + metrics = _optional_object(payload, "metrics") + reward = _optional_float(metrics, "outcome_reward") + if reward is None: + reward = _optional_float(payload, "score") + error = payload.get("error") + return cls( + rollout_id=RolloutId(identifier.strip()), + status=str(payload.get("status") or ""), + outcome_reward=reward, + error=error.strip() if isinstance(error, str) and error.strip() else None, + metrics=metrics, + raw=payload, + ) + + +@dataclass(frozen=True, slots=True) +class RolloutArtifact: + artifact_type: str + artifact_name: str | None + size_bytes: int | None + raw: JsonObject = field(repr=False) + + @classmethod + def from_wire(cls, value: JsonValue) -> RolloutArtifact: + payload = object_value(value, operation_id="container_pool_rollout_artifact") + size = payload.get("size_bytes") + return cls( + artifact_type=str(payload.get("artifact_type") or ""), + artifact_name=optional_text(payload, "artifact_name"), + size_bytes=int(size) if isinstance(size, int) and not isinstance(size, bool) else None, + raw=payload, + ) + + +__all__ = [ + "HARBOR_CONTAINER_SUBTYPE", + "TERMINAL_ROLLOUT_STATUSES", + "Pool", + "PoolId", + "PoolTask", + "PoolTaskId", + "Rollout", + "RolloutArtifact", + "RolloutId", + "RuntimeImageRelease", + "RuntimeImageReleaseId", +] diff --git a/synth_ai/sdk/research/container_pools/operations.py b/synth_ai/sdk/research/container_pools/operations.py new file mode 100644 index 00000000..93b4a9db --- /dev/null +++ b/synth_ai/sdk/research/container_pools/operations.py @@ -0,0 +1,168 @@ +"""Container-pool operation registry. + +Deliberately separate from ``sdk/research/operations.py``. That module is gated +against the vendored ``openapi/research-v1.json`` by an exact set comparison +(``testing/scripts/check_research_openapi_contract.py`` walks every +``_operation(...)`` call in that one file), and container pools are a different +backend API — ``/v1/pools``, not ``/smr/*``. Adding these entries there would +fail the gate as ``extra`` operations forever. + +The backend does not yet publish a curated pools contract to vendor here: +``GET /v1/pools/openapi.json`` returns the whole FastAPI application schema. +Until it does, this registry is the source of truth and has no drift gate. +""" + +from __future__ import annotations + +from synth_ai.core.http.request import HttpMethod, OperationId, OperationMetadata + + +def _operation( + operation_id: str, + method: HttpMethod, + path: str, + *, + mutation: bool = False, + idempotent: bool = False, +) -> OperationMetadata: + return OperationMetadata(OperationId(operation_id), method, path, mutation, idempotent) + + +CONTAINER_POOL_OPERATIONS = { + operation.operation_id: operation + for operation in ( + # Pools + _operation("list_container_pools", HttpMethod.GET, "/v1/pools"), + _operation("create_container_pool", HttpMethod.POST, "/v1/pools", mutation=True), + _operation("get_container_pool", HttpMethod.GET, "/v1/pools/{pool_id}"), + _operation( + "delete_container_pool", + HttpMethod.DELETE, + "/v1/pools/{pool_id}", + mutation=True, + idempotent=True, + ), + # Tasks + _operation("list_container_pool_tasks", HttpMethod.GET, "/v1/pools/{pool_id}/tasks"), + _operation( + "create_container_pool_task", + HttpMethod.POST, + "/v1/pools/{pool_id}/tasks", + mutation=True, + ), + _operation( + "update_container_pool_task", + HttpMethod.PATCH, + "/v1/pools/{pool_id}/tasks/{task_id}", + mutation=True, + ), + _operation( + "delete_container_pool_task", + HttpMethod.DELETE, + "/v1/pools/{pool_id}/tasks/{task_id}", + mutation=True, + idempotent=True, + ), + # Runtime image releases — the deployment primitive. + _operation( + "list_container_pool_runtime_image_releases", + HttpMethod.GET, + "/v1/pools/{pool_id}/runtime_image_releases", + ), + _operation( + "create_container_pool_runtime_image_release", + HttpMethod.POST, + "/v1/pools/{pool_id}/runtime_image_releases", + mutation=True, + ), + _operation( + "get_container_pool_runtime_image_release", + HttpMethod.GET, + "/v1/pools/{pool_id}/runtime_image_releases/{release_id}", + ), + _operation( + "delete_container_pool_runtime_image_release", + HttpMethod.DELETE, + "/v1/pools/{pool_id}/runtime_image_releases/{release_id}", + mutation=True, + idempotent=True, + ), + # Bind is what actually builds the snapshot; it is not idempotent today. + _operation( + "bind_container_pool_runtime_image_release", + HttpMethod.POST, + "/v1/pools/{pool_id}/runtime_image_releases/{release_id}/bind", + mutation=True, + ), + _operation( + "bind_container_pool_task_runtime_image_release", + HttpMethod.POST, + "/v1/pools/{pool_id}/tasks/{task_id}/runtime_image_releases/{release_id}/bind", + mutation=True, + ), + # Project-bound actor deployments. These are the shared mutation path + # for Workshop, Intern, swarms, and direct SDK consumers. + _operation( + "get_container_pool_deployment", + HttpMethod.GET, + "/v1/pools/{pool_id}/deployments/{task_id}", + ), + _operation( + "mutate_container_pool_deployment", + HttpMethod.POST, + "/v1/pools/{pool_id}/deployments/{task_id}/operations", + mutation=True, + idempotent=True, + ), + _operation( + "get_container_pool_deployment_operation", + HttpMethod.GET, + "/v1/pools/{pool_id}/deployment-operations/{operation_id}", + ), + _operation( + "find_container_pool_deployment_operation", + HttpMethod.GET, + "/v1/pools/{pool_id}/deployments/{task_id}/operations", + ), + # Rollouts + _operation( + "create_container_pool_rollout", + HttpMethod.POST, + "/v1/pools/{pool_id}/rollouts", + mutation=True, + ), + _operation("list_container_pool_rollouts", HttpMethod.GET, "/v1/pools/{pool_id}/rollouts"), + _operation( + "get_container_pool_rollout", + HttpMethod.GET, + "/v1/pools/{pool_id}/rollouts/{rollout_id}", + ), + _operation( + "cancel_container_pool_rollout", + HttpMethod.POST, + "/v1/pools/{pool_id}/rollouts/{rollout_id}/cancel", + mutation=True, + idempotent=True, + ), + _operation( + "list_container_pool_rollout_artifacts", + HttpMethod.GET, + "/v1/pools/{pool_id}/rollouts/{rollout_id}/artifacts", + ), + _operation( + "get_container_pool_rollout_usage", + HttpMethod.GET, + "/v1/pools/{pool_id}/rollouts/{rollout_id}/usage", + ), + ) +} + + +def container_pool_operation(operation_id: str) -> OperationMetadata: + try: + return CONTAINER_POOL_OPERATIONS[OperationId(operation_id)] + except KeyError as error: + raise ValueError(f"unknown container pool operation_id {operation_id!r}") from error + + +__all__ = ["CONTAINER_POOL_OPERATIONS", "container_pool_operation"] diff --git a/synth_ai/sdk/research/container_pools/packaging.py b/synth_ai/sdk/research/container_pools/packaging.py new file mode 100644 index 00000000..cc160789 --- /dev/null +++ b/synth_ai/sdk/research/container_pools/packaging.py @@ -0,0 +1,430 @@ +"""Build a Harbor task bundle archive for a container-pool runtime image release. + +The backend accepts a Harbor bundle as one base64 tar.gz on +``POST /v1/pools/{pool_id}/runtime_image_releases`` with +``metadata.container_subtype = "harbor"``. Three properties of that path are not +obvious from the request shape and are enforced here, because every one of them +fails late and confusingly otherwise: + +1. **The Docker build context is the Dockerfile's own directory, not the bundle + root.** ``materialize_harbor_runtime_release`` archives ``dockerfile.parent`` + and nothing else, so a Dockerfile that reaches above ``environment/`` builds + fine on a laptop and fails inside the pool. :func:`build_harbor_bundle_archive` + rejects those ``COPY``/``ADD`` instructions up front. + +2. **``instruction.md`` and ``tests/`` are injected at run time**, not baked by + the Dockerfile. Copying them in the image is harmless but drifts from what + actually executes; the verifier that runs is the one in the archive. + +3. **Whatever sits in the bundle directory ships.** The archive goes to a + container image and to S3. A ``codex exec`` run with ``CODEX_HOME`` pointed at + a bundle leaves ``.codex/auth.json`` — live OAuth access and refresh tokens — + next to the task source. :data:`EXCLUDED_DIRECTORY_NAMES` drops run residue, + and :func:`build_harbor_bundle_archive` additionally fails closed on anything + credential-shaped rather than trusting the exclusion list to be complete. +""" + +from __future__ import annotations + +import base64 +import io +import re +import tarfile +import tomllib +from dataclasses import dataclass +from pathlib import Path, PurePosixPath + +# Mirrors backend `services/container_pools/rhodes/harbor_runtime.py`. +MAX_ARCHIVE_BYTES = 128 * 1024 * 1024 +MAX_BASE64_CHARS = 180 * 1024 * 1024 +# Mirrors `harbor_subtype.py::_MAX_TASK_CONFIG_BYTES`. +MAX_TASK_CONFIG_BYTES = 1024 * 1024 + +TASK_CONFIG_PATH = "task.toml" +INSTRUCTION_PATH = "instruction.md" +DOCKERFILE_PATH = "environment/Dockerfile" + +#: Run residue that must never reach an image or S3. +EXCLUDED_DIRECTORY_NAMES = frozenset( + { + ".codex", # Codex OAuth bundle, logs, and sqlite state + ".cache", # uv archive tree + "__pycache__", + ".git", + ".venv", + "node_modules", + "target", # Rust build output + } +) + +#: Filenames that indicate a credential regardless of directory. +_CREDENTIAL_FILENAMES = frozenset({"auth.json", ".env", "credentials.json", ".netrc"}) + + +def _load_dockerignore(dockerfile: Path) -> list[str]: + """Read ignore patterns for a build context. + + The backend never sees a ``.dockerignore``: the archive the client uploads + *is* the context. Applying the rules here is therefore the only thing + keeping excluded trees out of the image and out of S3 — and for some + contexts it is the difference between fitting the archive cap and not. + BuildKit prefers ``.dockerignore`` over a context-root + ``.dockerignore``, so this checks the same two locations in the same order. + """ + for candidate in ( + dockerfile.with_name(dockerfile.name + ".dockerignore"), + dockerfile.parent / ".dockerignore", + ): + if not candidate.is_file(): + continue + patterns: list[str] = [] + for raw in candidate.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if line and not line.startswith("#"): + patterns.append(line) + return patterns + return [] + + +def _matches_dockerignore(relative: PurePosixPath, patterns: list[str]) -> bool: + """Approximate Docker's ignore matching for the patterns people actually write. + + Negations (``!pattern``) are treated as "do not ignore", matching Docker. + This is deliberately conservative: an unmatched pattern only means a file is + included, so the worst case is a larger archive, never a missing file. + """ + ignored = False + for pattern in patterns: + negated = pattern.startswith("!") + candidate = pattern[1:] if negated else pattern + candidate = candidate.strip("/") + if not candidate: + continue + matched = relative.match(candidate) or any( + PurePosixPath(*relative.parts[: index + 1]).match(candidate) + for index in range(len(relative.parts)) + ) + if matched: + ignored = not negated + return ignored + + +_COPY_SOURCE = re.compile(r"^\s*(COPY|ADD)\s+(?P.+)$", re.IGNORECASE) +_ARG_DEFAULT = re.compile( + r"^\s*ARG\s+(?P[A-Za-z_][A-Za-z0-9_]*)=(?P.*)$", re.IGNORECASE +) +_INTERPOLATION = re.compile( + r"\$\{(?P[A-Za-z_][A-Za-z0-9_]*)\}|\$(?P[A-Za-z_][A-Za-z0-9_]*)" +) + + +def _expand_build_args(source: str, build_args: dict[str, str]) -> str | None: + """Substitute ``ARG`` defaults into a COPY source path. + + Build args are not plumbed through the container-pool build path — the + backend's ``do_build`` takes a Dockerfile and a context and nothing else — + so a Dockerfile's ARG *defaults* are what the image is actually built with. + Expanding them here validates the paths the build will really resolve, and + catches a bundle whose vendored contents disagree with its ARG defaults. + + Returns ``None`` when a referenced variable has no default, since the value + is then genuinely unknown and the path cannot be checked either way. + """ + unresolved = False + + def _replace(match: re.Match[str]) -> str: + nonlocal unresolved + name = match.group("braced") or match.group("bare") + if name not in build_args: + unresolved = True + return "" + return build_args[name] + + expanded = _INTERPOLATION.sub(_replace, source) + return None if unresolved else expanded + + +class HarborBundleError(ValueError): + """The bundle cannot be packaged for a container pool.""" + + +@dataclass(frozen=True, slots=True) +class HarborBundle: + """A packaged Harbor task bundle plus the facts the caller has to record.""" + + archive_base64: str + archive_bytes: int + task_name: str + #: Timeouts and resources the backend will derive from ``task.toml``. + agent_timeout_s: int + verifier_timeout_s: int + allow_internet: bool + file_count: int + + @property + def rollout_timeout_s(self) -> int: + """What the backend will set as the rollout ceiling. + + ``materialize_harbor_runtime_release`` computes + ``agent + verifier + 300``. Platform ceiling is 6h. + """ + return self.agent_timeout_s + self.verifier_timeout_s + 300 + + +def _is_excluded(relative: PurePosixPath) -> bool: + return any(part in EXCLUDED_DIRECTORY_NAMES for part in relative.parts) + + +def _check_dockerfile_context(dockerfile: Path, *, bundle_root: Path) -> None: + """Reject a Dockerfile whose build context is wider than its own directory.""" + + offenders: list[str] = [] + build_args: dict[str, str] = {} + for number, line in enumerate(dockerfile.read_text(encoding="utf-8").splitlines(), 1): + arg_match = _ARG_DEFAULT.match(line) + if arg_match is not None: + build_args[arg_match.group("name")] = arg_match.group("value").strip().strip("\"'") + continue + match = _COPY_SOURCE.match(line) + if match is None: + continue + rest = match.group("rest") + if "--from=" in rest: + # A multi-stage copy reads from an earlier stage, not the context. + continue + # Everything but the final argument is a source path. + arguments = rest.split() + for source in arguments[:-1]: + if source.startswith("--"): + continue + expanded = _expand_build_args(source, build_args) + if expanded is None: + # A variable with no ARG default: the build value is unknown, so + # neither accepting nor rejecting the path would be honest. + continue + candidate = PurePosixPath(expanded) + if candidate.is_absolute() or ".." in candidate.parts: + offenders.append(f" line {number}: {line.strip()}") + continue + # The context root is the Dockerfile's own directory; a source + # naming anything above it cannot resolve during a pool build. + if not (dockerfile.parent / expanded).exists(): + detail = f" line {number}: {line.strip()}" + if expanded != source: + detail += f"\n (resolves to {expanded!r} via ARG defaults)" + offenders.append(detail) + if offenders: + relative = dockerfile.relative_to(bundle_root) + raise HarborBundleError( + f"{relative} copies from outside its own directory. The container-pool " + "build context is the Dockerfile's directory, not the bundle root, so " + "these instructions cannot resolve:\n" + + "\n".join(offenders) + + "\n\nVendor the needed files under " + f"{dockerfile.parent.relative_to(bundle_root)}/ or rewrite the paths." + ) + + +def _read_task_config(bundle_root: Path) -> dict[str, object]: + task_config = bundle_root / TASK_CONFIG_PATH + if not task_config.is_file(): + raise HarborBundleError(f"Harbor bundle requires {TASK_CONFIG_PATH} at its root.") + raw = task_config.read_bytes() + if len(raw) > MAX_TASK_CONFIG_BYTES: + raise HarborBundleError( + f"{TASK_CONFIG_PATH} is {len(raw)} bytes; the backend rejects anything " + f"over {MAX_TASK_CONFIG_BYTES}." + ) + try: + return tomllib.loads(raw.decode("utf-8")) + except tomllib.TOMLDecodeError as error: + raise HarborBundleError(f"{TASK_CONFIG_PATH} is not valid TOML: {error}") from error + + +def build_harbor_bundle_archive( + bundle_dir: str | Path, + *, + dockerfile_path: str = DOCKERFILE_PATH, + allow_credential_files: bool = False, +) -> HarborBundle: + """Package ``bundle_dir`` into a base64 tar.gz for a Harbor runtime release. + + Raises :class:`HarborBundleError` rather than producing an archive that would + fail at build or bind time, or that would ship a credential. + """ + + bundle_root = Path(bundle_dir).expanduser().resolve() + if not bundle_root.is_dir(): + raise HarborBundleError(f"{bundle_root} is not a directory.") + + task_payload = _read_task_config(bundle_root) + if not (bundle_root / INSTRUCTION_PATH).is_file(): + raise HarborBundleError(f"Harbor bundle requires {INSTRUCTION_PATH} at its root.") + + dockerfile = bundle_root / dockerfile_path + if not dockerfile.is_file(): + raise HarborBundleError(f"Harbor bundle requires {dockerfile_path}.") + _check_dockerfile_context(dockerfile, bundle_root=bundle_root) + + if not (bundle_root / "tests" / "test.sh").is_file(): + raise HarborBundleError( + "Harbor bundle requires tests/test.sh; it is injected at /tests and run " + "as the verifier phase." + ) + + ignore_patterns = _load_dockerignore(dockerfile) + members: list[tuple[Path, str]] = [] + for path in sorted(bundle_root.rglob("*")): + if not path.is_file(): + continue + relative = PurePosixPath(path.relative_to(bundle_root).as_posix()) + if _is_excluded(relative): + continue + if ignore_patterns and _matches_dockerignore(relative, ignore_patterns): + continue + if not allow_credential_files and relative.name in _CREDENTIAL_FILENAMES: + raise HarborBundleError( + f"{relative} looks like a credential and would ship inside the " + "container image and to S3. Remove it, or pass " + "allow_credential_files=True if it is genuinely task fixture data." + ) + members.append((path, relative.as_posix())) + + if not members: + raise HarborBundleError(f"{bundle_root} has no packageable files.") + + with io.BytesIO() as buffer: + with tarfile.open(fileobj=buffer, mode="w:gz") as archive: + for path, arcname in members: + archive.add(path, arcname=arcname) + raw = buffer.getvalue() + + if len(raw) > MAX_ARCHIVE_BYTES: + raise HarborBundleError( + f"Bundle archive is {len(raw)} bytes; the backend rejects anything over " + f"{MAX_ARCHIVE_BYTES}. Check for build output left in the bundle." + ) + encoded = base64.b64encode(raw).decode("ascii") + if len(encoded) > MAX_BASE64_CHARS: + raise HarborBundleError( + f"Encoded bundle is {len(encoded)} characters; the backend rejects " + f"anything over {MAX_BASE64_CHARS}." + ) + + task_section = task_payload.get("task") + task_section = task_section if isinstance(task_section, dict) else {} + agent_section = task_payload.get("agent") + agent_section = agent_section if isinstance(agent_section, dict) else {} + verifier_section = task_payload.get("verifier") + verifier_section = verifier_section if isinstance(verifier_section, dict) else {} + environment_section = task_payload.get("environment") + environment_section = environment_section if isinstance(environment_section, dict) else {} + + return HarborBundle( + archive_base64=encoded, + archive_bytes=len(raw), + task_name=str(task_section.get("name") or bundle_root.name), + agent_timeout_s=int(agent_section.get("timeout_sec") or 900), + verifier_timeout_s=int(verifier_section.get("timeout_sec") or 900), + allow_internet=environment_section.get("allow_internet") is not False, + file_count=len(members), + ) + + +@dataclass(frozen=True, slots=True) +class DockerContextArchive: + """A packaged build context for an arbitrary (non-Harbor) container.""" + + archive_base64: str + archive_bytes: int + file_count: int + dockerfile_path: str + ignored_by_dockerignore: int + + +def build_docker_context_archive( + context_dir: str | Path, + *, + dockerfile_path: str = "Dockerfile", + allow_credential_files: bool = False, +) -> DockerContextArchive: + """Package a plain Docker build context for an ``arbitrary`` pool task. + + Unlike :func:`build_harbor_bundle_archive` this requires no task.toml, + instruction, or tests — an arbitrary container serves the Synth HTTP + contract rather than running agent and verifier phases. The context is + rooted at ``context_dir`` rather than at the Dockerfile's directory, because + the arbitrary path uploads the context as given. + """ + + root = Path(context_dir).expanduser().resolve() + if not root.is_dir(): + raise HarborBundleError(f"{root} is not a directory.") + dockerfile = root / dockerfile_path + if not dockerfile.is_file(): + raise HarborBundleError(f"Build context is missing {dockerfile_path}.") + + ignore_patterns = _load_dockerignore(dockerfile) + members: list[tuple[Path, str]] = [] + ignored = 0 + for path in sorted(root.rglob("*")): + if not path.is_file(): + continue + relative = PurePosixPath(path.relative_to(root).as_posix()) + if _is_excluded(relative): + ignored += 1 + continue + if ignore_patterns and _matches_dockerignore(relative, ignore_patterns): + ignored += 1 + continue + if not allow_credential_files and relative.name in _CREDENTIAL_FILENAMES: + raise HarborBundleError( + f"{relative} looks like a credential and would ship inside the " + "container image and to S3. Remove it, or pass " + "allow_credential_files=True if it is genuinely fixture data." + ) + members.append((path, relative.as_posix())) + + if not members: + raise HarborBundleError(f"{root} has no packageable files.") + + with io.BytesIO() as buffer: + with tarfile.open(fileobj=buffer, mode="w:gz") as archive: + for path, arcname in members: + archive.add(path, arcname=arcname) + raw = buffer.getvalue() + + if len(raw) > MAX_ARCHIVE_BYTES: + raise HarborBundleError( + f"Build context archive is {len(raw)} bytes; the backend rejects anything " + f"over {MAX_ARCHIVE_BYTES}. {ignored} files were already excluded — check " + "for build output that no ignore rule covers." + ) + encoded = base64.b64encode(raw).decode("ascii") + if len(encoded) > MAX_BASE64_CHARS: + raise HarborBundleError( + f"Encoded context is {len(encoded)} characters; the backend rejects " + f"anything over {MAX_BASE64_CHARS}." + ) + + return DockerContextArchive( + archive_base64=encoded, + archive_bytes=len(raw), + file_count=len(members), + dockerfile_path=dockerfile_path, + ignored_by_dockerignore=ignored, + ) + + +__all__ = [ + "DOCKERFILE_PATH", + "EXCLUDED_DIRECTORY_NAMES", + "DockerContextArchive", + "HarborBundle", + "HarborBundleError", + "INSTRUCTION_PATH", + "MAX_ARCHIVE_BYTES", + "TASK_CONFIG_PATH", + "build_docker_context_archive", + "build_harbor_bundle_archive", +] diff --git a/synth_ai/sdk/research/contracts/evidence.py b/synth_ai/sdk/research/contracts/evidence.py index a2c2a53e..c0f1b0dd 100644 --- a/synth_ai/sdk/research/contracts/evidence.py +++ b/synth_ai/sdk/research/contracts/evidence.py @@ -263,32 +263,42 @@ def to_wire(self) -> JsonObject: } +_FRESHNESS_FIELDS = frozenset( + { + "generated_at", + "artifact_count", + "work_product_count", + "run_is_terminal", + } +) + + @dataclass(frozen=True, slots=True) class EvidenceFreshness: generated_at: datetime artifact_count: int work_product_count: int run_is_terminal: bool + # Backend SmrSwarmEvidenceFreshnessResponse.tool_call_count (default 0); + # older backends omit it, so it is accepted but not required. + tool_call_count: int = 0 @classmethod def from_wire(cls, value: JsonValue) -> EvidenceFreshness: + has_tool_call_count = isinstance(value, dict) and "tool_call_count" in value payload = _exact_object( value, label="swarm evidence freshness", - fields=frozenset( - { - "generated_at", - "artifact_count", - "work_product_count", - "run_is_terminal", - } - ), + fields=_FRESHNESS_FIELDS | ({"tool_call_count"} if has_tool_call_count else set()), ) return cls( generated_at=required_datetime(payload, "generated_at"), artifact_count=_non_negative_int(payload, "artifact_count"), work_product_count=_non_negative_int(payload, "work_product_count"), run_is_terminal=required_bool(payload, "run_is_terminal"), + tool_call_count=( + _non_negative_int(payload, "tool_call_count") if has_tool_call_count else 0 + ), ) def to_wire(self) -> JsonObject: @@ -296,10 +306,93 @@ def to_wire(self) -> JsonObject: "generated_at": self.generated_at.isoformat(), "artifact_count": self.artifact_count, "work_product_count": self.work_product_count, + "tool_call_count": self.tool_call_count, "run_is_terminal": self.run_is_terminal, } +class SwarmToolCallStatus(StrEnum): + SUCCEEDED = "succeeded" + FAILED = "failed" + UNKNOWN_TOOL = "unknown_tool" + + +_TOOL_CALL_FIELDS = frozenset( + { + "tool_call_id", + "stable_actor_key", + "actor_role", + "correlation_id", + "bundle_name", + "tool_name", + "arguments_digest", + "status", + "error_code", + "retryable", + "duration_ms", + "occurred_at", + } +) + + +@dataclass(frozen=True, slots=True) +class SwarmToolCallEvidence: + """Secret-safe durable outcome for one run-scoped MCP invocation. + + Mirrors backend ``SmrSwarmToolCallEvidenceResponse``; arguments are only a + digest, never the values. + """ + + tool_call_id: str + actor_role: str + tool_name: str + arguments_digest: str + status: SwarmToolCallStatus + retryable: bool + duration_ms: int + occurred_at: datetime + stable_actor_key: str | None = None + correlation_id: str | None = None + bundle_name: str | None = None + error_code: str | None = None + + @classmethod + def from_wire(cls, value: JsonValue) -> SwarmToolCallEvidence: + payload = _exact_object( + value, label="retrieve_swarm_evidence.tool_calls", fields=_TOOL_CALL_FIELDS + ) + return cls( + tool_call_id=required_text(payload, "tool_call_id"), + actor_role=required_text(payload, "actor_role"), + tool_name=required_text(payload, "tool_name"), + arguments_digest=required_text(payload, "arguments_digest"), + status=SwarmToolCallStatus(required_text(payload, "status")), + retryable=required_bool(payload, "retryable"), + duration_ms=_non_negative_int(payload, "duration_ms"), + occurred_at=required_datetime(payload, "occurred_at"), + stable_actor_key=optional_text(payload, "stable_actor_key"), + correlation_id=optional_text(payload, "correlation_id"), + bundle_name=optional_text(payload, "bundle_name"), + error_code=optional_text(payload, "error_code"), + ) + + def to_wire(self) -> JsonObject: + return { + "tool_call_id": self.tool_call_id, + "stable_actor_key": self.stable_actor_key, + "actor_role": self.actor_role, + "correlation_id": self.correlation_id, + "bundle_name": self.bundle_name, + "tool_name": self.tool_name, + "arguments_digest": self.arguments_digest, + "status": self.status.value, + "error_code": self.error_code, + "retryable": self.retryable, + "duration_ms": self.duration_ms, + "occurred_at": self.occurred_at.isoformat(), + } + + @dataclass(frozen=True, slots=True) class SwarmEvidence: swarm_id: SwarmId @@ -307,6 +400,7 @@ class SwarmEvidence: artifacts: tuple[EvidenceArtifact, ...] work_products: tuple[EvidenceWorkProduct, ...] freshness: EvidenceFreshness + tool_calls: tuple[SwarmToolCallEvidence, ...] = () def __post_init__(self) -> None: if self.freshness.artifact_count != len(self.artifacts): @@ -331,6 +425,8 @@ def from_wire(cls, value: JsonValue) -> SwarmEvidence: # consume them yet. "selected_artifact_contents", "trace_publications", + # Durable run-scoped MCP invocation outcomes (digests only). + "tool_calls", "freshness", } ), @@ -355,6 +451,13 @@ def from_wire(cls, value: JsonValue) -> SwarmEvidence: ) ), freshness=EvidenceFreshness.from_wire(payload["freshness"]), + tool_calls=tuple( + SwarmToolCallEvidence.from_wire(item) + for item in array_value( + payload["tool_calls"], + operation_id="retrieve_swarm_evidence.tool_calls", + ) + ), ) def to_wire(self) -> JsonObject: @@ -366,6 +469,7 @@ def to_wire(self) -> JsonObject: "work_products": [item.to_wire() for item in self.work_products], "selected_artifact_contents": [], "trace_publications": [], + "tool_calls": [call.to_wire() for call in self.tool_calls], "freshness": self.freshness.to_wire(), } @@ -376,6 +480,8 @@ def to_wire(self) -> JsonObject: "EvidenceFreshness", "EvidenceWorkProduct", "SwarmEvidence", + "SwarmToolCallEvidence", + "SwarmToolCallStatus", "WorkProductArtifactLink", "WorkProductArtifactRole", "WorkProductBlocker", diff --git a/synth_ai/sdk/research/contracts/intern_blockers.py b/synth_ai/sdk/research/contracts/intern_blockers.py new file mode 100644 index 00000000..1f8384a2 --- /dev/null +++ b/synth_ai/sdk/research/contracts/intern_blockers.py @@ -0,0 +1,86 @@ +"""Operator blocker handoffs; mirrors backend packages/intern/contracts.py.""" + +from datetime import datetime +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field, model_validator + +from .research_intern import ( + InternAsyncBlocker, + InternAsyncCommandReceipt, + InternProducedResourceReference, + InternRuntimeBinding, + InternSyncSession, +) + + +class _Contract(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + + +class InternBlockerOpenSyncRequest(_Contract): + idempotency_key: str = Field(min_length=1, max_length=512) + + +class InternBlockerHandoffContext(_Contract): + schema_version: Literal["smr.intern-async-blocker-handoff-context.v1"] = ( + "smr.intern-async-blocker-handoff-context.v1" + ) + blocker_id: str + async_assignment_id: str + binding: InternRuntimeBinding + action_schema_version: str + action_kind: str + action_digest: str = Field(pattern=r"^[0-9a-f]{64}$") + summary: str = Field(min_length=1, max_length=2000) + rationale: str = Field(min_length=1, max_length=4000) + preauthorization_rule: str + evidence_resources: tuple[InternProducedResourceReference, ...] = () + required_operator_capability: str + + +class InternBlockerHandoffReceipt(_Contract): + schema_version: Literal["smr.intern-async-blocker-handoff-receipt.v1"] = ( + "smr.intern-async-blocker-handoff-receipt.v1" + ) + handoff_receipt_id: str + blocker_id: str + org_id: str + sync_session_id: str + opened_by_user_id: str + idempotency_key: str + context: InternBlockerHandoffContext + context_digest: str = Field(pattern=r"^[0-9a-f]{64}$") + created_at: datetime + + +class InternBlockerOpenSyncResponse(_Contract): + schema_version: Literal["smr.intern-async-blocker-open-sync.v1"] = ( + "smr.intern-async-blocker-open-sync.v1" + ) + blocker: InternAsyncBlocker + sync_session: InternSyncSession + handoff_receipt: InternBlockerHandoffReceipt + + +class InternBlockerResolveRequest(_Contract): + idempotency_key: str = Field(min_length=1, max_length=512) + outcome: Literal["completed", "denied", "superseded"] + comment: str | None = Field(default=None, max_length=4000) + supporting_receipt_ids: tuple[str, ...] = Field(default=(), max_length=128) + + @model_validator(mode="after") + def validate_receipts(self): + if any(not value.strip() or len(value) > 512 for value in self.supporting_receipt_ids): + raise ValueError("supporting receipt IDs must be non-empty and at most 512 characters") + if len(set(self.supporting_receipt_ids)) != len(self.supporting_receipt_ids): + raise ValueError("supporting receipt IDs must be unique") + return self + + +class InternBlockerResolveResponse(_Contract): + schema_version: Literal["smr.intern-async-blocker-resolution.v1"] = ( + "smr.intern-async-blocker-resolution.v1" + ) + blocker: InternAsyncBlocker + continuation_command: InternAsyncCommandReceipt diff --git a/synth_ai/sdk/research/contracts/intern_grants.py b/synth_ai/sdk/research/contracts/intern_grants.py index b1f12989..58f0d029 100644 --- a/synth_ai/sdk/research/contracts/intern_grants.py +++ b/synth_ai/sdk/research/contracts/intern_grants.py @@ -103,6 +103,9 @@ class InternCapabilityOperation(StrEnum): TRACE_READ = "trace.read" TRACE_INGEST = "trace.ingest" POOL_READ = "pool.read" + POOL_DEPLOYMENT_CREATE = "pool.deployment_create" + POOL_DEPLOYMENT_UPDATE = "pool.deployment_update" + POOL_DEPLOYMENT_DELETE = "pool.deployment_delete" POOL_ASSIGN = "pool.assign" POOL_RELEASE = "pool.release" POOL_REMOUNT = "pool.remount" diff --git a/synth_ai/sdk/research/contracts/intern_resources.py b/synth_ai/sdk/research/contracts/intern_resources.py new file mode 100644 index 00000000..deab4b02 --- /dev/null +++ b/synth_ai/sdk/research/contracts/intern_resources.py @@ -0,0 +1,91 @@ +"""Mirrors backend packages/intern/resource_inventory.py. + +See backend notes/specifications/tanha/current/systems/platform/intern_resource_inventory.md. +Incomplete coverage and unknown disposition are never inferred to mean settled. +``coverage_complete`` is creator coverage; ``disposition_complete`` is cleanup. +""" + +from datetime import datetime +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field, StrictBool + +InternResourceDispositionValue = Literal["settled", "pending", "unknown", "excluded", "retained"] +InternRuntimeClosureKind = Literal[ + "conversation_closed", + "work_cancelled", + "work_completed", + "work_failed", + "archived", + "superseded_by_reopen", +] + + +class InternResourceDisposition(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + resource_kind: str + resource_id: str + cleanup_owner_run_id: str | None = None + relation: Literal["self", "owned", "borrowed", "shared", "retained"] + disposition: InternResourceDispositionValue + reason: str + cleanup_owner: str | None = None + epoch: int | None = Field(default=None, ge=0) + + +class InternResourceStopEffect(BaseModel): + """An effect actually performed when a runtime epoch closed.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + resource_kind: str + resource_id: str + # Close-time effects, then the real outcome of each internal run stop. + effect: Literal[ + "stop_requested", + "already_settled", + "retained", + "admission_fenced", + "stopped", + "already_terminal", + "stop_accepted", + "stop_failed", + ] + owner: str | None = None + reason: str + + +class InternRuntimeResourceEpoch(BaseModel): + """One admission epoch; closed epochs remain enumerated for recovery.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + epoch: int = Field(ge=0) + admission: Literal["open", "closed"] + closure_kind: InternRuntimeClosureKind | None = None + opened_at: datetime + closed_at: datetime | None = None + effects: list[InternResourceStopEffect] = Field(default_factory=list) + # Null while the backend re-drive pass is still converging this epoch. + cleanup_settled_at: datetime | None = None + + +class InternResourceInventory(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + runtime_kind: Literal["sync", "async"] + runtime_id: str + observed_at: datetime + coverage: Literal["registered-runtime-resources-v1"] + coverage_complete: StrictBool + incomplete_reasons: list[str] = Field(default_factory=list) + resources: list[InternResourceDisposition] + creator_set: str | None = None + profile: str | None = None + disposition_complete: StrictBool = False + epochs: list[InternRuntimeResourceEpoch] = Field(default_factory=list) + + @classmethod + def from_wire(cls, value): + inventory = cls.model_validate(value) + # A complete cleanup can never rest on incomplete creator coverage. + if inventory.disposition_complete and not inventory.coverage_complete: + raise ValueError("Intern inventory reported cleanup without coverage") + return inventory diff --git a/synth_ai/sdk/research/contracts/intern_usage.py b/synth_ai/sdk/research/contracts/intern_usage.py new file mode 100644 index 00000000..f381cfda --- /dev/null +++ b/synth_ai/sdk/research/contracts/intern_usage.py @@ -0,0 +1,106 @@ +"""Usage receipt for one Intern Sync session or Async assignment. + +Mirrors backend ``InternSessionUsageResponse`` +(``app/api/v1/managed_research/schemas/intern_usage.py``). ``spend_cents`` is +settled run spend only; while ``billing.billing_state`` is not ``settled`` the +total is incomplete, never a final $0. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass, field +from enum import StrEnum + + +class InternBillingState(StrEnum): + SETTLED = "settled" + PENDING = "pending" + STALLED = "stalled" + FAILED = "failed" + OBSERVABILITY_ONLY = "observability_only" + NOT_APPLICABLE = "not_applicable" + + +def _int(mapping: Mapping[str, object], key: str) -> int: + value = mapping.get(key) + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError(f"intern usage {key} must be an integer") + return value + + +@dataclass(frozen=True) +class InternUsageBilling: + billing_state: InternBillingState + stalled_row_count: int + stalled_spend_cents: int + billing_failed_row_count: int + show_stalled_banner: bool + message: str | None = None + + @classmethod + def from_wire(cls, payload: object) -> InternUsageBilling: + if not isinstance(payload, Mapping): + raise ValueError("intern usage billing must be an object") + banner = payload.get("show_stalled_banner") + if not isinstance(banner, bool): + raise ValueError("intern usage billing show_stalled_banner must be a boolean") + message = payload.get("message") + if message is not None and not isinstance(message, str): + raise ValueError("intern usage billing message must be a string or null") + return cls( + billing_state=InternBillingState(payload.get("billing_state")), + stalled_row_count=_int(payload, "stalled_row_count"), + stalled_spend_cents=_int(payload, "stalled_spend_cents"), + billing_failed_row_count=_int(payload, "billing_failed_row_count"), + show_stalled_banner=banner, + message=message, + ) + + +@dataclass(frozen=True) +class InternSessionUsage: + origin_runtime_kind: str + origin_runtime_id: str + org_id: str + spend_cents: int + token_count: int + run_count: int + billing: InternUsageBilling + bindings: tuple[dict[str, object], ...] = field(default_factory=tuple) + metadata: dict[str, object] = field(default_factory=dict) + + @property + def run_ids(self) -> tuple[str, ...]: + """Runs this Intern runtime launched, as recorded in the receipt.""" + return tuple(str(run_id) for run_id in self.metadata.get("run_ids", ()) or ()) + + @classmethod + def from_wire(cls, payload: object) -> InternSessionUsage: + if not isinstance(payload, Mapping): + raise ValueError("intern usage must be an object") + kind = payload.get("origin_runtime_kind") + if kind not in {"sync", "async"}: + raise ValueError(f"intern usage origin_runtime_kind is invalid: {kind!r}") + runtime_id = payload.get("origin_runtime_id") + org_id = payload.get("org_id") + if not isinstance(runtime_id, str) or not isinstance(org_id, str): + raise ValueError("intern usage origin_runtime_id and org_id are required") + bindings = payload.get("bindings") or [] + metadata = payload.get("metadata") or {} + if not isinstance(bindings, list) or not isinstance(metadata, Mapping): + raise ValueError("intern usage bindings must be a list and metadata an object") + return cls( + origin_runtime_kind=kind, + origin_runtime_id=runtime_id, + org_id=org_id, + spend_cents=_int(payload, "spend_cents"), + token_count=_int(payload, "token_count"), + run_count=_int(payload, "run_count"), + billing=InternUsageBilling.from_wire(payload.get("billing")), + bindings=tuple(dict(item) for item in bindings), + metadata=dict(metadata), + ) + + +__all__ = ["InternBillingState", "InternSessionUsage", "InternUsageBilling"] diff --git a/synth_ai/sdk/research/contracts/projects.py b/synth_ai/sdk/research/contracts/projects.py index f35ab91b..8331ed6e 100644 --- a/synth_ai/sdk/research/contracts/projects.py +++ b/synth_ai/sdk/research/contracts/projects.py @@ -52,6 +52,7 @@ class ProjectSpec: execution_policy: JsonObject = field(default_factory=dict) research: JsonObject = field(default_factory=dict) metered_infra: JsonObject = field(default_factory=dict) + policy: JsonObject = field(default_factory=dict) def __post_init__(self) -> None: for name, value in ( @@ -80,6 +81,7 @@ def to_wire(self) -> JsonObject: "execution_policy": dict(self.execution_policy), "research": dict(self.research), "metered_infra": dict(self.metered_infra), + "policy": dict(self.policy), } for name, value in ( ("reviewer_profile_id", self.reviewer_profile_id), diff --git a/synth_ai/sdk/research/contracts/research_intern.py b/synth_ai/sdk/research/contracts/research_intern.py index ec22227e..17648ce3 100644 --- a/synth_ai/sdk/research/contracts/research_intern.py +++ b/synth_ai/sdk/research/contracts/research_intern.py @@ -892,6 +892,7 @@ class InternAsyncCheckpoint(_StrictContract): class InternAsyncBlocker(_StrictContract): + async_assignment_id: str | None = None """One Async blocker. Most fields are populated only once the blocker has been opened as a Sync diff --git a/synth_ai/sdk/research/contracts/resource_settlement.py b/synth_ai/sdk/research/contracts/resource_settlement.py new file mode 100644 index 00000000..73c56ee4 --- /dev/null +++ b/synth_ai/sdk/research/contracts/resource_settlement.py @@ -0,0 +1,128 @@ +"""Fresh resource-settlement read for one run. + +Mirrors backend ``RunResourceSettlementResponse`` +(``services/smr/ownership/contracts.py``). The backend forbids extra fields, so +an unknown key here is contract drift and fails loudly rather than being +ignored. ``settled`` only speaks for resources the run registered; whether the +registered inventory is complete is ``coverage_complete``. A run with +``coverage="untracked"`` makes no settlement claim at all. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from datetime import datetime +from enum import StrEnum + + +class SettlementCoverage(StrEnum): + UNTRACKED = "untracked" + EXPLICIT_V1 = "explicit-v1" + + +class SettlementScope(StrEnum): + ROOT_TREE = "root_tree" + OWNED_SUBTREE = "owned_subtree" + + +_FIELDS = frozenset( + { + "run_id", + "observed_at", + "coverage", + "settled", + "registered_tree_settled", + "coverage_complete", + "scope_kind", + "root_run_id", + "edge_id", + "pending", + "unknown", + "confirmed", + "excluded", + "root_confirmed", + } +) + + +def _bool(mapping: Mapping[str, object], key: str, *, default: bool | None = None) -> bool: + value = mapping.get(key, default) + if not isinstance(value, bool): + raise ValueError(f"resource settlement {key} must be a boolean") + return value + + +def _optional_bool(mapping: Mapping[str, object], key: str) -> bool | None: + value = mapping.get(key) + if value is None or isinstance(value, bool): + return value + raise ValueError(f"resource settlement {key} must be a boolean or null") + + +def _optional_count(mapping: Mapping[str, object], key: str) -> int | None: + value = mapping.get(key) + if value is None: + return None + if isinstance(value, bool) or not isinstance(value, int) or value < 0: + raise ValueError(f"resource settlement {key} must be a non-negative integer or null") + return value + + +def _optional_str(mapping: Mapping[str, object], key: str) -> str | None: + value = mapping.get(key) + if value is None or isinstance(value, str): + return value + raise ValueError(f"resource settlement {key} must be a string or null") + + +@dataclass(frozen=True) +class RunResourceSettlement: + run_id: str + observed_at: datetime + coverage: SettlementCoverage + settled: bool + registered_tree_settled: bool = False + coverage_complete: bool = False + scope_kind: SettlementScope | None = None + root_run_id: str | None = None + edge_id: str | None = None + pending: int | None = None + unknown: int | None = None + confirmed: int | None = None + excluded: int | None = None + root_confirmed: bool | None = None + + @classmethod + def from_wire(cls, payload: object) -> RunResourceSettlement: + if not isinstance(payload, Mapping): + raise ValueError("resource settlement must be an object") + unknown_keys = set(payload) - _FIELDS + if unknown_keys: + raise ValueError(f"resource settlement has unknown fields: {sorted(unknown_keys)}") + run_id = payload.get("run_id") + observed_at = payload.get("observed_at") + if not isinstance(run_id, str) or not run_id: + raise ValueError("resource settlement run_id is required") + if not isinstance(observed_at, str): + raise ValueError("resource settlement observed_at is required") + scope_kind = _optional_str(payload, "scope_kind") + return cls( + run_id=run_id, + observed_at=datetime.fromisoformat(observed_at), + coverage=SettlementCoverage(payload.get("coverage")), + settled=_bool(payload, "settled"), + registered_tree_settled=_bool(payload, "registered_tree_settled", default=False), + coverage_complete=_bool(payload, "coverage_complete", default=False), + scope_kind=SettlementScope(scope_kind) if scope_kind is not None else None, + root_run_id=_optional_str(payload, "root_run_id"), + edge_id=_optional_str(payload, "edge_id"), + pending=_optional_count(payload, "pending"), + unknown=_optional_count(payload, "unknown"), + confirmed=_optional_count(payload, "confirmed"), + excluded=_optional_count(payload, "excluded"), + root_confirmed=_optional_bool(payload, "root_confirmed"), + ) + + +__all__ = ["RunResourceSettlement", "SettlementCoverage", "SettlementScope"] diff --git a/synth_ai/sdk/research/contracts/run_state.py b/synth_ai/sdk/research/contracts/run_state.py index 33765fb3..26d999ee 100644 --- a/synth_ai/sdk/research/contracts/run_state.py +++ b/synth_ai/sdk/research/contracts/run_state.py @@ -435,6 +435,9 @@ class ManagedResearchRun: project_id: str public_state: RunState effort_id: str | None = None + # The Intern Sync session or Async assignment that launched this run. + origin_runtime_kind: str | None = None + origin_runtime_id: str | None = None run_kind: ManagedResearchRunKind = ManagedResearchRunKind.RESEARCH runbook: str | None = None project_alias: str | None = None @@ -501,6 +504,8 @@ def from_wire(cls, payload: object) -> ManagedResearchRun: run_id=_require_string(mapping, "run_id", label="run.run_id"), project_id=project_id, effort_id=_optional_string(mapping, "effort_id"), + origin_runtime_kind=_optional_string(mapping, "origin_runtime_kind"), + origin_runtime_id=_optional_string(mapping, "origin_runtime_id"), run_kind=ManagedResearchRunKind( _optional_string(mapping, "run_kind") or ManagedResearchRunKind.RESEARCH ), diff --git a/synth_ai/sdk/research/contracts/smr_actor_policy_data.py b/synth_ai/sdk/research/contracts/smr_actor_policy_data.py index e6e73c4e..569464eb 100644 --- a/synth_ai/sdk/research/contracts/smr_actor_policy_data.py +++ b/synth_ai/sdk/research/contracts/smr_actor_policy_data.py @@ -26,8 +26,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ) @@ -53,8 +51,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -78,8 +74,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -103,8 +97,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -127,8 +119,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -151,8 +141,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -175,8 +163,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -199,8 +185,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -223,8 +207,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -248,8 +230,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -276,8 +256,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -302,8 +280,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, { @@ -327,8 +303,6 @@ "openrouter/poolside/laguna-s-2.1", "poolside/laguna-s-2.1", "synth_internal/laguna-s-2.1-nvfp4", - "x-ai/grok-4.3", - "x-ai/grok-build", ], }, ) diff --git a/synth_ai/sdk/research/contracts/smr_agent_models.py b/synth_ai/sdk/research/contracts/smr_agent_models.py index 419b9a1c..1a413e37 100644 --- a/synth_ai/sdk/research/contracts/smr_agent_models.py +++ b/synth_ai/sdk/research/contracts/smr_agent_models.py @@ -16,7 +16,6 @@ class SmrAgentModel(StrEnum): GPT_5_6_LUNA = "gpt-5.6-luna" GPT_5_6_SOL = "gpt-5.6-sol" GPT_5_6_TERRA = "gpt-5.6-terra" - CURSOR_GROK_4_5 = "cursor/grok-4.5" GPT_5_4 = "gpt-5.4" GPT_5_4_MINI = "gpt-5.4-mini" NEMOTRON_SUPER = "nemotron-super" @@ -25,8 +24,6 @@ class SmrAgentModel(StrEnum): CURSOR_COMPOSER_2_5 = "cursor/composer-2.5" CURSOR_GPT_5 = "cursor/gpt-5" CURSOR_SONNET_4 = "cursor/sonnet-4" - X_AI_GROK_4_3 = "x-ai/grok-4.3" - X_AI_GROK_BUILD = "x-ai/grok-build" MOONSHOTAI_KIMI_K2_6 = "moonshotai/kimi-k2.6" BASETEN_MOONSHOTAI_KIMI_K3 = "baseten/moonshotai/Kimi-K3" MODAL_MOONSHOTAI_KIMI_K3 = "modal/moonshotai/Kimi-K3" @@ -40,6 +37,7 @@ class SmrAgentModel(StrEnum): OPENROUTER_POOLSIDE_LAGUNA_S_2_1 = "openrouter/poolside/laguna-s-2.1" META_MUSE_SPARK_1_2 = "meta/muse-spark-1.2" SYNTH_INTERNAL_LAGUNA_S_2_1_NVFP4 = "synth_internal/laguna-s-2.1-nvfp4" + OPENROUTER_OPENAI_GPT_5_6_LUNA = "openrouter/openai/gpt-5.6-luna" SMR_AGENT_MODEL_VALUES: tuple[str, ...] = tuple(model.value for model in SmrAgentModel) diff --git a/synth_ai/sdk/research/contracts/smr_credential_providers.py b/synth_ai/sdk/research/contracts/smr_credential_providers.py index c58ee772..b8d4fb1f 100644 --- a/synth_ai/sdk/research/contracts/smr_credential_providers.py +++ b/synth_ai/sdk/research/contracts/smr_credential_providers.py @@ -9,7 +9,6 @@ class SmrCredentialProvider(StrEnum): DEEPSEEK = "deepseek" OPENAI = "openai" OPENROUTER = "openrouter" - XAI = "xai" TINKER = "tinker" diff --git a/synth_ai/sdk/research/contracts/smr_inference_providers.py b/synth_ai/sdk/research/contracts/smr_inference_providers.py index 40157e2d..edf1cb2d 100644 --- a/synth_ai/sdk/research/contracts/smr_inference_providers.py +++ b/synth_ai/sdk/research/contracts/smr_inference_providers.py @@ -15,7 +15,6 @@ class SmrInferenceProvider(StrEnum): POOLSIDE = "poolside" SYNTH = "synth" SYNTH_INTERNAL = "synth_internal" - XAI = "xai" SMR_INFERENCE_PROVIDER_VALUES: tuple[str, ...] = tuple( diff --git a/synth_ai/sdk/research/contracts/smr_providers.py b/synth_ai/sdk/research/contracts/smr_providers.py index 180c7364..ff89d966 100644 --- a/synth_ai/sdk/research/contracts/smr_providers.py +++ b/synth_ai/sdk/research/contracts/smr_providers.py @@ -20,7 +20,6 @@ class ResourceProvider(StrEnum): SYNTH_AI = "synth_ai" CURSOR = "cursor" DEEPSEEK = "deepseek" - XAI = "xai" MODAL = "modal" OPENAI_CHATGPT = "openai_chatgpt" BASETEN = "baseten" @@ -50,8 +49,6 @@ class ResourceCapability(StrEnum): "gemini", "synth", "synth_internal", - "xai", - "grok", "cursor", "modal", "openrouter", @@ -79,7 +76,6 @@ class ResourceCapability(StrEnum): ), Provider.CURSOR: frozenset({ActorResourceCapability.INFERENCE}), Provider.DEEPSEEK: frozenset({ActorResourceCapability.INFERENCE}), - Provider.XAI: frozenset({ActorResourceCapability.INFERENCE}), Provider.MODAL: frozenset({ActorResourceCapability.INFERENCE}), Provider.OPENAI_CHATGPT: frozenset({ActorResourceCapability.INFERENCE}), Provider.BASETEN: frozenset({ActorResourceCapability.INFERENCE}), @@ -213,7 +209,6 @@ def to_dict(self) -> dict[str, Any]: Provider.SYNTH_AI: SynthAIConfig(), Provider.CURSOR: SynthAIConfig(), Provider.DEEPSEEK: SynthAIConfig(), - Provider.XAI: SynthAIConfig(), Provider.MODAL: SynthAIConfig(), Provider.OPENAI_CHATGPT: OpenAIChatGPTConfig(), Provider.BASETEN: SynthAIConfig(), diff --git a/synth_ai/sdk/research/contracts/swarm_rollouts.py b/synth_ai/sdk/research/contracts/swarm_rollouts.py new file mode 100644 index 00000000..58c8be4e --- /dev/null +++ b/synth_ai/sdk/research/contracts/swarm_rollouts.py @@ -0,0 +1,344 @@ +"""Container-pool rollouts a swarm launched. + +Mirrors backend ``SwarmRolloutPage`` (``app/api/v1/managed_research/schemas/ +swarm_rollouts.py``). A rollout appears here only when its verified budget +parent is the swarm; caller-supplied metadata cannot place it in this list. + +Each rollout links run → pool/task → logical intent → executor launch (lease or +explicit lease disposition, executed image/source) → rollout artifacts. The +execution record is written by the backend executor from what it launched; +``None`` means unknown (legacy row or not yet started), never "no image". +""" + +from __future__ import annotations + +import re +from collections.abc import Mapping +from dataclasses import dataclass +from datetime import datetime + +_SHA256 = re.compile(r"sha256:[0-9a-f]{64}") + + +def _text(payload: Mapping[str, object], key: str) -> str: + value = payload.get(key) + if not isinstance(value, str) or not value: + raise ValueError(f"swarm rollout {key} is required") + return value + + +def _optional_text(payload: Mapping[str, object], key: str) -> str | None: + value = payload.get(key) + if value is None or isinstance(value, str): + return value + raise ValueError(f"swarm rollout {key} must be a string or null") + + +def _optional_datetime(payload: Mapping[str, object], key: str) -> datetime | None: + value = _optional_text(payload, key) + return datetime.fromisoformat(value) if value is not None else None + + +def _integer(payload: Mapping[str, object], key: str, *, minimum: int = 0) -> int: + value = payload.get(key) + if isinstance(value, bool) or not isinstance(value, int) or value < minimum: + raise ValueError(f"swarm rollout {key} must be an integer >= {minimum}") + return value + + +def _optional_digest(payload: Mapping[str, object], key: str) -> str | None: + value = _optional_text(payload, key) + if value is not None and not _SHA256.fullmatch(value): + raise ValueError(f"swarm rollout {key} must be a sha256 digest or null") + return value + + +def _mapping(payload: object, subject: str) -> Mapping[str, object]: + if not isinstance(payload, Mapping): + raise ValueError(f"swarm rollout {subject} must be an object") + return payload + + +@dataclass(frozen=True) +class SwarmRolloutReleaseCoordinates: + """Accepted release snapshot; not an attestation of the executed native image. + + See: backend/notes/specifications/tanha/current/systems/platform/intern_resource_inventory.md. + """ + + runtime_image_release_id: str | None = None + resolved_image_digest: str | None = None + shared_bundle_release_id: str | None = None + task_bundle_release_id: str | None = None + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutReleaseCoordinates: + if not isinstance(payload, Mapping): + raise ValueError("swarm rollout release coordinates must be an object") + return cls( + runtime_image_release_id=_optional_text(payload, "runtime_image_release_id"), + resolved_image_digest=_optional_text(payload, "resolved_image_digest"), + shared_bundle_release_id=_optional_text(payload, "shared_bundle_release_id"), + task_bundle_release_id=_optional_text(payload, "task_bundle_release_id"), + ) + + +@dataclass(frozen=True) +class SwarmRolloutLogicalIntent: + """The one intended evaluation this rollout executes. + + ``accepted_submissions`` counts submissions (request keys) that attached to + this rollout instead of paying for another. ``intent_id`` is set only for + an explicit caller intent. + """ + + intent_ref: str + source: str + scope_kind: str + accepted_submissions: int + intent_id: str | None = None + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutLogicalIntent: + payload = _mapping(payload, "logical intent") + source = _text(payload, "source") + scope_kind = _text(payload, "scope_kind") + intent_id = _optional_text(payload, "intent_id") + if source not in {"explicit", "derived"}: + raise ValueError("swarm rollout logical intent source is unknown") + if scope_kind not in {"smr_run", "intern_runtime", "organization", "intern_delegation"}: + raise ValueError("swarm rollout logical intent scope is unknown") + if (source == "explicit") != (intent_id is not None): + raise ValueError("only an explicit logical intent carries an intent_id") + return cls( + intent_ref=_text(payload, "intent_ref"), + source=source, + scope_kind=scope_kind, + accepted_submissions=_integer(payload, "accepted_submissions", minimum=1), + intent_id=intent_id, + ) + + +@dataclass(frozen=True) +class SwarmRolloutLease: + lease_id: str + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutLease: + return cls(lease_id=_text(_mapping(payload, "lease"), "lease_id")) + + +@dataclass(frozen=True) +class SwarmRolloutLeaseDisposition: + """Why ``lease`` is or is not set; the backend never infers a nearby lease.""" + + status: str + reason: str + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutLeaseDisposition: + payload = _mapping(payload, "lease disposition") + status = _text(payload, "status") + if status not in {"recorded", "not_applicable"}: + raise ValueError("swarm rollout lease disposition status is unknown") + return cls(status=status, reason=_text(payload, "reason")) + + +@dataclass(frozen=True) +class SwarmRolloutExecutionLaunch: + """One provider sandbox launch (Harbor: agent, then optional verifier).""" + + sequence: int + image_attestation: str + role: str | None = None + provider: str | None = None + launch_id: str | None = None + executed_image_digest: str | None = None + recorded_at: datetime | None = None + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutExecutionLaunch: + payload = _mapping(payload, "execution launch") + return cls( + sequence=_integer(payload, "sequence"), + image_attestation=_text(payload, "image_attestation"), + role=_optional_text(payload, "role"), + provider=_optional_text(payload, "provider"), + launch_id=_optional_text(payload, "launch_id"), + executed_image_digest=_optional_digest(payload, "executed_image_digest"), + recorded_at=_optional_datetime(payload, "recorded_at"), + ) + + +@dataclass(frozen=True) +class SwarmRolloutExecution: + """What the backend executor actually launched for the rollout. + + ``executed_image_digest`` is set only when the launch reference was + digest-pinned or the provider resolved the exact image ID; otherwise + ``image_attestation`` says why it is unknown. ``lease`` is always stated: + ``None`` comes with a ``not_applicable`` disposition and its reason. + """ + + lease_disposition: SwarmRolloutLeaseDisposition + deployment_binding: str + image_attestation: str + lease: SwarmRolloutLease | None = None + recorded_by: str | None = None + interface: str | None = None + interface_mode: str | None = None + provider: str | None = None + launch_id: str | None = None + executed_image_digest: str | None = None + executed_source_digest: str | None = None + launches: tuple[SwarmRolloutExecutionLaunch, ...] = () + recorded_at: datetime | None = None + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutExecution: + payload = _mapping(payload, "execution") + if "lease" not in payload: + raise ValueError("swarm rollout execution must state its lease (null when not used)") + lease = ( + SwarmRolloutLease.from_wire(payload["lease"]) if payload["lease"] is not None else None + ) + disposition = SwarmRolloutLeaseDisposition.from_wire(payload.get("lease_disposition")) + if (disposition.status == "recorded") != (lease is not None): + raise ValueError("swarm rollout lease and lease disposition disagree") + launches = payload.get("launches", []) + if not isinstance(launches, list): + raise ValueError("swarm rollout execution launches must be a list") + return cls( + lease_disposition=disposition, + deployment_binding=_text(payload, "deployment_binding"), + image_attestation=_text(payload, "image_attestation"), + lease=lease, + recorded_by=_optional_text(payload, "recorded_by"), + interface=_optional_text(payload, "interface"), + interface_mode=_optional_text(payload, "interface_mode"), + provider=_optional_text(payload, "provider"), + launch_id=_optional_text(payload, "launch_id"), + executed_image_digest=_optional_digest(payload, "executed_image_digest"), + executed_source_digest=_optional_digest(payload, "executed_source_digest"), + launches=tuple(SwarmRolloutExecutionLaunch.from_wire(item) for item in launches), + recorded_at=_optional_datetime(payload, "recorded_at"), + ) + + +@dataclass(frozen=True) +class SwarmRolloutArtifact: + """A rollout-owned retained artifact (identity only; storage stays private).""" + + artifact_id: str + artifact_type: str + size_bytes: int + content_type: str | None = None + created_at: datetime | None = None + + @classmethod + def from_wire(cls, payload: object) -> SwarmRolloutArtifact: + payload = _mapping(payload, "artifact") + return cls( + artifact_id=_text(payload, "artifact_id"), + artifact_type=_text(payload, "artifact_type"), + size_bytes=_integer(payload, "size_bytes"), + content_type=_optional_text(payload, "content_type"), + created_at=_optional_datetime(payload, "created_at"), + ) + + +@dataclass(frozen=True) +class SwarmRollout: + rollout_id: str + pool_id: str + adapter: str + status: str + seed: int + trace_correlation_id: str + budget_parent_run_id: str + task_id: str | None = None + success: bool | None = None + score: float | None = None + error: str | None = None + created_at: datetime | None = None + started_at: datetime | None = None + completed_at: datetime | None = None + cancelled_at: datetime | None = None + release_coordinates: SwarmRolloutReleaseCoordinates | None = None + logical_intent: SwarmRolloutLogicalIntent | None = None + execution: SwarmRolloutExecution | None = None + artifacts: tuple[SwarmRolloutArtifact, ...] = () + + @classmethod + def from_wire(cls, payload: object) -> SwarmRollout: + payload = _mapping(payload, "swarm rollout") + seed = payload.get("seed") + if isinstance(seed, bool) or not isinstance(seed, int): + raise ValueError("swarm rollout seed must be an integer") + success = payload.get("success") + if success is not None and not isinstance(success, bool): + raise ValueError("swarm rollout success must be a boolean or null") + score = payload.get("score") + if score is not None and (isinstance(score, bool) or not isinstance(score, (int, float))): + raise ValueError("swarm rollout score must be a number or null") + artifacts = payload.get("artifacts", []) + if not isinstance(artifacts, list): + raise ValueError("swarm rollout artifacts must be a list") + return cls( + rollout_id=_text(payload, "rollout_id"), + pool_id=_text(payload, "pool_id"), + adapter=_text(payload, "adapter"), + status=_text(payload, "status"), + seed=seed, + trace_correlation_id=_text(payload, "trace_correlation_id"), + budget_parent_run_id=_text(payload, "budget_parent_run_id"), + task_id=_optional_text(payload, "task_id"), + success=success, + score=float(score) if score is not None else None, + error=_optional_text(payload, "error"), + created_at=_optional_datetime(payload, "created_at"), + started_at=_optional_datetime(payload, "started_at"), + completed_at=_optional_datetime(payload, "completed_at"), + cancelled_at=_optional_datetime(payload, "cancelled_at"), + release_coordinates=( + SwarmRolloutReleaseCoordinates.from_wire(payload["release_coordinates"]) + if payload.get("release_coordinates") is not None + else None + ), + logical_intent=( + SwarmRolloutLogicalIntent.from_wire(payload["logical_intent"]) + if payload.get("logical_intent") is not None + else None + ), + execution=( + SwarmRolloutExecution.from_wire(payload["execution"]) + if payload.get("execution") is not None + else None + ), + artifacts=tuple(SwarmRolloutArtifact.from_wire(item) for item in artifacts), + ) + + +def swarm_rollouts_from_wire(payload: object, *, swarm_id: str) -> tuple[SwarmRollout, ...]: + if not isinstance(payload, Mapping) or payload.get("run_id") != swarm_id: + raise ValueError("swarm rollout page identity drifted") + items = payload.get("items") + if not isinstance(items, list): + raise ValueError("swarm rollout page items must be a list") + rollouts = tuple(SwarmRollout.from_wire(item) for item in items) + if any(rollout.budget_parent_run_id != swarm_id for rollout in rollouts): + raise ValueError("swarm rollout budget parent drifted") + return rollouts + + +__all__ = [ + "SwarmRollout", + "SwarmRolloutArtifact", + "SwarmRolloutExecution", + "SwarmRolloutExecutionLaunch", + "SwarmRolloutLease", + "SwarmRolloutLeaseDisposition", + "SwarmRolloutLogicalIntent", + "SwarmRolloutReleaseCoordinates", + "swarm_rollouts_from_wire", +] diff --git a/synth_ai/sdk/research/contracts/swarms.py b/synth_ai/sdk/research/contracts/swarms.py index 2b8faf6f..ea6e5e58 100644 --- a/synth_ai/sdk/research/contracts/swarms.py +++ b/synth_ai/sdk/research/contracts/swarms.py @@ -59,6 +59,8 @@ class ActorHarness(StrEnum): class ActiveActorModel(StrEnum): """First-class models allowed for shared top-level agent selection.""" + OPENROUTER_GPT_5_6_LUNA = "openrouter/openai/gpt-5.6-luna" + GPT_5_4_MINI = "gpt-5.4-mini" GPT_5_6_LUNA = "gpt-5.6-luna" CURSOR_COMPOSER_2_5 = "cursor/composer-2.5" @@ -85,8 +87,6 @@ class DeprecatedActorModel(StrEnum): DEEPSEEK_REASONER = "deepseek/deepseek-reasoner" CURSOR_GPT_5 = "cursor/gpt-5" CURSOR_SONNET_4 = "cursor/sonnet-4" - GROK_4_3 = "x-ai/grok-4.3" - GROK_BUILD = "x-ai/grok-build" KIMI_K2_6 = "moonshotai/kimi-k2.6" KIMI_K3_BASETEN = "baseten/moonshotai/Kimi-K3" GLM_5_2 = "baseten/zai-org/GLM-5.2" @@ -101,6 +101,7 @@ class ActorModel(StrEnum): :class:`DeprecatedActorModel` only for legacy actor-specific overrides. """ + OPENROUTER_GPT_5_6_LUNA = ActiveActorModel.OPENROUTER_GPT_5_6_LUNA.value GPT_5_4_MINI = ActiveActorModel.GPT_5_4_MINI.value GPT_5_6_LUNA = ActiveActorModel.GPT_5_6_LUNA.value CURSOR_COMPOSER_2_5 = ActiveActorModel.CURSOR_COMPOSER_2_5.value @@ -122,8 +123,6 @@ class ActorModel(StrEnum): DEEPSEEK_REASONER = DeprecatedActorModel.DEEPSEEK_REASONER.value CURSOR_GPT_5 = DeprecatedActorModel.CURSOR_GPT_5.value CURSOR_SONNET_4 = DeprecatedActorModel.CURSOR_SONNET_4.value - GROK_4_3 = DeprecatedActorModel.GROK_4_3.value - GROK_BUILD = DeprecatedActorModel.GROK_BUILD.value KIMI_K2_6 = DeprecatedActorModel.KIMI_K2_6.value KIMI_K3_BASETEN = DeprecatedActorModel.KIMI_K3_BASETEN.value GLM_5_2 = DeprecatedActorModel.GLM_5_2.value @@ -196,7 +195,6 @@ class ResourceProvider(StrEnum): SYNTH_AI = "synth_ai" CURSOR = "cursor" DEEPSEEK = "deepseek" - XAI = "xai" MODAL = "modal" OPENAI_CHATGPT = "openai_chatgpt" BASETEN = "baseten" @@ -212,7 +210,6 @@ class CredentialProvider(StrEnum): DEEPSEEK = "deepseek" OPENAI = "openai" OPENROUTER = "openrouter" - XAI = "xai" TINKER = "tinker" SYNTH_INTERNAL = "synth_internal" @@ -227,7 +224,6 @@ class InferenceProvider(StrEnum): GOOGLE = "google" OPENROUTER = "openrouter" SYNTH = "synth" - XAI = "xai" SYNTH_INTERNAL = "synth_internal" @@ -250,7 +246,6 @@ class KickoffMessageMode(StrEnum): InferenceProvider.OPENAI.value, InferenceProvider.MODAL.value, InferenceProvider.SYNTH.value, - InferenceProvider.XAI.value, InferenceProvider.CURSOR.value, } ) @@ -280,7 +275,7 @@ def normalize_provider_selection( unsupported = tuple(item for item in normalized if item not in _PUBLIC_PROVIDER_SELECTIONS) if unsupported: raise ValueError( - "provider supports auto, modal, openai, synth, xai, and cursor; " + "provider supports auto, modal, openai, synth, and cursor; " f"unsupported: {', '.join(unsupported)}" ) if len(set(normalized)) != len(normalized): @@ -1294,6 +1289,10 @@ class Swarm: finished_at: datetime | None = None terminal_outcome: SwarmTerminalOutcome | None = None work_completed: bool = False + # The Intern Sync session ("sync") or Async assignment ("async") that + # launched this swarm; both None for swarms no Intern runtime started. + origin_runtime_kind: str | None = None + origin_runtime_id: str | None = None @classmethod def from_wire(cls, value: JsonValue) -> Swarm: @@ -1301,6 +1300,9 @@ def from_wire(cls, value: JsonValue) -> Swarm: work_mode = optional_text(payload, "work_mode") effort_id = optional_text(payload, "effort_id") terminal_outcome = optional_text(payload, "terminal_outcome") + origin_runtime_kind = optional_text(payload, "origin_runtime_kind") + if origin_runtime_kind not in {None, "sync", "async"}: + raise ValueError(f"swarm origin_runtime_kind is invalid: {origin_runtime_kind!r}") return cls( swarm_id=SwarmId(required_text(payload, "run_id")), project_id=ProjectId(required_text(payload, "project_id")), @@ -1318,6 +1320,8 @@ def from_wire(cls, value: JsonValue) -> Swarm: SwarmTerminalOutcome(terminal_outcome) if terminal_outcome is not None else None ), work_completed=optional_bool(payload, "work_completed"), + origin_runtime_kind=origin_runtime_kind, + origin_runtime_id=optional_text(payload, "origin_runtime_id"), ) def require_terminal_outcome(self) -> SwarmTerminalOutcome: diff --git a/synth_ai/sdk/research/contracts/transcript.py b/synth_ai/sdk/research/contracts/transcript.py index bd79bc25..0254292e 100644 --- a/synth_ai/sdk/research/contracts/transcript.py +++ b/synth_ai/sdk/research/contracts/transcript.py @@ -40,11 +40,13 @@ class TranscriptProjectionAuthority(StrEnum): LIVE_CONTROL_PLANE = "smr_control_plane.redis.live_transcript.v1" TERMINAL_ARCHIVE = "smr_transcript_events.terminal_archive.v1" + DURABLE_REPLAY = "smr_transcript_events.durable_replay.v1" class TranscriptReplayMode(StrEnum): LIVE_TAIL = "live_tail" TERMINAL_ARCHIVE = "terminal_archive" + DURABLE_REPLAY = "durable_replay" class TranscriptCursorKind(StrEnum): @@ -276,7 +278,7 @@ def __post_init__(self) -> None: if not self.live_tail_available: raise ValueError("live_tail requires live_tail_available=true") elif self.live_tail_available: - raise ValueError("terminal archive cannot advertise a live tail") + raise ValueError("durable replay cannot advertise a live tail") @classmethod def from_wire(cls, value: JsonValue) -> TranscriptFreshness: @@ -456,3 +458,50 @@ def to_wire(self) -> JsonObject: "TranscriptView", "TranscriptVisibilityDecision", ] + + +TASK_COMPLETION_CLAIMED_EVENT_KIND = "task.completion_claimed" +TASK_COMPLETION_CLAIMED_SCHEMA_VERSION = "smr.task-completion-claimed.v1" + + +@dataclass(frozen=True, slots=True) +class TaskCompletionClaimedEvent: + """A worker's accepted completion claim, projected into the transcript. + + The backend derives it from the durable claim row: ``text`` is the + worker's stated result (bounded, redacted) and ``participant_id`` is the + worker's stable actor key, distinct per worker. + """ + + event_id: TranscriptEventId + participant_session_id: ParticipantSessionId + participant_id: str + turn_id: str | None + claim_id: str + task_key: str + claimed_state: str + text: str | None + summary: str | None + text_truncated: bool + + @classmethod + def from_transcript_event(cls, event: SwarmTranscriptEvent) -> TaskCompletionClaimedEvent: + if event.kind != TASK_COMPLETION_CLAIMED_EVENT_KIND: + raise ValueError(f"not a {TASK_COMPLETION_CLAIMED_EVENT_KIND} event: {event.kind}") + if event.event_id is None: + raise ValueError("task.completion_claimed requires a durable event_id") + payload = event.payload + if required_string(payload, "schema_version") != TASK_COMPLETION_CLAIMED_SCHEMA_VERSION: + raise ValueError("unsupported task.completion_claimed schema_version") + return cls( + event_id=event.event_id, + participant_session_id=event.participant_session_id, + participant_id=required_string(payload, "participant_id"), + turn_id=event.turn_id, + claim_id=required_string(payload, "claim_id"), + task_key=required_string(payload, "task_key"), + claimed_state=required_string(payload, "claimed_state"), + text=optional_string(payload, "text"), + summary=optional_string(payload, "summary"), + text_truncated=bool(payload.get("text_truncated", False)), + ) diff --git a/synth_ai/sdk/research/contracts/usage.py b/synth_ai/sdk/research/contracts/usage.py index f75bc41f..924b3963 100644 --- a/synth_ai/sdk/research/contracts/usage.py +++ b/synth_ai/sdk/research/contracts/usage.py @@ -130,23 +130,33 @@ def to_wire(self) -> JsonObject: } +_TOKEN_USAGE_FIELDS = frozenset({"sessions_seen", "session_snapshots_count", "totals", "by_model"}) + + @dataclass(frozen=True, slots=True) class TokenUsage: sessions_seen: int session_snapshots_count: int totals: TokenCounts by_model: Mapping[str, TokenCounts] + # Backend SmrSwarmTokenUsageResponse.unattributed_sessions (default 0): + # sessions whose token rows carry no resolved model. Backends before + # 2026-07-26 omit it, so it is accepted but not required. + unattributed_sessions: int = 0 def __post_init__(self) -> None: if self.session_snapshots_count != self.totals.snapshots: raise ValueError("session_snapshots_count must equal totals.snapshots") + if self.unattributed_sessions > self.sessions_seen: + raise ValueError("unattributed_sessions cannot exceed sessions_seen") @classmethod def from_wire(cls, value: JsonValue) -> TokenUsage: + has_unattributed = isinstance(value, dict) and "unattributed_sessions" in value payload = _exact_object( value, label="swarm token usage", - fields=frozenset({"sessions_seen", "session_snapshots_count", "totals", "by_model"}), + fields=_TOKEN_USAGE_FIELDS | ({"unattributed_sessions"} if has_unattributed else set()), ) models = object_value(payload["by_model"], operation_id="swarm token usage.by_model") return cls( @@ -156,12 +166,16 @@ def from_wire(cls, value: JsonValue) -> TokenUsage: by_model=MappingProxyType( {name: TokenCounts.from_wire(model) for name, model in models.items()} ), + unattributed_sessions=( + _non_negative_int(payload, "unattributed_sessions") if has_unattributed else 0 + ), ) def to_wire(self) -> JsonObject: return { "sessions_seen": self.sessions_seen, "session_snapshots_count": self.session_snapshots_count, + "unattributed_sessions": self.unattributed_sessions, "totals": self.totals.to_wire(), "by_model": {name: usage.to_wire() for name, usage in self.by_model.items()}, } diff --git a/synth_ai/sdk/research/facade.py b/synth_ai/sdk/research/facade.py index 960703f9..0004ba28 100644 --- a/synth_ai/sdk/research/facade.py +++ b/synth_ai/sdk/research/facade.py @@ -7,6 +7,7 @@ from synth_ai.sdk.research.account import ResearchAccountAPI from synth_ai.sdk.research.client import Client as CoreResearchClient +from synth_ai.sdk.research.container_pools.api import ContainerPoolsAPI from synth_ai.sdk.research.environments import EnvironmentsAPI from synth_ai.sdk.research.experiments import ResearchExperimentsAPI from synth_ai.sdk.research.factories import FactoriesAPI @@ -209,6 +210,16 @@ def image_releases(self) -> ImageReleasesAPI: """Immutable customer image-release receipts and actor runtime images.""" return self._core.image_releases + @property + def container_pools(self) -> ContainerPoolsAPI: + """Container pools, their runtime image releases, and graded rollouts. + + Distinct from `image_releases` above, which is the SMR actor runtime + surface at `/smr/v1/image-releases`. A pool's deployable image is a + *runtime image release* under `container_pools.create_release`. + """ + return self._core.container_pools + @property def files(self) -> FilesAPI: """Run and project file APIs, including trace-bundle run outputs. diff --git a/synth_ai/sdk/research/intern_blockers.py b/synth_ai/sdk/research/intern_blockers.py new file mode 100644 index 00000000..025c5b57 --- /dev/null +++ b/synth_ai/sdk/research/intern_blockers.py @@ -0,0 +1,123 @@ +"""Explicit Async blocker controls; see backend packages/intern/contracts.py.""" + +from .contracts.intern_blockers import ( + InternBlockerOpenSyncRequest, + InternBlockerOpenSyncResponse, + InternBlockerResolveRequest, + InternBlockerResolveResponse, +) +from .contracts.research_intern import InternAsyncBlocker + + +def _request_for(blocker_id, action=None, request=None): + from .research_intern import _request + + operations = { + None: "get_intern_async_blocker", + "open-sync": "open_intern_async_blocker_sync", + "resolve": "resolve_intern_async_blocker", + } + path = f"/smr/research-intern/async/blockers/{blocker_id}" + if action: + path += f"/{action}" + return _request( + operations[action], path, **({"body": request.model_dump(mode="json")} if request else {}) + ) + + +def _read(payload, blocker_id): + result = InternAsyncBlocker.from_wire(payload) + if result.blocker_id != blocker_id: + raise ValueError("Async blocker identity drifted") + return result + + +def _open(payload, blocker_id, request): + result = InternBlockerOpenSyncResponse.model_validate(payload) + receipt = result.handoff_receipt + if ( + result.blocker.blocker_id != blocker_id + or receipt.blocker_id != blocker_id + or receipt.context.blocker_id != blocker_id + or receipt.context.async_assignment_id != result.blocker.async_assignment_id + or receipt.idempotency_key != request.idempotency_key + or receipt.sync_session_id != result.sync_session.sync_session_id + or result.blocker.sync_session_id != receipt.sync_session_id + or receipt.org_id != result.sync_session.org_id + ): + raise ValueError("Async blocker handoff identity drifted") + return result + + +def _resolve(payload, blocker_id, request): + result = InternBlockerResolveResponse.model_validate(payload) + receipt = result.blocker.resolution_receipt or {} + if ( + result.blocker.blocker_id != blocker_id + or not result.blocker.async_assignment_id + or result.continuation_command.runtime_id != result.blocker.async_assignment_id + or receipt.get("idempotency_key") != request.idempotency_key + or receipt.get("outcome") != request.outcome + or receipt.get("comment") != request.comment + or tuple(receipt.get("supporting_receipt_ids") or ()) != request.supporting_receipt_ids + or receipt.get("continuation_command_id") != result.continuation_command.command_id + or receipt.get("sync_session_id") != result.blocker.sync_session_id + ): + raise ValueError("Async blocker resolution identity drifted") + return result + + +class InternBlockersAPI: + def __init__(self, transport): + self._transport = transport + + def get(self, blocker_id: str) -> InternAsyncBlocker: + return _read(self._transport.execute(_request_for(blocker_id)), blocker_id) + + def open_sync( + self, blocker_id: str, request: InternBlockerOpenSyncRequest + ) -> InternBlockerOpenSyncResponse: + """Open the handoff; this does not approve or resolve the blocked action.""" + return _open( + self._transport.execute(_request_for(blocker_id, "open-sync", request)), + blocker_id, + request, + ) + + def resolve( + self, blocker_id: str, request: InternBlockerResolveRequest + ) -> InternBlockerResolveResponse: + """Submit an explicit disposition and return its durable continuation receipt.""" + return _resolve( + self._transport.execute(_request_for(blocker_id, "resolve", request)), + blocker_id, + request, + ) + + +class AsyncInternBlockersAPI: + def __init__(self, transport): + self._transport = transport + + async def get(self, blocker_id: str) -> InternAsyncBlocker: + return _read(await self._transport.execute(_request_for(blocker_id)), blocker_id) + + async def open_sync( + self, blocker_id: str, request: InternBlockerOpenSyncRequest + ) -> InternBlockerOpenSyncResponse: + """Open the handoff; this does not approve or resolve the blocked action.""" + return _open( + await self._transport.execute(_request_for(blocker_id, "open-sync", request)), + blocker_id, + request, + ) + + async def resolve( + self, blocker_id: str, request: InternBlockerResolveRequest + ) -> InternBlockerResolveResponse: + """Submit an explicit disposition and return its durable continuation receipt.""" + return _resolve( + await self._transport.execute(_request_for(blocker_id, "resolve", request)), + blocker_id, + request, + ) diff --git a/synth_ai/sdk/research/operations.py b/synth_ai/sdk/research/operations.py index 0f724c12..160dc724 100644 --- a/synth_ai/sdk/research/operations.py +++ b/synth_ai/sdk/research/operations.py @@ -1263,6 +1263,60 @@ def _operation( "/smr/runs/{run_id}/usage-summary", idempotent=True, ), + _operation( + "get_run_resource_settlement", + HttpMethod.GET, + "/smr/runs/{run_id}/resource-settlement", + idempotent=True, + ), + _operation( + "list_swarm_rollouts", + HttpMethod.GET, + "/smr/runs/{run_id}/rollouts", + idempotent=True, + ), + _operation( + "get_intern_sync_session_usage", + HttpMethod.GET, + "/smr/research-intern/sync-sessions/{sync_session_id}/usage", + idempotent=True, + ), + _operation( + "get_intern_sync_session_resources", + HttpMethod.GET, + "/smr/research-intern/sync-sessions/{sync_session_id}/resources", + idempotent=True, + ), + _operation( + "get_intern_async_assignment_resources", + HttpMethod.GET, + "/smr/research-intern/async-assignments/{assignment_id}/resources", + idempotent=True, + ), + _operation( + "get_intern_async_assignment_usage", + HttpMethod.GET, + "/smr/research-intern/async-assignments/{assignment_id}/usage", + idempotent=True, + ), + _operation( + "get_intern_async_blocker", + HttpMethod.GET, + "/smr/research-intern/async/blockers/{blocker_id}", + idempotent=True, + ), + _operation( + "open_intern_async_blocker_sync", + HttpMethod.POST, + "/smr/research-intern/async/blockers/{blocker_id}/open-sync", + idempotent=True, + ), + _operation( + "resolve_intern_async_blocker", + HttpMethod.POST, + "/smr/research-intern/async/blockers/{blocker_id}/resolve", + idempotent=True, + ), _operation( "retrieve_swarm_workspace_archive", HttpMethod.GET, diff --git a/synth_ai/sdk/research/research_intern.py b/synth_ai/sdk/research/research_intern.py index 8503e5d4..16a617c0 100644 --- a/synth_ai/sdk/research/research_intern.py +++ b/synth_ai/sdk/research/research_intern.py @@ -24,6 +24,8 @@ FactoryRoleReceiptMintRequest, FactoryRoleReceiptResponse, ) +from synth_ai.sdk.research.contracts.intern_resources import InternResourceInventory +from synth_ai.sdk.research.contracts.intern_usage import InternSessionUsage from synth_ai.sdk.research.contracts.project_runtime import ( ProjectComputerExecuteRequest, ProjectComputerInspectRequest, @@ -1032,6 +1034,39 @@ def approvals(self, sync_session_id: str) -> tuple[InternSyncApprovalCard, ...]: raise ValueError("Sync Intern approval card identity drifted") return cards + def usage(self, sync_session_id: str) -> InternSessionUsage: + """Usage receipt for this session: direct inference plus the runs it launched. + + ``spend_cents`` is settled run spend; until ``billing.billing_state`` is + ``settled`` the total is incomplete, not a final zero. + """ + + usage = InternSessionUsage.from_wire( + self._transport.execute( + _request( + "get_intern_sync_session_usage", + f"{self._PATH}/{sync_session_id}/usage", + ) + ) + ) + if usage.origin_runtime_kind != "sync" or usage.origin_runtime_id != sync_session_id: + raise ValueError("Sync Intern usage identity drifted") + return usage + + def resources(self, sync_session_id: str) -> InternResourceInventory: + """Fresh recorded-session inventory; incomplete coverage remains explicit.""" + inventory = InternResourceInventory.from_wire( + self._transport.execute( + _request( + "get_intern_sync_session_resources", + f"{self._PATH}/{sync_session_id}/resources", + ) + ) + ) + if (inventory.runtime_kind, inventory.runtime_id) != ("sync", sync_session_id): + raise ValueError("Sync Intern inventory identity drifted") + return inventory + def decide_approval( self, approval_id: str, @@ -1309,6 +1344,12 @@ def tail( class ResearchInternAsyncRuntimeAPI: + @property + def blockers(self): + from .intern_blockers import InternBlockersAPI + + return InternBlockersAPI(self._transport) + """Synchronous transport for the organization's singleton Async Intern.""" _PATH = "/smr/research-intern/async" @@ -1346,6 +1387,34 @@ def get(self) -> InternAsyncRuntime: self._transport.execute(_request("get_intern_async_runtime", self._PATH)) ) + def resources(self, assignment_id: str) -> InternResourceInventory: + """Read the exact recorded assignment, never substitute the current singleton.""" + inventory = InternResourceInventory.from_wire( + self._transport.execute( + _request( + "get_intern_async_assignment_resources", + f"/smr/research-intern/async-assignments/{assignment_id}/resources", + ) + ) + ) + if (inventory.runtime_kind, inventory.runtime_id) != ("async", assignment_id): + raise ValueError("Async Intern inventory identity drifted") + return inventory + + def usage(self, assignment_id: str) -> InternSessionUsage: + """Read recorded-assignment usage; unsettled billing remains incomplete.""" + usage = InternSessionUsage.from_wire( + self._transport.execute( + _request( + "get_intern_async_assignment_usage", + f"/smr/research-intern/async-assignments/{assignment_id}/usage", + ) + ) + ) + if (usage.origin_runtime_kind, usage.origin_runtime_id) != ("async", assignment_id): + raise ValueError("Async Intern usage identity drifted") + return usage + def command(self, request: InternAsyncCommandRequest) -> InternAsyncCommandReceipt: """Submit a fenced Async command and reject command receipt identity drift.""" receipt = InternAsyncCommandReceipt.from_wire( @@ -2724,6 +2793,34 @@ class AsyncResearchInternSyncRuntimeAPI: _PATH = "/smr/research-intern/sync-sessions" + async def usage(self, sync_session_id: str) -> InternSessionUsage: + """Read session usage; unsettled billing remains incomplete.""" + usage = InternSessionUsage.from_wire( + await self._transport.execute( + _request( + "get_intern_sync_session_usage", + f"{self._PATH}/{sync_session_id}/usage", + ) + ) + ) + if (usage.origin_runtime_kind, usage.origin_runtime_id) != ("sync", sync_session_id): + raise ValueError("Sync Intern usage identity drifted") + return usage + + async def resources(self, sync_session_id: str) -> InternResourceInventory: + """Fresh recorded-session inventory; incomplete coverage remains explicit.""" + inventory = InternResourceInventory.from_wire( + await self._transport.execute( + _request( + "get_intern_sync_session_resources", + f"{self._PATH}/{sync_session_id}/resources", + ) + ) + ) + if (inventory.runtime_kind, inventory.runtime_id) != ("sync", sync_session_id): + raise ValueError("Sync Intern inventory identity drifted") + return inventory + def __init__( self, transport: AsyncHttpTransport, @@ -3145,10 +3242,44 @@ async def tail( class AsyncResearchInternAsyncRuntimeAPI: + @property + def blockers(self): + from .intern_blockers import AsyncInternBlockersAPI + + return AsyncInternBlockersAPI(self._transport) + """Native async transport for the organization's singleton Async Intern.""" _PATH = "/smr/research-intern/async" + async def usage(self, assignment_id: str) -> InternSessionUsage: + """Read recorded-assignment usage; unsettled billing remains incomplete.""" + usage = InternSessionUsage.from_wire( + await self._transport.execute( + _request( + "get_intern_async_assignment_usage", + f"/smr/research-intern/async-assignments/{assignment_id}/usage", + ) + ) + ) + if (usage.origin_runtime_kind, usage.origin_runtime_id) != ("async", assignment_id): + raise ValueError("Async Intern usage identity drifted") + return usage + + async def resources(self, assignment_id: str) -> InternResourceInventory: + """Read the exact recorded assignment, never substitute the current singleton.""" + inventory = InternResourceInventory.from_wire( + await self._transport.execute( + _request( + "get_intern_async_assignment_resources", + f"/smr/research-intern/async-assignments/{assignment_id}/resources", + ) + ) + ) + if (inventory.runtime_kind, inventory.runtime_id) != ("async", assignment_id): + raise ValueError("Async Intern inventory identity drifted") + return inventory + def __init__(self, transport: AsyncHttpTransport) -> None: self._transport = transport diff --git a/synth_ai/sdk/research/session/client.py b/synth_ai/sdk/research/session/client.py index ea5d800e..6720f7bf 100644 --- a/synth_ai/sdk/research/session/client.py +++ b/synth_ai/sdk/research/session/client.py @@ -47,6 +47,7 @@ from synth_ai.sdk.research.contracts.local_execution_profile import ( LocalExecutionProfile, ) +from synth_ai.sdk.research.contracts.resource_settlement import RunResourceSettlement from synth_ai.sdk.research.contracts.run_execution import RunExecutionProjection from synth_ai.sdk.research.contracts.run_launch import ( RunLaunchRequest, @@ -1463,6 +1464,9 @@ def get_billing_entitlements(self) -> BillingEntitlementSnapshot: def get_run_usage(self, run_id: str) -> SmrRunUsage: return self.usage.get_run_usage(run_id) + def get_run_resource_settlement(self, run_id: str) -> RunResourceSettlement: + return self.usage.get_run_resource_settlement(run_id) + def get_run_resource_limits(self, run_id: str) -> SmrResourceLimits: return self.usage.get_run_resource_limits(run_id) @@ -3034,6 +3038,38 @@ def list_project_files( label="list_project_files", ) + def create_org_file( + self, + *, + name: str, + content: str, + encoding: str = "utf-8", + content_type: str | None = None, + sync_session_id: str | None = None, + project_id: str | None = None, + metadata: Mapping[str, Any] | None = None, + ) -> dict[str, Any]: + """Create a file using the backend's org-file contract. + + # See: backend/app/api/v1/managed_research/files.py::create_file + """ + return _coerce_dict( + self._request_json( + "POST", + "/smr/files", + json_body={ + "name": name, + "content": content, + "encoding": encoding, + "content_type": content_type, + "sync_session_id": sync_session_id, + "project_id": project_id, + "metadata": dict(metadata or {}), + }, + ), + label="create_org_file", + ) + def create_project_files( self, project_id: str, @@ -5790,13 +5826,22 @@ def list_runs( public_state: str | None = None, limit: int = 50, cursor: str | None = None, + origin_runtime_kind: str | None = None, + origin_runtime_id: str | None = None, ) -> list[dict[str, Any]]: + """List runs; pass both origin fields to get the runs one Intern runtime launched.""" + if (origin_runtime_kind is None) != (origin_runtime_id is None): + raise ValueError("origin_runtime_kind and origin_runtime_id are set together") if active_only: + if origin_runtime_id is not None: + raise ValueError("active_only does not combine with an origin filter") return self.list_active_runs(project_id) params = build_query_params( public_state=public_state, limit=limit, cursor=cursor, + origin_runtime_kind=origin_runtime_kind, + origin_runtime_id=origin_runtime_id, ) return _coerce_dict_list( self._request_json("GET", f"/smr/projects/{project_id}/runs", params=params), diff --git a/synth_ai/sdk/research/session/files.py b/synth_ai/sdk/research/session/files.py index 57c43185..149eff41 100644 --- a/synth_ai/sdk/research/session/files.py +++ b/synth_ai/sdk/research/session/files.py @@ -4,7 +4,7 @@ from collections.abc import Iterable, Mapping from os import PathLike -from typing import Any +from typing import Any, Literal from synth_ai.sdk.research.contracts.types import ( ResourceUploadResult, @@ -16,6 +16,36 @@ class FilesAPI(_ClientNamespace): + def create_org( + self, + *, + name: str, + content: str, + encoding: Literal["utf-8", "base64"] = "utf-8", + content_type: str | None = None, + sync_session_id: str | None = None, + project_id: str | None = None, + metadata: Mapping[str, Any] | None = None, + ) -> dict[str, Any]: + """Create an org-owned file, optionally bound to a session or project. + + # See: backend/app/api/v1/managed_research/files.py::SmrFileCreateRequest + Unbound files retain a null project_id; they are not project StoredFiles. + """ + if not name.strip() or len(name) > 512: + raise ValueError("name must contain 1 through 512 characters") + if encoding not in {"utf-8", "base64"}: + raise ValueError("encoding must be utf-8 or base64") + return self._client.create_org_file( + name=name, + content=content, + encoding=encoding, + content_type=content_type, + sync_session_id=sync_session_id, + project_id=project_id, + metadata=dict(metadata or {}), + ) + def list_project( self, project_id: str, diff --git a/synth_ai/sdk/research/session/usage.py b/synth_ai/sdk/research/session/usage.py index 30b65d03..376efeed 100644 --- a/synth_ai/sdk/research/session/usage.py +++ b/synth_ai/sdk/research/session/usage.py @@ -4,6 +4,8 @@ from collections.abc import Mapping +from synth_ai.sdk.research.contracts.intern_usage import InternSessionUsage +from synth_ai.sdk.research.contracts.resource_settlement import RunResourceSettlement from synth_ai.sdk.research.contracts.wire_models import ( BillingEntitlementSnapshot, OrgLimits, @@ -113,6 +115,34 @@ def get_run_usage(self, run_id: str) -> SmrRunUsage: _raise_on_error_payload(self._client._request_json("GET", f"/smr/runs/{run_id}/usage")) ) + def get_intern_sync_session_usage(self, sync_session_id: str) -> InternSessionUsage: + """Usage receipt for one Intern Sync session, including the runs it launched.""" + return InternSessionUsage.from_wire( + _raise_on_error_payload( + self._client._request_json( + "GET", f"/smr/research-intern/sync-sessions/{sync_session_id}/usage" + ) + ) + ) + + def get_intern_async_assignment_usage(self, assignment_id: str) -> InternSessionUsage: + """Usage receipt for one Intern Async assignment, including the runs it launched.""" + return InternSessionUsage.from_wire( + _raise_on_error_payload( + self._client._request_json( + "GET", f"/smr/research-intern/async-assignments/{assignment_id}/usage" + ) + ) + ) + + def get_run_resource_settlement(self, run_id: str) -> RunResourceSettlement: + """Fresh (uncached) read of whether the resources a run registered are disposed.""" + return RunResourceSettlement.from_wire( + _raise_on_error_payload( + self._client._request_json("GET", f"/smr/runs/{run_id}/resource-settlement") + ) + ) + def get_run_resource_limits(self, run_id: str) -> SmrResourceLimits: return SmrResourceLimits.from_wire( _raise_on_error_payload( diff --git a/synth_ai/sdk/research/swarms.py b/synth_ai/sdk/research/swarms.py index 332a1168..673d52df 100644 --- a/synth_ai/sdk/research/swarms.py +++ b/synth_ai/sdk/research/swarms.py @@ -4,7 +4,7 @@ import asyncio import time -from collections.abc import AsyncIterator, Iterator +from collections.abc import AsyncIterator, Iterator, Mapping from synth_ai.core.contracts.json_value import JsonObject, JsonValue from synth_ai.core.errors import SynthError @@ -24,7 +24,9 @@ ContentDisposition, SwarmEvidence, ) +from synth_ai.sdk.research.contracts.resource_settlement import RunResourceSettlement from synth_ai.sdk.research.contracts.status import SwarmStatus +from synth_ai.sdk.research.contracts.swarm_rollouts import SwarmRollout, swarm_rollouts_from_wire from synth_ai.sdk.research.contracts.swarms import ( BranchResult, BranchSpec, @@ -53,12 +55,14 @@ def _request( *, query: JsonObject | None = None, body: JsonObject | None = None, + headers: Mapping[str, str] | None = None, ) -> HttpRequest: return HttpRequest( research_operation(operation_id), path, query=query or {}, body=body, + headers=headers or {}, ) @@ -313,12 +317,16 @@ def create( request: SwarmSpec, *, project_id: ProjectId | None = None, + runtime_binding_context_id: str | None = None, ) -> SwarmHandle: """Create a Swarm from a typed Swarm specification. + # See: backend/app/api/v1/managed_research/projects.py (project trigger) Args: request: Swarm specification to serialize into the create request body. project_id: Optional Project that should own the Swarm. + runtime_binding_context_id: Opaque manager-issued placement context + redeemed by the backend for this project trigger only. Returns: A handle for the created Swarm. @@ -329,7 +337,18 @@ def create( else: operation_id = "trigger_project_run" path = f"/smr/projects/{project_id}/trigger" - value = self._transport.execute(_request(operation_id, path, body=request.to_wire())) + headers: dict[str, str] = {} + if runtime_binding_context_id is not None: + if project_id is None: + raise ValueError("runtime binding context requires a project") + if not runtime_binding_context_id.strip() or any( + character in runtime_binding_context_id for character in "\r\n" + ): + raise ValueError("runtime_binding_context_id must be a non-empty header value") + headers["X-Synth-Runtime-Binding-Context"] = runtime_binding_context_id + value = self._transport.execute( + _request(operation_id, path, body=request.to_wire(), headers=headers) + ) return SwarmHandle(self, Swarm.from_wire(value)) def list( @@ -338,6 +357,8 @@ def list( *, limit: int = 100, cursor: str | None = None, + origin_runtime_kind: str | None = None, + origin_runtime_id: str | None = None, ) -> SyncPage[Swarm]: """List Swarms for a Project. @@ -345,13 +366,23 @@ def list( project_id: Project whose Swarms to list. limit: Maximum number of Swarms to request. cursor: Optional pagination cursor returned by the backend. + origin_runtime_kind: ``"sync"`` or ``"async"``; with + ``origin_runtime_id``, list only the Swarms that Intern runtime launched. + origin_runtime_id: Intern Sync session or Async assignment id. Returns: A typed page containing Swarms and any continuation cursor. """ + if (origin_runtime_kind is None) != (origin_runtime_id is None): + raise ValueError("origin_runtime_kind and origin_runtime_id are set together") + if origin_runtime_kind not in {None, "sync", "async"}: + raise ValueError(f"origin_runtime_kind is invalid: {origin_runtime_kind!r}") query: JsonObject = {"limit": limit} if cursor is not None: query["cursor"] = cursor + if origin_runtime_kind is not None and origin_runtime_id is not None: + query["origin_runtime_kind"] = origin_runtime_kind + query["origin_runtime_id"] = origin_runtime_id value = self._transport.execute( _request( "list_project_runs", @@ -393,6 +424,31 @@ def usage(self, swarm_id: SwarmId) -> SwarmUsage: ) return SwarmUsage.from_wire(value) + def rollouts(self, swarm_id: SwarmId, *, limit: int = 100) -> tuple[SwarmRollout, ...]: + """Container-pool rollouts this Swarm launched (verified budget parent only).""" + value = self._transport.execute( + _request( + "list_swarm_rollouts", + f"/smr/runs/{swarm_id}/rollouts", + query={"limit": limit}, + ) + ) + return swarm_rollouts_from_wire(value, swarm_id=str(swarm_id)) + + def resource_settlement(self, swarm_id: SwarmId) -> RunResourceSettlement: + """Fresh read of whether the resources this Swarm registered are disposed. + + ``settled`` covers registered resources only; ``coverage_complete`` says + whether that inventory is complete. ``coverage="untracked"`` is no claim. + """ + value = self._transport.execute( + _request( + "get_run_resource_settlement", + f"/smr/runs/{swarm_id}/resource-settlement", + ) + ) + return RunResourceSettlement.from_wire(value) + def evidence(self, swarm_id: SwarmId) -> SwarmEvidence: """Return durable artifact and WorkProduct evidence.""" value = self._transport.execute( @@ -875,12 +931,16 @@ async def create( request: SwarmSpec, *, project_id: ProjectId | None = None, + runtime_binding_context_id: str | None = None, ) -> AsyncSwarmHandle: """Create a Swarm from a typed Swarm specification. + # See: backend/app/api/v1/managed_research/projects.py (project trigger) Args: request: Swarm specification to serialize into the create request body. project_id: Optional Project that should own the Swarm. + runtime_binding_context_id: Opaque manager-issued placement context + redeemed by the backend for this project trigger only. Returns: A handle for the created Swarm. @@ -891,7 +951,18 @@ async def create( else: operation_id = "trigger_project_run" path = f"/smr/projects/{project_id}/trigger" - value = await self._transport.execute(_request(operation_id, path, body=request.to_wire())) + headers: dict[str, str] = {} + if runtime_binding_context_id is not None: + if project_id is None: + raise ValueError("runtime binding context requires a project") + if not runtime_binding_context_id.strip() or any( + character in runtime_binding_context_id for character in "\r\n" + ): + raise ValueError("runtime_binding_context_id must be a non-empty header value") + headers["X-Synth-Runtime-Binding-Context"] = runtime_binding_context_id + value = await self._transport.execute( + _request(operation_id, path, body=request.to_wire(), headers=headers) + ) return AsyncSwarmHandle(self, Swarm.from_wire(value)) async def list( @@ -900,6 +971,8 @@ async def list( *, limit: int = 100, cursor: str | None = None, + origin_runtime_kind: str | None = None, + origin_runtime_id: str | None = None, ) -> SyncPage[Swarm]: """List Swarms for a Project. @@ -907,13 +980,23 @@ async def list( project_id: Project whose Swarms to list. limit: Maximum number of Swarms to request. cursor: Optional pagination cursor returned by the backend. + origin_runtime_kind: ``"sync"`` or ``"async"``; with + ``origin_runtime_id``, list only the Swarms that Intern runtime launched. + origin_runtime_id: Intern Sync session or Async assignment id. Returns: A typed page containing Swarms and any continuation cursor. """ + if (origin_runtime_kind is None) != (origin_runtime_id is None): + raise ValueError("origin_runtime_kind and origin_runtime_id are set together") + if origin_runtime_kind not in {None, "sync", "async"}: + raise ValueError(f"origin_runtime_kind is invalid: {origin_runtime_kind!r}") query: JsonObject = {"limit": limit} if cursor is not None: query["cursor"] = cursor + if origin_runtime_kind is not None and origin_runtime_id is not None: + query["origin_runtime_kind"] = origin_runtime_kind + query["origin_runtime_id"] = origin_runtime_id value = await self._transport.execute( _request("list_project_runs", f"/smr/projects/{project_id}/runs", query=query) ) @@ -951,6 +1034,17 @@ async def usage(self, swarm_id: SwarmId) -> SwarmUsage: ) return SwarmUsage.from_wire(value) + async def rollouts(self, swarm_id: SwarmId, *, limit: int = 100) -> tuple[SwarmRollout, ...]: + """Container-pool rollouts this Swarm launched (verified budget parent only).""" + value = await self._transport.execute( + _request( + "list_swarm_rollouts", + f"/smr/runs/{swarm_id}/rollouts", + query={"limit": limit}, + ) + ) + return swarm_rollouts_from_wire(value, swarm_id=str(swarm_id)) + async def evidence(self, swarm_id: SwarmId) -> SwarmEvidence: """Return durable artifact and WorkProduct evidence.""" value = await self._transport.execute( diff --git a/tests/test_container_pools_sdk.py b/tests/test_container_pools_sdk.py new file mode 100644 index 00000000..8f0c018f --- /dev/null +++ b/tests/test_container_pools_sdk.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +import base64 +import io +import tarfile +from pathlib import Path +from typing import Any + +import pytest +from synth_ai.core.http.request import HttpRequest +from synth_ai.sdk.research.container_pools import ( + ContainerPoolsAPI, + HarborBundleError, + build_docker_context_archive, +) + + +class _Transport: + def __init__(self, responses: list[Any]) -> None: + self.responses = iter(responses) + self.requests: list[HttpRequest] = [] + + def execute(self, request: HttpRequest) -> Any: + self.requests.append(request) + return next(self.responses) + + +def test_container_pool_control_plane_routes_and_contracts() -> None: + transport = _Transport( + [ + {"pool_id": "pool one", "backend": "arbitrary", "status": "active"}, + {"release_id": "release/one", "source_kind": "docker_context"}, + {"bound": True}, + {"rollout_id": "rollout one", "status": "queued"}, + {"operation_id": "operation-1", "status": "completed"}, + {"operation_id": "operation-1", "state": "completed"}, + ] + ) + api = ContainerPoolsAPI(transport) # type: ignore[arg-type] + + pool = api.get("pool one") + release = api.get_release(pool.pool_id, "release/one") + bound = api.bind_release(pool.pool_id, release.release_id, bind_timeout_seconds=45) + rollout = api.create_rollout(pool.pool_id, task_id="task-1", seed=7) + mutation = api.mutate_deployment( + pool.pool_id, + "task-1", + project_id="11111111-1111-1111-1111-111111111111", + operation="update", + idempotency_key="update-1", + expected_revision="revision-1", + payload={"image": "example:v2"}, + ) + reconciled = api.find_deployment_operation( + pool.pool_id, + "task-1", + project_id="11111111-1111-1111-1111-111111111111", + idempotency_key="update-1", + ) + + assert pool.pool_id == "pool one" + assert release.release_id == "release/one" + assert bound == {"bound": True} + assert rollout.rollout_id == "rollout one" + assert mutation == {"operation_id": "operation-1", "status": "completed"} + assert reconciled == {"operation_id": "operation-1", "state": "completed"} + assert [request.path for request in transport.requests] == [ + "/v1/pools/pool%20one", + "/v1/pools/pool%20one/runtime_image_releases/release%2Fone", + "/v1/pools/pool%20one/runtime_image_releases/release%2Fone/bind", + "/v1/pools/pool%20one/rollouts", + "/v1/pools/pool%20one/deployments/task-1/operations", + "/v1/pools/pool%20one/deployments/task-1/operations", + ] + assert transport.requests[2].timeout_seconds == 45 + assert transport.requests[3].body == {"task_id": "task-1", "seed": 7} + assert transport.requests[4].body == { + "project_id": "11111111-1111-1111-1111-111111111111", + "operation": "update", + "idempotency_key": "update-1", + "payload": {"image": "example:v2"}, + "expected_revision": "revision-1", + } + assert transport.requests[5].query == { + "project_id": "11111111-1111-1111-1111-111111111111", + "idempotency_key": "update-1", + } + + +def _archive_names(encoded: str) -> set[str]: + raw = base64.b64decode(encoded) + with tarfile.open(fileobj=io.BytesIO(raw), mode="r:gz") as archive: + return {member.name for member in archive.getmembers() if member.isfile()} + + +def test_docker_context_packaging_honors_ignore_rules(tmp_path: Path) -> None: + (tmp_path / "Dockerfile").write_text("FROM scratch\nCOPY app.txt /app.txt\n") + (tmp_path / ".dockerignore").write_text("ignored.txt\n") + (tmp_path / "app.txt").write_text("payload") + (tmp_path / "ignored.txt").write_text("do not upload") + (tmp_path / ".git").mkdir() + (tmp_path / ".git" / "config").write_text("do not upload") + + packaged = build_docker_context_archive(tmp_path) + + assert _archive_names(packaged.archive_base64) == { + ".dockerignore", + "Dockerfile", + "app.txt", + } + assert packaged.ignored_by_dockerignore == 2 + + +def test_docker_context_packaging_rejects_credentials(tmp_path: Path) -> None: + (tmp_path / "Dockerfile").write_text("FROM scratch\n") + (tmp_path / ".env").write_text("SECRET=value") + + with pytest.raises(HarborBundleError, match="looks like a credential"): + build_docker_context_archive(tmp_path) diff --git a/tests/test_intern_blocker_controls.py b/tests/test_intern_blocker_controls.py new file mode 100644 index 00000000..15423338 --- /dev/null +++ b/tests/test_intern_blocker_controls.py @@ -0,0 +1,110 @@ +from unittest.mock import AsyncMock, Mock + +import pytest + +from synth_ai.sdk.research.intern_blockers import ( + InternBlockersAPI, + AsyncInternBlockersAPI, + _request_for, +) +from synth_ai.sdk.research.contracts.intern_blockers import ( + InternBlockerOpenSyncRequest, + InternBlockerResolveRequest, +) + + +def blocker(): + return dict(blocker_id="b", async_assignment_id="async", code="approval", message="review required", retryable=False) + + +def open_response(): + now = "2026-09-12T00:00:00Z" + return dict( + blocker={**blocker(), "sync_session_id": "sync"}, + sync_session=dict(sync_session_id="sync", research_intern_id="intern", org_id="org", + objective="review", status="ready", state_generation=0, + last_event_sequence=0, binding={}, temporal_workflow_id="wf", + execution_mode="fast", created_at=now, updated_at=now), + handoff_receipt=dict(handoff_receipt_id="h", blocker_id="b", org_id="org", + sync_session_id="sync", opened_by_user_id="user", idempotency_key="k", + context_digest="a" * 64, created_at=now, + context=dict(blocker_id="b", async_assignment_id="async", binding={}, + action_schema_version="v1", action_kind="test", action_digest="b" * 64, + summary="review", rationale="approval", preauthorization_rule="operator", + required_operator_capability="approve")), + ) + + +def test_open_handoff_checks_cross_receipt_identity(): + transport = Mock() + transport.execute.return_value = open_response() + api = InternBlockersAPI(transport) + request = InternBlockerOpenSyncRequest(idempotency_key="k") + assert api.open_sync("b", request).sync_session.sync_session_id == "sync" + transport.execute.return_value["handoff_receipt"]["sync_session_id"] = "other" + with pytest.raises(ValueError, match="identity drifted"): + api.open_sync("b", request) + + +@pytest.mark.asyncio +async def test_async_controls_parse_handoff_and_continuation(): + transport = Mock(execute=AsyncMock(return_value=open_response())) + api = AsyncInternBlockersAPI(transport) + assert (await api.open_sync("b", InternBlockerOpenSyncRequest(idempotency_key="k"))).blocker.blocker_id == "b" + transport.execute.return_value = dict(blocker=blocker(), continuation_command=dict( + schema_version="smr.intern-runtime-command-receipt.v1", command_id="command", + runtime_kind="async", runtime_id="async", status="received", previous_generation=0, + state_generation=1, decision_code="accepted", created_at="2026-09-12T00:00:00Z", + )) + transport.execute.return_value["blocker"]["resolution_receipt"] = dict( + idempotency_key="resolve", outcome="denied", comment=None, + supporting_receipt_ids=[], continuation_command_id="command", sync_session_id="sync", + ) + transport.execute.return_value["blocker"]["sync_session_id"] = "sync" + result = await api.resolve("b", InternBlockerResolveRequest(idempotency_key="resolve", outcome="denied")) + assert result.continuation_command.command_id == "command" + assert result.continuation_command.status == "received" + transport.execute.return_value["continuation_command"]["runtime_id"] = "other-assignment" + with pytest.raises(ValueError, match="identity drifted"): + await api.resolve("b", InternBlockerResolveRequest(idempotency_key="resolve", outcome="denied")) + transport.execute.return_value["continuation_command"]["runtime_id"] = "async" + transport.execute.return_value["blocker"]["resolution_receipt"]["outcome"] = "completed" + with pytest.raises(ValueError, match="identity drifted"): + await api.resolve("b", InternBlockerResolveRequest(idempotency_key="resolve", outcome="denied")) + + +def test_blocker_read_preserves_exact_identity(): + transport = Mock() + transport.execute.return_value = blocker() + assert InternBlockersAPI(transport).get("b").blocker_id == "b" + assert transport.execute.call_args.args[0].path == "/smr/research-intern/async/blockers/b" + transport.execute.return_value["blocker_id"] = "other" + with pytest.raises(ValueError, match="identity drifted"): + InternBlockersAPI(transport).get("b") + + +@pytest.mark.asyncio +async def test_async_blocker_read_uses_async_transport(): + transport = Mock(execute=AsyncMock(return_value=blocker())) + assert (await AsyncInternBlockersAPI(transport).get("b")).blocker_id == "b" + transport.execute.assert_awaited_once() + + +@pytest.mark.parametrize("ids", [("",), ("x", "x"), ("x" * 513,)]) +def test_resolution_rejects_invalid_supporting_receipts(ids): + with pytest.raises(ValueError): + InternBlockerResolveRequest( + idempotency_key="k", outcome="completed", supporting_receipt_ids=ids + ) + + +def test_actions_keep_explicit_idempotency_and_disposition(): + opening = _request_for("b", "open-sync", InternBlockerOpenSyncRequest(idempotency_key="open-1")) + resolution = _request_for( + "b", "resolve", InternBlockerResolveRequest(idempotency_key="resolve-1", outcome="denied") + ) + assert opening.path.endswith("/b/open-sync") + assert resolution.path.endswith("/b/resolve") + assert opening.body == {"idempotency_key": "open-1"} + assert resolution.body["outcome"] == "denied" + assert resolution.body["idempotency_key"] == "resolve-1" diff --git a/tests/test_intern_grant_capabilities.py b/tests/test_intern_grant_capabilities.py new file mode 100644 index 00000000..447be56e --- /dev/null +++ b/tests/test_intern_grant_capabilities.py @@ -0,0 +1,13 @@ +from synth_ai.sdk.research.contracts.intern_grants import InternCapabilityOperation + + +def test_pool_deployment_capabilities_are_publicly_mirrorable() -> None: + assert { + InternCapabilityOperation.POOL_DEPLOYMENT_CREATE.value, + InternCapabilityOperation.POOL_DEPLOYMENT_UPDATE.value, + InternCapabilityOperation.POOL_DEPLOYMENT_DELETE.value, + } == { + "pool.deployment_create", + "pool.deployment_update", + "pool.deployment_delete", + } diff --git a/tests/test_intern_resource_inventory.py b/tests/test_intern_resource_inventory.py new file mode 100644 index 00000000..a97d84f0 --- /dev/null +++ b/tests/test_intern_resource_inventory.py @@ -0,0 +1,195 @@ +"""Public resource reads preserve exact identity and incomplete disposition.""" + +from unittest.mock import AsyncMock, Mock + +import pytest + +from synth_ai.sdk.research.research_intern import ( + ResearchInternSyncRuntimeAPI, + ResearchInternAsyncRuntimeAPI, + AsyncResearchInternSyncRuntimeAPI, + AsyncResearchInternAsyncRuntimeAPI, +) + + +def payload(kind): + return dict( + runtime_kind=kind, + runtime_id="recorded", + observed_at="2026-09-12T00:00:00Z", + coverage="registered-runtime-resources-v1", + coverage_complete=False, + incomplete_reasons=["runtime_host_disposition_unavailable"], + resources=[ + dict( + resource_kind="intern_runtime", + resource_id="recorded", + relation="self", + disposition="unknown", + reason="runtime_host_disposition_unavailable", + ) + ], + ) + + +@pytest.mark.parametrize( + "kind,api,path", + [ + ( + "sync", + ResearchInternSyncRuntimeAPI, + "/smr/research-intern/sync-sessions/recorded/resources", + ), + ( + "async", + ResearchInternAsyncRuntimeAPI, + "/smr/research-intern/async-assignments/recorded/resources", + ), + ], +) +def test_exact_recorded_resource_read(kind, api, path): + transport = Mock() + transport.execute.return_value = payload(kind) + result = api(transport).resources("recorded") + assert transport.execute.call_args.args[0].path == path + assert not result.coverage_complete + assert result.resources[0].disposition == "unknown" + transport.execute.return_value = {**payload(kind), "runtime_id": "replacement"} + with pytest.raises(ValueError, match="identity drifted"): + api(transport).resources("recorded") + + +def complete_payload(kind): + """A covered runtime: complete coverage, still-pending cleanup and epochs.""" + return { + **payload(kind), + "coverage_complete": True, + "incomplete_reasons": [], + "creator_set": "intern-local-docker-creators-v1", + "profile": "local_docker", + "disposition_complete": False, + "resources": [ + dict( + resource_kind="intern_runtime", + resource_id="recorded", + relation="self", + disposition="retained", + reason="runtime_active", + cleanup_owner="research_intern:intern-1", + ), + dict( + resource_kind="container_lease", + resource_id="lease-0", + relation="owned", + disposition="pending", + reason="resource_not_settled", + cleanup_owner=f"intern_runtime:{kind}:recorded:epoch:0", + epoch=0, + ), + ], + "epochs": [ + dict( + epoch=0, + admission="closed", + closure_kind="superseded_by_reopen", + opened_at="2026-09-12T00:00:00Z", + closed_at="2026-09-12T01:00:00Z", + effects=[ + dict( + resource_kind="container_lease", + resource_id="lease-0", + effect="stop_requested", + owner=f"intern_runtime:{kind}:recorded:epoch:0", + reason="runtime_epoch_closed", + ) + ], + ), + dict(epoch=1, admission="open", opened_at="2026-09-12T01:00:00Z"), + ], + } + + +@pytest.mark.parametrize( + "kind,api", + [("sync", ResearchInternSyncRuntimeAPI), ("async", ResearchInternAsyncRuntimeAPI)], +) +def test_complete_coverage_keeps_retained_and_pending_distinct(kind, api): + transport = Mock() + transport.execute.return_value = complete_payload(kind) + result = api(transport).resources("recorded") + assert result.coverage_complete and not result.disposition_complete + assert [item.disposition for item in result.resources] == ["retained", "pending"] + assert result.resources[1].epoch == 0 + assert [epoch.admission for epoch in result.epochs] == ["closed", "open"] + assert result.epochs[0].effects[0].effect == "stop_requested" + # Cleanup can never be complete on incomplete coverage. + transport.execute.return_value = { + **payload(kind), "disposition_complete": True, + } + with pytest.raises(ValueError, match="cleanup without coverage"): + api(transport).resources("recorded") + for bad in ( + {"disposition": "deleted"}, + {"epoch": -1}, + ): + broken = complete_payload(kind) + broken["resources"][1] = {**broken["resources"][1], **bad} + transport.execute.return_value = broken + with pytest.raises(ValueError): + api(transport).resources("recorded") + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "kind,api", + [("sync", AsyncResearchInternSyncRuntimeAPI), ("async", AsyncResearchInternAsyncRuntimeAPI)], +) +async def test_async_transport_decodes_epochs_and_effects(kind, api): + transport = Mock() + transport.execute = AsyncMock(return_value=complete_payload(kind)) + result = await api(transport).resources("recorded") + assert result.creator_set == "intern-local-docker-creators-v1" + assert result.epochs[0].closure_kind == "superseded_by_reopen" + assert result.resources[0].cleanup_owner == "research_intern:intern-1" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "kind,api", + [("sync", AsyncResearchInternSyncRuntimeAPI), ("async", AsyncResearchInternAsyncRuntimeAPI)], +) +async def test_async_transport_uses_same_inventory_contract(kind, api): + transport = Mock() + transport.execute = AsyncMock(return_value=payload(kind)) + result = await api(transport).resources("recorded") + assert result.runtime_id == "recorded" and not result.coverage_complete + transport.execute.return_value = {**payload(kind), "provider_handle": "must not escape"} + with pytest.raises(ValueError): + await api(transport).resources("recorded") + + +# Lockstep with the backend-authored bounded contract. These mirrors live in +# contracts/intern_resources.py, outside the research_intern pairing table. +INVENTORY_MIRRORS = { + "InternResourceInventory": "InternResourceInventory", + "InternResourceDisposition": "InternResourceDisposition", + "InternRuntimeResourceEpoch": "InternRuntimeResourceEpoch", + "InternResourceStopEffect": "InternResourceStopEffect", +} + + +@pytest.mark.parametrize(("mirror_name", "schema_name"), sorted(INVENTORY_MIRRORS.items())) +def test_inventory_mirrors_match_backend_contract(mirror_name, schema_name): + import json + from pathlib import Path + + from synth_ai.sdk.research.contracts import intern_resources + + contract = Path(__file__).resolve().parents[1] / "openapi" / "research-v1.json" + schema = json.loads(contract.read_text(encoding="utf-8"))["components"]["schemas"][ + schema_name + ] + mirror = getattr(intern_resources, mirror_name) + assert set(mirror.model_fields) == set(schema["properties"]) + required = {name for name, field in mirror.model_fields.items() if field.is_required()} + assert not set(schema.get("required", ())) - required diff --git a/tests/test_intern_usage_reads.py b/tests/test_intern_usage_reads.py new file mode 100644 index 00000000..f0035f3e --- /dev/null +++ b/tests/test_intern_usage_reads.py @@ -0,0 +1,48 @@ +"""Exact runtime usage reads preserve pending billing and reject identity drift.""" + +from unittest.mock import AsyncMock, Mock + +import pytest + +from synth_ai.sdk.research.research_intern import ( + ResearchInternAsyncRuntimeAPI, + ResearchInternSyncRuntimeAPI, + AsyncResearchInternAsyncRuntimeAPI, + AsyncResearchInternSyncRuntimeAPI, +) + + +def receipt(kind): + return dict( + origin_runtime_kind=kind, origin_runtime_id="recorded", org_id="org", + spend_cents=0, token_count=0, run_count=0, + billing=dict(billing_state="pending", stalled_row_count=0, + stalled_spend_cents=0, billing_failed_row_count=0, + show_stalled_banner=False), + ) + + +@pytest.mark.parametrize("kind,api", [("sync", ResearchInternSyncRuntimeAPI), ("async", ResearchInternAsyncRuntimeAPI)]) +def test_sync_transport_usage(kind, api): + transport = Mock() + transport.execute.return_value = receipt(kind) + result = api(transport).usage("recorded") + assert result.billing.billing_state.value == "pending" + segment = "sync-sessions" if kind == "sync" else "async-assignments" + assert transport.execute.call_args.args[0].path == f"/smr/research-intern/{segment}/recorded/usage" + transport.execute.return_value["origin_runtime_id"] = "replacement" + with pytest.raises(ValueError, match="identity drifted"): + api(transport).usage("recorded") + + +@pytest.mark.asyncio +@pytest.mark.parametrize("kind,api", [("sync", AsyncResearchInternSyncRuntimeAPI), ("async", AsyncResearchInternAsyncRuntimeAPI)]) +async def test_async_transport_usage(kind, api): + transport = Mock(execute=AsyncMock(return_value=receipt(kind))) + result = await api(transport).usage("recorded") + assert result.billing.billing_state.value == "pending" + segment = "sync-sessions" if kind == "sync" else "async-assignments" + assert transport.execute.call_args.args[0].path == f"/smr/research-intern/{segment}/recorded/usage" + transport.execute.return_value["origin_runtime_kind"] = "async" if kind == "sync" else "sync" + with pytest.raises(ValueError, match="identity drifted"): + await api(transport).usage("recorded") diff --git a/tests/test_research_intern_contract_lockstep.py b/tests/test_research_intern_contract_lockstep.py new file mode 100644 index 00000000..03003a17 --- /dev/null +++ b/tests/test_research_intern_contract_lockstep.py @@ -0,0 +1,163 @@ +"""Hold the SDK's Intern contracts in lockstep with the backend-authored OpenAPI. + +The Intern contracts under ``synth_ai/sdk/research/contracts/`` are hand-written +mirrors of backend pydantic models: nothing generates them, and nothing until now +compared them. That is a silent-drift shape. Because every mirror is +``extra="forbid"``, a field the backend adds does not degrade the SDK -- it makes +``from_wire`` raise on a live response, and only on the code path that populates +the new field. The Async 24/7 cutover hit exactly that with +``host_lease.generation``. + +``openapi/research-v1.json`` is the backend-authored bounded contract, exported by +``backend/scripts/export_research_openapi.py`` and already held byte-identical to +the backend artifact by ``testing/scripts/check_research_openapi_contract.py``. +So it is a trustworthy stand-in for the backend models in a repo that cannot +import them, and these tests close the last link in that chain: + + backend contracts -> exported OpenAPI -> vendored contract -> SDK mirrors + +Scope is deliberately the Intern surface. The pairings below are the only +hand-maintained thing here -- the SDK prefixes its mirrors ``Intern*`` to keep +them distinct from the SMR nouns, so the names cannot be matched automatically. +Every field set and enum vocabulary on both sides is derived. +""" + +from __future__ import annotations + +import json +from enum import EnumMeta +from pathlib import Path +from typing import Any, Mapping + +import pytest +from pydantic import BaseModel +from synth_ai.sdk.research.contracts import research_intern + +CONTRACT_PATH = Path(__file__).resolve().parents[1] / "openapi" / "research-v1.json" + +#: SDK mirror -> backend schema in the bounded Research contract. +MIRRORED_MODELS: Mapping[str, str] = { + "InternAsyncRuntime": "AsyncRuntimeResponse", + "InternAsyncRuntimeHostLease": "AsyncRuntimeHostLease", + "InternAsyncRuntimeSpend": "AsyncRuntimeSpend", + "InternAsyncRuntimeBudget": "AsyncRuntimeBudget", + "InternAsyncEnsureRequest": "AsyncRuntimeEnsureRequest", + "InternAsyncCheckpoint": "AsyncCheckpointResponse", + "InternAsyncBlocker": "AsyncBlockerResponse", + "InternAsyncJudgmentItem": "AsyncJudgmentItemResponse", + "InternAsyncEffortWorkSummary": "AsyncEffortWorkSummary", + "InternAsyncCommandRequest": "InternRuntimeCommandRequest", + "InternResumeCondition": "ResumeConditionResponse", + "InternProducedResourceReference": "ProducedResourceReferenceV1", + "InternSyncSession": "SyncSessionResponse", + "InternRuntimeBinding": "RuntimeBinding", +} + +#: SDK mirror -> backend enum in the bounded Research contract. +MIRRORED_ENUMS: Mapping[str, str] = { + "InternAsyncStatus": "AsyncStatus", + "InternSyncStatus": "SyncStatus", + "InternProducedResourceKind": "ProducedResourceKind", + "InternAsyncExternalExecutionStatus": "ExternalExecutionStatus", + "InternAsyncEvidenceReadiness": "EvidenceReadiness", + "InternAsyncRuntimePhase": "RuntimePhaseWire", + "InternAsyncStopReason": "StopReason", + "InternAsyncResumeKind": "ResumeKind", + "InternAsyncWaitProducer": "WaitProducer", +} + + +def _schemas() -> Mapping[str, Any]: + payload = json.loads(CONTRACT_PATH.read_text(encoding="utf-8")) + return payload["components"]["schemas"] + + +@pytest.fixture(scope="module") +def schemas() -> Mapping[str, Any]: + return _schemas() + + +@pytest.mark.parametrize(("mirror_name", "schema_name"), sorted(MIRRORED_MODELS.items())) +def test_mirrored_model_fields_match_backend_contract( + schemas: Mapping[str, Any], mirror_name: str, schema_name: str +) -> None: + """Every mirrored model carries exactly the backend's field names.""" + + mirror: type[BaseModel] = getattr(research_intern, mirror_name) + schema = schemas[schema_name] + mirror_fields = set(mirror.model_fields) + backend_fields = set(schema["properties"]) + assert mirror_fields == backend_fields, ( + f"{mirror_name} drifted from backend {schema_name}: " + f"backend-only={sorted(backend_fields - mirror_fields)} " + f"(these make from_wire raise on a live response) " + f"sdk-only={sorted(mirror_fields - backend_fields)}" + ) + + +@pytest.mark.parametrize(("mirror_name", "schema_name"), sorted(MIRRORED_MODELS.items())) +def test_mirrored_model_required_fields_match_backend_contract( + schemas: Mapping[str, Any], mirror_name: str, schema_name: str +) -> None: + """A field the backend always sends is required on the mirror too. + + Optional-where-the-backend-is-required is the quieter half of the same drift: + it does not raise, it lets a caller read a default for something that is + never absent and branch on an outcome the backend cannot produce. + + Only that direction is asserted. The mirrors are deliberately *stricter* than + the contract on the constant discriminators (``schema_version``, + ``cardinality``, ``instance_kind``, ``leave_safe``), which the backend + declares with a default and therefore omits from ``required``. Demanding them + on the wire rejects a truncated response instead of defaulting past it. + """ + + mirror: type[BaseModel] = getattr(research_intern, mirror_name) + schema = schemas[schema_name] + mirror_required = {name for name, field in mirror.model_fields.items() if field.is_required()} + backend_required = set(schema.get("required", ())) + assert not backend_required - mirror_required, ( + f"{mirror_name} is looser than backend {schema_name}: " + f"optional here but always sent by the backend=" + f"{sorted(backend_required - mirror_required)}" + ) + + +@pytest.mark.parametrize(("mirror_name", "schema_name"), sorted(MIRRORED_ENUMS.items())) +def test_mirrored_enum_vocabulary_matches_backend_contract( + schemas: Mapping[str, Any], mirror_name: str, schema_name: str +) -> None: + """Every mirrored vocabulary admits exactly the backend's values.""" + + mirror: EnumMeta = getattr(research_intern, mirror_name) + schema = schemas[schema_name] + mirror_values = {member.value for member in mirror} + backend_values = set(schema["enum"]) + assert mirror_values == backend_values, ( + f"{mirror_name} drifted from backend {schema_name}: " + f"backend-only={sorted(backend_values - mirror_values)} " + f"(these make from_wire raise on a live response) " + f"sdk-only={sorted(mirror_values - backend_values)}" + ) + + +def test_every_mirrored_name_resolves() -> None: + """The pairing table itself cannot rot silently. + + A renamed mirror or a schema dropped from the bounded contract would + otherwise skip its own coverage rather than fail. + """ + + schemas = _schemas() + missing_mirrors = sorted( + name for name in (*MIRRORED_MODELS, *MIRRORED_ENUMS) if not hasattr(research_intern, name) + ) + missing_schemas = sorted( + name + for name in (*MIRRORED_MODELS.values(), *MIRRORED_ENUMS.values()) + if name not in schemas + ) + assert not missing_mirrors and not missing_schemas, ( + f"lockstep pairing table is stale: missing SDK mirrors={missing_mirrors} " + f"missing backend schemas={missing_schemas}" + ) diff --git a/tests/test_research_transcript_completion_claim.py b/tests/test_research_transcript_completion_claim.py new file mode 100644 index 00000000..94fdb866 --- /dev/null +++ b/tests/test_research_transcript_completion_claim.py @@ -0,0 +1,71 @@ +"""The SDK decodes task.completion_claimed transcript events.""" + +from __future__ import annotations + +import pytest + +from synth_ai.sdk.research.contracts.transcript import ( + SwarmTranscriptEvent, + TaskCompletionClaimedEvent, +) + + +def _wire(**payload_overrides): + payload = { + "schema_version": "smr.task-completion-claimed.v1", + "claim_id": "a48dd3a6-dd30-405b-92c6-59462f2e5132", + "task_id": "2d73dc80-1f29-50ad-8415-7774c4c15008", + "task_key": "workshop-left-05b8f11599b94aca86344ca3f3a8fcd6", + "participant_id": "workshop-left-05b8f11599b94aca86344ca3f3a8fcd6", + "worker_actor_key": "workshop-left-05b8f11599b94aca86344ca3f3a8fcd6", + "worker_actor_id": "e025cb06-0dc0-5b87-8370-95a17d4e8e64", + "claimed_state": "done", + "claim_kind": "completed_with_evidence", + "status": "accepted_as_claim", + "source_intent_id": "e99ad725-afc6-4b99-9b94-0b3c897bffc0", + "text": "Computed 1564 × 7 = 10948.", + "text_truncated": False, + "text_source": "claim_notes", + "summary": "Worker requested task state done.", + "summary_truncated": False, + "recorded_at": "2026-09-13T15:51:46.926877+00:00", + "turn_id_source": "durable_transcript_turn", + **payload_overrides, + } + return { + "schema_version": "synth.research.transcript-event.v1", + "event_id": "4c5b3a3e-7f7a-5c61-9d0c-1f2a3b4c5d6e", + "live_cursor": None, + "run_id": "50b06173-f15f-42c0-95d2-1fd5cecb7af9", + "participant_session_id": "run-local:worker:50b06173:2d73dc80", + "participant_role": "worker", + "thread_id": "thread-1", + "turn_id": "turn-1", + "occurred_at": "2026-09-13T15:51:46.926877+00:00", + "kind": "task.completion_claimed", + "payload": payload, + "semantic_scope": "participant", + "run_terminal": True, + "redaction_profile": "public_default", + "visibility_decision": "stream_redacted", + "payload_classification": "public_safe", + } + + +def test_completion_claim_event_decodes_attributed_contribution() -> None: + event = SwarmTranscriptEvent.from_wire(_wire()) + claim = TaskCompletionClaimedEvent.from_transcript_event(event) + assert claim.participant_id == "workshop-left-05b8f11599b94aca86344ca3f3a8fcd6" + assert claim.text == "Computed 1564 × 7 = 10948." + assert claim.turn_id == "turn-1" + assert claim.event_id == "4c5b3a3e-7f7a-5c61-9d0c-1f2a3b4c5d6e" + + +def test_completion_claim_rejects_other_kinds_and_schemas() -> None: + other = dict(_wire(), kind="message.completed") + with pytest.raises(ValueError, match="not a task.completion_claimed"): + TaskCompletionClaimedEvent.from_transcript_event(SwarmTranscriptEvent.from_wire(other)) + with pytest.raises(ValueError, match="schema_version"): + TaskCompletionClaimedEvent.from_transcript_event( + SwarmTranscriptEvent.from_wire(_wire(schema_version="v0")) + ) diff --git a/tests/test_research_transcript_durable_replay.py b/tests/test_research_transcript_durable_replay.py new file mode 100644 index 00000000..05f72c3a --- /dev/null +++ b/tests/test_research_transcript_durable_replay.py @@ -0,0 +1,44 @@ +"""The SDK decodes durable transcript replay pages served after terminality. + +Backend lockstep: services/smr/api_schemas.py TranscriptReplayMode / +TranscriptProjectionAuthority gained ``durable_replay`` so a live run whose +Redis stream was trimmed (or a caller resuming with a durable cursor) replays +durable rows with an explicit mode instead of an empty live tail. +""" + +from __future__ import annotations + +import pytest + +from synth_ai.sdk.research.contracts.transcript import ( + TranscriptFreshness, + TranscriptProjectionAuthority, + TranscriptReplayMode, +) + + +def test_durable_replay_freshness_round_trips() -> None: + wire = { + "observed_at": "2026-09-12T21:45:00+00:00", + "projection_authority": "smr_transcript_events.durable_replay.v1", + "replay_mode": "durable_replay", + "live_tail_available": False, + } + freshness = TranscriptFreshness.from_wire(wire) + assert freshness.replay_mode is TranscriptReplayMode.DURABLE_REPLAY + assert ( + freshness.projection_authority is TranscriptProjectionAuthority.DURABLE_REPLAY + ) + assert freshness.to_wire() == wire + + +def test_durable_replay_cannot_advertise_live_tail() -> None: + with pytest.raises(ValueError, match="cannot advertise a live tail"): + TranscriptFreshness.from_wire( + { + "observed_at": "2026-09-12T21:45:00+00:00", + "projection_authority": "smr_transcript_events.durable_replay.v1", + "replay_mode": "durable_replay", + "live_tail_available": True, + } + ) diff --git a/tests/test_run_origin_fields.py b/tests/test_run_origin_fields.py new file mode 100644 index 00000000..b9b66a5c --- /dev/null +++ b/tests/test_run_origin_fields.py @@ -0,0 +1,29 @@ +"""Runs carry the Intern runtime that launched them.""" + +from __future__ import annotations + +from synth_ai.sdk.research.contracts.run_state import ManagedResearchRun + + +def test_run_parses_launch_origin() -> None: + run = ManagedResearchRun.from_wire( + { + "run_id": "run-1", + "project_id": "proj-1", + "public_state": "running", + "origin_runtime_kind": "sync", + "origin_runtime_id": "sync-session-1", + } + ) + + assert run.origin_runtime_kind == "sync" + assert run.origin_runtime_id == "sync-session-1" + + +def test_run_without_intern_origin_reads_none() -> None: + run = ManagedResearchRun.from_wire( + {"run_id": "run-1", "project_id": "proj-1", "public_state": "running"} + ) + + assert run.origin_runtime_kind is None + assert run.origin_runtime_id is None diff --git a/tests/test_settlement_and_intern_usage_contracts.py b/tests/test_settlement_and_intern_usage_contracts.py new file mode 100644 index 00000000..e72dc7f5 --- /dev/null +++ b/tests/test_settlement_and_intern_usage_contracts.py @@ -0,0 +1,94 @@ +"""Typed reads for run resource settlement and Intern session usage.""" + +from __future__ import annotations + +import pytest + +from synth_ai.sdk.research.contracts.intern_usage import InternBillingState, InternSessionUsage +from synth_ai.sdk.research.contracts.resource_settlement import ( + RunResourceSettlement, + SettlementCoverage, + SettlementScope, +) + +_SETTLEMENT = { + "run_id": "run-1", + "observed_at": "2026-09-11T20:00:00+00:00", + "coverage": "explicit-v1", + "settled": True, + "registered_tree_settled": True, + "coverage_complete": False, + "scope_kind": "root_tree", + "root_run_id": "run-1", + "edge_id": None, + "pending": 0, + "unknown": 1, + "confirmed": 3, + "excluded": 0, + "root_confirmed": True, +} + + +def test_settlement_parses_every_backend_field() -> None: + settlement = RunResourceSettlement.from_wire(_SETTLEMENT) + + assert settlement.coverage is SettlementCoverage.EXPLICIT_V1 + assert settlement.scope_kind is SettlementScope.ROOT_TREE + assert settlement.settled and settlement.registered_tree_settled + assert settlement.coverage_complete is False + assert (settlement.pending, settlement.unknown, settlement.confirmed) == (0, 1, 3) + + +def test_settlement_rejects_contract_drift() -> None: + with pytest.raises(ValueError, match="unknown fields"): + RunResourceSettlement.from_wire({**_SETTLEMENT, "new_field": 1}) + with pytest.raises(ValueError): + RunResourceSettlement.from_wire({**_SETTLEMENT, "coverage": "complete"}) + with pytest.raises(ValueError): + RunResourceSettlement.from_wire({**_SETTLEMENT, "settled": "yes"}) + + +def test_untracked_run_makes_no_settlement_claim() -> None: + settlement = RunResourceSettlement.from_wire( + { + "run_id": "run-2", + "observed_at": "2026-09-11T20:00:00+00:00", + "coverage": "untracked", + "settled": False, + } + ) + + assert settlement.coverage is SettlementCoverage.UNTRACKED + assert settlement.pending is None and settlement.scope_kind is None + + +def test_intern_usage_parses_receipt_and_launched_runs() -> None: + usage = InternSessionUsage.from_wire( + { + "origin_runtime_kind": "sync", + "origin_runtime_id": "sync-1", + "org_id": "org-1", + "spend_cents": 42, + "token_count": 0, + "run_count": 2, + "billing": { + "billing_state": "pending", + "stalled_row_count": 0, + "stalled_spend_cents": 0, + "billing_failed_row_count": 0, + "message": None, + "show_stalled_banner": False, + }, + "bindings": [], + "metadata": {"run_ids": ["run-a", "run-b"], "direct_inference": {}}, + } + ) + + assert usage.billing.billing_state is InternBillingState.PENDING + assert usage.run_ids == ("run-a", "run-b") + assert usage.spend_cents == 42 + + +def test_intern_usage_rejects_unknown_origin_kind() -> None: + with pytest.raises(ValueError, match="origin_runtime_kind"): + InternSessionUsage.from_wire({"origin_runtime_kind": "batch"}) diff --git a/tests/test_swarm_evidence_tool_calls.py b/tests/test_swarm_evidence_tool_calls.py new file mode 100644 index 00000000..a77cffaf --- /dev/null +++ b/tests/test_swarm_evidence_tool_calls.py @@ -0,0 +1,88 @@ +"""Swarm evidence carries the backend's durable tool-call outcomes. + +Found live on slot3 (rc1): retrieve_swarm_evidence failed with +"fields drifted: extra=['tool_calls']" because the backend added +SmrSwarmEvidenceResponse.tool_calls and the SDK's exact-field parser did not +know it. +""" + +from __future__ import annotations + +import pytest + +from synth_ai.sdk.research.contracts.evidence import SwarmEvidence, SwarmToolCallStatus + +_CALL = { + "tool_call_id": "call-1", + "stable_actor_key": None, + "actor_role": "orchestrator", + "correlation_id": None, + "bundle_name": "pool_control", + "tool_name": "create_pool_rollout", + "arguments_digest": "a" * 64, + "status": "succeeded", + "error_code": None, + "retryable": False, + "duration_ms": 120, + "occurred_at": "2026-09-12T01:00:00+00:00", +} + + +def _evidence(**overrides): + payload = { + "schema_version": 1, + "run_id": "run-1", + "project_id": "proj-1", + "artifacts": [], + "work_products": [], + "selected_artifact_contents": [], + "trace_publications": [], + "tool_calls": [_CALL], + # The backend's freshness block also counts the tool calls (found live on + # slot3, rc1 E01 attempt 8: "freshness fields drifted: extra=['tool_call_count']"). + "freshness": { + "artifact_count": 0, + "work_product_count": 0, + "tool_call_count": 1, + "generated_at": "2026-09-12T01:00:00+00:00", + "run_is_terminal": True, + }, + } + payload.update(overrides) + return payload + + +def test_tool_calls_parse_into_typed_outcomes() -> None: + evidence = SwarmEvidence.from_wire(_evidence()) + + (call,) = evidence.tool_calls + assert call.tool_name == "create_pool_rollout" + assert call.status is SwarmToolCallStatus.SUCCEEDED + assert evidence.to_wire()["tool_calls"][0]["arguments_digest"] == "a" * 64 + + +def test_freshness_carries_the_backend_tool_call_count() -> None: + evidence = SwarmEvidence.from_wire(_evidence()) + + assert evidence.freshness.tool_call_count == 1 + assert evidence.to_wire()["freshness"]["tool_call_count"] == 1 + + +def test_freshness_without_tool_call_count_still_parses() -> None: + legacy = _evidence() + legacy["freshness"] = {k: v for k, v in legacy["freshness"].items() if k != "tool_call_count"} + + assert SwarmEvidence.from_wire(legacy).freshness.tool_call_count == 0 + + +def test_freshness_drift_is_still_refused() -> None: + drifted = _evidence() + drifted["freshness"] = {**drifted["freshness"], "unexpected": 1} + + with pytest.raises(ValueError, match="freshness fields drifted"): + SwarmEvidence.from_wire(drifted) + + +def test_tool_call_drift_is_still_refused() -> None: + with pytest.raises(ValueError, match="drifted"): + SwarmEvidence.from_wire(_evidence(tool_calls=[{**_CALL, "arguments": {"secret": 1}}])) diff --git a/tests/test_swarm_origin_settlement_facade.py b/tests/test_swarm_origin_settlement_facade.py new file mode 100644 index 00000000..d97a7514 --- /dev/null +++ b/tests/test_swarm_origin_settlement_facade.py @@ -0,0 +1,162 @@ +"""Public facade for the E01 journey: find an Intern's swarms, read settlement and usage.""" + +from __future__ import annotations + +from typing import Any + +import pytest +from synth_ai.core.http.request import HttpRequest +from synth_ai.sdk.research.contracts.common import ProjectId, SwarmId +from synth_ai.sdk.research.operations import research_operation +from synth_ai.sdk.research.research_intern import ResearchInternAPI +from synth_ai.sdk.research.swarms import SwarmsAPI + +NOW = "2026-09-11T12:00:00Z" +SWARM = { + "run_id": "run-1", + "project_id": "proj-1", + "org_id": "org-1", + "public_state": "running", + "runbook": "lite", + "trigger": "intern", + "created_at": NOW, + "updated_at": NOW, + "origin_runtime_kind": "sync", + "origin_runtime_id": "sync-1", +} +SETTLEMENT = { + "run_id": "run-1", + "observed_at": NOW, + "coverage": "explicit-v1", + "settled": False, + "pending": 2, +} +USAGE = { + "origin_runtime_kind": "sync", + "origin_runtime_id": "sync-1", + "org_id": "org-1", + "spend_cents": 0, + "token_count": 0, + "run_count": 1, + "billing": { + "billing_state": "pending", + "stalled_row_count": 0, + "stalled_spend_cents": 0, + "billing_failed_row_count": 0, + "message": None, + "show_stalled_banner": False, + }, + "bindings": [], + "metadata": {"run_ids": ["run-1"]}, +} + + +class _Transport: + def __init__(self, responses: dict[str, Any]) -> None: + self.responses = responses + self.requests: list[HttpRequest] = [] + + def execute(self, request: HttpRequest) -> Any: + self.requests.append(request) + return self.responses[str(request.operation.operation_id)] + + +@pytest.mark.parametrize( + ("operation_id", "path"), + [ + ("get_run_resource_settlement", "/smr/runs/{run_id}/resource-settlement"), + ( + "get_intern_sync_session_usage", + "/smr/research-intern/sync-sessions/{sync_session_id}/usage", + ), + ( + "get_intern_async_assignment_usage", + "/smr/research-intern/async-assignments/{assignment_id}/usage", + ), + ], +) +def test_new_operations_are_registered(operation_id: str, path: str) -> None: + assert research_operation(operation_id).path_template == path + + +def test_list_swarms_by_intern_origin() -> None: + transport = _Transport({"list_project_runs": [SWARM]}) + page = SwarmsAPI(transport).list( # type: ignore[arg-type] + ProjectId("proj-1"), origin_runtime_kind="sync", origin_runtime_id="sync-1" + ) + + assert [swarm.origin_runtime_id for swarm in page.items] == ["sync-1"] + assert transport.requests[0].query == { + "limit": 100, + "origin_runtime_kind": "sync", + "origin_runtime_id": "sync-1", + } + + +def test_half_origin_filter_fails_before_any_request() -> None: + transport = _Transport({}) + with pytest.raises(ValueError, match="set together"): + SwarmsAPI(transport).list(ProjectId("proj-1"), origin_runtime_kind="sync") # type: ignore[arg-type] + assert transport.requests == [] + + +def test_swarm_resource_settlement() -> None: + transport = _Transport({"get_run_resource_settlement": SETTLEMENT}) + settlement = SwarmsAPI(transport).resource_settlement(SwarmId("run-1")) # type: ignore[arg-type] + + assert settlement.settled is False and settlement.pending == 2 + assert transport.requests[0].path == "/smr/runs/run-1/resource-settlement" + + +def test_swarm_rollouts_are_typed_and_parent_checked() -> None: + rollout = { + "rollout_id": "rollout-1", + "pool_id": "pool-1", + "adapter": "harbor", + "status": "completed", + "seed": 3, + "trace_correlation_id": "trace-1", + "budget_parent_run_id": "run-1", + "success": True, + "score": 1, + "created_at": NOW.replace("Z", "+00:00"), + } + transport = _Transport({"list_swarm_rollouts": {"run_id": "run-1", "items": [rollout], "limit": 100}}) + rollouts = SwarmsAPI(transport).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + + assert [(r.rollout_id, r.score) for r in rollouts] == [("rollout-1", 1.0)] + assert rollouts[0].release_coordinates is None + assert transport.requests[0].path == "/smr/runs/run-1/rollouts" + + coordinates = { + "runtime_image_release_id": "image-release-1", + "resolved_image_digest": "sha256:" + "a" * 64, + "shared_bundle_release_id": None, + "task_bundle_release_id": "task-release-1", + } + rollout["release_coordinates"] = coordinates + pinned = SwarmsAPI(transport).rollouts(SwarmId("run-1"))[0].release_coordinates + assert pinned is not None + assert pinned.runtime_image_release_id == "image-release-1" + assert pinned.resolved_image_digest == coordinates["resolved_image_digest"] + assert pinned.task_bundle_release_id == "task-release-1" + for invalid in ([], "forged", {"runtime_image_release_id": {"secret": "value"}}): + rollout["release_coordinates"] = invalid + with pytest.raises(ValueError): + SwarmsAPI(transport).rollouts(SwarmId("run-1")) + rollout["release_coordinates"] = coordinates + + drifted = _Transport( + {"list_swarm_rollouts": {"run_id": "run-1", "items": [{**rollout, "budget_parent_run_id": "run-9"}], "limit": 100}} + ) + with pytest.raises(ValueError, match="budget parent drifted"): + SwarmsAPI(drifted).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + + +def test_sync_session_usage_checks_identity() -> None: + sync = ResearchInternAPI(_Transport({"get_intern_sync_session_usage": USAGE})).sync_ # type: ignore[arg-type] + assert sync.usage("sync-1").run_ids == ("run-1",) + + other = ResearchInternAPI(_Transport({"get_intern_sync_session_usage": USAGE})).sync_ # type: ignore[arg-type] + with pytest.raises(ValueError, match="identity drifted"): + other.usage("sync-2") diff --git a/tests/test_swarm_rollout_provenance.py b/tests/test_swarm_rollout_provenance.py new file mode 100644 index 00000000..50593488 --- /dev/null +++ b/tests/test_swarm_rollout_provenance.py @@ -0,0 +1,186 @@ +"""Swarm rollout reads decode run → pool/task → intent → launch/lease → image → artifact. + +Both transports, legacy unknowns, the explicit ``lease: null`` disposition and +field lockstep with the vendored backend OpenAPI contract. +""" + +from __future__ import annotations + +import asyncio +import copy +import dataclasses +import json +from pathlib import Path +from typing import Any + +import pytest +from synth_ai.core.http.request import HttpRequest +from synth_ai.sdk.research.contracts import swarm_rollouts as contracts +from synth_ai.sdk.research.contracts.common import SwarmId +from synth_ai.sdk.research.swarms import AsyncSwarmsAPI, SwarmsAPI + +CONTRACT = json.loads( + (Path(__file__).resolve().parents[1] / "openapi" / "research-v1.json").read_text() +) +DIGEST = "sha256:" + "a" * 64 +ROLLOUT: dict[str, Any] = { + "rollout_id": "rollout-1", + "pool_id": "pool-1", + "task_id": "task-1", + "adapter": "harbor", + "status": "completed", + "seed": 3, + "trace_correlation_id": "trace-1", + "budget_parent_run_id": "run-1", + "release_coordinates": None, + "logical_intent": { + "intent_ref": "rintent_1", + "source": "derived", + "intent_id": None, + "scope_kind": "smr_run", + "accepted_submissions": 2, + }, + "execution": { + "recorded_by": "rhodes_worker", + "interface": "harbor", + "interface_mode": "harbor_sandbox", + "provider": "docker", + "launch_id": "launch_1", + "lease": None, + "lease_disposition": { + "status": "not_applicable", + "reason": "harbor_rollout_launches_ephemeral_sandboxes", + }, + "deployment_binding": "ephemeral_per_rollout", + "executed_image_digest": DIGEST, + "image_attestation": "provider_image_id", + "executed_source_digest": None, + "launches": [ + { + "sequence": 0, + "role": "agent", + "provider": "docker", + "launch_id": "launch_1", + "executed_image_digest": DIGEST, + "image_attestation": "provider_image_id", + "recorded_at": "2026-09-12T12:00:00+00:00", + } + ], + "recorded_at": "2026-09-12T12:00:00+00:00", + }, + "artifacts": [ + { + "artifact_id": "artifact-1", + "artifact_type": "result", + "content_type": "application/json", + "size_bytes": 12, + "created_at": "2026-09-12T12:01:00Z", + } + ], + "success": True, + "score": 1, + "error": None, + "created_at": "2026-09-12T11:59:00Z", + "started_at": None, + "completed_at": None, + "cancelled_at": None, +} + + +def _page(*items: dict[str, Any]) -> dict[str, Any]: + return {"run_id": "run-1", "items": list(items), "limit": 100} + + +class _Transport: + def __init__(self, page: dict[str, Any]) -> None: + self.page = page + self.requests: list[HttpRequest] = [] + + def execute(self, request: HttpRequest) -> Any: + self.requests.append(request) + return self.page + + +class _AsyncTransport(_Transport): + async def execute(self, request: HttpRequest) -> Any: # type: ignore[override] + return super().execute(request) + + +def _assert_chain(rollout: contracts.SwarmRollout) -> None: + assert (rollout.budget_parent_run_id, rollout.pool_id, rollout.task_id) == ("run-1", "pool-1", "task-1") + assert rollout.logical_intent is not None + assert (rollout.logical_intent.source, rollout.logical_intent.accepted_submissions) == ("derived", 2) + execution = rollout.execution + assert execution is not None and execution.lease is None + assert execution.lease_disposition.status == "not_applicable" + assert execution.executed_image_digest == DIGEST + assert execution.launches[0].role == "agent" + assert [artifact.artifact_id for artifact in rollout.artifacts] == ["artifact-1"] + + +def test_sync_transport_decodes_the_provenance_chain() -> None: + transport = _Transport(_page(ROLLOUT)) + (rollout,) = SwarmsAPI(transport).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + _assert_chain(rollout) + assert transport.requests[0].path == "/smr/runs/run-1/rollouts" + + +def test_async_transport_decodes_the_same_chain() -> None: + transport = _AsyncTransport(_page(ROLLOUT)) + api = AsyncSwarmsAPI(transport) # type: ignore[arg-type] + (rollout,) = asyncio.run(api.rollouts(SwarmId("run-1"), limit=5)) + _assert_chain(rollout) + assert transport.requests[0].path == "/smr/runs/run-1/rollouts" + assert transport.requests[0].query == {"limit": 5} + + +def test_legacy_rows_decode_as_unknown_not_as_absent_execution() -> None: + legacy = {k: v for k, v in ROLLOUT.items() if k not in {"logical_intent", "execution", "artifacts"}} + (rollout,) = SwarmsAPI(_Transport(_page(legacy))).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + assert rollout.logical_intent is None and rollout.execution is None and rollout.artifacts == () + + +@pytest.mark.parametrize( + "mutate", + [ + lambda r: r["execution"].pop("lease"), # a lease must be stated, even as null + lambda r: r["execution"]["lease_disposition"].update(status="recorded"), + lambda r: r["execution"].update(lease={"lease_id": "nearby-lease"}), + lambda r: r["execution"].update(executed_image_digest="latest"), + lambda r: r["logical_intent"].update(accepted_submissions=0), + lambda r: r["logical_intent"].update(intent_id="derived-cannot-name-one"), + lambda r: r["logical_intent"].update(source="caller"), + lambda r: r.update(artifacts={"artifact_id": "a"}), + ], +) +def test_malformed_provenance_is_refused(mutate) -> None: + rollout = copy.deepcopy(ROLLOUT) + mutate(rollout) + with pytest.raises(ValueError): + SwarmsAPI(_Transport(_page(rollout))).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + + +def test_recorded_lease_decodes_when_the_interface_uses_one() -> None: + rollout = copy.deepcopy(ROLLOUT) + rollout["execution"]["lease"] = {"lease_id": "lease-1"} + rollout["execution"]["lease_disposition"] = {"status": "recorded", "reason": "interactive_lease"} + (decoded,) = SwarmsAPI(_Transport(_page(rollout))).rollouts(SwarmId("run-1")) # type: ignore[arg-type] + assert decoded.execution is not None and decoded.execution.lease == contracts.SwarmRolloutLease("lease-1") + + +@pytest.mark.parametrize( + ("mirror", "schema"), + [ + (contracts.SwarmRollout, "SwarmRolloutSummary"), + (contracts.SwarmRolloutLogicalIntent, "SwarmRolloutLogicalIntent"), + (contracts.SwarmRolloutExecution, "SwarmRolloutExecution"), + (contracts.SwarmRolloutExecutionLaunch, "SwarmRolloutExecutionLaunch"), + (contracts.SwarmRolloutLease, "SwarmRolloutLease"), + (contracts.SwarmRolloutLeaseDisposition, "SwarmRolloutLeaseDisposition"), + (contracts.SwarmRolloutArtifact, "SwarmRolloutArtifact"), + (contracts.SwarmRolloutReleaseCoordinates, "SwarmRolloutReleaseCoordinates"), + ], +) +def test_mirrors_hold_lockstep_with_the_backend_contract(mirror, schema) -> None: + properties = CONTRACT["components"]["schemas"][schema]["properties"] + assert {field.name for field in dataclasses.fields(mirror)} == set(properties) diff --git a/tests/test_swarm_usage_unattributed_sessions.py b/tests/test_swarm_usage_unattributed_sessions.py new file mode 100644 index 00000000..dae97271 --- /dev/null +++ b/tests/test_swarm_usage_unattributed_sessions.py @@ -0,0 +1,87 @@ +"""Swarm usage carries the backend's unattributed_sessions token count. + +Since 2026-07-26 the backend's SmrSwarmTokenUsageResponse includes +tokens.unattributed_sessions, and the SDK's exact-field token-usage parser +refused it ("swarm token usage fields drifted: extra=['unattributed_sessions']"), +so research.swarms.usage() raised for every swarm. +""" + +from __future__ import annotations + +import pytest +from synth_ai.sdk.research.contracts.usage import SwarmUsage + +_COUNTS = { + "input_tokens": 100, + "cached_input_tokens": 40, + "non_cached_input_tokens": 60, + "output_tokens": 30, + "reasoning_output_tokens": 10, + "non_reasoning_output_tokens": 20, + "snapshots": 2, +} + + +def _usage(**token_overrides): + tokens = { + "sessions_seen": 2, + "session_snapshots_count": 2, + "unattributed_sessions": 1, + "totals": dict(_COUNTS), + "by_model": {"unattributed": dict(_COUNTS)}, + } + tokens.update(token_overrides) + return { + "schema_version": 1, + "run_id": "run-1", + "project_id": "proj-1", + "money": { + "nominal_cents": 7, + "billed_cents": 9, + "internal_cost_cents": 5, + "nominal_pico_usd": 70_000_000_000, + "billed_pico_usd": 90_000_000_000, + "internal_cost_pico_usd": 50_000_000_000, + }, + "tokens": tokens, + "actors": [], + "freshness": { + "source": "usage_facts", + "as_of": "2026-09-12T01:00:00+00:00", + "record_count": 2, + "run_is_terminal": True, + }, + } + + +def test_usage_with_unattributed_sessions_parses() -> None: + usage = SwarmUsage.from_wire(_usage()) + + assert usage.tokens.unattributed_sessions == 1 + assert usage.tokens.sessions_seen == 2 + + +def test_usage_round_trips_through_to_wire() -> None: + wire = _usage() + + assert SwarmUsage.from_wire(wire).to_wire() == wire + assert SwarmUsage.from_wire(SwarmUsage.from_wire(wire).to_wire()) == SwarmUsage.from_wire(wire) + + +def test_usage_without_unattributed_sessions_still_parses() -> None: + legacy = _usage() + del legacy["tokens"]["unattributed_sessions"] + + assert SwarmUsage.from_wire(legacy).tokens.unattributed_sessions == 0 + + +def test_token_usage_drift_is_still_refused() -> None: + with pytest.raises(ValueError, match="swarm token usage fields drifted"): + SwarmUsage.from_wire(_usage(unexpected=1)) + + +def test_unattributed_sessions_is_typed() -> None: + with pytest.raises(ValueError, match="non-negative integer"): + SwarmUsage.from_wire(_usage(unattributed_sessions=None)) + with pytest.raises(ValueError, match="cannot exceed sessions_seen"): + SwarmUsage.from_wire(_usage(unattributed_sessions=3)) diff --git a/uv.lock b/uv.lock index af528975..4a51869a 100644 --- a/uv.lock +++ b/uv.lock @@ -137,6 +137,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -518,6 +527,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + [[package]] name = "fastjsonschema" version = "2.21.2" @@ -718,11 +743,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, ] [[package]] @@ -2040,6 +2065,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "starlette" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" }, +] + [[package]] name = "synth-ai" version = "0.19.0" @@ -2061,6 +2099,9 @@ dev = [ { name = "ruff" }, { name = "twine" }, ] +pools = [ + { name = "synth-containers" }, +] [package.dev-dependencies] dev = [ @@ -2094,9 +2135,10 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.0.0" }, { name = "pynacl", specifier = ">=1.5.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "synth-containers", marker = "extra == 'pools'", specifier = ">=0.4.3.dev2026091126,<0.5" }, { name = "twine", marker = "extra == 'dev'", specifier = ">=4.0.0" }, ] -provides-extras = ["dev", "research"] +provides-extras = ["pools", "dev", "research"] [package.metadata.requires-dev] dev = [ @@ -2118,6 +2160,26 @@ dev = [ { name = "ty", specifier = ">=0.0.1a5" }, ] +[[package]] +name = "synth-containers" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "idna" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "uvicorn" }, + { name = "websockets" }, + { name = "zstandard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/7f/d0682349835335198d05bdee717203ca0037481077d223f6453e4163a6e7/synth_containers-0.4.3.tar.gz", hash = "sha256:d0bab327439a93fc90c77f19c464a14b9f9cefb589ae76ba56bd66b07ea4efa7", size = 1084857, upload-time = "2026-09-10T00:29:47.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/0d/48e34c3340946a40301a9d2532700bdbf130034e2959a1402e40e9a39ef2/synth_containers-0.4.3-py3-none-any.whl", hash = "sha256:eaff16ec40b6e2c9a569751f415912178f3aa0ac63396749466ec92e1d734bf5", size = 949586, upload-time = "2026-09-10T00:29:45.202Z" }, +] + [[package]] name = "tenacity" version = "9.1.2" @@ -2241,6 +2303,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "uvicorn" +version = "0.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/ad/04bbb797c84fc1f26cb171f7394716f4865ffb8d8c5e1eef42565c2dfa6b/uvicorn-0.53.0.tar.gz", hash = "sha256:a9356f0cb89b3b8621529c5d5eebd69bfe154f4c3f68b4cf2de47e45fa855c2e", size = 110881, upload-time = "2026-09-14T07:44:23.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/18/0eea75741ee812e9f598b687619ce2454f6c3a1c5cd21ea990ec6bd26f45/uvicorn-0.53.0-py3-none-any.whl", hash = "sha256:e8dca71ec86dce5f04e333f0d56cdedf942446e6643b9cea1af0d6d3a02cb03e", size = 87081, upload-time = "2026-09-14T07:44:22.179Z" }, +] + [[package]] name = "wcwidth" version = "0.3.0" @@ -2250,6 +2325,161 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/0e/a5f0257ab47492b7afb5fb60347d14ba19445e2773fc8352d4be6bd2f6f8/wcwidth-0.3.0-py3-none-any.whl", hash = "sha256:073a1acb250e4add96cfd5ef84e0036605cd6e0d0782c8c15c80e42202348458", size = 85520, upload-time = "2026-01-21T17:44:08.002Z" }, ] +[[package]] +name = "websockets" +version = "17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/72/fba934cb3dff7a85d811820efffcd141ddd52b5a2a01637f64551373ff4d/websockets-17.1.tar.gz", hash = "sha256:acfea4c20bf54384883ea33b1240fc1db4f52e190823a4e2b334bc3e8bfca96a", size = 187520, upload-time = "2026-08-26T17:25:33.063Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ad/66a74d42fb537bd44056483eae6cbb7ebb10b742c300a0bf8cee427556d4/websockets-17.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:88b882764ef65147a7a5ae13168dedbe225a04e2ff4858fe543f2c402f093e9c", size = 216984, upload-time = "2026-08-26T14:55:20.747Z" }, + { url = "https://files.pythonhosted.org/packages/70/1b/344ab22cea729e872f759b926441f7b822ab6cd106db527736afc066927f/websockets-17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98a5b2589a56a4b4f098b0a958099a4356ab904a7844f1da3841efca469af7e9", size = 214667, upload-time = "2026-08-26T14:55:22.298Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/bace574b6ae80e1a8d6935b8c5f03fb67236233ec572e976fe826ff719cf/websockets-17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:020e271205f8ab3406d7a59cd00de6dec722315924411c421bd00642f18bad86", size = 214944, upload-time = "2026-08-26T14:55:23.618Z" }, + { url = "https://files.pythonhosted.org/packages/ee/87/08e35ca4a0ffafb500a16ff461bf9561ad2b755362adb5d077d4dba9affc/websockets-17.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:65be6bda2b537fefa4b3a5ccd6ab386533ce39dd8fe62433ec90901fdc81752d", size = 224004, upload-time = "2026-08-26T14:55:24.748Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/00ae2e147eaa086fe8bdddd36f57216ce72b9a9dfc0b17c717005ebdacaf/websockets-17.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c84bdef916556cbe1d5a43b423398be4dd3cba6522b463e53d848578b920695", size = 224278, upload-time = "2026-08-26T14:55:26.016Z" }, + { url = "https://files.pythonhosted.org/packages/98/e2/7aeb4e00defa68826f449392922a382ce7fdf542fe52190558dc1714e284/websockets-17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47a62d6045c6eaa0d8f97bc2fb68b8cf90077a0cbfd4e83d6f2d2145611ee134", size = 225511, upload-time = "2026-08-26T14:55:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e4/655be3d93c3edbe1a51606073b5454ea6b1b32d87aa26253a6df952417b7/websockets-17.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34879e19bb0a3c44f9317679435aea5327fac993933a704cbf353bf1234b10c7", size = 228802, upload-time = "2026-08-26T14:55:28.431Z" }, + { url = "https://files.pythonhosted.org/packages/e4/33/98549a2afa9d68fe1b5a8e0a61cd461a43ea1ab7209bce675eea67c79190/websockets-17.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d72879819f5145a342d0030c418702496c65a4b913ef81f5ae944dd91dd50f6", size = 226075, upload-time = "2026-08-26T14:55:29.695Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6b/cbc27e014d6c292b9b2709cfd32781a2b61eb30cd4c9130e7c57e41a204a/websockets-17.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f25e099fdfe3b09f953d84698f729a1f7d1e99101b2787d7a28ed77b323750", size = 224846, upload-time = "2026-08-26T14:55:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/10/a6/e57925f7a423d90f24559e85bac21a7f0b44c0cf4a5c0babc0759ca54bab/websockets-17.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469355ab1af100b9380f1afb09985019f4a4b94fa1dd0e9396db4361626d7ab8", size = 222136, upload-time = "2026-08-26T14:55:32.376Z" }, + { url = "https://files.pythonhosted.org/packages/32/07/b9de0400addb542ba7c819022abc3afa46cd7e518068881bceadac69d995/websockets-17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00679b7468b4c2b12b0757118174e8eabac56bb2f579a928a104d9554a56e098", size = 225000, upload-time = "2026-08-26T14:55:33.527Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bb/af2828a1d7f2beb792af6ba56d7b02d56262070266b95d2af9ef391fbfb0/websockets-17.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a9fe648abd1d9b89aebfa30407bfdd08a0271ec5dc7d44a4c6ccd1ce22cf562a", size = 223592, upload-time = "2026-08-26T14:55:34.636Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/7379f254730c1dc7d8e4dd8d686868d8f7be55bdc94c6d3a44538840f639/websockets-17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f47aafd92aa28b941180e6da8a42b0f711851b14b81a5b6bb28dbbb1fa35152c", size = 224360, upload-time = "2026-08-26T14:55:35.777Z" }, + { url = "https://files.pythonhosted.org/packages/88/c6/fcd91320dd71dda7046df9bc60f60c70ee15c052dee21e31ed6221dc8b5d/websockets-17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c89406fa3dcd4aa8662c6406cc5c0de1790e9614d2c3aaf03ca53a8a8ccf3405", size = 225404, upload-time = "2026-08-26T14:55:36.85Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b2/655a4f939388079f80f1b3f8a1b9d40783e70a376e7423988cc9a590a09f/websockets-17.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b3b451fd2723ad3191a209afe6f3f4bc86c83e9a85bdc255353b91803ee6aa66", size = 222982, upload-time = "2026-08-26T14:55:38.006Z" }, + { url = "https://files.pythonhosted.org/packages/87/75/37c84c4371c6aa668910d7841036c4397ea10554c07030603ccd5e44b02a/websockets-17.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:054c28db2dcec0e857e3b705d8c28012613e555b38c765d6a4f75340a4fc06a0", size = 224017, upload-time = "2026-08-26T14:55:39.39Z" }, + { url = "https://files.pythonhosted.org/packages/5e/20/8a9a94323bfcfe03bde3f9d98926bea4855856359702fe3ca0d07051ef5d/websockets-17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f8e822efd54137d8cc8310eb64635ab827a4a6c72ff08691f38aa624776d8ecb", size = 224252, upload-time = "2026-08-26T14:55:40.498Z" }, + { url = "https://files.pythonhosted.org/packages/3d/dd/aa66e6500188cd40306abeb92c9a738ca6dd7029d8d8532c538055ab5daf/websockets-17.1-cp311-cp311-win32.whl", hash = "sha256:dcb8d5f7edef7a399d322cf28d4c4e6f98dab64d301c8f50581a1080e5198142", size = 217484, upload-time = "2026-08-26T14:55:41.763Z" }, + { url = "https://files.pythonhosted.org/packages/01/a2/cdf3b551f0b9177023afd3a45d3b431a0d4064951008c4321a8b42ac2288/websockets-17.1-cp311-cp311-win_amd64.whl", hash = "sha256:b1bc819c6db90e8f91a38250a1ab4c058261871aa52d2fe36382eddedf146dee", size = 217779, upload-time = "2026-08-26T14:55:42.942Z" }, + { url = "https://files.pythonhosted.org/packages/e0/13/51253dbed7d16a4bb87b05110ad3bf12165f410e915f6da1edd4186d8dc1/websockets-17.1-cp311-cp311-win_arm64.whl", hash = "sha256:edadce7a22052056fd4384543019856b34850363c9d387929f677ae01d79709c", size = 217710, upload-time = "2026-08-26T14:55:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0d/098f23c4c858e5de9459ffc554fa07d5493fbcfca7f040b5800cf1cecc35/websockets-17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:76dd004f59115087c7b700474cb18f01325e37250032e19396c08ae41448e4b3", size = 217015, upload-time = "2026-08-26T14:55:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/13/86/bc1317b1a4d8c4688e2a7e564b5e004dab44c2534d7ca05de6ae9a863fca/websockets-17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:581fa678ef46f4277cc8491312468e582f8ad609dbab907ba6096a08c6a0ff98", size = 214692, upload-time = "2026-08-26T14:55:46.366Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e7/df821761772beaa48c211ee0e234930b35c1473778470773823f56d3911b/websockets-17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87f0d5e77548b0c40c8464cdb6108792e7e53f487c6400028a4ec28a8afbe5ab", size = 214959, upload-time = "2026-08-26T14:55:47.885Z" }, + { url = "https://files.pythonhosted.org/packages/3e/92/c3fb72f11764812fc648bf3838d224972427b348e8b3989d9e0a9df87da3/websockets-17.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:882af300d2c6a092b93767d5de03c7bb56dfb06314140c8e872d3f48e09f7b74", size = 224278, upload-time = "2026-08-26T14:55:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/fb/05/9f82d090c8d2d861604147ef6dfb938a90b039f9358d5193f1df62558593/websockets-17.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c863507ada5805517ca6dff1c524dcd42942efe6304dacf06700878398d21a6", size = 224557, upload-time = "2026-08-26T14:55:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/8a/50/5cbf677b865290fe36819ff00615826e7edc1df38786f770123ff39a933d/websockets-17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d41ef69d5416fbc1d98cf96c37be6192d10fd101c3e0f8b3ddc36e09432b3c08", size = 225791, upload-time = "2026-08-26T14:55:51.75Z" }, + { url = "https://files.pythonhosted.org/packages/c1/1c/eb8a032285243381b09a221ae384c972d5000453ad136add4d1595cec798/websockets-17.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5aefe78e6a3077fe22b5e64b04666a85a3eb8b934d40e8595a693adcbceb6f11", size = 228574, upload-time = "2026-08-26T14:55:52.922Z" }, + { url = "https://files.pythonhosted.org/packages/69/85/413736251cb3ac04ce84cbd90e893d9a36a9698d4820b323aff3aa187e50/websockets-17.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f64e001bb7fa89b9f32cfa600bf8e9ac8ca26759d9b92ae01453ee303d9cd7b4", size = 226428, upload-time = "2026-08-26T14:55:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2b/a08bcc7fa1ca81a10f84ba32b6e6edd73a913f4b0c2640eed1fd626efacd/websockets-17.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:677014a073bcb1fbaa7e21144786864f16c08f856d66834f611eceb9006cbab8", size = 225184, upload-time = "2026-08-26T14:55:55.943Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8a/3bd2d0cf6b148c8c866d5d9fdcde30c04bfd81fdfac86813e69377eb4448/websockets-17.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0de501b7f2db11e83739ac20e2d33d46da4604b829f506c24be80e7def069391", size = 222430, upload-time = "2026-08-26T14:55:57.103Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c9/8e891ae342668735eabbbc669895e15195e4b45f24a4beeb58af76f414c7/websockets-17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f62114a54117e4948a1e414e89521f7fe1e3c2f83f2a571a06a4fc6718b0900a", size = 225227, upload-time = "2026-08-26T14:55:58.375Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6f/c816f332dca11425e9bda7c07f7573eb5c5f8a735849d02b0d81e8ee20fa/websockets-17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:eec113a5b41d124ef42ff56b0d74a6da3fd986400038eab9e58ee42a4024e837", size = 223831, upload-time = "2026-08-26T14:55:59.664Z" }, + { url = "https://files.pythonhosted.org/packages/53/67/5e91d5308ce24fc1ec74f56536c12f4888bad45ff5ea50f3180f8c518c57/websockets-17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5f051f8030a51815dc00e24bd2e5f1435af095c1cc111d747ac6e2a3620d7641", size = 224600, upload-time = "2026-08-26T14:56:00.873Z" }, + { url = "https://files.pythonhosted.org/packages/bb/96/faa298ecf2570d35b0eb37caddf4992178d907e108ed74bfffb6bc092c29/websockets-17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:655a8e28010f09fd6fa317e857afab3af7647f33e41dee88fa421e92086d1090", size = 225707, upload-time = "2026-08-26T14:56:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/0b/12/5710d2482ca5061c1eec5eb46f6313837c760d4115b1795c85b6c08be4e3/websockets-17.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dc2b79afc074d2f3e64b26539350f697fe1b85ea1c49ea24eb588f247b053ce1", size = 223263, upload-time = "2026-08-26T14:56:03.092Z" }, + { url = "https://files.pythonhosted.org/packages/27/47/0c30f4eebfd1d93fae779d268f678d48847fb98516f5200849574eee8820/websockets-17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e4bd7eacb87d8cf3ed70d6392c770a0d92441f05d7d2a3efafb5bc171d5e3067", size = 224244, upload-time = "2026-08-26T14:56:04.321Z" }, + { url = "https://files.pythonhosted.org/packages/41/33/46c256195a1255079ae23d1b1267b2e1843dc5f46a67f973cdf2a3523dff/websockets-17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ccbf3f4a9890d50b3a08ee04029fde30a03bfdeffaa19977628bf17251764e60", size = 224520, upload-time = "2026-08-26T14:56:05.521Z" }, + { url = "https://files.pythonhosted.org/packages/06/9a/aef0792731df4352e5f417369b532b3325fe434765ca90c193f594ae1e67/websockets-17.1-cp312-cp312-win32.whl", hash = "sha256:7e724f843fa6a0614aece65a7c73e51d0f4412ca41dccac13c3caf98e69536bb", size = 217485, upload-time = "2026-08-26T14:56:06.715Z" }, + { url = "https://files.pythonhosted.org/packages/50/23/493ecfdaf32898e5ea24dc900e33e5e317f9662d5d9ab2d44b2e111b4e1c/websockets-17.1-cp312-cp312-win_amd64.whl", hash = "sha256:617243e19a0992095956f406ee9cd3bc4ba92862d83cb1d83bb59ce574412bec", size = 217786, upload-time = "2026-08-26T14:56:08.055Z" }, + { url = "https://files.pythonhosted.org/packages/97/3d/91954e2f7876f74ce1213e9b92c65a63b559cc4b942a931ebeb351cd9932/websockets-17.1-cp312-cp312-win_arm64.whl", hash = "sha256:9f4a08ff7cb68c27b18e09223cc6304e01d0f82d5a240d251266dfd2e6e44729", size = 217711, upload-time = "2026-08-26T14:56:09.267Z" }, + { url = "https://files.pythonhosted.org/packages/1d/31/5f6450a7879f4f063ef08897cc385ea3ce3f1fe17f08b11e3fd959abdf27/websockets-17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a0162a6372110a5601cb5c9fd826635cedf69f3e110c545dd19774e040b970e", size = 217006, upload-time = "2026-08-26T14:56:10.509Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2a/c1b006fc861695d2aa4e35327b842015ce1d98cf8f99241829b3d6460bfc/websockets-17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:829dba1bc049779de9b332088c1a6a9858e96bd67e50b6b644a95e02b67836bc", size = 214690, upload-time = "2026-08-26T14:56:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/46/69/66e5b7d01445e0eeb1d4ab419c30315f2c90cf7a8a8cd4ecc47f894dba54/websockets-17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd8f47dbf2e8adb15c847215f83436de3fdb120b51fdae0fbbdf69fd97a3ad80", size = 214947, upload-time = "2026-08-26T14:56:12.923Z" }, + { url = "https://files.pythonhosted.org/packages/07/ce/033cafe2d2538562efa876b9149a2c7a0f7787870a4b1bb6e28adc9ceb6b/websockets-17.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9f4c0377a83e163a303514fdfab501dbe379bdc13e5b9312a91d112658b29dce", size = 224329, upload-time = "2026-08-26T14:56:14.212Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/e1c2e8a67f6cc0aa43abe0046fb3b7a020980649e6a843751dc7ce9eb170/websockets-17.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c3241d684a76eaaef8b2dc789afde4343cd3aad55ea81e4e8ab3605b529bae51", size = 224611, upload-time = "2026-08-26T14:56:15.702Z" }, + { url = "https://files.pythonhosted.org/packages/be/de/07c6d48eb3d2069709410c851e7de10ab83d752c4bd09862899627c2729b/websockets-17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5f5c7a893507d0e83a80b88aefd6522f7e882cd53f9722c6f23f5a020c9557c", size = 225848, upload-time = "2026-08-26T14:56:16.962Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dd/3c68572d20509648cc2fb6f50ccf3deeb4b87270f2c8966e99476e278ea3/websockets-17.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00bf34b64501e3477e81fc281532ff3cbf4da26633c10b63979d5085d46602d3", size = 227290, upload-time = "2026-08-26T14:56:18.204Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4a/8f6651c8a22093539c9215af0c5bbf217b87b382c99d2112039b92d593c2/websockets-17.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ce0305b702b20d1e1d60a9aaace6bc89970e1753565543f310d549eab22c2435", size = 226476, upload-time = "2026-08-26T14:56:19.459Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/f6fc33cea86b1127fd1297b18c107e81580ab55a73a39f9a934441ef321f/websockets-17.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29176d8b429cfa0fa443c473878d37a5c06cfd0cb36b71ba4314accc71e05906", size = 225233, upload-time = "2026-08-26T14:56:20.939Z" }, + { url = "https://files.pythonhosted.org/packages/cb/83/65edaf05f7c9b1dea82f4d252fdc37706a84571646f06119a27b0a16fe19/websockets-17.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3709a1ab30b4b922027d22f68d2b61a0656a91680ac894a537624e6be7dd7f7c", size = 222488, upload-time = "2026-08-26T14:56:22.208Z" }, + { url = "https://files.pythonhosted.org/packages/07/42/d1169c2f7f1f0032b0d4b0c00f0711a070cd7c735de37bfeb876bc0f9606/websockets-17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:43bd0c1ceb924d67f5c1a5254d8361dd9d94246e6331a726064dfa2917880780", size = 225295, upload-time = "2026-08-26T14:56:23.445Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f4/64e2a386c3899b917c2933225c9b47887874229d159797f3bf1a11c20d51/websockets-17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1fce0f43e0d41422e0b2cad6561e1970df22f212f4c7e884967df7cf591b031c", size = 223891, upload-time = "2026-08-26T14:56:24.647Z" }, + { url = "https://files.pythonhosted.org/packages/26/b3/dfb5c482f7e310a3432fdbb045ddfe6d34114680e89a233d4ff900a32961/websockets-17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4031152769179ab8dcdeafc7b0e58052a49117560a28671700b47b2c7b717aad", size = 224661, upload-time = "2026-08-26T14:56:26.027Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cf/94865130a336029f46412adc127c4fbe380f46172b90ce251369e35c4302/websockets-17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a06f3b5085176763182449559e20391d7ce616a8972a9f7a33deda87ea6d4f3c", size = 225766, upload-time = "2026-08-26T14:56:27.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/eb8c658f86dfe562ed49a887a27424bfe9e618c26ea6f865b093d075d3a6/websockets-17.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:77b37cceca17291897c3c73bd30a7c7c7909593554b5da574ec852af83c1742a", size = 223323, upload-time = "2026-08-26T14:56:28.807Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7e/2629609652ece5ca0c7ac235927dd4511b08131e3a5d53439b798fddf002/websockets-17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d8e83333385cac6030a5167fd18bf96cc6c58b914c308e683f05b0cf94bc8dd0", size = 224276, upload-time = "2026-08-26T14:56:29.991Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/8525737fe840b38e5f40956c198fb586a4fac1e07144d41a5b949b989cf8/websockets-17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:073c5c3f7e127041fa9d34a9e29ceefee8c3cafbd267ed2927318f425144380d", size = 224558, upload-time = "2026-08-26T14:56:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/74/ab/3a958c6cbcf74b118f601c20a80ac8bd5e8dfec0bcf7345116feaeefb121/websockets-17.1-cp313-cp313-win32.whl", hash = "sha256:2afb58c7ba48b329d56769f8dfd89f394efe587b65ef806bae810a484d6d3608", size = 217475, upload-time = "2026-08-26T14:56:32.431Z" }, + { url = "https://files.pythonhosted.org/packages/22/36/fb521f0f2994c25509651f169efe5582dddd8713d57a0757ba87859372ef/websockets-17.1-cp313-cp313-win_amd64.whl", hash = "sha256:0340bbef6bfbe16da888b3983d666a4db4954ac3253c38f13bc7aba0c7db5a2f", size = 217784, upload-time = "2026-08-26T14:56:33.608Z" }, + { url = "https://files.pythonhosted.org/packages/68/92/9b8419584681a12a7534b746dfb2737c466efe2455483e2fbf8b941a04ec/websockets-17.1-cp313-cp313-win_arm64.whl", hash = "sha256:7a72efa3bf4fa3a6669a54420a472ad056da3973d827f10e3a536da463f926c2", size = 217715, upload-time = "2026-08-26T14:56:34.865Z" }, + { url = "https://files.pythonhosted.org/packages/90/0d/500cf5daea09d4669dff3a7d67159094a0bd6c4ef130381404f6edd3eb5f/websockets-17.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0c9982938980e086da59f70d05f9418cd143401a601a0faac10fa48f7bb1cd3e", size = 217048, upload-time = "2026-08-26T14:56:36.03Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/5b12c6168aa269cffbfd24d177cd492b130120403a418c7e89462e27b4ac/websockets-17.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:57b39dc8541cf7ed3f639da82bf7451060483967f9e733da1f8173e4095f0642", size = 214737, upload-time = "2026-08-26T14:56:37.43Z" }, + { url = "https://files.pythonhosted.org/packages/0c/36/e453e5106e4e2416f008ac222837c2f1637a063b08008afcd1088889b631/websockets-17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:96abdecbaae746851b87c3a36cb4a661df93ca3d92f114270f79228bf1d00de6", size = 214955, upload-time = "2026-08-26T14:56:38.71Z" }, + { url = "https://files.pythonhosted.org/packages/dd/30/0204bb86176db02cdfc678ce65ed808a66fab87d250ce61a8790800a60b0/websockets-17.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9fc873e239c5abeb150bc24dbd1a7af23a9254526383ce0a077f5e20adbeb19", size = 224331, upload-time = "2026-08-26T14:56:39.924Z" }, + { url = "https://files.pythonhosted.org/packages/46/c8/d8372256e00c4e3cab1115c45075d1eeedb642a3f2b42bd70c4deae03f06/websockets-17.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f42912fa9eb4cb7c7ec9fde9b3332ba339eb8a8811981043d4029599f3d950b", size = 224685, upload-time = "2026-08-26T14:56:41.169Z" }, + { url = "https://files.pythonhosted.org/packages/12/7d/650355b8f67f908ff99603351d4458d1a0b787d627950a47c38db7e25308/websockets-17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f98bf378d7a5be047a044a1a27c987a8f355e10e3b5754617dbe756248cbc5ce", size = 225927, upload-time = "2026-08-26T14:56:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/34/6c/a9ffa5b903579eed76017870f055d75ecc73988d9d0c9b65a92ba0bf2a27/websockets-17.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d334d11398086bb5559606cb42d51c013ea7c061c7db701521392373d3c087f5", size = 227300, upload-time = "2026-08-26T14:56:43.538Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5d/4551c2269066af7481ee44605a0813770961615b5b5da3e87a8f5cb859ea/websockets-17.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c27336b1a0ac56569493e858497870347854372395f50483725f8cdacc5a45c", size = 226533, upload-time = "2026-08-26T14:56:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/237a99233e5c445759a613831b3a92e91905afc064dc3bd0ad33c35fd1e2/websockets-17.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67258b00302a5aaf0b267771c7014b13429abd7ea17eebc4c55bd935ff101555", size = 225280, upload-time = "2026-08-26T14:56:45.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b5/e9407a91613d1d1cd932414143a1012096b26674a782fc55a0bd23217ee4/websockets-17.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:455ffeea0879d313205df1e745e5883e1feb7f31ecd26be882f5f0babd3db04f", size = 222540, upload-time = "2026-08-26T14:56:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/db/d2/db76628db0577b783205d9779f64d8e373416b04c62d1546be4b75dc8540/websockets-17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f7233eaf441a345a5943a929fd4b5ea3278f11aed35a9ed0f3106b8cb3ca846a", size = 225354, upload-time = "2026-08-26T14:56:48.32Z" }, + { url = "https://files.pythonhosted.org/packages/a9/4c/2174181c067b89a74ae18e2650c2ac29959f4b796afe876ab3f4d30d642c/websockets-17.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c65da239a5ad553619804c1f9d65c1a0b3005381c6158ee14da2c7444cbd0c78", size = 223867, upload-time = "2026-08-26T14:56:49.579Z" }, + { url = "https://files.pythonhosted.org/packages/df/75/274decb9a8253561b5be3261e02a6676fc8ecdf31e95b722e53d5bfb8fd2/websockets-17.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fa1ffa08c81a4f809cdab6129f8e55bee4650b9d6d3461019dda73aacd146b6", size = 224652, upload-time = "2026-08-26T14:56:50.885Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e6/49824f1fb4db7656d2f7492b1d8be16147b759d909490e32f4776843ee64/websockets-17.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:406b8107943a43ef4649b1e0cb0cdc052bbf08fe1c8905a623c4af9586e5cebb", size = 225822, upload-time = "2026-08-26T14:56:52.356Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6a/5dc43838c0b02a95f42c47a0de33c5ddd7767a9feeb4d0d8777ac1cfefe4/websockets-17.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4e8ffcb486c8490a34a4cef5e4409d8da5a1cb1681e5bf7d786ce5e84aa8540d", size = 223379, upload-time = "2026-08-26T14:56:53.699Z" }, + { url = "https://files.pythonhosted.org/packages/c2/62/585637cf06d6b321232f79c55dc14d65518d12cf87c94c44f5864068810e/websockets-17.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fb88076df585b69c5761c387c0081aa87d7b9eb1b205a6535ca4777e25650d81", size = 224330, upload-time = "2026-08-26T14:56:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/de/68/c3b234a6a1366b6ab5bbfaa4434a1b946e1dc4e8ddd6824bfd93a8835b7f/websockets-17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5d4724255fb8398acd9e583b97eb2279cec20e0bd0f9a94bf75f6056ef9f13da", size = 224622, upload-time = "2026-08-26T14:56:56.393Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d4/84cf3d1376f5d8207f55f43c1c818babd6b89447f5dcd01f18a6d5526796/websockets-17.1-cp314-cp314-win32.whl", hash = "sha256:be3f0129c5654517b2abf07dcb75bb1d9479759a4ccfb569e8293579e9fc029a", size = 217036, upload-time = "2026-08-26T14:56:57.652Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0f/9e7ac63c5d7cb642952200814f584318e65146df008b7d375d5d9c6b2c97/websockets-17.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a4dc6ef83f4559e0d05f313a375cb38f63c986096a9da99fe94fdd779d313e5", size = 217382, upload-time = "2026-08-26T14:56:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/1ae6b91f7f3ac05f5c9f14a72dc2181c115ff370bcd8a7f10f02c174adfd/websockets-17.1-cp314-cp314-win_arm64.whl", hash = "sha256:46c0331c9eaaf73a559f3a9e388466be0df96eb83d40f06f1ca6ab6613b35c82", size = 217268, upload-time = "2026-08-26T14:57:00.654Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f0/f65644d0e0b2b90918a8c41503841cc4072a58f2bf76c09bc36e751fc0dd/websockets-17.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d411ea5ca18ac1b12c0c94be88b60c18ca641ac43bcdfdf1c9f79d46cdbe1603", size = 217379, upload-time = "2026-08-26T14:57:02.181Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/4c46d1f620ac1a30f92b6eae78ee40a772a93f568647ca7ccdc5ea283cf8/websockets-17.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:07fa3e7c30e2c577928d359b56bf872a3e0cbcc15553eaa0907c1ee86344b56f", size = 214911, upload-time = "2026-08-26T14:57:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/4587e8406d7c1188e97b9cf466c081e93399380d447f885bfce81626cd37/websockets-17.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6de9acef07e3a78e9567fcd26c29011a4da8f050b13004bbf880a0fd82a6eea5", size = 215115, upload-time = "2026-08-26T14:57:04.692Z" }, + { url = "https://files.pythonhosted.org/packages/ec/06/1381c8fff525041025909eb80ace32489194a00ba22a0a8d428030afcc84/websockets-17.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ea0ed9373b880115911d9d39634bccc95b8ce590c9c42e8589f5cacc3ef3cee2", size = 224696, upload-time = "2026-08-26T14:57:05.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/9d/9034e867dc85340be058619751742b895f722326e83100d110063461ca07/websockets-17.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50903d335bfda026c2fa11dd9aed09d8cbee0c451e3a85122a9acb041b7dc69b", size = 224975, upload-time = "2026-08-26T14:57:07.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/eb/ed03aa3cae748ebf6397e5d44028f433f746bad09dc568ff754fda3a3c9b/websockets-17.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a74531ce81af587f906ab42f194032388fcff8fc7938402e5917c9147a39441", size = 226151, upload-time = "2026-08-26T14:57:08.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c9/cc1964a096d16f3b73cb1ee5f14f277f5a3bcac07c6e8f9a1dcded99f4c8/websockets-17.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8fbf28e639544503b7d1c96452a5e5e043e4108d89b1f3fa02910603622d19db", size = 228292, upload-time = "2026-08-26T14:57:09.846Z" }, + { url = "https://files.pythonhosted.org/packages/1a/26/46da6dd0363c2db2e4876fd59a40fd40c1943a82d7018d0a33afbce47d52/websockets-17.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f612dc57f00c07cf4aa2673f7cbceabd654ad2457b7e639f061b794d6e11f9fd", size = 226722, upload-time = "2026-08-26T14:57:11.118Z" }, + { url = "https://files.pythonhosted.org/packages/78/98/ecd8f5e1c5d0e54c08ebc5c66852271112166db68107cb0e17ca1bf25009/websockets-17.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c7ac77401227212dc6e849182feee50d57cf456ec6329ffd6979c94bb136c5c", size = 225451, upload-time = "2026-08-26T14:57:12.601Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/da8d2760db53e17aae763738b6ba834b1fcf16813d3632f3edb6951e1ec8/websockets-17.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32a2a68d989d6e5b74a9d5095415c51189ebae29fceb7cf2b64a1c0318a81256", size = 223003, upload-time = "2026-08-26T14:57:13.875Z" }, + { url = "https://files.pythonhosted.org/packages/a4/40/ea401c141a79c5b1d0021a0dab9d0df2051c108f1620fbb39a6e7c714c3b/websockets-17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:aec00f018d34c67500ff0438dc314b40277be4a1b983cbacbf53ccf7db63e257", size = 225704, upload-time = "2026-08-26T14:57:15.091Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8e/07ab3f44215d89840d5385fdcaaab1fed8caeffa67c6899e15062957c12c/websockets-17.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0014eaff8ad5b3b43feda2279f9d34bf2eaae040720b9fbbb55944b10f40b14d", size = 224192, upload-time = "2026-08-26T14:57:16.3Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/ccf1af0a23e5748d4e22292a377d78d15cf294d7e707bbb11a8990ae6bd5/websockets-17.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:db9d7ee47f3ba531e278be539af39e2c7c7d28fb94897b6cd1120d63b0ef5922", size = 225082, upload-time = "2026-08-26T14:57:17.531Z" }, + { url = "https://files.pythonhosted.org/packages/e2/db/e32200f99ce282e728d2929f2c429db353cf3282db7d0eba99eb32c9fec1/websockets-17.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:ff3e2ba7a9f0a110b0555452e9b5a03a34e11662544e01beea15f144b48ba7b7", size = 226101, upload-time = "2026-08-26T14:57:18.802Z" }, + { url = "https://files.pythonhosted.org/packages/28/3d/e7a6e9777b29433620167c98f3caaff0d6b08b1239a273ef7f7fd1393349/websockets-17.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6da17fc94bd270f5987b10bee113461ac36a36a98b0481ddcc98056e5a90001a", size = 223794, upload-time = "2026-08-26T14:57:20.313Z" }, + { url = "https://files.pythonhosted.org/packages/48/05/ac569090726dedd6656f3ee28b0c02dfb1ba76e898dceaccc2987a237cef/websockets-17.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:e8dc3fa6d6b7ead3f9de57895f41b116a28787548e066365d9d90f7356bcaad2", size = 224567, upload-time = "2026-08-26T14:57:21.634Z" }, + { url = "https://files.pythonhosted.org/packages/14/50/4ef62941111db6b31193f4fabbb65f845a5177579040cb8fe0d774d25034/websockets-17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b65d5fe48219dc2d5e158de9e6514e75600f379cc7e37108d35f31764c155566", size = 224993, upload-time = "2026-08-26T14:57:22.86Z" }, + { url = "https://files.pythonhosted.org/packages/28/42/2b95ada4ea19bf3a2072b68669ce4f4afb212690b727d31640576287fd68/websockets-17.1-cp314-cp314t-win32.whl", hash = "sha256:2cce251f3e2469b99b6802b55435bcdd07123b41870f54c87b336183af9d7e68", size = 217168, upload-time = "2026-08-26T14:57:24.466Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/67d5ee08dd8060a37d612fd40a625b5376ad19ae48fe1c8ad428c278b817/websockets-17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f6c38cdcaf98a911d7acc25577f2f9e710f3a2fc2bde1563556784320196b51", size = 217508, upload-time = "2026-08-26T14:57:25.983Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/822005d0c674451d2411027b878cdc128a2b7ea5a30d337d9e279da22eba/websockets-17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:d1e2f5fa2b6d01f0d85b4f223fea7ed1d504be282a02a81bd2be4817ef7a2f03", size = 217425, upload-time = "2026-08-26T14:57:27.324Z" }, + { url = "https://files.pythonhosted.org/packages/de/d5/99a6c6a1eb5d5ae9f45f59a3c97f4e3b21f310eb404a547fb3e7d2fc054c/websockets-17.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:88381602e379165b66244b2ebc29f9b23ea0851fbe63ae157f91ca324f072d6f", size = 216970, upload-time = "2026-08-26T14:57:28.575Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0e/1e7f6e833728193958d3ed3d67b5d57c3c7cfa948abf94d4bc553257c954/websockets-17.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:88bc5138e53903a85c354e59df7ba73ce306f7b09724cef74dba121e60a88ce2", size = 214699, upload-time = "2026-08-26T14:57:29.862Z" }, + { url = "https://files.pythonhosted.org/packages/07/00/95d39549f86e34425a0412bcbe61708dd1fc46af654e2134a6c4389102ad/websockets-17.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3546ef55b3a074494106508bc6505c73825970d2d9505f7bf53882b3e88b0d1e", size = 214927, upload-time = "2026-08-26T14:57:31.148Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ff/b442415fc4f7f9943b0fc8e8eebaa13923ca73361e167c439ba634eecbd9/websockets-17.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9ae55d24241fc055f22aea3ac924559069848bd0ad4ea065fdd72d2194685fe8", size = 224373, upload-time = "2026-08-26T14:57:32.833Z" }, + { url = "https://files.pythonhosted.org/packages/a8/dd/b83537aae4cf61615b9d8b2dbb235c0030ba85457a6d934798273814600f/websockets-17.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d7b349265fad6244013eecd99df8d83c12bf3013943e431f4fadd5bffc37db42", size = 224801, upload-time = "2026-08-26T14:57:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/76/83/5ab0abed58454909e8dbab45086ac68ee4556d7a8ada26735addc909b903/websockets-17.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc5789e5ea182b77a38881383ada5347202a6c66f4857d054e075290e80b604b", size = 225967, upload-time = "2026-08-26T14:57:35.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/26/e2412f2b998a8c1dfc00c0709ff6ee0c634dd0b0b4f92bdfe9667876b71c/websockets-17.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce13c7d233239e739600a57d4a73c1192ad8259e655a4d55aa1a454242bc809d", size = 227664, upload-time = "2026-08-26T14:57:36.493Z" }, + { url = "https://files.pythonhosted.org/packages/ec/25/0dd4495df3c0e02f6db705312ba85ab9b2dd42257dc23eb0da10066e4844/websockets-17.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1036189bd34b0bc1b10a4679321e2c7968af317efe6e8e4c1c5141c4254fb5bb", size = 226447, upload-time = "2026-08-26T14:57:37.781Z" }, + { url = "https://files.pythonhosted.org/packages/be/67/6df3f63ffc48f08126ed0cd2fd2a41092967c3e364f8ec100deae90b6d77/websockets-17.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e78fd4b7b2c5086a38671c9c882c1e643385eccea360b5b1fda4a105e590087e", size = 225343, upload-time = "2026-08-26T14:57:39.133Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8d/a8479bbb09ff054907d141123d8f52fb6ae5ac39c6dbe39e6a02a8408309/websockets-17.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:46e7a10bf04318c7b0c0273791925ae5e1cbe4a11e34aa934d2ef27862058a80", size = 222748, upload-time = "2026-08-26T14:57:40.478Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/4c3d2a3269cde3f3087916de9c3d9fc5d7196b46846d8c3a9ae59ad0a884/websockets-17.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:33e45c7ea38428e740a7f233555d71df0b875cef7fc080acebc9654475e35335", size = 225453, upload-time = "2026-08-26T14:57:41.859Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1c/6467b401d19408f34e1c7389c222c2c7e1dfdf08c551190269b5eabc726c/websockets-17.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:6e63c01803be425ff062b7f7fc201a74def1d49fc94a2410dd17375df75936e9", size = 224112, upload-time = "2026-08-26T14:57:43.136Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5f/744e032ac80e11039a7447657ebabb46e9b5c2dbcec83be571335212932f/websockets-17.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:722ec21717eec6477bce582147a28acdfe034e604239466a6a95daedb863e774", size = 224646, upload-time = "2026-08-26T14:57:44.871Z" }, + { url = "https://files.pythonhosted.org/packages/9f/47/bcb9128d9afc4d0934d9192e2a24897ca2f7a63df2654904915349c6c46d/websockets-17.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:e74e41f0ad12ff1e8983e349daef79d37cc8280c743ce9d134d6c74c18dab5d6", size = 225797, upload-time = "2026-08-26T14:57:46.338Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e0/b058047b7cf565e1105b10ef6b6b24a6ebe3575678c7dc75a645334705a7/websockets-17.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:12fe8984a32dbfd084e0603f1a8d740c0180cb85b3174585c54a80d2455a8394", size = 223605, upload-time = "2026-08-26T14:57:48.175Z" }, + { url = "https://files.pythonhosted.org/packages/b9/69/fc1555bff884de363f1bf9eebf2836dbeb29fa7e4f957debb7bbcf43abba/websockets-17.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:01dcb47deebc40b38fd4a493b9b9f4d0a704b7bec6f35e4d34085b329abce71a", size = 224508, upload-time = "2026-08-26T14:57:49.407Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/648d4e68621688b19093b06f7b497d520952e68cdea1c1b54371fe9491de/websockets-17.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f4c45ee2512d3757b5e6c67c5a34e435143f2ecb7df3324f9fd888688c45c0f4", size = 224767, upload-time = "2026-08-26T14:57:50.799Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/f8342b55864f71df13eb8e9ef7dce691b87a87f04f75bb8a1385b3336e7c/websockets-17.1-cp315-cp315-win32.whl", hash = "sha256:0f4f50dfe2cc810fc4e2de979b35e83bf8bb4bccdc6fe472d93762ea7b1d5927", size = 217003, upload-time = "2026-08-26T14:57:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f0/7b5fdb774c245e0b6217009e2a24d2105c1a64923949f33be41aa7959302/websockets-17.1-cp315-cp315-win_amd64.whl", hash = "sha256:4af784f3e436f65b355c117c6497320f2b5cf6a559295cb1c4c7338e335d45cc", size = 217300, upload-time = "2026-08-26T14:57:53.492Z" }, + { url = "https://files.pythonhosted.org/packages/76/33/1fe6ed1b5087516115ca451b2c240314b010647071f8fc3bd78a21e4dddb/websockets-17.1-cp315-cp315-win_arm64.whl", hash = "sha256:d58159af7835fde09c462394293c0d7aaf8fb4557d8f8e5699f5e722ccae013d", size = 217214, upload-time = "2026-08-26T14:57:54.88Z" }, + { url = "https://files.pythonhosted.org/packages/94/ca/ed02e75996a266d76c5fcb5dd9b930db4cf2b388ca5fa3d2a72086f81568/websockets-17.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:1a5cf4e7bbe3ca499e6a289206cb4fcb7444b09919e129bd517f57d5fa192c13", size = 217282, upload-time = "2026-08-26T14:57:56.108Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7d/d536f5bc89ea5b52fd1c1727c59fabafee6bc41f5ce92c3bd2f83047908c/websockets-17.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:416b4bc8789a1865a3ff643ec4ee073a5f52402d0dbeafd27b1798d5dd6b6a51", size = 214863, upload-time = "2026-08-26T14:57:57.355Z" }, + { url = "https://files.pythonhosted.org/packages/37/37/944cf17bad668e9be1247e6314f88a48b9faf7c250e383410db8b38af0b9/websockets-17.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:259f45358c76d3b18489e3e80636cdbe807e05ecf1b10fdf1a779106d23d0c8e", size = 215073, upload-time = "2026-08-26T14:57:58.719Z" }, + { url = "https://files.pythonhosted.org/packages/74/bf/3267966cc1bbc2b8fa62fd329651b0af502df1f5d1c0eed027ff339d6aa8/websockets-17.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9d01e8ede41fea4f5a847dad9d628355f74905f437a5b6856d67aa66d193800", size = 225229, upload-time = "2026-08-26T14:58:00.235Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d8/85ea722f483510abb39fc71aafb4465d17cf9051a275ab036874ff3c300c/websockets-17.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7b35181a14cbfcae163b4de545d22abfd07d06c2c41ca69cfcd99251d6888ab", size = 225500, upload-time = "2026-08-26T14:58:01.994Z" }, + { url = "https://files.pythonhosted.org/packages/50/ce/64c7d00005bd0d15ecb5c5fcb7fb2597b6b92ddd16c4fa6bbc3d2835ad63/websockets-17.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a8e768a048c2220697477ce2e67e4345dc9f693d0ee6af53945b5e30227c6a7", size = 226829, upload-time = "2026-08-26T14:58:03.327Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/096c67940fb957e667ca3c542818150434eb0388c6fdc90b3a502f3c3e96/websockets-17.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:880069d21cc33a558dcf180924a546d1ecf8ada5be3e4e70acee87019d706a24", size = 228457, upload-time = "2026-08-26T14:58:04.78Z" }, + { url = "https://files.pythonhosted.org/packages/51/fe/f2331b6b7ccc67589891da354fa46a5cb79e95f83b9fd0e734d77f1f2140/websockets-17.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cec1bb8f22abccc8d20f8ca63df9be41600c26c190f4b97ee86c675fd4a863a6", size = 227265, upload-time = "2026-08-26T14:58:06.102Z" }, + { url = "https://files.pythonhosted.org/packages/47/a5/fb1642302f8ec77ca922203074f155a9831a5128ad75e725059a476d1227/websockets-17.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f3a1d577e081667dda7f8e5b4796e6e32f9713c93e2a3d930669519840a3c623", size = 226143, upload-time = "2026-08-26T14:58:07.464Z" }, + { url = "https://files.pythonhosted.org/packages/d7/41/7133fcfb63f5562750b269d6a845c689dde6a2c6407286da395beea19ddd/websockets-17.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc053f9e95a76213c5eb7ed95779f7daf0d2bf0e4e03073629ebfa43a033f151", size = 223501, upload-time = "2026-08-26T14:58:08.766Z" }, + { url = "https://files.pythonhosted.org/packages/64/b1/82b36bfabc79ff2d383a1fc043cee6a13f794ef4f6bf1b4810ad6988cf6f/websockets-17.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:bb0efe019480a1c93e168ce96479273aaebd672fc8c350d5eed1e507ababb1b8", size = 226330, upload-time = "2026-08-26T14:58:09.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/7d/5b511b9bf6e9ad331e6ff902fcbcc71c3794d10ef3b5efe80ccb8f0a7861/websockets-17.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:615746b12b26a3fd4077bc6fbeb277a1c192a45dd57b531d07ad9ed5c52a9a7a", size = 224980, upload-time = "2026-08-26T14:58:11.303Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/aed08f25301f8eef23be903ff9319fcf35630ca2bdec9d226f7d804dd5b3/websockets-17.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:1a20136d61f9ca3a31493732762661fafc2c20e8861930214e21afc6a8a692a2", size = 225478, upload-time = "2026-08-26T14:58:12.543Z" }, + { url = "https://files.pythonhosted.org/packages/3e/47/0d63d4168536b4682c9d19b7399443b1176f25dbb68878374fa716670230/websockets-17.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:2786cbd273ab69c22612db8a41229ddf2c158060b17b5928884bf388d07887f3", size = 226588, upload-time = "2026-08-26T14:58:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/844bd0b6386fc81ed6a55f4b6dd26f01c6987eda205afa10175ea12b2164/websockets-17.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:b1c323fc3be1dc3f87f6c59458cb7d9e13dcbbf971d6c3f3e2bbaf58d3bfcdfe", size = 224336, upload-time = "2026-08-26T14:58:15.778Z" }, + { url = "https://files.pythonhosted.org/packages/96/18/03709c84bc88ec4dcea68d4be4ccd07d611073dec111203a5bf45af8809d/websockets-17.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12c8e2b25df59755954a04dfa09c990b96691025aaf7eafd19ed6da24b09c18d", size = 225197, upload-time = "2026-08-26T14:58:17.141Z" }, + { url = "https://files.pythonhosted.org/packages/27/cf/0d1c694b6466c89e875b85b32b51312c472cf6708eee91914866f5087dde/websockets-17.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f58f58b4b29bbea2a3635e2c56eff4a3adab011fe383802a9e542e31b97085fc", size = 225493, upload-time = "2026-08-26T14:58:18.521Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f5/99857c3dd9676749f33e3668665a34ad6099505fb8d75eb084f49f7807a9/websockets-17.1-cp315-cp315t-win32.whl", hash = "sha256:f78a3ffb1994304db2c0c4588e4d1a518079b557054fa3bb985a6f5e50ff49a3", size = 217130, upload-time = "2026-08-26T14:58:20.037Z" }, + { url = "https://files.pythonhosted.org/packages/2c/84/77599922ab441bfe61508f97dab2c71f8e114d31793993ea54011db16199/websockets-17.1-cp315-cp315t-win_amd64.whl", hash = "sha256:ad68c28a27246fed109a4409393d677b7e1388345cbbd2f5aee5c182d8506110", size = 217448, upload-time = "2026-08-26T14:58:21.382Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3c/8b9a225b523f06a9389be81f1b0ab07c49bec6014742e6aa359c1f920f1f/websockets-17.1-cp315-cp315t-win_arm64.whl", hash = "sha256:e552e0037230ac16e5f568de7012041344d1b18c9feed30ec2891b8eba55af81", size = 217372, upload-time = "2026-08-26T14:58:22.807Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e4/af4abbcf07eac6a725ec6f865611526b2b0c23d482723de551bec667880d/websockets-17.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10ecb38ffc05e1841b619d99c725307a223ef9ad58e7b1ed33311d472dc43918", size = 214602, upload-time = "2026-08-26T14:58:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/4d/fe/819fba7ba35f92b639333da7355041c07dd50048f9c76fba0b8e292a6483/websockets-17.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17aa424ab61620aad21b36b2240efc87b500cc496e7d0e999a5c2ae99395e886", size = 214874, upload-time = "2026-08-26T14:58:26.689Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a7/d370ab794f47fbeea648d17ad08caf0bb50131d6c04b7ad83e6af63c405a/websockets-17.1-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:764cf7bfa149365f32b7a0fd9fed32debdac29dd06295d5635cde1745b446cd8", size = 215821, upload-time = "2026-08-26T17:25:23.616Z" }, + { url = "https://files.pythonhosted.org/packages/9b/6b/251b00fe634e2a9c2cb5d6390e0e97cec55e3d18dd09b4b976620eed5d7b/websockets-17.1-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d1b108bd8f5f6a8b90801f6db3b3858d5deca889acfdb8ac497bbb24e4b0edf", size = 215714, upload-time = "2026-08-26T17:25:26.295Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b1/37fe0c96c206b4208a072c3a74add6a72af4b8228be3f5435163c5a6d099/websockets-17.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a62d8c424383c9dc769ff3672018df822603117e32686e567d452ed035b6fb2e", size = 216608, upload-time = "2026-08-26T17:25:28.134Z" }, + { url = "https://files.pythonhosted.org/packages/be/7e/75a0a491b512412e08333b9f8412757af6186fe1c598186261002de1a793/websockets-17.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8196d217eeca52b9235ee1f8a684a09885a5f953d5a31e80ef915bf2c5c94f9d", size = 217870, upload-time = "2026-08-26T17:25:29.745Z" }, + { url = "https://files.pythonhosted.org/packages/41/63/23572870e01836a98346075b9e17a8bc24a6ddd9800a3204ceee58677f3c/websockets-17.1-py3-none-any.whl", hash = "sha256:f221081107b8c48184d99f7019604486376e7ef826037e70aad6b02540732c23", size = 211134, upload-time = "2026-08-26T17:25:31.397Z" }, +] + [[package]] name = "yarl" version = "1.22.0" @@ -2368,3 +2598,77 @@ sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50e wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, +]