diff --git a/package-lock.json b/package-lock.json index 9ac3384..0a9f445 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,9 +55,9 @@ } }, "node_modules/@archastro/sdk-generator": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@archastro/sdk-generator/-/sdk-generator-0.11.3.tgz", - "integrity": "sha512-IORFdZgTbdPs2ec+gy+Y7bNFawAqDbpltt4VXmB7sBN7mtYUTj8/XPgqQd7UZfgewn1T0Wyf6dmMF4PgPZyFfg==", + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@archastro/sdk-generator/-/sdk-generator-0.11.9.tgz", + "integrity": "sha512-Ev80mFFnoaD6Dj8bjxL9sJsXLlNuXMvCZlTFfWRS6/JLOd4VTEOP9SEUQ0TGDXhM8vpQRSqkcXFvsQ9rwFSLwA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/specs/platform-openapi.json b/specs/platform-openapi.json index 4f8d6be..a888e59 100644 --- a/specs/platform-openapi.json +++ b/specs/platform-openapi.json @@ -287,10 +287,12 @@ "image_url": "https://example.com", "model": "dall-e-3", "revised_prompt": "A photorealistic image of a sunset over the ocean with warm golden hues.", + "session_id": "string", "size": "1024x1024", "usage": { "key": "value" }, + "usage_attempt_count": 1, "width": 1024 }, "properties": { @@ -334,6 +336,11 @@ "example": "A photorealistic image of a sunset over the ocean with warm golden hues.", "type": "string" }, + "session_id": { + "description": "UUID grouping this image request's provider attempts for usage and billing.", + "example": "string", + "type": "string" + }, "size": { "description": "Canonical size string as returned by the provider, e.g. `\"1024x1024\"`. `null` when not reported.", "example": "1024x1024", @@ -346,6 +353,11 @@ }, "type": "object" }, + "usage_attempt_count": { + "description": "Number of provider responses contributing usage records to this request, including image-generation retries.", + "example": 1, + "type": "integer" + }, "width": { "description": "Width of the generated image in pixels. `null` when the provider does not report dimensions.", "example": 1024, @@ -713,6 +725,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -771,6 +786,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -1117,6 +1135,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -1175,6 +1196,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -1249,6 +1273,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -1331,11 +1358,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -1760,6 +1797,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -1775,6 +1813,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -1857,11 +1898,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -2286,6 +2337,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -2840,6 +2892,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -2898,6 +2953,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -3112,6 +3170,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -3170,6 +3231,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -3821,6 +3885,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -3879,6 +3946,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -4247,6 +4317,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -4305,6 +4378,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -5452,6 +5528,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -5510,6 +5589,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -5615,6 +5697,11 @@ "name_prefix": "string", "parameters": {}, "parameters_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", + "setup": { + "next_action": "string", + "protocol_status": "not_checked", + "reason": "string" + }, "status": "active", "updated_at": "2024-01-01T00:00:00Z" }, @@ -5717,8 +5804,22 @@ "example": "cfg_0aBcDeFgHiJkLmNoPqRsTu", "type": "string" }, + "setup": { + "allOf": [ + { + "$ref": "#/components/schemas/Setup" + } + ], + "description": "Current MCP setup assessment, not a historical downgrade reason or upstream protocol probe.", + "example": { + "next_action": "string", + "protocol_status": "not_checked", + "reason": "string" + }, + "nullable": true + }, "status": { - "description": "Current status of the tool. One of `\"active\"` or `\"disabled\"`.", + "description": "Current status of the tool. One of `\"active\"` or `\"draft\"`.", "example": "active", "type": "string" }, @@ -5760,6 +5861,11 @@ "name_prefix": "string", "parameters": {}, "parameters_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", + "setup": { + "next_action": "string", + "protocol_status": "not_checked", + "reason": "string" + }, "status": "active", "updated_at": "2024-01-01T00:00:00Z" } @@ -5856,12 +5962,12 @@ "description": "Summary of the parent AgentTemplate config (`cfg_...`) being applied in this upgrade." }, "resource": { - "description": "Resource-type-specific identity details. Tools: `tool_type`, `builtin_tool_key`, `name_prefix`, `handler_type`, `instruction`. Routines: `handler_type`, `preset_name`, `event_type`, `schedule`, `trigger_context`. Skills: `instruction`. Computers: `region`. Only populated keys are present; `null` when nothing is known.", + "description": "Resource-type-specific identity details. Tools: `tool_type`, `builtin_tool_key`, `name_prefix`, `handler_type`, `instruction`. Routines: `handler_type`, `preset_name`, `event_type`, `schedule`, `trigger_context`. Skills: `instruction`. Computers: `region`. Knowledge: `mode`, `knowledge_key`, `extraction_output_ref`. Only populated keys are present; `null` when nothing is known.", "example": {}, "type": "object" }, "resource_type": { - "description": "Type of the child resource being changed. One of `\"agent\"`, `\"tool\"`, `\"routine\"`, `\"skill\"`, or `\"computer\"`.", + "description": "Type of the resource being changed. One of `\"agent\"`, `\"tool\"`, `\"routine\"`, `\"skill\"`, `\"computer\"`, or `\"knowledge\"`.", "example": "tool", "type": "string" }, @@ -5969,6 +6075,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -6027,6 +6136,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -6102,6 +6214,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -6383,6 +6498,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -6396,6 +6512,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -6444,6 +6561,12 @@ "example": "https://example.com", "type": "string" }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + "example": "string", + "nullable": true, + "type": "string" + }, "id": { "description": "Artifact ID (`art_...`).", "example": "art_0aBcDeFgHiJkLmNoPqRsTu", @@ -6468,6 +6591,11 @@ "example": "string", "type": "string" }, + "system": { + "description": "True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + "example": true, + "type": "boolean" + }, "team": { "description": "ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -6680,6 +6808,7 @@ "AuthTokens": { "description": "Credential bundle returned after a successful authentication exchange. Contains the access token, refresh token, and the authenticated user.", "example": { + "account_created": false, "expires_in": 3600, "metadata": { "key": "value" @@ -6712,6 +6841,11 @@ } }, "properties": { + "account_created": { + "description": "`true` when this request created the account (the same moment the platform records the signup), `false` for a returning sign-in or a token refresh. Registration always returns `true`; password login and refresh always return `false`.", + "example": false, + "type": "boolean" + }, "expires_in": { "description": "Number of seconds until `token` expires. After this period, use `refresh_token` to obtain a new access token.", "example": 3600, @@ -6752,7 +6886,8 @@ "refresh_token", "user", "token_type", - "expires_in" + "expires_in", + "account_created" ], "type": "object" }, @@ -7096,6 +7231,38 @@ ], "type": "object" }, + "Cancelled": { + "description": "A cancelled local-tool outcome.", + "example": { + "call_id": "string", + "reason": "string", + "status": "cancelled" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + }, + "status": { + "default": "cancelled", + "enum": [ + "cancelled" + ], + "example": "cancelled", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "reason" + ], + "type": "object" + }, "ChannelAck": { "description": "Empty acknowledgement payload returned by channel message handlers that produce no data. The wire envelope is `{\"status\": \"ok\", \"response\": {}}`.", "properties": {}, @@ -7163,6 +7330,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -7221,6 +7391,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -7348,6 +7521,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -7406,6 +7582,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -7601,6 +7780,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -7803,6 +7990,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -7927,6 +8122,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -7985,6 +8183,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8239,6 +8440,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -8363,6 +8572,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8421,6 +8633,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8591,6 +8806,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8649,6 +8867,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8776,6 +8997,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -8834,6 +9058,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -9029,6 +9256,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -9231,6 +9466,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -9355,6 +9598,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -9413,6 +9659,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -9514,8 +9763,36 @@ ], "type": "object" }, + "ChatLocalToolCall": { + "description": "One local function invocation requested by the model.", + "example": { + "arguments": {}, + "id": "string", + "name": "Example Name" + }, + "properties": { + "arguments": { + "example": {}, + "type": "object" + }, + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "id", + "name", + "arguments" + ], + "type": "object" + }, "ChatLocalToolDefinition": { - "description": "An OpenAI-compatible local function definition supplied while joining a personal thread.", + "description": "An OpenAI-compatible local function definition advertised by a personal-thread connection.", "example": { "function": { "description": "An example description.", @@ -9570,6 +9847,118 @@ ], "type": "object" }, + "ChatLocalToolProvider": { + "description": "Server-issued identity for the local-tool set currently advertised by this connection.", + "example": { + "generation": 1, + "provider_id": "string" + }, + "properties": { + "generation": { + "example": 1, + "type": "integer" + }, + "provider_id": { + "example": "string", + "type": "string" + } + }, + "required": [ + "provider_id", + "generation" + ], + "type": "object" + }, + "ChatLocalToolRequest": { + "description": "A correlated batch of local function calls targeted to one connection.", + "example": { + "calls": [ + { + "arguments": {}, + "id": "string", + "name": "Example Name" + } + ], + "id": "string" + }, + "properties": { + "calls": { + "items": { + "$ref": "#/components/schemas/ChatLocalToolCall" + }, + "type": "array" + }, + "id": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "calls" + ], + "type": "object" + }, + "ChatLocalToolResult": { + "description": "One terminal local-tool outcome returned by the owning client connection.", + "discriminator": { + "mapping": { + "cancelled": "#/components/schemas/Cancelled", + "error": "#/components/schemas/ChatLocalToolResultError", + "ok": "#/components/schemas/Ok" + }, + "propertyName": "status" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/Ok" + }, + { + "$ref": "#/components/schemas/ChatLocalToolResultError" + }, + { + "$ref": "#/components/schemas/Cancelled" + } + ] + }, + "ChatLocalToolResultError": { + "description": "A failed local-tool outcome.", + "example": { + "call_id": "string", + "code": "string", + "message": "string", + "status": "error" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "code": { + "example": "string", + "type": "string" + }, + "message": { + "example": "string", + "type": "string" + }, + "status": { + "default": "error", + "enum": [ + "error" + ], + "example": "error", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "code", + "message" + ], + "type": "object" + }, "ChatMarkThreadReadResponse": { "description": "Response returned after marking a chat thread as read. Confirms that the read marker was successfully recorded for the authenticated user.", "example": { @@ -9647,6 +10036,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -9705,6 +10097,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -9927,6 +10322,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -10082,6 +10485,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -10190,6 +10601,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -10248,6 +10662,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -10375,6 +10792,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -10433,6 +10853,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -10628,6 +11051,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -10830,6 +11261,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -10954,6 +11393,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -11012,6 +11454,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -11157,6 +11602,55 @@ ], "type": "object" }, + "Command": { + "description": "A workflow command that a workflow node can invoke, including its identifier, human-readable name, and JSON schemas for its inputs and outputs.", + "example": { + "description": "An example description.", + "errorOutput": {}, + "expectedInput": {}, + "id": "send_email", + "name": "Example Name", + "successOutput": {} + }, + "properties": { + "description": { + "description": "Human-readable description of what the command does, shown alongside the name in the workflow builder.", + "example": "An example description.", + "type": "string" + }, + "errorOutput": { + "description": "JSON Schema describing the shape of an error output. `null` if the command does not produce structured error data.", + "example": {}, + "type": "object" + }, + "expectedInput": { + "description": "JSON Schema describing the input the command expects. `null` if the command takes no input.", + "example": {}, + "type": "object" + }, + "id": { + "description": "Unique string identifier for the command, used to reference it in workflow node configurations.", + "example": "send_email", + "type": "string" + }, + "name": { + "description": "Human-readable display name for the command shown in the workflow builder.", + "example": "Example Name", + "type": "string" + }, + "successOutput": { + "description": "JSON Schema describing the shape of a successful output. `null` if the command produces no output on success.", + "example": {}, + "type": "object" + } + }, + "required": [ + "id", + "name", + "description" + ], + "type": "object" + }, "ComputerExecResult": { "description": "The result of executing a shell command on an agent's computer environment. Contains the captured output and the process exit code.", "example": { @@ -11816,6 +12310,199 @@ ], "type": "object" }, + "CurrentUser": { + "description": "The authenticated user's profile, live session context, and optional effective access.", + "example": { + "alias": "string", + "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", + "app_name": "Example Name", + "effective_access": { + "billing": [ + { + "administrator": true, + "principal_type": "user", + "trials": [ + { + "ends_at": "2024-01-01T00:00:00Z", + "payment_method_stored": true, + "plan": "string" + } + ] + } + ], + "entitlements": [ + { + "granted": true, + "key": "string", + "provided_by": [ + "personal" + ], + "value": true, + "value_type": "boolean" + } + ] + }, + "email": "user@example.com", + "id": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "is_system_user": true, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "notification_settings": {}, + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_kind": "company", + "org_name": "Example Name", + "org_role": "string", + "org_slug": "example-slug", + "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 + }, + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "sandbox_name": "Example Name", + "timezone": "America/New_York" + }, + "properties": { + "alias": { + "description": "Short handle or alias of the user.", + "example": "string", + "nullable": true, + "type": "string" + }, + "app": { + "description": "ID of the token-scoped app (`dap_...`).", + "example": "dap_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "app_name": { + "description": "Display name of the token-scoped app.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "effective_access": { + "$ref": "#/components/schemas/EffectiveAccess", + "description": "Present only when the request supplies one or more `entitlement[]` values." + }, + "email": { + "description": "Email address of the user.", + "example": "user@example.com", + "nullable": true, + "type": "string" + }, + "id": { + "description": "User ID (`usr_...`).", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "is_system_user": { + "description": "Whether the authenticated account is an internal system user.", + "example": true, + "type": "boolean" + }, + "metadata": { + "description": "Arbitrary user metadata.", + "example": { + "key": "value" + }, + "type": "object" + }, + "name": { + "description": "Full display name of the user.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "notification_settings": { + "description": "The authenticated user's notification preferences.", + "example": {}, + "type": "object" + }, + "org": { + "description": "ID of the current organization (`org_...`).", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "org_kind": { + "description": "Whether the current organization is company-domain or person-owned.", + "enum": [ + "company", + "personal" + ], + "example": "company", + "nullable": true, + "type": "string" + }, + "org_name": { + "description": "Display name of the current organization.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "org_role": { + "description": "The authenticated user's role in the current organization.", + "example": "string", + "nullable": true, + "type": "string" + }, + "org_slug": { + "description": "Stable slug of the current organization.", + "example": "example-slug", + "nullable": true, + "type": "string" + }, + "profile_picture": { + "allOf": [ + { + "$ref": "#/components/schemas/ImageSource" + } + ], + "description": "Resolved profile picture metadata.", + "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 + }, + "sandbox": { + "description": "ID of the current sandbox (`sbx_...`).", + "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "nullable": true, + "type": "string" + }, + "sandbox_name": { + "description": "Display name of the current sandbox.", + "example": "Example Name", + "nullable": true, + "type": "string" + }, + "timezone": { + "description": "The user's IANA timezone.", + "example": "America/New_York", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "notification_settings", + "is_system_user" + ], + "type": "object" + }, "CustomObject": { "description": "A custom object belonging to an organization. Custom objects store arbitrary structured data defined by a schema type and are scoped to an org, team, or user.", "example": { @@ -12159,6 +12846,49 @@ ], "type": "object" }, + "D1Database": { + "description": "Provider object fields for a D1 database.", + "example": { + "id": "string", + "jurisdiction": "string", + "name": "Example Name", + "read_replication": {}, + "type": "d1_database" + }, + "properties": { + "id": { + "description": "Provider-assigned object identifier.", + "example": "string", + "type": "string" + }, + "jurisdiction": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "read_replication": { + "example": {}, + "type": "object" + }, + "type": { + "default": "d1_database", + "enum": [ + "d1_database" + ], + "example": "d1_database", + "type": "string" + } + }, + "required": [ + "type", + "id", + "name" + ], + "type": "object" + }, "Deployment": { "description": "Deployment metadata.", "example": { @@ -12181,6 +12911,24 @@ }, "type": "object" }, + "Details": { + "description": "Service-specific external object payload selected by `type`.", + "discriminator": { + "mapping": { + "d1_database": "#/components/schemas/D1Database", + "r2_bucket": "#/components/schemas/R2Bucket" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/R2Bucket" + }, + { + "$ref": "#/components/schemas/D1Database" + } + ] + }, "DeviceAuthorizationDetailsResponse": { "description": "User-visible details for a pending OAuth 2.0 device authorization.", "example": { @@ -12355,6 +13103,189 @@ ], "type": "object" }, + "EffectiveAccess": { + "description": "Live effective access, billing authority, and relevant trials for the authenticated user.", + "example": { + "billing": [ + { + "administrator": true, + "principal_type": "user", + "trials": [ + { + "ends_at": "2024-01-01T00:00:00Z", + "payment_method_stored": true, + "plan": "string" + } + ] + } + ], + "entitlements": [ + { + "granted": true, + "key": "string", + "provided_by": [ + "personal" + ], + "value": true, + "value_type": "boolean" + } + ] + }, + "properties": { + "billing": { + "description": "Administration authority and requested-entitlement-relevant trials for the personal and current-organization principals.", + "items": { + "$ref": "#/components/schemas/EffectiveAccessBillingPrincipal" + }, + "type": "array" + }, + "entitlements": { + "description": "Requested catalog entitlements in request order.", + "items": { + "$ref": "#/components/schemas/EffectiveAccessEntitlement" + }, + "type": "array" + } + }, + "required": [ + "entitlements", + "billing" + ], + "type": "object" + }, + "EffectiveAccessBillingPrincipal": { + "description": "Billing authority and relevant active trials for one Viewer-derived principal kind.", + "example": { + "administrator": true, + "principal_type": "user", + "trials": [ + { + "ends_at": "2024-01-01T00:00:00Z", + "payment_method_stored": true, + "plan": "string" + } + ] + }, + "properties": { + "administrator": { + "description": "Whether the authenticated user may administer this principal's billing account.", + "example": true, + "type": "boolean" + }, + "principal_type": { + "enum": [ + "user", + "org" + ], + "example": "user", + "type": "string" + }, + "trials": { + "description": "Currently active trials for this Viewer-derived principal. Contains no billing-account or provider identifiers.", + "items": { + "$ref": "#/components/schemas/EffectiveAccessBillingTrial" + }, + "type": "array" + } + }, + "required": [ + "principal_type", + "administrator", + "trials" + ], + "type": "object" + }, + "EffectiveAccessBillingTrial": { + "description": "Privacy-safe current trial state for one Viewer-derived billing principal.", + "example": { + "ends_at": "2024-01-01T00:00:00Z", + "payment_method_stored": true, + "plan": "string" + }, + "properties": { + "ends_at": { + "description": "Effective trial end: the earlier of its accepted boundary and enrollment end.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "payment_method_stored": { + "description": "Whether a provider webhook has confirmed a stored payment method.", + "example": true, + "type": "boolean" + }, + "plan": { + "description": "Catalog plan whose trial is active.", + "example": "string", + "type": "string" + } + }, + "required": [ + "plan", + "ends_at", + "payment_method_stored" + ], + "type": "object" + }, + "EffectiveAccessEntitlement": { + "description": "A billing-derived effective entitlement for the authenticated user.", + "example": { + "granted": true, + "key": "string", + "provided_by": [ + "personal" + ], + "value": true, + "value_type": "boolean" + }, + "properties": { + "granted": { + "description": "Whether any eligible source grants the key.", + "example": true, + "type": "boolean" + }, + "key": { + "description": "Stable entitlement catalog key.", + "example": "string", + "type": "string" + }, + "provided_by": { + "description": "Privacy-safe principal kinds that contribute to the effective value.", + "example": [ + "personal" + ], + "items": { + "enum": [ + "personal", + "organization" + ], + "type": "string" + }, + "type": "array" + }, + "value": { + "description": "Merged entitlement value, or `null` when the key is not granted.", + "example": true, + "nullable": true, + "type": "boolean" + }, + "value_type": { + "description": "Catalog type used to interpret `value`.", + "enum": [ + "boolean" + ], + "example": "boolean", + "type": "string" + } + }, + "required": [ + "key", + "granted", + "value_type", + "provided_by" + ], + "type": "object" + }, "EventSubscription": { "description": "An app-scoped subscription to exact domain-event names.", "example": { @@ -12841,6 +13772,182 @@ ], "type": "object" }, + "ExpressionResult": { + "description": "Schema for the result of evaluating an expression", + "example": { + "output": [ + {} + ], + "result": 42 + }, + "properties": { + "output": { + "default": [], + "description": "Output lines from println calls, each with :line (source line number), :text, and optional :values (JSON-serializable structured data)", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "result": { + "description": "The evaluated JSON-safe result", + "example": 42 + } + }, + "required": [ + "result" + ], + "type": "object" + }, + "ExpressionValidation": { + "description": "Schema for expression validation responses", + "example": { + "error": "Unexpected token at line 3", + "findings": [ + {} + ], + "ok": true, + "symbols": [ + {} + ], + "valid": true, + "warnings": [ + "string" + ] + }, + "properties": { + "error": { + "description": "Validation error message", + "example": "Unexpected token at line 3", + "type": "string" + }, + "findings": { + "description": "Structured findings with position info for editor diagnostics", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "ok": { + "description": "Whether the validation call succeeded", + "example": true, + "type": "boolean" + }, + "symbols": { + "description": "Inferred symbol types at source positions (name, type, line, column)", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "valid": { + "description": "Whether the expression is valid", + "example": true, + "type": "boolean" + }, + "warnings": { + "description": "Semantic analysis warnings (non-blocking)", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, + "ExternalObject": { + "description": "An owner-scoped object provisioned through an external integration.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "id": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "integration": "int_0aBcDeFgHiJkLmNoPqRsTu", + "object": { + "id": "b5f2c1d8e9a04736", + "name": "agent-assets", + "type": "r2_bucket" + }, + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "service": "cloudflare", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "created_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "ArchAstro external object ID (`ext_...`).", + "example": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "integration": { + "description": "Integration ID (`int_...`).", + "example": "int_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "object": { + "$ref": "#/components/schemas/Details", + "description": "Service-specific object payload." + }, + "org": { + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "service": { + "default": "cloudflare", + "enum": [ + "cloudflare" + ], + "example": "cloudflare", + "type": "string" + }, + "team": { + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "updated_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + } + }, + "required": [ + "id", + "integration", + "service", + "object", + "created_at", + "updated_at" + ], + "type": "object" + }, "Extraction": { "description": "An extraction job: yields text from a document or website into a destination namespace, without committing knowledge to an agent.", "example": { @@ -13064,6 +14171,48 @@ ], "type": "object" }, + "GraphValidation": { + "description": "Schema for graph validation responses", + "example": { + "error": "Cycle detected between nodes 'node_1' and 'node_3'", + "findings": [ + {} + ], + "ok": true, + "valid": true + }, + "properties": { + "error": { + "description": "Error message if graph construction or validation failed", + "example": "Cycle detected between nodes 'node_1' and 'node_3'", + "type": "string" + }, + "findings": { + "description": "Structured findings from static analysis (unreachable nodes, dead-ends, cycles)", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "ok": { + "description": "Whether graph construction succeeded", + "example": true, + "type": "boolean" + }, + "valid": { + "description": "Whether the graph passed structural validation and analysis", + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, "HealthActionListResponse": { "description": "List response containing agent health actions for a given agent or organization.", "example": { @@ -14123,6 +15272,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "string", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -14250,6 +15407,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -14373,6 +15538,13 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "items": { + "$ref": "#/components/schemas/MessageContext" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -14561,6 +15733,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -14798,6 +15978,43 @@ ], "type": "object" }, + "MessageContext": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, "MessagePolicy": { "description": "Controls visibility and canonical recipient selection for routine-emitted messages.\n", "example": { @@ -14860,6 +16077,148 @@ ], "type": "object" }, + "NodeField": { + "description": "Schema for a field definition within a node type", + "example": { + "allowExpression": true, + "description": "An example description.", + "key": "prompt", + "label": "Prompt", + "options": [ + {} + ], + "required": true, + "type": "string" + }, + "properties": { + "allowExpression": { + "description": "Whether the field accepts expressions", + "example": true, + "type": "boolean" + }, + "defaultValue": { + "description": "Default JSON-safe value for the field" + }, + "description": { + "description": "Field description", + "example": "An example description.", + "type": "string" + }, + "key": { + "description": "Field identifier", + "example": "prompt", + "type": "string" + }, + "label": { + "description": "Display label", + "example": "Prompt", + "type": "string" + }, + "options": { + "description": "Available options for select fields", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "required": { + "default": false, + "description": "Whether the field is required", + "example": true, + "type": "boolean" + }, + "type": { + "description": "Field type (string, number, boolean, expression, etc.)", + "example": "string", + "type": "string" + } + }, + "required": [ + "key", + "label", + "type" + ], + "type": "object" + }, + "NodeType": { + "description": "Schema for a workflow node type definition", + "example": { + "badge": "HTTP", + "category": "Logic", + "color": "#2563EB", + "description": "An example description.", + "docs": "string", + "fields": [ + { + "allowExpression": true, + "description": "An example description.", + "key": "prompt", + "label": "Prompt", + "options": [ + {} + ], + "required": true, + "type": "string" + } + ], + "id": "http_request", + "label": "HTTP Request" + }, + "properties": { + "badge": { + "description": "Short badge text for UI", + "example": "HTTP", + "type": "string" + }, + "category": { + "description": "Category for grouping (e.g., Logic, Network, Utility)", + "example": "Logic", + "type": "string" + }, + "color": { + "description": "Hex color for UI display", + "example": "#2563EB", + "type": "string" + }, + "description": { + "description": "Node description", + "example": "An example description.", + "type": "string" + }, + "docs": { + "description": "Extended documentation", + "example": "string", + "type": "string" + }, + "fields": { + "description": "Field definitions for this node type", + "items": { + "$ref": "#/components/schemas/NodeField" + }, + "type": "array" + }, + "id": { + "description": "Unique node type identifier", + "example": "http_request", + "type": "string" + }, + "label": { + "description": "Display label", + "example": "HTTP Request", + "type": "string" + } + }, + "required": [ + "id", + "label", + "description", + "fields" + ], + "type": "object" + }, "Notification": { "description": "An inbox notification delivered to a recipient user. Includes type-specific render data resolved at request time.", "example": { @@ -15080,6 +16439,38 @@ ], "type": "object" }, + "Ok": { + "description": "A successful local-tool outcome.", + "example": { + "call_id": "string", + "content": "string", + "status": "ok" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "content": { + "example": "string", + "type": "string" + }, + "status": { + "default": "ok", + "enum": [ + "ok" + ], + "example": "ok", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "content" + ], + "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": { @@ -15188,6 +16579,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -15610,6 +17009,49 @@ ], "type": "object" }, + "R2Bucket": { + "description": "Provider object fields for an R2 bucket.", + "example": { + "id": "string", + "location": "string", + "name": "Example Name", + "storage_class": "string", + "type": "r2_bucket" + }, + "properties": { + "id": { + "description": "Provider-assigned object identifier.", + "example": "string", + "type": "string" + }, + "location": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "storage_class": { + "example": "string", + "type": "string" + }, + "type": { + "default": "r2_bucket", + "enum": [ + "r2_bucket" + ], + "example": "r2_bucket", + "type": "string" + } + }, + "required": [ + "type", + "id", + "name" + ], + "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": { @@ -15738,6 +17180,97 @@ ], "type": "object" }, + "RuntimeCapability": { + "description": "Short-lived bootstrap values for one hosted external-object runtime.", + "example": { + "bindings": {}, + "capability": "string", + "expires_at": "2024-01-01T00:00:00Z", + "gateway_url": "https://example.com" + }, + "properties": { + "bindings": { + "example": {}, + "type": "object" + }, + "capability": { + "description": "Opaque bearer capability. Keep server-side and do not persist it.", + "example": "string", + "type": "string" + }, + "expires_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "gateway_url": { + "example": "https://example.com", + "type": "string" + } + }, + "required": [ + "gateway_url", + "capability", + "bindings", + "expires_at" + ], + "type": "object" + }, + "RuntimeEnvVar": { + "description": "A single runtime environment variable available within a script execution context, including its key, description, and origin.", + "example": { + "description": "An example description.", + "key": "DATABASE_URL", + "source": "app" + }, + "properties": { + "description": { + "description": "Human-readable explanation of the variable's purpose. `null` if no description has been set.", + "example": "An example description.", + "type": "string" + }, + "key": { + "description": "The name of the environment variable as it appears in the script runtime (e.g. `DATABASE_URL`).", + "example": "DATABASE_URL", + "type": "string" + }, + "source": { + "description": "Origin of the environment variable. One of `\"app\"` (set on the application) or `\"org\"` (inherited from the organization).", + "example": "app", + "type": "string" + } + }, + "required": [ + "key", + "source" + ], + "type": "object" + }, + "RuntimeEnvVarList": { + "description": "The collection of runtime environment variables available to the current script execution context.", + "example": { + "data": [ + { + "description": "An example description.", + "key": "DATABASE_URL", + "source": "app" + } + ] + }, + "properties": { + "data": { + "description": "Array of runtime environment variable objects available in the current script execution context.", + "items": { + "$ref": "#/components/schemas/RuntimeEnvVar" + }, + "type": "array" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "Sandbox": { "description": "An isolated developer sandbox environment used for testing integrations without affecting production data or sending real emails.", "example": { @@ -15933,6 +17466,582 @@ ], "type": "object" }, + "ScriptLanguageSpec": { + "description": "Schema for ArchAstro script editor metadata", + "example": { + "builtins": [ + {} + ], + "keywords": [ + "string" + ], + "languageId": "archastro", + "namespaces": [ + {} + ], + "operators": [ + "string" + ], + "reservedNames": [ + "string" + ], + "snippets": [ + {} + ], + "specialIdentifiers": [ + {} + ], + "typeDefinitions": [ + {} + ], + "typeSystem": {}, + "version": 1 + }, + "properties": { + "builtins": { + "description": "Builtin function docs", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "keywords": { + "description": "Language keywords", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "languageId": { + "description": "Monaco language identifier", + "example": "archastro", + "type": "string" + }, + "namespaces": { + "description": "Importable namespace docs", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "operators": { + "description": "Language operators", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "reservedNames": { + "description": "Names that cannot be rebound", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "snippets": { + "description": "Editor snippet templates", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "specialIdentifiers": { + "description": "Special symbols such as JSONPath root/current", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "typeDefinitions": { + "description": "Named type definitions (event interfaces, etc.) with fields", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "typeSystem": { + "description": "Type-system primitives, generics, annotations, inference, and runtime-check guidance", + "example": {}, + "type": "object" + }, + "version": { + "description": "Schema version", + "example": 1, + "type": "integer" + } + }, + "required": [ + "languageId", + "version", + "keywords", + "operators", + "builtins", + "namespaces" + ], + "type": "object" + }, + "ScriptRunResult": { + "description": "Schema for the result of executing a script in the editor", + "example": { + "error": "string", + "findings": [ + {} + ], + "output": [ + {} + ] + }, + "properties": { + "error": { + "description": "Error message when script execution failed", + "example": "string", + "type": "string" + }, + "findings": { + "default": [], + "description": "Structured diagnostics with position info for editor markers (severity, message, line, column, end_line, end_column)", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "output": { + "default": [], + "description": "Output lines from println calls, each with :line (source line number), :text, and optional :values", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "result": { + "description": "The evaluated JSON-safe result (null when execution failed)" + } + }, + "type": "object" + }, + "ScriptTestAssertion": { + "description": "One `test.expect(...).` call from a ScriptTest run.\n\nMatchers never raise (the script language has no exceptions) — each call\nemits a structured assertion entry that the test runner harvests into the\nper-test report. Pass/fail is captured on `:pass`; `:expected` and\n`:actual` carry the matcher's inputs for diff rendering.\n", + "example": { + "describe": [ + "string" + ], + "it": "returns the expected result", + "kind": "assertion", + "line": 1, + "matcher": "toEqual", + "pass": true + }, + "properties": { + "actual": { + "description": "Actual value the matcher was called with — any JSON-safe value" + }, + "describe": { + "default": [], + "description": "Outer-to-inner describe path when the assertion fired", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "expected": { + "description": "Expected value (or arguments to the matcher) — any JSON-safe value: scalar, map, or list" + }, + "it": { + "description": "Name of the enclosing `test.it(...)` block", + "example": "returns the expected result", + "type": "string" + }, + "kind": { + "description": "Output entry kind, always `\"assertion\"` for this schema", + "example": "assertion", + "type": "string" + }, + "line": { + "description": "Source line of the `expect(...)` call", + "example": 1, + "type": "integer" + }, + "matcher": { + "description": "Matcher name (toEqual, toBe, toBeOk, toBeError, toContain, toMatch, toHaveLength)", + "example": "toEqual", + "type": "string" + }, + "pass": { + "description": "True when the matcher succeeded", + "example": true, + "type": "boolean" + } + }, + "required": [ + "pass" + ], + "type": "object" + }, + "ScriptTestCase": { + "description": "One `test.it(...)` block from a ScriptTest run.\n\nBuilt by the harvester from the raw output entries — pairs the case's\nmetadata with the assertions it produced and any runtime error that\naborted the body. An `it` block that emits zero assertions is treated\nas a failure (`pass: false`) so silent mistakes don't pass quietly.\n", + "example": { + "assertions": [ + { + "describe": [ + "string" + ], + "it": "returns the expected result", + "kind": "assertion", + "line": 1, + "matcher": "toEqual", + "pass": true + } + ], + "describe": [ + "string" + ], + "error": "string", + "name": "Example Name", + "pass": true + }, + "properties": { + "assertions": { + "default": [], + "description": "Every `test.expect(...)` matcher that ran inside this case", + "items": { + "$ref": "#/components/schemas/ScriptTestAssertion" + }, + "type": "array" + }, + "describe": { + "default": [], + "description": "Outer-to-inner describe path enclosing this case", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "error": { + "description": "Runtime error that aborted the body of this `it` block, when one occurred", + "example": "string", + "type": "string" + }, + "name": { + "description": "The name passed to `test.it(...)`", + "example": "Example Name", + "type": "string" + }, + "pass": { + "description": "True when the case has at least one assertion and every assertion passed", + "example": true, + "type": "boolean" + } + }, + "required": [ + "name", + "pass" + ], + "type": "object" + }, + "ScriptTestOutputEntry": { + "description": "One println/log entry interleaved with a ScriptTest run.\n\nAfter the test harvester filters out test-control entries (`it`,\n`assertion`, `it_error`), only `println` and `log.` calls remain\nin the report's `output` field. Both shapes share `text`/`values`/`line`\n(line is auto-annotated by the evaluator post-call); `level` and\n`timestamp` are only present on `log.*` entries.\n", + "example": { + "level": "info", + "line": 1, + "text": "Hello, world!", + "timestamp": "2024-01-01T00:00:00Z", + "values": [] + }, + "properties": { + "level": { + "description": "Log level (`debug`, `info`, `warn`, `error`) — present on log.* entries only", + "example": "info", + "type": "string" + }, + "line": { + "description": "Source line of the println/log call", + "example": 1, + "type": "integer" + }, + "text": { + "description": "Joined text representation of the args", + "example": "Hello, world!", + "type": "string" + }, + "timestamp": { + "description": "UTC timestamp — present on log.* entries only", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "values": { + "default": [], + "description": "Original args coerced to JSON-safe values — mixed scalars, maps, and lists since the call sites accept arbitrary expressions", + "example": [], + "items": {}, + "type": "array" + } + }, + "required": [ + "text" + ], + "type": "object" + }, + "ScriptTestRunResult": { + "description": "Schema for the result of running a ScriptTest", + "example": { + "assertion_count": 1, + "error": "string", + "findings": [ + {} + ], + "output": [ + { + "level": "info", + "line": 1, + "text": "Hello, world!", + "timestamp": "2024-01-01T00:00:00Z", + "values": [] + } + ], + "passed": true, + "suites": [ + { + "describe": [ + "string" + ], + "name": "Example Name", + "tests": [ + { + "assertions": [ + { + "describe": [ + "string" + ], + "it": "returns the expected result", + "kind": "assertion", + "line": 1, + "matcher": "toEqual", + "pass": true + } + ], + "describe": [ + "string" + ], + "error": "string", + "name": "Example Name", + "pass": true + } + ] + } + ], + "tests": [ + { + "assertions": [ + { + "describe": [ + "string" + ], + "it": "returns the expected result", + "kind": "assertion", + "line": 1, + "matcher": "toEqual", + "pass": true + } + ], + "describe": [ + "string" + ], + "error": "string", + "name": "Example Name", + "pass": true + } + ] + }, + "properties": { + "assertion_count": { + "default": 0, + "description": "Total number of `test.expect(...)` matcher calls that ran", + "example": 1, + "type": "integer" + }, + "error": { + "description": "Top-level error message when a runtime error fired outside any `it` block (parse/tokenize errors return 422 instead)", + "example": "string", + "type": "string" + }, + "findings": { + "default": [], + "description": "Structured diagnostics with position info for editor markers when the run errored before any tests could run", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "output": { + "default": [], + "description": "Non-test output entries (println, log.*) interleaved during the run", + "items": { + "$ref": "#/components/schemas/ScriptTestOutputEntry" + }, + "type": "array" + }, + "passed": { + "description": "True when every test in every suite passed", + "example": true, + "type": "boolean" + }, + "suites": { + "default": [], + "description": "One entry per describe path", + "items": { + "$ref": "#/components/schemas/ScriptTestSuite" + }, + "type": "array" + }, + "tests": { + "default": [], + "description": "Flat list of every `it` that ran, each with assertions and pass flag", + "items": { + "$ref": "#/components/schemas/ScriptTestCase" + }, + "type": "array" + } + }, + "required": [ + "passed" + ], + "type": "object" + }, + "ScriptTestSuite": { + "description": "One `test.describe(...)` group from a ScriptTest run.\n\nSuites are derived from the unique describe paths the harvester sees, so\nthere's no synthetic \"ungrouped\" suite — `it` blocks emitted outside any\n`describe` show up in the top-level `tests` array on the parent report\ninstead.\n", + "example": { + "describe": [ + "string" + ], + "name": "Example Name", + "tests": [ + { + "assertions": [ + { + "describe": [ + "string" + ], + "it": "returns the expected result", + "kind": "assertion", + "line": 1, + "matcher": "toEqual", + "pass": true + } + ], + "describe": [ + "string" + ], + "error": "string", + "name": "Example Name", + "pass": true + } + ] + }, + "properties": { + "describe": { + "description": "Outer-to-inner describe path identifying this suite", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "Human-readable suite name (`describe` path joined by ` > `)", + "example": "Example Name", + "type": "string" + }, + "tests": { + "default": [], + "description": "Every `test.it(...)` case run inside this describe", + "items": { + "$ref": "#/components/schemas/ScriptTestCase" + }, + "type": "array" + } + }, + "required": [ + "describe", + "name" + ], + "type": "object" + }, + "Setup": { + "description": "Current MCP setup assessment, not a historical downgrade reason or upstream protocol probe.", + "example": { + "next_action": "string", + "protocol_status": "not_checked", + "reason": "string" + }, + "properties": { + "next_action": { + "description": "Recommended next step for MCP setup.", + "example": "string", + "type": "string" + }, + "protocol_status": { + "description": "MCP protocol health is not checked by this assessment.", + "enum": [ + "not_checked" + ], + "example": "not_checked", + "type": "string" + }, + "reason": { + "description": "Reason for the current MCP setup state.", + "example": "string", + "type": "string" + } + }, + "required": [ + "reason", + "next_action", + "protocol_status" + ], + "type": "object" + }, "SlackChannelBinding": { "description": "A binding that connects a Slack channel to an ArchAstro team and one or more agents, enabling those agents to receive and respond to messages in that channel.", "example": { @@ -15962,7 +18071,7 @@ }, "properties": { "agents": { - "description": "IDs of every agent attached to this binding, including legacy concierge attachments. Use `resident_agent` and `route_kind` for the effective runtime route.", + "description": "IDs of every agent attached to this binding, including attachments left over from retired flows. Use `resident_agent` and `route_kind` for the effective runtime route.", "example": [ "string" ], @@ -16052,12 +18161,11 @@ "type": "string" }, "route_kind": { - "description": "Effective Slack ingress route. `fda` — a resident on a team-bound channel, replying through the Forward Deployed Agent chain. `resident` — a resident on an internal channel, replying through the channel mirror. `observer` — no resident is attached, so the channel is recorded and nobody replies. `concierge` — no longer returned anywhere; until Track F it was the value for a channel with no resident, meaning the shared concierge agent answered there. The value is retained in this enum so consumers matching on it do not break, and its removal rides a deliberate API change.", + "description": "Effective Slack ingress route. `fda` — a resident on a team-bound channel, replying through the Forward Deployed Agent chain. `resident` — a resident on an internal channel, replying through the channel mirror. `observer` — no resident is attached, so the channel is recorded and nobody replies.", "enum": [ "fda", "resident", - "observer", - "concierge" + "observer" ], "example": "fda", "type": "string" @@ -16910,6 +19018,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -17365,6 +19476,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -17483,6 +19597,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -17565,11 +19682,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -17725,6 +19852,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -18028,6 +20156,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -18579,6 +20710,7 @@ "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, @@ -18656,6 +20788,12 @@ "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 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, @@ -18853,7 +20991,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -18996,6 +21134,37 @@ ], "type": "object" }, + "TaskExternalLink": { + "description": "An indexed external object linked to a task.", + "example": { + "external_scope": "string", + "object_id": "string", + "object_type": "string" + }, + "properties": { + "external_scope": { + "description": "External container identity.", + "example": "string", + "type": "string" + }, + "object_id": { + "description": "Object identity within that container.", + "example": "string", + "type": "string" + }, + "object_type": { + "description": "External object kind.", + "example": "string", + "type": "string" + } + }, + "required": [ + "external_scope", + "object_type", + "object_id" + ], + "type": "object" + }, "TaskSessionLease": { "description": "A task-session lease returned only to its matching holder.", "example": { @@ -19319,6 +21488,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -19377,6 +21549,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -19552,6 +21727,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -19610,6 +21788,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -19848,6 +22029,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -19906,6 +22090,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -20191,6 +22378,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -20315,6 +22510,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -20373,6 +22571,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -20816,6 +23017,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -21603,6 +23812,60 @@ ], "type": "object" }, + "UserSSHKey": { + "description": "Metadata for a registered SSH public key. Key material is never returned.", + "example": { + "algorithm": "string", + "created_at": "2024-01-01T00:00:00Z", + "fingerprint": "string", + "id": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "label": "string", + "revoked_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "algorithm": { + "description": "SSH algorithm. V1 accepts `ssh-ed25519`.", + "example": "string", + "type": "string" + }, + "created_at": { + "description": "Registration time.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "fingerprint": { + "description": "OpenSSH SHA-256 fingerprint.", + "example": "string", + "type": "string" + }, + "id": { + "description": "Registered SSH key ID (`ssk_...`).", + "example": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "label": { + "description": "User-visible label for the key.", + "example": "string", + "type": "string" + }, + "revoked_at": { + "description": "Revocation time, or null while active.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "label", + "algorithm", + "fingerprint", + "created_at" + ], + "type": "object" + }, "ValidationResult": { "description": "The result of a configuration validation check, indicating whether the config is valid and listing any errors or warnings.", "example": { @@ -22461,6 +24724,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -22519,6 +24785,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -22865,6 +25134,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -22923,6 +25195,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -22997,6 +25272,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -23079,11 +25357,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -23508,6 +25796,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -23523,6 +25812,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -23605,11 +25897,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -24034,6 +26336,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -31306,7 +33609,7 @@ }, "/api/v1/agents/{agent}/search": { "post": { - "description": "Performs a semantic search over an agent's knowledge base and returns a ranked,\n`kind`-discriminated list of matching items.\n\nTwo item kinds may appear in `data`:\n\n- `\"chunk\"` — chunk-level results from the agent's context store. Present for all agents.\n- `\"document\"` — document-level results. Present only when the agent has an active\n `archastro/knowledge` installation.\n\nResults from both kinds are scored with Reciprocal Rank Fusion (RRF), normalized to\nbe comparable across kinds, then merged into a single ranked list. On a relevance tie,\nchunks appear before documents. The total number of results is capped at `max_results`\nacross both kinds.\n\nUse `mode` to choose the retrieval strategy: `\"hybrid\"` (default) combines vector and\nfull-text search; `\"vector\"` and `\"fulltext\"` select each strategy independently.\n", + "description": "Performs a semantic search over an agent's knowledge base and returns a ranked,\n`kind`-discriminated list of matching items.\n\nThreads are automatically indexed as conversational memory, including prior agent\nanswers. A match is not necessarily independent source evidence. Result `type`\nidentifies the source: `thread/messages` is conversation memory; use `source_types`\nwith the desired corpus slugs (for example `knowledge/documents`) to exclude it.\n`kind` describes storage granularity, not whether the content is conversation memory.\n\nTwo item kinds may appear in `data`:\n\n- `\"chunk\"` — chunk-level results from the agent's context store. Present for all agents.\n- `\"document\"` — document-level results. Present only when the agent has an active\n `archastro/knowledge` installation.\n\nResults from both kinds are scored with Reciprocal Rank Fusion (RRF), normalized to\nbe comparable across kinds, then merged into a single ranked list. On a relevance tie,\nchunks appear before documents. The total number of results is capped at `max_results`\nacross both kinds.\n\nUse `mode` to choose the retrieval strategy: `\"hybrid\"` (default) combines vector and\nfull-text search; `\"vector\"` and `\"fulltext\"` select each strategy independently.\n", "operationId": "post_api_v1_agents__agent_search", "parameters": [ { @@ -31361,7 +33664,7 @@ "type": "integer" }, "source_types": { - "description": "Array of source-type slugs used to filter chunk results, e.g. `[\"web\", \"file\"]`. Omit to include all source types.", + "description": "Exact source-type slugs filtering both chunks and documents, e.g. `[\"knowledge/documents\"]` for authored documents or `[\"thread/messages\"]` for conversation memory. Omit or pass an empty array to include all source types.", "example": [ "string" ], @@ -31479,7 +33782,8 @@ "snippet": "This document describes the onboarding workflow for new users, including account setup, initial configuration steps, and a guided tour of the main features…", "title": "Example Title", "total_lines": 42, - "total_size": 1024 + "total_size": 1024, + "type": "string" }, "properties": { "id": { @@ -31522,6 +33826,11 @@ "description": "Total byte size of the document's text content encoded as UTF-8. `0` for empty documents.", "example": 1024, "type": "integer" + }, + "type": { + "description": "Parent source-type slug, e.g. `knowledge/documents`. Returns `unknown` when the source is not loaded.", + "example": "string", + "type": "string" } }, "required": [ @@ -33312,6 +35621,7 @@ "output_format": "string", "prompt": "string", "quality": "string", + "session_id": "string", "size": "string", "style": "string", "width": 1 @@ -33391,6 +35701,11 @@ "example": "string", "type": "string" }, + "session_id": { + "description": "Optional session UUID for grouping provider attempts in usage records. Generated when omitted.", + "example": "string", + "type": "string" + }, "size": { "description": "Output dimensions as a WxH string, e.g. `\"1024x1024\"`. Applies to OpenAI-compatible models. Omit to use the model's default.", "example": "string", @@ -33434,6 +35749,9 @@ "401": { "description": "Unauthorized" }, + "402": { + "description": "Payment required" + }, "422": { "description": "Image editing failed" } @@ -33464,6 +35782,7 @@ "output_format": "string", "prompt": "string", "quality": "string", + "session_id": "string", "size": "string", "style": "string", "width": 1 @@ -33514,6 +35833,11 @@ "example": "string", "type": "string" }, + "session_id": { + "description": "Optional session UUID for grouping provider attempts in usage records. Generated when omitted.", + "example": "string", + "type": "string" + }, "size": { "description": "Output dimensions as a WxH string, e.g. `\"1024x1024\"`. Applies to OpenAI-compatible models. Omit to use the model's default.", "example": "string", @@ -33556,6 +35880,9 @@ "401": { "description": "Unauthorized" }, + "402": { + "description": "Payment required" + }, "422": { "description": "Image generation failed" } @@ -33725,6 +36052,171 @@ ] } }, + "/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", + "operationId": "post_api_v1_artifacts", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "artifact": {}, + "description": "An example description.", + "file": { + "data": "string", + "encoding": "base64", + "filename": "string", + "mime_type": "application/json" + }, + "group_key": "string", + "idempotency_key": "string", + "name": "Example Name", + "org": "string", + "permissions": {}, + "system": true, + "team": "string", + "thread": "string", + "user": "string" + }, + "properties": { + "agent": { + "description": "Agent ID (`agt_...`) to associate with the artifact.", + "example": "string", + "type": "string" + }, + "artifact": { + "description": "Legacy wrapper object containing artifact attributes. Prefer top-level fields.", + "example": {}, + "type": "object" + }, + "description": { + "description": "Optional longer artifact description.", + "example": "An example description.", + "type": "string" + }, + "file": { + "description": "File payload for the initial artifact version.", + "example": { + "data": "string", + "encoding": "base64", + "filename": "string", + "mime_type": "application/json" + }, + "properties": { + "data": { + "description": "File content encoded according to file.encoding (base64 by default).", + "example": "string", + "type": "string" + }, + "encoding": { + "description": "Data encoding; defaults to base64. Use utf8 for script-generated text.", + "enum": [ + "base64", + "utf8" + ], + "example": "base64", + "type": "string" + }, + "filename": { + "description": "Original filename.", + "example": "string", + "type": "string" + }, + "mime_type": { + "description": "MIME type for the file.", + "example": "application/json", + "type": "string" + } + }, + "required": [ + "data" + ], + "type": "object" + }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Artifact ID remains identity.", + "example": "string", + "nullable": true, + "type": "string" + }, + "idempotency_key": { + "description": "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.", + "example": "string", + "type": "string" + }, + "name": { + "description": "Human-readable artifact name.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "Organization owning a system artifact. Requires an organization admin or system viewer.", + "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, + "type": "boolean" + }, + "team": { + "description": "Team ID (`tem_...`) that should own the artifact. Mutually exclusive with `user`.", + "example": "string", + "type": "string" + }, + "thread": { + "description": "Thread ID (`thr_...`) to associate with the artifact.", + "example": "string", + "type": "string" + }, + "user": { + "description": "User ID (`usr_...`) that should own the artifact. Defaults to the authenticated user when omitted and no team is supplied.", + "example": "string", + "type": "string" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Artifact" + } + } + }, + "description": "The newly created artifact." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "422": { + "description": "Validation error" + } + }, + "summary": "Create an artifact", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/artifacts/{artifact}": { "delete": { "description": "Permanently deletes the artifact and all associated file versions. This\noperation is irreversible — deleted artifacts and their file content cannot\nbe recovered.\n\nThe authenticated user must have write access to the artifact's owning team\nor organization. Returns 404 if the artifact does not exist or is not\naccessible to the caller.\n", @@ -33833,6 +36325,7 @@ "file_content_type": "string", "file_name": "Example Name", "from_version": 1, + "group_key": "string", "name": "Example Name" }, "properties": { @@ -33890,6 +36383,12 @@ "example": 1, "type": "integer" }, + "group_key": { + "description": "Nonunique grouping key, limited to 1024 UTF-8 bytes. Omit to preserve; null clears. Grouping-only updates do not increment the content version and from_version does not protect against concurrent metadata updates.", + "example": "string", + "nullable": true, + "type": "string" + }, "name": { "description": "New display name for the artifact. Omit to leave the existing name unchanged.", "example": "Example Name", @@ -34184,7 +36683,7 @@ }, "/api/v1/auth/login/link": { "post": { - "description": "Sends a magic link to the given email address so an existing user can sign in\nwithout a password. The user clicks the link in their email and is redirected to\n`redirect_uri` with a token; pass that token to `/auth/verify_link` to obtain\nsession tokens.\n\nIf no account exists for the email, the endpoint still returns success to prevent\nemail enumeration — no link is sent in that case. Both `email` and `redirect_uri`\nare required. Requests are rate-limited per IP (10 per minute) and per email-IP pair\n(3 per minute) — exceeding either limit returns HTTP 429. Returns HTTP 204 on success.\n", + "description": "Sends a magic link to the given email address so an existing user can sign in\nwithout a password. The user clicks the link in their email and is redirected to\n`redirect_uri` with a token; pass that token to `/api/v1/auth/verify/link` to obtain\nsession tokens.\n\nIf no account exists for the email, the endpoint still returns success to prevent\nemail enumeration — no link is sent in that case. Both `email` and `redirect_uri`\nare required. Requests are rate-limited per IP (10 per minute) and per email-IP pair\n(3 per minute) — exceeding either limit returns HTTP 429. Returns HTTP 204 on success.\n", "operationId": "post_api_v1_auth_login_link", "parameters": [], "requestBody": { @@ -34291,7 +36790,7 @@ }, "/api/v1/auth/register": { "post": { - "description": "Creates a new user account and returns an access token, refresh token, and the new\nuser object. Two registration paths are supported:\n\n- **Team registration**: supply `team_invite` with a valid team invite ID. The new\n user is added to that team immediately upon registration. Returns HTTP 404 if the\n invite is not found.\n- **Standard registration**: supply `password`. An `invite_code` may optionally be\n included for invite-gated apps; an invalid code returns HTTP 404.\n\nExactly one of `team_invite` or `password` must be provided; omitting both returns\nHTTP 400. Password registration must be enabled for the app; disabled apps return\nHTTP 403. The response status is HTTP 201 on success.\n", + "description": "Creates a new user account and returns an access token, refresh token, and the new\nuser object. Two registration paths are supported:\n\n- **Team registration**: supply `team_invite` with a valid team invite ID. The new\n user is added to that team immediately upon registration. Returns HTTP 404 if the\n invite is not found.\n- **Standard registration**: supply `password`. An `invite_code` may optionally be\n included for invite-gated apps; an invalid code returns HTTP 404.\n\nProvide `team_invite` or `password`; when both are supplied, team registration\ntakes precedence. Omitting both returns HTTP 400. Unknown fields return HTTP 400\nwith field-level guidance. Use `full_name` for the profile name (returned as\n`user.name`); `name` is not an input field. `alias` is a separate optional handle.\nLegacy `team_invite_id` remains accepted; `team_invite` takes precedence.\nPassword registration must be enabled for the app; disabled apps return\nHTTP 403. The response status is HTTP 201 on success.\n", "operationId": "post_api_v1_auth_register", "parameters": [], "requestBody": { @@ -34394,7 +36893,7 @@ }, "/api/v1/auth/register/link": { "post": { - "description": "Starts a passwordless registration flow by sending a verification link to the given\nemail address. The recipient clicks the link and is redirected to `redirect_uri` with\na token; pass that token to `/auth/verify_link` to complete registration and obtain\nsession tokens.\n\nProfile fields (`full_name`, `alias`, `timezone`) are captured now and applied when\nthe link is verified. Requests are rate-limited per IP (10 per minute) and per\nemail-IP pair (3 per minute) — exceeding either limit returns HTTP 429. Returns\nHTTP 204 on success.\n", + "description": "Starts a passwordless registration flow by sending a verification link to the given\nemail address. The recipient clicks the link and is redirected to `redirect_uri` with\na token; pass that token to `/api/v1/auth/verify/link` to complete registration and obtain\nsession tokens.\n\nProfile fields (`full_name`, `alias`, `timezone`) and, with `set_org`, the `org`\ndetails are captured now and applied when the link is verified; nothing is created\nbefore the click. Requests are rate-limited per IP (10 per minute) and per\nemail-IP pair (3 per minute) — exceeding either limit returns HTTP 429. Returns\nHTTP 204 on success.\n", "operationId": "post_api_v1_auth_register_link", "parameters": [], "requestBody": { @@ -34405,6 +36904,14 @@ "alias": "string", "email": "user@example.com", "full_name": "Example Name", + "org": { + "enabled_auth_methods": [ + "string" + ], + "name": "Example Name", + "slug": "example-slug", + "website": "string" + }, "redirect_uri": "https://example.com", "set_org": true, "timezone": "America/New_York" @@ -34425,6 +36932,45 @@ "example": "Example Name", "type": "string" }, + "org": { + "description": "Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods).", + "example": { + "enabled_auth_methods": [ + "string" + ], + "name": "Example Name", + "slug": "example-slug", + "website": "string" + }, + "properties": { + "enabled_auth_methods": { + "description": "Sign-in methods the new organization starts with. Empty keeps the platform default.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "Organization display name.", + "example": "Example Name", + "type": "string" + }, + "slug": { + "description": "Organization slug. Must be spelled from the email's own domain: the platform default (domain stem) or the whole domain with separators dashed.", + "example": "example-slug", + "type": "string" + }, + "website": { + "description": "Organization website URL (http or https).", + "example": "string", + "type": "string" + } + }, + "type": "object" + }, "redirect_uri": { "description": "URL the user is redirected to after clicking the registration link. The token is appended as a query parameter.", "example": "https://example.com", @@ -34473,7 +37019,7 @@ }, "/api/v1/auth/request/link": { "post": { - "description": "Sends a passwordless magic link to the given email address. If an account with that\nemail already exists, a login link is sent. If no account exists, a registration link\nis sent and the recipient completes sign-up by clicking through. This unified endpoint\nlets you implement a single email-entry UI that handles both cases transparently.\n\nThe `redirect_uri` is validated against the app's registered hosts; an unregistered\nURI returns HTTP 400. Both `email` and `redirect_uri` are required. Requests are\nrate-limited per IP (10 per minute) and per email-IP pair (3 per minute). Returns\nHTTP 204 on success — no body.\n", + "description": "Sends a passwordless magic link to the given email address. If an account with that\nemail already exists, a login link is sent. If no account exists, a registration link\nis sent and the recipient completes sign-up by clicking through. This unified endpoint\nlets you implement a single email-entry UI that handles both cases transparently.\n\nThe `redirect_uri` is validated against the app's registered hosts; an unregistered\nURI returns HTTP 400. Both `email` and `redirect_uri` are required. Requests are\nrate-limited per IP (10 per minute) and per email-IP pair (3 per minute). Returns\nHTTP 204 on success — no body. If the app has Allowed Users enabled and the\naddress is not on the list, returns HTTP 403 `user_not_allowed`. That refusal\nis the same for new and existing addresses, so it does not reveal whether an\naccount exists.\n\nFor a new user, `full_name` and (with `set_org`) the `org` details ride the pending\nregistration and are applied when the link is verified; nothing is created before\nthe click. They are ignored when the email already belongs to a user. Malformed\nvalues return HTTP 422 before the address is looked up, so the response cannot\nreveal whether an account exists.\n", "operationId": "post_api_v1_auth_request_link", "parameters": [], "requestBody": { @@ -34482,8 +37028,19 @@ "schema": { "example": { "email": "user@example.com", + "full_name": "Example Name", + "mcp_client_id": "string", + "org": { + "enabled_auth_methods": [ + "string" + ], + "name": "Example Name", + "slug": "example-slug", + "website": "string" + }, "redirect_uri": "https://example.com", - "set_org": true + "set_org": true, + "signup_origin": "string" }, "properties": { "email": { @@ -34491,6 +37048,55 @@ "example": "user@example.com", "type": "string" }, + "full_name": { + "description": "Display name for the account the verified link will create.", + "example": "Example Name", + "type": "string" + }, + "mcp_client_id": { + "description": "Opaque dynamic-registration client id for an MCP consent signup. Platform resolves it to a closed analytics key.", + "example": "string", + "type": "string" + }, + "org": { + "description": "Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods).", + "example": { + "enabled_auth_methods": [ + "string" + ], + "name": "Example Name", + "slug": "example-slug", + "website": "string" + }, + "properties": { + "enabled_auth_methods": { + "description": "Sign-in methods the new organization starts with. Empty keeps the platform default.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "Organization display name.", + "example": "Example Name", + "type": "string" + }, + "slug": { + "description": "Organization slug. Must be spelled from the email's own domain: the platform default (domain stem) or the whole domain with separators dashed.", + "example": "example-slug", + "type": "string" + }, + "website": { + "description": "Organization website URL (http or https).", + "example": "string", + "type": "string" + } + }, + "type": "object" + }, "redirect_uri": { "description": "URL the user is redirected to after clicking the magic link. Must be registered with the app.", "example": "https://example.com", @@ -34500,6 +37106,11 @@ "description": "For a new user, create or reuse an organization from the work-email domain during confirmation.", "example": true, "type": "boolean" + }, + "signup_origin": { + "description": "Closed product intent for a new signup. Missing or unknown values become generic and never affect authentication.", + "example": "string", + "type": "string" } }, "type": "object" @@ -34515,6 +37126,9 @@ "400": { "description": "Missing email or redirect_uri" }, + "403": { + "description": "This email address is not allowed to access this app" + }, "422": { "description": "Validation failed" }, @@ -34534,7 +37148,7 @@ }, "/api/v1/auth/token": { "post": { - "description": "Consumes a single-use login token delivered via email and returns an access token,\nrefresh token, and the authenticated user object. One-time tokens are issued by the\npasswordless login flow and expire after a short window; submitting an expired or\nalready-used token returns HTTP 401.\n\nIf `timezone` is provided and the user's current timezone is still the default\n(`\"America/Los_Angeles\"`), the account timezone is updated in the same request.\nRequests are rate-limited to 10 per IP per minute; exceeding this returns HTTP 429.\n", + "description": "Exchanges an email-login token (including email reply links) or an internal\nimpersonation login token for session credentials. Passwordless magic-link tokens\nfrom `/api/v1/auth/login/link`, `/api/v1/auth/register/link`, or\n`/api/v1/auth/request/link` must instead go to `/api/v1/auth/verify/link`.\nNumeric email codes go to `/api/v1/auth/code/verify` with `email` and `code`.\nAccess and refresh tokens are not accepted here. Invalid, expired, or already-used\nlogin tokens return HTTP 401 with structured `error.type`, `error.code`, and\n`error.message` fields.\n\nIf `timezone` is provided and the user's current timezone is still the default\n(`\"America/Los_Angeles\"`), the account timezone is updated in the same request.\nRequests are rate-limited to 10 per IP per minute; exceeding this returns HTTP 429.\n", "operationId": "post_api_v1_auth_token", "parameters": [], "requestBody": { @@ -34552,7 +37166,7 @@ "type": "string" }, "token": { - "description": "Single-use login token extracted from the magic link or email code flow.", + "description": "Single-use email-login or impersonation token; not a passwordless magic-link token or numeric code.", "example": "string", "type": "string" } @@ -34602,7 +37216,7 @@ }, "/api/v1/auth/verify/link": { "post": { - "description": "Consumes a single-use token from a magic link URL and returns an access token,\nrefresh token, and the authenticated user object. This endpoint completes both the\nlogin flow (initiated by `/auth/request_login_link`) and the registration flow\n(initiated by `/auth/request_register_link` or `/auth/request_link`).\n\nExtract the token from the `token` query parameter of the magic link redirect URI\nand POST it here. Expired or already-used tokens return HTTP 401 — expired links\ncarry the error code `expired_token`, unknown or already-used tokens carry\n`invalid_or_expired_token`. If the app has disabled passwordless authentication\nthe request returns HTTP 403. Rate-limited to 10 requests per IP per minute —\nexceeding this returns HTTP 429.\n", + "description": "Consumes a single-use token from a magic link URL and returns an access token,\nrefresh token, and the authenticated user object. This endpoint completes both the\nlogin flow (initiated by `/api/v1/auth/login/link`) and the registration flow\n(initiated by `/api/v1/auth/register/link` or `/api/v1/auth/request/link`).\n\nExtract the token from the `token` query parameter of the magic link redirect URI\nand POST JSON `{\"token\":\"\"}` to\n`/api/v1/auth/verify/link`, not `/api/v1/auth/token`. Expired or already-used tokens return HTTP 401 — expired links\nhave `error.code` = `authentication_error` and `error.message` =\n`expired_token`; unknown or already-used tokens have `error.message` =\n`invalid_or_expired_token`. If the app has disabled passwordless authentication\nthe request returns HTTP 403. Rate-limited to 10 requests per IP per minute —\nexceeding this returns HTTP 429.\n", "operationId": "post_api_v1_auth_verify_link", "parameters": [], "requestBody": { @@ -36406,6 +39020,7 @@ "kind": "string", "mime_type": "application/json", "raw_content": "string", + "system": true, "team": "string", "user": "string" }, @@ -36440,6 +39055,11 @@ "example": "string", "type": "string" }, + "system": { + "description": "Set to true to validate as a system-owned config. Mutually exclusive with `team`, `user`, and `agent`.", + "example": true, + "type": "boolean" + }, "team": { "description": "Team ID (`team_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `user` and `agent`.", "example": "string", @@ -37875,7 +40495,7 @@ "description": "Not found" }, "409": { - "description": "Conflict - an object already occupies this row_key" + "description": "Conflict - an object already occupies this row_key or the workspace limit is reached" }, "422": { "description": "Validation failed" @@ -39196,6 +41816,810 @@ ] } }, + "/api/v1/external_object_capabilities": { + "delete": { + "operationId": "delete_api_v1_external_object_capabilities", + "parameters": [], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Revoke every active external-object capability for a hosted workload", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "post": { + "operationId": "post_api_v1_external_object_capabilities", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "bindings": {}, + "subject": "string" + }, + "properties": { + "bindings": { + "example": {}, + "type": "object" + }, + "subject": { + "example": "string", + "type": "string" + } + }, + "required": [ + "subject", + "bindings" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeCapability" + } + } + }, + "description": "Gateway bootstrap values for one hosted workload" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Invalid parameters" + }, + "502": { + "description": "Service unavailable" + } + }, + "summary": "Issue a revocable capability for hosted external-object bindings", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/external_objects": { + "get": { + "operationId": "get_api_v1_external_objects", + "parameters": [ + { + "example": "cloudflare", + "in": "query", + "name": "service", + "required": false, + "schema": { + "enum": [ + "cloudflare" + ], + "type": "string" + } + }, + { + "example": "r2_bucket", + "in": "query", + "name": "type", + "required": false, + "schema": { + "enum": [ + "r2_bucket", + "d1_database" + ], + "type": "string" + } + }, + { + "example": 1, + "in": "query", + "name": "page", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "example": 1, + "in": "query", + "name": "page_size", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "example": "string", + "in": "query", + "name": "search", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "example": { + "data": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "id": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "integration": "int_0aBcDeFgHiJkLmNoPqRsTu", + "object": { + "id": "b5f2c1d8e9a04736", + "name": "agent-assets", + "type": "r2_bucket" + }, + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "service": "cloudflare", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "has_next": true, + "has_prev": true, + "page": 1, + "page_size": 1, + "total_entries": 1, + "total_pages": 1 + }, + "properties": { + "data": { + "example": [ + { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "id": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "integration": "int_0aBcDeFgHiJkLmNoPqRsTu", + "object": { + "id": "b5f2c1d8e9a04736", + "name": "agent-assets", + "type": "r2_bucket" + }, + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "service": "cloudflare", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "items": { + "description": "An owner-scoped object provisioned through an external integration.", + "example": { + "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "id": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "integration": "int_0aBcDeFgHiJkLmNoPqRsTu", + "object": { + "id": "b5f2c1d8e9a04736", + "name": "agent-assets", + "type": "r2_bucket" + }, + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "service": "cloudflare", + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "properties": { + "agent": { + "example": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "created_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "ArchAstro external object ID (`ext_...`).", + "example": "ext_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "integration": { + "description": "Integration ID (`int_...`).", + "example": "int_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "object": { + "description": "Service-specific object payload.", + "discriminator": { + "propertyName": "type" + }, + "example": { + "id": "b5f2c1d8e9a04736", + "name": "agent-assets", + "type": "r2_bucket" + }, + "oneOf": [ + { + "description": "Provider object fields for an R2 bucket.", + "example": { + "id": "string", + "location": "string", + "name": "Example Name", + "storage_class": "string", + "type": "r2_bucket" + }, + "properties": { + "id": { + "description": "Provider-assigned object identifier.", + "example": "string", + "type": "string" + }, + "location": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "storage_class": { + "example": "string", + "type": "string" + }, + "type": { + "default": "r2_bucket", + "enum": [ + "r2_bucket" + ], + "example": "r2_bucket", + "type": "string" + } + }, + "required": [ + "type", + "id", + "name" + ], + "type": "object" + }, + { + "description": "Provider object fields for a D1 database.", + "example": { + "id": "string", + "jurisdiction": "string", + "name": "Example Name", + "read_replication": {}, + "type": "d1_database" + }, + "properties": { + "id": { + "description": "Provider-assigned object identifier.", + "example": "string", + "type": "string" + }, + "jurisdiction": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "read_replication": { + "example": {}, + "type": "object" + }, + "type": { + "default": "d1_database", + "enum": [ + "d1_database" + ], + "example": "d1_database", + "type": "string" + } + }, + "required": [ + "type", + "id", + "name" + ], + "type": "object" + } + ] + }, + "org": { + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "service": { + "default": "cloudflare", + "enum": [ + "cloudflare" + ], + "example": "cloudflare", + "type": "string" + }, + "team": { + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "updated_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + } + }, + "required": [ + "id", + "integration", + "service", + "object", + "created_at", + "updated_at" + ], + "type": "object" + }, + "type": "array" + }, + "has_next": { + "example": true, + "type": "boolean" + }, + "has_prev": { + "example": true, + "type": "boolean" + }, + "page": { + "example": 1, + "type": "integer" + }, + "page_size": { + "example": 1, + "type": "integer" + }, + "total_entries": { + "example": 1, + "type": "integer" + }, + "total_pages": { + "example": 1, + "type": "integer" + } + }, + "required": [ + "data", + "page", + "page_size", + "total_entries", + "total_pages", + "has_next", + "has_prev" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Bad request" + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "List external objects", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "post": { + "operationId": "post_api_v1_external_objects", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent": "string", + "org": "string", + "service": "cloudflare", + "team": "string", + "user": "string" + }, + "properties": { + "agent": { + "description": "Owning agent ID (`agt_...`). Supply exactly one owner field.", + "example": "string", + "type": "string" + }, + "object": { + "description": "Provisioning fields selected by external object type.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "description": "Fields used to provision an R2 bucket.", + "example": { + "location_hint": "string", + "name": "Example Name", + "storage_class": "string", + "type": "r2_bucket" + }, + "properties": { + "location_hint": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "storage_class": { + "example": "string", + "type": "string" + }, + "type": { + "default": "r2_bucket", + "enum": [ + "r2_bucket" + ], + "example": "r2_bucket", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + { + "description": "Fields used to provision a D1 database.", + "example": { + "jurisdiction": "string", + "location_hint": "string", + "name": "Example Name", + "type": "d1_database" + }, + "properties": { + "jurisdiction": { + "example": "string", + "type": "string" + }, + "location_hint": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "type": { + "default": "d1_database", + "enum": [ + "d1_database" + ], + "example": "d1_database", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + } + ] + }, + "org": { + "description": "Owning organization ID (`org_...`). Supply exactly one owner field.", + "example": "string", + "type": "string" + }, + "service": { + "enum": [ + "cloudflare" + ], + "example": "cloudflare", + "type": "string" + }, + "team": { + "description": "Owning team ID (`tem_...`). Supply exactly one owner field.", + "example": "string", + "type": "string" + }, + "user": { + "description": "Owning user ID (`usr_...`). Supply exactly one owner field.", + "example": "string", + "type": "string" + } + }, + "required": [ + "service", + "object" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExternalObject" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Integration is not connected. Authorize the integration first.; Invalid parameters; Validation failed" + }, + "502": { + "description": "Provider returned an error" + } + }, + "summary": "Provision an external object through an integration service", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/external_objects/{external_object}": { + "delete": { + "operationId": "delete_api_v1_external_objects__external_object", + "parameters": [ + { + "example": "string", + "in": "path", + "name": "external_object", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation failed" + }, + "502": { + "description": "Provider returned an error" + } + }, + "summary": "Delete an external object", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "get": { + "operationId": "get_api_v1_external_objects__external_object", + "parameters": [ + { + "example": "string", + "in": "path", + "name": "external_object", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExternalObject" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Not found" + } + }, + "summary": "Show an external object", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "patch": { + "operationId": "patch_api_v1_external_objects__external_object", + "parameters": [ + { + "example": "string", + "in": "path", + "name": "external_object", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "object": { + "description": "Mutable fields selected by external object type.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "description": "Mutable fields for an R2 bucket.", + "example": { + "storage_class": "string", + "type": "r2_bucket" + }, + "properties": { + "storage_class": { + "example": "string", + "type": "string" + }, + "type": { + "default": "r2_bucket", + "enum": [ + "r2_bucket" + ], + "example": "r2_bucket", + "type": "string" + } + }, + "required": [ + "type", + "storage_class" + ], + "type": "object" + }, + { + "description": "Mutable fields for a D1 database.", + "example": { + "read_replication_mode": "auto", + "type": "d1_database" + }, + "properties": { + "read_replication_mode": { + "enum": [ + "auto", + "disabled" + ], + "example": "auto", + "type": "string" + }, + "type": { + "default": "d1_database", + "enum": [ + "d1_database" + ], + "example": "d1_database", + "type": "string" + } + }, + "required": [ + "type", + "read_replication_mode" + ], + "type": "object" + } + ] + } + }, + "required": [ + "object" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExternalObject" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Invalid parameters; Validation failed" + }, + "502": { + "description": "Provider returned an error" + } + }, + "summary": "Update an external object", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/extractions": { "post": { "description": "Records a text-extraction job for a document (`file`) or a URL (`url` + `mode`).\nThe job is owner-scoped and tagged with the caller-supplied `destination`\nnamespace, **without** committing knowledge to an agent (no embeddings, no\nagent attach).\n\nExactly one of `file` or (`url` + `mode`) is required.\n\nDocument extraction (`file`) runs synchronously: the response already\nreflects the final state (`done` with its output, or an error if extraction\ncouldn't complete), status `201`. URL extraction (`url` + `mode`) submits an\nasync crawl and returns immediately with state `running`, status `202` —\npoll `GET /extractions/:extraction` for its terminal state.\n", @@ -40624,6 +44048,36 @@ "type": "string" } }, + { + "description": "Exact team IDs whose sources to include.", + "example": [ + "string" + ], + "in": "query", + "name": "team", + "required": false, + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Exact backing thread IDs whose sources to include.", + "example": [ + "string" + ], + "in": "query", + "name": "thread", + "required": false, + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, { "description": "Installation ID (`ins_...`). Returns only sources associated with this installation.", "example": "string", @@ -41355,6 +44809,209 @@ ] } }, + "/api/v1/knowledge_sources/{source}/search": { + "post": { + "description": "Executes a search query against the indexed documents in a single knowledge source and\nreturns matching results ranked by relevance.\n\nThree search modes are supported: `\"hybrid\"` (default) combines vector similarity and\nfull-text scoring, `\"vector\"` uses embedding-based semantic search only, and\n`\"fulltext\"` uses keyword-based search only. Use `\"hybrid\"` for most use cases;\nprefer `\"vector\"` when semantic meaning matters more than exact terms.\n\nOptionally restrict results to documents ingested within the last N days using\n`recency_days`. Results are ordered by relevance score descending.\n", + "operationId": "post_api_v1_knowledge_sources__source_search", + "parameters": [ + { + "description": "Knowledge source ID (`ksrc_...`) to search within.", + "example": "string", + "in": "path", + "name": "source", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "max_results": 1, + "min_similarity": 1.0, + "mode": "string", + "query": "string", + "recency_days": 1 + }, + "properties": { + "max_results": { + "description": "Maximum number of results to return. The platform applies its own upper bound; omit to use the default.", + "example": 1, + "type": "integer" + }, + "min_similarity": { + "description": "Cosine-similarity floor for the vector leg, 0.0-1.0. Candidates below it are discarded before ranking, so a high value trades recall for precision. Pass `0.0` to disable the floor when a missed match costs more than a weak one — note that with no floor every query returns results, so an empty response can no longer be read as \"no match\". Omit to use the default.", + "example": 1.0, + "type": "number" + }, + "mode": { + "description": "Search algorithm to use. One of `\"hybrid\"` (default, combines vector and full-text), `\"vector\"` (semantic similarity only), or `\"fulltext\"` (keyword matching only).", + "example": "string", + "type": "string" + }, + "query": { + "description": "Natural language or keyword query string to search for.", + "example": "string", + "type": "string" + }, + "recency_days": { + "description": "When set, limits results to documents ingested within the last N days. Omit to search across all documents regardless of age.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "query" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Search results ranked by relevance.", + "example": { + "data": [ + { + "content": "ArchAstro connects your agents to external knowledge sources for real-time context retrieval.", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "id": "cim_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "chunk", + "metadata": { + "key": "value" + }, + "raw_content": {}, + "type": "gmail" + } + ] + }, + "properties": { + "data": { + "description": "Array of matching knowledge items ordered by relevance score descending.", + "example": [ + { + "content": "ArchAstro connects your agents to external knowledge sources for real-time context retrieval.", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "id": "cim_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "chunk", + "metadata": { + "key": "value" + }, + "raw_content": {}, + "type": "gmail" + } + ], + "items": { + "description": "A single chunk returned by a knowledge search query. Represents an indexed content item matched against the search terms.", + "example": { + "content": "ArchAstro connects your agents to external knowledge sources for real-time context retrieval.", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "id": "cim_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "chunk", + "metadata": { + "key": "value" + }, + "raw_content": {}, + "type": "gmail" + }, + "properties": { + "content": { + "description": "Normalized plain-text content of the matched chunk.", + "example": "ArchAstro connects your agents to external knowledge sources for real-time context retrieval.", + "type": "string" + }, + "content_type": { + "description": "MIME type of the content, e.g. `\"text/plain\"` or `\"text/html\"`.", + "example": "application/json", + "type": "string" + }, + "created_at": { + "description": "When this item was indexed into the knowledge base (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "Context item ID (`cim_...`).", + "example": "cim_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "kind": { + "default": "chunk", + "description": "Result variant discriminator. Always `\"chunk\"` for this object type.", + "enum": [ + "chunk" + ], + "example": "chunk", + "type": "string" + }, + "metadata": { + "description": "Arbitrary key-value metadata attached to this item by the source connector.", + "example": { + "key": "value" + }, + "type": "object" + }, + "raw_content": { + "description": "Raw content payload as stored by the source connector, before normalization.", + "example": {}, + "type": "object" + }, + "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", + "type": "string" + } + }, + "required": [ + "kind", + "id" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "data" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "App-scoped token required. Use a token scoped to the target app." + }, + "404": { + "description": "Knowledge source not found" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Search within a knowledge source", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/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", @@ -42296,6 +45953,7 @@ { "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "company", "name": "Example Name" } ], @@ -42313,6 +45971,7 @@ { "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "company", "name": "Example Name" } ], @@ -42321,11 +45980,12 @@ "example": { "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "company", "name": "Example Name" }, "properties": { "domain": { - "description": "Primary domain associated with the organization, e.g. `\"acme.com\"`.", + "description": "Company domain. For personal orgs, the owner email is returned only to a viewer in that org.", "example": "acme.com", "type": "string" }, @@ -42334,6 +45994,15 @@ "example": "org_0aBcDeFgHiJkLmNoPqRsTu", "type": "string" }, + "kind": { + "description": "Whether this is a company-domain org or a person-owned personal org.", + "enum": [ + "company", + "personal" + ], + "example": "company", + "type": "string" + }, "name": { "description": "Display name of the organization.", "example": "Example Name", @@ -42343,7 +46012,7 @@ "required": [ "id", "name", - "domain" + "kind" ], "type": "object" }, @@ -42403,6 +46072,380 @@ ] } }, + "/api/v1/orgs/{org}/artifacts": { + "get": { + "description": "Returns completed system-owned organization snapshots, newest first. Private user, team, agent and thread artifacts are excluded.", + "operationId": "get_api_v1_orgs__org_artifacts", + "parameters": [ + { + "description": "Organization ID.", + "example": "string", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum snapshots returned, from 1 to 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor for the next page of older snapshots.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Exact, case-sensitive grouping key.", + "example": "string", + "in": "query", + "name": "group_key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Nonempty literal group prefix; mutually exclusive with group_key.", + "example": "string", + "in": "query", + "name": "group_key_prefix", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "current_version": "afv_0aBcDeFgHiJkLmNoPqRsTu", + "description": "An example description.", + "file": "string", + "file_name": "Example Name", + "file_url": "https://example.com", + "group_key": "string", + "id": "art_0aBcDeFgHiJkLmNoPqRsTu", + "image_source": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "sandbox": "string", + "system": true, + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "version": 1 + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "description": "Always null; pagination is forward-only.", + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "example": [ + { + "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "current_version": "afv_0aBcDeFgHiJkLmNoPqRsTu", + "description": "An example description.", + "file": "string", + "file_name": "Example Name", + "file_url": "https://example.com", + "group_key": "string", + "id": "art_0aBcDeFgHiJkLmNoPqRsTu", + "image_source": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "sandbox": "string", + "system": true, + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "version": 1 + } + ], + "items": { + "description": "A versioned artifact produced or managed by an agent, such as a generated file, report, or code output.", + "example": { + "agent": "agt_0aBcDeFgHiJkLmNoPqRsTu", + "content_type": "application/json", + "created_at": "2024-01-01T00:00:00Z", + "current_version": "afv_0aBcDeFgHiJkLmNoPqRsTu", + "description": "An example description.", + "file": "string", + "file_name": "Example Name", + "file_url": "https://example.com", + "group_key": "string", + "id": "art_0aBcDeFgHiJkLmNoPqRsTu", + "image_source": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "sandbox": "string", + "system": true, + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "version": 1 + }, + "properties": { + "agent": { + "description": "ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", + "example": "agt_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "content_type": { + "description": "MIME type of the current version's file, e.g. `\"text/csv\"` or `\"image/png\"`. `null` if no file is attached.", + "example": "application/json", + "type": "string" + }, + "created_at": { + "description": "When the artifact was first created (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "current_version": { + "description": "ID of the current (latest published) artifact version (`artv_...`). `null` if no version has been published.", + "example": "afv_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "description": { + "description": "Optional longer description of the artifact's contents or purpose. `null` if not set.", + "example": "An example description.", + "type": "string" + }, + "file": { + "description": "Storage file ID for the current version (`fil_...`). `null` if no file is attached.", + "example": "string", + "type": "string" + }, + "file_name": { + "description": "Original filename of the current version's file, e.g. `\"output.csv\"`. `null` if no file is attached.", + "example": "Example Name", + "type": "string" + }, + "file_url": { + "description": "Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", + "example": "https://example.com", + "type": "string" + }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + "example": "string", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Artifact ID (`art_...`).", + "example": "art_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "image_source": { + "description": "Image source metadata for rendering the current version's file inline. Present only when `content_type` starts with `\"image/\"`. `null` otherwise.", + "example": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "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" + }, + "name": { + "description": "Human-readable name for the artifact, e.g. `\"Q2 Report\"`. `null` if not set.", + "example": "Example Name", + "type": "string" + }, + "org": { + "description": "ID of the organization this artifact belongs to (`org_...`).", + "example": "org_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "sandbox": { + "description": "Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", + "example": "string", + "type": "string" + }, + "system": { + "description": "True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + "example": true, + "type": "boolean" + }, + "team": { + "description": "ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", + "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "thread": { + "description": "ID of the thread in which this artifact was created (`thr_...`). `null` if not thread-scoped.", + "example": "thr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "updated_at": { + "description": "When the artifact record was last modified (ISO 8601).", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "user": { + "description": "ID of the user who created this artifact (`usr_...`). `null` if not user-scoped.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "version": { + "description": "Current version number of the artifact. Increments each time a new version is published.", + "example": 1, + "type": "integer" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Invalid group filters; Invalid cursor" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Organization not found" + } + }, + "summary": "List completed organization artifacts", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/private_service_definitions/{app_id}/{private_service_id}": { "get": { "description": "Returns the canonical callable definition authorized by an enrollment token.", @@ -43044,7 +47087,7 @@ }, "/api/v1/sandboxes/{sandbox}/keys": { "post": { - "description": "Issues a new API key for the specified sandbox. Keys can be either\n`\"publishable\"` (safe to embed in client-side code) or `\"secret\"` (server-side\nonly). The full key value is returned once in the `full_key` field of this\nresponse and is never retrievable again — store it securely immediately.\n\nThe caller must authenticate with app-scoped credentials and be able to\nmodify the sandbox (org members for org sandboxes; developers / all-powerful\nfor app-level). If the sandbox does not belong to the caller's app or is not\nvisible, a 404 is returned. Multiple active keys per sandbox are supported;\nrevoke individual keys with the revoke key endpoint.\n", + "description": "Issues a new API key for the specified sandbox. Keys can be either\n`\"publishable\"` (safe to embed in client-side code) or `\"secret\"` (server-side\nonly). The full key value is returned in the `full_key` field. Secret values\nare never retrievable again — store them securely immediately. Publishable\nvalues remain available as `key_value` when retrieving the sandbox.\n\nThe caller must authenticate with app-scoped credentials and be able to\nmodify the sandbox (org members for org sandboxes; developers / all-powerful\nfor app-level). If the sandbox does not belong to the caller's app or is not\nvisible, a 404 is returned. Multiple active keys per sandbox are supported;\nrevoke individual keys with the revoke key endpoint.\n", "operationId": "post_api_v1_sandboxes__sandbox_keys", "parameters": [ { @@ -43109,6 +47152,292 @@ ] } }, + "/api/v1/scripts/language": { + "get": { + "description": "Returns the full language specification for the ArchAstro scripting engine,\nincluding keywords, operators, built-in functions, namespaces, code snippets,\nand type system information.\n\nUse this response to power editor features such as syntax highlighting,\nautocompletion, hover documentation, and snippet insertion. The specification\nis static for a given platform version; you do not need to poll it on every\nsession.\n\nRequires an authenticated viewer. No additional app scope is needed.\n", + "operationId": "get_api_v1_scripts_language", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScriptLanguageSpec" + } + } + }, + "description": "The script language specification for the current platform version." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Retrieve script language metadata", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/scripts/llm.txt": { + "get": { + "description": "Returns the plain-text system prompt used by script-authoring assistants.\nThe prompt is rendered from the current language specification so builtins,\nnamespaces, snippets, and type-system guidance stay synchronized with the\nrunning platform.\n", + "operationId": "get_api_v1_scripts_llm.txt", + "parameters": [], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Plain-text system prompt for script-authoring assistants." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Retrieve the script-authoring LLM prompt", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/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", + "operationId": "post_api_v1_scripts_run", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "resolution_context_config": "string", + "run_as_agent": "string", + "run_as_user": "string", + "scope": {}, + "script": "string" + }, + "properties": { + "resolution_context_config": { + "description": "Optional Script config ID establishing the authorized installation context for imports.", + "example": "string", + "type": "string" + }, + "run_as_agent": { + "description": "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.", + "example": "string", + "type": "string" + }, + "run_as_user": { + "description": "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.", + "example": "string", + "type": "string" + }, + "scope": { + "default": {}, + "description": "Initial scope injected into the script execution context. Use this to supply `variables`, `system`, or other top-level scope keys.", + "example": {}, + "type": "object" + }, + "script": { + "default": "", + "description": "The script source to execute. Defaults to an empty string.", + "example": "string", + "type": "string" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScriptRunResult" + } + } + }, + "description": "The script execution result, including the return value, captured output, and any runtime error or diagnostic findings." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Execution identity is not authorized" + }, + "404": { + "description": "Execution identity was not found" + }, + "422": { + "description": "run_as_user and run_as_agent are mutually exclusive" + } + }, + "summary": "Execute a workflow script", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/scripts/runtime_env_vars": { + "get": { + "description": "Returns metadata for all runtime environment variables available to scripts\nrunning within the specified app. The response lists each variable's name\nand description but does not include resolved values.\n\nUse this to surface the available `env.*` identifiers in script editor\nautocompletion or to inspect which variables are configured for an app\nbefore running a script.\n\nRequires an authenticated viewer scoped to the specified app.\n", + "operationId": "get_api_v1_scripts_runtime_env_vars", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuntimeEnvVarList" + } + } + }, + "description": "Paginated list of runtime environment variable metadata for the app." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "App-scoped token required. Use a token scoped to the target app." + } + }, + "summary": "List runtime environment variables for scripts", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/scripts/test": { + "post": { + "description": "Executes a Jest-style script test source (`describe` / `it` / `expect`) and\nreturns a structured assertion report. Use this endpoint to run unit tests\nagainst script logic during development or in CI pipelines.\n\nImports of the form `import(\"script:\")` inside the test script\nresolve against the `scripts` map supplied in the request body first, then\nfall back to deployed scripts in the caller's app. This lets you test local\nchanges to scripts before deploying them.\n\nRuntime environment variables defined for the app are automatically injected\ninto `variables.env` within the execution scope.\n\nWhen a mid-run runtime error occurs (after some tests have already executed),\nthe endpoint returns HTTP 200 with `passed: false`, the partial per-test\nbreakdown, and a top-level `error` and `findings` describing the crash. When\nthe test source itself cannot be parsed, the endpoint returns HTTP 422 with\nthe syntax error message.\n", + "operationId": "post_api_v1_scripts_test", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "resolution_context_config": "string", + "scope": {}, + "script": "string", + "scripts": {} + }, + "properties": { + "resolution_context_config": { + "description": "Optional config ID establishing the authorized installation context for imports.", + "example": "string", + "type": "string" + }, + "scope": { + "default": {}, + "description": "Optional overrides for the execution scope. Accepts `variables`, `system`, or other top-level scope keys.", + "example": {}, + "type": "object" + }, + "script": { + "default": "", + "description": "The test script source to execute. Should contain `describe` / `it` / `expect` blocks.", + "example": "string", + "type": "string" + }, + "scripts": { + "default": {}, + "description": "Optional map of local script sources keyed by lookup key (e.g. `\"my_script\"`). Used to resolve `import(\"script:\")` calls before falling back to deployed scripts in the caller's app.", + "example": {}, + "type": "object" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScriptTestRunResult" + } + } + }, + "description": "The assertion report for the test run, including per-suite and per-test results, assertion counts, captured output, and any runtime error or diagnostic findings." + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Script could not be parsed" + } + }, + "summary": "Run a script test suite", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/scripts/validate": { + "post": { + "description": "Parses and statically analyzes the provided script source, returning a list\nof diagnostic findings (errors and warnings) without executing the script.\nUse this endpoint to power real-time syntax and type checking in script\neditors.\n\nThe response always returns HTTP 200. Check the `findings` array in the\nreturned validation result for any errors or warnings. An empty `findings`\nlist means the script passed all static checks.\n\nRequires an authenticated viewer. No additional app scope is needed.\n", + "operationId": "post_api_v1_scripts_validate", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "script": "string" + }, + "properties": { + "script": { + "default": "", + "description": "The script source to validate. Defaults to an empty string.", + "example": "string", + "type": "string" + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExpressionValidation" + } + } + }, + "description": "Static analysis result containing a list of diagnostic findings for the script." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Validate a workflow script", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/slack_channel_bindings": { "get": { "description": "Returns a page of Slack channel bindings visible to the authenticated user.\nResults can be filtered by integration, team, agent, or organization. Omit all\nfilter params to retrieve every binding the caller can see.\n\nPagination is page-based. Pass `page` and `per_page` to navigate large result\nsets. `page` must be a positive integer; `per_page` must be between 1 and 100.\nInvalid values return 400.\n", @@ -43338,6 +47667,85 @@ ] } }, + "/api/v1/slack_channel_bindings/assign": { + "post": { + "description": "Makes the given agent the channel's sole resident (one resident per\nchannel: any previously attached agent is detached in the same\ntransaction and its mirror-thread read grant is revoked immediately).\nCreates a bare internal binding first when the channel has none.\n\nFetches the channel's `is_private` / `is_ext_shared` flags live from\nSlack (server-side, best-effort) to feed the fail-closed residency\ngates: a channel shared with an external workspace but not yet bound to\na customer team fails with `team_required_for_shared_channel` (bind the\nteam first via `upsert`/setup), and a private channel is member-managed\n— mutation without platform-verified in-channel evidence fails with\n`channel_membership_required`.\n\nWhen the binding is bound to a customer team, the agent is also\nenrolled as a member of that team (same pairing as `upsert`).\n", + "operationId": "post_api_v1_slack_channel_bindings_assign", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "agent_user_id": "string", + "channel_id": "string", + "slack_team_id": "string" + }, + "properties": { + "agent_user_id": { + "description": "Agent user ID to assign as the channel's sole resident.", + "example": "string", + "type": "string" + }, + "channel_id": { + "description": "Slack channel ID whose resident is being assigned (e.g. `C01234ABCDE`). A bare internal binding is created when the channel has none.", + "example": "string", + "type": "string" + }, + "slack_team_id": { + "description": "Slack workspace team ID that the channel belongs to (e.g. `T01234ABCDE`). Identifies which Slack integration to use.", + "example": "string", + "type": "string" + } + }, + "required": [ + "slack_team_id", + "channel_id", + "agent_user_id" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SlackChannelBinding" + } + } + }, + "description": "The binding after assignment, with the attached agent list." + }, + "400": { + "description": "Bad request" + }, + "401": { + "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" + }, + "404": { + "description": "Not found; Agent not found" + }, + "409": { + "description": "This channel is shared with an external workspace; bind it to a customer team before assigning an agent" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "Assign a Slack channel's resident agent", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/slack_channel_bindings/provision": { "post": { "description": "Opens a Slack Connect channel with a new customer — creating one and sending\nthe invite, or adopting a shared channel you already have — and records who is\nadding whom so the addition can finish once the customer accepts.\n\nThe returned binding is **pending**: nothing mirrors, and no per-customer Team,\nagent, or solution instance exists yet. Acceptance is asynchronous and may\nnever come. When it does, the addition completes in the background under the\nidentity of the admin who called this endpoint, re-checked live at that moment.\nA caller who has since lost their admin role does not get a substitute — the\naddition is refused and a human re-adds the customer.\n\nThe caller must be an admin of the Slack integration's own organization. This\nis the same authority the completion demands, checked here so a customer is\nnever invited into a channel whose addition can never finish.\n\nDeliberately not exposed as a script binding: this sends mail to a person\noutside the org, so it stays a vendor-admin HTTP surface.\n", @@ -46139,6 +50547,43 @@ ] } }, + "/api/v1/ssh_keys/{ssh_key}": { + "delete": { + "description": "Revokes an owned key immediately. The key remains visible as audit metadata.", + "operationId": "delete_api_v1_ssh_keys__ssh_key", + "parameters": [ + { + "description": "Registered SSH key ID (`ssk_...`).", + "example": "string", + "in": "path", + "name": "ssh_key", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + } + }, + "summary": "Revoke an SSH public key", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/status/ping": { "get": { "description": "Returns the validity and metadata of the bearer token supplied in the request.\nUse this endpoint to verify that an API key or session token is present, active,\nand unexpired before making authenticated calls.\n\nNo authentication is required to call this endpoint — it accepts any request,\nincluding those with no token at all. When a token is absent the `token.status`\nfield is `\"missing\"` and `token.active` is `false`. When a token is present but\ninvalid (expired, malformed, or referencing an unknown user) `token.active` is\n`false` and `token.status` describes the failure reason. When the token is valid,\n`token.active` is `true`, `token.status` is `\"active\"`, and `user` is populated\nwith the authenticated user's profile.\n", @@ -46188,6 +50633,9 @@ "404": { "description": "Task not found" }, + "409": { + "description": "Task changed since it was read" + }, "422": { "description": "Invalid parameters" } @@ -46281,7 +50729,7 @@ ] }, "put": { - "description": "Updates the supplied fields on a task and returns the complete updated task.\nAuthenticated users use their session identity. App-scoped developer and\nserver-to-server callers must explicitly supply the task's `org` and owner.\n`team` or `user` identifies that owner; when neither is present, `agent`\nidentifies an agent-owned task. With a team or user owner, `agent` identifies\nthe acting principal. Every reference is validated before the update.\n\nA cooperating coding-session client may supply both `lease_id` and\n`lease_session_id`. The task aggregate fences that update against the live\nlease and records server-sourced session provenance. Omitting both remains a\nnormal authorized human/API update.\n", + "description": "Updates the supplied fields on a task and returns the complete updated task.\nAuthenticated users use their session identity. App-scoped developer and\nserver-to-server callers must explicitly supply the task's `org` and owner.\n`team` or `user` identifies that owner; when neither is present, `agent`\nidentifies an agent-owned task. With a team or user owner, `agent` identifies\nthe acting principal. Every reference is validated before the update.\n\nA cooperating coding-session client may supply both `lease_id` and\n`lease_session_id`. The task aggregate fences that update against the live\nlease and records server-sourced session provenance. Omitting both remains a\nnormal authorized human/API update.\n\nSupply `expected_version` from the latest task representation to make the\nupdate conditional. A concurrent write returns `task_version_conflict`.\n", "operationId": "put_api_v1_tasks__task", "parameters": [ { @@ -46304,6 +50752,7 @@ "description": "An example description.", "due_date": "2024-01-01T00:00:00Z", "epic": "string", + "expected_version": 1, "lease_id": "string", "lease_session_id": "string", "links": {}, @@ -46348,6 +50797,11 @@ "example": "string", "type": "string" }, + "expected_version": { + "description": "Aggregate version returned by the latest task read. The update fails with `task_version_conflict` if the task changed first.", + "example": 1, + "type": "integer" + }, "lease_id": { "description": "Current caller-held lease UUID. Must be paired with `lease_session_id`.", "example": "string", @@ -46416,7 +50870,7 @@ "type": "string" }, "status": { - "description": "Updated status: `open`, `in_progress`, or `done`.", + "description": "Updated status: `open`, `in_progress`, `in_review`, or `done`.", "example": "string", "type": "string" }, @@ -46468,7 +50922,7 @@ "description": "Task not found" }, "409": { - "description": "Task lease has expired; Task lease does not match the current holder" + "description": "Task lease has expired; Task lease does not match the current holder; Task changed since it was read" }, "422": { "description": "Invalid parameters" @@ -46741,6 +51195,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -46827,6 +51282,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -46902,6 +51358,7 @@ "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, @@ -46979,6 +51436,12 @@ "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 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, @@ -47343,7 +51806,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -47652,6 +52115,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -47738,6 +52202,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -47813,6 +52278,7 @@ "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, @@ -47890,6 +52356,12 @@ "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 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, @@ -48254,7 +52726,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -49029,6 +53501,7 @@ "application/json": { "schema": { "example": { + "force": true, "harness": "string", "lease_duration_seconds": 1, "lease_id": "string", @@ -49037,6 +53510,12 @@ "session_name": "Example Name" }, "properties": { + "force": { + "default": false, + "description": "Take the lease from a live but dead holder instead of returning a conflict. Releases the prior session's lease and claims the new one atomically. Always scoped to the task's assigned owner, same as an ordinary claim.", + "example": true, + "type": "boolean" + }, "harness": { "description": "Bounded harness identifier.", "example": "string", @@ -49238,6 +53717,180 @@ "bearer" ] }, + "get": { + "description": "Returns indexed link identities ordered by external_scope, object_type, then object_id ascending. This does not read the legacy Task links map.", + "operationId": "get_api_v1_tasks__task_links", + "parameters": [ + { + "description": "Task ID (`tsk_...`).", + "example": "string", + "in": "path", + "name": "task", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Explicit owning team for privileged calls.", + "example": "string", + "in": "query", + "name": "team", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Explicit owning user for privileged calls.", + "example": "string", + "in": "query", + "name": "user", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Explicit owning agent for privileged calls.", + "example": "string", + "in": "query", + "name": "agent", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Explicit organization for privileged calls; must agree with owner.", + "example": "string", + "in": "query", + "name": "org", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum links 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": "External links attached to the task.", + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "external_scope": "string", + "object_id": "string", + "object_type": "string" + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "type": "string" + }, + "before_cursor": { + "example": "string", + "type": "string" + }, + "data": { + "example": [ + { + "external_scope": "string", + "object_id": "string", + "object_type": "string" + } + ], + "items": { + "description": "An indexed external object linked to a task.", + "example": { + "external_scope": "string", + "object_id": "string", + "object_type": "string" + }, + "properties": { + "external_scope": { + "description": "External container identity.", + "example": "string", + "type": "string" + }, + "object_id": { + "description": "Object identity within that container.", + "example": "string", + "type": "string" + }, + "object_type": { + "description": "External object kind.", + "example": "string", + "type": "string" + } + }, + "required": [ + "external_scope", + "object_type", + "object_id" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Task not found" + }, + "422": { + "description": "Invalid parameters" + } + }, + "summary": "List a task's external links", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, "post": { "operationId": "post_api_v1_tasks__task_links", "parameters": [ @@ -49294,11 +53947,11 @@ "content": { "application/json": { "schema": { - "type": "object" + "$ref": "#/components/schemas/TaskExternalLink" } } }, - "description": "The created external link." + "description": "The created external link, with its identity normalized." }, "401": { "description": "Unauthorized" @@ -49408,6 +54061,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -49494,6 +54148,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -49569,6 +54224,7 @@ "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, @@ -49646,6 +54302,12 @@ "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 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, @@ -50010,7 +54672,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -50221,6 +54883,9 @@ }, "404": { "description": "Team or member not found" + }, + "409": { + "description": "Conflict — cannot remove the last owner" } }, "summary": "Remove a team membership by ID", @@ -51591,6 +56256,26 @@ "description": "Returns all artifacts owned by the specified team. Artifacts represent\nAI-generated or user-uploaded files associated with agent sessions,\nthreads, or sandboxes — such as images, documents, and code outputs.\n\nThe authenticated user must be a member of the team. Attempting to list\nartifacts for a team the caller does not have access to returns 404\nrather than 403 to avoid leaking team existence.\n\nResults are returned in a single page without cursor pagination. Each\nartifact in the response reflects the state of its current version,\nincluding a short-lived signed `file_url` for direct download.\n", "operationId": "get_api_v1_teams__team_artifacts", "parameters": [ + { + "description": "Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match.", + "example": "string", + "in": "query", + "name": "group_key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key.", + "example": "string", + "in": "query", + "name": "group_key_prefix", + "required": false, + "schema": { + "type": "string" + } + }, { "description": "Team ID (`tea_...`). The authenticated user must be a member of this team.", "example": "string", @@ -51619,6 +56304,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -51632,6 +56318,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -51653,6 +56340,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -51666,6 +56354,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -51684,6 +56373,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -51697,6 +56387,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -51745,6 +56436,12 @@ "example": "https://example.com", "type": "string" }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + "example": "string", + "nullable": true, + "type": "string" + }, "id": { "description": "Artifact ID (`art_...`).", "example": "art_0aBcDeFgHiJkLmNoPqRsTu", @@ -51822,6 +56519,11 @@ "example": "string", "type": "string" }, + "system": { + "description": "True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + "example": true, + "type": "boolean" + }, "team": { "description": "ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -51866,6 +56568,9 @@ }, "description": "Successful response" }, + "400": { + "description": "Invalid group filters" + }, "401": { "description": "Unauthorized" }, @@ -52447,7 +57152,7 @@ }, "/api/v1/teams/{team}/invite": { "post": { - "description": "Generates a new invite code for the specified team. The authenticated user\nmust be a member of the team with the `owner` or `admin` role.\n\nThe returned code is a short alphanumeric string that other users can\npresent to join the team. Each call produces a new code; previously issued\ncodes are not invalidated by this request.\n", + "description": "Generates a new invite code for the specified team. The authenticated user\nmust be a member of the team with the `owner` or `admin` role.\n\nThe returned code is a short alphanumeric string that other users can\npresent to join the team. Each call produces a new code; previously issued\ncodes are not invalidated by this request. Codes expire after seven days.\n\nSharing a code intentionally grants membership across organizations within\nthe same application, including access to objects shared with team members.\nTreat it as a bearer credential. Revoke an individual code with\nDELETE /api/v1/teams/:team/invite/:code before issuing a replacement.\nRevocation does not remove existing members or their access.\n", "operationId": "post_api_v1_teams__team_invite", "parameters": [ { @@ -52489,6 +57194,53 @@ ] } }, + "/api/v1/teams/{team}/invite/{code}": { + "delete": { + "description": "Deletes one invite code for this team. Requires an owner or admin membership.\nFuture redemption returns 404, as for an unknown or expired code. Existing\nmemberships and their access are unchanged; remove members separately to end\ntheir access. Redemption already in flight is not cancelled.\n\nTo replace a code, revoke it and then create a new invite. Creating a new\ninvite alone does not invalidate any previous code. Other codes remain valid.\n", + "operationId": "delete_api_v1_teams__team_invite__code", + "parameters": [ + { + "description": "Team ID (`tm_...`).", + "example": "string", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The invite code to revoke.", + "example": "string", + "in": "path", + "name": "code", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Team or invite not found" + } + }, + "summary": "Revoke a team invite code", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/teams/{team}/invites": { "post": { "description": "Generates a new invite code for the specified team using server-to-server\nauthentication. Unlike the user-facing create endpoint, this variant does not\nrequire the caller to be a team member — it is intended for privileged\nback-end services acting on behalf of your platform.\n\nThe returned code is a short alphanumeric string that users can present to\njoin the team. Each call produces a new code; previously issued codes are\nnot invalidated by this request.\n", @@ -52689,6 +57441,9 @@ }, "404": { "description": "Team or member not found" + }, + "409": { + "description": "Conflict — cannot remove the last owner" } }, "summary": "Remove a member or org from a team", @@ -52778,6 +57533,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -52836,6 +57594,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53010,6 +57771,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53068,6 +57832,183 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], + "kind": "Solution", + "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", + "latest_version": "1.0.0", + "lookup_key": "string", + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_logo": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "org_name": "Example Name", + "org_slug": "example-slug", + "owners": [ + "string" + ], + "readme_url": "https://example.com", + "screenshot_urls": [ + "https://example.com" + ], + "solution_id": "01234567-89ab-cdef-0123-456789abcdef", + "solution_version": "1.2.0", + "tag_keys": [ + "string" + ], + "template_kind": "AgentTemplate", + "templates": [ + { + "description": "An example description.", + "display_name": "Example Name", + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "AgentTemplate", + "lookup_key": "string", + "name": "Example Name", + "readme_url": "https://example.com", + "virtual_path": "string" + } + ], + "updated_at": "2024-01-01T00:00:00Z", + "upgrade_available": true, + "virtual_path": "string" + }, + "template": { + "created_at": "2024-01-01T00:00:00Z", + "description": "An example description.", + "display_name": "Example Name", + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "agent_tool_template", + "lookup_key": "string", + "name": "Example Name", + "updated_at": "2024-01-01T00:00:00Z", + "virtual_path": "string" + } + }, + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "template_upgrade_available": true, + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + }, + "created_at": "2024-01-01T00:00:00Z", + "id": "tmb_0aBcDeFgHiJkLmNoPqRsTu", + "joined_at": "2024-01-01T00:00:00Z", + "metadata": { + "key": "value" + }, + "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 + }, + "role": "member", + "team": {}, + "type": "user", + "updated_at": "2024-01-01T00:00:00Z", + "user": { + "alias": "jdoe", + "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", + "app_name": "Example Name", + "created_by_agent_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_developer": "dva_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "email": "user@example.com", + "id": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "is_system_user": true, + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_name": "Example Name", + "org_role": "member", + "org_slug": "example-slug", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "sandbox_name": "Example Name" + } + } + ], + "items": { + "description": "A record representing a user's or agent's membership in a team, including their resolved identity details and role.", + "example": { + "agent": { + "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" + } + ] + }, + "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "2024-01-01T00:00:00Z", + "default_model": "claude-3-7-sonnet-latest", + "description": "An example description.", + "email": "user@example.com", + "id": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "identity": "You are a helpful assistant that answers questions about ArchAstro products.", + "last_applied_template_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", + "lookup_key": "string", + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_name": "Example Name", + "originator": "deploy-pipeline", + "phone_number": "+15555550123", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_solution": { + "current_solution": { + "category_keys": [ + "string" + ], + "created_at": "2024-01-01T00:00:00Z", + "description": "An example description.", + "events": {}, + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53117,120 +58058,7 @@ "upgrade_available": true, "virtual_path": "string" }, - "template": { - "created_at": "2024-01-01T00:00:00Z", - "description": "An example description.", - "display_name": "Example Name", - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "kind": "agent_tool_template", - "lookup_key": "string", - "name": "Example Name", - "updated_at": "2024-01-01T00:00:00Z", - "virtual_path": "string" - } - }, - "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", - "template_upgrade_available": true, - "updated_at": "2024-01-01T00:00:00Z", - "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" - }, - "created_at": "2024-01-01T00:00:00Z", - "id": "tmb_0aBcDeFgHiJkLmNoPqRsTu", - "joined_at": "2024-01-01T00:00:00Z", - "metadata": { - "key": "value" - }, - "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 - }, - "role": "member", - "team": {}, - "type": "user", - "updated_at": "2024-01-01T00:00:00Z", - "user": { - "alias": "jdoe", - "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", - "app_name": "Example Name", - "created_by_agent_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", - "created_by_developer": "dva_0aBcDeFgHiJkLmNoPqRsTu", - "created_by_org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "created_by_team": "tem_0aBcDeFgHiJkLmNoPqRsTu", - "created_by_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", - "email": "user@example.com", - "id": "usr_0aBcDeFgHiJkLmNoPqRsTu", - "is_system_user": true, - "metadata": { - "key": "value" - }, - "name": "Example Name", - "org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "org_name": "Example Name", - "org_role": "member", - "org_slug": "example-slug", - "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", - "sandbox_name": "Example Name" - } - } - ], - "items": { - "description": "A record representing a user's or agent's membership in a team, including their resolved identity details and role.", - "example": { - "agent": { - "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" - } - ] - }, - "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", - "created_at": "2024-01-01T00:00:00Z", - "default_model": "claude-3-7-sonnet-latest", - "description": "An example description.", - "email": "user@example.com", - "id": "agi_0aBcDeFgHiJkLmNoPqRsTu", - "identity": "You are a helpful assistant that answers questions about ArchAstro products.", - "last_applied_template_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", - "lookup_key": "string", - "metadata": { - "key": "value" - }, - "name": "Example Name", - "org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "org_name": "Example Name", - "originator": "deploy-pipeline", - "phone_number": "+15555550123", - "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", - "source_solution": { - "current_solution": { + "solution": { "category_keys": [ "string" ], @@ -53239,64 +58067,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", - "kind": "Solution", - "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", - "latest_version": "1.0.0", - "lookup_key": "string", - "metadata": { - "key": "value" - }, - "name": "Example Name", - "org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "org_logo": { - "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", - "height": 600, - "media": "med_0aBcDeFgHiJkLmNoPqRsTu", - "mime_type": "application/json", - "refresh_url": "https://example.com", - "url": "https://example.com", - "width": 800 - }, - "org_name": "Example Name", - "org_slug": "example-slug", - "owners": [ - "string" - ], - "readme_url": "https://example.com", - "screenshot_urls": [ - "https://example.com" - ], - "solution_id": "01234567-89ab-cdef-0123-456789abcdef", - "solution_version": "1.2.0", - "tag_keys": [ + "installed_config_ids": [ "string" ], - "template_kind": "AgentTemplate", - "templates": [ - { - "description": "An example description.", - "display_name": "Example Name", - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "kind": "AgentTemplate", - "lookup_key": "string", - "name": "Example Name", - "readme_url": "https://example.com", - "virtual_path": "string" - } - ], - "updated_at": "2024-01-01T00:00:00Z", - "upgrade_available": true, - "virtual_path": "string" - }, - "solution": { - "category_keys": [ - "string" - ], - "created_at": "2024-01-01T00:00:00Z", - "description": "An example description.", - "events": {}, - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "image_url": "https://example.com", "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53467,6 +58240,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53525,6 +58301,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53872,6 +58651,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -53930,6 +58712,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -54004,6 +58789,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -54086,11 +58874,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -54515,6 +59313,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -54530,6 +59329,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -54612,11 +59414,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -55041,6 +59853,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -55882,7 +60695,7 @@ } }, { - "description": "Filter tasks by status. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Omit to return tasks in all statuses.", + "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", @@ -56084,6 +60897,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -56173,6 +60987,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -56248,6 +61063,7 @@ "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, @@ -56325,6 +61141,12 @@ "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 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, @@ -56689,7 +61511,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -56799,6 +61621,7 @@ "description": "An example description.", "due_date": "2024-01-01T00:00:00Z", "epic": "billing-cadence", + "id": "string", "links": { "key": "value" }, @@ -56839,6 +61662,7 @@ "description": "An example description.", "due_date": "2024-01-01T00:00:00Z", "epic": "billing-cadence", + "id": "string", "links": { "key": "value" }, @@ -56877,6 +61701,11 @@ "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": { @@ -56932,7 +61761,7 @@ "type": "string" }, "status": { - "description": "Initial status for the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Defaults to `\"open\"` when omitted.", + "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" }, @@ -56990,6 +61819,9 @@ "404": { "description": "Task owner not found" }, + "409": { + "description": "Conflict" + }, "422": { "description": "Validation error" } @@ -57071,6 +61903,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -57161,6 +61994,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -57240,6 +62074,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -57318,6 +62153,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -57393,6 +62229,7 @@ "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, @@ -57470,6 +62307,12 @@ "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 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, @@ -57834,7 +62677,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -58172,6 +63015,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -58267,6 +63111,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -58346,6 +63191,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -58442,6 +63288,7 @@ "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, @@ -58519,6 +63366,12 @@ "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 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, @@ -58883,7 +63736,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -59032,7 +63885,7 @@ } }, { - "description": "Filter results by status. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Omit to include all statuses.", + "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", @@ -59164,6 +64017,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -59252,6 +64106,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -59327,6 +64182,7 @@ "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, @@ -59404,6 +64260,12 @@ "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 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, @@ -59768,7 +64630,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -60018,6 +64880,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -60142,6 +65012,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -60200,6 +65073,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -60415,6 +65291,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -60539,6 +65423,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -60597,6 +65484,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -60809,6 +65699,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -60933,6 +65831,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -60991,6 +65892,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -61429,6 +66333,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -62222,6 +67134,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -62449,6 +67410,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -62940,6 +67909,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -62998,6 +67970,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63124,6 +68099,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63182,6 +68160,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63528,6 +68509,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63586,6 +68570,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63660,6 +68647,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -63742,11 +68732,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -64171,6 +69171,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -64186,6 +69187,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -64268,11 +69272,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -64697,6 +69711,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -66237,6 +71252,26 @@ "description": "Returns all artifacts produced during a thread's AI conversation. Artifacts are\nstructured outputs such as code files, documents, or generated assets created\nby the AI agent in response to messages in the thread.\n\nThe authenticated user must have access to the specified thread. Results are\nreturned in a single page; there is no cursor-based pagination for this endpoint.\n", "operationId": "get_api_v1_threads__thread_artifacts", "parameters": [ + { + "description": "Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match.", + "example": "string", + "in": "query", + "name": "group_key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key.", + "example": "string", + "in": "query", + "name": "group_key_prefix", + "required": false, + "schema": { + "type": "string" + } + }, { "description": "Thread ID (`thr_...`). Must be accessible to the authenticated user.", "example": "string", @@ -66265,6 +71300,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -66278,6 +71314,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -66299,6 +71336,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -66312,6 +71350,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -66330,6 +71369,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -66343,6 +71383,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -66391,6 +71432,12 @@ "example": "https://example.com", "type": "string" }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + "example": "string", + "nullable": true, + "type": "string" + }, "id": { "description": "Artifact ID (`art_...`).", "example": "art_0aBcDeFgHiJkLmNoPqRsTu", @@ -66468,6 +71515,11 @@ "example": "string", "type": "string" }, + "system": { + "description": "True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + "example": true, + "type": "boolean" + }, "team": { "description": "ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -66512,6 +71564,9 @@ }, "description": "Successful response" }, + "400": { + "description": "Invalid group filters" + }, "401": { "description": "Unauthorized" }, @@ -67307,6 +72362,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -67454,6 +72517,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -67614,6 +72685,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -67753,6 +72832,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "string", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -67880,6 +72967,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -68680,6 +73775,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -68907,6 +74051,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -69338,7 +74490,7 @@ }, "/api/v1/threads/{thread}/search": { "get": { - "description": "Searches canonical message content in the specified thread. `\"text\"` mode\nperforms the existing case-insensitive substring search, `\"embedding\"` ranks\nstored message embeddings by cosine similarity, and `\"hybrid\"` combines the\ntext and embedding rankings with Reciprocal Rank Fusion (RRF). Only messages\nvisible to the authenticated caller are considered.\n\nResults are intentionally lean: each row contains only a bounded content\nsnippet, sender identity, and timestamp. Attachments, reactions, ACLs, and\nmetadata are neither hydrated nor serialized. At most 20 results are\nreturned. Text results support chronological cursor pagination. Embedding and\nhybrid results are relevance-ranked single pages and return null cursors.\n", + "description": "Searches canonical message content in the specified thread. `\"text\"` mode\nperforms the existing case-insensitive substring search, `\"embedding\"` ranks\nstored message embeddings by cosine similarity, and `\"hybrid\"` combines the\ntext and embedding rankings with Reciprocal Rank Fusion (RRF). Only messages\nvisible to the authenticated caller are considered.\n\nResults are intentionally lean: each row contains a bounded content snippet,\nsender identity, timestamp, and a small allowlisted structured-post evidence\nprojection. Attachments, reactions, ACLs, and arbitrary metadata are neither\nhydrated nor serialized. At most 20 results are returned. Text results\nsupport chronological cursor pagination. Embedding and hybrid results are\nrelevance-ranked single pages and return null cursors.\n", "operationId": "get_api_v1_threads__thread_search", "parameters": [ { @@ -69431,6 +74583,7 @@ "agent": "string", "content": "string", "created_at": "2024-01-01T00:00:00Z", + "evidence": {}, "id": "string", "similarity_score": 1.0, "user": "string" @@ -69456,17 +74609,19 @@ "agent": "string", "content": "string", "created_at": "2024-01-01T00:00:00Z", + "evidence": {}, "id": "string", "similarity_score": 1.0, "user": "string" } ], "items": { - "description": "A lean message search result.\n\nSearch results intentionally omit attachment, reaction, ACL, and metadata\npayloads so searching a large thread does not hydrate its message history.\n", + "description": "A lean message search result.\n\nSearch results intentionally omit attachment, reaction, ACL, and arbitrary\nmetadata payloads so searching a large thread does not hydrate its message\nhistory. A small allowlisted evidence projection preserves fields needed to\ninterpret structured lifecycle posts.\n", "example": { "agent": "string", "content": "string", "created_at": "2024-01-01T00:00:00Z", + "evidence": {}, "id": "string", "similarity_score": 1.0, "user": "string" @@ -69488,6 +74643,11 @@ "format": "date-time", "type": "string" }, + "evidence": { + "description": "Bounded, allowlisted structured-post evidence such as source, lifecycle type, references, repository context, and addressee. Empty for ordinary messages.", + "example": {}, + "type": "object" + }, "id": { "description": "Message ID (`msg_...`).", "example": "string", @@ -69507,7 +74667,8 @@ "required": [ "id", "content", - "created_at" + "created_at", + "evidence" ], "type": "object" }, @@ -70176,20 +75337,42 @@ "get": { "description": "Returns the user associated with the authenticated session or bearer\ntoken. This is the canonical way to resolve \"who am I?\" after\nauthentication.\n\nThe response includes the user's profile, notification settings, and\nprofile picture, along with the app, organization, and sandbox the\ntoken is scoped to and their display names — enough to establish full\nsession context in a single call. Unauthenticated requests return 401.\n", "operationId": "get_api_v1_users_me", - "parameters": [], + "parameters": [ + { + "description": "Entitlement catalog keys to evaluate. Supplying this expansion additionally requires `entitlements:read`.", + "example": [ + "string" + ], + "in": "query", + "name": "entitlement", + "required": false, + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/CurrentUser" } } }, "description": "The authenticated user object." }, + "400": { + "description": "Unknown entitlement catalog key" + }, "401": { "description": "Unauthorized" + }, + "403": { + "description": "Insufficient OAuth scope" } }, "summary": "Retrieve the current user", @@ -70249,6 +75432,26 @@ "description": "Returns all artifacts owned by the specified user. Artifacts represent\nAI-generated or user-uploaded files associated with agent sessions,\nthreads, or sandboxes — such as images, documents, and code outputs.\n\nThe authenticated user must be requesting their own artifacts or must\nhave administrative access. Attempting to list artifacts for a user\nthe caller is not authorized to access returns 403.\n\nResults are returned in a single page without cursor pagination. Each\nartifact in the response reflects the state of its current version,\nincluding a short-lived signed `file_url` for direct download.\n", "operationId": "get_api_v1_users__user_artifacts", "parameters": [ + { + "description": "Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match.", + "example": "string", + "in": "query", + "name": "group_key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key.", + "example": "string", + "in": "query", + "name": "group_key_prefix", + "required": false, + "schema": { + "type": "string" + } + }, { "description": "User ID (`usr_...`). The authenticated user must be this user or have access to their artifacts.", "example": "string", @@ -70277,6 +75480,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -70290,6 +75494,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70311,6 +75516,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -70324,6 +75530,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70342,6 +75549,7 @@ "file": "string", "file_name": "Example Name", "file_url": "https://example.com", + "group_key": "string", "id": "art_0aBcDeFgHiJkLmNoPqRsTu", "image_source": { "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", @@ -70355,6 +75563,7 @@ "name": "Example Name", "org": "org_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "string", + "system": true, "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", "thread": "thr_0aBcDeFgHiJkLmNoPqRsTu", "updated_at": "2024-01-01T00:00:00Z", @@ -70403,6 +75612,12 @@ "example": "https://example.com", "type": "string" }, + "group_key": { + "description": "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + "example": "string", + "nullable": true, + "type": "string" + }, "id": { "description": "Artifact ID (`art_...`).", "example": "art_0aBcDeFgHiJkLmNoPqRsTu", @@ -70480,6 +75695,11 @@ "example": "string", "type": "string" }, + "system": { + "description": "True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + "example": true, + "type": "boolean" + }, "team": { "description": "ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", "example": "tem_0aBcDeFgHiJkLmNoPqRsTu", @@ -70524,6 +75744,9 @@ }, "description": "Successful response" }, + "400": { + "description": "Invalid group filters" + }, "401": { "description": "Unauthorized" }, @@ -70538,6 +75761,131 @@ ] } }, + "/api/v1/users/{user}/integrations/{provider}/access_token": { + "get": { + "description": "Returns a valid GitHub access token for one integration owned by the\nauthenticated user. Expired tokens are refreshed before the response is\nreturned.\n\nThis endpoint accepts the user's own bearer token, or the app's secret key\nacting server-to-server for one of its users. Developer, agent, team, and\ncross-user viewers cannot retrieve the credential. Responses are marked\n`Cache-Control: no-store`.\n", + "operationId": "get_api_v1_users__user_integrations__provider_access_token", + "parameters": [ + { + "description": "User ID (`usr_...`). The authenticated user, or a user of the app whose secret key is presented.", + "example": "string", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Integration provider. Only `github` is accepted.", + "example": "string", + "in": "path", + "name": "provider", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "The valid GitHub credential and its integration metadata.", + "example": { + "access_token": "string", + "expires_at": "2024-01-01T00:00:00Z", + "integration": "string", + "provider": "string", + "scopes": [ + "string" + ], + "token_kind": "string", + "workspace_key": "string" + }, + "properties": { + "access_token": { + "description": "GitHub access token. Treat this value as a secret.", + "example": "string", + "type": "string" + }, + "expires_at": { + "description": "Access-token expiry, or null for non-expiring tokens.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "integration": { + "description": "Integration ID (`int_...`).", + "example": "string", + "type": "string" + }, + "provider": { + "description": "Always `github`.", + "example": "string", + "type": "string" + }, + "scopes": { + "description": "Recorded GitHub OAuth scopes.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "token_kind": { + "description": "Credential authority model: `oauth` or `github_app_user`.", + "example": "string", + "type": "string" + }, + "workspace_key": { + "description": "GitHub login associated with the credential.", + "example": "string", + "type": "string" + } + }, + "required": [ + "access_token", + "integration", + "provider", + "scopes", + "token_kind" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found; OAuth provider not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Invalid parameters" + }, + "502": { + "description": "Service unavailable" + } + }, + "summary": "Retrieve a GitHub integration access token", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, "/api/v1/users/{user}/invites": { "post": { "description": "Creates a new invite for the authenticated user. The invite can optionally be\nscoped to a specific thread, a persona, or carry arbitrary metadata. The\ncaller receives the new invite object at HTTP 201.\n\nThe invite key is always generated server-side (192-bit URL-safe random\nstring) and cannot be supplied by the caller.\n\nThe path `:user` must match the authenticated user. If a `thread_id` is\nprovided, the authenticated user must have permission to invite others to that\nthread; team threads are not supported and return an error. Supplying a\n`thread_id` that does not exist or that belongs to a different user returns\nan error. If a key collision occurs during creation the call returns a 409\nconflict — simply retry to generate a new key.\n", @@ -70672,12 +76020,14 @@ "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", "industry": "fintech", + "kind": "company", "name": "Example Name", "onboarding_solution_lookup_key": "string", "onboarding_track": "vendor", "owned_products": [ "agent-rooms" ], + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "slug": "example-slug", "status": "active", @@ -70696,12 +76046,14 @@ "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", "industry": "fintech", + "kind": "company", "name": "Example Name", "onboarding_solution_lookup_key": "string", "onboarding_track": "vendor", "owned_products": [ "agent-rooms" ], + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "slug": "example-slug", "status": "active", @@ -70717,12 +76069,14 @@ "domain": "acme.com", "id": "org_0aBcDeFgHiJkLmNoPqRsTu", "industry": "fintech", + "kind": "company", "name": "Example Name", "onboarding_solution_lookup_key": "string", "onboarding_track": "vendor", "owned_products": [ "agent-rooms" ], + "owner_user": "usr_0aBcDeFgHiJkLmNoPqRsTu", "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", "slug": "example-slug", "status": "active", @@ -70756,6 +76110,15 @@ "example": "fintech", "type": "string" }, + "kind": { + "description": "Whether this is a company-domain org or a person-owned personal org.", + "enum": [ + "company", + "personal" + ], + "example": "company", + "type": "string" + }, "name": { "description": "Display name of the organization. `null` if the org has not set a name.", "example": "Example Name", @@ -70781,6 +76144,11 @@ }, "type": "array" }, + "owner_user": { + "description": "Owner user ID for a personal org. `null` for company orgs.", + "example": "usr_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, "sandbox": { "description": "ID of the sandbox environment scoped to this organization (`snd_...`). `null` for organizations in production mode.", "example": "dsb_0aBcDeFgHiJkLmNoPqRsTu", @@ -70809,7 +76177,8 @@ } }, "required": [ - "id" + "id", + "kind" ], "type": "object" }, @@ -70858,6 +76227,8 @@ "schema": { "example": { "alias": "string", + "clear_full_name": true, + "clear_profile_picture": true, "full_name": "Example Name", "metadata": { "key": "value" @@ -70874,9 +76245,20 @@ "example": "string", "type": "string" }, + "clear_full_name": { + "description": "Set to true to clear the optional display name. Cannot be combined with full_name.", + "example": true, + "type": "boolean" + }, + "clear_profile_picture": { + "description": "Set to true to remove the current profile picture. Cannot be combined with profile_picture.", + "example": true, + "type": "boolean" + }, "full_name": { "description": "Updated display name for the user.", "example": "Example Name", + "nullable": true, "type": "string" }, "metadata": { @@ -70893,6 +76275,7 @@ "filename": "string", "mime_type": "application/json" }, + "nullable": true, "properties": { "data": { "description": "Base64-encoded binary content of the image file.", @@ -70944,6 +76327,245 @@ ] } }, + "/api/v1/users/{user}/ssh_keys": { + "get": { + "description": "Returns a cursor-paginated page of SSH-key metadata for the authenticated user.", + "operationId": "get_api_v1_users__user_ssh_keys", + "parameters": [ + { + "description": "User ID (`usr_...`) or `me`.", + "example": "string", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Maximum keys per page. Defaults to 50; maximum is 100.", + "example": 1, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "description": "Opaque cursor for the next page of older keys.", + "example": "string", + "in": "query", + "name": "after_cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Cursor-paginated registered SSH-key metadata.", + "example": { + "after_cursor": "string", + "before_cursor": "string", + "data": [ + { + "algorithm": "string", + "created_at": "2024-01-01T00:00:00Z", + "fingerprint": "string", + "id": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "label": "string", + "revoked_at": "2024-01-01T00:00:00Z" + } + ], + "has_more": true + }, + "properties": { + "after_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "before_cursor": { + "example": "string", + "nullable": true, + "type": "string" + }, + "data": { + "example": [ + { + "algorithm": "string", + "created_at": "2024-01-01T00:00:00Z", + "fingerprint": "string", + "id": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "label": "string", + "revoked_at": "2024-01-01T00:00:00Z" + } + ], + "items": { + "description": "Metadata for a registered SSH public key. Key material is never returned.", + "example": { + "algorithm": "string", + "created_at": "2024-01-01T00:00:00Z", + "fingerprint": "string", + "id": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "label": "string", + "revoked_at": "2024-01-01T00:00:00Z" + }, + "properties": { + "algorithm": { + "description": "SSH algorithm. V1 accepts `ssh-ed25519`.", + "example": "string", + "type": "string" + }, + "created_at": { + "description": "Registration time.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "fingerprint": { + "description": "OpenSSH SHA-256 fingerprint.", + "example": "string", + "type": "string" + }, + "id": { + "description": "Registered SSH key ID (`ssk_...`).", + "example": "ssk_0aBcDeFgHiJkLmNoPqRsTu", + "type": "string" + }, + "label": { + "description": "User-visible label for the key.", + "example": "string", + "type": "string" + }, + "revoked_at": { + "description": "Revocation time, or null while active.", + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "label", + "algorithm", + "fingerprint", + "created_at" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "data", + "has_more" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "400": { + "description": "Invalid cursor" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "summary": "List registered SSH keys", + "x-auth": [ + "publishable_key", + "bearer" + ] + }, + "post": { + "description": "Registers one comment-free Ed25519 public key for Git-over-SSH. The key is\nencrypted before storage; responses expose only its label, algorithm and\nOpenSSH SHA-256 fingerprint.\n\nThe caller must be the user named by `user` and must present a first-party\nsession or a `full_access` personal access token.\n", + "operationId": "post_api_v1_users__user_ssh_keys", + "parameters": [ + { + "description": "User ID (`usr_...`) or `me`.", + "example": "string", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "label": "string", + "public_key": "string" + }, + "properties": { + "label": { + "description": "A label such as `Work laptop`.", + "example": "string", + "type": "string" + }, + "public_key": { + "description": "One `ssh-ed25519 ` public key without options or a comment.", + "example": "string", + "type": "string" + } + }, + "required": [ + "label", + "public_key" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSSHKey" + } + } + }, + "description": "Metadata for the registered key; no key material." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "422": { + "description": "Validation failed" + } + }, + "summary": "Register an SSH public key", + "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", @@ -70980,7 +76602,7 @@ } }, { - "description": "Filter tasks by status. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Omit to return tasks in all statuses.", + "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", @@ -71182,6 +76804,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -71271,6 +76894,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -71346,6 +76970,7 @@ "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, @@ -71423,6 +77048,12 @@ "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 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, @@ -71787,7 +77418,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -71897,6 +77528,7 @@ "description": "An example description.", "due_date": "2024-01-01T00:00:00Z", "epic": "billing-cadence", + "id": "string", "links": { "key": "value" }, @@ -71937,6 +77569,7 @@ "description": "An example description.", "due_date": "2024-01-01T00:00:00Z", "epic": "billing-cadence", + "id": "string", "links": { "key": "value" }, @@ -71975,6 +77608,11 @@ "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": { @@ -72030,7 +77668,7 @@ "type": "string" }, "status": { - "description": "Initial status for the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Defaults to `\"open\"` when omitted.", + "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" }, @@ -72088,6 +77726,9 @@ "404": { "description": "Task owner not found" }, + "409": { + "description": "Conflict" + }, "422": { "description": "Validation error" } @@ -72169,6 +77810,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -72259,6 +77901,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -72338,6 +77981,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -72416,6 +78060,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -72491,6 +78136,7 @@ "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, @@ -72568,6 +78214,12 @@ "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 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, @@ -72932,7 +78584,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -73157,6 +78809,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -73252,6 +78905,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -73331,6 +78985,7 @@ "reason": "open_blockers", "task": { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -73427,6 +79082,7 @@ "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, @@ -73504,6 +79160,12 @@ "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 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, @@ -73868,7 +79530,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -74017,7 +79679,7 @@ } }, { - "description": "Filter results by status. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`. Omit to include all statuses.", + "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", @@ -74149,6 +79811,7 @@ "data": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -74237,6 +79900,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -74312,6 +79976,7 @@ "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, @@ -74389,6 +80054,12 @@ "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 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, @@ -74753,7 +80424,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, @@ -75050,6 +80721,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -75174,6 +80853,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -75232,6 +80914,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -75447,6 +81132,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -75571,6 +81264,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -75629,6 +81325,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -75841,6 +81540,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -75965,6 +81672,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -76023,6 +81733,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -76461,6 +82174,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -77254,6 +82975,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -77481,6 +83251,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -77972,6 +83750,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78030,6 +83811,138 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], + "kind": "Solution", + "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", + "latest_version": "1.0.0", + "lookup_key": "string", + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_logo": { + "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", + "height": 600, + "media": "med_0aBcDeFgHiJkLmNoPqRsTu", + "mime_type": "application/json", + "refresh_url": "https://example.com", + "url": "https://example.com", + "width": 800 + }, + "org_name": "Example Name", + "org_slug": "example-slug", + "owners": [ + "string" + ], + "readme_url": "https://example.com", + "screenshot_urls": [ + "https://example.com" + ], + "solution_id": "01234567-89ab-cdef-0123-456789abcdef", + "solution_version": "1.2.0", + "tag_keys": [ + "string" + ], + "template_kind": "AgentTemplate", + "templates": [ + { + "description": "An example description.", + "display_name": "Example Name", + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "AgentTemplate", + "lookup_key": "string", + "name": "Example Name", + "readme_url": "https://example.com", + "virtual_path": "string" + } + ], + "updated_at": "2024-01-01T00:00:00Z", + "upgrade_available": true, + "virtual_path": "string" + }, + "template": { + "created_at": "2024-01-01T00:00:00Z", + "description": "An example description.", + "display_name": "Example Name", + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "kind": "agent_tool_template", + "lookup_key": "string", + "name": "Example Name", + "updated_at": "2024-01-01T00:00:00Z", + "virtual_path": "string" + } + }, + "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", + "template_upgrade_available": true, + "updated_at": "2024-01-01T00:00:00Z", + "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" + } + ], + "items": { + "description": "An AI agent that can be configured with tools, routines, and skills, and invoked to handle conversations or tasks.", + "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" + } + ] + }, + "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", + "created_at": "string", + "default_model": "claude-3-7-sonnet-latest", + "description": "An example description.", + "email": "user@example.com", + "id": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "identity": "You are a helpful assistant that answers questions about ArchAstro products.", + "last_applied_template_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", + "lookup_key": "string", + "metadata": { + "key": "value" + }, + "name": "Example Name", + "org": "org_0aBcDeFgHiJkLmNoPqRsTu", + "org_name": "Example Name", + "originator": "deploy-pipeline", + "phone_number": "+15555550123", + "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", + "source_solution": { + "current_solution": { + "category_keys": [ + "string" + ], + "created_at": "2024-01-01T00:00:00Z", + "description": "An example description.", + "events": {}, + "id": "id_0aBcDeFgHiJkLmNoPqRsTu", + "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78079,75 +83992,7 @@ "upgrade_available": true, "virtual_path": "string" }, - "template": { - "created_at": "2024-01-01T00:00:00Z", - "description": "An example description.", - "display_name": "Example Name", - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "kind": "agent_tool_template", - "lookup_key": "string", - "name": "Example Name", - "updated_at": "2024-01-01T00:00:00Z", - "virtual_path": "string" - } - }, - "team": "tem_0aBcDeFgHiJkLmNoPqRsTu", - "template_upgrade_available": true, - "updated_at": "2024-01-01T00:00:00Z", - "user": "usr_0aBcDeFgHiJkLmNoPqRsTu" - } - ], - "items": { - "description": "An AI agent that can be configured with tools, routines, and skills, and invoked to handle conversations or tasks.", - "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" - } - ] - }, - "app": "dap_0aBcDeFgHiJkLmNoPqRsTu", - "created_at": "string", - "default_model": "claude-3-7-sonnet-latest", - "description": "An example description.", - "email": "user@example.com", - "id": "agi_0aBcDeFgHiJkLmNoPqRsTu", - "identity": "You are a helpful assistant that answers questions about ArchAstro products.", - "last_applied_template_config": "cfg_0aBcDeFgHiJkLmNoPqRsTu", - "lookup_key": "string", - "metadata": { - "key": "value" - }, - "name": "Example Name", - "org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "org_name": "Example Name", - "originator": "deploy-pipeline", - "phone_number": "+15555550123", - "sandbox": "dsb_0aBcDeFgHiJkLmNoPqRsTu", - "source_solution": { - "current_solution": { + "solution": { "category_keys": [ "string" ], @@ -78156,64 +84001,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", - "kind": "Solution", - "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", - "latest_version": "1.0.0", - "lookup_key": "string", - "metadata": { - "key": "value" - }, - "name": "Example Name", - "org": "org_0aBcDeFgHiJkLmNoPqRsTu", - "org_logo": { - "file": "fil_0aBcDeFgHiJkLmNoPqRsTu", - "height": 600, - "media": "med_0aBcDeFgHiJkLmNoPqRsTu", - "mime_type": "application/json", - "refresh_url": "https://example.com", - "url": "https://example.com", - "width": 800 - }, - "org_name": "Example Name", - "org_slug": "example-slug", - "owners": [ - "string" - ], - "readme_url": "https://example.com", - "screenshot_urls": [ - "https://example.com" - ], - "solution_id": "01234567-89ab-cdef-0123-456789abcdef", - "solution_version": "1.2.0", - "tag_keys": [ - "string" - ], - "template_kind": "AgentTemplate", - "templates": [ - { - "description": "An example description.", - "display_name": "Example Name", - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "kind": "AgentTemplate", - "lookup_key": "string", - "name": "Example Name", - "readme_url": "https://example.com", - "virtual_path": "string" - } - ], - "updated_at": "2024-01-01T00:00:00Z", - "upgrade_available": true, - "virtual_path": "string" - }, - "solution": { - "category_keys": [ + "installed_config_ids": [ "string" ], - "created_at": "2024-01-01T00:00:00Z", - "description": "An example description.", - "events": {}, - "id": "id_0aBcDeFgHiJkLmNoPqRsTu", - "image_url": "https://example.com", "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78560,6 +84350,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78618,6 +84411,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78692,6 +84488,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -78774,11 +84573,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -79203,6 +85012,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -79218,6 +85028,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -79300,11 +85113,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -79729,6 +85552,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -80786,45 +86610,263 @@ "description": "Invalid parameters; Validation failed" } }, - "summary": "Extend a workflow work item lease", + "summary": "Extend a workflow work item lease", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/work_items/{work_item}/start": { + "post": { + "operationId": "post_api_v1_work_items__work_item_start", + "parameters": [ + { + "description": "Claimed work item ID.", + "example": "string", + "in": "path", + "name": "work_item", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "lease_owner": "string" + }, + "properties": { + "lease_owner": { + "description": "Saved lease token.", + "example": "string", + "type": "string" + } + }, + "required": [ + "lease_owner" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "App-scoped token required. Use a token scoped to the target app.; Forbidden" + }, + "404": { + "description": "Agent not found; Resource not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Invalid parameters; Validation failed" + } + }, + "summary": "Mark claimed workflow work as running", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/work_items/{work_item}/submit": { + "post": { + "description": "Atomically records the command completion, marks the work item succeeded,\nadvances the journal sequence, and enqueues the owning workflow continuation.\nRetrying the same lease and result is idempotent; a different result conflicts.\n", + "operationId": "post_api_v1_work_items__work_item_submit", + "parameters": [ + { + "description": "Claimed or running work item ID.", + "example": "string", + "in": "path", + "name": "work_item", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "lease_owner": "string", + "result": {} + }, + "properties": { + "lease_owner": { + "description": "Saved lease token.", + "example": "string", + "type": "string" + }, + "result": { + "description": "JSON-serializable output returned to the workflow.", + "example": {}, + "type": "object" + } + }, + "required": [ + "lease_owner", + "result" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "App-scoped token required. Use a token scoped to the target app.; Forbidden" + }, + "404": { + "description": "Agent not found; Resource not found" + }, + "409": { + "description": "Conflict" + }, + "422": { + "description": "Invalid parameters; Validation failed" + } + }, + "summary": "Submit workflow work output and wake its durable execution", "x-auth": [ "publishable_key", "bearer" ] } }, - "/api/v1/work_items/{work_item}/start": { - "post": { - "operationId": "post_api_v1_work_items__work_item_start", + "/api/v1/workflows/commands": { + "get": { + "description": "Returns the full set of workflow commands registered with the workflow host.\nCommands represent discrete operations that a workflow node can invoke — each\ncarries its own input and output schemas used by the workflow builder.\n\nThis endpoint requires a valid developer session. The list is not paginated;\nall registered commands are returned in a single response.\n", + "operationId": "get_api_v1_workflows_commands", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Command" + }, + "type": "array" + } + } + }, + "description": "Array of all registered workflow command definitions." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "List available workflow commands", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/events/{name}/sample": { + "get": { + "description": "Returns a representative sample payload for the specified workflow event type.\nUse this to inspect the structure of an event before configuring a workflow\ntrigger or building an event handler.\n\nRequires a valid developer session. Returns 404 if the event name is not\nregistered in the platform event catalog.\n", + "operationId": "get_api_v1_workflows_events__name_sample", "parameters": [ { - "description": "Claimed work item ID.", - "example": "string", + "description": "Fully-qualified event type name registered in the catalog, e.g. `\"thread.created\"`. Returns 404 if the name is not recognized.", + "example": "Example Name", "in": "path", - "name": "work_item", + "name": "name", "required": true, "schema": { "type": "string" } } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Representative payload for the requested workflow event.", + "example": { + "sample": {} + }, + "properties": { + "sample": { + "description": "Representative event payload.", + "example": {}, + "type": "object" + } + }, + "required": [ + "sample" + ], + "type": "object" + } + } + }, + "description": "Successful response" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Event not found" + } + }, + "summary": "Retrieve a sample event payload", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/expressions/run": { + "post": { + "description": "Evaluates a single workflow expression string against a caller-supplied variable scope\nand returns the computed result. Use this endpoint to test and iterate on expressions\nduring workflow development before embedding them in a workflow graph.\n\nThe expression is evaluated synchronously. If evaluation fails (syntax error, runtime\nexception, or type mismatch), the endpoint returns a 422 with a human-readable reason\nrather than a 200 with an error payload. Successful responses always include any\n`println` output captured during evaluation.\n\nRequires an authenticated developer session. The expression runs in an isolated\ncontext and cannot access platform resources beyond what you supply in `scope`.\n", + "operationId": "post_api_v1_workflows_expressions_run", + "parameters": [], "requestBody": { "content": { "application/json": { "schema": { "example": { - "lease_owner": "string" + "expression": "string", + "scope": {} }, "properties": { - "lease_owner": { - "description": "Saved lease token.", + "expression": { + "default": "", + "description": "The workflow expression source to evaluate. Defaults to an empty string.", "example": "string", "type": "string" + }, + "scope": { + "default": {}, + "description": "Key-value map of variables available to the expression at evaluation time. Defaults to an empty map.", + "example": {}, + "type": "object" } }, - "required": [ - "lease_owner" - ], "type": "object" } } @@ -80832,72 +86874,285 @@ "required": true }, "responses": { - "204": { - "description": "No content" + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExpressionResult" + } + } + }, + "description": "The result of evaluating the expression, including any captured print output." }, "401": { "description": "Unauthorized" }, - "403": { - "description": "App-scoped token required. Use a token scoped to the target app.; Forbidden" - }, - "404": { - "description": "Agent not found; Resource not found" + "422": { + "description": "Expression evaluation failed" + } + }, + "summary": "Run a workflow expression", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/expressions/validate": { + "post": { + "description": "Parses and statically analyses a workflow expression string without executing it.\nReturns structured findings, inferred symbol types, and any warnings so that editors\nand build-time tooling can surface diagnostics before the expression is used in a\nlive workflow.\n\nThe response always includes an `ok` flag indicating whether the validation call\nitself succeeded, and a separate `valid` flag indicating whether the expression\npassed analysis. A 200 is returned regardless of whether the expression is valid;\na 401 is returned if the caller is not authenticated.\n\nRequires an authenticated developer session.\n", + "operationId": "post_api_v1_workflows_expressions_validate", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "expression": "string" + }, + "properties": { + "expression": { + "default": "", + "description": "The workflow expression source to validate. Defaults to an empty string.", + "example": "string", + "type": "string" + } + }, + "type": "object" + } + } }, - "409": { - "description": "Conflict" + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExpressionValidation" + } + } + }, + "description": "Validation outcome including parse errors, warnings, symbol types, and structured editor diagnostics." }, - "422": { - "description": "Invalid parameters; Validation failed" + "401": { + "description": "Unauthorized" } }, - "summary": "Mark claimed workflow work as running", + "summary": "Validate a workflow expression", "x-auth": [ "publishable_key", "bearer" ] } }, - "/api/v1/work_items/{work_item}/submit": { + "/api/v1/workflows/graph/validate": { "post": { - "description": "Atomically records the command completion, marks the work item succeeded,\nadvances the journal sequence, and enqueues the owning workflow continuation.\nRetrying the same lease and result is idempotent; a different result conflicts.\n", - "operationId": "post_api_v1_work_items__work_item_submit", + "description": "Submits a workflow graph definition for static analysis and returns a\nstructured validation report. The graph is checked for structural correctness\n(valid node and edge references) and then analyzed for logical issues such as\nunreachable nodes, dead-end paths, and cycles.\n\nThis endpoint is non-destructive — it does not persist any data. Use it\nduring development to surface problems with a graph definition before saving\nor executing it. Requires developer-level authentication.\n", + "operationId": "post_api_v1_workflows_graph_validate", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "example": { + "graph": {} + }, + "properties": { + "graph": { + "description": "Complete workflow graph definition to validate, containing node and edge declarations.", + "example": {}, + "type": "object" + } + }, + "required": [ + "graph" + ], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GraphValidation" + } + } + }, + "description": "Validation report describing whether the graph is structurally sound and any findings from static analysis." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Validate a workflow graph", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/llm.txt": { + "get": { + "description": "Returns the plain-text system prompt used by workflow-authoring assistants.\nThe prompt is rendered from the registered node types and commands so its\nauthoring guidance stays synchronized with the running platform.\n", + "operationId": "get_api_v1_workflows_llm.txt", + "parameters": [], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Plain-text system prompt for workflow-authoring assistants." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Retrieve the workflow-authoring LLM prompt", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/node_types": { + "get": { + "description": "Returns an array of all node type definitions registered with the workflow host. Node\ntypes describe the available building blocks for constructing workflow graphs, including\neach type's label, category, color, and configurable field definitions.\n\nThis endpoint requires a valid authenticated session. It does not support pagination\n— all registered node types are returned in a single response. The list reflects the\nnode types available at the time of the request; types are registered at startup and\ndo not change at runtime.\n", + "operationId": "get_api_v1_workflows_node_types", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/NodeType" + }, + "type": "array" + } + } + }, + "description": "Array of all registered workflow node type definitions." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "List workflow node types", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/node_types/{node_type}": { + "get": { + "description": "Returns the full definition for a single workflow node type, identified by its\nstring identifier. The response includes the node type's label, description, category,\ndisplay properties, and the complete list of configurable field definitions.\n\nReturns 404 if no node type with the given identifier is registered. This endpoint\nrequires a valid authenticated session.\n", + "operationId": "get_api_v1_workflows_node_types__node_type", "parameters": [ { - "description": "Claimed or running work item ID.", + "description": "Identifier of the node type to retrieve, e.g. `\"http_request\"` or `\"conditional\"`.", "example": "string", "in": "path", - "name": "work_item", + "name": "node_type", "required": true, "schema": { "type": "string" } } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NodeType" + } + } + }, + "description": "The requested workflow node type definition." + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Node type not found" + } + }, + "summary": "Retrieve a workflow node type", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/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", + "operationId": "post_api_v1_workflows_run", + "parameters": [], "requestBody": { "content": { "application/json": { "schema": { "example": { - "lease_owner": "string", - "result": {} + "context": {}, + "env": {}, + "graph": {}, + "node": {}, + "payload": {}, + "run_as_agent": "string", + "run_as_user": "string" }, "properties": { - "lease_owner": { - "description": "Saved lease token.", - "example": "string", - "type": "string" + "context": { + "default": {}, + "description": "Additional execution context made available to workflow nodes at runtime. Merged with the authenticated viewer context set by the platform. Defaults to an empty object if omitted.", + "example": {}, + "type": "object" }, - "result": { - "description": "JSON-serializable output returned to the workflow.", + "env": { + "default": {}, + "description": "Evaluator environment variables injected into expression contexts during execution. The platform automatically merges developer-tier runtime env vars before evaluation. Defaults to an empty object if omitted.", + "example": {}, + "type": "object" + }, + "graph": { + "description": "A complete workflow graph to execute. Must be an object conforming to the `WorkflowGraph` schema (keys: `start_node`, `nodes`, etc.). Mutually exclusive with `node` — supply exactly one.", + "example": {}, + "type": "object" + }, + "node": { + "description": "A single workflow node to execute in isolation. Must be an object with at minimum a `type` key identifying the node kind, plus any node-specific configuration values. Mutually exclusive with `graph` — supply exactly one.", + "example": {}, + "type": "object" + }, + "payload": { + "default": {}, + "description": "Arbitrary input data passed into the workflow as the initial payload. Defaults to an empty object if omitted.", "example": {}, "type": "object" + }, + "run_as_agent": { + "description": "Agent ID (`agt_...`) of the agent to impersonate for the execution. When set, workflow nodes run with this agent as the acting principal instead of the authenticated developer. The agent must belong to the current app. Mutually exclusive with `run_as_user`. `null` by default (runs as the authenticated developer).", + "example": "string", + "type": "string" + }, + "run_as_user": { + "description": "User ID (`usr_...`) of the user to impersonate for the execution. When set, workflow nodes run with this user as the acting principal instead of the authenticated developer. Mutually exclusive with `run_as_agent`. `null` by default (runs as the authenticated developer).", + "example": "string", + "type": "string" } }, - "required": [ - "lease_owner", - "result" - ], "type": "object" } } @@ -80905,26 +87160,144 @@ "required": true }, "responses": { - "204": { - "description": "No content" + "200": { + "content": { + "application/json": { + "schema": { + "description": "Synchronous result from running either a single node or a complete graph.", + "example": { + "context": {}, + "env": {}, + "error": "string", + "files": [ + {} + ], + "log": [ + "string" + ], + "nextNodeId": "string", + "records": [ + {} + ], + "startNodeId": "string", + "status": "string", + "wait": {} + }, + "properties": { + "context": { + "description": "Execution context.", + "example": {}, + "type": "object" + }, + "env": { + "description": "Execution environment.", + "example": {}, + "type": "object" + }, + "error": { + "description": "Execution error message.", + "example": "string", + "type": "string" + }, + "files": { + "description": "Files created during graph execution.", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "log": { + "description": "Node execution log lines.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, + "nextNodeId": { + "description": "Next node ID, when execution yielded.", + "example": "string", + "type": "string" + }, + "output": { + "description": "Node output or graph output lines." + }, + "payload": { + "description": "Final or node-produced JSON-safe payload." + }, + "records": { + "description": "Graph execution records.", + "example": [ + {} + ], + "items": { + "type": "object" + }, + "type": "array" + }, + "startNodeId": { + "description": "Graph start node ID.", + "example": "string", + "type": "string" + }, + "status": { + "description": "Graph execution status.", + "example": "string", + "type": "string" + }, + "wait": { + "description": "Yielded wait state.", + "example": {}, + "type": "object" + } + }, + "type": "object" + } + } + }, + "description": "Successful response" }, "401": { "description": "Unauthorized" }, - "403": { - "description": "App-scoped token required. Use a token scoped to the target app.; Forbidden" - }, - "404": { - "description": "Agent not found; Resource not found" - }, - "409": { - "description": "Conflict" - }, "422": { - "description": "Invalid parameters; Validation failed" + "description": "Execution failed" } }, - "summary": "Submit workflow work output and wake its durable execution", + "summary": "Execute a workflow node or graph", + "x-auth": [ + "publishable_key", + "bearer" + ] + } + }, + "/api/v1/workflows/sample": { + "get": { + "description": "Returns a minimal, runnable `WorkflowGraph` YAML document that illustrates the\nstructure of a workflow graph. Use this as a starting point when building or\ntesting your own workflow definitions.\n\nThe response is returned as plain YAML (`application/x-yaml`) rather than JSON.\nThe sample graph includes a trigger node, a transform node, and an event-emit\nnode wired together in sequence.\n", + "operationId": "get_api_v1_workflows_sample", + "parameters": [], + "responses": { + "200": { + "content": { + "application/x-yaml": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Minimal runnable WorkflowGraph YAML document." + }, + "401": { + "description": "Unauthorized" + } + }, + "summary": "Retrieve a sample workflow graph", "x-auth": [ "publishable_key", "bearer" @@ -81166,7 +87539,7 @@ }, "/oauth/token": { "post": { - "description": "Issues an access token and a refresh token in exchange for a valid grant.\nThree grant types are supported: `\"authorization_code\"`, `\"refresh_token\"`,\nand `\"urn:ietf:params:oauth:grant-type:device_code\"`.\n\nFor `\"authorization_code\"` grants, supply `code`, `client`, `redirect_uri`, and\noptionally `code_verifier` for PKCE flows. Each authorization code is single-use;\nconsuming it a second time returns `invalid_grant`.\n\nFor `\"refresh_token\"` grants, supply `refresh_token`. The endpoint rotates the\nrefresh token on every call and returns a fresh pair of tokens.\n\nFor device-code grants, supply `device_code` and `client`. Poll this endpoint\nafter receiving `authorization_pending` until the user approves or the code\nexpires. Slow down polling if you receive `slow_down`.\n\nThis endpoint is rate-limited to 20 requests per IP per 60 seconds. Exceeding\nthe limit returns HTTP 429 with `\"error\": \"too_many_requests\"`.\n", + "description": "Issues an access token and a refresh token in exchange for a valid grant.\nThree grant types are supported: `\"authorization_code\"`, `\"refresh_token\"`,\nand `\"urn:ietf:params:oauth:grant-type:device_code\"`.\n\nFor `\"authorization_code\"` grants, supply `code`, `client`, `redirect_uri`, and\noptionally `code_verifier` for PKCE flows. Each authorization code is single-use;\nconsuming it a second time returns `invalid_grant`.\n\nFor `\"refresh_token\"` grants, supply `refresh_token`. Resource-bound public\nclients must also supply `client`. The endpoint rotates the refresh token on\nevery call and returns a fresh pair of tokens.\n\nFor device-code grants, supply `device_code` and `client`. Poll this endpoint\nafter receiving `authorization_pending` until the user approves or the code\nexpires. Slow down polling if you receive `slow_down`.\n\nThis endpoint is rate-limited to 20 requests per IP per 60 seconds. Exceeding\nthe limit returns HTTP 429 with `\"error\": \"too_many_requests\"`.\n", "operationId": "post_oauth_token", "parameters": [], "requestBody": { @@ -81184,7 +87557,7 @@ }, "properties": { "client": { - "description": "OAuth client ID identifying the application requesting tokens. Required for `\"authorization_code\"` and device-code grants.", + "description": "OAuth client ID identifying the application requesting tokens. Required for `\"authorization_code\"`, device-code, and resource-bound refresh grants.", "example": "string", "type": "string" }, @@ -81541,6 +87914,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -81599,6 +87975,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -81945,6 +88324,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -82003,6 +88385,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -82077,6 +88462,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -82159,11 +88547,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -82588,6 +88986,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -82603,6 +89002,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -82685,11 +89087,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -83114,6 +89526,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -83732,7 +90145,7 @@ } ], "items": { - "description": "An OpenAI-compatible local function definition supplied while joining a personal thread.", + "description": "An OpenAI-compatible local function definition advertised by a personal-thread connection.", "example": { "function": { "description": "An example description.", @@ -83974,6 +90387,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -84032,6 +90448,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -84159,6 +90578,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -84217,6 +90639,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -84412,6 +90837,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -84614,6 +91047,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -84738,6 +91179,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -84796,6 +91240,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85050,6 +91497,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -85174,6 +91629,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85232,6 +91690,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85383,6 +91844,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85441,6 +91905,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85568,6 +92035,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85626,6 +92096,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -85821,6 +92294,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -86023,6 +92504,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -86147,6 +92636,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86205,6 +92697,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86359,6 +92854,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86417,6 +92915,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86763,6 +93264,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86821,6 +93325,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86895,6 +93402,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -86977,11 +93487,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -87406,6 +93926,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -87421,6 +93942,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -87503,11 +94027,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -87932,6 +94466,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -88116,6 +94651,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88174,6 +94712,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88327,6 +94868,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88385,6 +94929,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88537,6 +95084,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88595,6 +95145,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88941,6 +95494,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -88999,6 +95555,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -89073,6 +95632,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -89155,11 +95717,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -89584,6 +96156,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -89599,6 +96172,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -89681,11 +96257,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -90110,6 +96696,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -90490,6 +97077,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -90629,6 +97224,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "string", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -90756,6 +97359,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -91556,6 +98167,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -91783,6 +98443,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -92447,6 +99115,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -92571,6 +99247,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -92629,6 +99308,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -93067,6 +99749,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -93860,6 +100550,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -94087,6 +100826,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -94578,6 +101325,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -94636,6 +101386,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -94762,6 +101515,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -94820,6 +101576,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -95166,6 +101925,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -95224,6 +101986,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -95298,6 +102063,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -95380,11 +102148,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -95809,6 +102587,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -95824,6 +102603,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -95906,11 +102688,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -96335,6 +103127,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -96987,6 +103780,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -97111,6 +103912,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -97169,6 +103973,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -97607,6 +104414,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -98400,6 +105215,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -98627,6 +105491,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -99118,6 +105990,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99176,6 +106051,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99302,6 +106180,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99360,6 +106241,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99706,6 +106590,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99764,6 +106651,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99838,6 +106728,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -99920,11 +106813,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -100349,6 +107252,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -100364,6 +107268,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -100446,11 +107353,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -100875,6 +107792,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -101253,6 +108171,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -101395,6 +108321,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -101534,6 +108468,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "string", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -101661,6 +108603,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -102461,6 +109411,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -102688,6 +109687,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -103026,6 +110033,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -103084,6 +110094,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -103211,6 +110224,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -103269,6 +110285,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -103464,6 +110483,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -103666,6 +110693,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -103790,6 +110825,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -103848,6 +110886,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104000,6 +111041,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104058,6 +111102,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104185,6 +111232,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104243,6 +111293,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104438,6 +111491,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -104640,6 +111701,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -104764,6 +111833,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104822,6 +111894,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -104976,6 +112051,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -105034,6 +112112,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -105380,6 +112461,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -105438,6 +112522,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -105512,6 +112599,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -105594,11 +112684,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -106023,6 +113123,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -106038,6 +113139,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -106120,11 +113224,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -106549,6 +113663,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -106733,6 +113848,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -106791,6 +113909,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -106944,6 +114065,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107002,6 +114126,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107154,6 +114281,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107212,6 +114342,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107558,6 +114691,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107616,6 +114752,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107690,6 +114829,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -107772,11 +114914,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -108201,6 +115353,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -108216,6 +115369,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -108298,11 +115454,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -108727,6 +115893,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -109107,6 +116274,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -109246,6 +116421,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "string", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -109373,6 +116556,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -110173,6 +117364,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -110400,6 +117640,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -111064,6 +118312,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -111188,6 +118444,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -111246,6 +118505,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -111684,6 +118946,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -112477,6 +119747,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -112704,6 +120023,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -113195,6 +120522,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113253,6 +120583,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113379,6 +120712,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113437,6 +120773,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113783,6 +121122,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113841,6 +121183,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113915,6 +121260,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -113997,11 +121345,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -114426,6 +121784,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -114441,6 +121800,9 @@ "events": {}, "id": "id_0aBcDeFgHiJkLmNoPqRsTu", "image_url": "https://example.com", + "installed_config_ids": [ + "string" + ], "kind": "Solution", "latest_solution": "id_0aBcDeFgHiJkLmNoPqRsTu", "latest_version": "1.0.0", @@ -114523,11 +121885,21 @@ "type": "string" }, "image_url": { - "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows — the permanent URL is minted for system-scope (catalog) Solutions only.", + "description": "Absolute URL of the Solution's cover image — the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", "example": "https://example.com", "nullable": true, "type": "string" }, + "installed_config_ids": { + "description": "Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", + "example": [ + "string" + ], + "items": { + "type": "string" + }, + "type": "array" + }, "kind": { "description": "Resource type. Always `\"Solution\"`.", "example": "Solution", @@ -114952,6 +122324,7 @@ "kind", "templates", "owners", + "installed_config_ids", "upgrade_available" ], "type": "object" @@ -115197,6 +122570,14 @@ "params": { "example": { "content": "string", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "idempotency_key": "string", "reply_to": "string", "uploads": [ @@ -115208,6 +122589,54 @@ "example": "string", "type": "string" }, + "context": { + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "idempotency_key": { "example": "string", "type": "string" @@ -115335,6 +122764,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -115475,6 +122912,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -116267,6 +123712,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -116494,6 +123988,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -116744,6 +124246,14 @@ "params": { "example": { "content": "string", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "idempotency_key": "string", "reply_to": "string" }, @@ -116752,6 +124262,54 @@ "example": "string", "type": "string" }, + "context": { + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "idempotency_key": { "example": "string", "type": "string" @@ -116867,6 +124425,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -117007,6 +124573,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -117799,6 +125373,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -118026,6 +125649,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -118407,6 +126038,282 @@ "properties": {}, "type": "object" } + }, + { + "description": "Replace the local tools advertised by this connection for later messages it posts", + "event": "api:chat:publish_local_tools", + "params": { + "example": { + "local_tool_provider_id": "string", + "local_tools": [ + { + "function": { + "description": "An example description.", + "name": "Example Name", + "parameters": {} + }, + "type": "function" + } + ] + }, + "properties": { + "local_tool_provider_id": { + "example": "string", + "type": "string" + }, + "local_tools": { + "example": [ + { + "function": { + "description": "An example description.", + "name": "Example Name", + "parameters": {} + }, + "type": "function" + } + ], + "items": { + "description": "An OpenAI-compatible local function definition advertised by a personal-thread connection.", + "example": { + "function": { + "description": "An example description.", + "name": "Example Name", + "parameters": {} + }, + "type": "function" + }, + "properties": { + "function": { + "description": "A function implemented by the client connected to a personal thread.", + "example": { + "description": "An example description.", + "name": "Example Name", + "parameters": {} + }, + "properties": { + "description": { + "example": "An example description.", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + }, + "parameters": { + "example": {}, + "type": "object" + } + }, + "required": [ + "name", + "description", + "parameters" + ], + "type": "object" + }, + "type": { + "enum": [ + "function" + ], + "example": "function", + "type": "string" + } + }, + "required": [ + "type", + "function" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "local_tool_provider_id", + "local_tools" + ], + "type": "object" + }, + "returns": { + "description": "Server-issued identity for the local-tool set currently advertised by this connection.", + "example": { + "generation": 1, + "provider_id": "string" + }, + "properties": { + "generation": { + "example": 1, + "type": "integer" + }, + "provider_id": { + "example": "string", + "type": "string" + } + }, + "required": [ + "provider_id", + "generation" + ], + "type": "object" + } + }, + { + "description": "Return one complete result batch for a connection-local tool request", + "event": "api:chat:local_tool_result", + "params": { + "example": { + "agent_id": "string", + "generation": 1, + "provider_id": "string", + "request_id": "string", + "results": [] + }, + "properties": { + "agent_id": { + "example": "string", + "type": "string" + }, + "generation": { + "example": 1, + "type": "integer" + }, + "provider_id": { + "example": "string", + "type": "string" + }, + "request_id": { + "example": "string", + "type": "string" + }, + "results": { + "example": [], + "items": { + "description": "One terminal local-tool outcome returned by the owning client connection.", + "discriminator": { + "propertyName": "status" + }, + "oneOf": [ + { + "description": "A successful local-tool outcome.", + "example": { + "call_id": "string", + "content": "string", + "status": "ok" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "content": { + "example": "string", + "type": "string" + }, + "status": { + "default": "ok", + "enum": [ + "ok" + ], + "example": "ok", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "content" + ], + "type": "object" + }, + { + "description": "A failed local-tool outcome.", + "example": { + "call_id": "string", + "code": "string", + "message": "string", + "status": "error" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "code": { + "example": "string", + "type": "string" + }, + "message": { + "example": "string", + "type": "string" + }, + "status": { + "default": "error", + "enum": [ + "error" + ], + "example": "error", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "code", + "message" + ], + "type": "object" + }, + { + "description": "A cancelled local-tool outcome.", + "example": { + "call_id": "string", + "reason": "string", + "status": "cancelled" + }, + "properties": { + "call_id": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + }, + "status": { + "default": "cancelled", + "enum": [ + "cancelled" + ], + "example": "cancelled", + "type": "string" + } + }, + "required": [ + "call_id", + "status", + "reason" + ], + "type": "object" + } + ] + }, + "type": "array" + } + }, + "required": [ + "provider_id", + "generation", + "agent_id", + "request_id", + "results" + ], + "type": "object" + }, + "returns": { + "description": "Empty acknowledgement payload returned by channel message handlers that produce no data. The wire envelope is `{\"status\": \"ok\", \"response\": {}}`.", + "properties": {}, + "type": "object" + } } ], "name": "ApiChatChannel", @@ -118519,6 +126426,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -118668,6 +126583,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -119460,6 +127383,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -119687,6 +127659,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -120038,6 +128018,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -120179,6 +128167,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -120971,6 +128967,55 @@ "nullable": true, "type": "string" }, + "context": { + "description": "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + "example": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], + "items": { + "description": "Structured context captured with a chat message and delivered to agents as data.", + "example": { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + }, + "properties": { + "attributes": { + "description": "Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + "example": {}, + "type": "object" + }, + "content": { + "description": "Optional context body. The model receives it as escaped XML data, not a system instruction.", + "example": "string", + "nullable": true, + "type": "string" + }, + "title": { + "description": "Optional human-readable label for this context block.", + "example": "PR #10458", + "nullable": true, + "type": "string" + }, + "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", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "type": "array" + }, "created_at": { "description": "When the message was posted (ISO 8601).", "example": "string", @@ -121198,6 +129243,14 @@ ], "branched_thread": "string", "content": "Hello, how can I help you today?", + "context": [ + { + "attributes": {}, + "content": "string", + "title": "PR #10458", + "type": "archdev.pull_request" + } + ], "created_at": "2024-01-01T00:00:00Z", "has_replies": true, "id": "msg_0aBcDeFgHiJkLmNoPqRsTu", @@ -121615,6 +129668,146 @@ }, "type": "object" } + }, + { + "description": "Invoke local functions on the exact connection that published them", + "event": "local_tool_call", + "payload": { + "example": { + "agent_id": "string", + "expires_at": "2024-01-01T00:00:00Z", + "generation": 1, + "provider_id": "string", + "request": { + "calls": [ + { + "arguments": {}, + "id": "string", + "name": "Example Name" + } + ], + "id": "string" + }, + "thread_id": "string" + }, + "properties": { + "agent_id": { + "example": "string", + "type": "string" + }, + "expires_at": { + "example": "2024-01-01T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "generation": { + "example": 1, + "type": "integer" + }, + "provider_id": { + "example": "string", + "type": "string" + }, + "request": { + "description": "A correlated batch of local function calls targeted to one connection.", + "example": { + "calls": [ + { + "arguments": {}, + "id": "string", + "name": "Example Name" + } + ], + "id": "string" + }, + "properties": { + "calls": { + "example": [ + { + "arguments": {}, + "id": "string", + "name": "Example Name" + } + ], + "items": { + "description": "One local function invocation requested by the model.", + "example": { + "arguments": {}, + "id": "string", + "name": "Example Name" + }, + "properties": { + "arguments": { + "example": {}, + "type": "object" + }, + "id": { + "example": "string", + "type": "string" + }, + "name": { + "example": "Example Name", + "type": "string" + } + }, + "required": [ + "id", + "name", + "arguments" + ], + "type": "object" + }, + "type": "array" + }, + "id": { + "example": "string", + "type": "string" + } + }, + "required": [ + "id", + "calls" + ], + "type": "object" + }, + "thread_id": { + "example": "string", + "type": "string" + } + }, + "type": "object" + } + }, + { + "description": "Stop local work because the server no longer accepts its result", + "event": "local_tool_cancelled", + "payload": { + "example": { + "generation": 1, + "provider_id": "string", + "reason": "string", + "request_id": "string" + }, + "properties": { + "generation": { + "example": 1, + "type": "integer" + }, + "provider_id": { + "example": "string", + "type": "string" + }, + "reason": { + "example": "string", + "type": "string" + }, + "request_id": { + "example": "string", + "type": "string" + } + }, + "type": "object" + } } ], "x-auth": [ @@ -122129,6 +130322,7 @@ "tasks": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -122206,6 +130400,7 @@ "example": [ { "agent": "agi_0aBcDeFgHiJkLmNoPqRsTu", + "aggregate_version": 1, "blocked_by_count": 1, "closed_at": "2024-01-01T00:00:00Z", "comments_count": 1, @@ -122281,6 +130476,7 @@ "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, @@ -122358,6 +130554,12 @@ "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 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, @@ -122722,7 +130924,7 @@ "type": "string" }, "status": { - "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, or `\"done\"`.", + "description": "Current status of the task. One of `\"open\"`, `\"in_progress\"`, `\"in_review\"`, `\"paused\"`, `\"failed\"`, `\"superseding\"`, `\"done\"`, or `\"cancelled\"`.", "example": "open", "type": "string" }, diff --git a/src/archastro/platform/__init__.py b/src/archastro/platform/__init__.py index 2f81077..5cb098c 100644 --- a/src/archastro/platform/__init__.py +++ b/src/archastro/platform/__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: d14486b6976e +# Content hash: 1c08783af6ac from importlib.metadata import version as _pkg_version diff --git a/src/archastro/platform/auth.py b/src/archastro/platform/auth.py index 6b8de83..2e2253d 100644 --- a/src/archastro/platform/auth.py +++ b/src/archastro/platform/auth.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: 753c8a0332f7 +# Content hash: 4cece1e1b7b8 from __future__ import annotations @@ -81,7 +81,7 @@ async def request_login_magic_link( Request a magic link for login Sends a magic link to the given email address so an existing user can sign in without a password. The user clicks the link in their email and is redirected to - `redirect_uri` with a token; pass that token to `/auth/verify_link` to obtain + `redirect_uri` with a token; pass that token to `/api/v1/auth/verify/link` to obtain session tokens. If no account exists for the email, the endpoint still returns success to prevent email enumeration no link is sent in that case. Both `email` and `redirect_uri` @@ -159,8 +159,12 @@ async def register( invite is not found. - **Standard registration**: supply `password`. An `invite_code` may optionally be included for invite-gated apps; an invalid code returns HTTP 404. - Exactly one of `team_invite` or `password` must be provided; omitting both returns - HTTP 400. Password registration must be enabled for the app; disabled apps return + Provide `team_invite` or `password`; when both are supplied, team registration + takes precedence. Omitting both returns HTTP 400. Unknown fields return HTTP 400 + with field-level guidance. Use `full_name` for the profile name (returned as + `user.name`); `name` is not an input field. `alias` is a separate optional handle. + Legacy `team_invite_id` remains accepted; `team_invite` takes precedence. + Password registration must be enabled for the app; disabled apps return HTTP 403. The response status is HTTP 201 on success. Args: @@ -209,6 +213,7 @@ async def request_register_magic_link( alias: str | None = None, email: str | None = None, full_name: str | None = None, + org: str | None = None, redirect_uri: str | None = None, set_org: str | None = None, timezone: str | None = None, @@ -217,10 +222,11 @@ async def request_register_magic_link( Request a magic link for registration Starts a passwordless registration flow by sending a verification link to the given email address. The recipient clicks the link and is redirected to `redirect_uri` with - a token; pass that token to `/auth/verify_link` to complete registration and obtain + a token; pass that token to `/api/v1/auth/verify/link` to complete registration and obtain session tokens. - Profile fields (`full_name`, `alias`, `timezone`) are captured now and applied when - the link is verified. Requests are rate-limited per IP (10 per minute) and per + Profile fields (`full_name`, `alias`, `timezone`) and, with `set_org`, the `org` + details are captured now and applied when the link is verified; nothing is created + before the click. Requests are rate-limited per IP (10 per minute) and per email-IP pair (3 per minute) exceeding either limit returns HTTP 429. Returns HTTP 204 on success. @@ -228,6 +234,7 @@ async def request_register_magic_link( alias: Display alias (handle) for the new account. email: Email address to send the registration magic link to. full_name: Full name for the new account. + org: Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods). redirect_uri: URL the user is redirected to after clicking the registration link. The token is appended as a query parameter. set_org: Create or reuse an organization from the work-email domain during confirmation. timezone: IANA timezone name for the new account, e.g. `"America/New_York"`. @@ -242,6 +249,8 @@ async def request_register_magic_link( body["email"] = email if full_name is not None: body["full_name"] = full_name + if org is not None: + body["org"] = org if redirect_uri is not None: body["redirect_uri"] = redirect_uri if set_org is not None: @@ -257,7 +266,14 @@ async def request_register_magic_link( return data async def request_magic_link( - self, email: str | None = None, redirect_uri: str | None = None, set_org: str | None = None + self, + email: str | None = None, + full_name: str | None = None, + mcp_client_id: str | None = None, + org: str | None = None, + redirect_uri: str | None = None, + set_org: str | None = None, + signup_origin: str | None = None, ) -> dict: """ Request a magic link for login or registration @@ -268,12 +284,24 @@ async def request_magic_link( The `redirect_uri` is validated against the app's registered hosts; an unregistered URI returns HTTP 400. Both `email` and `redirect_uri` are required. Requests are rate-limited per IP (10 per minute) and per email-IP pair (3 per minute). Returns - HTTP 204 on success no body. + HTTP 204 on success no body. If the app has Allowed Users enabled and the + address is not on the list, returns HTTP 403 `user_not_allowed`. That refusal + is the same for new and existing addresses, so it does not reveal whether an + account exists. + For a new user, `full_name` and (with `set_org`) the `org` details ride the pending + registration and are applied when the link is verified; nothing is created before + the click. They are ignored when the email already belongs to a user. Malformed + values return HTTP 422 before the address is looked up, so the response cannot + reveal whether an account exists. Args: email: Email address to send the magic link to. + full_name: Display name for the account the verified link will create. + mcp_client_id: Opaque dynamic-registration client id for an MCP consent signup. Platform resolves it to a closed analytics key. + org: Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods). redirect_uri: URL the user is redirected to after clicking the magic link. Must be registered with the app. set_org: For a new user, create or reuse an organization from the work-email domain during confirmation. + signup_origin: Closed product intent for a new signup. Missing or unknown values become generic and never affect authentication. Returns: No content @@ -281,10 +309,18 @@ async def request_magic_link( body: dict[str, object] = {} if email is not None: body["email"] = email + if full_name is not None: + body["full_name"] = full_name + if mcp_client_id is not None: + body["mcp_client_id"] = mcp_client_id + if org is not None: + body["org"] = org if redirect_uri is not None: body["redirect_uri"] = redirect_uri if set_org is not None: body["set_org"] = set_org + if signup_origin is not None: + body["signup_origin"] = signup_origin data = await self._http.request( "/api/v1/auth/request/link", @@ -296,16 +332,20 @@ async def request_magic_link( async def exchange_login_token(self, token: str, timezone: str | None = None) -> AuthTokens: """ Exchange a one-time login token for session tokens - Consumes a single-use login token delivered via email and returns an access token, - refresh token, and the authenticated user object. One-time tokens are issued by the - passwordless login flow and expire after a short window; submitting an expired or - already-used token returns HTTP 401. + Exchanges an email-login token (including email reply links) or an internal + impersonation login token for session credentials. Passwordless magic-link tokens + from `/api/v1/auth/login/link`, `/api/v1/auth/register/link`, or + `/api/v1/auth/request/link` must instead go to `/api/v1/auth/verify/link`. + Numeric email codes go to `/api/v1/auth/code/verify` with `email` and `code`. + Access and refresh tokens are not accepted here. Invalid, expired, or already-used + login tokens return HTTP 401 with structured `error.type`, `error.code`, and + `error.message` fields. If `timezone` is provided and the user's current timezone is still the default (`"America/Los_Angeles"`), the account timezone is updated in the same request. Requests are rate-limited to 10 per IP per minute; exceeding this returns HTTP 429. Args: - token: Single-use login token extracted from the magic link or email code flow. + token: Single-use email-login or impersonation token; not a passwordless magic-link token or numeric code. timezone: IANA timezone name to apply to the account if the account timezone is still the default, e.g. `"Europe/London"`. Omit to leave the timezone unchanged. Returns: @@ -332,11 +372,13 @@ async def verify_magic_link(self, token: str | None = None) -> AuthTokens: Verify a magic link token Consumes a single-use token from a magic link URL and returns an access token, refresh token, and the authenticated user object. This endpoint completes both the - login flow (initiated by `/auth/request_login_link`) and the registration flow - (initiated by `/auth/request_register_link` or `/auth/request_link`). + login flow (initiated by `/api/v1/auth/login/link`) and the registration flow + (initiated by `/api/v1/auth/register/link` or `/api/v1/auth/request/link`). Extract the token from the `token` query parameter of the magic link redirect URI - and POST it here. Expired or already-used tokens return HTTP 401 expired links - carry the error code `expired_token`, unknown or already-used tokens carry + and POST JSON `{"token":""}` to + `/api/v1/auth/verify/link`, not `/api/v1/auth/token`. Expired or already-used tokens return HTTP 401 expired links + have `error.code` = `authentication_error` and `error.message` = + `expired_token`; unknown or already-used tokens have `error.message` = `invalid_or_expired_token`. If the app has disabled passwordless authentication the request returns HTTP 403. Rate-limited to 10 requests per IP per minute exceeding this returns HTTP 429. @@ -428,7 +470,7 @@ def request_login_magic_link( Request a magic link for login Sends a magic link to the given email address so an existing user can sign in without a password. The user clicks the link in their email and is redirected to - `redirect_uri` with a token; pass that token to `/auth/verify_link` to obtain + `redirect_uri` with a token; pass that token to `/api/v1/auth/verify/link` to obtain session tokens. If no account exists for the email, the endpoint still returns success to prevent email enumeration no link is sent in that case. Both `email` and `redirect_uri` @@ -506,8 +548,12 @@ def register( invite is not found. - **Standard registration**: supply `password`. An `invite_code` may optionally be included for invite-gated apps; an invalid code returns HTTP 404. - Exactly one of `team_invite` or `password` must be provided; omitting both returns - HTTP 400. Password registration must be enabled for the app; disabled apps return + Provide `team_invite` or `password`; when both are supplied, team registration + takes precedence. Omitting both returns HTTP 400. Unknown fields return HTTP 400 + with field-level guidance. Use `full_name` for the profile name (returned as + `user.name`); `name` is not an input field. `alias` is a separate optional handle. + Legacy `team_invite_id` remains accepted; `team_invite` takes precedence. + Password registration must be enabled for the app; disabled apps return HTTP 403. The response status is HTTP 201 on success. Args: @@ -556,6 +602,7 @@ def request_register_magic_link( alias: str | None = None, email: str | None = None, full_name: str | None = None, + org: str | None = None, redirect_uri: str | None = None, set_org: str | None = None, timezone: str | None = None, @@ -564,10 +611,11 @@ def request_register_magic_link( Request a magic link for registration Starts a passwordless registration flow by sending a verification link to the given email address. The recipient clicks the link and is redirected to `redirect_uri` with - a token; pass that token to `/auth/verify_link` to complete registration and obtain + a token; pass that token to `/api/v1/auth/verify/link` to complete registration and obtain session tokens. - Profile fields (`full_name`, `alias`, `timezone`) are captured now and applied when - the link is verified. Requests are rate-limited per IP (10 per minute) and per + Profile fields (`full_name`, `alias`, `timezone`) and, with `set_org`, the `org` + details are captured now and applied when the link is verified; nothing is created + before the click. Requests are rate-limited per IP (10 per minute) and per email-IP pair (3 per minute) exceeding either limit returns HTTP 429. Returns HTTP 204 on success. @@ -575,6 +623,7 @@ def request_register_magic_link( alias: Display alias (handle) for the new account. email: Email address to send the registration magic link to. full_name: Full name for the new account. + org: Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods). redirect_uri: URL the user is redirected to after clicking the registration link. The token is appended as a query parameter. set_org: Create or reuse an organization from the work-email domain during confirmation. timezone: IANA timezone name for the new account, e.g. `"America/New_York"`. @@ -589,6 +638,8 @@ def request_register_magic_link( body["email"] = email if full_name is not None: body["full_name"] = full_name + if org is not None: + body["org"] = org if redirect_uri is not None: body["redirect_uri"] = redirect_uri if set_org is not None: @@ -604,7 +655,14 @@ def request_register_magic_link( return data def request_magic_link( - self, email: str | None = None, redirect_uri: str | None = None, set_org: str | None = None + self, + email: str | None = None, + full_name: str | None = None, + mcp_client_id: str | None = None, + org: str | None = None, + redirect_uri: str | None = None, + set_org: str | None = None, + signup_origin: str | None = None, ) -> dict: """ Request a magic link for login or registration @@ -615,12 +673,24 @@ def request_magic_link( The `redirect_uri` is validated against the app's registered hosts; an unregistered URI returns HTTP 400. Both `email` and `redirect_uri` are required. Requests are rate-limited per IP (10 per minute) and per email-IP pair (3 per minute). Returns - HTTP 204 on success no body. + HTTP 204 on success no body. If the app has Allowed Users enabled and the + address is not on the list, returns HTTP 403 `user_not_allowed`. That refusal + is the same for new and existing addresses, so it does not reveal whether an + account exists. + For a new user, `full_name` and (with `set_org`) the `org` details ride the pending + registration and are applied when the link is verified; nothing is created before + the click. They are ignored when the email already belongs to a user. Malformed + values return HTTP 422 before the address is looked up, so the response cannot + reveal whether an account exists. Args: email: Email address to send the magic link to. + full_name: Display name for the account the verified link will create. + mcp_client_id: Opaque dynamic-registration client id for an MCP consent signup. Platform resolves it to a closed analytics key. + org: Company details for the organization a `set_org` registration creates at confirmation. Ignored when the domain org already exists or the mailbox is personal (which keeps the platform's own identity and takes only the sign-in methods). redirect_uri: URL the user is redirected to after clicking the magic link. Must be registered with the app. set_org: For a new user, create or reuse an organization from the work-email domain during confirmation. + signup_origin: Closed product intent for a new signup. Missing or unknown values become generic and never affect authentication. Returns: No content @@ -628,10 +698,18 @@ def request_magic_link( body: dict[str, object] = {} if email is not None: body["email"] = email + if full_name is not None: + body["full_name"] = full_name + if mcp_client_id is not None: + body["mcp_client_id"] = mcp_client_id + if org is not None: + body["org"] = org if redirect_uri is not None: body["redirect_uri"] = redirect_uri if set_org is not None: body["set_org"] = set_org + if signup_origin is not None: + body["signup_origin"] = signup_origin data = self._http.request( "/api/v1/auth/request/link", @@ -643,16 +721,20 @@ def request_magic_link( def exchange_login_token(self, token: str, timezone: str | None = None) -> AuthTokens: """ Exchange a one-time login token for session tokens - Consumes a single-use login token delivered via email and returns an access token, - refresh token, and the authenticated user object. One-time tokens are issued by the - passwordless login flow and expire after a short window; submitting an expired or - already-used token returns HTTP 401. + Exchanges an email-login token (including email reply links) or an internal + impersonation login token for session credentials. Passwordless magic-link tokens + from `/api/v1/auth/login/link`, `/api/v1/auth/register/link`, or + `/api/v1/auth/request/link` must instead go to `/api/v1/auth/verify/link`. + Numeric email codes go to `/api/v1/auth/code/verify` with `email` and `code`. + Access and refresh tokens are not accepted here. Invalid, expired, or already-used + login tokens return HTTP 401 with structured `error.type`, `error.code`, and + `error.message` fields. If `timezone` is provided and the user's current timezone is still the default (`"America/Los_Angeles"`), the account timezone is updated in the same request. Requests are rate-limited to 10 per IP per minute; exceeding this returns HTTP 429. Args: - token: Single-use login token extracted from the magic link or email code flow. + token: Single-use email-login or impersonation token; not a passwordless magic-link token or numeric code. timezone: IANA timezone name to apply to the account if the account timezone is still the default, e.g. `"Europe/London"`. Omit to leave the timezone unchanged. Returns: @@ -679,11 +761,13 @@ def verify_magic_link(self, token: str | None = None) -> AuthTokens: Verify a magic link token Consumes a single-use token from a magic link URL and returns an access token, refresh token, and the authenticated user object. This endpoint completes both the - login flow (initiated by `/auth/request_login_link`) and the registration flow - (initiated by `/auth/request_register_link` or `/auth/request_link`). + login flow (initiated by `/api/v1/auth/login/link`) and the registration flow + (initiated by `/api/v1/auth/register/link` or `/api/v1/auth/request/link`). Extract the token from the `token` query parameter of the magic link redirect URI - and POST it here. Expired or already-used tokens return HTTP 401 expired links - carry the error code `expired_token`, unknown or already-used tokens carry + and POST JSON `{"token":""}` to + `/api/v1/auth/verify/link`, not `/api/v1/auth/token`. Expired or already-used tokens return HTTP 401 expired links + have `error.code` = `authentication_error` and `error.message` = + `expired_token`; unknown or already-used tokens have `error.message` = `invalid_or_expired_token`. If the app has disabled passwordless authentication the request returns HTTP 403. Rate-limited to 10 requests per IP per minute exceeding this returns HTTP 429. diff --git a/src/archastro/platform/channels/api_chat_channel.py b/src/archastro/platform/channels/api_chat_channel.py index 721ada4..fb937f5 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: 068983a8456f +# Content hash: 516661809198 from collections.abc import Callable from datetime import datetime @@ -32,19 +32,43 @@ class ApiChatLoadMoreMessagesInput(TypedDict, total=False): limit: int +class ApiChatPostMessageInputContextItem(TypedDict, total=False): + attributes: dict[str, Any] | None + "Scalar key-value fields describing the context, such as route, repository, or pull-request number." + content: str | None + "Optional context body. The model receives it as escaped XML data, not a system instruction." + title: str | None + "Optional human-readable label for this context block." + type: Required[str] + "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores." + + class ApiChatPostMessageInput(TypedDict, total=False): "Post a new message with optional uploads and reply-to" content: Required[str] + context: list[ApiChatPostMessageInputContextItem] idempotency_key: str reply_to: str uploads: list[dict[str, Any]] +class ApiChatPostSimpleMessageInputContextItem(TypedDict, total=False): + attributes: dict[str, Any] | None + "Scalar key-value fields describing the context, such as route, repository, or pull-request number." + content: str | None + "Optional context body. The model receives it as escaped XML data, not a system instruction." + title: str | None + "Optional human-readable label for this context block." + type: Required[str] + "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores." + + class ApiChatPostSimpleMessageInput(TypedDict, total=False): "Post a simple text message" content: str + context: list[ApiChatPostSimpleMessageInputContextItem] idempotency_key: str reply_to: str @@ -82,6 +106,35 @@ class ApiChatTypingInput(TypedDict): is_typing: bool +class ApiChatPublishLocalToolsInputLocalToolsItemFunction(TypedDict): + description: str + name: str + parameters: dict[str, Any] + + +class ApiChatPublishLocalToolsInputLocalToolsItem(TypedDict): + function: ApiChatPublishLocalToolsInputLocalToolsItemFunction + "A function implemented by the client connected to a personal thread." + type: Literal["function"] + + +class ApiChatPublishLocalToolsInput(TypedDict): + "Replace the local tools advertised by this connection for later messages it posts" + + local_tool_provider_id: str + local_tools: list[ApiChatPublishLocalToolsInputLocalToolsItem] + + +class ApiChatLocalToolResultInput(TypedDict): + "Return one complete result batch for a connection-local tool request" + + agent_id: str + generation: int + provider_id: str + request_id: str + results: list[dict[str, Any] | dict[str, Any] | dict[str, Any]] + + class MessageAddedPayloadMessageAclAddItem(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.' @@ -242,6 +295,17 @@ class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False): "Width in pixels of the media item. Present on `media` type only. `null` otherwise." +class MessageAddedPayloadMessageContextItem(TypedDict, total=False): + attributes: dict[str, Any] | None + "Scalar key-value fields describing the context, such as route, repository, or pull-request number." + content: str | None + "Optional context body. The model receives it as escaped XML data, not a system instruction." + title: str | None + "Optional human-readable label for this context block." + type: Required[str] + "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores." + + class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False): payload: dict[str, Any] | None 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' @@ -266,6 +330,8 @@ class MessageAddedPayloadMessage(TypedDict, total=False): "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." content: str | None "Text content of the message. `null` for messages that contain only attachments." + context: list[MessageAddedPayloadMessageContextItem] | None + "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions." created_at: str | None "When the message was posted (ISO 8601)." has_replies: bool | None @@ -482,6 +548,17 @@ class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False): "Width in pixels of the media item. Present on `media` type only. `null` otherwise." +class MessageUpdatedPayloadMessageContextItem(TypedDict, total=False): + attributes: dict[str, Any] | None + "Scalar key-value fields describing the context, such as route, repository, or pull-request number." + content: str | None + "Optional context body. The model receives it as escaped XML data, not a system instruction." + title: str | None + "Optional human-readable label for this context block." + type: Required[str] + "Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores." + + class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False): payload: dict[str, Any] | None 'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).' @@ -506,6 +583,8 @@ class MessageUpdatedPayloadMessage(TypedDict, total=False): "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread." content: str | None "Text content of the message. `null` for messages that contain only attachments." + context: list[MessageUpdatedPayloadMessageContextItem] | None + "Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions." created_at: str | None "When the message was posted (ISO 8601)." has_replies: bool | None @@ -611,6 +690,38 @@ class TypingPayload(TypedDict, total=False): thread_id: str | None +class LocalToolCallPayloadRequestCallsItem(TypedDict): + arguments: dict[str, Any] + id: str + name: str + + +class LocalToolCallPayloadRequest(TypedDict): + calls: list[LocalToolCallPayloadRequestCallsItem] + id: str + + +class LocalToolCallPayload(TypedDict, total=False): + "Invoke local functions on the exact connection that published them" + + agent_id: str | None + expires_at: datetime | None + generation: int | None + provider_id: str | None + request: LocalToolCallPayloadRequest | None + "A correlated batch of local function calls targeted to one connection." + thread_id: str | None + + +class LocalToolCancelledPayload(TypedDict, total=False): + "Stop local work because the server no longer accepts its result" + + generation: int | None + provider_id: str | None + reason: str | None + request_id: str | None + + # Channel for real-time chat messaging. # Supports team-scoped and user-scoped threads with keyed, transient, and direct # thread access patterns. @@ -868,6 +979,18 @@ async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]: return await self._channel.push("api:chat:typing", payload) + # Replace the local tools advertised by this connection for later messages it posts + async def api_chat_publish_local_tools( + self, payload: ApiChatPublishLocalToolsInput + ) -> dict[str, Any]: + return await self._channel.push("api:chat:publish_local_tools", payload) + + # Return one complete result batch for a connection-local tool request + async def api_chat_local_tool_result( + self, payload: ApiChatLocalToolResultInput + ) -> dict[str, Any]: + return await self._channel.push("api:chat:local_tool_result", payload) + # Broadcast when a new message is added to a thread def on_message_added( self, callback: Callable[[MessageAddedPayload], None] @@ -891,3 +1014,15 @@ def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Cal # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted. def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]: return self._channel.on("typing", callback) + + # Invoke local functions on the exact connection that published them + def on_local_tool_call( + self, callback: Callable[[LocalToolCallPayload], None] + ) -> Callable[[], None]: + return self._channel.on("local_tool_call", callback) + + # Stop local work because the server no longer accepts its result + def on_local_tool_cancelled( + self, callback: Callable[[LocalToolCancelledPayload], None] + ) -> Callable[[], None]: + return self._channel.on("local_tool_cancelled", callback) diff --git a/src/archastro/platform/channels/api_tasks_channel.py b/src/archastro/platform/channels/api_tasks_channel.py index de404da..273afa6 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: 426450fa3df6 +# Content hash: 581f4d31d853 from collections.abc import Callable from datetime import datetime @@ -78,6 +78,8 @@ class TasksUpdatedPayloadTasksItemOwnerActor(TypedDict, total=False): class TasksUpdatedPayloadTasksItem(TypedDict, total=False): agent: str | None "ID of the agent that owns this task (`agi_...`). `null` if the task is scoped to a team or user." + 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." closed_at: datetime | None @@ -131,7 +133,7 @@ class TasksUpdatedPayloadTasksItem(TypedDict, total=False): source_type: str | None "Kind of source object (for example `repository`). `null` when the task has no source." status: Required[str] - 'Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.' + '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." tags: list[str] | None diff --git a/src/archastro/platform/client.py b/src/archastro/platform/client.py index fffa8ce..3455990 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: f24da3972099 +# Content hash: 6a1a025e96fb from urllib.parse import urlparse, urlunparse @@ -55,6 +55,8 @@ def __init__( self.custom_objects = self.v1.custom_objects self.event_subscription_deliveries = self.v1.event_subscription_deliveries self.event_subscriptions = self.v1.event_subscriptions + self.external_object_capabilities = self.v1.external_object_capabilities + self.external_objects = self.v1.external_objects self.extractions = self.v1.extractions self.files = self.v1.files self.installation_sources = self.v1.installation_sources @@ -70,11 +72,13 @@ def __init__( self.private_service_enrollments = self.v1.private_service_enrollments self.private_services = self.v1.private_services self.sandboxes = self.v1.sandboxes + self.scripts = self.v1.scripts self.slack_channel_bindings = self.v1.slack_channel_bindings self.solution_categories = self.v1.solution_categories self.solution_instances = self.v1.solution_instances self.solution_tags = self.v1.solution_tags self.solutions = self.v1.solutions + self.ssh_keys = self.v1.ssh_keys self.status = self.v1.status self.tasks = self.v1.tasks self.team_memberships = self.v1.team_memberships @@ -84,6 +88,7 @@ def __init__( self.trajectories = self.v1.trajectories self.users = self.v1.users self.work_items = self.v1.work_items + self.workflows = self.v1.workflows self.ai = self.v1.ai self.oauth = self.v1.oauth self._refresh_token: str | None = None @@ -288,6 +293,8 @@ def __init__( self.custom_objects = self.v1.custom_objects self.event_subscription_deliveries = self.v1.event_subscription_deliveries self.event_subscriptions = self.v1.event_subscriptions + self.external_object_capabilities = self.v1.external_object_capabilities + self.external_objects = self.v1.external_objects self.extractions = self.v1.extractions self.files = self.v1.files self.installation_sources = self.v1.installation_sources @@ -303,11 +310,13 @@ def __init__( self.private_service_enrollments = self.v1.private_service_enrollments self.private_services = self.v1.private_services self.sandboxes = self.v1.sandboxes + self.scripts = self.v1.scripts self.slack_channel_bindings = self.v1.slack_channel_bindings self.solution_categories = self.v1.solution_categories self.solution_instances = self.v1.solution_instances self.solution_tags = self.v1.solution_tags self.solutions = self.v1.solutions + self.ssh_keys = self.v1.ssh_keys self.status = self.v1.status self.tasks = self.v1.tasks self.team_memberships = self.v1.team_memberships @@ -317,6 +326,7 @@ def __init__( self.trajectories = self.v1.trajectories self.users = self.v1.users self.work_items = self.v1.work_items + self.workflows = self.v1.workflows self.ai = self.v1.ai self.oauth = self.v1.oauth self._refresh_token: str | None = None diff --git a/src/archastro/platform/types/__init__.py b/src/archastro/platform/types/__init__.py index 9ad95c3..0ed885f 100644 --- a/src/archastro/platform/types/__init__.py +++ b/src/archastro/platform/types/__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: e6e437459282 +# Content hash: e091b32c718b from .ai import * # noqa: F401,F403 from .artifacts import * # noqa: F401,F403 @@ -9,14 +9,19 @@ from .common import * # noqa: F401,F403 from .config import * # noqa: F401,F403 from .device import * # noqa: F401,F403 +from .events import * # noqa: F401,F403 +from .expressions import * # noqa: F401,F403 from .extractions import * # noqa: F401,F403 +from .graph import * # noqa: F401,F403 from .image import * # noqa: F401,F403 from .invites import * # noqa: F401,F403 from .notifications import * # noqa: F401,F403 from .oauth import * # noqa: F401,F403 +from .scripts import * # noqa: F401,F403 from .status import * # noqa: F401,F403 from .system import * # noqa: F401,F403 from .tasks import * # noqa: F401,F403 from .teams import * # noqa: F401,F403 from .threads import * # noqa: F401,F403 from .users import * # noqa: F401,F403 +from .workflows import * # noqa: F401,F403 diff --git a/src/archastro/platform/types/ai.py b/src/archastro/platform/types/ai.py index ab16713..b538574 100644 --- a/src/archastro/platform/types/ai.py +++ b/src/archastro/platform/types/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: 9ba45d20ba35 +# Content hash: 7e0accb3214e from typing import Any @@ -252,6 +252,10 @@ class AIImageResult(BaseModel): default=None, description="The prompt as rewritten by the provider before generation. Some providers (e.g. DALL-E 3) automatically expand or safety-check the original prompt. `null` when the provider does not revise prompts.", ) + session_id: str | None = Field( + default=None, + description="UUID grouping this image request's provider attempts for usage and billing.", + ) size: str | None = Field( default=None, description='Canonical size string as returned by the provider, e.g. `"1024x1024"`. `null` when not reported.', @@ -260,6 +264,10 @@ class AIImageResult(BaseModel): default=None, description="Provider-reported token and compute usage for the request. Structure varies by provider. `null` when usage data is unavailable.", ) + usage_attempt_count: int | None = Field( + default=None, + description="Number of provider responses contributing usage records to this request, including image-generation retries.", + ) width: int | None = Field( default=None, description="Width of the generated image in pixels. `null` when the provider does not report dimensions.", diff --git a/src/archastro/platform/types/artifacts.py b/src/archastro/platform/types/artifacts.py index 3ab6611..5794800 100644 --- a/src/archastro/platform/types/artifacts.py +++ b/src/archastro/platform/types/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: 3be98d76d114 +# Content hash: b1c2b3d9044a from datetime import datetime @@ -45,6 +45,10 @@ class Artifact(BaseModel): default=None, description="Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", ) + group_key: str | None = Field( + default=None, + description="Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + ) id: str = Field(..., description="Artifact ID (`art_...`).") image_source: ImageSource | None = Field( default=None, @@ -61,6 +65,10 @@ class Artifact(BaseModel): default=None, description="Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", ) + system: bool | None = Field( + default=None, + description="True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + ) team: str | None = Field( default=None, description="ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", diff --git a/src/archastro/platform/types/chat.py b/src/archastro/platform/types/chat.py index 908fcfb..049f052 100644 --- a/src/archastro/platform/types/chat.py +++ b/src/archastro/platform/types/chat.py @@ -1,12 +1,12 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 43c3317ab780 +# Content hash: 25c7806cf204 -from typing import Any, Literal +from typing import Annotated, Any, Literal from pydantic import BaseModel, Field -from .common import Agent, Message +from .common import Agent, Cancelled, Message, Ok from .teams import Team from .threads import Thread from .users import User @@ -107,6 +107,16 @@ class ChatLoadMoreMessagesResponse(BaseModel): ) +class ChatLocalToolCall(BaseModel): + """ + One local function invocation requested by the model. + """ + + arguments: dict[str, Any] + id: str + name: str + + class ChatLocalToolFunction(BaseModel): """ A function implemented by the client connected to a personal thread. @@ -119,13 +129,48 @@ class ChatLocalToolFunction(BaseModel): class ChatLocalToolDefinition(BaseModel): """ - An OpenAI-compatible local function definition supplied while joining a personal thread. + An OpenAI-compatible local function definition advertised by a personal-thread connection. """ function: ChatLocalToolFunction type: Literal["function"] +class ChatLocalToolProvider(BaseModel): + """ + Server-issued identity for the local-tool set currently advertised by this connection. + """ + + generation: int + provider_id: str + + +class ChatLocalToolRequest(BaseModel): + """ + A correlated batch of local function calls targeted to one connection. + """ + + calls: list[ChatLocalToolCall] + id: str + + +class ChatLocalToolResultError(BaseModel): + """ + A failed local-tool outcome. + """ + + call_id: str + code: str + message: str + status: Literal["error"] = "error" + + +# One terminal local-tool outcome returned by the owning client connection. +ChatLocalToolResult = Annotated[ + Ok | ChatLocalToolResultError | Cancelled, Field(discriminator="status") +] + + class ChatMarkThreadReadResponse(BaseModel): """ Response returned after marking a chat thread as read. Confirms that the read marker was successfully recorded for the authenticated user. diff --git a/src/archastro/platform/types/common.py b/src/archastro/platform/types/common.py index 3a93d42..4de0207 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: 5649ca8f18df +# Content hash: de3c1b188a0c from datetime import datetime from typing import Annotated, Any, Literal @@ -11,6 +11,7 @@ from .config import Config from .image import ImageSource from .users import User +from .workflows import WorkflowJournal, WorkflowJournalEntry class AclGrant(BaseModel): @@ -322,6 +323,18 @@ class WorkerStatus(BaseModel): ) +class Setup(BaseModel): + """ + Current MCP setup assessment, not a historical downgrade reason or upstream protocol probe. + """ + + next_action: str = Field(..., description="Recommended next step for MCP setup.") + protocol_status: Literal["not_checked"] = Field( + ..., description="MCP protocol health is not checked by this assessment." + ) + reason: str = Field(..., description="Reason for the current MCP setup state.") + + 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. @@ -447,6 +460,10 @@ class AuthTokens(BaseModel): Credential bundle returned after a successful authentication exchange. Contains the access token, refresh token, and the authenticated user. """ + account_created: bool = Field( + ..., + description="`true` when this request created the account (the same moment the platform records the signup), `false` for a returning sign-in or a token refresh. Registration always returns `true`; password login and refresh always return `false`.", + ) expires_in: int = Field( ..., description="Number of seconds until `token` expires. After this period, use `refresh_token` to obtain a new access token.", @@ -578,6 +595,16 @@ class BuiltinToolCatalogEntry(BaseModel): ) +class Cancelled(BaseModel): + """ + A cancelled local-tool outcome. + """ + + call_id: str + reason: str + status: Literal["cancelled"] = "cancelled" + + class ChannelAck(BaseModel): """ Empty acknowledgement payload returned by channel message handlers that produce no data. The wire envelope is `{"status": "ok", "response": {}}`. @@ -586,6 +613,28 @@ class ChannelAck(BaseModel): pass +class MessageContext(BaseModel): + """ + Structured context captured with a chat message and delivered to agents as data. + """ + + attributes: dict[str, Any] | None = Field( + default=None, + description="Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + ) + content: str | None = Field( + default=None, + description="Optional context body. The model receives it as escaped XML data, not a system instruction.", + ) + title: str | None = Field( + default=None, description="Optional human-readable label for this context block." + ) + type: str = Field( + ..., + description="Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", + ) + + class MessageReaction(BaseModel): """ A compact reaction record embedded in a message's `reactions` array, representing a single user's reaction to a message. @@ -637,6 +686,10 @@ class Message(BaseModel): default=None, description="Text content of the message. `null` for messages that contain only attachments.", ) + context: list[MessageContext] | None = Field( + default=None, + description="Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + ) created_at: str | None = Field( default=None, description="When the message was posted (ISO 8601)." ) @@ -721,6 +774,47 @@ class Message(BaseModel): ) +class Ok(BaseModel): + """ + A successful local-tool outcome. + """ + + call_id: str + content: str + status: Literal["ok"] = "ok" + + +class Command(BaseModel): + """ + A workflow command that a workflow node can invoke, including its identifier, human-readable name, and JSON schemas for its inputs and outputs. + """ + + description: str = Field( + ..., + description="Human-readable description of what the command does, shown alongside the name in the workflow builder.", + ) + errorOutput: dict[str, Any] | None = Field( + default=None, + description="JSON Schema describing the shape of an error output. `null` if the command does not produce structured error data.", + ) + expectedInput: dict[str, Any] | None = Field( + default=None, + description="JSON Schema describing the input the command expects. `null` if the command takes no input.", + ) + id: str = Field( + ..., + description="Unique string identifier for the command, used to reference it in workflow node configurations.", + ) + name: str = Field( + ..., + description="Human-readable display name for the command shown in the workflow builder.", + ) + successOutput: dict[str, Any] | None = Field( + default=None, + description="JSON Schema describing the shape of a successful output. `null` if the command produces no output on success.", + ) + + 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. @@ -911,6 +1005,115 @@ class CreatedPrivateServiceEnrollment(BaseModel): private_service: str +class EffectiveAccessBillingTrial(BaseModel): + """ + Privacy-safe current trial state for one Viewer-derived billing principal. + """ + + ends_at: datetime = Field( + ..., + description="Effective trial end: the earlier of its accepted boundary and enrollment end.", + ) + payment_method_stored: bool = Field( + ..., description="Whether a provider webhook has confirmed a stored payment method." + ) + plan: str = Field(..., description="Catalog plan whose trial is active.") + + +class EffectiveAccessBillingPrincipal(BaseModel): + """ + Billing authority and relevant active trials for one Viewer-derived principal kind. + """ + + administrator: bool = Field( + ..., + description="Whether the authenticated user may administer this principal's billing account.", + ) + principal_type: Literal["user", "org"] + trials: list[EffectiveAccessBillingTrial] = Field( + ..., + description="Currently active trials for this Viewer-derived principal. Contains no billing-account or provider identifiers.", + ) + + +class EffectiveAccessEntitlement(BaseModel): + """ + A billing-derived effective entitlement for the authenticated user. + """ + + granted: bool = Field(..., description="Whether any eligible source grants the key.") + key: str = Field(..., description="Stable entitlement catalog key.") + provided_by: list[Literal["personal", "organization"]] = Field( + ..., description="Privacy-safe principal kinds that contribute to the effective value." + ) + value: bool | None = Field( + default=None, description="Merged entitlement value, or `null` when the key is not granted." + ) + value_type: Literal["boolean"] = Field( + ..., description="Catalog type used to interpret `value`." + ) + + +class EffectiveAccess(BaseModel): + """ + Live effective access, billing authority, and relevant trials for the authenticated user. + """ + + billing: list[EffectiveAccessBillingPrincipal] = Field( + ..., + description="Administration authority and requested-entitlement-relevant trials for the personal and current-organization principals.", + ) + entitlements: list[EffectiveAccessEntitlement] = Field( + ..., description="Requested catalog entitlements in request order." + ) + + +class CurrentUser(BaseModel): + """ + The authenticated user's profile, live session context, and optional effective access. + """ + + alias: str | None = Field(default=None, description="Short handle or alias of the user.") + app: str | None = Field(default=None, description="ID of the token-scoped app (`dap_...`).") + app_name: str | None = Field(default=None, description="Display name of the token-scoped app.") + effective_access: EffectiveAccess | None = Field( + default=None, + description="Present only when the request supplies one or more `entitlement[]` values.", + ) + email: str | None = Field(default=None, description="Email address of the user.") + id: str = Field(..., description="User ID (`usr_...`).") + is_system_user: bool = Field( + ..., description="Whether the authenticated account is an internal system user." + ) + metadata: dict[str, Any] | None = Field(default=None, description="Arbitrary user metadata.") + name: str | None = Field(default=None, description="Full display name of the user.") + notification_settings: dict[str, Any] = Field( + ..., description="The authenticated user's notification preferences." + ) + org: str | None = Field(default=None, description="ID of the current organization (`org_...`).") + org_kind: Literal["company", "personal"] | None = Field( + default=None, + description="Whether the current organization is company-domain or person-owned.", + ) + org_name: str | None = Field( + default=None, description="Display name of the current organization." + ) + org_role: str | None = Field( + default=None, description="The authenticated user's role in the current organization." + ) + org_slug: str | None = Field( + default=None, description="Stable slug of the current organization." + ) + profile_picture: ImageSource | None = Field( + default=None, description="Resolved profile picture metadata." + ) + sandbox: str | None = Field(default=None, description="ID of the current sandbox (`sbx_...`).") + sandbox_name: str | None = Field( + default=None, description="Display name of the current sandbox." + ) + timezone: str | None = Field(default=None, description="The user's IANA timezone.") + + class CustomObject(BaseModel): """ A custom object belonging to an organization. Custom objects store arbitrary structured data defined by a schema type and are scoped to an org, team, or user. @@ -1040,6 +1243,18 @@ class CustomObjectUpdateFieldsResponse(BaseModel): operation_id: str = Field(..., description="Idempotency key acknowledged for this update.") +class D1Database(BaseModel): + """ + Provider object fields for a D1 database. + """ + + id: str = Field(..., description="Provider-assigned object identifier.") + jurisdiction: str | None = None + name: str + read_replication: dict[str, Any] | None = None + type: Literal["d1_database"] = "d1_database" + + class Deployment(BaseModel): """ Deployment metadata. @@ -1054,6 +1269,22 @@ class Deployment(BaseModel): ) +class R2Bucket(BaseModel): + """ + Provider object fields for an R2 bucket. + """ + + id: str = Field(..., description="Provider-assigned object identifier.") + location: str | None = None + name: str + storage_class: str | None = None + type: Literal["r2_bucket"] = "r2_bucket" + + +# Service-specific external object payload selected by `type`. +Details = Annotated[R2Bucket | D1Database, Field(discriminator="type")] + + class DomainEvent(BaseModel): """ A domain event with stable attribution fields and an event-specific payload. @@ -1074,103 +1305,21 @@ class DomainEvent(BaseModel): user: str | None = None -class EventSubscription(BaseModel): +class ExternalObject(BaseModel): """ - An app-scoped subscription to exact domain-event names. + An owner-scoped object provisioned through an external integration. """ agent: str | None = None - available_count: int created_at: datetime - dropped_events_total: int - dropped_through_position: int - event_names: list[str] - id: str - last_overflow_at: datetime | None = None - leased_count: int - max_pending_events: int - name: str + id: str = Field(..., description="ArchAstro external object ID (`ext_...`).") + integration: str = Field(..., description="Integration ID (`int_...`).") + object: Details = Field(..., description="Service-specific object payload.") org: str | None = None - queue_epoch: int - retention_seconds: int - sandbox: str | None = None - status: Literal["active", "paused"] + service: Literal["cloudflare"] = "cloudflare" team: str | None = None updated_at: datetime user: str | None = None - visibility_timeout_seconds: int - - -class EventSubscriptionDelivery(BaseModel): - """ - A domain event leased from a subscription queue. - """ - - delivery_id: str - event: DomainEvent - lease_expires_at: datetime - receipt_handle: str - receive_count: int - sequence: int - - -class EventSubscriptionClaim(BaseModel): - """ - Result of atomically claiming the head delivery. - """ - - data: list[EventSubscriptionDelivery] - dropped_events_total: int - dropped_through_position: int - has_more: bool - queue_epoch: int - - -class EventSubscriptionQueueEntry(BaseModel): - """ - A non-reserving view of one queued delivery. - """ - - delivery_id: str - event: DomainEvent - lease_expires_at: datetime | None = None - receive_count: int - sequence: int - state: Literal["available", "leased"] - - -class EventSubscriptionHead(BaseModel): - """ - A non-reserving view of the queue head. - """ - - data: EventSubscriptionQueueEntry | None = None - - -class EventSubscriptionPage(BaseModel): - """ - A page of domain-event subscriptions. - """ - - data: list[EventSubscription] - page: int - per_page: int - total_count: int - total_pages: int - - -class EventSubscriptionQueue(BaseModel): - """ - A cursor-paginated non-reserving view of a subscription queue. - """ - - after_cursor: str | None = None - before_cursor: str | None = None - data: list[EventSubscriptionQueueEntry] - dropped_events_total: int - dropped_through_position: int - has_more: bool - queue_epoch: int class StorageFile(BaseModel): @@ -1643,6 +1792,44 @@ class KnowledgeSourceKindListResponse(BaseModel): ) +class NodeField(BaseModel): + """ + Schema for a field definition within a node type + """ + + allowExpression: bool | None = Field( + default=None, description="Whether the field accepts expressions" + ) + defaultValue: Any | None = Field( + default=None, description="Default JSON-safe value for the field" + ) + description: str | None = Field(default=None, description="Field description") + key: str = Field(..., description="Field identifier") + label: str = Field(..., description="Display label") + options: list[dict[str, Any]] | None = Field( + default=None, description="Available options for select fields" + ) + required: bool | None = Field(default=False, description="Whether the field is required") + type: str = Field(..., description="Field type (string, number, boolean, expression, etc.)") + + +class NodeType(BaseModel): + """ + Schema for a workflow node type definition + """ + + badge: str | None = Field(default=None, description="Short badge text for UI") + category: str | None = Field( + default=None, description="Category for grouping (e.g., Logic, Network, Utility)" + ) + color: str | None = Field(default=None, description="Hex color for UI display") + description: str = Field(..., description="Node description") + docs: str | None = Field(default=None, description="Extended documentation") + fields: list[NodeField] = Field(..., description="Field definitions for this node type") + id: str = Field(..., description="Unique node type identifier") + label: str = Field(..., description="Display label") + + class PaginatedReplies(BaseModel): """ A paginated list of reply messages for a thread. The reply array is returned directly, not nested inside a `data` wrapper. @@ -1804,84 +1991,69 @@ class RoutinePreset(BaseModel): ) -class WorkflowJournalEntry(BaseModel): +class RunJournalPage(BaseModel): """ - One ordered, replayable record from a durable workflow journal. + A forward-paginated journal entry page for an automation or routine run. """ - command_id: str | None = Field( + after_cursor: str | None = Field( default=None, - description="Durable command identifier associated with the record, when present.", - ) - created_at: datetime | None = Field( - default=None, description="When this entry was durably committed." + description="Opaque cursor for the next entry page. `null` when this is the final page.", ) - id: str = Field(..., description="Journal entry ID (`wdr_...`).") - node_id: str | None = Field( - default=None, - description="Workflow node associated with the record. `null` for execution-level records.", + before_cursor: str | None = Field( + default=None, description="Always `null`; journal pagination is forward-only." ) - record: dict[str, Any] = Field( + data: list[WorkflowJournalEntry] = Field( ..., - description="Replayable workflow record body, including payload, context, environment, metadata, and timestamp.", + description="Journal entries ordered by ascending sequence. Empty when the run has no journal.", ) - sequence: int = Field(..., description="Monotonically increasing sequence within the journal.") - timer_id: str | None = Field( + has_more: bool = Field(..., description="Whether additional entries exist after this page.") + journal: WorkflowJournal | None = Field( default=None, - description="Durable timer identifier associated with the record, when present.", + description="Durable execution summary. `null` when this run has no journal, which is valid for script-backed, preview, or legacy runs.", ) - type: str = Field( - ..., - description="Workflow record type, such as `node_started`, `node_completed`, or `node_failed`.", + + +class RuntimeCapability(BaseModel): + """ + Short-lived bootstrap values for one hosted external-object runtime. + """ + + bindings: dict[str, Any] + capability: str = Field( + ..., description="Opaque bearer capability. Keep server-side and do not persist it." ) + expires_at: datetime + gateway_url: str -class WorkflowJournal(BaseModel): +class RuntimeEnvVar(BaseModel): """ - Summary of the durable workflow execution journal associated with a run. + A single runtime environment variable available within a script execution context, including its key, description, and origin. """ - completed_at: datetime | None = Field( + description: str | None = Field( default=None, - description="When durable workflow execution reached a terminal state. `null` while it is active.", - ) - created_at: datetime | None = Field(default=None, description="When the journal was created.") - current_sequence: int = Field( - ..., description="Highest workflow record sequence durably committed to this journal." - ) - id: str = Field(..., description="Journal execution ID (`wde_...`).") - started_at: datetime | None = Field( - default=None, description="When durable workflow execution started." + description="Human-readable explanation of the variable's purpose. `null` if no description has been set.", ) - status: str = Field( + key: str = Field( ..., - description="Current durable execution status: `pending`, `running`, `waiting`, `completed`, `failed`, or `cancelled`.", + description="The name of the environment variable as it appears in the script runtime (e.g. `DATABASE_URL`).", ) - updated_at: datetime | None = Field( - default=None, description="When the journal was last updated." + source: str = Field( + ..., + description='Origin of the environment variable. One of `"app"` (set on the application) or `"org"` (inherited from the organization).', ) -class RunJournalPage(BaseModel): +class RuntimeEnvVarList(BaseModel): """ - A forward-paginated journal entry page for an automation or routine run. + The collection of runtime environment variables available to the current script execution context. """ - after_cursor: str | None = Field( - default=None, - description="Opaque cursor for the next entry page. `null` when this is the final page.", - ) - before_cursor: str | None = Field( - default=None, description="Always `null`; journal pagination is forward-only." - ) - data: list[WorkflowJournalEntry] = Field( + data: list[RuntimeEnvVar] = Field( ..., - description="Journal entries ordered by ascending sequence. Empty when the run has no journal.", - ) - has_more: bool = Field(..., description="Whether additional entries exist after this page.") - journal: WorkflowJournal | None = Field( - default=None, - description="Durable execution summary. `null` when this run has no journal, which is valid for script-backed, preview, or legacy runs.", + description="Array of runtime environment variable objects available in the current script execution context.", ) @@ -1972,7 +2144,7 @@ class SlackChannelBinding(BaseModel): agents: list[str] | None = Field( default=None, - description="IDs of every agent attached to this binding, including legacy concierge attachments. Use `resident_agent` and `route_kind` for the effective runtime route.", + description="IDs of every agent attached to this binding, including attachments left over from retired flows. Use `resident_agent` and `route_kind` for the effective runtime route.", ) allow_bot_conversations: bool = Field( ..., @@ -2025,9 +2197,9 @@ class SlackChannelBinding(BaseModel): default=None, description="ID of the resident agent selected by Slack ingress. `null` when no resident is attached and the channel is an observer.", ) - route_kind: Literal["fda", "resident", "observer", "concierge"] = Field( + route_kind: Literal["fda", "resident", "observer"] = Field( ..., - description="Effective Slack ingress route. `fda` a resident on a team-bound channel, replying through the Forward Deployed Agent chain. `resident` a resident on an internal channel, replying through the channel mirror. `observer` no resident is attached, so the channel is recorded and nobody replies. `concierge` no longer returned anywhere; until Track F it was the value for a channel with no resident, meaning the shared concierge agent answered there. The value is retained in this enum so consumers matching on it do not break, and its removal rides a deliberate API change.", + description="Effective Slack ingress route. `fda` a resident on a team-bound channel, replying through the Forward Deployed Agent chain. `resident` a resident on an internal channel, replying through the channel mirror. `observer` no resident is attached, so the channel is recorded and nobody replies.", ) scope_key: str | None = Field( default=None, @@ -2178,83 +2350,6 @@ class ValidationResult(BaseModel): ) -class WorkflowWorkItem(BaseModel): - """ - Externally executable work yielded by a durable workflow. - """ - - agent: str = Field(..., description="Agent assigned to execute this work.") - attempt_count: int = Field( - ..., description="Number of times this work has been freshly claimed or reclaimed." - ) - command_id: str = Field( - ..., description="Opaque journal command identity used to resume the workflow exactly once." - ) - created_at: datetime - execution: str = Field(..., description="Durable workflow execution that owns this work.") - id: str = Field(..., description="Work item ID (`wdi_...`).") - lease_expires_at: datetime | None = Field( - default=None, - description="When the current claim expires. Null for queued or terminal work.", - ) - node_id: str = Field(..., description="Workflow graph node that yielded the work.") - payload: dict[str, Any] = Field( - ..., description="Instructions and participant bindings needed to execute the work." - ) - routine_run: str | None = Field( - default=None, - description="Routine run that owns the execution, when this work came from a routine.", - ) - status: str = Field(..., description="Current queue lifecycle status.") - type: str = Field( - ..., description="Stable resource discriminator. Always `workflow_work_item`." - ) - updated_at: datetime - - -class WorkflowWorkItemLease(BaseModel): - """ - A claimed workflow work item and its caller-held lease token. - """ - - lease_owner: str = Field( - ..., - description="Opaque lease token that must be persisted and presented for later transitions.", - ) - work_item: WorkflowWorkItem = Field( - ..., description="The claimed, resumed, started, or heartbeated work item." - ) - - -class WorkflowWorkItemClaim(BaseModel): - """ - Result of polling an agent's durable workflow work queue. - """ - - data: WorkflowWorkItemLease | None = Field( - default=None, - description="Claimed or resumed work and its lease; null when no eligible item exists.", - ) - - -class WorkflowWorkItemList(BaseModel): - """ - Active durable workflow work available to the viewer. - """ - - after_cursor: str | None = Field( - default=None, description="Opaque cursor for the next page, or null at the end." - ) - before_cursor: str | None = Field( - default=None, description="Always null because queue pagination is forward-only." - ) - data: list[WorkflowWorkItem] = Field( - ..., - description="Active work items. Lease tokens are intentionally never included in list responses.", - ) - has_more: bool = Field(..., description="Whether another page of work exists.") - - class WorkingMemoryEntry(BaseModel): """ A key-value memory record stored for an agent, optionally scoped to a user. Memory entries persist across invocations and may carry an expiration time. @@ -2421,7 +2516,11 @@ class SolutionSummary(BaseModel): id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -3356,8 +3455,12 @@ class AgentTool(BaseModel): default=None, description="ID of the config record (`cfg_...`) storing the tool's parameter schema. `null` when parameters are defined inline.", ) + setup: Setup | None = Field( + default=None, + description="Current MCP setup assessment, not a historical downgrade reason or upstream protocol probe.", + ) status: str | None = Field( - default=None, description='Current status of the tool. One of `"active"` or `"disabled"`.' + default=None, description='Current status of the tool. One of `"active"` or `"draft"`.' ) updated_at: datetime | None = Field( default=None, description="When the tool was last modified (ISO 8601)." @@ -3436,11 +3539,11 @@ class AgentUpgradeChange(BaseModel): ) resource: dict[str, Any] | None = Field( default=None, - description="Resource-type-specific identity details. Tools: `tool_type`, `builtin_tool_key`, `name_prefix`, `handler_type`, `instruction`. Routines: `handler_type`, `preset_name`, `event_type`, `schedule`, `trigger_context`. Skills: `instruction`. Computers: `region`. Only populated keys are present; `null` when nothing is known.", + description="Resource-type-specific identity details. Tools: `tool_type`, `builtin_tool_key`, `name_prefix`, `handler_type`, `instruction`. Routines: `handler_type`, `preset_name`, `event_type`, `schedule`, `trigger_context`. Skills: `instruction`. Computers: `region`. Knowledge: `mode`, `knowledge_key`, `extraction_output_ref`. Only populated keys are present; `null` when nothing is known.", ) resource_type: str = Field( ..., - description='Type of the child resource being changed. One of `"agent"`, `"tool"`, `"routine"`, `"skill"`, or `"computer"`.', + description='Type of the resource being changed. One of `"agent"`, `"tool"`, `"routine"`, `"skill"`, `"computer"`, or `"knowledge"`.', ) source_template_config: UpgradeTemplateSummary | None = Field( default=None, diff --git a/src/archastro/platform/types/events.py b/src/archastro/platform/types/events.py new file mode 100644 index 0000000..9532300 --- /dev/null +++ b/src/archastro/platform/types/events.py @@ -0,0 +1,109 @@ +# 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 + +from datetime import datetime +from typing import Literal + +from pydantic import BaseModel + +from .common import DomainEvent + + +class EventSubscription(BaseModel): + """ + An app-scoped subscription to exact domain-event names. + """ + + agent: str | None = None + available_count: int + created_at: datetime + dropped_events_total: int + dropped_through_position: int + event_names: list[str] + id: str + last_overflow_at: datetime | None = None + leased_count: int + max_pending_events: int + name: str + org: str | None = None + queue_epoch: int + retention_seconds: int + sandbox: str | None = None + status: Literal["active", "paused"] + team: str | None = None + updated_at: datetime + user: str | None = None + visibility_timeout_seconds: int + + +class EventSubscriptionDelivery(BaseModel): + """ + A domain event leased from a subscription queue. + """ + + delivery_id: str + event: DomainEvent + lease_expires_at: datetime + receipt_handle: str + receive_count: int + sequence: int + + +class EventSubscriptionClaim(BaseModel): + """ + Result of atomically claiming the head delivery. + """ + + data: list[EventSubscriptionDelivery] + dropped_events_total: int + dropped_through_position: int + has_more: bool + queue_epoch: int + + +class EventSubscriptionQueueEntry(BaseModel): + """ + A non-reserving view of one queued delivery. + """ + + delivery_id: str + event: DomainEvent + lease_expires_at: datetime | None = None + receive_count: int + sequence: int + state: Literal["available", "leased"] + + +class EventSubscriptionHead(BaseModel): + """ + A non-reserving view of the queue head. + """ + + data: EventSubscriptionQueueEntry | None = None + + +class EventSubscriptionPage(BaseModel): + """ + A page of domain-event subscriptions. + """ + + data: list[EventSubscription] + page: int + per_page: int + total_count: int + total_pages: int + + +class EventSubscriptionQueue(BaseModel): + """ + A cursor-paginated non-reserving view of a subscription queue. + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[EventSubscriptionQueueEntry] + dropped_events_total: int + dropped_through_position: int + has_more: bool + queue_epoch: int diff --git a/src/archastro/platform/types/expressions.py b/src/archastro/platform/types/expressions.py new file mode 100644 index 0000000..a081cc3 --- /dev/null +++ b/src/archastro/platform/types/expressions.py @@ -0,0 +1,39 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 8f12e992bceb + +from typing import Any + +from pydantic import BaseModel, Field + + +class ExpressionResult(BaseModel): + """ + Schema for the result of evaluating an expression + """ + + output: list[dict[str, Any]] | None = Field( + default=[], + description="Output lines from println calls, each with :line (source line number), :text, and optional :values (JSON-serializable structured data)", + ) + result: Any = Field(..., description="The evaluated JSON-safe result") + + +class ExpressionValidation(BaseModel): + """ + Schema for expression validation responses + """ + + error: str | None = Field(default=None, description="Validation error message") + findings: list[dict[str, Any]] | None = Field( + default=None, description="Structured findings with position info for editor diagnostics" + ) + ok: bool = Field(..., description="Whether the validation call succeeded") + symbols: list[dict[str, Any]] | None = Field( + default=None, + description="Inferred symbol types at source positions (name, type, line, column)", + ) + valid: bool | None = Field(default=None, description="Whether the expression is valid") + warnings: list[str] | None = Field( + default=None, description="Semantic analysis warnings (non-blocking)" + ) diff --git a/src/archastro/platform/types/graph.py b/src/archastro/platform/types/graph.py new file mode 100644 index 0000000..33b811e --- /dev/null +++ b/src/archastro/platform/types/graph.py @@ -0,0 +1,25 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: fd6b31894aef + +from typing import Any + +from pydantic import BaseModel, Field + + +class GraphValidation(BaseModel): + """ + Schema for graph validation responses + """ + + error: str | None = Field( + default=None, description="Error message if graph construction or validation failed" + ) + findings: list[dict[str, Any]] | None = Field( + default=None, + description="Structured findings from static analysis (unreachable nodes, dead-ends, cycles)", + ) + ok: bool = Field(..., description="Whether graph construction succeeded") + valid: bool | None = Field( + default=None, description="Whether the graph passed structural validation and analysis" + ) diff --git a/src/archastro/platform/types/scripts.py b/src/archastro/platform/types/scripts.py new file mode 100644 index 0000000..eda1d75 --- /dev/null +++ b/src/archastro/platform/types/scripts.py @@ -0,0 +1,195 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 32227d023a50 + +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field + + +class ScriptLanguageSpec(BaseModel): + """ + Schema for ArchAstro script editor metadata + """ + + builtins: list[dict[str, Any]] = Field(..., description="Builtin function docs") + keywords: list[str] = Field(..., description="Language keywords") + languageId: str = Field(..., description="Monaco language identifier") + namespaces: list[dict[str, Any]] = Field(..., description="Importable namespace docs") + operators: list[str] = Field(..., description="Language operators") + reservedNames: list[str] | None = Field( + default=None, description="Names that cannot be rebound" + ) + snippets: list[dict[str, Any]] | None = Field( + default=None, description="Editor snippet templates" + ) + specialIdentifiers: list[dict[str, Any]] | None = Field( + default=None, description="Special symbols such as JSONPath root/current" + ) + typeDefinitions: list[dict[str, Any]] | None = Field( + default=None, description="Named type definitions (event interfaces, etc.) with fields" + ) + typeSystem: dict[str, Any] | None = Field( + default=None, + description="Type-system primitives, generics, annotations, inference, and runtime-check guidance", + ) + version: int = Field(..., description="Schema version") + + +class ScriptRunResult(BaseModel): + """ + Schema for the result of executing a script in the editor + """ + + error: str | None = Field( + default=None, description="Error message when script execution failed" + ) + findings: list[dict[str, Any]] | None = Field( + default=[], + description="Structured diagnostics with position info for editor markers (severity, message, line, column, end_line, end_column)", + ) + output: list[dict[str, Any]] | None = Field( + default=[], + description="Output lines from println calls, each with :line (source line number), :text, and optional :values", + ) + result: Any | None = Field( + default=None, description="The evaluated JSON-safe result (null when execution failed)" + ) + + +class ScriptTestAssertion(BaseModel): + """ + One `test.expect(...).` call from a ScriptTest run. + Matchers never raise (the script language has no exceptions) each call + emits a structured assertion entry that the test runner harvests into the + per-test report. Pass/fail is captured on `:pass`; `:expected` and + `:actual` carry the matcher's inputs for diff rendering. + """ + + model_config = ConfigDict(populate_by_name=True) + + actual: Any | None = Field( + default=None, description="Actual value the matcher was called with any JSON-safe value" + ) + describe: list[str] | None = Field( + default=[], description="Outer-to-inner describe path when the assertion fired" + ) + expected: Any | None = Field( + default=None, + description="Expected value (or arguments to the matcher) any JSON-safe value: scalar, map, or list", + ) + it: str | None = Field(default=None, description="Name of the enclosing `test.it(...)` block") + kind: str | None = Field( + default=None, description='Output entry kind, always `"assertion"` for this schema' + ) + line: int | None = Field(default=None, description="Source line of the `expect(...)` call") + matcher: str | None = Field( + default=None, + description="Matcher name (toEqual, toBe, toBeOk, toBeError, toContain, toMatch, toHaveLength)", + ) + pass_: bool = Field(..., alias="pass", description="True when the matcher succeeded") + + +class ScriptTestCase(BaseModel): + """ + One `test.it(...)` block from a ScriptTest run. + Built by the harvester from the raw output entries pairs the case's + metadata with the assertions it produced and any runtime error that + aborted the body. An `it` block that emits zero assertions is treated + as a failure (`pass: false`) so silent mistakes don't pass quietly. + """ + + model_config = ConfigDict(populate_by_name=True) + + assertions: list[ScriptTestAssertion] | None = Field( + default=[], description="Every `test.expect(...)` matcher that ran inside this case" + ) + describe: list[str] | None = Field( + default=[], description="Outer-to-inner describe path enclosing this case" + ) + error: str | None = Field( + default=None, + description="Runtime error that aborted the body of this `it` block, when one occurred", + ) + name: str = Field(..., description="The name passed to `test.it(...)`") + pass_: bool = Field( + ..., + alias="pass", + description="True when the case has at least one assertion and every assertion passed", + ) + + +class ScriptTestOutputEntry(BaseModel): + """ + One println/log entry interleaved with a ScriptTest run. + After the test harvester filters out test-control entries (`it`, + `assertion`, `it_error`), only `println` and `log.` calls remain + in the report's `output` field. Both shapes share `text`/`values`/`line` + (line is auto-annotated by the evaluator post-call); `level` and + `timestamp` are only present on `log.*` entries. + """ + + level: str | None = Field( + default=None, + description="Log level (`debug`, `info`, `warn`, `error`) present on log.* entries only", + ) + line: int | None = Field(default=None, description="Source line of the println/log call") + text: str = Field(..., description="Joined text representation of the args") + timestamp: datetime | None = Field( + default=None, description="UTC timestamp present on log.* entries only" + ) + values: list[Any] | None = Field( + default=[], + description="Original args coerced to JSON-safe values mixed scalars, maps, and lists since the call sites accept arbitrary expressions", + ) + + +class ScriptTestSuite(BaseModel): + """ + One `test.describe(...)` group from a ScriptTest run. + Suites are derived from the unique describe paths the harvester sees, so + there's no synthetic "ungrouped" suite `it` blocks emitted outside any + `describe` show up in the top-level `tests` array on the parent report + instead. + """ + + describe: list[str] = Field( + ..., description="Outer-to-inner describe path identifying this suite" + ) + name: str = Field( + ..., description="Human-readable suite name (`describe` path joined by ` > `)" + ) + tests: list[ScriptTestCase] | None = Field( + default=[], description="Every `test.it(...)` case run inside this describe" + ) + + +class ScriptTestRunResult(BaseModel): + """ + Schema for the result of running a ScriptTest + """ + + assertion_count: int | None = Field( + default=0, description="Total number of `test.expect(...)` matcher calls that ran" + ) + error: str | None = Field( + default=None, + description="Top-level error message when a runtime error fired outside any `it` block (parse/tokenize errors return 422 instead)", + ) + findings: list[dict[str, Any]] | None = Field( + default=[], + description="Structured diagnostics with position info for editor markers when the run errored before any tests could run", + ) + output: list[ScriptTestOutputEntry] | None = Field( + default=[], + description="Non-test output entries (println, log.*) interleaved during the run", + ) + passed: bool = Field(..., description="True when every test in every suite passed") + suites: list[ScriptTestSuite] | None = Field( + default=[], description="One entry per describe path" + ) + tests: list[ScriptTestCase] | None = Field( + default=[], + description="Flat list of every `it` that ran, each with assertions and pass flag", + ) diff --git a/src/archastro/platform/types/tasks.py b/src/archastro/platform/types/tasks.py index 53ad922..1dc15e8 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: 093546997f8d +# Content hash: debabe624df8 from datetime import datetime from typing import Any @@ -33,6 +33,10 @@ class Task(BaseModel): 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 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.", @@ -131,7 +135,7 @@ class Task(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -199,6 +203,16 @@ class TaskComment(BaseModel): ) +class TaskExternalLink(BaseModel): + """ + An indexed external object linked to a task. + """ + + external_scope: str = Field(..., description="External container identity.") + object_id: str = Field(..., description="Object identity within that container.") + object_type: str = Field(..., description="External object kind.") + + class TaskSessionLease(BaseModel): """ A task-session lease returned only to its matching holder. diff --git a/src/archastro/platform/types/users.py b/src/archastro/platform/types/users.py index c67fd55..7e4ba7f 100644 --- a/src/archastro/platform/types/users.py +++ b/src/archastro/platform/types/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: 53d1623c79d0 +# Content hash: 2c5b687a5a01 from datetime import datetime from typing import Any @@ -110,3 +110,18 @@ class UserInvite(BaseModel): user: InviteCreator | None = Field( default=None, description="The user who created this invite." ) + + +class UserSSHKey(BaseModel): + """ + Metadata for a registered SSH public key. Key material is never returned. + """ + + algorithm: str = Field(..., description="SSH algorithm. V1 accepts `ssh-ed25519`.") + created_at: datetime = Field(..., description="Registration time.") + fingerprint: str = Field(..., description="OpenSSH SHA-256 fingerprint.") + id: str = Field(..., description="Registered SSH key ID (`ssk_...`).") + label: str = Field(..., description="User-visible label for the key.") + revoked_at: datetime | None = Field( + default=None, description="Revocation time, or null while active." + ) diff --git a/src/archastro/platform/types/workflows.py b/src/archastro/platform/types/workflows.py new file mode 100644 index 0000000..7864f9a --- /dev/null +++ b/src/archastro/platform/types/workflows.py @@ -0,0 +1,143 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 441c73648dc4 + +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, Field + + +class WorkflowJournalEntry(BaseModel): + """ + One ordered, replayable record from a durable workflow journal. + """ + + command_id: str | None = Field( + default=None, + description="Durable command identifier associated with the record, when present.", + ) + created_at: datetime | None = Field( + default=None, description="When this entry was durably committed." + ) + id: str = Field(..., description="Journal entry ID (`wdr_...`).") + node_id: str | None = Field( + default=None, + description="Workflow node associated with the record. `null` for execution-level records.", + ) + record: dict[str, Any] = Field( + ..., + description="Replayable workflow record body, including payload, context, environment, metadata, and timestamp.", + ) + sequence: int = Field(..., description="Monotonically increasing sequence within the journal.") + timer_id: str | None = Field( + default=None, + description="Durable timer identifier associated with the record, when present.", + ) + type: str = Field( + ..., + description="Workflow record type, such as `node_started`, `node_completed`, or `node_failed`.", + ) + + +class WorkflowJournal(BaseModel): + """ + Summary of the durable workflow execution journal associated with a run. + """ + + completed_at: datetime | None = Field( + default=None, + description="When durable workflow execution reached a terminal state. `null` while it is active.", + ) + created_at: datetime | None = Field(default=None, description="When the journal was created.") + current_sequence: int = Field( + ..., description="Highest workflow record sequence durably committed to this journal." + ) + id: str = Field(..., description="Journal execution ID (`wde_...`).") + started_at: datetime | None = Field( + default=None, description="When durable workflow execution started." + ) + status: str = Field( + ..., + description="Current durable execution status: `pending`, `running`, `waiting`, `completed`, `failed`, or `cancelled`.", + ) + updated_at: datetime | None = Field( + default=None, description="When the journal was last updated." + ) + + +class WorkflowWorkItem(BaseModel): + """ + Externally executable work yielded by a durable workflow. + """ + + agent: str = Field(..., description="Agent assigned to execute this work.") + attempt_count: int = Field( + ..., description="Number of times this work has been freshly claimed or reclaimed." + ) + command_id: str = Field( + ..., description="Opaque journal command identity used to resume the workflow exactly once." + ) + created_at: datetime + execution: str = Field(..., description="Durable workflow execution that owns this work.") + id: str = Field(..., description="Work item ID (`wdi_...`).") + lease_expires_at: datetime | None = Field( + default=None, + description="When the current claim expires. Null for queued or terminal work.", + ) + node_id: str = Field(..., description="Workflow graph node that yielded the work.") + payload: dict[str, Any] = Field( + ..., description="Instructions and participant bindings needed to execute the work." + ) + routine_run: str | None = Field( + default=None, + description="Routine run that owns the execution, when this work came from a routine.", + ) + status: str = Field(..., description="Current queue lifecycle status.") + type: str = Field( + ..., description="Stable resource discriminator. Always `workflow_work_item`." + ) + updated_at: datetime + + +class WorkflowWorkItemLease(BaseModel): + """ + A claimed workflow work item and its caller-held lease token. + """ + + lease_owner: str = Field( + ..., + description="Opaque lease token that must be persisted and presented for later transitions.", + ) + work_item: WorkflowWorkItem = Field( + ..., description="The claimed, resumed, started, or heartbeated work item." + ) + + +class WorkflowWorkItemClaim(BaseModel): + """ + Result of polling an agent's durable workflow work queue. + """ + + data: WorkflowWorkItemLease | None = Field( + default=None, + description="Claimed or resumed work and its lease; null when no eligible item exists.", + ) + + +class WorkflowWorkItemList(BaseModel): + """ + Active durable workflow work available to the viewer. + """ + + after_cursor: str | None = Field( + default=None, description="Opaque cursor for the next page, or null at the end." + ) + before_cursor: str | None = Field( + default=None, description="Always null because queue pagination is forward-only." + ) + data: list[WorkflowWorkItem] = Field( + ..., + description="Active work items. Lease tokens are intentionally never included in list responses.", + ) + has_more: bool = Field(..., description="Whether another page of work exists.") diff --git a/src/archastro/platform/v1/__init__.py b/src/archastro/platform/v1/__init__.py index 81ec661..d224134 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: 33b7b81d0ce6 +# Content hash: 54d6fea29e1b from ..runtime.http_client import HttpClient, SyncHttpClient from .resources.activity_feed import ActivityFeedResource, AsyncActivityFeedResource @@ -29,6 +29,11 @@ EventSubscriptionDeliveryResource, ) from .resources.event_subscriptions import AsyncEventSubscriptionResource, EventSubscriptionResource +from .resources.external_object_capabilities import ( + AsyncExternalObjectCapabilityResource, + ExternalObjectCapabilityResource, +) +from .resources.external_objects import AsyncExternalObjectResource, ExternalObjectResource from .resources.extractions import AsyncExtractionResource, ExtractionResource from .resources.files import AsyncFileResource, FileResource from .resources.installation_sources import ( @@ -60,6 +65,7 @@ ) from .resources.private_services import AsyncPrivateServiceResource, PrivateServiceResource from .resources.sandboxes import AsyncSandboxResource, SandboxResource +from .resources.scripts import AsyncScriptResource, ScriptResource from .resources.slack_channel_bindings import ( AsyncSlackChannelBindingResource, SlackChannelBindingResource, @@ -68,6 +74,7 @@ from .resources.solution_instances import AsyncSolutionInstanceResource, SolutionInstanceResource from .resources.solution_tags import AsyncSolutionTagResource, SolutionTagResource from .resources.solutions import AsyncSolutionResource, SolutionResource +from .resources.ssh_keys import AsyncSshKeyResource, SshKeyResource from .resources.status import AsyncStatuResource, StatuResource from .resources.tasks import AsyncTaskResource, TaskResource from .resources.team_memberships import AsyncTeamMembershipResource, TeamMembershipResource @@ -77,6 +84,7 @@ from .resources.trajectories import AsyncTrajectoryResource, TrajectoryResource from .resources.users import AsyncUserResource, UserResource from .resources.work_items import AsyncWorkItemResource, WorkItemResource +from .resources.workflows import AsyncWorkflowResource, WorkflowResource class V1: @@ -100,6 +108,8 @@ def __init__(self, http: SyncHttpClient): self.custom_objects = CustomObjectResource(http) self.event_subscription_deliveries = EventSubscriptionDeliveryResource(http) self.event_subscriptions = EventSubscriptionResource(http) + self.external_object_capabilities = ExternalObjectCapabilityResource(http) + self.external_objects = ExternalObjectResource(http) self.extractions = ExtractionResource(http) self.files = FileResource(http) self.installation_sources = InstallationSourceResource(http) @@ -115,11 +125,13 @@ def __init__(self, http: SyncHttpClient): self.private_service_enrollments = PrivateServiceEnrollmentResource(http) self.private_services = PrivateServiceResource(http) self.sandboxes = SandboxResource(http) + self.scripts = ScriptResource(http) self.slack_channel_bindings = SlackChannelBindingResource(http) self.solution_categories = SolutionCategoryResource(http) self.solution_instances = SolutionInstanceResource(http) self.solution_tags = SolutionTagResource(http) self.solutions = SolutionResource(http) + self.ssh_keys = SshKeyResource(http) self.status = StatuResource(http) self.tasks = TaskResource(http) self.team_memberships = TeamMembershipResource(http) @@ -129,6 +141,7 @@ def __init__(self, http: SyncHttpClient): self.trajectories = TrajectoryResource(http) self.users = UserResource(http) self.work_items = WorkItemResource(http) + self.workflows = WorkflowResource(http) self.ai = AiResource(http) self.oauth = OauthResource(http) @@ -154,6 +167,8 @@ def __init__(self, http: HttpClient): self.custom_objects = AsyncCustomObjectResource(http) self.event_subscription_deliveries = AsyncEventSubscriptionDeliveryResource(http) self.event_subscriptions = AsyncEventSubscriptionResource(http) + self.external_object_capabilities = AsyncExternalObjectCapabilityResource(http) + self.external_objects = AsyncExternalObjectResource(http) self.extractions = AsyncExtractionResource(http) self.files = AsyncFileResource(http) self.installation_sources = AsyncInstallationSourceResource(http) @@ -169,11 +184,13 @@ def __init__(self, http: HttpClient): self.private_service_enrollments = AsyncPrivateServiceEnrollmentResource(http) self.private_services = AsyncPrivateServiceResource(http) self.sandboxes = AsyncSandboxResource(http) + self.scripts = AsyncScriptResource(http) self.slack_channel_bindings = AsyncSlackChannelBindingResource(http) self.solution_categories = AsyncSolutionCategoryResource(http) self.solution_instances = AsyncSolutionInstanceResource(http) self.solution_tags = AsyncSolutionTagResource(http) self.solutions = AsyncSolutionResource(http) + self.ssh_keys = AsyncSshKeyResource(http) self.status = AsyncStatuResource(http) self.tasks = AsyncTaskResource(http) self.team_memberships = AsyncTeamMembershipResource(http) @@ -183,5 +200,6 @@ def __init__(self, http: HttpClient): self.trajectories = AsyncTrajectoryResource(http) self.users = AsyncUserResource(http) self.work_items = AsyncWorkItemResource(http) + self.workflows = AsyncWorkflowResource(http) self.ai = AsyncAiResource(http) self.oauth = AsyncOauthResource(http) diff --git a/src/archastro/platform/v1/resources/__init__.py b/src/archastro/platform/v1/resources/__init__.py index bd1191b..45759a7 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: 08b6aec85d81 +# Content hash: 3a194bfa6a9b from .activity_feed import ( ActivityFeedResource, # noqa: F401 @@ -82,6 +82,14 @@ AsyncEventSubscriptionResource, # noqa: F401 EventSubscriptionResource, # noqa: F401 ) +from .external_object_capabilities import ( + AsyncExternalObjectCapabilityResource, # noqa: F401 + ExternalObjectCapabilityResource, # noqa: F401 +) +from .external_objects import ( + AsyncExternalObjectResource, # noqa: F401 + ExternalObjectResource, # noqa: F401 +) from .extractions import ( AsyncExtractionResource, # noqa: F401 ExtractionResource, # noqa: F401 @@ -146,6 +154,10 @@ AsyncSandboxResource, # noqa: F401 SandboxResource, # noqa: F401 ) +from .scripts import ( + AsyncScriptResource, # noqa: F401 + ScriptResource, # noqa: F401 +) from .slack_channel_bindings import ( AsyncSlackChannelBindingResource, # noqa: F401 SlackChannelBindingResource, # noqa: F401 @@ -166,6 +178,10 @@ AsyncSolutionResource, # noqa: F401 SolutionResource, # noqa: F401 ) +from .ssh_keys import ( + AsyncSshKeyResource, # noqa: F401 + SshKeyResource, # noqa: F401 +) from .status import ( AsyncStatuResource, # noqa: F401 StatuResource, # noqa: F401 @@ -202,3 +218,7 @@ AsyncWorkItemResource, # noqa: F401 WorkItemResource, # noqa: F401 ) +from .workflows import ( + AsyncWorkflowResource, # noqa: F401 + WorkflowResource, # noqa: F401 +) diff --git a/src/archastro/platform/v1/resources/agents.py b/src/archastro/platform/v1/resources/agents.py index 343cc97..50f6a94 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: 655ad9482090 +# Content hash: be99f4bb2c4a from __future__ import annotations @@ -30,12 +30,11 @@ Installation, InstallationKindListResponse, InstallationListResponse, - WorkflowWorkItemClaim, - WorkflowWorkItemList, WorkingMemoryEntry, WorkingMemoryEntryListResponse, ) from ...types.threads import Thread +from ...types.workflows import WorkflowWorkItemClaim, WorkflowWorkItemList class AgentAgentComputerCreateInput(TypedDict, total=False): @@ -534,7 +533,7 @@ class AgentSearchInput(TypedDict, total=False): recency_days: int | None "When set, restricts results to items indexed within the last N days." source_types: list[str] | None - 'Array of source-type slugs used to filter chunk results, e.g. `["web", "file"]`. Omit to include all source types.' + 'Exact source-type slugs filtering both chunks and documents, e.g. `["knowledge/documents"]` for authored documents or `["thread/messages"]` for conversation memory. Omit or pass an empty array to include all source types.' class AgentThreadsInputThreadMembersItem(TypedDict): @@ -1528,6 +1527,11 @@ async def search(self, agent: str, input: AgentSearchInput) -> AgentSearchRespon Search an agent's knowledge base Performs a semantic search over an agent's knowledge base and returns a ranked, `kind`-discriminated list of matching items. + Threads are automatically indexed as conversational memory, including prior agent + answers. A match is not necessarily independent source evidence. Result `type` + identifies the source: `thread/messages` is conversation memory; use `source_types` + with the desired corpus slugs (for example `knowledge/documents`) to exclude it. + `kind` describes storage granularity, not whether the content is conversation memory. Two item kinds may appear in `data`: - `"chunk"` chunk-level results from the agent's context store. Present for all agents. - `"document"` document-level results. Present only when the agent has an active @@ -1547,7 +1551,7 @@ async def search(self, agent: str, input: AgentSearchInput) -> AgentSearchRespon input.mode: Retrieval strategy. One of `"hybrid"` (default), `"vector"`, or `"fulltext"`. input.query: Natural-language search query used to retrieve relevant knowledge items. input.recency_days: When set, restricts results to items indexed within the last N days. - input.source_types: Array of source-type slugs used to filter chunk results, e.g. `["web", "file"]`. Omit to include all source types. + input.source_types: Exact source-type slugs filtering both chunks and documents, e.g. `["knowledge/documents"]` for authored documents or `["thread/messages"]` for conversation memory. Omit or pass an empty array to include all source types. Returns: Successful response @@ -2443,6 +2447,11 @@ def search(self, agent: str, input: AgentSearchInput) -> AgentSearchResponse: Search an agent's knowledge base Performs a semantic search over an agent's knowledge base and returns a ranked, `kind`-discriminated list of matching items. + Threads are automatically indexed as conversational memory, including prior agent + answers. A match is not necessarily independent source evidence. Result `type` + identifies the source: `thread/messages` is conversation memory; use `source_types` + with the desired corpus slugs (for example `knowledge/documents`) to exclude it. + `kind` describes storage granularity, not whether the content is conversation memory. Two item kinds may appear in `data`: - `"chunk"` chunk-level results from the agent's context store. Present for all agents. - `"document"` document-level results. Present only when the agent has an active @@ -2462,7 +2471,7 @@ def search(self, agent: str, input: AgentSearchInput) -> AgentSearchResponse: input.mode: Retrieval strategy. One of `"hybrid"` (default), `"vector"`, or `"fulltext"`. input.query: Natural-language search query used to retrieve relevant knowledge items. input.recency_days: When set, restricts results to items indexed within the last N days. - input.source_types: Array of source-type slugs used to filter chunk results, e.g. `["web", "file"]`. Omit to include all source types. + input.source_types: Exact source-type slugs filtering both chunks and documents, e.g. `["knowledge/documents"]` for authored documents or `["thread/messages"]` for conversation memory. Omit or pass an empty array to include all source types. Returns: Successful response diff --git a/src/archastro/platform/v1/resources/ai.py b/src/archastro/platform/v1/resources/ai.py index 09691c9..69df27d 100644 --- a/src/archastro/platform/v1/resources/ai.py +++ b/src/archastro/platform/v1/resources/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: 9d89f769ef68 +# Content hash: 6513b9a30b14 from __future__ import annotations @@ -228,6 +228,8 @@ class ImageEditsInput(TypedDict, total=False): "Natural-language description of the edit to apply to the source image(s)." quality: str | None "Quality preset for the output image. Accepted values and behavior are model-dependent." + session_id: str | None + "Optional session UUID for grouping provider attempts in usage records. Generated when omitted." size: str | None 'Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model\'s default.' style: str | None @@ -257,6 +259,8 @@ class ImageGenerationsInput(TypedDict, total=False): "Natural-language description of the image to generate." quality: str | None "Quality preset for the output image. Accepted values and behavior are model-dependent." + session_id: str | None + "Optional session UUID for grouping provider attempts in usage records. Generated when omitted." size: str | None 'Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model\'s default.' style: str | None @@ -553,6 +557,7 @@ async def edits(self, input: ImageEditsInput) -> AIImageResult: input.output_format: Desired MIME type or format for the returned image. Common values: `"png"`, `"jpeg"`, `"webp"`. Defaults to the model's native format. input.prompt: Natural-language description of the edit to apply to the source image(s). input.quality: Quality preset for the output image. Accepted values and behavior are model-dependent. + input.session_id: Optional session UUID for grouping provider attempts in usage records. Generated when omitted. input.size: Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model's default. input.style: Style preset applied to the edit. Accepted values and behavior are model-dependent. input.width: Explicit output width in pixels. Takes precedence over `size` when both are provided. Not supported by all models. @@ -591,6 +596,7 @@ async def generations(self, input: ImageGenerationsInput) -> AIImageResult: input.output_format: Desired MIME type or format for the returned image. Common values: `"png"`, `"jpeg"`, `"webp"`. Defaults to the model's native format. input.prompt: Natural-language description of the image to generate. input.quality: Quality preset for the output image. Accepted values and behavior are model-dependent. + input.session_id: Optional session UUID for grouping provider attempts in usage records. Generated when omitted. input.size: Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model's default. input.style: Style preset applied to the generated image. Accepted values and behavior are model-dependent. input.width: Explicit output width in pixels. Takes precedence over `size` when both are provided. Not supported by all models. @@ -776,6 +782,7 @@ def edits(self, input: ImageEditsInput) -> AIImageResult: input.output_format: Desired MIME type or format for the returned image. Common values: `"png"`, `"jpeg"`, `"webp"`. Defaults to the model's native format. input.prompt: Natural-language description of the edit to apply to the source image(s). input.quality: Quality preset for the output image. Accepted values and behavior are model-dependent. + input.session_id: Optional session UUID for grouping provider attempts in usage records. Generated when omitted. input.size: Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model's default. input.style: Style preset applied to the edit. Accepted values and behavior are model-dependent. input.width: Explicit output width in pixels. Takes precedence over `size` when both are provided. Not supported by all models. @@ -814,6 +821,7 @@ def generations(self, input: ImageGenerationsInput) -> AIImageResult: input.output_format: Desired MIME type or format for the returned image. Common values: `"png"`, `"jpeg"`, `"webp"`. Defaults to the model's native format. input.prompt: Natural-language description of the image to generate. input.quality: Quality preset for the output image. Accepted values and behavior are model-dependent. + input.session_id: Optional session UUID for grouping provider attempts in usage records. Generated when omitted. input.size: Output dimensions as a WxH string, e.g. `"1024x1024"`. Applies to OpenAI-compatible models. Omit to use the model's default. input.style: Style preset applied to the generated image. Accepted values and behavior are model-dependent. input.width: Explicit output width in pixels. Takes precedence over `size` when both are provided. Not supported by all models. diff --git a/src/archastro/platform/v1/resources/artifacts.py b/src/archastro/platform/v1/resources/artifacts.py index 7dcdab7..e9c4564 100644 --- a/src/archastro/platform/v1/resources/artifacts.py +++ b/src/archastro/platform/v1/resources/artifacts.py @@ -1,15 +1,57 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 59f5108d3342 +# Content hash: 257a07ac71e4 from __future__ import annotations -from typing import Required, TypedDict +from typing import Any, Literal, Required, TypedDict from ...runtime.http_client import HttpClient, SyncHttpClient from ...types.artifacts import Artifact +class ArtifactCreateInputFile(TypedDict, total=False): + data: Required[str] + "File content encoded according to file.encoding (base64 by default)." + encoding: Literal["base64", "utf8"] | None + "Data encoding; defaults to base64. Use utf8 for script-generated text." + filename: str | None + "Original filename." + mime_type: str | None + "MIME type for the file." + + +class ArtifactCreateInput(TypedDict, total=False): + "Create an artifact" + + agent: str | None + "Agent ID (`agt_...`) to associate with the artifact." + artifact: dict[str, Any] | None + "Legacy wrapper object containing artifact attributes. Prefer top-level fields." + description: str | None + "Optional longer artifact description." + file: ArtifactCreateInputFile | None + "File payload for the initial artifact version." + group_key: str | None + "Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Artifact ID remains identity." + idempotency_key: str | None + "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." + name: str | None + "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 + "Team ID (`tem_...`) that should own the artifact. Mutually exclusive with `user`." + thread: str | None + "Thread ID (`thr_...`) to associate with the artifact." + user: str | None + "User ID (`usr_...`) that should own the artifact. Defaults to the authenticated user when omitted and no team is supplied." + + class ArtifactReplaceInputFile(TypedDict, total=False): data: Required[str] "Base64-encoded binary content." @@ -34,6 +76,8 @@ class ArtifactReplaceInput(TypedDict, total=False): "Legacy flat filename. Prefer `file.filename`." from_version: Required[int] "The artifact's current version number, used for optimistic concurrency control. Returns 409 if this value does not match the server's current version." + group_key: str | None + "Nonunique grouping key, limited to 1024 UTF-8 bytes. Omit to preserve; null clears. Grouping-only updates do not increment the content version and from_version does not protect against concurrent metadata updates." name: str | None "New display name for the artifact. Omit to leave the existing name unchanged." @@ -42,6 +86,48 @@ class AsyncArtifactResource: def __init__(self, http: HttpClient): self._http = http + async def create(self, input: ArtifactCreateInput) -> Artifact: + """ + Create an artifact + Creates a new artifact and stores its file content. A file payload is required; + supply it via the `file.data` (Base64-encoded by default), `file.filename`, and + `file.mime_type` fields. The artifact is scoped to the owner resolved from the + request body either a team, a user, or an explicit system owner. When no + owner is supplied and the viewer is an authenticated user, the artifact + defaults to that user. + For company-readable snapshots, pass `system: true` and `org`. Organization + members can read these artifacts; organization admins and system viewers can + create them. Scripts may supply text directly using `file.encoding: "utf8"`. + Optionally associate the artifact with an existing thread or agent by passing + `thread` or `agent`. Legacy requests that wrap fields in an `artifact` object + are still accepted. + + Args: + input: Request body. + 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. + input.file: File payload for the initial artifact version. + input.group_key: Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Artifact ID remains identity. + 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. + input.user: User ID (`usr_...`) that should own the artifact. Defaults to the authenticated user when omitted and no team is supplied. + + Returns: + The newly created artifact. + """ + return await self._http.request( + "/api/v1/artifacts", + method="POST", + body=input, + response_type=Artifact, + ) + async def delete(self, artifact: str) -> None: """ Delete an artifact @@ -100,6 +186,7 @@ async def replace(self, artifact: str, input: ArtifactReplaceInput) -> Artifact: input.file_content_type: Legacy flat MIME type. Prefer `file.mime_type`. input.file_name: Legacy flat filename. Prefer `file.filename`. input.from_version: The artifact's current version number, used for optimistic concurrency control. Returns 409 if this value does not match the server's current version. + input.group_key: Nonunique grouping key, limited to 1024 UTF-8 bytes. Omit to preserve; null clears. Grouping-only updates do not increment the content version and from_version does not protect against concurrent metadata updates. input.name: New display name for the artifact. Omit to leave the existing name unchanged. Returns: @@ -157,6 +244,48 @@ class ArtifactResource: def __init__(self, http: SyncHttpClient): self._http = http + def create(self, input: ArtifactCreateInput) -> Artifact: + """ + Create an artifact + Creates a new artifact and stores its file content. A file payload is required; + supply it via the `file.data` (Base64-encoded by default), `file.filename`, and + `file.mime_type` fields. The artifact is scoped to the owner resolved from the + request body either a team, a user, or an explicit system owner. When no + owner is supplied and the viewer is an authenticated user, the artifact + defaults to that user. + For company-readable snapshots, pass `system: true` and `org`. Organization + members can read these artifacts; organization admins and system viewers can + create them. Scripts may supply text directly using `file.encoding: "utf8"`. + Optionally associate the artifact with an existing thread or agent by passing + `thread` or `agent`. Legacy requests that wrap fields in an `artifact` object + are still accepted. + + Args: + input: Request body. + 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. + input.file: File payload for the initial artifact version. + input.group_key: Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Artifact ID remains identity. + 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. + input.user: User ID (`usr_...`) that should own the artifact. Defaults to the authenticated user when omitted and no team is supplied. + + Returns: + The newly created artifact. + """ + return self._http.request( + "/api/v1/artifacts", + method="POST", + body=input, + response_type=Artifact, + ) + def delete(self, artifact: str) -> None: """ Delete an artifact @@ -215,6 +344,7 @@ def replace(self, artifact: str, input: ArtifactReplaceInput) -> Artifact: input.file_content_type: Legacy flat MIME type. Prefer `file.mime_type`. input.file_name: Legacy flat filename. Prefer `file.filename`. input.from_version: The artifact's current version number, used for optimistic concurrency control. Returns 409 if this value does not match the server's current version. + input.group_key: Nonunique grouping key, limited to 1024 UTF-8 bytes. Omit to preserve; null clears. Grouping-only updates do not increment the content version and from_version does not protect against concurrent metadata updates. input.name: New display name for the artifact. Omit to leave the existing name unchanged. Returns: diff --git a/src/archastro/platform/v1/resources/config.py b/src/archastro/platform/v1/resources/config.py index d87419f..24014e9 100644 --- a/src/archastro/platform/v1/resources/config.py +++ b/src/archastro/platform/v1/resources/config.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: d9371c141e7e +# Content hash: 1dfc4d69c270 from __future__ import annotations @@ -93,6 +93,8 @@ class ConfigValidateInput(TypedDict, total=False): 'MIME type of `raw_content`, e.g. `"application/x-yaml"` or `"application/json"`. Used to parse the content before validation.' raw_content: Required[str] "Raw content bytes to validate. Parsed according to `mime_type` before schema validation." + system: bool | None + "Set to true to validate as a system-owned config. Mutually exclusive with `team`, `user`, and `agent`." team: str | None "Team ID (`team_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `user` and `agent`." user: str | None @@ -880,6 +882,7 @@ async def validate(self, input: ConfigValidateInput) -> ValidationResult: input.kind: Config kind whose schema the content is validated against, e.g. `"Agent"` or `"APITool"`. input.mime_type: MIME type of `raw_content`, e.g. `"application/x-yaml"` or `"application/json"`. Used to parse the content before validation. input.raw_content: Raw content bytes to validate. Parsed according to `mime_type` before schema validation. + input.system: Set to true to validate as a system-owned config. Mutually exclusive with `team`, `user`, and `agent`. input.team: Team ID (`team_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `user` and `agent`. input.user: User ID (`usr_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `team` and `agent`. @@ -1579,6 +1582,7 @@ def validate(self, input: ConfigValidateInput) -> ValidationResult: input.kind: Config kind whose schema the content is validated against, e.g. `"Agent"` or `"APITool"`. input.mime_type: MIME type of `raw_content`, e.g. `"application/x-yaml"` or `"application/json"`. Used to parse the content before validation. input.raw_content: Raw content bytes to validate. Parsed according to `mime_type` before schema validation. + input.system: Set to true to validate as a system-owned config. Mutually exclusive with `team`, `user`, and `agent`. input.team: Team ID (`team_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `user` and `agent`. input.user: User ID (`usr_...`) that would own the config. Used for owner-aware validation rules. Mutually exclusive with `team` and `agent`. diff --git a/src/archastro/platform/v1/resources/event_subscriptions.py b/src/archastro/platform/v1/resources/event_subscriptions.py index 94970a2..24f4396 100644 --- a/src/archastro/platform/v1/resources/event_subscriptions.py +++ b/src/archastro/platform/v1/resources/event_subscriptions.py @@ -1,13 +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: ee8786d0c9ca +# Content hash: 0fc58a1033bf from __future__ import annotations from typing import Literal, Required, TypedDict from ...runtime.http_client import HttpClient, SyncHttpClient -from ...types.common import ( +from ...types.events import ( EventSubscription, EventSubscriptionClaim, EventSubscriptionHead, diff --git a/src/archastro/platform/v1/resources/external_object_capabilities.py b/src/archastro/platform/v1/resources/external_object_capabilities.py new file mode 100644 index 0000000..7d89314 --- /dev/null +++ b/src/archastro/platform/v1/resources/external_object_capabilities.py @@ -0,0 +1,79 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 05e042d75417 + +from __future__ import annotations + +from typing import Any, TypedDict + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import RuntimeCapability + + +class ExternalObjectCapabilityCreateInput(TypedDict): + "Issue a revocable capability for hosted external-object bindings" + + bindings: dict[str, Any] + subject: str + + +class AsyncExternalObjectCapabilityResource: + def __init__(self, http: HttpClient): + self._http = http + + async def remove(self) -> None: + """ + Revoke every active external-object capability for a hosted workload + + Returns: + No content + """ + await self._http.request("/api/v1/external_object_capabilities", method="DELETE") + + async def create(self, input: ExternalObjectCapabilityCreateInput) -> RuntimeCapability: + """ + Issue a revocable capability for hosted external-object bindings + + Args: + input: Request body. + + Returns: + Gateway bootstrap values for one hosted workload + """ + return await self._http.request( + "/api/v1/external_object_capabilities", + method="POST", + body=input, + response_type=RuntimeCapability, + ) + + +class ExternalObjectCapabilityResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def remove(self) -> None: + """ + Revoke every active external-object capability for a hosted workload + + Returns: + No content + """ + self._http.request("/api/v1/external_object_capabilities", method="DELETE") + + def create(self, input: ExternalObjectCapabilityCreateInput) -> RuntimeCapability: + """ + Issue a revocable capability for hosted external-object bindings + + Args: + input: Request body. + + Returns: + Gateway bootstrap values for one hosted workload + """ + return self._http.request( + "/api/v1/external_object_capabilities", + method="POST", + body=input, + response_type=RuntimeCapability, + ) diff --git a/src/archastro/platform/v1/resources/external_objects.py b/src/archastro/platform/v1/resources/external_objects.py new file mode 100644 index 0000000..7b46d26 --- /dev/null +++ b/src/archastro/platform/v1/resources/external_objects.py @@ -0,0 +1,263 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 3f9c8fa7d5a1 + +from __future__ import annotations + +from datetime import datetime +from typing import Any, Literal, Required, TypedDict + +from pydantic import BaseModel, Field + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import ExternalObject + + +class ExternalObjectCreateInput(TypedDict, total=False): + "Provision an external object through an integration service" + + agent: str | None + "Owning agent ID (`agt_...`). Supply exactly one owner field." + object: Required[dict[str, Any] | dict[str, Any]] + "Provisioning fields selected by external object type." + org: str | None + "Owning organization ID (`org_...`). Supply exactly one owner field." + service: Required[Literal["cloudflare"]] + team: str | None + "Owning team ID (`tem_...`). Supply exactly one owner field." + user: str | None + "Owning user ID (`usr_...`). Supply exactly one owner field." + + +class ExternalObjectUpdateInput(TypedDict): + "Update an external object" + + object: dict[str, Any] | dict[str, Any] + "Mutable fields selected by external object type." + + +class ExternalObjectListResponseDataItem(BaseModel): + agent: str | None = None + created_at: datetime + id: str = Field(..., description="ArchAstro external object ID (`ext_...`).") + integration: str = Field(..., description="Integration ID (`int_...`).") + object: dict[str, Any] | dict[str, Any] = Field( + ..., description="Service-specific object payload." + ) + org: str | None = None + service: Literal["cloudflare"] = "cloudflare" + team: str | None = None + updated_at: datetime + user: str | None = None + + +class ExternalObjectListResponse(BaseModel): + """ + Successful response + """ + + data: list[ExternalObjectListResponseDataItem] + has_next: bool + has_prev: bool + page: int + page_size: int + total_entries: int + total_pages: int + + +class AsyncExternalObjectResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list( + self, + *, + service: Literal["cloudflare"] | None = None, + type: Literal["r2_bucket", "d1_database"] | None = None, + page: int | None = None, + page_size: int | None = None, + search: str | None = None, + ) -> ExternalObjectListResponse: + """ + List external objects + + Returns: + Successful response + """ + query: dict[str, object] = {} + if service is not None: + query["service"] = service + if type is not None: + query["type"] = type + if page is not None: + query["page"] = page + if page_size is not None: + query["page_size"] = page_size + if search is not None: + query["search"] = search + return await self._http.request( + "/api/v1/external_objects", + query=query, + response_type=ExternalObjectListResponse, + ) + + async def create(self, input: ExternalObjectCreateInput) -> ExternalObject: + """ + Provision an external object through an integration service + + Args: + input: Request body. + input.agent: Owning agent ID (`agt_...`). Supply exactly one owner field. + input.object: Provisioning fields selected by external object type. + input.org: Owning organization ID (`org_...`). Supply exactly one owner field. + input.team: Owning team ID (`tem_...`). Supply exactly one owner field. + input.user: Owning user ID (`usr_...`). Supply exactly one owner field. + + Returns: + Successful response + """ + return await self._http.request( + "/api/v1/external_objects", + method="POST", + body=input, + response_type=ExternalObject, + ) + + async def delete(self, external_object: str) -> None: + """ + Delete an external object + + Returns: + No content + """ + await self._http.request(f"/api/v1/external_objects/{external_object}", method="DELETE") + + async def get(self, external_object: str) -> ExternalObject: + """ + Show an external object + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/external_objects/{external_object}", + response_type=ExternalObject, + ) + + async def update( + self, external_object: str, input: ExternalObjectUpdateInput + ) -> ExternalObject: + """ + Update an external object + + Args: + input: Request body. + input.object: Mutable fields selected by external object type. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/external_objects/{external_object}", + method="PATCH", + body=input, + response_type=ExternalObject, + ) + + +class ExternalObjectResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list( + self, + *, + service: Literal["cloudflare"] | None = None, + type: Literal["r2_bucket", "d1_database"] | None = None, + page: int | None = None, + page_size: int | None = None, + search: str | None = None, + ) -> ExternalObjectListResponse: + """ + List external objects + + Returns: + Successful response + """ + query: dict[str, object] = {} + if service is not None: + query["service"] = service + if type is not None: + query["type"] = type + if page is not None: + query["page"] = page + if page_size is not None: + query["page_size"] = page_size + if search is not None: + query["search"] = search + return self._http.request( + "/api/v1/external_objects", + query=query, + response_type=ExternalObjectListResponse, + ) + + def create(self, input: ExternalObjectCreateInput) -> ExternalObject: + """ + Provision an external object through an integration service + + Args: + input: Request body. + input.agent: Owning agent ID (`agt_...`). Supply exactly one owner field. + input.object: Provisioning fields selected by external object type. + input.org: Owning organization ID (`org_...`). Supply exactly one owner field. + input.team: Owning team ID (`tem_...`). Supply exactly one owner field. + input.user: Owning user ID (`usr_...`). Supply exactly one owner field. + + Returns: + Successful response + """ + return self._http.request( + "/api/v1/external_objects", + method="POST", + body=input, + response_type=ExternalObject, + ) + + def delete(self, external_object: str) -> None: + """ + Delete an external object + + Returns: + No content + """ + self._http.request(f"/api/v1/external_objects/{external_object}", method="DELETE") + + def get(self, external_object: str) -> ExternalObject: + """ + Show an external object + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/external_objects/{external_object}", + response_type=ExternalObject, + ) + + def update(self, external_object: str, input: ExternalObjectUpdateInput) -> ExternalObject: + """ + Update an external object + + Args: + input: Request body. + input.object: Mutable fields selected by external object type. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/external_objects/{external_object}", + method="PATCH", + body=input, + response_type=ExternalObject, + ) diff --git a/src/archastro/platform/v1/resources/knowledge_sources.py b/src/archastro/platform/v1/resources/knowledge_sources.py index 1fb80c3..94e3eb6 100644 --- a/src/archastro/platform/v1/resources/knowledge_sources.py +++ b/src/archastro/platform/v1/resources/knowledge_sources.py @@ -1,9 +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: afd9411b682a +# Content hash: c34bfb6c75e1 from __future__ import annotations +import builtins from datetime import datetime from typing import Any, Literal, Required, TypedDict @@ -77,6 +78,21 @@ class KnowledgeSourceIngestInput(TypedDict, total=False): "Display title for the ingested document. Applied in push mode only; ignored when `pull: true`." +class KnowledgeSourceSearchInput(TypedDict, total=False): + "Search within a knowledge source" + + max_results: int | None + "Maximum number of results to return. The platform applies its own upper bound; omit to use the default." + min_similarity: float | None + 'Cosine-similarity floor for the vector leg, 0.0-1.0. Candidates below it are discarded before ranking, so a high value trades recall for precision. Pass `0.0` to disable the floor when a missed match costs more than a weak one note that with no floor every query returns results, so an empty response can no longer be read as "no match". Omit to use the default.' + mode: str | None + 'Search algorithm to use. One of `"hybrid"` (default, combines vector and full-text), `"vector"` (semantic similarity only), or `"fulltext"` (keyword matching only).' + query: Required[str] + "Natural language or keyword query string to search for." + recency_days: int | None + "When set, limits results to documents ingested within the last N days. Omit to search across all documents regardless of age." + + class KnowledgeSourceListResponseDataItem(BaseModel): agent: str | None = Field( default=None, @@ -157,6 +173,45 @@ class KnowledgeSourceListResponse(BaseModel): total_pages: int = Field(..., description="Total number of pages available.") +class KnowledgeSourceSearchResponseDataItem(BaseModel): + content: str | None = Field( + default=None, description="Normalized plain-text content of the matched chunk." + ) + content_type: str | None = Field( + default=None, description='MIME type of the content, e.g. `"text/plain"` or `"text/html"`.' + ) + created_at: datetime | None = Field( + default=None, description="When this item was indexed into the knowledge base (ISO 8601)." + ) + id: str = Field(..., description="Context item ID (`cim_...`).") + kind: Literal["chunk"] = Field( + default="chunk", + description='Result variant discriminator. Always `"chunk"` for this object type.', + ) + metadata: dict[str, Any] | None = Field( + default=None, + description="Arbitrary key-value metadata attached to this item by the source connector.", + ) + raw_content: dict[str, Any] | None = Field( + default=None, + description="Raw content payload as stored by the source connector, before normalization.", + ) + 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.', + ) + + +class KnowledgeSourceSearchResponse(BaseModel): + """ + Successful response + """ + + data: list[KnowledgeSourceSearchResponseDataItem] = Field( + ..., description="Array of matching knowledge items ordered by relevance score descending." + ) + + class AsyncKnowledgeSourceKindResource: def __init__(self, http: HttpClient): self._http = http @@ -192,6 +247,8 @@ async def list( page_size: int | None = None, search: str | None = None, type: str | None = None, + team: builtins.list[str] | None = None, + thread: builtins.list[str] | None = None, installation: str | None = None, agent: str | None = None, org: str | None = None, @@ -212,6 +269,8 @@ async def list( page_size: Number of knowledge sources to return per page. Defaults to 25. search: Filter sources whose type contains this string. Case-insensitive substring match. type: Exact knowledge source type to filter by, e.g. `"knowledge/documents"`. + team: Exact team IDs whose sources to include. + thread: Exact backing thread IDs whose sources to include. installation: Installation ID (`ins_...`). Returns only sources associated with this installation. agent: Agent ID (`agt_...`). Returns only sources owned by or associated with this agent. org: Organization ID (`org_...`). Returns only sources belonging to this organization. Combine with `owner_scope: "system"` to retrieve org-level system sources. @@ -229,6 +288,10 @@ async def list( query["search"] = search if type is not None: query["type"] = type + if team is not None: + query["team"] = team + if thread is not None: + query["thread"] = thread if installation is not None: query["installation"] = installation if agent is not None: @@ -379,6 +442,39 @@ async def ingest(self, source: str, input: KnowledgeSourceIngestInput) -> Contex response_type=ContextIngestion, ) + async def search( + self, source: str, input: KnowledgeSourceSearchInput + ) -> KnowledgeSourceSearchResponse: + """ + Search within a knowledge source + Executes a search query against the indexed documents in a single knowledge source and + returns matching results ranked by relevance. + Three search modes are supported: `"hybrid"` (default) combines vector similarity and + full-text scoring, `"vector"` uses embedding-based semantic search only, and + `"fulltext"` uses keyword-based search only. Use `"hybrid"` for most use cases; + prefer `"vector"` when semantic meaning matters more than exact terms. + Optionally restrict results to documents ingested within the last N days using + `recency_days`. Results are ordered by relevance score descending. + + Args: + source: Knowledge source ID (`ksrc_...`) to search within. + input: Request body. + input.max_results: Maximum number of results to return. The platform applies its own upper bound; omit to use the default. + input.min_similarity: Cosine-similarity floor for the vector leg, 0.0-1.0. Candidates below it are discarded before ranking, so a high value trades recall for precision. Pass `0.0` to disable the floor when a missed match costs more than a weak one note that with no floor every query returns results, so an empty response can no longer be read as "no match". Omit to use the default. + input.mode: Search algorithm to use. One of `"hybrid"` (default, combines vector and full-text), `"vector"` (semantic similarity only), or `"fulltext"` (keyword matching only). + input.query: Natural language or keyword query string to search for. + input.recency_days: When set, limits results to documents ingested within the last N days. Omit to search across all documents regardless of age. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/knowledge_sources/{source}/search", + method="POST", + body=input, + response_type=KnowledgeSourceSearchResponse, + ) + class KnowledgeSourceKindResource: def __init__(self, http: SyncHttpClient): @@ -415,6 +511,8 @@ def list( page_size: int | None = None, search: str | None = None, type: str | None = None, + team: builtins.list[str] | None = None, + thread: builtins.list[str] | None = None, installation: str | None = None, agent: str | None = None, org: str | None = None, @@ -435,6 +533,8 @@ def list( page_size: Number of knowledge sources to return per page. Defaults to 25. search: Filter sources whose type contains this string. Case-insensitive substring match. type: Exact knowledge source type to filter by, e.g. `"knowledge/documents"`. + team: Exact team IDs whose sources to include. + thread: Exact backing thread IDs whose sources to include. installation: Installation ID (`ins_...`). Returns only sources associated with this installation. agent: Agent ID (`agt_...`). Returns only sources owned by or associated with this agent. org: Organization ID (`org_...`). Returns only sources belonging to this organization. Combine with `owner_scope: "system"` to retrieve org-level system sources. @@ -452,6 +552,10 @@ def list( query["search"] = search if type is not None: query["type"] = type + if team is not None: + query["team"] = team + if thread is not None: + query["thread"] = thread if installation is not None: query["installation"] = installation if agent is not None: @@ -601,3 +705,36 @@ def ingest(self, source: str, input: KnowledgeSourceIngestInput) -> ContextInges body=input, response_type=ContextIngestion, ) + + def search( + self, source: str, input: KnowledgeSourceSearchInput + ) -> KnowledgeSourceSearchResponse: + """ + Search within a knowledge source + Executes a search query against the indexed documents in a single knowledge source and + returns matching results ranked by relevance. + Three search modes are supported: `"hybrid"` (default) combines vector similarity and + full-text scoring, `"vector"` uses embedding-based semantic search only, and + `"fulltext"` uses keyword-based search only. Use `"hybrid"` for most use cases; + prefer `"vector"` when semantic meaning matters more than exact terms. + Optionally restrict results to documents ingested within the last N days using + `recency_days`. Results are ordered by relevance score descending. + + Args: + source: Knowledge source ID (`ksrc_...`) to search within. + input: Request body. + input.max_results: Maximum number of results to return. The platform applies its own upper bound; omit to use the default. + input.min_similarity: Cosine-similarity floor for the vector leg, 0.0-1.0. Candidates below it are discarded before ranking, so a high value trades recall for precision. Pass `0.0` to disable the floor when a missed match costs more than a weak one note that with no floor every query returns results, so an empty response can no longer be read as "no match". Omit to use the default. + input.mode: Search algorithm to use. One of `"hybrid"` (default, combines vector and full-text), `"vector"` (semantic similarity only), or `"fulltext"` (keyword matching only). + input.query: Natural language or keyword query string to search for. + input.recency_days: When set, limits results to documents ingested within the last N days. Omit to search across all documents regardless of age. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/knowledge_sources/{source}/search", + method="POST", + body=input, + response_type=KnowledgeSourceSearchResponse, + ) diff --git a/src/archastro/platform/v1/resources/oauth.py b/src/archastro/platform/v1/resources/oauth.py index 0939559..3a46bc2 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: fc8f712c2ee7 +# Content hash: 1a97916433da from __future__ import annotations @@ -46,7 +46,7 @@ class OauthTokenInput(TypedDict, total=False): "Exchange a grant for OAuth tokens" client: str | None - 'OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"` and device-code grants.' + 'OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"`, device-code, and resource-bound refresh grants.' code: str | None 'Single-use authorization code issued by the authorization endpoint. Required for `"authorization_code"` grants.' code_verifier: str | None @@ -207,8 +207,9 @@ async def token(self, input: OauthTokenInput) -> OAuthTokenResponse: For `"authorization_code"` grants, supply `code`, `client`, `redirect_uri`, and optionally `code_verifier` for PKCE flows. Each authorization code is single-use; consuming it a second time returns `invalid_grant`. - For `"refresh_token"` grants, supply `refresh_token`. The endpoint rotates the - refresh token on every call and returns a fresh pair of tokens. + For `"refresh_token"` grants, supply `refresh_token`. Resource-bound public + clients must also supply `client`. The endpoint rotates the refresh token on + every call and returns a fresh pair of tokens. For device-code grants, supply `device_code` and `client`. Poll this endpoint after receiving `authorization_pending` until the user approves or the code expires. Slow down polling if you receive `slow_down`. @@ -217,7 +218,7 @@ async def token(self, input: OauthTokenInput) -> OAuthTokenResponse: Args: input: Request body. - input.client: OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"` and device-code grants. + input.client: OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"`, device-code, and resource-bound refresh grants. input.code: Single-use authorization code issued by the authorization endpoint. Required for `"authorization_code"` grants. input.code_verifier: PKCE code verifier corresponding to the `code_challenge` sent in the authorization request. Required when the authorization code was issued with a code challenge; omit otherwise. input.device_code: Device code received from the device authorization endpoint. Required for device-code grants. @@ -371,8 +372,9 @@ def token(self, input: OauthTokenInput) -> OAuthTokenResponse: For `"authorization_code"` grants, supply `code`, `client`, `redirect_uri`, and optionally `code_verifier` for PKCE flows. Each authorization code is single-use; consuming it a second time returns `invalid_grant`. - For `"refresh_token"` grants, supply `refresh_token`. The endpoint rotates the - refresh token on every call and returns a fresh pair of tokens. + For `"refresh_token"` grants, supply `refresh_token`. Resource-bound public + clients must also supply `client`. The endpoint rotates the refresh token on + every call and returns a fresh pair of tokens. For device-code grants, supply `device_code` and `client`. Poll this endpoint after receiving `authorization_pending` until the user approves or the code expires. Slow down polling if you receive `slow_down`. @@ -381,7 +383,7 @@ def token(self, input: OauthTokenInput) -> OAuthTokenResponse: Args: input: Request body. - input.client: OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"` and device-code grants. + input.client: OAuth client ID identifying the application requesting tokens. Required for `"authorization_code"`, device-code, and resource-bound refresh grants. input.code: Single-use authorization code issued by the authorization endpoint. Required for `"authorization_code"` grants. input.code_verifier: PKCE code verifier corresponding to the `code_challenge` sent in the authorization request. Required when the authorization code was issued with a code challenge; omit otherwise. input.device_code: Device code received from the device authorization endpoint. Required for device-code grants. diff --git a/src/archastro/platform/v1/resources/orgs.py b/src/archastro/platform/v1/resources/orgs.py index 168ca70..2054d9c 100644 --- a/src/archastro/platform/v1/resources/orgs.py +++ b/src/archastro/platform/v1/resources/orgs.py @@ -1,19 +1,26 @@ # Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. # This file is auto-generated by @archastro/sdk-generator. Do not edit. -# Content hash: 79672ef46739 +# Content hash: db5e3465b54b from __future__ import annotations +from datetime import datetime +from typing import Literal + from pydantic import BaseModel, Field from ...runtime.http_client import HttpClient, SyncHttpClient class OrgListResponseDataItem(BaseModel): - domain: str = Field( - ..., description='Primary domain associated with the organization, e.g. `"acme.com"`.' + domain: str | None = Field( + default=None, + description="Company domain. For personal orgs, the owner email is returned only to a viewer in that org.", ) id: str = Field(..., description="Organization ID (`org_...`).") + kind: Literal["company", "personal"] = Field( + ..., description="Whether this is a company-domain org or a person-owned personal org." + ) name: str = Field(..., description="Display name of the organization.") @@ -44,6 +51,125 @@ class OrgListResponse(BaseModel): ) +class OrgArtifactsResponseDataItemImageSource(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 OrgArtifactsResponseDataItem(BaseModel): + agent: str | None = Field( + default=None, + description="ID of the agent that produced this artifact (`agt_...`). `null` if not agent-produced.", + ) + content_type: str | None = Field( + default=None, + description='MIME type of the current version\'s file, e.g. `"text/csv"` or `"image/png"`. `null` if no file is attached.', + ) + created_at: datetime | None = Field( + default=None, description="When the artifact was first created (ISO 8601)." + ) + current_version: str | None = Field( + default=None, + description="ID of the current (latest published) artifact version (`artv_...`). `null` if no version has been published.", + ) + description: str | None = Field( + default=None, + description="Optional longer description of the artifact's contents or purpose. `null` if not set.", + ) + file: str | None = Field( + default=None, + description="Storage file ID for the current version (`fil_...`). `null` if no file is attached.", + ) + file_name: str | None = Field( + default=None, + description='Original filename of the current version\'s file, e.g. `"output.csv"`. `null` if no file is attached.', + ) + file_url: str | None = Field( + default=None, + description="Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", + ) + group_key: str | None = Field( + default=None, + description="Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + ) + id: str = Field(..., description="Artifact ID (`art_...`).") + image_source: OrgArtifactsResponseDataItemImageSource | None = Field( + default=None, + description='Image source metadata for rendering the current version\'s file inline. Present only when `content_type` starts with `"image/"`. `null` otherwise.', + ) + name: str | None = Field( + default=None, + description='Human-readable name for the artifact, e.g. `"Q2 Report"`. `null` if not set.', + ) + org: str | None = Field( + default=None, description="ID of the organization this artifact belongs to (`org_...`)." + ) + sandbox: str | None = Field( + default=None, + description="Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", + ) + system: bool | None = Field( + default=None, + description="True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + ) + team: str | None = Field( + default=None, + description="ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", + ) + thread: str | None = Field( + default=None, + description="ID of the thread in which this artifact was created (`thr_...`). `null` if not thread-scoped.", + ) + updated_at: datetime | None = Field( + default=None, description="When the artifact record was last modified (ISO 8601)." + ) + user: str | None = Field( + default=None, + description="ID of the user who created this artifact (`usr_...`). `null` if not user-scoped.", + ) + version: int | None = Field( + default=None, + description="Current version number of the artifact. Increments each time a new version is published.", + ) + + +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 AsyncOrgResource: def __init__(self, http: HttpClient): self._http = http @@ -81,6 +207,44 @@ async def list( query["page_size"] = page_size return await self._http.request("/api/v1/orgs", query=query, response_type=OrgListResponse) + async def artifacts( + self, + org: str, + *, + limit: int | None = None, + after_cursor: str | None = None, + group_key: str | None = None, + group_key_prefix: str | None = None, + ) -> OrgArtifactsResponse: + """ + List completed organization artifacts + Returns completed system-owned organization snapshots, newest first. Private user, team, agent and thread artifacts are excluded. + + Args: + org: Organization ID. + limit: Maximum snapshots returned, from 1 to 100. + after_cursor: Opaque cursor for the next page of older snapshots. + group_key: Exact, case-sensitive grouping key. + group_key_prefix: Nonempty literal group prefix; mutually exclusive with group_key. + + 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 + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix + return await self._http.request( + f"/api/v1/orgs/{org}/artifacts", + query=query, + response_type=OrgArtifactsResponse, + ) + class OrgResource: def __init__(self, http: SyncHttpClient): @@ -118,3 +282,41 @@ def list( if page_size is not None: query["page_size"] = page_size return self._http.request("/api/v1/orgs", query=query, response_type=OrgListResponse) + + def artifacts( + self, + org: str, + *, + limit: int | None = None, + after_cursor: str | None = None, + group_key: str | None = None, + group_key_prefix: str | None = None, + ) -> OrgArtifactsResponse: + """ + List completed organization artifacts + Returns completed system-owned organization snapshots, newest first. Private user, team, agent and thread artifacts are excluded. + + Args: + org: Organization ID. + limit: Maximum snapshots returned, from 1 to 100. + after_cursor: Opaque cursor for the next page of older snapshots. + group_key: Exact, case-sensitive grouping key. + group_key_prefix: Nonempty literal group prefix; mutually exclusive with group_key. + + 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 + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix + return self._http.request( + f"/api/v1/orgs/{org}/artifacts", + query=query, + response_type=OrgArtifactsResponse, + ) diff --git a/src/archastro/platform/v1/resources/sandboxes.py b/src/archastro/platform/v1/resources/sandboxes.py index f9b82eb..aabd94c 100644 --- a/src/archastro/platform/v1/resources/sandboxes.py +++ b/src/archastro/platform/v1/resources/sandboxes.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: 68f11ff350c3 +# Content hash: 8afcca499218 from __future__ import annotations @@ -114,8 +114,9 @@ async def keys(self, sandbox: str, input: SandboxKeysInput) -> SandboxKey: Create a sandbox key Issues a new API key for the specified sandbox. Keys can be either `"publishable"` (safe to embed in client-side code) or `"secret"` (server-side - only). The full key value is returned once in the `full_key` field of this - response and is never retrievable again store it securely immediately. + only). The full key value is returned in the `full_key` field. Secret values + are never retrievable again store them securely immediately. Publishable + values remain available as `key_value` when retrieving the sandbox. The caller must authenticate with app-scoped credentials and be able to modify the sandbox (org members for org sandboxes; developers / all-powerful for app-level). If the sandbox does not belong to the caller's app or is not @@ -219,8 +220,9 @@ def keys(self, sandbox: str, input: SandboxKeysInput) -> SandboxKey: Create a sandbox key Issues a new API key for the specified sandbox. Keys can be either `"publishable"` (safe to embed in client-side code) or `"secret"` (server-side - only). The full key value is returned once in the `full_key` field of this - response and is never retrievable again store it securely immediately. + only). The full key value is returned in the `full_key` field. Secret values + are never retrievable again store them securely immediately. Publishable + values remain available as `key_value` when retrieving the sandbox. The caller must authenticate with app-scoped credentials and be able to modify the sandbox (org members for org sandboxes; developers / all-powerful for app-level). If the sandbox does not belong to the caller's app or is not diff --git a/src/archastro/platform/v1/resources/scripts.py b/src/archastro/platform/v1/resources/scripts.py new file mode 100644 index 0000000..1683f33 --- /dev/null +++ b/src/archastro/platform/v1/resources/scripts.py @@ -0,0 +1,348 @@ +# 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 + +from __future__ import annotations + +from typing import Any, TypedDict + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import RuntimeEnvVarList +from ...types.expressions import ExpressionValidation +from ...types.scripts import ScriptLanguageSpec, ScriptRunResult, ScriptTestRunResult + + +class ScriptRunInput(TypedDict, total=False): + "Execute a workflow script" + + resolution_context_config: str | None + "Optional Script config ID establishing the authorized installation context for imports." + run_as_agent: str | None + "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." + run_as_user: str | None + "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." + 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." + + +class ScriptTestInput(TypedDict, total=False): + "Run a script test suite" + + resolution_context_config: str | None + "Optional config ID establishing the authorized installation context for imports." + scope: dict[str, Any] | None + "Optional overrides for the execution scope. Accepts `variables`, `system`, or other top-level scope keys." + script: str | None + "The test script source to execute. Should contain `describe` / `it` / `expect` blocks." + scripts: dict[str, Any] | None + 'Optional map of local script sources keyed by lookup key (e.g. `"my_script"`). Used to resolve `import("script:")` calls before falling back to deployed scripts in the caller\'s app.' + + +class ScriptValidateInput(TypedDict, total=False): + "Validate a workflow script" + + script: str | None + "The script source to validate. Defaults to an empty string." + + +class AsyncScriptResource: + def __init__(self, http: HttpClient): + self._http = http + + async def language(self) -> ScriptLanguageSpec: + """ + Retrieve script language metadata + Returns the full language specification for the ArchAstro scripting engine, + including keywords, operators, built-in functions, namespaces, code snippets, + and type system information. + Use this response to power editor features such as syntax highlighting, + autocompletion, hover documentation, and snippet insertion. The specification + is static for a given platform version; you do not need to poll it on every + session. + Requires an authenticated viewer. No additional app scope is needed. + + Returns: + The script language specification for the current platform version. + """ + return await self._http.request( + "/api/v1/scripts/language", + response_type=ScriptLanguageSpec, + ) + + async def llm_txt(self) -> dict[str, str]: + """ + Retrieve the script-authoring LLM prompt + Returns the plain-text system prompt used by script-authoring assistants. + The prompt is rendered from the current language specification so builtins, + namespaces, snippets, and type-system guidance stay synchronized with the + running platform. + + Returns: + Plain-text system prompt for script-authoring assistants. + """ + return await self._http.request_raw("/api/v1/scripts/llm.txt") + + 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. + 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 + partial output; the error is also recorded in the app's activity feed. + + Args: + input: Request body. + 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. + + Returns: + The script execution result, including the return value, captured output, and any runtime error or diagnostic findings. + """ + return await self._http.request( + "/api/v1/scripts/run", + method="POST", + body=input, + response_type=ScriptRunResult, + ) + + async def runtime_env_vars(self) -> RuntimeEnvVarList: + """ + List runtime environment variables for scripts + Returns metadata for all runtime environment variables available to scripts + running within the specified app. The response lists each variable's name + and description but does not include resolved values. + Use this to surface the available `env.*` identifiers in script editor + autocompletion or to inspect which variables are configured for an app + before running a script. + Requires an authenticated viewer scoped to the specified app. + + Returns: + Paginated list of runtime environment variable metadata for the app. + """ + return await self._http.request( + "/api/v1/scripts/runtime_env_vars", + response_type=RuntimeEnvVarList, + ) + + async def test(self, input: ScriptTestInput) -> ScriptTestRunResult: + """ + Run a script test suite + Executes a Jest-style script test source (`describe` / `it` / `expect`) and + returns a structured assertion report. Use this endpoint to run unit tests + against script logic during development or in CI pipelines. + Imports of the form `import("script:")` inside the test script + resolve against the `scripts` map supplied in the request body first, then + fall back to deployed scripts in the caller's app. This lets you test local + changes to scripts before deploying them. + Runtime environment variables defined for the app are automatically injected + into `variables.env` within the execution scope. + When a mid-run runtime error occurs (after some tests have already executed), + the endpoint returns HTTP 200 with `passed: false`, the partial per-test + breakdown, and a top-level `error` and `findings` describing the crash. When + the test source itself cannot be parsed, the endpoint returns HTTP 422 with + the syntax error message. + + Args: + input: Request body. + input.resolution_context_config: Optional config ID establishing the authorized installation context for imports. + input.scope: Optional overrides for the execution scope. Accepts `variables`, `system`, or other top-level scope keys. + input.script: The test script source to execute. Should contain `describe` / `it` / `expect` blocks. + input.scripts: Optional map of local script sources keyed by lookup key (e.g. `"my_script"`). Used to resolve `import("script:")` calls before falling back to deployed scripts in the caller's app. + + Returns: + The assertion report for the test run, including per-suite and per-test results, assertion counts, captured output, and any runtime error or diagnostic findings. + """ + return await self._http.request( + "/api/v1/scripts/test", + method="POST", + body=input, + response_type=ScriptTestRunResult, + ) + + async def validate(self, input: ScriptValidateInput) -> ExpressionValidation: + """ + Validate a workflow script + Parses and statically analyzes the provided script source, returning a list + of diagnostic findings (errors and warnings) without executing the script. + Use this endpoint to power real-time syntax and type checking in script + editors. + The response always returns HTTP 200. Check the `findings` array in the + returned validation result for any errors or warnings. An empty `findings` + list means the script passed all static checks. + Requires an authenticated viewer. No additional app scope is needed. + + Args: + input: Request body. + input.script: The script source to validate. Defaults to an empty string. + + Returns: + Static analysis result containing a list of diagnostic findings for the script. + """ + return await self._http.request( + "/api/v1/scripts/validate", + method="POST", + body=input, + response_type=ExpressionValidation, + ) + + +class ScriptResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def language(self) -> ScriptLanguageSpec: + """ + Retrieve script language metadata + Returns the full language specification for the ArchAstro scripting engine, + including keywords, operators, built-in functions, namespaces, code snippets, + and type system information. + Use this response to power editor features such as syntax highlighting, + autocompletion, hover documentation, and snippet insertion. The specification + is static for a given platform version; you do not need to poll it on every + session. + Requires an authenticated viewer. No additional app scope is needed. + + Returns: + The script language specification for the current platform version. + """ + return self._http.request("/api/v1/scripts/language", response_type=ScriptLanguageSpec) + + def llm_txt(self) -> dict[str, str]: + """ + Retrieve the script-authoring LLM prompt + Returns the plain-text system prompt used by script-authoring assistants. + The prompt is rendered from the current language specification so builtins, + namespaces, snippets, and type-system guidance stay synchronized with the + running platform. + + Returns: + Plain-text system prompt for script-authoring assistants. + """ + return self._http.request_raw("/api/v1/scripts/llm.txt") + + 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. + 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 + partial output; the error is also recorded in the app's activity feed. + + Args: + input: Request body. + 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. + + Returns: + The script execution result, including the return value, captured output, and any runtime error or diagnostic findings. + """ + return self._http.request( + "/api/v1/scripts/run", + method="POST", + body=input, + response_type=ScriptRunResult, + ) + + def runtime_env_vars(self) -> RuntimeEnvVarList: + """ + List runtime environment variables for scripts + Returns metadata for all runtime environment variables available to scripts + running within the specified app. The response lists each variable's name + and description but does not include resolved values. + Use this to surface the available `env.*` identifiers in script editor + autocompletion or to inspect which variables are configured for an app + before running a script. + Requires an authenticated viewer scoped to the specified app. + + Returns: + Paginated list of runtime environment variable metadata for the app. + """ + return self._http.request( + "/api/v1/scripts/runtime_env_vars", + response_type=RuntimeEnvVarList, + ) + + def test(self, input: ScriptTestInput) -> ScriptTestRunResult: + """ + Run a script test suite + Executes a Jest-style script test source (`describe` / `it` / `expect`) and + returns a structured assertion report. Use this endpoint to run unit tests + against script logic during development or in CI pipelines. + Imports of the form `import("script:")` inside the test script + resolve against the `scripts` map supplied in the request body first, then + fall back to deployed scripts in the caller's app. This lets you test local + changes to scripts before deploying them. + Runtime environment variables defined for the app are automatically injected + into `variables.env` within the execution scope. + When a mid-run runtime error occurs (after some tests have already executed), + the endpoint returns HTTP 200 with `passed: false`, the partial per-test + breakdown, and a top-level `error` and `findings` describing the crash. When + the test source itself cannot be parsed, the endpoint returns HTTP 422 with + the syntax error message. + + Args: + input: Request body. + input.resolution_context_config: Optional config ID establishing the authorized installation context for imports. + input.scope: Optional overrides for the execution scope. Accepts `variables`, `system`, or other top-level scope keys. + input.script: The test script source to execute. Should contain `describe` / `it` / `expect` blocks. + input.scripts: Optional map of local script sources keyed by lookup key (e.g. `"my_script"`). Used to resolve `import("script:")` calls before falling back to deployed scripts in the caller's app. + + Returns: + The assertion report for the test run, including per-suite and per-test results, assertion counts, captured output, and any runtime error or diagnostic findings. + """ + return self._http.request( + "/api/v1/scripts/test", + method="POST", + body=input, + response_type=ScriptTestRunResult, + ) + + def validate(self, input: ScriptValidateInput) -> ExpressionValidation: + """ + Validate a workflow script + Parses and statically analyzes the provided script source, returning a list + of diagnostic findings (errors and warnings) without executing the script. + Use this endpoint to power real-time syntax and type checking in script + editors. + The response always returns HTTP 200. Check the `findings` array in the + returned validation result for any errors or warnings. An empty `findings` + list means the script passed all static checks. + Requires an authenticated viewer. No additional app scope is needed. + + Args: + input: Request body. + input.script: The script source to validate. Defaults to an empty string. + + Returns: + Static analysis result containing a list of diagnostic findings for the script. + """ + return self._http.request( + "/api/v1/scripts/validate", + method="POST", + body=input, + response_type=ExpressionValidation, + ) diff --git a/src/archastro/platform/v1/resources/slack_channel_bindings.py b/src/archastro/platform/v1/resources/slack_channel_bindings.py index ed4893e..b87e8a7 100644 --- a/src/archastro/platform/v1/resources/slack_channel_bindings.py +++ b/src/archastro/platform/v1/resources/slack_channel_bindings.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: 739c91726193 +# Content hash: dcc720cd25b2 from __future__ import annotations @@ -38,6 +38,17 @@ class SlackChannelBindingCreateInput(TypedDict, total=False): "ID of the team to bind the Slack channel to. The caller must have team-manage rights on this team." +class SlackChannelBindingAssignInput(TypedDict): + "Assign a Slack channel's resident agent" + + agent_user_id: str + "Agent user ID to assign as the channel's sole resident." + channel_id: str + "Slack channel ID whose resident is being assigned (e.g. `C01234ABCDE`). A bare internal binding is created when the channel has none." + slack_team_id: str + "Slack workspace team ID that the channel belongs to (e.g. `T01234ABCDE`). Identifies which Slack integration to use." + + class SlackChannelBindingProvisionInput(TypedDict, total=False): "Start adding a customer over Slack Connect" @@ -168,6 +179,39 @@ async def create(self, input: SlackChannelBindingCreateInput) -> SlackChannelBin response_type=SlackChannelBinding, ) + async def assign(self, input: SlackChannelBindingAssignInput) -> SlackChannelBinding: + """ + Assign a Slack channel's resident agent + Makes the given agent the channel's sole resident (one resident per + channel: any previously attached agent is detached in the same + transaction and its mirror-thread read grant is revoked immediately). + Creates a bare internal binding first when the channel has none. + Fetches the channel's `is_private` / `is_ext_shared` flags live from + Slack (server-side, best-effort) to feed the fail-closed residency + gates: a channel shared with an external workspace but not yet bound to + a customer team fails with `team_required_for_shared_channel` (bind the + team first via `upsert`/setup), and a private channel is member-managed + mutation without platform-verified in-channel evidence fails with + `channel_membership_required`. + When the binding is bound to a customer team, the agent is also + enrolled as a member of that team (same pairing as `upsert`). + + Args: + input: Request body. + input.agent_user_id: Agent user ID to assign as the channel's sole resident. + input.channel_id: Slack channel ID whose resident is being assigned (e.g. `C01234ABCDE`). A bare internal binding is created when the channel has none. + input.slack_team_id: Slack workspace team ID that the channel belongs to (e.g. `T01234ABCDE`). Identifies which Slack integration to use. + + Returns: + The binding after assignment, with the attached agent list. + """ + return await self._http.request( + "/api/v1/slack_channel_bindings/assign", + method="POST", + body=input, + response_type=SlackChannelBinding, + ) + async def provision(self, input: SlackChannelBindingProvisionInput) -> SlackChannelBinding: """ Start adding a customer over Slack Connect @@ -440,6 +484,39 @@ def create(self, input: SlackChannelBindingCreateInput) -> SlackChannelBinding: response_type=SlackChannelBinding, ) + def assign(self, input: SlackChannelBindingAssignInput) -> SlackChannelBinding: + """ + Assign a Slack channel's resident agent + Makes the given agent the channel's sole resident (one resident per + channel: any previously attached agent is detached in the same + transaction and its mirror-thread read grant is revoked immediately). + Creates a bare internal binding first when the channel has none. + Fetches the channel's `is_private` / `is_ext_shared` flags live from + Slack (server-side, best-effort) to feed the fail-closed residency + gates: a channel shared with an external workspace but not yet bound to + a customer team fails with `team_required_for_shared_channel` (bind the + team first via `upsert`/setup), and a private channel is member-managed + mutation without platform-verified in-channel evidence fails with + `channel_membership_required`. + When the binding is bound to a customer team, the agent is also + enrolled as a member of that team (same pairing as `upsert`). + + Args: + input: Request body. + input.agent_user_id: Agent user ID to assign as the channel's sole resident. + input.channel_id: Slack channel ID whose resident is being assigned (e.g. `C01234ABCDE`). A bare internal binding is created when the channel has none. + input.slack_team_id: Slack workspace team ID that the channel belongs to (e.g. `T01234ABCDE`). Identifies which Slack integration to use. + + Returns: + The binding after assignment, with the attached agent list. + """ + return self._http.request( + "/api/v1/slack_channel_bindings/assign", + method="POST", + body=input, + response_type=SlackChannelBinding, + ) + def provision(self, input: SlackChannelBindingProvisionInput) -> SlackChannelBinding: """ Start adding a customer over Slack Connect diff --git a/src/archastro/platform/v1/resources/ssh_keys.py b/src/archastro/platform/v1/resources/ssh_keys.py new file mode 100644 index 0000000..91993b7 --- /dev/null +++ b/src/archastro/platform/v1/resources/ssh_keys.py @@ -0,0 +1,43 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 1e4635f05ba7 + +from __future__ import annotations + +from ...runtime.http_client import HttpClient, SyncHttpClient + + +class AsyncSshKeyResource: + def __init__(self, http: HttpClient): + self._http = http + + async def delete(self, ssh_key: str) -> None: + """ + Revoke an SSH public key + Revokes an owned key immediately. The key remains visible as audit metadata. + + Args: + ssh_key: Registered SSH key ID (`ssk_...`). + + Returns: + No content + """ + await self._http.request(f"/api/v1/ssh_keys/{ssh_key}", method="DELETE") + + +class SshKeyResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def delete(self, ssh_key: str) -> None: + """ + Revoke an SSH public key + Revokes an owned key immediately. The key remains visible as audit metadata. + + Args: + ssh_key: Registered SSH key ID (`ssk_...`). + + Returns: + No content + """ + self._http.request(f"/api/v1/ssh_keys/{ssh_key}", method="DELETE") diff --git a/src/archastro/platform/v1/resources/tasks.py b/src/archastro/platform/v1/resources/tasks.py index f4af32e..1621064 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: 274298b8025e +# Content hash: 60b86d22d3a4 from __future__ import annotations @@ -10,7 +10,13 @@ from pydantic import BaseModel, Field from ...runtime.http_client import HttpClient, SyncHttpClient -from ...types.tasks import Task, TaskComment, TaskSessionLease, TaskSessionLeaseSummary +from ...types.tasks import ( + Task, + TaskComment, + TaskExternalLink, + TaskSessionLease, + TaskSessionLeaseSummary, +) class BlockerCreateInput(TypedDict, total=False): @@ -50,6 +56,8 @@ class CommentReplaceInput(TypedDict): class LeaseCreateInput(TypedDict, total=False): "Claim a task for a coding session" + force: bool | None + "Take the lease from a live but dead holder instead of returning a conflict. Releases the prior session's lease and claims the new one atomically. Always scoped to the task's assigned owner, same as an ordinary claim." harness: Required[str] "Bounded harness identifier." lease_duration_seconds: int | None @@ -97,6 +105,8 @@ class TaskReplaceInput(TypedDict, total=False): "Updated due date in ISO 8601 format, or null to clear it." epic: str | None "Replacement grouping label. Pass null to clear it." + expected_version: int | None + "Aggregate version returned by the latest task read. The update fails with `task_version_conflict` if the task changed first." lease_id: str | None "Current caller-held lease UUID. Must be paired with `lease_session_id`." lease_session_id: str | None @@ -124,7 +134,7 @@ class TaskReplaceInput(TypedDict, total=False): source_type: str | None "Replacement source object kind. Must be supplied with the other source fields." status: str | None - "Updated status: `open`, `in_progress`, or `done`." + "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." team: str | None @@ -244,6 +254,10 @@ class BlockerListResponseDataItem(BaseModel): 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 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.", @@ -342,7 +356,7 @@ class BlockerListResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -476,6 +490,23 @@ class CommentListResponse(BaseModel): has_more: bool +class LinkListResponseDataItem(BaseModel): + external_scope: str = Field(..., description="External container identity.") + object_id: str = Field(..., description="Object identity within that container.") + object_type: str = Field(..., description="External object kind.") + + +class LinkListResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[LinkListResponseDataItem] + has_more: bool + + class TaskActivityResponseDataItem(BaseModel): event_type: str | None = Field( default=None, @@ -612,6 +643,10 @@ class TaskBlockingResponseDataItem(BaseModel): 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 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.", @@ -710,7 +745,7 @@ class TaskBlockingResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -859,6 +894,10 @@ class TaskSubtasksResponseDataItem(BaseModel): 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 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.", @@ -957,7 +996,7 @@ class TaskSubtasksResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -1260,6 +1299,7 @@ async def create(self, task: str, input: LeaseCreateInput) -> TaskSessionLease: Args: task: Task ID (`tsk_...`). input: Request body. + input.force: Take the lease from a live but dead holder instead of returning a conflict. Releases the prior session's lease and claims the new one atomically. Always scoped to the task's assigned owner, same as an ordinary claim. input.harness: Bounded harness identifier. input.lease_duration_seconds: Requested lease lifetime in seconds; the task aggregate enforces its bounds. input.lease_id: Caller-generated lease UUID. @@ -1317,7 +1357,53 @@ async def remove(self, task: str) -> None: """ await self._http.request(f"/api/v1/tasks/{task}/links", method="DELETE") - async def create(self, task: str, input: LinkCreateInput) -> dict[str, Any]: + async def list( + self, + task: str, + *, + team: str | None = None, + user: str | None = None, + agent: str | None = None, + org: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> LinkListResponse: + """ + List a task's external links + Returns indexed link identities ordered by external_scope, object_type, then object_id ascending. This does not read the legacy Task links map. + + Args: + task: Task ID (`tsk_...`). + team: Explicit owning team for privileged calls. + user: Explicit owning user for privileged calls. + agent: Explicit owning agent for privileged calls. + org: Explicit organization for privileged calls; must agree with owner. + limit: Maximum links to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if team is not None: + query["team"] = team + if user is not None: + query["user"] = user + if agent is not None: + query["agent"] = agent + if org is not None: + query["org"] = org + 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/tasks/{task}/links", + query=query, + response_type=LinkListResponse, + ) + + async def create(self, task: str, input: LinkCreateInput) -> TaskExternalLink: """ Add an external link to a task @@ -1329,9 +1415,14 @@ async def create(self, task: str, input: LinkCreateInput) -> dict[str, Any]: input.object_type: External object type. Returns: - The created external link. + The created external link, with its identity normalized. """ - return await self._http.request(f"/api/v1/tasks/{task}/links", method="POST", body=input) + return await self._http.request( + f"/api/v1/tasks/{task}/links", + method="POST", + body=input, + response_type=TaskExternalLink, + ) class AsyncTaskResource: @@ -1416,6 +1507,8 @@ async def replace(self, task: str, input: TaskReplaceInput) -> Task: `lease_session_id`. The task aggregate fences that update against the live lease and records server-sourced session provenance. Omitting both remains a normal authorized human/API update. + Supply `expected_version` from the latest task representation to make the + update conditional. A concurrent write returns `task_version_conflict`. Args: task: Task ID (`tsk_...`). @@ -1424,6 +1517,7 @@ async def replace(self, task: str, input: TaskReplaceInput) -> Task: input.description: Updated long-form description. input.due_date: Updated due date in ISO 8601 format, or null to clear it. input.epic: Replacement grouping label. Pass null to clear it. + input.expected_version: Aggregate version returned by the latest task read. The update fails with `task_version_conflict` if the task changed first. input.lease_id: Current caller-held lease UUID. Must be paired with `lease_session_id`. input.lease_session_id: Current coding-session UUID. Must be paired with `lease_id`. input.links: Replacement related-links object. @@ -1437,7 +1531,7 @@ async def replace(self, task: str, input: TaskReplaceInput) -> Task: 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`, or `done`. + 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.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. @@ -1863,6 +1957,7 @@ def create(self, task: str, input: LeaseCreateInput) -> TaskSessionLease: Args: task: Task ID (`tsk_...`). input: Request body. + input.force: Take the lease from a live but dead holder instead of returning a conflict. Releases the prior session's lease and claims the new one atomically. Always scoped to the task's assigned owner, same as an ordinary claim. input.harness: Bounded harness identifier. input.lease_duration_seconds: Requested lease lifetime in seconds; the task aggregate enforces its bounds. input.lease_id: Caller-generated lease UUID. @@ -1920,7 +2015,53 @@ def remove(self, task: str) -> None: """ self._http.request(f"/api/v1/tasks/{task}/links", method="DELETE") - def create(self, task: str, input: LinkCreateInput) -> dict[str, Any]: + def list( + self, + task: str, + *, + team: str | None = None, + user: str | None = None, + agent: str | None = None, + org: str | None = None, + limit: int | None = None, + after_cursor: str | None = None, + ) -> LinkListResponse: + """ + List a task's external links + Returns indexed link identities ordered by external_scope, object_type, then object_id ascending. This does not read the legacy Task links map. + + Args: + task: Task ID (`tsk_...`). + team: Explicit owning team for privileged calls. + user: Explicit owning user for privileged calls. + agent: Explicit owning agent for privileged calls. + org: Explicit organization for privileged calls; must agree with owner. + limit: Maximum links to return. Capped at 100. + after_cursor: Opaque cursor returned by the previous page. + + Returns: + Successful response + """ + query: dict[str, object] = {} + if team is not None: + query["team"] = team + if user is not None: + query["user"] = user + if agent is not None: + query["agent"] = agent + if org is not None: + query["org"] = org + 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/tasks/{task}/links", + query=query, + response_type=LinkListResponse, + ) + + def create(self, task: str, input: LinkCreateInput) -> TaskExternalLink: """ Add an external link to a task @@ -1932,9 +2073,14 @@ def create(self, task: str, input: LinkCreateInput) -> dict[str, Any]: input.object_type: External object type. Returns: - The created external link. + The created external link, with its identity normalized. """ - return self._http.request(f"/api/v1/tasks/{task}/links", method="POST", body=input) + return self._http.request( + f"/api/v1/tasks/{task}/links", + method="POST", + body=input, + response_type=TaskExternalLink, + ) class TaskResource: @@ -2019,6 +2165,8 @@ def replace(self, task: str, input: TaskReplaceInput) -> Task: `lease_session_id`. The task aggregate fences that update against the live lease and records server-sourced session provenance. Omitting both remains a normal authorized human/API update. + Supply `expected_version` from the latest task representation to make the + update conditional. A concurrent write returns `task_version_conflict`. Args: task: Task ID (`tsk_...`). @@ -2027,6 +2175,7 @@ def replace(self, task: str, input: TaskReplaceInput) -> Task: input.description: Updated long-form description. input.due_date: Updated due date in ISO 8601 format, or null to clear it. input.epic: Replacement grouping label. Pass null to clear it. + input.expected_version: Aggregate version returned by the latest task read. The update fails with `task_version_conflict` if the task changed first. input.lease_id: Current caller-held lease UUID. Must be paired with `lease_session_id`. input.lease_session_id: Current coding-session UUID. Must be paired with `lease_id`. input.links: Replacement related-links object. @@ -2040,7 +2189,7 @@ def replace(self, task: str, input: TaskReplaceInput) -> Task: 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`, or `done`. + 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.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. diff --git a/src/archastro/platform/v1/resources/teams.py b/src/archastro/platform/v1/resources/teams.py index 3906d88..6b228f3 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: d4926eae556b +# Content hash: aa08841149e3 from __future__ import annotations @@ -51,6 +51,8 @@ class TeamTaskCreateInputTask(TypedDict, total=False): "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 @@ -72,7 +74,7 @@ class TeamTaskCreateInputTask(TypedDict, total=False): source_type: str | None "Kind of source object (for example `repository`)." status: str | None - 'Initial status for the task. One of `"open"`, `"in_progress"`, or `"done"`. Defaults to `"open"` when omitted.' + '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." thread: str | None @@ -620,7 +622,11 @@ class MemberListResponseDataItemAgentSourceSolutionCurrentSolution(BaseModel): id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -869,7 +875,11 @@ class MemberListResponseDataItemAgentSourceSolutionSolution(BaseModel): id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -1352,6 +1362,10 @@ class TeamTaskListResponseDataItem(BaseModel): 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 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.", @@ -1450,7 +1464,7 @@ class TeamTaskListResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -1605,6 +1619,10 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): 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 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.", @@ -1703,7 +1721,7 @@ class TeamTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -1875,6 +1893,10 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): 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 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.", @@ -1973,7 +1995,7 @@ class TeamTaskReadyResponseDataItemTask(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -2139,6 +2161,10 @@ class TeamTaskSearchResponseDataItem(BaseModel): 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 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.", @@ -2237,7 +2263,7 @@ class TeamTaskSearchResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -2560,6 +2586,24 @@ class TeamThreadListResponseDataItemParentMessageAttachmentsItem(BaseModel): ) +class TeamThreadListResponseDataItemParentMessageContextItem(BaseModel): + attributes: dict[str, Any] | None = Field( + default=None, + description="Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + ) + content: str | None = Field( + default=None, + description="Optional context body. The model receives it as escaped XML data, not a system instruction.", + ) + title: str | None = Field( + default=None, description="Optional human-readable label for this context block." + ) + type: str = Field( + ..., + description="Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", + ) + + class TeamThreadListResponseDataItemParentMessageReactionsItem(BaseModel): payload: dict[str, Any] | None = Field( default=None, @@ -2603,6 +2647,10 @@ class TeamThreadListResponseDataItemParentMessage(BaseModel): default=None, description="Text content of the message. `null` for messages that contain only attachments.", ) + context: list[TeamThreadListResponseDataItemParentMessageContextItem] | None = Field( + default=None, + description="Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + ) created_at: str | None = Field( default=None, description="When the message was posted (ISO 8601)." ) @@ -2985,7 +3033,11 @@ class TeamThreadListResponseDataItemParticipatingAgentsItemSourceSolutionCurrent id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -3242,7 +3294,11 @@ class TeamThreadListResponseDataItemParticipatingAgentsItemSourceSolutionSolutio id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -3818,6 +3874,10 @@ class TeamArtifactsResponseDataItem(BaseModel): default=None, description="Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", ) + group_key: str | None = Field( + default=None, + description="Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + ) id: str = Field(..., description="Artifact ID (`art_...`).") image_source: TeamArtifactsResponseDataItemImageSource | None = Field( default=None, @@ -3834,6 +3894,10 @@ class TeamArtifactsResponseDataItem(BaseModel): default=None, description="Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", ) + system: bool | None = Field( + default=None, + description="True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + ) team: str | None = Field( default=None, description="ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", @@ -4030,6 +4094,56 @@ async def create(self, team: str, input: TeamCustomObjectCreateInput) -> CustomO ) +class AsyncTeamInviteResource: + def __init__(self, http: HttpClient): + self._http = http + + async def create(self, team: str) -> TeamInvite: + """ + Create a team invite + Generates a new invite code for the specified team. The authenticated user + must be a member of the team with the `owner` or `admin` role. + The returned code is a short alphanumeric string that other users can + present to join the team. Each call produces a new code; previously issued + codes are not invalidated by this request. Codes expire after seven days. + Sharing a code intentionally grants membership across organizations within + the same application, including access to objects shared with team members. + Treat it as a bearer credential. Revoke an individual code with + DELETE /api/v1/teams/:team/invite/:code before issuing a replacement. + Revocation does not remove existing members or their access. + + Args: + team: Team ID (`tm_...`) identifying the team for which to generate the invite code. + + Returns: + The newly created team invite containing the join code. + """ + return await self._http.request( + f"/api/v1/teams/{team}/invite", + method="POST", + response_type=TeamInvite, + ) + + async def delete(self, team: str, code: str) -> None: + """ + Revoke a team invite code + Deletes one invite code for this team. Requires an owner or admin membership. + Future redemption returns 404, as for an unknown or expired code. Existing + memberships and their access are unchanged; remove members separately to end + their access. Redemption already in flight is not cancelled. + To replace a code, revoke it and then create a new invite. Creating a new + invite alone does not invalidate any previous code. Other codes remain valid. + + Args: + team: Team ID (`tm_...`) identifying the team for which to generate the invite code. + code: The invite code to revoke. + + Returns: + No content + """ + await self._http.request(f"/api/v1/teams/{team}/invite/{code}", method="DELETE") + + class AsyncMemberResource: def __init__(self, http: HttpClient): self._http = http @@ -4181,7 +4295,7 @@ async def list( team: Team ID (`tem_...`). Only tasks belonging to this team are returned. user: User ID (`usr_...`) for user-scoped tasks. org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. - status: Filter tasks by status. One of `"open"`, `"in_progress"`, or `"done"`. Omit to return tasks in all statuses. + 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). @@ -4457,7 +4571,7 @@ async def search( org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. 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"`, or `"done"`. Omit to include all statuses. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. owner_user: Restrict results to tasks assigned to the user with this public ID (`usr_...`). owner_agent: Restrict results to tasks assigned to the agent with this public ID (`agi_...`). priority: Filter results by priority, from 0 (highest) to 4 (lowest). @@ -4613,6 +4727,7 @@ class AsyncTeamResource: def __init__(self, http: HttpClient): self._http = http self.custom_objects = AsyncTeamCustomObjectResource(http) + self.invite = AsyncTeamInviteResource(http) self.members = AsyncMemberResource(http) self.tasks = AsyncTeamTaskResource(http) self.threads = AsyncTeamThreadResource(http) @@ -4786,7 +4901,9 @@ async def update(self, team: str, input: TeamUpdateInput) -> Team: response_type=Team, ) - async def artifacts(self, team: str) -> TeamArtifactsResponse: + async def artifacts( + self, team: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> TeamArtifactsResponse: """ List a team's artifacts Returns all artifacts owned by the specified team. Artifacts represent @@ -4801,36 +4918,23 @@ async def artifacts(self, team: str) -> TeamArtifactsResponse: Args: team: Team ID (`tea_...`). The authenticated user must be a member of this team. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return await self._http.request( f"/api/v1/teams/{team}/artifacts", + query=query, response_type=TeamArtifactsResponse, ) - async def invite(self, team: str) -> TeamInvite: - """ - Create a team invite - Generates a new invite code for the specified team. The authenticated user - must be a member of the team with the `owner` or `admin` role. - The returned code is a short alphanumeric string that other users can - present to join the team. Each call produces a new code; previously issued - codes are not invalidated by this request. - - Args: - team: Team ID (`tm_...`) identifying the team for which to generate the invite code. - - Returns: - The newly created team invite containing the join code. - """ - return await self._http.request( - f"/api/v1/teams/{team}/invite", - method="POST", - response_type=TeamInvite, - ) - async def invites(self, team: str) -> TeamInvitesResponse: """ Create a team invite (server-to-server) @@ -5018,6 +5122,56 @@ def create(self, team: str, input: TeamCustomObjectCreateInput) -> CustomObject: ) +class TeamInviteResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def create(self, team: str) -> TeamInvite: + """ + Create a team invite + Generates a new invite code for the specified team. The authenticated user + must be a member of the team with the `owner` or `admin` role. + The returned code is a short alphanumeric string that other users can + present to join the team. Each call produces a new code; previously issued + codes are not invalidated by this request. Codes expire after seven days. + Sharing a code intentionally grants membership across organizations within + the same application, including access to objects shared with team members. + Treat it as a bearer credential. Revoke an individual code with + DELETE /api/v1/teams/:team/invite/:code before issuing a replacement. + Revocation does not remove existing members or their access. + + Args: + team: Team ID (`tm_...`) identifying the team for which to generate the invite code. + + Returns: + The newly created team invite containing the join code. + """ + return self._http.request( + f"/api/v1/teams/{team}/invite", + method="POST", + response_type=TeamInvite, + ) + + def delete(self, team: str, code: str) -> None: + """ + Revoke a team invite code + Deletes one invite code for this team. Requires an owner or admin membership. + Future redemption returns 404, as for an unknown or expired code. Existing + memberships and their access are unchanged; remove members separately to end + their access. Redemption already in flight is not cancelled. + To replace a code, revoke it and then create a new invite. Creating a new + invite alone does not invalidate any previous code. Other codes remain valid. + + Args: + team: Team ID (`tm_...`) identifying the team for which to generate the invite code. + code: The invite code to revoke. + + Returns: + No content + """ + self._http.request(f"/api/v1/teams/{team}/invite/{code}", method="DELETE") + + class MemberResource: def __init__(self, http: SyncHttpClient): self._http = http @@ -5166,7 +5320,7 @@ def list( team: Team ID (`tem_...`). Only tasks belonging to this team are returned. user: User ID (`usr_...`) for user-scoped tasks. org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. - status: Filter tasks by status. One of `"open"`, `"in_progress"`, or `"done"`. Omit to return tasks in all statuses. + 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). @@ -5442,7 +5596,7 @@ def search( org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. 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"`, or `"done"`. Omit to include all statuses. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. owner_user: Restrict results to tasks assigned to the user with this public ID (`usr_...`). owner_agent: Restrict results to tasks assigned to the agent with this public ID (`agi_...`). priority: Filter results by priority, from 0 (highest) to 4 (lowest). @@ -5596,6 +5750,7 @@ class TeamResource: def __init__(self, http: SyncHttpClient): self._http = http self.custom_objects = TeamCustomObjectResource(http) + self.invite = TeamInviteResource(http) self.members = MemberResource(http) self.tasks = TeamTaskResource(http) self.threads = TeamThreadResource(http) @@ -5760,7 +5915,9 @@ def update(self, team: str, input: TeamUpdateInput) -> Team: response_type=Team, ) - def artifacts(self, team: str) -> TeamArtifactsResponse: + def artifacts( + self, team: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> TeamArtifactsResponse: """ List a team's artifacts Returns all artifacts owned by the specified team. Artifacts represent @@ -5775,36 +5932,23 @@ def artifacts(self, team: str) -> TeamArtifactsResponse: Args: team: Team ID (`tea_...`). The authenticated user must be a member of this team. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return self._http.request( f"/api/v1/teams/{team}/artifacts", + query=query, response_type=TeamArtifactsResponse, ) - def invite(self, team: str) -> TeamInvite: - """ - Create a team invite - Generates a new invite code for the specified team. The authenticated user - must be a member of the team with the `owner` or `admin` role. - The returned code is a short alphanumeric string that other users can - present to join the team. Each call produces a new code; previously issued - codes are not invalidated by this request. - - Args: - team: Team ID (`tm_...`) identifying the team for which to generate the invite code. - - Returns: - The newly created team invite containing the join code. - """ - return self._http.request( - f"/api/v1/teams/{team}/invite", - method="POST", - response_type=TeamInvite, - ) - def invites(self, team: str) -> TeamInvitesResponse: """ Create a team invite (server-to-server) diff --git a/src/archastro/platform/v1/resources/threads.py b/src/archastro/platform/v1/resources/threads.py index b31594e..5fbd9bb 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: 8d947a8b5d00 +# Content hash: a0c8f7a7dc6a from __future__ import annotations @@ -256,6 +256,10 @@ class ThreadArtifactsResponseDataItem(BaseModel): default=None, description="Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", ) + group_key: str | None = Field( + default=None, + description="Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + ) id: str = Field(..., description="Artifact ID (`art_...`).") image_source: ThreadArtifactsResponseDataItemImageSource | None = Field( default=None, @@ -272,6 +276,10 @@ class ThreadArtifactsResponseDataItem(BaseModel): default=None, description="Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", ) + system: bool | None = Field( + default=None, + description="True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + ) team: str | None = Field( default=None, description="ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", @@ -581,6 +589,24 @@ class ThreadMessagesResponseDataMessagesItemAttachmentsItem(BaseModel): ) +class ThreadMessagesResponseDataMessagesItemContextItem(BaseModel): + attributes: dict[str, Any] | None = Field( + default=None, + description="Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + ) + content: str | None = Field( + default=None, + description="Optional context body. The model receives it as escaped XML data, not a system instruction.", + ) + title: str | None = Field( + default=None, description="Optional human-readable label for this context block." + ) + type: str = Field( + ..., + description="Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", + ) + + class ThreadMessagesResponseDataMessagesItemReactionsItem(BaseModel): payload: dict[str, Any] | None = Field( default=None, @@ -624,6 +650,10 @@ class ThreadMessagesResponseDataMessagesItem(BaseModel): default=None, description="Text content of the message. `null` for messages that contain only attachments.", ) + context: list[ThreadMessagesResponseDataMessagesItemContextItem] | None = Field( + default=None, + description="Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + ) created_at: str | None = Field( default=None, description="When the message was posted (ISO 8601)." ) @@ -747,6 +777,10 @@ class ThreadSearchResponseDataItem(BaseModel): description="A bounded snippet around the first matching occurrence (at most 240 characters).", ) created_at: datetime = Field(..., description="When the message was posted.") + evidence: dict[str, Any] = Field( + ..., + description="Bounded, allowlisted structured-post evidence such as source, lifecycle type, references, repository context, and addressee. Empty for ordinary messages.", + ) id: str = Field(..., description="Message ID (`msg_...`).") similarity_score: float | None = Field( default=None, @@ -1130,7 +1164,9 @@ async def agents(self, thread: str) -> ThreadAgentsResponse: response_type=ThreadAgentsResponse, ) - async def artifacts(self, thread: str) -> ThreadArtifactsResponse: + async def artifacts( + self, thread: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> ThreadArtifactsResponse: """ List artifacts for a thread Returns all artifacts produced during a thread's AI conversation. Artifacts are @@ -1141,12 +1177,20 @@ async def artifacts(self, thread: str) -> ThreadArtifactsResponse: Args: thread: Thread ID (`thr_...`). Must be accessible to the authenticated user. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return await self._http.request( f"/api/v1/threads/{thread}/artifacts", + query=query, response_type=ThreadArtifactsResponse, ) @@ -1326,11 +1370,12 @@ async def search( stored message embeddings by cosine similarity, and `"hybrid"` combines the text and embedding rankings with Reciprocal Rank Fusion (RRF). Only messages visible to the authenticated caller are considered. - Results are intentionally lean: each row contains only a bounded content - snippet, sender identity, and timestamp. Attachments, reactions, ACLs, and - metadata are neither hydrated nor serialized. At most 20 results are - returned. Text results support chronological cursor pagination. Embedding and - hybrid results are relevance-ranked single pages and return null cursors. + Results are intentionally lean: each row contains a bounded content snippet, + sender identity, timestamp, and a small allowlisted structured-post evidence + projection. Attachments, reactions, ACLs, and arbitrary metadata are neither + hydrated nor serialized. At most 20 results are returned. Text results + support chronological cursor pagination. Embedding and hybrid results are + relevance-ranked single pages and return null cursors. Args: thread: Thread ID (`thr_...`). Must be visible to the authenticated caller. @@ -1709,7 +1754,9 @@ def agents(self, thread: str) -> ThreadAgentsResponse: response_type=ThreadAgentsResponse, ) - def artifacts(self, thread: str) -> ThreadArtifactsResponse: + def artifacts( + self, thread: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> ThreadArtifactsResponse: """ List artifacts for a thread Returns all artifacts produced during a thread's AI conversation. Artifacts are @@ -1720,12 +1767,20 @@ def artifacts(self, thread: str) -> ThreadArtifactsResponse: Args: thread: Thread ID (`thr_...`). Must be accessible to the authenticated user. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return self._http.request( f"/api/v1/threads/{thread}/artifacts", + query=query, response_type=ThreadArtifactsResponse, ) @@ -1905,11 +1960,12 @@ def search( stored message embeddings by cosine similarity, and `"hybrid"` combines the text and embedding rankings with Reciprocal Rank Fusion (RRF). Only messages visible to the authenticated caller are considered. - Results are intentionally lean: each row contains only a bounded content - snippet, sender identity, and timestamp. Attachments, reactions, ACLs, and - metadata are neither hydrated nor serialized. At most 20 results are - returned. Text results support chronological cursor pagination. Embedding and - hybrid results are relevance-ranked single pages and return null cursors. + Results are intentionally lean: each row contains a bounded content snippet, + sender identity, timestamp, and a small allowlisted structured-post evidence + projection. Attachments, reactions, ACLs, and arbitrary metadata are neither + hydrated nor serialized. At most 20 results are returned. Text results + support chronological cursor pagination. Embedding and hybrid results are + relevance-ranked single pages and return null cursors. Args: thread: Thread ID (`thr_...`). Must be visible to the authenticated caller. diff --git a/src/archastro/platform/v1/resources/users.py b/src/archastro/platform/v1/resources/users.py index e426022..99470c6 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: 1d4ce5e7f7c6 +# Content hash: c897f0ca35c8 from __future__ import annotations @@ -11,10 +11,20 @@ from pydantic import BaseModel, Field from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import CurrentUser from ...types.system import SystemAccessToken from ...types.tasks import Task from ...types.threads import Thread -from ...types.users import User, UserInvite +from ...types.users import User, UserInvite, UserSSHKey + + +class UserSshKeyCreateInput(TypedDict): + "Register an SSH public key" + + label: str + "A label such as `Work laptop`." + public_key: str + "One `ssh-ed25519 ` public key without options or a comment." class UserTaskCreateInputTask(TypedDict, total=False): @@ -24,6 +34,8 @@ class UserTaskCreateInputTask(TypedDict, total=False): "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 @@ -45,7 +57,7 @@ class UserTaskCreateInputTask(TypedDict, total=False): source_type: str | None "Kind of source object (for example `repository`)." status: str | None - 'Initial status for the task. One of `"open"`, `"in_progress"`, or `"done"`. Defaults to `"open"` when omitted.' + '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." thread: str | None @@ -167,6 +179,10 @@ class UserProfileInput(TypedDict, total=False): alias: str | None "Short display alias shown in place of the full name in compact UI contexts." + clear_full_name: bool | None + "Set to true to clear the optional display name. Cannot be combined with full_name." + clear_profile_picture: bool | None + "Set to true to remove the current profile picture. Cannot be combined with profile_picture." full_name: str | None "Updated display name for the user." metadata: dict[str, Any] | None @@ -175,6 +191,48 @@ class UserProfileInput(TypedDict, total=False): "New profile picture to upload as a base64-encoded image. Replaces any existing picture." +class IntegrationAccessTokenResponse(BaseModel): + """ + Successful response + """ + + access_token: str = Field(..., description="GitHub access token. Treat this value as a secret.") + expires_at: datetime | None = Field( + default=None, description="Access-token expiry, or null for non-expiring tokens." + ) + integration: str = Field(..., description="Integration ID (`int_...`).") + provider: str = Field(..., description="Always `github`.") + scopes: list[str] = Field(..., description="Recorded GitHub OAuth scopes.") + token_kind: str = Field( + ..., description="Credential authority model: `oauth` or `github_app_user`." + ) + workspace_key: str | None = Field( + default=None, description="GitHub login associated with the credential." + ) + + +class UserSshKeyListResponseDataItem(BaseModel): + algorithm: str = Field(..., description="SSH algorithm. V1 accepts `ssh-ed25519`.") + created_at: datetime = Field(..., description="Registration time.") + fingerprint: str = Field(..., description="OpenSSH SHA-256 fingerprint.") + id: str = Field(..., description="Registered SSH key ID (`ssk_...`).") + label: str = Field(..., description="User-visible label for the key.") + revoked_at: datetime | None = Field( + default=None, description="Revocation time, or null while active." + ) + + +class UserSshKeyListResponse(BaseModel): + """ + Successful response + """ + + after_cursor: str | None = None + before_cursor: str | None = None + data: list[UserSshKeyListResponseDataItem] + has_more: bool + + class UserTaskListResponseDataItemCreatedByActorProfilePicture(BaseModel): file: str | None = Field( default=None, @@ -286,6 +344,10 @@ class UserTaskListResponseDataItem(BaseModel): 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 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.", @@ -384,7 +446,7 @@ class UserTaskListResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -539,6 +601,10 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): 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 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.", @@ -637,7 +703,7 @@ class UserTaskBlockerCyclesResponseDataItemTasksItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -792,6 +858,10 @@ class UserTaskReadyResponseDataItemTask(BaseModel): 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 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.", @@ -890,7 +960,7 @@ class UserTaskReadyResponseDataItemTask(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -1056,6 +1126,10 @@ class UserTaskSearchResponseDataItem(BaseModel): 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 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.", @@ -1154,7 +1228,7 @@ class UserTaskSearchResponseDataItem(BaseModel): ) status: str = Field( ..., - description='Current status of the task. One of `"open"`, `"in_progress"`, or `"done"`.', + 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, @@ -1477,6 +1551,24 @@ class UserThreadListResponseDataItemParentMessageAttachmentsItem(BaseModel): ) +class UserThreadListResponseDataItemParentMessageContextItem(BaseModel): + attributes: dict[str, Any] | None = Field( + default=None, + description="Scalar key-value fields describing the context, such as route, repository, or pull-request number.", + ) + content: str | None = Field( + default=None, + description="Optional context body. The model receives it as escaped XML data, not a system instruction.", + ) + title: str | None = Field( + default=None, description="Optional human-readable label for this context block." + ) + type: str = Field( + ..., + description="Stable context kind. Must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, or underscores.", + ) + + class UserThreadListResponseDataItemParentMessageReactionsItem(BaseModel): payload: dict[str, Any] | None = Field( default=None, @@ -1520,6 +1612,10 @@ class UserThreadListResponseDataItemParentMessage(BaseModel): default=None, description="Text content of the message. `null` for messages that contain only attachments.", ) + context: list[UserThreadListResponseDataItemParentMessageContextItem] | None = Field( + default=None, + description="Immutable structured context captured when the message was posted. Each entry has `type`, optional `title` and `content`, and scalar `attributes`. Always present; defaults to an empty array. Context is delivered to agents as escaped XML data, not as system instructions.", + ) created_at: str | None = Field( default=None, description="When the message was posted (ISO 8601)." ) @@ -1902,7 +1998,11 @@ class UserThreadListResponseDataItemParticipatingAgentsItemSourceSolutionCurrent id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -2159,7 +2259,11 @@ class UserThreadListResponseDataItemParticipatingAgentsItemSourceSolutionSolutio id: str = Field(..., description="Solution config ID (`cfg_...`).") image_url: str | None = Field( default=None, - description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image, and always `null` for org-scoped rows the permanent URL is minted for system-scope (catalog) Solutions only.", + description="Absolute URL of the Solution's cover image the bundled asset the body's `image:` field names. A stable, non-expiring capability URL (like `org_logo.url`), safe to hold in caches and OpenGraph tags; it 404s if the Solution stops declaring a cover. `null` when the Solution has no cover image. Org-only rows remain `null`; when a list entry merges an org copy with its system counterpart, it may reuse the system row's public cover URL. Permanent URLs are never minted for private org assets.", + ) + installed_config_ids: list[str] = Field( + ..., + description="Config IDs (`cfg_...`) of this Solution's installations in the viewer's organization. The array is empty when the Solution is visible only from the system catalog.", ) kind: str = Field(..., description='Resource type. Always `"Solution"`.') latest_solution: str | None = Field( @@ -2663,6 +2767,10 @@ class UserArtifactsResponseDataItem(BaseModel): default=None, description="Short-lived signed URL for downloading the current version's file. `null` if no file is attached.", ) + group_key: str | None = Field( + default=None, + description="Optional nonunique, case-sensitive grouping key, limited to 1024 UTF-8 bytes. Null when unset; belongs to the artifact, not a content version.", + ) id: str = Field(..., description="Artifact ID (`art_...`).") image_source: UserArtifactsResponseDataItemImageSource | None = Field( default=None, @@ -2679,6 +2787,10 @@ class UserArtifactsResponseDataItem(BaseModel): default=None, description="Identifier of the sandbox environment associated with this artifact. `null` if not sandbox-scoped.", ) + system: bool | None = Field( + default=None, + description="True when the artifact has no user, team, or agent owner. An organization may own a system artifact.", + ) team: str | None = Field( default=None, description="ID of the team that owns this artifact (`tea_...`). `null` if not team-scoped.", @@ -2727,6 +2839,9 @@ class UserOrgsResponseDataItem(BaseModel): default=None, description='Industry category the organization belongs to, e.g. `"fintech"` or `"healthcare"`. `null` if not set.', ) + kind: Literal["company", "personal"] = Field( + ..., description="Whether this is a company-domain org or a person-owned personal org." + ) name: str | None = Field( default=None, description="Display name of the organization. `null` if the org has not set a name.", @@ -2743,6 +2858,9 @@ class UserOrgsResponseDataItem(BaseModel): default=None, description='Catalog product IDs this organization\'s plan includes, e.g. `["agent-rooms"]`, `["agent-solutions"]`, `["agent-customer-management"]`. Empty when the org has no plan. Clients use this to show which products the org actually has rather than inferring from feature flags. Derived from the org\'s plan, so it reflects what is currently paid for.', ) + owner_user: str | None = Field( + default=None, description="Owner user ID for a personal org. `null` for company orgs." + ) sandbox: str | None = Field( default=None, description="ID of the sandbox environment scoped to this organization (`snd_...`). `null` for organizations in production mode.", @@ -2774,6 +2892,90 @@ class UserOrgsResponse(BaseModel): ) +class AsyncIntegrationResource: + def __init__(self, http: HttpClient): + self._http = http + + async def access_token(self, user: str, provider: str) -> IntegrationAccessTokenResponse: + """ + Retrieve a GitHub integration access token + Returns a valid GitHub access token for one integration owned by the + authenticated user. Expired tokens are refreshed before the response is + returned. + This endpoint accepts the user's own bearer token, or the app's secret key + acting server-to-server for one of its users. Developer, agent, team, and + cross-user viewers cannot retrieve the credential. Responses are marked + `Cache-Control: no-store`. + + Args: + user: User ID (`usr_...`). The authenticated user, or a user of the app whose secret key is presented. + provider: Integration provider. Only `github` is accepted. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/users/{user}/integrations/{provider}/access_token", + response_type=IntegrationAccessTokenResponse, + ) + + +class AsyncUserSshKeyResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list( + self, user: str, *, limit: int | None = None, after_cursor: str | None = None + ) -> UserSshKeyListResponse: + """ + List registered SSH keys + Returns a cursor-paginated page of SSH-key metadata for the authenticated user. + + Args: + user: User ID (`usr_...`) or `me`. + limit: Maximum keys per page. Defaults to 50; maximum is 100. + after_cursor: Opaque cursor for the next page of older keys. + + 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/users/{user}/ssh_keys", + query=query, + response_type=UserSshKeyListResponse, + ) + + async def create(self, user: str, input: UserSshKeyCreateInput) -> UserSSHKey: + """ + Register an SSH public key + Registers one comment-free Ed25519 public key for Git-over-SSH. The key is + encrypted before storage; responses expose only its label, algorithm and + OpenSSH SHA-256 fingerprint. + The caller must be the user named by `user` and must present a first-party + session or a `full_access` personal access token. + + Args: + user: User ID (`usr_...`) or `me`. + input: Request body. + input.label: A label such as `Work laptop`. + input.public_key: One `ssh-ed25519 ` public key without options or a comment. + + Returns: + Metadata for the registered key; no key material. + """ + return await self._http.request( + f"/api/v1/users/{user}/ssh_keys", + method="POST", + body=input, + response_type=UserSSHKey, + ) + + class AsyncUserTaskResource: def __init__(self, http: HttpClient): self._http = http @@ -2819,7 +3021,7 @@ async def list( user: User ID (`usr_...`) for user-scoped tasks. team: Team ID (`tem_...`). Only tasks belonging to this team are returned. org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. - status: Filter tasks by status. One of `"open"`, `"in_progress"`, or `"done"`. Omit to return tasks in all statuses. + 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). @@ -3065,7 +3267,7 @@ async def search( org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. 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"`, or `"done"`. Omit to include all statuses. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. owner_user: Restrict results to tasks assigned to the user with this public ID (`usr_...`). owner_agent: Restrict results to tasks assigned to the agent with this public ID (`agi_...`). priority: Filter results by priority, from 0 (highest) to 4 (lowest). @@ -3275,11 +3477,13 @@ async def delete(self, user: str, token: str) -> SystemAccessToken: class AsyncUserResource: def __init__(self, http: HttpClient): self._http = http + self.integrations = AsyncIntegrationResource(http) + self.ssh_keys = AsyncUserSshKeyResource(http) self.tasks = AsyncUserTaskResource(http) self.threads = AsyncUserThreadResource(http) self.tokens = AsyncTokenResource(http) - async def me(self) -> User: + async def me(self, *, entitlement: list[str] | None = None) -> CurrentUser: """ Retrieve the current user Returns the user associated with the authenticated session or bearer @@ -3290,10 +3494,16 @@ async def me(self) -> User: token is scoped to and their display names enough to establish full session context in a single call. Unauthenticated requests return 401. + Args: + entitlement: Entitlement catalog keys to evaluate. Supplying this expansion additionally requires `entitlements:read`. + Returns: The authenticated user object. """ - return await self._http.request("/api/v1/users/me", response_type=User) + query: dict[str, object] = {} + if entitlement is not None: + query["entitlement"] = entitlement + return await self._http.request("/api/v1/users/me", query=query, response_type=CurrentUser) async def get(self, user: str) -> User: """ @@ -3313,7 +3523,9 @@ async def get(self, user: str) -> User: """ return await self._http.request(f"/api/v1/users/{user}", response_type=User) - async def artifacts(self, user: str) -> UserArtifactsResponse: + async def artifacts( + self, user: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> UserArtifactsResponse: """ List a user's artifacts Returns all artifacts owned by the specified user. Artifacts represent @@ -3328,12 +3540,20 @@ async def artifacts(self, user: str) -> UserArtifactsResponse: Args: user: User ID (`usr_...`). The authenticated user must be this user or have access to their artifacts. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return await self._http.request( f"/api/v1/users/{user}/artifacts", + query=query, response_type=UserArtifactsResponse, ) @@ -3401,6 +3621,8 @@ async def profile(self, user: str, input: UserProfileInput) -> User: user: User ID (`usr_...`) or `"me"` for the authenticated user. input: Request body. input.alias: Short display alias shown in place of the full name in compact UI contexts. + input.clear_full_name: Set to true to clear the optional display name. Cannot be combined with full_name. + input.clear_profile_picture: Set to true to remove the current profile picture. Cannot be combined with profile_picture. input.full_name: Updated display name for the user. input.metadata: Arbitrary key-value metadata to associate with the user. Existing keys are merged; pass `null` for a key to remove it. input.profile_picture: New profile picture to upload as a base64-encoded image. Replaces any existing picture. @@ -3416,6 +3638,90 @@ async def profile(self, user: str, input: UserProfileInput) -> User: ) +class IntegrationResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def access_token(self, user: str, provider: str) -> IntegrationAccessTokenResponse: + """ + Retrieve a GitHub integration access token + Returns a valid GitHub access token for one integration owned by the + authenticated user. Expired tokens are refreshed before the response is + returned. + This endpoint accepts the user's own bearer token, or the app's secret key + acting server-to-server for one of its users. Developer, agent, team, and + cross-user viewers cannot retrieve the credential. Responses are marked + `Cache-Control: no-store`. + + Args: + user: User ID (`usr_...`). The authenticated user, or a user of the app whose secret key is presented. + provider: Integration provider. Only `github` is accepted. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/users/{user}/integrations/{provider}/access_token", + response_type=IntegrationAccessTokenResponse, + ) + + +class UserSshKeyResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list( + self, user: str, *, limit: int | None = None, after_cursor: str | None = None + ) -> UserSshKeyListResponse: + """ + List registered SSH keys + Returns a cursor-paginated page of SSH-key metadata for the authenticated user. + + Args: + user: User ID (`usr_...`) or `me`. + limit: Maximum keys per page. Defaults to 50; maximum is 100. + after_cursor: Opaque cursor for the next page of older keys. + + 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/users/{user}/ssh_keys", + query=query, + response_type=UserSshKeyListResponse, + ) + + def create(self, user: str, input: UserSshKeyCreateInput) -> UserSSHKey: + """ + Register an SSH public key + Registers one comment-free Ed25519 public key for Git-over-SSH. The key is + encrypted before storage; responses expose only its label, algorithm and + OpenSSH SHA-256 fingerprint. + The caller must be the user named by `user` and must present a first-party + session or a `full_access` personal access token. + + Args: + user: User ID (`usr_...`) or `me`. + input: Request body. + input.label: A label such as `Work laptop`. + input.public_key: One `ssh-ed25519 ` public key without options or a comment. + + Returns: + Metadata for the registered key; no key material. + """ + return self._http.request( + f"/api/v1/users/{user}/ssh_keys", + method="POST", + body=input, + response_type=UserSSHKey, + ) + + class UserTaskResource: def __init__(self, http: SyncHttpClient): self._http = http @@ -3461,7 +3767,7 @@ def list( user: User ID (`usr_...`) for user-scoped tasks. team: Team ID (`tem_...`). Only tasks belonging to this team are returned. org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. - status: Filter tasks by status. One of `"open"`, `"in_progress"`, or `"done"`. Omit to return tasks in all statuses. + 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). @@ -3707,7 +4013,7 @@ def search( org: Optional organization (`org_...`) for developer and server-to-server calls. When omitted, the org is taken from the owner principal (team, user, or agent). When set, it must match that principal's org; pass null for an owner outside an organization. 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"`, or `"done"`. Omit to include all statuses. + status: Filter results by status. One of `"open"`, `"in_progress"`, `"in_review"`, or `"done"`. Omit to include all statuses. owner_user: Restrict results to tasks assigned to the user with this public ID (`usr_...`). owner_agent: Restrict results to tasks assigned to the agent with this public ID (`agi_...`). priority: Filter results by priority, from 0 (highest) to 4 (lowest). @@ -3914,11 +4220,13 @@ def delete(self, user: str, token: str) -> SystemAccessToken: class UserResource: def __init__(self, http: SyncHttpClient): self._http = http + self.integrations = IntegrationResource(http) + self.ssh_keys = UserSshKeyResource(http) self.tasks = UserTaskResource(http) self.threads = UserThreadResource(http) self.tokens = TokenResource(http) - def me(self) -> User: + def me(self, *, entitlement: list[str] | None = None) -> CurrentUser: """ Retrieve the current user Returns the user associated with the authenticated session or bearer @@ -3929,10 +4237,16 @@ def me(self) -> User: token is scoped to and their display names enough to establish full session context in a single call. Unauthenticated requests return 401. + Args: + entitlement: Entitlement catalog keys to evaluate. Supplying this expansion additionally requires `entitlements:read`. + Returns: The authenticated user object. """ - return self._http.request("/api/v1/users/me", response_type=User) + query: dict[str, object] = {} + if entitlement is not None: + query["entitlement"] = entitlement + return self._http.request("/api/v1/users/me", query=query, response_type=CurrentUser) def get(self, user: str) -> User: """ @@ -3952,7 +4266,9 @@ def get(self, user: str) -> User: """ return self._http.request(f"/api/v1/users/{user}", response_type=User) - def artifacts(self, user: str) -> UserArtifactsResponse: + def artifacts( + self, user: str, *, group_key: str | None = None, group_key_prefix: str | None = None + ) -> UserArtifactsResponse: """ List a user's artifacts Returns all artifacts owned by the specified user. Artifacts represent @@ -3967,12 +4283,20 @@ def artifacts(self, user: str) -> UserArtifactsResponse: Args: user: User ID (`usr_...`). The authenticated user must be this user or have access to their artifacts. + group_key: Case-sensitive exact group key. Mutually exclusive with group_key_prefix; null keys do not match. + group_key_prefix: Nonempty case-sensitive literal prefix (percent, underscore and backslash are literal). Mutually exclusive with group_key. Returns: Successful response """ + query: dict[str, object] = {} + if group_key is not None: + query["group_key"] = group_key + if group_key_prefix is not None: + query["group_key_prefix"] = group_key_prefix return self._http.request( f"/api/v1/users/{user}/artifacts", + query=query, response_type=UserArtifactsResponse, ) @@ -4037,6 +4361,8 @@ def profile(self, user: str, input: UserProfileInput) -> User: user: User ID (`usr_...`) or `"me"` for the authenticated user. input: Request body. input.alias: Short display alias shown in place of the full name in compact UI contexts. + input.clear_full_name: Set to true to clear the optional display name. Cannot be combined with full_name. + input.clear_profile_picture: Set to true to remove the current profile picture. Cannot be combined with profile_picture. input.full_name: Updated display name for the user. input.metadata: Arbitrary key-value metadata to associate with the user. Existing keys are merged; pass `null` for a key to remove it. input.profile_picture: New profile picture to upload as a base64-encoded image. Replaces any existing picture. diff --git a/src/archastro/platform/v1/resources/work_items.py b/src/archastro/platform/v1/resources/work_items.py index fa17cf3..f8eab2c 100644 --- a/src/archastro/platform/v1/resources/work_items.py +++ b/src/archastro/platform/v1/resources/work_items.py @@ -1,13 +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: 92e0c4b9c2d0 +# Content hash: 7eaf84b263de from __future__ import annotations from typing import Any, Required, TypedDict from ...runtime.http_client import HttpClient, SyncHttpClient -from ...types.common import WorkflowWorkItemList +from ...types.workflows import WorkflowWorkItemList class WorkItemFailInput(TypedDict): diff --git a/src/archastro/platform/v1/resources/workflows.py b/src/archastro/platform/v1/resources/workflows.py new file mode 100644 index 0000000..ece11ea --- /dev/null +++ b/src/archastro/platform/v1/resources/workflows.py @@ -0,0 +1,591 @@ +# 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 + +from __future__ import annotations + +import builtins +from typing import Any, TypedDict + +from pydantic import BaseModel, Field + +from ...runtime.http_client import HttpClient, SyncHttpClient +from ...types.common import Command, NodeType +from ...types.expressions import ExpressionResult, ExpressionValidation +from ...types.graph import GraphValidation + + +class ExpressionRunInput(TypedDict, total=False): + "Run a workflow expression" + + expression: str | None + "The workflow expression source to evaluate. Defaults to an empty string." + scope: dict[str, Any] | None + "Key-value map of variables available to the expression at evaluation time. Defaults to an empty map." + + +class ExpressionValidateInput(TypedDict, total=False): + "Validate a workflow expression" + + expression: str | None + "The workflow expression source to validate. Defaults to an empty string." + + +class GraphValidateInput(TypedDict): + "Validate a workflow graph" + + graph: dict[str, Any] + "Complete workflow graph definition to validate, containing node and edge declarations." + + +class WorkflowRunInput(TypedDict, total=False): + "Execute a workflow node or graph" + + context: dict[str, Any] | None + "Additional execution context made available to workflow nodes at runtime. Merged with the authenticated viewer context set by the platform. Defaults to an empty object if omitted." + env: dict[str, Any] | None + "Evaluator environment variables injected into expression contexts during execution. The platform automatically merges developer-tier runtime env vars before evaluation. Defaults to an empty object if omitted." + graph: dict[str, Any] | None + "A complete workflow graph to execute. Must be an object conforming to the `WorkflowGraph` schema (keys: `start_node`, `nodes`, etc.). Mutually exclusive with `node` supply exactly one." + node: dict[str, Any] | None + "A single workflow node to execute in isolation. Must be an object with at minimum a `type` key identifying the node kind, plus any node-specific configuration values. Mutually exclusive with `graph` supply exactly one." + payload: dict[str, Any] | None + "Arbitrary input data passed into the workflow as the initial payload. Defaults to an empty object if omitted." + run_as_agent: str | None + "Agent ID (`agt_...`) of the agent to impersonate for the execution. When set, workflow nodes run with this agent as the acting principal instead of the authenticated developer. The agent must belong to the current app. Mutually exclusive with `run_as_user`. `null` by default (runs as the authenticated developer)." + run_as_user: str | None + "User ID (`usr_...`) of the user to impersonate for the execution. When set, workflow nodes run with this user as the acting principal instead of the authenticated developer. Mutually exclusive with `run_as_agent`. `null` by default (runs as the authenticated developer)." + + +class EventSampleResponse(BaseModel): + """ + Successful response + """ + + sample: dict[str, Any] = Field(..., description="Representative event payload.") + + +class WorkflowRunResponse(BaseModel): + """ + Successful response + """ + + context: dict[str, Any] | None = Field(default=None, description="Execution context.") + env: dict[str, Any] | None = Field(default=None, description="Execution environment.") + error: str | None = Field(default=None, description="Execution error message.") + files: list[dict[str, Any]] | None = Field( + default=None, description="Files created during graph execution." + ) + log: list[str] | None = Field(default=None, description="Node execution log lines.") + nextNodeId: str | None = Field( + default=None, description="Next node ID, when execution yielded." + ) + output: Any | None = Field(default=None, description="Node output or graph output lines.") + payload: Any | None = Field( + default=None, description="Final or node-produced JSON-safe payload." + ) + records: list[dict[str, Any]] | None = Field( + default=None, description="Graph execution records." + ) + startNodeId: str | None = Field(default=None, description="Graph start node ID.") + status: str | None = Field(default=None, description="Graph execution status.") + wait: dict[str, Any] | None = Field(default=None, description="Yielded wait state.") + + +class AsyncEventResource: + def __init__(self, http: HttpClient): + self._http = http + + async def sample(self, name: str) -> EventSampleResponse: + """ + Retrieve a sample event payload + Returns a representative sample payload for the specified workflow event type. + Use this to inspect the structure of an event before configuring a workflow + trigger or building an event handler. + Requires a valid developer session. Returns 404 if the event name is not + registered in the platform event catalog. + + Args: + name: Fully-qualified event type name registered in the catalog, e.g. `"thread.created"`. Returns 404 if the name is not recognized. + + Returns: + Successful response + """ + return await self._http.request( + f"/api/v1/workflows/events/{name}/sample", + response_type=EventSampleResponse, + ) + + +class AsyncExpressionResource: + def __init__(self, http: HttpClient): + self._http = http + + async def run(self, input: ExpressionRunInput) -> ExpressionResult: + """ + Run a workflow expression + Evaluates a single workflow expression string against a caller-supplied variable scope + and returns the computed result. Use this endpoint to test and iterate on expressions + during workflow development before embedding them in a workflow graph. + The expression is evaluated synchronously. If evaluation fails (syntax error, runtime + exception, or type mismatch), the endpoint returns a 422 with a human-readable reason + rather than a 200 with an error payload. Successful responses always include any + `println` output captured during evaluation. + Requires an authenticated developer session. The expression runs in an isolated + context and cannot access platform resources beyond what you supply in `scope`. + + Args: + input: Request body. + input.expression: The workflow expression source to evaluate. Defaults to an empty string. + input.scope: Key-value map of variables available to the expression at evaluation time. Defaults to an empty map. + + Returns: + The result of evaluating the expression, including any captured print output. + """ + return await self._http.request( + "/api/v1/workflows/expressions/run", + method="POST", + body=input, + response_type=ExpressionResult, + ) + + async def validate(self, input: ExpressionValidateInput) -> ExpressionValidation: + """ + Validate a workflow expression + Parses and statically analyses a workflow expression string without executing it. + Returns structured findings, inferred symbol types, and any warnings so that editors + and build-time tooling can surface diagnostics before the expression is used in a + live workflow. + The response always includes an `ok` flag indicating whether the validation call + itself succeeded, and a separate `valid` flag indicating whether the expression + passed analysis. A 200 is returned regardless of whether the expression is valid; + a 401 is returned if the caller is not authenticated. + Requires an authenticated developer session. + + Args: + input: Request body. + input.expression: The workflow expression source to validate. Defaults to an empty string. + + Returns: + Validation outcome including parse errors, warnings, symbol types, and structured editor diagnostics. + """ + return await self._http.request( + "/api/v1/workflows/expressions/validate", + method="POST", + body=input, + response_type=ExpressionValidation, + ) + + +class AsyncGraphResource: + def __init__(self, http: HttpClient): + self._http = http + + async def validate(self, input: GraphValidateInput) -> GraphValidation: + """ + Validate a workflow graph + Submits a workflow graph definition for static analysis and returns a + structured validation report. The graph is checked for structural correctness + (valid node and edge references) and then analyzed for logical issues such as + unreachable nodes, dead-end paths, and cycles. + This endpoint is non-destructive it does not persist any data. Use it + during development to surface problems with a graph definition before saving + or executing it. Requires developer-level authentication. + + Args: + input: Request body. + input.graph: Complete workflow graph definition to validate, containing node and edge declarations. + + Returns: + Validation report describing whether the graph is structurally sound and any findings from static analysis. + """ + return await self._http.request( + "/api/v1/workflows/graph/validate", + method="POST", + body=input, + response_type=GraphValidation, + ) + + +class AsyncNodeTypeResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list(self) -> builtins.list[NodeType]: + """ + List workflow node types + Returns an array of all node type definitions registered with the workflow host. Node + types describe the available building blocks for constructing workflow graphs, including + each type's label, category, color, and configurable field definitions. + This endpoint requires a valid authenticated session. It does not support pagination + all registered node types are returned in a single response. The list reflects the + node types available at the time of the request; types are registered at startup and + do not change at runtime. + + Returns: + Array of all registered workflow node type definitions. + """ + return await self._http.request( + "/api/v1/workflows/node_types", + response_type=list[NodeType], + ) + + async def get(self, node_type: str) -> NodeType: + """ + Retrieve a workflow node type + Returns the full definition for a single workflow node type, identified by its + string identifier. The response includes the node type's label, description, category, + display properties, and the complete list of configurable field definitions. + Returns 404 if no node type with the given identifier is registered. This endpoint + requires a valid authenticated session. + + Args: + node_type: Identifier of the node type to retrieve, e.g. `"http_request"` or `"conditional"`. + + Returns: + The requested workflow node type definition. + """ + return await self._http.request( + f"/api/v1/workflows/node_types/{node_type}", + response_type=NodeType, + ) + + +class AsyncSampleResource: + def __init__(self, http: HttpClient): + self._http = http + + async def list(self) -> dict[str, str]: + """ + Retrieve a sample workflow graph + Returns a minimal, runnable `WorkflowGraph` YAML document that illustrates the + structure of a workflow graph. Use this as a starting point when building or + testing your own workflow definitions. + The response is returned as plain YAML (`application/x-yaml`) rather than JSON. + The sample graph includes a trigger node, a transform node, and an event-emit + node wired together in sequence. + + Returns: + Minimal runnable WorkflowGraph YAML document. + """ + return await self._http.request_raw("/api/v1/workflows/sample") + + +class AsyncWorkflowResource: + def __init__(self, http: HttpClient): + self._http = http + self.events = AsyncEventResource(http) + self.expressions = AsyncExpressionResource(http) + self.graph = AsyncGraphResource(http) + self.node_types = AsyncNodeTypeResource(http) + self.sample = AsyncSampleResource(http) + + async def commands(self) -> list[Command]: + """ + List available workflow commands + Returns the full set of workflow commands registered with the workflow host. + Commands represent discrete operations that a workflow node can invoke each + carries its own input and output schemas used by the workflow builder. + This endpoint requires a valid developer session. The list is not paginated; + all registered commands are returned in a single response. + + Returns: + Array of all registered workflow command definitions. + """ + return await self._http.request("/api/v1/workflows/commands", response_type=list[Command]) + + async def llm_txt(self) -> dict[str, str]: + """ + Retrieve the workflow-authoring LLM prompt + Returns the plain-text system prompt used by workflow-authoring assistants. + The prompt is rendered from the registered node types and commands so its + authoring guidance stays synchronized with the running platform. + + Returns: + Plain-text system prompt for workflow-authoring assistants. + """ + return await self._http.request_raw("/api/v1/workflows/llm.txt") + + async def run(self, input: WorkflowRunInput) -> WorkflowRunResponse: + """ + Execute a workflow node or graph + Executes a single workflow node or a complete workflow graph and returns the result. + Supply either `node` (to run one node in isolation) or `graph` (to execute a full + directed graph); the two parameters are mutually exclusive and exactly one must be + provided. + 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. + 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 + message and any partial records produced before the failure. + + Args: + input: Request body. + input.context: Additional execution context made available to workflow nodes at runtime. Merged with the authenticated viewer context set by the platform. Defaults to an empty object if omitted. + input.env: Evaluator environment variables injected into expression contexts during execution. The platform automatically merges developer-tier runtime env vars before evaluation. Defaults to an empty object if omitted. + input.graph: A complete workflow graph to execute. Must be an object conforming to the `WorkflowGraph` schema (keys: `start_node`, `nodes`, etc.). Mutually exclusive with `node` supply exactly one. + input.node: A single workflow node to execute in isolation. Must be an object with at minimum a `type` key identifying the node kind, plus any node-specific configuration values. Mutually exclusive with `graph` supply exactly one. + input.payload: Arbitrary input data passed into the workflow as the initial payload. Defaults to an empty object if omitted. + input.run_as_agent: Agent ID (`agt_...`) of the agent to impersonate for the execution. When set, workflow nodes run with this agent as the acting principal instead of the authenticated developer. The agent must belong to the current app. Mutually exclusive with `run_as_user`. `null` by default (runs as the authenticated developer). + input.run_as_user: User ID (`usr_...`) of the user to impersonate for the execution. When set, workflow nodes run with this user as the acting principal instead of the authenticated developer. Mutually exclusive with `run_as_agent`. `null` by default (runs as the authenticated developer). + + Returns: + Successful response + """ + return await self._http.request( + "/api/v1/workflows/run", + method="POST", + body=input, + response_type=WorkflowRunResponse, + ) + + +class EventResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def sample(self, name: str) -> EventSampleResponse: + """ + Retrieve a sample event payload + Returns a representative sample payload for the specified workflow event type. + Use this to inspect the structure of an event before configuring a workflow + trigger or building an event handler. + Requires a valid developer session. Returns 404 if the event name is not + registered in the platform event catalog. + + Args: + name: Fully-qualified event type name registered in the catalog, e.g. `"thread.created"`. Returns 404 if the name is not recognized. + + Returns: + Successful response + """ + return self._http.request( + f"/api/v1/workflows/events/{name}/sample", + response_type=EventSampleResponse, + ) + + +class ExpressionResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def run(self, input: ExpressionRunInput) -> ExpressionResult: + """ + Run a workflow expression + Evaluates a single workflow expression string against a caller-supplied variable scope + and returns the computed result. Use this endpoint to test and iterate on expressions + during workflow development before embedding them in a workflow graph. + The expression is evaluated synchronously. If evaluation fails (syntax error, runtime + exception, or type mismatch), the endpoint returns a 422 with a human-readable reason + rather than a 200 with an error payload. Successful responses always include any + `println` output captured during evaluation. + Requires an authenticated developer session. The expression runs in an isolated + context and cannot access platform resources beyond what you supply in `scope`. + + Args: + input: Request body. + input.expression: The workflow expression source to evaluate. Defaults to an empty string. + input.scope: Key-value map of variables available to the expression at evaluation time. Defaults to an empty map. + + Returns: + The result of evaluating the expression, including any captured print output. + """ + return self._http.request( + "/api/v1/workflows/expressions/run", + method="POST", + body=input, + response_type=ExpressionResult, + ) + + def validate(self, input: ExpressionValidateInput) -> ExpressionValidation: + """ + Validate a workflow expression + Parses and statically analyses a workflow expression string without executing it. + Returns structured findings, inferred symbol types, and any warnings so that editors + and build-time tooling can surface diagnostics before the expression is used in a + live workflow. + The response always includes an `ok` flag indicating whether the validation call + itself succeeded, and a separate `valid` flag indicating whether the expression + passed analysis. A 200 is returned regardless of whether the expression is valid; + a 401 is returned if the caller is not authenticated. + Requires an authenticated developer session. + + Args: + input: Request body. + input.expression: The workflow expression source to validate. Defaults to an empty string. + + Returns: + Validation outcome including parse errors, warnings, symbol types, and structured editor diagnostics. + """ + return self._http.request( + "/api/v1/workflows/expressions/validate", + method="POST", + body=input, + response_type=ExpressionValidation, + ) + + +class GraphResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def validate(self, input: GraphValidateInput) -> GraphValidation: + """ + Validate a workflow graph + Submits a workflow graph definition for static analysis and returns a + structured validation report. The graph is checked for structural correctness + (valid node and edge references) and then analyzed for logical issues such as + unreachable nodes, dead-end paths, and cycles. + This endpoint is non-destructive it does not persist any data. Use it + during development to surface problems with a graph definition before saving + or executing it. Requires developer-level authentication. + + Args: + input: Request body. + input.graph: Complete workflow graph definition to validate, containing node and edge declarations. + + Returns: + Validation report describing whether the graph is structurally sound and any findings from static analysis. + """ + return self._http.request( + "/api/v1/workflows/graph/validate", + method="POST", + body=input, + response_type=GraphValidation, + ) + + +class NodeTypeResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list(self) -> builtins.list[NodeType]: + """ + List workflow node types + Returns an array of all node type definitions registered with the workflow host. Node + types describe the available building blocks for constructing workflow graphs, including + each type's label, category, color, and configurable field definitions. + This endpoint requires a valid authenticated session. It does not support pagination + all registered node types are returned in a single response. The list reflects the + node types available at the time of the request; types are registered at startup and + do not change at runtime. + + Returns: + Array of all registered workflow node type definitions. + """ + return self._http.request("/api/v1/workflows/node_types", response_type=list[NodeType]) + + def get(self, node_type: str) -> NodeType: + """ + Retrieve a workflow node type + Returns the full definition for a single workflow node type, identified by its + string identifier. The response includes the node type's label, description, category, + display properties, and the complete list of configurable field definitions. + Returns 404 if no node type with the given identifier is registered. This endpoint + requires a valid authenticated session. + + Args: + node_type: Identifier of the node type to retrieve, e.g. `"http_request"` or `"conditional"`. + + Returns: + The requested workflow node type definition. + """ + return self._http.request( + f"/api/v1/workflows/node_types/{node_type}", + response_type=NodeType, + ) + + +class SampleResource: + def __init__(self, http: SyncHttpClient): + self._http = http + + def list(self) -> dict[str, str]: + """ + Retrieve a sample workflow graph + Returns a minimal, runnable `WorkflowGraph` YAML document that illustrates the + structure of a workflow graph. Use this as a starting point when building or + testing your own workflow definitions. + The response is returned as plain YAML (`application/x-yaml`) rather than JSON. + The sample graph includes a trigger node, a transform node, and an event-emit + node wired together in sequence. + + Returns: + Minimal runnable WorkflowGraph YAML document. + """ + return self._http.request_raw("/api/v1/workflows/sample") + + +class WorkflowResource: + def __init__(self, http: SyncHttpClient): + self._http = http + self.events = EventResource(http) + self.expressions = ExpressionResource(http) + self.graph = GraphResource(http) + self.node_types = NodeTypeResource(http) + self.sample = SampleResource(http) + + def commands(self) -> list[Command]: + """ + List available workflow commands + Returns the full set of workflow commands registered with the workflow host. + Commands represent discrete operations that a workflow node can invoke each + carries its own input and output schemas used by the workflow builder. + This endpoint requires a valid developer session. The list is not paginated; + all registered commands are returned in a single response. + + Returns: + Array of all registered workflow command definitions. + """ + return self._http.request("/api/v1/workflows/commands", response_type=list[Command]) + + def llm_txt(self) -> dict[str, str]: + """ + Retrieve the workflow-authoring LLM prompt + Returns the plain-text system prompt used by workflow-authoring assistants. + The prompt is rendered from the registered node types and commands so its + authoring guidance stays synchronized with the running platform. + + Returns: + Plain-text system prompt for workflow-authoring assistants. + """ + return self._http.request_raw("/api/v1/workflows/llm.txt") + + def run(self, input: WorkflowRunInput) -> WorkflowRunResponse: + """ + Execute a workflow node or graph + Executes a single workflow node or a complete workflow graph and returns the result. + Supply either `node` (to run one node in isolation) or `graph` (to execute a full + directed graph); the two parameters are mutually exclusive and exactly one must be + provided. + 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. + 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 + message and any partial records produced before the failure. + + Args: + input: Request body. + input.context: Additional execution context made available to workflow nodes at runtime. Merged with the authenticated viewer context set by the platform. Defaults to an empty object if omitted. + input.env: Evaluator environment variables injected into expression contexts during execution. The platform automatically merges developer-tier runtime env vars before evaluation. Defaults to an empty object if omitted. + input.graph: A complete workflow graph to execute. Must be an object conforming to the `WorkflowGraph` schema (keys: `start_node`, `nodes`, etc.). Mutually exclusive with `node` supply exactly one. + input.node: A single workflow node to execute in isolation. Must be an object with at minimum a `type` key identifying the node kind, plus any node-specific configuration values. Mutually exclusive with `graph` supply exactly one. + input.payload: Arbitrary input data passed into the workflow as the initial payload. Defaults to an empty object if omitted. + input.run_as_agent: Agent ID (`agt_...`) of the agent to impersonate for the execution. When set, workflow nodes run with this agent as the acting principal instead of the authenticated developer. The agent must belong to the current app. Mutually exclusive with `run_as_user`. `null` by default (runs as the authenticated developer). + input.run_as_user: User ID (`usr_...`) of the user to impersonate for the execution. When set, workflow nodes run with this user as the acting principal instead of the authenticated developer. Mutually exclusive with `run_as_agent`. `null` by default (runs as the authenticated developer). + + Returns: + Successful response + """ + return self._http.request( + "/api/v1/workflows/run", + method="POST", + body=input, + response_type=WorkflowRunResponse, + ) diff --git a/tests/contract/channels/test_api_chat_channel.py b/tests/contract/channels/test_api_chat_channel.py index 6398be5..8e482c5 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: a855958b5ea6 +# Content hash: 5df96c308eaf """ Contract tests for ApiChatChannel — generated from the channel spec. @@ -476,6 +476,7 @@ async def test_api_chat_channel_api_chat_post_message_sends_valid_push_and_recei reply = await channel.api_chat_post_message( { "content": "test content", + "context": [{"type": "test"}], "idempotency_key": "test-key", "reply_to": "test-value", "uploads": [{}], @@ -536,7 +537,12 @@ async def test_api_chat_channel_api_chat_post_simple_message_sends_valid_push_an limit=1, ) reply = await channel.api_chat_post_simple_message( - {"content": "test content", "idempotency_key": "test-key", "reply_to": "test-value"} + { + "content": "test content", + "context": [{"type": "test"}], + "idempotency_key": "test-key", + "reply_to": "test-value", + } ) assert reply["status"] == "ok" @@ -823,6 +829,150 @@ async def test_api_chat_channel_api_chat_typing_returns_error_envelope_when_requ assert reply["status"] == "error" +async def test_api_chat_channel_api_chat_publish_local_tools_sends_valid_push_and_receives_contract_valid_reply( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "api:chat:publish_local_tools": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + reply = await channel.api_chat_publish_local_tools( + { + "local_tool_provider_id": "test-id", + "local_tools": [ + { + "function": { + "description": "test description", + "name": "test-name", + "parameters": {}, + }, + "type": "function", + } + ], + } + ) + assert reply["status"] == "ok" + + observed = await client.observations( + "api:chat:team:test-id:thread:test-id", "api:chat:publish_local_tools" + ) + assert len(observed) == 1 + assert observed[0]["params"]["local_tool_provider_id"] == "test-id" + assert observed[0]["params"]["local_tools"] == [ + { + "function": {"description": "test description", "name": "test-name", "parameters": {}}, + "type": "function", + } + ] + + +async def test_api_chat_channel_api_chat_publish_local_tools_returns_error_envelope_when_required_missing( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [{"type": "autoReply"}], + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + reply = await channel.api_chat_publish_local_tools({}) + assert reply["status"] == "error" + + +async def test_api_chat_channel_api_chat_local_tool_result_sends_valid_push_and_receives_contract_valid_reply( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [{"type": "autoReply"}], + "onMessage": { + "api:chat:local_tool_result": [{"type": "autoReply"}], + }, + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + reply = await channel.api_chat_local_tool_result( + { + "agent_id": "test-id", + "generation": 1, + "provider_id": "test-id", + "request_id": "test-id", + "results": [{"call_id": "test-id", "content": "test content", "status": "ok"}], + } + ) + assert reply["status"] == "ok" + + observed = await client.observations( + "api:chat:team:test-id:thread:test-id", "api:chat:local_tool_result" + ) + assert len(observed) == 1 + assert observed[0]["params"]["agent_id"] == "test-id" + assert observed[0]["params"]["generation"] == 1 + assert observed[0]["params"]["provider_id"] == "test-id" + assert observed[0]["params"]["request_id"] == "test-id" + assert observed[0]["params"]["results"] == [ + {"call_id": "test-id", "content": "test content", "status": "ok"} + ] + + +async def test_api_chat_channel_api_chat_local_tool_result_returns_error_envelope_when_required_missing( + rig, +): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [{"type": "autoReply"}], + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + reply = await channel.api_chat_local_tool_result({}) + assert reply["status"] == "error" + + async def test_api_chat_channel_on_message_added_delivers_contract_valid_payloads(rig): client, socket = rig await client.register_scenario( @@ -978,6 +1128,68 @@ def handler(payload): assert payload is not None +async def test_api_chat_channel_on_local_tool_call_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "local_tool_call"}, + ], + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_local_tool_call(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + +async def test_api_chat_channel_on_local_tool_cancelled_delivers_contract_valid_payloads(rig): + client, socket = rig + await client.register_scenario( + { + "topic": "api:chat:team:test-id:thread:test-id", + "onJoin": [ + {"type": "autoReply"}, + {"type": "autoPush", "event": "local_tool_cancelled"}, + ], + } + ) + channel = await ApiChatChannel.join_team_thread( + socket, + "test-id", + "test-id", + after_cursor="test-value", + before_cursor="test-value", + include_metadata=True, + limit=1, + ) + future: asyncio.Future = asyncio.get_event_loop().create_future() + + def handler(payload): + if not future.done(): + future.set_result(payload) + + channel.on_local_tool_cancelled(handler) + payload = await asyncio.wait_for(future, timeout=1.0) + assert payload is not None + + async def test_api_chat_channel_leave_leaves_cleanly_through_generated_leave(rig): client, socket = rig await client.register_scenario( diff --git a/tests/contract/v1/test_ai.py b/tests/contract/v1/test_ai.py index ead5ba9..7af3f99 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: 256a7e298b64 +# Content hash: 9b2b00417d06 import pytest from pydantic import BaseModel @@ -403,6 +403,21 @@ def test_ai_image_edits_error_401(): ec.close() +def test_ai_image_edits_error_402(): + ec = _error_client(402) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ai.image.edits( + { + "images": [{"image_data": "test-value", "image_type": "test-value"}], + "prompt": "test-value", + } + ) + assert exc_info.value.status == 402 + finally: + ec.close() + + def test_ai_image_edits_error_422(): ec = _error_client(422) try: @@ -466,6 +481,22 @@ async def test_async_ai_image_edits_error_401(): await ec.close() +@pytest.mark.asyncio +async def test_async_ai_image_edits_error_402(): + ec = _async_error_client(402) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ai.image.edits( + { + "images": [{"image_data": "test-value", "image_type": "test-value"}], + "prompt": "test-value", + } + ) + assert exc_info.value.status == 402 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_ai_image_edits_error_422(): ec = _async_error_client(422) @@ -512,6 +543,16 @@ def test_ai_image_generations_error_401(): ec.close() +def test_ai_image_generations_error_402(): + ec = _error_client(402) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ai.image.generations({"prompt": "test-value"}) + assert exc_info.value.status == 402 + finally: + ec.close() + + def test_ai_image_generations_error_422(): ec = _error_client(422) try: @@ -555,6 +596,17 @@ async def test_async_ai_image_generations_error_401(): await ec.close() +@pytest.mark.asyncio +async def test_async_ai_image_generations_error_402(): + ec = _async_error_client(402) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ai.image.generations({"prompt": "test-value"}) + assert exc_info.value.status == 402 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_ai_image_generations_error_422(): ec = _async_error_client(422) diff --git a/tests/contract/v1/test_artifacts.py b/tests/contract/v1/test_artifacts.py index b57a30f..2b7a53c 100644 --- a/tests/contract/v1/test_artifacts.py +++ b/tests/contract/v1/test_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: 52ce08b3232d +# Content hash: ad22f699f41f import pytest from pydantic import BaseModel @@ -43,6 +43,90 @@ def _async_error_client(code: int) -> AsyncPlatformClient: ) +def test_artifacts_create_success(): + client = _client() + try: + result = client.v1.artifacts.create({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Artifact" + finally: + client.close() + + +def test_artifacts_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.artifacts.create({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_artifacts_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.artifacts.create({}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_artifacts_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.artifacts.create({}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_artifacts_create_success(): + client = _async_client() + try: + result = await client.v1.artifacts.create({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "Artifact" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_artifacts_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.artifacts.create({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_artifacts_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.artifacts.create({}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_artifacts_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.artifacts.create({}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_artifacts_delete_success(): client = _client() try: diff --git a/tests/contract/v1/test_external_object_capabilities.py b/tests/contract/v1/test_external_object_capabilities.py new file mode 100644 index 0000000..9bf77fc --- /dev/null +++ b/tests/contract/v1/test_external_object_capabilities.py @@ -0,0 +1,265 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 10a746599b5b + +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_external_object_capabilities_remove_success(): + client = _client() + try: + result = client.v1.external_object_capabilities.remove() + assert result is None + finally: + client.close() + + +def test_external_object_capabilities_remove_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_object_capabilities_remove_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_external_object_capabilities_remove_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_remove_success(): + client = _async_client() + try: + result = await client.v1.external_object_capabilities.remove() + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_remove_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_remove_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_remove_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.remove() + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_external_object_capabilities_create_success(): + client = _client() + try: + result = client.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "RuntimeCapability" + finally: + client.close() + + +def test_external_object_capabilities_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.create({"bindings": {}, "subject": "test-value"}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_object_capabilities_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.create({"bindings": {}, "subject": "test-value"}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_external_object_capabilities_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.create({"bindings": {}, "subject": "test-value"}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_external_object_capabilities_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.create({"bindings": {}, "subject": "test-value"}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_external_object_capabilities_create_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_object_capabilities.create({"bindings": {}, "subject": "test-value"}) + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_success(): + client = _async_client() + try: + result = await client.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "RuntimeCapability" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_object_capabilities_create_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_object_capabilities.create( + {"bindings": {}, "subject": "test-value"} + ) + assert exc_info.value.status == 502 + finally: + await ec.close() diff --git a/tests/contract/v1/test_external_objects.py b/tests/contract/v1/test_external_objects.py new file mode 100644 index 0000000..f1116ca --- /dev/null +++ b/tests/contract/v1/test_external_objects.py @@ -0,0 +1,666 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 3ba321b53d05 + +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_external_objects_list_success(): + client = _client() + try: + result = client.v1.external_objects.list() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObjectListResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_external_objects_list_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.list() + assert exc_info.value.status == 400 + finally: + ec.close() + + +def test_external_objects_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.list() + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_objects_list_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.list() + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_list_success(): + client = _async_client() + try: + result = await client.v1.external_objects.list() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObjectListResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_list_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.list() + assert exc_info.value.status == 400 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.list() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_list_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.list() + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_external_objects_create_success(): + client = _client() + try: + result = client.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + client.close() + + +def test_external_objects_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_objects_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_external_objects_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_external_objects_create_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_external_objects_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_external_objects_create_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_success(): + client = _async_client() + try: + result = await client.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_create_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.create( + {"object": {"name": "test-name", "type": "r2_bucket"}, "service": "cloudflare"} + ) + assert exc_info.value.status == 502 + finally: + await ec.close() + + +def test_external_objects_delete_success(): + client = _client() + try: + result = client.v1.external_objects.delete("test-value") + assert result is None + finally: + client.close() + + +def test_external_objects_delete_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_objects_delete_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_external_objects_delete_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_external_objects_delete_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_external_objects_delete_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_success(): + client = _async_client() + try: + result = await client.v1.external_objects.delete("test-value") + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_delete_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.delete("test-value") + assert exc_info.value.status == 502 + finally: + await ec.close() + + +def test_external_objects_get_success(): + client = _client() + try: + result = client.v1.external_objects.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + client.close() + + +def test_external_objects_get_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.get("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_objects_get_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.get("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_get_success(): + client = _async_client() + try: + result = await client.v1.external_objects.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_get_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.get("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_get_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.get("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +def test_external_objects_update_success(): + client = _client() + try: + result = client.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + client.close() + + +def test_external_objects_update_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_external_objects_update_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_external_objects_update_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_external_objects_update_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_external_objects_update_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_external_objects_update_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_success(): + client = _async_client() + try: + result = await client.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExternalObject" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_external_objects_update_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.external_objects.update( + "test-value", {"object": {"storage_class": "test-value", "type": "r2_bucket"}} + ) + assert exc_info.value.status == 502 + finally: + await ec.close() diff --git a/tests/contract/v1/test_knowledge_sources.py b/tests/contract/v1/test_knowledge_sources.py index cb04841..a447829 100644 --- a/tests/contract/v1/test_knowledge_sources.py +++ b/tests/contract/v1/test_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: 7abc9cc5fbcf +# Content hash: 095c545c0847 import pytest from pydantic import BaseModel @@ -631,6 +631,113 @@ async def test_async_knowledge_sources_ingest_error_429(): await ec.close() +def test_knowledge_sources_search_success(): + client = _client() + try: + result = client.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "KnowledgeSourceSearchResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_knowledge_sources_search_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_knowledge_sources_search_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_knowledge_sources_search_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_knowledge_sources_search_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_knowledge_sources_search_success(): + client = _async_client() + try: + result = await client.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "KnowledgeSourceSearchResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_knowledge_sources_search_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_knowledge_sources_search_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_knowledge_sources_search_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_knowledge_sources_search_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.knowledge_sources.search("test-value", {"query": "test-value"}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_knowledge_sources_kinds_list_success(): client = _client() try: diff --git a/tests/contract/v1/test_orgs.py b/tests/contract/v1/test_orgs.py index a3d56b5..66ddfa2 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: 7e6fcb480e7e +# Content hash: 651467deec13 import pytest from pydantic import BaseModel @@ -106,3 +106,110 @@ async def test_async_orgs_list_error_403(): assert exc_info.value.status == 403 finally: await ec.close() + + +def test_orgs_artifacts_success(): + client = _client() + try: + result = client.v1.orgs.artifacts("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgArtifactsResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_orgs_artifacts_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + ec.close() + + +def test_orgs_artifacts_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_orgs_artifacts_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_orgs_artifacts_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_artifacts_success(): + client = _async_client() + try: + result = await client.v1.orgs.artifacts("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "OrgArtifactsResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_orgs_artifacts_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_artifacts_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_artifacts_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_orgs_artifacts_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.orgs.artifacts("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() diff --git a/tests/contract/v1/test_scripts.py b/tests/contract/v1/test_scripts.py new file mode 100644 index 0000000..84aeb35 --- /dev/null +++ b/tests/contract/v1/test_scripts.py @@ -0,0 +1,400 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 72a06dbae6eb + +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_scripts_language_success(): + client = _client() + try: + result = client.v1.scripts.language() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptLanguageSpec" + finally: + client.close() + + +def test_scripts_language_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.language() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_language_success(): + client = _async_client() + try: + result = await client.v1.scripts.language() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptLanguageSpec" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_language_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.language() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_scripts_llm_txt_success(): + client = _client() + try: + result = client.v1.scripts.llm_txt() + assert result["content"] is not None + assert result["mime_type"] + finally: + client.close() + + +def test_scripts_llm_txt_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.llm_txt() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_llm_txt_success(): + client = _async_client() + try: + result = await client.v1.scripts.llm_txt() + assert result["content"] is not None + assert result["mime_type"] + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_llm_txt_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.llm_txt() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_scripts_run_success(): + client = _client() + try: + result = client.v1.scripts.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptRunResult" + finally: + client.close() + + +def test_scripts_run_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.run({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_scripts_run_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.run({}) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_scripts_run_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.run({}) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_scripts_run_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.run({}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_run_success(): + client = _async_client() + try: + result = await client.v1.scripts.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptRunResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_run_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.run({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_run_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.run({}) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_run_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.run({}) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_run_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.run({}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_scripts_runtime_env_vars_success(): + client = _client() + try: + result = client.v1.scripts.runtime_env_vars() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "RuntimeEnvVarList" + finally: + client.close() + + +def test_scripts_runtime_env_vars_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.runtime_env_vars() + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_scripts_runtime_env_vars_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.runtime_env_vars() + assert exc_info.value.status == 403 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_runtime_env_vars_success(): + client = _async_client() + try: + result = await client.v1.scripts.runtime_env_vars() + assert isinstance(result, BaseModel) + assert type(result).__name__ == "RuntimeEnvVarList" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_runtime_env_vars_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.runtime_env_vars() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_runtime_env_vars_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.runtime_env_vars() + assert exc_info.value.status == 403 + finally: + await ec.close() + + +def test_scripts_test_success(): + client = _client() + try: + result = client.v1.scripts.test({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptTestRunResult" + finally: + client.close() + + +def test_scripts_test_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.test({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_scripts_test_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.test({}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_test_success(): + client = _async_client() + try: + result = await client.v1.scripts.test({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ScriptTestRunResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_test_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.test({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_test_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.test({}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_scripts_validate_success(): + client = _client() + try: + result = client.v1.scripts.validate({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionValidation" + finally: + client.close() + + +def test_scripts_validate_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.scripts.validate({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_scripts_validate_success(): + client = _async_client() + try: + result = await client.v1.scripts.validate({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionValidation" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_scripts_validate_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.scripts.validate({}) + assert exc_info.value.status == 401 + finally: + await ec.close() diff --git a/tests/contract/v1/test_slack_channel_bindings.py b/tests/contract/v1/test_slack_channel_bindings.py index 8d21fd6..aad8005 100644 --- a/tests/contract/v1/test_slack_channel_bindings.py +++ b/tests/contract/v1/test_slack_channel_bindings.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: d385d1360ea4 +# Content hash: 8244287b712f import pytest from pydantic import BaseModel @@ -372,6 +372,181 @@ async def test_async_slack_channel_bindings_create_error_422(): await ec.close() +def test_slack_channel_bindings_assign_success(): + client = _client() + try: + result = client.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "SlackChannelBinding" + finally: + client.close() + + +def test_slack_channel_bindings_assign_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 400 + finally: + ec.close() + + +def test_slack_channel_bindings_assign_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_slack_channel_bindings_assign_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_slack_channel_bindings_assign_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_slack_channel_bindings_assign_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_slack_channel_bindings_assign_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_success(): + client = _async_client() + try: + result = await client.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "SlackChannelBinding" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 400 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_slack_channel_bindings_assign_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.slack_channel_bindings.assign( + {"agent_user_id": "test-id", "channel_id": "test-id", "slack_team_id": "test-id"} + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_slack_channel_bindings_provision_success(): client = _client() try: diff --git a/tests/contract/v1/test_ssh_keys.py b/tests/contract/v1/test_ssh_keys.py new file mode 100644 index 0000000..e4fc73d --- /dev/null +++ b/tests/contract/v1/test_ssh_keys.py @@ -0,0 +1,124 @@ +# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. +# This file is auto-generated by @archastro/sdk-generator. Do not edit. +# Content hash: 6148aec2b43d + +import pytest + +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_ssh_keys_delete_success(): + client = _client() + try: + result = client.v1.ssh_keys.delete("test-key") + assert result is None + finally: + client.close() + + +def test_ssh_keys_delete_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_ssh_keys_delete_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_ssh_keys_delete_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_ssh_keys_delete_success(): + client = _async_client() + try: + result = await client.v1.ssh_keys.delete("test-key") + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_ssh_keys_delete_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_ssh_keys_delete_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_ssh_keys_delete_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.ssh_keys.delete("test-key") + assert exc_info.value.status == 404 + finally: + await ec.close() diff --git a/tests/contract/v1/test_tasks.py b/tests/contract/v1/test_tasks.py index 232b4f3..2d94993 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: 7d959fa5c972 +# Content hash: bd53049ebaa4 import pytest from pydantic import BaseModel @@ -72,6 +72,16 @@ def test_tasks_delete_error_404(): ec.close() +def test_tasks_delete_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.delete("test-value") + assert exc_info.value.status == 409 + finally: + ec.close() + + def test_tasks_delete_error_422(): ec = _error_client(422) try: @@ -114,6 +124,17 @@ async def test_async_tasks_delete_error_404(): await ec.close() +@pytest.mark.asyncio +async def test_async_tasks_delete_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.delete("test-value") + assert exc_info.value.status == 409 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_tasks_delete_error_422(): ec = _async_error_client(422) @@ -1902,6 +1923,92 @@ async def test_async_tasks_links_remove_error_502(): await ec.close() +def test_tasks_links_list_success(): + client = _client() + try: + result = client.v1.tasks.links.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "LinkListResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_tasks_links_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_tasks_links_list_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_tasks_links_list_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_links_list_success(): + client = _async_client() + try: + result = await client.v1.tasks.links.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "LinkListResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_tasks_links_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_links_list_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_tasks_links_list_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.tasks.links.list("test-value") + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_tasks_links_create_success(): client = _client() try: @@ -1909,7 +2016,8 @@ def test_tasks_links_create_success(): "test-value", {"external_scope": "test-value", "object_id": "test-id", "object_type": "test-value"}, ) - assert result is not None + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskExternalLink" finally: client.close() @@ -1990,7 +2098,8 @@ async def test_async_tasks_links_create_success(): "test-value", {"external_scope": "test-value", "object_id": "test-id", "object_type": "test-value"}, ) - assert result is not None + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TaskExternalLink" finally: await client.close() diff --git a/tests/contract/v1/test_team_memberships.py b/tests/contract/v1/test_team_memberships.py index 9b1dd46..eddc45b 100644 --- a/tests/contract/v1/test_team_memberships.py +++ b/tests/contract/v1/test_team_memberships.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: 757b23a687d4 +# Content hash: f2354b149c10 import pytest from pydantic import BaseModel @@ -145,6 +145,16 @@ def test_team_memberships_delete_error_404(): ec.close() +def test_team_memberships_delete_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.team_memberships.delete("test-value") + assert exc_info.value.status == 409 + finally: + ec.close() + + @pytest.mark.asyncio async def test_async_team_memberships_delete_success(): client = _async_client() @@ -186,3 +196,14 @@ async def test_async_team_memberships_delete_error_404(): assert exc_info.value.status == 404 finally: await ec.close() + + +@pytest.mark.asyncio +async def test_async_team_memberships_delete_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.team_memberships.delete("test-value") + assert exc_info.value.status == 409 + finally: + await ec.close() diff --git a/tests/contract/v1/test_teams.py b/tests/contract/v1/test_teams.py index 7b8820d..0613257 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: 2b5338fb5372 +# Content hash: ee5ab8a6f480 import pytest from pydantic import BaseModel @@ -600,6 +600,16 @@ def test_teams_artifacts_success(): client.close() +def test_teams_artifacts_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + ec.close() + + def test_teams_artifacts_error_401(): ec = _error_client(401) try: @@ -633,106 +643,33 @@ async def test_async_teams_artifacts_success(): @pytest.mark.asyncio -async def test_async_teams_artifacts_error_401(): - ec = _async_error_client(401) - try: - with pytest.raises(ApiError) as exc_info: - await ec.v1.teams.artifacts("test-value") - assert exc_info.value.status == 401 - finally: - await ec.close() - - -@pytest.mark.asyncio -async def test_async_teams_artifacts_error_404(): - ec = _async_error_client(404) +async def test_async_teams_artifacts_error_400(): + ec = _async_error_client(400) try: with pytest.raises(ApiError) as exc_info: await ec.v1.teams.artifacts("test-value") - assert exc_info.value.status == 404 + assert exc_info.value.status == 400 finally: await ec.close() -def test_teams_invite_success(): - client = _client() - try: - result = client.v1.teams.invite("test-value") - assert isinstance(result, BaseModel) - assert type(result).__name__ == "TeamInvite" - finally: - client.close() - - -def test_teams_invite_error_401(): - ec = _error_client(401) - try: - with pytest.raises(ApiError) as exc_info: - ec.v1.teams.invite("test-value") - assert exc_info.value.status == 401 - finally: - ec.close() - - -def test_teams_invite_error_403(): - ec = _error_client(403) - try: - with pytest.raises(ApiError) as exc_info: - ec.v1.teams.invite("test-value") - assert exc_info.value.status == 403 - finally: - ec.close() - - -def test_teams_invite_error_404(): - ec = _error_client(404) - try: - with pytest.raises(ApiError) as exc_info: - ec.v1.teams.invite("test-value") - assert exc_info.value.status == 404 - finally: - ec.close() - - -@pytest.mark.asyncio -async def test_async_teams_invite_success(): - client = _async_client() - try: - result = await client.v1.teams.invite("test-value") - assert isinstance(result, BaseModel) - assert type(result).__name__ == "TeamInvite" - finally: - await client.close() - - @pytest.mark.asyncio -async def test_async_teams_invite_error_401(): +async def test_async_teams_artifacts_error_401(): ec = _async_error_client(401) try: with pytest.raises(ApiError) as exc_info: - await ec.v1.teams.invite("test-value") + await ec.v1.teams.artifacts("test-value") assert exc_info.value.status == 401 finally: await ec.close() @pytest.mark.asyncio -async def test_async_teams_invite_error_403(): - ec = _async_error_client(403) - try: - with pytest.raises(ApiError) as exc_info: - await ec.v1.teams.invite("test-value") - assert exc_info.value.status == 403 - finally: - await ec.close() - - -@pytest.mark.asyncio -async def test_async_teams_invite_error_404(): +async def test_async_teams_artifacts_error_404(): ec = _async_error_client(404) try: with pytest.raises(ApiError) as exc_info: - await ec.v1.teams.invite("test-value") + await ec.v1.teams.artifacts("test-value") assert exc_info.value.status == 404 finally: await ec.close() @@ -1244,6 +1181,172 @@ async def test_async_teams_custom_objects_create_error_422(): await ec.close() +def test_teams_invite_create_success(): + client = _client() + try: + result = client.v1.teams.invite.create("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TeamInvite" + finally: + client.close() + + +def test_teams_invite_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_teams_invite_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_teams_invite_create_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_create_success(): + client = _async_client() + try: + result = await client.v1.teams.invite.create("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "TeamInvite" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_create_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.create("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +def test_teams_invite_delete_success(): + client = _client() + try: + result = client.v1.teams.invite.delete("test-value", "test-value") + assert result is None + finally: + client.close() + + +def test_teams_invite_delete_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_teams_invite_delete_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_teams_invite_delete_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_delete_success(): + client = _async_client() + try: + result = await client.v1.teams.invite.delete("test-value", "test-value") + assert result is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_delete_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_delete_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_teams_invite_delete_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.invite.delete("test-value", "test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + def test_teams_members_remove_success(): client = _client() try: @@ -1293,6 +1396,16 @@ def test_teams_members_remove_error_404(): ec.close() +def test_teams_members_remove_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.members.remove("test-value") + assert exc_info.value.status == 409 + finally: + ec.close() + + @pytest.mark.asyncio async def test_async_teams_members_remove_success(): client = _async_client() @@ -1347,6 +1460,17 @@ async def test_async_teams_members_remove_error_404(): await ec.close() +@pytest.mark.asyncio +async def test_async_teams_members_remove_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.members.remove("test-value") + assert exc_info.value.status == 409 + finally: + await ec.close() + + def test_teams_members_list_success(): client = _client() try: @@ -1738,6 +1862,16 @@ def test_teams_tasks_create_error_404(): ec.close() +def test_teams_tasks_create_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.teams.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 409 + finally: + ec.close() + + def test_teams_tasks_create_error_422(): ec = _error_client(422) try: @@ -1781,6 +1915,17 @@ async def test_async_teams_tasks_create_error_404(): await ec.close() +@pytest.mark.asyncio +async def test_async_teams_tasks_create_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.teams.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_teams_tasks_create_error_422(): ec = _async_error_client(422) diff --git a/tests/contract/v1/test_threads.py b/tests/contract/v1/test_threads.py index 0723fd6..4d062ae 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: 42fb0c5c2ab6 +# Content hash: 65b65fcc98bb import pytest from pydantic import BaseModel @@ -432,6 +432,16 @@ def test_threads_artifacts_success(): client.close() +def test_threads_artifacts_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.threads.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + ec.close() + + def test_threads_artifacts_error_401(): ec = _error_client(401) try: @@ -474,6 +484,17 @@ async def test_async_threads_artifacts_success(): await client.close() +@pytest.mark.asyncio +async def test_async_threads_artifacts_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.threads.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_threads_artifacts_error_401(): ec = _async_error_client(401) diff --git a/tests/contract/v1/test_users.py b/tests/contract/v1/test_users.py index 5a6a58c..2474673 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: 68e4b3af77bf +# Content hash: 4f47fc4db593 import pytest from pydantic import BaseModel @@ -48,11 +48,21 @@ def test_users_me_success(): try: result = client.v1.users.me() assert isinstance(result, BaseModel) - assert type(result).__name__ == "User" + assert type(result).__name__ == "CurrentUser" finally: client.close() +def test_users_me_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.me() + assert exc_info.value.status == 400 + finally: + ec.close() + + def test_users_me_error_401(): ec = _error_client(401) try: @@ -63,17 +73,38 @@ def test_users_me_error_401(): ec.close() +def test_users_me_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.me() + assert exc_info.value.status == 403 + finally: + ec.close() + + @pytest.mark.asyncio async def test_async_users_me_success(): client = _async_client() try: result = await client.v1.users.me() assert isinstance(result, BaseModel) - assert type(result).__name__ == "User" + assert type(result).__name__ == "CurrentUser" finally: await client.close() +@pytest.mark.asyncio +async def test_async_users_me_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.me() + assert exc_info.value.status == 400 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_users_me_error_401(): ec = _async_error_client(401) @@ -85,6 +116,17 @@ async def test_async_users_me_error_401(): await ec.close() +@pytest.mark.asyncio +async def test_async_users_me_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.me() + assert exc_info.value.status == 403 + finally: + await ec.close() + + def test_users_get_success(): client = _client() try: @@ -159,6 +201,16 @@ def test_users_artifacts_success(): client.close() +def test_users_artifacts_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + ec.close() + + def test_users_artifacts_error_401(): ec = _error_client(401) try: @@ -191,6 +243,17 @@ async def test_async_users_artifacts_success(): await client.close() +@pytest.mark.asyncio +async def test_async_users_artifacts_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.artifacts("test-value") + assert exc_info.value.status == 400 + finally: + await ec.close() + + @pytest.mark.asyncio async def test_async_users_artifacts_error_401(): ec = _async_error_client(401) @@ -446,6 +509,339 @@ async def test_async_users_profile_error_422(): await ec.close() +def test_users_integrations_access_token_success(): + client = _client() + try: + result = client.v1.users.integrations.access_token("test-value", "test-id") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "IntegrationAccessTokenResponse" + finally: + client.close() + + +def test_users_integrations_access_token_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_users_integrations_access_token_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_users_integrations_access_token_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 404 + finally: + ec.close() + + +def test_users_integrations_access_token_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 409 + finally: + ec.close() + + +def test_users_integrations_access_token_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 422 + finally: + ec.close() + + +def test_users_integrations_access_token_error_502(): + ec = _error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 502 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_success(): + client = _async_client() + try: + result = await client.v1.users.integrations.access_token("test-value", "test-id") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "IntegrationAccessTokenResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 409 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 422 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_integrations_access_token_error_502(): + ec = _async_error_client(502) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.integrations.access_token("test-value", "test-id") + assert exc_info.value.status == 502 + finally: + await ec.close() + + +def test_users_ssh_keys_list_success(): + client = _client() + try: + result = client.v1.users.ssh_keys.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserSshKeyListResponse" + assert isinstance(result.data, list) + finally: + client.close() + + +def test_users_ssh_keys_list_error_400(): + ec = _error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 400 + finally: + ec.close() + + +def test_users_ssh_keys_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_users_ssh_keys_list_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 403 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_list_success(): + client = _async_client() + try: + result = await client.v1.users.ssh_keys.list("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserSshKeyListResponse" + assert isinstance(result.data, list) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_list_error_400(): + ec = _async_error_client(400) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 400 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_list_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.list("test-value") + assert exc_info.value.status == 403 + finally: + await ec.close() + + +def test_users_ssh_keys_create_success(): + client = _client() + try: + result = client.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserSSHKey" + finally: + client.close() + + +def test_users_ssh_keys_create_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_users_ssh_keys_create_error_403(): + ec = _error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 403 + finally: + ec.close() + + +def test_users_ssh_keys_create_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_create_success(): + client = _async_client() + try: + result = await client.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "UserSSHKey" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_create_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_create_error_403(): + ec = _async_error_client(403) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 403 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_users_ssh_keys_create_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.ssh_keys.create( + "test-value", {"label": "test-value", "public_key": "test-key"} + ) + assert exc_info.value.status == 422 + finally: + await ec.close() + + def test_users_tasks_list_success(): client = _client() try: @@ -562,6 +958,16 @@ def test_users_tasks_create_error_404(): ec.close() +def test_users_tasks_create_error_409(): + ec = _error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.users.tasks.create("test-value", {"task": {"name": "test-name"}}) + assert exc_info.value.status == 409 + finally: + ec.close() + + def test_users_tasks_create_error_422(): ec = _error_client(422) try: @@ -605,6 +1011,17 @@ async def test_async_users_tasks_create_error_404(): await ec.close() +@pytest.mark.asyncio +async def test_async_users_tasks_create_error_409(): + ec = _async_error_client(409) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.users.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_users_tasks_create_error_422(): ec = _async_error_client(422) diff --git a/tests/contract/v1/test_workflows.py b/tests/contract/v1/test_workflows.py new file mode 100644 index 0000000..3b21900 --- /dev/null +++ b/tests/contract/v1/test_workflows.py @@ -0,0 +1,547 @@ +# 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 + +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_workflows_commands_success(): + client = _client() + try: + result = client.v1.workflows.commands() + assert isinstance(result, list) + assert all(type(item).__name__ == "Command" for item in result) + finally: + client.close() + + +def test_workflows_commands_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.commands() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_commands_success(): + client = _async_client() + try: + result = await client.v1.workflows.commands() + assert isinstance(result, list) + assert all(type(item).__name__ == "Command" for item in result) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_commands_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.commands() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_workflows_llm_txt_success(): + client = _client() + try: + result = client.v1.workflows.llm_txt() + assert result["content"] is not None + assert result["mime_type"] + finally: + client.close() + + +def test_workflows_llm_txt_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.llm_txt() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_llm_txt_success(): + client = _async_client() + try: + result = await client.v1.workflows.llm_txt() + assert result["content"] is not None + assert result["mime_type"] + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_llm_txt_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.llm_txt() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_workflows_run_success(): + client = _client() + try: + result = client.v1.workflows.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "WorkflowRunResponse" + finally: + client.close() + + +def test_workflows_run_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.run({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_workflows_run_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.run({}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_run_success(): + client = _async_client() + try: + result = await client.v1.workflows.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "WorkflowRunResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_run_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.run({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_run_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.run({}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_workflows_events_sample_success(): + client = _client() + try: + result = client.v1.workflows.events.sample("test-name") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "EventSampleResponse" + finally: + client.close() + + +def test_workflows_events_sample_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.events.sample("test-name") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_workflows_events_sample_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.events.sample("test-name") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_events_sample_success(): + client = _async_client() + try: + result = await client.v1.workflows.events.sample("test-name") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "EventSampleResponse" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_events_sample_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.events.sample("test-name") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_events_sample_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.events.sample("test-name") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +def test_workflows_expressions_run_success(): + client = _client() + try: + result = client.v1.workflows.expressions.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionResult" + finally: + client.close() + + +def test_workflows_expressions_run_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.expressions.run({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_workflows_expressions_run_error_422(): + ec = _error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.expressions.run({}) + assert exc_info.value.status == 422 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_expressions_run_success(): + client = _async_client() + try: + result = await client.v1.workflows.expressions.run({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionResult" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_expressions_run_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.expressions.run({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_expressions_run_error_422(): + ec = _async_error_client(422) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.expressions.run({}) + assert exc_info.value.status == 422 + finally: + await ec.close() + + +def test_workflows_expressions_validate_success(): + client = _client() + try: + result = client.v1.workflows.expressions.validate({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionValidation" + finally: + client.close() + + +def test_workflows_expressions_validate_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.expressions.validate({}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_expressions_validate_success(): + client = _async_client() + try: + result = await client.v1.workflows.expressions.validate({}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "ExpressionValidation" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_expressions_validate_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.expressions.validate({}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_workflows_graph_validate_success(): + client = _client() + try: + result = client.v1.workflows.graph.validate({"graph": {}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "GraphValidation" + finally: + client.close() + + +def test_workflows_graph_validate_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.graph.validate({"graph": {}}) + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_graph_validate_success(): + client = _async_client() + try: + result = await client.v1.workflows.graph.validate({"graph": {}}) + assert isinstance(result, BaseModel) + assert type(result).__name__ == "GraphValidation" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_graph_validate_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.graph.validate({"graph": {}}) + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_workflows_node_types_list_success(): + client = _client() + try: + result = client.v1.workflows.node_types.list() + assert isinstance(result, list) + assert all(type(item).__name__ == "NodeType" for item in result) + finally: + client.close() + + +def test_workflows_node_types_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.node_types.list() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_node_types_list_success(): + client = _async_client() + try: + result = await client.v1.workflows.node_types.list() + assert isinstance(result, list) + assert all(type(item).__name__ == "NodeType" for item in result) + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_node_types_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.node_types.list() + assert exc_info.value.status == 401 + finally: + await ec.close() + + +def test_workflows_node_types_get_success(): + client = _client() + try: + result = client.v1.workflows.node_types.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "NodeType" + finally: + client.close() + + +def test_workflows_node_types_get_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.node_types.get("test-value") + assert exc_info.value.status == 401 + finally: + ec.close() + + +def test_workflows_node_types_get_error_404(): + ec = _error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.node_types.get("test-value") + assert exc_info.value.status == 404 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_node_types_get_success(): + client = _async_client() + try: + result = await client.v1.workflows.node_types.get("test-value") + assert isinstance(result, BaseModel) + assert type(result).__name__ == "NodeType" + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_node_types_get_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.node_types.get("test-value") + assert exc_info.value.status == 401 + finally: + await ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_node_types_get_error_404(): + ec = _async_error_client(404) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.node_types.get("test-value") + assert exc_info.value.status == 404 + finally: + await ec.close() + + +def test_workflows_sample_list_success(): + client = _client() + try: + result = client.v1.workflows.sample.list() + assert result["content"] is not None + assert result["mime_type"] + finally: + client.close() + + +def test_workflows_sample_list_error_401(): + ec = _error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + ec.v1.workflows.sample.list() + assert exc_info.value.status == 401 + finally: + ec.close() + + +@pytest.mark.asyncio +async def test_async_workflows_sample_list_success(): + client = _async_client() + try: + result = await client.v1.workflows.sample.list() + assert result["content"] is not None + assert result["mime_type"] + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_async_workflows_sample_list_error_401(): + ec = _async_error_client(401) + try: + with pytest.raises(ApiError) as exc_info: + await ec.v1.workflows.sample.list() + assert exc_info.value.status == 401 + finally: + await ec.close()