archastro.platform.v1.resources.solutions
1# Copyright (c) 2026 ArchAstro Inc. All Rights Reserved. 2# This file is auto-generated by @archastro/sdk-generator. Do not edit. 3# Content hash: b18497f24bab 4 5from __future__ import annotations 6 7import builtins 8from typing import Any, Required, TypedDict 9 10from ...runtime.http_client import HttpClient, SyncHttpClient 11from ...types.common import ( 12 SolutionDependentsResponse, 13 SolutionImportResponse, 14 SolutionInstallResponse, 15 SolutionListResponse, 16 SolutionSummary, 17 SolutionUpgradeResponse, 18) 19 20 21class SolutionCreateInputSolutionBundleConfigsItem(TypedDict, total=False): 22 content: Required[str] 23 "Full text content of the configuration file." 24 content_type: str | None 25 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 26 relative_path: Required[str] 27 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation." 28 29 30class SolutionCreateInputSolutionBundleSetupActionsItem(TypedDict, total=False): 31 depends_on: list[str] | None 32 "List of other setup action identifiers that must be completed before this action becomes actionable." 33 description: str | None 34 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 35 kind: Required[str] 36 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 37 params: dict[str, Any] | None 38 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 39 required: bool | None 40 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 41 sort_order: int | None 42 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 43 title: Required[str] 44 "Short human-readable label displayed in the setup checklist." 45 verify_config: dict[str, Any] | None 46 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.' 47 48 49class SolutionCreateInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 50 content: Required[str] 51 "Full text content of the file." 52 content_type: str | None 53 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 54 relative_path: Required[str] 55 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.' 56 57 58class SolutionCreateInputSolutionBundleSkillsItem(TypedDict, total=False): 59 content: Required[str] 60 "Full text content of the `SKILL.md` file." 61 content_type: str | None 62 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 63 files: list[SolutionCreateInputSolutionBundleSkillsItemFilesItem] | None 64 "Additional files nested inside the skill folder, each with its own path and content." 65 relative_path: Required[str] 66 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).' 67 68 69class SolutionCreateInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 70 content: Required[str] 71 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 72 content_type: str | None 73 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 74 data_encoding: str | None 75 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 76 relative_path: Required[str] 77 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)." 78 79 80class SolutionCreateInputSolutionBundleSolution(TypedDict, total=False): 81 content: Required[str] 82 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 83 content_type: str | None 84 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 85 files: list[SolutionCreateInputSolutionBundleSolutionFilesItem] | None 86 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 87 lookup_key: Required[str] 88 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config." 89 90 91class SolutionCreateInputSolutionBundleTemplate(TypedDict, total=False): 92 content: Required[str] 93 "Full text content of the agent template file, typically a YAML document." 94 content_type: str | None 95 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 96 relative_path: Required[str] 97 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 98 99 100class SolutionCreateInputSolutionBundleTemplatesItem(TypedDict, total=False): 101 content: Required[str] 102 "Full text content of the agent template file, typically a YAML document." 103 content_type: str | None 104 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 105 relative_path: Required[str] 106 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 107 108 109class SolutionCreateInputSolutionBundle(TypedDict, total=False): 110 configs: list[SolutionCreateInputSolutionBundleConfigsItem] | None 111 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 112 lookup_key_prefix: str | None 113 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 114 lookup_key_suffix: str | None 115 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 116 setup_actions: list[SolutionCreateInputSolutionBundleSetupActionsItem] | None 117 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 118 skills: list[SolutionCreateInputSolutionBundleSkillsItem] | None 119 "Skill bundles (root config plus supporting files) that this solution depends on." 120 solution: Required[SolutionCreateInputSolutionBundleSolution] 121 "The solution config to install, including the solution.yaml body and any referenced component files." 122 template: SolutionCreateInputSolutionBundleTemplate | None 123 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 124 templates: list[SolutionCreateInputSolutionBundleTemplatesItem] | None 125 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`." 126 127 128class SolutionCreateInput(TypedDict, total=False): 129 "Import a Solution into the library" 130 131 dry_run: bool | None 132 "When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`." 133 org: str | None 134 "Organization ID (`org_...`) for the import destination scope." 135 solution: str | None 136 "Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`." 137 solution_bundle: SolutionCreateInputSolutionBundle | None 138 "Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`." 139 team: str | None 140 "Team ID (`team_...`) for the import destination scope." 141 user: str | None 142 "User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set." 143 virtual_path_prefix: str | None 144 "Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix." 145 146 147class SolutionInstallInput(TypedDict, total=False): 148 "Install a Solution" 149 150 allow_auto_import: bool | None 151 "When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing." 152 lookup_key: str | None 153 "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)." 154 lookup_key_suffix: str | None 155 "Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions." 156 name: str | None 157 "Display name override for the provisioned Agent. Ignored for non-Agent Solutions." 158 org: str | None 159 "Organization ID (`org_...`) for the install destination scope." 160 target: str | None 161 "ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources." 162 team: str | None 163 "Team ID (`team_...`) for the install destination scope." 164 template: str | None 165 "Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly." 166 user: str | None 167 "User ID (`usr_...`) for the install destination scope." 168 169 170class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False): 171 content: Required[str] 172 "Full text content of the configuration file." 173 content_type: str | None 174 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 175 relative_path: Required[str] 176 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation." 177 178 179class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False): 180 depends_on: list[str] | None 181 "List of other setup action identifiers that must be completed before this action becomes actionable." 182 description: str | None 183 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 184 kind: Required[str] 185 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 186 params: dict[str, Any] | None 187 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 188 required: bool | None 189 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 190 sort_order: int | None 191 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 192 title: Required[str] 193 "Short human-readable label displayed in the setup checklist." 194 verify_config: dict[str, Any] | None 195 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.' 196 197 198class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 199 content: Required[str] 200 "Full text content of the file." 201 content_type: str | None 202 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 203 relative_path: Required[str] 204 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.' 205 206 207class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False): 208 content: Required[str] 209 "Full text content of the `SKILL.md` file." 210 content_type: str | None 211 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 212 files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None 213 "Additional files nested inside the skill folder, each with its own path and content." 214 relative_path: Required[str] 215 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).' 216 217 218class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 219 content: Required[str] 220 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 221 content_type: str | None 222 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 223 data_encoding: str | None 224 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 225 relative_path: Required[str] 226 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)." 227 228 229class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False): 230 content: Required[str] 231 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 232 content_type: str | None 233 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 234 files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None 235 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 236 lookup_key: Required[str] 237 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config." 238 239 240class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False): 241 content: Required[str] 242 "Full text content of the agent template file, typically a YAML document." 243 content_type: str | None 244 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 245 relative_path: Required[str] 246 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 247 248 249class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False): 250 content: Required[str] 251 "Full text content of the agent template file, typically a YAML document." 252 content_type: str | None 253 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 254 relative_path: Required[str] 255 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 256 257 258class SolutionUpgradeInputSolutionBundle(TypedDict, total=False): 259 configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None 260 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 261 lookup_key_prefix: str | None 262 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 263 lookup_key_suffix: str | None 264 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 265 setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None 266 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 267 skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None 268 "Skill bundles (root config plus supporting files) that this solution depends on." 269 solution: Required[SolutionUpgradeInputSolutionBundleSolution] 270 "The solution config to install, including the solution.yaml body and any referenced component files." 271 template: SolutionUpgradeInputSolutionBundleTemplate | None 272 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 273 templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None 274 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`." 275 276 277class SolutionUpgradeInput(TypedDict, total=False): 278 "Upgrade an installed Solution" 279 280 allow_downgrade: bool | None 281 "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`." 282 dry_run: bool | None 283 "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`." 284 expected_review_fingerprint: str | None 285 "Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply." 286 org: str | None 287 "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this." 288 solution_bundle: SolutionUpgradeInputSolutionBundle | None 289 "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`." 290 target_solution: str | None 291 "Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`." 292 293 294class AsyncSolutionResource: 295 def __init__(self, http: HttpClient): 296 self._http = http 297 298 async def list( 299 self, 300 *, 301 page: int | None = None, 302 page_size: int | None = None, 303 lookup_key: str | None = None, 304 path_prefix: str | None = None, 305 owners: builtins.list[str] | None = None, 306 ) -> SolutionListResponse: 307 """ 308 List Solutions 309 Returns a paginated list of Solutions visible to the caller, merging two 310 scopes: app-level Solutions (system-owned rows with no org affiliation, 311 visible to everyone including unauthenticated callers so they can power 312 the public catalog) and org-level Solutions (system-owned rows stamped with 313 the viewer's org ID, included when an authenticated viewer carries an org 314 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 315 and therefore only ever see the app-level scope. 316 Solutions that appear under both scopes are deduplicated by their stable 317 `solution_id` value. The merged entry's `owners` array lists every scope the 318 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 319 has a higher `solution_version` than the org-level copy, the response includes 320 `upgrade_available: true` and `latest_version` so callers can prompt for an 321 upgrade. 322 323 Args: 324 page: Page number to return. Defaults to `1`. 325 page_size: Number of Solutions per page. Defaults to `25`. 326 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 327 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 328 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 329 330 Returns: 331 Paginated list of Solution summaries visible to the caller. 332 """ 333 query: dict[str, object] = {} 334 if page is not None: 335 query["page"] = page 336 if page_size is not None: 337 query["page_size"] = page_size 338 if lookup_key is not None: 339 query["lookup_key"] = lookup_key 340 if path_prefix is not None: 341 query["path_prefix"] = path_prefix 342 if owners is not None: 343 query["owners"] = owners 344 return await self._http.request( 345 "/api/v1/solutions", 346 query=query, 347 response_type=SolutionListResponse, 348 ) 349 350 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 351 """ 352 Import a Solution into the library 353 Imports a Solution and its bundled configs (skills, scripts, templates, files) 354 into the library for the target scope. Two mutually exclusive import modes 355 are supported: pass `solution` to re-import an existing system-owned catalog 356 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 357 self-contained inline bundle. Exactly one must be present. 358 The operation upserts the bundle in a single transaction. When `dry_run` is 359 `true` the same pipeline runs but the transaction is rolled back no rows are 360 persisted and the response reflects what would have been written. The 361 response shape is the same in both cases: the Solution summary plus 362 `installed_configs` listing each config the import created or would create. 363 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 364 Solution into the library; install provisions a runtime resource (Agent, 365 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 366 367 Args: 368 input: Request body. 369 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 370 input.org: Organization ID (`org_...`) for the import destination scope. 371 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 372 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 373 input.team: Team ID (`team_...`) for the import destination scope. 374 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 375 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 376 377 Returns: 378 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 379 """ 380 return await self._http.request( 381 "/api/v1/solutions", 382 method="POST", 383 body=input, 384 response_type=SolutionImportResponse, 385 ) 386 387 async def delete(self, solution: str) -> None: 388 """ 389 Delete a Solution 390 Permanently deletes an imported Solution and all configs bundled with it, 391 including templates, skills, scripts, and files. The deletion runs in a 392 single transaction; provider-stored blobs are swept asynchronously after commit. 393 Org-scope callers (for example, an org admin in the Library settings page) can 394 only delete their org's copy of the Solution. App-scope callers can delete 395 Solutions at either scope. RBAC is enforced inside the core delete flow. 396 Returns `204 No Content` on success. 397 398 Args: 399 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 400 401 Returns: 402 Empty body. HTTP 204 indicates the Solution was deleted successfully. 403 """ 404 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 405 406 async def get(self, solution: str) -> SolutionSummary: 407 """ 408 Retrieve a Solution 409 Returns a single Solution identified by its config ID (`cfg_...`) or 410 `lookup_key`, in the same summary shape the list endpoint emits. The response 411 always includes a freshly-minted `readme_url` call this endpoint to refresh 412 an expired README token without making any other state change. 413 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 414 are visible to everyone including unauthenticated callers, so the public 415 catalog can render a Solution's detail page logged-out; org-scoped Solutions 416 are only visible to authenticated viewers whose org context matches. Anything 417 else returns 404. 418 When the resolved Solution is org-scoped, the endpoint compares its 419 `solution_version` against the matching app-level copy. If the app-level copy 420 is at a higher version the response includes `upgrade_available: true` and 421 `latest_version`. App-level resolutions always report `upgrade_available: false`. 422 423 Args: 424 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 425 426 Returns: 427 Solution summary including a freshly-minted `readme_url` token valid for one hour. 428 """ 429 return await self._http.request( 430 f"/api/v1/solutions/{solution}", 431 response_type=SolutionSummary, 432 ) 433 434 async def dependents(self, solution: str) -> SolutionDependentsResponse: 435 """ 436 Preview Solution delete impact 437 Returns a read-only preview of what deleting the specified Solution would 438 affect: the agents that reference the Solution's bundle, and the count of 439 bundled configs that would be orphaned rather than cascade-deleted. 440 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 441 surface a warning when live agents depend on the Solution. 442 Visibility scope mirrors the delete endpoint: org-scope viewers see their 443 org's copy; app-scope viewers can inspect either scope. 444 445 Args: 446 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 447 448 Returns: 449 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 450 """ 451 return await self._http.request( 452 f"/api/v1/solutions/{solution}/dependents", 453 response_type=SolutionDependentsResponse, 454 ) 455 456 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 457 """ 458 Install a Solution 459 Provisions a runtime resource from an already-imported Solution. The type of 460 resource created depends on the template the Solution wraps: an 461 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 462 Automation, and attachment templates (`AgentRoutineTemplate`, 463 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 464 sub-resource to an existing Agent specified by `target`. 465 For Solutions that bundle more than one template, pass `template` (the ID or 466 `lookup_key` of the desired template) to select which one to provision. 467 Single-template Solutions do not require `template`. 468 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 469 the library; install provisions a runtime resource from it. 470 471 Args: 472 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 473 input: Request body. 474 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 475 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 476 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 477 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 478 input.org: Organization ID (`org_...`) for the install destination scope. 479 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 480 input.team: Team ID (`team_...`) for the install destination scope. 481 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 482 input.user: User ID (`usr_...`) for the install destination scope. 483 484 Returns: 485 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 486 """ 487 return await self._http.request( 488 f"/api/v1/solutions/{solution}/install", 489 method="POST", 490 body=input, 491 response_type=SolutionInstallResponse, 492 ) 493 494 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 495 """ 496 Retrieve a Solution README or asset 497 Serves the README markdown or a bundled asset for an imported Solution. Both 498 modes use the same path and require a short-lived signed `token` in the query 499 string rather than an `Authorization` header, so browsers can load asset URLs 500 directly from `<img src>` attributes without custom request logic. 501 When `file` is omitted the response is the Solution's `readme` field rendered 502 as `text/markdown`. All local asset references in the markdown are rewritten to 503 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 504 images inline without additional authentication. 505 When `file` is set the response is the raw bytes of the matching asset (a File 506 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 507 name) with the asset's stored `Content-Type`. 508 Tokens are scoped to a single Solution, carry the viewer's app, org, and 509 sandbox context from the time they were minted, and expire after one hour. 510 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 511 always returns a newly minted `readme_url`. 512 513 Args: 514 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 515 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 516 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 517 518 Returns: 519 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 520 """ 521 query: dict[str, object] = {} 522 query["token"] = token 523 if file is not None: 524 query["file"] = file 525 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 526 527 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 528 """ 529 Upgrade an installed Solution 530 Applies an incoming bundle to an already-installed Solution in a single atomic 531 transaction, bringing its configs in line with the new bundle. Config IDs are 532 preserved across the upgrade. Configs that existed in the old bundle but are 533 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 534 Two mutually exclusive source modes: pass `target_solution` to pull the 535 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 536 `solution_bundle` to supply a complete inline bundle directly. Exactly one 537 must be present. 538 When `dry_run` is `true` the full diff is computed and returned but no 539 changes are written. Pass the dry-run response's `review_fingerprint` as 540 `expected_review_fingerprint` when applying to guard against the bundle 541 changing between review and apply. 542 543 Args: 544 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 545 input: Request body. 546 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 547 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 548 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 549 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 550 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 551 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 552 553 Returns: 554 Updated Solution plus the full upgrade diff. 555 """ 556 return await self._http.request( 557 f"/api/v1/solutions/{solution}/upgrade", 558 method="POST", 559 body=input, 560 response_type=SolutionUpgradeResponse, 561 ) 562 563 564class SolutionResource: 565 def __init__(self, http: SyncHttpClient): 566 self._http = http 567 568 def list( 569 self, 570 *, 571 page: int | None = None, 572 page_size: int | None = None, 573 lookup_key: str | None = None, 574 path_prefix: str | None = None, 575 owners: builtins.list[str] | None = None, 576 ) -> SolutionListResponse: 577 """ 578 List Solutions 579 Returns a paginated list of Solutions visible to the caller, merging two 580 scopes: app-level Solutions (system-owned rows with no org affiliation, 581 visible to everyone including unauthenticated callers so they can power 582 the public catalog) and org-level Solutions (system-owned rows stamped with 583 the viewer's org ID, included when an authenticated viewer carries an org 584 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 585 and therefore only ever see the app-level scope. 586 Solutions that appear under both scopes are deduplicated by their stable 587 `solution_id` value. The merged entry's `owners` array lists every scope the 588 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 589 has a higher `solution_version` than the org-level copy, the response includes 590 `upgrade_available: true` and `latest_version` so callers can prompt for an 591 upgrade. 592 593 Args: 594 page: Page number to return. Defaults to `1`. 595 page_size: Number of Solutions per page. Defaults to `25`. 596 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 597 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 598 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 599 600 Returns: 601 Paginated list of Solution summaries visible to the caller. 602 """ 603 query: dict[str, object] = {} 604 if page is not None: 605 query["page"] = page 606 if page_size is not None: 607 query["page_size"] = page_size 608 if lookup_key is not None: 609 query["lookup_key"] = lookup_key 610 if path_prefix is not None: 611 query["path_prefix"] = path_prefix 612 if owners is not None: 613 query["owners"] = owners 614 return self._http.request( 615 "/api/v1/solutions", 616 query=query, 617 response_type=SolutionListResponse, 618 ) 619 620 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 621 """ 622 Import a Solution into the library 623 Imports a Solution and its bundled configs (skills, scripts, templates, files) 624 into the library for the target scope. Two mutually exclusive import modes 625 are supported: pass `solution` to re-import an existing system-owned catalog 626 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 627 self-contained inline bundle. Exactly one must be present. 628 The operation upserts the bundle in a single transaction. When `dry_run` is 629 `true` the same pipeline runs but the transaction is rolled back no rows are 630 persisted and the response reflects what would have been written. The 631 response shape is the same in both cases: the Solution summary plus 632 `installed_configs` listing each config the import created or would create. 633 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 634 Solution into the library; install provisions a runtime resource (Agent, 635 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 636 637 Args: 638 input: Request body. 639 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 640 input.org: Organization ID (`org_...`) for the import destination scope. 641 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 642 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 643 input.team: Team ID (`team_...`) for the import destination scope. 644 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 645 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 646 647 Returns: 648 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 649 """ 650 return self._http.request( 651 "/api/v1/solutions", 652 method="POST", 653 body=input, 654 response_type=SolutionImportResponse, 655 ) 656 657 def delete(self, solution: str) -> None: 658 """ 659 Delete a Solution 660 Permanently deletes an imported Solution and all configs bundled with it, 661 including templates, skills, scripts, and files. The deletion runs in a 662 single transaction; provider-stored blobs are swept asynchronously after commit. 663 Org-scope callers (for example, an org admin in the Library settings page) can 664 only delete their org's copy of the Solution. App-scope callers can delete 665 Solutions at either scope. RBAC is enforced inside the core delete flow. 666 Returns `204 No Content` on success. 667 668 Args: 669 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 670 671 Returns: 672 Empty body. HTTP 204 indicates the Solution was deleted successfully. 673 """ 674 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 675 676 def get(self, solution: str) -> SolutionSummary: 677 """ 678 Retrieve a Solution 679 Returns a single Solution identified by its config ID (`cfg_...`) or 680 `lookup_key`, in the same summary shape the list endpoint emits. The response 681 always includes a freshly-minted `readme_url` call this endpoint to refresh 682 an expired README token without making any other state change. 683 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 684 are visible to everyone including unauthenticated callers, so the public 685 catalog can render a Solution's detail page logged-out; org-scoped Solutions 686 are only visible to authenticated viewers whose org context matches. Anything 687 else returns 404. 688 When the resolved Solution is org-scoped, the endpoint compares its 689 `solution_version` against the matching app-level copy. If the app-level copy 690 is at a higher version the response includes `upgrade_available: true` and 691 `latest_version`. App-level resolutions always report `upgrade_available: false`. 692 693 Args: 694 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 695 696 Returns: 697 Solution summary including a freshly-minted `readme_url` token valid for one hour. 698 """ 699 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary) 700 701 def dependents(self, solution: str) -> SolutionDependentsResponse: 702 """ 703 Preview Solution delete impact 704 Returns a read-only preview of what deleting the specified Solution would 705 affect: the agents that reference the Solution's bundle, and the count of 706 bundled configs that would be orphaned rather than cascade-deleted. 707 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 708 surface a warning when live agents depend on the Solution. 709 Visibility scope mirrors the delete endpoint: org-scope viewers see their 710 org's copy; app-scope viewers can inspect either scope. 711 712 Args: 713 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 714 715 Returns: 716 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 717 """ 718 return self._http.request( 719 f"/api/v1/solutions/{solution}/dependents", 720 response_type=SolutionDependentsResponse, 721 ) 722 723 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 724 """ 725 Install a Solution 726 Provisions a runtime resource from an already-imported Solution. The type of 727 resource created depends on the template the Solution wraps: an 728 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 729 Automation, and attachment templates (`AgentRoutineTemplate`, 730 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 731 sub-resource to an existing Agent specified by `target`. 732 For Solutions that bundle more than one template, pass `template` (the ID or 733 `lookup_key` of the desired template) to select which one to provision. 734 Single-template Solutions do not require `template`. 735 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 736 the library; install provisions a runtime resource from it. 737 738 Args: 739 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 740 input: Request body. 741 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 742 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 743 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 744 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 745 input.org: Organization ID (`org_...`) for the install destination scope. 746 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 747 input.team: Team ID (`team_...`) for the install destination scope. 748 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 749 input.user: User ID (`usr_...`) for the install destination scope. 750 751 Returns: 752 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 753 """ 754 return self._http.request( 755 f"/api/v1/solutions/{solution}/install", 756 method="POST", 757 body=input, 758 response_type=SolutionInstallResponse, 759 ) 760 761 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 762 """ 763 Retrieve a Solution README or asset 764 Serves the README markdown or a bundled asset for an imported Solution. Both 765 modes use the same path and require a short-lived signed `token` in the query 766 string rather than an `Authorization` header, so browsers can load asset URLs 767 directly from `<img src>` attributes without custom request logic. 768 When `file` is omitted the response is the Solution's `readme` field rendered 769 as `text/markdown`. All local asset references in the markdown are rewritten to 770 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 771 images inline without additional authentication. 772 When `file` is set the response is the raw bytes of the matching asset (a File 773 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 774 name) with the asset's stored `Content-Type`. 775 Tokens are scoped to a single Solution, carry the viewer's app, org, and 776 sandbox context from the time they were minted, and expire after one hour. 777 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 778 always returns a newly minted `readme_url`. 779 780 Args: 781 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 782 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 783 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 784 785 Returns: 786 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 787 """ 788 query: dict[str, object] = {} 789 query["token"] = token 790 if file is not None: 791 query["file"] = file 792 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 793 794 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 795 """ 796 Upgrade an installed Solution 797 Applies an incoming bundle to an already-installed Solution in a single atomic 798 transaction, bringing its configs in line with the new bundle. Config IDs are 799 preserved across the upgrade. Configs that existed in the old bundle but are 800 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 801 Two mutually exclusive source modes: pass `target_solution` to pull the 802 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 803 `solution_bundle` to supply a complete inline bundle directly. Exactly one 804 must be present. 805 When `dry_run` is `true` the full diff is computed and returned but no 806 changes are written. Pass the dry-run response's `review_fingerprint` as 807 `expected_review_fingerprint` when applying to guard against the bundle 808 changing between review and apply. 809 810 Args: 811 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 812 input: Request body. 813 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 814 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 815 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 816 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 817 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 818 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 819 820 Returns: 821 Updated Solution plus the full upgrade diff. 822 """ 823 return self._http.request( 824 f"/api/v1/solutions/{solution}/upgrade", 825 method="POST", 826 body=input, 827 response_type=SolutionUpgradeResponse, 828 )
22class SolutionCreateInputSolutionBundleConfigsItem(TypedDict, total=False): 23 content: Required[str] 24 "Full text content of the configuration file." 25 content_type: str | None 26 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 27 relative_path: Required[str] 28 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
31class SolutionCreateInputSolutionBundleSetupActionsItem(TypedDict, total=False): 32 depends_on: list[str] | None 33 "List of other setup action identifiers that must be completed before this action becomes actionable." 34 description: str | None 35 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 36 kind: Required[str] 37 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 38 params: dict[str, Any] | None 39 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 40 required: bool | None 41 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 42 sort_order: int | None 43 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 44 title: Required[str] 45 "Short human-readable label displayed in the setup checklist." 46 verify_config: dict[str, Any] | None 47 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.'
List of other setup action identifiers that must be completed before this action becomes actionable.
Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.
Category of setup step. One of "env_var" (configure an environment variable), "install" (complete an installation step), "custom" (a user-defined action), or "integration" (authorize an OAuth-backed MCP server integration).
Kind-specific configuration for the action. For "env_var" steps this typically includes key and scope; for "install" steps it includes installation_kind; for "integration" steps it includes mcp_server_ref. Shape varies by kind.
When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.
50class SolutionCreateInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 51 content: Required[str] 52 "Full text content of the file." 53 content_type: str | None 54 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 55 relative_path: Required[str] 56 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
59class SolutionCreateInputSolutionBundleSkillsItem(TypedDict, total=False): 60 content: Required[str] 61 "Full text content of the `SKILL.md` file." 62 content_type: str | None 63 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 64 files: list[SolutionCreateInputSolutionBundleSkillsItemFilesItem] | None 65 "Additional files nested inside the skill folder, each with its own path and content." 66 relative_path: Required[str] 67 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
Additional files nested inside the skill folder, each with its own path and content.
70class SolutionCreateInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 71 content: Required[str] 72 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 73 content_type: str | None 74 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 75 data_encoding: str | None 76 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 77 relative_path: Required[str] 78 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.
MIME type of the file. Defaults to a value inferred from the file extension when omitted.
Encoding of content. "raw" (default) stores the value verbatim. "base64" decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.
81class SolutionCreateInputSolutionBundleSolution(TypedDict, total=False): 82 content: Required[str] 83 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 84 content_type: str | None 85 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 86 files: list[SolutionCreateInputSolutionBundleSolutionFilesItem] | None 87 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 88 lookup_key: Required[str] 89 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.
MIME type of content. Defaults to application/x-yaml; pass application/json when submitting JSON.
Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via path:// URIs. Each entry is persisted as a child file record.
92class SolutionCreateInputSolutionBundleTemplate(TypedDict, total=False): 93 content: Required[str] 94 "Full text content of the agent template file, typically a YAML document." 95 content_type: str | None 96 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 97 relative_path: Required[str] 98 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
101class SolutionCreateInputSolutionBundleTemplatesItem(TypedDict, total=False): 102 content: Required[str] 103 "Full text content of the agent template file, typically a YAML document." 104 content_type: str | None 105 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 106 relative_path: Required[str] 107 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
110class SolutionCreateInputSolutionBundle(TypedDict, total=False): 111 configs: list[SolutionCreateInputSolutionBundleConfigsItem] | None 112 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 113 lookup_key_prefix: str | None 114 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 115 lookup_key_suffix: str | None 116 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 117 setup_actions: list[SolutionCreateInputSolutionBundleSetupActionsItem] | None 118 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 119 skills: list[SolutionCreateInputSolutionBundleSkillsItem] | None 120 "Skill bundles (root config plus supporting files) that this solution depends on." 121 solution: Required[SolutionCreateInputSolutionBundleSolution] 122 "The solution config to install, including the solution.yaml body and any referenced component files." 123 template: SolutionCreateInputSolutionBundleTemplate | None 124 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 125 templates: list[SolutionCreateInputSolutionBundleTemplatesItem] | None 126 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`."
Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install.
String prepended (with a - separator) to every uploaded config's lookup_key and every path:// reference in the solution body. Typical value is solutions-<install-id>.
String appended to every uploaded config's lookup_key and every path:// reference in the solution body. Should be stable for a given install and unique per attempt.
Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own setup_actions. Omit when bundling multiple templates.
Skill bundles (root config plus supporting files) that this solution depends on.
The solution config to install, including the solution.yaml body and any referenced component files.
Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via template_path:. Mutually exclusive with template.
129class SolutionCreateInput(TypedDict, total=False): 130 "Import a Solution into the library" 131 132 dry_run: bool | None 133 "When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`." 134 org: str | None 135 "Organization ID (`org_...`) for the import destination scope." 136 solution: str | None 137 "Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`." 138 solution_bundle: SolutionCreateInputSolutionBundle | None 139 "Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`." 140 team: str | None 141 "Team ID (`team_...`) for the import destination scope." 142 user: str | None 143 "User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set." 144 virtual_path_prefix: str | None 145 "Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix."
Import a Solution into the library
When true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to false.
Config ID (cfg_...) or lookup_key of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with solution_bundle.
Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with solution.
148class SolutionInstallInput(TypedDict, total=False): 149 "Install a Solution" 150 151 allow_auto_import: bool | None 152 "When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing." 153 lookup_key: str | None 154 "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)." 155 lookup_key_suffix: str | None 156 "Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions." 157 name: str | None 158 "Display name override for the provisioned Agent. Ignored for non-Agent Solutions." 159 org: str | None 160 "Organization ID (`org_...`) for the install destination scope." 161 target: str | None 162 "ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources." 163 team: str | None 164 "Team ID (`team_...`) for the install destination scope." 165 template: str | None 166 "Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly." 167 user: str | None 168 "User ID (`usr_...`) for the install destination scope."
Install a Solution
When true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit org param. Defaults to false; without it the endpoint returns 404 when the org-scoped Solution is missing.
Lookup key override for the provisioned resource (for example, the Agent's agent_key).
Suffix appended to every config_ref: resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions.
ID or lookup_key of the parent Agent to attach to. Required when installing an AgentRoutineTemplate, AgentToolTemplate, AgentSkillTemplate, or AgentComputerTemplate Solution, since those produce sub-resources attached to an existing Agent. Omit for AgentTemplate and AutomationTemplate Solutions, which provision standalone resources.
Config ID (cfg_...) or lookup_key of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly.
171class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False): 172 content: Required[str] 173 "Full text content of the configuration file." 174 content_type: str | None 175 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 176 relative_path: Required[str] 177 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
180class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False): 181 depends_on: list[str] | None 182 "List of other setup action identifiers that must be completed before this action becomes actionable." 183 description: str | None 184 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 185 kind: Required[str] 186 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 187 params: dict[str, Any] | None 188 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 189 required: bool | None 190 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 191 sort_order: int | None 192 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 193 title: Required[str] 194 "Short human-readable label displayed in the setup checklist." 195 verify_config: dict[str, Any] | None 196 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.'
List of other setup action identifiers that must be completed before this action becomes actionable.
Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.
Category of setup step. One of "env_var" (configure an environment variable), "install" (complete an installation step), "custom" (a user-defined action), or "integration" (authorize an OAuth-backed MCP server integration).
Kind-specific configuration for the action. For "env_var" steps this typically includes key and scope; for "install" steps it includes installation_kind; for "integration" steps it includes mcp_server_ref. Shape varies by kind.
When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.
199class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 200 content: Required[str] 201 "Full text content of the file." 202 content_type: str | None 203 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 204 relative_path: Required[str] 205 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
208class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False): 209 content: Required[str] 210 "Full text content of the `SKILL.md` file." 211 content_type: str | None 212 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 213 files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None 214 "Additional files nested inside the skill folder, each with its own path and content." 215 relative_path: Required[str] 216 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
Additional files nested inside the skill folder, each with its own path and content.
219class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 220 content: Required[str] 221 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 222 content_type: str | None 223 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 224 data_encoding: str | None 225 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 226 relative_path: Required[str] 227 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.
MIME type of the file. Defaults to a value inferred from the file extension when omitted.
Encoding of content. "raw" (default) stores the value verbatim. "base64" decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.
230class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False): 231 content: Required[str] 232 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 233 content_type: str | None 234 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 235 files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None 236 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 237 lookup_key: Required[str] 238 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.
MIME type of content. Defaults to application/x-yaml; pass application/json when submitting JSON.
Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via path:// URIs. Each entry is persisted as a child file record.
241class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False): 242 content: Required[str] 243 "Full text content of the agent template file, typically a YAML document." 244 content_type: str | None 245 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 246 relative_path: Required[str] 247 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
250class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False): 251 content: Required[str] 252 "Full text content of the agent template file, typically a YAML document." 253 content_type: str | None 254 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 255 relative_path: Required[str] 256 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
259class SolutionUpgradeInputSolutionBundle(TypedDict, total=False): 260 configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None 261 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 262 lookup_key_prefix: str | None 263 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 264 lookup_key_suffix: str | None 265 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 266 setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None 267 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 268 skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None 269 "Skill bundles (root config plus supporting files) that this solution depends on." 270 solution: Required[SolutionUpgradeInputSolutionBundleSolution] 271 "The solution config to install, including the solution.yaml body and any referenced component files." 272 template: SolutionUpgradeInputSolutionBundleTemplate | None 273 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 274 templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None 275 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`."
Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install.
String prepended (with a - separator) to every uploaded config's lookup_key and every path:// reference in the solution body. Typical value is solutions-<install-id>.
String appended to every uploaded config's lookup_key and every path:// reference in the solution body. Should be stable for a given install and unique per attempt.
Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own setup_actions. Omit when bundling multiple templates.
Skill bundles (root config plus supporting files) that this solution depends on.
The solution config to install, including the solution.yaml body and any referenced component files.
Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via template_path:. Mutually exclusive with template.
278class SolutionUpgradeInput(TypedDict, total=False): 279 "Upgrade an installed Solution" 280 281 allow_downgrade: bool | None 282 "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`." 283 dry_run: bool | None 284 "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`." 285 expected_review_fingerprint: str | None 286 "Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply." 287 org: str | None 288 "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this." 289 solution_bundle: SolutionUpgradeInputSolutionBundle | None 290 "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`." 291 target_solution: str | None 292 "Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`."
Upgrade an installed Solution
When true, permits an incoming solution_version lower than the currently installed version. Defaults to false.
When true, computes and returns the full upgrade diff without persisting any changes. Defaults to false.
Optional stale-review guard. Pass the review_fingerprint returned by a prior dry_run call to ensure the bundle has not changed between review and apply.
Organization ID (org_...) used to resolve org-scoped lookup_key values. Config IDs (cfg_...) are globally unique and do not require this.
Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with target_solution.
Config ID (cfg_...) or lookup_key of the Solution to use as the incoming upgrade source. Mutually exclusive with solution_bundle.
295class AsyncSolutionResource: 296 def __init__(self, http: HttpClient): 297 self._http = http 298 299 async def list( 300 self, 301 *, 302 page: int | None = None, 303 page_size: int | None = None, 304 lookup_key: str | None = None, 305 path_prefix: str | None = None, 306 owners: builtins.list[str] | None = None, 307 ) -> SolutionListResponse: 308 """ 309 List Solutions 310 Returns a paginated list of Solutions visible to the caller, merging two 311 scopes: app-level Solutions (system-owned rows with no org affiliation, 312 visible to everyone including unauthenticated callers so they can power 313 the public catalog) and org-level Solutions (system-owned rows stamped with 314 the viewer's org ID, included when an authenticated viewer carries an org 315 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 316 and therefore only ever see the app-level scope. 317 Solutions that appear under both scopes are deduplicated by their stable 318 `solution_id` value. The merged entry's `owners` array lists every scope the 319 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 320 has a higher `solution_version` than the org-level copy, the response includes 321 `upgrade_available: true` and `latest_version` so callers can prompt for an 322 upgrade. 323 324 Args: 325 page: Page number to return. Defaults to `1`. 326 page_size: Number of Solutions per page. Defaults to `25`. 327 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 328 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 329 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 330 331 Returns: 332 Paginated list of Solution summaries visible to the caller. 333 """ 334 query: dict[str, object] = {} 335 if page is not None: 336 query["page"] = page 337 if page_size is not None: 338 query["page_size"] = page_size 339 if lookup_key is not None: 340 query["lookup_key"] = lookup_key 341 if path_prefix is not None: 342 query["path_prefix"] = path_prefix 343 if owners is not None: 344 query["owners"] = owners 345 return await self._http.request( 346 "/api/v1/solutions", 347 query=query, 348 response_type=SolutionListResponse, 349 ) 350 351 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 352 """ 353 Import a Solution into the library 354 Imports a Solution and its bundled configs (skills, scripts, templates, files) 355 into the library for the target scope. Two mutually exclusive import modes 356 are supported: pass `solution` to re-import an existing system-owned catalog 357 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 358 self-contained inline bundle. Exactly one must be present. 359 The operation upserts the bundle in a single transaction. When `dry_run` is 360 `true` the same pipeline runs but the transaction is rolled back no rows are 361 persisted and the response reflects what would have been written. The 362 response shape is the same in both cases: the Solution summary plus 363 `installed_configs` listing each config the import created or would create. 364 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 365 Solution into the library; install provisions a runtime resource (Agent, 366 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 367 368 Args: 369 input: Request body. 370 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 371 input.org: Organization ID (`org_...`) for the import destination scope. 372 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 373 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 374 input.team: Team ID (`team_...`) for the import destination scope. 375 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 376 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 377 378 Returns: 379 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 380 """ 381 return await self._http.request( 382 "/api/v1/solutions", 383 method="POST", 384 body=input, 385 response_type=SolutionImportResponse, 386 ) 387 388 async def delete(self, solution: str) -> None: 389 """ 390 Delete a Solution 391 Permanently deletes an imported Solution and all configs bundled with it, 392 including templates, skills, scripts, and files. The deletion runs in a 393 single transaction; provider-stored blobs are swept asynchronously after commit. 394 Org-scope callers (for example, an org admin in the Library settings page) can 395 only delete their org's copy of the Solution. App-scope callers can delete 396 Solutions at either scope. RBAC is enforced inside the core delete flow. 397 Returns `204 No Content` on success. 398 399 Args: 400 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 401 402 Returns: 403 Empty body. HTTP 204 indicates the Solution was deleted successfully. 404 """ 405 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 406 407 async def get(self, solution: str) -> SolutionSummary: 408 """ 409 Retrieve a Solution 410 Returns a single Solution identified by its config ID (`cfg_...`) or 411 `lookup_key`, in the same summary shape the list endpoint emits. The response 412 always includes a freshly-minted `readme_url` call this endpoint to refresh 413 an expired README token without making any other state change. 414 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 415 are visible to everyone including unauthenticated callers, so the public 416 catalog can render a Solution's detail page logged-out; org-scoped Solutions 417 are only visible to authenticated viewers whose org context matches. Anything 418 else returns 404. 419 When the resolved Solution is org-scoped, the endpoint compares its 420 `solution_version` against the matching app-level copy. If the app-level copy 421 is at a higher version the response includes `upgrade_available: true` and 422 `latest_version`. App-level resolutions always report `upgrade_available: false`. 423 424 Args: 425 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 426 427 Returns: 428 Solution summary including a freshly-minted `readme_url` token valid for one hour. 429 """ 430 return await self._http.request( 431 f"/api/v1/solutions/{solution}", 432 response_type=SolutionSummary, 433 ) 434 435 async def dependents(self, solution: str) -> SolutionDependentsResponse: 436 """ 437 Preview Solution delete impact 438 Returns a read-only preview of what deleting the specified Solution would 439 affect: the agents that reference the Solution's bundle, and the count of 440 bundled configs that would be orphaned rather than cascade-deleted. 441 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 442 surface a warning when live agents depend on the Solution. 443 Visibility scope mirrors the delete endpoint: org-scope viewers see their 444 org's copy; app-scope viewers can inspect either scope. 445 446 Args: 447 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 448 449 Returns: 450 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 451 """ 452 return await self._http.request( 453 f"/api/v1/solutions/{solution}/dependents", 454 response_type=SolutionDependentsResponse, 455 ) 456 457 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 458 """ 459 Install a Solution 460 Provisions a runtime resource from an already-imported Solution. The type of 461 resource created depends on the template the Solution wraps: an 462 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 463 Automation, and attachment templates (`AgentRoutineTemplate`, 464 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 465 sub-resource to an existing Agent specified by `target`. 466 For Solutions that bundle more than one template, pass `template` (the ID or 467 `lookup_key` of the desired template) to select which one to provision. 468 Single-template Solutions do not require `template`. 469 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 470 the library; install provisions a runtime resource from it. 471 472 Args: 473 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 474 input: Request body. 475 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 476 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 477 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 478 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 479 input.org: Organization ID (`org_...`) for the install destination scope. 480 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 481 input.team: Team ID (`team_...`) for the install destination scope. 482 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 483 input.user: User ID (`usr_...`) for the install destination scope. 484 485 Returns: 486 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 487 """ 488 return await self._http.request( 489 f"/api/v1/solutions/{solution}/install", 490 method="POST", 491 body=input, 492 response_type=SolutionInstallResponse, 493 ) 494 495 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 496 """ 497 Retrieve a Solution README or asset 498 Serves the README markdown or a bundled asset for an imported Solution. Both 499 modes use the same path and require a short-lived signed `token` in the query 500 string rather than an `Authorization` header, so browsers can load asset URLs 501 directly from `<img src>` attributes without custom request logic. 502 When `file` is omitted the response is the Solution's `readme` field rendered 503 as `text/markdown`. All local asset references in the markdown are rewritten to 504 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 505 images inline without additional authentication. 506 When `file` is set the response is the raw bytes of the matching asset (a File 507 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 508 name) with the asset's stored `Content-Type`. 509 Tokens are scoped to a single Solution, carry the viewer's app, org, and 510 sandbox context from the time they were minted, and expire after one hour. 511 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 512 always returns a newly minted `readme_url`. 513 514 Args: 515 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 516 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 517 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 518 519 Returns: 520 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 521 """ 522 query: dict[str, object] = {} 523 query["token"] = token 524 if file is not None: 525 query["file"] = file 526 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 527 528 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 529 """ 530 Upgrade an installed Solution 531 Applies an incoming bundle to an already-installed Solution in a single atomic 532 transaction, bringing its configs in line with the new bundle. Config IDs are 533 preserved across the upgrade. Configs that existed in the old bundle but are 534 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 535 Two mutually exclusive source modes: pass `target_solution` to pull the 536 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 537 `solution_bundle` to supply a complete inline bundle directly. Exactly one 538 must be present. 539 When `dry_run` is `true` the full diff is computed and returned but no 540 changes are written. Pass the dry-run response's `review_fingerprint` as 541 `expected_review_fingerprint` when applying to guard against the bundle 542 changing between review and apply. 543 544 Args: 545 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 546 input: Request body. 547 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 548 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 549 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 550 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 551 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 552 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 553 554 Returns: 555 Updated Solution plus the full upgrade diff. 556 """ 557 return await self._http.request( 558 f"/api/v1/solutions/{solution}/upgrade", 559 method="POST", 560 body=input, 561 response_type=SolutionUpgradeResponse, 562 )
299 async def list( 300 self, 301 *, 302 page: int | None = None, 303 page_size: int | None = None, 304 lookup_key: str | None = None, 305 path_prefix: str | None = None, 306 owners: builtins.list[str] | None = None, 307 ) -> SolutionListResponse: 308 """ 309 List Solutions 310 Returns a paginated list of Solutions visible to the caller, merging two 311 scopes: app-level Solutions (system-owned rows with no org affiliation, 312 visible to everyone including unauthenticated callers so they can power 313 the public catalog) and org-level Solutions (system-owned rows stamped with 314 the viewer's org ID, included when an authenticated viewer carries an org 315 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 316 and therefore only ever see the app-level scope. 317 Solutions that appear under both scopes are deduplicated by their stable 318 `solution_id` value. The merged entry's `owners` array lists every scope the 319 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 320 has a higher `solution_version` than the org-level copy, the response includes 321 `upgrade_available: true` and `latest_version` so callers can prompt for an 322 upgrade. 323 324 Args: 325 page: Page number to return. Defaults to `1`. 326 page_size: Number of Solutions per page. Defaults to `25`. 327 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 328 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 329 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 330 331 Returns: 332 Paginated list of Solution summaries visible to the caller. 333 """ 334 query: dict[str, object] = {} 335 if page is not None: 336 query["page"] = page 337 if page_size is not None: 338 query["page_size"] = page_size 339 if lookup_key is not None: 340 query["lookup_key"] = lookup_key 341 if path_prefix is not None: 342 query["path_prefix"] = path_prefix 343 if owners is not None: 344 query["owners"] = owners 345 return await self._http.request( 346 "/api/v1/solutions", 347 query=query, 348 response_type=SolutionListResponse, 349 )
List Solutions
Returns a paginated list of Solutions visible to the caller, merging two
scopes: app-level Solutions (system-owned rows with no org affiliation,
visible to everyone including unauthenticated callers so they can power
the public catalog) and org-level Solutions (system-owned rows stamped with
the viewer's org ID, included when an authenticated viewer carries an org
context). Unauthenticated callers resolve to an app-scoped anonymous viewer
and therefore only ever see the app-level scope.
Solutions that appear under both scopes are deduplicated by their stable
solution_id value. The merged entry's owners array lists every scope the
Solution was found under ("system" and/or "org"). When the app-level copy
has a higher solution_version than the org-level copy, the response includes
upgrade_available: true and latest_version so callers can prompt for an
upgrade.
Arguments:
- page: Page number to return. Defaults to
1. - page_size: Number of Solutions per page. Defaults to
25. - lookup_key: Filter to the Solution whose
lookup_keymatches exactly. - path_prefix: Filter to Solutions whose
virtual_pathstarts with this prefix. - owners: Restrict results to a subset of owner scopes. Accepted values:
"system"(app-level Solutions) and"org"(viewer's org-level Solutions). Omit to include all scopes the viewer can see.
Returns:
Paginated list of Solution summaries visible to the caller.
351 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 352 """ 353 Import a Solution into the library 354 Imports a Solution and its bundled configs (skills, scripts, templates, files) 355 into the library for the target scope. Two mutually exclusive import modes 356 are supported: pass `solution` to re-import an existing system-owned catalog 357 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 358 self-contained inline bundle. Exactly one must be present. 359 The operation upserts the bundle in a single transaction. When `dry_run` is 360 `true` the same pipeline runs but the transaction is rolled back no rows are 361 persisted and the response reflects what would have been written. The 362 response shape is the same in both cases: the Solution summary plus 363 `installed_configs` listing each config the import created or would create. 364 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 365 Solution into the library; install provisions a runtime resource (Agent, 366 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 367 368 Args: 369 input: Request body. 370 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 371 input.org: Organization ID (`org_...`) for the import destination scope. 372 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 373 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 374 input.team: Team ID (`team_...`) for the import destination scope. 375 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 376 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 377 378 Returns: 379 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 380 """ 381 return await self._http.request( 382 "/api/v1/solutions", 383 method="POST", 384 body=input, 385 response_type=SolutionImportResponse, 386 )
Import a Solution into the library
Imports a Solution and its bundled configs (skills, scripts, templates, files)
into the library for the target scope. Two mutually exclusive import modes
are supported: pass solution to re-import an existing system-owned catalog
Solution by ID or lookup_key, or pass solution_bundle to supply a
self-contained inline bundle. Exactly one must be present.
The operation upserts the bundle in a single transaction. When dry_run is
true the same pipeline runs but the transaction is rolled back no rows are
persisted and the response reflects what would have been written. The
response shape is the same in both cases: the Solution summary plus
installed_configs listing each config the import created or would create.
Pairs with POST /api/v1/solutions/:solution/install: this endpoint puts the
Solution into the library; install provisions a runtime resource (Agent,
AgentRoutine, AgentTool, etc.) from an already-imported Solution.
Arguments:
- input: Request body.
- input.dry_run: When
true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults tofalse. - input.org: Organization ID (
org_...) for the import destination scope. - input.solution: Config ID (
cfg_...) orlookup_keyof an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive withsolution_bundle. - input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with
solution. - input.team: Team ID (
team_...) for the import destination scope. - input.user: User ID (
usr_...) for the import destination scope. Only one oforg,team, orusermay be set. - input.virtual_path_prefix: Path prefix under which all uploaded configs'
virtual_pathvalues are anchored (for examplesolutions/<uuid>). Stable per install; omit to use no prefix.
Returns:
The imported Solution in summary form, plus
installed_configsone entry per config the transaction created or would create indry_runmode.installed_configsis deprecated; prefer thesolutionsummary shape for new integrations.
388 async def delete(self, solution: str) -> None: 389 """ 390 Delete a Solution 391 Permanently deletes an imported Solution and all configs bundled with it, 392 including templates, skills, scripts, and files. The deletion runs in a 393 single transaction; provider-stored blobs are swept asynchronously after commit. 394 Org-scope callers (for example, an org admin in the Library settings page) can 395 only delete their org's copy of the Solution. App-scope callers can delete 396 Solutions at either scope. RBAC is enforced inside the core delete flow. 397 Returns `204 No Content` on success. 398 399 Args: 400 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 401 402 Returns: 403 Empty body. HTTP 204 indicates the Solution was deleted successfully. 404 """ 405 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
Delete a Solution
Permanently deletes an imported Solution and all configs bundled with it,
including templates, skills, scripts, and files. The deletion runs in a
single transaction; provider-stored blobs are swept asynchronously after commit.
Org-scope callers (for example, an org admin in the Library settings page) can
only delete their org's copy of the Solution. App-scope callers can delete
Solutions at either scope. RBAC is enforced inside the core delete flow.
Returns 204 No Content on success.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to delete.
Returns:
Empty body. HTTP 204 indicates the Solution was deleted successfully.
407 async def get(self, solution: str) -> SolutionSummary: 408 """ 409 Retrieve a Solution 410 Returns a single Solution identified by its config ID (`cfg_...`) or 411 `lookup_key`, in the same summary shape the list endpoint emits. The response 412 always includes a freshly-minted `readme_url` call this endpoint to refresh 413 an expired README token without making any other state change. 414 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 415 are visible to everyone including unauthenticated callers, so the public 416 catalog can render a Solution's detail page logged-out; org-scoped Solutions 417 are only visible to authenticated viewers whose org context matches. Anything 418 else returns 404. 419 When the resolved Solution is org-scoped, the endpoint compares its 420 `solution_version` against the matching app-level copy. If the app-level copy 421 is at a higher version the response includes `upgrade_available: true` and 422 `latest_version`. App-level resolutions always report `upgrade_available: false`. 423 424 Args: 425 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 426 427 Returns: 428 Solution summary including a freshly-minted `readme_url` token valid for one hour. 429 """ 430 return await self._http.request( 431 f"/api/v1/solutions/{solution}", 432 response_type=SolutionSummary, 433 )
Retrieve a Solution
Returns a single Solution identified by its config ID (cfg_...) or
lookup_key, in the same summary shape the list endpoint emits. The response
always includes a freshly-minted readme_url call this endpoint to refresh
an expired README token without making any other state change.
Visibility matches the list endpoint: app-level Solutions (no org affiliation)
are visible to everyone including unauthenticated callers, so the public
catalog can render a Solution's detail page logged-out; org-scoped Solutions
are only visible to authenticated viewers whose org context matches. Anything
else returns 404.
When the resolved Solution is org-scoped, the endpoint compares its
solution_version against the matching app-level copy. If the app-level copy
is at a higher version the response includes upgrade_available: true and
latest_version. App-level resolutions always report upgrade_available: false.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to retrieve.
Returns:
Solution summary including a freshly-minted
readme_urltoken valid for one hour.
435 async def dependents(self, solution: str) -> SolutionDependentsResponse: 436 """ 437 Preview Solution delete impact 438 Returns a read-only preview of what deleting the specified Solution would 439 affect: the agents that reference the Solution's bundle, and the count of 440 bundled configs that would be orphaned rather than cascade-deleted. 441 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 442 surface a warning when live agents depend on the Solution. 443 Visibility scope mirrors the delete endpoint: org-scope viewers see their 444 org's copy; app-scope viewers can inspect either scope. 445 446 Args: 447 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 448 449 Returns: 450 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 451 """ 452 return await self._http.request( 453 f"/api/v1/solutions/{solution}/dependents", 454 response_type=SolutionDependentsResponse, 455 )
Preview Solution delete impact
Returns a read-only preview of what deleting the specified Solution would
affect: the agents that reference the Solution's bundle, and the count of
bundled configs that would be orphaned rather than cascade-deleted.
Use this endpoint before calling DELETE /api/v1/solutions/:solution to
surface a warning when live agents depend on the Solution.
Visibility scope mirrors the delete endpoint: org-scope viewers see their
org's copy; app-scope viewers can inspect either scope.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to inspect.
Returns:
Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
457 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 458 """ 459 Install a Solution 460 Provisions a runtime resource from an already-imported Solution. The type of 461 resource created depends on the template the Solution wraps: an 462 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 463 Automation, and attachment templates (`AgentRoutineTemplate`, 464 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 465 sub-resource to an existing Agent specified by `target`. 466 For Solutions that bundle more than one template, pass `template` (the ID or 467 `lookup_key` of the desired template) to select which one to provision. 468 Single-template Solutions do not require `template`. 469 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 470 the library; install provisions a runtime resource from it. 471 472 Args: 473 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 474 input: Request body. 475 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 476 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 477 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 478 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 479 input.org: Organization ID (`org_...`) for the install destination scope. 480 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 481 input.team: Team ID (`team_...`) for the install destination scope. 482 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 483 input.user: User ID (`usr_...`) for the install destination scope. 484 485 Returns: 486 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 487 """ 488 return await self._http.request( 489 f"/api/v1/solutions/{solution}/install", 490 method="POST", 491 body=input, 492 response_type=SolutionInstallResponse, 493 )
Install a Solution
Provisions a runtime resource from an already-imported Solution. The type of
resource created depends on the template the Solution wraps: an
AgentTemplate produces an Agent, an AutomationTemplate produces an
Automation, and attachment templates (AgentRoutineTemplate,
AgentToolTemplate, AgentSkillTemplate, AgentComputerTemplate) attach a
sub-resource to an existing Agent specified by target.
For Solutions that bundle more than one template, pass template (the ID or
lookup_key of the desired template) to select which one to provision.
Single-template Solutions do not require template.
Pairs with POST /api/v1/solutions (import): import puts the Solution into
the library; install provisions a runtime resource from it.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the already-imported Solution to install from. - input: Request body.
- input.allow_auto_import: When
true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicitorgparam. Defaults tofalse; without it the endpoint returns 404 when the org-scoped Solution is missing. - input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's
agent_key). - input.lookup_key_suffix: Suffix appended to every
config_ref:resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. - input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions.
- input.org: Organization ID (
org_...) for the install destination scope. - input.target: ID or
lookup_keyof the parent Agent to attach to. Required when installing anAgentRoutineTemplate,AgentToolTemplate,AgentSkillTemplate, orAgentComputerTemplateSolution, since those produce sub-resources attached to an existing Agent. Omit forAgentTemplateandAutomationTemplateSolutions, which provision standalone resources. - input.team: Team ID (
team_...) for the install destination scope. - input.template: Config ID (
cfg_...) orlookup_keyof the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. - input.user: User ID (
usr_...) for the install destination scope.
Returns:
The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
495 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 496 """ 497 Retrieve a Solution README or asset 498 Serves the README markdown or a bundled asset for an imported Solution. Both 499 modes use the same path and require a short-lived signed `token` in the query 500 string rather than an `Authorization` header, so browsers can load asset URLs 501 directly from `<img src>` attributes without custom request logic. 502 When `file` is omitted the response is the Solution's `readme` field rendered 503 as `text/markdown`. All local asset references in the markdown are rewritten to 504 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 505 images inline without additional authentication. 506 When `file` is set the response is the raw bytes of the matching asset (a File 507 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 508 name) with the asset's stored `Content-Type`. 509 Tokens are scoped to a single Solution, carry the viewer's app, org, and 510 sandbox context from the time they were minted, and expire after one hour. 511 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 512 always returns a newly minted `readme_url`. 513 514 Args: 515 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 516 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 517 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 518 519 Returns: 520 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 521 """ 522 query: dict[str, object] = {} 523 query["token"] = token 524 if file is not None: 525 query["file"] = file 526 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
Retrieve a Solution README or asset
Serves the README markdown or a bundled asset for an imported Solution. Both
modes use the same path and require a short-lived signed token in the query
string rather than an Authorization header, so browsers can load asset URLs
directly from <img src> attributes without custom request logic.
When file is omitted the response is the Solution's readme field rendered
as text/markdown. All local asset references in the markdown are rewritten to
point back at this endpoint with ?file=PATH&token=TOKEN so browsers can load
images inline without additional authentication.
When file is set the response is the raw bytes of the matching asset (a File
child whose relative_path equals PATH, or an inline assets entry by
name) with the asset's stored Content-Type.
Tokens are scoped to a single Solution, carry the viewer's app, org, and
sandbox context from the time they were minted, and expire after one hour.
Obtain a fresh token by calling GET /api/v1/solutions/:solution, which
always returns a newly minted readme_url.
Arguments:
- solution: Solution config ID (
cfg_...) identifying the Solution whose README or asset to retrieve. - token: Signed URL token minted by the list or show endpoint. Expires after one hour.
- file: Relative path of the asset to retrieve (for example
images/hero.png). When present the response is the raw asset bytes with its realContent-Type; when absent the response is the README markdown.
Returns:
README markdown (
text/markdown) whenfileis omitted, or the raw asset bytes with the asset'sContent-Typewhenfileis set.
528 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 529 """ 530 Upgrade an installed Solution 531 Applies an incoming bundle to an already-installed Solution in a single atomic 532 transaction, bringing its configs in line with the new bundle. Config IDs are 533 preserved across the upgrade. Configs that existed in the old bundle but are 534 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 535 Two mutually exclusive source modes: pass `target_solution` to pull the 536 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 537 `solution_bundle` to supply a complete inline bundle directly. Exactly one 538 must be present. 539 When `dry_run` is `true` the full diff is computed and returned but no 540 changes are written. Pass the dry-run response's `review_fingerprint` as 541 `expected_review_fingerprint` when applying to guard against the bundle 542 changing between review and apply. 543 544 Args: 545 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 546 input: Request body. 547 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 548 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 549 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 550 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 551 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 552 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 553 554 Returns: 555 Updated Solution plus the full upgrade diff. 556 """ 557 return await self._http.request( 558 f"/api/v1/solutions/{solution}/upgrade", 559 method="POST", 560 body=input, 561 response_type=SolutionUpgradeResponse, 562 )
Upgrade an installed Solution
Applies an incoming bundle to an already-installed Solution in a single atomic
transaction, bringing its configs in line with the new bundle. Config IDs are
preserved across the upgrade. Configs that existed in the old bundle but are
absent from the new one are orphaned (top-level) or hard-deleted (child rows).
Two mutually exclusive source modes: pass target_solution to pull the
incoming bundle from an existing Solution by ID or lookup_key, or pass
solution_bundle to supply a complete inline bundle directly. Exactly one
must be present.
When dry_run is true the full diff is computed and returned but no
changes are written. Pass the dry-run response's review_fingerprint as
expected_review_fingerprint when applying to guard against the bundle
changing between review and apply.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the currently installed Solution to upgrade. - input: Request body.
- input.allow_downgrade: When
true, permits an incomingsolution_versionlower than the currently installed version. Defaults tofalse. - input.dry_run: When
true, computes and returns the full upgrade diff without persisting any changes. Defaults tofalse. - input.expected_review_fingerprint: Optional stale-review guard. Pass the
review_fingerprintreturned by a priordry_runcall to ensure the bundle has not changed between review and apply. - input.org: Organization ID (
org_...) used to resolve org-scopedlookup_keyvalues. Config IDs (cfg_...) are globally unique and do not require this. - input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with
target_solution. - input.target_solution: Config ID (
cfg_...) orlookup_keyof the Solution to use as the incoming upgrade source. Mutually exclusive withsolution_bundle.
Returns:
Updated Solution plus the full upgrade diff.
565class SolutionResource: 566 def __init__(self, http: SyncHttpClient): 567 self._http = http 568 569 def list( 570 self, 571 *, 572 page: int | None = None, 573 page_size: int | None = None, 574 lookup_key: str | None = None, 575 path_prefix: str | None = None, 576 owners: builtins.list[str] | None = None, 577 ) -> SolutionListResponse: 578 """ 579 List Solutions 580 Returns a paginated list of Solutions visible to the caller, merging two 581 scopes: app-level Solutions (system-owned rows with no org affiliation, 582 visible to everyone including unauthenticated callers so they can power 583 the public catalog) and org-level Solutions (system-owned rows stamped with 584 the viewer's org ID, included when an authenticated viewer carries an org 585 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 586 and therefore only ever see the app-level scope. 587 Solutions that appear under both scopes are deduplicated by their stable 588 `solution_id` value. The merged entry's `owners` array lists every scope the 589 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 590 has a higher `solution_version` than the org-level copy, the response includes 591 `upgrade_available: true` and `latest_version` so callers can prompt for an 592 upgrade. 593 594 Args: 595 page: Page number to return. Defaults to `1`. 596 page_size: Number of Solutions per page. Defaults to `25`. 597 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 598 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 599 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 600 601 Returns: 602 Paginated list of Solution summaries visible to the caller. 603 """ 604 query: dict[str, object] = {} 605 if page is not None: 606 query["page"] = page 607 if page_size is not None: 608 query["page_size"] = page_size 609 if lookup_key is not None: 610 query["lookup_key"] = lookup_key 611 if path_prefix is not None: 612 query["path_prefix"] = path_prefix 613 if owners is not None: 614 query["owners"] = owners 615 return self._http.request( 616 "/api/v1/solutions", 617 query=query, 618 response_type=SolutionListResponse, 619 ) 620 621 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 622 """ 623 Import a Solution into the library 624 Imports a Solution and its bundled configs (skills, scripts, templates, files) 625 into the library for the target scope. Two mutually exclusive import modes 626 are supported: pass `solution` to re-import an existing system-owned catalog 627 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 628 self-contained inline bundle. Exactly one must be present. 629 The operation upserts the bundle in a single transaction. When `dry_run` is 630 `true` the same pipeline runs but the transaction is rolled back no rows are 631 persisted and the response reflects what would have been written. The 632 response shape is the same in both cases: the Solution summary plus 633 `installed_configs` listing each config the import created or would create. 634 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 635 Solution into the library; install provisions a runtime resource (Agent, 636 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 637 638 Args: 639 input: Request body. 640 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 641 input.org: Organization ID (`org_...`) for the import destination scope. 642 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 643 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 644 input.team: Team ID (`team_...`) for the import destination scope. 645 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 646 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 647 648 Returns: 649 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 650 """ 651 return self._http.request( 652 "/api/v1/solutions", 653 method="POST", 654 body=input, 655 response_type=SolutionImportResponse, 656 ) 657 658 def delete(self, solution: str) -> None: 659 """ 660 Delete a Solution 661 Permanently deletes an imported Solution and all configs bundled with it, 662 including templates, skills, scripts, and files. The deletion runs in a 663 single transaction; provider-stored blobs are swept asynchronously after commit. 664 Org-scope callers (for example, an org admin in the Library settings page) can 665 only delete their org's copy of the Solution. App-scope callers can delete 666 Solutions at either scope. RBAC is enforced inside the core delete flow. 667 Returns `204 No Content` on success. 668 669 Args: 670 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 671 672 Returns: 673 Empty body. HTTP 204 indicates the Solution was deleted successfully. 674 """ 675 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 676 677 def get(self, solution: str) -> SolutionSummary: 678 """ 679 Retrieve a Solution 680 Returns a single Solution identified by its config ID (`cfg_...`) or 681 `lookup_key`, in the same summary shape the list endpoint emits. The response 682 always includes a freshly-minted `readme_url` call this endpoint to refresh 683 an expired README token without making any other state change. 684 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 685 are visible to everyone including unauthenticated callers, so the public 686 catalog can render a Solution's detail page logged-out; org-scoped Solutions 687 are only visible to authenticated viewers whose org context matches. Anything 688 else returns 404. 689 When the resolved Solution is org-scoped, the endpoint compares its 690 `solution_version` against the matching app-level copy. If the app-level copy 691 is at a higher version the response includes `upgrade_available: true` and 692 `latest_version`. App-level resolutions always report `upgrade_available: false`. 693 694 Args: 695 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 696 697 Returns: 698 Solution summary including a freshly-minted `readme_url` token valid for one hour. 699 """ 700 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary) 701 702 def dependents(self, solution: str) -> SolutionDependentsResponse: 703 """ 704 Preview Solution delete impact 705 Returns a read-only preview of what deleting the specified Solution would 706 affect: the agents that reference the Solution's bundle, and the count of 707 bundled configs that would be orphaned rather than cascade-deleted. 708 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 709 surface a warning when live agents depend on the Solution. 710 Visibility scope mirrors the delete endpoint: org-scope viewers see their 711 org's copy; app-scope viewers can inspect either scope. 712 713 Args: 714 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 715 716 Returns: 717 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 718 """ 719 return self._http.request( 720 f"/api/v1/solutions/{solution}/dependents", 721 response_type=SolutionDependentsResponse, 722 ) 723 724 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 725 """ 726 Install a Solution 727 Provisions a runtime resource from an already-imported Solution. The type of 728 resource created depends on the template the Solution wraps: an 729 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 730 Automation, and attachment templates (`AgentRoutineTemplate`, 731 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 732 sub-resource to an existing Agent specified by `target`. 733 For Solutions that bundle more than one template, pass `template` (the ID or 734 `lookup_key` of the desired template) to select which one to provision. 735 Single-template Solutions do not require `template`. 736 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 737 the library; install provisions a runtime resource from it. 738 739 Args: 740 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 741 input: Request body. 742 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 743 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 744 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 745 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 746 input.org: Organization ID (`org_...`) for the install destination scope. 747 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 748 input.team: Team ID (`team_...`) for the install destination scope. 749 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 750 input.user: User ID (`usr_...`) for the install destination scope. 751 752 Returns: 753 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 754 """ 755 return self._http.request( 756 f"/api/v1/solutions/{solution}/install", 757 method="POST", 758 body=input, 759 response_type=SolutionInstallResponse, 760 ) 761 762 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 763 """ 764 Retrieve a Solution README or asset 765 Serves the README markdown or a bundled asset for an imported Solution. Both 766 modes use the same path and require a short-lived signed `token` in the query 767 string rather than an `Authorization` header, so browsers can load asset URLs 768 directly from `<img src>` attributes without custom request logic. 769 When `file` is omitted the response is the Solution's `readme` field rendered 770 as `text/markdown`. All local asset references in the markdown are rewritten to 771 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 772 images inline without additional authentication. 773 When `file` is set the response is the raw bytes of the matching asset (a File 774 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 775 name) with the asset's stored `Content-Type`. 776 Tokens are scoped to a single Solution, carry the viewer's app, org, and 777 sandbox context from the time they were minted, and expire after one hour. 778 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 779 always returns a newly minted `readme_url`. 780 781 Args: 782 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 783 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 784 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 785 786 Returns: 787 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 788 """ 789 query: dict[str, object] = {} 790 query["token"] = token 791 if file is not None: 792 query["file"] = file 793 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 794 795 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 796 """ 797 Upgrade an installed Solution 798 Applies an incoming bundle to an already-installed Solution in a single atomic 799 transaction, bringing its configs in line with the new bundle. Config IDs are 800 preserved across the upgrade. Configs that existed in the old bundle but are 801 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 802 Two mutually exclusive source modes: pass `target_solution` to pull the 803 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 804 `solution_bundle` to supply a complete inline bundle directly. Exactly one 805 must be present. 806 When `dry_run` is `true` the full diff is computed and returned but no 807 changes are written. Pass the dry-run response's `review_fingerprint` as 808 `expected_review_fingerprint` when applying to guard against the bundle 809 changing between review and apply. 810 811 Args: 812 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 813 input: Request body. 814 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 815 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 816 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 817 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 818 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 819 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 820 821 Returns: 822 Updated Solution plus the full upgrade diff. 823 """ 824 return self._http.request( 825 f"/api/v1/solutions/{solution}/upgrade", 826 method="POST", 827 body=input, 828 response_type=SolutionUpgradeResponse, 829 )
569 def list( 570 self, 571 *, 572 page: int | None = None, 573 page_size: int | None = None, 574 lookup_key: str | None = None, 575 path_prefix: str | None = None, 576 owners: builtins.list[str] | None = None, 577 ) -> SolutionListResponse: 578 """ 579 List Solutions 580 Returns a paginated list of Solutions visible to the caller, merging two 581 scopes: app-level Solutions (system-owned rows with no org affiliation, 582 visible to everyone including unauthenticated callers so they can power 583 the public catalog) and org-level Solutions (system-owned rows stamped with 584 the viewer's org ID, included when an authenticated viewer carries an org 585 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 586 and therefore only ever see the app-level scope. 587 Solutions that appear under both scopes are deduplicated by their stable 588 `solution_id` value. The merged entry's `owners` array lists every scope the 589 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 590 has a higher `solution_version` than the org-level copy, the response includes 591 `upgrade_available: true` and `latest_version` so callers can prompt for an 592 upgrade. 593 594 Args: 595 page: Page number to return. Defaults to `1`. 596 page_size: Number of Solutions per page. Defaults to `25`. 597 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 598 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 599 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 600 601 Returns: 602 Paginated list of Solution summaries visible to the caller. 603 """ 604 query: dict[str, object] = {} 605 if page is not None: 606 query["page"] = page 607 if page_size is not None: 608 query["page_size"] = page_size 609 if lookup_key is not None: 610 query["lookup_key"] = lookup_key 611 if path_prefix is not None: 612 query["path_prefix"] = path_prefix 613 if owners is not None: 614 query["owners"] = owners 615 return self._http.request( 616 "/api/v1/solutions", 617 query=query, 618 response_type=SolutionListResponse, 619 )
List Solutions
Returns a paginated list of Solutions visible to the caller, merging two
scopes: app-level Solutions (system-owned rows with no org affiliation,
visible to everyone including unauthenticated callers so they can power
the public catalog) and org-level Solutions (system-owned rows stamped with
the viewer's org ID, included when an authenticated viewer carries an org
context). Unauthenticated callers resolve to an app-scoped anonymous viewer
and therefore only ever see the app-level scope.
Solutions that appear under both scopes are deduplicated by their stable
solution_id value. The merged entry's owners array lists every scope the
Solution was found under ("system" and/or "org"). When the app-level copy
has a higher solution_version than the org-level copy, the response includes
upgrade_available: true and latest_version so callers can prompt for an
upgrade.
Arguments:
- page: Page number to return. Defaults to
1. - page_size: Number of Solutions per page. Defaults to
25. - lookup_key: Filter to the Solution whose
lookup_keymatches exactly. - path_prefix: Filter to Solutions whose
virtual_pathstarts with this prefix. - owners: Restrict results to a subset of owner scopes. Accepted values:
"system"(app-level Solutions) and"org"(viewer's org-level Solutions). Omit to include all scopes the viewer can see.
Returns:
Paginated list of Solution summaries visible to the caller.
621 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 622 """ 623 Import a Solution into the library 624 Imports a Solution and its bundled configs (skills, scripts, templates, files) 625 into the library for the target scope. Two mutually exclusive import modes 626 are supported: pass `solution` to re-import an existing system-owned catalog 627 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 628 self-contained inline bundle. Exactly one must be present. 629 The operation upserts the bundle in a single transaction. When `dry_run` is 630 `true` the same pipeline runs but the transaction is rolled back no rows are 631 persisted and the response reflects what would have been written. The 632 response shape is the same in both cases: the Solution summary plus 633 `installed_configs` listing each config the import created or would create. 634 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 635 Solution into the library; install provisions a runtime resource (Agent, 636 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 637 638 Args: 639 input: Request body. 640 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 641 input.org: Organization ID (`org_...`) for the import destination scope. 642 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 643 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 644 input.team: Team ID (`team_...`) for the import destination scope. 645 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 646 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 647 648 Returns: 649 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 650 """ 651 return self._http.request( 652 "/api/v1/solutions", 653 method="POST", 654 body=input, 655 response_type=SolutionImportResponse, 656 )
Import a Solution into the library
Imports a Solution and its bundled configs (skills, scripts, templates, files)
into the library for the target scope. Two mutually exclusive import modes
are supported: pass solution to re-import an existing system-owned catalog
Solution by ID or lookup_key, or pass solution_bundle to supply a
self-contained inline bundle. Exactly one must be present.
The operation upserts the bundle in a single transaction. When dry_run is
true the same pipeline runs but the transaction is rolled back no rows are
persisted and the response reflects what would have been written. The
response shape is the same in both cases: the Solution summary plus
installed_configs listing each config the import created or would create.
Pairs with POST /api/v1/solutions/:solution/install: this endpoint puts the
Solution into the library; install provisions a runtime resource (Agent,
AgentRoutine, AgentTool, etc.) from an already-imported Solution.
Arguments:
- input: Request body.
- input.dry_run: When
true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults tofalse. - input.org: Organization ID (
org_...) for the import destination scope. - input.solution: Config ID (
cfg_...) orlookup_keyof an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive withsolution_bundle. - input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with
solution. - input.team: Team ID (
team_...) for the import destination scope. - input.user: User ID (
usr_...) for the import destination scope. Only one oforg,team, orusermay be set. - input.virtual_path_prefix: Path prefix under which all uploaded configs'
virtual_pathvalues are anchored (for examplesolutions/<uuid>). Stable per install; omit to use no prefix.
Returns:
The imported Solution in summary form, plus
installed_configsone entry per config the transaction created or would create indry_runmode.installed_configsis deprecated; prefer thesolutionsummary shape for new integrations.
658 def delete(self, solution: str) -> None: 659 """ 660 Delete a Solution 661 Permanently deletes an imported Solution and all configs bundled with it, 662 including templates, skills, scripts, and files. The deletion runs in a 663 single transaction; provider-stored blobs are swept asynchronously after commit. 664 Org-scope callers (for example, an org admin in the Library settings page) can 665 only delete their org's copy of the Solution. App-scope callers can delete 666 Solutions at either scope. RBAC is enforced inside the core delete flow. 667 Returns `204 No Content` on success. 668 669 Args: 670 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 671 672 Returns: 673 Empty body. HTTP 204 indicates the Solution was deleted successfully. 674 """ 675 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
Delete a Solution
Permanently deletes an imported Solution and all configs bundled with it,
including templates, skills, scripts, and files. The deletion runs in a
single transaction; provider-stored blobs are swept asynchronously after commit.
Org-scope callers (for example, an org admin in the Library settings page) can
only delete their org's copy of the Solution. App-scope callers can delete
Solutions at either scope. RBAC is enforced inside the core delete flow.
Returns 204 No Content on success.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to delete.
Returns:
Empty body. HTTP 204 indicates the Solution was deleted successfully.
677 def get(self, solution: str) -> SolutionSummary: 678 """ 679 Retrieve a Solution 680 Returns a single Solution identified by its config ID (`cfg_...`) or 681 `lookup_key`, in the same summary shape the list endpoint emits. The response 682 always includes a freshly-minted `readme_url` call this endpoint to refresh 683 an expired README token without making any other state change. 684 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 685 are visible to everyone including unauthenticated callers, so the public 686 catalog can render a Solution's detail page logged-out; org-scoped Solutions 687 are only visible to authenticated viewers whose org context matches. Anything 688 else returns 404. 689 When the resolved Solution is org-scoped, the endpoint compares its 690 `solution_version` against the matching app-level copy. If the app-level copy 691 is at a higher version the response includes `upgrade_available: true` and 692 `latest_version`. App-level resolutions always report `upgrade_available: false`. 693 694 Args: 695 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 696 697 Returns: 698 Solution summary including a freshly-minted `readme_url` token valid for one hour. 699 """ 700 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary)
Retrieve a Solution
Returns a single Solution identified by its config ID (cfg_...) or
lookup_key, in the same summary shape the list endpoint emits. The response
always includes a freshly-minted readme_url call this endpoint to refresh
an expired README token without making any other state change.
Visibility matches the list endpoint: app-level Solutions (no org affiliation)
are visible to everyone including unauthenticated callers, so the public
catalog can render a Solution's detail page logged-out; org-scoped Solutions
are only visible to authenticated viewers whose org context matches. Anything
else returns 404.
When the resolved Solution is org-scoped, the endpoint compares its
solution_version against the matching app-level copy. If the app-level copy
is at a higher version the response includes upgrade_available: true and
latest_version. App-level resolutions always report upgrade_available: false.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to retrieve.
Returns:
Solution summary including a freshly-minted
readme_urltoken valid for one hour.
702 def dependents(self, solution: str) -> SolutionDependentsResponse: 703 """ 704 Preview Solution delete impact 705 Returns a read-only preview of what deleting the specified Solution would 706 affect: the agents that reference the Solution's bundle, and the count of 707 bundled configs that would be orphaned rather than cascade-deleted. 708 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 709 surface a warning when live agents depend on the Solution. 710 Visibility scope mirrors the delete endpoint: org-scope viewers see their 711 org's copy; app-scope viewers can inspect either scope. 712 713 Args: 714 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 715 716 Returns: 717 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 718 """ 719 return self._http.request( 720 f"/api/v1/solutions/{solution}/dependents", 721 response_type=SolutionDependentsResponse, 722 )
Preview Solution delete impact
Returns a read-only preview of what deleting the specified Solution would
affect: the agents that reference the Solution's bundle, and the count of
bundled configs that would be orphaned rather than cascade-deleted.
Use this endpoint before calling DELETE /api/v1/solutions/:solution to
surface a warning when live agents depend on the Solution.
Visibility scope mirrors the delete endpoint: org-scope viewers see their
org's copy; app-scope viewers can inspect either scope.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to inspect.
Returns:
Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
724 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 725 """ 726 Install a Solution 727 Provisions a runtime resource from an already-imported Solution. The type of 728 resource created depends on the template the Solution wraps: an 729 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 730 Automation, and attachment templates (`AgentRoutineTemplate`, 731 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 732 sub-resource to an existing Agent specified by `target`. 733 For Solutions that bundle more than one template, pass `template` (the ID or 734 `lookup_key` of the desired template) to select which one to provision. 735 Single-template Solutions do not require `template`. 736 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 737 the library; install provisions a runtime resource from it. 738 739 Args: 740 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 741 input: Request body. 742 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 743 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 744 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 745 input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions. 746 input.org: Organization ID (`org_...`) for the install destination scope. 747 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 748 input.team: Team ID (`team_...`) for the install destination scope. 749 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 750 input.user: User ID (`usr_...`) for the install destination scope. 751 752 Returns: 753 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 754 """ 755 return self._http.request( 756 f"/api/v1/solutions/{solution}/install", 757 method="POST", 758 body=input, 759 response_type=SolutionInstallResponse, 760 )
Install a Solution
Provisions a runtime resource from an already-imported Solution. The type of
resource created depends on the template the Solution wraps: an
AgentTemplate produces an Agent, an AutomationTemplate produces an
Automation, and attachment templates (AgentRoutineTemplate,
AgentToolTemplate, AgentSkillTemplate, AgentComputerTemplate) attach a
sub-resource to an existing Agent specified by target.
For Solutions that bundle more than one template, pass template (the ID or
lookup_key of the desired template) to select which one to provision.
Single-template Solutions do not require template.
Pairs with POST /api/v1/solutions (import): import puts the Solution into
the library; install provisions a runtime resource from it.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the already-imported Solution to install from. - input: Request body.
- input.allow_auto_import: When
true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicitorgparam. Defaults tofalse; without it the endpoint returns 404 when the org-scoped Solution is missing. - input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's
agent_key). - input.lookup_key_suffix: Suffix appended to every
config_ref:resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. - input.name: Display name override for the provisioned Agent. Ignored for non-Agent Solutions.
- input.org: Organization ID (
org_...) for the install destination scope. - input.target: ID or
lookup_keyof the parent Agent to attach to. Required when installing anAgentRoutineTemplate,AgentToolTemplate,AgentSkillTemplate, orAgentComputerTemplateSolution, since those produce sub-resources attached to an existing Agent. Omit forAgentTemplateandAutomationTemplateSolutions, which provision standalone resources. - input.team: Team ID (
team_...) for the install destination scope. - input.template: Config ID (
cfg_...) orlookup_keyof the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. - input.user: User ID (
usr_...) for the install destination scope.
Returns:
The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
762 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 763 """ 764 Retrieve a Solution README or asset 765 Serves the README markdown or a bundled asset for an imported Solution. Both 766 modes use the same path and require a short-lived signed `token` in the query 767 string rather than an `Authorization` header, so browsers can load asset URLs 768 directly from `<img src>` attributes without custom request logic. 769 When `file` is omitted the response is the Solution's `readme` field rendered 770 as `text/markdown`. All local asset references in the markdown are rewritten to 771 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 772 images inline without additional authentication. 773 When `file` is set the response is the raw bytes of the matching asset (a File 774 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 775 name) with the asset's stored `Content-Type`. 776 Tokens are scoped to a single Solution, carry the viewer's app, org, and 777 sandbox context from the time they were minted, and expire after one hour. 778 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 779 always returns a newly minted `readme_url`. 780 781 Args: 782 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 783 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 784 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 785 786 Returns: 787 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 788 """ 789 query: dict[str, object] = {} 790 query["token"] = token 791 if file is not None: 792 query["file"] = file 793 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
Retrieve a Solution README or asset
Serves the README markdown or a bundled asset for an imported Solution. Both
modes use the same path and require a short-lived signed token in the query
string rather than an Authorization header, so browsers can load asset URLs
directly from <img src> attributes without custom request logic.
When file is omitted the response is the Solution's readme field rendered
as text/markdown. All local asset references in the markdown are rewritten to
point back at this endpoint with ?file=PATH&token=TOKEN so browsers can load
images inline without additional authentication.
When file is set the response is the raw bytes of the matching asset (a File
child whose relative_path equals PATH, or an inline assets entry by
name) with the asset's stored Content-Type.
Tokens are scoped to a single Solution, carry the viewer's app, org, and
sandbox context from the time they were minted, and expire after one hour.
Obtain a fresh token by calling GET /api/v1/solutions/:solution, which
always returns a newly minted readme_url.
Arguments:
- solution: Solution config ID (
cfg_...) identifying the Solution whose README or asset to retrieve. - token: Signed URL token minted by the list or show endpoint. Expires after one hour.
- file: Relative path of the asset to retrieve (for example
images/hero.png). When present the response is the raw asset bytes with its realContent-Type; when absent the response is the README markdown.
Returns:
README markdown (
text/markdown) whenfileis omitted, or the raw asset bytes with the asset'sContent-Typewhenfileis set.
795 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 796 """ 797 Upgrade an installed Solution 798 Applies an incoming bundle to an already-installed Solution in a single atomic 799 transaction, bringing its configs in line with the new bundle. Config IDs are 800 preserved across the upgrade. Configs that existed in the old bundle but are 801 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 802 Two mutually exclusive source modes: pass `target_solution` to pull the 803 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 804 `solution_bundle` to supply a complete inline bundle directly. Exactly one 805 must be present. 806 When `dry_run` is `true` the full diff is computed and returned but no 807 changes are written. Pass the dry-run response's `review_fingerprint` as 808 `expected_review_fingerprint` when applying to guard against the bundle 809 changing between review and apply. 810 811 Args: 812 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 813 input: Request body. 814 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 815 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 816 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 817 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 818 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 819 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 820 821 Returns: 822 Updated Solution plus the full upgrade diff. 823 """ 824 return self._http.request( 825 f"/api/v1/solutions/{solution}/upgrade", 826 method="POST", 827 body=input, 828 response_type=SolutionUpgradeResponse, 829 )
Upgrade an installed Solution
Applies an incoming bundle to an already-installed Solution in a single atomic
transaction, bringing its configs in line with the new bundle. Config IDs are
preserved across the upgrade. Configs that existed in the old bundle but are
absent from the new one are orphaned (top-level) or hard-deleted (child rows).
Two mutually exclusive source modes: pass target_solution to pull the
incoming bundle from an existing Solution by ID or lookup_key, or pass
solution_bundle to supply a complete inline bundle directly. Exactly one
must be present.
When dry_run is true the full diff is computed and returned but no
changes are written. Pass the dry-run response's review_fingerprint as
expected_review_fingerprint when applying to guard against the bundle
changing between review and apply.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the currently installed Solution to upgrade. - input: Request body.
- input.allow_downgrade: When
true, permits an incomingsolution_versionlower than the currently installed version. Defaults tofalse. - input.dry_run: When
true, computes and returns the full upgrade diff without persisting any changes. Defaults tofalse. - input.expected_review_fingerprint: Optional stale-review guard. Pass the
review_fingerprintreturned by a priordry_runcall to ensure the bundle has not changed between review and apply. - input.org: Organization ID (
org_...) used to resolve org-scopedlookup_keyvalues. Config IDs (cfg_...) are globally unique and do not require this. - input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with
target_solution. - input.target_solution: Config ID (
cfg_...) orlookup_keyof the Solution to use as the incoming upgrade source. Mutually exclusive withsolution_bundle.
Returns:
Updated Solution plus the full upgrade diff.