From af66a49aa275576b89b315873bed0a34d407f3b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 22:23:12 +0000 Subject: [PATCH] chore(sdk): regenerate from archastro-openapi@main (generator 0.11.12) --- package-lock.json | 6 +- specs/platform-openapi.json | 14701 +++++++++++++++- src/archastro/platform/channels/__init__.py | 3 +- .../platform/channels/api_artifact_channel.py | 231 + .../platform/channels/api_chat_channel.py | 22 +- .../platform/channels/api_object_channel.py | 3 +- .../platform/channels/api_tasks_channel.py | 22 +- src/archastro/platform/client.py | 8 +- src/archastro/platform/types/__init__.py | 4 +- src/archastro/platform/types/ai.py | 47 +- src/archastro/platform/types/artifacts.py | 81 +- src/archastro/platform/types/automations.py | 4 +- src/archastro/platform/types/common.py | 215 +- src/archastro/platform/types/computers.py | 21 + src/archastro/platform/types/events.py | 6 +- src/archastro/platform/types/relays.py | 16 + src/archastro/platform/types/tasks.py | 61 +- src/archastro/platform/v1/__init__.py | 14 +- .../platform/v1/resources/__init__.py | 14 +- .../platform/v1/resources/agent_computers.py | 86 +- .../platform/v1/resources/agent_requests.py | 226 + .../platform/v1/resources/agent_sessions.py | 82 +- src/archastro/platform/v1/resources/agents.py | 88 +- src/archastro/platform/v1/resources/ai.py | 180 +- .../platform/v1/resources/artifacts.py | 82 +- .../platform/v1/resources/custom_objects.py | 298 +- .../v1/resources/event_subscriptions.py | 28 +- src/archastro/platform/v1/resources/files.py | 92 +- .../v1/resources/knowledge_sources.py | 21 +- src/archastro/platform/v1/resources/kv.py | 281 +- .../platform/v1/resources/notifications.py | 30 +- src/archastro/platform/v1/resources/oauth.py | 6 +- src/archastro/platform/v1/resources/orgs.py | 2246 ++- .../resources/private_service_enrollments.py | 34 +- src/archastro/platform/v1/resources/relays.py | 144 + .../platform/v1/resources/scripts.py | 62 +- .../resources/task_ownership_conversions.py | 50 + src/archastro/platform/v1/resources/tasks.py | 243 +- src/archastro/platform/v1/resources/teams.py | 453 +- .../platform/v1/resources/threads.py | 235 +- src/archastro/platform/v1/resources/users.py | 454 +- .../platform/v1/resources/workflows.py | 14 +- .../channels/test_api_artifact_channel.py | 392 + .../channels/test_api_chat_channel.py | 6 +- tests/contract/streams/test_agent_sessions.py | 9 +- tests/contract/v1/test_agent_computers.py | 105 +- tests/contract/v1/test_agent_requests.py | 484 + tests/contract/v1/test_agent_sessions.py | 86 +- tests/contract/v1/test_agents.py | 128 +- tests/contract/v1/test_ai.py | 67 +- tests/contract/v1/test_custom_objects.py | 128 +- tests/contract/v1/test_files.py | 128 +- tests/contract/v1/test_kv.py | 50 +- tests/contract/v1/test_orgs.py | 769 +- .../v1/test_private_service_enrollments.py | 105 +- tests/contract/v1/test_relays.py | 379 + .../v1/test_task_ownership_conversions.py | 148 + tests/contract/v1/test_tasks.py | 254 +- tests/contract/v1/test_teams.py | 236 +- tests/contract/v1/test_threads.py | 170 +- tests/contract/v1/test_users.py | 236 +- tests/contract/v1/test_workflows.py | 44 +- 62 files changed, 24047 insertions(+), 791 deletions(-) create mode 100644 src/archastro/platform/channels/api_artifact_channel.py create mode 100644 src/archastro/platform/types/computers.py create mode 100644 src/archastro/platform/types/relays.py create mode 100644 src/archastro/platform/v1/resources/agent_requests.py create mode 100644 src/archastro/platform/v1/resources/relays.py create mode 100644 src/archastro/platform/v1/resources/task_ownership_conversions.py create mode 100644 tests/contract/channels/test_api_artifact_channel.py create mode 100644 tests/contract/v1/test_agent_requests.py create mode 100644 tests/contract/v1/test_relays.py create mode 100644 tests/contract/v1/test_task_ownership_conversions.py diff --git a/package-lock.json b/package-lock.json index 7b7ed06..f88f7bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,9 +55,9 @@ } }, "node_modules/@archastro/sdk-generator": { - "version": "0.11.9", - "resolved": "https://registry.npmjs.org/@archastro/sdk-generator/-/sdk-generator-0.11.9.tgz", - "integrity": "sha512-Ev80mFFnoaD6Dj8bjxL9sJsXLlNuXMvCZlTFfWRS6/JLOd4VTEOP9SEUQ0TGDXhM8vpQRSqkcXFvsQ9rwFSLwA==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/@archastro/sdk-generator/-/sdk-generator-0.11.12.tgz", + "integrity": "sha512-opetHxvv9j8l9SY51uPh77ApKj0ta1ie6Y5AbukurN9dGM9LXkYBvEPlWn+CLy5W1UpJfKgPoCfHOGtTx8uZfQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/specs/platform-openapi.json b/specs/platform-openapi.json index 454777a..1b72868 100644 --- a/specs/platform-openapi.json +++ b/specs/platform-openapi.json @@ -4,12 +4,25 @@ "AIChatStreamDone": { "description": "Terminal event marking the end of a streaming chat completion (SSE `done` event).", "example": { + "completions": [ + { + "response_model": "string", + "timing": {} + } + ], "finish_reason": "stop", "run_count": 1, "total_usage": {}, "usage": {} }, "properties": { + "completions": { + "description": "Ordered accepted completions; empty records retain unknown identities.", + "items": { + "$ref": "#/components/schemas/AICompletionProvenance" + }, + "type": "array" + }, "finish_reason": { "description": "The overall finish reason for the completion.", "example": "stop", @@ -36,13 +49,27 @@ "AIChatStreamError": { "description": "Terminal error event for a streaming chat completion (SSE `error` event).", "example": { - "message": "string" + "message": "string", + "retryable": true, + "status": 1 }, "properties": { "message": { "description": "Human-readable description of the error that terminated the stream.", "example": "string", "type": "string" + }, + "retryable": { + "description": "Whether the provider failure can be retried.", + "example": true, + "nullable": true, + "type": "boolean" + }, + "status": { + "description": "Upstream HTTP status, when reported.", + "example": 1, + "nullable": true, + "type": "integer" } }, "required": [ @@ -77,6 +104,7 @@ } ] }, + "response_model": "string", "usage": {} }, "properties": { @@ -89,6 +117,11 @@ "$ref": "#/components/schemas/AIMessage", "description": "The complete assistant message for this run, assembled from the preceding deltas." }, + "response_model": { + "description": "Provider-reported model identity for this accepted completion.", + "example": "string", + "type": "string" + }, "usage": { "description": "Token consumption for this run, keyed by model ID. `null` when usage data is unavailable.", "example": {}, @@ -192,9 +225,35 @@ }, "type": "object" }, + "AICompletionProvenance": { + "description": "One accepted provider completion. An empty record preserves an unknown identity.", + "example": { + "response_model": "string", + "timing": {} + }, + "properties": { + "response_model": { + "description": "Model identity reported by the provider response, not the requested model or usage key.", + "example": "string", + "type": "string" + }, + "timing": { + "description": "Optional monotonic timing for this accepted completion and its retries. request_ms includes all attempts, backoff and local processing; provider_attempts_ms is the sum of provider request wall times, including transport and parsing; retry_backoff_ms is measured retry sleep. attempt_count includes the accepted call and retry_count counts earlier attempts. These overlapping measurements are not provider-internal inference time. Missing timing is unknown, not zero.", + "example": {}, + "type": "object" + } + }, + "type": "object" + }, "AICompletionResult": { "description": "The result of an AI chat completion request. Returned by chat completion endpoints after the model finishes generating.", "example": { + "completions": [ + { + "response_model": "string", + "timing": {} + } + ], "finish_reason": "stop", "message": { "content": "How can I help you today?", @@ -244,9 +303,17 @@ ] } ], + "response_model": "string", "token_usage": {} }, "properties": { + "completions": { + "description": "Ordered accepted provider completions, including empty records for unknown identities.", + "items": { + "$ref": "#/components/schemas/AICompletionProvenance" + }, + "type": "array" + }, "finish_reason": { "description": "The reason the model stopped generating. Common values include `\"stop\"` (natural end), `\"length\"` (token limit reached), and `\"tool_calls\"` (the model invoked a tool).", "example": "stop", @@ -263,8 +330,13 @@ }, "type": "array" }, + "response_model": { + "description": "Final accepted completion provider-reported identity; absent when unknown.", + "example": "string", + "type": "string" + }, "token_usage": { - "description": "Token consumption breakdown keyed by model ID. Each value is a map with `\"input_tokens\"` and `\"output_tokens\"` counts. `null` when usage data is unavailable.", + "description": "Token consumption keyed by model ID. Each value has `input` (all prompt tokens, including cache reads and writes) and `output` counts. When reported, `fresh_input`, `cache_read`, and `cache_creation` are disjoint input buckets; missing cache counts remain unknown. `null` when usage data is unavailable.", "example": {}, "type": "object" } @@ -277,11 +349,12 @@ "type": "object" }, "AIEvaluationAnswer": { - "description": "One typed evaluation answer. `type` selects boolean, choice, or score.\n`id` matches the question id.\n", + "description": "One typed evaluation answer. `type` selects boolean, choice, score, or refusal.\n`id` matches the question id.\n", "discriminator": { "mapping": { "boolean": "#/components/schemas/AIEvaluationAnswerBoolean", "choice": "#/components/schemas/AIEvaluationAnswerChoice", + "refusal": "#/components/schemas/Refusal", "score": "#/components/schemas/AIEvaluationAnswerScore" }, "propertyName": "type" @@ -295,6 +368,9 @@ }, { "$ref": "#/components/schemas/AIEvaluationAnswerScore" + }, + { + "$ref": "#/components/schemas/Refusal" } ] }, @@ -435,7 +511,7 @@ }, "properties": { "answers": { - "description": "Typed answers. Each value is a boolean, choice, or score object with the question `id`.", + "description": "Typed boolean, choice, score, or refusal answers with the question `id`.", "items": { "$ref": "#/components/schemas/AIEvaluationAnswer" }, @@ -3543,6 +3619,7 @@ "created_at": "2024-01-01T00:00:00Z", "error_message": "string", "id": "cmp_0aBcDeFgHiJkLmNoPqRsTu", + "key": "string", "last_active_at": "2024-01-01T00:00:00Z", "lookup_key": "main-computer", "metadata": { @@ -3553,12 +3630,14 @@ "region": "us-east-1", "sprite_url": "https://example.com", "status": "ready", + "thread_id": "string", "updated_at": "2024-01-01T00:00:00Z" }, "properties": { "agent": { "description": "ID of the agent that owns this computer (`agi_...`). `null` if the computer is not yet assigned to an agent.", "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, "type": "string" }, "app": { @@ -3582,6 +3661,7 @@ "error_message": { "description": "Human-readable error description when `status` is `\"error\"`. `null` otherwise.", "example": "string", + "nullable": true, "type": "string" }, "id": { @@ -3589,15 +3669,23 @@ "example": "cmp_0aBcDeFgHiJkLmNoPqRsTu", "type": "string" }, + "key": { + "description": "Stable key unique within the thread, or null if unset.", + "example": "string", + "nullable": true, + "type": "string" + }, "last_active_at": { "description": "When the computer last reported activity or received a command. `null` if the computer has never been active.", "example": "2024-01-01T00:00:00Z", "format": "date-time", + "nullable": true, "type": "string" }, "lookup_key": { "description": "Unique, stable identifier you assign to this computer within its app. `null` if not set.", "example": "main-computer", + "nullable": true, "type": "string" }, "metadata": { @@ -3613,23 +3701,32 @@ "type": "string" }, "provider": { - "description": "Compute backend powering this computer: `\"sprites\"` (Fly Sprites) or `\"vercel\"` (Vercel Sandbox).", + "description": "Compute backend powering this computer: `\"sprites\"` (Fly Sprites), `\"vercel\"` (Vercel Sandbox) or `\"e2b\"` (E2B).", "example": "sprites", "type": "string" }, "region": { - "description": "Cloud region where the computer is hosted, e.g. `\"us-east-1\"`. `null` if not yet assigned or when the provider has no region concept (e.g. `\"vercel\"`).", + "description": "Cloud region where the computer is hosted, e.g. `\"us-east-1\"`. `null` if not yet assigned or when the provider has no region concept (e.g. `\"vercel\"`, `\"e2b\"`).", "example": "us-east-1", + "nullable": true, "type": "string" }, "sprite_url": { "description": "URL of the live screenshot sprite used to render a real-time preview of the computer's screen. `null` when no sprite is available.", "example": "https://example.com", + "nullable": true, "type": "string" }, "status": { "description": "Current lifecycle state of the computer. Common values include `\"provisioning\"`, `\"ready\"`, `\"error\"`, and `\"terminated\"`.", "example": "ready", + "nullable": true, + "type": "string" + }, + "thread_id": { + "description": "Thread this computer belongs to, or null for a non-thread computer.", + "example": "string", + "nullable": true, "type": "string" }, "updated_at": { @@ -3657,6 +3754,7 @@ "created_at": "2024-01-01T00:00:00Z", "error_message": "string", "id": "cmp_0aBcDeFgHiJkLmNoPqRsTu", + "key": "string", "last_active_at": "2024-01-01T00:00:00Z", "lookup_key": "main-computer", "metadata": { @@ -3667,6 +3765,7 @@ "region": "us-east-1", "sprite_url": "https://example.com", "status": "ready", + "thread_id": "string", "updated_at": "2024-01-01T00:00:00Z" } ] @@ -4705,6 +4804,201 @@ ], "type": "object" }, + "AgentRequest": { + "description": "A request for human approval or input. Secret input is never returned.", + "example": { + "agent": { + "id": "string", + "name": "Example Name" + }, + "approvers": { + "mode": "string" + }, + "expires_at": "2024-01-01T00:00:00Z", + "id": "arq_0aBcDeFgHiJkLmNoPqRsTu", + "is_approver": true, + "kind": "string", + "origin": { + "routine_id": "string", + "session_id": "string", + "thread_id": "string" + }, + "payload": { + "question": "string" + }, + "resolution": { + "answer": "string", + "reason": "string" + }, + "resolved_at": "2024-01-01T00:00:00Z", + "resolved_by": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "status": "string", + "summary": { + "lines": [ + "string" + ], + "title": "Example Title" + } + }, + "properties": { + "agent": { + "$ref": "#/components/schemas/AgentRequestAgent", + "description": "Originating agent." + }, + "approvers": { + "$ref": "#/components/schemas/Approvers", + "description": "Approval policy, excluding user IDs." + }, + "expires_at": { + "description": "Expiration time.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "Request ID (`arq_...`).", + "example": "arq_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_approver": { + "description": "Whether the authenticated caller is an approver, regardless of request status.", + "example": true, + "type": "boolean" + }, + "kind": { + "description": "Registered request kind.", + "example": "string", + "type": "string" + }, + "origin": { + "$ref": "#/components/schemas/Origin", + "description": "Origin references." + }, + "payload": { + "$ref": "#/components/schemas/Payload", + "description": "Question request payload." + }, + "resolution": { + "allOf": [ + { + "$ref": "#/components/schemas/Resolution" + } + ], + "description": "Answer or rejection reason; null before resolution.", + "example": { + "answer": "string", + "reason": "string" + }, + "nullable": true + }, + "resolved_at": { + "description": "Resolution time.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "resolved_by": { + "description": "Resolving user ID.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "status": { + "description": "Request status.", + "example": "string", + "type": "string" + }, + "summary": { + "$ref": "#/components/schemas/Summary", + "description": "Kind-provided summary." + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "AgentRequestAgent": { + "description": "Originating agent.", + "example": { + "id": "string", + "name": "Example Name" + }, + "properties": { + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "AgentRequestListResponse": { + "description": "A page of visible agent requests.", + "example": { + "data": [ + { + "agent": { + "id": "string", + "name": "Example Name" + }, + "approvers": { + "mode": "string" + }, + "expires_at": "2024-01-01T00:00:00Z", + "id": "arq_0aBcDeFgHiJkLmNoPqRsTu", + "is_approver": true, + "kind": "string", + "origin": { + "routine_id": "string", + "session_id": "string", + "thread_id": "string" + }, + "payload": { + "question": "string" + }, + "resolution": { + "answer": "string", + "reason": "string" + }, + "resolved_at": "2024-01-01T00:00:00Z", + "resolved_by": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "status": "string", + "summary": { + "lines": [ + "string" + ], + "title": "Example Title" + } + } + ], + "next_cursor": "string" + }, + "properties": { + "data": { + "description": "Visible requests.", + "items": { + "$ref": "#/components/schemas/AgentRequest" + }, + "type": "array" + }, + "next_cursor": { + "description": "Pass as cursor for the next page; null at the end.", + "example": "string", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "AgentRoutine": { "description": "An agent routine defines a reusable handler — script, preset, or chain — that runs in response to events or on a schedule.", "example": { @@ -5568,6 +5862,47 @@ ], "type": "object" }, + "AgentSessionEvent": { + "description": "One event from the coding harness running an agent session on a computer.", + "example": { + "event": "tool_use", + "harness": "claude", + "id": "cse_...", + "occurred_at": "2024-01-01T00:00:00Z", + "payload": {} + }, + "properties": { + "event": { + "description": "Event name: `session_started`, `message`, `tool_use`, `needs_input`, `activity`, `error`, `turn_complete`, `finished`, `adopted`, or `harness_raw`.", + "example": "tool_use", + "type": "string" + }, + "harness": { + "description": "Harness that emitted it.", + "example": "claude", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Event ID; pass as `after` to page.", + "example": "cse_...", + "type": "string" + }, + "occurred_at": { + "description": "When the event happened in the sandbox.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "payload": { + "description": "Harness-specific event data.", + "example": {}, + "nullable": true, + "type": "object" + } + }, + "type": "object" + }, "AgentSessionListResponse": { "description": "Paginated list response containing an array of agent session objects.", "example": { @@ -6693,9 +7028,53 @@ ], "type": "object" }, + "Approvers": { + "description": "Approval policy, excluding user IDs.", + "example": { + "mode": "string" + }, + "properties": { + "mode": { + "example": "string", + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" + }, "Artifact": { "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -6726,6 +7105,43 @@ "version": 1 }, "properties": { + "acl": { + "allOf": [ + { + "$ref": "#/components/schemas/Acl" + } + ], + "description": "Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "nullable": true + }, "agent": { "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", @@ -6834,6 +7250,254 @@ ], "type": "object" }, + "ArtifactEditingAck": { + "description": "Acknowledges an Artifact editing presence or leave request.", + "example": { + "ok": true + }, + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, + "ArtifactEditingActor": { + "description": "A server-issued collaborator identity and its ephemeral presence.", + "example": { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "meta": {} + }, + "properties": { + "actor_id": { + "description": "Opaque identity unique to this channel join.", + "example": "string", + "type": "string" + }, + "cursor": { + "allOf": [ + { + "$ref": "#/components/schemas/ArtifactEditingCursor" + } + ], + "description": "Current code-point cursor, or null.", + "example": { + "position": 1, + "selection_end": 1 + }, + "nullable": true + }, + "meta": { + "description": "Server-derived collaborator display metadata.", + "example": {}, + "type": "object" + } + }, + "required": [ + "actor_id", + "meta" + ], + "type": "object" + }, + "ArtifactEditingCheckpointResponse": { + "description": "Confirms the exact live revision that reached durable ArtifactVersion storage.", + "example": { + "artifact_version": "string", + "artifact_version_number": 1, + "checkpointed_revision": 1, + "dirty": true, + "incarnation": "string" + }, + "properties": { + "artifact_version": { + "description": "New durable ArtifactVersion ID.", + "example": "string", + "type": "string" + }, + "artifact_version_number": { + "example": 1, + "type": "integer" + }, + "checkpointed_revision": { + "example": 1, + "type": "integer" + }, + "dirty": { + "description": "True when later live operations remain unsaved.", + "example": true, + "type": "boolean" + }, + "incarnation": { + "example": "string", + "type": "string" + } + }, + "required": [ + "incarnation", + "checkpointed_revision", + "artifact_version", + "artifact_version_number", + "dirty" + ], + "type": "object" + }, + "ArtifactEditingCursor": { + "description": "A Unicode code-point cursor and optional selection endpoint.", + "example": { + "position": 1, + "selection_end": 1 + }, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "ArtifactEditingJoinResponse": { + "description": "Authoritative live Artifact snapshot returned by an editing-channel join.", + "example": { + "actor_id": "string", + "actors": [ + { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "meta": {} + } + ], + "artifact": "string", + "artifact_version": "string", + "artifact_version_number": 1, + "can_write": true, + "checkpoint_error": "string", + "checkpointed_revision": 1, + "connection_id": "string", + "content": "string", + "dirty": true, + "incarnation": "string", + "revision": 1 + }, + "properties": { + "actor_id": { + "description": "Server-issued identity for this channel join.", + "example": "string", + "type": "string" + }, + "actors": { + "items": { + "$ref": "#/components/schemas/ArtifactEditingActor" + }, + "type": "array" + }, + "artifact": { + "description": "Public Artifact ID (`art_...`).", + "example": "string", + "type": "string" + }, + "artifact_version": { + "description": "Durable version ID (`artv_...`).", + "example": "string", + "type": "string" + }, + "artifact_version_number": { + "example": 1, + "type": "integer" + }, + "can_write": { + "example": true, + "type": "boolean" + }, + "checkpoint_error": { + "description": "Last durable-save failure reason, or null.", + "example": "string", + "nullable": true, + "type": "string" + }, + "checkpointed_revision": { + "description": "Latest revision known durable.", + "example": 1, + "type": "integer" + }, + "connection_id": { + "description": "Server-issued connection identity.", + "example": "string", + "type": "string" + }, + "content": { + "description": "UTF-8 document content.", + "example": "string", + "type": "string" + }, + "dirty": { + "description": "True while a live tail remains crash-loss exposed.", + "example": true, + "type": "boolean" + }, + "incarnation": { + "description": "Opaque live-session fence token.", + "example": "string", + "type": "string" + }, + "revision": { + "description": "Live OT revision in Unicode code points.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "artifact", + "artifact_version", + "artifact_version_number", + "actor_id", + "connection_id", + "incarnation", + "content", + "revision", + "checkpointed_revision", + "dirty", + "can_write", + "actors" + ], + "type": "object" + }, + "ArtifactEditingOperationAck": { + "description": "Acknowledges an operation only after the authoritative OT server applied it.", + "example": { + "revision": 1 + }, + "properties": { + "revision": { + "description": "Authoritative live revision after application.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object" + }, "Attachment": { "description": "A rich attachment associated with a message, such as a file, scraped link, artifact, task, media item, or inline action.", "example": { @@ -6863,8 +7527,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -6878,8 +7544,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -7196,8 +7864,9 @@ "type": "string" }, "event_id": { - "description": "ID of the platform event that triggered this run. `null` for directly invoked automations.", + "description": "Event or invocation identifier for this run. Direct invocations use the supplied idempotency key or a generated UUID. `null` when no event identifier was recorded.", "example": "string", + "nullable": true, "type": "string" }, "id": { @@ -7208,6 +7877,7 @@ "participants": { "description": "Invoke-time map of symbolic participant references to agent IDs. `null` when no participants were supplied.", "example": {}, + "nullable": true, "type": "object" }, "payload": { @@ -7218,6 +7888,7 @@ "result": { "description": "The output produced after the automation finished executing. Contains workflow-defined keys alongside any returned output. `null` if the run has not yet completed.", "example": {}, + "nullable": true, "type": "object" }, "status": { @@ -7228,6 +7899,7 @@ "team": { "description": "ID of the team that owns this run (`tea_...`). `null` if the run is owned by a user rather than a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, "type": "string" }, "updated_at": { @@ -7239,6 +7911,7 @@ "user": { "description": "ID of the user that owns this run (`usr_...`). `null` if the run is owned by a team rather than a user.", "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, "type": "string" } }, @@ -7967,8 +8640,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -7982,8 +8657,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -7999,7 +8676,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -8178,8 +8855,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -8193,8 +8872,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -8210,7 +8891,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -8629,8 +9310,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -8644,8 +9327,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -8661,7 +9346,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -9446,8 +10131,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -9461,8 +10148,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -9478,7 +10167,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -9657,8 +10346,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -9672,8 +10363,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -9689,7 +10382,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -10514,8 +11207,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -10529,8 +11224,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -10546,7 +11243,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -10678,8 +11375,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -10693,8 +11392,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -10710,7 +11411,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -11245,8 +11946,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -11260,8 +11963,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -11277,7 +11982,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -11456,8 +12161,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -11471,8 +12178,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -11488,7 +12197,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -12556,9 +13265,13 @@ { "granted": true, "key": "string", + "limit": 1, "provided_by": [ "personal" ], + "quantity": 1, + "remaining": 1, + "used": 1, "value": true, "value_type": "boolean" } @@ -12572,6 +13285,7 @@ }, "name": "Example Name", "notification_settings": {}, + "oauth_client_name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "org_kind": "company", "org_name": "Example Name", @@ -12647,6 +13361,11 @@ "example": {}, "type": "object" }, + "oauth_client_name": { + "description": "Registered display name of the verified OAuth client, when available. Not a verified vendor identity.", + "example": "Example Name", + "type": "string" + }, "org": { "description": "ID of the current organization (`org_...`).", "example": "org_0aBcDeFgHiJkLmNoPqRsTu", @@ -12756,7 +13475,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -12775,12 +13496,24 @@ "$ref": "#/components/schemas/Acl", "description": "Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else." }, + "agent": { + "description": "ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, "created_at": { "description": "When the custom object was created (ISO 8601).", "example": "2024-01-01T00:00:00Z", "format": "date-time", "type": "string" }, + "expires_at": { + "description": "Absolute expiry time (RFC 3339). Null when no expiry is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "fields": { "description": "Map of field names to their current values as defined by the object's schema type.", "example": { @@ -12927,7 +13660,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -12978,13 +13713,15 @@ "type": "integer" }, "total_entries": { - "description": "Total number of custom objects matching the query across all pages.", + "description": "Total number of custom objects matching the query across all pages; null when include_count is false.", "example": 1, + "nullable": true, "type": "integer" }, "total_pages": { - "description": "Total number of pages available for the current query.", + "description": "Total number of pages available for the current query; null when include_count is false.", "example": 1, + "nullable": true, "type": "integer" } }, @@ -13345,9 +14082,13 @@ { "granted": true, "key": "string", + "limit": 1, "provided_by": [ "personal" ], + "quantity": 1, + "remaining": 1, + "used": 1, "value": true, "value_type": "boolean" } @@ -13454,9 +14195,13 @@ "example": { "granted": true, "key": "string", + "limit": 1, "provided_by": [ "personal" ], + "quantity": 1, + "remaining": 1, + "used": 1, "value": true, "value_type": "boolean" }, @@ -13471,6 +14216,12 @@ "example": "string", "type": "string" }, + "limit": { + "description": "Unexpired metered allowance, including exhausted grants; null for boolean entitlements.", + "example": 1, + "nullable": true, + "type": "integer" + }, "provided_by": { "description": "Privacy-safe principal kinds that contribute to the effective value.", "example": [ @@ -13485,6 +14236,24 @@ }, "type": "array" }, + "quantity": { + "description": "Effective purchased quantity from the recurring terms linked to this key (for example seats), summed across granting principals. `null` when the key is not granted or no granting plan links a term to it.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "remaining": { + "description": "Remaining metered allowance credits; null for boolean entitlements.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "used": { + "description": "Signed metered consumption in the allowance window; may be negative after reversals. Null for boolean entitlements.", + "example": 1, + "nullable": true, + "type": "integer" + }, "value": { "description": "Merged entitlement value, or `null` when the key is not granted.", "example": true, @@ -13494,7 +14263,8 @@ "value_type": { "description": "Catalog type used to interpret `value`.", "enum": [ - "boolean" + "boolean", + "metered" ], "example": "boolean", "type": "string" @@ -13519,6 +14289,7 @@ "event_names": [ "Example Name" ], + "filter": {}, "id": "string", "last_overflow_at": "2024-01-01T00:00:00Z", "leased_count": 1, @@ -13532,7 +14303,8 @@ "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", - "visibility_timeout_seconds": 1 + "visibility_timeout_seconds": 1, + "watch_key": "string" }, "properties": { "agent": { @@ -13565,6 +14337,10 @@ }, "type": "array" }, + "filter": { + "example": {}, + "type": "object" + }, "id": { "example": "string", "type": "string" @@ -13626,6 +14402,10 @@ "visibility_timeout_seconds": { "example": 1, "type": "integer" + }, + "watch_key": { + "example": "string", + "type": "string" } }, "required": [ @@ -13808,6 +14588,7 @@ "event_names": [ "Example Name" ], + "filter": {}, "id": "string", "last_overflow_at": "2024-01-01T00:00:00Z", "leased_count": 1, @@ -13821,7 +14602,8 @@ "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", - "visibility_timeout_seconds": 1 + "visibility_timeout_seconds": 1, + "watch_key": "string" } ], "page": 1, @@ -14937,26 +15719,51 @@ "type": "object" }, "KeyValueStorageEntry": { - "description": "A single key-value storage entry belonging to a user. Represents one key/value pair written to a user's isolated storage namespace within an app.", + "description": "An owner-scoped key/value pair within an app, optionally expiring.", "example": { + "agent": "string", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "key": "theme", + "org": "string", + "team": "string", "updated_at": "2024-01-01T00:00:00Z", "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "value": "dark" }, "properties": { + "agent": { + "description": "Agent owner, if any.", + "example": "string", + "type": "string" + }, "created_at": { "description": "When this storage entry was first created (ISO 8601). `null` if not yet persisted.", "example": "2024-01-01T00:00:00Z", "format": "date-time", "type": "string" }, + "expires_at": { + "description": "Expiry deadline, or null for entries without expiry.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, "key": { "description": "The string key used to store and look up this entry.", "example": "theme", "type": "string" }, + "org": { + "description": "Organization scope.", + "example": "string", + "type": "string" + }, + "team": { + "description": "Team owner, if any.", + "example": "string", + "type": "string" + }, "updated_at": { "description": "When this storage entry was last updated (ISO 8601). `null` if not yet persisted.", "example": "2024-01-01T00:00:00Z", @@ -14964,7 +15771,7 @@ "type": "string" }, "user": { - "description": "ID of the user who owns this storage entry (`usr_...`).", + "description": "ID of the user owner, or null for non-user ownership.", "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", "type": "string" }, @@ -14975,7 +15782,6 @@ } }, "required": [ - "user", "key", "value" ], @@ -14986,8 +15792,12 @@ "example": { "data": [ { + "agent": "string", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "key": "theme", + "org": "string", + "team": "string", "updated_at": "2024-01-01T00:00:00Z", "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "user_email": "user@example.com", @@ -15049,8 +15859,12 @@ "KeyValueStorageEntryWithUser": { "description": "A key-value storage entry enriched with owner information. Developer and server-to-server callers receive `user_email` and `user_name` populated; end-user (user-JWT) callers receive those fields as `null`.", "example": { + "agent": "string", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "key": "theme", + "org": "string", + "team": "string", "updated_at": "2024-01-01T00:00:00Z", "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "user_email": "user@example.com", @@ -15058,17 +15872,38 @@ "value": "dark" }, "properties": { + "agent": { + "description": "Agent owner, if any.", + "example": "string", + "type": "string" + }, "created_at": { "description": "When this storage entry was first created (ISO 8601).", "example": "2024-01-01T00:00:00Z", "format": "date-time", "type": "string" }, + "expires_at": { + "description": "Expiry deadline, or null for entries without expiry.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, "key": { "description": "The string key used to store and look up this entry.", "example": "theme", "type": "string" }, + "org": { + "description": "Organization scope.", + "example": "string", + "type": "string" + }, + "team": { + "description": "Team owner, if any.", + "example": "string", + "type": "string" + }, "updated_at": { "description": "When this storage entry was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -15076,7 +15911,7 @@ "type": "string" }, "user": { - "description": "ID of the user who owns this storage entry (`usr_...`).", + "description": "ID of the user owner, or null for non-user ownership.", "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", "type": "string" }, @@ -15097,7 +15932,6 @@ } }, "required": [ - "user", "key", "value", "created_at", @@ -15289,8 +16123,10 @@ "MediaVariant": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -15304,12 +16140,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -15322,6 +16166,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -15362,6 +16212,12 @@ }, "nullable": true }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -15374,6 +16230,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -15468,8 +16331,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -15483,8 +16348,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -15500,7 +16367,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "string", @@ -15604,8 +16471,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -15619,8 +16488,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -15636,7 +16507,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -15931,8 +16802,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -15946,8 +16819,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -15963,7 +16838,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -16209,7 +17084,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -16231,7 +17106,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -16696,6 +17571,32 @@ ], "type": "object" }, + "Origin": { + "description": "Origin references.", + "example": { + "routine_id": "string", + "session_id": "string", + "thread_id": "string" + }, + "properties": { + "routine_id": { + "example": "string", + "nullable": true, + "type": "string" + }, + "session_id": { + "example": "string", + "nullable": true, + "type": "string" + }, + "thread_id": { + "example": "string", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, "PaginatedReplies": { "description": "A paginated list of reply messages for a thread. The reply array is returned directly, not nested inside a `data` wrapper.", "example": { @@ -16778,8 +17679,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -16793,8 +17696,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -16810,7 +17715,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -16885,6 +17790,22 @@ ], "type": "object" }, + "Payload": { + "description": "Question request payload.", + "example": { + "question": "string" + }, + "properties": { + "question": { + "example": "string", + "type": "string" + } + }, + "required": [ + "question" + ], + "type": "object" + }, "PresetConfig": { "description": "Configuration for a preset routine handler. Controls the agent's behavior, session persistence, and model selection for a given routine or chain step.", "example": { @@ -17278,6 +18199,75 @@ ], "type": "object" }, + "Refusal": { + "description": "The model declined to judge this question.", + "example": { + "id": "string", + "type": "refusal" + }, + "properties": { + "id": { + "example": "string", + "type": "string" + }, + "type": { + "default": "refusal", + "enum": [ + "refusal" + ], + "example": "refusal", + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object" + }, + "Relay": { + "description": "Public relay authorization record.", + "example": { + "expires_at": "2024-01-01T00:00:00Z", + "id": "string" + }, + "properties": { + "expires_at": { + "description": "Relay expiry.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "Relay ID.", + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "expires_at" + ], + "type": "object" + }, + "Resolution": { + "description": "Answer or rejection reason; null before resolution.", + "example": { + "answer": "string", + "reason": "string" + }, + "properties": { + "answer": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + } + }, + "type": "object" + }, "RoutinePreset": { "description": "A named preset that defines the execution model and constraints for a routine. Presets are shared definitions; individual routines reference a preset by name.", "example": { @@ -20829,6 +21819,35 @@ ], "type": "object" }, + "Summary": { + "description": "Kind-provided summary.", + "example": { + "lines": [ + "string" + ], + "title": "Example Title" + }, + "properties": { + "lines": { + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "title": { + "example": "Example Title", + "type": "string" + } + }, + "required": [ + "title", + "lines" + ], + "type": "object" + }, "SystemAccessToken": { "description": "A long-lived API credential associated with a system account, used to authenticate server-to-server requests.", "example": { @@ -20967,6 +21986,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -20993,15 +22013,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -21021,7 +22045,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -21117,7 +22141,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -21182,7 +22211,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -21192,6 +22221,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -21216,13 +22251,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -21236,6 +22278,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -21243,7 +22297,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -21360,6 +22414,106 @@ ], "type": "object" }, + "TaskEnsureResult": { + "description": "The current source task and whether this request created it.", + "example": { + "created": true, + "task": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + }, + "properties": { + "created": { + "description": "Whether this request created the task.", + "example": true, + "type": "boolean" + }, + "task": { + "$ref": "#/components/schemas/Task", + "description": "The current task." + } + }, + "required": [ + "created", + "task" + ], + "type": "object" + }, "TaskExternalLink": { "description": "An indexed external object linked to a task.", "example": { @@ -21391,15 +22545,48 @@ ], "type": "object" }, + "TaskOwnershipJob": { + "description": "The background job accepted for a task ownership conversion.", + "example": { + "job_id": 1, + "projection_pending": true, + "status": "string" + }, + "properties": { + "job_id": { + "description": "Background job ID.", + "example": 1, + "type": "integer" + }, + "projection_pending": { + "description": "Completed transfers are still reaching task read models.", + "example": true, + "type": "boolean" + }, + "status": { + "description": "Current background job state.", + "example": "string", + "type": "string" + } + }, + "required": [ + "job_id", + "status" + ], + "type": "object" + }, "TaskSessionLease": { "description": "A task-session lease returned only to its matching holder.", "example": { "expires_at": "2024-01-01T00:00:00Z", "harness": "string", "lease_id": "string", + "lease_remaining_ms": 1, "renewed_at": "2024-01-01T00:00:00Z", "session_id": "string", - "session_name": "Example Name" + "session_name": "Example Name", + "task_aggregate_version": 1, + "task_status": "string" }, "properties": { "expires_at": { @@ -21418,6 +22605,11 @@ "example": "string", "type": "string" }, + "lease_remaining_ms": { + "description": "Milliseconds of lease left when the response was built, clamped at zero. A renewal made while more than half the lease remains appends no event and leaves the expiry unchanged, so clients derive their deadline from this value.", + "example": 1, + "type": "integer" + }, "renewed_at": { "description": "Server timestamp for the most recent claim, reclaim, or renewal.", "example": "2024-01-01T00:00:00Z", @@ -21433,6 +22625,16 @@ "description": "Display name supplied by the matching coding session.", "example": "Example Name", "type": "string" + }, + "task_aggregate_version": { + "description": "Task aggregate version including the claim, reclaim, or renewal event.", + "example": 1, + "type": "integer" + }, + "task_status": { + "description": "Native task status after the lease event.", + "example": "string", + "type": "string" } }, "required": [ @@ -21441,7 +22643,10 @@ "session_name", "harness", "expires_at", - "renewed_at" + "lease_remaining_ms", + "renewed_at", + "task_status", + "task_aggregate_version" ], "type": "object" }, @@ -22602,8 +23807,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -22617,8 +23824,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -22634,7 +23843,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -23242,8 +24451,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -23257,8 +24468,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -23274,7 +24487,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -23509,8 +24722,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -23524,8 +24739,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -27077,6 +28294,259 @@ "publishable_key", "bearer" ] + }, + "patch": { + "description": "Updates the computer's direct access grants. Requires permission to modify\nthe computer. Read grants allow retrieval and command execution; write grants\nalso allow management and deletion. Use acl.grants to replace grants, or\nacl.add/acl.remove to patch them. An empty acl.grants array revokes all direct\ngrants; omitting acl preserves them. Sharing does not add the computer to\nagent or thread lists.\n", + "operationId": "patch_api_v1_agent_computers__computer", + "parameters": [ + { + "description": "Computer ID (`cmp_...`).", + "example": "string", + "in": "path", + "name": "computer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + } + }, + "properties": { + "acl": { + "description": "Access control list replacement or add/remove operations.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Empty response body. Returns HTTP 204 on success." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Computer management permission required" + }, + "404": { + "description": "Computer not found" + }, + "422": { + "description": "Invalid grants" + } + }, + "summary": "Update a computer", + "x-auth": [ + "publishable_key", + "bearer" + ] } }, "/api/v1/agent_computers/{computer}/exec": { @@ -27845,6 +29315,283 @@ ] } }, + "/api/v1/agent_requests": { + "get": { + "description": "Lists requests visible under the context policy, newest first. Filters are combined. Pagination is not a snapshot; later pages continue after the last returned request.", + "operationId": "get_api_v1_agent_requests", + "parameters": [ + { + "description": "Filter by agent ID.", + "example": "string", + "in": "query", + "name": "agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "open, resolved, rejected, expired or cancelled.", + "example": "string", + "in": "query", + "name": "status", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter by registered kind.", + "example": "string", + "in": "query", + "name": "kind", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Page size, 1–100; default 25.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque next_cursor from a prior page.", + "example": "string", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentRequestListResponse" + } + } + }, + "description": "A page of visible requests." + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Invalid filter or cursor" + } + }, + "summary": "List agent requests", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/agent_requests/{agent_request}": { + "get": { + "description": "Uses the request context visibility and approver policy. Agents cannot resolve or reject requests.", + "operationId": "get_api_v1_agent_requests__agent_request", + "parameters": [ + { + "description": "Request ID (`arq_...`).", + "example": "string", + "in": "path", + "name": "agent_request", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentRequest" + } + } + }, + "description": "The request, without secret input." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Not an approver" + }, + "404": { + "description": "Request not found" + }, + "409": { + "description": "not_open or stale" + }, + "422": { + "description": "Input validation failed" + } + }, + "summary": "Show an agent request", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/agent_requests/{agent_request}/reject": { + "post": { + "description": "Uses the request context visibility and approver policy. Agents cannot resolve or reject requests.", + "operationId": "post_api_v1_agent_requests__agent_request_reject", + "parameters": [ + { + "description": "Request ID (`arq_...`).", + "example": "string", + "in": "path", + "name": "agent_request", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "reason": "string" + }, + "properties": { + "reason": { + "description": "Reason for rejecting the request.", + "example": "string", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentRequest" + } + } + }, + "description": "The request, without secret input." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Not an approver" + }, + "404": { + "description": "Request not found" + }, + "409": { + "description": "not_open or stale" + }, + "422": { + "description": "Input validation failed" + } + }, + "summary": "Reject an agent request", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/agent_requests/{agent_request}/resolve": { + "post": { + "description": "Uses the request context visibility and approver policy. Agents cannot resolve or reject requests.", + "operationId": "post_api_v1_agent_requests__agent_request_resolve", + "parameters": [ + { + "description": "Request ID (`arq_...`).", + "example": "string", + "in": "path", + "name": "agent_request", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "input": {} + }, + "properties": { + "input": { + "description": "Kind-specific input. Secrets must use secret_value.", + "example": {}, + "type": "object" + } + }, + "required": [ + "input" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentRequest" + } + } + }, + "description": "The request, without secret input." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Not an approver" + }, + "404": { + "description": "Request not found" + }, + "409": { + "description": "not_open or stale" + }, + "422": { + "description": "Input validation failed" + } + }, + "summary": "Resolve an agent request", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/agent_routine_runs": { "get": { "description": "Returns a paginated list of agent routine runs across all routines visible to\nthe authenticated app scope. Results are ordered by creation time descending\n(most recent first).\n\nUse the `agent` parameter to filter runs to one or more specific agents. Use\n`status` to narrow results to runs in a particular state. Pagination is\nbidirectional: supply `after_cursor` to page forward through newer runs or\n`before_cursor` to page backward through older runs.\n\nThis endpoint requires an app scope. Requests without a valid app credential\nreturn 403.\n", @@ -29638,6 +31385,73 @@ ] } }, + "/api/v1/agent_sessions/{agent_session}/events": { + "get": { + "description": "Returns what the coding harness (Claude Code, Codex) running an agent\nsession on a computer has done so far: messages, tool use, turn\ncompletions, in order. Events arrive while the harness runs; page with\n`after`. The session stream endpoint pushes the same events live.\n", + "operationId": "get_api_v1_agent_sessions__agent_session_events", + "parameters": [ + { + "description": "Agent session ID (`ase_...`).", + "example": "string", + "in": "path", + "name": "agent_session", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Return events after this event ID (`cse_...`).", + "example": "string", + "in": "query", + "name": "after", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum events to return (default 200, max 1000).", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AgentSessionEvent" + }, + "type": "array" + } + } + }, + "description": "Events in the order they happened." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden - app scope required" + }, + "404": { + "description": "Session not found" + } + }, + "summary": "List an agent session's harness events", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/agent_sessions/{agent_session}/message": { "post": { "description": "Appends a message to the inbox of the specified agent session. The agent\nreads inbox messages at the start of each turn; sending a message to a\n`\"waiting\"` session signals it to resume execution.\n\nUse `role` to identify the sender type. The default role is `\"user\"`.\nArbitrary key-value metadata may be attached to the message for tracking\nor display purposes.\n\nRequires an app-scoped API key. The session must belong to an agent owned\nby the authenticated app.\n", @@ -29726,7 +31540,7 @@ }, "/api/v1/agent_sessions/{agent_session}/stream": { "get": { - "description": "Opens a Server-Sent Events connection that emits a `session_update` event\nwhenever the agent session's status changes, replaying the current status on\nconnect and closing on a terminal status (`completed`, `failed`, `cancelled`).\n", + "description": "Opens a Server-Sent Events connection that emits a `session_update` event\nwhenever the agent session's status changes, replaying the current status on\nconnect and closing on a terminal status (`completed`, `failed`, `cancelled`).\nSessions that run a coding harness on a computer also emit a `session_event`\nfor each harness event (messages, tool use, turn completion) as it happens;\nlist earlier ones with the session events endpoint.\n", "operationId": "get_api_v1_agent_sessions__agent_session_stream", "parameters": [ { @@ -29745,7 +31559,14 @@ "content": { "text/event-stream": { "schema": { - "$ref": "#/components/schemas/AgentSession" + "oneOf": [ + { + "$ref": "#/components/schemas/AgentSession" + }, + { + "$ref": "#/components/schemas/AgentSessionEvent" + } + ] } } }, @@ -29762,6 +31583,9 @@ ], "x-sdk-streaming": { "events": { + "session_event": { + "$ref": "#/components/schemas/AgentSessionEvent" + }, "session_update": { "$ref": "#/components/schemas/AgentSession" } @@ -31920,6 +33744,34 @@ "application/json": { "schema": { "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "config": {}, "lookup_key": "string", "metadata": { @@ -31930,6 +33782,179 @@ "region": "string" }, "properties": { + "acl": { + "description": "Initial access control list.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "config": { "description": "Provider-specific configuration for the computer. Supported keys vary by provider. A top-level `provider` takes precedence over `config.provider`.", "example": {}, @@ -31953,7 +33978,7 @@ "type": "string" }, "provider": { - "description": "Compute backend for the computer: `\"sprites\"` (Fly Sprites, the default) or `\"vercel\"` (Vercel Sandbox). Folded into `config.provider`.", + "description": "Compute backend for the computer: `\"sprites\"` (Fly Sprites, the default), `\"vercel\"` (Vercel Sandbox) or `\"e2b\"` (E2B). Folded into `config.provider`.", "example": "string", "type": "string" }, @@ -33968,6 +35993,8 @@ "key": "value" }, "raw_content": {}, + "structured_data": "string", + "structured_data_summary": "string", "type": "gmail" }, "properties": { @@ -34013,6 +36040,18 @@ "example": {}, "type": "object" }, + "structured_data": { + "description": "Public ID (`sda_...`) of the structured-data record attached to the source message. Present only for such items on source search.", + "example": "string", + "nullable": true, + "type": "string" + }, + "structured_data_summary": { + "description": "Search summary of that record, computed when the request is served. When present, `content` is the indexed text and may be stale.", + "example": "string", + "nullable": true, + "type": "string" + }, "type": { "description": "Type identifier of the parent knowledge source (e.g. `\"gmail\"`, `\"github_activity\"`). Returns `\"unknown\"` when the source association is not loaded.", "example": "gmail", @@ -34124,6 +36163,81 @@ ] } }, + "/api/v1/agents/{agent}/self_solution": { + "get": { + "description": "Lazily snapshots an authorized agent into its single private, versioned Solution.", + "operationId": "get_api_v1_agents__agent_self_solution", + "parameters": [ + { + "description": "Agent ID or lookup key.", + "example": "string", + "in": "path", + "name": "agent", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Solution ID, solution version and stable lookup key.", + "example": { + "lookup_key": "string", + "solution_id": "string", + "solution_version": "string" + }, + "properties": { + "lookup_key": { + "example": "string", + "type": "string" + }, + "solution_id": { + "example": "string", + "type": "string" + }, + "solution_version": { + "example": "string", + "type": "string" + } + }, + "required": [ + "solution_id", + "solution_version", + "lookup_key" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Agent not found" + }, + "409": { + "description": "Self-solution conflict" + }, + "422": { + "description": "Unexportable dependency" + }, + "500": { + "description": "Self-solution bootstrap failed" + } + }, + "summary": "Ensure an agent's private self-solution", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/agents/{agent}/threads": { "post": { "description": "Creates a new thread owned by the specified agent. The thread is scoped to the\nagent's identity and is immediately available for messaging.\n\nThe authenticated caller must have access to the agent's parent app. If your\nAPI key is scoped to a specific app, pass that app's ID via the `app` parameter.\nAttempting to create a thread for an agent you cannot access returns 404.\n\nBy default the platform may send an automatic welcome message into the new\nthread. Pass `skip_welcome_message: true` to suppress this behavior.\n", @@ -35874,7 +37988,7 @@ }, "/api/v1/ai/evaluation/evaluations": { "post": { - "description": "Sends a shared `state` and a list of typed questions to an evaluation\nmodel and returns typed answers. This is not chat completion and not\nJSON-schema structured output: there is no generated text.\n\nEach question is a `boolean`, `choice`, or `score` object with its own\n`id`. Answers come back as the same three typed objects, each carrying\nthat `id`.\n\nThe authenticated app must have the `llm_calls` entitlement enabled on its\nplan. Requests that exceed the plan quota are rejected with `402`. Token\nusage is recorded against the authenticated app and organization.\n", + "description": "Sends a shared `state` and a list of typed questions to an evaluation\nmodel and returns typed answers. This is not chat completion and not\nJSON-schema structured output: there is no generated text.\n\nEach question is a `boolean`, `choice`, or `score` object with its own\n`id`. Answers carry that `id`, including a `refusal` answer containing\nonly `id` and `type` when the model declines to judge a question.\n\nImage input, labelled score levels, and refusal answers are model-dependent.\nImages are separate from `state` and use the chat completion image-part format.\nModels without image support reject image input with `400 unsupported_capability`.\n\nThe authenticated app must have the `llm_calls` entitlement enabled on its\nplan. Requests that exceed the plan quota are rejected with `402`. Token\nusage is recorded against the authenticated app and organization.\n", "operationId": "post_api_v1_ai_evaluation_evaluations", "parameters": [], "requestBody": { @@ -35882,11 +37996,68 @@ "application/json": { "schema": { "example": { + "images": [ + { + "image_data": "string", + "image_type": "string", + "image_url": "https://example.com", + "type": "image" + } + ], "model": "string", "questions": [], "session_id": "string" }, "properties": { + "images": { + "description": "Optional image content parts. Omitted or empty means no images. Requires model image support.", + "example": [ + { + "image_data": "string", + "image_type": "string", + "image_url": "https://example.com", + "type": "image" + } + ], + "items": { + "description": "An image content part in the chat completion input format.", + "example": { + "image_data": "string", + "image_type": "string", + "image_url": "https://example.com", + "type": "image" + }, + "properties": { + "image_data": { + "description": "Base64 image bytes; requires image_type.", + "example": "string", + "type": "string" + }, + "image_type": { + "description": "Image MIME type, such as image/png.", + "example": "string", + "type": "string" + }, + "image_url": { + "description": "Image URL; mutually exclusive with image_data.", + "example": "https://example.com", + "type": "string" + }, + "type": { + "enum": [ + "image" + ], + "example": "image", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "model": { "description": "Evaluation model identifier. Omit to use the platform default.", "example": "string", @@ -36003,22 +38174,16 @@ { "description": "Place the state along ordered `criteria` levels.", "example": { - "criteria": [ - "string" - ], + "criteria": [], "id": "frustration", "instructions": "string", "type": "score" }, "properties": { "criteria": { - "description": "At least two ordered level descriptions.", - "example": [ - "string" - ], - "items": { - "type": "string" - }, + "description": "At least two ordered levels, each a string or an object with string label and description.", + "example": [], + "items": {}, "type": "array" }, "id": { @@ -36080,7 +38245,7 @@ } } }, - "description": "Typed answers as a list of boolean, choice, or score objects, plus model and usage." + "description": "Typed boolean, choice, score, or refusal answers, plus model and usage." }, "400": { "description": "Bad request" @@ -36816,6 +38981,256 @@ ] } }, + "/api/v1/ai/usage": { + "get": { + "description": "Returns the authenticated user's own LLM usage aggregated over a bounded\ntime window. Each row carries request and token sums plus the customer-billed\ncost for the group. Cost is derived from the customer's active billing plan;\nprovider-reported cost is never returned.\n\nPass `group_by` one or more times to choose the grouping dimensions\n(`task`, `model`, `agent_kind`, `agent_id`, `hour`). The window (`since` to\n`until`) may span at most 31 days. Results are capped at `limit` groups.\n", + "operationId": "get_api_v1_ai_usage", + "parameters": [ + { + "description": "Window start as an ISO 8601 timestamp (inclusive). Defaults to 7 days before `until`.", + "example": "string", + "in": "query", + "name": "since", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Window end as an ISO 8601 timestamp (inclusive). Defaults to now.", + "example": "string", + "in": "query", + "name": "until", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Grouping dimensions. Repeat for multiple dimensions (e.g. `group_by[]=task&group_by[]=model`). Defaults to `model`.", + "example": [ + "task" + ], + "in": "query", + "name": "group_by", + "required": false, + "schema": { + "items": { + "enum": [ + "task", + "model", + "agent_kind", + "agent_id", + "hour" + ], + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Restrict results to these caller task IDs. Omit to include every task.", + "example": [ + "string" + ], + "in": "query", + "name": "task", + "required": false, + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Maximum number of groups to return. Defaults to 50; maximum is 200.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Aggregated LLM usage for the authenticated user.", + "example": { + "data": [ + { + "agent_id": "string", + "agent_kind": "string", + "billed_usd": 1.0, + "cache_read_tokens": 1, + "cache_write_tokens": 1, + "completion_tokens": 1, + "hour": "string", + "model": "string", + "priced": true, + "prompt_tokens": 1, + "requests": 1, + "task": "string" + } + ], + "since": "string", + "until": "string" + }, + "properties": { + "data": { + "description": "One row per group with request/token sums, the group key fields, `billed_usd`, and `priced`.", + "example": [ + { + "agent_id": "string", + "agent_kind": "string", + "billed_usd": 1.0, + "cache_read_tokens": 1, + "cache_write_tokens": 1, + "completion_tokens": 1, + "hour": "string", + "model": "string", + "priced": true, + "prompt_tokens": 1, + "requests": 1, + "task": "string" + } + ], + "items": { + "description": "One aggregated LLM usage group: request/token sums plus billed cost and the group key fields.", + "example": { + "agent_id": "string", + "agent_kind": "string", + "billed_usd": 1.0, + "cache_read_tokens": 1, + "cache_write_tokens": 1, + "completion_tokens": 1, + "hour": "string", + "model": "string", + "priced": true, + "prompt_tokens": 1, + "requests": 1, + "task": "string" + }, + "properties": { + "agent_id": { + "description": "Agent ID for this group. Present only when grouped by `agent_id`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "agent_kind": { + "description": "Agent kind for this group. Present only when grouped by `agent_kind`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "billed_usd": { + "description": "Customer-billed cost for this group in USD. `null` when `priced` is `false`.", + "example": 1.0, + "nullable": true, + "type": "number" + }, + "cache_read_tokens": { + "description": "Summed cache-read tokens in this group.", + "example": 1, + "type": "integer" + }, + "cache_write_tokens": { + "description": "Summed cache-write tokens in this group.", + "example": 1, + "type": "integer" + }, + "completion_tokens": { + "description": "Summed completion (output) tokens in this group.", + "example": 1, + "type": "integer" + }, + "hour": { + "description": "Hour bucket (ISO 8601) for this group. Present only when grouped by `hour`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "model": { + "description": "Model identifier for this group. Present only when grouped by `model`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "priced": { + "description": "`true` when `billed_usd` was priced from the customer's active billing plan; `false` when the group could not be priced.", + "example": true, + "type": "boolean" + }, + "prompt_tokens": { + "description": "Summed prompt (input) tokens in this group.", + "example": 1, + "type": "integer" + }, + "requests": { + "description": "Number of LLM calls in this group.", + "example": 1, + "type": "integer" + }, + "task": { + "description": "Caller task ID for this group. Present only when grouped by `task`.", + "example": "string", + "nullable": true, + "type": "string" + } + }, + "required": [ + "requests", + "prompt_tokens", + "completion_tokens", + "cache_read_tokens", + "cache_write_tokens", + "priced" + ], + "type": "object" + }, + "type": "array" + }, + "since": { + "description": "Effective window start (ISO 8601).", + "example": "string", + "type": "string" + }, + "until": { + "description": "Effective window end (ISO 8601).", + "example": "string", + "type": "string" + } + }, + "required": [ + "data", + "since", + "until" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Read the authenticated user's LLM usage", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/artifacts": { "post": { "description": "Creates a new artifact and stores its file content. A file payload is required;\nsupply it via the `file.data` (Base64-encoded by default), `file.filename`, and\n`file.mime_type` fields. The artifact is scoped to the owner resolved from the\nrequest body — either a team, a user, or an explicit system owner. When no\nowner is supplied and the viewer is an authenticated user, the artifact\ndefaults to that user.\n\nFor company-readable snapshots, pass `system: true` and `org`. Organization\nmembers can read these artifacts; organization admins and system viewers can\ncreate them. Scripts may supply text directly using `file.encoding: \"utf8\"`.\n\nOptionally associate the artifact with an existing thread or agent by passing\n`thread` or `agent`. Legacy requests that wrap fields in an `artifact` object\nare still accepted.\n", @@ -36826,6 +39241,34 @@ "application/json": { "schema": { "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "string", "artifact": {}, "description": "An example description.", @@ -36839,13 +39282,185 @@ "idempotency_key": "string", "name": "Example Name", "org": "string", - "permissions": {}, "system": true, "team": "string", "thread": "string", "user": "string" }, "properties": { + "acl": { + "description": "Initial access control list. Grants add read or write access beyond the owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "agent": { "description": "Agent ID (`agt_...`) to associate with the artifact.", "example": "string", @@ -36921,11 +39536,6 @@ "example": "string", "type": "string" }, - "permissions": { - "description": "Optional artifact permissions map.", - "example": {}, - "type": "object" - }, "system": { "description": "When true, create a system-owned artifact. Mutually exclusive with `team` and `user`.", "example": true, @@ -37079,6 +39689,34 @@ "application/json": { "schema": { "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "description": "An example description.", "file": { "data": "string", @@ -37094,6 +39732,179 @@ "name": "Example Name" }, "properties": { + "acl": { + "description": "Access control list replacement (`grants`) or add/remove operations. Omit to leave grants unchanged. ACL-only updates do not increment the content version.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "description": { "description": "New description for the artifact. Omit to leave the existing description unchanged.", "example": "An example description.", @@ -40820,6 +43631,36 @@ "type": "string" } }, + { + "description": "JSON containment predicates on fields, applied before pagination and counting.", + "example": {}, + "in": "query", + "name": "field_filters", + "required": false, + "schema": { + "type": "object" + } + }, + { + "description": "Only system-owned objects (no user, team, agent or thread owner). Visibility is unchanged.", + "example": true, + "in": "query", + "name": "system_only", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Top-level JSON field for descending text order, with ID tie-breaker. Full-text relevance takes precedence.", + "example": "string", + "in": "query", + "name": "order_by_field", + "required": false, + "schema": { + "type": "string" + } + }, { "description": "Exact `row_key` value to match. When supplied, only objects with this partition key are returned.", "example": "string", @@ -40845,6 +43686,21 @@ "type": "array" } }, + { + "description": "JSON-encoded array of up to 100 objects with required string row_key and optional string-array sort_key. Missing or empty sort_key selects the whole partition. Mutually exclusive with row_key and sort_key. Pagination applies globally across all matched partitions.", + "example": [ + {} + ], + "in": "query", + "name": "keys", + "required": false, + "schema": { + "items": { + "type": "object" + }, + "type": "array" + } + }, { "description": "One or more team IDs (`team_...`) to filter by owning team. Returns objects owned by any of the supplied teams.", "example": [ @@ -40935,6 +43791,17 @@ "type": "integer" } }, + { + "description": "Include only objects updated at or after this ISO 8601 timestamp. Applies before pagination and counting.", + "example": "2024-01-01T00:00:00Z", + "in": "query", + "name": "updated_since", + "required": false, + "schema": { + "format": "date-time", + "type": "string" + } + }, { "description": "Number of objects per page. Defaults to `25`; maximum is `100`.", "example": 1, @@ -40944,6 +43811,16 @@ "schema": { "type": "integer" } + }, + { + "description": "Include total_entries and total_pages (default true). When false, totals are null, has_next uses one extra row, and pages beyond the end return an empty list without clamping. The page offset must fit PostgreSQL's signed 64-bit integer range.", + "example": true, + "in": "query", + "name": "include_count", + "required": false, + "schema": { + "type": "boolean" + } } ], "responses": { @@ -40970,7 +43847,11 @@ "summary": "List custom objects", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "objects:read" ] }, "post": { @@ -41012,6 +43893,7 @@ }, "agent": "string", "config": "string", + "expires_at": "2024-01-01T00:00:00Z", "fields": {}, "org": "string", "schema_key": "string", @@ -41205,6 +44087,13 @@ "example": "string", "type": "string" }, + "expires_at": { + "description": "Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "fields": { "description": "Key-value map of field values for the new object. Must conform to the schema's field definitions. Omit to create an object with all fields at their default or null values.", "example": {}, @@ -41280,6 +44169,497 @@ } }, "summary": "Create a custom object", + "x-auth": [ + "publishable_key", + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "objects:write" + ] + } + }, + "/api/v1/custom_objects/patch": { + "post": { + "description": "Applies numeric patch ops (`increment`, `max`, `min`, `set_if_newer` with\ndotted `path`s) atomically to a custom object and returns the updated\nobject along with per-op results. The authenticated viewer must have\npermission to modify the object.\n\nAddress the row in one of two ways: by `object` ID (`cobj_...`), or by\n`schema` + `row_key` (with optional `sort_key` for schemas that declare\none). The keyed form resolves the row for the same owner the create\ncontract would use and, when no row exists yet, creates it from\n`create_with` merged over the key fields before applying the ops — so an\naggregator's first call creates the counter and later calls increment it.\nA racing create resolves to the same row instead of a duplicate.\n\nPass `idempotency_key` to make retries safe: a retry with the same key and\nops replays the stored result without re-applying the write (survives\nserver restarts); reusing a key with different ops returns 409.\n", + "operationId": "post_api_v1_custom_objects_patch", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "create_with": {}, + "idempotency_key": "string", + "object": "string", + "ops": [ + {} + ], + "row_key": "string", + "schema": "string", + "sort_key": [ + "string" + ], + "team": "string", + "user": "string" + }, + "properties": { + "agent": { + "description": "Agent user ID owning the keyed row. At most one owner selector may be present.", + "example": "string", + "type": "string" + }, + "create_with": { + "description": "Field values used to create the row when the keyed form finds no existing row. Merged over the key fields before validation; ignored by the `object` form.", + "example": {}, + "type": "object" + }, + "idempotency_key": { + "description": "Client-supplied deduplication key for this patch. A retry with the same key and the same ops replays the stored result without re-applying the write (survives server restarts); reusing a key with different ops returns 409.", + "example": "string", + "type": "string" + }, + "object": { + "description": "Custom object ID (`cobj_...`) to patch. Exactly one of `object` or `schema` + `row_key` is required.", + "example": "string", + "type": "string" + }, + "ops": { + "description": "Numeric patch ops to apply in order (e.g. `[%{\"op\" => \"increment\", \"path\" => \"count\", \"by\" => 1}]`). Supported ops: `increment` (`by`), `max`/`min` (`value`), `set_if_newer` (`value`, `fence_path`, `fence`).", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "row_key": { + "description": "Row key naming the row within `schema` (e.g. a counter name). Requires `schema`.", + "example": "string", + "type": "string" + }, + "schema": { + "description": "Schema type identifier (`lookup_key`) for the keyed form. Requires `row_key`.", + "example": "string", + "type": "string" + }, + "sort_key": { + "description": "Sort-key segments partitioning rows within the `row_key` bucket. Only meaningful for schemas that declare a `sort_key`.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "team": { + "description": "Team ID (`tem_...`) owning the keyed row. Defaults to the viewer's owner like the create contract.", + "example": "string", + "type": "string" + }, + "user": { + "description": "User ID (`usr_...`) owning the keyed row. At most one owner selector may be present.", + "example": "string", + "type": "string" + } + }, + "required": [ + "ops" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "The patched custom object, its version, and per-op results.", + "example": { + "data": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", + "fields": { + "key": "value" + }, + "id": "cobj_0aBcDeFgHiJkLmNoPqRsTu", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "row_key": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "schema_type": "contact", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "version": 1 + }, + "meta": {} + }, + "properties": { + "data": { + "description": "The custom object after the patch has been applied.", + "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", + "fields": { + "key": "value" + }, + "id": "cobj_0aBcDeFgHiJkLmNoPqRsTu", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "row_key": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "schema_type": "contact", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "version": 1 + }, + "properties": { + "acl": { + "description": "Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "agent": { + "description": "ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "created_at": { + "description": "When the custom object was created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "Absolute expiry time (RFC 3339). Null when no expiry is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "fields": { + "description": "Map of field names to their current values as defined by the object's schema type.", + "example": { + "key": "value" + }, + "type": "object" + }, + "id": { + "description": "Unique identifier for the custom object (`cobj_...`).", + "example": "cobj_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "org": { + "description": "ID of the organization this object belongs to (`org_...`).", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "row_key": { + "description": "An optional stable key used to identify this object by a caller-controlled string rather than its generated ID. `null` if not set.", + "example": "string", + "type": "string" + }, + "sandbox": { + "description": "ID of the sandbox environment this object is scoped to (`dsb_...`). `null` for production objects.", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "schema_type": { + "description": "The lookup key of the schema type that defines this object's field structure. `null` if the schema type has not been set.", + "example": "contact", + "type": "string" + }, + "team": { + "description": "ID of the team that owns this object (`tem_...`). `null` if the object is not team-scoped.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "updated_at": { + "description": "When the custom object was last modified (ISO 8601). `null` if the object has never been updated after creation.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user that owns this object (`usr_...`). `null` if the object is not user-scoped.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "version": { + "description": "Optimistic concurrency version of the object. Increments with each successful update; pass this value in write operations to detect conflicting changes.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "meta": { + "description": "Version metadata and per-op patch results.", + "example": {}, + "type": "object" + } + }, + "required": [ + "data" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict - row already exists at this key or idempotency key reused" + }, + "422": { + "description": "Validation failed" + } + }, + "summary": "Apply numeric patch ops to a custom object", "x-auth": [ "publishable_key", "bearer" @@ -41393,7 +44773,11 @@ "summary": "Retrieve a custom object", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "objects:read" ] }, "put": { @@ -41445,8 +44829,10 @@ ] }, "expected_version": 1, + "expires_at": "2024-01-01T00:00:00Z", "field_ops": {}, "fields": {}, + "idempotency_key": "string", "type": "string" }, "properties": { @@ -41628,6 +45014,13 @@ "example": 1, "type": "integer" }, + "expires_at": { + "description": "Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "field_ops": { "description": "Granular array operations to apply per field (e.g. append, prepend, remove). A field must not appear in both `fields` and `field_ops`.", "example": {}, @@ -41638,6 +45031,11 @@ "example": {}, "type": "object" }, + "idempotency_key": { + "description": "Client-supplied deduplication key for this write. A retry with the same key and the same body replays the stored result without re-applying the write (survives server restarts); reusing a key with a different body returns 409.", + "example": "string", + "type": "string" + }, "type": { "description": "Schema type identifier (`lookup_key`) of the object. Optional; used for routing context only.", "example": "string", @@ -41686,7 +45084,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -41734,7 +45134,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -41922,12 +45324,24 @@ }, "type": "object" }, + "agent": { + "description": "ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, "created_at": { "description": "When the custom object was created (ISO 8601).", "example": "2024-01-01T00:00:00Z", "format": "date-time", "type": "string" }, + "expires_at": { + "description": "Absolute expiry time (RFC 3339). Null when no expiry is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "fields": { "description": "Map of field names to their current values as defined by the object's schema type.", "example": { @@ -42021,7 +45435,11 @@ "summary": "Update a custom object", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "objects:write" ] } }, @@ -42147,11 +45565,18 @@ "event_names": [ "Example Name" ], + "filter": {}, "max_pending_events": 1, "name": "Example Name", + "previous_filter": {}, + "previous_filters": [ + {} + ], + "previous_watch_key": "string", "retention_seconds": 1, "status": "active", - "visibility_timeout_seconds": 1 + "visibility_timeout_seconds": 1, + "watch_key": "string" }, "properties": { "event_names": { @@ -42164,6 +45589,11 @@ }, "type": "array" }, + "filter": { + "description": "Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events.", + "example": {}, + "type": "object" + }, "max_pending_events": { "description": "Queue cap. Defaults to 100; maximum 1000.", "example": 1, @@ -42174,6 +45604,26 @@ "example": "Example Name", "type": "string" }, + "previous_filter": { + "description": "Expected legacy filter for atomic watch-key adoption.", + "example": {}, + "type": "object" + }, + "previous_filters": { + "description": "Additional exact historical filters accepted for atomic watch-key adoption.", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "previous_watch_key": { + "description": "Optionally adopt an existing key in the exact same owner and residence when the destination is absent and its filter exactly matches previous_filter, one of previous_filters, or filter. Preserves queue identity and deliveries; mismatches reject the request without changing either queue.", + "example": "string", + "type": "string" + }, "retention_seconds": { "description": "Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only.", "example": 1, @@ -42192,6 +45642,11 @@ "description": "Default claim lease in seconds. Defaults to 300.", "example": 1, "type": "integer" + }, + "watch_key": { + "description": "Optional durable identity within the caller owner and residence. Repeated creates replace routes and filter on that queue; omission creates a new queue regardless of name.", + "example": "string", + "type": "string" } }, "required": [ @@ -42219,7 +45674,7 @@ "description": "Unauthorized" }, "422": { - "description": "Invalid parameters; Validation failed" + "description": "Invalid parameters; Filter clauses must be bounded string lists over allowlisted dims; Filter is only valid when every event name is a custom (script-emitted) event type; Validation failed" } }, "summary": "Create a domain-event subscription", @@ -42324,6 +45779,7 @@ "event_names": [ "Example Name" ], + "filter": {}, "max_pending_events": 1, "name": "Example Name", "retention_seconds": 1, @@ -42340,6 +45796,11 @@ }, "type": "array" }, + "filter": { + "description": "Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events.", + "example": {}, + "type": "object" + }, "max_pending_events": { "example": 1, "type": "integer" @@ -42390,7 +45851,7 @@ "description": "Event subscription not found" }, "422": { - "description": "Invalid parameters; Validation failed" + "description": "Invalid parameters; Filter clauses must be bounded string lists over allowlisted dims; Filter is only valid when every event name is a custom (script-emitted) event type; Validation failed" } }, "summary": "Update a domain-event subscription", @@ -43553,7 +47014,7 @@ }, "/api/v1/files": { "post": { - "description": "Creates a new file from base64-encoded content and returns the resulting file object,\nincluding a signed download URL. Use this endpoint to store images, documents, or\nother binary assets that can then be referenced by agents, teams, or users.\n\nThe JSON body uses Platform's default 8,000,000-byte parser budget; the\nArchDev Platform proxy has a separate 32 MiB request cap. Base64 requires\n`4 * ceil(binary_bytes / 3)` bytes, plus the UTF-8 JSON envelope (including\nfilename, owner IDs, escaping and other fields). For an envelope of E bytes,\nbudget at most `3 * floor((8,000,000 - E) / 4)` binary bytes: less than\n6,000,000 bytes (about 5.72 MiB). Do not rely on parser chunk overshoot.\nOversized JSON requests are rejected with HTTP 413 before upload processing.\n\nApp scope is derived from the authenticated viewer's bearer token or publishable key.\nYou may optionally associate the file with an organization, team, user, or agent by\npassing the corresponding ID. If no owner is specified and the viewer is a user, the\nfile is automatically attributed to that user.\n\nPass `share: true` to additionally mint a stable public URL for the file\n(returned as `share_url`), fetchable by anyone without authentication — for\nexample to embed an uploaded image in a GitHub PR body or other external\nmarkdown. The URL does not expire. Sharing is revoked by setting\n`share: false` on `PATCH /api/v1/files/:file` with the same credential\n(or `archastro update file --unshare`); re-enabling sharing\nreactivates previously issued URLs. Only image content types can be\nshared.\n\nReturns `422` when the `data` field is not valid base64, the changeset is\ninvalid, or `share` is requested for a non-image content type.\nReturns `403` when the request lacks the required app scope.\n", + "description": "Creates a new file from base64-encoded content and returns the resulting file object,\nincluding a signed download URL. Use this endpoint to store images, documents, or\nother binary assets that can then be referenced by agents, teams, or users.\n\nThe JSON body uses Platform's default 8,000,000-byte parser budget; the\nan upstream proxy in front of Platform may have a separate 32 MiB request cap. Base64 requires\n`4 * ceil(binary_bytes / 3)` bytes, plus the UTF-8 JSON envelope (including\nfilename, owner IDs, escaping and other fields). For an envelope of E bytes,\nbudget at most `3 * floor((8,000,000 - E) / 4)` binary bytes: less than\n6,000,000 bytes (about 5.72 MiB). Do not rely on parser chunk overshoot.\nOversized JSON requests are rejected with HTTP 413 before upload processing.\n\nApp scope is derived from the authenticated viewer's bearer token or publishable key.\nYou may optionally associate the file with an organization, team, user, or agent by\npassing the corresponding ID. If no owner is specified and the viewer is a user, the\nfile is automatically attributed to that user.\n\nPass `share: true` to additionally mint a stable public URL for the file\n(returned as `share_url`), fetchable by anyone without authentication — for\nexample to embed an uploaded image in a GitHub PR body or other external\nmarkdown. The URL does not expire. Sharing is revoked by setting\n`share: false` on `PATCH /api/v1/files/:file` with the same credential\n(or `archastro update file --unshare`); re-enabling sharing\nreactivates previously issued URLs. Only image content types can be\nshared.\n\nReturns `422` when the `data` field is not valid base64, the changeset is\ninvalid, or `share` is requested for a non-image content type.\nReturns `403` when the request lacks the required app scope.\n", "operationId": "post_api_v1_files", "parameters": [], "requestBody": { @@ -43937,11 +47398,11 @@ }, "/api/v1/files/{file}/avatar": { "get": { - "description": "Returns the raw image bytes for an agent's profile picture identified by `file`.\nThis endpoint is designed for integration partners (such as Slack) that fetch\navatar URLs via plain GET requests without bearer token support. Authorization\nis performed via a short, stable capability `token` rather than an HTTP header.\n\nThe `token` is an HMAC-based capability tied to the file ID. It does not expire,\nbut it is invalidated when the agent's profile picture is replaced or the agent is\ndeleted — shared caches may continue serving the old image until the\n`Cache-Control` max-age of one hour elapses. The endpoint never redirects to\na signed storage URL; bytes are served inline so behavior is consistent across\nstorage backends.\n\nAll failure modes — invalid file ID, invalid token, file not currently referenced\nas an agent avatar — return a uniform `404` to avoid acting as an existence oracle.\n", + "description": "Returns the raw image bytes for an agent's or user's profile picture identified\nby `file`. Chat messages and integration partners (such as Slack) fetch avatars\nthrough it with plain GET requests, without bearer tokens. Authorization is\nperformed via a short, stable capability `token` rather than an HTTP header.\n\nThe `token` is an HMAC-based capability tied to the file ID. It does not expire,\nbut it is invalidated when the profile picture is replaced or its agent or user\nis deleted — shared caches may continue serving the old image until the\n`Cache-Control` max-age of one hour elapses. The endpoint never redirects to\na signed storage URL; bytes are served inline so behavior is consistent across\nstorage backends.\n\nAll failure modes — invalid file ID, invalid token, file not currently referenced\nas an agent avatar — return a uniform `404` to avoid acting as an existence oracle.\n", "operationId": "get_api_v1_files__file_avatar", "parameters": [ { - "description": "File ID of the agent's profile picture (`fil_...`). Must be currently set as an agent's profile picture within the same app.", + "description": "File ID of the profile picture (`fil_...`). Must be currently set as an agent's or user's profile picture within the same app.", "example": "string", "in": "path", "name": "file", @@ -43971,13 +47432,64 @@ } } }, - "description": "Raw image bytes of the agent avatar, served with the file's original content type." + "description": "Raw image bytes of the avatar, served with the file's original content type." }, "404": { "description": "Not found" } }, - "summary": "Fetch an agent avatar image" + "summary": "Fetch an agent or user avatar image" + } + }, + "/api/v1/files/{file}/download": { + "get": { + "description": "Returns the raw binary content of a file the authenticated viewer may see.\nThe response `Content-Type` header reflects the file's stored MIME type and\n`Content-Disposition` names the file as an attachment, so a browser can hand\nthe body to the download manager directly.\n\nUnlike `Show` (which mints a storage URL the browser must resolve itself),\nthis endpoint serves bytes inline through the authenticated API mount, so it\nbehaves identically across storage backends: local files are not exposed as\nunauthenticated static paths, and signed GCS URLs never cross the browser\nboundary. A file that exists but is not visible to the current viewer\nreturns `404` rather than `403` to avoid revealing the file's existence.\n\nByte downloads are limited to 32 MiB (33,554,432 bytes). The storage provider\nenforces this limit on actual content, independently of stored size metadata.\nLarger content returns HTTP `413` with error code `content_too_large`.\n", + "operationId": "get_api_v1_files__file_download", + "parameters": [ + { + "description": "File ID (`fil_...`) of the file to download.", + "example": "string", + "in": "path", + "name": "file", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "*/*": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Raw binary content of the file, served with the file's stored MIME type." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden - app scope required" + }, + "404": { + "description": "File not found" + }, + "413": { + "description": "File content exceeds the 32 MiB download limit" + }, + "502": { + "description": "File content temporarily unavailable" + } + }, + "summary": "Download a file's bytes", + "x-auth": [ + "publishable_key", + "bearer" + ] } }, "/api/v1/files/{file}/org_logo": { @@ -45145,7 +48657,11 @@ "summary": "List knowledge sources", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "knowledge:read" ] }, "post": { @@ -45677,13 +49193,17 @@ "key": "value" }, "raw_content": {}, + "structured_data": "string", + "structured_data_summary": "string", "type": "gmail" } - ] + ], + "degraded": true, + "effective_mode": "hybrid" }, "properties": { "data": { - "description": "Array of matching knowledge items ordered by relevance score descending.", + "description": "Array of matching knowledge items ordered by relevance score descending. An item indexed from a message that has structured data attached also carries `structured_data` and `structured_data_summary`, the record's search summary computed at read time. `content` is still the indexed text and may be stale.", "example": [ { "content": "ArchAstro connects your agents to external knowledge sources for real-time context retrieval.", @@ -45695,6 +49215,8 @@ "key": "value" }, "raw_content": {}, + "structured_data": "string", + "structured_data_summary": "string", "type": "gmail" } ], @@ -45710,6 +49232,8 @@ "key": "value" }, "raw_content": {}, + "structured_data": "string", + "structured_data_summary": "string", "type": "gmail" }, "properties": { @@ -45755,6 +49279,18 @@ "example": {}, "type": "object" }, + "structured_data": { + "description": "Public ID (`sda_...`) of the structured-data record attached to the source message. Present only for such items on source search.", + "example": "string", + "nullable": true, + "type": "string" + }, + "structured_data_summary": { + "description": "Search summary of that record, computed when the request is served. When present, `content` is the indexed text and may be stale.", + "example": "string", + "nullable": true, + "type": "string" + }, "type": { "description": "Type identifier of the parent knowledge source (e.g. `\"gmail\"`, `\"github_activity\"`). Returns `\"unknown\"` when the source association is not loaded.", "example": "gmail", @@ -45768,6 +49304,21 @@ "type": "object" }, "type": "array" + }, + "degraded": { + "description": "True when a `hybrid` search ran fulltext only because the vector leg was unavailable. Absent when the query was empty.", + "example": true, + "type": "boolean" + }, + "effective_mode": { + "description": "Retrieval mode that actually ran. Differs from the requested `mode` when a `hybrid` search fell back to `fulltext`. Absent when the query was empty.", + "enum": [ + "hybrid", + "vector", + "fulltext" + ], + "example": "hybrid", + "type": "string" } }, "required": [ @@ -45795,15 +49346,59 @@ "summary": "Search within a knowledge source", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "knowledge:read" ] } }, "/api/v1/kv": { "get": { - "description": "Returns key-value storage entries in one of two modes depending on the caller's\nauth scope.\n\n**User-JWT callers** receive a flat list of all their own entries with no\npagination fields. The `page`, `page_size`, `user`, `user_search`, and `key`\nparams are ignored.\n\n**Developer and server-to-server callers** receive a page-based paginated\nresponse across all users within the caller's app. Use `user` to scope results\nto a single user, `user_search` to do a substring match on email or full name,\nand `key` to filter entries whose key starts with the given prefix. Results are\nordered by creation time descending.\n", + "description": "Returns key-value storage entries in one of two modes depending on the caller's\nauth scope.\n\n**User-JWT callers** receive a flat list of all their own entries with no\npagination fields. The `page`, `page_size`, `user`, `user_search`, and `key`\nparams are ignored.\n\n**Developer and server-to-server callers** receive a page-based paginated\nresponse across all users within the caller's app. Use `user` to scope results\nto a single user, `user_search` to do a substring match on email or full name,\nand `key` to filter entries whose key starts with the given prefix. Results are\nordered by update time descending. Supplying org, team, agent, or system selects\na single owner's flat list instead. Expired entries are excluded in every mode.\n", "operationId": "get_api_v1_kv", "parameters": [ + { + "description": "Organization scope for the owner.", + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Team owner ID.", + "example": "string", + "in": "query", + "name": "team", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Agent owner ID.", + "example": "string", + "in": "query", + "name": "agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Select system ownership within the app or organization.", + "example": true, + "in": "query", + "name": "system", + "required": false, + "schema": { + "type": "boolean" + } + }, { "description": "Page number to retrieve. Applies to developer and server-to-server callers only. Defaults to 1.", "example": 1, @@ -45873,7 +49468,10 @@ "description": "Unauthorized" }, "403": { - "description": "App-scoped token required. Use a token scoped to the target app." + "description": "Forbidden; App-scoped token required. Use a token scoped to the target app." + }, + "422": { + "description": "Invalid parameters" } }, "summary": "List key-value storage entries", @@ -45883,7 +49481,7 @@ ] }, "post": { - "description": "Creates a new key-value storage entry for the target user under the given key.\nThe key must not already exist for this user; use the upsert endpoint to create\nor overwrite in a single call.\n\nEnd-user (user-JWT) callers always write to their own storage. Developer and\nserver-to-server callers must supply a `user` param identifying the target user\nwithin their app's scope. Attempting to write for a user in a different app\nreturns 404.\n", + "description": "Creates a key once per owner. An existing key returns HTTP 409 conflict, including expired rows awaiting sweep.\n\nUser-JWT callers use their own storage. Elevated callers select an owner with\nsystem: true, team, user, or agent (in that priority order). An org alone selects\nsystem ownership within that organization; all owners must be in the app scope.\nCreate and upsert accept a positive ttl_seconds; absent means no expiry.\n\nScript binding (`kv.create`): the entry is owned by the triggering\nautomation's owner; scripts pass no owner params. A duplicate key\nreturns a script Err with code \"conflict\" instead of raising.\n", "operationId": "post_api_v1_kv", "parameters": [], "requestBody": { @@ -45891,18 +49489,48 @@ "application/json": { "schema": { "example": { + "agent": "string", "key": "string", + "org": "string", + "system": true, + "team": "string", + "ttl_seconds": 1, "user": "string", "value": "string" }, "properties": { + "agent": { + "description": "Agent owner ID.", + "example": "string", + "type": "string" + }, "key": { "description": "Storage key for the entry. Must be a non-empty string unique to this user.", "example": "string", "type": "string" }, + "org": { + "description": "Organization scope for the owner.", + "example": "string", + "type": "string" + }, + "system": { + "description": "Select system ownership within the app or organization.", + "example": true, + "type": "boolean" + }, + "team": { + "description": "Team owner ID.", + "example": "string", + "type": "string" + }, + "ttl_seconds": { + "description": "Positive lifetime in seconds; omitted means no expiry.", + "example": 1, + "type": "integer" + }, "user": { - "description": "Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers.", + "description": "User owner ID. Elevated callers may instead select team, agent, org, or system ownership.", "example": "string", "type": "string" }, @@ -45945,6 +49573,9 @@ "404": { "description": "User not found" }, + "409": { + "description": "Conflict" + }, "422": { "description": "Invalid parameters" } @@ -45958,7 +49589,7 @@ }, "/api/v1/kv/{key}": { "delete": { - "description": "Permanently deletes the key-value storage entry identified by `key` for the\ntarget user. Returns 204 No Content on success and 404 if the entry does not\nexist.\n\nEnd-user (user-JWT) callers can only delete entries they own. Developer and\nserver-to-server callers must supply a `user` param identifying the target user\nwithin their app's scope.\n", + "description": "Deletes an unexpired entry by owner and key. Returns 204, or 404 when absent.\n\nUser-JWT callers use their own storage. Elevated callers select an owner with\nsystem: true, team, user, or agent (in that priority order). An org alone selects\nsystem ownership within that organization; all owners must be in the app scope.\nCreate and upsert accept a positive ttl_seconds; absent means no expiry.\n\nScript binding (`kv.delete`): removes the entry owned by the triggering\nautomation's owner; scripts pass no owner params.\n", "operationId": "delete_api_v1_kv__key", "parameters": [ { @@ -45973,8 +49604,36 @@ } ], "responses": { - "204": { - "description": "Empty response body. HTTP 204 No Content on success." + "200": { + "content": { + "application/json": { + "schema": { + "description": "Confirmation that the entry was deleted.", + "example": { + "deleted": true, + "key": "string" + }, + "properties": { + "deleted": { + "description": "Always `true` when the deletion succeeds.", + "example": true, + "type": "boolean" + }, + "key": { + "description": "Storage key of the deleted entry.", + "example": "string", + "type": "string" + } + }, + "required": [ + "deleted", + "key" + ], + "type": "object" + } + } + }, + "description": "Successful response" }, "400": { "description": "Bad request" @@ -45996,9 +49655,49 @@ ] }, "get": { - "description": "Returns the key-value storage entry identified by `key` for the target user.\nReturns 404 if no entry exists for that key.\n\nEnd-user (user-JWT) callers retrieve entries from their own storage. Developer\nand server-to-server callers must supply a `user` param identifying the target\nuser within their app's scope.\n", + "description": "Returns an unexpired entry by owner and key, or 404.\n\nUser-JWT callers use their own storage. Elevated callers select an owner with\nsystem: true, team, user, or agent (in that priority order). An org alone selects\nsystem ownership within that organization; all owners must be in the app scope.\nCreate and upsert accept a positive ttl_seconds; absent means no expiry.\n\nScript binding (`kv.get`): reads the entry owned by the triggering\nautomation's owner; scripts pass no owner params.\n", "operationId": "get_api_v1_kv__key", "parameters": [ + { + "description": "Organization scope for the owner.", + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Team owner ID.", + "example": "string", + "in": "query", + "name": "team", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Agent owner ID.", + "example": "string", + "in": "query", + "name": "agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Select system ownership within the app or organization.", + "example": true, + "in": "query", + "name": "system", + "required": false, + "schema": { + "type": "boolean" + } + }, { "description": "Storage key of the entry to retrieve. Must be a non-empty string.", "example": "string", @@ -46010,7 +49709,7 @@ } }, { - "description": "Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers.", + "description": "User owner ID. Elevated callers may instead select team, agent, org, or system ownership.", "example": "string", "in": "query", "name": "user", @@ -46048,10 +49747,11 @@ "x-auth": [ "publishable_key", "bearer" - ] + ], + "x-sdk-name": "get" }, "put": { - "description": "Creates a new key-value storage entry for the given `key`, or overwrites the\nvalue if an entry already exists. This is the idempotent alternative to the\ncreate endpoint: safe to call regardless of whether the key already exists.\n\nEnd-user (user-JWT) callers always write to their own storage. Developer and\nserver-to-server callers must supply a `user` param identifying the target user\nwithin their app's scope. Attempting to write for a user in a different app\nreturns 404.\n", + "description": "Creates or replaces a value and its expiry. Omitted ttl_seconds clears any previous expiry.\n\nUser-JWT callers use their own storage. Elevated callers select an owner with\nsystem: true, team, user, or agent (in that priority order). An org alone selects\nsystem ownership within that organization; all owners must be in the app scope.\nCreate and upsert accept a positive ttl_seconds; absent means no expiry.\n\nScript binding (`kv.upsert`): overwrites the entry owned by the triggering\nautomation's owner; scripts pass no owner params.\n", "operationId": "put_api_v1_kv__key", "parameters": [ { @@ -46070,12 +49770,42 @@ "application/json": { "schema": { "example": { + "agent": "string", + "org": "string", + "system": true, + "team": "string", + "ttl_seconds": 1, "user": "string", "value": "string" }, "properties": { + "agent": { + "description": "Agent owner ID.", + "example": "string", + "type": "string" + }, + "org": { + "description": "Organization scope for the owner.", + "example": "string", + "type": "string" + }, + "system": { + "description": "Select system ownership within the app or organization.", + "example": true, + "type": "boolean" + }, + "team": { + "description": "Team owner ID.", + "example": "string", + "type": "string" + }, + "ttl_seconds": { + "description": "Positive lifetime in seconds; omitted means no expiry.", + "example": 1, + "type": "integer" + }, "user": { - "description": "Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers.", + "description": "User owner ID. Elevated callers may instead select team, agent, org, or system ownership.", "example": "string", "type": "string" }, @@ -46430,7 +50160,11 @@ "summary": "List a user's notifications", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "notifications:read" ] } }, @@ -46459,7 +50193,7 @@ }, "/api/v1/notifications/send": { "post": { - "description": "Delivers a custom-typed notification to one of the calling app's users.\nApps define notification types by declaring `NotificationType` config objects\nin their bundle (one per `lookup_key`). Supply the type as\n`\"custom:\"` and provide a `data` map that is merged with\nplatform-provided context to render the notification's display fields.\n\nOnly app-scoped tokens may call this endpoint — user tokens are rejected with\n403. The app scope is stamped onto the notification automatically; an app\ncannot target recipients outside its tenant. Built-in platform types such as\n`\"app_info\"` and `\"billing_alert\"` are not accepted here.\n\nPass `idempotency_key` to deduplicate sends. If you call this endpoint twice\nwith the same `idempotency_key` for the same recipient, the second call\nreturns the original notification without creating a duplicate. The key is\nscoped to the calling app, so the same raw key used by different apps cannot\ncollide.\n", + "description": "Delivers a custom-typed notification to one of the calling app's users.\nApps define notification types by declaring `NotificationType` config objects\nin their bundle (one per `lookup_key`). Supply the type as\n`\"custom:\"` and provide a `data` map that is merged with\nplatform-provided context to render the notification's display fields.\n\nOnly app-scoped tokens may call this endpoint — user tokens are rejected with\n403. The app scope is stamped onto the notification automatically; an app\ncannot target recipients outside its tenant. Built-in platform types such as\n`\"app_info\"` and `\"billing_alert\"` are not accepted here.\n\nThe `notifications.send` script binding also accepts the viewer a script runs\nas: a user, an agent, or a team's system viewer. Such a sender may notify only\na user it can see in its own organization and sandbox. When the sender is a\nuser, the recipient must also be able to see the sender. An agent cannot see\nuser records, so an agent sender must name a source message: it may then\nnotify a user in its own organization and sandbox who can read that message.\n\nPass `thread` and `message` together to name the message the notification is\nabout. The send is then refused unless that message is in that thread and both\nthe sender and the recipient can read it, so a notification cannot quote a\nmessage its recipient cannot open.\n\nPass `idempotency_key` to deduplicate sends. If you call this endpoint twice\nwith the same `idempotency_key` for the same recipient, the second call\nreturns the original notification without creating a duplicate. The key is\nscoped to the calling app, so the same raw key used by different apps cannot\ncollide.\n", "operationId": "post_api_v1_notifications_send", "parameters": [], "requestBody": { @@ -46469,6 +50203,8 @@ "example": { "data": {}, "idempotency_key": "string", + "message": "string", + "thread": "string", "type": "string", "user": "string" }, @@ -46483,6 +50219,16 @@ "example": "string", "type": "string" }, + "message": { + "description": "Source message (`msg_...`) that the sender and the recipient must both be able to read.", + "example": "string", + "type": "string" + }, + "thread": { + "description": "Thread (`thr_...`) of the source message. Requires `message`.", + "example": "string", + "type": "string" + }, "type": { "description": "Custom notification type identifier in the form `\"custom:\"`, where `` matches a `NotificationType` config declared in the calling app's bundle.", "example": "string", @@ -46578,7 +50324,11 @@ "summary": "Get the unread notification count", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "notifications:read" ] } }, @@ -46652,7 +50402,11 @@ "summary": "Mark a notification as read", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "notifications:write" ] } }, @@ -46925,6 +50679,34 @@ "before_cursor": "string", "data": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -46972,6 +50754,34 @@ "data": { "example": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -47005,6 +50815,34 @@ "items": { "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -47035,6 +50873,180 @@ "version": 1 }, "properties": { + "acl": { + "description": "Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "nullable": true, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "agent": { "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", @@ -47388,6 +51400,4702 @@ ] } }, + "/api/v1/orgs/{org}/task_epics": { + "get": { + "description": "Groups org-owned tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created.", + "operationId": "get_api_v1_orgs__org_task_epics", + "parameters": [ + { + "description": "Organization ID (`org_...`) owning the tasks.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source scope; supply all three source fields.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source type.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source ID.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact highest open priority, from 0 to 4.", + "example": 1, + "in": "query", + "name": "top_priority", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Include epics with no non-closed tasks; defaults to false.", + "example": true, + "in": "query", + "name": "include_closed", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Maximum epics; defaults to 50, capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque forward cursor from the preceding page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "has_more": true, + "no_epic_open_count": 1 + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "example": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "items": { + "description": "Read-only progress and scheduling summary for an epic label.", + "example": { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "blocked_count": { + "example": 1, + "type": "integer" + }, + "counts": { + "description": "Task counts keyed by status, including closed tasks.", + "example": {}, + "type": "object" + }, + "epic": { + "example": "string", + "type": "string" + }, + "next_ready": { + "example": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An open task with no unfinished prerequisite or active lease.", + "example": { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers": { + "example": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An unblocked root and the number of open tasks downstream within its epic.", + "example": { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "downstream_count": { + "example": 1, + "type": "integer" + }, + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority", + "downstream_count" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers_partial": { + "description": "True when root ranking used a truncated in-epic graph.", + "example": true, + "type": "boolean" + }, + "top_open_priority": { + "example": 1, + "nullable": true, + "type": "integer" + }, + "updated_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "epic", + "counts", + "blocked_count", + "updated_at", + "root_blockers", + "root_blockers_partial", + "next_ready" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + }, + "no_epic_open_count": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "data", + "has_more", + "no_epic_open_count" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid filters or owner context" + } + }, + "summary": "List an organization's epic summaries", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks": { + "get": { + "description": "Returns org-owned tasks for the specified organization: rows with the org\ntenant and no narrower user/team/agent scope. Explicitly personal, team, or\nagent tasks are excluded here; list those via the user/team collections or\nfetch them directly when authorized.\n\nEvery organization member may list. App-scoped developer and\nserver-to-server callers supply the org in the route.\n", + "operationId": "get_api_v1_orgs__org_tasks", + "parameters": [ + { + "description": "Organization ID (`org_...`) whose org-owned tasks should be returned.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks by status. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, or `\"done\"`. Omit to return tasks in all statuses.", + "example": "string", + "in": "query", + "name": "status", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`).", + "example": "string", + "in": "query", + "name": "owner_user", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`).", + "example": "string", + "in": "query", + "name": "owner_agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks by priority, from 0 (highest) to 4 (lowest).", + "example": 1, + "in": "query", + "name": "priority", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Return only tasks carrying this tag (matched against the canonical lowercase form).", + "example": "string", + "in": "query", + "name": "tag", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks.", + "example": "string", + "in": "query", + "name": "parent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this object kind. Must be supplied with the other source filters.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this object identity. Must be supplied with the other source filters.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks with this exact epic label.", + "example": "string", + "in": "query", + "name": "epic", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Restrict results to tasks whose name or description contains this string.", + "example": "string", + "in": "query", + "name": "search", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Sort key. One of `\"created\"` (default — most recently created first), `\"due_date\"` (soonest due first; tasks without a due date always sort last), or `\"priority\"` (most urgent first). Ties break by most recently created.", + "example": "string", + "in": "query", + "name": "sort", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Sort direction, `\"asc\"` or `\"desc\"`. Defaults to `\"desc\"` for `created` and `\"asc\"` for `due_date` and `priority`.", + "example": "string", + "in": "query", + "name": "order", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks with a due date strictly before this ISO 8601 datetime (`2026-08-01T00:00:00Z`) or date (`2026-08-01`, meaning midnight UTC). Tasks without a due date are excluded.", + "example": "string", + "in": "query", + "name": "due_before", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded.", + "example": "string", + "in": "query", + "name": "due_after", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `\"done\"`. A task due today is not overdue.", + "example": true, + "in": "query", + "name": "overdue", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work.", + "example": true, + "in": "query", + "name": "ready", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry.", + "example": true, + "in": "query", + "name": "attention", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Maximum number of tasks to return. Capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor returned by the previous page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Filtered list of tasks for the organization.", + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "description": "Array of task objects matching the requested filters.", + "example": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "items": { + "description": "A task representing a unit of work, optionally assignable to a user or agent.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "description": "ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "aggregate_version": { + "description": "Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "blocked_by_count": { + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "example": 1, + "type": "integer" + }, + "closed_at": { + "description": "When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "comments_count": { + "description": "Total number of comments posted on this task.", + "example": 1, + "type": "integer" + }, + "created_at": { + "description": "When the task was created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "created_by_actor": { + "description": "Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "created_by_agent": { + "description": "ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "created_by_user": { + "description": "ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "current_lease": { + "description": "Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + "example": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "nullable": true, + "properties": { + "expires_at": { + "description": "Server-calculated lease expiry in ISO 8601 format.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "harness": { + "description": "Bounded harness identifier for the coding session.", + "example": "string", + "type": "string" + }, + "session_name": { + "description": "Display name supplied by the coding session that holds the lease.", + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "session_name", + "harness", + "expires_at" + ], + "type": "object" + }, + "description": { + "description": "Long-form description or notes for the task. `null` if no description has been provided.", + "example": "An example description.", + "nullable": true, + "type": "string" + }, + "due_date": { + "description": "Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "epic": { + "description": "Free-form grouping label. `null` when the task is not in an epic.", + "example": "string", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Task ID (`tsk_...`).", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_blocked": { + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + "example": true, + "type": "boolean" + }, + "links": { + "description": "Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "metadata": { + "description": "Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable title of the task.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_actor": { + "description": "Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "owner_agent": { + "description": "ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_user": { + "description": "ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "parent": { + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "priority": { + "description": "Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + "example": 2, + "type": "integer" + }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, + "sandbox": { + "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "source_id": { + "description": "Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_scope": { + "description": "Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_type": { + "description": "Kind of source object (for example `repository`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "status": { + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", + "example": "open", + "type": "string" + }, + "subtasks_count": { + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "example": 1, + "type": "integer" + }, + "tags": { + "description": "Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, + "team": { + "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "thread": { + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "updated_at": { + "description": "When the task was last modified (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "name", + "status" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid filters" + } + }, + "summary": "List an organization's tasks", + "x-auth": [ + "publishable_key", + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "tasks:read" + ] + }, + "post": { + "description": "Creates a new org-owned task (org tenant, no narrower user/team/agent\nscope) and returns the full task object. User-authenticated calls are\nattributed to the authenticated user or agent. App-scoped developer and\nserver-to-server callers must provide an explicit `user` or `agent` actor\nfor the organization. Every referenced principal is validated against the\napp and organization before creation.\n", + "operationId": "post_api_v1_orgs__org_tasks", + "parameters": [ + { + "description": "Organization ID (`org_...`). The task will be owned by this organization.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "task": { + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "billing-cadence", + "id": "string", + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "owner_agent": "string", + "owner_user": "string", + "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", + "priority": 2, + "project_id": "string", + "source_id": "ArchAstro/firstlanding", + "source_scope": "github.com", + "source_type": "repository", + "status": "open", + "tags": [ + "backend", + "q3-launch" + ], + "target_binary": "my-cli", + "target_version": "0.48.9", + "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" + }, + "user": "string" + }, + "properties": { + "agent": { + "description": "Explicit acting agent (`agi_...`) for a developer or server-to-server call. Mutually exclusive with an acting `user`; the agent must belong to the organization.", + "example": "string", + "type": "string" + }, + "task": { + "description": "Attributes for the task to create. `name` is required; all other fields are optional.", + "example": { + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "billing-cadence", + "id": "string", + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "owner_agent": "string", + "owner_user": "string", + "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", + "priority": 2, + "project_id": "string", + "source_id": "ArchAstro/firstlanding", + "source_scope": "github.com", + "source_type": "repository", + "status": "open", + "tags": [ + "backend", + "q3-launch" + ], + "target_binary": "my-cli", + "target_version": "0.48.9", + "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" + }, + "properties": { + "description": { + "description": "Optional long-form description or notes for the task. Supports plain text.", + "example": "An example description.", + "type": "string" + }, + "due_date": { + "description": "Date and time by which the task should be completed (ISO 8601). Omit to create the task without a due date.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "epic": { + "description": "Optional free-form grouping label.", + "example": "billing-cadence", + "type": "string" + }, + "id": { + "description": "Optional caller-generated task public ID (`tsk_...`). Persist it before creating a task when creation must survive a lost response. An existing ID returns 409 without modifying the task; read the task by ID to reconcile. Omit for a server-generated ID.", + "example": "string", + "type": "string" + }, + "links": { + "description": "Arbitrary key-value map of named URLs or references associated with the task (e.g. external ticket links).", + "example": { + "key": "value" + }, + "type": "object" + }, + "metadata": { + "description": "Arbitrary key-value map for storing application-specific data alongside the task. Omit to create the task with no metadata.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable title for the task.", + "example": "Example Name", + "type": "string" + }, + "owner_agent": { + "description": "ID of the agent to assign as owner (`agi_...`). Mutually exclusive with `owner_user`; omit to leave the task unassigned.", + "example": "string", + "type": "string" + }, + "owner_user": { + "description": "ID of the user to assign as owner (`usr_...`). Mutually exclusive with `owner_agent`; omit to leave the task unassigned.", + "example": "string", + "type": "string" + }, + "parent": { + "description": "Create this task as a subtask of an existing top-level task (`tsk_...`). Subtasks nest exactly one level.", + "example": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", + "type": "string" + }, + "priority": { + "description": "Priority level from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when omitted.", + "example": 2, + "type": "integer" + }, + "project_id": { + "description": "Optional project ID (`prj_...`). Pass null to clear it.", + "example": "string", + "type": "string" + }, + "source_id": { + "description": "Source object identity (for example `ArchAstro/firstlanding`).", + "example": "ArchAstro/firstlanding", + "type": "string" + }, + "source_scope": { + "description": "Container of the work this task is about (for example `github.com`). Must be supplied with `source_type` and `source_id`.", + "example": "github.com", + "type": "string" + }, + "source_type": { + "description": "Kind of source object (for example `repository`).", + "example": "repository", + "type": "string" + }, + "status": { + "description": "Initial status for the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`. Defaults to `\"open\"` when omitted.", + "example": "open", + "type": "string" + }, + "tags": { + "description": "Labels for grouping and filtering (max 20, each up to 40 characters). Stored canonically: lowercase, trimmed, de-duplicated.", + "example": [ + "backend", + "q3-launch" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "target_binary": { + "description": "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`.", + "example": "my-cli", + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`.", + "example": "0.48.9", + "type": "string" + }, + "thread": { + "description": "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation.", + "example": "thr_01j3k5m7n9p2r4s6t8v0w1x2", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "user": { + "description": "Explicit acting user (`usr_...`) for a developer or server-to-server call. Mutually exclusive with an acting `agent`.", + "example": "string", + "type": "string" + } + }, + "required": [ + "task" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Task" + } + } + }, + "description": "The newly created task." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Validation error" + } + }, + "summary": "Create a task for an organization", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks/blocker_cycles": { + "get": { + "description": "Runs an on-demand diagnostic over unfinished org-owned tasks for the\nspecified organization and returns a forward cursor-paginated page of\ncomplete cyclic blocker components. Detection is bounded to owners with at\nmost 100 unfinished tasks. This endpoint is read-only: cycles do not prevent\ntask updates, lease acquisition, or completion.\n", + "operationId": "get_api_v1_orgs__org_tasks_blocker_cycles", + "parameters": [ + { + "description": "Organization ID (`org_...`) whose org-owned tasks are diagnosed.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum cycle components to return. Defaults to 50; maximum is 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor returned by the preceding page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "On-demand task blocker cycle diagnostics.", + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "tasks": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ] + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "type": "string" + }, + "before_cursor": { + "example": "string", + "type": "string" + }, + "data": { + "example": [ + { + "tasks": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ] + } + ], + "items": { + "description": "A strongly connected component of unfinished task blocker edges.", + "example": { + "tasks": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ] + }, + "properties": { + "tasks": { + "description": "Every unfinished task in this cyclic blocker component.", + "example": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "items": { + "description": "A task representing a unit of work, optionally assignable to a user or agent.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "description": "ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "aggregate_version": { + "description": "Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "blocked_by_count": { + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "example": 1, + "type": "integer" + }, + "closed_at": { + "description": "When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "comments_count": { + "description": "Total number of comments posted on this task.", + "example": 1, + "type": "integer" + }, + "created_at": { + "description": "When the task was created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "created_by_actor": { + "description": "Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "created_by_agent": { + "description": "ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "created_by_user": { + "description": "ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "current_lease": { + "description": "Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + "example": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "nullable": true, + "properties": { + "expires_at": { + "description": "Server-calculated lease expiry in ISO 8601 format.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "harness": { + "description": "Bounded harness identifier for the coding session.", + "example": "string", + "type": "string" + }, + "session_name": { + "description": "Display name supplied by the coding session that holds the lease.", + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "session_name", + "harness", + "expires_at" + ], + "type": "object" + }, + "description": { + "description": "Long-form description or notes for the task. `null` if no description has been provided.", + "example": "An example description.", + "nullable": true, + "type": "string" + }, + "due_date": { + "description": "Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "epic": { + "description": "Free-form grouping label. `null` when the task is not in an epic.", + "example": "string", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Task ID (`tsk_...`).", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_blocked": { + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + "example": true, + "type": "boolean" + }, + "links": { + "description": "Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "metadata": { + "description": "Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable title of the task.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_actor": { + "description": "Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "owner_agent": { + "description": "ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_user": { + "description": "ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "parent": { + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "priority": { + "description": "Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + "example": 2, + "type": "integer" + }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, + "sandbox": { + "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "source_id": { + "description": "Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_scope": { + "description": "Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_type": { + "description": "Kind of source object (for example `repository`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "status": { + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", + "example": "open", + "type": "string" + }, + "subtasks_count": { + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "example": 1, + "type": "integer" + }, + "tags": { + "description": "Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, + "team": { + "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "thread": { + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "updated_at": { + "description": "When the task was last modified (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "name", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "tasks" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid owner context or diagnostic limit" + } + }, + "summary": "List task blocker cycles for an organization", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks/by-source": { + "put": { + "description": "Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted.", + "operationId": "put_api_v1_orgs__org_tasks_by-source", + "parameters": [ + { + "description": "Explicit organization for privileged callers.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "description": "An example description.", + "epic": "string", + "name": "Example Name", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "status": "string", + "team": "string", + "user": "string" + }, + "properties": { + "agent": { + "description": "Explicit acting agent for privileged callers.", + "example": "string", + "type": "string" + }, + "description": { + "description": "Initial task description.", + "example": "An example description.", + "type": "string" + }, + "epic": { + "description": "Initial epic grouping.", + "example": "string", + "type": "string" + }, + "name": { + "description": "Initial task name.", + "example": "Example Name", + "type": "string" + }, + "source_id": { + "description": "Source identity, for example owner/repo#123.", + "example": "string", + "type": "string" + }, + "source_scope": { + "description": "Source scope, for example github.", + "example": "string", + "type": "string" + }, + "source_type": { + "description": "Source type, for example pull_request.", + "example": "string", + "type": "string" + }, + "status": { + "description": "Initial task status.", + "example": "string", + "type": "string" + }, + "team": { + "description": "Team owner.", + "example": "string", + "type": "string" + }, + "user": { + "description": "User owner, or explicit acting user on a team route.", + "example": "string", + "type": "string" + } + }, + "required": [ + "source_scope", + "source_type", + "source_id", + "name" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskEnsureResult" + } + } + }, + "description": "The task and whether this request created it." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task owner not found" + }, + "422": { + "description": "Validation error" + } + }, + "summary": "Find or create a task by source", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks/metrics": { + "get": { + "description": "Returns task activity metrics scoped to org-owned tasks (org tenant, no\nnarrower user/team/agent scope). `open` counts currently open or\nin-progress org tasks, `created` counts tasks inserted in the UTC-day\nwindow, and `completed` counts tasks whose `closed_at` falls in that window\nwhile still in a closed status. Hard-deleted tasks are absent from the\nprojection and therefore omitted. The daily series is zero-filled across\nthe requested window and includes reconstructed end-of-day `open` stock.\n\nAny organization member may read this count-only summary. Requests from\ncallers without org access return 404 so organization existence is not\ndisclosed.\n", + "operationId": "get_api_v1_orgs__org_tasks_metrics", + "parameters": [ + { + "description": "Organization ID (`org_...`) whose task metrics should be returned.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "UTC-day window. One of 7, 30, 90, or 365; defaults to 30.", + "example": 1, + "in": "query", + "name": "days", + "required": false, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Organization-scoped task activity metrics.", + "example": { + "completed": 1, + "created": 1, + "days": 1, + "end_at": "2024-01-01T00:00:00Z", + "open": 1, + "series": [ + {} + ], + "start_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "completed": { + "example": 1, + "type": "integer" + }, + "created": { + "example": 1, + "type": "integer" + }, + "days": { + "example": 1, + "type": "integer" + }, + "end_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "open": { + "example": 1, + "type": "integer" + }, + "series": { + "description": "Zero-filled UTC-day buckets with `date`, end-of-day `open`, `created`, and `completed`.", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "start_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "days", + "start_at", + "end_at", + "open", + "created", + "completed", + "series" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Get task activity metrics for an organization", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks/ready": { + "get": { + "description": "Returns open org-owned tasks with no unfinished blockers and no active\nsession lease. Readiness is calculated by the server from the current task\nprojection. It is a snapshot, not a reservation; claim a task lease before\nstarting work.\n\nResults are ordered by priority (0, the most urgent, first), then oldest\nfirst, so the first page is always the most urgent ready work.\n\nPass `explain=true` to include every open task with a stable readiness reason.\n", + "operationId": "get_api_v1_orgs__org_tasks_ready", + "parameters": [ + { + "description": "Organization ID (`org_...`) owning the tasks.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Include blocked and actively leased open tasks with exclusion reasons.", + "example": true, + "in": "query", + "name": "explain", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Only include tasks assigned to the authenticated user.", + "example": true, + "in": "query", + "name": "assigned_to_me", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Only include tasks whose source matches this container. Must be supplied with `source_type` and `source_id`.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Only include tasks whose source matches this object kind.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Only include tasks whose source matches this object identity.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Only include tasks with this exact epic label.", + "example": "string", + "in": "query", + "name": "epic", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum number of readiness entries to return. Capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor returned by the previous page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Cursor-paginated readiness results for the organization.", + "example": { + "after_cursor": "string", + "authoritative": true, + "before_cursor": "string", + "data": [ + { + "readiness": "ready", + "reason": "open_blockers", + "task": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "type": "string" + }, + "authoritative": { + "description": "Always false because projections can lag writes and a later claim can race this read.", + "example": true, + "type": "boolean" + }, + "before_cursor": { + "example": "string", + "type": "string" + }, + "data": { + "example": [ + { + "readiness": "ready", + "reason": "open_blockers", + "task": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + } + ], + "items": { + "description": "A task plus the server-calculated reason it is or is not ready.", + "example": { + "readiness": "ready", + "reason": "open_blockers", + "task": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + }, + "properties": { + "readiness": { + "description": "One of `ready`, `blocked`, or `leased`.", + "enum": [ + "ready", + "blocked", + "leased" + ], + "example": "ready", + "type": "string" + }, + "reason": { + "description": "Stable exclusion reason: `open_blockers` or `active_lease`; omitted when ready.", + "enum": [ + "open_blockers", + "active_lease" + ], + "example": "open_blockers", + "nullable": true, + "type": "string" + }, + "task": { + "description": "The task evaluated for readiness.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "description": "ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "aggregate_version": { + "description": "Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "blocked_by_count": { + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "example": 1, + "type": "integer" + }, + "closed_at": { + "description": "When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "comments_count": { + "description": "Total number of comments posted on this task.", + "example": 1, + "type": "integer" + }, + "created_at": { + "description": "When the task was created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "created_by_actor": { + "description": "Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "created_by_agent": { + "description": "ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "created_by_user": { + "description": "ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "current_lease": { + "description": "Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + "example": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "nullable": true, + "properties": { + "expires_at": { + "description": "Server-calculated lease expiry in ISO 8601 format.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "harness": { + "description": "Bounded harness identifier for the coding session.", + "example": "string", + "type": "string" + }, + "session_name": { + "description": "Display name supplied by the coding session that holds the lease.", + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "session_name", + "harness", + "expires_at" + ], + "type": "object" + }, + "description": { + "description": "Long-form description or notes for the task. `null` if no description has been provided.", + "example": "An example description.", + "nullable": true, + "type": "string" + }, + "due_date": { + "description": "Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "epic": { + "description": "Free-form grouping label. `null` when the task is not in an epic.", + "example": "string", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Task ID (`tsk_...`).", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_blocked": { + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + "example": true, + "type": "boolean" + }, + "links": { + "description": "Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "metadata": { + "description": "Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable title of the task.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_actor": { + "description": "Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "owner_agent": { + "description": "ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_user": { + "description": "ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "parent": { + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "priority": { + "description": "Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + "example": 2, + "type": "integer" + }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, + "sandbox": { + "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "source_id": { + "description": "Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_scope": { + "description": "Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_type": { + "description": "Kind of source object (for example `repository`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "status": { + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", + "example": "open", + "type": "string" + }, + "subtasks_count": { + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "example": 1, + "type": "integer" + }, + "tags": { + "description": "Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, + "team": { + "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "thread": { + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "updated_at": { + "description": "When the task was last modified (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "name", + "status" + ], + "type": "object" + } + }, + "required": [ + "task", + "readiness" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "authoritative", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid owner context or pagination cursor" + } + }, + "summary": "List an organization's ready tasks", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/orgs/{org}/tasks/search": { + "get": { + "description": "Performs a full-text search over org-owned tasks for the specified\norganization and returns matching results. Combine `q` with the optional\nfilters to narrow the result set further. When no query is provided, the\nendpoint behaves like a filtered list.\n\nThe `query` field in the response echoes the effective search query.\nExplicitly personal, team, or agent tasks are excluded here.\n", + "operationId": "get_api_v1_orgs__org_tasks_search", + "parameters": [ + { + "description": "Organization ID (`org_...`) whose org-owned tasks are searched.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Full-text search query matched against task names and descriptions. Takes precedence over `query` when both are provided.", + "example": "string", + "in": "query", + "name": "q", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Alias for `q`. Use `q` when possible; this parameter exists for compatibility.", + "example": "string", + "in": "query", + "name": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter results by status. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, or `\"done\"`. Omit to include all statuses.", + "example": "string", + "in": "query", + "name": "status", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`).", + "example": "string", + "in": "query", + "name": "owner_user", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`).", + "example": "string", + "in": "query", + "name": "owner_agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter tasks by priority, from 0 (highest) to 4 (lowest).", + "example": 1, + "in": "query", + "name": "priority", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Return only tasks carrying this tag (matched against the canonical lowercase form).", + "example": "string", + "in": "query", + "name": "tag", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks.", + "example": "string", + "in": "query", + "name": "parent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this object kind. Must be supplied with the other source filters.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks whose source matches this object identity. Must be supplied with the other source filters.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Return only tasks with this exact epic label.", + "example": "string", + "in": "query", + "name": "epic", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum number of tasks to return. Capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor returned by the previous page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Search results for the organization's tasks.", + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "has_more": true, + "query": "string" + }, + "properties": { + "after_cursor": { + "example": "string", + "type": "string" + }, + "before_cursor": { + "example": "string", + "type": "string" + }, + "data": { + "description": "Array of task objects matching the query and filters.", + "example": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "items": { + "description": "A task representing a unit of work, optionally assignable to a user or agent.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, + "blocked_by_count": 1, + "closed_at": "2024-01-01T00:00:00Z", + "comments_count": 1, + "created_at": "2024-01-01T00:00:00Z", + "created_by_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "created_by_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "current_lease": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "description": "An example description.", + "due_date": "2024-01-01T00:00:00Z", + "epic": "string", + "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "is_blocked": true, + "is_stalled": true, + "links": { + "key": "value" + }, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "owner_actor": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "owner_agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "priority": 2, + "project_id": "string", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", + "status": "open", + "subtasks_count": 1, + "tags": [ + "string" + ], + "target_binary": "string", + "target_version": "string", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "description": "ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "aggregate_version": { + "description": "Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + "example": 1, + "nullable": true, + "type": "integer" + }, + "blocked_by_count": { + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "example": 1, + "type": "integer" + }, + "closed_at": { + "description": "When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "comments_count": { + "description": "Total number of comments posted on this task.", + "example": 1, + "type": "integer" + }, + "created_at": { + "description": "When the task was created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "created_by_actor": { + "description": "Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "created_by_agent": { + "description": "ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "created_by_user": { + "description": "ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "current_lease": { + "description": "Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + "example": { + "expires_at": "2024-01-01T00:00:00Z", + "harness": "string", + "session_name": "Example Name" + }, + "nullable": true, + "properties": { + "expires_at": { + "description": "Server-calculated lease expiry in ISO 8601 format.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "harness": { + "description": "Bounded harness identifier for the coding session.", + "example": "string", + "type": "string" + }, + "session_name": { + "description": "Display name supplied by the coding session that holds the lease.", + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "session_name", + "harness", + "expires_at" + ], + "type": "object" + }, + "description": { + "description": "Long-form description or notes for the task. `null` if no description has been provided.", + "example": "An example description.", + "nullable": true, + "type": "string" + }, + "due_date": { + "description": "Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "epic": { + "description": "Free-form grouping label. `null` when the task is not in an epic.", + "example": "string", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Task ID (`tsk_...`).", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_blocked": { + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + "example": true, + "type": "boolean" + }, + "links": { + "description": "Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "metadata": { + "description": "Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable title of the task.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_actor": { + "description": "Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + "example": { + "alias": "alice", + "id": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "name": "Example Name", + "profile_picture": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + } + }, + "nullable": true, + "properties": { + "alias": { + "description": "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + "example": "alice", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Composite actor identifier. Format is `\"user-\"` for human users or `\"agent-\"` for agents.", + "example": "user-usr_01j3k5m7n9p2r4s6t8v0w1x2", + "nullable": true, + "type": "string" + }, + "name": { + "description": "Display name of the actor shown in the UI. `null` if no name is set.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "description": "Profile picture for the actor. `null` if the actor has no profile picture.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "nullable": true, + "properties": { + "file": { + "description": "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "height": { + "description": "Height of the image in pixels. `null` if not known.", + "example": 600, + "nullable": true, + "type": "integer" + }, + "media": { + "description": "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + "example": "med_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "mime_type": { + "description": "MIME type of the image, e.g. `\"image/png\"` or `\"image/jpeg\"`. `null` if not known.", + "example": "application/json", + "nullable": true, + "type": "string" + }, + "refresh_url": { + "description": "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "url": { + "description": "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + "example": "https://example.com", + "nullable": true, + "type": "string" + }, + "width": { + "description": "Width of the image in pixels. `null` if not known.", + "example": 800, + "nullable": true, + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "owner_agent": { + "description": "ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "owner_user": { + "description": "ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "parent": { + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "priority": { + "description": "Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + "example": 2, + "type": "integer" + }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, + "sandbox": { + "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "source_id": { + "description": "Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_scope": { + "description": "Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "source_type": { + "description": "Kind of source object (for example `repository`). `null` when the task has no source.", + "example": "string", + "nullable": true, + "type": "string" + }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "status": { + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", + "example": "open", + "type": "string" + }, + "subtasks_count": { + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "example": 1, + "type": "integer" + }, + "tags": { + "description": "Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, + "team": { + "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "thread": { + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "updated_at": { + "description": "When the task was last modified (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "name", + "status" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + }, + "query": { + "example": "string", + "type": "string" + } + }, + "required": [ + "data", + "has_more", + "query" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Organization not found" + }, + "422": { + "description": "Invalid filters" + } + }, + "summary": "Search an organization's tasks", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/orgs/{org}/threads": { "get": { "description": "Returns org-visibility threads for the specified organization. Every member\nof the organization can list these threads; they require no team membership.\nApp-scoped developer tokens for the organization's app can list them too.\n", @@ -47404,7 +56112,7 @@ } }, { - "description": "Optional exact thread key, for example `archdev-room`.", + "description": "Optional exact thread key, for example `support-room`.", "example": "string", "in": "query", "name": "key", @@ -47517,8 +56225,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -47532,8 +56242,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -47549,7 +56261,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -47929,8 +56641,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -47944,8 +56658,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -47961,7 +56677,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -48338,8 +57054,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -48353,8 +57071,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -48370,7 +57090,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -48973,8 +57693,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -48988,8 +57710,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -49005,7 +57729,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -49381,8 +58105,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -49396,8 +58122,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -49435,8 +58163,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -49450,8 +58180,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -49608,8 +58340,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -49623,8 +58357,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -49632,8 +58368,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -49647,12 +58385,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -49665,6 +58411,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -49745,6 +58497,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -49757,6 +58515,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -49816,7 +58581,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -49825,7 +58590,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -49847,7 +58612,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -50059,8 +58824,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -50074,8 +58841,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -50091,7 +58860,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -52634,7 +61403,11 @@ "summary": "List organization-visibility threads", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "threads:read" ] }, "post": { @@ -53005,7 +61778,233 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivateServiceEnrollmentPage" + "$ref": "#/components/schemas/PrivateServiceEnrollmentPage" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Bad request" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "503": { + "description": "Private service control is unavailable" + } + }, + "summary": "List private service enrollments", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "post": { + "description": "Creates or safely replaces an unreserved one-time connector enrollment token.", + "operationId": "post_api_v1_private_service_enrollments", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "org": "string", + "private_service": "string" + }, + "properties": { + "org": { + "description": "Organization ID or slug. Required for developer and server callers.", + "example": "string", + "type": "string" + }, + "private_service": { + "description": "Private service ID (`pvs_...`).", + "example": "string", + "type": "string" + } + }, + "required": [ + "private_service" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedPrivateServiceEnrollment" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Bad request" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict" + }, + "503": { + "description": "Private service control is unavailable" + } + }, + "summary": "Create a private service enrollment", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/private_service_enrollments/{private_service_enrollment_id}": { + "delete": { + "description": "Removes online connector authorization and prevents credential renewal.", + "operationId": "delete_api_v1_private_service_enrollments__private_service_enrollment_id", + "parameters": [ + { + "description": "Canonical certificate-bound service identity.", + "example": "string", + "in": "path", + "name": "private_service_enrollment_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Authorization was durably revoked." + }, + "400": { + "description": "Bad request" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "503": { + "description": "Private service control is unavailable" + } + }, + "summary": "Revoke a private service enrollment", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "get": { + "operationId": "get_api_v1_private_service_enrollments__private_service_enrollment_id", + "parameters": [ + { + "description": "Organization ID or slug. Required for developer and server callers.", + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Canonical certificate-bound service identity.", + "example": "string", + "in": "path", + "name": "private_service_enrollment_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PrivateServiceEnrollment" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Bad request" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "503": { + "description": "Private service control is unavailable" + } + }, + "summary": "Retrieve a private service enrollment", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/private_services": { + "get": { + "operationId": "get_api_v1_private_services", + "parameters": [ + { + "description": "Organization ID or slug. Required for developer and server callers.", + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Page number. Defaults to 1.", + "example": 1, + "in": "query", + "name": "page", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Results per page. Defaults to 25; maximum is 100.", + "example": 1, + "in": "query", + "name": "page_size", + "required": false, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PrivateServicePage" } } }, @@ -53024,38 +62023,89 @@ "description": "Private service control is unavailable" } }, - "summary": "List private service enrollments", + "summary": "List private services", "x-auth": [ "publishable_key", "bearer" ] }, "post": { - "description": "Creates or safely replaces an unreserved one-time connector enrollment token.", - "operationId": "post_api_v1_private_service_enrollments", + "description": "Creates one immutable private service in the selected organization.", + "operationId": "post_api_v1_private_services", "parameters": [], "requestBody": { "content": { "application/json": { "schema": { "example": { - "org": "string", - "private_service": "string" + "functions": [ + { + "description": "An example description.", + "input_schema": {}, + "name": "Example Name", + "output_schema": {} + } + ], + "org": "string" }, "properties": { + "functions": { + "description": "Complete callable contracts. Input schemas are required; output schemas are optional.", + "example": [ + { + "description": "An example description.", + "input_schema": {}, + "name": "Example Name", + "output_schema": {} + } + ], + "items": { + "description": "A documented callable operation exposed by a private service.", + "example": { + "description": "An example description.", + "input_schema": {}, + "name": "Example Name", + "output_schema": {} + }, + "properties": { + "description": { + "description": "Human-readable guidance describing when and why to call the operation.", + "example": "An example description.", + "type": "string" + }, + "input_schema": { + "description": "JSON Schema Draft 7 object describing the operation's argument object.", + "example": {}, + "type": "object" + }, + "name": { + "description": "Stable operation name used when invoking the private service.", + "example": "Example Name", + "type": "string" + }, + "output_schema": { + "description": "Optional JSON Schema Draft 7 object describing the successful result.", + "example": {}, + "type": "object" + } + }, + "required": [ + "name", + "description", + "input_schema" + ], + "type": "object" + }, + "type": "array" + }, "org": { "description": "Organization ID or slug. Required for developer and server callers.", "example": "string", "type": "string" - }, - "private_service": { - "description": "Private service ID (`pvs_...`).", - "example": "string", - "type": "string" } }, "required": [ - "private_service" + "functions" ], "type": "object" } @@ -53068,11 +62118,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreatedPrivateServiceEnrollment" + "$ref": "#/components/schemas/PrivateService" } } }, - "description": "Successful response" + "description": "The newly created private service." }, "400": { "description": "Bad request" @@ -53090,16 +62140,16 @@ "description": "Private service control is unavailable" } }, - "summary": "Create a private service enrollment", + "summary": "Create a private service", "x-auth": [ "publishable_key", "bearer" ] } }, - "/api/v1/private_service_enrollments/{private_service_enrollment_id}": { + "/api/v1/private_services/{private_service_id}": { "get": { - "operationId": "get_api_v1_private_service_enrollments__private_service_enrollment_id", + "operationId": "get_api_v1_private_services__private_service_id", "parameters": [ { "description": "Organization ID or slug. Required for developer and server callers.", @@ -53112,10 +62162,9 @@ } }, { - "description": "Canonical certificate-bound service identity.", "example": "string", "in": "path", - "name": "private_service_enrollment_id", + "name": "private_service_id", "required": true, "schema": { "type": "string" @@ -53127,7 +62176,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivateServiceEnrollment" + "$ref": "#/components/schemas/PrivateService" } } }, @@ -53146,45 +62195,86 @@ "description": "Private service control is unavailable" } }, - "summary": "Retrieve a private service enrollment", + "summary": "Retrieve a private service", "x-auth": [ "publishable_key", "bearer" ] } }, - "/api/v1/private_services": { - "get": { - "operationId": "get_api_v1_private_services", - "parameters": [ - { - "description": "Organization ID or slug. Required for developer and server callers.", - "example": "string", - "in": "query", - "name": "org", - "required": false, - "schema": { - "type": "string" + "/api/v1/relays": { + "post": { + "description": "Only the owning user in the same app may access this relay.", + "operationId": "post_api_v1_relays", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "purpose": "string", + "ttl_seconds": 1 + }, + "properties": { + "purpose": { + "example": "string", + "type": "string" + }, + "ttl_seconds": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "purpose", + "ttl_seconds" + ], + "type": "object" + } } }, - { - "description": "Page number. Defaults to 1.", - "example": 1, - "in": "query", - "name": "page", - "required": false, - "schema": { - "type": "integer" - } + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Relay" + } + } + }, + "description": "Successful response" + }, + "403": { + "description": "Forbidden" }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Create an owner-only relay", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/relays/{relay}": { + "delete": { + "description": "Only the owning user in the same app may access this relay.", + "operationId": "delete_api_v1_relays__relay", + "parameters": [ { - "description": "Results per page. Defaults to 25; maximum is 100.", - "example": 1, - "in": "query", - "name": "page_size", - "required": false, + "example": "string", + "in": "path", + "name": "relay", + "required": true, "schema": { - "type": "integer" + "type": "string" } } ], @@ -53193,128 +62283,52 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivateServicePage" + "$ref": "#/components/schemas/Relay" } } }, "description": "Successful response" }, - "400": { - "description": "Bad request" - }, "403": { "description": "Forbidden" }, "404": { "description": "Not found" }, - "503": { - "description": "Private service control is unavailable" + "422": { + "description": "Invalid parameters" } }, - "summary": "List private services", + "summary": "Delete an owner-only relay", "x-auth": [ "publishable_key", "bearer" ] }, - "post": { - "description": "Creates one immutable private service in the selected organization.", - "operationId": "post_api_v1_private_services", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "example": { - "functions": [ - { - "description": "An example description.", - "input_schema": {}, - "name": "Example Name", - "output_schema": {} - } - ], - "org": "string" - }, - "properties": { - "functions": { - "description": "Complete callable contracts. Input schemas are required; output schemas are optional.", - "example": [ - { - "description": "An example description.", - "input_schema": {}, - "name": "Example Name", - "output_schema": {} - } - ], - "items": { - "description": "A documented callable operation exposed by a private service.", - "example": { - "description": "An example description.", - "input_schema": {}, - "name": "Example Name", - "output_schema": {} - }, - "properties": { - "description": { - "description": "Human-readable guidance describing when and why to call the operation.", - "example": "An example description.", - "type": "string" - }, - "input_schema": { - "description": "JSON Schema Draft 7 object describing the operation's argument object.", - "example": {}, - "type": "object" - }, - "name": { - "description": "Stable operation name used when invoking the private service.", - "example": "Example Name", - "type": "string" - }, - "output_schema": { - "description": "Optional JSON Schema Draft 7 object describing the successful result.", - "example": {}, - "type": "object" - } - }, - "required": [ - "name", - "description", - "input_schema" - ], - "type": "object" - }, - "type": "array" - }, - "org": { - "description": "Organization ID or slug. Required for developer and server callers.", - "example": "string", - "type": "string" - } - }, - "required": [ - "functions" - ], - "type": "object" - } + "get": { + "description": "Only the owning user in the same app may access this relay.", + "operationId": "get_api_v1_relays__relay", + "parameters": [ + { + "example": "string", + "in": "path", + "name": "relay", + "required": true, + "schema": { + "type": "string" } - }, - "required": true - }, + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivateService" + "$ref": "#/components/schemas/Relay" } } }, - "description": "The newly created private service." - }, - "400": { - "description": "Bad request" + "description": "Successful response" }, "403": { "description": "Forbidden" @@ -53322,69 +62336,76 @@ "404": { "description": "Not found" }, - "409": { - "description": "Conflict" - }, - "503": { - "description": "Private service control is unavailable" + "422": { + "description": "Invalid parameters" } }, - "summary": "Create a private service", + "summary": "Show an owner-only relay", "x-auth": [ "publishable_key", "bearer" ] } }, - "/api/v1/private_services/{private_service_id}": { - "get": { - "operationId": "get_api_v1_private_services__private_service_id", + "/api/v1/relays/{relay}/extend": { + "post": { + "description": "Only the owning user in the same app may access this relay.", + "operationId": "post_api_v1_relays__relay_extend", "parameters": [ - { - "description": "Organization ID or slug. Required for developer and server callers.", - "example": "string", - "in": "query", - "name": "org", - "required": false, - "schema": { - "type": "string" - } - }, { "example": "string", "in": "path", - "name": "private_service_id", + "name": "relay", "required": true, "schema": { "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "ttl_seconds": 1 + }, + "properties": { + "ttl_seconds": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "ttl_seconds" + ], + "type": "object" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivateService" + "$ref": "#/components/schemas/Relay" } } }, "description": "Successful response" }, - "400": { - "description": "Bad request" - }, "403": { "description": "Forbidden" }, "404": { "description": "Not found" }, - "503": { - "description": "Private service control is unavailable" + "422": { + "description": "Invalid parameters" } }, - "summary": "Retrieve a private service", + "summary": "Extend an owner-only relay", "x-auth": [ "publishable_key", "bearer" @@ -53679,7 +62700,7 @@ }, "/api/v1/scripts/run": { "post": { - "description": "Executes the provided script source and returns the result value together\nwith any `print` output captured during the run. Use this endpoint to\nevaluate scripts interactively during development, or to drive automation\nfrom external tooling.\n\nThe script runs with the authenticated viewer's identity by default. Pass\n`run_as_user` to impersonate a specific user, or `run_as_agent` to run as\na specific agent, subject to the caller's delegation permissions. These two params are mutually exclusive — supplying both\nreturns a 422 error.\n\nRuntime environment variables resolved for the execution identity's app and organization are automatically injected\ninto the script's `variables.env` scope. When the script raises a runtime\nerror, the response still returns HTTP 200 with `error`, `findings`, and any\npartial output; the error is also recorded in the app's activity feed.\n", + "description": "Executes script source and returns the result value together with any\n`print` output captured during the run. Pass `script` for interactive\nevaluation, or `config` to run a stored Script config by ID. Server-side\nproducers (for example a webhook service emitting through `events.emit`)\nmust use `config`; raw source from those callers is not an authorized\napplication script.\n\nThe script runs with the authenticated viewer's identity by default. Org\ntargeting for secret-key callers comes from the `X-ArchAstro-Org-Id`\nheader, not from the body. Pass `run_as_user` to impersonate a specific\nuser, or `run_as_agent` to run as a specific agent, subject to the\ncaller's delegation permissions. These two params are mutually exclusive\n— supplying both returns a 422 error. `script` and `config` are also\nmutually exclusive.\n\nRuntime environment variables resolved for the execution identity's app and organization are automatically injected\ninto the script's `variables.env` scope. When the script raises a runtime\nerror, the response still returns HTTP 200 with `error`, `findings`, and any\npartial output; the error is also recorded in the app's activity feed.\n", "operationId": "post_api_v1_scripts_run", "parameters": [], "requestBody": { @@ -53687,13 +62708,26 @@ "application/json": { "schema": { "example": { + "config": "string", + "config_key": "string", "resolution_context_config": "string", "run_as_agent": "string", "run_as_user": "string", "scope": {}, - "script": "string" + "script": "string", + "thread": "string" }, "properties": { + "config": { + "description": "Stored Script config ID (`cfg_...`) whose current version is executed. Mutually exclusive with `script`. Required for server-side producers that must not supply raw source.", + "example": "string", + "type": "string" + }, + "config_key": { + "description": "`lookup_key` of an app-level Script config whose current version is executed. Resolved among the app's configs that belong to no organization, never an organization's config with the same key. Mutually exclusive with `script` and `config`.", + "example": "string", + "type": "string" + }, "resolution_context_config": { "description": "Optional Script config ID establishing the authorized installation context for imports.", "example": "string", @@ -53717,7 +62751,12 @@ }, "script": { "default": "", - "description": "The script source to execute. Defaults to an empty string.", + "description": "The script source to execute. Defaults to an empty string. Mutually exclusive with `config`.", + "example": "string", + "type": "string" + }, + "thread": { + "description": "Optional thread ID for agent execution. Requires run_as_agent, caller access to the thread, matching agent app and organization, and agent membership in the thread.", "example": "string", "type": "string" } @@ -53746,16 +62785,20 @@ "description": "Execution identity is not authorized" }, "404": { - "description": "Execution identity was not found" + "description": "Script config not found" }, "422": { - "description": "run_as_user and run_as_agent are mutually exclusive" + "description": "config_key is mutually exclusive with script and config" } }, "summary": "Execute a workflow script", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "scripts:run_config" ] } }, @@ -54197,7 +63240,7 @@ "description": "Unauthorized" }, "403": { - "description": "Forbidden; The Slack integration referenced by this binding is not visible to the caller; This private channel is member-managed; managing its agent requires being a member of the channel" + "description": "Forbidden; The Slack integration referenced by this binding is not visible to the caller; This private channel is member-managed; managing its agent requires verified membership in the channel. Link your Slack account from the app's Slack App Home tab so your membership can be matched, or run `/archagent connect` from inside the channel" }, "404": { "description": "Not found; Agent not found" @@ -57077,6 +66120,57 @@ ] } }, + "/api/v1/task_ownership_conversions/{job_id}": { + "get": { + "description": "Returns only the requesting user's job status, even after task visibility changes.", + "operationId": "get_api_v1_task_ownership_conversions__job_id", + "parameters": [ + { + "description": "The accepted background job ID.", + "example": 1, + "in": "path", + "name": "job_id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskOwnershipJob" + } + } + }, + "description": "Current background job state." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Read the status of a task ownership conversion", + "x-auth": [ + "publishable_key", + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "tasks:read" + ] + } + }, "/api/v1/tasks/{task}": { "delete": { "description": "Deletes a task from task lists and detail views. The task event stream is\nretained for auditability, while comments are removed and direct subtasks\nare promoted to top-level tasks.\n\nThe delete event is accepted before the read model is updated. Clients\nshould remove the task from local collections immediately; subsequent reads\nconverge once the projection processes the event.\n\nAuthenticated users may delete tasks they can access using their session\nidentity. App-scoped developer and server-to-server callers must explicitly\nsupply the task's `org` and owner. `team` or `user` identifies that owner;\nwhen neither is present, `agent` identifies an agent-owned task. With a team\nor user owner, `agent` identifies the acting principal. Each reference is\nvalidated before deletion.\n", @@ -57195,7 +66289,11 @@ "summary": "Retrieve a task", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "tasks:read" ] }, "put": { @@ -57235,6 +66333,8 @@ "owner_user": "string", "parent": "string", "priority": 1, + "project_id": "string", + "require_status_lease": true, "source_id": "string", "source_scope": "string", "source_type": "string", @@ -57242,6 +66342,8 @@ "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "string", "user": "string" }, @@ -57324,6 +66426,16 @@ "example": 1, "type": "integer" }, + "project_id": { + "description": "Project ID (`prj_...`). Omit to preserve; pass null to clear.", + "example": "string", + "type": "string" + }, + "require_status_lease": { + "description": "Session clients must set true for status changes. A live lease then requires the holder's lease_id and lease_session_id; human and host overrides remain available.", + "example": true, + "type": "boolean" + }, "source_id": { "description": "Replacement source object identity. Must be supplied with the other source fields.", "example": "string", @@ -57354,6 +66466,16 @@ }, "type": "array" }, + "target_binary": { + "description": "Replacement target binary (for example `my-cli`): a lowercase identifier of 1-64 characters. Pass together with `target_version`, or pass both as null to clear the target.", + "example": "string", + "type": "string" + }, + "target_version": { + "description": "Replacement minimum semantic version of `target_binary` (for example `0.48.9`). Must be supplied with `target_binary`.", + "example": "string", + "type": "string" + }, "team": { "description": "Explicit owning team (`tem_...`) for a developer or server-to-server call.", "example": "string", @@ -57392,7 +66514,7 @@ "description": "Task not found" }, "409": { - "description": "Task lease has expired; Task lease does not match the current holder; Task changed since it was read" + "description": "Task is leased by another coding session; Task lease has expired; Task lease does not match the current holder; Task changed since it was read" }, "422": { "description": "Invalid parameters" @@ -57696,6 +66818,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -57722,15 +66845,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -57783,6 +66910,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -57809,15 +66937,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -57859,6 +66991,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -57885,15 +67018,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -57913,7 +67050,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -58102,7 +67239,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -58241,7 +67383,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -58251,6 +67393,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -58275,13 +67423,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -58295,6 +67450,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -58302,7 +67469,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -58616,6 +67783,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -58642,15 +67810,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -58703,6 +67875,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -58729,15 +67902,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -58779,6 +67956,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -58805,15 +67983,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -58833,7 +68015,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -59022,7 +68204,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -59161,7 +68348,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -59171,6 +68358,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -59195,13 +68388,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -59215,6 +68415,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -59222,7 +68434,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -59673,7 +68885,8 @@ "example": { "comment": { "body": "Looks good to me, ready for review." - } + }, + "expected_version": 1 }, "properties": { "comment": { @@ -59692,6 +68905,11 @@ "body" ], "type": "object" + }, + "expected_version": { + "description": "Reject the comment if the task changed since this version.", + "example": 1, + "type": "integer" } }, "required": [ @@ -60444,6 +69662,94 @@ ] } }, + "/api/v1/tasks/{task}/ownership_conversions": { + "post": { + "description": "Queues ownership changes for the connected task graph across parent, child, blocker, and blocking relationships. The selected tasks and their versions are frozen when queued. Each transfer checks the requesting user’s current authority; conflicts fail the job without undoing completed transfers.", + "operationId": "post_api_v1_tasks__task_ownership_conversions", + "parameters": [ + { + "description": "Root task (`tsk_...`).", + "example": "string", + "in": "path", + "name": "task", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "dest_agent": "string", + "dest_org": "string", + "dest_team": "string", + "dest_user": "string" + }, + "properties": { + "dest_agent": { + "description": "Destination agent (`agi_...`).", + "example": "string", + "type": "string" + }, + "dest_org": { + "description": "Destination organization (`org_...`).", + "example": "string", + "type": "string" + }, + "dest_team": { + "description": "Destination team (`tem_...`).", + "example": "string", + "type": "string" + }, + "dest_user": { + "description": "Destination user (`usr_...`). Supply exactly one destination.", + "example": "string", + "type": "string" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskOwnershipJob" + } + } + }, + "description": "The queued background job." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Convert ownership of a connected task graph", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/tasks/{task}/subtasks": { "get": { "description": "Returns a bounded page of the specified task's subtasks (tasks whose\n`parent` is this task), newest first. Subtasks nest exactly one level, so\nentries never have subtasks of their own. Privileged callers explicitly\nprovide the owning `team`, `user`, or `agent` and `org`.\n", @@ -60563,6 +69869,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -60589,15 +69896,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -60650,6 +69961,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -60676,15 +69988,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -60726,6 +70042,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -60752,15 +70069,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -60780,7 +70101,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -60969,7 +70290,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -61108,7 +70434,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -61118,6 +70444,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -61142,13 +70474,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -61162,6 +70501,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -61169,7 +70520,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -61228,6 +70579,103 @@ ] } }, + "/api/v1/tasks/{task}/transfer_ownership": { + "post": { + "description": "Changes one task’s privacy owner at the supplied aggregate version. Assignment, creator, source identity, comments, and relationship IDs remain unchanged. Source and destination must have the same app and sandbox; the authenticated user must have authority over both scopes.", + "operationId": "post_api_v1_tasks__task_transfer_ownership", + "parameters": [ + { + "description": "Task (`tsk_...`).", + "example": "string", + "in": "path", + "name": "task", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "dest_agent": "string", + "dest_org": "string", + "dest_team": "string", + "dest_user": "string", + "expected_version": 1 + }, + "properties": { + "dest_agent": { + "description": "Destination agent (`agi_...`).", + "example": "string", + "type": "string" + }, + "dest_org": { + "description": "Destination organization (`org_...`).", + "example": "string", + "type": "string" + }, + "dest_team": { + "description": "Destination team (`tem_...`).", + "example": "string", + "type": "string" + }, + "dest_user": { + "description": "Destination user (`usr_...`). Supply exactly one destination.", + "example": "string", + "type": "string" + }, + "expected_version": { + "description": "The task version reviewed by the caller.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "expected_version" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Task" + } + } + }, + "description": "The task with its new owner." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict; Task changed since it was read" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Transfer one task to a different owner", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/team_memberships": { "get": { "description": "Returns a paginated list of team memberships across all teams accessible to\nthe authenticated caller. Use the `team`, `user`, and `agent` filters to\nnarrow results to a specific team or principal.\n\nThis endpoint requires S2S (service-to-service) authentication. Callers\nauthenticated with a user token receive a 401. Each membership includes the\nresolved `type` (`\"user\"`, `\"agent\"`, or `\"unknown\"`), display name, and\nprofile picture derived from the associated user or agent at request time.\n\nResults are returned in offset-based pages. Pass `page` and `page_size` to\nnavigate; the response includes `has_next` and `has_prev` to determine\nwhether adjacent pages exist.\n", @@ -62767,6 +72215,34 @@ "example": { "data": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -62803,6 +72279,34 @@ "description": "Array of artifact objects belonging to the team.", "example": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -62836,6 +72340,34 @@ "items": { "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -62866,6 +72398,180 @@ "version": 1 }, "properties": { + "acl": { + "description": "Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "nullable": true, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "agent": { "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", @@ -63169,7 +72875,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -63219,7 +72927,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -63265,7 +72975,9 @@ } ] }, + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", "created_at": "2024-01-01T00:00:00Z", + "expires_at": "2024-01-01T00:00:00Z", "fields": { "key": "value" }, @@ -63453,12 +73165,24 @@ }, "type": "object" }, + "agent": { + "description": "ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, "created_at": { "description": "When the custom object was created (ISO 8601).", "example": "2024-01-01T00:00:00Z", "format": "date-time", "type": "string" }, + "expires_at": { + "description": "Absolute expiry time (RFC 3339). Null when no expiry is set.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "fields": { "description": "Map of field names to their current values as defined by the object's schema type.", "example": { @@ -67174,6 +76898,390 @@ ] } }, + "/api/v1/teams/{team}/task_epics": { + "get": { + "description": "Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created.", + "operationId": "get_api_v1_teams__team_task_epics", + "parameters": [ + { + "example": "string", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + }, + { + "example": "string", + "in": "query", + "name": "user", + "required": false, + "schema": { + "type": "string" + } + }, + { + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source scope; supply all three source fields.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source type.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source ID.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact highest open priority, from 0 to 4.", + "example": 1, + "in": "query", + "name": "top_priority", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Include epics with no non-closed tasks; defaults to false.", + "example": true, + "in": "query", + "name": "include_closed", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Maximum epics; defaults to 50, capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque forward cursor from the preceding page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "has_more": true, + "no_epic_open_count": 1 + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "example": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "items": { + "description": "Read-only progress and scheduling summary for an epic label.", + "example": { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "blocked_count": { + "example": 1, + "type": "integer" + }, + "counts": { + "description": "Task counts keyed by status, including closed tasks.", + "example": {}, + "type": "object" + }, + "epic": { + "example": "string", + "type": "string" + }, + "next_ready": { + "example": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An open task with no unfinished prerequisite or active lease.", + "example": { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers": { + "example": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An unblocked root and the number of open tasks downstream within its epic.", + "example": { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "downstream_count": { + "example": 1, + "type": "integer" + }, + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority", + "downstream_count" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers_partial": { + "description": "True when root ranking used a truncated in-epic graph.", + "example": true, + "type": "boolean" + }, + "top_open_priority": { + "example": 1, + "nullable": true, + "type": "integer" + }, + "updated_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "epic", + "counts", + "blocked_count", + "updated_at", + "root_blockers", + "root_blockers_partial", + "next_ready" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + }, + "no_epic_open_count": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "data", + "has_more", + "no_epic_open_count" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task owner not found" + }, + "422": { + "description": "Invalid filters or owner context" + } + }, + "summary": "List an owner's epic summaries", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/teams/{team}/tasks": { "get": { "description": "Returns tasks owned by the specified user or team. You can narrow results using the\noptional filters below. By default results are returned in reverse chronological\norder (most recently created first); use `sort` and `order` to sort by due date or\npriority instead.\n\nUser-authenticated callers may list their personal tasks or tasks for teams they\nhave joined. Privileged callers provide the owner in the route; the owner's\norganization is implied by that principal. An explicit `org` is optional and,\nwhen set, must match the owner's organization.\n", @@ -67379,6 +77487,16 @@ "type": "boolean" } }, + { + "description": "When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry.", + "example": true, + "in": "query", + "name": "attention", + "required": false, + "schema": { + "type": "boolean" + } + }, { "description": "Maximum number of tasks to return. Capped at 100.", "example": 1, @@ -67443,6 +77561,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -67469,15 +77588,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -67533,6 +77656,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -67559,15 +77683,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -67609,6 +77737,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -67635,15 +77764,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -67663,7 +77796,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -67852,7 +77985,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -67991,7 +78129,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -68001,6 +78139,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -68025,13 +78169,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -68045,6 +78196,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -68052,7 +78215,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -68107,7 +78270,11 @@ "summary": "List an owner's tasks", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "tasks:read" ] }, "post": { @@ -68148,6 +78315,7 @@ "owner_user": "string", "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", "priority": 2, + "project_id": "string", "source_id": "ArchAstro/firstlanding", "source_scope": "github.com", "source_type": "repository", @@ -68156,6 +78324,8 @@ "backend", "q3-launch" ], + "target_binary": "my-cli", + "target_version": "0.48.9", "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" }, "user": "string" @@ -68189,6 +78359,7 @@ "owner_user": "string", "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", "priority": 2, + "project_id": "string", "source_id": "ArchAstro/firstlanding", "source_scope": "github.com", "source_type": "repository", @@ -68197,6 +78368,8 @@ "backend", "q3-launch" ], + "target_binary": "my-cli", + "target_version": "0.48.9", "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" }, "properties": { @@ -68260,6 +78433,11 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Pass null to clear it.", + "example": "string", + "type": "string" + }, "source_id": { "description": "Source object identity (for example `ArchAstro/firstlanding`).", "example": "ArchAstro/firstlanding", @@ -68291,6 +78469,16 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`.", + "example": "my-cli", + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`.", + "example": "0.48.9", + "type": "string" + }, "thread": { "description": "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation.", "example": "thr_01j3k5m7n9p2r4s6t8v0w1x2", @@ -68449,6 +78637,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -68475,15 +78664,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -68540,6 +78733,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -68566,15 +78760,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -68620,6 +78818,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -68646,15 +78845,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -68699,6 +78902,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -68725,15 +78929,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -68775,6 +78983,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -68801,15 +79010,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -68829,7 +79042,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -69018,7 +79231,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -69157,7 +79375,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -69167,6 +79385,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -69191,13 +79415,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -69211,6 +79442,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -69218,7 +79461,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -69285,6 +79528,130 @@ ] } }, + "/api/v1/teams/{team}/tasks/by-source": { + "put": { + "description": "Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted.", + "operationId": "put_api_v1_teams__team_tasks_by-source", + "parameters": [ + { + "description": "Team owner.", + "example": "string", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "description": "An example description.", + "epic": "string", + "name": "Example Name", + "org": "string", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "status": "string", + "user": "string" + }, + "properties": { + "agent": { + "description": "Explicit acting agent for privileged callers.", + "example": "string", + "type": "string" + }, + "description": { + "description": "Initial task description.", + "example": "An example description.", + "type": "string" + }, + "epic": { + "description": "Initial epic grouping.", + "example": "string", + "type": "string" + }, + "name": { + "description": "Initial task name.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "Explicit organization for privileged callers.", + "example": "string", + "type": "string" + }, + "source_id": { + "description": "Source identity, for example owner/repo#123.", + "example": "string", + "type": "string" + }, + "source_scope": { + "description": "Source scope, for example github.", + "example": "string", + "type": "string" + }, + "source_type": { + "description": "Source type, for example pull_request.", + "example": "string", + "type": "string" + }, + "status": { + "description": "Initial task status.", + "example": "string", + "type": "string" + }, + "user": { + "description": "User owner, or explicit acting user on a team route.", + "example": "string", + "type": "string" + } + }, + "required": [ + "source_scope", + "source_type", + "source_id", + "name" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskEnsureResult" + } + } + }, + "description": "The task and whether this request created it." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task owner not found" + }, + "422": { + "description": "Validation error" + } + }, + "summary": "Find or create a task by source", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/teams/{team}/tasks/metrics": { "get": { "description": "Returns task activity metrics scoped to one team (the Network in ArchAgents).\n`open` counts currently open or in-progress team tasks, `created` counts\ntasks inserted in the UTC-day window, and `completed` counts tasks whose\n`closed_at` falls in that window while still in a closed status. Hard-deleted\ntasks are absent from the projection and therefore omitted. The daily series\nis zero-filled across the requested window and includes reconstructed\nend-of-day `open` stock (from remaining projection rows' inserted_at/closed_at).\n\nAny authenticated team member may read this count-only Network summary.\nRequests from callers without team access return 404 so team existence is\nnot disclosed.\n", @@ -69400,7 +79767,7 @@ }, "/api/v1/teams/{team}/tasks/ready": { "get": { - "description": "Returns open tasks with no unfinished blockers and no active session lease.\nReadiness is calculated by the server from the current task projection. It is\na snapshot, not a reservation; claim a task lease before starting work.\n\nPass `explain=true` to include every open task with a stable readiness reason.\n", + "description": "Returns open tasks with no unfinished blockers and no active session lease.\nReadiness is calculated by the server from the current task projection. It is\na snapshot, not a reservation; claim a task lease before starting work.\n\nResults are ordered by priority (0, the most urgent, first), then oldest\nfirst, so the first page is always the most urgent ready work.\n\nPass `explain=true` to include every open task with a stable readiness reason.\n", "operationId": "get_api_v1_teams__team_tasks_ready", "parameters": [ { @@ -69561,6 +79928,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -69587,15 +79955,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -69657,6 +80029,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -69683,15 +80056,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -69737,6 +80114,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -69763,15 +80141,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -69834,6 +80216,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -69860,15 +80243,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -69888,7 +80275,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -70077,7 +80464,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -70216,7 +80608,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -70226,6 +80618,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -70250,13 +80648,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -70270,6 +80675,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -70277,7 +80694,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -70563,6 +80980,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -70589,15 +81007,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70652,6 +81074,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -70678,15 +81101,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70728,6 +81155,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -70754,15 +81182,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70782,7 +81214,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -70971,7 +81403,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -71110,7 +81547,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -71120,6 +81557,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -71144,13 +81587,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -71164,6 +81614,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -71171,7 +81633,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -71369,8 +81831,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -71384,8 +81848,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -71401,7 +81867,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -71781,8 +82247,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -71796,8 +82264,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -71813,7 +82283,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -72190,8 +82660,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -72205,8 +82677,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -72222,7 +82696,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -72825,8 +83299,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -72840,8 +83316,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -72857,7 +83335,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -73233,8 +83711,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -73248,8 +83728,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -73287,8 +83769,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -73302,8 +83786,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -73460,8 +83946,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -73475,8 +83963,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -73484,8 +83974,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -73499,12 +83991,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -73517,6 +84017,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -73597,6 +84103,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -73609,6 +84121,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -73668,7 +84187,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -73677,7 +84196,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -73699,7 +84218,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -73911,8 +84430,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -73926,8 +84447,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -73943,7 +84466,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -76933,7 +87456,11 @@ "summary": "Retrieve a message", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "threads:read" ] }, "put": { @@ -77823,6 +88350,34 @@ "example": { "data": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -77859,6 +88414,34 @@ "description": "Array of artifact objects produced during the thread's conversation.", "example": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -77892,6 +88475,34 @@ "items": { "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -77922,6 +88533,180 @@ "version": 1 }, "properties": { + "acl": { + "description": "Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "nullable": true, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "agent": { "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", @@ -78115,6 +88900,350 @@ ] } }, + "/api/v1/threads/{thread}/computers": { + "get": { + "description": "Returns all live computers in the visible thread, oldest first, without pagination.", + "operationId": "get_api_v1_threads__thread_computers", + "parameters": [ + { + "description": "Thread ID (`thr_...`).", + "example": "string", + "in": "path", + "name": "thread", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentComputerListResponse" + } + } + }, + "description": "The thread's live computers." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Thread not found" + } + }, + "summary": "List a thread's computers", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "post": { + "description": "Creates a computer owned by the thread's owner. Requires permission to manage the thread; every thread viewer may use the computer.", + "operationId": "post_api_v1_threads__thread_computers", + "parameters": [ + { + "description": "Thread ID (`thr_...`).", + "example": "string", + "in": "path", + "name": "thread", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "config": {}, + "key": "string", + "metadata": { + "key": "value" + }, + "name": "Example Name", + "provider": "string", + "region": "string" + }, + "properties": { + "acl": { + "description": "Initial access control list.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "config": { + "description": "Provider configuration; top-level provider takes precedence.", + "example": {}, + "type": "object" + }, + "key": { + "description": "Stable key unique within this thread.", + "example": "string", + "type": "string" + }, + "metadata": { + "description": "Arbitrary computer metadata.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Human-readable computer name.", + "example": "Example Name", + "type": "string" + }, + "provider": { + "description": "Compute backend: sprites (default), vercel or e2b.", + "example": "string", + "type": "string" + }, + "region": { + "description": "Provider region; defaults to iad.", + "example": "string", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentComputer" + } + } + }, + "description": "The newly created computer. Returns HTTP 201." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Thread not found" + }, + "422": { + "description": "Unsupported thread or invalid computer" + } + }, + "summary": "Provision a computer for a thread", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/threads/{thread}/mark_read": { "post": { "description": "Records that a user has read up to a specific message in the thread. Unread\nindicators and badge counts are cleared up to the specified message.\n\nYou must supply exactly one of `last_read_message` or `use_latest_message`.\nOmitting both returns 400. If `use_latest_message` is `true` and the thread\nhas no messages, the request succeeds silently with no state change.\n\nFor server-to-server (S2S) requests where no user identity is present in the\ntoken, the `user` param is required to identify whose read state to update.\n", @@ -78559,7 +89688,7 @@ }, "/api/v1/threads/{thread}/messages": { "get": { - "description": "Returns a cursor-paginated list of messages belonging to the specified thread,\nordered from oldest to newest. Supply `before_cursor`, `after_cursor`, or both\nto page through or bound the result set; omit both to receive the most recent page.\nSupply `anchor` and `direction` to fetch a window before, after, or around a\nspecific message. Use `anchor=last_matching&anchor_agent_mode=embedded` to\nresolve the anchor from the latest embedded-agent message, and add\n`anchor_agent` to scope that resolution to a single sender agent.\nSupply `metadata` as a JSON-encoded structured expression to filter message\nmetadata before cursor pagination or anchored window limits are applied.\n\nThe authenticated user must have access to the thread's owner (workspace or user).\nA 403 is returned if the thread exists but is not accessible to the caller; a 404\nis returned if the thread does not exist or is not visible to the authenticated user.\n\nThreads with `kind: feed` return only top-level posts, each with its newest\nreplies inline under `replies` and reply cursors for paging the rest.\n\nPass `include_reply_counts: true` to annotate each message with the number of\nthreaded replies it has received. This adds a small amount of latency and should\nbe omitted when reply counts are not needed.\n", + "description": "Returns a cursor-paginated list of messages belonging to the specified thread,\nordered from oldest to newest. Supply `before_cursor`, `after_cursor`, or both\nto page through or bound the result set; omit both to receive the most recent page.\nSupply `anchor` and `direction` to fetch a window before, after, or around a\nspecific message. Use `anchor=last_matching&anchor_agent_mode=embedded` to\nresolve the anchor from the latest embedded-agent message, and add\n`anchor_agent` to scope that resolution to a single sender agent.\nSupply `metadata` as a JSON-encoded structured expression to filter message\nmetadata before cursor pagination or anchored window limits are applied.\n\nThe authenticated user must have access to the thread's owner (workspace or user).\nA 403 is returned if the thread exists but is not accessible to the caller; a 404\nis returned if the thread does not exist or is not visible to the authenticated user.\n\nThreads with `kind: feed` return only top-level posts, each with its newest\nreplies inline under `replies` and reply cursors for paging the rest.\nSet `include_replies: true` to page messages and replies together by creation\norder instead, without inline reply previews. Metadata filters and the page\nlimit apply to the updates themselves, before any client-side grouping.\n\nPass `include_reply_counts: true` to annotate each message with the number of\nthreaded replies it has received. This adds a small amount of latency and should\nbe omitted when reply counts are not needed.\n", "operationId": "get_api_v1_threads__thread_messages", "parameters": [ { @@ -78690,6 +89819,16 @@ "type": "integer" } }, + { + "description": "Include replies in feed message pagination instead of returning root posts with inline replies. Defaults to false.", + "example": true, + "in": "query", + "name": "include_replies", + "required": false, + "schema": { + "type": "boolean" + } + }, { "description": "Message ID (`msg_...`) to use as a window anchor, or `last_matching` to resolve the anchor from the latest message matching the anchor filters. Cannot be combined with `before_cursor` or `after_cursor`.", "example": "string", @@ -78867,8 +90006,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -78882,8 +90023,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -78899,7 +90042,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -79023,8 +90166,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79038,8 +90183,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -79055,7 +90202,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -79192,8 +90339,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79207,8 +90356,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -79224,7 +90375,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -79340,8 +90491,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79355,8 +90508,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -79372,7 +90527,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "string", @@ -79476,8 +90631,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79491,8 +90648,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -79508,7 +90667,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -79891,8 +91050,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79906,8 +91067,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -79945,8 +91108,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -79960,8 +91125,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -80118,8 +91285,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -80133,8 +91302,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -80142,8 +91313,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -80157,12 +91330,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -80175,6 +91356,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -80255,6 +91442,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -80267,6 +91460,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -80326,7 +91526,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -80335,7 +91535,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -80357,7 +91557,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -80569,8 +91769,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -80584,8 +91786,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -80601,7 +91805,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -80875,7 +92079,11 @@ "summary": "List messages in a thread", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "threads:read" ] } }, @@ -81257,7 +92465,11 @@ "summary": "Search messages in a thread", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "threads:read" ] } }, @@ -81933,7 +93145,7 @@ }, "/api/v1/users/{user}": { "get": { - "description": "Returns the user identified by `user`. The authenticated user must share\nat least one team with the target user; requests for users outside any\nshared team are rejected with 403.\n\nA user may always retrieve their own profile with this endpoint. Use the\n`GET /users/me` endpoint as a convenience alias for retrieving the\nauthenticated user without specifying an ID.\n", + "description": "Returns the user identified by `user`. The authenticated user must share\nan organization or at least one team with the target user; other requests\nare rejected with 403.\n\nA user may always retrieve their own profile with this endpoint. Use the\n`GET /users/me` endpoint as a convenience alias for retrieving the\nauthenticated user without specifying an ID.\n", "operationId": "get_api_v1_users__user", "parameters": [ { @@ -82017,6 +93229,34 @@ "example": { "data": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -82053,6 +93293,34 @@ "description": "Array of artifact objects belonging to the user.", "example": [ { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -82086,6 +93354,34 @@ "items": { "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", "example": { + "acl": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", @@ -82116,6 +93412,180 @@ "version": 1 }, "properties": { + "acl": { + "description": "Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + "example": { + "add": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "grants": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "remove": [ + { + "principal": "string", + "principal_type": "user" + } + ] + }, + "nullable": true, + "properties": { + "add": { + "description": "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "grants": { + "description": "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + "example": [ + { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "A single access-control grant that pairs a principal with the set of actions it is allowed to perform.", + "example": { + "actions": [ + "read", + "write" + ], + "principal": "string", + "principal_type": "user" + }, + "properties": { + "actions": { + "description": "Array of action strings the principal is permitted to perform, e.g. `[\"read\", \"write\"]`. Must contain at least one entry.", + "example": [ + "read", + "write" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "principal": { + "description": "The identifier of the principal. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`; omit entirely when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal receiving the grant. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type", + "actions" + ], + "type": "object" + }, + "type": "array" + }, + "remove": { + "description": "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + "example": [ + { + "principal": "string", + "principal_type": "user" + } + ], + "items": { + "description": "Identifies a principal to be removed from an access-control list.", + "example": { + "principal": "string", + "principal_type": "user" + }, + "properties": { + "principal": { + "description": "The identifier of the principal to remove. A string ID for `\"user\"`, `\"team\"`, `\"org\"`, and `\"agent\"` types; one of `\"admin\"`, `\"member\"`, or `\"viewer\"` for `\"org_role\"`. Omit when `principal_type` is `\"everyone\"`.", + "example": "string", + "type": "string" + }, + "principal_type": { + "description": "The kind of principal to remove. One of `\"user\"`, `\"team\"`, `\"org\"`, `\"org_role\"`, `\"agent\"`, or `\"everyone\"`.", + "example": "user", + "type": "string" + } + }, + "required": [ + "principal_type" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, "agent": { "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", @@ -83111,6 +94581,390 @@ ] } }, + "/api/v1/users/{user}/task_epics": { + "get": { + "description": "Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created.", + "operationId": "get_api_v1_users__user_task_epics", + "parameters": [ + { + "example": "string", + "in": "query", + "name": "team", + "required": false, + "schema": { + "type": "string" + } + }, + { + "example": "string", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + }, + { + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source scope; supply all three source fields.", + "example": "string", + "in": "query", + "name": "source_scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source type.", + "example": "string", + "in": "query", + "name": "source_type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact source ID.", + "example": "string", + "in": "query", + "name": "source_id", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact highest open priority, from 0 to 4.", + "example": 1, + "in": "query", + "name": "top_priority", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Include epics with no non-closed tasks; defaults to false.", + "example": true, + "in": "query", + "name": "include_closed", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Maximum epics; defaults to 50, capped at 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque forward cursor from the preceding page.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "has_more": true, + "no_epic_open_count": 1 + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "example": [ + { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "items": { + "description": "Read-only progress and scheduling summary for an epic label.", + "example": { + "blocked_count": 1, + "counts": {}, + "epic": "string", + "next_ready": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "root_blockers_partial": true, + "top_open_priority": 1, + "updated_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "blocked_count": { + "example": 1, + "type": "integer" + }, + "counts": { + "description": "Task counts keyed by status, including closed tasks.", + "example": {}, + "type": "object" + }, + "epic": { + "example": "string", + "type": "string" + }, + "next_ready": { + "example": [ + { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An open task with no unfinished prerequisite or active lease.", + "example": { + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers": { + "example": [ + { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + } + ], + "items": { + "description": "An unblocked root and the number of open tasks downstream within its epic.", + "example": { + "downstream_count": 1, + "id": "string", + "name": "Example Name", + "priority": 1, + "status": "string" + }, + "properties": { + "downstream_count": { + "example": 1, + "type": "integer" + }, + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "priority": { + "example": 1, + "type": "integer" + }, + "status": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "name", + "status", + "priority", + "downstream_count" + ], + "type": "object" + }, + "type": "array" + }, + "root_blockers_partial": { + "description": "True when root ranking used a truncated in-epic graph.", + "example": true, + "type": "boolean" + }, + "top_open_priority": { + "example": 1, + "nullable": true, + "type": "integer" + }, + "updated_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "epic", + "counts", + "blocked_count", + "updated_at", + "root_blockers", + "root_blockers_partial", + "next_ready" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + }, + "no_epic_open_count": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "data", + "has_more", + "no_epic_open_count" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task owner not found" + }, + "422": { + "description": "Invalid filters or owner context" + } + }, + "summary": "List an owner's epic summaries", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/users/{user}/tasks": { "get": { "description": "Returns tasks owned by the specified user or team. You can narrow results using the\noptional filters below. By default results are returned in reverse chronological\norder (most recently created first); use `sort` and `order` to sort by due date or\npriority instead.\n\nUser-authenticated callers may list their personal tasks or tasks for teams they\nhave joined. Privileged callers provide the owner in the route; the owner's\norganization is implied by that principal. An explicit `org` is optional and,\nwhen set, must match the owner's organization.\n", @@ -83316,6 +95170,16 @@ "type": "boolean" } }, + { + "description": "When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry.", + "example": true, + "in": "query", + "name": "attention", + "required": false, + "schema": { + "type": "boolean" + } + }, { "description": "Maximum number of tasks to return. Capped at 100.", "example": 1, @@ -83380,6 +95244,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -83406,15 +95271,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -83470,6 +95339,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -83496,15 +95366,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -83546,6 +95420,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -83572,15 +95447,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -83600,7 +95479,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -83789,7 +95668,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -83928,7 +95812,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -83938,6 +95822,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -83962,13 +95852,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -83982,6 +95879,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -83989,7 +95898,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -84044,7 +95953,11 @@ "summary": "List an owner's tasks", "x-auth": [ "publishable_key", - "bearer" + "bearer", + "device_flow" + ], + "x-required-scopes": [ + "tasks:read" ] }, "post": { @@ -84085,6 +95998,7 @@ "owner_user": "string", "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", "priority": 2, + "project_id": "string", "source_id": "ArchAstro/firstlanding", "source_scope": "github.com", "source_type": "repository", @@ -84093,6 +96007,8 @@ "backend", "q3-launch" ], + "target_binary": "my-cli", + "target_version": "0.48.9", "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" }, "team": "string" @@ -84126,6 +96042,7 @@ "owner_user": "string", "parent": "tsk_01j3k5m7n9p2r4s6t8v0w1x2", "priority": 2, + "project_id": "string", "source_id": "ArchAstro/firstlanding", "source_scope": "github.com", "source_type": "repository", @@ -84134,6 +96051,8 @@ "backend", "q3-launch" ], + "target_binary": "my-cli", + "target_version": "0.48.9", "thread": "thr_01j3k5m7n9p2r4s6t8v0w1x2" }, "properties": { @@ -84197,6 +96116,11 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Pass null to clear it.", + "example": "string", + "type": "string" + }, "source_id": { "description": "Source object identity (for example `ArchAstro/firstlanding`).", "example": "ArchAstro/firstlanding", @@ -84228,6 +96152,16 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`.", + "example": "my-cli", + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`.", + "example": "0.48.9", + "type": "string" + }, "thread": { "description": "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation.", "example": "thr_01j3k5m7n9p2r4s6t8v0w1x2", @@ -84386,6 +96320,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -84412,15 +96347,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -84477,6 +96416,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -84503,15 +96443,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -84557,6 +96501,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -84583,15 +96528,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -84636,6 +96585,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -84662,15 +96612,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -84712,6 +96666,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -84738,15 +96693,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -84766,7 +96725,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -84955,7 +96914,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -85094,7 +97058,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -85104,6 +97068,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -85128,13 +97098,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -85148,6 +97125,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -85155,7 +97144,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -85222,9 +97211,133 @@ ] } }, + "/api/v1/users/{user}/tasks/by-source": { + "put": { + "description": "Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted.", + "operationId": "put_api_v1_users__user_tasks_by-source", + "parameters": [ + { + "description": "User owner, or explicit acting user on a team route.", + "example": "string", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "description": "An example description.", + "epic": "string", + "name": "Example Name", + "org": "string", + "source_id": "string", + "source_scope": "string", + "source_type": "string", + "status": "string", + "team": "string" + }, + "properties": { + "agent": { + "description": "Explicit acting agent for privileged callers.", + "example": "string", + "type": "string" + }, + "description": { + "description": "Initial task description.", + "example": "An example description.", + "type": "string" + }, + "epic": { + "description": "Initial epic grouping.", + "example": "string", + "type": "string" + }, + "name": { + "description": "Initial task name.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "Explicit organization for privileged callers.", + "example": "string", + "type": "string" + }, + "source_id": { + "description": "Source identity, for example owner/repo#123.", + "example": "string", + "type": "string" + }, + "source_scope": { + "description": "Source scope, for example github.", + "example": "string", + "type": "string" + }, + "source_type": { + "description": "Source type, for example pull_request.", + "example": "string", + "type": "string" + }, + "status": { + "description": "Initial task status.", + "example": "string", + "type": "string" + }, + "team": { + "description": "Team owner.", + "example": "string", + "type": "string" + } + }, + "required": [ + "source_scope", + "source_type", + "source_id", + "name" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskEnsureResult" + } + } + }, + "description": "The task and whether this request created it." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task owner not found" + }, + "422": { + "description": "Validation error" + } + }, + "summary": "Find or create a task by source", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/users/{user}/tasks/ready": { "get": { - "description": "Returns open tasks with no unfinished blockers and no active session lease.\nReadiness is calculated by the server from the current task projection. It is\na snapshot, not a reservation; claim a task lease before starting work.\n\nPass `explain=true` to include every open task with a stable readiness reason.\n", + "description": "Returns open tasks with no unfinished blockers and no active session lease.\nReadiness is calculated by the server from the current task projection. It is\na snapshot, not a reservation; claim a task lease before starting work.\n\nResults are ordered by priority (0, the most urgent, first), then oldest\nfirst, so the first page is always the most urgent ready work.\n\nPass `explain=true` to include every open task with a stable readiness reason.\n", "operationId": "get_api_v1_users__user_tasks_ready", "parameters": [ { @@ -85385,6 +97498,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -85411,15 +97525,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -85481,6 +97599,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -85507,15 +97626,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -85561,6 +97684,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -85587,15 +97711,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -85658,6 +97786,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -85684,15 +97813,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -85712,7 +97845,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -85901,7 +98034,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -86040,7 +98178,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -86050,6 +98188,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -86074,13 +98218,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -86094,6 +98245,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -86101,7 +98264,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -86387,6 +98550,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -86413,15 +98577,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -86476,6 +98644,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -86502,15 +98671,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -86552,6 +98725,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -86578,15 +98752,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -86606,7 +98784,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -86795,7 +98973,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -86934,7 +99117,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -86944,6 +99127,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -86968,13 +99157,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -86988,6 +99184,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -86995,7 +99203,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -87240,8 +99448,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -87255,8 +99465,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -87272,7 +99484,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -87652,8 +99864,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -87667,8 +99881,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -87684,7 +99900,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -88061,8 +100277,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -88076,8 +100294,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -88093,7 +100313,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -88696,8 +100916,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -88711,8 +100933,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -88728,7 +100952,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -89104,8 +101328,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -89119,8 +101345,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -89158,8 +101386,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -89173,8 +101403,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -89331,8 +101563,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -89346,8 +101580,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -89355,8 +101591,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -89370,12 +101608,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -89388,6 +101634,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -89468,6 +101720,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -89480,6 +101738,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -89539,7 +101804,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -89548,7 +101813,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -89570,7 +101835,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -89782,8 +102047,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -89797,8 +102064,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -89814,7 +102083,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -93658,7 +105927,7 @@ }, "/api/v1/workflows/run": { "post": { - "description": "Executes a single workflow node or a complete workflow graph and returns the result.\nSupply either `node` (to run one node in isolation) or `graph` (to execute a full\ndirected graph); the two parameters are mutually exclusive and exactly one must be\nprovided.\n\nThe authenticated developer is used as the default workflow principal for entitlement\nchecks on billable operations such as LLM calls. Supply `run_as_user` or\n`run_as_agent` to override the acting identity for the execution — at most one\noverride may be set per request.\n\nExecution errors are logged to the activity feed under the acting identity so they\nappear alongside automation-driven workflow errors in operator dashboards. A 422\nresponse is returned when execution fails; the response body includes the error\nmessage and any partial records produced before the failure.\n", + "description": "Executes a single workflow node or a complete workflow graph and returns the result.\nSupply either `node` (to run one node in isolation) or `graph` (to execute a full\ndirected graph); the two parameters are mutually exclusive and exactly one must be\nprovided.\n\nThe authenticated developer is used as the default workflow principal for entitlement\nchecks on billable operations such as LLM calls. Supply `run_as_user` or\n`run_as_agent` to override the acting identity for the execution — at most one\noverride may be set per request. Overrides are subject to the caller's\ndelegation permissions, the same ones `/scripts/run` applies: app secret keys\nand developer tokens may run as any user or agent in the app; other callers may\nrun as themselves, and as an agent they own (org admins as any agent in their\norg). A refused override returns 403 before anything runs.\n\nExecution errors are logged to the activity feed under the acting identity so they\nappear alongside automation-driven workflow errors in operator dashboards. A 422\nresponse is returned when execution fails; the response body includes the error\nmessage and any partial records produced before the failure.\n", "operationId": "post_api_v1_workflows_run", "parameters": [], "requestBody": { @@ -93826,6 +106095,12 @@ "401": { "description": "Unauthorized" }, + "403": { + "description": "Execution identity is not authorized" + }, + "404": { + "description": "Execution identity was not found" + }, "422": { "description": "Execution failed" } @@ -94114,7 +106389,8 @@ "device_code": "string", "grant_type": "string", "redirect_uri": "https://example.com", - "refresh_token": "string" + "refresh_token": "string", + "resource": "string" }, "properties": { "client": { @@ -94151,6 +106427,11 @@ "description": "Refresh token received from a previous token response. Required for `\"refresh_token\"` grants. The token is rotated on each successful call.", "example": "string", "type": "string" + }, + "resource": { + "description": "Resource audience. Required for code exchange and refresh of a token bound to a resource server; must match the consent grant.", + "example": "string", + "type": "string" } }, "required": [ @@ -96501,6 +108782,827 @@ "bearer" ] }, + { + "description": "Authenticated collaborative editing for durable Artifacts.\n\nThe live OT revision and `dirty` flag describe synchronization, not durability.\nOnly `checkpointed_revision` and checkpoint events confirm a completed save;\nprocess or node loss can discard the dirty tail after the last successful save.\n", + "joins": [ + { + "description": "Join an Artifact's authenticated live editing session.", + "name": "join_artifact", + "params": { + "example": { + "artifact_id": "string", + "as_agent": "string" + }, + "properties": { + "artifact_id": { + "example": "string", + "type": "string" + }, + "as_agent": { + "example": "string", + "type": "string" + } + }, + "required": [ + "artifact_id" + ], + "type": "object" + }, + "pattern": "api:artifact:{artifact_id}", + "returns": { + "description": "Authoritative live Artifact snapshot returned by an editing-channel join.", + "example": { + "actor_id": "string", + "actors": [ + { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "meta": {} + } + ], + "artifact": "string", + "artifact_version": "string", + "artifact_version_number": 1, + "can_write": true, + "checkpoint_error": "string", + "checkpointed_revision": 1, + "connection_id": "string", + "content": "string", + "dirty": true, + "incarnation": "string", + "revision": 1 + }, + "properties": { + "actor_id": { + "description": "Server-issued identity for this channel join.", + "example": "string", + "type": "string" + }, + "actors": { + "example": [ + { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "meta": {} + } + ], + "items": { + "description": "A server-issued collaborator identity and its ephemeral presence.", + "example": { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "meta": {} + }, + "properties": { + "actor_id": { + "description": "Opaque identity unique to this channel join.", + "example": "string", + "type": "string" + }, + "cursor": { + "description": "Current code-point cursor, or null.", + "example": { + "position": 1, + "selection_end": 1 + }, + "nullable": true, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "meta": { + "description": "Server-derived collaborator display metadata.", + "example": {}, + "type": "object" + } + }, + "required": [ + "actor_id", + "meta" + ], + "type": "object" + }, + "type": "array" + }, + "artifact": { + "description": "Public Artifact ID (`art_...`).", + "example": "string", + "type": "string" + }, + "artifact_version": { + "description": "Durable version ID (`artv_...`).", + "example": "string", + "type": "string" + }, + "artifact_version_number": { + "example": 1, + "type": "integer" + }, + "can_write": { + "example": true, + "type": "boolean" + }, + "checkpoint_error": { + "description": "Last durable-save failure reason, or null.", + "example": "string", + "nullable": true, + "type": "string" + }, + "checkpointed_revision": { + "description": "Latest revision known durable.", + "example": 1, + "type": "integer" + }, + "connection_id": { + "description": "Server-issued connection identity.", + "example": "string", + "type": "string" + }, + "content": { + "description": "UTF-8 document content.", + "example": "string", + "type": "string" + }, + "dirty": { + "description": "True while a live tail remains crash-loss exposed.", + "example": true, + "type": "boolean" + }, + "incarnation": { + "description": "Opaque live-session fence token.", + "example": "string", + "type": "string" + }, + "revision": { + "description": "Live OT revision in Unicode code points.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "artifact", + "artifact_version", + "artifact_version_number", + "actor_id", + "connection_id", + "incarnation", + "content", + "revision", + "checkpointed_revision", + "dirty", + "can_write", + "actors" + ], + "type": "object" + } + } + ], + "messages": [ + { + "description": "Submit an OT operation against a live revision; success replies only after application.", + "event": "operation", + "params": { + "example": { + "cursor": { + "position": 1, + "selection_end": 1 + }, + "op": [], + "op_id": "string", + "revision": 1 + }, + "properties": { + "cursor": { + "description": "A Unicode code-point cursor and optional selection endpoint.", + "example": { + "position": 1, + "selection_end": 1 + }, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "op": { + "example": [], + "type": "array" + }, + "op_id": { + "example": "string", + "type": "string" + }, + "revision": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "revision", + "op" + ], + "type": "object" + }, + "returns": { + "description": "Acknowledges an operation only after the authoritative OT server applied it.", + "example": { + "revision": 1 + }, + "properties": { + "revision": { + "description": "Authoritative live revision after application.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object" + } + }, + { + "description": "Publish a cursor mapped from the supplied revision to the current head.", + "event": "cursor", + "params": { + "example": { + "cursor": { + "position": 1, + "selection_end": 1 + }, + "revision": 1 + }, + "properties": { + "cursor": { + "description": "A Unicode code-point cursor and optional selection endpoint.", + "example": { + "position": 1, + "selection_end": 1 + }, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "revision": { + "example": 1, + "type": "integer" + } + }, + "type": "object" + }, + "returns": { + "description": "Acknowledges an Artifact editing presence or leave request.", + "example": { + "ok": true + }, + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + }, + { + "description": "Relay opaque, transient coordination to joined actors in document order.", + "event": "signal", + "params": { + "example": { + "kind": "string", + "payload": {}, + "to": "string" + }, + "properties": { + "kind": { + "example": "string", + "type": "string" + }, + "payload": { + "example": {}, + "type": "object" + }, + "to": { + "example": "string", + "type": "string" + } + }, + "required": [ + "kind", + "to", + "payload" + ], + "type": "object" + }, + "returns": { + "description": "Acknowledges an Artifact editing presence or leave request.", + "example": { + "ok": true + }, + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + }, + { + "description": "Persist the current live snapshot as a new durable ArtifactVersion.", + "event": "checkpoint", + "params": { + "properties": {}, + "type": "object" + }, + "returns": { + "description": "Confirms the exact live revision that reached durable ArtifactVersion storage.", + "example": { + "artifact_version": "string", + "artifact_version_number": 1, + "checkpointed_revision": 1, + "dirty": true, + "incarnation": "string" + }, + "properties": { + "artifact_version": { + "description": "New durable ArtifactVersion ID.", + "example": "string", + "type": "string" + }, + "artifact_version_number": { + "example": 1, + "type": "integer" + }, + "checkpointed_revision": { + "example": 1, + "type": "integer" + }, + "dirty": { + "description": "True when later live operations remain unsaved.", + "example": true, + "type": "boolean" + }, + "incarnation": { + "example": "string", + "type": "string" + } + }, + "required": [ + "incarnation", + "checkpointed_revision", + "artifact_version", + "artifact_version_number", + "dirty" + ], + "type": "object" + } + }, + { + "description": "Detach this server-issued actor from the live session.", + "event": "leave", + "params": { + "properties": {}, + "type": "object" + }, + "returns": { + "description": "Acknowledges an Artifact editing presence or leave request.", + "example": { + "ok": true + }, + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + ], + "name": "ApiArtifactChannel", + "pushes": [ + { + "description": "An identified operation was refused; it was not applied.", + "event": "op_rejected", + "payload": { + "example": { + "op_id": "string", + "reason": "string" + }, + "properties": { + "op_id": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + } + }, + "required": [ + "op_id", + "reason" + ], + "type": "object" + } + }, + { + "description": "A peer operation applied by the authoritative OT server.", + "event": "operation", + "payload": { + "example": { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "incarnation": "string", + "op": [], + "revision": 1 + }, + "properties": { + "actor_id": { + "example": "string", + "type": "string" + }, + "cursor": { + "description": "A Unicode code-point cursor and optional selection endpoint.", + "example": { + "position": 1, + "selection_end": 1 + }, + "nullable": true, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "incarnation": { + "example": "string", + "type": "string" + }, + "op": { + "example": [], + "type": "array" + }, + "revision": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "actor_id", + "revision", + "op", + "cursor", + "incarnation" + ], + "type": "object" + } + }, + { + "description": "Opaque coordination from another joined actor; never persisted or replayed.", + "event": "signal", + "payload": { + "example": { + "from": "string", + "incarnation": "string", + "kind": "string", + "payload": {}, + "revision": 1, + "to": "string" + }, + "properties": { + "from": { + "example": "string", + "type": "string" + }, + "incarnation": { + "example": "string", + "type": "string" + }, + "kind": { + "example": "string", + "type": "string" + }, + "payload": { + "example": {}, + "type": "object" + }, + "revision": { + "example": 1, + "type": "integer" + }, + "to": { + "example": "string", + "type": "string" + } + }, + "required": [ + "kind", + "to", + "from", + "revision", + "payload", + "incarnation" + ], + "type": "object" + } + }, + { + "description": "A peer joined, moved its cursor, or left.", + "event": "presence", + "payload": { + "example": { + "actor_id": "string", + "cursor": { + "position": 1, + "selection_end": 1 + }, + "incarnation": "string", + "meta": {}, + "revision": 1, + "type": "joined" + }, + "properties": { + "actor_id": { + "example": "string", + "type": "string" + }, + "cursor": { + "description": "A Unicode code-point cursor and optional selection endpoint.", + "example": { + "position": 1, + "selection_end": 1 + }, + "nullable": true, + "properties": { + "position": { + "description": "Zero-based Unicode code-point position.", + "example": 1, + "type": "integer" + }, + "selection_end": { + "description": "Zero-based Unicode code-point selection endpoint.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "position" + ], + "type": "object" + }, + "incarnation": { + "example": "string", + "type": "string" + }, + "meta": { + "example": {}, + "nullable": true, + "type": "object" + }, + "revision": { + "example": 1, + "nullable": true, + "type": "integer" + }, + "type": { + "enum": [ + "joined", + "cursor", + "left" + ], + "example": "joined", + "type": "string" + } + }, + "required": [ + "revision", + "type", + "actor_id", + "meta", + "cursor", + "incarnation" + ], + "type": "object" + } + }, + { + "description": "A live revision reached durable ArtifactVersion storage.", + "event": "checkpoint", + "payload": { + "example": { + "artifact_version": "string", + "artifact_version_number": 1, + "checkpointed_revision": 1, + "dirty": true, + "incarnation": "string" + }, + "properties": { + "artifact_version": { + "example": "string", + "type": "string" + }, + "artifact_version_number": { + "example": 1, + "type": "integer" + }, + "checkpointed_revision": { + "example": 1, + "type": "integer" + }, + "dirty": { + "example": true, + "type": "boolean" + }, + "incarnation": { + "example": "string", + "type": "string" + } + }, + "required": [ + "incarnation", + "checkpointed_revision", + "artifact_version", + "artifact_version_number", + "dirty" + ], + "type": "object" + } + }, + { + "description": "A save failed; the dirty live tail remains crash-loss exposed.", + "event": "checkpoint_failed", + "payload": { + "example": { + "automatic": true, + "checkpointed_revision": 1, + "dirty": true, + "incarnation": "string", + "reason": "string", + "revision": 1 + }, + "properties": { + "automatic": { + "example": true, + "type": "boolean" + }, + "checkpointed_revision": { + "example": 1, + "type": "integer" + }, + "dirty": { + "example": true, + "type": "boolean" + }, + "incarnation": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + }, + "revision": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "incarnation", + "checkpointed_revision", + "revision", + "dirty", + "reason", + "automatic" + ], + "type": "object" + } + }, + { + "description": "The live version was replaced, archived, or deleted and this incarnation is terminal.", + "event": "session_invalid", + "payload": { + "example": { + "incarnation": "string", + "reason": "replaced" + }, + "properties": { + "incarnation": { + "example": "string", + "type": "string" + }, + "reason": { + "enum": [ + "replaced", + "archived", + "deleted", + "owner_down" + ], + "example": "replaced", + "type": "string" + } + }, + "required": [ + "reason", + "incarnation" + ], + "type": "object" + } + }, + { + "description": "Current read authority failed; this subscriber was detached before further egress.", + "event": "access_revoked", + "payload": { + "example": { + "reason": "string" + }, + "properties": { + "reason": { + "example": "string", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + } + } + ], + "x-auth": [ + "bearer" + ] + }, { "description": "Channel for real-time chat messaging.\n\nSupports team-scoped and user-scoped threads with keyed, transient, and direct\nthread access patterns, plus org-scoped joins for organization-visibility\nthreads, where org membership alone grants read and post.\n", "joins": [ @@ -97421,8 +110523,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -97436,8 +110540,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -97453,7 +110559,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -97632,8 +110738,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -97647,8 +110755,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -97664,7 +110774,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -98083,8 +111193,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -98098,8 +111210,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -98115,7 +111229,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -98881,8 +111995,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -98896,8 +112012,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -98913,7 +112031,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -99092,8 +112210,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -99107,8 +112227,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -99124,7 +112246,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -103666,8 +116788,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -103681,8 +116805,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -103698,7 +116824,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -103814,8 +116940,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -103829,8 +116957,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -103846,7 +116976,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "string", @@ -103950,8 +117080,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -103965,8 +117097,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -103982,7 +117116,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -104365,8 +117499,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -104380,8 +117516,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -104419,8 +117557,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -104434,8 +117574,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -104592,8 +117734,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -104607,8 +117751,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -104616,8 +117762,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -104631,12 +117779,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -104649,6 +117805,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -104729,6 +117891,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -104741,6 +117909,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -104800,7 +117975,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -104809,7 +117984,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -104831,7 +118006,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -105043,8 +118218,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -105058,8 +118235,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -105075,7 +118254,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -105716,8 +118895,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -105731,8 +118912,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -105748,7 +118931,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -106351,8 +119534,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -106366,8 +119551,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -106383,7 +119570,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -106759,8 +119946,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -106774,8 +119963,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -106813,8 +120004,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -106828,8 +120021,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -106986,8 +120181,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -107001,8 +120198,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -107010,8 +120209,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -107025,12 +120226,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -107043,6 +120252,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -107123,6 +120338,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -107135,6 +120356,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -107194,7 +120422,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -107203,7 +120431,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -107225,7 +120453,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -107437,8 +120665,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -107452,8 +120682,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -107469,7 +120701,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -110393,8 +123625,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -110408,8 +123642,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -110425,7 +123661,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -111028,8 +124264,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -111043,8 +124281,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -111060,7 +124300,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -111436,8 +124676,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -111451,8 +124693,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -111490,8 +124734,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -111505,8 +124751,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -111663,8 +124911,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -111678,8 +124928,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -111687,8 +124939,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -111702,12 +124956,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -111720,6 +124982,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -111800,6 +125068,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -111812,6 +125086,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -111871,7 +125152,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -111880,7 +125161,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -111902,7 +125183,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -112114,8 +125395,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -112129,8 +125412,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -112146,7 +125431,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -114796,8 +128081,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -114811,8 +128098,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -114828,7 +128117,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -114947,8 +128236,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -114962,8 +128253,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -114979,7 +128272,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -115095,8 +128388,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115110,8 +128405,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -115127,7 +128424,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "string", @@ -115231,8 +128528,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115246,8 +128545,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -115263,7 +128564,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -115646,8 +128947,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115661,8 +128964,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -115700,8 +129005,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115715,8 +129022,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -115873,8 +129182,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115888,8 +129199,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -115897,8 +129210,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -115912,12 +129227,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -115930,6 +129253,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -116010,6 +129339,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -116022,6 +129357,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -116081,7 +129423,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -116090,7 +129432,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -116112,7 +129454,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -116324,8 +129666,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -116339,8 +129683,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -116356,7 +129702,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -117121,8 +130467,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -117136,8 +130484,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -117153,7 +130503,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -117332,8 +130682,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -117347,8 +130699,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -117364,7 +130718,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -118131,8 +131485,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -118146,8 +131502,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -118163,7 +131521,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -118342,8 +131700,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -118357,8 +131717,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -118374,7 +131736,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -122916,8 +136278,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -122931,8 +136295,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -122948,7 +136314,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -123064,8 +136430,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123079,8 +136447,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -123096,7 +136466,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "string", @@ -123200,8 +136570,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123215,8 +136587,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -123232,7 +136606,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -123615,8 +136989,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123630,8 +137006,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -123669,8 +137047,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123684,8 +137064,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -123842,8 +137224,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123857,8 +137241,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -123866,8 +137252,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -123881,12 +137269,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -123899,6 +137295,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -123979,6 +137381,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -123991,6 +137399,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -124050,7 +137465,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -124059,7 +137474,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -124081,7 +137496,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -124293,8 +137708,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -124308,8 +137725,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -124325,7 +137744,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -124966,8 +138385,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -124981,8 +138402,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -124998,7 +138421,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -125601,8 +139024,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -125616,8 +139041,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -125633,7 +139060,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -126009,8 +139436,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -126024,8 +139453,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -126063,8 +139494,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -126078,8 +139511,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -126236,8 +139671,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -126251,8 +139688,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -126260,8 +139699,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -126275,12 +139716,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -126293,6 +139742,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -126373,6 +139828,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -126385,6 +139846,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -126444,7 +139912,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -126453,7 +139921,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -126475,7 +139943,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -126687,8 +140155,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -126702,8 +140172,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -126719,7 +140191,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -129267,10 +142739,16 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "idempotency_key": "string", + "mentions": [ + "string" + ], + "metadata": { + "key": "value" + }, "reply_to": "string", "uploads": [ {} @@ -129287,7 +142765,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -129296,7 +142774,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -129318,7 +142796,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -129333,6 +142811,21 @@ "example": "string", "type": "string" }, + "mentions": { + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "metadata": { + "example": { + "key": "value" + }, + "type": "object" + }, "reply_to": { "example": "string", "type": "string" @@ -129430,8 +142923,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -129445,8 +142940,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -129462,7 +142959,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -129579,8 +143076,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -129594,8 +143093,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -129611,7 +143112,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -129986,8 +143487,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -130001,8 +143504,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -130040,8 +143545,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -130055,8 +143562,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -130213,8 +143722,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -130228,8 +143739,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -130237,8 +143750,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -130252,12 +143767,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -130270,6 +143793,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -130350,6 +143879,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -130362,6 +143897,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -130421,7 +143963,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -130430,7 +143972,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -130452,7 +143994,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -130664,8 +144206,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -130679,8 +144223,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -130696,7 +144242,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -130954,10 +144500,16 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "idempotency_key": "string", + "mentions": [ + "string" + ], + "metadata": { + "key": "value" + }, "reply_to": "string" }, "properties": { @@ -130971,7 +144523,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -130980,7 +144532,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -131002,7 +144554,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -131017,6 +144569,21 @@ "example": "string", "type": "string" }, + "mentions": { + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "metadata": { + "example": { + "key": "value" + }, + "type": "object" + }, "reply_to": { "example": "string", "type": "string" @@ -131102,8 +144669,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131117,8 +144686,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -131134,7 +144705,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -131251,8 +144822,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131266,8 +144839,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -131283,7 +144858,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -131658,8 +145233,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131673,8 +145250,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -131712,8 +145291,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131727,8 +145308,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -131885,8 +145468,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131900,8 +145485,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -131909,8 +145496,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -131924,12 +145513,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -131942,6 +145539,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -132022,6 +145625,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -132034,6 +145643,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -132093,7 +145709,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -132102,7 +145718,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -132124,7 +145740,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -132336,8 +145952,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -132351,8 +145969,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -132368,7 +145988,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -133114,8 +146734,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133129,8 +146751,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -133146,7 +146770,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -133272,8 +146896,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133287,8 +146913,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -133304,7 +146932,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -133679,8 +147307,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133694,8 +147324,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -133733,8 +147365,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133748,8 +147382,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -133906,8 +147542,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133921,8 +147559,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -133930,8 +147570,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -133945,12 +147587,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -133963,6 +147613,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -134043,6 +147699,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -134055,6 +147717,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -134114,7 +147783,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -134123,7 +147792,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -134145,7 +147814,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -134357,8 +148026,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -134372,8 +148043,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -134389,7 +148062,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -134717,8 +148390,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -134732,8 +148407,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -134749,7 +148426,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -134867,8 +148544,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -134882,8 +148561,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -134899,7 +148580,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -135274,8 +148955,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -135289,8 +148972,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -135328,8 +149013,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -135343,8 +149030,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -135501,8 +149190,10 @@ "description": "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise.", "example": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -135516,8 +149207,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -135525,8 +149218,10 @@ "items": { "description": "A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time.", "example": { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -135540,12 +149235,20 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 }, "properties": { + "byte_size": { + "description": "Size of the stored bytes.", + "example": 1, + "nullable": true, + "type": "integer" + }, "content_type": { "description": "MIME type of this variant's file (e.g., `\"image/jpeg\"`, `\"video/mp4\"`). `null` if the file is not loaded.", "example": "application/json", @@ -135558,6 +149261,12 @@ "format": "date-time", "type": "string" }, + "duration_ms": { + "description": "Client-declared video duration in milliseconds; not server-probed.", + "example": 1, + "nullable": true, + "type": "integer" + }, "file": { "description": "ID of the underlying storage file that backs this variant (`fil_...`).", "example": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -135638,6 +149347,12 @@ }, "type": "object" }, + "sha256": { + "description": "Lowercase SHA-256 of the stored bytes.", + "example": "string", + "nullable": true, + "type": "string" + }, "updated_at": { "description": "When this variant was last updated (ISO 8601).", "example": "2024-01-01T00:00:00Z", @@ -135650,6 +149365,13 @@ "nullable": true, "type": "string" }, + "url_expires_at": { + "description": "Expiry of this variant's playable URL.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "variant_key": { "description": "Identifier for this variant's processing tier. Common values include `\"original\"` (the unmodified upload) and `\"thumbnail\"` (a resized preview).", "example": "original", @@ -135709,7 +149431,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "items": { @@ -135718,7 +149440,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" }, "properties": { "attributes": { @@ -135740,7 +149462,7 @@ }, "type": { "description": "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", - "example": "archdev.pull_request", + "example": "myapp.pull_request", "type": "string" } }, @@ -135952,8 +149674,10 @@ "url": "https://example.com", "variants": [ { + "byte_size": 1, "content_type": "application/json", "created_at": "2024-01-01T00:00:00Z", + "duration_ms": 1, "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", "filename": "string", "height": 600, @@ -135967,8 +149691,10 @@ "url": "https://example.com", "width": 800 }, + "sha256": "string", "updated_at": "2024-01-01T00:00:00Z", "url": "https://example.com", + "url_expires_at": "2024-01-01T00:00:00Z", "variant_key": "original", "width": 800 } @@ -135984,7 +149710,7 @@ "attributes": {}, "content": "string", "title": "PR #10458", - "type": "archdev.pull_request" + "type": "myapp.pull_request" } ], "created_at": "2024-01-01T00:00:00Z", @@ -136944,16 +150670,22 @@ "event": "object_deleted", "payload": { "example": { - "id": "string" + "id": "string", + "reason": "string" }, "properties": { "id": { "example": "string", "type": "string" + }, + "reason": { + "example": "string", + "type": "string" } }, "required": [ - "id" + "id", + "reason" ], "type": "object" } @@ -137089,6 +150821,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -137115,15 +150848,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -137167,6 +150904,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -137193,15 +150931,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -137243,6 +150985,7 @@ "epic": "string", "id": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "is_blocked": true, + "is_stalled": true, "links": { "key": "value" }, @@ -137269,15 +151012,19 @@ "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "parent": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "priority": 2, + "project_id": "string", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "source_id": "string", "source_scope": "string", "source_type": "string", + "stalled_since": "2024-01-01T00:00:00Z", "status": "open", "subtasks_count": 1, "tags": [ "string" ], + "target_binary": "string", + "target_version": "string", "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -137297,7 +151044,7 @@ "type": "integer" }, "blocked_by_count": { - "description": "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + "description": "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", "example": 1, "type": "integer" }, @@ -137486,7 +151233,12 @@ "type": "string" }, "is_blocked": { - "description": "`true` while at least one blocking task is not yet done. Informational only — a blocked task can still change status — and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + "description": "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + "example": true, + "type": "boolean" + }, + "is_stalled": { + "description": "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", "example": true, "type": "boolean" }, @@ -137625,7 +151377,7 @@ "type": "string" }, "parent": { - "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + "description": "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", "example": "tsk_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" @@ -137635,6 +151387,12 @@ "example": 2, "type": "integer" }, + "project_id": { + "description": "Optional project ID (`prj_...`). Membership does not change task visibility.", + "example": "string", + "nullable": true, + "type": "string" + }, "sandbox": { "description": "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -137659,13 +151417,20 @@ "nullable": true, "type": "string" }, + "stalled_since": { + "description": "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + }, "status": { "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, "subtasks_count": { - "description": "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + "description": "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", "example": 1, "type": "integer" }, @@ -137679,6 +151444,18 @@ }, "type": "array" }, + "target_binary": { + "description": "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + "example": "string", + "nullable": true, + "type": "string" + }, + "target_version": { + "description": "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + "example": "string", + "nullable": true, + "type": "string" + }, "team": { "description": "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -137686,7 +151463,7 @@ "type": "string" }, "thread": { - "description": "ID of the thread this task is bound to (`thr_...`) — the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + "description": "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", "nullable": true, "type": "string" diff --git a/src/archastro/platform/channels/__init__.py b/src/archastro/platform/channels/__init__.py index 6316e70..7b8004b 100644 --- a/src/archastro/platform/channels/__init__.py +++ b/src/archastro/platform/channels/__init__.py @@ -1,8 +1,9 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: b7966fbe5c9f +# Content hash: e4aaefc73836 from .api_activity_feed_channel import ApiActivityFeedChannel # noqa: F401 +from .api_artifact_channel import ApiArtifactChannel # noqa: F401 from .api_chat_channel import ApiChatChannel # noqa: F401 from .api_object_channel import ApiObjectChannel # noqa: F401 from .api_tasks_channel import ApiTasksChannel # noqa: F401 diff --git a/src/archastro/platform/channels/api_artifact_channel.py b/src/archastro/platform/channels/api_artifact_channel.py new file mode 100644 index 0000000..a6b3400 --- /dev/null +++ b/src/archastro/platform/channels/api_artifact_channel.py @@ -0,0 +1,231 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 681181d58578 + +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, Literal, Required, TypedDict + +if TYPE_CHECKING: + from archastro.phx_channel.socket import Socket + + +class OperationInputCursor(TypedDict, total=False): + position: Required[int] + "Zero-based Unicode code-point position." + selection_end: int | None + "Zero-based Unicode code-point selection endpoint." + + +class OperationInput(TypedDict, total=False): + "Submit an OT operation against a live revision; success replies only after application." + + cursor: OperationInputCursor + "A Unicode code-point cursor and optional selection endpoint." + op: Required[list[Any]] + op_id: str + revision: Required[int] + + +class CursorInputCursor(TypedDict, total=False): + position: Required[int] + "Zero-based Unicode code-point position." + selection_end: int | None + "Zero-based Unicode code-point selection endpoint." + + +class CursorInput(TypedDict, total=False): + "Publish a cursor mapped from the supplied revision to the current head." + + cursor: CursorInputCursor + "A Unicode code-point cursor and optional selection endpoint." + revision: int + + +class SignalInput(TypedDict): + "Relay opaque, transient coordination to joined actors in document order." + + kind: str + payload: dict[str, Any] + to: str + + +class OpRejectedPayload(TypedDict): + "An identified operation was refused; it was not applied." + + op_id: str + reason: str + + +class OperationPayloadCursor(TypedDict, total=False): + position: Required[int] + "Zero-based Unicode code-point position." + selection_end: int | None + "Zero-based Unicode code-point selection endpoint." + + +class OperationPayload(TypedDict): + "A peer operation applied by the authoritative OT server." + + actor_id: str + cursor: OperationPayloadCursor | None + "A Unicode code-point cursor and optional selection endpoint." + incarnation: str + op: list[Any] + revision: int + + +SignalPayload = TypedDict( + "SignalPayload", + { + "from": str, + "incarnation": str, + "kind": str, + "payload": dict[str, Any], + "revision": int, + "to": str, + }, +) +"Opaque coordination from another joined actor; never persisted or replayed." + + +class PresencePayloadCursor(TypedDict, total=False): + position: Required[int] + "Zero-based Unicode code-point position." + selection_end: int | None + "Zero-based Unicode code-point selection endpoint." + + +class PresencePayload(TypedDict): + "A peer joined, moved its cursor, or left." + + actor_id: str + cursor: PresencePayloadCursor | None + "A Unicode code-point cursor and optional selection endpoint." + incarnation: str + meta: dict[str, Any] | None + revision: int | None + type: Literal["joined", "cursor", "left"] + + +class CheckpointPayload(TypedDict): + "A live revision reached durable ArtifactVersion storage." + + artifact_version: str + artifact_version_number: int + checkpointed_revision: int + dirty: bool + incarnation: str + + +class CheckpointFailedPayload(TypedDict): + "A save failed; the dirty live tail remains crash-loss exposed." + + automatic: bool + checkpointed_revision: int + dirty: bool + incarnation: str + reason: str + revision: int + + +class SessionInvalidPayload(TypedDict): + "The live version was replaced, archived, or deleted and this incarnation is terminal." + + incarnation: str + reason: Literal["replaced", "archived", "deleted", "owner_down"] + + +class AccessRevokedPayload(TypedDict): + "Current read authority failed; this subscriber was detached before further egress." + + reason: str + + +# Authenticated collaborative editing for durable Artifacts. +# The live OT revision and `dirty` flag describe synchronization, not durability. +# Only `checkpointed_revision` and checkpoint events confirm a completed save; +# process or node loss can discard the dirty tail after the last successful save. +class ApiArtifactChannel: + def __init__(self, channel, join_response=None): + self._channel = channel + self.join_response = join_response + + # Join an Artifact's authenticated live editing session. + @staticmethod + def topic_artifact(artifact_id: str) -> str: + return f"api:artifact:{artifact_id}" + + # Join an Artifact's authenticated live editing session. + @classmethod + async def join_artifact( + cls, socket: "Socket", artifact_id: str, *, as_agent: str | None = None + ) -> "ApiArtifactChannel": + topic = cls.topic_artifact(artifact_id) + channel = socket.channel(topic) + payload: dict[str, object] = {} + if as_agent is not None: + payload["as_agent"] = as_agent + join_response = await channel.join(payload) + return cls(channel, join_response) + + # Leave the underlying channel. + async def leave(self): + await self._channel.leave() + + # Submit an OT operation against a live revision; success replies only after application. + async def operation(self, payload: OperationInput) -> dict[str, Any]: + return await self._channel.push("operation", payload) + + # Publish a cursor mapped from the supplied revision to the current head. + async def cursor(self, payload: CursorInput) -> dict[str, Any]: + return await self._channel.push("cursor", payload) + + # Relay opaque, transient coordination to joined actors in document order. + async def signal(self, payload: SignalInput) -> dict[str, Any]: + return await self._channel.push("signal", payload) + + # Persist the current live snapshot as a new durable ArtifactVersion. + async def checkpoint(self, payload: dict) -> dict[str, Any]: + return await self._channel.push("checkpoint", payload) + + # Detach this server-issued actor from the live session. + async def leave_message(self, payload: dict) -> dict[str, Any]: + return await self._channel.push("leave", payload) + + # An identified operation was refused; it was not applied. + def on_op_rejected(self, callback: Callable[[OpRejectedPayload], None]) -> Callable[[], None]: + return self._channel.on("op_rejected", callback) + + # A peer operation applied by the authoritative OT server. + def on_operation(self, callback: Callable[[OperationPayload], None]) -> Callable[[], None]: + return self._channel.on("operation", callback) + + # Opaque coordination from another joined actor; never persisted or replayed. + def on_signal(self, callback: Callable[[SignalPayload], None]) -> Callable[[], None]: + return self._channel.on("signal", callback) + + # A peer joined, moved its cursor, or left. + def on_presence(self, callback: Callable[[PresencePayload], None]) -> Callable[[], None]: + return self._channel.on("presence", callback) + + # A live revision reached durable ArtifactVersion storage. + def on_checkpoint(self, callback: Callable[[CheckpointPayload], None]) -> Callable[[], None]: + return self._channel.on("checkpoint", callback) + + # A save failed; the dirty live tail remains crash-loss exposed. + def on_checkpoint_failed( + self, callback: Callable[[CheckpointFailedPayload], None] + ) -> Callable[[], None]: + return self._channel.on("checkpoint_failed", callback) + + # The live version was replaced, archived, or deleted and this incarnation is terminal. + def on_session_invalid( + self, callback: Callable[[SessionInvalidPayload], None] + ) -> Callable[[], None]: + return self._channel.on("session_invalid", callback) + + # Current read authority failed; this subscriber was detached before further egress. + def on_access_revoked( + self, callback: Callable[[AccessRevokedPayload], None] + ) -> Callable[[], None]: + return self._channel.on("access_revoked", callback) diff --git a/src/archastro/platform/channels/api_chat_channel.py b/src/archastro/platform/channels/api_chat_channel.py index 5c16557..6ea6022 100644 --- a/src/archastro/platform/channels/api_chat_channel.py +++ b/src/archastro/platform/channels/api_chat_channel.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 6cff96f782bb +# Content hash: bc65fd615b6b from collections.abc import Callable from datetime import datetime @@ -49,6 +49,8 @@ class ApiChatPostMessageInput(TypedDict, total=False): content: Required[str] context: list[ApiChatPostMessageInputContextItem] idempotency_key: str + mentions: list[str] + metadata: dict[str, Any] reply_to: str uploads: list[dict[str, Any]] @@ -70,6 +72,8 @@ class ApiChatPostSimpleMessageInput(TypedDict, total=False): content: str context: list[ApiChatPostSimpleMessageInputContextItem] idempotency_key: str + mentions: list[str] + metadata: dict[str, Any] reply_to: str @@ -232,10 +236,14 @@ class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): + byte_size: int | None + "Size of the stored bytes." content_type: str | None 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' created_at: datetime | None "When this variant was created (ISO 8601)." + duration_ms: int | None + "Client-declared video duration in milliseconds; not server-probed." file: str | None "ID of the underlying storage file that backs this variant (`fil_...`)." filename: str | None @@ -246,10 +254,14 @@ class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=Fal "Media variant ID (`mvr_...`)." image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." + sha256: str | None + "Lowercase SHA-256 of the stored bytes." updated_at: datetime | None "When this variant was last updated (ISO 8601)." url: str | None "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." + url_expires_at: datetime | None + "Expiry of this variant's playable URL." variant_key: str | None 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' width: int | None @@ -487,10 +499,14 @@ class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDi class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False): + byte_size: int | None + "Size of the stored bytes." content_type: str | None 'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.' created_at: datetime | None "When this variant was created (ISO 8601)." + duration_ms: int | None + "Client-declared video duration in milliseconds; not server-probed." file: str | None "ID of the underlying storage file that backs this variant (`fil_...`)." filename: str | None @@ -501,10 +517,14 @@ class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=F "Media variant ID (`mvr_...`)." image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types." + sha256: str | None + "Lowercase SHA-256 of the stored bytes." updated_at: datetime | None "When this variant was last updated (ISO 8601)." url: str | None "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable." + url_expires_at: datetime | None + "Expiry of this variant's playable URL." variant_key: str | None 'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).' width: int | None diff --git a/src/archastro/platform/channels/api_object_channel.py b/src/archastro/platform/channels/api_object_channel.py index 8d29e19..b2e53e1 100644 --- a/src/archastro/platform/channels/api_object_channel.py +++ b/src/archastro/platform/channels/api_object_channel.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 97417bfbd8b3 +# Content hash: 5e56b0c728e8 from collections.abc import Callable from typing import TYPE_CHECKING, Any, Required, TypedDict @@ -35,6 +35,7 @@ class ObjectCreatedPayload(TypedDict): class ObjectDeletedPayload(TypedDict): id: str + reason: str class PresenceUpdatedPayload(TypedDict): diff --git a/src/archastro/platform/channels/api_tasks_channel.py b/src/archastro/platform/channels/api_tasks_channel.py index 273afa6..39f46dd 100644 --- a/src/archastro/platform/channels/api_tasks_channel.py +++ b/src/archastro/platform/channels/api_tasks_channel.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 581f4d31d853 +# Content hash: 923456011b8a from collections.abc import Callable from datetime import datetime @@ -81,7 +81,7 @@ class TasksUpdatedPayloadTasksItem(TypedDict, total=False): aggregate_version: int | None "Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes." blocked_by_count: int | None - "Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind." + "Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind." closed_at: datetime | None "When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open." comments_count: int | None @@ -105,7 +105,9 @@ class TasksUpdatedPayloadTasksItem(TypedDict, total=False): id: Required[str] "Task ID (`tsk_...`)." is_blocked: bool | None - "`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read." + "`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses." + is_stalled: bool | None + "True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads." links: dict[str, Any] | None "Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set." metadata: dict[str, Any] | None @@ -121,9 +123,11 @@ class TasksUpdatedPayloadTasksItem(TypedDict, total=False): owner_user: str | None "ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted." parent: str | None - "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level." + "ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level." priority: int | None "Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set." + project_id: str | None + "Optional project ID (`prj_...`). Membership does not change task visibility." sandbox: str | None "ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment." source_id: str | None @@ -132,16 +136,22 @@ class TasksUpdatedPayloadTasksItem(TypedDict, total=False): "Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`." source_type: str | None "Kind of source object (for example `repository`). `null` when the task has no source." + stalled_since: datetime | None + "Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled." status: Required[str] 'Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.' subtasks_count: int | None - "Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks." + "Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks." tags: list[str] | None "Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged." + target_binary: str | None + "Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`." + target_version: str | None + "Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target." team: str | None "ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team." thread: str | None - "ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread." + "ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread." updated_at: datetime | None "When the task was last modified (ISO 8601)." user: str | None diff --git a/src/archastro/platform/client.py b/src/archastro/platform/client.py index a537608..566bb03 100644 --- a/src/archastro/platform/client.py +++ b/src/archastro/platform/client.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 740a9271cb0f +# Content hash: b950691a4ecf from urllib.parse import urlparse, urlunparse @@ -41,6 +41,7 @@ def __init__( self.agent_env_vars = self.v1.agent_env_vars self.agent_health_actions = self.v1.agent_health_actions self.agent_installations = self.v1.agent_installations + self.agent_requests = self.v1.agent_requests self.agent_routine_runs = self.v1.agent_routine_runs self.agent_routines = self.v1.agent_routines self.agent_sessions = self.v1.agent_sessions @@ -72,6 +73,7 @@ def __init__( self.private_service_definitions = self.v1.private_service_definitions self.private_service_enrollments = self.v1.private_service_enrollments self.private_services = self.v1.private_services + self.relays = self.v1.relays self.sandboxes = self.v1.sandboxes self.scripts = self.v1.scripts self.slack_channel_bindings = self.v1.slack_channel_bindings @@ -81,6 +83,7 @@ def __init__( self.solutions = self.v1.solutions self.ssh_keys = self.v1.ssh_keys self.status = self.v1.status + self.task_ownership_conversions = self.v1.task_ownership_conversions self.tasks = self.v1.tasks self.team_memberships = self.v1.team_memberships self.teams = self.v1.teams @@ -279,6 +282,7 @@ def __init__( self.agent_env_vars = self.v1.agent_env_vars self.agent_health_actions = self.v1.agent_health_actions self.agent_installations = self.v1.agent_installations + self.agent_requests = self.v1.agent_requests self.agent_routine_runs = self.v1.agent_routine_runs self.agent_routines = self.v1.agent_routines self.agent_sessions = self.v1.agent_sessions @@ -310,6 +314,7 @@ def __init__( self.private_service_definitions = self.v1.private_service_definitions self.private_service_enrollments = self.v1.private_service_enrollments self.private_services = self.v1.private_services + self.relays = self.v1.relays self.sandboxes = self.v1.sandboxes self.scripts = self.v1.scripts self.slack_channel_bindings = self.v1.slack_channel_bindings @@ -319,6 +324,7 @@ def __init__( self.solutions = self.v1.solutions self.ssh_keys = self.v1.ssh_keys self.status = self.v1.status + self.task_ownership_conversions = self.v1.task_ownership_conversions self.tasks = self.v1.tasks self.team_memberships = self.v1.team_memberships self.teams = self.v1.teams diff --git a/src/archastro/platform/types/__init__.py b/src/archastro/platform/types/__init__.py index 0ed885f..8117f98 100644 --- a/src/archastro/platform/types/__init__.py +++ b/src/archastro/platform/types/__init__.py @@ -1,12 +1,13 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: e091b32c718b +# Content hash: 95d918bdedd0 from .ai import * # noqa: F401,F403 from .artifacts import * # noqa: F401,F403 from .automations import * # noqa: F401,F403 from .chat import * # noqa: F401,F403 from .common import * # noqa: F401,F403 +from .computers import * # noqa: F401,F403 from .config import * # noqa: F401,F403 from .device import * # noqa: F401,F403 from .events import * # noqa: F401,F403 @@ -17,6 +18,7 @@ from .invites import * # noqa: F401,F403 from .notifications import * # noqa: F401,F403 from .oauth import * # noqa: F401,F403 +from .relays import * # noqa: F401,F403 from .scripts import * # noqa: F401,F403 from .status import * # noqa: F401,F403 from .system import * # noqa: F401,F403 diff --git a/src/archastro/platform/types/ai.py b/src/archastro/platform/types/ai.py index f0ae39c..54bb0be 100644 --- a/src/archastro/platform/types/ai.py +++ b/src/archastro/platform/types/ai.py @@ -1,17 +1,38 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: a2885af89722 +# Content hash: 6c3ad9b487cf from typing import Annotated, Any, Literal from pydantic import BaseModel, Field +from .common import Refusal + + +class AICompletionProvenance(BaseModel): + """ + One accepted provider completion. An empty record preserves an unknown identity. + """ + + response_model: str | None = Field( + default=None, + description="Model identity reported by the provider response, not the requested model or usage key.", + ) + timing: dict[str, Any] | None = Field( + default=None, + description="Optional monotonic timing for this accepted completion and its retries. request_ms includes all attempts, backoff and local processing; provider_attempts_ms is the sum of provider request wall times, including transport and parsing; retry_backoff_ms is measured retry sleep. attempt_count includes the accepted call and retry_count counts earlier attempts. These overlapping measurements are not provider-internal inference time. Missing timing is unknown, not zero.", + ) + class AIChatStreamDone(BaseModel): """ Terminal event marking the end of a streaming chat completion (SSE `done` event). """ + completions: list[AICompletionProvenance] | None = Field( + default=None, + description="Ordered accepted completions; empty records retain unknown identities.", + ) finish_reason: str | None = Field( default=None, description="The overall finish reason for the completion." ) @@ -36,6 +57,10 @@ class AIChatStreamError(BaseModel): message: str = Field( ..., description="Human-readable description of the error that terminated the stream." ) + retryable: bool | None = Field( + default=None, description="Whether the provider failure can be retried." + ) + status: int | None = Field(default=None, description="Upstream HTTP status, when reported.") class AIToolCall(BaseModel): @@ -132,6 +157,9 @@ class AIChatStreamMessageComplete(BaseModel): ..., description="The complete assistant message for this run, assembled from the preceding deltas.", ) + response_model: str | None = Field( + default=None, description="Provider-reported model identity for this accepted completion." + ) usage: dict[str, Any] | None = Field( default=None, description="Token consumption for this run, keyed by model ID. `null` when usage data is unavailable.", @@ -198,6 +226,10 @@ class AICompletionResult(BaseModel): The result of an AI chat completion request. Returned by chat completion endpoints after the model finishes generating. """ + completions: list[AICompletionProvenance] | None = Field( + default=None, + description="Ordered accepted provider completions, including empty records for unknown identities.", + ) finish_reason: str = Field( ..., description='The reason the model stopped generating. Common values include `"stop"` (natural end), `"length"` (token limit reached), and `"tool_calls"` (the model invoked a tool).', @@ -209,9 +241,13 @@ class AICompletionResult(BaseModel): ..., description="The complete message history for the conversation, including all user, assistant, and tool messages in order.", ) + response_model: str | None = Field( + default=None, + description="Final accepted completion provider-reported identity; absent when unknown.", + ) token_usage: dict[str, Any] | None = Field( default=None, - description='Token consumption breakdown keyed by model ID. Each value is a map with `"input_tokens"` and `"output_tokens"` counts. `null` when usage data is unavailable.', + description="Token consumption keyed by model ID. Each value has `input` (all prompt tokens, including cache reads and writes) and `output` counts. When reported, `fresh_input`, `cache_read`, and `cache_creation` are disjoint input buckets; missing cache counts remain unknown. `null` when usage data is unavailable.", ) @@ -250,10 +286,10 @@ class AIEvaluationAnswerScore(BaseModel): type: Literal["score"] = "score" -# One typed evaluation answer. `type` selects boolean, choice, or score. +# One typed evaluation answer. `type` selects boolean, choice, score, or refusal. # `id` matches the question id. AIEvaluationAnswer = Annotated[ - AIEvaluationAnswerBoolean | AIEvaluationAnswerChoice | AIEvaluationAnswerScore, + AIEvaluationAnswerBoolean | AIEvaluationAnswerChoice | AIEvaluationAnswerScore | Refusal, Field(discriminator="type"), ] @@ -280,8 +316,7 @@ class AIEvaluationResult(BaseModel): """ answers: list[AIEvaluationAnswer] = Field( - ..., - description="Typed answers. Each value is a boolean, choice, or score object with the question `id`.", + ..., description="Typed boolean, choice, score, or refusal answers with the question `id`." ) model: str = Field(..., description="Model identifier that produced the answers.") session_id: str | None = Field( diff --git a/src/archastro/platform/types/artifacts.py b/src/archastro/platform/types/artifacts.py index 5794800..e8a732d 100644 --- a/src/archastro/platform/types/artifacts.py +++ b/src/archastro/platform/types/artifacts.py @@ -1,11 +1,13 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: b1c2b3d9044a +# Content hash: c85f0df95fc5 from datetime import datetime +from typing import Any from pydantic import BaseModel, Field +from .common import Acl from .image import ImageSource @@ -14,6 +16,10 @@ class Artifact(BaseModel): A versioned artifact produced or managed by an agent, such as a generated file, report, or code output. """ + acl: Acl | None = Field( + default=None, + description="Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + ) agent: str | None = Field( default=None, description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", @@ -88,3 +94,76 @@ class Artifact(BaseModel): default=None, description="Current version number of the artifact. Increments each time a new version is published.", ) + + +class ArtifactEditingAck(BaseModel): + """ + Acknowledges an Artifact editing presence or leave request. + """ + + ok: bool + + +class ArtifactEditingCursor(BaseModel): + """ + A Unicode code-point cursor and optional selection endpoint. + """ + + position: int = Field(..., description="Zero-based Unicode code-point position.") + selection_end: int | None = Field( + default=None, description="Zero-based Unicode code-point selection endpoint." + ) + + +class ArtifactEditingActor(BaseModel): + """ + A server-issued collaborator identity and its ephemeral presence. + """ + + actor_id: str = Field(..., description="Opaque identity unique to this channel join.") + cursor: ArtifactEditingCursor | None = Field( + default=None, description="Current code-point cursor, or null." + ) + meta: dict[str, Any] = Field(..., description="Server-derived collaborator display metadata.") + + +class ArtifactEditingCheckpointResponse(BaseModel): + """ + Confirms the exact live revision that reached durable ArtifactVersion storage. + """ + + artifact_version: str = Field(..., description="New durable ArtifactVersion ID.") + artifact_version_number: int + checkpointed_revision: int + dirty: bool = Field(..., description="True when later live operations remain unsaved.") + incarnation: str + + +class ArtifactEditingJoinResponse(BaseModel): + """ + Authoritative live Artifact snapshot returned by an editing-channel join. + """ + + actor_id: str = Field(..., description="Server-issued identity for this channel join.") + actors: list[ArtifactEditingActor] + artifact: str = Field(..., description="Public Artifact ID (`art_...`).") + artifact_version: str = Field(..., description="Durable version ID (`artv_...`).") + artifact_version_number: int + can_write: bool + checkpoint_error: str | None = Field( + default=None, description="Last durable-save failure reason, or null." + ) + checkpointed_revision: int = Field(..., description="Latest revision known durable.") + connection_id: str = Field(..., description="Server-issued connection identity.") + content: str = Field(..., description="UTF-8 document content.") + dirty: bool = Field(..., description="True while a live tail remains crash-loss exposed.") + incarnation: str = Field(..., description="Opaque live-session fence token.") + revision: int = Field(..., description="Live OT revision in Unicode code points.") + + +class ArtifactEditingOperationAck(BaseModel): + """ + Acknowledges an operation only after the authoritative OT server applied it. + """ + + revision: int = Field(..., description="Authoritative live revision after application.") diff --git a/src/archastro/platform/types/automations.py b/src/archastro/platform/types/automations.py index 8603d05..a513ac7 100644 --- a/src/archastro/platform/types/automations.py +++ b/src/archastro/platform/types/automations.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 0e837510897f +# Content hash: 2f5da28f3d4e from datetime import datetime from typing import Any @@ -58,7 +58,7 @@ class AutomationRun(BaseModel): ) event_id: str | None = Field( default=None, - description="ID of the platform event that triggered this run. `null` for directly invoked automations.", + description="Event or invocation identifier for this run. Direct invocations use the supplied idempotency key or a generated UUID. `null` when no event identifier was recorded.", ) id: str = Field(..., description="Automation run ID (`atr_...`).") participants: dict[str, Any] | None = Field( diff --git a/src/archastro/platform/types/common.py b/src/archastro/platform/types/common.py index d89c630..197857a 100644 --- a/src/archastro/platform/types/common.py +++ b/src/archastro/platform/types/common.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: dbf264aff25a +# Content hash: 8551e05dcaf9 from datetime import datetime from typing import Annotated, Any, Literal @@ -14,6 +14,15 @@ from .workflows import WorkflowJournal, WorkflowJournalEntry +class Refusal(BaseModel): + """ + The model declined to judge this question. + """ + + id: str + type: Literal["refusal"] = "refusal" + + class AclGrant(BaseModel): """ A single access-control grant that pairs a principal with the set of actions it is allowed to perform. @@ -252,6 +261,50 @@ class InstalledConfigEntry(BaseModel): ) +class Approvers(BaseModel): + """ + Approval policy, excluding user IDs. + """ + + mode: str + + +class Origin(BaseModel): + """ + Origin references. + """ + + routine_id: str | None = None + session_id: str | None = None + thread_id: str | None = None + + +class Payload(BaseModel): + """ + Question request payload. + """ + + question: str + + +class Resolution(BaseModel): + """ + Answer or rejection reason; null before resolution. + """ + + answer: str | None = None + reason: str | None = None + + +class Summary(BaseModel): + """ + Kind-provided summary. + """ + + lines: list[str] + title: str + + class MessagePolicy(BaseModel): """ Controls visibility and canonical recipient selection for routine-emitted messages. @@ -340,6 +393,7 @@ class MediaVariant(BaseModel): A processed variant of a media item, such as the original upload or a resized thumbnail, including a signed download URL resolved at request time. """ + byte_size: int | None = Field(default=None, description="Size of the stored bytes.") content_type: str | None = Field( default=None, description='MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.', @@ -347,6 +401,10 @@ class MediaVariant(BaseModel): created_at: datetime | None = Field( default=None, description="When this variant was created (ISO 8601)." ) + duration_ms: int | None = Field( + default=None, + description="Client-declared video duration in milliseconds; not server-probed.", + ) file: str | None = Field( default=None, description="ID of the underlying storage file that backs this variant (`fil_...`).", @@ -363,6 +421,7 @@ class MediaVariant(BaseModel): default=None, description="Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types.", ) + sha256: str | None = Field(default=None, description="Lowercase SHA-256 of the stored bytes.") updated_at: datetime | None = Field( default=None, description="When this variant was last updated (ISO 8601)." ) @@ -370,6 +429,9 @@ class MediaVariant(BaseModel): default=None, description="Signed download URL for this variant, resolved at request time. `null` if the file is unavailable.", ) + url_expires_at: datetime | None = Field( + default=None, description="Expiry of this variant's playable URL." + ) variant_key: str | None = Field( default=None, description='Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).', @@ -819,21 +881,6 @@ class Command(BaseModel): ) -class ComputerExecResult(BaseModel): - """ - The result of executing a shell command on an agent's computer environment. Contains the captured output and the process exit code. - """ - - exit_code: int | None = Field( - default=None, - description="The UNIX exit code returned by the process. `0` indicates success; any non-zero value indicates an error. `null` if the process did not terminate normally.", - ) - output: str | None = Field( - default=None, - description="The combined stdout and stderr output produced by the command. `null` if the command produced no output.", - ) - - class ContextDocument(BaseModel): """ A context document stored within a context source. Carries metadata and size information only; retrieve the full text content via the `/content` endpoint. @@ -1047,13 +1094,29 @@ class EffectiveAccessEntitlement(BaseModel): granted: bool = Field(..., description="Whether any eligible source grants the key.") key: str = Field(..., description="Stable entitlement catalog key.") + limit: int | None = Field( + default=None, + description="Unexpired metered allowance, including exhausted grants; null for boolean entitlements.", + ) provided_by: list[Literal["personal", "organization"]] = Field( ..., description="Privacy-safe principal kinds that contribute to the effective value." ) + quantity: int | None = Field( + default=None, + description="Effective purchased quantity from the recurring terms linked to this key (for example seats), summed across granting principals. `null` when the key is not granted or no granting plan links a term to it.", + ) + remaining: int | None = Field( + default=None, + description="Remaining metered allowance credits; null for boolean entitlements.", + ) + used: int | None = Field( + default=None, + description="Signed metered consumption in the allowance window; may be negative after reversals. Null for boolean entitlements.", + ) value: bool | None = Field( default=None, description="Merged entitlement value, or `null` when the key is not granted." ) - value_type: Literal["boolean"] = Field( + value_type: Literal["boolean", "metered"] = Field( ..., description="Catalog type used to interpret `value`." ) @@ -1094,6 +1157,10 @@ class CurrentUser(BaseModel): notification_settings: dict[str, Any] = Field( ..., description="The authenticated user's notification preferences." ) + oauth_client_name: str | None = Field( + default=None, + description="Registered display name of the verified OAuth client, when available. Not a verified vendor identity.", + ) org: str | None = Field(default=None, description="ID of the current organization (`org_...`).") org_kind: Literal["company", "personal"] | None = Field( default=None, @@ -1127,9 +1194,16 @@ class CustomObject(BaseModel): default=None, description="Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else.", ) + agent: str | None = Field( + default=None, + description="ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + ) created_at: datetime | None = Field( default=None, description="When the custom object was created (ISO 8601)." ) + expires_at: datetime | None = Field( + default=None, description="Absolute expiry time (RFC 3339). Null when no expiry is set." + ) fields: dict[str, Any] | None = Field( default=None, description="Map of field names to their current values as defined by the object's schema type.", @@ -1209,11 +1283,13 @@ class CustomObjectListResponse(BaseModel): ) page: int = Field(..., description="The current page number (1-indexed).") page_size: int = Field(..., description="Maximum number of results returned per page.") - total_entries: int = Field( - ..., description="Total number of custom objects matching the query across all pages." + total_entries: int | None = Field( + ..., + description="Total number of custom objects matching the query across all pages; null when include_count is false.", ) - total_pages: int = Field( - ..., description="Total number of pages available for the current query." + total_pages: int | None = Field( + ..., + description="Total number of pages available for the current query; null when include_count is false.", ) @@ -1630,19 +1706,27 @@ class InstallationSourceListResponse(BaseModel): class KeyValueStorageEntry(BaseModel): """ - A single key-value storage entry belonging to a user. Represents one key/value pair written to a user's isolated storage namespace within an app. + An owner-scoped key/value pair within an app, optionally expiring. """ + agent: str | None = Field(default=None, description="Agent owner, if any.") created_at: datetime | None = Field( default=None, description="When this storage entry was first created (ISO 8601). `null` if not yet persisted.", ) + expires_at: datetime | None = Field( + default=None, description="Expiry deadline, or null for entries without expiry." + ) key: str = Field(..., description="The string key used to store and look up this entry.") + org: str | None = Field(default=None, description="Organization scope.") + team: str | None = Field(default=None, description="Team owner, if any.") updated_at: datetime | None = Field( default=None, description="When this storage entry was last updated (ISO 8601). `null` if not yet persisted.", ) - user: str = Field(..., description="ID of the user who owns this storage entry (`usr_...`).") + user: str | None = Field( + default=None, description="ID of the user owner, or null for non-user ownership." + ) value: str = Field(..., description="The string value stored under `key` for this user.") @@ -1651,14 +1735,22 @@ class KeyValueStorageEntryWithUser(BaseModel): A key-value storage entry enriched with owner information. Developer and server-to-server callers receive `user_email` and `user_name` populated; end-user (user-JWT) callers receive those fields as `null`. """ + agent: str | None = Field(default=None, description="Agent owner, if any.") created_at: datetime = Field( ..., description="When this storage entry was first created (ISO 8601)." ) + expires_at: datetime | None = Field( + default=None, description="Expiry deadline, or null for entries without expiry." + ) key: str = Field(..., description="The string key used to store and look up this entry.") + org: str | None = Field(default=None, description="Organization scope.") + team: str | None = Field(default=None, description="Team owner, if any.") updated_at: datetime = Field( ..., description="When this storage entry was last updated (ISO 8601)." ) - user: str = Field(..., description="ID of the user who owns this storage entry (`usr_...`).") + user: str | None = Field( + default=None, description="ID of the user owner, or null for non-user ownership." + ) user_email: str | None = Field( default=None, description="Email address of the owning user. `null` for end-user (user-JWT) callers; populated for developer and server-to-server callers.", @@ -2740,6 +2832,9 @@ class AgentComputer(BaseModel): description='Human-readable error description when `status` is `"error"`. `null` otherwise.', ) id: str = Field(..., description="Computer ID (`cmp_...`).") + key: str | None = Field( + default=None, description="Stable key unique within the thread, or null if unset." + ) last_active_at: datetime | None = Field( default=None, description="When the computer last reported activity or received a command. `null` if the computer has never been active.", @@ -2757,11 +2852,11 @@ class AgentComputer(BaseModel): ) provider: str | None = Field( default=None, - description='Compute backend powering this computer: `"sprites"` (Fly Sprites) or `"vercel"` (Vercel Sandbox).', + description='Compute backend powering this computer: `"sprites"` (Fly Sprites), `"vercel"` (Vercel Sandbox) or `"e2b"` (E2B).', ) region: str | None = Field( default=None, - description='Cloud region where the computer is hosted, e.g. `"us-east-1"`. `null` if not yet assigned or when the provider has no region concept (e.g. `"vercel"`).', + description='Cloud region where the computer is hosted, e.g. `"us-east-1"`. `null` if not yet assigned or when the provider has no region concept (e.g. `"vercel"`, `"e2b"`).', ) sprite_url: str | None = Field( default=None, @@ -2771,6 +2866,10 @@ class AgentComputer(BaseModel): default=None, description='Current lifecycle state of the computer. Common values include `"provisioning"`, `"ready"`, `"error"`, and `"terminated"`.', ) + thread_id: str | None = Field( + default=None, + description="Thread this computer belongs to, or null for a non-thread computer.", + ) updated_at: datetime | None = Field( default=None, description="When the computer record was last modified (ISO 8601)." ) @@ -2974,6 +3073,53 @@ class AgentListResponse(BaseModel): ) +class AgentRequestAgent(BaseModel): + """ + Originating agent. + """ + + id: str + name: str | None = None + + +class AgentRequest(BaseModel): + """ + A request for human approval or input. Secret input is never returned. + """ + + agent: AgentRequestAgent | None = Field(default=None, description="Originating agent.") + approvers: Approvers | None = Field( + default=None, description="Approval policy, excluding user IDs." + ) + expires_at: datetime | None = Field(default=None, description="Expiration time.") + id: str = Field(..., description="Request ID (`arq_...`).") + is_approver: bool | None = Field( + default=None, + description="Whether the authenticated caller is an approver, regardless of request status.", + ) + kind: str | None = Field(default=None, description="Registered request kind.") + origin: Origin | None = Field(default=None, description="Origin references.") + payload: Payload | None = Field(default=None, description="Question request payload.") + resolution: Resolution | None = Field( + default=None, description="Answer or rejection reason; null before resolution." + ) + resolved_at: datetime | None = Field(default=None, description="Resolution time.") + resolved_by: str | None = Field(default=None, description="Resolving user ID.") + status: str | None = Field(default=None, description="Request status.") + summary: Summary | None = Field(default=None, description="Kind-provided summary.") + + +class AgentRequestListResponse(BaseModel): + """ + A page of visible agent requests. + """ + + data: list[AgentRequest] = Field(..., description="Visible requests.") + next_cursor: str | None = Field( + default=None, description="Pass as cursor for the next page; null at the end." + ) + + class AgentRoutine(BaseModel): """ An agent routine defines a reusable handler script, preset, or chain that runs in response to events or on a schedule. @@ -3322,6 +3468,23 @@ class AgentSession(BaseModel): ) +class AgentSessionEvent(BaseModel): + """ + One event from the coding harness running an agent session on a computer. + """ + + event: str | None = Field( + default=None, + description="Event name: `session_started`, `message`, `tool_use`, `needs_input`, `activity`, `error`, `turn_complete`, `finished`, `adopted`, or `harness_raw`.", + ) + harness: str | None = Field(default=None, description="Harness that emitted it.") + id: str | None = Field(default=None, description="Event ID; pass as `after` to page.") + occurred_at: datetime | None = Field( + default=None, description="When the event happened in the sandbox." + ) + payload: dict[str, Any] | None = Field(default=None, description="Harness-specific event data.") + + class AgentSessionListResponse(BaseModel): """ Paginated list response containing an array of agent session objects. diff --git a/src/archastro/platform/types/computers.py b/src/archastro/platform/types/computers.py new file mode 100644 index 0000000..74acb2b --- /dev/null +++ b/src/archastro/platform/types/computers.py @@ -0,0 +1,21 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 832fce2a8b08 + + +from pydantic import BaseModel, Field + + +class ComputerExecResult(BaseModel): + """ + The result of executing a shell command on an agent's computer environment. Contains the captured output and the process exit code. + """ + + exit_code: int | None = Field( + default=None, + description="The UNIX exit code returned by the process. `0` indicates success; any non-zero value indicates an error. `null` if the process did not terminate normally.", + ) + output: str | None = Field( + default=None, + description="The combined stdout and stderr output produced by the command. `null` if the command produced no output.", + ) diff --git a/src/archastro/platform/types/events.py b/src/archastro/platform/types/events.py index 9532300..19b0b40 100644 --- a/src/archastro/platform/types/events.py +++ b/src/archastro/platform/types/events.py @@ -1,9 +1,9 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: b80a39bee85f +# Content hash: 60b20f07ddae from datetime import datetime -from typing import Literal +from typing import Any, Literal from pydantic import BaseModel @@ -21,6 +21,7 @@ class EventSubscription(BaseModel): dropped_events_total: int dropped_through_position: int event_names: list[str] + filter: dict[str, Any] | None = None id: str last_overflow_at: datetime | None = None leased_count: int @@ -35,6 +36,7 @@ class EventSubscription(BaseModel): updated_at: datetime user: str | None = None visibility_timeout_seconds: int + watch_key: str | None = None class EventSubscriptionDelivery(BaseModel): diff --git a/src/archastro/platform/types/relays.py b/src/archastro/platform/types/relays.py new file mode 100644 index 0000000..62302b6 --- /dev/null +++ b/src/archastro/platform/types/relays.py @@ -0,0 +1,16 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: afc413754d7e + +from datetime import datetime + +from pydantic import BaseModel, Field + + +class Relay(BaseModel): + """ + Public relay authorization record. + """ + + expires_at: datetime = Field(..., description="Relay expiry.") + id: str = Field(..., description="Relay ID.") diff --git a/src/archastro/platform/types/tasks.py b/src/archastro/platform/types/tasks.py index 1dc15e8..c859160 100644 --- a/src/archastro/platform/types/tasks.py +++ b/src/archastro/platform/types/tasks.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: debabe624df8 +# Content hash: 3a75d35595f1 from datetime import datetime from typing import Any @@ -39,7 +39,7 @@ class Task(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -82,7 +82,11 @@ class Task(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -111,12 +115,16 @@ class Task(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -133,25 +141,37 @@ class Task(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -203,6 +223,15 @@ class TaskComment(BaseModel): ) +class TaskEnsureResult(BaseModel): + """ + The current source task and whether this request created it. + """ + + created: bool = Field(..., description="Whether this request created the task.") + task: Task = Field(..., description="The current task.") + + class TaskExternalLink(BaseModel): """ An indexed external object linked to a task. @@ -213,6 +242,18 @@ class TaskExternalLink(BaseModel): object_type: str = Field(..., description="External object kind.") +class TaskOwnershipJob(BaseModel): + """ + The background job accepted for a task ownership conversion. + """ + + job_id: int = Field(..., description="Background job ID.") + projection_pending: bool | None = Field( + default=None, description="Completed transfers are still reaching task read models." + ) + status: str = Field(..., description="Current background job state.") + + class TaskSessionLease(BaseModel): """ A task-session lease returned only to its matching holder. @@ -227,6 +268,10 @@ class TaskSessionLease(BaseModel): lease_id: str = Field( ..., description="Caller-generated fencing token required for renewal and release." ) + lease_remaining_ms: int = Field( + ..., + description="Milliseconds of lease left when the response was built, clamped at zero. A renewal made while more than half the lease remains appends no event and leaves the expiry unchanged, so clients derive their deadline from this value.", + ) renewed_at: datetime = Field( ..., description="Server timestamp for the most recent claim, reclaim, or renewal." ) @@ -234,3 +279,7 @@ class TaskSessionLease(BaseModel): session_name: str = Field( ..., description="Display name supplied by the matching coding session." ) + task_aggregate_version: int = Field( + ..., description="Task aggregate version including the claim, reclaim, or renewal event." + ) + task_status: str = Field(..., description="Native task status after the lease event.") diff --git a/src/archastro/platform/v1/__init__.py b/src/archastro/platform/v1/__init__.py index f16c19d..3f49cfd 100644 --- a/src/archastro/platform/v1/__init__.py +++ b/src/archastro/platform/v1/__init__.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: a79a7ec7a9c5 +# Content hash: 67fee539d52d from ..runtime.http_client import HttpClient, SyncHttpClient from .resources.activity_feed import ActivityFeedResource, AsyncActivityFeedResource @@ -11,6 +11,7 @@ AsyncAgentHealthActionResource, ) from .resources.agent_installations import AgentInstallationResource, AsyncAgentInstallationResource +from .resources.agent_requests import AgentRequestResource, AsyncAgentRequestResource from .resources.agent_routine_runs import AgentRoutineRunResource, AsyncAgentRoutineRunResource from .resources.agent_routines import AgentRoutineResource, AsyncAgentRoutineResource from .resources.agent_sessions import AgentSessionResource, AsyncAgentSessionResource @@ -64,6 +65,7 @@ PrivateServiceEnrollmentResource, ) from .resources.private_services import AsyncPrivateServiceResource, PrivateServiceResource +from .resources.relays import AsyncRelayResource, RelayResource from .resources.sandboxes import AsyncSandboxResource, SandboxResource from .resources.scripts import AsyncScriptResource, ScriptResource from .resources.slack_channel_bindings import ( @@ -76,6 +78,10 @@ from .resources.solutions import AsyncSolutionResource, SolutionResource from .resources.ssh_keys import AsyncSshKeyResource, SshKeyResource from .resources.status import AsyncStatuResource, StatuResource +from .resources.task_ownership_conversions import ( + AsyncTaskOwnershipConversionResource, + TaskOwnershipConversionResource, +) from .resources.tasks import AsyncTaskResource, TaskResource from .resources.team_memberships import AsyncTeamMembershipResource, TeamMembershipResource from .resources.teams import AsyncTeamResource, TeamResource @@ -94,6 +100,7 @@ def __init__(self, http: SyncHttpClient): self.agent_env_vars = AgentEnvVarResource(http) self.agent_health_actions = AgentHealthActionResource(http) self.agent_installations = AgentInstallationResource(http) + self.agent_requests = AgentRequestResource(http) self.agent_routine_runs = AgentRoutineRunResource(http) self.agent_routines = AgentRoutineResource(http) self.agent_sessions = AgentSessionResource(http) @@ -125,6 +132,7 @@ def __init__(self, http: SyncHttpClient): self.private_service_definitions = PrivateServiceDefinitionResource(http) self.private_service_enrollments = PrivateServiceEnrollmentResource(http) self.private_services = PrivateServiceResource(http) + self.relays = RelayResource(http) self.sandboxes = SandboxResource(http) self.scripts = ScriptResource(http) self.slack_channel_bindings = SlackChannelBindingResource(http) @@ -134,6 +142,7 @@ def __init__(self, http: SyncHttpClient): self.solutions = SolutionResource(http) self.ssh_keys = SshKeyResource(http) self.status = StatuResource(http) + self.task_ownership_conversions = TaskOwnershipConversionResource(http) self.tasks = TaskResource(http) self.team_memberships = TeamMembershipResource(http) self.teams = TeamResource(http) @@ -153,6 +162,7 @@ def __init__(self, http: HttpClient): self.agent_env_vars = AsyncAgentEnvVarResource(http) self.agent_health_actions = AsyncAgentHealthActionResource(http) self.agent_installations = AsyncAgentInstallationResource(http) + self.agent_requests = AsyncAgentRequestResource(http) self.agent_routine_runs = AsyncAgentRoutineRunResource(http) self.agent_routines = AsyncAgentRoutineResource(http) self.agent_sessions = AsyncAgentSessionResource(http) @@ -184,6 +194,7 @@ def __init__(self, http: HttpClient): self.private_service_definitions = AsyncPrivateServiceDefinitionResource(http) self.private_service_enrollments = AsyncPrivateServiceEnrollmentResource(http) self.private_services = AsyncPrivateServiceResource(http) + self.relays = AsyncRelayResource(http) self.sandboxes = AsyncSandboxResource(http) self.scripts = AsyncScriptResource(http) self.slack_channel_bindings = AsyncSlackChannelBindingResource(http) @@ -193,6 +204,7 @@ def __init__(self, http: HttpClient): self.solutions = AsyncSolutionResource(http) self.ssh_keys = AsyncSshKeyResource(http) self.status = AsyncStatuResource(http) + self.task_ownership_conversions = AsyncTaskOwnershipConversionResource(http) self.tasks = AsyncTaskResource(http) self.team_memberships = AsyncTeamMembershipResource(http) self.teams = AsyncTeamResource(http) diff --git a/src/archastro/platform/v1/resources/__init__.py b/src/archastro/platform/v1/resources/__init__.py index f6d5ac9..7f401b5 100644 --- a/src/archastro/platform/v1/resources/__init__.py +++ b/src/archastro/platform/v1/resources/__init__.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 6852079bf2dc +# Content hash: e6f3b46288bb from .activity_feed import ( ActivityFeedResource, # noqa: F401 @@ -22,6 +22,10 @@ AgentInstallationResource, # noqa: F401 AsyncAgentInstallationResource, # noqa: F401 ) +from .agent_requests import ( + AgentRequestResource, # noqa: F401 + AsyncAgentRequestResource, # noqa: F401 +) from .agent_routine_runs import ( AgentRoutineRunResource, # noqa: F401 AsyncAgentRoutineRunResource, # noqa: F401 @@ -150,6 +154,10 @@ AsyncPrivateServiceResource, # noqa: F401 PrivateServiceResource, # noqa: F401 ) +from .relays import ( + AsyncRelayResource, # noqa: F401 + RelayResource, # noqa: F401 +) from .sandboxes import ( AsyncSandboxResource, # noqa: F401 SandboxResource, # noqa: F401 @@ -186,6 +194,10 @@ AsyncStatuResource, # noqa: F401 StatuResource, # noqa: F401 ) +from .task_ownership_conversions import ( + AsyncTaskOwnershipConversionResource, # noqa: F401 + TaskOwnershipConversionResource, # noqa: F401 +) from .tasks import ( AsyncTaskResource, # noqa: F401 TaskResource, # noqa: F401 diff --git a/src/archastro/platform/v1/resources/agent_computers.py b/src/archastro/platform/v1/resources/agent_computers.py index b4ed94d..1f28211 100644 --- a/src/archastro/platform/v1/resources/agent_computers.py +++ b/src/archastro/platform/v1/resources/agent_computers.py @@ -1,13 +1,55 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: d6c791b32b50 +# Content hash: 58d0acadaa0a from __future__ import annotations from typing import Required, TypedDict from ...runtime.http_client import HttpClient, SyncHttpClient -from ...types.common import AgentComputer, ComputerExecResult +from ...types.common import AgentComputer +from ...types.computers import ComputerExecResult + + +class AgentComputerUpdateInputAclAddItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentComputerUpdateInputAclGrantsItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentComputerUpdateInputAclRemoveItem(TypedDict, total=False): + principal: str | None + 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentComputerUpdateInputAcl(TypedDict, total=False): + add: list[AgentComputerUpdateInputAclAddItem] | None + "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." + grants: list[AgentComputerUpdateInputAclGrantsItem] | None + "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." + remove: list[AgentComputerUpdateInputAclRemoveItem] | None + "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." + + +class AgentComputerUpdateInput(TypedDict, total=False): + "Update a computer" + + acl: AgentComputerUpdateInputAcl | None + "Access control list replacement or add/remove operations." class AgentComputerExecInput(TypedDict, total=False): @@ -60,6 +102,26 @@ async def get(self, computer: str) -> AgentComputer: response_type=AgentComputer, ) + async def update(self, computer: str, input: AgentComputerUpdateInput) -> None: + """ + Update a computer + Updates the computer's direct access grants. Requires permission to modify + the computer. Read grants allow retrieval and command execution; write grants + also allow management and deletion. Use acl.grants to replace grants, or + acl.add/acl.remove to patch them. An empty acl.grants array revokes all direct + grants; omitting acl preserves them. Sharing does not add the computer to + agent or thread lists. + + Args: + computer: Computer ID (`cmp_...`). + input: Request body. + input.acl: Access control list replacement or add/remove operations. + + Returns: + Empty response body. Returns HTTP 204 on success. + """ + await self._http.request(f"/api/v1/agent_computers/{computer}", method="PATCH", body=input) + async def exec(self, computer: str, input: AgentComputerExecInput) -> ComputerExecResult: """ Execute a command on a computer @@ -152,6 +214,26 @@ def get(self, computer: str) -> AgentComputer: response_type=AgentComputer, ) + def update(self, computer: str, input: AgentComputerUpdateInput) -> None: + """ + Update a computer + Updates the computer's direct access grants. Requires permission to modify + the computer. Read grants allow retrieval and command execution; write grants + also allow management and deletion. Use acl.grants to replace grants, or + acl.add/acl.remove to patch them. An empty acl.grants array revokes all direct + grants; omitting acl preserves them. Sharing does not add the computer to + agent or thread lists. + + Args: + computer: Computer ID (`cmp_...`). + input: Request body. + input.acl: Access control list replacement or add/remove operations. + + Returns: + Empty response body. Returns HTTP 204 on success. + """ + self._http.request(f"/api/v1/agent_computers/{computer}", method="PATCH", body=input) + def exec(self, computer: str, input: AgentComputerExecInput) -> ComputerExecResult: """ Execute a command on a computer diff --git a/src/archastro/platform/v1/resources/agent_requests.py b/src/archastro/platform/v1/resources/agent_requests.py new file mode 100644 index 0000000..b82fea3 --- /dev/null +++ b/src/archastro/platform/v1/resources/agent_requests.py @@ -0,0 +1,226 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 9643ae844548 + +from __future__ import annotations + +from typing import Any, TypedDict + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import AgentRequest, AgentRequestListResponse + + +class AgentRequestRejectInput(TypedDict): + "Reject an agent request" + + reason: str + "Reason for rejecting the request." + + +class AgentRequestResolveInput(TypedDict): + "Resolve an agent request" + + input: dict[str, Any] + "Kind-specific input. Secrets must use secret_value." + + +class AsyncAgentRequestResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list( + self, + *, + agent: str | None = None, + status: str | None = None, + kind: str | None = None, + limit: int | None = None, + cursor: str | None = None, + ) -> AgentRequestListResponse: + """ + List agent requests + Lists requests visible under the context policy, newest first. Filters are combined. Pagination is not a snapshot; later pages continue after the last returned request. + + Args: + agent: Filter by agent ID. + status: open, resolved, rejected, expired or cancelled. + kind: Filter by registered kind. + limit: Page size, 1 100; default 25. + cursor: Opaque next_cursor from a prior page. + + Returns: + A page of visible requests. + """ + query: dict[str, object] = {} + if agent is not None: + query["agent"] = agent + if status is not None: + query["status"] = status + if kind is not None: + query["kind"] = kind + if limit is not None: + query["limit"] = limit + if cursor is not None: + query["cursor"] = cursor + return await self._http.request( + "/api/v1/agent_requests", + query=query, + response_type=AgentRequestListResponse, + ) + + async def get(self, agent_request: str) -> AgentRequest: + """ + Show an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + + Returns: + The request, without secret input. + """ + return await self._http.request( + f"/api/v1/agent_requests/{agent_request}", + response_type=AgentRequest, + ) + + async def reject(self, agent_request: str, input: AgentRequestRejectInput) -> AgentRequest: + """ + Reject an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + input: Request body. + input.reason: Reason for rejecting the request. + + Returns: + The request, without secret input. + """ + return await self._http.request( + f"/api/v1/agent_requests/{agent_request}/reject", + method="POST", + body=input, + response_type=AgentRequest, + ) + + async def resolve(self, agent_request: str, input: AgentRequestResolveInput) -> AgentRequest: + """ + Resolve an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + input: Request body. + input.input: Kind-specific input. Secrets must use secret_value. + + Returns: + The request, without secret input. + """ + return await self._http.request( + f"/api/v1/agent_requests/{agent_request}/resolve", + method="POST", + body=input, + response_type=AgentRequest, + ) + + +class AgentRequestResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list( + self, + *, + agent: str | None = None, + status: str | None = None, + kind: str | None = None, + limit: int | None = None, + cursor: str | None = None, + ) -> AgentRequestListResponse: + """ + List agent requests + Lists requests visible under the context policy, newest first. Filters are combined. Pagination is not a snapshot; later pages continue after the last returned request. + + Args: + agent: Filter by agent ID. + status: open, resolved, rejected, expired or cancelled. + kind: Filter by registered kind. + limit: Page size, 1 100; default 25. + cursor: Opaque next_cursor from a prior page. + + Returns: + A page of visible requests. + """ + query: dict[str, object] = {} + if agent is not None: + query["agent"] = agent + if status is not None: + query["status"] = status + if kind is not None: + query["kind"] = kind + if limit is not None: + query["limit"] = limit + if cursor is not None: + query["cursor"] = cursor + return self._http.request( + "/api/v1/agent_requests", + query=query, + response_type=AgentRequestListResponse, + ) + + def get(self, agent_request: str) -> AgentRequest: + """ + Show an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + + Returns: + The request, without secret input. + """ + return self._http.request( + f"/api/v1/agent_requests/{agent_request}", + response_type=AgentRequest, + ) + + def reject(self, agent_request: str, input: AgentRequestRejectInput) -> AgentRequest: + """ + Reject an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + input: Request body. + input.reason: Reason for rejecting the request. + + Returns: + The request, without secret input. + """ + return self._http.request( + f"/api/v1/agent_requests/{agent_request}/reject", + method="POST", + body=input, + response_type=AgentRequest, + ) + + def resolve(self, agent_request: str, input: AgentRequestResolveInput) -> AgentRequest: + """ + Resolve an agent request + Uses the request context visibility and approver policy. Agents cannot resolve or reject requests. + + Args: + agent_request: Request ID (`arq_...`). + input: Request body. + input.input: Kind-specific input. Secrets must use secret_value. + + Returns: + The request, without secret input. + """ + return self._http.request( + f"/api/v1/agent_requests/{agent_request}/resolve", + method="POST", + body=input, + response_type=AgentRequest, + ) diff --git a/src/archastro/platform/v1/resources/agent_sessions.py b/src/archastro/platform/v1/resources/agent_sessions.py index f9de5ca..93192a8 100644 --- a/src/archastro/platform/v1/resources/agent_sessions.py +++ b/src/archastro/platform/v1/resources/agent_sessions.py @@ -1,15 +1,20 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 87b5e0da34ba +# Content hash: 3d3e0fd19297 from __future__ import annotations import builtins from collections.abc import AsyncIterator, Iterator -from typing import Any, Literal, Required, TypedDict +from typing import ( + Any, + Literal, + Required, + TypedDict, +) from ...runtime.http_client import HttpClient, SyncHttpClient -from ...types.common import AgentSession, AgentSessionListResponse +from ...types.common import AgentSession, AgentSessionEvent, AgentSessionListResponse class AgentSessionCreateInput(TypedDict, total=False): @@ -57,12 +62,17 @@ class AgentSessionMessageInput(TypedDict, total=False): 'Role of the message sender. Typically `"user"` or `"tool"`. Defaults to `"user"`.' +class AgentSessionStreamEventSessionEvent(TypedDict): + event: Literal["session_event"] + data: AgentSessionEvent + + class AgentSessionStreamEventSessionUpdate(TypedDict): event: Literal["session_update"] data: AgentSession -AgentSessionStreamEvent = AgentSessionStreamEventSessionUpdate +AgentSessionStreamEvent = AgentSessionStreamEventSessionEvent | AgentSessionStreamEventSessionUpdate class AsyncAgentSessionResource: @@ -249,6 +259,35 @@ async def cancel(self, agent_session: str) -> AgentSession: response_type=AgentSession, ) + async def events( + self, agent_session: str, *, after: str | None = None, limit: int | None = None + ) -> builtins.list[AgentSessionEvent]: + """ + List an agent session's harness events + Returns what the coding harness (Claude Code, Codex) running an agent + session on a computer has done so far: messages, tool use, turn + completions, in order. Events arrive while the harness runs; page with + `after`. The session stream endpoint pushes the same events live. + + Args: + agent_session: Agent session ID (`ase_...`). + after: Return events after this event ID (`cse_...`). + limit: Maximum events to return (default 200, max 1000). + + Returns: + Events in the order they happened. + """ + query: dict[str, object] = {} + if after is not None: + query["after"] = after + if limit is not None: + query["limit"] = limit + return await self._http.request( + f"/api/v1/agent_sessions/{agent_session}/events", + query=query, + response_type=list[AgentSessionEvent], + ) + async def message(self, agent_session: str, input: AgentSessionMessageInput) -> AgentSession: """ Send a message to an agent session @@ -284,6 +323,9 @@ async def stream(self, agent_session: str) -> AsyncIterator[AgentSessionStreamEv Opens a Server-Sent Events connection that emits a `session_update` event whenever the agent session's status changes, replaying the current status on connect and closing on a terminal status (`completed`, `failed`, `cancelled`). + Sessions that run a coding harness on a computer also emit a `session_event` + for each harness event (messages, tool use, turn completion) as it happens; + list earlier ones with the session events endpoint. Args: agent_session: ID of the agent session to stream. @@ -479,6 +521,35 @@ def cancel(self, agent_session: str) -> AgentSession: response_type=AgentSession, ) + def events( + self, agent_session: str, *, after: str | None = None, limit: int | None = None + ) -> builtins.list[AgentSessionEvent]: + """ + List an agent session's harness events + Returns what the coding harness (Claude Code, Codex) running an agent + session on a computer has done so far: messages, tool use, turn + completions, in order. Events arrive while the harness runs; page with + `after`. The session stream endpoint pushes the same events live. + + Args: + agent_session: Agent session ID (`ase_...`). + after: Return events after this event ID (`cse_...`). + limit: Maximum events to return (default 200, max 1000). + + Returns: + Events in the order they happened. + """ + query: dict[str, object] = {} + if after is not None: + query["after"] = after + if limit is not None: + query["limit"] = limit + return self._http.request( + f"/api/v1/agent_sessions/{agent_session}/events", + query=query, + response_type=list[AgentSessionEvent], + ) + def message(self, agent_session: str, input: AgentSessionMessageInput) -> AgentSession: """ Send a message to an agent session @@ -514,6 +585,9 @@ def stream(self, agent_session: str) -> Iterator[AgentSessionStreamEvent]: Opens a Server-Sent Events connection that emits a `session_update` event whenever the agent session's status changes, replaying the current status on connect and closing on a terminal status (`completed`, `failed`, `cancelled`). + Sessions that run a coding harness on a computer also emit a `session_event` + for each harness event (messages, tool use, turn completion) as it happens; + list earlier ones with the session events endpoint. Args: agent_session: ID of the agent session to stream. diff --git a/src/archastro/platform/v1/resources/agents.py b/src/archastro/platform/v1/resources/agents.py index 90a11c5..e171fec 100644 --- a/src/archastro/platform/v1/resources/agents.py +++ b/src/archastro/platform/v1/resources/agents.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 7de00aaa38e6 +# Content hash: 84e4708f4187 from __future__ import annotations @@ -37,9 +37,45 @@ from ...types.workflows import WorkflowWorkItemClaim, WorkflowWorkItemList +class AgentAgentComputerCreateInputAclAddItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentAgentComputerCreateInputAclGrantsItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentAgentComputerCreateInputAclRemoveItem(TypedDict, total=False): + principal: str | None + 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class AgentAgentComputerCreateInputAcl(TypedDict, total=False): + add: list[AgentAgentComputerCreateInputAclAddItem] | None + "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." + grants: list[AgentAgentComputerCreateInputAclGrantsItem] | None + "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." + remove: list[AgentAgentComputerCreateInputAclRemoveItem] | None + "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." + + class AgentAgentComputerCreateInput(TypedDict, total=False): "Provision a computer for an agent" + acl: AgentAgentComputerCreateInputAcl | None + "Initial access control list." config: dict[str, Any] | None "Provider-specific configuration for the computer. Supported keys vary by provider. A top-level `provider` takes precedence over `config.provider`." lookup_key: str | None @@ -49,7 +85,7 @@ class AgentAgentComputerCreateInput(TypedDict, total=False): name: Required[str] "Human-readable display name for the computer." provider: str | None - 'Compute backend for the computer: `"sprites"` (Fly Sprites, the default) or `"vercel"` (Vercel Sandbox). Folded into `config.provider`.' + 'Compute backend for the computer: `"sprites"` (Fly Sprites, the default), `"vercel"` (Vercel Sandbox) or `"e2b"` (E2B). Folded into `config.provider`.' region: str | None 'Region in which to provision the computer, e.g. `"iad"`. Defaults to `"iad"` when omitted.' @@ -710,6 +746,16 @@ class AgentSearchResponse(BaseModel): ) +class AgentSelfSolutionResponse(BaseModel): + """ + Successful response + """ + + lookup_key: str + solution_id: str + solution_version: str + + class AsyncAgentAgentComputerResource: def __init__(self, http: HttpClient): self._http = http @@ -748,11 +794,12 @@ async def create(self, agent: str, input: AgentAgentComputerCreateInput) -> Agen Args: agent: Agent IDs (`agi_...`). When provided, only computers belonging to these agents are returned. Multiple values are OR'd. input: Request body. + input.acl: Initial access control list. input.config: Provider-specific configuration for the computer. Supported keys vary by provider. A top-level `provider` takes precedence over `config.provider`. input.lookup_key: Stable, user-defined key for this computer. Must be unique within the app. Use it to look up the computer without storing its ID. input.metadata: Arbitrary key-value metadata to attach to the computer. Not interpreted by the platform; returned as-is on all subsequent reads. input.name: Human-readable display name for the computer. - input.provider: Compute backend for the computer: `"sprites"` (Fly Sprites, the default) or `"vercel"` (Vercel Sandbox). Folded into `config.provider`. + input.provider: Compute backend for the computer: `"sprites"` (Fly Sprites, the default), `"vercel"` (Vercel Sandbox) or `"e2b"` (E2B). Folded into `config.provider`. input.region: Region in which to provision the computer, e.g. `"iad"`. Defaults to `"iad"` when omitted. Returns: @@ -1563,6 +1610,22 @@ async def search(self, agent: str, input: AgentSearchInput) -> AgentSearchRespon response_type=AgentSearchResponse, ) + async def self_solution(self, agent: str) -> AgentSelfSolutionResponse: + """ + Ensure an agent's private self-solution + Lazily snapshots an authorized agent into its single private, versioned Solution. + + Args: + agent: Agent ID or lookup key. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/agents/{agent}/self_solution", + response_type=AgentSelfSolutionResponse, + ) + async def threads(self, agent: str, input: AgentThreadsInput) -> Thread: """ Create a thread for an agent @@ -1675,11 +1738,12 @@ def create(self, agent: str, input: AgentAgentComputerCreateInput) -> AgentCompu Args: agent: Agent IDs (`agi_...`). When provided, only computers belonging to these agents are returned. Multiple values are OR'd. input: Request body. + input.acl: Initial access control list. input.config: Provider-specific configuration for the computer. Supported keys vary by provider. A top-level `provider` takes precedence over `config.provider`. input.lookup_key: Stable, user-defined key for this computer. Must be unique within the app. Use it to look up the computer without storing its ID. input.metadata: Arbitrary key-value metadata to attach to the computer. Not interpreted by the platform; returned as-is on all subsequent reads. input.name: Human-readable display name for the computer. - input.provider: Compute backend for the computer: `"sprites"` (Fly Sprites, the default) or `"vercel"` (Vercel Sandbox). Folded into `config.provider`. + input.provider: Compute backend for the computer: `"sprites"` (Fly Sprites, the default), `"vercel"` (Vercel Sandbox) or `"e2b"` (E2B). Folded into `config.provider`. input.region: Region in which to provision the computer, e.g. `"iad"`. Defaults to `"iad"` when omitted. Returns: @@ -2483,6 +2547,22 @@ def search(self, agent: str, input: AgentSearchInput) -> AgentSearchResponse: response_type=AgentSearchResponse, ) + def self_solution(self, agent: str) -> AgentSelfSolutionResponse: + """ + Ensure an agent's private self-solution + Lazily snapshots an authorized agent into its single private, versioned Solution. + + Args: + agent: Agent ID or lookup key. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/agents/{agent}/self_solution", + response_type=AgentSelfSolutionResponse, + ) + def threads(self, agent: str, input: AgentThreadsInput) -> Thread: """ Create a thread for an agent diff --git a/src/archastro/platform/v1/resources/ai.py b/src/archastro/platform/v1/resources/ai.py index e307448..a10c59b 100644 --- a/src/archastro/platform/v1/resources/ai.py +++ b/src/archastro/platform/v1/resources/ai.py @@ -1,11 +1,16 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: faea2849d1d8 +# Content hash: a1da119e9408 from __future__ import annotations from collections.abc import AsyncIterator, Iterator -from typing import Any, Literal, Required, TypedDict +from typing import ( + Any, + Literal, + Required, + TypedDict, +) from pydantic import BaseModel, Field @@ -205,9 +210,21 @@ class EmbeddingSimilarityComparisonInput(TypedDict): "Second text to embed and compare." +class EvaluationEvaluationsInputImagesItem(TypedDict, total=False): + image_data: str | None + "Base64 image bytes; requires image_type." + image_type: str | None + "Image MIME type, such as image/png." + image_url: str | None + "Image URL; mutually exclusive with image_data." + type: Required[Literal["image"]] + + class EvaluationEvaluationsInput(TypedDict, total=False): "Create an evaluation" + images: list[EvaluationEvaluationsInputImagesItem] | None + "Optional image content parts. Omitted or empty means no images. Requires model image support." model: str | None "Evaluation model identifier. Omit to use the platform default." questions: Required[list[dict[str, Any] | dict[str, Any] | dict[str, Any]]] @@ -444,6 +461,57 @@ class AiSessionTokenResponse(BaseModel): token: str +class AiUsageResponseDataItem(BaseModel): + agent_id: str | None = Field( + default=None, + description="Agent ID for this group. Present only when grouped by `agent_id`.", + ) + agent_kind: str | None = Field( + default=None, + description="Agent kind for this group. Present only when grouped by `agent_kind`.", + ) + billed_usd: float | None = Field( + default=None, + description="Customer-billed cost for this group in USD. `null` when `priced` is `false`.", + ) + cache_read_tokens: int = Field(..., description="Summed cache-read tokens in this group.") + cache_write_tokens: int = Field(..., description="Summed cache-write tokens in this group.") + completion_tokens: int = Field( + ..., description="Summed completion (output) tokens in this group." + ) + hour: str | None = Field( + default=None, + description="Hour bucket (ISO 8601) for this group. Present only when grouped by `hour`.", + ) + model: str | None = Field( + default=None, + description="Model identifier for this group. Present only when grouped by `model`.", + ) + priced: bool = Field( + ..., + description="`true` when `billed_usd` was priced from the customer's active billing plan; `false` when the group could not be priced.", + ) + prompt_tokens: int = Field(..., description="Summed prompt (input) tokens in this group.") + requests: int = Field(..., description="Number of LLM calls in this group.") + task: str | None = Field( + default=None, + description="Caller task ID for this group. Present only when grouped by `task`.", + ) + + +class AiUsageResponse(BaseModel): + """ + Successful response + """ + + data: list[AiUsageResponseDataItem] = Field( + ..., + description="One row per group with request/token sums, the group key fields, `billed_usd`, and `priced`.", + ) + since: str = Field(..., description="Effective window start (ISO 8601).") + until: str = Field(..., description="Effective window end (ISO 8601).") + + class StreamCreateEventDone(TypedDict): event: Literal["done"] data: AIChatStreamDone @@ -620,21 +688,25 @@ async def evaluations(self, input: EvaluationEvaluationsInput) -> AIEvaluationRe model and returns typed answers. This is not chat completion and not JSON-schema structured output: there is no generated text. Each question is a `boolean`, `choice`, or `score` object with its own - `id`. Answers come back as the same three typed objects, each carrying - that `id`. + `id`. Answers carry that `id`, including a `refusal` answer containing + only `id` and `type` when the model declines to judge a question. + Image input, labelled score levels, and refusal answers are model-dependent. + Images are separate from `state` and use the chat completion image-part format. + Models without image support reject image input with `400 unsupported_capability`. The authenticated app must have the `llm_calls` entitlement enabled on its plan. Requests that exceed the plan quota are rejected with `402`. Token usage is recorded against the authenticated app and organization. Args: input: Request body. + input.images: Optional image content parts. Omitted or empty means no images. Requires model image support. input.model: Evaluation model identifier. Omit to use the platform default. input.questions: Nonempty list of boolean, choice, or score questions. Each question must include `id`, `type`, and `instructions`. input.session_id: Optional UUID grouping this evaluation under one session in the Developers dashboard. Generated when omitted. input.state: Shared content the model judges. A string, JSON object, or JSON array. Every question in the request sees this same state. Returns: - Typed answers as a list of boolean, choice, or score objects, plus model and usage. + Typed boolean, choice, score, or refusal answers, plus model and usage. """ return await self._http.request( "/api/v1/ai/evaluation/evaluations", @@ -787,6 +859,52 @@ async def session_token(self, input: AiSessionTokenInput) -> AiSessionTokenRespo response_type=AiSessionTokenResponse, ) + async def usage( + self, + *, + since: str | None = None, + until: str | None = None, + group_by: list[Literal["task", "model", "agent_kind", "agent_id", "hour"]] | None = None, + task: list[str] | None = None, + limit: int | None = None, + ) -> AiUsageResponse: + """ + Read the authenticated user's LLM usage + Returns the authenticated user's own LLM usage aggregated over a bounded + time window. Each row carries request and token sums plus the customer-billed + cost for the group. Cost is derived from the customer's active billing plan; + provider-reported cost is never returned. + Pass `group_by` one or more times to choose the grouping dimensions + (`task`, `model`, `agent_kind`, `agent_id`, `hour`). The window (`since` to + `until`) may span at most 31 days. Results are capped at `limit` groups. + + Args: + since: Window start as an ISO 8601 timestamp (inclusive). Defaults to 7 days before `until`. + until: Window end as an ISO 8601 timestamp (inclusive). Defaults to now. + group_by: Grouping dimensions. Repeat for multiple dimensions (e.g. `group_by[]=task&group_by[]=model`). Defaults to `model`. + task: Restrict results to these caller task IDs. Omit to include every task. + limit: Maximum number of groups to return. Defaults to 50; maximum is 200. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if since is not None: + query["since"] = since + if until is not None: + query["until"] = until + if group_by is not None: + query["group_by"] = group_by + if task is not None: + query["task"] = task + if limit is not None: + query["limit"] = limit + return await self._http.request( + "/api/v1/ai/usage", + query=query, + response_type=AiUsageResponse, + ) + class StreamResource: def __init__(self, http: SyncHttpClient): @@ -917,21 +1035,25 @@ def evaluations(self, input: EvaluationEvaluationsInput) -> AIEvaluationResult: model and returns typed answers. This is not chat completion and not JSON-schema structured output: there is no generated text. Each question is a `boolean`, `choice`, or `score` object with its own - `id`. Answers come back as the same three typed objects, each carrying - that `id`. + `id`. Answers carry that `id`, including a `refusal` answer containing + only `id` and `type` when the model declines to judge a question. + Image input, labelled score levels, and refusal answers are model-dependent. + Images are separate from `state` and use the chat completion image-part format. + Models without image support reject image input with `400 unsupported_capability`. The authenticated app must have the `llm_calls` entitlement enabled on its plan. Requests that exceed the plan quota are rejected with `402`. Token usage is recorded against the authenticated app and organization. Args: input: Request body. + input.images: Optional image content parts. Omitted or empty means no images. Requires model image support. input.model: Evaluation model identifier. Omit to use the platform default. input.questions: Nonempty list of boolean, choice, or score questions. Each question must include `id`, `type`, and `instructions`. input.session_id: Optional UUID grouping this evaluation under one session in the Developers dashboard. Generated when omitted. input.state: Shared content the model judges. A string, JSON object, or JSON array. Every question in the request sees this same state. Returns: - Typed answers as a list of boolean, choice, or score objects, plus model and usage. + Typed boolean, choice, score, or refusal answers, plus model and usage. """ return self._http.request( "/api/v1/ai/evaluation/evaluations", @@ -1080,3 +1202,45 @@ def session_token(self, input: AiSessionTokenInput) -> AiSessionTokenResponse: body=input, response_type=AiSessionTokenResponse, ) + + def usage( + self, + *, + since: str | None = None, + until: str | None = None, + group_by: list[Literal["task", "model", "agent_kind", "agent_id", "hour"]] | None = None, + task: list[str] | None = None, + limit: int | None = None, + ) -> AiUsageResponse: + """ + Read the authenticated user's LLM usage + Returns the authenticated user's own LLM usage aggregated over a bounded + time window. Each row carries request and token sums plus the customer-billed + cost for the group. Cost is derived from the customer's active billing plan; + provider-reported cost is never returned. + Pass `group_by` one or more times to choose the grouping dimensions + (`task`, `model`, `agent_kind`, `agent_id`, `hour`). The window (`since` to + `until`) may span at most 31 days. Results are capped at `limit` groups. + + Args: + since: Window start as an ISO 8601 timestamp (inclusive). Defaults to 7 days before `until`. + until: Window end as an ISO 8601 timestamp (inclusive). Defaults to now. + group_by: Grouping dimensions. Repeat for multiple dimensions (e.g. `group_by[]=task&group_by[]=model`). Defaults to `model`. + task: Restrict results to these caller task IDs. Omit to include every task. + limit: Maximum number of groups to return. Defaults to 50; maximum is 200. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if since is not None: + query["since"] = since + if until is not None: + query["until"] = until + if group_by is not None: + query["group_by"] = group_by + if task is not None: + query["task"] = task + if limit is not None: + query["limit"] = limit + return self._http.request("/api/v1/ai/usage", query=query, response_type=AiUsageResponse) diff --git a/src/archastro/platform/v1/resources/artifacts.py b/src/archastro/platform/v1/resources/artifacts.py index 833622a..98fdad6 100644 --- a/src/archastro/platform/v1/resources/artifacts.py +++ b/src/archastro/platform/v1/resources/artifacts.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 5e614d5e0347 +# Content hash: 6a317c2bbbbe from __future__ import annotations @@ -10,6 +10,40 @@ from ...types.artifacts import Artifact +class ArtifactCreateInputAclAddItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactCreateInputAclGrantsItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactCreateInputAclRemoveItem(TypedDict, total=False): + principal: str | None + 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactCreateInputAcl(TypedDict, total=False): + add: list[ArtifactCreateInputAclAddItem] | None + "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." + grants: list[ArtifactCreateInputAclGrantsItem] | None + "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." + remove: list[ArtifactCreateInputAclRemoveItem] | None + "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." + + class ArtifactCreateInputFile(TypedDict, total=False): data: Required[str] "File content encoded according to file.encoding (base64 by default)." @@ -24,6 +58,8 @@ class ArtifactCreateInputFile(TypedDict, total=False): class ArtifactCreateInput(TypedDict, total=False): "Create an artifact" + acl: ArtifactCreateInputAcl | None + "Initial access control list. Grants add read or write access beyond the owner." agent: str | None "Agent ID (`agt_...`) to associate with the artifact." artifact: dict[str, Any] | None @@ -40,8 +76,6 @@ class ArtifactCreateInput(TypedDict, total=False): "Human-readable artifact name." org: str | None "Organization owning a system artifact. Requires an organization admin or system viewer." - permissions: dict[str, Any] | None - "Optional artifact permissions map." system: bool | None "When true, create a system-owned artifact. Mutually exclusive with `team` and `user`." team: str | None @@ -52,6 +86,40 @@ class ArtifactCreateInput(TypedDict, total=False): "User ID (`usr_...`) that should own the artifact. Defaults to the authenticated user when omitted and no team is supplied." +class ArtifactReplaceInputAclAddItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactReplaceInputAclGrantsItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactReplaceInputAclRemoveItem(TypedDict, total=False): + principal: str | None + 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ArtifactReplaceInputAcl(TypedDict, total=False): + add: list[ArtifactReplaceInputAclAddItem] | None + "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." + grants: list[ArtifactReplaceInputAclGrantsItem] | None + "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." + remove: list[ArtifactReplaceInputAclRemoveItem] | None + "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." + + class ArtifactReplaceInputFile(TypedDict, total=False): data: Required[str] "File content encoded according to file.encoding." @@ -66,6 +134,8 @@ class ArtifactReplaceInputFile(TypedDict, total=False): class ArtifactReplaceInput(TypedDict, total=False): "Update an artifact" + acl: ArtifactReplaceInputAcl | None + "Access control list replacement (`grants`) or add/remove operations. Omit to leave grants unchanged. ACL-only updates do not increment the content version." description: str | None "New description for the artifact. Omit to leave the existing description unchanged." file: ArtifactReplaceInputFile | None @@ -106,6 +176,7 @@ async def create(self, input: ArtifactCreateInput) -> Artifact: Args: input: Request body. + input.acl: Initial access control list. Grants add read or write access beyond the owner. input.agent: Agent ID (`agt_...`) to associate with the artifact. input.artifact: Legacy wrapper object containing artifact attributes. Prefer top-level fields. input.description: Optional longer artifact description. @@ -114,7 +185,6 @@ async def create(self, input: ArtifactCreateInput) -> Artifact: input.idempotency_key: Optional PR-evidence retry key, limited to 255 characters. Requires both team and thread. Matching app, team, thread, name, and key values return the original artifact. input.name: Human-readable artifact name. input.org: Organization owning a system artifact. Requires an organization admin or system viewer. - input.permissions: Optional artifact permissions map. input.system: When true, create a system-owned artifact. Mutually exclusive with `team` and `user`. input.team: Team ID (`tem_...`) that should own the artifact. Mutually exclusive with `user`. input.thread: Thread ID (`thr_...`) to associate with the artifact. @@ -182,6 +252,7 @@ async def replace(self, artifact: str, input: ArtifactReplaceInput) -> Artifact: Args: artifact: Artifact ID (`art_...`) of the artifact to update. input: Request body. + input.acl: Access control list replacement (`grants`) or add/remove operations. Omit to leave grants unchanged. ACL-only updates do not increment the content version. input.description: New description for the artifact. Omit to leave the existing description unchanged. input.file: Replacement file payload. Omit to leave the current file unchanged. input.file_content: Legacy flat Base64 file content. Prefer `file.data`. @@ -264,6 +335,7 @@ def create(self, input: ArtifactCreateInput) -> Artifact: Args: input: Request body. + input.acl: Initial access control list. Grants add read or write access beyond the owner. input.agent: Agent ID (`agt_...`) to associate with the artifact. input.artifact: Legacy wrapper object containing artifact attributes. Prefer top-level fields. input.description: Optional longer artifact description. @@ -272,7 +344,6 @@ def create(self, input: ArtifactCreateInput) -> Artifact: input.idempotency_key: Optional PR-evidence retry key, limited to 255 characters. Requires both team and thread. Matching app, team, thread, name, and key values return the original artifact. input.name: Human-readable artifact name. input.org: Organization owning a system artifact. Requires an organization admin or system viewer. - input.permissions: Optional artifact permissions map. input.system: When true, create a system-owned artifact. Mutually exclusive with `team` and `user`. input.team: Team ID (`tem_...`) that should own the artifact. Mutually exclusive with `user`. input.thread: Thread ID (`thr_...`) to associate with the artifact. @@ -340,6 +411,7 @@ def replace(self, artifact: str, input: ArtifactReplaceInput) -> Artifact: Args: artifact: Artifact ID (`art_...`) of the artifact to update. input: Request body. + input.acl: Access control list replacement (`grants`) or add/remove operations. Omit to leave grants unchanged. ACL-only updates do not increment the content version. input.description: New description for the artifact. Omit to leave the existing description unchanged. input.file: Replacement file payload. Omit to leave the current file unchanged. input.file_content: Legacy flat Base64 file content. Prefer `file.data`. diff --git a/src/archastro/platform/v1/resources/custom_objects.py b/src/archastro/platform/v1/resources/custom_objects.py index f7c91ec..ef65731 100644 --- a/src/archastro/platform/v1/resources/custom_objects.py +++ b/src/archastro/platform/v1/resources/custom_objects.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 281bca96ed4e +# Content hash: ff8413024cce from __future__ import annotations @@ -57,6 +57,8 @@ class CustomObjectCreateInput(TypedDict, total=False): "Agent user ID to set as the object owner. Used when neither `team` nor `user` is supplied." config: str | None "Config ID (`cfg_...`) that resolves to the target schema. Provide one of `type`, `schema_key`, or `config`." + expires_at: datetime | None + "Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update." fields: dict[str, Any] | None "Key-value map of field values for the new object. Must conform to the schema's field definitions. Omit to create an object with all fields at their default or null values." org: str | None @@ -75,6 +77,31 @@ class CustomObjectCreateInput(TypedDict, total=False): "User ID (`user_...`) to set as the object owner. Used when neither `team` nor a higher-priority owner is set." +class CustomObjectPatchInput(TypedDict, total=False): + "Apply numeric patch ops to a custom object" + + agent: str | None + "Agent user ID owning the keyed row. At most one owner selector may be present." + create_with: dict[str, Any] | None + "Field values used to create the row when the keyed form finds no existing row. Merged over the key fields before validation; ignored by the `object` form." + idempotency_key: str | None + "Client-supplied deduplication key for this patch. A retry with the same key and the same ops replays the stored result without re-applying the write (survives server restarts); reusing a key with different ops returns 409." + object: str | None + "Custom object ID (`cobj_...`) to patch. Exactly one of `object` or `schema` + `row_key` is required." + ops: Required[list[dict[str, Any]]] + 'Numeric patch ops to apply in order (e.g. `[%{"op" => "increment", "path" => "count", "by" => 1}]`). Supported ops: `increment` (`by`), `max`/`min` (`value`), `set_if_newer` (`value`, `fence_path`, `fence`).' + row_key: str | None + "Row key naming the row within `schema` (e.g. a counter name). Requires `schema`." + schema: str | None + "Schema type identifier (`lookup_key`) for the keyed form. Requires `row_key`." + sort_key: list[str] | None + "Sort-key segments partitioning rows within the `row_key` bucket. Only meaningful for schemas that declare a `sort_key`." + team: str | None + "Team ID (`tem_...`) owning the keyed row. Defaults to the viewer's owner like the create contract." + user: str | None + "User ID (`usr_...`) owning the keyed row. At most one owner selector may be present." + + class CustomObjectReplaceInputAclAddItem(TypedDict, total=False): actions: Required[list[str]] 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' @@ -116,14 +143,140 @@ class CustomObjectReplaceInput(TypedDict, total=False): "Updated access control list. Supports full replacement via `grants` or targeted `add`/`remove` operations." expected_version: int | None "Version number the caller expects to be current. If the object's actual current version does not match, the request returns 409 to signal a concurrent modification. Omit to skip optimistic locking and keep the existing OCC merge retry." + expires_at: datetime | None + "Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update." field_ops: dict[str, Any] | None "Granular array operations to apply per field (e.g. append, prepend, remove). A field must not appear in both `fields` and `field_ops`." fields: dict[str, Any] | None "Key-value map of field values to merge into the object. Only the supplied keys are affected." + idempotency_key: str | None + "Client-supplied deduplication key for this write. A retry with the same key and the same body replays the stored result without re-applying the write (survives server restarts); reusing a key with a different body returns 409." type: str | None "Schema type identifier (`lookup_key`) of the object. Optional; used for routing context only." +class CustomObjectPatchResponseDataAclAddItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class CustomObjectPatchResponseDataAclGrantsItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class CustomObjectPatchResponseDataAclRemoveItem(BaseModel): + principal: str | None = Field( + default=None, + description='The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class CustomObjectPatchResponseDataAcl(BaseModel): + add: list[CustomObjectPatchResponseDataAclAddItem] | None = Field( + default=None, + description="Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + ) + grants: list[CustomObjectPatchResponseDataAclGrantsItem] | None = Field( + default=None, + description="Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + ) + remove: list[CustomObjectPatchResponseDataAclRemoveItem] | None = Field( + default=None, + description="Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + ) + + +class CustomObjectPatchResponseData(BaseModel): + acl: CustomObjectPatchResponseDataAcl | None = Field( + default=None, + description="Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else.", + ) + agent: str | None = Field( + default=None, + description="ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + ) + created_at: datetime | None = Field( + default=None, description="When the custom object was created (ISO 8601)." + ) + expires_at: datetime | None = Field( + default=None, description="Absolute expiry time (RFC 3339). Null when no expiry is set." + ) + fields: dict[str, Any] | None = Field( + default=None, + description="Map of field names to their current values as defined by the object's schema type.", + ) + id: str = Field(..., description="Unique identifier for the custom object (`cobj_...`).") + org: str | None = Field( + default=None, description="ID of the organization this object belongs to (`org_...`)." + ) + row_key: str | None = Field( + default=None, + description="An optional stable key used to identify this object by a caller-controlled string rather than its generated ID. `null` if not set.", + ) + sandbox: str | None = Field( + default=None, + description="ID of the sandbox environment this object is scoped to (`dsb_...`). `null` for production objects.", + ) + schema_type: str | None = Field( + default=None, + description="The lookup key of the schema type that defines this object's field structure. `null` if the schema type has not been set.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this object (`tem_...`). `null` if the object is not team-scoped.", + ) + updated_at: datetime | None = Field( + default=None, + description="When the custom object was last modified (ISO 8601). `null` if the object has never been updated after creation.", + ) + user: str | None = Field( + default=None, + description="ID of the user that owns this object (`usr_...`). `null` if the object is not user-scoped.", + ) + version: int | None = Field( + default=None, + description="Optimistic concurrency version of the object. Increments with each successful update; pass this value in write operations to detect conflicting changes.", + ) + + +class CustomObjectPatchResponse(BaseModel): + """ + Successful response + """ + + data: CustomObjectPatchResponseData = Field( + ..., description="The custom object after the patch has been applied." + ) + meta: dict[str, Any] | None = Field( + default=None, description="Version metadata and per-op patch results." + ) + + class CustomObjectDeleteResponse(BaseModel): """ Successful response @@ -194,9 +347,16 @@ class CustomObjectReplaceResponseData(BaseModel): default=None, description="Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else.", ) + agent: str | None = Field( + default=None, + description="ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + ) created_at: datetime | None = Field( default=None, description="When the custom object was created (ISO 8601)." ) + expires_at: datetime | None = Field( + default=None, description="Absolute expiry time (RFC 3339). Null when no expiry is set." + ) fields: dict[str, Any] | None = Field( default=None, description="Map of field names to their current values as defined by the object's schema type.", @@ -257,8 +417,12 @@ async def list( *, type: str | None = None, schema_key: str | None = None, + field_filters: dict[str, Any] | None = None, + system_only: bool | None = None, + order_by_field: str | None = None, row_key: str | None = None, sort_key: builtins.list[str] | None = None, + keys: builtins.list[dict[str, Any]] | None = None, team: builtins.list[str] | None = None, user: builtins.list[str] | None = None, agent: builtins.list[str] | None = None, @@ -266,7 +430,9 @@ async def list( query: str | None = None, search: str | None = None, page: int | None = None, + updated_since: datetime | None = None, page_size: int | None = None, + include_count: bool | None = None, ) -> CustomObjectListResponse: """ List custom objects @@ -289,8 +455,12 @@ async def list( Args: type: Schema type identifier (`lookup_key`) to filter by. Only objects of this type are returned. Alias of `schema_key`; either name is accepted. schema_key: Legacy alias for `type`. Prefer `type` on new clients. When both are supplied, `type` wins. + field_filters: JSON containment predicates on fields, applied before pagination and counting. + system_only: Only system-owned objects (no user, team, agent or thread owner). Visibility is unchanged. + order_by_field: Top-level JSON field for descending text order, with ID tie-breaker. Full-text relevance takes precedence. row_key: Exact `row_key` value to match. When supplied, only objects with this partition key are returned. sort_key: One or more `sort_key` values to match within the `row_key` partition. Requires `row_key` to be set. + keys: JSON-encoded array of up to 100 objects with required string row_key and optional string-array sort_key. Missing or empty sort_key selects the whole partition. Mutually exclusive with row_key and sort_key. Pagination applies globally across all matched partitions. team: One or more team IDs (`team_...`) to filter by owning team. Returns objects owned by any of the supplied teams. user: One or more user IDs (`user_...`) to filter by owning user. Returns objects owned by any of the supplied users. agent: One or more agent IDs to filter by owning agent. Returns objects owned by any of the supplied agents. @@ -298,7 +468,9 @@ async def list( query: Full-text search string matched against the schema's configured search fields. When supplied, results are ordered by relevance score descending instead of creation time descending. search: Case-insensitive substring search applied across the schema type and serialized field values. Deprecated prefer `query` for full-text search. Retained for developer-namespace clients. page: Page number to retrieve (1-indexed). Defaults to `1`. + updated_since: Include only objects updated at or after this ISO 8601 timestamp. Applies before pagination and counting. page_size: Number of objects per page. Defaults to `25`; maximum is `100`. + include_count: Include total_entries and total_pages (default true). When false, totals are null, has_next uses one extra row, and pages beyond the end return an empty list without clamping. The page offset must fit PostgreSQL's signed 64-bit integer range. Returns: Paginated list of custom objects matching the supplied filters. @@ -308,10 +480,18 @@ async def list( query["type"] = type if schema_key is not None: query["schema_key"] = schema_key + if field_filters is not None: + query["field_filters"] = field_filters + if system_only is not None: + query["system_only"] = system_only + if order_by_field is not None: + query["order_by_field"] = order_by_field if row_key is not None: query["row_key"] = row_key if sort_key is not None: query["sort_key"] = sort_key + if keys is not None: + query["keys"] = keys if team is not None: query["team"] = team if user is not None: @@ -326,8 +506,12 @@ async def list( query["search"] = search if page is not None: query["page"] = page + if updated_since is not None: + query["updated_since"] = updated_since if page_size is not None: query["page_size"] = page_size + if include_count is not None: + query["include_count"] = include_count return await self._http.request( "/api/v1/custom_objects", query=query, @@ -359,6 +543,7 @@ async def create(self, input: CustomObjectCreateInput) -> CustomObject: input.acl: Access control list for the custom object. Supports explicit `read` and `write` grants to users, teams, organizations, organization roles, agents, or everyone. input.agent: Agent user ID to set as the object owner. Used when neither `team` nor `user` is supplied. input.config: Config ID (`cfg_...`) that resolves to the target schema. Provide one of `type`, `schema_key`, or `config`. + input.expires_at: Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update. input.fields: Key-value map of field values for the new object. Must conform to the schema's field definitions. Omit to create an object with all fields at their default or null values. input.org: Organization ID (`org_...`) to associate with the object. Typically required for system-owned objects. input.schema_key: Legacy alias for `type` (schema `lookup_key`). Prefer `type` on new clients. Provide one of `type`, `schema_key`, or `config`. @@ -378,6 +563,47 @@ async def create(self, input: CustomObjectCreateInput) -> CustomObject: response_type=CustomObject, ) + async def patch(self, input: CustomObjectPatchInput) -> CustomObjectPatchResponse: + """ + Apply numeric patch ops to a custom object + Applies numeric patch ops (`increment`, `max`, `min`, `set_if_newer` with + dotted `path`s) atomically to a custom object and returns the updated + object along with per-op results. The authenticated viewer must have + permission to modify the object. + Address the row in one of two ways: by `object` ID (`cobj_...`), or by + `schema` + `row_key` (with optional `sort_key` for schemas that declare + one). The keyed form resolves the row for the same owner the create + contract would use and, when no row exists yet, creates it from + `create_with` merged over the key fields before applying the ops so an + aggregator's first call creates the counter and later calls increment it. + A racing create resolves to the same row instead of a duplicate. + Pass `idempotency_key` to make retries safe: a retry with the same key and + ops replays the stored result without re-applying the write (survives + server restarts); reusing a key with different ops returns 409. + + Args: + input: Request body. + input.agent: Agent user ID owning the keyed row. At most one owner selector may be present. + input.create_with: Field values used to create the row when the keyed form finds no existing row. Merged over the key fields before validation; ignored by the `object` form. + input.idempotency_key: Client-supplied deduplication key for this patch. A retry with the same key and the same ops replays the stored result without re-applying the write (survives server restarts); reusing a key with different ops returns 409. + input.object: Custom object ID (`cobj_...`) to patch. Exactly one of `object` or `schema` + `row_key` is required. + input.ops: Numeric patch ops to apply in order (e.g. `[%{"op" => "increment", "path" => "count", "by" => 1}]`). Supported ops: `increment` (`by`), `max`/`min` (`value`), `set_if_newer` (`value`, `fence_path`, `fence`). + input.row_key: Row key naming the row within `schema` (e.g. a counter name). Requires `schema`. + input.schema: Schema type identifier (`lookup_key`) for the keyed form. Requires `row_key`. + input.sort_key: Sort-key segments partitioning rows within the `row_key` bucket. Only meaningful for schemas that declare a `sort_key`. + input.team: Team ID (`tem_...`) owning the keyed row. Defaults to the viewer's owner like the create contract. + input.user: User ID (`usr_...`) owning the keyed row. At most one owner selector may be present. + + Returns: + Successful response + """ + return await self._http.request( + "/api/v1/custom_objects/patch", + method="POST", + body=input, + response_type=CustomObjectPatchResponse, + ) + async def delete(self, object: str) -> CustomObjectDeleteResponse: """ Delete a custom object @@ -446,8 +672,10 @@ async def replace( input: Request body. input.acl: Updated access control list. Supports full replacement via `grants` or targeted `add`/`remove` operations. input.expected_version: Version number the caller expects to be current. If the object's actual current version does not match, the request returns 409 to signal a concurrent modification. Omit to skip optimistic locking and keep the existing OCC merge retry. + input.expires_at: Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update. input.field_ops: Granular array operations to apply per field (e.g. append, prepend, remove). A field must not appear in both `fields` and `field_ops`. input.fields: Key-value map of field values to merge into the object. Only the supplied keys are affected. + input.idempotency_key: Client-supplied deduplication key for this write. A retry with the same key and the same body replays the stored result without re-applying the write (survives server restarts); reusing a key with a different body returns 409. input.type: Schema type identifier (`lookup_key`) of the object. Optional; used for routing context only. Returns: @@ -470,8 +698,12 @@ def list( *, type: str | None = None, schema_key: str | None = None, + field_filters: dict[str, Any] | None = None, + system_only: bool | None = None, + order_by_field: str | None = None, row_key: str | None = None, sort_key: builtins.list[str] | None = None, + keys: builtins.list[dict[str, Any]] | None = None, team: builtins.list[str] | None = None, user: builtins.list[str] | None = None, agent: builtins.list[str] | None = None, @@ -479,7 +711,9 @@ def list( query: str | None = None, search: str | None = None, page: int | None = None, + updated_since: datetime | None = None, page_size: int | None = None, + include_count: bool | None = None, ) -> CustomObjectListResponse: """ List custom objects @@ -502,8 +736,12 @@ def list( Args: type: Schema type identifier (`lookup_key`) to filter by. Only objects of this type are returned. Alias of `schema_key`; either name is accepted. schema_key: Legacy alias for `type`. Prefer `type` on new clients. When both are supplied, `type` wins. + field_filters: JSON containment predicates on fields, applied before pagination and counting. + system_only: Only system-owned objects (no user, team, agent or thread owner). Visibility is unchanged. + order_by_field: Top-level JSON field for descending text order, with ID tie-breaker. Full-text relevance takes precedence. row_key: Exact `row_key` value to match. When supplied, only objects with this partition key are returned. sort_key: One or more `sort_key` values to match within the `row_key` partition. Requires `row_key` to be set. + keys: JSON-encoded array of up to 100 objects with required string row_key and optional string-array sort_key. Missing or empty sort_key selects the whole partition. Mutually exclusive with row_key and sort_key. Pagination applies globally across all matched partitions. team: One or more team IDs (`team_...`) to filter by owning team. Returns objects owned by any of the supplied teams. user: One or more user IDs (`user_...`) to filter by owning user. Returns objects owned by any of the supplied users. agent: One or more agent IDs to filter by owning agent. Returns objects owned by any of the supplied agents. @@ -511,7 +749,9 @@ def list( query: Full-text search string matched against the schema's configured search fields. When supplied, results are ordered by relevance score descending instead of creation time descending. search: Case-insensitive substring search applied across the schema type and serialized field values. Deprecated prefer `query` for full-text search. Retained for developer-namespace clients. page: Page number to retrieve (1-indexed). Defaults to `1`. + updated_since: Include only objects updated at or after this ISO 8601 timestamp. Applies before pagination and counting. page_size: Number of objects per page. Defaults to `25`; maximum is `100`. + include_count: Include total_entries and total_pages (default true). When false, totals are null, has_next uses one extra row, and pages beyond the end return an empty list without clamping. The page offset must fit PostgreSQL's signed 64-bit integer range. Returns: Paginated list of custom objects matching the supplied filters. @@ -521,10 +761,18 @@ def list( query["type"] = type if schema_key is not None: query["schema_key"] = schema_key + if field_filters is not None: + query["field_filters"] = field_filters + if system_only is not None: + query["system_only"] = system_only + if order_by_field is not None: + query["order_by_field"] = order_by_field if row_key is not None: query["row_key"] = row_key if sort_key is not None: query["sort_key"] = sort_key + if keys is not None: + query["keys"] = keys if team is not None: query["team"] = team if user is not None: @@ -539,8 +787,12 @@ def list( query["search"] = search if page is not None: query["page"] = page + if updated_since is not None: + query["updated_since"] = updated_since if page_size is not None: query["page_size"] = page_size + if include_count is not None: + query["include_count"] = include_count return self._http.request( "/api/v1/custom_objects", query=query, @@ -572,6 +824,7 @@ def create(self, input: CustomObjectCreateInput) -> CustomObject: input.acl: Access control list for the custom object. Supports explicit `read` and `write` grants to users, teams, organizations, organization roles, agents, or everyone. input.agent: Agent user ID to set as the object owner. Used when neither `team` nor `user` is supplied. input.config: Config ID (`cfg_...`) that resolves to the target schema. Provide one of `type`, `schema_key`, or `config`. + input.expires_at: Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update. input.fields: Key-value map of field values for the new object. Must conform to the schema's field definitions. Omit to create an object with all fields at their default or null values. input.org: Organization ID (`org_...`) to associate with the object. Typically required for system-owned objects. input.schema_key: Legacy alias for `type` (schema `lookup_key`). Prefer `type` on new clients. Provide one of `type`, `schema_key`, or `config`. @@ -591,6 +844,47 @@ def create(self, input: CustomObjectCreateInput) -> CustomObject: response_type=CustomObject, ) + def patch(self, input: CustomObjectPatchInput) -> CustomObjectPatchResponse: + """ + Apply numeric patch ops to a custom object + Applies numeric patch ops (`increment`, `max`, `min`, `set_if_newer` with + dotted `path`s) atomically to a custom object and returns the updated + object along with per-op results. The authenticated viewer must have + permission to modify the object. + Address the row in one of two ways: by `object` ID (`cobj_...`), or by + `schema` + `row_key` (with optional `sort_key` for schemas that declare + one). The keyed form resolves the row for the same owner the create + contract would use and, when no row exists yet, creates it from + `create_with` merged over the key fields before applying the ops so an + aggregator's first call creates the counter and later calls increment it. + A racing create resolves to the same row instead of a duplicate. + Pass `idempotency_key` to make retries safe: a retry with the same key and + ops replays the stored result without re-applying the write (survives + server restarts); reusing a key with different ops returns 409. + + Args: + input: Request body. + input.agent: Agent user ID owning the keyed row. At most one owner selector may be present. + input.create_with: Field values used to create the row when the keyed form finds no existing row. Merged over the key fields before validation; ignored by the `object` form. + input.idempotency_key: Client-supplied deduplication key for this patch. A retry with the same key and the same ops replays the stored result without re-applying the write (survives server restarts); reusing a key with different ops returns 409. + input.object: Custom object ID (`cobj_...`) to patch. Exactly one of `object` or `schema` + `row_key` is required. + input.ops: Numeric patch ops to apply in order (e.g. `[%{"op" => "increment", "path" => "count", "by" => 1}]`). Supported ops: `increment` (`by`), `max`/`min` (`value`), `set_if_newer` (`value`, `fence_path`, `fence`). + input.row_key: Row key naming the row within `schema` (e.g. a counter name). Requires `schema`. + input.schema: Schema type identifier (`lookup_key`) for the keyed form. Requires `row_key`. + input.sort_key: Sort-key segments partitioning rows within the `row_key` bucket. Only meaningful for schemas that declare a `sort_key`. + input.team: Team ID (`tem_...`) owning the keyed row. Defaults to the viewer's owner like the create contract. + input.user: User ID (`usr_...`) owning the keyed row. At most one owner selector may be present. + + Returns: + Successful response + """ + return self._http.request( + "/api/v1/custom_objects/patch", + method="POST", + body=input, + response_type=CustomObjectPatchResponse, + ) + def delete(self, object: str) -> CustomObjectDeleteResponse: """ Delete a custom object @@ -657,8 +951,10 @@ def replace(self, object: str, input: CustomObjectReplaceInput) -> CustomObjectR input: Request body. input.acl: Updated access control list. Supports full replacement via `grants` or targeted `add`/`remove` operations. input.expected_version: Version number the caller expects to be current. If the object's actual current version does not match, the request returns 409 to signal a concurrent modification. Omit to skip optimistic locking and keep the existing OCC merge retry. + input.expires_at: Absolute expiry time in RFC 3339 format. Pass null to clear; omit to leave unchanged on update. input.field_ops: Granular array operations to apply per field (e.g. append, prepend, remove). A field must not appear in both `fields` and `field_ops`. input.fields: Key-value map of field values to merge into the object. Only the supplied keys are affected. + input.idempotency_key: Client-supplied deduplication key for this write. A retry with the same key and the same body replays the stored result without re-applying the write (survives server restarts); reusing a key with a different body returns 409. input.type: Schema type identifier (`lookup_key`) of the object. Optional; used for routing context only. Returns: diff --git a/src/archastro/platform/v1/resources/event_subscriptions.py b/src/archastro/platform/v1/resources/event_subscriptions.py index 24f4396..8c01d05 100644 --- a/src/archastro/platform/v1/resources/event_subscriptions.py +++ b/src/archastro/platform/v1/resources/event_subscriptions.py @@ -1,10 +1,10 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 0fc58a1033bf +# Content hash: 11a031f3d19d from __future__ import annotations -from typing import Literal, Required, TypedDict +from typing import Any, Literal, Required, TypedDict from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.events import ( @@ -21,22 +21,34 @@ class EventSubscriptionCreateInput(TypedDict, total=False): event_names: Required[list[str]] "Exact event names to receive." + filter: dict[str, Any] | None + "Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events." max_pending_events: int | None "Queue cap. Defaults to 100; maximum 1000." name: Required[str] "Customer-defined subscription name." + previous_filter: dict[str, Any] | None + "Expected legacy filter for atomic watch-key adoption." + previous_filters: list[dict[str, Any]] | None + "Additional exact historical filters accepted for atomic watch-key adoption." + previous_watch_key: str | None + "Optionally adopt an existing key in the exact same owner and residence when the destination is absent and its filter exactly matches previous_filter, one of previous_filters, or filter. Preserves queue identity and deliveries; mismatches reject the request without changing either queue." retention_seconds: int | None "Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only." status: Literal["active", "paused"] | None "Initial status. Defaults to active." visibility_timeout_seconds: int | None "Default claim lease in seconds. Defaults to 300." + watch_key: str | None + "Optional durable identity within the caller owner and residence. Repeated creates replace routes and filter on that queue; omission creates a new queue regardless of name." class EventSubscriptionUpdateInput(TypedDict, total=False): "Update a domain-event subscription" event_names: list[str] | None + filter: dict[str, Any] | None + "Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events." max_pending_events: int | None name: str | None retention_seconds: int | None @@ -97,11 +109,16 @@ async def create(self, input: EventSubscriptionCreateInput) -> EventSubscription Args: input: Request body. input.event_names: Exact event names to receive. + input.filter: Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events. input.max_pending_events: Queue cap. Defaults to 100; maximum 1000. input.name: Customer-defined subscription name. + input.previous_filter: Expected legacy filter for atomic watch-key adoption. + input.previous_filters: Additional exact historical filters accepted for atomic watch-key adoption. + input.previous_watch_key: Optionally adopt an existing key in the exact same owner and residence when the destination is absent and its filter exactly matches previous_filter, one of previous_filters, or filter. Preserves queue identity and deliveries; mismatches reject the request without changing either queue. input.retention_seconds: Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only. input.status: Initial status. Defaults to active. input.visibility_timeout_seconds: Default claim lease in seconds. Defaults to 300. + input.watch_key: Optional durable identity within the caller owner and residence. Repeated creates replace routes and filter on that queue; omission creates a new queue regardless of name. Returns: The new volatile subscription. @@ -152,6 +169,7 @@ async def update( Args: subscription: Subscription ID (`esub_...`). input: Request body. + input.filter: Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events. input.retention_seconds: Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only. Returns: @@ -278,11 +296,16 @@ def create(self, input: EventSubscriptionCreateInput) -> EventSubscription: Args: input: Request body. input.event_names: Exact event names to receive. + input.filter: Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events. input.max_pending_events: Queue cap. Defaults to 100; maximum 1000. input.name: Customer-defined subscription name. + input.previous_filter: Expected legacy filter for atomic watch-key adoption. + input.previous_filters: Additional exact historical filters accepted for atomic watch-key adoption. + input.previous_watch_key: Optionally adopt an existing key in the exact same owner and residence when the destination is absent and its filter exactly matches previous_filter, one of previous_filters, or filter. Preserves queue identity and deliveries; mismatches reject the request without changing either queue. input.retention_seconds: Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only. input.status: Initial status. Defaults to active. input.visibility_timeout_seconds: Default claim lease in seconds. Defaults to 300. + input.watch_key: Optional durable identity within the caller owner and residence. Repeated creates replace routes and filter on that queue; omission creates a new queue regardless of name. Returns: The new volatile subscription. @@ -331,6 +354,7 @@ def update(self, subscription: str, input: EventSubscriptionUpdateInput) -> Even Args: subscription: Subscription ID (`esub_...`). input: Request body. + input.filter: Optional match over subject_key, actor_key, and allowlisted dims. Only valid when every event name is a custom (script-emitted) event type. Missing filter matches all events. input.retention_seconds: Delivery retention in seconds. Defaults to 86400 (24 hours); between 7200 and 2592000. Applies to future deliveries only. Returns: diff --git a/src/archastro/platform/v1/resources/files.py b/src/archastro/platform/v1/resources/files.py index cb7a384..d3031f1 100644 --- a/src/archastro/platform/v1/resources/files.py +++ b/src/archastro/platform/v1/resources/files.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 452760534483 +# Content hash: eefe9c7e925d from __future__ import annotations @@ -89,7 +89,7 @@ async def create(self, input: FileCreateInput) -> StorageFile: including a signed download URL. Use this endpoint to store images, documents, or other binary assets that can then be referenced by agents, teams, or users. The JSON body uses Platform's default 8,000,000-byte parser budget; the - ArchDev Platform proxy has a separate 32 MiB request cap. Base64 requires + an upstream proxy in front of Platform may have a separate 32 MiB request cap. Base64 requires `4 * ceil(binary_bytes / 3)` bytes, plus the UTF-8 JSON envelope (including filename, owner IDs, escaping and other fields). For an envelope of E bytes, budget at most `3 * floor((8,000,000 - E) / 4)` binary bytes: less than @@ -166,14 +166,14 @@ async def update(self, file: str, input: FileUpdateInput) -> StorageFile: async def avatar(self, file: str, token: str) -> dict[str, str]: """ - Fetch an agent avatar image - Returns the raw image bytes for an agent's profile picture identified by `file`. - This endpoint is designed for integration partners (such as Slack) that fetch - avatar URLs via plain GET requests without bearer token support. Authorization - is performed via a short, stable capability `token` rather than an HTTP header. + Fetch an agent or user avatar image + Returns the raw image bytes for an agent's or user's profile picture identified + by `file`. Chat messages and integration partners (such as Slack) fetch avatars + through it with plain GET requests, without bearer tokens. Authorization is + performed via a short, stable capability `token` rather than an HTTP header. The `token` is an HMAC-based capability tied to the file ID. It does not expire, - but it is invalidated when the agent's profile picture is replaced or the agent is - deleted shared caches may continue serving the old image until the + but it is invalidated when the profile picture is replaced or its agent or user + is deleted shared caches may continue serving the old image until the `Cache-Control` max-age of one hour elapses. The endpoint never redirects to a signed storage URL; bytes are served inline so behavior is consistent across storage backends. @@ -181,16 +181,41 @@ async def avatar(self, file: str, token: str) -> dict[str, str]: as an agent avatar return a uniform `404` to avoid acting as an existence oracle. Args: - file: File ID of the agent's profile picture (`fil_...`). Must be currently set as an agent's profile picture within the same app. + file: File ID of the profile picture (`fil_...`). Must be currently set as an agent's or user's profile picture within the same app. token: HMAC capability token authorizing access to this specific file. Obtained from the avatar URL minted when the profile picture was set. Returns: - Raw image bytes of the agent avatar, served with the file's original content type. + Raw image bytes of the avatar, served with the file's original content type. """ query: dict[str, object] = {} query["token"] = token return await self._http.request_raw(f"/api/v1/files/{file}/avatar", query=query) + async def download(self, file: str) -> dict[str, str]: + """ + Download a file's bytes + Returns the raw binary content of a file the authenticated viewer may see. + The response `Content-Type` header reflects the file's stored MIME type and + `Content-Disposition` names the file as an attachment, so a browser can hand + the body to the download manager directly. + Unlike `Show` (which mints a storage URL the browser must resolve itself), + this endpoint serves bytes inline through the authenticated API mount, so it + behaves identically across storage backends: local files are not exposed as + unauthenticated static paths, and signed GCS URLs never cross the browser + boundary. A file that exists but is not visible to the current viewer + returns `404` rather than `403` to avoid revealing the file's existence. + Byte downloads are limited to 32 MiB (33,554,432 bytes). The storage provider + enforces this limit on actual content, independently of stored size metadata. + Larger content returns HTTP `413` with error code `content_too_large`. + + Args: + file: File ID (`fil_...`) of the file to download. + + Returns: + Raw binary content of the file, served with the file's stored MIME type. + """ + return await self._http.request_raw(f"/api/v1/files/{file}/download") + async def org_logo(self, file: str, token: str) -> dict[str, str]: """ Fetch an org logo image @@ -261,7 +286,7 @@ def create(self, input: FileCreateInput) -> StorageFile: including a signed download URL. Use this endpoint to store images, documents, or other binary assets that can then be referenced by agents, teams, or users. The JSON body uses Platform's default 8,000,000-byte parser budget; the - ArchDev Platform proxy has a separate 32 MiB request cap. Base64 requires + an upstream proxy in front of Platform may have a separate 32 MiB request cap. Base64 requires `4 * ceil(binary_bytes / 3)` bytes, plus the UTF-8 JSON envelope (including filename, owner IDs, escaping and other fields). For an envelope of E bytes, budget at most `3 * floor((8,000,000 - E) / 4)` binary bytes: less than @@ -338,14 +363,14 @@ def update(self, file: str, input: FileUpdateInput) -> StorageFile: def avatar(self, file: str, token: str) -> dict[str, str]: """ - Fetch an agent avatar image - Returns the raw image bytes for an agent's profile picture identified by `file`. - This endpoint is designed for integration partners (such as Slack) that fetch - avatar URLs via plain GET requests without bearer token support. Authorization - is performed via a short, stable capability `token` rather than an HTTP header. + Fetch an agent or user avatar image + Returns the raw image bytes for an agent's or user's profile picture identified + by `file`. Chat messages and integration partners (such as Slack) fetch avatars + through it with plain GET requests, without bearer tokens. Authorization is + performed via a short, stable capability `token` rather than an HTTP header. The `token` is an HMAC-based capability tied to the file ID. It does not expire, - but it is invalidated when the agent's profile picture is replaced or the agent is - deleted shared caches may continue serving the old image until the + but it is invalidated when the profile picture is replaced or its agent or user + is deleted shared caches may continue serving the old image until the `Cache-Control` max-age of one hour elapses. The endpoint never redirects to a signed storage URL; bytes are served inline so behavior is consistent across storage backends. @@ -353,16 +378,41 @@ def avatar(self, file: str, token: str) -> dict[str, str]: as an agent avatar return a uniform `404` to avoid acting as an existence oracle. Args: - file: File ID of the agent's profile picture (`fil_...`). Must be currently set as an agent's profile picture within the same app. + file: File ID of the profile picture (`fil_...`). Must be currently set as an agent's or user's profile picture within the same app. token: HMAC capability token authorizing access to this specific file. Obtained from the avatar URL minted when the profile picture was set. Returns: - Raw image bytes of the agent avatar, served with the file's original content type. + Raw image bytes of the avatar, served with the file's original content type. """ query: dict[str, object] = {} query["token"] = token return self._http.request_raw(f"/api/v1/files/{file}/avatar", query=query) + def download(self, file: str) -> dict[str, str]: + """ + Download a file's bytes + Returns the raw binary content of a file the authenticated viewer may see. + The response `Content-Type` header reflects the file's stored MIME type and + `Content-Disposition` names the file as an attachment, so a browser can hand + the body to the download manager directly. + Unlike `Show` (which mints a storage URL the browser must resolve itself), + this endpoint serves bytes inline through the authenticated API mount, so it + behaves identically across storage backends: local files are not exposed as + unauthenticated static paths, and signed GCS URLs never cross the browser + boundary. A file that exists but is not visible to the current viewer + returns `404` rather than `403` to avoid revealing the file's existence. + Byte downloads are limited to 32 MiB (33,554,432 bytes). The storage provider + enforces this limit on actual content, independently of stored size metadata. + Larger content returns HTTP `413` with error code `content_too_large`. + + Args: + file: File ID (`fil_...`) of the file to download. + + Returns: + Raw binary content of the file, served with the file's stored MIME type. + """ + return self._http.request_raw(f"/api/v1/files/{file}/download") + def org_logo(self, file: str, token: str) -> dict[str, str]: """ Fetch an org logo image diff --git a/src/archastro/platform/v1/resources/knowledge_sources.py b/src/archastro/platform/v1/resources/knowledge_sources.py index 94e3eb6..b5ce969 100644 --- a/src/archastro/platform/v1/resources/knowledge_sources.py +++ b/src/archastro/platform/v1/resources/knowledge_sources.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: c34bfb6c75e1 +# Content hash: 481469aba270 from __future__ import annotations @@ -196,6 +196,14 @@ class KnowledgeSourceSearchResponseDataItem(BaseModel): default=None, description="Raw content payload as stored by the source connector, before normalization.", ) + structured_data: str | None = Field( + default=None, + description="Public ID (`sda_...`) of the structured-data record attached to the source message. Present only for such items on source search.", + ) + structured_data_summary: str | None = Field( + default=None, + description="Search summary of that record, computed when the request is served. When present, `content` is the indexed text and may be stale.", + ) type: str | None = Field( default=None, description='Type identifier of the parent knowledge source (e.g. `"gmail"`, `"github_activity"`). Returns `"unknown"` when the source association is not loaded.', @@ -208,7 +216,16 @@ class KnowledgeSourceSearchResponse(BaseModel): """ data: list[KnowledgeSourceSearchResponseDataItem] = Field( - ..., description="Array of matching knowledge items ordered by relevance score descending." + ..., + description="Array of matching knowledge items ordered by relevance score descending. An item indexed from a message that has structured data attached also carries `structured_data` and `structured_data_summary`, the record's search summary computed at read time. `content` is still the indexed text and may be stale.", + ) + degraded: bool | None = Field( + default=None, + description="True when a `hybrid` search ran fulltext only because the vector leg was unavailable. Absent when the query was empty.", + ) + effective_mode: Literal["hybrid", "vector", "fulltext"] | None = Field( + default=None, + description="Retrieval mode that actually ran. Differs from the requested `mode` when a `hybrid` search fell back to `fulltext`. Absent when the query was empty.", ) diff --git a/src/archastro/platform/v1/resources/kv.py b/src/archastro/platform/v1/resources/kv.py index a11bd63..70e6ad4 100644 --- a/src/archastro/platform/v1/resources/kv.py +++ b/src/archastro/platform/v1/resources/kv.py @@ -1,11 +1,13 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 82a6b05d3008 +# Content hash: c7ca581aeeaa from __future__ import annotations from typing import Required, TypedDict +from pydantic import BaseModel, Field + from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.common import KeyValueStorageEntry, KeyValueStorageEntryPage @@ -13,10 +15,20 @@ class KvCreateInput(TypedDict, total=False): "Create a key-value storage entry" + agent: str | None + "Agent owner ID." key: Required[str] "Storage key for the entry. Must be a non-empty string unique to this user." + org: str | None + "Organization scope for the owner." + system: bool | None + "Select system ownership within the app or organization." + team: str | None + "Team owner ID." + ttl_seconds: int | None + "Positive lifetime in seconds; omitted means no expiry." user: str | None - "Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers." + "User owner ID. Elevated callers may instead select team, agent, org, or system ownership." value: Required[str] "Value to store under `key`. Must be a non-empty string." @@ -24,12 +36,31 @@ class KvCreateInput(TypedDict, total=False): class KvUpsertInput(TypedDict, total=False): "Create or update a key-value storage entry" + agent: str | None + "Agent owner ID." + org: str | None + "Organization scope for the owner." + system: bool | None + "Select system ownership within the app or organization." + team: str | None + "Team owner ID." + ttl_seconds: int | None + "Positive lifetime in seconds; omitted means no expiry." user: str | None - "Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers." + "User owner ID. Elevated callers may instead select team, agent, org, or system ownership." value: Required[str] "New value to store under `key`. Must be a non-empty string. Replaces any existing value." +class KvDeleteResponse(BaseModel): + """ + Successful response + """ + + deleted: bool = Field(..., description="Always `true` when the deletion succeeds.") + key: str = Field(..., description="Storage key of the deleted entry.") + + class AsyncKvResource: def __init__(self, http: HttpClient): self._http = http @@ -37,6 +68,10 @@ def __init__(self, http: HttpClient): async def list( self, *, + org: str | None = None, + team: str | None = None, + agent: str | None = None, + system: bool | None = None, page: int | None = None, page_size: int | None = None, user: str | None = None, @@ -54,9 +89,14 @@ async def list( response across all users within the caller's app. Use `user` to scope results to a single user, `user_search` to do a substring match on email or full name, and `key` to filter entries whose key starts with the given prefix. Results are - ordered by creation time descending. + ordered by update time descending. Supplying org, team, agent, or system selects + a single owner's flat list instead. Expired entries are excluded in every mode. Args: + org: Organization scope for the owner. + team: Team owner ID. + agent: Agent owner ID. + system: Select system ownership within the app or organization. page: Page number to retrieve. Applies to developer and server-to-server callers only. Defaults to 1. page_size: Number of entries per page. Applies to developer and server-to-server callers only. Defaults to 25; maximum is 100. user: Filter results to entries belonging to this user ID. Applies to developer and server-to-server callers only. @@ -67,6 +107,14 @@ async def list( Key-value storage entries for the current page, with pagination metadata for developer and server-to-server callers. """ query: dict[str, object] = {} + if org is not None: + query["org"] = org + if team is not None: + query["team"] = team + if agent is not None: + query["agent"] = agent + if system is not None: + query["system"] = system if page is not None: query["page"] = page if page_size is not None: @@ -86,18 +134,24 @@ async def list( async def create(self, input: KvCreateInput) -> KeyValueStorageEntry: """ Create a key-value storage entry - Creates a new key-value storage entry for the target user under the given key. - The key must not already exist for this user; use the upsert endpoint to create - or overwrite in a single call. - End-user (user-JWT) callers always write to their own storage. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. Attempting to write for a user in a different app - returns 404. + Creates a key once per owner. An existing key returns HTTP 409 conflict, including expired rows awaiting sweep. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.create`): the entry is owned by the triggering + automation's owner; scripts pass no owner params. A duplicate key + returns a script Err with code "conflict" instead of raising. Args: input: Request body. + input.agent: Agent owner ID. input.key: Storage key for the entry. Must be a non-empty string unique to this user. - input.user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + input.org: Organization scope for the owner. + input.system: Select system ownership within the app or organization. + input.team: Team owner ID. + input.ttl_seconds: Positive lifetime in seconds; omitted means no expiry. + input.user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. input.value: Value to store under `key`. Must be a non-empty string. Returns: @@ -110,41 +164,69 @@ async def create(self, input: KvCreateInput) -> KeyValueStorageEntry: response_type=KeyValueStorageEntry, ) - async def delete(self, key: str) -> None: + async def delete(self, key: str) -> KvDeleteResponse: """ Delete a key-value storage entry - Permanently deletes the key-value storage entry identified by `key` for the - target user. Returns 204 No Content on success and 404 if the entry does not - exist. - End-user (user-JWT) callers can only delete entries they own. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. + Deletes an unexpired entry by owner and key. Returns 204, or 404 when absent. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.delete`): removes the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key of the entry to delete. Must be a non-empty string. Returns: - Empty response body. HTTP 204 No Content on success. + Successful response """ - await self._http.request(f"/api/v1/kv/{key}", method="DELETE") + return await self._http.request( + f"/api/v1/kv/{key}", + method="DELETE", + response_type=KvDeleteResponse, + ) - async def get(self, key: str, *, user: str | None = None) -> KeyValueStorageEntry: + async def get( + self, + key: str, + *, + org: str | None = None, + team: str | None = None, + agent: str | None = None, + system: bool | None = None, + user: str | None = None, + ) -> KeyValueStorageEntry: """ Retrieve a key-value storage entry - Returns the key-value storage entry identified by `key` for the target user. - Returns 404 if no entry exists for that key. - End-user (user-JWT) callers retrieve entries from their own storage. Developer - and server-to-server callers must supply a `user` param identifying the target - user within their app's scope. + Returns an unexpired entry by owner and key, or 404. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.get`): reads the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key of the entry to retrieve. Must be a non-empty string. - user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + org: Organization scope for the owner. + team: Team owner ID. + agent: Agent owner ID. + system: Select system ownership within the app or organization. + user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. Returns: The key-value storage entry for the given key. """ query: dict[str, object] = {} + if org is not None: + query["org"] = org + if team is not None: + query["team"] = team + if agent is not None: + query["agent"] = agent + if system is not None: + query["system"] = system if user is not None: query["user"] = user return await self._http.request( @@ -156,18 +238,23 @@ async def get(self, key: str, *, user: str | None = None) -> KeyValueStorageEntr async def upsert(self, key: str, input: KvUpsertInput) -> KeyValueStorageEntry: """ Create or update a key-value storage entry - Creates a new key-value storage entry for the given `key`, or overwrites the - value if an entry already exists. This is the idempotent alternative to the - create endpoint: safe to call regardless of whether the key already exists. - End-user (user-JWT) callers always write to their own storage. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. Attempting to write for a user in a different app - returns 404. + Creates or replaces a value and its expiry. Omitted ttl_seconds clears any previous expiry. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.upsert`): overwrites the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key to create or overwrite. Must be a non-empty string. input: Request body. - input.user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + input.agent: Agent owner ID. + input.org: Organization scope for the owner. + input.system: Select system ownership within the app or organization. + input.team: Team owner ID. + input.ttl_seconds: Positive lifetime in seconds; omitted means no expiry. + input.user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. input.value: New value to store under `key`. Must be a non-empty string. Replaces any existing value. Returns: @@ -188,6 +275,10 @@ def __init__(self, http: SyncHttpClient): def list( self, *, + org: str | None = None, + team: str | None = None, + agent: str | None = None, + system: bool | None = None, page: int | None = None, page_size: int | None = None, user: str | None = None, @@ -205,9 +296,14 @@ def list( response across all users within the caller's app. Use `user` to scope results to a single user, `user_search` to do a substring match on email or full name, and `key` to filter entries whose key starts with the given prefix. Results are - ordered by creation time descending. + ordered by update time descending. Supplying org, team, agent, or system selects + a single owner's flat list instead. Expired entries are excluded in every mode. Args: + org: Organization scope for the owner. + team: Team owner ID. + agent: Agent owner ID. + system: Select system ownership within the app or organization. page: Page number to retrieve. Applies to developer and server-to-server callers only. Defaults to 1. page_size: Number of entries per page. Applies to developer and server-to-server callers only. Defaults to 25; maximum is 100. user: Filter results to entries belonging to this user ID. Applies to developer and server-to-server callers only. @@ -218,6 +314,14 @@ def list( Key-value storage entries for the current page, with pagination metadata for developer and server-to-server callers. """ query: dict[str, object] = {} + if org is not None: + query["org"] = org + if team is not None: + query["team"] = team + if agent is not None: + query["agent"] = agent + if system is not None: + query["system"] = system if page is not None: query["page"] = page if page_size is not None: @@ -237,18 +341,24 @@ def list( def create(self, input: KvCreateInput) -> KeyValueStorageEntry: """ Create a key-value storage entry - Creates a new key-value storage entry for the target user under the given key. - The key must not already exist for this user; use the upsert endpoint to create - or overwrite in a single call. - End-user (user-JWT) callers always write to their own storage. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. Attempting to write for a user in a different app - returns 404. + Creates a key once per owner. An existing key returns HTTP 409 conflict, including expired rows awaiting sweep. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.create`): the entry is owned by the triggering + automation's owner; scripts pass no owner params. A duplicate key + returns a script Err with code "conflict" instead of raising. Args: input: Request body. + input.agent: Agent owner ID. input.key: Storage key for the entry. Must be a non-empty string unique to this user. - input.user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + input.org: Organization scope for the owner. + input.system: Select system ownership within the app or organization. + input.team: Team owner ID. + input.ttl_seconds: Positive lifetime in seconds; omitted means no expiry. + input.user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. input.value: Value to store under `key`. Must be a non-empty string. Returns: @@ -261,41 +371,69 @@ def create(self, input: KvCreateInput) -> KeyValueStorageEntry: response_type=KeyValueStorageEntry, ) - def delete(self, key: str) -> None: + def delete(self, key: str) -> KvDeleteResponse: """ Delete a key-value storage entry - Permanently deletes the key-value storage entry identified by `key` for the - target user. Returns 204 No Content on success and 404 if the entry does not - exist. - End-user (user-JWT) callers can only delete entries they own. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. + Deletes an unexpired entry by owner and key. Returns 204, or 404 when absent. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.delete`): removes the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key of the entry to delete. Must be a non-empty string. Returns: - Empty response body. HTTP 204 No Content on success. + Successful response """ - self._http.request(f"/api/v1/kv/{key}", method="DELETE") + return self._http.request( + f"/api/v1/kv/{key}", + method="DELETE", + response_type=KvDeleteResponse, + ) - def get(self, key: str, *, user: str | None = None) -> KeyValueStorageEntry: + def get( + self, + key: str, + *, + org: str | None = None, + team: str | None = None, + agent: str | None = None, + system: bool | None = None, + user: str | None = None, + ) -> KeyValueStorageEntry: """ Retrieve a key-value storage entry - Returns the key-value storage entry identified by `key` for the target user. - Returns 404 if no entry exists for that key. - End-user (user-JWT) callers retrieve entries from their own storage. Developer - and server-to-server callers must supply a `user` param identifying the target - user within their app's scope. + Returns an unexpired entry by owner and key, or 404. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.get`): reads the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key of the entry to retrieve. Must be a non-empty string. - user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + org: Organization scope for the owner. + team: Team owner ID. + agent: Agent owner ID. + system: Select system ownership within the app or organization. + user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. Returns: The key-value storage entry for the given key. """ query: dict[str, object] = {} + if org is not None: + query["org"] = org + if team is not None: + query["team"] = team + if agent is not None: + query["agent"] = agent + if system is not None: + query["system"] = system if user is not None: query["user"] = user return self._http.request( @@ -307,18 +445,23 @@ def get(self, key: str, *, user: str | None = None) -> KeyValueStorageEntry: def upsert(self, key: str, input: KvUpsertInput) -> KeyValueStorageEntry: """ Create or update a key-value storage entry - Creates a new key-value storage entry for the given `key`, or overwrites the - value if an entry already exists. This is the idempotent alternative to the - create endpoint: safe to call regardless of whether the key already exists. - End-user (user-JWT) callers always write to their own storage. Developer and - server-to-server callers must supply a `user` param identifying the target user - within their app's scope. Attempting to write for a user in a different app - returns 404. + Creates or replaces a value and its expiry. Omitted ttl_seconds clears any previous expiry. + User-JWT callers use their own storage. Elevated callers select an owner with + system: true, team, user, or agent (in that priority order). An org alone selects + system ownership within that organization; all owners must be in the app scope. + Create and upsert accept a positive ttl_seconds; absent means no expiry. + Script binding (`kv.upsert`): overwrites the entry owned by the triggering + automation's owner; scripts pass no owner params. Args: key: Storage key to create or overwrite. Must be a non-empty string. input: Request body. - input.user: Target user ID. Required when calling as a developer or with a server-to-server key; ignored for end-user callers. + input.agent: Agent owner ID. + input.org: Organization scope for the owner. + input.system: Select system ownership within the app or organization. + input.team: Team owner ID. + input.ttl_seconds: Positive lifetime in seconds; omitted means no expiry. + input.user: User owner ID. Elevated callers may instead select team, agent, org, or system ownership. input.value: New value to store under `key`. Must be a non-empty string. Replaces any existing value. Returns: diff --git a/src/archastro/platform/v1/resources/notifications.py b/src/archastro/platform/v1/resources/notifications.py index ba5051f..98dd0d5 100644 --- a/src/archastro/platform/v1/resources/notifications.py +++ b/src/archastro/platform/v1/resources/notifications.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: e30b5fbd5ce5 +# Content hash: 253a531b0f1b from __future__ import annotations @@ -20,6 +20,10 @@ class NotificationSendInput(TypedDict, total=False): "Arbitrary key-value payload merged with platform-provided context (recipient, app, org, brand) when rendering the notification's display fields. Defaults to an empty object when omitted." idempotency_key: str | None "Optional deduplication key. A second call with the same `idempotency_key` for the same recipient returns the originally-created notification without inserting a new record. Scoped per calling app." + message: str | None + "Source message (`msg_...`) that the sender and the recipient must both be able to read." + thread: str | None + "Thread (`thr_...`) of the source message. Requires `message`." type: Required[str] 'Custom notification type identifier in the form `"custom:"`, where `` matches a `NotificationType` config declared in the calling app\'s bundle.' user: Required[str] @@ -155,6 +159,16 @@ async def send(self, input: NotificationSendInput) -> Notification: 403. The app scope is stamped onto the notification automatically; an app cannot target recipients outside its tenant. Built-in platform types such as `"app_info"` and `"billing_alert"` are not accepted here. + The `notifications.send` script binding also accepts the viewer a script runs + as: a user, an agent, or a team's system viewer. Such a sender may notify only + a user it can see in its own organization and sandbox. When the sender is a + user, the recipient must also be able to see the sender. An agent cannot see + user records, so an agent sender must name a source message: it may then + notify a user in its own organization and sandbox who can read that message. + Pass `thread` and `message` together to name the message the notification is + about. The send is then refused unless that message is in that thread and both + the sender and the recipient can read it, so a notification cannot quote a + message its recipient cannot open. Pass `idempotency_key` to deduplicate sends. If you call this endpoint twice with the same `idempotency_key` for the same recipient, the second call returns the original notification without creating a duplicate. The key is @@ -165,6 +179,8 @@ async def send(self, input: NotificationSendInput) -> Notification: input: Request body. input.data: Arbitrary key-value payload merged with platform-provided context (recipient, app, org, brand) when rendering the notification's display fields. Defaults to an empty object when omitted. input.idempotency_key: Optional deduplication key. A second call with the same `idempotency_key` for the same recipient returns the originally-created notification without inserting a new record. Scoped per calling app. + input.message: Source message (`msg_...`) that the sender and the recipient must both be able to read. + input.thread: Thread (`thr_...`) of the source message. Requires `message`. input.type: Custom notification type identifier in the form `"custom:"`, where `` matches a `NotificationType` config declared in the calling app's bundle. input.user: Recipient user ID (`usr_...`). Must be a member of the calling app's tenant. @@ -324,6 +340,16 @@ def send(self, input: NotificationSendInput) -> Notification: 403. The app scope is stamped onto the notification automatically; an app cannot target recipients outside its tenant. Built-in platform types such as `"app_info"` and `"billing_alert"` are not accepted here. + The `notifications.send` script binding also accepts the viewer a script runs + as: a user, an agent, or a team's system viewer. Such a sender may notify only + a user it can see in its own organization and sandbox. When the sender is a + user, the recipient must also be able to see the sender. An agent cannot see + user records, so an agent sender must name a source message: it may then + notify a user in its own organization and sandbox who can read that message. + Pass `thread` and `message` together to name the message the notification is + about. The send is then refused unless that message is in that thread and both + the sender and the recipient can read it, so a notification cannot quote a + message its recipient cannot open. Pass `idempotency_key` to deduplicate sends. If you call this endpoint twice with the same `idempotency_key` for the same recipient, the second call returns the original notification without creating a duplicate. The key is @@ -334,6 +360,8 @@ def send(self, input: NotificationSendInput) -> Notification: input: Request body. input.data: Arbitrary key-value payload merged with platform-provided context (recipient, app, org, brand) when rendering the notification's display fields. Defaults to an empty object when omitted. input.idempotency_key: Optional deduplication key. A second call with the same `idempotency_key` for the same recipient returns the originally-created notification without inserting a new record. Scoped per calling app. + input.message: Source message (`msg_...`) that the sender and the recipient must both be able to read. + input.thread: Thread (`thr_...`) of the source message. Requires `message`. input.type: Custom notification type identifier in the form `"custom:"`, where `` matches a `NotificationType` config declared in the calling app's bundle. input.user: Recipient user ID (`usr_...`). Must be a member of the calling app's tenant. diff --git a/src/archastro/platform/v1/resources/oauth.py b/src/archastro/platform/v1/resources/oauth.py index 3a46bc2..7639bf1 100644 --- a/src/archastro/platform/v1/resources/oauth.py +++ b/src/archastro/platform/v1/resources/oauth.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 1a97916433da +# Content hash: 42ce6ede96b6 from __future__ import annotations @@ -59,6 +59,8 @@ class OauthTokenInput(TypedDict, total=False): 'Redirect URI that was used in the original authorization request. Must exactly match the URI on record for the client. Required for `"authorization_code"` grants.' refresh_token: str | None 'Refresh token received from a previous token response. Required for `"refresh_token"` grants. The token is rotated on each successful call.' + resource: str | None + "Resource audience. Required for code exchange and refresh of a token bound to a resource server; must match the consent grant." class OauthScopesResponse(BaseModel): @@ -225,6 +227,7 @@ async def token(self, input: OauthTokenInput) -> OAuthTokenResponse: input.grant_type: The OAuth 2.0 grant type. One of `"authorization_code"`, `"refresh_token"`, or `"urn:ietf:params:oauth:grant-type:device_code"`. input.redirect_uri: Redirect URI that was used in the original authorization request. Must exactly match the URI on record for the client. Required for `"authorization_code"` grants. input.refresh_token: Refresh token received from a previous token response. Required for `"refresh_token"` grants. The token is rotated on each successful call. + input.resource: Resource audience. Required for code exchange and refresh of a token bound to a resource server; must match the consent grant. Returns: Token pair issued for the authenticated user. @@ -390,6 +393,7 @@ def token(self, input: OauthTokenInput) -> OAuthTokenResponse: input.grant_type: The OAuth 2.0 grant type. One of `"authorization_code"`, `"refresh_token"`, or `"urn:ietf:params:oauth:grant-type:device_code"`. input.redirect_uri: Redirect URI that was used in the original authorization request. Must exactly match the URI on record for the client. Required for `"authorization_code"` grants. input.refresh_token: Refresh token received from a previous token response. Required for `"refresh_token"` grants. The token is rotated on each successful call. + input.resource: Resource audience. Required for code exchange and refresh of a token bound to a resource server; must match the consent grant. Returns: Token pair issued for the authenticated user. diff --git a/src/archastro/platform/v1/resources/orgs.py b/src/archastro/platform/v1/resources/orgs.py index 65e2ceb..b446584 100644 --- a/src/archastro/platform/v1/resources/orgs.py +++ b/src/archastro/platform/v1/resources/orgs.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 76f06a2bde7f +# Content hash: e193124aa024 from __future__ import annotations @@ -11,9 +11,89 @@ from pydantic import BaseModel, Field from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.tasks import Task, TaskEnsureResult from ...types.threads import Thread +class OrgTaskCreateInputTask(TypedDict, total=False): + description: str | None + "Optional long-form description or notes for the task. Supports plain text." + due_date: datetime | None + "Date and time by which the task should be completed (ISO 8601). Omit to create the task without a due date." + epic: str | None + "Optional free-form grouping label." + id: str | None + "Optional caller-generated task public ID (`tsk_...`). Persist it before creating a task when creation must survive a lost response. An existing ID returns 409 without modifying the task; read the task by ID to reconcile. Omit for a server-generated ID." + links: dict[str, Any] | None + "Arbitrary key-value map of named URLs or references associated with the task (e.g. external ticket links)." + metadata: dict[str, Any] | None + "Arbitrary key-value map for storing application-specific data alongside the task. Omit to create the task with no metadata." + name: Required[str] + "Human-readable title for the task." + owner_agent: str | None + "ID of the agent to assign as owner (`agi_...`). Mutually exclusive with `owner_user`; omit to leave the task unassigned." + owner_user: str | None + "ID of the user to assign as owner (`usr_...`). Mutually exclusive with `owner_agent`; omit to leave the task unassigned." + parent: str | None + "Create this task as a subtask of an existing top-level task (`tsk_...`). Subtasks nest exactly one level." + priority: int | None + "Priority level from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when omitted." + project_id: str | None + "Optional project ID (`prj_...`). Pass null to clear it." + source_id: str | None + "Source object identity (for example `ArchAstro/firstlanding`)." + source_scope: str | None + "Container of the work this task is about (for example `github.com`). Must be supplied with `source_type` and `source_id`." + source_type: str | None + "Kind of source object (for example `repository`)." + status: str | None + 'Initial status for the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`. Defaults to `"open"` when omitted.' + tags: list[str] | None + "Labels for grouping and filtering (max 20, each up to 40 characters). Stored canonically: lowercase, trimmed, de-duplicated." + target_binary: str | None + "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`." + target_version: str | None + "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`." + thread: str | None + "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation." + + +class OrgTaskCreateInput(TypedDict, total=False): + "Create a task for an organization" + + agent: str | None + "Explicit acting agent (`agi_...`) for a developer or server-to-server call. Mutually exclusive with an acting `user`; the agent must belong to the organization." + task: Required[OrgTaskCreateInputTask] + "Attributes for the task to create. `name` is required; all other fields are optional." + user: str | None + "Explicit acting user (`usr_...`) for a developer or server-to-server call. Mutually exclusive with an acting `agent`." + + +class OrgTaskBySourceInput(TypedDict, total=False): + "Find or create a task by source" + + agent: str | None + "Explicit acting agent for privileged callers." + description: str | None + "Initial task description." + epic: str | None + "Initial epic grouping." + name: Required[str] + "Initial task name." + source_id: Required[str] + "Source identity, for example owner/repo#123." + source_scope: Required[str] + "Source scope, for example github." + source_type: Required[str] + "Source type, for example pull_request." + status: str | None + "Initial task status." + team: str | None + "Team owner." + user: str | None + "User owner, or explicit acting user on a team route." + + class OrgThreadCreateInputThreadMembersItem(TypedDict): id: str "Public user (`usr_...`) or agent (`agt_...`) ID matching `type`." @@ -66,13 +146,1146 @@ class OrgThreadCreateInputThread(TypedDict, total=False): "Thread visibility. A team-owned thread with members must explicitly use `restricted` or `private`. User- and agent-owned threads with members default to `private` and reject every other value. `org` is only valid on an organization-owned thread with no team, user, or agent owner." -class OrgThreadCreateInput(TypedDict, total=False): - "Create an organization-visibility thread" +class OrgThreadCreateInput(TypedDict, total=False): + "Create an organization-visibility thread" + + skip_welcome_message: bool | None + "When `true`, suppresses the automatic welcome message that is otherwise sent into the thread on creation. Defaults to `false`." + thread: Required[OrgThreadCreateInputThread] + "Attributes for the new thread. Visibility is forced to `org`." + + +class OrgTaskListResponseDataItemCreatedByActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskListResponseDataItemCreatedByActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskListResponseDataItemCreatedByActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskListResponseDataItemCurrentLease(BaseModel): + expires_at: datetime = Field( + ..., description="Server-calculated lease expiry in ISO 8601 format." + ) + harness: str = Field(..., description="Bounded harness identifier for the coding session.") + session_name: str = Field( + ..., description="Display name supplied by the coding session that holds the lease." + ) + + +class OrgTaskListResponseDataItemOwnerActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskListResponseDataItemOwnerActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskListResponseDataItemOwnerActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskListResponseDataItem(BaseModel): + agent: str | None = Field( + default=None, + description="ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + ) + aggregate_version: int | None = Field( + default=None, + description="Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + ) + blocked_by_count: int | None = Field( + default=None, + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + ) + closed_at: datetime | None = Field( + default=None, + description="When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + ) + comments_count: int | None = Field( + default=None, description="Total number of comments posted on this task." + ) + created_at: datetime | None = Field( + default=None, description="When the task was created (ISO 8601)." + ) + created_by_actor: OrgTaskListResponseDataItemCreatedByActor | None = Field( + default=None, + description="Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + ) + created_by_agent: str | None = Field( + default=None, + description="ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + ) + created_by_user: str | None = Field( + default=None, + description="ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + ) + current_lease: OrgTaskListResponseDataItemCurrentLease | None = Field( + default=None, + description="Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + ) + description: str | None = Field( + default=None, + description="Long-form description or notes for the task. `null` if no description has been provided.", + ) + due_date: datetime | None = Field( + default=None, + description="Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + ) + epic: str | None = Field( + default=None, + description="Free-form grouping label. `null` when the task is not in an epic.", + ) + id: str = Field(..., description="Task ID (`tsk_...`).") + is_blocked: bool | None = Field( + default=None, + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + ) + links: dict[str, Any] | None = Field( + default=None, + description="Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + ) + metadata: dict[str, Any] | None = Field( + default=None, + description="Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + ) + name: str = Field(..., description="Human-readable title of the task.") + org: str | None = Field( + default=None, + description="ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + ) + owner_actor: OrgTaskListResponseDataItemOwnerActor | None = Field( + default=None, + description="Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + ) + owner_agent: str | None = Field( + default=None, + description="ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + ) + owner_user: str | None = Field( + default=None, + description="ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + ) + parent: str | None = Field( + default=None, + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + ) + priority: int | None = Field( + default=None, + description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) + sandbox: str | None = Field( + default=None, + description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + ) + source_id: str | None = Field( + default=None, + description="Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + ) + source_scope: str | None = Field( + default=None, + description="Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + ) + source_type: str | None = Field( + default=None, + description="Kind of source object (for example `repository`). `null` when the task has no source.", + ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) + status: str = Field( + ..., + description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', + ) + subtasks_count: int | None = Field( + default=None, + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + ) + tags: list[str] | None = Field( + default=None, + description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + ) + thread: str | None = Field( + default=None, + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + ) + updated_at: datetime | None = Field( + default=None, description="When the task was last modified (ISO 8601)." + ) + user: str | None = Field( + default=None, + description="ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + ) + + +class OrgTaskListResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[OrgTaskListResponseDataItem] = Field( + ..., description="Array of task objects matching the requested filters." + ) + has_more: bool + + +class OrgTaskBlockerCyclesResponseDataItemTasksItemCreatedByActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskBlockerCyclesResponseDataItemTasksItemCreatedByActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: ( + OrgTaskBlockerCyclesResponseDataItemTasksItemCreatedByActorProfilePicture | None + ) = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskBlockerCyclesResponseDataItemTasksItemCurrentLease(BaseModel): + expires_at: datetime = Field( + ..., description="Server-calculated lease expiry in ISO 8601 format." + ) + harness: str = Field(..., description="Bounded harness identifier for the coding session.") + session_name: str = Field( + ..., description="Display name supplied by the coding session that holds the lease." + ) + + +class OrgTaskBlockerCyclesResponseDataItemTasksItemOwnerActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskBlockerCyclesResponseDataItemTasksItemOwnerActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: ( + OrgTaskBlockerCyclesResponseDataItemTasksItemOwnerActorProfilePicture | None + ) = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): + agent: str | None = Field( + default=None, + description="ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + ) + aggregate_version: int | None = Field( + default=None, + description="Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + ) + blocked_by_count: int | None = Field( + default=None, + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + ) + closed_at: datetime | None = Field( + default=None, + description="When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + ) + comments_count: int | None = Field( + default=None, description="Total number of comments posted on this task." + ) + created_at: datetime | None = Field( + default=None, description="When the task was created (ISO 8601)." + ) + created_by_actor: OrgTaskBlockerCyclesResponseDataItemTasksItemCreatedByActor | None = Field( + default=None, + description="Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + ) + created_by_agent: str | None = Field( + default=None, + description="ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + ) + created_by_user: str | None = Field( + default=None, + description="ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + ) + current_lease: OrgTaskBlockerCyclesResponseDataItemTasksItemCurrentLease | None = Field( + default=None, + description="Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + ) + description: str | None = Field( + default=None, + description="Long-form description or notes for the task. `null` if no description has been provided.", + ) + due_date: datetime | None = Field( + default=None, + description="Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + ) + epic: str | None = Field( + default=None, + description="Free-form grouping label. `null` when the task is not in an epic.", + ) + id: str = Field(..., description="Task ID (`tsk_...`).") + is_blocked: bool | None = Field( + default=None, + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + ) + links: dict[str, Any] | None = Field( + default=None, + description="Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + ) + metadata: dict[str, Any] | None = Field( + default=None, + description="Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + ) + name: str = Field(..., description="Human-readable title of the task.") + org: str | None = Field( + default=None, + description="ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + ) + owner_actor: OrgTaskBlockerCyclesResponseDataItemTasksItemOwnerActor | None = Field( + default=None, + description="Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + ) + owner_agent: str | None = Field( + default=None, + description="ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + ) + owner_user: str | None = Field( + default=None, + description="ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + ) + parent: str | None = Field( + default=None, + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + ) + priority: int | None = Field( + default=None, + description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) + sandbox: str | None = Field( + default=None, + description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + ) + source_id: str | None = Field( + default=None, + description="Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + ) + source_scope: str | None = Field( + default=None, + description="Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + ) + source_type: str | None = Field( + default=None, + description="Kind of source object (for example `repository`). `null` when the task has no source.", + ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) + status: str = Field( + ..., + description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', + ) + subtasks_count: int | None = Field( + default=None, + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + ) + tags: list[str] | None = Field( + default=None, + description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + ) + thread: str | None = Field( + default=None, + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + ) + updated_at: datetime | None = Field( + default=None, description="When the task was last modified (ISO 8601)." + ) + user: str | None = Field( + default=None, + description="ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + ) + + +class OrgTaskBlockerCyclesResponseDataItem(BaseModel): + tasks: list[OrgTaskBlockerCyclesResponseDataItemTasksItem] = Field( + ..., description="Every unfinished task in this cyclic blocker component." + ) + + +class OrgTaskBlockerCyclesResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[OrgTaskBlockerCyclesResponseDataItem] + has_more: bool + + +class OrgTaskMetricsResponse(BaseModel): + """ + Successful response + """ + + completed: int + created: int + days: int + end_at: datetime + open: int + series: list[dict[str, Any]] = Field( + ..., + description="Zero-filled UTC-day buckets with `date`, end-of-day `open`, `created`, and `completed`.", + ) + start_at: datetime + + +class OrgTaskReadyResponseDataItemTaskCreatedByActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskReadyResponseDataItemTaskCreatedByActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskReadyResponseDataItemTaskCreatedByActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskReadyResponseDataItemTaskCurrentLease(BaseModel): + expires_at: datetime = Field( + ..., description="Server-calculated lease expiry in ISO 8601 format." + ) + harness: str = Field(..., description="Bounded harness identifier for the coding session.") + session_name: str = Field( + ..., description="Display name supplied by the coding session that holds the lease." + ) + + +class OrgTaskReadyResponseDataItemTaskOwnerActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskReadyResponseDataItemTaskOwnerActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskReadyResponseDataItemTaskOwnerActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskReadyResponseDataItemTask(BaseModel): + agent: str | None = Field( + default=None, + description="ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + ) + aggregate_version: int | None = Field( + default=None, + description="Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + ) + blocked_by_count: int | None = Field( + default=None, + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + ) + closed_at: datetime | None = Field( + default=None, + description="When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + ) + comments_count: int | None = Field( + default=None, description="Total number of comments posted on this task." + ) + created_at: datetime | None = Field( + default=None, description="When the task was created (ISO 8601)." + ) + created_by_actor: OrgTaskReadyResponseDataItemTaskCreatedByActor | None = Field( + default=None, + description="Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + ) + created_by_agent: str | None = Field( + default=None, + description="ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + ) + created_by_user: str | None = Field( + default=None, + description="ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + ) + current_lease: OrgTaskReadyResponseDataItemTaskCurrentLease | None = Field( + default=None, + description="Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + ) + description: str | None = Field( + default=None, + description="Long-form description or notes for the task. `null` if no description has been provided.", + ) + due_date: datetime | None = Field( + default=None, + description="Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + ) + epic: str | None = Field( + default=None, + description="Free-form grouping label. `null` when the task is not in an epic.", + ) + id: str = Field(..., description="Task ID (`tsk_...`).") + is_blocked: bool | None = Field( + default=None, + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + ) + links: dict[str, Any] | None = Field( + default=None, + description="Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + ) + metadata: dict[str, Any] | None = Field( + default=None, + description="Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + ) + name: str = Field(..., description="Human-readable title of the task.") + org: str | None = Field( + default=None, + description="ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + ) + owner_actor: OrgTaskReadyResponseDataItemTaskOwnerActor | None = Field( + default=None, + description="Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + ) + owner_agent: str | None = Field( + default=None, + description="ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + ) + owner_user: str | None = Field( + default=None, + description="ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + ) + parent: str | None = Field( + default=None, + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + ) + priority: int | None = Field( + default=None, + description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) + sandbox: str | None = Field( + default=None, + description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + ) + source_id: str | None = Field( + default=None, + description="Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + ) + source_scope: str | None = Field( + default=None, + description="Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + ) + source_type: str | None = Field( + default=None, + description="Kind of source object (for example `repository`). `null` when the task has no source.", + ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) + status: str = Field( + ..., + description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', + ) + subtasks_count: int | None = Field( + default=None, + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + ) + tags: list[str] | None = Field( + default=None, + description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + ) + thread: str | None = Field( + default=None, + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + ) + updated_at: datetime | None = Field( + default=None, description="When the task was last modified (ISO 8601)." + ) + user: str | None = Field( + default=None, + description="ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + ) + + +class OrgTaskReadyResponseDataItem(BaseModel): + readiness: Literal["ready", "blocked", "leased"] = Field( + ..., description="One of `ready`, `blocked`, or `leased`." + ) + reason: Literal["open_blockers", "active_lease"] | None = Field( + default=None, + description="Stable exclusion reason: `open_blockers` or `active_lease`; omitted when ready.", + ) + task: OrgTaskReadyResponseDataItemTask = Field( + ..., description="The task evaluated for readiness." + ) + + +class OrgTaskReadyResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + authoritative: bool = Field( + ..., + description="Always false because projections can lag writes and a later claim can race this read.", + ) + before_cursor: str | None = None + data: list[OrgTaskReadyResponseDataItem] + has_more: bool + + +class OrgTaskSearchResponseDataItemCreatedByActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskSearchResponseDataItemCreatedByActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskSearchResponseDataItemCreatedByActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskSearchResponseDataItemCurrentLease(BaseModel): + expires_at: datetime = Field( + ..., description="Server-calculated lease expiry in ISO 8601 format." + ) + harness: str = Field(..., description="Bounded harness identifier for the coding session.") + session_name: str = Field( + ..., description="Display name supplied by the coding session that holds the lease." + ) + + +class OrgTaskSearchResponseDataItemOwnerActorProfilePicture(BaseModel): + file: str | None = Field( + default=None, + description="ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file.", + ) + height: int | None = Field( + default=None, description="Height of the image in pixels. `null` if not known." + ) + media: str | None = Field( + default=None, + description="ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity.", + ) + mime_type: str | None = Field( + default=None, + description='MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.', + ) + refresh_url: str | None = Field( + default=None, + description="Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing.", + ) + url: str | None = Field( + default=None, + description="Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires.", + ) + width: int | None = Field( + default=None, description="Width of the image in pixels. `null` if not known." + ) + + +class OrgTaskSearchResponseDataItemOwnerActor(BaseModel): + alias: str | None = Field( + default=None, + description="Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured.", + ) + id: str | None = Field( + default=None, + description='Composite actor identifier. Format is `"user-"` for human users or `"agent-"` for agents.', + ) + name: str | None = Field( + default=None, + description="Display name of the actor shown in the UI. `null` if no name is set.", + ) + profile_picture: OrgTaskSearchResponseDataItemOwnerActorProfilePicture | None = Field( + default=None, + description="Profile picture for the actor. `null` if the actor has no profile picture.", + ) + + +class OrgTaskSearchResponseDataItem(BaseModel): + agent: str | None = Field( + default=None, + description="ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user.", + ) + aggregate_version: int | None = Field( + default=None, + description="Event-stream version for optimistic update fencing. Pass this value as `expected_version` on an update to reject concurrent writes.", + ) + blocked_by_count: int | None = Field( + default=None, + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + ) + closed_at: datetime | None = Field( + default=None, + description="When the task was marked as done or otherwise closed (ISO 8601). `null` if the task is still open.", + ) + comments_count: int | None = Field( + default=None, description="Total number of comments posted on this task." + ) + created_at: datetime | None = Field( + default=None, description="When the task was created (ISO 8601)." + ) + created_by_actor: OrgTaskSearchResponseDataItemCreatedByActor | None = Field( + default=None, + description="Resolved creator details including `id`, `name`, `alias`, and `profile_picture`. `null` if no creator is set or the creator cannot be resolved (e.g. creating agent was deleted).", + ) + created_by_agent: str | None = Field( + default=None, + description="ID of the agent that created this task (`agi_...`). `null` if the task was created by a human user, or if the creating agent was later deleted.", + ) + created_by_user: str | None = Field( + default=None, + description="ID of the user who created this task (`usr_...`). `null` if the task was created by an agent, or if creator provenance was cleared after the creator was deleted.", + ) + current_lease: OrgTaskSearchResponseDataItemCurrentLease | None = Field( + default=None, + description="Viewer-safe live coding-session lease summary. `null` when the task is unleased or the projected lease has expired. Fencing identifiers are never included.", + ) + description: str | None = Field( + default=None, + description="Long-form description or notes for the task. `null` if no description has been provided.", + ) + due_date: datetime | None = Field( + default=None, + description="Date and time by which the task should be completed (ISO 8601). `null` if no due date is set.", + ) + epic: str | None = Field( + default=None, + description="Free-form grouping label. `null` when the task is not in an epic.", + ) + id: str = Field(..., description="Task ID (`tsk_...`).") + is_blocked: bool | None = Field( + default=None, + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", + ) + links: dict[str, Any] | None = Field( + default=None, + description="Key-value map of named URLs or references associated with the task. Returns an empty object when no links have been set.", + ) + metadata: dict[str, Any] | None = Field( + default=None, + description="Arbitrary key-value map of application-specific data stored alongside the task. Returns an empty object when no metadata has been set.", + ) + name: str = Field(..., description="Human-readable title of the task.") + org: str | None = Field( + default=None, + description="ID of the organization this task belongs to (`org_...`). `null` for tasks outside an org context.", + ) + owner_actor: OrgTaskSearchResponseDataItemOwnerActor | None = Field( + default=None, + description="Resolved owner details including `id`, `name`, `alias`, and `profile_picture`. `null` if the task is unassigned or the owner cannot be resolved (e.g. assigned agent was deleted).", + ) + owner_agent: str | None = Field( + default=None, + description="ID of the agent assigned as owner (`agi_...`). `null` if the owner is a human user, the task is unassigned, or the assigned agent was deleted.", + ) + owner_user: str | None = Field( + default=None, + description="ID of the user assigned as owner (`usr_...`). `null` if the owner is an agent, the task is unassigned, or the assigned agent was deleted.", + ) + parent: str | None = Field( + default=None, + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", + ) + priority: int | None = Field( + default=None, + description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", + ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) + sandbox: str | None = Field( + default=None, + description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", + ) + source_id: str | None = Field( + default=None, + description="Source object identity (for example `ArchAstro/firstlanding`). `null` when the task has no source.", + ) + source_scope: str | None = Field( + default=None, + description="Container of the work this task is about (for example `github.com`). `null` when the task has no source. Set together with `source_type` and `source_id`.", + ) + source_type: str | None = Field( + default=None, + description="Kind of source object (for example `repository`). `null` when the task has no source.", + ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) + status: str = Field( + ..., + description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', + ) + subtasks_count: int | None = Field( + default=None, + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + ) + tags: list[str] | None = Field( + default=None, + description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", + ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", + ) + thread: str | None = Field( + default=None, + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + ) + updated_at: datetime | None = Field( + default=None, description="When the task was last modified (ISO 8601)." + ) + user: str | None = Field( + default=None, + description="ID of the user that owns this task (`usr_...`). `null` if the task is scoped to a team.", + ) + + +class OrgTaskSearchResponse(BaseModel): + """ + Successful response + """ - skip_welcome_message: bool | None - "When `true`, suppresses the automatic welcome message that is otherwise sent into the thread on creation. Defaults to `false`." - thread: Required[OrgThreadCreateInputThread] - "Attributes for the new thread. Visibility is forced to `org`." + after_cursor: str | None = None + before_cursor: str | None = None + data: list[OrgTaskSearchResponseDataItem] = Field( + ..., description="Array of task objects matching the query and filters." + ) + has_more: bool + query: str class OrgThreadListResponseDataItemParentMessageAclAddItem(BaseModel): @@ -240,6 +1453,7 @@ class OrgThreadListResponseDataItemParentMessageAttachmentsItemVariantsItemImage class OrgThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(BaseModel): + byte_size: int | None = Field(default=None, description="Size of the stored bytes.") content_type: str | None = Field( default=None, description='MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.', @@ -247,6 +1461,10 @@ class OrgThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Base created_at: datetime | None = Field( default=None, description="When this variant was created (ISO 8601)." ) + duration_ms: int | None = Field( + default=None, + description="Client-declared video duration in milliseconds; not server-probed.", + ) file: str | None = Field( default=None, description="ID of the underlying storage file that backs this variant (`fil_...`).", @@ -265,6 +1483,7 @@ class OrgThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Base default=None, description="Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types.", ) + sha256: str | None = Field(default=None, description="Lowercase SHA-256 of the stored bytes.") updated_at: datetime | None = Field( default=None, description="When this variant was last updated (ISO 8601)." ) @@ -272,6 +1491,9 @@ class OrgThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Base default=None, description="Signed download URL for this variant, resolved at request time. `null` if the file is unavailable.", ) + url_expires_at: datetime | None = Field( + default=None, description="Expiry of this variant's playable URL." + ) variant_key: str | None = Field( default=None, description='Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).', @@ -1491,6 +2713,62 @@ class OrgListResponse(BaseModel): ) +class OrgArtifactsResponseDataItemAclAddItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class OrgArtifactsResponseDataItemAclGrantsItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class OrgArtifactsResponseDataItemAclRemoveItem(BaseModel): + principal: str | None = Field( + default=None, + description='The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class OrgArtifactsResponseDataItemAcl(BaseModel): + add: list[OrgArtifactsResponseDataItemAclAddItem] | None = Field( + default=None, + description="Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + ) + grants: list[OrgArtifactsResponseDataItemAclGrantsItem] | None = Field( + default=None, + description="Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + ) + remove: list[OrgArtifactsResponseDataItemAclRemoveItem] | None = Field( + default=None, + description="Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + ) + + class OrgArtifactsResponseDataItemImageSource(BaseModel): file: str | None = Field( default=None, @@ -1521,6 +2799,10 @@ class OrgArtifactsResponseDataItemImageSource(BaseModel): class OrgArtifactsResponseDataItem(BaseModel): + acl: OrgArtifactsResponseDataItemAcl | None = Field( + default=None, + description="Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + ) agent: str | None = Field( default=None, description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", @@ -1597,42 +2879,455 @@ class OrgArtifactsResponseDataItem(BaseModel): ) -class OrgArtifactsResponse(BaseModel): - """ - Successful response - """ +class OrgArtifactsResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = Field( + default=None, description="Always null; pagination is forward-only." + ) + data: list[OrgArtifactsResponseDataItem] + has_more: bool + + +class OrgLinkedAccountsResponseDataItem(BaseModel): + account_id: str = Field( + ..., description="The service's name for the account: the login, for GitHub." + ) + provider: str = Field( + ..., description='Service the account is on. Currently always `"github"`.' + ) + user: str = Field(..., description="User ID (`usr_...`) the account belongs to.") + + +class OrgLinkedAccountsResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = Field( + default=None, description="Always null; pagination is forward-only." + ) + data: list[OrgLinkedAccountsResponseDataItem] = Field( + ..., description="Linked accounts, by user." + ) + has_more: bool + + +class OrgTaskEpicsResponseDataItemNextReadyItem(BaseModel): + id: str + name: str + priority: int + status: str + + +class OrgTaskEpicsResponseDataItemRootBlockersItem(BaseModel): + downstream_count: int + id: str + name: str + priority: int + status: str + + +class OrgTaskEpicsResponseDataItem(BaseModel): + blocked_count: int + counts: dict[str, Any] = Field( + ..., description="Task counts keyed by status, including closed tasks." + ) + epic: str + next_ready: list[OrgTaskEpicsResponseDataItemNextReadyItem] + root_blockers: list[OrgTaskEpicsResponseDataItemRootBlockersItem] + root_blockers_partial: bool = Field( + ..., description="True when root ranking used a truncated in-epic graph." + ) + top_open_priority: int | None = None + updated_at: datetime + + +class OrgTaskEpicsResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[OrgTaskEpicsResponseDataItem] + has_more: bool + no_epic_open_count: int + + +class AsyncOrgTaskResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list( + self, + org: str, + *, + status: str | None = None, + owner_user: str | None = None, + owner_agent: str | None = None, + priority: int | None = None, + tag: str | None = None, + parent: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + search: str | None = None, + sort: str | None = None, + order: str | None = None, + due_before: str | None = None, + due_after: str | None = None, + overdue: bool | None = None, + ready: bool | None = None, + attention: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskListResponse: + """ + List an organization's tasks + Returns org-owned tasks for the specified organization: rows with the org + tenant and no narrower user/team/agent scope. Explicitly personal, team, or + agent tasks are excluded here; list those via the user/team collections or + fetch them directly when authorized. + Every organization member may list. App-scoped developer and + server-to-server callers supply the org in the route. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + status: Filter tasks by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to return tasks in all statuses. + owner_user: Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`). + owner_agent: Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`). + priority: Filter tasks by priority, from 0 (highest) to 4 (lowest). + tag: Return only tasks carrying this tag (matched against the canonical lowercase form). + parent: Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks. + source_scope: Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Return only tasks whose source matches this object kind. Must be supplied with the other source filters. + source_id: Return only tasks whose source matches this object identity. Must be supplied with the other source filters. + epic: Return only tasks with this exact epic label. + search: Restrict results to tasks whose name or description contains this string. + sort: Sort key. One of `"created"` (default most recently created first), `"due_date"` (soonest due first; tasks without a due date always sort last), or `"priority"` (most urgent first). Ties break by most recently created. + order: Sort direction, `"asc"` or `"desc"`. Defaults to `"desc"` for `created` and `"asc"` for `due_date` and `priority`. + due_before: Return only tasks with a due date strictly before this ISO 8601 datetime (`2026-08-01T00:00:00Z`) or date (`2026-08-01`, meaning midnight UTC). Tasks without a due date are excluded. + due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. + overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. + ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. + limit: Maximum number of tasks to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if status is not None: + query["status"] = status + if owner_user is not None: + query["owner_user"] = owner_user + if owner_agent is not None: + query["owner_agent"] = owner_agent + if priority is not None: + query["priority"] = priority + if tag is not None: + query["tag"] = tag + if parent is not None: + query["parent"] = parent + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if search is not None: + query["search"] = search + if sort is not None: + query["sort"] = sort + if order is not None: + query["order"] = order + if due_before is not None: + query["due_before"] = due_before + if due_after is not None: + query["due_after"] = due_after + if overdue is not None: + query["overdue"] = overdue + if ready is not None: + query["ready"] = ready + if attention is not None: + query["attention"] = attention + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/orgs/{org}/tasks", + query=query, + response_type=OrgTaskListResponse, + ) + + async def create(self, org: str, input: OrgTaskCreateInput) -> Task: + """ + Create a task for an organization + Creates a new org-owned task (org tenant, no narrower user/team/agent + scope) and returns the full task object. User-authenticated calls are + attributed to the authenticated user or agent. App-scoped developer and + server-to-server callers must provide an explicit `user` or `agent` actor + for the organization. Every referenced principal is validated against the + app and organization before creation. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + input: Request body. + input.agent: Explicit acting agent (`agi_...`) for a developer or server-to-server call. Mutually exclusive with an acting `user`; the agent must belong to the organization. + input.task: Attributes for the task to create. `name` is required; all other fields are optional. + input.user: Explicit acting user (`usr_...`) for a developer or server-to-server call. Mutually exclusive with an acting `agent`. + + Returns: + The newly created task. + """ + return await self._http.request( + f"/api/v1/orgs/{org}/tasks", + method="POST", + body=input, + response_type=Task, + ) + + async def blocker_cycles( + self, org: str, *, limit: int | None = None, after_cursor: str | None = None + ) -> OrgTaskBlockerCyclesResponse: + """ + List task blocker cycles for an organization + Runs an on-demand diagnostic over unfinished org-owned tasks for the + specified organization and returns a forward cursor-paginated page of + complete cyclic blocker components. Detection is bounded to owners with at + most 100 unfinished tasks. This endpoint is read-only: cycles do not prevent + task updates, lease acquisition, or completion. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + limit: Maximum cycle components to return. Defaults to 50; maximum is 100. + after_cursor: Opaque cursor returned by the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/orgs/{org}/tasks/blocker_cycles", + query=query, + response_type=OrgTaskBlockerCyclesResponse, + ) + + async def by_source(self, org: str, input: OrgTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.team: Team owner. + input.user: User owner, or explicit acting user on a team route. - after_cursor: str | None = None - before_cursor: str | None = Field( - default=None, description="Always null; pagination is forward-only." - ) - data: list[OrgArtifactsResponseDataItem] - has_more: bool + Returns: + The task and whether this request created it. + """ + return await self._http.request( + f"/api/v1/orgs/{org}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + async def metrics(self, org: str, *, days: int | None = None) -> OrgTaskMetricsResponse: + """ + Get task activity metrics for an organization + Returns task activity metrics scoped to org-owned tasks (org tenant, no + narrower user/team/agent scope). `open` counts currently open or + in-progress org tasks, `created` counts tasks inserted in the UTC-day + window, and `completed` counts tasks whose `closed_at` falls in that window + while still in a closed status. Hard-deleted tasks are absent from the + projection and therefore omitted. The daily series is zero-filled across + the requested window and includes reconstructed end-of-day `open` stock. + Any organization member may read this count-only summary. Requests from + callers without org access return 404 so organization existence is not + disclosed. -class OrgLinkedAccountsResponseDataItem(BaseModel): - account_id: str = Field( - ..., description="The service's name for the account: the login, for GitHub." - ) - provider: str = Field( - ..., description='Service the account is on. Currently always `"github"`.' - ) - user: str = Field(..., description="User ID (`usr_...`) the account belongs to.") + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + days: UTC-day window. One of 7, 30, 90, or 365; defaults to 30. + Returns: + Successful response + """ + query: dict[str, object] = {} + if days is not None: + query["days"] = days + return await self._http.request( + f"/api/v1/orgs/{org}/tasks/metrics", + query=query, + response_type=OrgTaskMetricsResponse, + ) -class OrgLinkedAccountsResponse(BaseModel): - """ - Successful response - """ + async def ready( + self, + org: str, + *, + explain: bool | None = None, + assigned_to_me: bool | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskReadyResponse: + """ + List an organization's ready tasks + Returns open org-owned tasks with no unfinished blockers and no active + session lease. Readiness is calculated by the server from the current task + projection. It is a snapshot, not a reservation; claim a task lease before + starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. + Pass `explain=true` to include every open task with a stable readiness reason. - after_cursor: str | None = None - before_cursor: str | None = Field( - default=None, description="Always null; pagination is forward-only." - ) - data: list[OrgLinkedAccountsResponseDataItem] = Field( - ..., description="Linked accounts, by user." - ) - has_more: bool + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + explain: Include blocked and actively leased open tasks with exclusion reasons. + assigned_to_me: Only include tasks assigned to the authenticated user. + source_scope: Only include tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Only include tasks whose source matches this object kind. + source_id: Only include tasks whose source matches this object identity. + epic: Only include tasks with this exact epic label. + limit: Maximum number of readiness entries to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if explain is not None: + query["explain"] = explain + if assigned_to_me is not None: + query["assigned_to_me"] = assigned_to_me + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/orgs/{org}/tasks/ready", + query=query, + response_type=OrgTaskReadyResponse, + ) + + async def search( + self, + org: str, + *, + q: str | None = None, + query: str | None = None, + status: str | None = None, + owner_user: str | None = None, + owner_agent: str | None = None, + priority: int | None = None, + tag: str | None = None, + parent: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskSearchResponse: + """ + Search an organization's tasks + Performs a full-text search over org-owned tasks for the specified + organization and returns matching results. Combine `q` with the optional + filters to narrow the result set further. When no query is provided, the + endpoint behaves like a filtered list. + The `query` field in the response echoes the effective search query. + Explicitly personal, team, or agent tasks are excluded here. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + q: Full-text search query matched against task names and descriptions. Takes precedence over `query` when both are provided. + query: Alias for `q`. Use `q` when possible; this parameter exists for compatibility. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. + owner_user: Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`). + owner_agent: Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`). + priority: Filter tasks by priority, from 0 (highest) to 4 (lowest). + tag: Return only tasks carrying this tag (matched against the canonical lowercase form). + parent: Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks. + source_scope: Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Return only tasks whose source matches this object kind. Must be supplied with the other source filters. + source_id: Return only tasks whose source matches this object identity. Must be supplied with the other source filters. + epic: Return only tasks with this exact epic label. + limit: Maximum number of tasks to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if q is not None: + query["q"] = q + if query is not None: + query["query"] = query + if status is not None: + query["status"] = status + if owner_user is not None: + query["owner_user"] = owner_user + if owner_agent is not None: + query["owner_agent"] = owner_agent + if priority is not None: + query["priority"] = priority + if tag is not None: + query["tag"] = tag + if parent is not None: + query["parent"] = parent + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/orgs/{org}/tasks/search", + query=query, + response_type=OrgTaskSearchResponse, + ) class AsyncOrgThreadResource: @@ -1648,7 +3343,7 @@ async def list(self, org: str, *, key: str | None = None) -> OrgThreadListRespon Args: org: Organization ID (`org_...`) whose org-visibility threads should be listed. - key: Optional exact thread key, for example `archdev-room`. + key: Optional exact thread key, for example `support-room`. Returns: Successful response @@ -1693,6 +3388,7 @@ async def create(self, org: str, input: OrgThreadCreateInput) -> Thread: class AsyncOrgResource: def __init__(self, http: HttpClient): self._http = http + self.tasks = AsyncOrgTaskResource(http) self.threads = AsyncOrgThreadResource(http) async def list( @@ -1806,6 +3502,427 @@ async def linked_accounts( response_type=OrgLinkedAccountsResponse, ) + async def task_epics( + self, + org: str, + *, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskEpicsResponse: + """ + List an organization's epic summaries + Groups org-owned tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + org: Organization ID (`org_...`) owning the tasks. + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/orgs/{org}/task_epics", + query=query, + response_type=OrgTaskEpicsResponse, + ) + + +class OrgTaskResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list( + self, + org: str, + *, + status: str | None = None, + owner_user: str | None = None, + owner_agent: str | None = None, + priority: int | None = None, + tag: str | None = None, + parent: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + search: str | None = None, + sort: str | None = None, + order: str | None = None, + due_before: str | None = None, + due_after: str | None = None, + overdue: bool | None = None, + ready: bool | None = None, + attention: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskListResponse: + """ + List an organization's tasks + Returns org-owned tasks for the specified organization: rows with the org + tenant and no narrower user/team/agent scope. Explicitly personal, team, or + agent tasks are excluded here; list those via the user/team collections or + fetch them directly when authorized. + Every organization member may list. App-scoped developer and + server-to-server callers supply the org in the route. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + status: Filter tasks by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to return tasks in all statuses. + owner_user: Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`). + owner_agent: Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`). + priority: Filter tasks by priority, from 0 (highest) to 4 (lowest). + tag: Return only tasks carrying this tag (matched against the canonical lowercase form). + parent: Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks. + source_scope: Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Return only tasks whose source matches this object kind. Must be supplied with the other source filters. + source_id: Return only tasks whose source matches this object identity. Must be supplied with the other source filters. + epic: Return only tasks with this exact epic label. + search: Restrict results to tasks whose name or description contains this string. + sort: Sort key. One of `"created"` (default most recently created first), `"due_date"` (soonest due first; tasks without a due date always sort last), or `"priority"` (most urgent first). Ties break by most recently created. + order: Sort direction, `"asc"` or `"desc"`. Defaults to `"desc"` for `created` and `"asc"` for `due_date` and `priority`. + due_before: Return only tasks with a due date strictly before this ISO 8601 datetime (`2026-08-01T00:00:00Z`) or date (`2026-08-01`, meaning midnight UTC). Tasks without a due date are excluded. + due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. + overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. + ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. + limit: Maximum number of tasks to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if status is not None: + query["status"] = status + if owner_user is not None: + query["owner_user"] = owner_user + if owner_agent is not None: + query["owner_agent"] = owner_agent + if priority is not None: + query["priority"] = priority + if tag is not None: + query["tag"] = tag + if parent is not None: + query["parent"] = parent + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if search is not None: + query["search"] = search + if sort is not None: + query["sort"] = sort + if order is not None: + query["order"] = order + if due_before is not None: + query["due_before"] = due_before + if due_after is not None: + query["due_after"] = due_after + if overdue is not None: + query["overdue"] = overdue + if ready is not None: + query["ready"] = ready + if attention is not None: + query["attention"] = attention + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/orgs/{org}/tasks", + query=query, + response_type=OrgTaskListResponse, + ) + + def create(self, org: str, input: OrgTaskCreateInput) -> Task: + """ + Create a task for an organization + Creates a new org-owned task (org tenant, no narrower user/team/agent + scope) and returns the full task object. User-authenticated calls are + attributed to the authenticated user or agent. App-scoped developer and + server-to-server callers must provide an explicit `user` or `agent` actor + for the organization. Every referenced principal is validated against the + app and organization before creation. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + input: Request body. + input.agent: Explicit acting agent (`agi_...`) for a developer or server-to-server call. Mutually exclusive with an acting `user`; the agent must belong to the organization. + input.task: Attributes for the task to create. `name` is required; all other fields are optional. + input.user: Explicit acting user (`usr_...`) for a developer or server-to-server call. Mutually exclusive with an acting `agent`. + + Returns: + The newly created task. + """ + return self._http.request( + f"/api/v1/orgs/{org}/tasks", + method="POST", + body=input, + response_type=Task, + ) + + def blocker_cycles( + self, org: str, *, limit: int | None = None, after_cursor: str | None = None + ) -> OrgTaskBlockerCyclesResponse: + """ + List task blocker cycles for an organization + Runs an on-demand diagnostic over unfinished org-owned tasks for the + specified organization and returns a forward cursor-paginated page of + complete cyclic blocker components. Detection is bounded to owners with at + most 100 unfinished tasks. This endpoint is read-only: cycles do not prevent + task updates, lease acquisition, or completion. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + limit: Maximum cycle components to return. Defaults to 50; maximum is 100. + after_cursor: Opaque cursor returned by the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/orgs/{org}/tasks/blocker_cycles", + query=query, + response_type=OrgTaskBlockerCyclesResponse, + ) + + def by_source(self, org: str, input: OrgTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.team: Team owner. + input.user: User owner, or explicit acting user on a team route. + + Returns: + The task and whether this request created it. + """ + return self._http.request( + f"/api/v1/orgs/{org}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + + def metrics(self, org: str, *, days: int | None = None) -> OrgTaskMetricsResponse: + """ + Get task activity metrics for an organization + Returns task activity metrics scoped to org-owned tasks (org tenant, no + narrower user/team/agent scope). `open` counts currently open or + in-progress org tasks, `created` counts tasks inserted in the UTC-day + window, and `completed` counts tasks whose `closed_at` falls in that window + while still in a closed status. Hard-deleted tasks are absent from the + projection and therefore omitted. The daily series is zero-filled across + the requested window and includes reconstructed end-of-day `open` stock. + Any organization member may read this count-only summary. Requests from + callers without org access return 404 so organization existence is not + disclosed. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + days: UTC-day window. One of 7, 30, 90, or 365; defaults to 30. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if days is not None: + query["days"] = days + return self._http.request( + f"/api/v1/orgs/{org}/tasks/metrics", + query=query, + response_type=OrgTaskMetricsResponse, + ) + + def ready( + self, + org: str, + *, + explain: bool | None = None, + assigned_to_me: bool | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskReadyResponse: + """ + List an organization's ready tasks + Returns open org-owned tasks with no unfinished blockers and no active + session lease. Readiness is calculated by the server from the current task + projection. It is a snapshot, not a reservation; claim a task lease before + starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. + Pass `explain=true` to include every open task with a stable readiness reason. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + explain: Include blocked and actively leased open tasks with exclusion reasons. + assigned_to_me: Only include tasks assigned to the authenticated user. + source_scope: Only include tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Only include tasks whose source matches this object kind. + source_id: Only include tasks whose source matches this object identity. + epic: Only include tasks with this exact epic label. + limit: Maximum number of readiness entries to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if explain is not None: + query["explain"] = explain + if assigned_to_me is not None: + query["assigned_to_me"] = assigned_to_me + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/orgs/{org}/tasks/ready", + query=query, + response_type=OrgTaskReadyResponse, + ) + + def search( + self, + org: str, + *, + q: str | None = None, + query: str | None = None, + status: str | None = None, + owner_user: str | None = None, + owner_agent: str | None = None, + priority: int | None = None, + tag: str | None = None, + parent: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + epic: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskSearchResponse: + """ + Search an organization's tasks + Performs a full-text search over org-owned tasks for the specified + organization and returns matching results. Combine `q` with the optional + filters to narrow the result set further. When no query is provided, the + endpoint behaves like a filtered list. + The `query` field in the response echoes the effective search query. + Explicitly personal, team, or agent tasks are excluded here. + + Args: + org: Organization ID (`org_...`) whose org-owned tasks should be returned. + q: Full-text search query matched against task names and descriptions. Takes precedence over `query` when both are provided. + query: Alias for `q`. Use `q` when possible; this parameter exists for compatibility. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. + owner_user: Filter tasks assigned to a specific user. Provide the user's public ID (`usr_...`). + owner_agent: Filter tasks assigned to a specific agent. Provide the agent's public ID (`agi_...`). + priority: Filter tasks by priority, from 0 (highest) to 4 (lowest). + tag: Return only tasks carrying this tag (matched against the canonical lowercase form). + parent: Return only subtasks of the given task (`tsk_...`), or pass `none` to return only top-level tasks. + source_scope: Return only tasks whose source matches this container. Must be supplied with `source_type` and `source_id`. + source_type: Return only tasks whose source matches this object kind. Must be supplied with the other source filters. + source_id: Return only tasks whose source matches this object identity. Must be supplied with the other source filters. + epic: Return only tasks with this exact epic label. + limit: Maximum number of tasks to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if q is not None: + query["q"] = q + if query is not None: + query["query"] = query + if status is not None: + query["status"] = status + if owner_user is not None: + query["owner_user"] = owner_user + if owner_agent is not None: + query["owner_agent"] = owner_agent + if priority is not None: + query["priority"] = priority + if tag is not None: + query["tag"] = tag + if parent is not None: + query["parent"] = parent + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if epic is not None: + query["epic"] = epic + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/orgs/{org}/tasks/search", + query=query, + response_type=OrgTaskSearchResponse, + ) + class OrgThreadResource: def __init__(self, http: SyncHttpClient): @@ -1820,7 +3937,7 @@ def list(self, org: str, *, key: str | None = None) -> OrgThreadListResponse: Args: org: Organization ID (`org_...`) whose org-visibility threads should be listed. - key: Optional exact thread key, for example `archdev-room`. + key: Optional exact thread key, for example `support-room`. Returns: Successful response @@ -1865,6 +3982,7 @@ def create(self, org: str, input: OrgThreadCreateInput) -> Thread: class OrgResource: def __init__(self, http: SyncHttpClient): self._http = http + self.tasks = OrgTaskResource(http) self.threads = OrgThreadResource(http) def list( @@ -1977,3 +4095,53 @@ def linked_accounts( query=query, response_type=OrgLinkedAccountsResponse, ) + + def task_epics( + self, + org: str, + *, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> OrgTaskEpicsResponse: + """ + List an organization's epic summaries + Groups org-owned tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + org: Organization ID (`org_...`) owning the tasks. + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/orgs/{org}/task_epics", + query=query, + response_type=OrgTaskEpicsResponse, + ) diff --git a/src/archastro/platform/v1/resources/private_service_enrollments.py b/src/archastro/platform/v1/resources/private_service_enrollments.py index 5aabea4..319864b 100644 --- a/src/archastro/platform/v1/resources/private_service_enrollments.py +++ b/src/archastro/platform/v1/resources/private_service_enrollments.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 99e6da9a184b +# Content hash: 35906c269bf3 from __future__ import annotations @@ -76,6 +76,22 @@ async def create( response_type=CreatedPrivateServiceEnrollment, ) + async def delete(self, private_service_enrollment_id: str) -> None: + """ + Revoke a private service enrollment + Removes online connector authorization and prevents credential renewal. + + Args: + private_service_enrollment_id: Canonical certificate-bound service identity. + + Returns: + Authorization was durably revoked. + """ + await self._http.request( + f"/api/v1/private_service_enrollments/{private_service_enrollment_id}", + method="DELETE", + ) + async def get( self, private_service_enrollment_id: str, *, org: str | None = None ) -> PrivateServiceEnrollment: @@ -150,6 +166,22 @@ def create(self, input: PrivateServiceEnrollmentCreateInput) -> CreatedPrivateSe response_type=CreatedPrivateServiceEnrollment, ) + def delete(self, private_service_enrollment_id: str) -> None: + """ + Revoke a private service enrollment + Removes online connector authorization and prevents credential renewal. + + Args: + private_service_enrollment_id: Canonical certificate-bound service identity. + + Returns: + Authorization was durably revoked. + """ + self._http.request( + f"/api/v1/private_service_enrollments/{private_service_enrollment_id}", + method="DELETE", + ) + def get( self, private_service_enrollment_id: str, *, org: str | None = None ) -> PrivateServiceEnrollment: diff --git a/src/archastro/platform/v1/resources/relays.py b/src/archastro/platform/v1/resources/relays.py new file mode 100644 index 0000000..98e8602 --- /dev/null +++ b/src/archastro/platform/v1/resources/relays.py @@ -0,0 +1,144 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: b775160d8a63 + +from __future__ import annotations + +from typing import TypedDict + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.relays import Relay + + +class RelayCreateInput(TypedDict): + "Create an owner-only relay" + + purpose: str + ttl_seconds: int + + +class RelayExtendInput(TypedDict): + "Extend an owner-only relay" + + ttl_seconds: int + + +class AsyncRelayResource: + def __init__(self, http: HttpClient): + self._http = http + + async def create(self, input: RelayCreateInput) -> Relay: + """ + Create an owner-only relay + Only the owning user in the same app may access this relay. + + Args: + input: Request body. + + Returns: + Successful response + """ + return await self._http.request( + "/api/v1/relays", + method="POST", + body=input, + response_type=Relay, + ) + + async def delete(self, relay: str) -> Relay: + """ + Delete an owner-only relay + Only the owning user in the same app may access this relay. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/relays/{relay}", + method="DELETE", + response_type=Relay, + ) + + async def get(self, relay: str) -> Relay: + """ + Show an owner-only relay + Only the owning user in the same app may access this relay. + + Returns: + Successful response + """ + return await self._http.request(f"/api/v1/relays/{relay}", response_type=Relay) + + async def extend(self, relay: str, input: RelayExtendInput) -> Relay: + """ + Extend an owner-only relay + Only the owning user in the same app may access this relay. + + Args: + input: Request body. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/relays/{relay}/extend", + method="POST", + body=input, + response_type=Relay, + ) + + +class RelayResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def create(self, input: RelayCreateInput) -> Relay: + """ + Create an owner-only relay + Only the owning user in the same app may access this relay. + + Args: + input: Request body. + + Returns: + Successful response + """ + return self._http.request("/api/v1/relays", method="POST", body=input, response_type=Relay) + + def delete(self, relay: str) -> Relay: + """ + Delete an owner-only relay + Only the owning user in the same app may access this relay. + + Returns: + Successful response + """ + return self._http.request(f"/api/v1/relays/{relay}", method="DELETE", response_type=Relay) + + def get(self, relay: str) -> Relay: + """ + Show an owner-only relay + Only the owning user in the same app may access this relay. + + Returns: + Successful response + """ + return self._http.request(f"/api/v1/relays/{relay}", response_type=Relay) + + def extend(self, relay: str, input: RelayExtendInput) -> Relay: + """ + Extend an owner-only relay + Only the owning user in the same app may access this relay. + + Args: + input: Request body. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/relays/{relay}/extend", + method="POST", + body=input, + response_type=Relay, + ) diff --git a/src/archastro/platform/v1/resources/scripts.py b/src/archastro/platform/v1/resources/scripts.py index 1683f33..919dd0f 100644 --- a/src/archastro/platform/v1/resources/scripts.py +++ b/src/archastro/platform/v1/resources/scripts.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 9ffdb1f5efaa +# Content hash: 9c57dd4942ea from __future__ import annotations @@ -15,6 +15,10 @@ class ScriptRunInput(TypedDict, total=False): "Execute a workflow script" + config: str | None + "Stored Script config ID (`cfg_...`) whose current version is executed. Mutually exclusive with `script`. Required for server-side producers that must not supply raw source." + config_key: str | None + "`lookup_key` of an app-level Script config whose current version is executed. Resolved among the app's configs that belong to no organization, never an organization's config with the same key. Mutually exclusive with `script` and `config`." resolution_context_config: str | None "Optional Script config ID establishing the authorized installation context for imports." run_as_agent: str | None @@ -24,7 +28,9 @@ class ScriptRunInput(TypedDict, total=False): scope: dict[str, Any] | None "Initial scope injected into the script execution context. Use this to supply `variables`, `system`, or other top-level scope keys." script: str | None - "The script source to execute. Defaults to an empty string." + "The script source to execute. Defaults to an empty string. Mutually exclusive with `config`." + thread: str | None + "Optional thread ID for agent execution. Requires run_as_agent, caller access to the thread, matching agent app and organization, and agent membership in the thread." class ScriptTestInput(TypedDict, total=False): @@ -87,14 +93,19 @@ async def llm_txt(self) -> dict[str, str]: async def run(self, input: ScriptRunInput) -> ScriptRunResult: """ Execute a workflow script - Executes the provided script source and returns the result value together - with any `print` output captured during the run. Use this endpoint to - evaluate scripts interactively during development, or to drive automation - from external tooling. - The script runs with the authenticated viewer's identity by default. Pass - `run_as_user` to impersonate a specific user, or `run_as_agent` to run as - a specific agent, subject to the caller's delegation permissions. These two params are mutually exclusive supplying both - returns a 422 error. + Executes script source and returns the result value together with any + `print` output captured during the run. Pass `script` for interactive + evaluation, or `config` to run a stored Script config by ID. Server-side + producers (for example a webhook service emitting through `events.emit`) + must use `config`; raw source from those callers is not an authorized + application script. + The script runs with the authenticated viewer's identity by default. Org + targeting for secret-key callers comes from the `X-ArchAstro-Org-Id` + header, not from the body. Pass `run_as_user` to impersonate a specific + user, or `run_as_agent` to run as a specific agent, subject to the + caller's delegation permissions. These two params are mutually exclusive + supplying both returns a 422 error. `script` and `config` are also + mutually exclusive. Runtime environment variables resolved for the execution identity's app and organization are automatically injected into the script's `variables.env` scope. When the script raises a runtime error, the response still returns HTTP 200 with `error`, `findings`, and any @@ -102,11 +113,14 @@ async def run(self, input: ScriptRunInput) -> ScriptRunResult: Args: input: Request body. + input.config: Stored Script config ID (`cfg_...`) whose current version is executed. Mutually exclusive with `script`. Required for server-side producers that must not supply raw source. + input.config_key: `lookup_key` of an app-level Script config whose current version is executed. Resolved among the app's configs that belong to no organization, never an organization's config with the same key. Mutually exclusive with `script` and `config`. input.resolution_context_config: Optional Script config ID establishing the authorized installation context for imports. input.run_as_agent: Agent ID (`agt_...`) to impersonate during execution. When set, the script's `system.viewer` is replaced with a viewer for this agent. Mutually exclusive with `run_as_user`. `null` by default. input.run_as_user: User ID (`usr_...`) to impersonate during execution. When set, the script's `system.viewer` is replaced with a viewer for this user. Mutually exclusive with `run_as_agent`. `null` by default. input.scope: Initial scope injected into the script execution context. Use this to supply `variables`, `system`, or other top-level scope keys. - input.script: The script source to execute. Defaults to an empty string. + input.script: The script source to execute. Defaults to an empty string. Mutually exclusive with `config`. + input.thread: Optional thread ID for agent execution. Requires run_as_agent, caller access to the thread, matching agent app and organization, and agent membership in the thread. Returns: The script execution result, including the return value, captured output, and any runtime error or diagnostic findings. @@ -236,14 +250,19 @@ def llm_txt(self) -> dict[str, str]: def run(self, input: ScriptRunInput) -> ScriptRunResult: """ Execute a workflow script - Executes the provided script source and returns the result value together - with any `print` output captured during the run. Use this endpoint to - evaluate scripts interactively during development, or to drive automation - from external tooling. - The script runs with the authenticated viewer's identity by default. Pass - `run_as_user` to impersonate a specific user, or `run_as_agent` to run as - a specific agent, subject to the caller's delegation permissions. These two params are mutually exclusive supplying both - returns a 422 error. + Executes script source and returns the result value together with any + `print` output captured during the run. Pass `script` for interactive + evaluation, or `config` to run a stored Script config by ID. Server-side + producers (for example a webhook service emitting through `events.emit`) + must use `config`; raw source from those callers is not an authorized + application script. + The script runs with the authenticated viewer's identity by default. Org + targeting for secret-key callers comes from the `X-ArchAstro-Org-Id` + header, not from the body. Pass `run_as_user` to impersonate a specific + user, or `run_as_agent` to run as a specific agent, subject to the + caller's delegation permissions. These two params are mutually exclusive + supplying both returns a 422 error. `script` and `config` are also + mutually exclusive. Runtime environment variables resolved for the execution identity's app and organization are automatically injected into the script's `variables.env` scope. When the script raises a runtime error, the response still returns HTTP 200 with `error`, `findings`, and any @@ -251,11 +270,14 @@ def run(self, input: ScriptRunInput) -> ScriptRunResult: Args: input: Request body. + input.config: Stored Script config ID (`cfg_...`) whose current version is executed. Mutually exclusive with `script`. Required for server-side producers that must not supply raw source. + input.config_key: `lookup_key` of an app-level Script config whose current version is executed. Resolved among the app's configs that belong to no organization, never an organization's config with the same key. Mutually exclusive with `script` and `config`. input.resolution_context_config: Optional Script config ID establishing the authorized installation context for imports. input.run_as_agent: Agent ID (`agt_...`) to impersonate during execution. When set, the script's `system.viewer` is replaced with a viewer for this agent. Mutually exclusive with `run_as_user`. `null` by default. input.run_as_user: User ID (`usr_...`) to impersonate during execution. When set, the script's `system.viewer` is replaced with a viewer for this user. Mutually exclusive with `run_as_agent`. `null` by default. input.scope: Initial scope injected into the script execution context. Use this to supply `variables`, `system`, or other top-level scope keys. - input.script: The script source to execute. Defaults to an empty string. + input.script: The script source to execute. Defaults to an empty string. Mutually exclusive with `config`. + input.thread: Optional thread ID for agent execution. Requires run_as_agent, caller access to the thread, matching agent app and organization, and agent membership in the thread. Returns: The script execution result, including the return value, captured output, and any runtime error or diagnostic findings. diff --git a/src/archastro/platform/v1/resources/task_ownership_conversions.py b/src/archastro/platform/v1/resources/task_ownership_conversions.py new file mode 100644 index 0000000..47717bb --- /dev/null +++ b/src/archastro/platform/v1/resources/task_ownership_conversions.py @@ -0,0 +1,50 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 3294b6630a47 + +from __future__ import annotations + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.tasks import TaskOwnershipJob + + +class AsyncTaskOwnershipConversionResource: + def __init__(self, http: HttpClient): + self._http = http + + async def get(self, job_id: str) -> TaskOwnershipJob: + """ + Read the status of a task ownership conversion + Returns only the requesting user's job status, even after task visibility changes. + + Args: + job_id: The accepted background job ID. + + Returns: + Current background job state. + """ + return await self._http.request( + f"/api/v1/task_ownership_conversions/{job_id}", + response_type=TaskOwnershipJob, + ) + + +class TaskOwnershipConversionResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def get(self, job_id: str) -> TaskOwnershipJob: + """ + Read the status of a task ownership conversion + Returns only the requesting user's job status, even after task visibility changes. + + Args: + job_id: The accepted background job ID. + + Returns: + Current background job state. + """ + return self._http.request( + f"/api/v1/task_ownership_conversions/{job_id}", + response_type=TaskOwnershipJob, + ) diff --git a/src/archastro/platform/v1/resources/tasks.py b/src/archastro/platform/v1/resources/tasks.py index 83baccf..44ae627 100644 --- a/src/archastro/platform/v1/resources/tasks.py +++ b/src/archastro/platform/v1/resources/tasks.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 67016fab74b4 +# Content hash: 929c29305abf from __future__ import annotations @@ -14,6 +14,7 @@ Task, TaskComment, TaskExternalLink, + TaskOwnershipJob, TaskSessionLease, TaskSessionLeaseSummary, ) @@ -39,11 +40,13 @@ class CommentCreateInputComment(TypedDict): "The plain-text content of the comment. Must be a non-empty string." -class CommentCreateInput(TypedDict): +class CommentCreateInput(TypedDict, total=False): "Create a comment on a task" - comment: CommentCreateInputComment + comment: Required[CommentCreateInputComment] "Parameters for the comment to create, including its body." + expected_version: int | None + "Reject the comment if the task changed since this version." class CommentReplaceInput(TypedDict): @@ -127,6 +130,10 @@ class TaskReplaceInput(TypedDict, total=False): "Move this task under a top-level parent (`tsk_...`), or pass null to promote it to a top-level task. A task that has subtasks cannot become one." priority: int | None "Updated priority from 0 (highest) to 4 (lowest)." + project_id: str | None + "Project ID (`prj_...`). Omit to preserve; pass null to clear." + require_status_lease: bool | None + "Session clients must set true for status changes. A live lease then requires the holder's lease_id and lease_session_id; human and host overrides remain available." source_id: str | None "Replacement source object identity. Must be supplied with the other source fields." source_scope: str | None @@ -137,12 +144,44 @@ class TaskReplaceInput(TypedDict, total=False): "Updated status: `open`, `in_progress`, `in_review`, or `done`." tags: list[str] | None "Replacement tag list (max 20, each up to 40 characters; normalized to lowercase). Pass an empty array to clear all tags." + target_binary: str | None + "Replacement target binary (for example `my-cli`): a lowercase identifier of 1-64 characters. Pass together with `target_version`, or pass both as null to clear the target." + target_version: str | None + "Replacement minimum semantic version of `target_binary` (for example `0.48.9`). Must be supplied with `target_binary`." team: str | None "Explicit owning team (`tem_...`) for a developer or server-to-server call." user: str | None "Explicit user (`usr_...`) for a developer or server-to-server call. With `team`, this identifies the acting team member." +class TaskOwnershipConversionsInput(TypedDict, total=False): + "Convert ownership of a connected task graph" + + dest_agent: str | None + "Destination agent (`agi_...`)." + dest_org: str | None + "Destination organization (`org_...`)." + dest_team: str | None + "Destination team (`tem_...`)." + dest_user: str | None + "Destination user (`usr_...`). Supply exactly one destination." + + +class TaskTransferOwnershipInput(TypedDict, total=False): + "Transfer one task to a different owner" + + dest_agent: str | None + "Destination agent (`agi_...`)." + dest_org: str | None + "Destination organization (`org_...`)." + dest_team: str | None + "Destination team (`tem_...`)." + dest_user: str | None + "Destination user (`usr_...`). Supply exactly one destination." + expected_version: Required[int] + "The task version reviewed by the caller." + + class BlockerListResponseDataItemCreatedByActorProfilePicture(BaseModel): file: str | None = Field( default=None, @@ -260,7 +299,7 @@ class BlockerListResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -303,7 +342,11 @@ class BlockerListResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -332,12 +375,16 @@ class BlockerListResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -354,25 +401,37 @@ class BlockerListResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -649,7 +708,7 @@ class TaskBlockingResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -692,7 +751,11 @@ class TaskBlockingResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -721,12 +784,16 @@ class TaskBlockingResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -743,25 +810,37 @@ class TaskBlockingResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -900,7 +979,7 @@ class TaskSubtasksResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -943,7 +1022,11 @@ class TaskSubtasksResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -972,12 +1055,16 @@ class TaskSubtasksResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -994,25 +1081,37 @@ class TaskSubtasksResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -1195,6 +1294,7 @@ async def create(self, task: str, input: CommentCreateInput) -> TaskComment: task: Task ID (`tsk_...`). input: Request body. input.comment: Parameters for the comment to create, including its body. + input.expected_version: Reject the comment if the task changed since this version. Returns: The newly created comment. @@ -1551,11 +1651,15 @@ async def replace(self, task: str, input: TaskReplaceInput) -> Task: input.owner_user: Assign to a user by public ID (`usr_...`). input.parent: Move this task under a top-level parent (`tsk_...`), or pass null to promote it to a top-level task. A task that has subtasks cannot become one. input.priority: Updated priority from 0 (highest) to 4 (lowest). + input.project_id: Project ID (`prj_...`). Omit to preserve; pass null to clear. + input.require_status_lease: Session clients must set true for status changes. A live lease then requires the holder's lease_id and lease_session_id; human and host overrides remain available. input.source_id: Replacement source object identity. Must be supplied with the other source fields. input.source_scope: Replacement source container. Pass together with `source_type` and `source_id`, or pass all three as null to clear the source. input.source_type: Replacement source object kind. Must be supplied with the other source fields. input.status: Updated status: `open`, `in_progress`, `in_review`, or `done`. input.tags: Replacement tag list (max 20, each up to 40 characters; normalized to lowercase). Pass an empty array to clear all tags. + input.target_binary: Replacement target binary (for example `my-cli`): a lowercase identifier of 1-64 characters. Pass together with `target_version`, or pass both as null to clear the target. + input.target_version: Replacement minimum semantic version of `target_binary` (for example `0.48.9`). Must be supplied with `target_binary`. input.team: Explicit owning team (`tem_...`) for a developer or server-to-server call. input.user: Explicit user (`usr_...`) for a developer or server-to-server call. With `team`, this identifies the acting team member. @@ -1665,6 +1769,31 @@ async def blocking( response_type=TaskBlockingResponse, ) + async def ownership_conversions( + self, task: str, input: TaskOwnershipConversionsInput + ) -> TaskOwnershipJob: + """ + Convert ownership of a connected task graph + Queues ownership changes for the connected task graph across parent, child, blocker, and blocking relationships. The selected tasks and their versions are frozen when queued. Each transfer checks the requesting user s current authority; conflicts fail the job without undoing completed transfers. + + Args: + task: Root task (`tsk_...`). + input: Request body. + input.dest_agent: Destination agent (`agi_...`). + input.dest_org: Destination organization (`org_...`). + input.dest_team: Destination team (`tem_...`). + input.dest_user: Destination user (`usr_...`). Supply exactly one destination. + + Returns: + The queued background job. + """ + return await self._http.request( + f"/api/v1/tasks/{task}/ownership_conversions", + method="POST", + body=input, + response_type=TaskOwnershipJob, + ) + async def subtasks( self, task: str, @@ -1714,6 +1843,30 @@ async def subtasks( response_type=TaskSubtasksResponse, ) + async def transfer_ownership(self, task: str, input: TaskTransferOwnershipInput) -> Task: + """ + Transfer one task to a different owner + Changes one task s privacy owner at the supplied aggregate version. Assignment, creator, source identity, comments, and relationship IDs remain unchanged. Source and destination must have the same app and sandbox; the authenticated user must have authority over both scopes. + + Args: + task: Task (`tsk_...`). + input: Request body. + input.dest_agent: Destination agent (`agi_...`). + input.dest_org: Destination organization (`org_...`). + input.dest_team: Destination team (`tem_...`). + input.dest_user: Destination user (`usr_...`). Supply exactly one destination. + input.expected_version: The task version reviewed by the caller. + + Returns: + The task with its new owner. + """ + return await self._http.request( + f"/api/v1/tasks/{task}/transfer_ownership", + method="POST", + body=input, + response_type=Task, + ) + class BlockerResource: def __init__(self, http: SyncHttpClient): @@ -1876,6 +2029,7 @@ def create(self, task: str, input: CommentCreateInput) -> TaskComment: task: Task ID (`tsk_...`). input: Request body. input.comment: Parameters for the comment to create, including its body. + input.expected_version: Reject the comment if the task changed since this version. Returns: The newly created comment. @@ -2232,11 +2386,15 @@ def replace(self, task: str, input: TaskReplaceInput) -> Task: input.owner_user: Assign to a user by public ID (`usr_...`). input.parent: Move this task under a top-level parent (`tsk_...`), or pass null to promote it to a top-level task. A task that has subtasks cannot become one. input.priority: Updated priority from 0 (highest) to 4 (lowest). + input.project_id: Project ID (`prj_...`). Omit to preserve; pass null to clear. + input.require_status_lease: Session clients must set true for status changes. A live lease then requires the holder's lease_id and lease_session_id; human and host overrides remain available. input.source_id: Replacement source object identity. Must be supplied with the other source fields. input.source_scope: Replacement source container. Pass together with `source_type` and `source_id`, or pass all three as null to clear the source. input.source_type: Replacement source object kind. Must be supplied with the other source fields. input.status: Updated status: `open`, `in_progress`, `in_review`, or `done`. input.tags: Replacement tag list (max 20, each up to 40 characters; normalized to lowercase). Pass an empty array to clear all tags. + input.target_binary: Replacement target binary (for example `my-cli`): a lowercase identifier of 1-64 characters. Pass together with `target_version`, or pass both as null to clear the target. + input.target_version: Replacement minimum semantic version of `target_binary` (for example `0.48.9`). Must be supplied with `target_binary`. input.team: Explicit owning team (`tem_...`) for a developer or server-to-server call. input.user: Explicit user (`usr_...`) for a developer or server-to-server call. With `team`, this identifies the acting team member. @@ -2346,6 +2504,31 @@ def blocking( response_type=TaskBlockingResponse, ) + def ownership_conversions( + self, task: str, input: TaskOwnershipConversionsInput + ) -> TaskOwnershipJob: + """ + Convert ownership of a connected task graph + Queues ownership changes for the connected task graph across parent, child, blocker, and blocking relationships. The selected tasks and their versions are frozen when queued. Each transfer checks the requesting user s current authority; conflicts fail the job without undoing completed transfers. + + Args: + task: Root task (`tsk_...`). + input: Request body. + input.dest_agent: Destination agent (`agi_...`). + input.dest_org: Destination organization (`org_...`). + input.dest_team: Destination team (`tem_...`). + input.dest_user: Destination user (`usr_...`). Supply exactly one destination. + + Returns: + The queued background job. + """ + return self._http.request( + f"/api/v1/tasks/{task}/ownership_conversions", + method="POST", + body=input, + response_type=TaskOwnershipJob, + ) + def subtasks( self, task: str, @@ -2394,3 +2577,27 @@ def subtasks( query=query, response_type=TaskSubtasksResponse, ) + + def transfer_ownership(self, task: str, input: TaskTransferOwnershipInput) -> Task: + """ + Transfer one task to a different owner + Changes one task s privacy owner at the supplied aggregate version. Assignment, creator, source identity, comments, and relationship IDs remain unchanged. Source and destination must have the same app and sandbox; the authenticated user must have authority over both scopes. + + Args: + task: Task (`tsk_...`). + input: Request body. + input.dest_agent: Destination agent (`agi_...`). + input.dest_org: Destination organization (`org_...`). + input.dest_team: Destination team (`tem_...`). + input.dest_user: Destination user (`usr_...`). Supply exactly one destination. + input.expected_version: The task version reviewed by the caller. + + Returns: + The task with its new owner. + """ + return self._http.request( + f"/api/v1/tasks/{task}/transfer_ownership", + method="POST", + body=input, + response_type=Task, + ) diff --git a/src/archastro/platform/v1/resources/teams.py b/src/archastro/platform/v1/resources/teams.py index 1ad227b..322734f 100644 --- a/src/archastro/platform/v1/resources/teams.py +++ b/src/archastro/platform/v1/resources/teams.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 14d31bec228a +# Content hash: 7f84db90b859 from __future__ import annotations @@ -12,7 +12,7 @@ from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.common import CustomObject -from ...types.tasks import Task +from ...types.tasks import Task, TaskEnsureResult from ...types.teams import Team, TeamDeleteImpactResponse, TeamInvite, TeamMembership from ...types.threads import Thread @@ -67,6 +67,8 @@ class TeamTaskCreateInputTask(TypedDict, total=False): "Create this task as a subtask of an existing top-level task (`tsk_...`). Subtasks nest exactly one level." priority: int | None "Priority level from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when omitted." + project_id: str | None + "Optional project ID (`prj_...`). Pass null to clear it." source_id: str | None "Source object identity (for example `ArchAstro/firstlanding`)." source_scope: str | None @@ -77,6 +79,10 @@ class TeamTaskCreateInputTask(TypedDict, total=False): 'Initial status for the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`. Defaults to `"open"` when omitted.' tags: list[str] | None "Labels for grouping and filtering (max 20, each up to 40 characters). Stored canonically: lowercase, trimmed, de-duplicated." + target_binary: str | None + "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`." + target_version: str | None + "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`." thread: str | None "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation." @@ -94,6 +100,31 @@ class TeamTaskCreateInput(TypedDict, total=False): "User ID (`usr_...`). On a user route this is the task owner and creator; on a team route it is the explicit acting user for a developer or server-to-server call." +class TeamTaskBySourceInput(TypedDict, total=False): + "Find or create a task by source" + + agent: str | None + "Explicit acting agent for privileged callers." + description: str | None + "Initial task description." + epic: str | None + "Initial epic grouping." + name: Required[str] + "Initial task name." + org: str | None + "Explicit organization for privileged callers." + source_id: Required[str] + "Source identity, for example owner/repo#123." + source_scope: Required[str] + "Source scope, for example github." + source_type: Required[str] + "Source type, for example pull_request." + status: str | None + "Initial task status." + user: str | None + "User owner, or explicit acting user on a team route." + + class TeamThreadCreateInputThreadMembersItem(TypedDict): id: str "Public user (`usr_...`) or agent (`agt_...`) ID matching `type`." @@ -349,9 +380,16 @@ class TeamCustomObjectListResponseDataItem(BaseModel): default=None, description="Access control list governing read and write access to this custom object. Only returned to resource owners and privileged or organization-admin viewers; `null` for everyone else.", ) + agent: str | None = Field( + default=None, + description="ID of the agent that owns this object (`agt_...`). `null` for other ownership types.", + ) created_at: datetime | None = Field( default=None, description="When the custom object was created (ISO 8601)." ) + expires_at: datetime | None = Field( + default=None, description="Absolute expiry time (RFC 3339). Null when no expiry is set." + ) fields: dict[str, Any] | None = Field( default=None, description="Map of field names to their current values as defined by the object's schema type.", @@ -1368,7 +1406,7 @@ class TeamTaskListResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -1411,7 +1449,11 @@ class TeamTaskListResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -1440,12 +1482,16 @@ class TeamTaskListResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -1462,25 +1508,37 @@ class TeamTaskListResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -1625,7 +1683,7 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -1668,7 +1726,11 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -1697,12 +1759,16 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -1719,25 +1785,37 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -1899,7 +1977,7 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -1942,7 +2020,11 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -1971,12 +2053,16 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -1993,25 +2079,37 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -2167,7 +2265,7 @@ class TeamTaskSearchResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -2210,7 +2308,11 @@ class TeamTaskSearchResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -2239,12 +2341,16 @@ class TeamTaskSearchResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -2261,25 +2367,37 @@ class TeamTaskSearchResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -2469,6 +2587,7 @@ class TeamThreadListResponseDataItemParentMessageAttachmentsItemVariantsItemImag class TeamThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(BaseModel): + byte_size: int | None = Field(default=None, description="Size of the stored bytes.") content_type: str | None = Field( default=None, description='MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.', @@ -2476,6 +2595,10 @@ class TeamThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas created_at: datetime | None = Field( default=None, description="When this variant was created (ISO 8601)." ) + duration_ms: int | None = Field( + default=None, + description="Client-declared video duration in milliseconds; not server-probed.", + ) file: str | None = Field( default=None, description="ID of the underlying storage file that backs this variant (`fil_...`).", @@ -2494,6 +2617,7 @@ class TeamThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas default=None, description="Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types.", ) + sha256: str | None = Field(default=None, description="Lowercase SHA-256 of the stored bytes.") updated_at: datetime | None = Field( default=None, description="When this variant was last updated (ISO 8601)." ) @@ -2501,6 +2625,9 @@ class TeamThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas default=None, description="Signed download URL for this variant, resolved at request time. `null` if the file is unavailable.", ) + url_expires_at: datetime | None = Field( + default=None, description="Expiry of this variant's playable URL." + ) variant_key: str | None = Field( default=None, description='Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).', @@ -3817,6 +3944,62 @@ class TeamListResponse(BaseModel): ) +class TeamArtifactsResponseDataItemAclAddItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class TeamArtifactsResponseDataItemAclGrantsItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class TeamArtifactsResponseDataItemAclRemoveItem(BaseModel): + principal: str | None = Field( + default=None, + description='The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class TeamArtifactsResponseDataItemAcl(BaseModel): + add: list[TeamArtifactsResponseDataItemAclAddItem] | None = Field( + default=None, + description="Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + ) + grants: list[TeamArtifactsResponseDataItemAclGrantsItem] | None = Field( + default=None, + description="Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + ) + remove: list[TeamArtifactsResponseDataItemAclRemoveItem] | None = Field( + default=None, + description="Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + ) + + class TeamArtifactsResponseDataItemImageSource(BaseModel): file: str | None = Field( default=None, @@ -3847,6 +4030,10 @@ class TeamArtifactsResponseDataItemImageSource(BaseModel): class TeamArtifactsResponseDataItem(BaseModel): + acl: TeamArtifactsResponseDataItemAcl | None = Field( + default=None, + description="Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + ) agent: str | None = Field( default=None, description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", @@ -4010,6 +4197,48 @@ class TeamTaskAssigneesResponse(BaseModel): ) +class TeamTaskEpicsResponseDataItemNextReadyItem(BaseModel): + id: str + name: str + priority: int + status: str + + +class TeamTaskEpicsResponseDataItemRootBlockersItem(BaseModel): + downstream_count: int + id: str + name: str + priority: int + status: str + + +class TeamTaskEpicsResponseDataItem(BaseModel): + blocked_count: int + counts: dict[str, Any] = Field( + ..., description="Task counts keyed by status, including closed tasks." + ) + epic: str + next_ready: list[TeamTaskEpicsResponseDataItemNextReadyItem] + root_blockers: list[TeamTaskEpicsResponseDataItemRootBlockersItem] + root_blockers_partial: bool = Field( + ..., description="True when root ranking used a truncated in-epic graph." + ) + top_open_priority: int | None = None + updated_at: datetime + + +class TeamTaskEpicsResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[TeamTaskEpicsResponseDataItem] + has_more: bool + no_epic_open_count: int + + class AsyncTeamCustomObjectResource: def __init__(self, http: HttpClient): self._http = http @@ -4281,6 +4510,7 @@ async def list( due_after: str | None = None, overdue: bool | None = None, ready: bool | None = None, + attention: bool | None = None, limit: int | None = None, after_cursor: str | None = None, ) -> TeamTaskListResponse: @@ -4316,6 +4546,7 @@ async def list( due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. limit: Maximum number of tasks to return. Capped at 100. after_cursor: Opaque cursor returned by the previous page. @@ -4361,6 +4592,8 @@ async def list( query["overdue"] = overdue if ready is not None: query["ready"] = ready + if attention is not None: + query["attention"] = attention if limit is not None: query["limit"] = limit if after_cursor is not None: @@ -4442,6 +4675,35 @@ async def blocker_cycles( response_type=TeamTaskBlockerCyclesResponse, ) + async def by_source(self, team: str, input: TeamTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + team: Team ID (`tem_...`). Only tasks belonging to this team are returned. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.org: Explicit organization for privileged callers. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.user: User owner, or explicit acting user on a team route. + + Returns: + The task and whether this request created it. + """ + return await self._http.request( + f"/api/v1/teams/{team}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + async def metrics(self, team: str, *, days: int | None = None) -> TeamTaskMetricsResponse: """ Get task activity metrics for a team @@ -4492,6 +4754,8 @@ async def ready( Returns open tasks with no unfinished blockers and no active session lease. Readiness is calculated by the server from the current task projection. It is a snapshot, not a reservation; claim a task lease before starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. Pass `explain=true` to include every open task with a stable readiness reason. Args: @@ -5055,6 +5319,61 @@ async def task_assignees( response_type=TeamTaskAssigneesResponse, ) + async def task_epics( + self, + team: str, + *, + user: str | None = None, + org: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> TeamTaskEpicsResponse: + """ + List an owner's epic summaries + Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if user is not None: + query["user"] = user + if org is not None: + query["org"] = org + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/teams/{team}/task_epics", + query=query, + response_type=TeamTaskEpicsResponse, + ) + class TeamCustomObjectResource: def __init__(self, http: SyncHttpClient): @@ -5324,6 +5643,7 @@ def list( due_after: str | None = None, overdue: bool | None = None, ready: bool | None = None, + attention: bool | None = None, limit: int | None = None, after_cursor: str | None = None, ) -> TeamTaskListResponse: @@ -5359,6 +5679,7 @@ def list( due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. limit: Maximum number of tasks to return. Capped at 100. after_cursor: Opaque cursor returned by the previous page. @@ -5404,6 +5725,8 @@ def list( query["overdue"] = overdue if ready is not None: query["ready"] = ready + if attention is not None: + query["attention"] = attention if limit is not None: query["limit"] = limit if after_cursor is not None: @@ -5485,6 +5808,35 @@ def blocker_cycles( response_type=TeamTaskBlockerCyclesResponse, ) + def by_source(self, team: str, input: TeamTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + team: Team ID (`tem_...`). Only tasks belonging to this team are returned. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.org: Explicit organization for privileged callers. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.user: User owner, or explicit acting user on a team route. + + Returns: + The task and whether this request created it. + """ + return self._http.request( + f"/api/v1/teams/{team}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + def metrics(self, team: str, *, days: int | None = None) -> TeamTaskMetricsResponse: """ Get task activity metrics for a team @@ -5535,6 +5887,8 @@ def ready( Returns open tasks with no unfinished blockers and no active session lease. Readiness is calculated by the server from the current task projection. It is a snapshot, not a reservation; claim a task lease before starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. Pass `explain=true` to include every open task with a stable readiness reason. Args: @@ -6084,3 +6438,58 @@ def task_assignees(self, team: str, *, org: str | None = None) -> TeamTaskAssign query=query, response_type=TeamTaskAssigneesResponse, ) + + def task_epics( + self, + team: str, + *, + user: str | None = None, + org: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> TeamTaskEpicsResponse: + """ + List an owner's epic summaries + Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if user is not None: + query["user"] = user + if org is not None: + query["org"] = org + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/teams/{team}/task_epics", + query=query, + response_type=TeamTaskEpicsResponse, + ) diff --git a/src/archastro/platform/v1/resources/threads.py b/src/archastro/platform/v1/resources/threads.py index 18ca810..d8257f6 100644 --- a/src/archastro/platform/v1/resources/threads.py +++ b/src/archastro/platform/v1/resources/threads.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 73b7afcbd2f8 +# Content hash: fe8db978c7bd from __future__ import annotations @@ -11,9 +11,63 @@ from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.chat import ChatMember +from ...types.common import AgentComputer, AgentComputerListResponse from ...types.threads import Thread, ThreadReadStatus, ThreadSettings +class ComputerCreateInputAclAddItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ComputerCreateInputAclGrantsItem(TypedDict, total=False): + actions: Required[list[str]] + 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' + principal: str | None + 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ComputerCreateInputAclRemoveItem(TypedDict, total=False): + principal: str | None + 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' + principal_type: Required[str] + 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' + + +class ComputerCreateInputAcl(TypedDict, total=False): + add: list[ComputerCreateInputAclAddItem] | None + "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." + grants: list[ComputerCreateInputAclGrantsItem] | None + "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." + remove: list[ComputerCreateInputAclRemoveItem] | None + "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." + + +class ComputerCreateInput(TypedDict, total=False): + "Provision a computer for a thread" + + acl: ComputerCreateInputAcl | None + "Initial access control list." + config: dict[str, Any] | None + "Provider configuration; top-level provider takes precedence." + key: str | None + "Stable key unique within this thread." + metadata: dict[str, Any] | None + "Arbitrary computer metadata." + name: Required[str] + "Human-readable computer name." + provider: str | None + "Compute backend: sprites (default), vercel or e2b." + region: str | None + "Provider region; defaults to iad." + + class ThreadMemberCreateInput(TypedDict, total=False): "Add a member to a thread" @@ -195,6 +249,62 @@ class ThreadAgentsResponse(BaseModel): ) +class ThreadArtifactsResponseDataItemAclAddItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class ThreadArtifactsResponseDataItemAclGrantsItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class ThreadArtifactsResponseDataItemAclRemoveItem(BaseModel): + principal: str | None = Field( + default=None, + description='The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class ThreadArtifactsResponseDataItemAcl(BaseModel): + add: list[ThreadArtifactsResponseDataItemAclAddItem] | None = Field( + default=None, + description="Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + ) + grants: list[ThreadArtifactsResponseDataItemAclGrantsItem] | None = Field( + default=None, + description="Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + ) + remove: list[ThreadArtifactsResponseDataItemAclRemoveItem] | None = Field( + default=None, + description="Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + ) + + class ThreadArtifactsResponseDataItemImageSource(BaseModel): file: str | None = Field( default=None, @@ -225,6 +335,10 @@ class ThreadArtifactsResponseDataItemImageSource(BaseModel): class ThreadArtifactsResponseDataItem(BaseModel): + acl: ThreadArtifactsResponseDataItemAcl | None = Field( + default=None, + description="Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + ) agent: str | None = Field( default=None, description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", @@ -474,6 +588,7 @@ class ThreadMessagesResponseDataMessagesItemAttachmentsItemVariantsItemImageSour class ThreadMessagesResponseDataMessagesItemAttachmentsItemVariantsItem(BaseModel): + byte_size: int | None = Field(default=None, description="Size of the stored bytes.") content_type: str | None = Field( default=None, description='MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.', @@ -481,6 +596,10 @@ class ThreadMessagesResponseDataMessagesItemAttachmentsItemVariantsItem(BaseMode created_at: datetime | None = Field( default=None, description="When this variant was created (ISO 8601)." ) + duration_ms: int | None = Field( + default=None, + description="Client-declared video duration in milliseconds; not server-probed.", + ) file: str | None = Field( default=None, description="ID of the underlying storage file that backs this variant (`fil_...`).", @@ -499,6 +618,7 @@ class ThreadMessagesResponseDataMessagesItemAttachmentsItemVariantsItem(BaseMode default=None, description="Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types.", ) + sha256: str | None = Field(default=None, description="Lowercase SHA-256 of the stored bytes.") updated_at: datetime | None = Field( default=None, description="When this variant was last updated (ISO 8601)." ) @@ -506,6 +626,9 @@ class ThreadMessagesResponseDataMessagesItemAttachmentsItemVariantsItem(BaseMode default=None, description="Signed download URL for this variant, resolved at request time. `null` if the file is unavailable.", ) + url_expires_at: datetime | None = Field( + default=None, description="Expiry of this variant's playable URL." + ) variant_key: str | None = Field( default=None, description='Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).', @@ -869,6 +992,53 @@ class ThreadTrajectoriesResponse(BaseModel): ) +class AsyncComputerResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list(self, thread: str) -> AgentComputerListResponse: + """ + List a thread's computers + Returns all live computers in the visible thread, oldest first, without pagination. + + Args: + thread: Thread ID (`thr_...`). + + Returns: + The thread's live computers. + """ + return await self._http.request( + f"/api/v1/threads/{thread}/computers", + response_type=AgentComputerListResponse, + ) + + async def create(self, thread: str, input: ComputerCreateInput) -> AgentComputer: + """ + Provision a computer for a thread + Creates a computer owned by the thread's owner. Requires permission to manage the thread; every thread viewer may use the computer. + + Args: + thread: Thread ID (`thr_...`). + input: Request body. + input.acl: Initial access control list. + input.config: Provider configuration; top-level provider takes precedence. + input.key: Stable key unique within this thread. + input.metadata: Arbitrary computer metadata. + input.name: Human-readable computer name. + input.provider: Compute backend: sprites (default), vercel or e2b. + input.region: Provider region; defaults to iad. + + Returns: + The newly created computer. Returns HTTP 201. + """ + return await self._http.request( + f"/api/v1/threads/{thread}/computers", + method="POST", + body=input, + response_type=AgentComputer, + ) + + class AsyncThreadMemberResource: def __init__(self, http: HttpClient): self._http = http @@ -1070,6 +1240,7 @@ async def replace(self, thread: str, input: TagReplaceInput) -> Thread: class AsyncThreadResource: def __init__(self, http: HttpClient): self._http = http + self.computers = AsyncComputerResource(http) self.members = AsyncThreadMemberResource(http) self.settings = AsyncSettingResource(http) self.tags = AsyncTagResource(http) @@ -1229,6 +1400,7 @@ async def messages( after_cursor: str | None = None, metadata: dict[str, Any] | None = None, limit: int | None = None, + include_replies: bool | None = None, anchor: str | None = None, direction: Literal["before", "after", "around"] | None = None, before_limit: int | None = None, @@ -1254,6 +1426,9 @@ async def messages( is returned if the thread does not exist or is not visible to the authenticated user. Threads with `kind: feed` return only top-level posts, each with its newest replies inline under `replies` and reply cursors for paging the rest. + Set `include_replies: true` to page messages and replies together by creation + order instead, without inline reply previews. Metadata filters and the page + limit apply to the updates themselves, before any client-side grouping. Pass `include_reply_counts: true` to annotate each message with the number of threaded replies it has received. This adds a small amount of latency and should be omitted when reply counts are not needed. @@ -1264,6 +1439,7 @@ async def messages( after_cursor: Opaque cursor returned in a previous response's `after_cursor` field. When provided, returns messages immediately after that position. May be combined with `before_cursor` to bound a range. metadata: Structured metadata filter expression. Only messages whose `metadata` object satisfies the expression are returned. The filter is applied before cursor pagination and anchored window limits. limit: Maximum number of messages to return per page. Defaults to 20; maximum is 100. + include_replies: Include replies in feed message pagination instead of returning root posts with inline replies. Defaults to false. anchor: Message ID (`msg_...`) to use as a window anchor, or `last_matching` to resolve the anchor from the latest message matching the anchor filters. Cannot be combined with `before_cursor` or `after_cursor`. direction: Window direction relative to `anchor`. `before` returns older messages, `after` returns newer messages, and `around` returns messages on both sides. Defaults to `after` when `anchor` is supplied. `direction=around` cannot be combined with an explicit `limit`; use `before_limit` and `after_limit`. before_limit: For `direction=around`, maximum number of messages older than the anchor. Defaults to 20; maximum is 100. @@ -1285,6 +1461,8 @@ async def messages( query["metadata"] = metadata if limit is not None: query["limit"] = limit + if include_replies is not None: + query["include_replies"] = include_replies if anchor is not None: query["anchor"] = anchor if direction is not None: @@ -1461,6 +1639,53 @@ async def trajectories( ) +class ComputerResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list(self, thread: str) -> AgentComputerListResponse: + """ + List a thread's computers + Returns all live computers in the visible thread, oldest first, without pagination. + + Args: + thread: Thread ID (`thr_...`). + + Returns: + The thread's live computers. + """ + return self._http.request( + f"/api/v1/threads/{thread}/computers", + response_type=AgentComputerListResponse, + ) + + def create(self, thread: str, input: ComputerCreateInput) -> AgentComputer: + """ + Provision a computer for a thread + Creates a computer owned by the thread's owner. Requires permission to manage the thread; every thread viewer may use the computer. + + Args: + thread: Thread ID (`thr_...`). + input: Request body. + input.acl: Initial access control list. + input.config: Provider configuration; top-level provider takes precedence. + input.key: Stable key unique within this thread. + input.metadata: Arbitrary computer metadata. + input.name: Human-readable computer name. + input.provider: Compute backend: sprites (default), vercel or e2b. + input.region: Provider region; defaults to iad. + + Returns: + The newly created computer. Returns HTTP 201. + """ + return self._http.request( + f"/api/v1/threads/{thread}/computers", + method="POST", + body=input, + response_type=AgentComputer, + ) + + class ThreadMemberResource: def __init__(self, http: SyncHttpClient): self._http = http @@ -1662,6 +1887,7 @@ def replace(self, thread: str, input: TagReplaceInput) -> Thread: class ThreadResource: def __init__(self, http: SyncHttpClient): self._http = http + self.computers = ComputerResource(http) self.members = ThreadMemberResource(http) self.settings = SettingResource(http) self.tags = TagResource(http) @@ -1821,6 +2047,7 @@ def messages( after_cursor: str | None = None, metadata: dict[str, Any] | None = None, limit: int | None = None, + include_replies: bool | None = None, anchor: str | None = None, direction: Literal["before", "after", "around"] | None = None, before_limit: int | None = None, @@ -1846,6 +2073,9 @@ def messages( is returned if the thread does not exist or is not visible to the authenticated user. Threads with `kind: feed` return only top-level posts, each with its newest replies inline under `replies` and reply cursors for paging the rest. + Set `include_replies: true` to page messages and replies together by creation + order instead, without inline reply previews. Metadata filters and the page + limit apply to the updates themselves, before any client-side grouping. Pass `include_reply_counts: true` to annotate each message with the number of threaded replies it has received. This adds a small amount of latency and should be omitted when reply counts are not needed. @@ -1856,6 +2086,7 @@ def messages( after_cursor: Opaque cursor returned in a previous response's `after_cursor` field. When provided, returns messages immediately after that position. May be combined with `before_cursor` to bound a range. metadata: Structured metadata filter expression. Only messages whose `metadata` object satisfies the expression are returned. The filter is applied before cursor pagination and anchored window limits. limit: Maximum number of messages to return per page. Defaults to 20; maximum is 100. + include_replies: Include replies in feed message pagination instead of returning root posts with inline replies. Defaults to false. anchor: Message ID (`msg_...`) to use as a window anchor, or `last_matching` to resolve the anchor from the latest message matching the anchor filters. Cannot be combined with `before_cursor` or `after_cursor`. direction: Window direction relative to `anchor`. `before` returns older messages, `after` returns newer messages, and `around` returns messages on both sides. Defaults to `after` when `anchor` is supplied. `direction=around` cannot be combined with an explicit `limit`; use `before_limit` and `after_limit`. before_limit: For `direction=around`, maximum number of messages older than the anchor. Defaults to 20; maximum is 100. @@ -1877,6 +2108,8 @@ def messages( query["metadata"] = metadata if limit is not None: query["limit"] = limit + if include_replies is not None: + query["include_replies"] = include_replies if anchor is not None: query["anchor"] = anchor if direction is not None: diff --git a/src/archastro/platform/v1/resources/users.py b/src/archastro/platform/v1/resources/users.py index 7d57700..d08fa9a 100644 --- a/src/archastro/platform/v1/resources/users.py +++ b/src/archastro/platform/v1/resources/users.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: e3a983ce419d +# Content hash: 557ee782b674 from __future__ import annotations @@ -13,7 +13,7 @@ from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.common import CurrentUser from ...types.system import SystemAccessToken -from ...types.tasks import Task +from ...types.tasks import Task, TaskEnsureResult from ...types.threads import Thread from ...types.users import User, UserInvite, UserSSHKey @@ -50,6 +50,8 @@ class UserTaskCreateInputTask(TypedDict, total=False): "Create this task as a subtask of an existing top-level task (`tsk_...`). Subtasks nest exactly one level." priority: int | None "Priority level from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when omitted." + project_id: str | None + "Optional project ID (`prj_...`). Pass null to clear it." source_id: str | None "Source object identity (for example `ArchAstro/firstlanding`)." source_scope: str | None @@ -60,6 +62,10 @@ class UserTaskCreateInputTask(TypedDict, total=False): 'Initial status for the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`. Defaults to `"open"` when omitted.' tags: list[str] | None "Labels for grouping and filtering (max 20, each up to 40 characters). Stored canonically: lowercase, trimmed, de-duplicated." + target_binary: str | None + "Binary that must run this work, as a lowercase identifier of 1-64 characters (letters, digits, `.`, `_`, `-`; for example `my-cli`). Must be supplied with `target_version`." + target_version: str | None + "Minimum semantic version of `target_binary` that can run this work (`MAJOR.MINOR.PATCH` with optional prerelease and build, no leading `v`). Must be supplied with `target_binary`." thread: str | None "Bind the task to a thread (`thr_...`) owned by the same team or user as the task. A bound task appears in that thread's task scope, exactly like a task filed from inside the conversation. Omit for a task not tied to a conversation." @@ -77,6 +83,31 @@ class UserTaskCreateInput(TypedDict, total=False): "Team ID (`tem_...`). The task will be owned by this team." +class UserTaskBySourceInput(TypedDict, total=False): + "Find or create a task by source" + + agent: str | None + "Explicit acting agent for privileged callers." + description: str | None + "Initial task description." + epic: str | None + "Initial epic grouping." + name: Required[str] + "Initial task name." + org: str | None + "Explicit organization for privileged callers." + source_id: Required[str] + "Source identity, for example owner/repo#123." + source_scope: Required[str] + "Source scope, for example github." + source_type: Required[str] + "Source type, for example pull_request." + status: str | None + "Initial task status." + team: str | None + "Team owner." + + class UserThreadCreateInputThreadMembersItem(TypedDict): id: str "Public user (`usr_...`) or agent (`agt_...`) ID matching `type`." @@ -350,7 +381,7 @@ class UserTaskListResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -393,7 +424,11 @@ class UserTaskListResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -422,12 +457,16 @@ class UserTaskListResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -444,25 +483,37 @@ class UserTaskListResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -607,7 +658,7 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -650,7 +701,11 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -679,12 +734,16 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -701,25 +760,37 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -864,7 +935,7 @@ class UserTaskReadyResponseDataItemTask(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -907,7 +978,11 @@ class UserTaskReadyResponseDataItemTask(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -936,12 +1011,16 @@ class UserTaskReadyResponseDataItemTask(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -958,25 +1037,37 @@ class UserTaskReadyResponseDataItemTask(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -1132,7 +1223,7 @@ class UserTaskSearchResponseDataItem(BaseModel): ) blocked_by_count: int | None = Field( default=None, - description="Number of tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", + description="Number of readable tasks marked as blocking this task, whether or not they are done (see `GET /tasks/{task}/blockers`). Computed on list/show reads; create/update responses may lag one read behind.", ) closed_at: datetime | None = Field( default=None, @@ -1175,7 +1266,11 @@ class UserTaskSearchResponseDataItem(BaseModel): id: str = Field(..., description="Task ID (`tsk_...`).") is_blocked: bool | None = Field( default=None, - description="`true` while at least one blocking task is not yet done. Informational only a blocked task can still change status and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on list/show reads; create/update responses report `false` until the next read.", + description="`true` while at least one blocking task is not yet done. Informational only - a blocked task can still change status - and derived at read time, so the task un-blocks automatically when its last open blocker completes. Computed on reads, including mutation responses.", + ) + is_stalled: bool | None = Field( + default=None, + description="True exactly when status is in_progress and the task has no lease or its lease expiry is in the past. Derived at read time from status and lease expiry, including on unannotated reads.", ) links: dict[str, Any] | None = Field( default=None, @@ -1204,12 +1299,16 @@ class UserTaskSearchResponseDataItem(BaseModel): ) parent: str | None = Field( default=None, - description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks. Subtasks nest exactly one level.", + description="ID of the parent task when this task is a subtask (`tsk_...`). `null` for top-level tasks or when the parent is not readable. Subtasks nest exactly one level.", ) priority: int | None = Field( default=None, description="Priority level of the task from `0` (highest) to `4` (lowest). Defaults to `2` (medium) when not explicitly set.", ) + project_id: str | None = Field( + default=None, + description="Optional project ID (`prj_...`). Membership does not change task visibility.", + ) sandbox: str | None = Field( default=None, description="ID of the developer sandbox this task is scoped to (`dsb_...`). `null` for tasks outside a sandbox environment.", @@ -1226,25 +1325,37 @@ class UserTaskSearchResponseDataItem(BaseModel): default=None, description="Kind of source object (for example `repository`). `null` when the task has no source.", ) + stalled_since: datetime | None = Field( + default=None, + description="Lease expiry for a stalled task, or its last update when no lease expiry exists. Null when not stalled.", + ) status: str = Field( ..., description='Current status of the task. One of `"open"`, `"in_progress"`, `"in_review"`, `"paused"`, `"failed"`, `"superseding"`, `"done"`, or `"cancelled"`.', ) subtasks_count: int | None = Field( default=None, - description="Number of subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", + description="Number of readable subtasks under this task. Computed on list/show reads; create/update responses may report 0 until the next read. Always 0 for subtasks.", ) tags: list[str] | None = Field( default=None, description="Labels for grouping and filtering, stored lowercase and de-duplicated. Empty array when untagged.", ) + target_binary: str | None = Field( + default=None, + description="Binary that must run this work (for example `my-cli`). A worker should take the task only when its own `target_binary` build is at least `target_version`. `null` when the task has no target. Set together with `target_version`.", + ) + target_version: str | None = Field( + default=None, + description="Minimum semantic version of `target_binary` that can run this work (for example `0.48.9`), in canonical `MAJOR.MINOR.PATCH[-prerelease][+build]` form. `null` when the task has no target.", + ) team: str | None = Field( default=None, description="ID of the team that owns this task (`tem_...`). `null` if the task is not scoped to a team.", ) thread: str | None = Field( default=None, - description="ID of the thread this task is bound to (`thr_...`) the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", + description="ID of the thread this task is bound to (`thr_...`) - the conversation it was filed from, or the thread passed at creation. `null` for tasks not tied to a thread.", ) updated_at: datetime | None = Field( default=None, description="When the task was last modified (ISO 8601)." @@ -1434,6 +1545,7 @@ class UserThreadListResponseDataItemParentMessageAttachmentsItemVariantsItemImag class UserThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(BaseModel): + byte_size: int | None = Field(default=None, description="Size of the stored bytes.") content_type: str | None = Field( default=None, description='MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.', @@ -1441,6 +1553,10 @@ class UserThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas created_at: datetime | None = Field( default=None, description="When this variant was created (ISO 8601)." ) + duration_ms: int | None = Field( + default=None, + description="Client-declared video duration in milliseconds; not server-probed.", + ) file: str | None = Field( default=None, description="ID of the underlying storage file that backs this variant (`fil_...`).", @@ -1459,6 +1575,7 @@ class UserThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas default=None, description="Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types.", ) + sha256: str | None = Field(default=None, description="Lowercase SHA-256 of the stored bytes.") updated_at: datetime | None = Field( default=None, description="When this variant was last updated (ISO 8601)." ) @@ -1466,6 +1583,9 @@ class UserThreadListResponseDataItemParentMessageAttachmentsItemVariantsItem(Bas default=None, description="Signed download URL for this variant, resolved at request time. `null` if the file is unavailable.", ) + url_expires_at: datetime | None = Field( + default=None, description="Expiry of this variant's playable URL." + ) variant_key: str | None = Field( default=None, description='Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).', @@ -2710,6 +2830,62 @@ class TokenListResponse(BaseModel): ) +class UserArtifactsResponseDataItemAclAddItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class UserArtifactsResponseDataItemAclGrantsItem(BaseModel): + actions: list[str] = Field( + ..., + description='Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.', + ) + principal: str | None = Field( + default=None, + description='The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class UserArtifactsResponseDataItemAclRemoveItem(BaseModel): + principal: str | None = Field( + default=None, + description='The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.', + ) + principal_type: str = Field( + ..., + description='The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.', + ) + + +class UserArtifactsResponseDataItemAcl(BaseModel): + add: list[UserArtifactsResponseDataItemAclAddItem] | None = Field( + default=None, + description="Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`.", + ) + grants: list[UserArtifactsResponseDataItemAclGrantsItem] | None = Field( + default=None, + description="Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`.", + ) + remove: list[UserArtifactsResponseDataItemAclRemoveItem] | None = Field( + default=None, + description="Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`.", + ) + + class UserArtifactsResponseDataItemImageSource(BaseModel): file: str | None = Field( default=None, @@ -2740,6 +2916,10 @@ class UserArtifactsResponseDataItemImageSource(BaseModel): class UserArtifactsResponseDataItem(BaseModel): + acl: UserArtifactsResponseDataItemAcl | None = Field( + default=None, + description="Access control list for the artifact. Contains a `grants` array where each entry specifies `principal_type`, `principal`, and `actions` (`read`, `write`). `null` when the artifact has no grants or the viewer is not its owner.", + ) agent: str | None = Field( default=None, description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", @@ -2896,6 +3076,48 @@ class UserOrgsResponse(BaseModel): ) +class UserTaskEpicsResponseDataItemNextReadyItem(BaseModel): + id: str + name: str + priority: int + status: str + + +class UserTaskEpicsResponseDataItemRootBlockersItem(BaseModel): + downstream_count: int + id: str + name: str + priority: int + status: str + + +class UserTaskEpicsResponseDataItem(BaseModel): + blocked_count: int + counts: dict[str, Any] = Field( + ..., description="Task counts keyed by status, including closed tasks." + ) + epic: str + next_ready: list[UserTaskEpicsResponseDataItemNextReadyItem] + root_blockers: list[UserTaskEpicsResponseDataItemRootBlockersItem] + root_blockers_partial: bool = Field( + ..., description="True when root ranking used a truncated in-epic graph." + ) + top_open_priority: int | None = None + updated_at: datetime + + +class UserTaskEpicsResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[UserTaskEpicsResponseDataItem] + has_more: bool + no_epic_open_count: int + + class AsyncIntegrationResource: def __init__(self, http: HttpClient): self._http = http @@ -3007,6 +3229,7 @@ async def list( due_after: str | None = None, overdue: bool | None = None, ready: bool | None = None, + attention: bool | None = None, limit: int | None = None, after_cursor: str | None = None, ) -> UserTaskListResponse: @@ -3042,6 +3265,7 @@ async def list( due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. limit: Maximum number of tasks to return. Capped at 100. after_cursor: Opaque cursor returned by the previous page. @@ -3087,6 +3311,8 @@ async def list( query["overdue"] = overdue if ready is not None: query["ready"] = ready + if attention is not None: + query["attention"] = attention if limit is not None: query["limit"] = limit if after_cursor is not None: @@ -3168,6 +3394,35 @@ async def blocker_cycles( response_type=UserTaskBlockerCyclesResponse, ) + async def by_source(self, user: str, input: UserTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + user: User ID (`usr_...`) for user-scoped tasks. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.org: Explicit organization for privileged callers. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.team: Team owner. + + Returns: + The task and whether this request created it. + """ + return await self._http.request( + f"/api/v1/users/{user}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + async def ready( self, user: str, @@ -3188,6 +3443,8 @@ async def ready( Returns open tasks with no unfinished blockers and no active session lease. Readiness is calculated by the server from the current task projection. It is a snapshot, not a reservation; claim a task lease before starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. Pass `explain=true` to include every open task with a stable readiness reason. Args: @@ -3513,8 +3770,8 @@ async def get(self, user: str) -> User: """ Retrieve a user by ID Returns the user identified by `user`. The authenticated user must share - at least one team with the target user; requests for users outside any - shared team are rejected with 403. + an organization or at least one team with the target user; other requests + are rejected with 403. A user may always retrieve their own profile with this endpoint. Use the `GET /users/me` endpoint as a convenience alias for retrieving the authenticated user without specifying an ID. @@ -3641,6 +3898,61 @@ async def profile(self, user: str, input: UserProfileInput) -> User: response_type=User, ) + async def task_epics( + self, + user: str, + *, + team: str | None = None, + org: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> UserTaskEpicsResponse: + """ + List an owner's epic summaries + Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if team is not None: + query["team"] = team + if org is not None: + query["org"] = org + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return await self._http.request( + f"/api/v1/users/{user}/task_epics", + query=query, + response_type=UserTaskEpicsResponse, + ) + class IntegrationResource: def __init__(self, http: SyncHttpClient): @@ -3753,6 +4065,7 @@ def list( due_after: str | None = None, overdue: bool | None = None, ready: bool | None = None, + attention: bool | None = None, limit: int | None = None, after_cursor: str | None = None, ) -> UserTaskListResponse: @@ -3788,6 +4101,7 @@ def list( due_after: Return only tasks with a due date strictly after this ISO 8601 datetime or date. Tasks without a due date are excluded. overdue: When `true`, return only overdue tasks: a due date before the current UTC day and a status other than `"done"`. A task due today is not overdue. ready: When true, return only open tasks with no unfinished blockers and no active session lease. This is a projection snapshot; claim a lease before starting work. + attention: When true, return paused, failed, or in-progress tasks with no lease or a past lease expiry. limit: Maximum number of tasks to return. Capped at 100. after_cursor: Opaque cursor returned by the previous page. @@ -3833,6 +4147,8 @@ def list( query["overdue"] = overdue if ready is not None: query["ready"] = ready + if attention is not None: + query["attention"] = attention if limit is not None: query["limit"] = limit if after_cursor is not None: @@ -3914,6 +4230,35 @@ def blocker_cycles( response_type=UserTaskBlockerCyclesResponse, ) + def by_source(self, user: str, input: UserTaskBySourceInput) -> TaskEnsureResult: + """ + Find or create a task by source + Atomically creates one task per owner, tenant and source. Retries return the current task without updating it. Historical tasks created through ordinary create are not adopted. + + Args: + user: User ID (`usr_...`) for user-scoped tasks. + input: Request body. + input.agent: Explicit acting agent for privileged callers. + input.description: Initial task description. + input.epic: Initial epic grouping. + input.name: Initial task name. + input.org: Explicit organization for privileged callers. + input.source_id: Source identity, for example owner/repo#123. + input.source_scope: Source scope, for example github. + input.source_type: Source type, for example pull_request. + input.status: Initial task status. + input.team: Team owner. + + Returns: + The task and whether this request created it. + """ + return self._http.request( + f"/api/v1/users/{user}/tasks/by-source", + method="PUT", + body=input, + response_type=TaskEnsureResult, + ) + def ready( self, user: str, @@ -3934,6 +4279,8 @@ def ready( Returns open tasks with no unfinished blockers and no active session lease. Readiness is calculated by the server from the current task projection. It is a snapshot, not a reservation; claim a task lease before starting work. + Results are ordered by priority (0, the most urgent, first), then oldest + first, so the first page is always the most urgent ready work. Pass `explain=true` to include every open task with a stable readiness reason. Args: @@ -4256,8 +4603,8 @@ def get(self, user: str) -> User: """ Retrieve a user by ID Returns the user identified by `user`. The authenticated user must share - at least one team with the target user; requests for users outside any - shared team are rejected with 403. + an organization or at least one team with the target user; other requests + are rejected with 403. A user may always retrieve their own profile with this endpoint. Use the `GET /users/me` endpoint as a convenience alias for retrieving the authenticated user without specifying an ID. @@ -4380,3 +4727,58 @@ def profile(self, user: str, input: UserProfileInput) -> User: body=input, response_type=User, ) + + def task_epics( + self, + user: str, + *, + team: str | None = None, + org: str | None = None, + source_scope: str | None = None, + source_type: str | None = None, + source_id: str | None = None, + top_priority: int | None = None, + include_closed: bool | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> UserTaskEpicsResponse: + """ + List an owner's epic summaries + Groups tasks by their exact epic label, ordered by label. Closed tasks include cancelled tasks, but cancelled prerequisites still block. Source filters select the tasks summarized; blockers outside that selection still affect readiness. Root ranking is capped per epic and root_blockers_partial is true when that graph is truncated. No epic entity is created. + + Args: + source_scope: Exact source scope; supply all three source fields. + source_type: Exact source type. + source_id: Exact source ID. + top_priority: Exact highest open priority, from 0 to 4. + include_closed: Include epics with no non-closed tasks; defaults to false. + limit: Maximum epics; defaults to 50, capped at 100. + after_cursor: Opaque forward cursor from the preceding page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if team is not None: + query["team"] = team + if org is not None: + query["org"] = org + if source_scope is not None: + query["source_scope"] = source_scope + if source_type is not None: + query["source_type"] = source_type + if source_id is not None: + query["source_id"] = source_id + if top_priority is not None: + query["top_priority"] = top_priority + if include_closed is not None: + query["include_closed"] = include_closed + if limit is not None: + query["limit"] = limit + if after_cursor is not None: + query["after_cursor"] = after_cursor + return self._http.request( + f"/api/v1/users/{user}/task_epics", + query=query, + response_type=UserTaskEpicsResponse, + ) diff --git a/src/archastro/platform/v1/resources/workflows.py b/src/archastro/platform/v1/resources/workflows.py index ece11ea..7662e8b 100644 --- a/src/archastro/platform/v1/resources/workflows.py +++ b/src/archastro/platform/v1/resources/workflows.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: e2876aa52c71 +# Content hash: 1afd297ab2c6 from __future__ import annotations @@ -316,7 +316,11 @@ async def run(self, input: WorkflowRunInput) -> WorkflowRunResponse: The authenticated developer is used as the default workflow principal for entitlement checks on billable operations such as LLM calls. Supply `run_as_user` or `run_as_agent` to override the acting identity for the execution at most one - override may be set per request. + override may be set per request. Overrides are subject to the caller's + delegation permissions, the same ones `/scripts/run` applies: app secret keys + and developer tokens may run as any user or agent in the app; other callers may + run as themselves, and as an agent they own (org admins as any agent in their + org). A refused override returns 403 before anything runs. Execution errors are logged to the activity feed under the acting identity so they appear alongside automation-driven workflow errors in operator dashboards. A 422 response is returned when execution fails; the response body includes the error @@ -564,7 +568,11 @@ def run(self, input: WorkflowRunInput) -> WorkflowRunResponse: The authenticated developer is used as the default workflow principal for entitlement checks on billable operations such as LLM calls. Supply `run_as_user` or `run_as_agent` to override the acting identity for the execution at most one - override may be set per request. + override may be set per request. Overrides are subject to the caller's + delegation permissions, the same ones `/scripts/run` applies: app secret keys + and developer tokens may run as any user or agent in the app; other callers may + run as themselves, and as an agent they own (org admins as any agent in their + org). A refused override returns 403 before anything runs. Execution errors are logged to the activity feed under the acting identity so they appear alongside automation-driven workflow errors in operator dashboards. A 422 response is returned when execution fails; the response body includes the error diff --git a/tests/contract/channels/test_api_artifact_channel.py b/tests/contract/channels/test_api_artifact_channel.py new file mode 100644 index 0000000..2e94003 --- /dev/null +++ b/tests/contract/channels/test_api_artifact_channel.py @@ -0,0 +1,392 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: c9b84a12155b + +""" +Contract tests for ApiArtifactChannel — generated from the channel spec. + +Drives the generated channel class through a real WebSocket against the +harness-service subprocess (spawned by conftest.py). Scenarios are +registered over HTTP — there is no in-process closure shortcut, so the +same service Python, TypeScript, and any future language can target. +""" + +import asyncio + +import pytest +import pytest_asyncio + +from archastro.phx_channel import HarnessServiceClient +from archastro.phx_channel.channel import ChannelError +from archastro.platform.channels.api_artifact_channel import ApiArtifactChannel + +# Mark every coroutine in this file as async. Generated tests are imported +# into arbitrary SDK projects whose pytest config may or may not set +# asyncio_mode=auto — pytestmark keeps them runnable either way. +pytestmark = pytest.mark.asyncio + + +@pytest_asyncio.fixture +async def rig(harness_service): + client = HarnessServiceClient( + ws_url=harness_service["wsUrl"], + control_url=harness_service["controlUrl"], + ) + await client.reset() + socket = await client.open_socket() + try: + yield (client, socket) + finally: + await client.close() + + +async def test_api_artifact_channel_join_artifact_joins_and_receives_contract_valid_reply(rig): + _, socket = rig + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + assert isinstance(channel, ApiArtifactChannel) + assert channel.join_response is not None + + +async def test_api_artifact_channel_join_artifact_surfaces_server_error_reply_as_channel_error(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "replyError", "payload": {"reason": "test_error"}}], + } + ) + with pytest.raises(ChannelError): + await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + + +async def test_api_artifact_channel_operation_sends_valid_push_and_receives_contract_valid_reply( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "operation": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.operation( + {"cursor": {"position": 1}, "op": [{}], "op_id": "test-id", "revision": 1} + ) + assert reply["status"] == "ok" + + observed = await client.observations("api:artifact:test-id", "operation") + assert len(observed) == 1 + assert observed[0]["params"]["op"] == [{}] + assert observed[0]["params"]["revision"] == 1 + + +async def test_api_artifact_channel_operation_returns_error_envelope_when_required_missing(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.operation({}) + assert reply["status"] == "error" + + +async def test_api_artifact_channel_cursor_sends_valid_push_and_receives_contract_valid_reply(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "cursor": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.cursor({"cursor": {"position": 1}, "revision": 1}) + assert reply["status"] == "ok" + + observed = await client.observations("api:artifact:test-id", "cursor") + assert len(observed) == 1 + assert observed[0]["params"] is not None + + +async def test_api_artifact_channel_signal_sends_valid_push_and_receives_contract_valid_reply(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "signal": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.signal({"kind": "test", "payload": {}, "to": "test-value"}) + assert reply["status"] == "ok" + + observed = await client.observations("api:artifact:test-id", "signal") + assert len(observed) == 1 + assert observed[0]["params"]["kind"] == "test" + assert observed[0]["params"]["payload"] == {} + assert observed[0]["params"]["to"] == "test-value" + + +async def test_api_artifact_channel_signal_returns_error_envelope_when_required_missing(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.signal({}) + assert reply["status"] == "error" + + +async def test_api_artifact_channel_checkpoint_sends_valid_push_and_receives_contract_valid_reply( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "checkpoint": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.checkpoint({}) + assert reply["status"] == "ok" + + observed = await client.observations("api:artifact:test-id", "checkpoint") + assert len(observed) == 1 + assert observed[0]["params"] is not None + + +async def test_api_artifact_channel_leave_message_sends_valid_push_and_receives_contract_valid_reply( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "leave": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + reply = await channel.leave_message({}) + assert reply["status"] == "ok" + + observed = await client.observations("api:artifact:test-id", "leave") + assert len(observed) == 1 + assert observed[0]["params"] is not None + + +async def test_api_artifact_channel_on_op_rejected_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "op_rejected"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_op_rejected(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_operation_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "operation"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_operation(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_signal_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "signal"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_signal(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_presence_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "presence"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_presence(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_checkpoint_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "checkpoint"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_checkpoint(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_checkpoint_failed_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "checkpoint_failed"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_checkpoint_failed(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_session_invalid_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "session_invalid"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_session_invalid(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_on_access_revoked_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "access_revoked"}, + ], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_access_revoked(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_artifact_channel_leave_leaves_cleanly_through_generated_leave(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:artifact:test-id", + "onJoin": [{"type": "autoReply"}], + } + ) + channel = await ApiArtifactChannel.join_artifact(socket, "test-id", as_agent="test-value") + await channel.leave() diff --git a/tests/contract/channels/test_api_chat_channel.py b/tests/contract/channels/test_api_chat_channel.py index 78570c1..cbfde6b 100644 --- a/tests/contract/channels/test_api_chat_channel.py +++ b/tests/contract/channels/test_api_chat_channel.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 3635861564de +# Content hash: 2277b5e98500 """ Contract tests for ApiChatChannel — generated from the channel spec. @@ -513,6 +513,8 @@ async def test_api_chat_channel_api_chat_post_message_sends_valid_push_and_recei "content": "test content", "context": [{"type": "test"}], "idempotency_key": "test-key", + "mentions": ["test-value"], + "metadata": {}, "reply_to": "test-value", "uploads": [{}], } @@ -576,6 +578,8 @@ async def test_api_chat_channel_api_chat_post_simple_message_sends_valid_push_an "content": "test content", "context": [{"type": "test"}], "idempotency_key": "test-key", + "mentions": ["test-value"], + "metadata": {}, "reply_to": "test-value", } ) diff --git a/tests/contract/streams/test_agent_sessions.py b/tests/contract/streams/test_agent_sessions.py index 2c58615..acc0e3f 100644 --- a/tests/contract/streams/test_agent_sessions.py +++ b/tests/contract/streams/test_agent_sessions.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: e91c51bdb136 +# Content hash: a43fe7e02a5f import pytest @@ -21,7 +21,10 @@ async def test_agent_sessions_stream_yields_sse_events(harness_service): await harness.register_stream_scenario( { "route": "GET /api/v1/agent_sessions/{agent_session}/stream", - "actions": [{"type": "autoEmit", "event": "session_update"}], + "actions": [ + {"type": "autoEmit", "event": "session_event"}, + {"type": "autoEmit", "event": "session_update"}, + ], } ) client = AsyncPlatformClient( @@ -33,7 +36,7 @@ async def test_agent_sessions_stream_yields_sse_events(harness_service): events = [] async for ev in client.v1.agent_sessions.stream("test-value"): events.append(ev) - assert [e["event"] for e in events] == ["session_update"] + assert [e["event"] for e in events] == ["session_event", "session_update"] finally: await client.close() await harness.close() diff --git a/tests/contract/v1/test_agent_computers.py b/tests/contract/v1/test_agent_computers.py index 0926142..0dc494b 100644 --- a/tests/contract/v1/test_agent_computers.py +++ b/tests/contract/v1/test_agent_computers.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: bd994b8c3f15 +# Content hash: 5a7702f427ae import pytest from pydantic import BaseModel @@ -209,6 +209,109 @@ async def test_async_agent_computers_get_error_404(): await ec.close() +def test_agent_computers_update_success(): + client = _client() + try: + result = client.v1.agent_computers.update("test-value", {}) + assert result is None + finally: + client.close() + + +def test_agent_computers_update_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_computers_update_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_agent_computers_update_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_agent_computers_update_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_computers_update_success(): + client = _async_client() + try: + result = await client.v1.agent_computers.update("test-value", {}) + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_computers_update_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_computers_update_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_computers_update_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_computers_update_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_computers.update("test-value", {}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_agent_computers_exec_success(): client = _client() try: diff --git a/tests/contract/v1/test_agent_requests.py b/tests/contract/v1/test_agent_requests.py new file mode 100644 index 0000000..07832cb --- /dev/null +++ b/tests/contract/v1/test_agent_requests.py @@ -0,0 +1,484 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: b5b626f10faf + +import pytest +from pydantic import BaseModel + +from archastro.platform import AsyncPlatformClient, PlatformClient +from archastro.platform.runtime.http_client import ApiError + +PRISM_URL = "http://127.0.0.1:4040" + + +def _client() -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _error_client(code: int) -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def _async_client() -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _async_error_client(code: int) -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def test_agent_requests_list_success(): + client = _client() + try: + result = client.v1.agent_requests.list() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequestListResponse" + finally: + client.close() + + +def test_agent_requests_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.list() + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_requests_list_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.list() + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_list_success(): + client = _async_client() + try: + result = await client.v1.agent_requests.list() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequestListResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.list() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_list_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.list() + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_agent_requests_get_success(): + client = _client() + try: + result = client.v1.agent_requests.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + client.close() + + +def test_agent_requests_get_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_requests_get_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_agent_requests_get_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_agent_requests_get_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_agent_requests_get_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_success(): + client = _async_client() + try: + result = await client.v1.agent_requests.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_get_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.get("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_agent_requests_reject_success(): + client = _client() + try: + result = client.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + client.close() + + +def test_agent_requests_reject_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_requests_reject_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_agent_requests_reject_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_agent_requests_reject_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_agent_requests_reject_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_success(): + client = _async_client() + try: + result = await client.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_reject_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.reject("test-value", {"reason": "test-value"}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_agent_requests_resolve_success(): + client = _client() + try: + result = client.v1.agent_requests.resolve("test-value", {"input": {}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + client.close() + + +def test_agent_requests_resolve_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_requests_resolve_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_agent_requests_resolve_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_agent_requests_resolve_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_agent_requests_resolve_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_success(): + client = _async_client() + try: + result = await client.v1.agent_requests.resolve("test-value", {"input": {}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentRequest" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_requests_resolve_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_requests.resolve("test-value", {"input": {}}) + assert exc_info.value.status == 422 + finally: + await ec.close() diff --git a/tests/contract/v1/test_agent_sessions.py b/tests/contract/v1/test_agent_sessions.py index 9142188..92683b7 100644 --- a/tests/contract/v1/test_agent_sessions.py +++ b/tests/contract/v1/test_agent_sessions.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 2e8872ab1f9b +# Content hash: 22cfb892424c import pytest from pydantic import BaseModel @@ -591,6 +591,90 @@ async def test_async_agent_sessions_cancel_error_404(): await ec.close() +def test_agent_sessions_events_success(): + client = _client() + try: + result = client.v1.agent_sessions.events("test-value") + assert isinstance(result, list) + assert all(type(item).__name__ == "AgentSessionEvent" for item in result) + finally: + client.close() + + +def test_agent_sessions_events_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agent_sessions_events_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_agent_sessions_events_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_sessions_events_success(): + client = _async_client() + try: + result = await client.v1.agent_sessions.events("test-value") + assert isinstance(result, list) + assert all(type(item).__name__ == "AgentSessionEvent" for item in result) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agent_sessions_events_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_sessions_events_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agent_sessions_events_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agent_sessions.events("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + def test_agent_sessions_message_success(): client = _client() try: diff --git a/tests/contract/v1/test_agents.py b/tests/contract/v1/test_agents.py index d253406..9b9e703 100644 --- a/tests/contract/v1/test_agents.py +++ b/tests/contract/v1/test_agents.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 14edaf6d6edd +# Content hash: 0c446f68fc8e import pytest from pydantic import BaseModel @@ -987,6 +987,132 @@ async def test_async_agents_search_error_422(): await ec.close() +def test_agents_self_solution_success(): + client = _client() + try: + result = client.v1.agents.self_solution("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentSelfSolutionResponse" + finally: + client.close() + + +def test_agents_self_solution_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_agents_self_solution_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_agents_self_solution_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_agents_self_solution_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_agents_self_solution_error_500(): + ec = _error_client(500) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 500 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_success(): + client = _async_client() + try: + result = await client.v1.agents.self_solution("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentSelfSolutionResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_agents_self_solution_error_500(): + ec = _async_error_client(500) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.agents.self_solution("test-value") + assert exc_info.value.status == 500 + finally: + await ec.close() + + def test_agents_threads_success(): client = _client() try: diff --git a/tests/contract/v1/test_ai.py b/tests/contract/v1/test_ai.py index ca98041..9ee7437 100644 --- a/tests/contract/v1/test_ai.py +++ b/tests/contract/v1/test_ai.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: ac5f9d33be54 +# Content hash: d535aa27ba1c import pytest from pydantic import BaseModel @@ -127,6 +127,71 @@ async def test_async_ai_session_token_error_422(): await ec.close() +def test_ai_usage_success(): + client = _client() + try: + result = client.v1.ai.usage() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AiUsageResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_ai_usage_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ai.usage() + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_ai_usage_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ai.usage() + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_ai_usage_success(): + client = _async_client() + try: + result = await client.v1.ai.usage() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AiUsageResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_ai_usage_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ai.usage() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_ai_usage_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ai.usage() + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_ai_chat_models_success(): client = _client() try: diff --git a/tests/contract/v1/test_custom_objects.py b/tests/contract/v1/test_custom_objects.py index e03f35f..6f6d84d 100644 --- a/tests/contract/v1/test_custom_objects.py +++ b/tests/contract/v1/test_custom_objects.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 53db68be181a +# Content hash: e41f485994e7 import pytest from pydantic import BaseModel @@ -253,6 +253,132 @@ async def test_async_custom_objects_create_error_422(): await ec.close() +def test_custom_objects_patch_success(): + client = _client() + try: + result = client.v1.custom_objects.patch({"ops": [{}]}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "CustomObjectPatchResponse" + finally: + client.close() + + +def test_custom_objects_patch_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_custom_objects_patch_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_custom_objects_patch_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_custom_objects_patch_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_custom_objects_patch_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_success(): + client = _async_client() + try: + result = await client.v1.custom_objects.patch({"ops": [{}]}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "CustomObjectPatchResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_custom_objects_patch_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.custom_objects.patch({"ops": [{}]}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_custom_objects_delete_success(): client = _client() try: diff --git a/tests/contract/v1/test_files.py b/tests/contract/v1/test_files.py index c518cb8..1287607 100644 --- a/tests/contract/v1/test_files.py +++ b/tests/contract/v1/test_files.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: ce91660cc835 +# Content hash: c5e778874d58 import pytest from pydantic import BaseModel @@ -290,6 +290,132 @@ async def test_async_files_avatar_error_404(): await ec.close() +def test_files_download_success(): + client = _client() + try: + result = client.v1.files.download("test-value") + assert result["content"] is not None + assert result["mime_type"] + finally: + client.close() + + +def test_files_download_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.files.download("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_files_download_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.files.download("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_files_download_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.files.download("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_files_download_error_413(): + ec = _error_client(413) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.files.download("test-value") + assert exc_info.value.status == 413 + finally: + ec.close() + + +def test_files_download_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.files.download("test-value") + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_files_download_success(): + client = _async_client() + try: + result = await client.v1.files.download("test-value") + assert result["content"] is not None + assert result["mime_type"] + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_files_download_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.files.download("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_files_download_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.files.download("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_files_download_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.files.download("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_files_download_error_413(): + ec = _async_error_client(413) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.files.download("test-value") + assert exc_info.value.status == 413 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_files_download_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.files.download("test-value") + assert exc_info.value.status == 502 + finally: + await ec.close() + + def test_files_org_logo_success(): client = _client() try: diff --git a/tests/contract/v1/test_kv.py b/tests/contract/v1/test_kv.py index 132278b..cf99f3a 100644 --- a/tests/contract/v1/test_kv.py +++ b/tests/contract/v1/test_kv.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: d94ed81f3456 +# Content hash: a5d6d16abdce import pytest from pydantic import BaseModel @@ -83,6 +83,16 @@ def test_kv_list_error_403(): ec.close() +def test_kv_list_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.kv.list() + assert exc_info.value.status == 422 + finally: + ec.close() + + @pytest.mark.asyncio async def test_async_kv_list_success(): client = _async_client() @@ -127,6 +137,17 @@ async def test_async_kv_list_error_403(): await ec.close() +@pytest.mark.asyncio +async def test_async_kv_list_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.kv.list() + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_kv_create_success(): client = _client() try: @@ -177,6 +198,16 @@ def test_kv_create_error_404(): ec.close() +def test_kv_create_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.kv.create({"key": "test-key", "value": "test-value"}) + assert exc_info.value.status == 409 + finally: + ec.close() + + def test_kv_create_error_422(): ec = _error_client(422) try: @@ -242,6 +273,17 @@ async def test_async_kv_create_error_404(): await ec.close() +@pytest.mark.asyncio +async def test_async_kv_create_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.kv.create({"key": "test-key", "value": "test-value"}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_kv_create_error_422(): ec = _async_error_client(422) @@ -257,7 +299,8 @@ def test_kv_delete_success(): client = _client() try: result = client.v1.kv.delete("test-key") - assert result is None + assert isinstance(result, BaseModel) + assert type(result).__name__ == "KvDeleteResponse" finally: client.close() @@ -307,7 +350,8 @@ async def test_async_kv_delete_success(): client = _async_client() try: result = await client.v1.kv.delete("test-key") - assert result is None + assert isinstance(result, BaseModel) + assert type(result).__name__ == "KvDeleteResponse" finally: await client.close() diff --git a/tests/contract/v1/test_orgs.py b/tests/contract/v1/test_orgs.py index 149ca59..bed5ad6 100644 --- a/tests/contract/v1/test_orgs.py +++ b/tests/contract/v1/test_orgs.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 4eed66ef003a +# Content hash: 1ca1483446d1 import pytest from pydantic import BaseModel @@ -301,6 +301,773 @@ async def test_async_orgs_linked_accounts_error_404(): await ec.close() +def test_orgs_task_epics_success(): + client = _client() + try: + result = client.v1.orgs.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_task_epics_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_task_epics_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_task_epics_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_task_epics_success(): + client = _async_client() + try: + result = await client.v1.orgs.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_task_epics_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_task_epics_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_task_epics_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_list_success(): + client = _client() + try: + result = client.v1.orgs.tasks.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskListResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_tasks_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_list_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_list_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_list_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskListResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_list_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_list_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.list("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_create_success(): + client = _client() + try: + result = client.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Task" + finally: + client.close() + + +def test_orgs_tasks_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_create_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_orgs_tasks_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_create_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Task" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_create_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_blocker_cycles_success(): + client = _client() + try: + result = client.v1.orgs.tasks.blocker_cycles("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskBlockerCyclesResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_tasks_blocker_cycles_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_blocker_cycles_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_blocker_cycles_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_blocker_cycles_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.blocker_cycles("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskBlockerCyclesResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_blocker_cycles_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_blocker_cycles_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_blocker_cycles_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.blocker_cycles("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_by_source_success(): + client = _client() + try: + result = client.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + client.close() + + +def test_orgs_tasks_by_source_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_by_source_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_by_source_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_by_source_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_by_source_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_by_source_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_by_source_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_metrics_success(): + client = _client() + try: + result = client.v1.orgs.tasks.metrics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskMetricsResponse" + finally: + client.close() + + +def test_orgs_tasks_metrics_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_metrics_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_metrics_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_metrics_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.metrics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskMetricsResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_metrics_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_metrics_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_metrics_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.metrics("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_ready_success(): + client = _client() + try: + result = client.v1.orgs.tasks.ready("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskReadyResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_tasks_ready_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_ready_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_ready_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_ready_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.ready("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskReadyResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_ready_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_ready_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_ready_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.ready("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_orgs_tasks_search_success(): + client = _client() + try: + result = client.v1.orgs.tasks.search("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskSearchResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_tasks_search_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_tasks_search_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_orgs_tasks_search_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_search_success(): + client = _async_client() + try: + result = await client.v1.orgs.tasks.search("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgTaskSearchResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_search_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_search_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_tasks_search_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.tasks.search("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_orgs_threads_list_success(): client = _client() try: diff --git a/tests/contract/v1/test_private_service_enrollments.py b/tests/contract/v1/test_private_service_enrollments.py index 50e73a5..cfe6047 100644 --- a/tests/contract/v1/test_private_service_enrollments.py +++ b/tests/contract/v1/test_private_service_enrollments.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: f131f37d40d8 +# Content hash: 84144299e188 import pytest from pydantic import BaseModel @@ -276,6 +276,109 @@ async def test_async_private_service_enrollments_create_error_503(): await ec.close() +def test_private_service_enrollments_delete_success(): + client = _client() + try: + result = client.v1.private_service_enrollments.delete("test-id") + assert result is None + finally: + client.close() + + +def test_private_service_enrollments_delete_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 400 + finally: + ec.close() + + +def test_private_service_enrollments_delete_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_private_service_enrollments_delete_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_private_service_enrollments_delete_error_503(): + ec = _error_client(503) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 503 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_private_service_enrollments_delete_success(): + client = _async_client() + try: + result = await client.v1.private_service_enrollments.delete("test-id") + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_private_service_enrollments_delete_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 400 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_private_service_enrollments_delete_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_private_service_enrollments_delete_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_private_service_enrollments_delete_error_503(): + ec = _async_error_client(503) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.private_service_enrollments.delete("test-id") + assert exc_info.value.status == 503 + finally: + await ec.close() + + def test_private_service_enrollments_get_success(): client = _client() try: diff --git a/tests/contract/v1/test_relays.py b/tests/contract/v1/test_relays.py new file mode 100644 index 0000000..92034cb --- /dev/null +++ b/tests/contract/v1/test_relays.py @@ -0,0 +1,379 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: eda2e1b20281 + +import pytest +from pydantic import BaseModel + +from archastro.platform import AsyncPlatformClient, PlatformClient +from archastro.platform.runtime.http_client import ApiError + +PRISM_URL = "http://127.0.0.1:4040" + + +def _client() -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _error_client(code: int) -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def _async_client() -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _async_error_client(code: int) -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def test_relays_create_success(): + client = _client() + try: + result = client.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + client.close() + + +def test_relays_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_relays_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_relays_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_create_success(): + client = _async_client() + try: + result = await client.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_relays_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.create({"purpose": "test-value", "ttl_seconds": 1}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_relays_delete_success(): + client = _client() + try: + result = client.v1.relays.delete("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + client.close() + + +def test_relays_delete_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.delete("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_relays_delete_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.delete("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_relays_delete_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.delete("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_delete_success(): + client = _async_client() + try: + result = await client.v1.relays.delete("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_relays_delete_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.delete("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_delete_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.delete("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_delete_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.delete("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_relays_get_success(): + client = _client() + try: + result = client.v1.relays.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + client.close() + + +def test_relays_get_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.get("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_relays_get_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.get("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_relays_get_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.get("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_get_success(): + client = _async_client() + try: + result = await client.v1.relays.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_relays_get_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.get("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_get_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.get("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_get_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.get("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_relays_extend_success(): + client = _client() + try: + result = client.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + client.close() + + +def test_relays_extend_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_relays_extend_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_relays_extend_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_extend_success(): + client = _async_client() + try: + result = await client.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Relay" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_relays_extend_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_extend_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_relays_extend_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.relays.extend("test-value", {"ttl_seconds": 1}) + assert exc_info.value.status == 422 + finally: + await ec.close() diff --git a/tests/contract/v1/test_task_ownership_conversions.py b/tests/contract/v1/test_task_ownership_conversions.py new file mode 100644 index 0000000..7cfee5a --- /dev/null +++ b/tests/contract/v1/test_task_ownership_conversions.py @@ -0,0 +1,148 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 79597200fe7c + +import pytest +from pydantic import BaseModel + +from archastro.platform import AsyncPlatformClient, PlatformClient +from archastro.platform.runtime.http_client import ApiError + +PRISM_URL = "http://127.0.0.1:4040" + + +def _client() -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _error_client(code: int) -> PlatformClient: + return PlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def _async_client() -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key"}, + access_token="test-token", + ) + + +def _async_error_client(code: int) -> AsyncPlatformClient: + return AsyncPlatformClient( + base_url=PRISM_URL, + default_headers={"x-archastro-api-key": "pk_test-key", "Prefer": f"code={code}"}, + access_token="test-token", + ) + + +def test_task_ownership_conversions_get_success(): + client = _client() + try: + result = client.v1.task_ownership_conversions.get(1) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskOwnershipJob" + finally: + client.close() + + +def test_task_ownership_conversions_get_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_task_ownership_conversions_get_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_task_ownership_conversions_get_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_task_ownership_conversions_get_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_task_ownership_conversions_get_success(): + client = _async_client() + try: + result = await client.v1.task_ownership_conversions.get(1) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskOwnershipJob" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_task_ownership_conversions_get_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_task_ownership_conversions_get_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_task_ownership_conversions_get_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_task_ownership_conversions_get_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.task_ownership_conversions.get(1) + assert exc_info.value.status == 422 + finally: + await ec.close() diff --git a/tests/contract/v1/test_tasks.py b/tests/contract/v1/test_tasks.py index 2d94993..ce84eaa 100644 --- a/tests/contract/v1/test_tasks.py +++ b/tests/contract/v1/test_tasks.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: bd53049ebaa4 +# Content hash: e27df6d7e919 import pytest from pydantic import BaseModel @@ -528,6 +528,132 @@ async def test_async_tasks_blocking_error_422(): await ec.close() +def test_tasks_ownership_conversions_success(): + client = _client() + try: + result = client.v1.tasks.ownership_conversions("test-value", {}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskOwnershipJob" + finally: + client.close() + + +def test_tasks_ownership_conversions_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_tasks_ownership_conversions_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_tasks_ownership_conversions_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_tasks_ownership_conversions_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_tasks_ownership_conversions_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_success(): + client = _async_client() + try: + result = await client.v1.tasks.ownership_conversions("test-value", {}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskOwnershipJob" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_ownership_conversions_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.ownership_conversions("test-value", {}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_tasks_subtasks_success(): client = _client() try: @@ -614,6 +740,132 @@ async def test_async_tasks_subtasks_error_422(): await ec.close() +def test_tasks_transfer_ownership_success(): + client = _client() + try: + result = client.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Task" + finally: + client.close() + + +def test_tasks_transfer_ownership_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_tasks_transfer_ownership_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_tasks_transfer_ownership_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_tasks_transfer_ownership_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_tasks_transfer_ownership_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_success(): + client = _async_client() + try: + result = await client.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Task" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_transfer_ownership_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.transfer_ownership("test-value", {"expected_version": 1}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_tasks_blockers_list_success(): client = _client() try: diff --git a/tests/contract/v1/test_teams.py b/tests/contract/v1/test_teams.py index 83b2f13..2c71a82 100644 --- a/tests/contract/v1/test_teams.py +++ b/tests/contract/v1/test_teams.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: f9b2f9040855 +# Content hash: 221a750640f9 import pytest from pydantic import BaseModel @@ -1114,6 +1114,92 @@ async def test_async_teams_task_assignees_error_422(): await ec.close() +def test_teams_task_epics_success(): + client = _client() + try: + result = client.v1.teams.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TeamTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_teams_task_epics_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_teams_task_epics_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_teams_task_epics_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_task_epics_success(): + client = _async_client() + try: + result = await client.v1.teams.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TeamTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_teams_task_epics_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_task_epics_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_task_epics_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_teams_custom_objects_list_success(): client = _client() try: @@ -2107,6 +2193,154 @@ async def test_async_teams_tasks_blocker_cycles_error_422(): await ec.close() +def test_teams_tasks_by_source_success(): + client = _client() + try: + result = client.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + client.close() + + +def test_teams_tasks_by_source_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_teams_tasks_by_source_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_teams_tasks_by_source_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_tasks_by_source_success(): + client = _async_client() + try: + result = await client.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_teams_tasks_by_source_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_tasks_by_source_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_tasks_by_source_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_teams_tasks_metrics_success(): client = _client() try: diff --git a/tests/contract/v1/test_threads.py b/tests/contract/v1/test_threads.py index 4d062ae..77edb95 100644 --- a/tests/contract/v1/test_threads.py +++ b/tests/contract/v1/test_threads.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 65b65fcc98bb +# Content hash: e926dfc73044 import pytest from pydantic import BaseModel @@ -1250,6 +1250,174 @@ async def test_async_threads_trajectories_error_404(): await ec.close() +def test_threads_computers_list_success(): + client = _client() + try: + result = client.v1.threads.computers.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentComputerListResponse" + finally: + client.close() + + +def test_threads_computers_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.list("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_threads_computers_list_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.list("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_list_success(): + client = _async_client() + try: + result = await client.v1.threads.computers.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentComputerListResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.list("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_list_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.list("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +def test_threads_computers_create_success(): + client = _client() + try: + result = client.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentComputer" + finally: + client.close() + + +def test_threads_computers_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_threads_computers_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_threads_computers_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_threads_computers_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_create_success(): + client = _async_client() + try: + result = await client.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "AgentComputer" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_threads_computers_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.computers.create("test-value", {"name": "test-name"}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_threads_members_remove_success(): client = _client() try: diff --git a/tests/contract/v1/test_users.py b/tests/contract/v1/test_users.py index 2474673..87441c8 100644 --- a/tests/contract/v1/test_users.py +++ b/tests/contract/v1/test_users.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 4f47fc4db593 +# Content hash: af103bbf2002 import pytest from pydantic import BaseModel @@ -509,6 +509,92 @@ async def test_async_users_profile_error_422(): await ec.close() +def test_users_task_epics_success(): + client = _client() + try: + result = client.v1.users.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_users_task_epics_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_users_task_epics_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_users_task_epics_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_users_task_epics_success(): + client = _async_client() + try: + result = await client.v1.users.task_epics("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserTaskEpicsResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_users_task_epics_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_task_epics_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_task_epics_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.task_epics("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_users_integrations_access_token_success(): client = _client() try: @@ -1119,6 +1205,154 @@ async def test_async_users_tasks_blocker_cycles_error_422(): await ec.close() +def test_users_tasks_by_source_success(): + client = _client() + try: + result = client.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + client.close() + + +def test_users_tasks_by_source_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_users_tasks_by_source_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_users_tasks_by_source_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_users_tasks_by_source_success(): + client = _async_client() + try: + result = await client.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskEnsureResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_users_tasks_by_source_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_tasks_by_source_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_tasks_by_source_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.tasks.by_source( + "test-value", + { + "name": "test-name", + "source_id": "test-id", + "source_scope": "test-value", + "source_type": "test-value", + }, + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_users_tasks_ready_success(): client = _client() try: diff --git a/tests/contract/v1/test_workflows.py b/tests/contract/v1/test_workflows.py index 3b21900..043e706 100644 --- a/tests/contract/v1/test_workflows.py +++ b/tests/contract/v1/test_workflows.py @@ -1,6 +1,6 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: fe6f239f05a7 +# Content hash: 1d53f33c67e4 import pytest from pydantic import BaseModel @@ -147,6 +147,26 @@ def test_workflows_run_error_401(): ec.close() +def test_workflows_run_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.run({}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_workflows_run_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.run({}) + assert exc_info.value.status == 404 + finally: + ec.close() + + def test_workflows_run_error_422(): ec = _error_client(422) try: @@ -179,6 +199,28 @@ async def test_async_workflows_run_error_401(): await ec.close() +@pytest.mark.asyncio +async def test_async_workflows_run_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.run({}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_run_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.run({}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_workflows_run_error_422(): ec = _async_error_client(422)